chiark / gitweb /
7aa638b0766edce9c92859a530a389fbd4277b33
[elogind.git] / src / python-systemd / _reader.c
1 /*-*- Mode: C; c-basic-offset: 8; 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 #include <systemd/sd-journal.h>
22
23 #include <Python.h>
24 #include <structmember.h>
25 #include <datetime.h>
26
27 typedef struct {
28     PyObject_HEAD
29     sd_journal *j;
30 } Journal;
31 static PyTypeObject JournalType;
32
33 static void
34 Journal_dealloc(Journal* self)
35 {
36     sd_journal_close(self->j);
37     Py_TYPE(self)->tp_free((PyObject*)self);
38 }
39
40 PyDoc_STRVAR(Journal__doc__,
41 "Journal([flags][,path]) -> ...\n"
42 "Journal instance\n\n"
43 "Returns instance of Journal, which allows filtering and return\n"
44 "of journal entries.\n"
45 "Argument `flags` sets open flags of the journal, which can be one\n"
46 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
47 "journal on local machine only; RUNTIME_ONLY opens only\n"
48 "volatile journal files; and SYSTEM_ONLY opens only\n"
49 "journal files of system services and the kernel.\n"
50 "Argument `path` is the directory of journal files. Note that\n"
51 "currently flags are ignored when `path` is present as they are\n"
52 " not relevant.");
53 static int
54 Journal_init(Journal *self, PyObject *args, PyObject *keywds)
55 {
56     int flags=SD_JOURNAL_LOCAL_ONLY;
57     char *path=NULL;
58
59     static char *kwlist[] = {"flags", "path", NULL};
60     if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist,
61                                       &flags, &path))
62         return 1;
63
64     int r;
65     Py_BEGIN_ALLOW_THREADS
66     if (path) {
67         r = sd_journal_open_directory(&self->j, path, 0);
68     }else{
69         r = sd_journal_open(&self->j, flags);
70     }
71     Py_END_ALLOW_THREADS
72     if (r < 0) {
73         errno = -r;
74         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
75                             r == -ENOMEM ? PyExc_MemoryError :
76                             PyExc_OSError;
77         PyErr_SetFromErrnoWithFilename(errtype, path);
78         return -1;
79     }
80
81     return 0;
82 }
83
84 PyDoc_STRVAR(Journal_get_next__doc__,
85 "get_next([skip]) -> dict\n\n"
86 "Return dictionary of the next log entry. Optional skip value will\n"
87 "return the `skip`th log entry.");
88 static PyObject *
89 Journal_get_next(Journal *self, PyObject *args)
90 {
91     int64_t skip=1LL;
92     if (! PyArg_ParseTuple(args, "|L", &skip))
93         return NULL;
94
95     if (skip == 0LL) {
96         PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer");
97         return NULL;
98     }
99
100     int r = -EINVAL;
101     Py_BEGIN_ALLOW_THREADS
102     if (skip == 1LL) {
103         r = sd_journal_next(self->j);
104     }else if (skip == -1LL) {
105         r = sd_journal_previous(self->j);
106     }else if (skip > 1LL) {
107         r = sd_journal_next_skip(self->j, skip);
108     }else if (skip < -1LL) {
109         r = sd_journal_previous_skip(self->j, -skip);
110     }
111     Py_END_ALLOW_THREADS
112
113     if (r < 0) {
114         errno = -r;
115         PyErr_SetFromErrno(PyExc_OSError);
116         return NULL;
117     }else if ( r == 0) { //EOF
118         return PyDict_New();
119     }
120
121     PyObject *dict;
122     dict = PyDict_New();
123
124     const void *msg;
125     size_t msg_len;
126     const char *delim_ptr;
127     PyObject *key, *value, *cur_value, *tmp_list;
128
129     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
130         delim_ptr = memchr(msg, '=', msg_len);
131 #if PY_MAJOR_VERSION >=3
132         key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
133 #else
134         key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg);
135 #endif
136         value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) );
137         if (PyDict_Contains(dict, key)) {
138             cur_value = PyDict_GetItem(dict, key);
139             if (PyList_CheckExact(cur_value)) {
140                 PyList_Append(cur_value, value);
141             }else{
142                 tmp_list = PyList_New(0);
143                 PyList_Append(tmp_list, cur_value);
144                 PyList_Append(tmp_list, value);
145                 PyDict_SetItem(dict, key, tmp_list);
146                 Py_DECREF(tmp_list);
147             }
148         }else{
149             PyDict_SetItem(dict, key, value);
150         }
151         Py_DECREF(key);
152         Py_DECREF(value);
153     }
154
155     uint64_t realtime;
156     if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) {
157         char realtime_str[20];
158         sprintf(realtime_str, "%llu", (long long unsigned) realtime);
159
160 #if PY_MAJOR_VERSION >=3
161         key = PyUnicode_FromString("__REALTIME_TIMESTAMP");
162 #else
163         key = PyString_FromString("__REALTIME_TIMESTAMP");
164 #endif
165         value = PyBytes_FromString(realtime_str);
166         PyDict_SetItem(dict, key, value);
167         Py_DECREF(key);
168         Py_DECREF(value);
169     }
170
171     sd_id128_t sd_id;
172     uint64_t monotonic;
173     if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) {
174         char monotonic_str[20];
175         sprintf(monotonic_str, "%llu", (long long unsigned) monotonic);
176 #if PY_MAJOR_VERSION >=3
177         key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP");
178 #else
179         key = PyString_FromString("__MONOTONIC_TIMESTAMP");
180 #endif
181         value = PyBytes_FromString(monotonic_str);
182
183         PyDict_SetItem(dict, key, value);
184         Py_DECREF(key);
185         Py_DECREF(value);
186     }
187
188     char *cursor;
189     if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0...
190 #if PY_MAJOR_VERSION >=3
191         key = PyUnicode_FromString("__CURSOR");
192 #else
193         key = PyString_FromString("__CURSOR");
194 #endif
195         value = PyBytes_FromString(cursor);
196         PyDict_SetItem(dict, key, value);
197         free(cursor);
198         Py_DECREF(key);
199         Py_DECREF(value);
200     }
201
202     return dict;
203 }
204
205 PyDoc_STRVAR(Journal_get_previous__doc__,
206 "get_previous([skip]) -> dict\n\n"
207 "Return dictionary of the previous log entry. Optional skip value\n"
208 "will return the -`skip`th log entry. Equivalent to get_next(-skip).");
209 static PyObject *
210 Journal_get_previous(Journal *self, PyObject *args)
211 {
212     int64_t skip=1LL;
213     if (! PyArg_ParseTuple(args, "|L", &skip))
214         return NULL;
215
216     return PyObject_CallMethod((PyObject *)self, "get_next", "L", -skip);
217 }
218
219 PyDoc_STRVAR(Journal_add_match__doc__,
220 "add_match(match) -> None\n\n"
221 "Add a match to filter journal log entries. All matches of different\n"
222 "fields are combined in logical AND, and matches of the same field\n"
223 "are automatically combined in logical OR.\n"
224 "Match is string of form \"field=value\".");
225 static PyObject *
226 Journal_add_match(Journal *self, PyObject *args, PyObject *keywds)
227 {
228     char *match;
229     int match_len;
230     if (! PyArg_ParseTuple(args, "s#", &match, &match_len))
231         return NULL;
232
233     int r;
234     r = sd_journal_add_match(self->j, match, match_len);
235     if (r < 0) {
236         errno = -r;
237         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
238                             r == -ENOMEM ? PyExc_MemoryError :
239                             PyExc_OSError;
240         PyErr_SetFromErrno(errtype);
241         return NULL;
242     }
243
244     Py_RETURN_NONE;
245 }
246
247 PyDoc_STRVAR(Journal_add_disjunction__doc__,
248 "add_disjunction() -> None\n\n"
249 "Once called, all matches before and after are combined in logical\n"
250 "OR.");
251 static PyObject *
252 Journal_add_disjunction(Journal *self, PyObject *args)
253 {
254     int r;
255     r = sd_journal_add_disjunction(self->j);
256     if (r < 0) {
257         errno = -r;
258         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
259                             PyExc_OSError;
260         PyErr_SetFromErrno(errtype);
261         return NULL;
262     }
263     Py_RETURN_NONE;
264 }
265
266 PyDoc_STRVAR(Journal_flush_matches__doc__,
267 "flush_matches() -> None\n\n"
268 "Clears all current match filters.");
269 static PyObject *
270 Journal_flush_matches(Journal *self, PyObject *args)
271 {
272     sd_journal_flush_matches(self->j);
273     Py_RETURN_NONE;
274 }
275
276 PyDoc_STRVAR(Journal_seek__doc__,
277 "seek(offset[, whence]) -> None\n\n"
278 "Seek through journal by `offset` number of entries. Argument\n"
279 "`whence` defines what the offset is relative to:\n"
280 "os.SEEK_SET (default) from first match in journal;\n"
281 "os.SEEK_CUR from current position in journal;\n"
282 "and os.SEEK_END is from last match in journal.");
283 static PyObject *
284 Journal_seek(Journal *self, PyObject *args, PyObject *keywds)
285 {
286     int64_t offset;
287     int whence=SEEK_SET;
288     static char *kwlist[] = {"offset", "whence", NULL};
289
290     if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist,
291                                       &offset, &whence))
292         return NULL;
293
294     PyObject *result=NULL;
295     if (whence == SEEK_SET){
296         int r;
297         Py_BEGIN_ALLOW_THREADS
298         r = sd_journal_seek_head(self->j);
299         Py_END_ALLOW_THREADS
300         if (r < 0) {
301             errno = -r;
302             PyErr_SetFromErrno(PyExc_OSError);
303             return NULL;
304         }
305         if (offset > 0LL) {
306             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
307         }
308     }else if (whence == SEEK_CUR){
309         result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
310     }else if (whence == SEEK_END){
311         int r;
312         Py_BEGIN_ALLOW_THREADS
313         r = sd_journal_seek_tail(self->j);
314         Py_END_ALLOW_THREADS
315         if (r < 0) {
316             errno = -r;
317             PyErr_SetFromErrno(PyExc_OSError);
318             return NULL;
319         }
320         if (offset < 0LL) {
321             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
322         }else{
323             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL);
324         }
325     }else{
326         PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
327     }
328
329     if (result)
330         Py_DECREF(result);
331     if (PyErr_Occurred())
332         return NULL;
333     Py_RETURN_NONE;
334 }
335
336 PyDoc_STRVAR(Journal_seek_realtime__doc__,
337 "seek_realtime(realtime) -> None\n\n"
338 "Seek to nearest matching journal entry to `realtime`. Argument\n"
339 "`realtime` can must be an integer unix timestamp in usecs.");
340 static PyObject *
341 Journal_seek_realtime(Journal *self, PyObject *args)
342 {
343     uint64_t timestamp;
344     if (! PyArg_ParseTuple(args, "K", &timestamp))
345         return NULL;
346
347     if ((int64_t) timestamp < 0LL) {
348         PyErr_SetString(PyExc_ValueError, "Time must be positive integer");
349         return NULL;
350     }
351
352     int r;
353     Py_BEGIN_ALLOW_THREADS
354     r = sd_journal_seek_realtime_usec(self->j, timestamp);
355     Py_END_ALLOW_THREADS
356     if (r < 0) {
357         errno = -r;
358         PyErr_SetFromErrno(PyExc_OSError);
359         return NULL;
360     }
361     Py_RETURN_NONE;
362 }
363
364 PyDoc_STRVAR(Journal_seek_monotonic__doc__,
365 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
366 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
367 "`monotonic` is an timestamp from boot in seconds.\n"
368 "Argument `bootid` is a string representing which boot the\n"
369 "monotonic time is reference to. Defaults to current bootid.");
370 static PyObject *
371 Journal_seek_monotonic(Journal *self, PyObject *args)
372 {
373     double timedouble;
374     char *bootid=NULL;
375     if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
376         return NULL;
377
378     uint64_t timestamp;
379     timestamp = (uint64_t) (timedouble * 1.0E6);
380
381     if ((int64_t) timestamp < 0LL) {
382         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
383         return NULL;
384     }
385
386     sd_id128_t sd_id;
387     int r;
388     if (bootid) {
389         r = sd_id128_from_string(bootid, &sd_id);
390         if (r == -EINVAL) {
391             PyErr_SetString(PyExc_ValueError, "Invalid bootid");
392             return NULL;
393         }else if (r < 0) {
394             errno = -r;
395             PyErr_SetFromErrno(PyExc_OSError);
396             return NULL;
397         }
398     }else{
399         r = sd_id128_get_boot(&sd_id);
400         if (r == -EIO) {
401             PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
402             return NULL;
403         }else if (r < 0) {
404             errno = -r;
405             PyErr_SetFromErrno(PyExc_OSError);
406             return NULL;
407         }
408     }
409
410     Py_BEGIN_ALLOW_THREADS
411     r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp);
412     Py_END_ALLOW_THREADS
413     if (r < 0) {
414         errno = -r;
415         PyErr_SetFromErrno(PyExc_OSError);
416         return NULL;
417     }
418     Py_RETURN_NONE;
419 }
420  
421 PyDoc_STRVAR(Journal_wait__doc__,
422 "wait([timeout]) -> Change state (integer)\n\n"
423 "Waits until there is a change in the journal. Argument `timeout`\n"
424 "is the maximum number of seconds to wait before returning\n"
425 "regardless if journal has changed. If `timeout` is not given or is\n"
426 "0, then it will block forever.\n"
427 "Will return constants: NOP if no change; APPEND if new\n"
428 "entries have been added to the end of the journal; and\n"
429 "INVALIDATE if journal files have been added or removed.");
430 static PyObject *
431 Journal_wait(Journal *self, PyObject *args, PyObject *keywds)
432 {
433     int64_t timeout=0LL;
434     if (! PyArg_ParseTuple(args, "|L", &timeout))
435         return NULL;
436
437     int r;
438     Py_BEGIN_ALLOW_THREADS
439     if ( timeout == 0LL) {
440         r = sd_journal_wait(self->j, (uint64_t) -1);
441     }else{
442         r = sd_journal_wait(self->j, timeout * 1E6);
443     }
444     Py_END_ALLOW_THREADS
445     if (r < 0) {
446         errno = -r;
447         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
448                             PyExc_OSError;
449         PyErr_SetFromErrno(errtype);
450         return NULL;
451     }
452 #if PY_MAJOR_VERSION >=3
453     return PyLong_FromLong(r);
454 #else
455     return PyInt_FromLong(r);
456 #endif
457 }
458
459 PyDoc_STRVAR(Journal_seek_cursor__doc__,
460 "seek_cursor(cursor) -> None\n\n"
461 "Seeks to journal entry by given unique reference `cursor`.");
462 static PyObject *
463 Journal_seek_cursor(Journal *self, PyObject *args)
464 {
465     const char *cursor;
466     if (! PyArg_ParseTuple(args, "s", &cursor))
467         return NULL;
468
469     int r;
470     Py_BEGIN_ALLOW_THREADS
471     r = sd_journal_seek_cursor(self->j, cursor);
472     Py_END_ALLOW_THREADS
473     if (r < 0) {
474         errno = -r;
475         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
476                             r == -ENOMEM ? PyExc_MemoryError :
477                             PyExc_OSError;
478         PyErr_SetFromErrno(errtype);
479         return NULL;
480     }
481     Py_RETURN_NONE;
482 }
483
484 static PyObject *
485 Journal_iter(PyObject *self)
486 {
487     Py_INCREF(self);
488     return self;
489 }
490
491 static PyObject *
492 Journal_iternext(PyObject *self)
493 {
494     PyObject *dict;
495     Py_ssize_t dict_size;
496
497     dict = PyObject_CallMethod(self, "get_next", "");
498     dict_size = PyDict_Size(dict);
499     if ((int64_t) dict_size > 0LL) {
500         return dict;
501     }else{
502         Py_DECREF(dict);
503         PyErr_SetNone(PyExc_StopIteration);
504         return NULL;
505     }
506 }
507
508 #ifdef SD_JOURNAL_FOREACH_UNIQUE
509 PyDoc_STRVAR(Journal_query_unique__doc__,
510 "query_unique(field) -> a set of values\n\n"
511 "Returns a set of unique values in journal for given `field`.\n"
512 "Note this does not respect any journal matches.");
513 static PyObject *
514 Journal_query_unique(Journal *self, PyObject *args)
515 {
516     char *query;
517     if (! PyArg_ParseTuple(args, "s", &query))
518         return NULL;
519
520     int r;
521     Py_BEGIN_ALLOW_THREADS
522     r = sd_journal_query_unique(self->j, query);
523     Py_END_ALLOW_THREADS
524     if (r < 0) {
525         errno = -r;
526         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
527                             r == -ENOMEM ? PyExc_MemoryError :
528                             PyExc_OSError;
529         PyErr_SetFromErrno(errtype);
530         return NULL;
531     }
532
533     const void *uniq;
534     size_t uniq_len;
535     const char *delim_ptr;
536     PyObject *value_set, *key, *value;
537     value_set = PySet_New(0);
538
539 #if PY_MAJOR_VERSION >=3
540     key = PyUnicode_FromString(query);
541 #else
542     key = PyString_FromString(query);
543 #endif
544
545     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
546         delim_ptr = memchr(uniq, '=', uniq_len);
547         value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1));
548         PySet_Add(value_set, value);
549         Py_DECREF(value);
550     }
551     Py_DECREF(key);
552     return value_set;
553 }
554 #endif //def SD_JOURNAL_FOREACH_UNIQUE
555
556 static PyObject *
557 Journal_get_data_threshold(Journal *self, void *closure)
558 {
559     size_t cvalue;
560     PyObject *value;
561     int r;
562
563     r = sd_journal_get_data_threshold(self->j, &cvalue);
564     if (r < 0) {
565         errno = -r;
566         PyErr_SetFromErrno(PyExc_OSError);
567         return NULL;
568     }
569
570 #if PY_MAJOR_VERSION >=3
571     value = PyLong_FromSize_t(cvalue);
572 #else
573     value = PyInt_FromSize_t(cvalue);
574 #endif
575     return value;
576 }
577
578 static int
579 Journal_set_data_threshold(Journal *self, PyObject *value, void *closure)
580 {
581     if (value == NULL) {
582         PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold");
583         return -1;
584     }
585 #if PY_MAJOR_VERSION >=3
586     if (! PyLong_Check(value)){
587 #else
588     if (! PyInt_Check(value)){
589 #endif
590         PyErr_SetString(PyExc_TypeError, "Data threshold must be int");
591         return -1;
592     }
593     int r;
594 #if PY_MAJOR_VERSION >=3
595     r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value));
596 #else
597     r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value));
598 #endif
599     if (r < 0) {
600         errno = -r;
601         PyErr_SetFromErrno(PyExc_OSError);
602         return -1;
603     }
604     return 0;
605 }
606
607 static PyGetSetDef Journal_getseters[] = {
608     {"data_threshold",
609     (getter)Journal_get_data_threshold,
610     (setter)Journal_set_data_threshold,
611     "data threshold",
612     NULL},
613     {NULL}
614 };
615
616 static PyMethodDef Journal_methods[] = {
617     {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS,
618     Journal_get_next__doc__},
619     {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS,
620     Journal_get_previous__doc__},
621     {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS,
622     Journal_add_match__doc__},
623     {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS,
624     Journal_add_disjunction__doc__},
625     {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS,
626     Journal_flush_matches__doc__},
627     {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS,
628     Journal_seek__doc__},
629     {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS,
630     Journal_seek_realtime__doc__},
631     {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS,
632     Journal_seek_monotonic__doc__},
633     {"wait", (PyCFunction)Journal_wait, METH_VARARGS,
634     Journal_wait__doc__},
635     {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS,
636     Journal_seek_cursor__doc__},
637 #ifdef SD_JOURNAL_FOREACH_UNIQUE
638     {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS,
639     Journal_query_unique__doc__},
640 #endif
641     {NULL}  /* Sentinel */
642 };
643
644 static PyTypeObject JournalType = {
645     PyVarObject_HEAD_INIT(NULL, 0)
646     "_reader.Journal",           /*tp_name*/
647     sizeof(Journal),                  /*tp_basicsize*/
648     0,                                /*tp_itemsize*/
649     (destructor)Journal_dealloc,      /*tp_dealloc*/
650     0,                                /*tp_print*/
651     0,                                /*tp_getattr*/
652     0,                                /*tp_setattr*/
653     0,                                /*tp_compare*/
654     0,                                /*tp_repr*/
655     0,                                /*tp_as_number*/
656     0,                                /*tp_as_sequence*/
657     0,                                /*tp_as_mapping*/
658     0,                                /*tp_hash */
659     0,                                /*tp_call*/
660     0,                                /*tp_str*/
661     0,                                /*tp_getattro*/
662     0,                                /*tp_setattro*/
663     0,                                /*tp_as_buffer*/
664     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
665     Journal__doc__,                   /* tp_doc */
666     0,                                /* tp_traverse */
667     0,                                /* tp_clear */
668     0,                                /* tp_richcompare */
669     0,                                /* tp_weaklistoffset */
670     Journal_iter,                     /* tp_iter */
671     Journal_iternext,                 /* tp_iternext */
672     Journal_methods,                  /* tp_methods */
673     0,                                /* tp_members */
674     Journal_getseters,                /* tp_getset */
675     0,                                /* tp_base */
676     0,                                /* tp_dict */
677     0,                                /* tp_descr_get */
678     0,                                /* tp_descr_set */
679     0,                                /* tp_dictoffset */
680     (initproc)Journal_init,           /* tp_init */
681     0,                                /* tp_alloc */
682     PyType_GenericNew,                /* tp_new */
683 };
684
685 #if PY_MAJOR_VERSION >= 3
686 static PyModuleDef _reader_module = {
687     PyModuleDef_HEAD_INIT,
688     "_reader",
689     "Module that reads systemd journal similar to journalctl.",
690     -1,
691     NULL, NULL, NULL, NULL, NULL
692 };
693 #endif
694
695 PyMODINIT_FUNC
696 #if PY_MAJOR_VERSION >= 3
697 PyInit__reader(void)
698 #else
699 init_reader(void) 
700 #endif
701 {
702     PyObject* m;
703
704     PyDateTime_IMPORT;
705
706     if (PyType_Ready(&JournalType) < 0)
707 #if PY_MAJOR_VERSION >= 3
708         return NULL;
709 #else
710         return;
711 #endif
712
713 #if PY_MAJOR_VERSION >= 3
714     m = PyModule_Create(&_reader_module);
715     if (m == NULL)
716         return NULL;
717 #else
718     m = Py_InitModule3("_reader", NULL,
719                    "Module that reads systemd journal similar to journalctl.");
720     if (m == NULL)
721         return;
722 #endif
723
724     Py_INCREF(&JournalType);
725     PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType);
726     PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP);
727     PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND);
728     PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE);
729     PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY);
730     PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY);
731     PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY);
732
733 #if PY_MAJOR_VERSION >= 3
734     return m;
735 #endif
736 }