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