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__)' diff --git a/systemd/_reader.c b/systemd/_reader.c index 333eb9a..5a10a5d 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,9 @@ 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); + } Py_TYPE(self)->tp_free((PyObject*)self); } @@ -271,9 +300,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 +314,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 +329,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 +343,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 +358,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 +467,10 @@ static PyObject* Reader_close(Reader *self, PyObject *args) { assert(self); assert(!args); - sd_journal_close(self->j); - self->j = NULL; + if (!self->closed) { + self->closed = 1; + decr_ref_count(self); /* decrement initial reference (without incr) */ + } Py_RETURN_NONE; } @@ -501,7 +532,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 +543,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 +813,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 +831,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 +851,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 +881,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 +955,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 +981,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 +1001,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 +1066,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 +1203,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 +1295,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 +1361,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[] = {