chiark / gitweb /
160ab69a3caa5c13f80f9e2bb10de0eeb1dbb8e3
[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 <stdio.h>
26
27 #include <systemd/sd-journal.h>
28
29 #include "pyutil.h"
30 #include "macro.h"
31 #include "util.h"
32
33 #if PY_MAJOR_VERSION >=3
34 # define unicode_FromStringAndSize PyUnicode_FromStringAndSize
35 # define unicode_FromString PyUnicode_FromString
36 # define long_FromLong PyLong_FromLong
37 # define long_FromSize_t PyLong_FromSize_t
38 # define long_Check PyLong_Check
39 # define long_AsLong PyLong_AsLong
40 #else
41 /* Python 3 type naming convention is used */
42 # define unicode_FromStringAndSize PyString_FromStringAndSize
43 # define unicode_FromString PyString_FromString
44 # define long_FromLong PyInt_FromLong
45 # define long_FromSize_t PyInt_FromSize_t
46 # define long_Check PyInt_Check
47 # define long_AsLong PyInt_AsLong
48 #endif
49
50 typedef struct {
51     PyObject_HEAD
52     sd_journal *j;
53 } Reader;
54 static PyTypeObject ReaderType;
55
56 static int set_error(int r, const char* path, const char* invalid_message) {
57     if (r >= 0)
58         return r;
59     if (r == -EINVAL && invalid_message)
60         PyErr_SetString(PyExc_ValueError, invalid_message);
61     else if (r == -ENOMEM)
62         PyErr_SetString(PyExc_MemoryError, "Not enough memory");
63     else {
64         errno = -r;
65         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
66     }
67     return -1;
68 }
69
70 #if PY_MAJOR_VERSION >= 3
71 static PyTypeObject MonotonicType;
72
73 PyDoc_STRVAR(MonotonicType__doc__,
74              "A tuple of (timestamp, bootid) for holding monotonic timestamps");
75
76 static PyStructSequence_Field MonotonicType_fields[] = {
77     {(char*) "timestamp", (char*) "Time"},
78     {(char*) "bootid", (char*) "Unique identifier of the boot"},
79     {NULL, NULL}
80 };
81
82 static PyStructSequence_Desc Monotonic_desc = {
83     (char*) "journal.Monotonic",
84     MonotonicType__doc__,
85     MonotonicType_fields,
86     2,
87 };
88 #endif
89
90 static void Reader_dealloc(Reader* self)
91 {
92     sd_journal_close(self->j);
93     Py_TYPE(self)->tp_free((PyObject*)self);
94 }
95
96 PyDoc_STRVAR(Reader__doc__,
97              "Reader([flags | path]) -> ...\n\n"
98              "Reader allows filtering and retrieval of Journal entries.\n"
99              "Note: this is a low-level interface, and probably not what you\n"
100              "want, use systemd.journal.Reader instead.\n\n"
101              "Argument `flags` sets open flags of the journal, which can be one\n"
102              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
103              "journal on local machine only; RUNTIME_ONLY opens only\n"
104              "volatile journal files; and SYSTEM_ONLY opens only\n"
105              "journal files of system services and the kernel.\n\n"
106              "Argument `path` is the directory of journal files. Note that\n"
107              "`flags` and `path` are exclusive.\n");
108 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
109 {
110     int flags = 0, r;
111     char *path = NULL;
112
113     static const char* const kwlist[] = {"flags", "path", NULL};
114     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
115                                      &flags, &path))
116         return -1;
117
118     if (!flags)
119         flags = SD_JOURNAL_LOCAL_ONLY;
120     else
121         if (path) {
122             PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
123             return -1;
124         }
125
126     Py_BEGIN_ALLOW_THREADS
127     if (path)
128         r = sd_journal_open_directory(&self->j, path, 0);
129     else
130         r = sd_journal_open(&self->j, flags);
131     Py_END_ALLOW_THREADS
132
133     return set_error(r, path, "Invalid flags or path");
134 }
135
136 PyDoc_STRVAR(Reader_fileno__doc__,
137              "fileno() -> int\n\n"
138              "Get a file descriptor to poll for changes in the journal.\n"
139              "This method invokes sd_journal_get_fd().\n"
140              "See man:sd_journal_get_fd(3).");
141 static PyObject* Reader_fileno(Reader *self, PyObject *args)
142 {
143     int r;
144     r = sd_journal_get_fd(self->j);
145     set_error(r, NULL, NULL);
146     if (r < 0)
147         return NULL;
148     return long_FromLong(r);
149 }
150
151 PyDoc_STRVAR(Reader_reliable_fd__doc__,
152              "reliable_fd() -> bool\n\n"
153              "Returns True iff the journal can be polled reliably.\n"
154              "This method invokes sd_journal_reliable_fd().\n"
155              "See man:sd_journal_reliable_fd(3).");
156 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
157 {
158     int r;
159     r = sd_journal_reliable_fd(self->j);
160     set_error(r, NULL, NULL);
161     if (r < 0)
162         return NULL;
163     return PyBool_FromLong(r);
164 }
165
166 PyDoc_STRVAR(Reader_close__doc__,
167              "close() -> None\n\n"
168              "Free resources allocated by this Reader object.\n"
169              "This method invokes sd_journal_close().\n"
170              "See man:sd_journal_close(3).");
171 static PyObject* Reader_close(Reader *self, PyObject *args)
172 {
173     sd_journal_close(self->j);
174     self->j = NULL;
175     Py_RETURN_NONE;
176 }
177
178 PyDoc_STRVAR(Reader_get_next__doc__,
179              "get_next([skip]) -> dict\n\n"
180              "Return dictionary of the next log entry. Optional skip value will\n"
181              "return the `skip`\\-th log entry.");
182 static PyObject* Reader_get_next(Reader *self, PyObject *args)
183 {
184     PyObject *dict;
185     const void *msg;
186     size_t msg_len;
187     int64_t skip = 1LL;
188     int r;
189
190     if (!PyArg_ParseTuple(args, "|L", &skip))
191         return NULL;
192
193     if (skip == 0LL) {
194         PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
195         return NULL;
196     }
197
198     Py_BEGIN_ALLOW_THREADS
199     if (skip == 1LL)
200         r = sd_journal_next(self->j);
201     else if (skip == -1LL)
202         r = sd_journal_previous(self->j);
203     else if (skip > 1LL)
204         r = sd_journal_next_skip(self->j, skip);
205     else if (skip < -1LL)
206         r = sd_journal_previous_skip(self->j, -skip);
207     else
208         assert_not_reached("should not be here");
209     Py_END_ALLOW_THREADS
210
211     set_error(r, NULL, NULL);
212     if (r < 0)
213         return NULL;
214     else if (r == 0) /* EOF */
215         return PyDict_New();
216
217     dict = PyDict_New();
218     if (!dict)
219             return NULL;
220
221     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
222         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
223         const char *delim_ptr;
224
225         delim_ptr = memchr(msg, '=', msg_len);
226         if (!delim_ptr) {
227             PyErr_SetString(PyExc_OSError,
228                             "journal gave us a field without '='");
229             goto error;
230         }
231
232         key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
233         if (!key)
234             goto error;
235
236         value = PyBytes_FromStringAndSize(
237                 delim_ptr + 1,
238                 (const char*) msg + msg_len - (delim_ptr + 1) );
239         if (!value)
240             goto error;
241
242         if (PyDict_Contains(dict, key)) {
243             PyObject *cur_value = PyDict_GetItem(dict, key);
244
245             if (PyList_CheckExact(cur_value)) {
246                 r = PyList_Append(cur_value, value);
247                 if (r < 0)
248                     goto error;
249             } else {
250                 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
251                 if (!tmp_list)
252                     goto error;
253
254                 r = PyList_Append(tmp_list, cur_value);
255                 if (r < 0)
256                     goto error;
257
258                 r = PyList_Append(tmp_list, value);
259                 if (r < 0)
260                     goto error;
261
262                 r = PyDict_SetItem(dict, key, tmp_list);
263                 if (r < 0)
264                     goto error;
265             }
266         } else {
267             r = PyDict_SetItem(dict, key, value);
268             if (r < 0)
269                 goto error;
270         }
271     }
272
273     {
274         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
275         uint64_t realtime;
276
277         r = sd_journal_get_realtime_usec(self->j, &realtime);
278         if (set_error(r, NULL, NULL))
279             goto error;
280
281         key = unicode_FromString("__REALTIME_TIMESTAMP");
282         if (!key)
283             goto error;
284
285         assert_cc(sizeof(unsigned long long) == sizeof(realtime));
286         value = PyLong_FromUnsignedLongLong(realtime);
287         if (!value)
288             goto error;
289
290         if (PyDict_SetItem(dict, key, value))
291             goto error;
292     }
293
294     {
295         PyObject _cleanup_Py_DECREF_
296             *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
297         sd_id128_t id;
298         uint64_t monotonic;
299
300         r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
301         if (set_error(r, NULL, NULL))
302             goto error;
303
304         assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
305         key = unicode_FromString("__MONOTONIC_TIMESTAMP");
306         timestamp = PyLong_FromUnsignedLongLong(monotonic);
307         bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
308 #if PY_MAJOR_VERSION >= 3
309         value = PyStructSequence_New(&MonotonicType);
310 #else
311         value = PyTuple_New(2);
312 #endif
313         if (!key || !timestamp || !bytes || !value)
314             goto error;
315
316         Py_INCREF(timestamp);
317         Py_INCREF(bytes);
318
319 #if PY_MAJOR_VERSION >= 3
320         PyStructSequence_SET_ITEM(value, 0, timestamp);
321         PyStructSequence_SET_ITEM(value, 1, bytes);
322 #else
323         PyTuple_SET_ITEM(value, 0, timestamp);
324         PyTuple_SET_ITEM(value, 1, bytes);
325 #endif
326
327         if (PyDict_SetItem(dict, key, value))
328             goto error;
329     }
330
331     {
332         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
333         char _cleanup_free_ *cursor = NULL;
334
335         r = sd_journal_get_cursor(self->j, &cursor);
336         if (set_error(r, NULL, NULL))
337             goto error;
338
339         key = unicode_FromString("__CURSOR");
340         if (!key)
341             goto error;
342
343         value = PyBytes_FromString(cursor);
344         if (!value)
345             goto error;
346
347         if (PyDict_SetItem(dict, key, value))
348             goto error;
349     }
350
351     return dict;
352 error:
353     Py_DECREF(dict);
354     return NULL;
355 }
356
357 PyDoc_STRVAR(Reader_get_previous__doc__,
358              "get_previous([skip]) -> dict\n\n"
359              "Return dictionary of the previous log entry. Optional skip value\n"
360              "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
361 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
362 {
363     int64_t skip = 1LL;
364     if (!PyArg_ParseTuple(args, "|L", &skip))
365         return NULL;
366
367     return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
368                                (char*) "L", -skip);
369 }
370
371 PyDoc_STRVAR(Reader_add_match__doc__,
372              "add_match(match) -> None\n\n"
373              "Add a match to filter journal log entries. All matches of different\n"
374              "fields are combined with logical AND, and matches of the same field\n"
375              "are automatically combined with logical OR.\n"
376              "Match is a string of the form \"FIELD=value\".");
377 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
378 {
379     char *match;
380     int match_len, r;
381     if (!PyArg_ParseTuple(args, "s#", &match, &match_len))
382         return NULL;
383
384     r = sd_journal_add_match(self->j, match, match_len);
385     set_error(r, NULL, "Invalid match");
386     if (r < 0)
387             return NULL;
388
389     Py_RETURN_NONE;
390 }
391
392 PyDoc_STRVAR(Reader_add_disjunction__doc__,
393              "add_disjunction() -> None\n\n"
394              "Inserts a logical OR between matches added before and afterwards.");
395 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
396 {
397     int r;
398     r = sd_journal_add_disjunction(self->j);
399     set_error(r, NULL, NULL);
400     if (r < 0)
401         return NULL;
402     Py_RETURN_NONE;
403 }
404
405 PyDoc_STRVAR(Reader_flush_matches__doc__,
406              "flush_matches() -> None\n\n"
407              "Clear all current match filters.");
408 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
409 {
410     sd_journal_flush_matches(self->j);
411     Py_RETURN_NONE;
412 }
413
414 PyDoc_STRVAR(Reader_seek_head__doc__,
415              "seek_head() -> None\n\n"
416              "Jump to the beginning of the journal.\n"
417              "This method invokes sd_journal_seek_head().\n"
418              "See man:sd_journal_seek_head(3).");
419 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
420 {
421     int r;
422     Py_BEGIN_ALLOW_THREADS
423     r = sd_journal_seek_head(self->j);
424     Py_END_ALLOW_THREADS
425     if (set_error(r, NULL, NULL))
426         return NULL;
427     Py_RETURN_NONE;
428 }
429
430 PyDoc_STRVAR(Reader_seek_tail__doc__,
431              "seek_tail() -> None\n\n"
432              "Jump to the end of the journal.\n"
433              "This method invokes sd_journal_seek_tail().\n"
434              "See man:sd_journal_seek_tail(3).");
435 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
436 {
437     int r;
438     Py_BEGIN_ALLOW_THREADS
439     r = sd_journal_seek_tail(self->j);
440     Py_END_ALLOW_THREADS
441     if (set_error(r, NULL, NULL))
442         return NULL;
443     Py_RETURN_NONE;
444 }
445
446 PyDoc_STRVAR(Reader_seek_realtime__doc__,
447              "seek_realtime(realtime) -> None\n\n"
448              "Seek to nearest matching journal entry to `realtime`. Argument\n"
449              "`realtime` can must be an integer unix timestamp.");
450 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
451 {
452     double timedouble;
453     uint64_t timestamp;
454     int r;
455
456     if (!PyArg_ParseTuple(args, "d", &timedouble))
457         return NULL;
458
459     timestamp = (uint64_t) (timedouble * 1.0E6);
460     if ((int64_t) timestamp < 0LL) {
461         PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
462         return NULL;
463     }
464
465     Py_BEGIN_ALLOW_THREADS
466     r = sd_journal_seek_realtime_usec(self->j, timestamp);
467     Py_END_ALLOW_THREADS
468     if (set_error(r, NULL, NULL))
469         return NULL;
470     Py_RETURN_NONE;
471 }
472
473 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
474              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
475              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
476              "`monotonic` is an timestamp from boot in seconds.\n"
477              "Argument `bootid` is a string representing which boot the\n"
478              "monotonic time is reference to. Defaults to current bootid.");
479 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
480 {
481     double timedouble;
482     char *bootid = NULL;
483     uint64_t timestamp;
484     sd_id128_t id;
485     int r;
486
487     if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
488         return NULL;
489
490     timestamp = (uint64_t) (timedouble * 1.0E6);
491
492     if ((int64_t) timestamp < 0LL) {
493         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
494         return NULL;
495     }
496
497     if (bootid) {
498         r = sd_id128_from_string(bootid, &id);
499         if (set_error(r, NULL, "Invalid bootid"))
500             return NULL;
501     } else {
502         Py_BEGIN_ALLOW_THREADS
503         r = sd_id128_get_boot(&id);
504         Py_END_ALLOW_THREADS
505         if (set_error(r, NULL, NULL))
506             return NULL;
507     }
508
509     Py_BEGIN_ALLOW_THREADS
510     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
511     Py_END_ALLOW_THREADS
512     if (set_error(r, NULL, NULL))
513         return NULL;
514     Py_RETURN_NONE;
515 }
516
517 PyDoc_STRVAR(Reader_wait__doc__,
518              "wait([timeout]) -> state change (integer)\n\n"
519              "Wait for a change in the journal. Argument `timeout` specifies\n"
520              "the maximum number of seconds to wait before returning\n"
521              "regardless of wheter the journal has changed. If `timeout` is not given\n"
522              "or is 0, then block forever.\n"
523              "Will return constants: NOP if no change; APPEND if new\n"
524              "entries have been added to the end of the journal; and\n"
525              "INVALIDATE if journal files have been added or removed.");
526 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
527 {
528     int r;
529     int64_t timeout = 0LL;
530
531     if (!PyArg_ParseTuple(args, "|L", &timeout))
532         return NULL;
533
534     Py_BEGIN_ALLOW_THREADS
535     r = sd_journal_wait(self->j,
536                         timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
537     Py_END_ALLOW_THREADS
538     if (set_error(r, NULL, NULL) < 0)
539         return NULL;
540
541     return long_FromLong(r);
542 }
543
544 PyDoc_STRVAR(Reader_seek_cursor__doc__,
545              "seek_cursor(cursor) -> None\n\n"
546              "Seek to journal entry by given unique reference `cursor`.");
547 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
548 {
549     const char *cursor;
550     int r;
551
552     if (!PyArg_ParseTuple(args, "s", &cursor))
553         return NULL;
554
555     Py_BEGIN_ALLOW_THREADS
556     r = sd_journal_seek_cursor(self->j, cursor);
557     Py_END_ALLOW_THREADS
558     if (set_error(r, NULL, "Invalid cursor"))
559         return NULL;
560     Py_RETURN_NONE;
561 }
562
563 static PyObject* Reader_iter(PyObject *self)
564 {
565     Py_INCREF(self);
566     return self;
567 }
568
569 static PyObject* Reader_iternext(PyObject *self)
570 {
571     PyObject *dict;
572     Py_ssize_t dict_size;
573
574     dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
575     if (PyErr_Occurred())
576         return NULL;
577     dict_size = PyDict_Size(dict);
578     if ((int64_t) dict_size > 0LL) {
579         return dict;
580     } else {
581         Py_DECREF(dict);
582         PyErr_SetNone(PyExc_StopIteration);
583         return NULL;
584     }
585 }
586
587 PyDoc_STRVAR(Reader_query_unique__doc__,
588              "query_unique(field) -> a set of values\n\n"
589              "Return a set of unique values appearing in journal for the\n"
590              "given `field`. Note this does not respect any journal matches.");
591 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
592 {
593     char *query;
594     int r;
595     const void *uniq;
596     size_t uniq_len;
597     PyObject *value_set, *key, *value;
598
599     if (!PyArg_ParseTuple(args, "s", &query))
600         return NULL;
601
602     Py_BEGIN_ALLOW_THREADS
603     r = sd_journal_query_unique(self->j, query);
604     Py_END_ALLOW_THREADS
605     if (set_error(r, NULL, "Invalid field name"))
606         return NULL;
607
608     value_set = PySet_New(0);
609     key = unicode_FromString(query);
610
611     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
612         const char *delim_ptr;
613
614         delim_ptr = memchr(uniq, '=', uniq_len);
615         value = PyBytes_FromStringAndSize(
616             delim_ptr + 1,
617             (const char*) uniq + uniq_len - (delim_ptr + 1));
618         PySet_Add(value_set, value);
619         Py_DECREF(value);
620     }
621     Py_DECREF(key);
622     return value_set;
623 }
624
625 PyDoc_STRVAR(data_threshold__doc__,
626              "Threshold for field size truncation in bytes.\n\n"
627              "Fields longer than this will be truncated to the threshold size.\n"
628              "Defaults to 64Kb.");
629
630 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
631 {
632     size_t cvalue;
633     int r;
634
635     r = sd_journal_get_data_threshold(self->j, &cvalue);
636     if (set_error(r, NULL, NULL))
637         return NULL;
638
639     return long_FromSize_t(cvalue);
640 }
641
642 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
643 {
644     int r;
645     if (value == NULL) {
646         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
647         return -1;
648     }
649     if (!long_Check(value)){
650         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
651         return -1;
652     }
653     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
654     return set_error(r, NULL, NULL);
655 }
656
657 static PyGetSetDef Reader_getseters[] = {
658     {(char*) "data_threshold",
659      (getter) Reader_get_data_threshold,
660      (setter) Reader_set_data_threshold,
661      (char*) data_threshold__doc__,
662      NULL},
663     {NULL}
664 };
665
666 static PyMethodDef Reader_methods[] = {
667     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
668     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
669     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
670     {"get_next",        (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
671     {"get_previous",    (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
672     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
673     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
674     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
675     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
676     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
677     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
678     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
679     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
680     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
681     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
682     {NULL}  /* Sentinel */
683 };
684
685 static PyTypeObject ReaderType = {
686     PyVarObject_HEAD_INIT(NULL, 0)
687     "_reader._Reader",                        /*tp_name*/
688     sizeof(Reader),                           /*tp_basicsize*/
689     0,                                        /*tp_itemsize*/
690     (destructor)Reader_dealloc,               /*tp_dealloc*/
691     0,                                        /*tp_print*/
692     0,                                        /*tp_getattr*/
693     0,                                        /*tp_setattr*/
694     0,                                        /*tp_compare*/
695     0,                                        /*tp_repr*/
696     0,                                        /*tp_as_number*/
697     0,                                        /*tp_as_sequence*/
698     0,                                        /*tp_as_mapping*/
699     0,                                        /*tp_hash */
700     0,                                        /*tp_call*/
701     0,                                        /*tp_str*/
702     0,                                        /*tp_getattro*/
703     0,                                        /*tp_setattro*/
704     0,                                        /*tp_as_buffer*/
705     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
706     Reader__doc__,                            /* tp_doc */
707     0,                                        /* tp_traverse */
708     0,                                        /* tp_clear */
709     0,                                        /* tp_richcompare */
710     0,                                        /* tp_weaklistoffset */
711     Reader_iter,                              /* tp_iter */
712     Reader_iternext,                          /* tp_iternext */
713     Reader_methods,                           /* tp_methods */
714     0,                                        /* tp_members */
715     Reader_getseters,                         /* tp_getset */
716     0,                                        /* tp_base */
717     0,                                        /* tp_dict */
718     0,                                        /* tp_descr_get */
719     0,                                        /* tp_descr_set */
720     0,                                        /* tp_dictoffset */
721     (initproc) Reader_init,                   /* tp_init */
722     0,                                        /* tp_alloc */
723     PyType_GenericNew,                        /* tp_new */
724 };
725
726 #define SUMMARY \
727     "Module that reads the systemd journal similar to journalctl."
728
729 #if PY_MAJOR_VERSION >= 3
730 static PyModuleDef _reader_module = {
731     PyModuleDef_HEAD_INIT,
732     "_reader",
733     SUMMARY,
734     -1,
735     NULL, NULL, NULL, NULL, NULL
736 };
737 #endif
738
739 #if PY_MAJOR_VERSION >= 3
740 static bool initialized = false;
741 #endif
742
743 #pragma GCC diagnostic push
744 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
745
746 PyMODINIT_FUNC
747 #if PY_MAJOR_VERSION >= 3
748 PyInit__reader(void)
749 #else
750 init_reader(void)
751 #endif
752 {
753     PyObject* m;
754
755     PyDateTime_IMPORT;
756
757     if (PyType_Ready(&ReaderType) < 0)
758 #if PY_MAJOR_VERSION >= 3
759         return NULL;
760 #else
761         return;
762 #endif
763
764 #if PY_MAJOR_VERSION >= 3
765     m = PyModule_Create(&_reader_module);
766     if (m == NULL)
767         return NULL;
768
769     if (!initialized) {
770         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
771         initialized = true;
772     }
773 #else
774     m = Py_InitModule3("_reader", NULL, SUMMARY);
775     if (m == NULL)
776         return;
777 #endif
778
779     Py_INCREF(&ReaderType);
780 #if PY_MAJOR_VERSION >= 3
781     Py_INCREF(&MonotonicType);
782 #endif
783     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
784 #if PY_MAJOR_VERSION >= 3
785         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
786 #endif
787         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
788         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
789         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
790         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
791         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
792         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
793 #if PY_MAJOR_VERSION >= 3
794         Py_DECREF(m);
795         return NULL;
796 #endif
797     }
798
799 #if PY_MAJOR_VERSION >= 3
800     return m;
801 #endif
802 }
803
804 #pragma GCC diagnostic pop