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