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>
28 PyObject *default_call;
31 static PyTypeObject JournalType;
34 Journal_dealloc(Journal* self)
36 sd_journal_close(self->j);
37 Py_XDECREF(self->default_call);
38 Py_XDECREF(self->call_dict);
39 Py_TYPE(self)->tp_free((PyObject*)self);
43 Journal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
47 self = (Journal *)type->tp_alloc(type, 0);
49 self->call_dict = PyDict_New();
50 self->default_call = Py_None;
53 return (PyObject *) self;
56 PyDoc_STRVAR(Journal__doc__,
57 "Journal([flags][, default_call][, call_dict][,path]) -> ...\n"
58 "Journal instance\n\n"
59 "Returns instance of Journal, which allows filtering and return\n"
60 "of journal entries.\n"
61 "Argument `flags` sets open flags of the journal, which can be one\n"
62 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
63 "journal on local machine only; RUNTIME_ONLY opens only\n"
64 "volatile journal files; and SYSTEM_ONLY opens only\n"
65 "journal files of system services and the kernel.\n"
66 "Argument `default_call` must be a callable that accepts one\n"
67 "argument which is string/bytes value of a field and returns\n"
69 "Argument `call_dict` is a dictionary where the key represents\n"
70 "a field name, and value is a callable as per `default_call`.\n"
71 "A set of sane defaults for `default_call` and `call_dict` are\n"
73 "Argument `path` is the directory of journal files. Note that\n"
74 "currently flags are ignored when `path` is present as they are\n"
77 Journal_init(Journal *self, PyObject *args, PyObject *keywds)
79 int flags=SD_JOURNAL_LOCAL_ONLY;
81 PyObject *default_call=NULL, *call_dict=NULL;
83 static char *kwlist[] = {"flags", "default_call", "call_dict", "path", NULL};
84 if (! PyArg_ParseTupleAndKeywords(args, keywds, "|iOOs", kwlist,
85 &flags, &default_call, &call_dict, &path))
89 if (PyCallable_Check(default_call) || default_call == Py_None) {
90 Py_DECREF(self->default_call);
91 self->default_call = default_call;
92 Py_INCREF(self->default_call);
94 PyErr_SetString(PyExc_TypeError, "Default call not callable");
100 if (PyDict_Check(call_dict)) {
101 Py_DECREF(self->call_dict);
102 self->call_dict = call_dict;
103 Py_INCREF(self->call_dict);
104 }else if (call_dict == Py_None) {
105 Py_DECREF(self->call_dict);
106 self->call_dict = PyDict_New();
108 PyErr_SetString(PyExc_TypeError, "Call dictionary must be dict type");
115 r = sd_journal_open_directory(&self->j, path, 0);
117 Py_BEGIN_ALLOW_THREADS
118 r = sd_journal_open(&self->j, flags);
122 PyErr_SetString(PyExc_ValueError, "Invalid flags or path");
124 }else if (r == -ENOMEM) {
125 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
128 PyErr_SetString(PyExc_RuntimeError, "Error opening journal");
136 Journal___process_field(Journal *self, PyObject *key, const void *value, ssize_t value_len)
138 PyObject *callable=NULL, *return_value=NULL;
139 if (PyDict_Check(self->call_dict))
140 callable = PyDict_GetItem(self->call_dict, key);
142 if (PyCallable_Check(callable)) {
143 #if PY_MAJOR_VERSION >=3
144 return_value = PyObject_CallFunction(callable, "y#", value, value_len);
146 return_value = PyObject_CallFunction(callable, "s#", value, value_len);
151 if (!return_value && PyCallable_Check(self->default_call))
152 #if PY_MAJOR_VERSION >=3
153 return_value = PyObject_CallFunction(self->default_call, "y#", value, value_len);
155 return_value = PyObject_CallFunction(self->default_call, "s#", value, value_len);
159 #if PY_MAJOR_VERSION >=3
160 return_value = PyBytes_FromStringAndSize(value, value_len);
162 return_value = PyString_FromStringAndSize(value, value_len);
166 return_value = Py_None;
171 PyDoc_STRVAR(Journal_get_next__doc__,
172 "get_next([skip]) -> dict\n\n"
173 "Return dictionary of the next log entry. Optional skip value will\n"
174 "return the `skip`th log entry.");
176 Journal_get_next(Journal *self, PyObject *args)
179 if (! PyArg_ParseTuple(args, "|L", &skip))
184 Py_BEGIN_ALLOW_THREADS
185 r = sd_journal_next(self->j);
187 }else if (skip == -1LL) {
188 Py_BEGIN_ALLOW_THREADS
189 r = sd_journal_previous(self->j);
191 }else if (skip > 1LL) {
192 Py_BEGIN_ALLOW_THREADS
193 r = sd_journal_next_skip(self->j, skip);
195 }else if (skip < -1LL) {
196 Py_BEGIN_ALLOW_THREADS
197 r = sd_journal_previous_skip(self->j, -skip);
200 PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer");
205 PyErr_SetString(PyExc_RuntimeError, "Error getting next message");
207 }else if ( r == 0) { //EOF
216 const char *delim_ptr;
217 PyObject *key, *value, *cur_value, *tmp_list;
219 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
220 delim_ptr = memchr(msg, '=', msg_len);
221 #if PY_MAJOR_VERSION >=3
222 key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
224 key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg);
226 value = Journal___process_field(self, key, delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) );
227 if (PyDict_Contains(dict, key)) {
228 cur_value = PyDict_GetItem(dict, key);
229 if (PyList_CheckExact(cur_value) && PyList_Size(cur_value) > 1) {
230 PyList_Append(cur_value, value);
232 tmp_list = PyList_New(0);
233 PyList_Append(tmp_list, cur_value);
234 PyList_Append(tmp_list, value);
235 PyDict_SetItem(dict, key, tmp_list);
239 PyDict_SetItem(dict, key, value);
246 if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) {
247 char realtime_str[20];
248 sprintf(realtime_str, "%llu", (long long unsigned) realtime);
250 #if PY_MAJOR_VERSION >=3
251 key = PyUnicode_FromString("__REALTIME_TIMESTAMP");
253 key = PyString_FromString("__REALTIME_TIMESTAMP");
255 value = Journal___process_field(self, key, realtime_str, strlen(realtime_str));
256 PyDict_SetItem(dict, key, value);
263 if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) {
264 char monotonic_str[20];
265 sprintf(monotonic_str, "%llu", (long long unsigned) monotonic);
266 #if PY_MAJOR_VERSION >=3
267 key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP");
269 key = PyString_FromString("__MONOTONIC_TIMESTAMP");
271 value = Journal___process_field(self, key, monotonic_str, strlen(monotonic_str));
273 PyDict_SetItem(dict, key, value);
279 if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0...
280 #if PY_MAJOR_VERSION >=3
281 key = PyUnicode_FromString("__CURSOR");
283 key = PyString_FromString("__CURSOR");
285 value = Journal___process_field(self, key, cursor, strlen(cursor));
286 PyDict_SetItem(dict, key, value);
295 PyDoc_STRVAR(Journal_get_previous__doc__,
296 "get_previous([skip]) -> dict\n\n"
297 "Return dictionary of the previous log entry. Optional skip value\n"
298 "will return the -`skip`th log entry. Equivalent to get_next(-skip).");
300 Journal_get_previous(Journal *self, PyObject *args)
303 if (! PyArg_ParseTuple(args, "|L", &skip))
306 PyObject *dict, *arg;
307 arg = Py_BuildValue("(L)", -skip);
308 dict = Journal_get_next(self, arg);
313 PyDoc_STRVAR(Journal_add_match__doc__,
314 "add_match(match, ..., field=value, ...) -> None\n\n"
315 "Add a match to filter journal log entries. All matches of different\n"
316 "field are combined in logical AND, and matches of the same field\n"
317 "are automatically combined in logical OR.\n"
318 "Matches can be passed as strings \"field=value\", or keyword\n"
319 "arguments field=\"value\".");
321 Journal_add_match(Journal *self, PyObject *args, PyObject *keywds)
323 Py_ssize_t arg_match_len;
326 for (i = 0; i < PySequence_Size(args); i++) {
327 #if PY_MAJOR_VERSION >=3
329 arg = PySequence_Fast_GET_ITEM(args, i);
330 if (PyUnicode_Check(arg)) {
331 #if PY_MINOR_VERSION >=3
332 arg_match = PyUnicode_AsUTF8AndSize(arg, &arg_match_len);
335 temp = PyUnicode_AsUTF8String(arg);
336 PyBytes_AsStringAndSize(temp, &arg_match, &arg_match_len);
339 }else if (PyBytes_Check(arg)) {
340 PyBytes_AsStringAndSize(arg, &arg_match, &arg_match_len);
342 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
345 PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(args, i), &arg_match, &arg_match_len);
347 if (PyErr_Occurred())
349 r = sd_journal_add_match(self->j, arg_match, arg_match_len);
351 PyErr_SetString(PyExc_ValueError, "Invalid match");
353 }else if (r == -ENOMEM) {
354 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
357 PyErr_SetString(PyExc_RuntimeError, "Error adding match");
365 PyObject *key, *value;
366 Py_ssize_t pos=0, match_key_len, match_value_len;
368 char *match_key, *match_value;
370 while (PyDict_Next(keywds, &pos, &key, &value)) {
371 #if PY_MAJOR_VERSION >=3
372 if (PyUnicode_Check(key)) {
373 #if PY_MINOR_VERSION >=3
374 match_key = PyUnicode_AsUTF8AndSize(key, &match_key_len);
377 temp2 = PyUnicode_AsUTF8String(key);
378 PyBytes_AsStringAndSize(temp2, &match_key, &match_key_len);
381 }else if (PyBytes_Check(key)) {
382 PyBytes_AsStringAndSize(key, &match_key, &match_key_len);
384 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
386 if (PyUnicode_Check(value)) {
387 #if PY_MINOR_VERSION >=3
388 match_value = PyUnicode_AsUTF8AndSize(value, &match_value_len);
391 temp3 = PyUnicode_AsUTF8String(value);
392 PyBytes_AsStringAndSize(temp3, &match_value, &match_value_len);
395 }else if (PyBytes_Check(value)) {
396 PyBytes_AsStringAndSize(value, &match_value, &match_value_len);
398 PyErr_SetString(PyExc_TypeError, "expected bytes or string");
401 PyString_AsStringAndSize(key, &match_key, &match_key_len);
402 PyString_AsStringAndSize(value, &match_value, &match_value_len);
404 if (PyErr_Occurred())
407 match_len = match_key_len + 1 + match_value_len;
408 match = malloc(match_len);
409 memcpy(match, match_key, match_key_len);
410 memcpy(match + match_key_len, "=", 1);
411 memcpy(match + match_key_len + 1, match_value, match_value_len);
413 r = sd_journal_add_match(self->j, match, match_len);
416 PyErr_SetString(PyExc_ValueError, "Invalid match");
418 }else if (r == -ENOMEM) {
419 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
422 PyErr_SetString(PyExc_RuntimeError, "Error adding match");
430 PyDoc_STRVAR(Journal_add_disjunction__doc__,
431 "add_disjunction() -> None\n\n"
432 "Once called, all matches before and after are combined in logical\n"
435 Journal_add_disjunction(Journal *self, PyObject *args)
438 r = sd_journal_add_disjunction(self->j);
440 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
443 PyErr_SetString(PyExc_RuntimeError, "Error adding disjunction");
449 PyDoc_STRVAR(Journal_flush_matches__doc__,
450 "flush_matches() -> None\n\n"
451 "Clears all current match filters.");
453 Journal_flush_matches(Journal *self, PyObject *args)
455 sd_journal_flush_matches(self->j);
459 PyDoc_STRVAR(Journal_seek__doc__,
460 "seek(offset[, whence]) -> None\n\n"
461 "Seek through journal by `offset` number of entries. Argument\n"
462 "`whence` defines what the offset is relative to:\n"
463 "os.SEEK_SET (default) from first match in journal;\n"
464 "os.SEEK_CUR from current position in journal;\n"
465 "and os.SEEK_END is from last match in journal.");
467 Journal_seek(Journal *self, PyObject *args, PyObject *keywds)
471 static char *kwlist[] = {"offset", "whence", NULL};
473 if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist,
478 if (whence == SEEK_SET){
480 Py_BEGIN_ALLOW_THREADS
481 r = sd_journal_seek_head(self->j);
484 PyErr_SetString(PyExc_RuntimeError, "Error seeking to head");
488 arg = Py_BuildValue("(L)", offset);
489 Py_DECREF(Journal_get_next(self, arg));
492 }else if (whence == SEEK_CUR){
493 arg = Py_BuildValue("(L)", offset);
494 Py_DECREF(Journal_get_next(self, arg));
496 }else if (whence == SEEK_END){
498 Py_BEGIN_ALLOW_THREADS
499 r = sd_journal_seek_tail(self->j);
502 PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail");
505 arg = Py_BuildValue("(L)", -1LL);
506 Py_DECREF(Journal_get_next(self, arg));
509 arg = Py_BuildValue("(L)", offset);
510 Py_DECREF(Journal_get_next(self, arg));
514 PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
520 PyDoc_STRVAR(Journal_seek_realtime__doc__,
521 "seek_realtime(realtime) -> None\n\n"
522 "Seek to nearest matching journal entry to `realtime`. Argument\n"
523 "`realtime` can be an integer unix timestamp in usecs or a "
524 "datetime instance.");
526 Journal_seek_realtime(Journal *self, PyObject *args)
529 if (! PyArg_ParseTuple(args, "O", &arg))
532 uint64_t timestamp=-1LL;
533 if (PyDateTime_Check(arg)) {
536 temp = PyObject_CallMethod(arg, "strftime", "s", "%s%f");
537 #if PY_MAJOR_VERSION >=3
539 temp2 = PyUnicode_AsUTF8String(temp);
540 timestamp_str = PyBytes_AsString(temp2);
543 timestamp_str = PyString_AsString(temp);
546 timestamp = strtoull(timestamp_str, NULL, 10);
547 }else if (PyLong_Check(arg)) {
548 timestamp = PyLong_AsUnsignedLongLong(arg);
549 #if PY_MAJOR_VERSION <3
550 }else if (PyInt_Check(arg)) {
551 timestamp = PyInt_AsUnsignedLongLongMask(arg);
554 if ((int64_t) timestamp < 0LL) {
555 PyErr_SetString(PyExc_ValueError, "Time must be positive integer or datetime instance");
560 Py_BEGIN_ALLOW_THREADS
561 r = sd_journal_seek_realtime_usec(self->j, timestamp);
564 PyErr_SetString(PyExc_RuntimeError, "Error seek to time");
570 PyDoc_STRVAR(Journal_seek_monotonic__doc__,
571 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
572 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
573 "`monotonic` is an timestamp from boot in secs, or a\n"
574 "timedelta instance.\n"
575 "Argument `bootid` is a string representing which boot the\n"
576 "monotonic time is reference to. Defaults to current bootid.");
578 Journal_seek_monotonic(Journal *self, PyObject *args)
582 if (! PyArg_ParseTuple(args, "O|s", &arg, &bootid))
585 uint64_t timestamp=-1LL;
586 if PyDelta_Check(arg) {
588 temp = PyObject_CallMethod(arg, "total_seconds", NULL);
589 timestamp = (uint64_t) (PyFloat_AsDouble(temp) * 1E6);
591 }else if (PyFloat_Check(arg)) {
592 timestamp = (uint64_t) (PyFloat_AsDouble(arg) * 1E6);
593 }else if (PyLong_Check(arg)) {
594 timestamp = PyLong_AsUnsignedLongLong(arg) * (uint64_t) 1E6;
595 #if PY_MAJOR_VERSION <3
596 }else if (PyInt_Check(arg)) {
597 timestamp = PyInt_AsUnsignedLongLongMask(arg) * (uint64_t) 1E6;
602 if ((int64_t) timestamp < 0LL) {
603 PyErr_SetString(PyExc_ValueError, "Time must be positive number or timedelta instance");
610 r = sd_id128_from_string(bootid, &sd_id);
612 PyErr_SetString(PyExc_ValueError, "Invalid bootid");
615 PyErr_SetString(PyExc_RuntimeError, "Error processing bootid");
619 r = sd_id128_get_boot(&sd_id);
621 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
624 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
629 Py_BEGIN_ALLOW_THREADS
630 r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp);
633 PyErr_SetString(PyExc_RuntimeError, "Error seek to time");
639 PyDoc_STRVAR(Journal_wait__doc__,
640 "wait([timeout]) -> Change state (integer)\n\n"
641 "Waits until there is a change in the journal. Argument `timeout`\n"
642 "is the maximum number of seconds to wait before returning\n"
643 "regardless if journal has changed. If `timeout` is not given or is\n"
644 "0, then it will block forever.\n"
645 "Will return constants: NOP if no change; APPEND if new\n"
646 "entries have been added to the end of the journal; and\n"
647 "INVALIDATE if journal files have been added or removed.");
649 Journal_wait(Journal *self, PyObject *args, PyObject *keywds)
652 if (! PyArg_ParseTuple(args, "|L", &timeout))
656 if ( timeout == 0LL) {
657 Py_BEGIN_ALLOW_THREADS
658 r = sd_journal_wait(self->j, (uint64_t) -1);
661 Py_BEGIN_ALLOW_THREADS
662 r = sd_journal_wait(self->j, timeout * 1E6);
665 #if PY_MAJOR_VERSION >=3
666 return PyLong_FromLong(r);
668 return PyInt_FromLong(r);
672 PyDoc_STRVAR(Journal_seek_cursor__doc__,
673 "seek_cursor(cursor) -> None\n\n"
674 "Seeks to journal entry by given unique reference `cursor`.");
676 Journal_seek_cursor(Journal *self, PyObject *args)
679 if (! PyArg_ParseTuple(args, "s", &cursor))
683 Py_BEGIN_ALLOW_THREADS
684 r = sd_journal_seek_cursor(self->j, cursor);
687 PyErr_SetString(PyExc_ValueError, "Invalid cursor");
689 }else if (r == -ENOMEM) {
690 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
693 PyErr_SetString(PyExc_RuntimeError, "Error seeking to cursor");
700 Journal_iter(PyObject *self)
707 Journal_iternext(PyObject *self)
709 Journal *iter = (Journal *)self;
710 PyObject *dict, *arg;
711 Py_ssize_t dict_size;
713 arg = Py_BuildValue("()");
714 dict = Journal_get_next(iter, arg);
716 dict_size = PyDict_Size(dict);
717 if ((int64_t) dict_size > 0LL) {
721 PyErr_SetNone(PyExc_StopIteration);
726 #ifdef SD_JOURNAL_FOREACH_UNIQUE
727 PyDoc_STRVAR(Journal_query_unique__doc__,
728 "query_unique(field) -> a set of values\n\n"
729 "Returns a set of unique values in journal for given `field`.\n"
730 "Note this does not respect any journal matches.");
732 Journal_query_unique(Journal *self, PyObject *args)
735 if (! PyArg_ParseTuple(args, "s", &query))
739 Py_BEGIN_ALLOW_THREADS
740 r = sd_journal_query_unique(self->j, query);
743 PyErr_SetString(PyExc_ValueError, "Invalid field name");
745 } else if (r == -ENOMEM) {
746 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
749 PyErr_SetString(PyExc_RuntimeError, "Error querying journal");
755 const char *delim_ptr;
756 PyObject *value_set, *key, *value;
757 value_set = PySet_New(0);
759 #if PY_MAJOR_VERSION >=3
760 key = PyUnicode_FromString(query);
762 key = PyString_FromString(query);
765 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
766 delim_ptr = memchr(uniq, '=', uniq_len);
767 value = Journal___process_field(self, key, delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1));
768 PySet_Add(value_set, value);
774 #endif //def SD_JOURNAL_FOREACH_UNIQUE
776 PyDoc_STRVAR(Journal_log_level__doc__,
777 "log_level(level) -> None\n\n"
778 "Sets maximum log level by setting matches for PRIORITY.");
780 Journal_log_level(Journal *self, PyObject *args)
783 if (! PyArg_ParseTuple(args, "i", &level))
786 if (level < 0 || level > 7) {
787 PyErr_SetString(PyExc_ValueError, "Log level should be 0 <= level <= 7");
792 PyObject *arg, *keywds;
793 for(i = 0; i <= level; i++) {
794 sprintf(level_str, "%i", i);
795 arg = PyTuple_New(0);
796 keywds = Py_BuildValue("{s:s}", "PRIORITY", level_str);
797 Journal_add_match(self, arg, keywds);
800 if (PyErr_Occurred())
806 PyDoc_STRVAR(Journal_this_boot__doc__,
807 "this_boot() -> None\n\n"
808 "Sets match filter for the current _BOOT_ID.");
810 Journal_this_boot(Journal *self, PyObject *args)
814 r = sd_id128_get_boot(&sd_id);
816 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
819 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
824 sd_id128_to_string(sd_id, bootid);
826 PyObject *arg, *keywds;
827 arg = PyTuple_New(0);
828 keywds = Py_BuildValue("{s:s}", "_BOOT_ID", bootid);
829 Journal_add_match(self, arg, keywds);
832 if (PyErr_Occurred())
838 PyDoc_STRVAR(Journal_this_machine__doc__,
839 "this_machine() -> None\n\n"
840 "Sets match filter for the current _MACHINE_ID.");
842 Journal_this_machine(Journal *self, PyObject *args)
846 r = sd_id128_get_machine(&sd_id);
848 PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
851 PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID");
856 sd_id128_to_string(sd_id, machineid);
858 PyObject *arg, *keywds;
859 arg = PyTuple_New(0);
860 keywds = Py_BuildValue("{s:s}", "_MACHINE_ID", machineid);
861 Journal_add_match(self, arg, keywds);
864 if (PyErr_Occurred())
871 Journal_get_default_call(Journal *self, void *closure)
873 Py_INCREF(self->default_call);
874 return self->default_call;
878 Journal_set_default_call(Journal *self, PyObject *value, void *closure)
881 PyErr_SetString(PyExc_TypeError, "Cannot delete default_call");
884 if (! PyCallable_Check(value)) {
885 PyErr_SetString(PyExc_TypeError, "default_call must be callable");
888 Py_DECREF(self->default_call);
890 self->default_call = value;
896 Journal_get_call_dict(Journal *self, void *closure)
898 Py_INCREF(self->call_dict);
899 return self->call_dict;
903 Journal_set_call_dict(Journal *self, PyObject *value, void *closure)
906 PyErr_SetString(PyExc_TypeError, "Cannot delete call_dict");
909 if (! PyDict_Check(value)) {
910 PyErr_SetString(PyExc_TypeError, "call_dict must be dict type");
913 Py_DECREF(self->call_dict);
915 self->call_dict = value;
921 Journal_get_data_threshold(Journal *self, void *closure)
927 r = sd_journal_get_data_threshold(self->j, &cvalue);
929 PyErr_SetString(PyExc_RuntimeError, "Error getting data threshold");
933 #if PY_MAJOR_VERSION >=3
934 value = PyLong_FromSize_t(cvalue);
936 value = PyInt_FromSize_t(cvalue);
942 Journal_set_data_threshold(Journal *self, PyObject *value, void *closure)
945 PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold");
948 #if PY_MAJOR_VERSION >=3
949 if (! PyLong_Check(value)){
951 if (! PyInt_Check(value)){
953 PyErr_SetString(PyExc_TypeError, "Data threshold must be int");
957 #if PY_MAJOR_VERSION >=3
958 r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value));
960 r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value));
963 PyErr_SetString(PyExc_RuntimeError, "Error setting data threshold");
969 static PyGetSetDef Journal_getseters[] = {
971 (getter)Journal_get_data_threshold,
972 (setter)Journal_set_data_threshold,
976 (getter)Journal_get_call_dict,
977 (setter)Journal_set_call_dict,
978 "dictionary of calls for each field",
981 (getter)Journal_get_default_call,
982 (setter)Journal_set_default_call,
983 "default call for values for fields",
988 static PyMethodDef Journal_methods[] = {
989 {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS,
990 Journal_get_next__doc__},
991 {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS,
992 Journal_get_previous__doc__},
993 {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS,
994 Journal_add_match__doc__},
995 {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS,
996 Journal_add_disjunction__doc__},
997 {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS,
998 Journal_flush_matches__doc__},
999 {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS,
1000 Journal_seek__doc__},
1001 {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS,
1002 Journal_seek_realtime__doc__},
1003 {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS,
1004 Journal_seek_monotonic__doc__},
1005 {"wait", (PyCFunction)Journal_wait, METH_VARARGS,
1006 Journal_wait__doc__},
1007 {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS,
1008 Journal_seek_cursor__doc__},
1009 #ifdef SD_JOURNAL_FOREACH_UNIQUE
1010 {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS,
1011 Journal_query_unique__doc__},
1013 {"log_level", (PyCFunction)Journal_log_level, METH_VARARGS,
1014 Journal_log_level__doc__},
1015 {"this_boot", (PyCFunction)Journal_this_boot, METH_NOARGS,
1016 Journal_this_boot__doc__},
1017 {"this_machine", (PyCFunction)Journal_this_machine, METH_NOARGS,
1018 Journal_this_machine__doc__},
1019 {NULL} /* Sentinel */
1022 static PyTypeObject JournalType = {
1023 PyVarObject_HEAD_INIT(NULL, 0)
1024 "_reader.Journal", /*tp_name*/
1025 sizeof(Journal), /*tp_basicsize*/
1027 (destructor)Journal_dealloc, /*tp_dealloc*/
1034 0, /*tp_as_sequence*/
1035 0, /*tp_as_mapping*/
1042 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
1043 Journal__doc__, /* tp_doc */
1044 0, /* tp_traverse */
1046 0, /* tp_richcompare */
1047 0, /* tp_weaklistoffset */
1048 Journal_iter, /* tp_iter */
1049 Journal_iternext, /* tp_iternext */
1050 Journal_methods, /* tp_methods */
1052 Journal_getseters, /* tp_getset */
1055 0, /* tp_descr_get */
1056 0, /* tp_descr_set */
1057 0, /* tp_dictoffset */
1058 (initproc)Journal_init, /* tp_init */
1060 Journal_new, /* tp_new */
1063 #if PY_MAJOR_VERSION >= 3
1064 static PyModuleDef _reader_module = {
1065 PyModuleDef_HEAD_INIT,
1067 "Module that reads systemd journal similar to journalctl.",
1069 NULL, NULL, NULL, NULL, NULL
1074 #if PY_MAJOR_VERSION >= 3
1075 PyInit__reader(void)
1084 if (PyType_Ready(&JournalType) < 0)
1085 #if PY_MAJOR_VERSION >= 3
1091 #if PY_MAJOR_VERSION >= 3
1092 m = PyModule_Create(&_reader_module);
1096 m = Py_InitModule3("_reader", NULL,
1097 "Module that reads systemd journal similar to journalctl.");
1102 Py_INCREF(&JournalType);
1103 PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType);
1104 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP);
1105 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND);
1106 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE);
1107 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY);
1108 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY);
1109 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY);
1111 #if PY_MAJOR_VERSION >= 3