chiark / gitweb /
sd-rtnl: fix reading of nla type
[elogind.git] / src / python-systemd / _reader.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Steven Hiscocks, Zbigniew JÄ™drzejewski-Szmek
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <Python.h>
23 #include <structmember.h>
24 #include <datetime.h>
25 #include <time.h>
26 #include <stdio.h>
27
28 #include "systemd/sd-journal.h"
29
30 #include "pyutil.h"
31 #include "macro.h"
32 #include "util.h"
33 #include "strv.h"
34 #include "build.h"
35
36 typedef struct {
37         PyObject_HEAD
38         sd_journal *j;
39 } Reader;
40 static PyTypeObject ReaderType;
41
42 PyDoc_STRVAR(module__doc__,
43              "Class to reads the systemd journal similar to journalctl.");
44
45
46 #if PY_MAJOR_VERSION >= 3
47 static PyTypeObject MonotonicType;
48
49 PyDoc_STRVAR(MonotonicType__doc__,
50              "A tuple of (timestamp, bootid) for holding monotonic timestamps");
51
52 static PyStructSequence_Field MonotonicType_fields[] = {
53         {(char*) "timestamp", (char*) "Time"},
54         {(char*) "bootid", (char*) "Unique identifier of the boot"},
55         {} /* Sentinel */
56 };
57
58 static PyStructSequence_Desc Monotonic_desc = {
59         (char*) "journal.Monotonic",
60         MonotonicType__doc__,
61         MonotonicType_fields,
62         2,
63 };
64 #endif
65
66 /**
67  * Convert a Python sequence object into a strv (char**), and
68  * None into a NULL pointer.
69  */
70 static int strv_converter(PyObject* obj, void *_result) {
71         char ***result = _result;
72         Py_ssize_t i, len;
73
74         assert(result);
75
76         if (!obj)
77                 return 0;
78
79         if (obj == Py_None) {
80                 *result = NULL;
81                 return 1;
82         }
83
84         if (!PySequence_Check(obj))
85                 return 0;
86
87         len = PySequence_Length(obj);
88         *result = new0(char*, len + 1);
89         if (!*result) {
90                 set_error(-ENOMEM, NULL, NULL);
91                 return 0;
92         }
93
94         for (i = 0; i < len; i++) {
95                 PyObject *item;
96 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
97                 int r;
98                 PyObject *bytes;
99 #endif
100                 char *s, *s2;
101
102                 item = PySequence_ITEM(obj, i);
103 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
104                 r = PyUnicode_FSConverter(item, &bytes);
105                 if (r == 0)
106                         goto cleanup;
107
108                 s = PyBytes_AsString(bytes);
109 #else
110                 s = PyString_AsString(item);
111 #endif
112                 if (!s)
113                         goto cleanup;
114
115                 s2 = strdup(s);
116                 if (!s2)
117                         log_oom();
118
119                 (*result)[i] = s2;
120         }
121
122         return 1;
123
124 cleanup:
125         strv_free(*result);
126         *result = NULL;
127
128         return 0;
129 }
130
131 static void Reader_dealloc(Reader* self) {
132         sd_journal_close(self->j);
133         Py_TYPE(self)->tp_free((PyObject*)self);
134 }
135
136 PyDoc_STRVAR(Reader__doc__,
137              "_Reader([flags | path | files]) -> ...\n\n"
138              "_Reader allows filtering and retrieval of Journal entries.\n"
139              "Note: this is a low-level interface, and probably not what you\n"
140              "want, use systemd.journal.Reader instead.\n\n"
141              "Argument `flags` sets open flags of the journal, which can be one\n"
142              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
143              "journal on local machine only; RUNTIME_ONLY opens only\n"
144              "volatile journal files; and SYSTEM opens journal files of\n"
145              "system services and the kernel, and CURRENT_USER opens files\n"
146              "of the current user.\n\n"
147              "Argument `path` is the directory of journal files.\n"
148              "Argument `files` is a list of files. Note that\n"
149              "`flags`, `path`, and `files` are exclusive.\n\n"
150              "_Reader implements the context manager protocol: the journal\n"
151              "will be closed when exiting the block.");
152 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) {
153         int flags = 0, r;
154         char *path = NULL;
155         char **files = NULL;
156
157         static const char* const kwlist[] = {"flags", "path", "files", NULL};
158         if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist,
159                                          &flags, &path, strv_converter, &files))
160                 return -1;
161
162         if (!!flags + !!path + !!files > 1) {
163                 PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files");
164                 return -1;
165         }
166
167         if (!flags)
168                 flags = SD_JOURNAL_LOCAL_ONLY;
169
170         Py_BEGIN_ALLOW_THREADS
171         if (path)
172                 r = sd_journal_open_directory(&self->j, path, 0);
173         else if (files)
174                 r = sd_journal_open_files(&self->j, (const char**) files, 0);
175         else
176                 r = sd_journal_open(&self->j, flags);
177         Py_END_ALLOW_THREADS
178
179         return set_error(r, path, "Invalid flags or path");
180 }
181
182 PyDoc_STRVAR(Reader_fileno__doc__,
183              "fileno() -> int\n\n"
184              "Get a file descriptor to poll for changes in the journal.\n"
185              "This method invokes sd_journal_get_fd().\n"
186              "See man:sd_journal_get_fd(3).");
187 static PyObject* Reader_fileno(Reader *self, PyObject *args) {
188         int fd;
189
190         fd = sd_journal_get_fd(self->j);
191         set_error(fd, NULL, NULL);
192         if (fd < 0)
193                 return NULL;
194         return long_FromLong(fd);
195 }
196
197 PyDoc_STRVAR(Reader_reliable_fd__doc__,
198              "reliable_fd() -> bool\n\n"
199              "Returns True iff the journal can be polled reliably.\n"
200              "This method invokes sd_journal_reliable_fd().\n"
201              "See man:sd_journal_reliable_fd(3).");
202 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) {
203         int r;
204
205         r = sd_journal_reliable_fd(self->j);
206         if (set_error(r, NULL, NULL) < 0)
207                 return NULL;
208         return PyBool_FromLong(r);
209 }
210
211 PyDoc_STRVAR(Reader_get_events__doc__,
212              "get_events() -> int\n\n"
213              "Returns a mask of poll() events to wait for on the file\n"
214              "descriptor returned by .fileno().\n\n"
215              "See man:sd_journal_get_events(3) for further discussion.");
216 static PyObject* Reader_get_events(Reader *self, PyObject *args) {
217         int r;
218
219         r = sd_journal_get_events(self->j);
220         if (set_error(r, NULL, NULL) < 0)
221                 return NULL;
222         return long_FromLong(r);
223 }
224
225 PyDoc_STRVAR(Reader_get_timeout__doc__,
226              "get_timeout() -> int or None\n\n"
227              "Returns a timeout value for usage in poll(), the time since the\n"
228              "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
229              "is necessary.\n\n"
230              "The return value must be converted to a relative timeout in\n"
231              "milliseconds if it is to be used as an argument for poll().\n"
232              "See man:sd_journal_get_timeout(3) for further discussion.");
233 static PyObject* Reader_get_timeout(Reader *self, PyObject *args) {
234         int r;
235         uint64_t t;
236
237         r = sd_journal_get_timeout(self->j, &t);
238         if (set_error(r, NULL, NULL) < 0)
239                 return NULL;
240
241         if (t == (uint64_t) -1)
242                 Py_RETURN_NONE;
243
244         assert_cc(sizeof(unsigned long long) == sizeof(t));
245         return PyLong_FromUnsignedLongLong(t);
246 }
247
248 PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
249              "get_timeout_ms() -> int\n\n"
250              "Returns a timeout value suitable for usage in poll(), the value\n"
251              "returned by .get_timeout() converted to relative ms, or -1 if\n"
252              "no timeout is necessary.");
253 static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) {
254         int r;
255         uint64_t t;
256
257         r = sd_journal_get_timeout(self->j, &t);
258         if (set_error(r, NULL, NULL) < 0)
259                 return NULL;
260
261         return absolute_timeout(t);
262 }
263
264 PyDoc_STRVAR(Reader_close__doc__,
265              "close() -> None\n\n"
266              "Free resources allocated by this Reader object.\n"
267              "This method invokes sd_journal_close().\n"
268              "See man:sd_journal_close(3).");
269 static PyObject* Reader_close(Reader *self, PyObject *args) {
270         assert(self);
271         assert(!args);
272
273         sd_journal_close(self->j);
274         self->j = NULL;
275         Py_RETURN_NONE;
276 }
277
278 PyDoc_STRVAR(Reader_get_usage__doc__,
279              "get_usage() -> int\n\n"
280              "Returns the total disk space currently used by journal\n"
281              "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
282              "passed when opening the journal this value will only reflect\n"
283              "the size of journal files of the local host, otherwise\n"
284              "of all hosts.\n\n"
285              "This method invokes sd_journal_get_usage().\n"
286              "See man:sd_journal_get_usage(3).");
287 static PyObject* Reader_get_usage(Reader *self, PyObject *args) {
288         int r;
289         uint64_t bytes;
290
291         r = sd_journal_get_usage(self->j, &bytes);
292         if (set_error(r, NULL, NULL) < 0)
293                 return NULL;
294
295         assert_cc(sizeof(unsigned long long) == sizeof(bytes));
296         return PyLong_FromUnsignedLongLong(bytes);
297 }
298
299 PyDoc_STRVAR(Reader___enter____doc__,
300              "__enter__() -> self\n\n"
301              "Part of the context manager protocol.\n"
302              "Returns self.\n");
303 static PyObject* Reader___enter__(PyObject *self, PyObject *args) {
304         assert(self);
305         assert(!args);
306
307         Py_INCREF(self);
308         return self;
309 }
310
311 PyDoc_STRVAR(Reader___exit____doc__,
312              "__exit__(type, value, traceback) -> None\n\n"
313              "Part of the context manager protocol.\n"
314              "Closes the journal.\n");
315 static PyObject* Reader___exit__(Reader *self, PyObject *args) {
316         return Reader_close(self, NULL);
317 }
318
319 PyDoc_STRVAR(Reader_next__doc__,
320              "next([skip]) -> bool\n\n"
321              "Go to the next log entry. Optional skip value means to go to\n"
322              "the `skip`\\-th log entry.\n"
323              "Returns False if at end of file, True otherwise.");
324 static PyObject* Reader_next(Reader *self, PyObject *args) {
325         int64_t skip = 1LL;
326         int r;
327
328         if (!PyArg_ParseTuple(args, "|L:next", &skip))
329                 return NULL;
330
331         if (skip == 0LL) {
332                 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
333                 return NULL;
334         }
335
336         Py_BEGIN_ALLOW_THREADS
337         if (skip == 1LL)
338                 r = sd_journal_next(self->j);
339         else if (skip == -1LL)
340                 r = sd_journal_previous(self->j);
341         else if (skip > 1LL)
342                 r = sd_journal_next_skip(self->j, skip);
343         else if (skip < -1LL)
344                 r = sd_journal_previous_skip(self->j, -skip);
345         else
346                 assert_not_reached("should not be here");
347         Py_END_ALLOW_THREADS
348
349         if (set_error(r, NULL, NULL) < 0)
350                 return NULL;
351         return PyBool_FromLong(r);
352 }
353
354 PyDoc_STRVAR(Reader_previous__doc__,
355              "previous([skip]) -> bool\n\n"
356              "Go to the previous log entry. Optional skip value means to \n"
357              "go to the `skip`\\-th previous log entry.\n"
358              "Returns False if at start of file, True otherwise.");
359 static PyObject* Reader_previous(Reader *self, PyObject *args) {
360         int64_t skip = 1LL;
361         if (!PyArg_ParseTuple(args, "|L:previous", &skip))
362                 return NULL;
363
364         return PyObject_CallMethod((PyObject *)self, (char*) "_next",
365                                    (char*) "L", -skip);
366 }
367
368 static int extract(const char* msg, size_t msg_len,
369                    PyObject **key, PyObject **value) {
370         PyObject *k = NULL, *v;
371         const char *delim_ptr;
372
373         delim_ptr = memchr(msg, '=', msg_len);
374         if (!delim_ptr) {
375                 PyErr_SetString(PyExc_OSError,
376                                 "journal gave us a field without '='");
377                 return -1;
378         }
379
380         if (key) {
381                 k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
382                 if (!k)
383                         return -1;
384         }
385
386         if (value) {
387                 v = PyBytes_FromStringAndSize(delim_ptr + 1,
388                                               (const char*) msg + msg_len - (delim_ptr + 1));
389                 if (!v) {
390                         Py_XDECREF(k);
391                         return -1;
392                 }
393
394                 *value = v;
395         }
396
397         if (key)
398                 *key = k;
399
400         return 0;
401 }
402
403 PyDoc_STRVAR(Reader_get__doc__,
404              "get(str) -> str\n\n"
405              "Return data associated with this key in current log entry.\n"
406              "Throws KeyError is the data is not available.");
407 static PyObject* Reader_get(Reader *self, PyObject *args) {
408         const char* field;
409         const void* msg;
410         size_t msg_len;
411         PyObject *value;
412         int r;
413
414         assert(self);
415         assert(args);
416
417         if (!PyArg_ParseTuple(args, "s:get", &field))
418                 return NULL;
419
420         r = sd_journal_get_data(self->j, field, &msg, &msg_len);
421         if (r == -ENOENT) {
422                 PyErr_SetString(PyExc_KeyError, field);
423                 return NULL;
424         }
425         if (set_error(r, NULL, "field name is not valid") < 0)
426                 return NULL;
427
428         r = extract(msg, msg_len, NULL, &value);
429         if (r < 0)
430                 return NULL;
431         return value;
432 }
433
434 PyDoc_STRVAR(Reader_get_all__doc__,
435              "_get_all() -> dict\n\n"
436              "Return dictionary of the current log entry.");
437 static PyObject* Reader_get_all(Reader *self, PyObject *args) {
438         PyObject *dict;
439         const void *msg;
440         size_t msg_len;
441         int r;
442
443         dict = PyDict_New();
444         if (!dict)
445                 return NULL;
446
447         SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
448                 _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL;
449
450                 r = extract(msg, msg_len, &key, &value);
451                 if (r < 0)
452                         goto error;
453
454                 if (PyDict_Contains(dict, key)) {
455                         PyObject *cur_value = PyDict_GetItem(dict, key);
456
457                         if (PyList_CheckExact(cur_value)) {
458                                 r = PyList_Append(cur_value, value);
459                                 if (r < 0)
460                                         goto error;
461                         } else {
462                                 _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0);
463                                 if (!tmp_list)
464                                         goto error;
465
466                                 r = PyList_Append(tmp_list, cur_value);
467                                 if (r < 0)
468                                         goto error;
469
470                                 r = PyList_Append(tmp_list, value);
471                                 if (r < 0)
472                                         goto error;
473
474                                 r = PyDict_SetItem(dict, key, tmp_list);
475                                 if (r < 0)
476                                         goto error;
477                         }
478                 } else {
479                         r = PyDict_SetItem(dict, key, value);
480                         if (r < 0)
481                                 goto error;
482                 }
483         }
484
485         return dict;
486
487 error:
488         Py_DECREF(dict);
489         return NULL;
490 }
491
492 PyDoc_STRVAR(Reader_get_realtime__doc__,
493              "get_realtime() -> int\n\n"
494              "Return the realtime timestamp for the current journal entry\n"
495              "in microseconds.\n\n"
496              "Wraps sd_journal_get_realtime_usec().\n"
497              "See man:sd_journal_get_realtime_usec(3).");
498 static PyObject* Reader_get_realtime(Reader *self, PyObject *args) {
499         uint64_t timestamp;
500         int r;
501
502         assert(self);
503         assert(!args);
504
505         r = sd_journal_get_realtime_usec(self->j, &timestamp);
506         if (set_error(r, NULL, NULL) < 0)
507                 return NULL;
508
509         assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
510         return PyLong_FromUnsignedLongLong(timestamp);
511 }
512
513 PyDoc_STRVAR(Reader_get_monotonic__doc__,
514              "get_monotonic() -> (timestamp, bootid)\n\n"
515              "Return the monotonic timestamp for the current journal entry\n"
516              "as a tuple of time in microseconds and bootid.\n\n"
517              "Wraps sd_journal_get_monotonic_usec().\n"
518              "See man:sd_journal_get_monotonic_usec(3).");
519 static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) {
520         uint64_t timestamp;
521         sd_id128_t id;
522         PyObject *monotonic, *bootid, *tuple;
523         int r;
524
525         assert(self);
526         assert(!args);
527
528         r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
529         if (set_error(r, NULL, NULL) < 0)
530                 return NULL;
531
532         assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
533         monotonic = PyLong_FromUnsignedLongLong(timestamp);
534         bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
535 #if PY_MAJOR_VERSION >= 3
536         tuple = PyStructSequence_New(&MonotonicType);
537 #else
538         tuple = PyTuple_New(2);
539 #endif
540         if (!monotonic || !bootid || !tuple) {
541                 Py_XDECREF(monotonic);
542                 Py_XDECREF(bootid);
543                 Py_XDECREF(tuple);
544                 return NULL;
545         }
546
547 #if PY_MAJOR_VERSION >= 3
548         PyStructSequence_SET_ITEM(tuple, 0, monotonic);
549         PyStructSequence_SET_ITEM(tuple, 1, bootid);
550 #else
551         PyTuple_SET_ITEM(tuple, 0, monotonic);
552         PyTuple_SET_ITEM(tuple, 1, bootid);
553 #endif
554
555         return tuple;
556 }
557
558 PyDoc_STRVAR(Reader_add_match__doc__,
559              "add_match(match) -> None\n\n"
560              "Add a match to filter journal log entries. All matches of different\n"
561              "fields are combined with logical AND, and matches of the same field\n"
562              "are automatically combined with logical OR.\n"
563              "Match is a string of the form \"FIELD=value\".");
564 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) {
565         char *match;
566         int match_len, r;
567         if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
568                 return NULL;
569
570         r = sd_journal_add_match(self->j, match, match_len);
571         if (set_error(r, NULL, "Invalid match") < 0)
572                 return NULL;
573
574         Py_RETURN_NONE;
575 }
576
577 PyDoc_STRVAR(Reader_add_disjunction__doc__,
578              "add_disjunction() -> None\n\n"
579              "Inserts a logical OR between matches added since previous\n"
580              "add_disjunction() or add_conjunction() and the next\n"
581              "add_disjunction() or add_conjunction().\n\n"
582              "See man:sd_journal_add_disjunction(3) for explanation.");
583 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) {
584         int r;
585         r = sd_journal_add_disjunction(self->j);
586         if (set_error(r, NULL, NULL) < 0)
587                 return NULL;
588         Py_RETURN_NONE;
589 }
590
591 PyDoc_STRVAR(Reader_add_conjunction__doc__,
592              "add_conjunction() -> None\n\n"
593              "Inserts a logical AND between matches added since previous\n"
594              "add_disjunction() or add_conjunction() and the next\n"
595              "add_disjunction() or add_conjunction().\n\n"
596              "See man:sd_journal_add_disjunction(3) for explanation.");
597 static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) {
598         int r;
599         r = sd_journal_add_conjunction(self->j);
600         if (set_error(r, NULL, NULL) < 0)
601                 return NULL;
602         Py_RETURN_NONE;
603 }
604
605 PyDoc_STRVAR(Reader_flush_matches__doc__,
606              "flush_matches() -> None\n\n"
607              "Clear all current match filters.");
608 static PyObject* Reader_flush_matches(Reader *self, PyObject *args) {
609         sd_journal_flush_matches(self->j);
610         Py_RETURN_NONE;
611 }
612
613 PyDoc_STRVAR(Reader_seek_head__doc__,
614              "seek_head() -> None\n\n"
615              "Jump to the beginning of the journal.\n"
616              "This method invokes sd_journal_seek_head().\n"
617              "See man:sd_journal_seek_head(3).");
618 static PyObject* Reader_seek_head(Reader *self, PyObject *args) {
619         int r;
620         Py_BEGIN_ALLOW_THREADS
621         r = sd_journal_seek_head(self->j);
622         Py_END_ALLOW_THREADS
623
624         if (set_error(r, NULL, NULL) < 0)
625                 return NULL;
626
627         Py_RETURN_NONE;
628 }
629
630 PyDoc_STRVAR(Reader_seek_tail__doc__,
631              "seek_tail() -> None\n\n"
632              "Jump to the end of the journal.\n"
633              "This method invokes sd_journal_seek_tail().\n"
634              "See man:sd_journal_seek_tail(3).");
635 static PyObject* Reader_seek_tail(Reader *self, PyObject *args) {
636         int r;
637
638         Py_BEGIN_ALLOW_THREADS
639         r = sd_journal_seek_tail(self->j);
640         Py_END_ALLOW_THREADS
641
642         if (set_error(r, NULL, NULL) < 0)
643                 return NULL;
644         Py_RETURN_NONE;
645 }
646
647 PyDoc_STRVAR(Reader_seek_realtime__doc__,
648              "seek_realtime(realtime) -> None\n\n"
649              "Seek to nearest matching journal entry to `realtime`. Argument\n"
650              "`realtime` in specified in seconds.");
651 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) {
652         uint64_t timestamp;
653         int r;
654
655         if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
656                 return NULL;
657
658         Py_BEGIN_ALLOW_THREADS
659         r = sd_journal_seek_realtime_usec(self->j, timestamp);
660         Py_END_ALLOW_THREADS
661
662         if (set_error(r, NULL, NULL) < 0)
663                 return NULL;
664
665         Py_RETURN_NONE;
666 }
667
668 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
669              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
670              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
671              "`monotonic` is an timestamp from boot in microseconds.\n"
672              "Argument `bootid` is a string representing which boot the\n"
673              "monotonic time is reference to. Defaults to current bootid.");
674 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) {
675         char *bootid = NULL;
676         uint64_t timestamp;
677         sd_id128_t id;
678         int r;
679
680         if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
681                 return NULL;
682
683         if (bootid) {
684                 r = sd_id128_from_string(bootid, &id);
685                 if (set_error(r, NULL, "Invalid bootid") < 0)
686                         return NULL;
687         } else {
688                 Py_BEGIN_ALLOW_THREADS
689                 r = sd_id128_get_boot(&id);
690                 Py_END_ALLOW_THREADS
691
692                 if (set_error(r, NULL, NULL) < 0)
693                         return NULL;
694         }
695
696         Py_BEGIN_ALLOW_THREADS
697         r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
698         Py_END_ALLOW_THREADS
699
700         if (set_error(r, NULL, NULL) < 0)
701                 return NULL;
702
703         Py_RETURN_NONE;
704 }
705
706
707 PyDoc_STRVAR(Reader_process__doc__,
708              "process() -> state change (integer)\n\n"
709              "Process events and reset the readable state of the file\n"
710              "descriptor returned by .fileno().\n\n"
711              "Will return constants: NOP if no change; APPEND if new\n"
712              "entries have been added to the end of the journal; and\n"
713              "INVALIDATE if journal files have been added or removed.\n\n"
714              "See man:sd_journal_process(3) for further discussion.");
715 static PyObject* Reader_process(Reader *self, PyObject *args) {
716         int r;
717
718         assert(!args);
719
720         Py_BEGIN_ALLOW_THREADS
721         r = sd_journal_process(self->j);
722         Py_END_ALLOW_THREADS
723         if (set_error(r, NULL, NULL) < 0)
724                 return NULL;
725
726         return long_FromLong(r);
727 }
728
729 PyDoc_STRVAR(Reader_wait__doc__,
730              "wait([timeout]) -> state change (integer)\n\n"
731              "Wait for a change in the journal. Argument `timeout` specifies\n"
732              "the maximum number of microseconds to wait before returning\n"
733              "regardless of wheter the journal has changed. If `timeout` is -1,\n"
734              "then block forever.\n\n"
735              "Will return constants: NOP if no change; APPEND if new\n"
736              "entries have been added to the end of the journal; and\n"
737              "INVALIDATE if journal files have been added or removed.\n\n"
738              "See man:sd_journal_wait(3) for further discussion.");
739 static PyObject* Reader_wait(Reader *self, PyObject *args) {
740         int r;
741         int64_t timeout;
742
743         if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
744                 return NULL;
745
746         Py_BEGIN_ALLOW_THREADS
747         r = sd_journal_wait(self->j, timeout);
748         Py_END_ALLOW_THREADS
749
750         if (set_error(r, NULL, NULL) < 0)
751                 return NULL;
752
753         return long_FromLong(r);
754 }
755
756 PyDoc_STRVAR(Reader_seek_cursor__doc__,
757              "seek_cursor(cursor) -> None\n\n"
758              "Seek to journal entry by given unique reference `cursor`.");
759 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) {
760         const char *cursor;
761         int r;
762
763         if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
764                 return NULL;
765
766         Py_BEGIN_ALLOW_THREADS
767         r = sd_journal_seek_cursor(self->j, cursor);
768         Py_END_ALLOW_THREADS
769
770         if (set_error(r, NULL, "Invalid cursor") < 0)
771                 return NULL;
772
773         Py_RETURN_NONE;
774 }
775
776 PyDoc_STRVAR(Reader_get_cursor__doc__,
777              "get_cursor() -> str\n\n"
778              "Return a cursor string for the current journal entry.\n\n"
779              "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
780 static PyObject* Reader_get_cursor(Reader *self, PyObject *args) {
781         _cleanup_free_ char *cursor = NULL;
782         int r;
783
784         assert(self);
785         assert(!args);
786
787         r = sd_journal_get_cursor(self->j, &cursor);
788         if (set_error(r, NULL, NULL) < 0)
789                 return NULL;
790
791         return unicode_FromString(cursor);
792 }
793
794 PyDoc_STRVAR(Reader_test_cursor__doc__,
795              "test_cursor(str) -> bool\n\n"
796              "Test whether the cursor string matches current journal entry.\n\n"
797              "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
798 static PyObject* Reader_test_cursor(Reader *self, PyObject *args) {
799         const char *cursor;
800         int r;
801
802         assert(self);
803         assert(args);
804
805         if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
806                 return NULL;
807
808         r = sd_journal_test_cursor(self->j, cursor);
809         if (set_error(r, NULL, NULL) < 0)
810                 return NULL;
811
812         return PyBool_FromLong(r);
813 }
814
815 PyDoc_STRVAR(Reader_query_unique__doc__,
816              "query_unique(field) -> a set of values\n\n"
817              "Return a set of unique values appearing in journal for the\n"
818              "given `field`. Note this does not respect any journal matches.");
819 static PyObject* Reader_query_unique(Reader *self, PyObject *args) {
820         char *query;
821         int r;
822         const void *uniq;
823         size_t uniq_len;
824         PyObject *value_set, *key, *value;
825
826         if (!PyArg_ParseTuple(args, "s:query_unique", &query))
827                 return NULL;
828
829         Py_BEGIN_ALLOW_THREADS
830         r = sd_journal_query_unique(self->j, query);
831         Py_END_ALLOW_THREADS
832
833         if (set_error(r, NULL, "Invalid field name") < 0)
834                 return NULL;
835
836         value_set = PySet_New(0);
837         key = unicode_FromString(query);
838
839         SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
840                 const char *delim_ptr;
841
842                 delim_ptr = memchr(uniq, '=', uniq_len);
843                 value = PyBytes_FromStringAndSize(
844                                 delim_ptr + 1,
845                                 (const char*) uniq + uniq_len - (delim_ptr + 1));
846                 PySet_Add(value_set, value);
847                 Py_DECREF(value);
848         }
849
850         Py_DECREF(key);
851         return value_set;
852 }
853
854 PyDoc_STRVAR(Reader_get_catalog__doc__,
855              "get_catalog() -> str\n\n"
856              "Retrieve a message catalog entry for the current journal entry.\n"
857              "Will throw IndexError if the entry has no MESSAGE_ID\n"
858              "and KeyError is the id is specified, but hasn't been found\n"
859              "in the catalog.\n\n"
860              "Wraps man:sd_journal_get_catalog(3).");
861 static PyObject* Reader_get_catalog(Reader *self, PyObject *args) {
862         int r;
863         _cleanup_free_ char *msg = NULL;
864
865         assert(self);
866         assert(!args);
867
868         Py_BEGIN_ALLOW_THREADS
869         r = sd_journal_get_catalog(self->j, &msg);
870         Py_END_ALLOW_THREADS
871
872         if (r == -ENOENT) {
873                 const void* mid;
874                 size_t mid_len;
875
876                 r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
877                 if (r == 0) {
878                         const size_t l = sizeof("MESSAGE_ID");
879                         assert(mid_len > l);
880                         PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l),
881                                      (const char*) mid + l);
882                 } else if (r == -ENOENT)
883                         PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
884                 else
885                         set_error(r, NULL, NULL);
886                 return NULL;
887         }
888
889         if (set_error(r, NULL, NULL) < 0)
890                 return NULL;
891
892         return unicode_FromString(msg);
893 }
894
895 PyDoc_STRVAR(get_catalog__doc__,
896              "get_catalog(id128) -> str\n\n"
897              "Retrieve a message catalog entry for the given id.\n"
898              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
899 static PyObject* get_catalog(PyObject *self, PyObject *args) {
900         int r;
901         char *id_ = NULL;
902         sd_id128_t id;
903         _cleanup_free_ char *msg = NULL;
904
905         assert(args);
906
907         if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
908                 return NULL;
909
910         r = sd_id128_from_string(id_, &id);
911         if (set_error(r, NULL, "Invalid id128") < 0)
912                 return NULL;
913
914         Py_BEGIN_ALLOW_THREADS
915         r = sd_journal_get_catalog_for_message_id(id, &msg);
916         Py_END_ALLOW_THREADS
917
918         if (set_error(r, NULL, NULL) < 0)
919                 return NULL;
920
921         return unicode_FromString(msg);
922 }
923
924 PyDoc_STRVAR(data_threshold__doc__,
925              "Threshold for field size truncation in bytes.\n\n"
926              "Fields longer than this will be truncated to the threshold size.\n"
927              "Defaults to 64Kb.");
928
929 static PyObject* Reader_get_data_threshold(Reader *self, void *closure) {
930         size_t cvalue;
931         int r;
932
933         r = sd_journal_get_data_threshold(self->j, &cvalue);
934         if (set_error(r, NULL, NULL) < 0)
935                 return NULL;
936
937         return long_FromSize_t(cvalue);
938 }
939
940 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) {
941         int r;
942
943         if (value == NULL) {
944                 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
945                 return -1;
946         }
947
948         if (!long_Check(value)){
949                 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
950                 return -1;
951         }
952
953         r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
954         return set_error(r, NULL, NULL);
955 }
956
957 PyDoc_STRVAR(closed__doc__,
958              "True iff journal is closed");
959 static PyObject* Reader_get_closed(Reader *self, void *closure) {
960         return PyBool_FromLong(self->j == NULL);
961 }
962
963 static PyGetSetDef Reader_getsetters[] = {
964         { (char*) "data_threshold",
965           (getter) Reader_get_data_threshold,
966           (setter) Reader_set_data_threshold,
967           (char*) data_threshold__doc__,
968           NULL },
969         { (char*) "closed",
970           (getter) Reader_get_closed,
971           NULL,
972           (char*) closed__doc__,
973           NULL },
974         {} /* Sentinel */
975 };
976
977 static PyMethodDef Reader_methods[] = {
978         {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
979         {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
980         {"get_events",      (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
981         {"get_timeout",     (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
982         {"get_timeout_ms",  (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
983         {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
984         {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
985         {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
986         {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
987         {"_next",           (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
988         {"_previous",       (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
989         {"_get",            (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
990         {"_get_all",        (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
991         {"_get_realtime",   (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
992         {"_get_monotonic",  (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
993         {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
994         {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
995         {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__},
996         {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
997         {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
998         {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
999         {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
1000         {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
1001         {"process",         (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
1002         {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
1003         {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
1004         {"_get_cursor",     (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
1005         {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
1006         {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
1007         {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
1008         {}  /* Sentinel */
1009 };
1010
1011 static PyTypeObject ReaderType = {
1012         PyVarObject_HEAD_INIT(NULL, 0)
1013         .tp_name = "_reader._Reader",
1014         .tp_basicsize = sizeof(Reader),
1015         .tp_dealloc = (destructor) Reader_dealloc,
1016         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1017         .tp_doc = Reader__doc__,
1018         .tp_methods = Reader_methods,
1019         .tp_getset = Reader_getsetters,
1020         .tp_init = (initproc) Reader_init,
1021         .tp_new = PyType_GenericNew,
1022 };
1023
1024 static PyMethodDef methods[] = {
1025         { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
1026         {} /* Sentinel */
1027 };
1028
1029 #if PY_MAJOR_VERSION >= 3
1030 static PyModuleDef module = {
1031         PyModuleDef_HEAD_INIT,
1032         "_reader",
1033         module__doc__,
1034         -1,
1035         methods,
1036 };
1037 #endif
1038
1039 #if PY_MAJOR_VERSION >= 3
1040 static bool initialized = false;
1041 #endif
1042
1043 DISABLE_WARNING_MISSING_PROTOTYPES;
1044
1045 PyMODINIT_FUNC
1046 #if PY_MAJOR_VERSION >= 3
1047 PyInit__reader(void)
1048 #else
1049 init_reader(void)
1050 #endif
1051 {
1052         PyObject* m;
1053
1054         PyDateTime_IMPORT;
1055
1056         if (PyType_Ready(&ReaderType) < 0)
1057 #if PY_MAJOR_VERSION >= 3
1058                 return NULL;
1059 #else
1060                 return;
1061 #endif
1062
1063 #if PY_MAJOR_VERSION >= 3
1064         m = PyModule_Create(&module);
1065         if (m == NULL)
1066                 return NULL;
1067
1068         if (!initialized) {
1069                 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1070                 initialized = true;
1071         }
1072 #else
1073         m = Py_InitModule3("_reader", methods, module__doc__);
1074         if (m == NULL)
1075                 return;
1076 #endif
1077
1078         Py_INCREF(&ReaderType);
1079 #if PY_MAJOR_VERSION >= 3
1080         Py_INCREF(&MonotonicType);
1081 #endif
1082         if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1083 #if PY_MAJOR_VERSION >= 3
1084             PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1085 #endif
1086             PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1087             PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1088             PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1089             PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1090             PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1091             PyModule_AddIntConstant(m, "SYSTEM", SD_JOURNAL_SYSTEM) ||
1092             PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) ||
1093             PyModule_AddIntConstant(m, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) ||
1094             PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
1095 #if PY_MAJOR_VERSION >= 3
1096                 Py_DECREF(m);
1097                 return NULL;
1098 #endif
1099         }
1100
1101 #if PY_MAJOR_VERSION >= 3
1102         return m;
1103 #endif
1104 }
1105
1106 REENABLE_WARNING;