summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-09-18 21:58:21 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-09-21 15:32:58 -0400
commitdadcd3d723c7781ee9a3ab54341030cad10904bd (patch)
treee5ab9b986b7abb589916885df1acaed9c61a58d8
parent42f627b30236e92f0b7dd76db8069580ce8a9199 (diff)
downloadpython-systemd-dadcd3d723c7781ee9a3ab54341030cad10904bd.tar.gz
journal: allow sd_journal_open_directory_fd to be used
-rw-r--r--systemd/_reader.c157
-rw-r--r--systemd/journal.py5
-rw-r--r--systemd/test/test_journal.py16
3 files changed, 125 insertions, 53 deletions
diff --git a/systemd/_reader.c b/systemd/_reader.c
index 6aa3e90..ff9ddc4 100644
--- a/systemd/_reader.c
+++ b/systemd/_reader.c
@@ -44,6 +44,10 @@
# define HAVE_HAS_PERSISTENT_FILES
#endif
+#if LIBSYSTEMD_VERSION >= 230
+# define HAVE_JOURNAL_OPEN_DIRECTORY_FD
+#endif
+
typedef struct {
PyObject_HEAD
sd_journal *j;
@@ -75,22 +79,49 @@ static PyStructSequence_Desc Monotonic_desc = {
#endif
/**
- * Convert a Python sequence object into a strv (char**), and
- * None into a NULL pointer.
+ * Convert a str or bytes object into a C-string path.
+ * Returns NULL on error.
*/
-static int strv_converter(PyObject* obj, void *_result) {
- char ***result = _result;
- Py_ssize_t i, len;
+static char* convert_path(PyObject *path, PyObject **bytes) {
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+ int r;
+
+ r = PyUnicode_FSConverter(path, bytes);
+ if (r == 0)
+ return NULL;
+
+ return PyBytes_AsString(*bytes);
+#else
+ return PyString_AsString(path);
+#endif
+}
+
+/**
+ * Return NULL is obj is None, and the object otherwise.
+ */
+static int null_converter(PyObject* obj, void *_result) {
+ PyObject **result = _result;
assert(result);
if (!obj)
return 0;
- if (obj == Py_None) {
+ if (obj == Py_None)
*result = NULL;
- return 1;
- }
+ else
+ *result = obj;
+ return 1;
+}
+
+/**
+ * Convert a Python sequence object into a strv (char**).
+ */
+static int strv_converter(PyObject* obj, void *_result) {
+ char ***result = _result;
+ Py_ssize_t i, len;
+
+ assert(result);
if (!PySequence_Check(obj))
return 0;
@@ -104,32 +135,21 @@ static int strv_converter(PyObject* obj, void *_result) {
for (i = 0; i < len; i++) {
PyObject *item;
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
- int r;
- PyObject *bytes;
-#endif
- char *s, *s2;
+ _cleanup_Py_DECREF_ PyObject *bytes = NULL;
+ char *s;
item = PySequence_ITEM(obj, i);
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
- r = PyUnicode_FSConverter(item, &bytes);
- if (r == 0)
- goto cleanup;
-
- s = PyBytes_AsString(bytes);
-#else
- s = PyString_AsString(item);
-#endif
+ s = convert_path(item, &bytes);
if (!s)
goto cleanup;
- s2 = strdup(s);
- if (!s2) {
+ s = strdup(s);
+ if (!s) {
set_error(-ENOMEM, NULL, NULL);
goto cleanup;
}
- (*result)[i] = s2;
+ (*result)[i] = s;
}
return 1;
@@ -151,49 +171,84 @@ PyDoc_STRVAR(Reader__doc__,
"_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"
- "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 opens journal files of\n"
- "system services and the kernel, and CURRENT_USER opens files\n"
- "of the current user.\n\n"
- "Argument `path` is the directory of journal files.\n"
- "Argument `files` is a list of files. Note that\n"
- "`flags`, `path`, and `files` are exclusive.\n\n"
- "_Reader implements the context manager protocol: the journal\n"
- "will be closed when exiting the block.");
+ "Argument `flags` sets open flags of the journal, which can be one of, or an ORed\n"
+ "combination of constants: LOCAL_ONLY (default) opens journal on local machine only;\n"
+ "RUNTIME_ONLY opens only volatile journal files; and SYSTEM opens journal files of\n"
+ "system services and the kernel, and CURRENT_USER opens file of the current user.\n"
+ "\n"
+ "Instead of opening the system journal, argument `path` may specify a directory\n"
+ "which contains the journal. It maybe be either a file system path (a string), or\n"
+ "a file descriptor (an integer). Alternatively, argument `files` may specify a list\n"
+ "of journal file names. Note that `flags`, `path`, `files`, `directory_fd` are\n"
+ "exclusive.\n\n"
+ "_Reader implements the context manager protocol: the journal will be closed when\n"
+ "exiting the block.");
static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) {
int flags = 0, r;
- char *path = NULL;
- char **files = NULL;
+ PyObject *_path = NULL;
+ _cleanup_strv_free_ char **files = NULL;
static const char* const kwlist[] = {"flags", "path", "files", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist,
- &flags, &path, strv_converter, &files))
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iO&O&:__init__", (char**) kwlist,
+ &flags,
+ null_converter, &_path,
+ strv_converter, &files))
return -1;
- if (!!flags + !!path + !!files > 1) {
- PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files");
+ if (!!flags + !!_path + !!files > 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot use more than one of flags, path, and files");
return -1;
}
- if (!flags)
- flags = SD_JOURNAL_LOCAL_ONLY;
+ if (_path) {
+ if (long_Check(_path)) {
+ long directory_fd = long_AsLong(_path);
+ if (PyErr_Occurred())
+ return -1;
- Py_BEGIN_ALLOW_THREADS
- if (path)
- r = sd_journal_open_directory(&self->j, path, 0);
- else if (files) {
+ if ((int) directory_fd != directory_fd) {
+ PyErr_SetString(PyExc_OverflowError, "Value too large");
+ return -1;
+ }
+
+#ifdef HAVE_JOURNAL_OPEN_DIRECTORY_FD
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_open_directory_fd(&self->j, (int) directory_fd, 0);
+ Py_END_ALLOW_THREADS
+#else
+ r = -ENOSYS;
+#endif
+ } else {
+ char *path = NULL;
+ _cleanup_Py_DECREF_ PyObject *path_bytes = NULL;
+
+ path = convert_path(_path, &path_bytes);
+ if (!path)
+ return -1;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = sd_journal_open_directory(&self->j, path, 0);
+ Py_END_ALLOW_THREADS
+ }
+ } else if (files) {
#ifdef HAVE_JOURNAL_OPEN_FILES
+ Py_BEGIN_ALLOW_THREADS
r = sd_journal_open_files(&self->j, (const char**) files, 0);
+ Py_END_ALLOW_THREADS
#else
r = -ENOSYS;
#endif
- } else
+ } else {
+ if (!flags)
+ flags = SD_JOURNAL_LOCAL_ONLY;
+
+ Py_BEGIN_ALLOW_THREADS
r = sd_journal_open(&self->j, flags);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS
+ }
- return set_error(r, path, "Invalid flags or path");
+ return set_error(r, NULL, "Opening the journal failed");
}
PyDoc_STRVAR(Reader_fileno__doc__,
diff --git a/systemd/journal.py b/systemd/journal.py
index 9829baa..4440dec 100644
--- a/systemd/journal.py
+++ b/systemd/journal.py
@@ -139,8 +139,9 @@ class Reader(_Reader):
on local machine only; RUNTIME_ONLY opens only volatile journal files;
and SYSTEM_ONLY opens only journal files of system services and the kernel.
- Argument `path` is the directory of journal files. Note that `flags` and
- `path` are exclusive.
+ Argument `path` is the directory of journal files, either a file system
+ path or a file descriptor. Note that `flags`, `path`, and `files` are
+ exclusive.
Argument `converters` is a dictionary which updates the
DEFAULT_CONVERTERS to convert journal field values. Field names are used
diff --git a/systemd/test/test_journal.py b/systemd/test/test_journal.py
index 152fccf..4b23a70 100644
--- a/systemd/test/test_journal.py
+++ b/systemd/test/test_journal.py
@@ -1,6 +1,7 @@
import logging
import uuid
import errno
+import os
from systemd import journal, id128
import pytest
@@ -52,6 +53,21 @@ def test_reader_init_path(tmpdir):
with pytest.raises(ValueError):
journal.Reader(journal.LOCAL_ONLY, path=tmpdir.strpath)
+def test_reader_init_path_invalid_fd():
+ with pytest.raises(OSError):
+ journal.Reader(0, path=-1)
+
+def test_reader_init_path_nondirectory_fd():
+ with pytest.raises(OSError):
+ journal.Reader(0, path=0)
+
+def test_reader_init_path_fd(tmpdir):
+ fd = os.open(tmpdir.strpath, os.O_RDONLY)
+ j = journal.Reader(path=fd)
+ with pytest.raises(ValueError):
+ journal.Reader(journal.LOCAL_ONLY, path=fd)
+ assert list(j) == []
+
def test_reader_as_cm(tmpdir):
j = journal.Reader(path=tmpdir.strpath)
with j: