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