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