X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fpython-systemd%2F_reader.c;h=d1188a147a52df7de9bb12642ed74d77caf500e6;hb=bad490b0518e4fba1ac04022e0f7f7fa5c65dc76;hp=b74d2beab90e1449087ad758ebd230d08a3db2cf;hpb=1cdcd71be06a7f51012766d6b2f9e828b715f5e7;p=elogind.git diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c index b74d2beab..d1188a147 100644 --- a/src/python-systemd/_reader.c +++ b/src/python-systemd/_reader.c @@ -83,8 +83,8 @@ static void Reader_dealloc(Reader* self) } PyDoc_STRVAR(Reader__doc__, - "Reader([flags | path]) -> ...\n\n" - "Reader allows filtering and retrieval of Journal entries.\n" + "_Reader([flags | path]) -> ...\n\n" + "_Reader allows filtering and retrieval of Journal entries.\n" "Note: this is a low-level interface, and probably not what you\n" "want, use systemd.journal.Reader instead.\n\n" "Argument `flags` sets open flags of the journal, which can be one\n" @@ -93,7 +93,9 @@ PyDoc_STRVAR(Reader__doc__, "volatile journal files; and SYSTEM_ONLY opens only\n" "journal files of system services and the kernel.\n\n" "Argument `path` is the directory of journal files. Note that\n" - "`flags` and `path` are exclusive.\n"); + "`flags` and `path` are exclusive.\n\n" + "_Reader implements the context manager protocol: the journal\n" + "will be closed when exiting the block."); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { int flags = 0, r; @@ -221,19 +223,17 @@ static PyObject* Reader___exit__(Reader *self, PyObject *args) } -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) +PyDoc_STRVAR(Reader_next__doc__, + "next([skip]) -> bool\n\n" + "Go to the next log entry. Optional skip value means to go to\n" + "the `skip`\\-th log entry.\n" + "Returns False if at end of file, True otherwise."); +static PyObject* Reader_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:_Reader.get_next", &skip)) + if (!PyArg_ParseTuple(args, "|L:next", &skip)) return NULL; if (skip == 0LL) { @@ -257,7 +257,93 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) set_error(r, NULL, NULL); if (r < 0) return NULL; - else if (r == 0) /* EOF */ + return PyBool_FromLong(r); +} + + +static int extract(const char* msg, size_t msg_len, + PyObject **key, PyObject **value) { + PyObject *k = NULL, *v; + const char *delim_ptr; + + delim_ptr = memchr(msg, '=', msg_len); + if (!delim_ptr) { + PyErr_SetString(PyExc_OSError, + "journal gave us a field without '='"); + return -1; + } + + if (key) { + k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); + if (!k) + return -1; + } + + if (value) { + v = PyBytes_FromStringAndSize(delim_ptr + 1, + (const char*) msg + msg_len - (delim_ptr + 1)); + if (!v) { + Py_XDECREF(k); + return -1; + } + + *value = v; + } + + if (key) + *key = k; + + return 0; +} + +PyDoc_STRVAR(Reader_get__doc__, + "get(str) -> str\n\n" + "Return data associated with this key in current log entry.\n" + "Throws KeyError is the data is not available."); +static PyObject* Reader_get(Reader *self, PyObject *args) +{ + const char* field; + const void* msg; + size_t msg_len; + PyObject *value; + int r; + + assert(self); + assert(args); + + if (!PyArg_ParseTuple(args, "s:get", &field)) + return NULL; + + r = sd_journal_get_data(self->j, field, &msg, &msg_len); + if (r == -ENOENT) { + PyErr_SetString(PyExc_KeyError, field); + return NULL; + } else if (set_error(r, NULL, "field name is not valid")) + return NULL; + + r = extract(msg, msg_len, NULL, &value); + if (r < 0) + return NULL; + return value; +} + + +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. Returns an empty dict on EOF."); +static PyObject* Reader_get_next(Reader *self, PyObject *args) +{ + PyObject _cleanup_Py_DECREF_ *tmp = NULL; + PyObject *dict; + const void *msg; + size_t msg_len; + int r; + + tmp = Reader_next(self, args); + if (!tmp) + return NULL; + if (tmp == Py_False) /* EOF */ return PyDict_New(); dict = PyDict_New(); @@ -266,23 +352,9 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) 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) + r = extract(msg, msg_len, &key, &value); + if (r < 0) goto error; if (PyDict_Contains(dict, key)) { @@ -316,68 +388,81 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) } } - { - PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; - uint64_t realtime; + return dict; - r = sd_journal_get_realtime_usec(self->j, &realtime); - if (set_error(r, NULL, NULL)) - goto error; +error: + Py_DECREF(dict); + return NULL; +} - 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; +PyDoc_STRVAR(Reader_get_realtime__doc__, + "get_realtime() -> int\n\n" + "Return the realtime timestamp for the current journal entry\n" + "in microseconds.\n\n" + "Wraps sd_journal_get_realtime_usec().\n" + "See man:sd_journal_get_realtime_usec(3)."); +static PyObject* Reader_get_realtime(Reader *self, PyObject *args) +{ + uint64_t timestamp; + int r; - if (PyDict_SetItem(dict, key, value)) - goto error; - } + assert(self); + assert(!args); - { - PyObject _cleanup_Py_DECREF_ - *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL; - sd_id128_t id; - uint64_t monotonic; + r = sd_journal_get_realtime_usec(self->j, ×tamp); + if (set_error(r, NULL, NULL)) + return NULL; - 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(timestamp)); + return PyLong_FromUnsignedLongLong(timestamp); +} + + +PyDoc_STRVAR(Reader_get_monotonic__doc__, + "get_monotonic() -> (timestamp, bootid)\n\n" + "Return the monotonic timestamp for the current journal entry\n" + "as a tuple of time in microseconds and bootid.\n\n" + "Wraps sd_journal_get_monotonic_usec().\n" + "See man:sd_journal_get_monotonic_usec(3)."); +static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) +{ + uint64_t timestamp; + sd_id128_t id; + PyObject *monotonic, *bootid, *tuple; + int r; + + assert(self); + assert(!args); + + r = sd_journal_get_monotonic_usec(self->j, ×tamp, &id); + if (set_error(r, NULL, NULL)) + return NULL; - 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)); + assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); + monotonic = PyLong_FromUnsignedLongLong(timestamp); + bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); #if PY_MAJOR_VERSION >= 3 - value = PyStructSequence_New(&MonotonicType); + tuple = PyStructSequence_New(&MonotonicType); #else - value = PyTuple_New(2); + tuple = PyTuple_New(2); #endif - if (!key || !timestamp || !bytes || !value) - goto error; - - Py_INCREF(timestamp); - Py_INCREF(bytes); + if (!monotonic || !bootid || !tuple) { + Py_XDECREF(monotonic); + Py_XDECREF(bootid); + Py_XDECREF(tuple); + return NULL; + } #if PY_MAJOR_VERSION >= 3 - PyStructSequence_SET_ITEM(value, 0, timestamp); - PyStructSequence_SET_ITEM(value, 1, bytes); + PyStructSequence_SET_ITEM(tuple, 0, monotonic); + PyStructSequence_SET_ITEM(tuple, 1, bootid); #else - PyTuple_SET_ITEM(value, 0, timestamp); - PyTuple_SET_ITEM(value, 1, bytes); + PyTuple_SET_ITEM(tuple, 0, monotonic); + PyTuple_SET_ITEM(tuple, 1, bootid); #endif - if (PyDict_SetItem(dict, key, value)) - goto error; - } - - return dict; -error: - Py_DECREF(dict); - return NULL; + return tuple; } @@ -388,7 +473,7 @@ PyDoc_STRVAR(Reader_get_previous__doc__, static PyObject* Reader_get_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip)) + if (!PyArg_ParseTuple(args, "|L:get_previous", &skip)) return NULL; return PyObject_CallMethod((PyObject *)self, (char*) "get_next", @@ -406,7 +491,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds { char *match; int match_len, r; - if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len)) + if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) return NULL; r = sd_journal_add_match(self->j, match, match_len); @@ -570,7 +655,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) const char *cursor; int r; - if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor)) + if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -614,7 +699,7 @@ static PyObject* Reader_test_cursor(Reader *self, PyObject *args) assert(self); assert(args); - if (!PyArg_ParseTuple(args, "s:_Reader.get_cursor", &cursor)) + if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor)) return NULL; r = sd_journal_test_cursor(self->j, cursor); @@ -663,7 +748,7 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) size_t uniq_len; PyObject *value_set, *key, *value; - if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query)) + if (!PyArg_ParseTuple(args, "s:query_unique", &query)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -693,6 +778,9 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) 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) { @@ -705,7 +793,22 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_get_catalog(self->j, &msg); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + 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 int 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; + } else if (set_error(r, NULL, NULL)) return NULL; return unicode_FromString(msg); @@ -805,8 +908,12 @@ static PyMethodDef Reader_methods[] = { {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, + {"next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, + {"get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, + {"get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, + {"get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__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__}, @@ -865,7 +972,7 @@ static PyTypeObject ReaderType = { }; static PyMethodDef methods[] = { - { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, + { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, { NULL, NULL, 0, NULL } /* Sentinel */ };