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