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",
79 static void Reader_dealloc(Reader* self)
81 sd_journal_close(self->j);
82 Py_TYPE(self)->tp_free((PyObject*)self);
85 PyDoc_STRVAR(Reader__doc__,
86 "Reader([flags | path]) -> ...\n\n"
87 "Reader allows filtering and retrieval of Journal entries.\n"
88 "Note: this is a low-level interface, and probably not what you\n"
89 "want, use systemd.journal.Reader instead.\n\n"
90 "Argument `flags` sets open flags of the journal, which can be one\n"
91 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
92 "journal on local machine only; RUNTIME_ONLY opens only\n"
93 "volatile journal files; and SYSTEM_ONLY opens only\n"
94 "journal files of system services and the kernel.\n\n"
95 "Argument `path` is the directory of journal files. Note that\n"
96 "`flags` and `path` are exclusive.\n");
97 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
102 static const char* const kwlist[] = {"flags", "path", NULL};
103 if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
108 flags = SD_JOURNAL_LOCAL_ONLY;
111 PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
115 Py_BEGIN_ALLOW_THREADS
117 r = sd_journal_open_directory(&self->j, path, 0);
119 r = sd_journal_open(&self->j, flags);
122 return set_error(r, path, "Invalid flags or path");
126 PyDoc_STRVAR(Reader_fileno__doc__,
127 "fileno() -> int\n\n"
128 "Get a file descriptor to poll for changes in the journal.\n"
129 "This method invokes sd_journal_get_fd().\n"
130 "See man:sd_journal_get_fd(3).");
131 static PyObject* Reader_fileno(Reader *self, PyObject *args)
134 r = sd_journal_get_fd(self->j);
135 set_error(r, NULL, NULL);
138 return long_FromLong(r);
142 PyDoc_STRVAR(Reader_reliable_fd__doc__,
143 "reliable_fd() -> bool\n\n"
144 "Returns True iff the journal can be polled reliably.\n"
145 "This method invokes sd_journal_reliable_fd().\n"
146 "See man:sd_journal_reliable_fd(3).");
147 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
150 r = sd_journal_reliable_fd(self->j);
151 set_error(r, NULL, NULL);
154 return PyBool_FromLong(r);
158 PyDoc_STRVAR(Reader_close__doc__,
159 "close() -> None\n\n"
160 "Free resources allocated by this Reader object.\n"
161 "This method invokes sd_journal_close().\n"
162 "See man:sd_journal_close(3).");
163 static PyObject* Reader_close(Reader *self, PyObject *args)
168 sd_journal_close(self->j);
174 PyDoc_STRVAR(Reader___enter____doc__,
175 "__enter__() -> self\n\n"
176 "Part of the context manager protocol.\n"
178 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
187 PyDoc_STRVAR(Reader___exit____doc__,
188 "__exit__(type, value, traceback) -> None\n\n"
189 "Part of the context manager protocol.\n"
190 "Closes the journal.\n");
191 static PyObject* Reader___exit__(Reader *self, PyObject *args)
195 sd_journal_close(self->j);
201 PyDoc_STRVAR(Reader_get_next__doc__,
202 "get_next([skip]) -> dict\n\n"
203 "Return dictionary of the next log entry. Optional skip value will\n"
204 "return the `skip`\\-th log entry.");
205 static PyObject* Reader_get_next(Reader *self, PyObject *args)
213 if (!PyArg_ParseTuple(args, "|L:_Reader.get_next", &skip))
217 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
221 Py_BEGIN_ALLOW_THREADS
223 r = sd_journal_next(self->j);
224 else if (skip == -1LL)
225 r = sd_journal_previous(self->j);
227 r = sd_journal_next_skip(self->j, skip);
228 else if (skip < -1LL)
229 r = sd_journal_previous_skip(self->j, -skip);
231 assert_not_reached("should not be here");
234 set_error(r, NULL, NULL);
237 else if (r == 0) /* EOF */
244 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
245 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
246 const char *delim_ptr;
248 delim_ptr = memchr(msg, '=', msg_len);
250 PyErr_SetString(PyExc_OSError,
251 "journal gave us a field without '='");
255 key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
259 value = PyBytes_FromStringAndSize(
261 (const char*) msg + msg_len - (delim_ptr + 1) );
265 if (PyDict_Contains(dict, key)) {
266 PyObject *cur_value = PyDict_GetItem(dict, key);
268 if (PyList_CheckExact(cur_value)) {
269 r = PyList_Append(cur_value, value);
273 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
277 r = PyList_Append(tmp_list, cur_value);
281 r = PyList_Append(tmp_list, value);
285 r = PyDict_SetItem(dict, key, tmp_list);
290 r = PyDict_SetItem(dict, key, value);
297 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
300 r = sd_journal_get_realtime_usec(self->j, &realtime);
301 if (set_error(r, NULL, NULL))
304 key = unicode_FromString("__REALTIME_TIMESTAMP");
308 assert_cc(sizeof(unsigned long long) == sizeof(realtime));
309 value = PyLong_FromUnsignedLongLong(realtime);
313 if (PyDict_SetItem(dict, key, value))
318 PyObject _cleanup_Py_DECREF_
319 *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
323 r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
324 if (set_error(r, NULL, NULL))
327 assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
328 key = unicode_FromString("__MONOTONIC_TIMESTAMP");
329 timestamp = PyLong_FromUnsignedLongLong(monotonic);
330 bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
331 #if PY_MAJOR_VERSION >= 3
332 value = PyStructSequence_New(&MonotonicType);
334 value = PyTuple_New(2);
336 if (!key || !timestamp || !bytes || !value)
339 Py_INCREF(timestamp);
342 #if PY_MAJOR_VERSION >= 3
343 PyStructSequence_SET_ITEM(value, 0, timestamp);
344 PyStructSequence_SET_ITEM(value, 1, bytes);
346 PyTuple_SET_ITEM(value, 0, timestamp);
347 PyTuple_SET_ITEM(value, 1, bytes);
350 if (PyDict_SetItem(dict, key, value))
355 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
356 char _cleanup_free_ *cursor = NULL;
358 r = sd_journal_get_cursor(self->j, &cursor);
359 if (set_error(r, NULL, NULL))
362 key = unicode_FromString("__CURSOR");
366 value = PyBytes_FromString(cursor);
370 if (PyDict_SetItem(dict, key, value))
381 PyDoc_STRVAR(Reader_get_previous__doc__,
382 "get_previous([skip]) -> dict\n\n"
383 "Return dictionary of the previous log entry. Optional skip value\n"
384 "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
385 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
388 if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip))
391 return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
396 PyDoc_STRVAR(Reader_add_match__doc__,
397 "add_match(match) -> None\n\n"
398 "Add a match to filter journal log entries. All matches of different\n"
399 "fields are combined with logical AND, and matches of the same field\n"
400 "are automatically combined with logical OR.\n"
401 "Match is a string of the form \"FIELD=value\".");
402 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
406 if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len))
409 r = sd_journal_add_match(self->j, match, match_len);
410 set_error(r, NULL, "Invalid match");
418 PyDoc_STRVAR(Reader_add_disjunction__doc__,
419 "add_disjunction() -> None\n\n"
420 "Inserts a logical OR between matches added before and afterwards.");
421 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
424 r = sd_journal_add_disjunction(self->j);
425 set_error(r, NULL, NULL);
432 PyDoc_STRVAR(Reader_flush_matches__doc__,
433 "flush_matches() -> None\n\n"
434 "Clear all current match filters.");
435 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
437 sd_journal_flush_matches(self->j);
442 PyDoc_STRVAR(Reader_seek_head__doc__,
443 "seek_head() -> None\n\n"
444 "Jump to the beginning of the journal.\n"
445 "This method invokes sd_journal_seek_head().\n"
446 "See man:sd_journal_seek_head(3).");
447 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
450 Py_BEGIN_ALLOW_THREADS
451 r = sd_journal_seek_head(self->j);
453 if (set_error(r, NULL, NULL))
459 PyDoc_STRVAR(Reader_seek_tail__doc__,
460 "seek_tail() -> None\n\n"
461 "Jump to the end of the journal.\n"
462 "This method invokes sd_journal_seek_tail().\n"
463 "See man:sd_journal_seek_tail(3).");
464 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
467 Py_BEGIN_ALLOW_THREADS
468 r = sd_journal_seek_tail(self->j);
470 if (set_error(r, NULL, NULL))
476 PyDoc_STRVAR(Reader_seek_realtime__doc__,
477 "seek_realtime(realtime) -> None\n\n"
478 "Seek to nearest matching journal entry to `realtime`. Argument\n"
479 "`realtime` can must be an integer unix timestamp.");
480 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
486 if (!PyArg_ParseTuple(args, "d:_Reader.seek_realtime", &timedouble))
489 timestamp = (uint64_t) (timedouble * 1.0E6);
490 if ((int64_t) timestamp < 0LL) {
491 PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
495 Py_BEGIN_ALLOW_THREADS
496 r = sd_journal_seek_realtime_usec(self->j, timestamp);
498 if (set_error(r, NULL, NULL))
504 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
505 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
506 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
507 "`monotonic` is an timestamp from boot in seconds.\n"
508 "Argument `bootid` is a string representing which boot the\n"
509 "monotonic time is reference to. Defaults to current bootid.");
510 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
518 if (!PyArg_ParseTuple(args, "d|z:_Reader.seek_monotonic", &timedouble, &bootid))
521 timestamp = (uint64_t) (timedouble * 1.0E6);
523 if ((int64_t) timestamp < 0LL) {
524 PyErr_SetString(PyExc_ValueError, "Time must be positive number");
529 r = sd_id128_from_string(bootid, &id);
530 if (set_error(r, NULL, "Invalid bootid"))
533 Py_BEGIN_ALLOW_THREADS
534 r = sd_id128_get_boot(&id);
536 if (set_error(r, NULL, NULL))
540 Py_BEGIN_ALLOW_THREADS
541 r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
543 if (set_error(r, NULL, NULL))
549 PyDoc_STRVAR(Reader_wait__doc__,
550 "wait([timeout]) -> state change (integer)\n\n"
551 "Wait for a change in the journal. Argument `timeout` specifies\n"
552 "the maximum number of seconds to wait before returning\n"
553 "regardless of wheter the journal has changed. If `timeout` is not given\n"
554 "or is 0, then block forever.\n"
555 "Will return constants: NOP if no change; APPEND if new\n"
556 "entries have been added to the end of the journal; and\n"
557 "INVALIDATE if journal files have been added or removed.");
558 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
561 int64_t timeout = 0LL;
563 if (!PyArg_ParseTuple(args, "|L:_Reader.wait", &timeout))
566 Py_BEGIN_ALLOW_THREADS
567 r = sd_journal_wait(self->j,
568 timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
570 if (set_error(r, NULL, NULL) < 0)
573 return long_FromLong(r);
577 PyDoc_STRVAR(Reader_seek_cursor__doc__,
578 "seek_cursor(cursor) -> None\n\n"
579 "Seek to journal entry by given unique reference `cursor`.");
580 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
585 if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor))
588 Py_BEGIN_ALLOW_THREADS
589 r = sd_journal_seek_cursor(self->j, cursor);
591 if (set_error(r, NULL, "Invalid cursor"))
597 static PyObject* Reader_iter(PyObject *self)
603 static PyObject* Reader_iternext(PyObject *self)
606 Py_ssize_t dict_size;
608 dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
609 if (PyErr_Occurred())
611 dict_size = PyDict_Size(dict);
612 if ((int64_t) dict_size > 0LL) {
616 PyErr_SetNone(PyExc_StopIteration);
622 PyDoc_STRVAR(Reader_query_unique__doc__,
623 "query_unique(field) -> a set of values\n\n"
624 "Return a set of unique values appearing in journal for the\n"
625 "given `field`. Note this does not respect any journal matches.");
626 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
632 PyObject *value_set, *key, *value;
634 if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query))
637 Py_BEGIN_ALLOW_THREADS
638 r = sd_journal_query_unique(self->j, query);
640 if (set_error(r, NULL, "Invalid field name"))
643 value_set = PySet_New(0);
644 key = unicode_FromString(query);
646 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
647 const char *delim_ptr;
649 delim_ptr = memchr(uniq, '=', uniq_len);
650 value = PyBytes_FromStringAndSize(
652 (const char*) uniq + uniq_len - (delim_ptr + 1));
653 PySet_Add(value_set, value);
661 PyDoc_STRVAR(Reader_get_catalog__doc__,
662 "get_catalog() -> str\n\n"
663 "Retrieve a message catalog entry for the current journal entry.\n"
664 "Wraps man:sd_journal_get_catalog(3).");
665 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
668 char _cleanup_free_ *msg = NULL;
673 Py_BEGIN_ALLOW_THREADS
674 r = sd_journal_get_catalog(self->j, &msg);
676 if (set_error(r, NULL, NULL))
679 return unicode_FromString(msg);
683 PyDoc_STRVAR(get_catalog__doc__,
684 "get_catalog(id128) -> str\n\n"
685 "Retrieve a message catalog entry for the given id.\n"
686 "Wraps man:sd_journal_get_catalog_for_message_id(3).");
687 static PyObject* get_catalog(PyObject *self, PyObject *args)
692 char _cleanup_free_ *msg = NULL;
697 if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
700 r = sd_id128_from_string(id_, &id);
701 if (set_error(r, NULL, "Invalid id128"))
704 Py_BEGIN_ALLOW_THREADS
705 r = sd_journal_get_catalog_for_message_id(id, &msg);
707 if (set_error(r, NULL, NULL))
710 return unicode_FromString(msg);
714 PyDoc_STRVAR(data_threshold__doc__,
715 "Threshold for field size truncation in bytes.\n\n"
716 "Fields longer than this will be truncated to the threshold size.\n"
717 "Defaults to 64Kb.");
719 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
724 r = sd_journal_get_data_threshold(self->j, &cvalue);
725 if (set_error(r, NULL, NULL))
728 return long_FromSize_t(cvalue);
731 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
735 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
738 if (!long_Check(value)){
739 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
742 r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
743 return set_error(r, NULL, NULL);
747 PyDoc_STRVAR(closed__doc__,
748 "True iff journal is closed");
749 static PyObject* Reader_get_closed(Reader *self, void *closure)
751 return PyBool_FromLong(self->j == NULL);
755 static PyGetSetDef Reader_getsetters[] = {
756 {(char*) "data_threshold",
757 (getter) Reader_get_data_threshold,
758 (setter) Reader_set_data_threshold,
759 (char*) data_threshold__doc__,
762 (getter) Reader_get_closed,
764 (char*) closed__doc__,
769 static PyMethodDef Reader_methods[] = {
770 {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
771 {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
772 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
773 {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
774 {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
775 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
776 {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
777 {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
778 {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
779 {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
780 {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
781 {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
782 {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
783 {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
784 {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
785 {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
786 {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
787 {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
788 {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
789 {NULL} /* Sentinel */
792 static PyTypeObject ReaderType = {
793 PyVarObject_HEAD_INIT(NULL, 0)
794 "_reader._Reader", /*tp_name*/
795 sizeof(Reader), /*tp_basicsize*/
797 (destructor)Reader_dealloc, /*tp_dealloc*/
804 0, /*tp_as_sequence*/
812 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
813 Reader__doc__, /* tp_doc */
816 0, /* tp_richcompare */
817 0, /* tp_weaklistoffset */
818 Reader_iter, /* tp_iter */
819 Reader_iternext, /* tp_iternext */
820 Reader_methods, /* tp_methods */
822 Reader_getsetters, /* tp_getset */
825 0, /* tp_descr_get */
826 0, /* tp_descr_set */
827 0, /* tp_dictoffset */
828 (initproc) Reader_init, /* tp_init */
830 PyType_GenericNew, /* tp_new */
833 static PyMethodDef methods[] = {
834 { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
835 { NULL, NULL, 0, NULL } /* Sentinel */
838 #if PY_MAJOR_VERSION >= 3
839 static PyModuleDef module = {
840 PyModuleDef_HEAD_INIT,
845 NULL, NULL, NULL, NULL
849 #if PY_MAJOR_VERSION >= 3
850 static bool initialized = false;
853 #pragma GCC diagnostic push
854 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
857 #if PY_MAJOR_VERSION >= 3
867 if (PyType_Ready(&ReaderType) < 0)
868 #if PY_MAJOR_VERSION >= 3
874 #if PY_MAJOR_VERSION >= 3
875 m = PyModule_Create(&module);
880 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
884 m = Py_InitModule3("_reader", methods, module__doc__);
889 Py_INCREF(&ReaderType);
890 #if PY_MAJOR_VERSION >= 3
891 Py_INCREF(&MonotonicType);
893 if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
894 #if PY_MAJOR_VERSION >= 3
895 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
897 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
898 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
899 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
900 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
901 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
902 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
903 #if PY_MAJOR_VERSION >= 3
909 #if PY_MAJOR_VERSION >= 3
914 #pragma GCC diagnostic pop