2 _reader - Python module that reads systemd journal similar to journalctl
3 Copyright (C) 2012 Steven Hiscocks
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include <systemd/sd-journal.h>
22 #include <structmember.h>
29 static PyTypeObject JournalType;
32 Journal_dealloc(Journal* self)
34 sd_journal_close(self->j);
35 Py_TYPE(self)->tp_free((PyObject*)self);
38 PyDoc_STRVAR(Journal__doc__,
39 "Journal([flags][,path]) -> ...\n"
40 "Journal instance\n\n"
41 "Returns instance of Journal, which allows filtering and return\n"
42 "of journal entries.\n"
43 "Argument `flags` sets open flags of the journal, which can be one\n"
44 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
45 "journal on local machine only; RUNTIME_ONLY opens only\n"
46 "volatile journal files; and SYSTEM_ONLY opens only\n"
47 "journal files of system services and the kernel.\n"
48 "Argument `path` is the directory of journal files. Note that\n"
49 "currently flags are ignored when `path` is present as they are\n"
52 Journal_init(Journal *self, PyObject *args, PyObject *keywds)
54 int flags=SD_JOURNAL_LOCAL_ONLY;
57 static char *kwlist[] = {"flags", NULL};
58 if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist,
64 r = sd_journal_open_directory(&self->j, path, 0);
66 Py_BEGIN_ALLOW_THREADS
67 r = sd_journal_open(&self->j, flags);
71 PyErr_SetString(PyExc_ValueError, "Invalid flags or path");
73 }else if (r == -ENOMEM) {
74 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
77 PyErr_SetString(PyExc_RuntimeError, "Error opening journal");
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.");
89 Journal_get_next(Journal *self, PyObject *args)
92 if (! PyArg_ParseTuple(args, "|L", &skip))
97 Py_BEGIN_ALLOW_THREADS
98 r = sd_journal_next(self->j);
100 }else if (skip == -1LL) {
101 Py_BEGIN_ALLOW_THREADS
102 r = sd_journal_previous(self->j);
104 }else if (skip > 1LL) {
105 Py_BEGIN_ALLOW_THREADS
106 r = sd_journal_next_skip(self->j, skip);
108 }else if (skip < -1LL) {
109 Py_BEGIN_ALLOW_THREADS
110 r = sd_journal_previous_skip(self->j, -skip);
113 PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer");
118 PyErr_SetString(PyExc_RuntimeError, "Error getting next message");
120 }else if ( r == 0) { //EOF
129 const char *delim_ptr;
130 PyObject *key, *value, *cur_value, *tmp_list;
132 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
133 delim_ptr = memchr(msg, '=', msg_len);
134 #if PY_MAJOR_VERSION >=3
135 key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
137 key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg);
139 value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) );
140 if (PyDict_Contains(dict, key)) {
141 cur_value = PyDict_GetItem(dict, key);
142 if (PyList_CheckExact(cur_value)) {
143 PyList_Append(cur_value, value);
145 tmp_list = PyList_New(0);
146 PyList_Append(tmp_list, cur_value);
147 PyList_Append(tmp_list, value);
148 PyDict_SetItem(dict, key, tmp_list);
152 PyDict_SetItem(dict, key, value);
159 if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) {
160 char realtime_str[20];
161 sprintf(realtime_str, "%llu", (long long unsigned) realtime);
163 #if PY_MAJOR_VERSION >=3
164 key = PyUnicode_FromString("__REALTIME_TIMESTAMP");
166 key = PyString_FromString("__REALTIME_TIMESTAMP");
168 value = PyBytes_FromString(realtime_str);
169 PyDict_SetItem(dict, key, value);
176 if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) {
177 char monotonic_str[20];
178 sprintf(monotonic_str, "%llu", (long long unsigned) monotonic);
179 #if PY_MAJOR_VERSION >=3
180 key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP");
182 key = PyString_FromString("__MONOTONIC_TIMESTAMP");
184 value = PyBytes_FromString(monotonic_str);
186 PyDict_SetItem(dict, key, value);
192 if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0...
193 #if PY_MAJOR_VERSION >=3
194 key = PyUnicode_FromString("__CURSOR");
196 key = PyString_FromString("__CURSOR");
198 value = PyBytes_FromString(cursor);
199 PyDict_SetItem(dict, key, value);
208 PyDoc_STRVAR(Journal_get_previous__doc__,
209 "get_previous([skip]) -> dict\n\n"
210 "Return dictionary of the previous log entry. Optional skip value\n"
211 "will return the -`skip`th log entry. Equivalent to get_next(-skip).");
213 Journal_get_previous(Journal *self, PyObject *args)
216 if (! PyArg_ParseTuple(args, "|L", &skip))
219 return PyObject_CallMethod((PyObject *)self, "get_next", "L", -skip);
222 PyDoc_STRVAR(Journal_add_match__doc__,
223 "add_match(match, ..., field=value, ...) -> None\n\n"
224 "Add a match to filter journal log entries. All matches of different\n"
225 "field are combined in logical AND, and matches of the same field\n"
226 "are automatically combined in logical OR.\n"
227 "Matches can be passed as strings \"field=value\", or keyword\n"
228 "arguments field=\"value\".");
230 Journal_add_match(Journal *self, PyObject *args, PyObject *keywds)
232 Py_ssize_t arg_match_len;
235 for (i = 0; i < PySequence_Size(args); i++) {
236 #if PY_MAJOR_VERSION >=3
238 arg = PySequence_Fast_GET_ITEM(args, i);
239 if (PyUnicode_Check(arg)) {
240 #if PY_MINOR_VERSION >=3
241 arg_match = PyUnicode_AsUTF8AndSize(arg, &arg_match_len);
244 temp = PyUnicode_AsUTF8String(arg);
245 PyBytes_AsStringAndSize(temp, &arg_match, &arg_match_len);
248 }else if (PyBytes_Check(arg)) {
249 PyBytes_AsStringAndSize(arg, &arg_match, &arg_match_len);
251 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
254 PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(args, i), &arg_match, &arg_match_len);
256 if (PyErr_Occurred())
258 r = sd_journal_add_match(self->j, arg_match, arg_match_len);
260 PyErr_SetString(PyExc_ValueError, "Invalid match");
262 }else if (r == -ENOMEM) {
263 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
266 PyErr_SetString(PyExc_RuntimeError, "Error adding match");
274 PyObject *key, *value;
275 Py_ssize_t pos=0, match_key_len, match_value_len;
277 char *match_key, *match_value;
279 while (PyDict_Next(keywds, &pos, &key, &value)) {
280 #if PY_MAJOR_VERSION >=3
281 if (PyUnicode_Check(key)) {
282 #if PY_MINOR_VERSION >=3
283 match_key = PyUnicode_AsUTF8AndSize(key, &match_key_len);
286 temp2 = PyUnicode_AsUTF8String(key);
287 PyBytes_AsStringAndSize(temp2, &match_key, &match_key_len);
290 }else if (PyBytes_Check(key)) {
291 PyBytes_AsStringAndSize(key, &match_key, &match_key_len);
293 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
295 if (PyUnicode_Check(value)) {
296 #if PY_MINOR_VERSION >=3
297 match_value = PyUnicode_AsUTF8AndSize(value, &match_value_len);
300 temp3 = PyUnicode_AsUTF8String(value);
301 PyBytes_AsStringAndSize(temp3, &match_value, &match_value_len);
304 }else if (PyBytes_Check(value)) {
305 PyBytes_AsStringAndSize(value, &match_value, &match_value_len);
307 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
310 PyString_AsStringAndSize(key, &match_key, &match_key_len);
311 PyString_AsStringAndSize(value, &match_value, &match_value_len);
313 if (PyErr_Occurred())
316 match_len = match_key_len + 1 + match_value_len;
317 match = malloc(match_len);
318 memcpy(match, match_key, match_key_len);
319 memcpy(match + match_key_len, "=", 1);
320 memcpy(match + match_key_len + 1, match_value, match_value_len);
322 r = sd_journal_add_match(self->j, match, match_len);
325 PyErr_SetString(PyExc_ValueError, "Invalid match");
327 }else if (r == -ENOMEM) {
328 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
331 PyErr_SetString(PyExc_RuntimeError, "Error adding match");
339 PyDoc_STRVAR(Journal_add_disjunction__doc__,
340 "add_disjunction() -> None\n\n"
341 "Once called, all matches before and after are combined in logical\n"
344 Journal_add_disjunction(Journal *self, PyObject *args)
347 r = sd_journal_add_disjunction(self->j);
349 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
352 PyErr_SetString(PyExc_RuntimeError, "Error adding disjunction");
358 PyDoc_STRVAR(Journal_flush_matches__doc__,
359 "flush_matches() -> None\n\n"
360 "Clears all current match filters.");
362 Journal_flush_matches(Journal *self, PyObject *args)
364 sd_journal_flush_matches(self->j);
368 PyDoc_STRVAR(Journal_seek__doc__,
369 "seek(offset[, whence]) -> None\n\n"
370 "Seek through journal by `offset` number of entries. Argument\n"
371 "`whence` defines what the offset is relative to:\n"
372 "os.SEEK_SET (default) from first match in journal;\n"
373 "os.SEEK_CUR from current position in journal;\n"
374 "and os.SEEK_END is from last match in journal.");
376 Journal_seek(Journal *self, PyObject *args, PyObject *keywds)
380 static char *kwlist[] = {"offset", "whence", NULL};
382 if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist,
387 if (whence == SEEK_SET){
389 Py_BEGIN_ALLOW_THREADS
390 r = sd_journal_seek_head(self->j);
393 PyErr_SetString(PyExc_RuntimeError, "Error seeking to head");
397 Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset));
399 }else if (whence == SEEK_CUR){
400 Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset));
401 }else if (whence == SEEK_END){
403 Py_BEGIN_ALLOW_THREADS
404 r = sd_journal_seek_tail(self->j);
407 PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail");
410 Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL));
412 Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset));
415 PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
421 PyDoc_STRVAR(Journal_seek_realtime__doc__,
422 "seek_realtime(realtime) -> None\n\n"
423 "Seek to nearest matching journal entry to `realtime`. Argument\n"
424 "`realtime` can be an integer unix timestamp in usecs or a "
425 "datetime instance.");
427 Journal_seek_realtime(Journal *self, PyObject *args)
430 if (! PyArg_ParseTuple(args, "O", &arg))
433 uint64_t timestamp=-1LL;
434 if (PyDateTime_Check(arg)) {
437 temp = PyObject_CallMethod(arg, "strftime", "s", "%s%f");
438 #if PY_MAJOR_VERSION >=3
440 temp2 = PyUnicode_AsUTF8String(temp);
441 timestamp_str = PyBytes_AsString(temp2);
444 timestamp_str = PyString_AsString(temp);
447 timestamp = strtoull(timestamp_str, NULL, 10);
448 }else if (PyLong_Check(arg)) {
449 timestamp = PyLong_AsUnsignedLongLong(arg);
450 #if PY_MAJOR_VERSION <3
451 }else if (PyInt_Check(arg)) {
452 timestamp = PyInt_AsUnsignedLongLongMask(arg);
455 if ((int64_t) timestamp < 0LL) {
456 PyErr_SetString(PyExc_ValueError, "Time must be positive integer or datetime instance");
461 Py_BEGIN_ALLOW_THREADS
462 r = sd_journal_seek_realtime_usec(self->j, timestamp);
465 PyErr_SetString(PyExc_RuntimeError, "Error seek to time");
471 PyDoc_STRVAR(Journal_seek_monotonic__doc__,
472 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
473 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
474 "`monotonic` is an timestamp from boot in secs, or a\n"
475 "timedelta instance.\n"
476 "Argument `bootid` is a string representing which boot the\n"
477 "monotonic time is reference to. Defaults to current bootid.");
479 Journal_seek_monotonic(Journal *self, PyObject *args)
483 if (! PyArg_ParseTuple(args, "O|s", &arg, &bootid))
486 uint64_t timestamp=-1LL;
487 if PyDelta_Check(arg) {
489 temp = PyObject_CallMethod(arg, "total_seconds", NULL);
490 timestamp = (uint64_t) (PyFloat_AsDouble(temp) * 1E6);
492 }else if (PyFloat_Check(arg)) {
493 timestamp = (uint64_t) (PyFloat_AsDouble(arg) * 1E6);
494 }else if (PyLong_Check(arg)) {
495 timestamp = PyLong_AsUnsignedLongLong(arg) * (uint64_t) 1E6;
496 #if PY_MAJOR_VERSION <3
497 }else if (PyInt_Check(arg)) {
498 timestamp = PyInt_AsUnsignedLongLongMask(arg) * (uint64_t) 1E6;
503 if ((int64_t) timestamp < 0LL) {
504 PyErr_SetString(PyExc_ValueError, "Time must be positive number or timedelta instance");
511 r = sd_id128_from_string(bootid, &sd_id);
513 PyErr_SetString(PyExc_ValueError, "Invalid bootid");
516 PyErr_SetString(PyExc_RuntimeError, "Error processing bootid");
520 r = sd_id128_get_boot(&sd_id);
522 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
525 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
530 Py_BEGIN_ALLOW_THREADS
531 r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp);
534 PyErr_SetString(PyExc_RuntimeError, "Error seek to time");
540 PyDoc_STRVAR(Journal_wait__doc__,
541 "wait([timeout]) -> Change state (integer)\n\n"
542 "Waits until there is a change in the journal. Argument `timeout`\n"
543 "is the maximum number of seconds to wait before returning\n"
544 "regardless if journal has changed. If `timeout` is not given or is\n"
545 "0, then it will block forever.\n"
546 "Will return constants: NOP if no change; APPEND if new\n"
547 "entries have been added to the end of the journal; and\n"
548 "INVALIDATE if journal files have been added or removed.");
550 Journal_wait(Journal *self, PyObject *args, PyObject *keywds)
553 if (! PyArg_ParseTuple(args, "|L", &timeout))
557 if ( timeout == 0LL) {
558 Py_BEGIN_ALLOW_THREADS
559 r = sd_journal_wait(self->j, (uint64_t) -1);
562 Py_BEGIN_ALLOW_THREADS
563 r = sd_journal_wait(self->j, timeout * 1E6);
566 #if PY_MAJOR_VERSION >=3
567 return PyLong_FromLong(r);
569 return PyInt_FromLong(r);
573 PyDoc_STRVAR(Journal_seek_cursor__doc__,
574 "seek_cursor(cursor) -> None\n\n"
575 "Seeks to journal entry by given unique reference `cursor`.");
577 Journal_seek_cursor(Journal *self, PyObject *args)
580 if (! PyArg_ParseTuple(args, "s", &cursor))
584 Py_BEGIN_ALLOW_THREADS
585 r = sd_journal_seek_cursor(self->j, cursor);
588 PyErr_SetString(PyExc_ValueError, "Invalid cursor");
590 }else if (r == -ENOMEM) {
591 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
594 PyErr_SetString(PyExc_RuntimeError, "Error seeking to cursor");
601 Journal_iter(PyObject *self)
608 Journal_iternext(PyObject *self)
610 PyObject *dict, *arg;
611 Py_ssize_t dict_size;
613 dict = PyObject_CallMethod(self, "get_next", "");
614 dict_size = PyDict_Size(dict);
615 if ((int64_t) dict_size > 0LL) {
619 PyErr_SetNone(PyExc_StopIteration);
624 #ifdef SD_JOURNAL_FOREACH_UNIQUE
625 PyDoc_STRVAR(Journal_query_unique__doc__,
626 "query_unique(field) -> a set of values\n\n"
627 "Returns a set of unique values in journal for given `field`.\n"
628 "Note this does not respect any journal matches.");
630 Journal_query_unique(Journal *self, PyObject *args)
633 if (! PyArg_ParseTuple(args, "s", &query))
637 Py_BEGIN_ALLOW_THREADS
638 r = sd_journal_query_unique(self->j, query);
641 PyErr_SetString(PyExc_ValueError, "Invalid field name");
643 } else if (r == -ENOMEM) {
644 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
647 PyErr_SetString(PyExc_RuntimeError, "Error querying journal");
653 const char *delim_ptr;
654 PyObject *value_set, *key, *value;
655 value_set = PySet_New(0);
657 #if PY_MAJOR_VERSION >=3
658 key = PyUnicode_FromString(query);
660 key = PyString_FromString(query);
663 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
664 delim_ptr = memchr(uniq, '=', uniq_len);
665 value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1));
666 PySet_Add(value_set, value);
672 #endif //def SD_JOURNAL_FOREACH_UNIQUE
674 PyDoc_STRVAR(Journal_this_boot__doc__,
675 "this_boot() -> None\n\n"
676 "Sets match filter for the current _BOOT_ID.");
678 Journal_this_boot(Journal *self, PyObject *args)
682 r = sd_id128_get_boot(&sd_id);
684 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
687 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
692 sd_id128_to_string(sd_id, bootid);
694 PyObject *arg, *keywds;
695 arg = PyTuple_New(0);
696 keywds = Py_BuildValue("{s:s}", "_BOOT_ID", bootid);
697 Journal_add_match(self, arg, keywds);
700 if (PyErr_Occurred())
706 PyDoc_STRVAR(Journal_this_machine__doc__,
707 "this_machine() -> None\n\n"
708 "Sets match filter for the current _MACHINE_ID.");
710 Journal_this_machine(Journal *self, PyObject *args)
714 r = sd_id128_get_machine(&sd_id);
716 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
719 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
724 sd_id128_to_string(sd_id, machineid);
726 PyObject *arg, *keywds;
727 arg = PyTuple_New(0);
728 keywds = Py_BuildValue("{s:s}", "_MACHINE_ID", machineid);
729 Journal_add_match(self, arg, keywds);
732 if (PyErr_Occurred())
739 Journal_get_data_threshold(Journal *self, void *closure)
745 r = sd_journal_get_data_threshold(self->j, &cvalue);
747 PyErr_SetString(PyExc_RuntimeError, "Error getting data threshold");
751 #if PY_MAJOR_VERSION >=3
752 value = PyLong_FromSize_t(cvalue);
754 value = PyInt_FromSize_t(cvalue);
760 Journal_set_data_threshold(Journal *self, PyObject *value, void *closure)
763 PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold");
766 #if PY_MAJOR_VERSION >=3
767 if (! PyLong_Check(value)){
769 if (! PyInt_Check(value)){
771 PyErr_SetString(PyExc_TypeError, "Data threshold must be int");
775 #if PY_MAJOR_VERSION >=3
776 r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value));
778 r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value));
781 PyErr_SetString(PyExc_RuntimeError, "Error setting data threshold");
787 static PyGetSetDef Journal_getseters[] = {
789 (getter)Journal_get_data_threshold,
790 (setter)Journal_set_data_threshold,
796 static PyMethodDef Journal_methods[] = {
797 {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS,
798 Journal_get_next__doc__},
799 {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS,
800 Journal_get_previous__doc__},
801 {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS,
802 Journal_add_match__doc__},
803 {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS,
804 Journal_add_disjunction__doc__},
805 {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS,
806 Journal_flush_matches__doc__},
807 {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS,
808 Journal_seek__doc__},
809 {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS,
810 Journal_seek_realtime__doc__},
811 {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS,
812 Journal_seek_monotonic__doc__},
813 {"wait", (PyCFunction)Journal_wait, METH_VARARGS,
814 Journal_wait__doc__},
815 {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS,
816 Journal_seek_cursor__doc__},
817 #ifdef SD_JOURNAL_FOREACH_UNIQUE
818 {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS,
819 Journal_query_unique__doc__},
821 {"this_boot", (PyCFunction)Journal_this_boot, METH_NOARGS,
822 Journal_this_boot__doc__},
823 {"this_machine", (PyCFunction)Journal_this_machine, METH_NOARGS,
824 Journal_this_machine__doc__},
825 {NULL} /* Sentinel */
828 static PyTypeObject JournalType = {
829 PyVarObject_HEAD_INIT(NULL, 0)
830 "_reader.Journal", /*tp_name*/
831 sizeof(Journal), /*tp_basicsize*/
833 (destructor)Journal_dealloc, /*tp_dealloc*/
840 0, /*tp_as_sequence*/
848 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
849 Journal__doc__, /* tp_doc */
852 0, /* tp_richcompare */
853 0, /* tp_weaklistoffset */
854 Journal_iter, /* tp_iter */
855 Journal_iternext, /* tp_iternext */
856 Journal_methods, /* tp_methods */
858 Journal_getseters, /* tp_getset */
861 0, /* tp_descr_get */
862 0, /* tp_descr_set */
863 0, /* tp_dictoffset */
864 (initproc)Journal_init, /* tp_init */
866 PyType_GenericNew, /* tp_new */
869 #if PY_MAJOR_VERSION >= 3
870 static PyModuleDef _reader_module = {
871 PyModuleDef_HEAD_INIT,
873 "Module that reads systemd journal similar to journalctl.",
875 NULL, NULL, NULL, NULL, NULL
880 #if PY_MAJOR_VERSION >= 3
890 if (PyType_Ready(&JournalType) < 0)
891 #if PY_MAJOR_VERSION >= 3
897 #if PY_MAJOR_VERSION >= 3
898 m = PyModule_Create(&_reader_module);
902 m = Py_InitModule3("_reader", NULL,
903 "Module that reads systemd journal similar to journalctl.");
908 Py_INCREF(&JournalType);
909 PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType);
910 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP);
911 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND);
912 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE);
913 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY);
914 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY);
915 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY);
917 #if PY_MAJOR_VERSION >= 3