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