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