summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO1
-rw-r--r--src/python-systemd/_reader.c109
-rw-r--r--src/python-systemd/docs/journal.rst3
-rw-r--r--src/python-systemd/journal.py10
4 files changed, 103 insertions, 20 deletions
diff --git a/TODO b/TODO
index 8c99afcd74..c0779d4377 100644
--- a/TODO
+++ b/TODO
@@ -584,7 +584,6 @@ Features:
* drop cap bounding set in readahead and other services
* systemd-python:
- - allow reading of only select fields in systemd.journal._reader.Reader
- figure out a simple way to wait for journal events in a way that
works with ^C
- add documentation to systemd.daemon
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c
index c128cf3c14..d1188a147a 100644
--- a/src/python-systemd/_reader.c
+++ b/src/python-systemd/_reader.c
@@ -261,6 +261,73 @@ static PyObject* Reader_next(Reader *self, PyObject *args)
}
+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"
@@ -285,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)) {
@@ -336,6 +389,7 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args)
}
return dict;
+
error:
Py_DECREF(dict);
return NULL;
@@ -724,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)
{
@@ -736,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);
@@ -837,6 +909,7 @@ static PyMethodDef Reader_methods[] = {
{"__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__},
@@ -899,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 */
};
diff --git a/src/python-systemd/docs/journal.rst b/src/python-systemd/docs/journal.rst
index 9dc495ffdb..08756b99be 100644
--- a/src/python-systemd/docs/journal.rst
+++ b/src/python-systemd/docs/journal.rst
@@ -23,6 +23,9 @@ Accessing the Journal
.. automethod:: __init__
+.. autofunction:: _get_catalog
+.. autofunction:: get_catalog
+
.. autoclass:: Monotonic
.. autoattribute:: systemd.journal.DEFAULT_CONVERTERS
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index a522aecc70..c918c439b5 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -35,7 +35,7 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
from ._journal import sendv, stream_fd
from ._reader import (_Reader, NOP, APPEND, INVALIDATE,
LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY,
- get_catalog)
+ _get_catalog)
from . import id128 as _id128
if _sys.version_info >= (3,):
@@ -137,6 +137,9 @@ class Reader(_Reader):
the conversion fails with a ValueError, unconverted bytes
object will be returned. (Note that ValueEror is a superclass
of UnicodeDecodeError).
+
+ Reader implements the context manager protocol: the journal
+ will be closed when exiting the block.
"""
super(Reader, self).__init__(flags, path)
if _sys.version_info >= (3,3):
@@ -293,6 +296,11 @@ class Reader(_Reader):
self.add_match(_MACHINE_ID=machineid)
+def get_catalog(mid):
+ if isinstance(mid, _uuid.UUID):
+ mid = mid.get_hex()
+ return _get_catalog(mid)
+
def _make_line(field, value):
if isinstance(value, bytes):
return field.encode('utf-8') + b'=' + value