diff options
Diffstat (limited to 'src/python-systemd/_reader.c')
-rw-r--r-- | src/python-systemd/_reader.c | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c new file mode 100644 index 0000000000..7f200d53f6 --- /dev/null +++ b/src/python-systemd/_reader.c @@ -0,0 +1,774 @@ +/*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Steven Hiscocks, Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <Python.h> +#include <structmember.h> +#include <datetime.h> +#include <stdio.h> + +#include <systemd/sd-journal.h> + +#include "pyutil.h" +#include "macro.h" +#include "util.h" + +#if PY_MAJOR_VERSION >=3 +# define unicode_FromStringAndSize PyUnicode_FromStringAndSize +# define unicode_FromString PyUnicode_FromString +# define long_FromLong PyLong_FromLong +# define long_FromSize_t PyLong_FromSize_t +# define long_Check PyLong_Check +# define long_AsLong PyLong_AsLong +#else +/* Python 3 type naming convention is used */ +# define unicode_FromStringAndSize PyString_FromStringAndSize +# define unicode_FromString PyString_FromString +# define long_FromLong PyInt_FromLong +# define long_FromSize_t PyInt_FromSize_t +# define long_Check PyInt_Check +# define long_AsLong PyInt_AsLong +#endif + +typedef struct { + PyObject_HEAD + sd_journal *j; +} Reader; +static PyTypeObject ReaderType; + +static int set_error(int r, const char* path, const char* invalid_message) { + if (r >= 0) + return r; + if (r == -EINVAL && invalid_message) + PyErr_SetString(PyExc_ValueError, invalid_message); + else if (r == -ENOMEM) + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + else { + errno = -r; + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + } + return 1; +} + +#if PY_MAJOR_VERSION >= 3 +static PyTypeObject MonotonicType; + +PyDoc_STRVAR(MonotonicType__doc__, + "A tuple of (timestamp, bootid) for holding monotonic timestamps"); + +static PyStructSequence_Field MonotonicType_fields[] = { + {(char*) "timestamp", (char*) "Time"}, + {(char*) "bootid", (char*) "Unique identifier of the boot"}, + {NULL, NULL} +}; + +static PyStructSequence_Desc Monotonic_desc = { + (char*) "journal.Monotonic", + MonotonicType__doc__, + MonotonicType_fields, + 2, +}; +#endif + +static void Reader_dealloc(Reader* self) +{ + sd_journal_close(self->j); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyDoc_STRVAR(Reader__doc__, + "Reader([flags][,path]) -> ...\n\n" + "Reader allows filtering and retrieval of Journal entries.\n" + "Argument `flags` sets open flags of the journal, which can be one\n" + "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" + "journal on local machine only; RUNTIME_ONLY opens only\n" + "volatile journal files; and SYSTEM_ONLY opens only\n" + "journal files of system services and the kernel.\n" + "Argument `path` is the directory of journal files. Note that\n" + "currently flags are ignored when `path` is present as they are\n" + "not relevant."); +static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) +{ + int flags = SD_JOURNAL_LOCAL_ONLY, r; + char *path = NULL; + + static const char* const kwlist[] = {"flags", "path", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist, + &flags, &path)) + return 1; + + Py_BEGIN_ALLOW_THREADS + if (path) + r = sd_journal_open_directory(&self->j, path, 0); + else + r = sd_journal_open(&self->j, flags); + Py_END_ALLOW_THREADS + + return set_error(r, path, "Invalid flags or path"); +} + +PyDoc_STRVAR(Reader_get_next__doc__, + "get_next([skip]) -> dict\n\n" + "Return dictionary of the next log entry. Optional skip value will\n" + "return the `skip`\\-th log entry."); +static PyObject* Reader_get_next(Reader *self, PyObject *args) +{ + PyObject *dict; + const void *msg; + size_t msg_len; + int64_t skip = 1LL; + int r; + + if (!PyArg_ParseTuple(args, "|L", &skip)) + return NULL; + + if (skip == 0LL) { + PyErr_SetString(PyExc_ValueError, "skip must be nonzero"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (skip == 1LL) + r = sd_journal_next(self->j); + else if (skip == -1LL) + r = sd_journal_previous(self->j); + else if (skip > 1LL) + r = sd_journal_next_skip(self->j, skip); + else if (skip < -1LL) + r = sd_journal_previous_skip(self->j, -skip); + else + assert_not_reached("should not be here"); + Py_END_ALLOW_THREADS + + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + else if (r == 0) /* EOF */ + return PyDict_New(); + + dict = PyDict_New(); + if (!dict) + return NULL; + + SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + const char *delim_ptr; + + delim_ptr = memchr(msg, '=', msg_len); + if (!delim_ptr) { + PyErr_SetString(PyExc_OSError, + "journal gave us a field without '='"); + goto error; + } + + key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); + if (!key) + goto error; + + value = PyBytes_FromStringAndSize( + delim_ptr + 1, + (const char*) msg + msg_len - (delim_ptr + 1) ); + if (!value) + goto error; + + if (PyDict_Contains(dict, key)) { + PyObject *cur_value = PyDict_GetItem(dict, key); + + if (PyList_CheckExact(cur_value)) { + r = PyList_Append(cur_value, value); + if (r < 0) + goto error; + } else { + PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0); + if (!tmp_list) + goto error; + + r = PyList_Append(tmp_list, cur_value); + if (r < 0) + goto error; + + r = PyList_Append(tmp_list, value); + if (r < 0) + goto error; + + PyDict_SetItem(dict, key, tmp_list); + if (r < 0) + goto error; + } + } else { + r = PyDict_SetItem(dict, key, value); + if (r < 0) + goto error; + } + } + + { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + uint64_t realtime; + + r = sd_journal_get_realtime_usec(self->j, &realtime); + if (set_error(r, NULL, NULL)) + goto error; + + key = unicode_FromString("__REALTIME_TIMESTAMP"); + if (!key) + goto error; + + assert_cc(sizeof(unsigned long long) == sizeof(realtime)); + value = PyLong_FromUnsignedLongLong(realtime); + if (!value) + goto error; + + if (PyDict_SetItem(dict, key, value)) + goto error; + } + + { + PyObject _cleanup_Py_DECREF_ + *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL; + sd_id128_t id; + uint64_t monotonic; + + r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id); + if (set_error(r, NULL, NULL)) + goto error; + + assert_cc(sizeof(unsigned long long) == sizeof(monotonic)); + key = unicode_FromString("__MONOTONIC_TIMESTAMP"); + timestamp = PyLong_FromUnsignedLongLong(monotonic); + bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); +#if PY_MAJOR_VERSION >= 3 + value = PyStructSequence_New(&MonotonicType); +#else + value = PyTuple_New(2); +#endif + if (!key || !timestamp || !bytes || !value) + goto error; + + Py_INCREF(timestamp); + Py_INCREF(bytes); + +#if PY_MAJOR_VERSION >= 3 + PyStructSequence_SET_ITEM(value, 0, timestamp); + PyStructSequence_SET_ITEM(value, 1, bytes); +#else + PyTuple_SET_ITEM(value, 0, timestamp); + PyTuple_SET_ITEM(value, 1, bytes); +#endif + + if (PyDict_SetItem(dict, key, value)) + goto error; + } + + { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + char _cleanup_free_ *cursor = NULL; + + r = sd_journal_get_cursor(self->j, &cursor); + if (set_error(r, NULL, NULL)) + goto error; + + key = unicode_FromString("__CURSOR"); + if (!key) + goto error; + + value = PyBytes_FromString(cursor); + if (!value) + goto error; + + if (PyDict_SetItem(dict, key, value)) + goto error; + } + + return dict; +error: + Py_DECREF(dict); + return NULL; +} + +PyDoc_STRVAR(Reader_get_previous__doc__, + "get_previous([skip]) -> dict\n\n" + "Return dictionary of the previous log entry. Optional skip value\n" + "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip)."); +static PyObject* Reader_get_previous(Reader *self, PyObject *args) +{ + int64_t skip = 1LL; + if (!PyArg_ParseTuple(args, "|L", &skip)) + return NULL; + + return PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", -skip); +} + +PyDoc_STRVAR(Reader_add_match__doc__, + "add_match(match) -> None\n\n" + "Add a match to filter journal log entries. All matches of different\n" + "fields are combined with logical AND, and matches of the same field\n" + "are automatically combined with logical OR.\n" + "Match is a string of the form \"FIELD=value\"."); +static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) +{ + char *match; + int match_len, r; + if (!PyArg_ParseTuple(args, "s#", &match, &match_len)) + return NULL; + + r = sd_journal_add_match(self->j, match, match_len); + set_error(r, NULL, "Invalid match"); + if (r < 0) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_add_disjunction__doc__, + "add_disjunction() -> None\n\n" + "Inserts a logical OR between matches added before and afterwards."); +static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) +{ + int r; + r = sd_journal_add_disjunction(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_flush_matches__doc__, + "flush_matches() -> None\n\n" + "Clear all current match filters."); +static PyObject* Reader_flush_matches(Reader *self, PyObject *args) +{ + sd_journal_flush_matches(self->j); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_seek__doc__, + "seek(offset[, whence]) -> None\n\n" + "Jump `offset` entries in the journal. Argument\n" + "`whence` defines what the offset is relative to:\n" + "os.SEEK_SET (default) from first match in journal;\n" + "os.SEEK_CUR from current position in journal;\n" + "and os.SEEK_END is from last match in journal."); +static PyObject* Reader_seek(Reader *self, PyObject *args, PyObject *keywds) +{ + int64_t offset; + int whence = SEEK_SET; + PyObject *result = NULL; + + static const char* const kwlist[] = {"offset", "whence", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist, + &offset, &whence)) + return NULL; + + switch(whence) { + case SEEK_SET: { + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_head(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + if (offset > 0LL) + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset); + break; + } + case SEEK_CUR: + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset); + break; + case SEEK_END: { + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_tail(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset < 0LL ? offset : -1LL); + break; + } + default: + PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); + } + + Py_XDECREF(result); + if (PyErr_Occurred()) + return NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_seek_realtime__doc__, + "seek_realtime(realtime) -> None\n\n" + "Seek to nearest matching journal entry to `realtime`. Argument\n" + "`realtime` can must be an integer unix timestamp."); +static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) +{ + double timedouble; + uint64_t timestamp; + int r; + + if (!PyArg_ParseTuple(args, "d", &timedouble)) + return NULL; + + timestamp = (uint64_t) (timedouble * 1.0E6); + if ((int64_t) timestamp < 0LL) { + PyErr_SetString(PyExc_ValueError, "Time must be a positive integer"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_realtime_usec(self->j, timestamp); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_seek_monotonic__doc__, + "seek_monotonic(monotonic[, bootid]) -> None\n\n" + "Seek to nearest matching journal entry to `monotonic`. Argument\n" + "`monotonic` is an timestamp from boot in seconds.\n" + "Argument `bootid` is a string representing which boot the\n" + "monotonic time is reference to. Defaults to current bootid."); +static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) +{ + double timedouble; + char *bootid = NULL; + uint64_t timestamp; + sd_id128_t id; + int r; + + if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) + return NULL; + + timestamp = (uint64_t) (timedouble * 1.0E6); + + if ((int64_t) timestamp < 0LL) { + PyErr_SetString(PyExc_ValueError, "Time must be positive number"); + return NULL; + } + + if (bootid) { + r = sd_id128_from_string(bootid, &id); + if (set_error(r, NULL, "Invalid bootid")) + return NULL; + } else { + Py_BEGIN_ALLOW_THREADS + r = sd_id128_get_boot(&id); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader_wait__doc__, + "wait([timeout]) -> state change (integer)\n\n" + "Wait for a change in the journal. Argument `timeout` specifies\n" + "the maximum number of seconds to wait before returning\n" + "regardless of wheter the journal has changed. If `timeout` is not given\n" + "or is 0, then block forever.\n" + "Will return constants: NOP if no change; APPEND if new\n" + "entries have been added to the end of the journal; and\n" + "INVALIDATE if journal files have been added or removed."); +static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) +{ + int r; + int64_t timeout = 0LL; + + if (!PyArg_ParseTuple(args, "|L", &timeout)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_wait(self->j, timeout ==0 ? (uint64_t) -1 : timeout * 1E6); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + return long_FromLong(r); +} + +PyDoc_STRVAR(Reader_seek_cursor__doc__, + "seek_cursor(cursor) -> None\n\n" + "Seek to journal entry by given unique reference `cursor`."); +static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) +{ + const char *cursor; + int r; + + if (!PyArg_ParseTuple(args, "s", &cursor)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_cursor(self->j, cursor); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, "Invalid cursor")) + return NULL; + Py_RETURN_NONE; +} + +static PyObject* Reader_iter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyObject* Reader_iternext(PyObject *self) +{ + PyObject *dict; + Py_ssize_t dict_size; + + dict = PyObject_CallMethod(self, (char*) "get_next", (char*) ""); + if (PyErr_Occurred()) + return NULL; + dict_size = PyDict_Size(dict); + if ((int64_t) dict_size > 0LL) { + return dict; + } else { + Py_DECREF(dict); + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +PyDoc_STRVAR(Reader_query_unique__doc__, + "query_unique(field) -> a set of values\n\n" + "Return a set of unique values appearing in journal for the\n" + "given `field`. Note this does not respect any journal matches."); +static PyObject* Reader_query_unique(Reader *self, PyObject *args) +{ + char *query; + int r; + const void *uniq; + size_t uniq_len; + PyObject *value_set, *key, *value; + + if (!PyArg_ParseTuple(args, "s", &query)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_query_unique(self->j, query); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, "Invalid field name")) + return NULL; + + value_set = PySet_New(0); + key = unicode_FromString(query); + + SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { + const char *delim_ptr; + + delim_ptr = memchr(uniq, '=', uniq_len); + value = PyBytes_FromStringAndSize( + delim_ptr + 1, + (const char*) uniq + uniq_len - (delim_ptr + 1)); + PySet_Add(value_set, value); + Py_DECREF(value); + } + Py_DECREF(key); + return value_set; +} + +PyDoc_STRVAR(data_threshold__doc__, + "Threshold for field size truncation in bytes.\n\n" + "Fields longer than this will be truncated to the threshold size.\n" + "Defaults to 64Kb."); + +static PyObject* Reader_get_data_threshold(Reader *self, void *closure) +{ + size_t cvalue; + int r; + + r = sd_journal_get_data_threshold(self->j, &cvalue); + if (set_error(r, NULL, NULL)) + return NULL; + + return long_FromSize_t(cvalue); +} + +static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) +{ + int r; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); + return -1; + } + if (!long_Check(value)){ + PyErr_SetString(PyExc_TypeError, "Data threshold must be an int"); + return -1; + } + r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value)); + return set_error(r, NULL, NULL); +} + +static PyGetSetDef Reader_getseters[] = { + {(char*) "data_threshold", + (getter) Reader_get_data_threshold, + (setter) Reader_set_data_threshold, + (char*) data_threshold__doc__, + NULL}, + {NULL} +}; + +static PyMethodDef Reader_methods[] = { + {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, + {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, + {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, + {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, + {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, + {"seek", (PyCFunction) Reader_seek, METH_VARARGS | METH_KEYWORDS, Reader_seek__doc__}, + {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__}, + {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__}, + {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, + {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, + {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject ReaderType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_reader._Reader", /*tp_name*/ + sizeof(Reader), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Reader_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Reader__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + Reader_iter, /* tp_iter */ + Reader_iternext, /* tp_iternext */ + Reader_methods, /* tp_methods */ + 0, /* tp_members */ + Reader_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc) Reader_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ +}; + +#define SUMMARY \ + "Module that reads the systemd journal similar to journalctl." + +#if PY_MAJOR_VERSION >= 3 +static PyModuleDef _reader_module = { + PyModuleDef_HEAD_INIT, + "_reader", + SUMMARY, + -1, + NULL, NULL, NULL, NULL, NULL +}; +#endif + +#if PY_MAJOR_VERSION >= 3 +static bool initialized = false; +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION >= 3 +PyInit__reader(void) +#else +init_reader(void) +#endif +{ + PyObject* m; + + PyDateTime_IMPORT; + + if (PyType_Ready(&ReaderType) < 0) +#if PY_MAJOR_VERSION >= 3 + return NULL; +#else + return; +#endif + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&_reader_module); + if (m == NULL) + return NULL; + + if (!initialized) { + PyStructSequence_InitType(&MonotonicType, &Monotonic_desc); + initialized = true; + } +#else + m = Py_InitModule3("_reader", NULL, SUMMARY); + if (m == NULL) + return; +#endif + + Py_INCREF(&ReaderType); +#if PY_MAJOR_VERSION >= 3 + Py_INCREF(&MonotonicType); +#endif + if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) || +#if PY_MAJOR_VERSION >= 3 + PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) || +#endif + PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) || + PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) || + PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || + PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) || + PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) || + PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) { +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(m); + return NULL; +#endif + } + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif +} + +#pragma GCC diagnostic pop |