chiark / gitweb /
8f21678c81cc4c3fa19b8b22fff5ba966a35079a
[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, ..., field=value, ...) -> None\n\n"
221 "Add a match to filter journal log entries. All matches of different\n"
222 "field are combined in logical AND, and matches of the same field\n"
223 "are automatically combined in logical OR.\n"
224 "Matches can be passed as strings \"field=value\", or keyword\n"
225 "arguments field=\"value\".");
226 static PyObject *
227 Journal_add_match(Journal *self, PyObject *args, PyObject *keywds)
228 {
229     char *match;
230     int match_len;
231     if (! PyArg_ParseTuple(args, "s#", &match, &match_len))
232         return NULL;
233
234     int r;
235     r = sd_journal_add_match(self->j, match, match_len);
236     if (r < 0) {
237         errno = -r;
238         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
239                             r == -ENOMEM ? PyExc_MemoryError :
240                             PyExc_OSError;
241         PyErr_SetFromErrno(errtype);
242         return NULL;
243     }
244
245     Py_RETURN_NONE;
246 }
247
248 PyDoc_STRVAR(Journal_add_disjunction__doc__,
249 "add_disjunction() -> None\n\n"
250 "Once called, all matches before and after are combined in logical\n"
251 "OR.");
252 static PyObject *
253 Journal_add_disjunction(Journal *self, PyObject *args)
254 {
255     int r;
256     r = sd_journal_add_disjunction(self->j);
257     if (r < 0) {
258         errno = -r;
259         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
260                             PyExc_OSError;
261         PyErr_SetFromErrno(errtype);
262         return NULL;
263     }
264     Py_RETURN_NONE;
265 }
266
267 PyDoc_STRVAR(Journal_flush_matches__doc__,
268 "flush_matches() -> None\n\n"
269 "Clears all current match filters.");
270 static PyObject *
271 Journal_flush_matches(Journal *self, PyObject *args)
272 {
273     sd_journal_flush_matches(self->j);
274     Py_RETURN_NONE;
275 }
276
277 PyDoc_STRVAR(Journal_seek__doc__,
278 "seek(offset[, whence]) -> None\n\n"
279 "Seek through journal by `offset` number of entries. Argument\n"
280 "`whence` defines what the offset is relative to:\n"
281 "os.SEEK_SET (default) from first match in journal;\n"
282 "os.SEEK_CUR from current position in journal;\n"
283 "and os.SEEK_END is from last match in journal.");
284 static PyObject *
285 Journal_seek(Journal *self, PyObject *args, PyObject *keywds)
286 {
287     int64_t offset;
288     int whence=SEEK_SET;
289     static char *kwlist[] = {"offset", "whence", NULL};
290
291     if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist,
292                                       &offset, &whence))
293         return NULL;
294
295     PyObject *result=NULL;
296     if (whence == SEEK_SET){
297         int r;
298         Py_BEGIN_ALLOW_THREADS
299         r = sd_journal_seek_head(self->j);
300         Py_END_ALLOW_THREADS
301         if (r < 0) {
302             errno = -r;
303             PyErr_SetFromErrno(PyExc_OSError);
304             return NULL;
305         }
306         if (offset > 0LL) {
307             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
308         }
309     }else if (whence == SEEK_CUR){
310         result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
311     }else if (whence == SEEK_END){
312         int r;
313         Py_BEGIN_ALLOW_THREADS
314         r = sd_journal_seek_tail(self->j);
315         Py_END_ALLOW_THREADS
316         if (r < 0) {
317             errno = -r;
318             PyErr_SetFromErrno(PyExc_OSError);
319             return NULL;
320         }
321         if (offset < 0LL) {
322             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
323         }else{
324             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL);
325         }
326     }else{
327         PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
328     }
329
330     if (result)
331         Py_DECREF(result);
332     if (PyErr_Occurred())
333         return NULL;
334     Py_RETURN_NONE;
335 }
336
337 PyDoc_STRVAR(Journal_seek_realtime__doc__,
338 "seek_realtime(realtime) -> None\n\n"
339 "Seek to nearest matching journal entry to `realtime`. Argument\n"
340 "`realtime` can be an integer unix timestamp in usecs or a "
341 "datetime instance.");
342 static PyObject *
343 Journal_seek_realtime(Journal *self, PyObject *args)
344 {
345     uint64_t timestamp;
346     if (! PyArg_ParseTuple(args, "K", &timestamp))
347         return NULL;
348
349     if ((int64_t) timestamp < 0LL) {
350         PyErr_SetString(PyExc_ValueError, "Time must be positive integer");
351         return NULL;
352     }
353
354     int r;
355     Py_BEGIN_ALLOW_THREADS
356     r = sd_journal_seek_realtime_usec(self->j, timestamp);
357     Py_END_ALLOW_THREADS
358     if (r < 0) {
359         errno = -r;
360         PyErr_SetFromErrno(PyExc_OSError);
361         return NULL;
362     }
363     Py_RETURN_NONE;
364 }
365
366 PyDoc_STRVAR(Journal_seek_monotonic__doc__,
367 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
368 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
369 "`monotonic` is an timestamp from boot in secs, or a\n"
370 "timedelta instance.\n"
371 "Argument `bootid` is a string representing which boot the\n"
372 "monotonic time is reference to. Defaults to current bootid.");
373 static PyObject *
374 Journal_seek_monotonic(Journal *self, PyObject *args)
375 {
376     double timedouble;
377     char *bootid=NULL;
378     if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
379         return NULL;
380
381     uint64_t timestamp;
382     timestamp = (uint64_t) (timedouble * 1.0E6);
383
384     if ((int64_t) timestamp < 0LL) {
385         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
386         return NULL;
387     }
388
389     sd_id128_t sd_id;
390     int r;
391     if (bootid) {
392         r = sd_id128_from_string(bootid, &sd_id);
393         if (r == -EINVAL) {
394             PyErr_SetString(PyExc_ValueError, "Invalid bootid");
395             return NULL;
396         }else if (r < 0) {
397             errno = -r;
398             PyErr_SetFromErrno(PyExc_OSError);
399             return NULL;
400         }
401     }else{
402         r = sd_id128_get_boot(&sd_id);
403         if (r == -EIO) {
404             PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
405             return NULL;
406         }else if (r < 0) {
407             errno = -r;
408             PyErr_SetFromErrno(PyExc_OSError);
409             return NULL;
410         }
411     }
412
413     Py_BEGIN_ALLOW_THREADS
414     r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp);
415     Py_END_ALLOW_THREADS
416     if (r < 0) {
417         errno = -r;
418         PyErr_SetFromErrno(PyExc_OSError);
419         return NULL;
420     }
421     Py_RETURN_NONE;
422 }
423  
424 PyDoc_STRVAR(Journal_wait__doc__,
425 "wait([timeout]) -> Change state (integer)\n\n"
426 "Waits until there is a change in the journal. Argument `timeout`\n"
427 "is the maximum number of seconds to wait before returning\n"
428 "regardless if journal has changed. If `timeout` is not given or is\n"
429 "0, then it will block forever.\n"
430 "Will return constants: NOP if no change; APPEND if new\n"
431 "entries have been added to the end of the journal; and\n"
432 "INVALIDATE if journal files have been added or removed.");
433 static PyObject *
434 Journal_wait(Journal *self, PyObject *args, PyObject *keywds)
435 {
436     int64_t timeout=0LL;
437     if (! PyArg_ParseTuple(args, "|L", &timeout))
438         return NULL;
439
440     int r;
441     Py_BEGIN_ALLOW_THREADS
442     if ( timeout == 0LL) {
443         r = sd_journal_wait(self->j, (uint64_t) -1);
444     }else{
445         r = sd_journal_wait(self->j, timeout * 1E6);
446     }
447     Py_END_ALLOW_THREADS
448     if (r < 0) {
449         errno = -r;
450         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
451                             PyExc_OSError;
452         PyErr_SetFromErrno(errtype);
453         return NULL;
454     }
455 #if PY_MAJOR_VERSION >=3
456     return PyLong_FromLong(r);
457 #else
458     return PyInt_FromLong(r);
459 #endif
460 }
461
462 PyDoc_STRVAR(Journal_seek_cursor__doc__,
463 "seek_cursor(cursor) -> None\n\n"
464 "Seeks to journal entry by given unique reference `cursor`.");
465 static PyObject *
466 Journal_seek_cursor(Journal *self, PyObject *args)
467 {
468     const char *cursor;
469     if (! PyArg_ParseTuple(args, "s", &cursor))
470         return NULL;
471
472     int r;
473     Py_BEGIN_ALLOW_THREADS
474     r = sd_journal_seek_cursor(self->j, cursor);
475     Py_END_ALLOW_THREADS
476     if (r < 0) {
477         errno = -r;
478         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
479                             r == -ENOMEM ? PyExc_MemoryError :
480                             PyExc_OSError;
481         PyErr_SetFromErrno(errtype);
482         return NULL;
483     }
484     Py_RETURN_NONE;
485 }
486
487 static PyObject *
488 Journal_iter(PyObject *self)
489 {
490     Py_INCREF(self);
491     return self;
492 }
493
494 static PyObject *
495 Journal_iternext(PyObject *self)
496 {
497     PyObject *dict;
498     Py_ssize_t dict_size;
499
500     dict = PyObject_CallMethod(self, "get_next", "");
501     dict_size = PyDict_Size(dict);
502     if ((int64_t) dict_size > 0LL) {
503         return dict;
504     }else{
505         Py_DECREF(dict);
506         PyErr_SetNone(PyExc_StopIteration);
507         return NULL;
508     }
509 }
510
511 #ifdef SD_JOURNAL_FOREACH_UNIQUE
512 PyDoc_STRVAR(Journal_query_unique__doc__,
513 "query_unique(field) -> a set of values\n\n"
514 "Returns a set of unique values in journal for given `field`.\n"
515 "Note this does not respect any journal matches.");
516 static PyObject *
517 Journal_query_unique(Journal *self, PyObject *args)
518 {
519     char *query;
520     if (! PyArg_ParseTuple(args, "s", &query))
521         return NULL;
522
523     int r;
524     Py_BEGIN_ALLOW_THREADS
525     r = sd_journal_query_unique(self->j, query);
526     Py_END_ALLOW_THREADS
527     if (r < 0) {
528         errno = -r;
529         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
530                             r == -ENOMEM ? PyExc_MemoryError :
531                             PyExc_OSError;
532         PyErr_SetFromErrno(errtype);
533         return NULL;
534     }
535
536     const void *uniq;
537     size_t uniq_len;
538     const char *delim_ptr;
539     PyObject *value_set, *key, *value;
540     value_set = PySet_New(0);
541
542 #if PY_MAJOR_VERSION >=3
543     key = PyUnicode_FromString(query);
544 #else
545     key = PyString_FromString(query);
546 #endif
547
548     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
549         delim_ptr = memchr(uniq, '=', uniq_len);
550         value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1));
551         PySet_Add(value_set, value);
552         Py_DECREF(value);
553     }
554     Py_DECREF(key);
555     return value_set;
556 }
557 #endif //def SD_JOURNAL_FOREACH_UNIQUE
558
559 static PyObject *
560 Journal_get_data_threshold(Journal *self, void *closure)
561 {
562     size_t cvalue;
563     PyObject *value;
564     int r;
565
566     r = sd_journal_get_data_threshold(self->j, &cvalue);
567     if (r < 0) {
568         errno = -r;
569         PyErr_SetFromErrno(PyExc_OSError);
570         return NULL;
571     }
572
573 #if PY_MAJOR_VERSION >=3
574     value = PyLong_FromSize_t(cvalue);
575 #else
576     value = PyInt_FromSize_t(cvalue);
577 #endif
578     return value;
579 }
580
581 static int
582 Journal_set_data_threshold(Journal *self, PyObject *value, void *closure)
583 {
584     if (value == NULL) {
585         PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold");
586         return -1;
587     }
588 #if PY_MAJOR_VERSION >=3
589     if (! PyLong_Check(value)){
590 #else
591     if (! PyInt_Check(value)){
592 #endif
593         PyErr_SetString(PyExc_TypeError, "Data threshold must be int");
594         return -1;
595     }
596     int r;
597 #if PY_MAJOR_VERSION >=3
598     r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value));
599 #else
600     r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value));
601 #endif
602     if (r < 0) {
603         errno = -r;
604         PyErr_SetFromErrno(PyExc_OSError);
605         return -1;
606     }
607     return 0;
608 }
609
610 static PyGetSetDef Journal_getseters[] = {
611     {"data_threshold",
612     (getter)Journal_get_data_threshold,
613     (setter)Journal_set_data_threshold,
614     "data threshold",
615     NULL},
616     {NULL}
617 };
618
619 static PyMethodDef Journal_methods[] = {
620     {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS,
621     Journal_get_next__doc__},
622     {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS,
623     Journal_get_previous__doc__},
624     {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS,
625     Journal_add_match__doc__},
626     {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS,
627     Journal_add_disjunction__doc__},
628     {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS,
629     Journal_flush_matches__doc__},
630     {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS,
631     Journal_seek__doc__},
632     {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS,
633     Journal_seek_realtime__doc__},
634     {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS,
635     Journal_seek_monotonic__doc__},
636     {"wait", (PyCFunction)Journal_wait, METH_VARARGS,
637     Journal_wait__doc__},
638     {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS,
639     Journal_seek_cursor__doc__},
640 #ifdef SD_JOURNAL_FOREACH_UNIQUE
641     {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS,
642     Journal_query_unique__doc__},
643 #endif
644     {NULL}  /* Sentinel */
645 };
646
647 static PyTypeObject JournalType = {
648     PyVarObject_HEAD_INIT(NULL, 0)
649     "_reader.Journal",           /*tp_name*/
650     sizeof(Journal),                  /*tp_basicsize*/
651     0,                                /*tp_itemsize*/
652     (destructor)Journal_dealloc,      /*tp_dealloc*/
653     0,                                /*tp_print*/
654     0,                                /*tp_getattr*/
655     0,                                /*tp_setattr*/
656     0,                                /*tp_compare*/
657     0,                                /*tp_repr*/
658     0,                                /*tp_as_number*/
659     0,                                /*tp_as_sequence*/
660     0,                                /*tp_as_mapping*/
661     0,                                /*tp_hash */
662     0,                                /*tp_call*/
663     0,                                /*tp_str*/
664     0,                                /*tp_getattro*/
665     0,                                /*tp_setattro*/
666     0,                                /*tp_as_buffer*/
667     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
668     Journal__doc__,                   /* tp_doc */
669     0,                                /* tp_traverse */
670     0,                                /* tp_clear */
671     0,                                /* tp_richcompare */
672     0,                                /* tp_weaklistoffset */
673     Journal_iter,                     /* tp_iter */
674     Journal_iternext,                 /* tp_iternext */
675     Journal_methods,                  /* tp_methods */
676     0,                                /* tp_members */
677     Journal_getseters,                /* tp_getset */
678     0,                                /* tp_base */
679     0,                                /* tp_dict */
680     0,                                /* tp_descr_get */
681     0,                                /* tp_descr_set */
682     0,                                /* tp_dictoffset */
683     (initproc)Journal_init,           /* tp_init */
684     0,                                /* tp_alloc */
685     PyType_GenericNew,                /* tp_new */
686 };
687
688 #if PY_MAJOR_VERSION >= 3
689 static PyModuleDef _reader_module = {
690     PyModuleDef_HEAD_INIT,
691     "_reader",
692     "Module that reads systemd journal similar to journalctl.",
693     -1,
694     NULL, NULL, NULL, NULL, NULL
695 };
696 #endif
697
698 PyMODINIT_FUNC
699 #if PY_MAJOR_VERSION >= 3
700 PyInit__reader(void)
701 #else
702 init_reader(void) 
703 #endif
704 {
705     PyObject* m;
706
707     PyDateTime_IMPORT;
708
709     if (PyType_Ready(&JournalType) < 0)
710 #if PY_MAJOR_VERSION >= 3
711         return NULL;
712 #else
713         return;
714 #endif
715
716 #if PY_MAJOR_VERSION >= 3
717     m = PyModule_Create(&_reader_module);
718     if (m == NULL)
719         return NULL;
720 #else
721     m = Py_InitModule3("_reader", NULL,
722                    "Module that reads systemd journal similar to journalctl.");
723     if (m == NULL)
724         return;
725 #endif
726
727     Py_INCREF(&JournalType);
728     PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType);
729     PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP);
730     PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND);
731     PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE);
732     PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY);
733     PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY);
734     PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY);
735
736 #if PY_MAJOR_VERSION >= 3
737     return m;
738 #endif
739 }