1 /*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Steven Hiscocks, Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
23 #include <structmember.h>
27 #include <systemd/sd-journal.h>
37 static PyTypeObject ReaderType;
39 static int set_error(int r, const char* path, const char* invalid_message) {
42 if (r == -EINVAL && invalid_message)
43 PyErr_SetString(PyExc_ValueError, invalid_message);
44 else if (r == -ENOMEM)
45 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
48 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
54 PyDoc_STRVAR(module__doc__,
55 "Class to reads the systemd journal similar to journalctl.");
58 #if PY_MAJOR_VERSION >= 3
59 static PyTypeObject MonotonicType;
61 PyDoc_STRVAR(MonotonicType__doc__,
62 "A tuple of (timestamp, bootid) for holding monotonic timestamps");
64 static PyStructSequence_Field MonotonicType_fields[] = {
65 {(char*) "timestamp", (char*) "Time"},
66 {(char*) "bootid", (char*) "Unique identifier of the boot"},
70 static PyStructSequence_Desc Monotonic_desc = {
71 (char*) "journal.Monotonic",
78 static void Reader_dealloc(Reader* self)
80 sd_journal_close(self->j);
81 Py_TYPE(self)->tp_free((PyObject*)self);
84 PyDoc_STRVAR(Reader__doc__,
85 "Reader([flags | path]) -> ...\n\n"
86 "Reader allows filtering and retrieval of Journal entries.\n"
87 "Note: this is a low-level interface, and probably not what you\n"
88 "want, use systemd.journal.Reader instead.\n\n"
89 "Argument `flags` sets open flags of the journal, which can be one\n"
90 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
91 "journal on local machine only; RUNTIME_ONLY opens only\n"
92 "volatile journal files; and SYSTEM_ONLY opens only\n"
93 "journal files of system services and the kernel.\n\n"
94 "Argument `path` is the directory of journal files. Note that\n"
95 "`flags` and `path` are exclusive.\n");
96 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
101 static const char* const kwlist[] = {"flags", "path", NULL};
102 if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
107 flags = SD_JOURNAL_LOCAL_ONLY;
110 PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
114 Py_BEGIN_ALLOW_THREADS
116 r = sd_journal_open_directory(&self->j, path, 0);
118 r = sd_journal_open(&self->j, flags);
121 return set_error(r, path, "Invalid flags or path");
124 PyDoc_STRVAR(Reader_fileno__doc__,
125 "fileno() -> int\n\n"
126 "Get a file descriptor to poll for changes in the journal.\n"
127 "This method invokes sd_journal_get_fd().\n"
128 "See man:sd_journal_get_fd(3).");
129 static PyObject* Reader_fileno(Reader *self, PyObject *args)
132 r = sd_journal_get_fd(self->j);
133 set_error(r, NULL, NULL);
136 return long_FromLong(r);
139 PyDoc_STRVAR(Reader_reliable_fd__doc__,
140 "reliable_fd() -> bool\n\n"
141 "Returns True iff the journal can be polled reliably.\n"
142 "This method invokes sd_journal_reliable_fd().\n"
143 "See man:sd_journal_reliable_fd(3).");
144 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
147 r = sd_journal_reliable_fd(self->j);
148 set_error(r, NULL, NULL);
151 return PyBool_FromLong(r);
154 PyDoc_STRVAR(Reader_close__doc__,
155 "close() -> None\n\n"
156 "Free resources allocated by this Reader object.\n"
157 "This method invokes sd_journal_close().\n"
158 "See man:sd_journal_close(3).");
159 static PyObject* Reader_close(Reader *self, PyObject *args)
164 sd_journal_close(self->j);
169 PyDoc_STRVAR(Reader___enter____doc__,
170 "__enter__() -> self\n\n"
171 "Part of the context manager protocol.\n"
173 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
182 PyDoc_STRVAR(Reader___exit____doc__,
183 "__exit__(type, value, traceback) -> None\n\n"
184 "Part of the context manager protocol.\n"
185 "Closes the journal.\n");
186 static PyObject* Reader___exit__(Reader *self, PyObject *args)
190 sd_journal_close(self->j);
195 PyDoc_STRVAR(Reader_get_next__doc__,
196 "get_next([skip]) -> dict\n\n"
197 "Return dictionary of the next log entry. Optional skip value will\n"
198 "return the `skip`\\-th log entry.");
199 static PyObject* Reader_get_next(Reader *self, PyObject *args)
207 if (!PyArg_ParseTuple(args, "|L", &skip))
211 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
215 Py_BEGIN_ALLOW_THREADS
217 r = sd_journal_next(self->j);
218 else if (skip == -1LL)
219 r = sd_journal_previous(self->j);
221 r = sd_journal_next_skip(self->j, skip);
222 else if (skip < -1LL)
223 r = sd_journal_previous_skip(self->j, -skip);
225 assert_not_reached("should not be here");
228 set_error(r, NULL, NULL);
231 else if (r == 0) /* EOF */
238 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
239 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
240 const char *delim_ptr;
242 delim_ptr = memchr(msg, '=', msg_len);
244 PyErr_SetString(PyExc_OSError,
245 "journal gave us a field without '='");
249 key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
253 value = PyBytes_FromStringAndSize(
255 (const char*) msg + msg_len - (delim_ptr + 1) );
259 if (PyDict_Contains(dict, key)) {
260 PyObject *cur_value = PyDict_GetItem(dict, key);
262 if (PyList_CheckExact(cur_value)) {
263 r = PyList_Append(cur_value, value);
267 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
271 r = PyList_Append(tmp_list, cur_value);
275 r = PyList_Append(tmp_list, value);
279 r = PyDict_SetItem(dict, key, tmp_list);
284 r = PyDict_SetItem(dict, key, value);
291 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
294 r = sd_journal_get_realtime_usec(self->j, &realtime);
295 if (set_error(r, NULL, NULL))
298 key = unicode_FromString("__REALTIME_TIMESTAMP");
302 assert_cc(sizeof(unsigned long long) == sizeof(realtime));
303 value = PyLong_FromUnsignedLongLong(realtime);
307 if (PyDict_SetItem(dict, key, value))
312 PyObject _cleanup_Py_DECREF_
313 *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
317 r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
318 if (set_error(r, NULL, NULL))
321 assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
322 key = unicode_FromString("__MONOTONIC_TIMESTAMP");
323 timestamp = PyLong_FromUnsignedLongLong(monotonic);
324 bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
325 #if PY_MAJOR_VERSION >= 3
326 value = PyStructSequence_New(&MonotonicType);
328 value = PyTuple_New(2);
330 if (!key || !timestamp || !bytes || !value)
333 Py_INCREF(timestamp);
336 #if PY_MAJOR_VERSION >= 3
337 PyStructSequence_SET_ITEM(value, 0, timestamp);
338 PyStructSequence_SET_ITEM(value, 1, bytes);
340 PyTuple_SET_ITEM(value, 0, timestamp);
341 PyTuple_SET_ITEM(value, 1, bytes);
344 if (PyDict_SetItem(dict, key, value))
349 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
350 char _cleanup_free_ *cursor = NULL;
352 r = sd_journal_get_cursor(self->j, &cursor);
353 if (set_error(r, NULL, NULL))
356 key = unicode_FromString("__CURSOR");
360 value = PyBytes_FromString(cursor);
364 if (PyDict_SetItem(dict, key, value))
374 PyDoc_STRVAR(Reader_get_previous__doc__,
375 "get_previous([skip]) -> dict\n\n"
376 "Return dictionary of the previous log entry. Optional skip value\n"
377 "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
378 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
381 if (!PyArg_ParseTuple(args, "|L", &skip))
384 return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
388 PyDoc_STRVAR(Reader_add_match__doc__,
389 "add_match(match) -> None\n\n"
390 "Add a match to filter journal log entries. All matches of different\n"
391 "fields are combined with logical AND, and matches of the same field\n"
392 "are automatically combined with logical OR.\n"
393 "Match is a string of the form \"FIELD=value\".");
394 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
398 if (!PyArg_ParseTuple(args, "s#", &match, &match_len))
401 r = sd_journal_add_match(self->j, match, match_len);
402 set_error(r, NULL, "Invalid match");
409 PyDoc_STRVAR(Reader_add_disjunction__doc__,
410 "add_disjunction() -> None\n\n"
411 "Inserts a logical OR between matches added before and afterwards.");
412 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
415 r = sd_journal_add_disjunction(self->j);
416 set_error(r, NULL, NULL);
422 PyDoc_STRVAR(Reader_flush_matches__doc__,
423 "flush_matches() -> None\n\n"
424 "Clear all current match filters.");
425 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
427 sd_journal_flush_matches(self->j);
431 PyDoc_STRVAR(Reader_seek_head__doc__,
432 "seek_head() -> None\n\n"
433 "Jump to the beginning of the journal.\n"
434 "This method invokes sd_journal_seek_head().\n"
435 "See man:sd_journal_seek_head(3).");
436 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
439 Py_BEGIN_ALLOW_THREADS
440 r = sd_journal_seek_head(self->j);
442 if (set_error(r, NULL, NULL))
447 PyDoc_STRVAR(Reader_seek_tail__doc__,
448 "seek_tail() -> None\n\n"
449 "Jump to the end of the journal.\n"
450 "This method invokes sd_journal_seek_tail().\n"
451 "See man:sd_journal_seek_tail(3).");
452 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
455 Py_BEGIN_ALLOW_THREADS
456 r = sd_journal_seek_tail(self->j);
458 if (set_error(r, NULL, NULL))
463 PyDoc_STRVAR(Reader_seek_realtime__doc__,
464 "seek_realtime(realtime) -> None\n\n"
465 "Seek to nearest matching journal entry to `realtime`. Argument\n"
466 "`realtime` can must be an integer unix timestamp.");
467 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
473 if (!PyArg_ParseTuple(args, "d", &timedouble))
476 timestamp = (uint64_t) (timedouble * 1.0E6);
477 if ((int64_t) timestamp < 0LL) {
478 PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
482 Py_BEGIN_ALLOW_THREADS
483 r = sd_journal_seek_realtime_usec(self->j, timestamp);
485 if (set_error(r, NULL, NULL))
490 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
491 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
492 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
493 "`monotonic` is an timestamp from boot in seconds.\n"
494 "Argument `bootid` is a string representing which boot the\n"
495 "monotonic time is reference to. Defaults to current bootid.");
496 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
504 if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
507 timestamp = (uint64_t) (timedouble * 1.0E6);
509 if ((int64_t) timestamp < 0LL) {
510 PyErr_SetString(PyExc_ValueError, "Time must be positive number");
515 r = sd_id128_from_string(bootid, &id);
516 if (set_error(r, NULL, "Invalid bootid"))
519 Py_BEGIN_ALLOW_THREADS
520 r = sd_id128_get_boot(&id);
522 if (set_error(r, NULL, NULL))
526 Py_BEGIN_ALLOW_THREADS
527 r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
529 if (set_error(r, NULL, NULL))
534 PyDoc_STRVAR(Reader_wait__doc__,
535 "wait([timeout]) -> state change (integer)\n\n"
536 "Wait for a change in the journal. Argument `timeout` specifies\n"
537 "the maximum number of seconds to wait before returning\n"
538 "regardless of wheter the journal has changed. If `timeout` is not given\n"
539 "or is 0, then block forever.\n"
540 "Will return constants: NOP if no change; APPEND if new\n"
541 "entries have been added to the end of the journal; and\n"
542 "INVALIDATE if journal files have been added or removed.");
543 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
546 int64_t timeout = 0LL;
548 if (!PyArg_ParseTuple(args, "|L", &timeout))
551 Py_BEGIN_ALLOW_THREADS
552 r = sd_journal_wait(self->j,
553 timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
555 if (set_error(r, NULL, NULL) < 0)
558 return long_FromLong(r);
561 PyDoc_STRVAR(Reader_seek_cursor__doc__,
562 "seek_cursor(cursor) -> None\n\n"
563 "Seek to journal entry by given unique reference `cursor`.");
564 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
569 if (!PyArg_ParseTuple(args, "s", &cursor))
572 Py_BEGIN_ALLOW_THREADS
573 r = sd_journal_seek_cursor(self->j, cursor);
575 if (set_error(r, NULL, "Invalid cursor"))
580 static PyObject* Reader_iter(PyObject *self)
586 static PyObject* Reader_iternext(PyObject *self)
589 Py_ssize_t dict_size;
591 dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
592 if (PyErr_Occurred())
594 dict_size = PyDict_Size(dict);
595 if ((int64_t) dict_size > 0LL) {
599 PyErr_SetNone(PyExc_StopIteration);
604 PyDoc_STRVAR(Reader_query_unique__doc__,
605 "query_unique(field) -> a set of values\n\n"
606 "Return a set of unique values appearing in journal for the\n"
607 "given `field`. Note this does not respect any journal matches.");
608 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
614 PyObject *value_set, *key, *value;
616 if (!PyArg_ParseTuple(args, "s", &query))
619 Py_BEGIN_ALLOW_THREADS
620 r = sd_journal_query_unique(self->j, query);
622 if (set_error(r, NULL, "Invalid field name"))
625 value_set = PySet_New(0);
626 key = unicode_FromString(query);
628 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
629 const char *delim_ptr;
631 delim_ptr = memchr(uniq, '=', uniq_len);
632 value = PyBytes_FromStringAndSize(
634 (const char*) uniq + uniq_len - (delim_ptr + 1));
635 PySet_Add(value_set, value);
643 PyDoc_STRVAR(Reader_get_catalog__doc__,
644 "get_catalog() -> str\n\n"
645 "Retrieve a message catalog entry for the current journal entry.\n"
646 "Wraps man:sd_journal_get_catalog(3).");
647 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
650 char _cleanup_free_ *msg = NULL;
655 Py_BEGIN_ALLOW_THREADS
656 r = sd_journal_get_catalog(self->j, &msg);
658 if (set_error(r, NULL, NULL))
661 return unicode_FromString(msg);
665 PyDoc_STRVAR(get_catalog__doc__,
666 "get_catalog(id128) -> str\n\n"
667 "Retrieve a message catalog entry for the given id.\n"
668 "Wraps man:sd_journal_get_catalog_for_message_id(3).");
669 static PyObject* get_catalog(PyObject *self, PyObject *args)
674 char _cleanup_free_ *msg = NULL;
679 if (!PyArg_ParseTuple(args, "z", &id_))
682 r = sd_id128_from_string(id_, &id);
683 if (set_error(r, NULL, "Invalid id128"))
686 Py_BEGIN_ALLOW_THREADS
687 r = sd_journal_get_catalog_for_message_id(id, &msg);
689 if (set_error(r, NULL, NULL))
692 return unicode_FromString(msg);
696 PyDoc_STRVAR(data_threshold__doc__,
697 "Threshold for field size truncation in bytes.\n\n"
698 "Fields longer than this will be truncated to the threshold size.\n"
699 "Defaults to 64Kb.");
701 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
706 r = sd_journal_get_data_threshold(self->j, &cvalue);
707 if (set_error(r, NULL, NULL))
710 return long_FromSize_t(cvalue);
713 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
717 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
720 if (!long_Check(value)){
721 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
724 r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
725 return set_error(r, NULL, NULL);
728 PyDoc_STRVAR(closed__doc__,
729 "True iff journal is closed");
730 static PyObject* Reader_get_closed(Reader *self, void *closure)
732 return PyBool_FromLong(self->j == NULL);
735 static PyGetSetDef Reader_getsetters[] = {
736 {(char*) "data_threshold",
737 (getter) Reader_get_data_threshold,
738 (setter) Reader_set_data_threshold,
739 (char*) data_threshold__doc__,
742 (getter) Reader_get_closed,
744 (char*) closed__doc__,
749 static PyMethodDef Reader_methods[] = {
750 {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
751 {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
752 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
753 {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
754 {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
755 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
756 {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
757 {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
758 {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
759 {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
760 {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
761 {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
762 {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
763 {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
764 {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
765 {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
766 {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
767 {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
768 {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
769 {NULL} /* Sentinel */
772 static PyTypeObject ReaderType = {
773 PyVarObject_HEAD_INIT(NULL, 0)
774 "_reader._Reader", /*tp_name*/
775 sizeof(Reader), /*tp_basicsize*/
777 (destructor)Reader_dealloc, /*tp_dealloc*/
784 0, /*tp_as_sequence*/
792 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
793 Reader__doc__, /* tp_doc */
796 0, /* tp_richcompare */
797 0, /* tp_weaklistoffset */
798 Reader_iter, /* tp_iter */
799 Reader_iternext, /* tp_iternext */
800 Reader_methods, /* tp_methods */
802 Reader_getsetters, /* tp_getset */
805 0, /* tp_descr_get */
806 0, /* tp_descr_set */
807 0, /* tp_dictoffset */
808 (initproc) Reader_init, /* tp_init */
810 PyType_GenericNew, /* tp_new */
813 static PyMethodDef methods[] = {
814 { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
815 { NULL, NULL, 0, NULL } /* Sentinel */
818 #if PY_MAJOR_VERSION >= 3
819 static PyModuleDef module = {
820 PyModuleDef_HEAD_INIT,
825 NULL, NULL, NULL, NULL
829 #if PY_MAJOR_VERSION >= 3
830 static bool initialized = false;
833 #pragma GCC diagnostic push
834 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
837 #if PY_MAJOR_VERSION >= 3
847 if (PyType_Ready(&ReaderType) < 0)
848 #if PY_MAJOR_VERSION >= 3
854 #if PY_MAJOR_VERSION >= 3
855 m = PyModule_Create(&module);
860 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
864 m = Py_InitModule3("_reader", methods, module__doc__);
869 Py_INCREF(&ReaderType);
870 #if PY_MAJOR_VERSION >= 3
871 Py_INCREF(&MonotonicType);
873 if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
874 #if PY_MAJOR_VERSION >= 3
875 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
877 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
878 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
879 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
880 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
881 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
882 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
883 #if PY_MAJOR_VERSION >= 3
889 #if PY_MAJOR_VERSION >= 3
894 #pragma GCC diagnostic pop