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