From 245784c42fc5d4a41afc4ab77736f84245f4138b Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 1 Apr 2025 16:33:18 +0200 Subject: [PATCH 1/4] improve thread safety of journal.Reader class by simple protection with ref-counter; avoid segfault by closing journal across threads (closes gh-143) --- systemd/_reader.c | 108 +++++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 333eb9a..3b8bbcb 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -64,6 +64,8 @@ typedef struct { PyObject_HEAD sd_journal *j; + unsigned closed; + unsigned ref_count; } Reader; static PyTypeObject ReaderType; @@ -89,6 +91,31 @@ static PyStructSequence_Desc Monotonic_desc = { 2, }; +static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + Reader *self = (Reader *)PyType_GenericNew(type, args, kwds); + self->j = NULL; + self->closed = 0; + self->ref_count = 1; /* initial reference */ + return (PyObject *)self; +} + +static inline void decr_ref_count(Reader *self) { + if (!self->ref_count) return; + if (!--self->ref_count && self->j) { + sd_journal_close(self->j); + self->j = NULL; + } +} + +#define INCR_REF_BEGIN_ALLOW_THREADS(self) \ + self->ref_count++; \ + Py_BEGIN_ALLOW_THREADS +#define DECR_REF_END_ALLOW_THREADS(self) \ + Py_END_ALLOW_THREADS \ + decr_ref_count(self); + + + /** * Convert a str or bytes object into a C-string path. * Returns NULL on error. @@ -220,7 +247,10 @@ static int intlist_converter(PyObject* obj, int **_result, size_t *_len) { } static void Reader_dealloc(Reader* self) { - sd_journal_close(self->j); + if (self->j) { + sd_journal_close(self->j); + self->j = NULL; + } Py_TYPE(self)->tp_free((PyObject*)self); } @@ -271,9 +301,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { return -1; #if HAVE_JOURNAL_OPEN_DIRECTORY_FD - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open_directory_fd(&self->j, (int) fd, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) #else r = -ENOSYS; #endif @@ -285,9 +315,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { if (!path) return -1; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open_directory(&self->j, path, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) } } else if (_files) { _cleanup_Py_DECREF_ PyObject *item0 = NULL; @@ -300,9 +330,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { return -1; #if HAVE_JOURNAL_OPEN_FILES - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open_files(&self->j, (const char**) files, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) #else r = -ENOSYS; #endif @@ -314,9 +344,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { return -1; #if HAVE_JOURNAL_OPEN_DIRECTORY_FD - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open_files_fd(&self->j, fds, n_fds, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) #else r = -ENOSYS; #endif @@ -329,16 +359,16 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { if (!namespace) return -1; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open_namespace(&self->j, namespace, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) #else r = -ENOSYS; #endif } else { - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_open(&self->j, flags); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) } return set_error(r, NULL, "Opening the journal failed"); @@ -438,8 +468,8 @@ static PyObject* Reader_close(Reader *self, PyObject *args) { assert(self); assert(!args); - sd_journal_close(self->j); - self->j = NULL; + self->closed = 1; + decr_ref_count(self); /* decrement initial reference (without incr) */ Py_RETURN_NONE; } @@ -501,7 +531,7 @@ static PyObject* Reader_next(Reader *self, PyObject *args) { return NULL; } - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) if (skip == 1LL) r = sd_journal_next(self->j); else if (skip == -1LL) @@ -512,7 +542,7 @@ static PyObject* Reader_next(Reader *self, PyObject *args) { r = sd_journal_previous_skip(self->j, -skip); else assert(!"should be here"); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -782,9 +812,9 @@ PyDoc_STRVAR(Reader_seek_head__doc__, "See :manpage:`sd_journal_seek_head(3)`."); static PyObject* Reader_seek_head(Reader *self, PyObject *args) { int r; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_seek_head(self->j); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -800,9 +830,9 @@ PyDoc_STRVAR(Reader_seek_tail__doc__, static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { int r; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_seek_tail(self->j); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -820,9 +850,9 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) return NULL; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_seek_realtime_usec(self->j, timestamp); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -850,17 +880,17 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { if (set_error(r, NULL, "Invalid bootid") < 0) return NULL; } else { - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_id128_get_boot(&id); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; } - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -924,9 +954,9 @@ static PyObject* Reader_process(Reader *self, PyObject *args) { assert(!args); - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_process(self->j); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -950,9 +980,9 @@ static PyObject* Reader_wait(Reader *self, PyObject *args) { if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) return NULL; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_wait(self->j, timeout); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, NULL) < 0) return NULL; @@ -970,9 +1000,9 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) return NULL; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_seek_cursor(self->j, cursor); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, "Invalid cursor") < 0) return NULL; @@ -1035,9 +1065,9 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) { if (!PyArg_ParseTuple(args, "s:query_unique", &query)) return NULL; - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_query_unique(self->j, query); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (set_error(r, NULL, "Invalid field name") < 0) return NULL; @@ -1172,9 +1202,9 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { assert(self); assert(!args); - Py_BEGIN_ALLOW_THREADS + INCR_REF_BEGIN_ALLOW_THREADS(self) r = sd_journal_get_catalog(self->j, &msg); - Py_END_ALLOW_THREADS + DECR_REF_END_ALLOW_THREADS(self) if (r == -ENOENT) { const void* mid; @@ -1264,7 +1294,7 @@ static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closur PyDoc_STRVAR(closed__doc__, "True iff journal is closed"); static PyObject* Reader_get_closed(Reader *self, void *closure) { - return PyBool_FromLong(!self->j); + return PyBool_FromLong(self->closed || !self->j); } static PyGetSetDef Reader_getsetters[] = { @@ -1330,7 +1360,7 @@ static PyTypeObject ReaderType = { .tp_methods = Reader_methods, .tp_getset = Reader_getsetters, .tp_init = (initproc) Reader_init, - .tp_new = PyType_GenericNew, + .tp_new = Reader_new, }; static PyMethodDef methods[] = { From 7fa91c80e33936488d0ae284fdfa1bd819f6c14e Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 1 Apr 2025 16:55:18 +0200 Subject: [PATCH 2/4] amend: close and decrement count for initial reference only once (avoids unexpected states on repeated Reader.close) --- systemd/_reader.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 3b8bbcb..a797151 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -468,8 +468,10 @@ static PyObject* Reader_close(Reader *self, PyObject *args) { assert(self); assert(!args); - self->closed = 1; - decr_ref_count(self); /* decrement initial reference (without incr) */ + if (!self->closed) { + self->closed = 1; + decr_ref_count(self); /* decrement initial reference (without incr) */ + } Py_RETURN_NONE; } From 7c99a39e6dc40367b4bbd3173f3db9dcc799cc97 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 2 Apr 2025 16:09:35 +0200 Subject: [PATCH 3/4] repair GHA-CI (update versions, pip install using --break-system-packages, etc) --- .github/workflows/build.yml | 14 +++++++++----- .github/workflows/install.yml | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93f1bb1..64198bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ permissions: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ matrix.python }}-${{ github.ref }} cancel-in-progress: true @@ -22,19 +22,20 @@ jobs: fail-fast: false matrix: python: [ - "3.7", "3.8", "3.9", "3.10", - "3.11.0-rc.1", + "3.11", + "3.12", + "3.13" ] name: Python ${{ matrix.python }} steps: - name: Repository checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Configure Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} architecture: x64 @@ -43,6 +44,9 @@ jobs: run: | sudo apt -y update sudo apt -y install gcc libsystemd-dev + if dpkg --compare-versions "${{ matrix.python }}" ge 3.12; then + python -m pip install setuptools || echo "can't install setuptools" + fi python -m pip install pytest sphinx - name: Build (Python ${{ matrix.python }}) diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index c1d202c..5e3dc22 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -24,7 +24,7 @@ jobs: container: [ "archlinux:latest", "debian:testing", - "quay.io/centos/centos:stream8", + "quay.io/centos/centos:stream9", "quay.io/fedora/fedora:rawhide", "ubuntu:focal", ] @@ -33,7 +33,7 @@ jobs: name: ${{ matrix.container }} steps: - name: Repository checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install dependencies shell: bash @@ -45,20 +45,25 @@ jobs: gcc git pkg-config - python3 systemd ) case "$DIST_ID" in arch) + pacman --noconfirm -Sy python3 || echo "Issue installing/upgrading python3" + pacman --noconfirm -Sy python-pip + python3 -m pip install --upgrade pip || echo "can't upgrade pip" pacman --noconfirm -Sy "${DEPS_COMMON[@]}" systemd-libs - python3 -m ensurepip ;; centos|fedora) - dnf -y install "${DEPS_COMMON[@]}" systemd-devel python3-devel python3-pip + dnf -y install python3 python3-devel python3-pip + python3 -m pip install --upgrade pip || echo "can't upgrade pip" + dnf -y install "${DEPS_COMMON[@]}" systemd-devel ;; ubuntu|debian) apt -y update + DEBIAN_FRONTEND=noninteractive apt -y install python3 python3-dev python3-pip + python3 -m pip install --upgrade pip || echo "can't upgrade pip" DEBIAN_FRONTEND=noninteractive apt -y install "${DEPS_COMMON[@]}" libsystemd-dev python3-dev python3-pip ;; *) @@ -66,13 +71,13 @@ jobs: exit 1 esac - python3 -m pip install pytest sphinx + python3 -m pip install --break-system-packages pytest sphinx - name: Build & install shell: bash run: | set -x - python3 -m pip install -I -v . + python3 -m pip install --break-system-packages -I -v . # Avoid importing the systemd module from the git repository cd / python3 -c 'from systemd import journal; print(journal.__version__)' From 646b3b3db84e9812bdbce0143b148adbe368812d Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Tue, 1 Jul 2025 14:03:46 +0200 Subject: [PATCH 4/4] remove noop, unneeded by dealloc --- systemd/_reader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index a797151..5a10a5d 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -249,7 +249,6 @@ static int intlist_converter(PyObject* obj, int **_result, size_t *_len) { static void Reader_dealloc(Reader* self) { if (self->j) { sd_journal_close(self->j); - self->j = NULL; } Py_TYPE(self)->tp_free((PyObject*)self); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy