+
+PyDoc_STRVAR(Reader_process__doc__,
+ "process() -> state change (integer)\n\n"
+ "Process events and reset the readable state of the file\n"
+ "descriptor returned by .fileno().\n\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.\n\n"
+ "See man:sd_journal_process(3) for further discussion.");
+static PyObject* Reader_process(Reader *self, PyObject *args) {
+ int r;
+
+ assert(!args);
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_process(self->j);
+ Py_END_ALLOW_THREADS
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return long_FromLong(r);
+}
+
+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 microseconds to wait before returning\n"
+ "regardless of wheter the journal has changed. If `timeout` is -1,\n"
+ "then block forever.\n\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.\n\n"
+ "See man:sd_journal_wait(3) for further discussion.");
+static PyObject* Reader_wait(Reader *self, PyObject *args) {
+ int r;
+ int64_t timeout;
+
+ if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_wait(self->j, timeout);
+ Py_END_ALLOW_THREADS
+
+ if (set_error(r, NULL, NULL) < 0)
+ 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:seek_cursor", &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") < 0)
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Reader_get_cursor__doc__,
+ "get_cursor() -> str\n\n"
+ "Return a cursor string for the current journal entry.\n\n"
+ "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
+static PyObject* Reader_get_cursor(Reader *self, PyObject *args) {
+ _cleanup_free_ char *cursor = NULL;
+ int r;
+
+ assert(self);
+ assert(!args);
+
+ r = sd_journal_get_cursor(self->j, &cursor);
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return unicode_FromString(cursor);
+}
+
+PyDoc_STRVAR(Reader_test_cursor__doc__,
+ "test_cursor(str) -> bool\n\n"
+ "Test whether the cursor string matches current journal entry.\n\n"
+ "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
+static PyObject* Reader_test_cursor(Reader *self, PyObject *args) {
+ const char *cursor;
+ int r;
+
+ assert(self);
+ assert(args);
+
+ if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
+ return NULL;
+
+ r = sd_journal_test_cursor(self->j, cursor);
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return PyBool_FromLong(r);
+}
+
+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_unique", &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") < 0)
+ 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(Reader_get_catalog__doc__,
+ "get_catalog() -> str\n\n"
+ "Retrieve a message catalog entry for the current journal entry.\n"
+ "Will throw IndexError if the entry has no MESSAGE_ID\n"
+ "and KeyError is the id is specified, but hasn't been found\n"
+ "in the catalog.\n\n"
+ "Wraps man:sd_journal_get_catalog(3).");
+static PyObject* Reader_get_catalog(Reader *self, PyObject *args) {
+ int r;
+ _cleanup_free_ char *msg = NULL;
+
+ assert(self);
+ assert(!args);
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_get_catalog(self->j, &msg);
+ Py_END_ALLOW_THREADS
+
+ if (r == -ENOENT) {
+ const void* mid;
+ size_t mid_len;
+
+ r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
+ if (r == 0) {
+ const size_t l = sizeof("MESSAGE_ID");
+ assert(mid_len > l);
+ PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l),
+ (const char*) mid + l);
+ } else if (r == -ENOENT)
+ PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
+ else
+ set_error(r, NULL, NULL);
+ return NULL;
+ }
+
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return unicode_FromString(msg);
+}
+
+PyDoc_STRVAR(get_catalog__doc__,
+ "get_catalog(id128) -> str\n\n"
+ "Retrieve a message catalog entry for the given id.\n"
+ "Wraps man:sd_journal_get_catalog_for_message_id(3).");
+static PyObject* get_catalog(PyObject *self, PyObject *args) {
+ int r;
+ char *id_ = NULL;
+ sd_id128_t id;
+ _cleanup_free_ char *msg = NULL;
+
+ assert(args);
+
+ if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
+ return NULL;
+
+ r = sd_id128_from_string(id_, &id);
+ if (set_error(r, NULL, "Invalid id128") < 0)
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_get_catalog_for_message_id(id, &msg);
+ Py_END_ALLOW_THREADS
+
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ return unicode_FromString(msg);
+}
+
+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) < 0)
+ 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);
+}
+
+PyDoc_STRVAR(closed__doc__,
+ "True iff journal is closed");
+static PyObject* Reader_get_closed(Reader *self, void *closure) {
+ return PyBool_FromLong(self->j == NULL);
+}
+
+static PyGetSetDef Reader_getsetters[] = {
+ { (char*) "data_threshold",
+ (getter) Reader_get_data_threshold,
+ (setter) Reader_set_data_threshold,
+ (char*) data_threshold__doc__,
+ NULL },
+ { (char*) "closed",
+ (getter) Reader_get_closed,
+ NULL,
+ (char*) closed__doc__,
+ NULL },
+ {} /* Sentinel */