chiark / gitweb /
journal/tests: free allocated memory in test
[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__doc__,
364              "seek(offset[, whence]) -> None\n\n"
365              "Jump `offset` entries in the journal. Argument\n"
366              "`whence` defines what the offset is relative to:\n"
367              "os.SEEK_SET (default) from first match in journal;\n"
368              "os.SEEK_CUR from current position in journal;\n"
369              "and os.SEEK_END is from last match in journal.");
370 static PyObject* Reader_seek(Reader *self, PyObject *args, PyObject *keywds)
371 {
372     int64_t offset;
373     int whence = SEEK_SET;
374     PyObject *result = NULL;
375
376     static const char* const kwlist[] = {"offset", "whence", NULL};
377     if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist,
378                                      &offset, &whence))
379         return NULL;
380
381     switch(whence) {
382     case SEEK_SET: {
383         int r;
384         Py_BEGIN_ALLOW_THREADS
385         r = sd_journal_seek_head(self->j);
386         Py_END_ALLOW_THREADS
387         if (set_error(r, NULL, NULL))
388             return NULL;
389
390         if (offset > 0LL)
391             result = PyObject_CallMethod((PyObject *)self, (char*) "get_next",
392                                          (char*) "L", offset);
393         break;
394     }
395     case SEEK_CUR:
396         result = PyObject_CallMethod((PyObject *)self, (char*) "get_next",
397                                      (char*) "L", offset);
398         break;
399     case SEEK_END: {
400         int r;
401         Py_BEGIN_ALLOW_THREADS
402         r = sd_journal_seek_tail(self->j);
403         Py_END_ALLOW_THREADS
404         if (set_error(r, NULL, NULL))
405             return NULL;
406
407         result = PyObject_CallMethod((PyObject *)self, (char*) "get_next",
408                                      (char*) "L", offset < 0LL ? offset : -1LL);
409         break;
410     }
411     default:
412         PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
413     }
414
415     Py_XDECREF(result);
416     if (PyErr_Occurred())
417         return NULL;
418     Py_RETURN_NONE;
419 }
420
421 PyDoc_STRVAR(Reader_seek_realtime__doc__,
422              "seek_realtime(realtime) -> None\n\n"
423              "Seek to nearest matching journal entry to `realtime`. Argument\n"
424              "`realtime` can must be an integer unix timestamp.");
425 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
426 {
427     double timedouble;
428     uint64_t timestamp;
429     int r;
430
431     if (!PyArg_ParseTuple(args, "d", &timedouble))
432         return NULL;
433
434     timestamp = (uint64_t) (timedouble * 1.0E6);
435     if ((int64_t) timestamp < 0LL) {
436         PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
437         return NULL;
438     }
439
440     Py_BEGIN_ALLOW_THREADS
441     r = sd_journal_seek_realtime_usec(self->j, timestamp);
442     Py_END_ALLOW_THREADS
443     if (set_error(r, NULL, NULL))
444         return NULL;
445     Py_RETURN_NONE;
446 }
447
448 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
449              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
450              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
451              "`monotonic` is an timestamp from boot in seconds.\n"
452              "Argument `bootid` is a string representing which boot the\n"
453              "monotonic time is reference to. Defaults to current bootid.");
454 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
455 {
456     double timedouble;
457     char *bootid = NULL;
458     uint64_t timestamp;
459     sd_id128_t id;
460     int r;
461
462     if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
463         return NULL;
464
465     timestamp = (uint64_t) (timedouble * 1.0E6);
466
467     if ((int64_t) timestamp < 0LL) {
468         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
469         return NULL;
470     }
471
472     if (bootid) {
473         r = sd_id128_from_string(bootid, &id);
474         if (set_error(r, NULL, "Invalid bootid"))
475             return NULL;
476     } else {
477         Py_BEGIN_ALLOW_THREADS
478         r = sd_id128_get_boot(&id);
479         Py_END_ALLOW_THREADS
480         if (set_error(r, NULL, NULL))
481             return NULL;
482     }
483
484     Py_BEGIN_ALLOW_THREADS
485     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
486     Py_END_ALLOW_THREADS
487     if (set_error(r, NULL, NULL))
488         return NULL;
489     Py_RETURN_NONE;
490 }
491
492 PyDoc_STRVAR(Reader_wait__doc__,
493              "wait([timeout]) -> state change (integer)\n\n"
494              "Wait for a change in the journal. Argument `timeout` specifies\n"
495              "the maximum number of seconds to wait before returning\n"
496              "regardless of wheter the journal has changed. If `timeout` is not given\n"
497              "or is 0, then block forever.\n"
498              "Will return constants: NOP if no change; APPEND if new\n"
499              "entries have been added to the end of the journal; and\n"
500              "INVALIDATE if journal files have been added or removed.");
501 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
502 {
503     int r;
504     int64_t timeout = 0LL;
505
506     if (!PyArg_ParseTuple(args, "|L", &timeout))
507         return NULL;
508
509     Py_BEGIN_ALLOW_THREADS
510     r = sd_journal_wait(self->j, timeout ==0 ? (uint64_t) -1 : timeout * 1E6);
511     Py_END_ALLOW_THREADS
512     if (set_error(r, NULL, NULL))
513         return NULL;
514
515     return long_FromLong(r);
516 }
517
518 PyDoc_STRVAR(Reader_seek_cursor__doc__,
519              "seek_cursor(cursor) -> None\n\n"
520              "Seek to journal entry by given unique reference `cursor`.");
521 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
522 {
523     const char *cursor;
524     int r;
525
526     if (!PyArg_ParseTuple(args, "s", &cursor))
527         return NULL;
528
529     Py_BEGIN_ALLOW_THREADS
530     r = sd_journal_seek_cursor(self->j, cursor);
531     Py_END_ALLOW_THREADS
532     if (set_error(r, NULL, "Invalid cursor"))
533         return NULL;
534     Py_RETURN_NONE;
535 }
536
537 static PyObject* Reader_iter(PyObject *self)
538 {
539     Py_INCREF(self);
540     return self;
541 }
542
543 static PyObject* Reader_iternext(PyObject *self)
544 {
545     PyObject *dict;
546     Py_ssize_t dict_size;
547
548     dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
549     if (PyErr_Occurred())
550         return NULL;
551     dict_size = PyDict_Size(dict);
552     if ((int64_t) dict_size > 0LL) {
553         return dict;
554     } else {
555         Py_DECREF(dict);
556         PyErr_SetNone(PyExc_StopIteration);
557         return NULL;
558     }
559 }
560
561 PyDoc_STRVAR(Reader_query_unique__doc__,
562              "query_unique(field) -> a set of values\n\n"
563              "Return a set of unique values appearing in journal for the\n"
564              "given `field`. Note this does not respect any journal matches.");
565 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
566 {
567     char *query;
568     int r;
569     const void *uniq;
570     size_t uniq_len;
571     PyObject *value_set, *key, *value;
572
573     if (!PyArg_ParseTuple(args, "s", &query))
574         return NULL;
575
576     Py_BEGIN_ALLOW_THREADS
577     r = sd_journal_query_unique(self->j, query);
578     Py_END_ALLOW_THREADS
579     if (set_error(r, NULL, "Invalid field name"))
580         return NULL;
581
582     value_set = PySet_New(0);
583     key = unicode_FromString(query);
584
585     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
586         const char *delim_ptr;
587
588         delim_ptr = memchr(uniq, '=', uniq_len);
589         value = PyBytes_FromStringAndSize(
590             delim_ptr + 1,
591             (const char*) uniq + uniq_len - (delim_ptr + 1));
592         PySet_Add(value_set, value);
593         Py_DECREF(value);
594     }
595     Py_DECREF(key);
596     return value_set;
597 }
598
599 PyDoc_STRVAR(data_threshold__doc__,
600              "Threshold for field size truncation in bytes.\n\n"
601              "Fields longer than this will be truncated to the threshold size.\n"
602              "Defaults to 64Kb.");
603
604 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
605 {
606     size_t cvalue;
607     int r;
608
609     r = sd_journal_get_data_threshold(self->j, &cvalue);
610     if (set_error(r, NULL, NULL))
611         return NULL;
612
613     return long_FromSize_t(cvalue);
614 }
615
616 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
617 {
618     int r;
619     if (value == NULL) {
620         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
621         return -1;
622     }
623     if (!long_Check(value)){
624         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
625         return -1;
626     }
627     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
628     return set_error(r, NULL, NULL);
629 }
630
631 static PyGetSetDef Reader_getseters[] = {
632     {(char*) "data_threshold",
633      (getter) Reader_get_data_threshold,
634      (setter) Reader_set_data_threshold,
635      (char*) data_threshold__doc__,
636      NULL},
637     {NULL}
638 };
639
640 static PyMethodDef Reader_methods[] = {
641     {"get_next",        (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
642     {"get_previous",    (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
643     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
644     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
645     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
646     {"seek",            (PyCFunction) Reader_seek, METH_VARARGS | METH_KEYWORDS,  Reader_seek__doc__},
647     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
648     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
649     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
650     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
651     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
652     {NULL}  /* Sentinel */
653 };
654
655 static PyTypeObject ReaderType = {
656     PyVarObject_HEAD_INIT(NULL, 0)
657     "_reader._Reader",                        /*tp_name*/
658     sizeof(Reader),                           /*tp_basicsize*/
659     0,                                        /*tp_itemsize*/
660     (destructor)Reader_dealloc,               /*tp_dealloc*/
661     0,                                        /*tp_print*/
662     0,                                        /*tp_getattr*/
663     0,                                        /*tp_setattr*/
664     0,                                        /*tp_compare*/
665     0,                                        /*tp_repr*/
666     0,                                        /*tp_as_number*/
667     0,                                        /*tp_as_sequence*/
668     0,                                        /*tp_as_mapping*/
669     0,                                        /*tp_hash */
670     0,                                        /*tp_call*/
671     0,                                        /*tp_str*/
672     0,                                        /*tp_getattro*/
673     0,                                        /*tp_setattro*/
674     0,                                        /*tp_as_buffer*/
675     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
676     Reader__doc__,                            /* tp_doc */
677     0,                                        /* tp_traverse */
678     0,                                        /* tp_clear */
679     0,                                        /* tp_richcompare */
680     0,                                        /* tp_weaklistoffset */
681     Reader_iter,                              /* tp_iter */
682     Reader_iternext,                          /* tp_iternext */
683     Reader_methods,                           /* tp_methods */
684     0,                                        /* tp_members */
685     Reader_getseters,                         /* tp_getset */
686     0,                                        /* tp_base */
687     0,                                        /* tp_dict */
688     0,                                        /* tp_descr_get */
689     0,                                        /* tp_descr_set */
690     0,                                        /* tp_dictoffset */
691     (initproc) Reader_init,                   /* tp_init */
692     0,                                        /* tp_alloc */
693     PyType_GenericNew,                        /* tp_new */
694 };
695
696 #define SUMMARY \
697     "Module that reads the systemd journal similar to journalctl."
698
699 #if PY_MAJOR_VERSION >= 3
700 static PyModuleDef _reader_module = {
701     PyModuleDef_HEAD_INIT,
702     "_reader",
703     SUMMARY,
704     -1,
705     NULL, NULL, NULL, NULL, NULL
706 };
707 #endif
708
709 #if PY_MAJOR_VERSION >= 3
710 static bool initialized = false;
711 #endif
712
713 #pragma GCC diagnostic push
714 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
715
716 PyMODINIT_FUNC
717 #if PY_MAJOR_VERSION >= 3
718 PyInit__reader(void)
719 #else
720 init_reader(void)
721 #endif
722 {
723     PyObject* m;
724
725     PyDateTime_IMPORT;
726
727     if (PyType_Ready(&ReaderType) < 0)
728 #if PY_MAJOR_VERSION >= 3
729         return NULL;
730 #else
731         return;
732 #endif
733
734 #if PY_MAJOR_VERSION >= 3
735     m = PyModule_Create(&_reader_module);
736     if (m == NULL)
737         return NULL;
738
739     if (!initialized) {
740         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
741         initialized = true;
742     }
743 #else
744     m = Py_InitModule3("_reader", NULL, SUMMARY);
745     if (m == NULL)
746         return;
747 #endif
748
749     Py_INCREF(&ReaderType);
750 #if PY_MAJOR_VERSION >= 3
751     Py_INCREF(&MonotonicType);
752 #endif
753     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
754 #if PY_MAJOR_VERSION >= 3
755         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
756 #endif
757         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
758         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
759         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
760         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
761         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
762         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
763 #if PY_MAJOR_VERSION >= 3
764         Py_DECREF(m);
765         return NULL;
766 #endif
767     }
768
769 #if PY_MAJOR_VERSION >= 3
770     return m;
771 #endif
772 }
773
774 #pragma GCC diagnostic pop