From 76eac71fc1db67da5053e47c142c5fe3f600da14 Mon Sep 17 00:00:00 2001 From: "deepin-community-bot[bot]" <156989552+deepin-community-bot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 08:36:18 +0000 Subject: [PATCH] feat: update python-systemd to 235-1 --- Makefile | 12 +- NEWS | 31 ++++ README.md | 150 +++++++++++++++---- debian/changelog | 10 ++ debian/control | 5 +- debian/patches/PY_SSIZE_T_CLEAN.patch | 41 ------ debian/patches/series | 1 - debian/rules | 1 + docs/conf.py | 1 + docs/id128.rst | 21 +++ setup.py | 19 +-- systemd/_daemon.c | 130 +++++++++++++---- systemd/_journal.c | 15 +- systemd/_reader.c | 202 ++++++++++++++++++-------- systemd/daemon.py | 24 ++- systemd/id128-constants.h | 21 +++ systemd/id128-defines.h | 21 +++ systemd/id128.c | 62 ++++++-- systemd/journal.py | 59 +++++--- systemd/login.c | 18 +-- systemd/test/test_daemon.py | 57 +++++++- systemd/test/test_id128.py | 46 ++++++ systemd/test/test_journal.py | 34 ++++- systemd/util.c | 1 + 24 files changed, 760 insertions(+), 222 deletions(-) delete mode 100644 debian/patches/PY_SSIZE_T_CLEAN.patch delete mode 100644 debian/patches/series create mode 100644 systemd/test/test_id128.py diff --git a/Makefile b/Makefile index 26f75b8..02d357f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ PYTHON = python SED = sed -SPHINX_BUILD = sphinx-build ETAGS = etags INCLUDE_DIR := $(shell pkg-config --variable=includedir libsystemd) INCLUDE_FLAGS := $(shell pkg-config --cflags libsystemd) @@ -8,8 +7,10 @@ VERSION := $(shell $(PYTHON) setup.py --version) TESTFLAGS = -v define buildscript -import sys,sysconfig -print("build/lib.{}-{}.{}".format(sysconfig.get_platform(), *sys.version_info[:2])) +import sys, sysconfig, setuptools +sversion = int(setuptools.__version__.split(".")[0]) +end = sys.implementation.cache_tag if sversion >= 61 else "{}.{}".format(*sys.version_info[:2]) +print("build/lib.{}-{}".format(sysconfig.get_platform(), end)) endef builddir := $(shell $(PYTHON) -c '$(buildscript)') @@ -49,9 +50,10 @@ clean: distclean: clean rm -rf dist MANIFEST -SPHINXOPTS = -D version=$(VERSION) -D release=$(VERSION) +SPHINXOPTS += -D version=$(VERSION) -D release=$(VERSION) sphinx-%: build - PYTHONPATH=$(builddir) $(SPHINX_BUILD) -b $* $(SPHINXOPTS) docs build/$* + cd build && \ + PYTHONPATH=../$(builddir) $(PYTHON) -m sphinx -b $* $(SPHINXOPTS) ../docs $* @echo Output has been generated in build/$* doc: sphinx-html diff --git a/NEWS b/NEWS index 6daac80..476baac 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,36 @@ Python wrappers for libsystemd API +CHANGES WITH 235: + + * Adapt the rename of systemd-activate to systemd-socket-activate + performed in systemd 230. + + * Support for sd_listen_fds_with_names added in systemd 227. + + * Support for sd_journal_get_cutoff_realtime_usec added in systemd + 186. + + * Make the Reader PY_SSIZE_T_CLEAN for py3.10 compatibility. + + * id128: update for systemd-243 compatibility and other fixes. + + * C syntax modernization. A minimum of C99 is assumed. + + * Fix seek_realtime to work with timezone aware date on Python 3. + + * journal: add namespace support. + + * Fixes for memory leaks and documentation. + + * Support for Python 2 will be removed after this release. + + Contributions from: Alexander Olekhnovich, Andrew Stone, + Architector #4, Chris Mullins, Dan Bungert, Dominik Prien, + Federico Ceratto, Frantisek Sumsal, Glandos, Hendrikto, Khem + Raj, Léonard Gérard, Marcel Waldvogel, Marco Paolini, Samuel + BF, Tamaki Nishino, Tim Orling, Tomasz Meresiński, Zbigniew + Jędrzejewski-Szmek, ytyt-yt + CHANGES WITH 234: * Support for the new sd_is_socket_sockaddr added in systemd 233 diff --git a/README.md b/README.md index 0118060..0107f50 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ python-systemd Python module for native access to the systemd facilities. Functionality is separated into a number of modules: -- systemd.journal supports sending of structured messages to the journal +- `systemd.journal` supports sending of structured messages to the journal and reading journal files, -- systemd.daemon wraps parts of libsystemd useful for writing daemons +- `systemd.daemon` wraps parts of `libsystemd` useful for writing daemons and socket activation, -- systemd.id128 provides functions for querying machine and boot identifiers +- `systemd.id128` provides functions for querying machine and boot identifiers and a lists of message identifiers provided by systemd, -- systemd.login wraps parts of libsystemd used to query logged in users +- `systemd.login` wraps parts of `libsystemd` used to query logged in users and available seats and machines. Installation @@ -17,37 +17,43 @@ Installation This module should be packaged for almost all Linux distributions. Use -On Fedora/RHEL/CentOS +On Fedora: - dnf install python-systemd python3-systemd + dnf install python3-systemd -On Debian/Ubuntu/Mint +On Debian/Ubuntu/Mint: - apt-get install python-systemd python3-systemd + apt update + apt install python3-systemd -On openSUSE and SLE +On openSUSE and SLE: - zypper in python-systemd + zypper in python3-systemd + +On Arch: + + pacman -Sy python-systemd To build from source +-------------------- -On Fedora 21+ with Python 2: +On CentOS, RHEL, and Fedora with Python 2: dnf install git python-pip gcc python-devel systemd-devel - pip install git+https://github.com/systemd/python-systemd.git#egg=systemd + pip install 'git+https://github.com/systemd/python-systemd.git#egg=systemd-python' -On Fedora 21+ with Python 3: +On Fedora with Python 3: dnf install git python3-pip gcc python3-devel systemd-devel - pip3 install git+https://github.com/systemd/python-systemd.git#egg=systemd + pip3 install 'git+https://github.com/systemd/python-systemd.git#egg=systemd-python' On Debian or Ubuntu with Python 2: - apt-get install libsystemd-{journal,daemon,login,id128}-dev gcc python-dev pkg-config + apt install libsystemd-{journal,daemon,login,id128}-dev gcc python-dev pkg-config On Debian or Ubuntu with Python 3: - apt-get install libsystemd-{journal,daemon,login,id128}-dev gcc python3-dev pkg-config + apt install libsystemd-{journal,daemon,login,id128}-dev gcc python3-dev pkg-config The project is also available on pypi as `systemd-python`. @@ -61,7 +67,7 @@ Quick example: journal.send('Hello, again, world', FIELD2='Greetings!', FIELD3='Guten tag') journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') -There is one required argument -- the message, and additional fields +There is one required argument — the message, and additional fields can be specified as keyword arguments. Following the journald API, all names are uppercase. @@ -75,15 +81,109 @@ The journald sendv call can also be accessed directly: The two examples should give the same results in the log. -Notes: +Reading from the journal is often similar to using the `journalctl` utility. + +Show all entries since 20 minutes ago (`journalctl --since "20 minutes ago"`): + + from systemd import journal + from datetime import datetime, timedelta + j = journal.Reader() + j.seek_realtime(datetime.now() - timedelta(minutes=20)) + for entry in j: + print(entry['MESSAGE']) + +Show entries between two timestamps (`journalctl --since "50 minutes ago" --until "10 minutes ago"`): + + from systemd import journal + from datetime import datetime, timedelta + j = journal.Reader() + since = datetime.now() - timedelta(minutes=50) + until = datetime.now() - timedelta(minutes=10) + j.seek_realtime(since) + for entry in j: + if entry['__REALTIME_TIMESTAMP'] > until: + break + print(entry['MESSAGE']) + +Show explanations of log messages alongside entries (`journalctl -x`): + + from systemd import journal + j = journal.Reader() + for entry in j: + print("MESSAGE: ", entry['MESSAGE']) + try: + print("CATALOG: ", j.get_catalog()) + except: + pass + +Show entries by a specific executable (`journalctl /usr/bin/vim`): + + from systemd import journal + j = journal.Reader() + j.add_match('_EXE=/usr/bin/vim') + for entry in j: + print(entry['MESSAGE']) - * Unlike the native C version of journald's sd_journal_send(), - printf-style substitution is not supported. Perform any - substitution using Python's % operator or .format() capabilities - first. - * A ValueError is raised if sd_journald_sendv() results in an error. - This might happen if there are no arguments or one of them is - invalid. + - Note: matches can be added from many different fields, for example + entries from a specific process ID can be matched with the `_PID` + field, and entries from a specific unit (ie. `journalctl -u + systemd-udevd.service`) can be matched with `_SYSTEMD_UNIT`. + See all fields available at the + [systemd.journal-fields docs](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html). + +Show kernel ring buffer (`journalctl -k`): + + from systemd import journal + j = journal.Reader() + j.add_match('_TRANSPORT=kernel') + for entry in j: + print(entry['MESSAGE']) + +Read entries in reverse (`journalctl _EXE=/usr/bin/vim -r`): + + from systemd import journal + class ReverseReader(journal.Reader): + def __next__(self): + ans = self.get_previous() + if ans: + return ans + raise StopIteration() + + j = ReverseReader() + j.add_match('_EXE=/usr/bin/vim') + j.seek_tail() + for entry in j: + print(entry['MESSAGE']) + + +Notes +----- + +* Unlike the native C version of journald's `sd_journal_send()`, + printf-style substitution is not supported. Perform any substitution + using Python's f-strings first (or `.format()` or the `%` operator). +* A `ValueError` is raised if `sd_journald_sendv()` results in an + error. This might happen if there are no arguments or one of them is + invalid. + +A handler class for the Python logging framework is also provided: + + import logging + from systemd import journal + logger = logging.getLogger('custom_logger_name') + logger.addHandler(journal.JournalHandler(SYSLOG_IDENTIFIER='custom_unit_name')) + logger.warning("Some message: %s", 'detail') + +`libsystemd` version compatibility +---------------------------------- + +This module may be compiled against any version of `libsystemd`. At +compilation time, any functionality that is not available in that +version is disabled, and the resulting binary module will depend on +symbols that were available at compilation time. This means that the +resulting binary module is compatible with that or any later version +of `libsystemd`. To obtain maximum possible functionality, this module +must be compile against suitably recent libsystemd. Documentation ============= diff --git a/debian/changelog b/debian/changelog index 46b72c9..8e0d848 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +python-systemd (235-1) unstable; urgency=medium + + * New upstream version 235 + * Drop patches, merged upstream + * Bump Standards-Version to 3.6.1, no further changes + * Add Build-Depends on python3-setuptools + * Enable all hardening build flags + + -- Michael Biebl Tue, 16 Aug 2022 17:12:47 +0200 + python-systemd (234-4) unstable; urgency=medium [ Debian Janitor ] diff --git a/debian/control b/debian/control index 4042285..31124af 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Debian systemd Maintainers Uploaders: Michael Biebl , Martin Pitt -Standards-Version: 4.6.0 +Standards-Version: 4.6.1 Rules-Requires-Root: no Vcs-Git: https://salsa.debian.org/systemd-team/python-systemd.git Vcs-Browser: https://salsa.debian.org/systemd-team/python-systemd @@ -13,7 +13,8 @@ Build-Depends: debhelper-compat (= 13), dh-python, libsystemd-dev, pkg-config, - python3-all-dev + python3-all-dev, + python3-setuptools, Package: python3-systemd Section: python diff --git a/debian/patches/PY_SSIZE_T_CLEAN.patch b/debian/patches/PY_SSIZE_T_CLEAN.patch deleted file mode 100644 index 9849fd2..0000000 --- a/debian/patches/PY_SSIZE_T_CLEAN.patch +++ /dev/null @@ -1,41 +0,0 @@ -Description: cherry-pick "reader: make PY_SSIZE_T_CLEAN" -Author: Zbigniew Jędrzejewski-Szmek -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1963582 -Origin: https://github.com/systemd/python-systemd/commit/c71bbac357f0ac722e1bcb2edfa925b68cca23c9 -Forwarded: not-needed -Last-Update: 2022-03-03 ---- a/systemd/_reader.c -+++ b/systemd/_reader.c -@@ -18,7 +18,12 @@ - along with python-systemd; If not, see . - ***/ - -+#define PY_SSIZE_T_CLEAN -+#pragma GCC diagnostic push -+#pragma GCC diagnostic ignored "-Wredundant-decls" - #include -+#pragma GCC diagnostic pop -+ - #include - #include - #include -@@ -707,11 +712,17 @@ - "Match is a string of the form \"FIELD=value\"."); - static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) { - char *match; -- int match_len, r; -+ Py_ssize_t match_len; -+ int r; - if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) - return NULL; - -- r = sd_journal_add_match(self->j, match, match_len); -+ if (match_len > INT_MAX) { -+ set_error(-ENOBUFS, NULL, NULL); -+ return NULL; -+ } -+ -+ r = sd_journal_add_match(self->j, match, (int) match_len); - if (set_error(r, NULL, "Invalid match") < 0) - return NULL; - diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index 53c72cb..0000000 --- a/debian/patches/series +++ /dev/null @@ -1 +0,0 @@ -PY_SSIZE_T_CLEAN.patch diff --git a/debian/rules b/debian/rules index ef5e729..e4e9f51 100755 --- a/debian/rules +++ b/debian/rules @@ -2,6 +2,7 @@ #export DH_VERBOSE=1 #export DEB_BUILD_OPTIONS="nostrip" +export DEB_BUILD_MAINT_OPTIONS = hardening=+all export PYBUILD_NAME=systemd # Explicitly tell dh to use pybuild, otherwise it will pick the diff --git a/docs/conf.py b/docs/conf.py index 1919170..05df507 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -156,6 +156,7 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'python-systemddoc' +manpages_url = 'https://www.freedesktop.org/software/systemd/man/{page}.html' # -- Options for LaTeX output -------------------------------------------------- diff --git a/docs/id128.rst b/docs/id128.rst index 8146e9b..cd562d5 100644 --- a/docs/id128.rst +++ b/docs/id128.rst @@ -11,10 +11,13 @@ .. autoattribute:: systemd.id128.SD_MESSAGE_BOOTCHART .. autoattribute:: systemd.id128.SD_MESSAGE_CONFIG_ERROR .. autoattribute:: systemd.id128.SD_MESSAGE_COREDUMP + .. autoattribute:: systemd.id128.SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE .. autoattribute:: systemd.id128.SD_MESSAGE_DNSSEC_DOWNGRADE .. autoattribute:: systemd.id128.SD_MESSAGE_DNSSEC_FAILURE .. autoattribute:: systemd.id128.SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED + .. autoattribute:: systemd.id128.SD_MESSAGE_FACTORY_RESET .. autoattribute:: systemd.id128.SD_MESSAGE_FORWARD_SYSLOG_MISSED + .. autoattribute:: systemd.id128.SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS .. autoattribute:: systemd.id128.SD_MESSAGE_HIBERNATE_KEY .. autoattribute:: systemd.id128.SD_MESSAGE_INVALID_CONFIGURATION .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_DROPPED @@ -26,8 +29,13 @@ .. autoattribute:: systemd.id128.SD_MESSAGE_LID_OPENED .. autoattribute:: systemd.id128.SD_MESSAGE_MACHINE_START .. autoattribute:: systemd.id128.SD_MESSAGE_MACHINE_STOP + .. autoattribute:: systemd.id128.SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE + .. autoattribute:: systemd.id128.SD_MESSAGE_NOBODY_USER_UNSUITABLE .. autoattribute:: systemd.id128.SD_MESSAGE_OVERMOUNTING + .. autoattribute:: systemd.id128.SD_MESSAGE_POWER_KEY_LONG_PRESS .. autoattribute:: systemd.id128.SD_MESSAGE_POWER_KEY + .. autoattribute:: systemd.id128.SD_MESSAGE_REBOOT_KEY_LONG_PRESS + .. autoattribute:: systemd.id128.SD_MESSAGE_REBOOT_KEY .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_START .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_STOP .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_START @@ -37,17 +45,30 @@ .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_STOP .. autoattribute:: systemd.id128.SD_MESSAGE_SPAWN_FAILED .. autoattribute:: systemd.id128.SD_MESSAGE_STARTUP_FINISHED + .. autoattribute:: systemd.id128.SD_MESSAGE_SUSPEND_KEY_LONG_PRESS .. autoattribute:: systemd.id128.SD_MESSAGE_SUSPEND_KEY .. autoattribute:: systemd.id128.SD_MESSAGE_SYSTEM_DOCKED + .. autoattribute:: systemd.id128.SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED .. autoattribute:: systemd.id128.SD_MESSAGE_SYSTEM_UNDOCKED + .. autoattribute:: systemd.id128.SD_MESSAGE_TAINTED .. autoattribute:: systemd.id128.SD_MESSAGE_TIME_CHANGE + .. autoattribute:: systemd.id128.SD_MESSAGE_TIME_SYNC .. autoattribute:: systemd.id128.SD_MESSAGE_TIMEZONE_CHANGE .. autoattribute:: systemd.id128.SD_MESSAGE_TRUNCATED_CORE .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_FAILED + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_FAILURE_RESULT + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_OOMD_KILL + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_OUT_OF_MEMORY + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_PROCESS_EXIT .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADED .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADING + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RESOURCES + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RESTART_SCHEDULED + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_SKIPPED .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTED .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTING .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPED .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPING + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_SUCCESS + .. autoattribute:: systemd.id128.SD_MESSAGE_UNSAFE_USER_NAME .. autoattribute:: systemd.id128.SD_MESSAGE_USER_STARTUP_FINISHED diff --git a/setup.py b/setup.py index 983eecd..fe9c03a 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ import sys, os -from distutils.core import setup, Extension +from setuptools import setup, Extension from subprocess import Popen, PIPE, check_output def call(*cmd): @@ -21,7 +21,7 @@ def pkgconfig(package, **kw): for token in result.split(): kw.setdefault(flag_map.get(token[:2]), []).append(token[2:]) - # allow version detection to be overriden using environment variables + # allow version detection to be overridden using environment variables version = os.getenv(pkg_version) if not version: version = check_output([pkgconf, '--modversion', package], @@ -45,36 +45,36 @@ def lib(*names, **kw): + '\n'.join(results) + '\n') sys.exit(status) -version = '234' +version = '235' defines = {'define_macros':[('PACKAGE_VERSION', '"{}"'.format(version))]} _journal = Extension('systemd/_journal', sources = ['systemd/_journal.c', 'systemd/pyutil.c'], - extra_compile_args=['-Werror=implicit-function-declaration'], + extra_compile_args=['-std=c99', '-Werror=implicit-function-declaration'], **lib('libsystemd', 'libsystemd-journal', **defines)) _reader = Extension('systemd/_reader', sources = ['systemd/_reader.c', 'systemd/pyutil.c', 'systemd/strv.c'], - extra_compile_args=['-Werror=implicit-function-declaration'], + extra_compile_args=['-std=c99', '-Werror=implicit-function-declaration'], **lib('libsystemd', 'libsystemd-journal', **defines)) _daemon = Extension('systemd/_daemon', sources = ['systemd/_daemon.c', 'systemd/pyutil.c', 'systemd/util.c'], - extra_compile_args=['-Werror=implicit-function-declaration'], + extra_compile_args=['-std=c99', '-Werror=implicit-function-declaration'], **lib('libsystemd', 'libsystemd-daemon', **defines)) id128 = Extension('systemd/id128', sources = ['systemd/id128.c', 'systemd/pyutil.c'], - extra_compile_args=['-Werror=implicit-function-declaration'], + extra_compile_args=['-std=c99', '-Werror=implicit-function-declaration'], **lib('libsystemd', 'libsystemd-id128', **defines)) login = Extension('systemd/login', sources = ['systemd/login.c', 'systemd/pyutil.c', 'systemd/strv.c'], - extra_compile_args=['-Werror=implicit-function-declaration'], + extra_compile_args=['-std=c99', '-Werror=implicit-function-declaration'], **lib('libsystemd', 'libsystemd-login', **defines)) setup (name = 'systemd-python', version = version, @@ -94,7 +94,8 @@ def lib(*names, **kw): py_modules = ['systemd.journal', 'systemd.daemon', 'systemd.test.test_daemon', 'systemd.test.test_journal', - 'systemd.test.test_login'], + 'systemd.test.test_login', + 'systemd.test.test_id128'], ext_modules = [_journal, _reader, _daemon, diff --git a/systemd/_daemon.c b/systemd/_daemon.c index fe6d890..e83da48 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -33,22 +33,16 @@ #include "macro.h" #include "util.h" -#if LIBSYSTEMD_VERSION >= 214 -# define HAVE_PID_NOTIFY -#endif - -#if LIBSYSTEMD_VERSION >= 219 -# define HAVE_PID_NOTIFY_WITH_FDS -#endif +#define HAVE_PID_NOTIFY (LIBSYSTEMD_VERSION >= 214) +#define HAVE_PID_NOTIFY_WITH_FDS (LIBSYSTEMD_VERSION >= 219) +#define HAVE_SD_LISTEN_FDS_WITH_NAMES (LIBSYSTEMD_VERSION >= 227) +#define HAVE_IS_SOCKET_SOCKADDR (LIBSYSTEMD_VERSION >= 233) -#if LIBSYSTEMD_VERSION >= 233 -# define HAVE_IS_SOCKET_SOCKADDR -#endif PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-daemon library.\n\n" - "Provides _listen_fds, notify, booted, and is_* functions\n" - "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n" + "Provides _listen_fds*, notify, booted, and is_* functions\n" + "which wrap sd_listen_fds*, sd_notify, sd_booted, sd_is_*;\n" "useful for socket activation and checking if the system is\n" "running under systemd." ); @@ -61,7 +55,7 @@ PyDoc_STRVAR(booted__doc__, static PyObject* booted(PyObject *self, PyObject *args) { int r; - assert(args == NULL); + assert(!args); r = sd_booted(); if (set_error(r, NULL, NULL) < 0) @@ -104,7 +98,7 @@ static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|OiO:notify", (char**) kwlist, &msg, &obj, &_pid, &fds)) return NULL; - if (obj != NULL) + if (obj) unset = PyObject_IsTrue(obj); if (unset < 0) return NULL; @@ -115,7 +109,7 @@ static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { return NULL; } - if (fds != NULL) { + if (fds) { Py_ssize_t i, len; len = PySequence_Length(fds); @@ -123,11 +117,11 @@ static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { return NULL; arr = PyMem_NEW(int, len); - if (!fds) + if (!arr) return NULL; for (i = 0; i < len; i++) { - PyObject *item = PySequence_GetItem(fds, i); + _cleanup_Py_DECREF_ PyObject *item = PySequence_GetItem(fds, i); if (!item) return NULL; @@ -145,17 +139,17 @@ static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { n_fds = len; } - if (pid == 0 && fds == NULL) + if (pid == 0 && !fds) r = sd_notify(unset, msg); - else if (fds == NULL) { -#ifdef HAVE_PID_NOTIFY + else if (!fds) { +#if HAVE_PID_NOTIFY r = sd_pid_notify(pid, unset, msg); #else set_error(-ENOSYS, NULL, "Compiled without support for sd_pid_notify"); return NULL; #endif } else { -#ifdef HAVE_PID_NOTIFY_WITH_FDS +#if HAVE_PID_NOTIFY_WITH_FDS r = sd_pid_notify_with_fds(pid, unset, msg, arr, n_fds); #else set_error(-ENOSYS, NULL, "Compiled without support for sd_pid_notify_with_fds"); @@ -191,7 +185,7 @@ static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) { if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds", (char**) kwlist, &obj)) return NULL; - if (obj != NULL) + if (obj) unset = PyObject_IsTrue(obj); if (unset < 0) return NULL; @@ -204,6 +198,80 @@ static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) { return long_FromLong(r); } +PyDoc_STRVAR(listen_fds_with_names__doc__, + "_listen_fds_with_names(unset_environment=True) -> (int, str...)\n\n" + "Wraps sd_listen_fds_with_names(3).\n" +#if HAVE_SD_LISTEN_FDS_WITH_NAMES + "Return the number of descriptors passed to this process by the init system\n" + "and their names as part of the socket-based activation logic.\n" +#else + "NOT SUPPORTED: compiled without support sd_listen_fds_with_names" +#endif +); + +static void free_names(char **names) { + if (names == NULL) + return; + for (char **n = names; *n != NULL; n++) + free(*n); + free(names); +} +static PyObject* listen_fds_with_names(PyObject *self, PyObject *args, PyObject *keywds) { + int r; + int unset = false; + char **names = NULL; + PyObject *tpl, *item; + + static const char* const kwlist[] = {"unset_environment", NULL}; +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3 + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|p:_listen_fds_with_names", + (char**) kwlist, &unset)) + return NULL; +#else + PyObject *obj = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds_with_names", + (char**) kwlist, &obj)) + return NULL; + if (obj != NULL) + unset = PyObject_IsTrue(obj); + if (unset < 0) + return NULL; +#endif + +#if HAVE_SD_LISTEN_FDS_WITH_NAMES + r = sd_listen_fds_with_names(unset, &names); + if (set_error(r, NULL, NULL) < 0) + return NULL; + + tpl = PyTuple_New(r+1); + if (tpl == NULL) + return NULL; + + item = long_FromLong(r); + if (item == NULL) { + Py_DECREF(tpl); + return NULL; + } + if (PyTuple_SetItem(tpl, 0, item) < 0) { + Py_DECREF(tpl); + return NULL; + } + for (int i = 0; i < r && names[i] != NULL; i++) { + item = unicode_FromString(names[i]); + if (PyTuple_SetItem(tpl, 1+i, item) < 0) { + Py_DECREF(tpl); + free_names(names); + return NULL; + } + } + free_names(names); + return tpl; +#else /* !HAVE_SD_LISTEN_FDS_WITH_NAMES */ + set_error(-ENOSYS, NULL, "Compiled without support for sd_listen_fds_with_names"); + return NULL; +#endif /* HAVE_SD_LISTEN_FDS_WITH_NAMES */ +} + PyDoc_STRVAR(is_fifo__doc__, "_is_fifo(fd, path) -> bool\n\n" "Returns True iff the descriptor refers to a FIFO or a pipe.\n" @@ -320,7 +388,7 @@ static PyObject* is_socket_inet(PyObject *self, PyObject *args) { PyDoc_STRVAR(is_socket_sockaddr__doc__, "_is_socket_sockaddr(fd, address, type=0, flowinfo=0, listening=-1) -> bool\n\n" "Wraps sd_is_socket_inet_sockaddr(3).\n" -#ifdef HAVE_IS_SOCKET_SOCKADDR +#if HAVE_IS_SOCKET_SOCKADDR "`address` is a systemd-style numerical IPv4 or IPv6 address as used in\n" "ListenStream=. A port may be included after a colon (\":\"). See\n" "systemd.socket(5) for details.\n\n" @@ -360,7 +428,7 @@ static PyObject* is_socket_sockaddr(PyObject *self, PyObject *args) { addr.in6.sin6_flowinfo = flowinfo; } -#ifdef HAVE_IS_SOCKET_SOCKADDR +#if HAVE_IS_SOCKET_SOCKADDR r = sd_is_socket_sockaddr(fd, type, &addr.sa, addr_len, listening); if (set_error(r, NULL, NULL) < 0) return NULL; @@ -411,6 +479,8 @@ static PyMethodDef methods[] = { { "booted", booted, METH_NOARGS, booted__doc__}, { "notify", (PyCFunction) notify, METH_VARARGS | METH_KEYWORDS, notify__doc__}, { "_listen_fds", (PyCFunction) listen_fds, METH_VARARGS | METH_KEYWORDS, listen_fds__doc__}, + { "_listen_fds_with_names", (PyCFunction) listen_fds_with_names, + METH_VARARGS | METH_KEYWORDS, listen_fds_with_names__doc__}, { "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__}, { "_is_mq", is_mq, METH_VARARGS, is_mq__doc__}, { "_is_socket", is_socket, METH_VARARGS, is_socket__doc__}, @@ -427,7 +497,7 @@ PyMODINIT_FUNC init_daemon(void) { PyObject *m; m = Py_InitModule3("_daemon", methods, module__doc__); - if (m == NULL) + if (!m) return; PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START); @@ -439,10 +509,10 @@ REENABLE_WARNING; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, - "_daemon", /* name of module */ - module__doc__, /* module documentation, may be NULL */ - 0, /* size of per-interpreter state of the module */ - methods + .m_name = "_daemon", /* name of module */ + .m_doc = module__doc__, /* module documentation, may be NULL */ + .m_size = 0, /* size of per-interpreter state of the module */ + .m_methods = methods, }; DISABLE_WARNING_MISSING_PROTOTYPES; @@ -450,7 +520,7 @@ PyMODINIT_FUNC PyInit__daemon(void) { PyObject *m; m = PyModule_Create(&module); - if (m == NULL) + if (!m) return NULL; if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START) || diff --git a/systemd/_journal.c b/systemd/_journal.c index 52f1e7f..4f53dba 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -54,7 +54,7 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { if (PyUnicode_Check(item)) { encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict"); - if (encoded[i] == NULL) + if (!encoded[i]) goto out; item = encoded[i]; } @@ -110,7 +110,7 @@ static PyObject* journal_stream_fd(PyObject *self, PyObject *args) { static PyMethodDef methods[] = { { "sendv", journal_sendv, METH_VARARGS, journal_sendv__doc__ }, { "stream_fd", journal_stream_fd, METH_VARARGS, journal_stream_fd__doc__ }, - { NULL, NULL, 0, NULL } /* Sentinel */ + {} /* Sentinel */ }; #if PY_MAJOR_VERSION < 3 @@ -120,7 +120,7 @@ PyMODINIT_FUNC init_journal(void) { PyObject *m; m = Py_InitModule("_journal", methods); - if (m == NULL) + if (!m) return; PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); @@ -131,10 +131,9 @@ REENABLE_WARNING; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, - "_journal", /* name of module */ - NULL, /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module */ - methods + .m_name = "_journal", /* name of module */ + .m_size = -1, /* size of per-interpreter state of the module */ + .m_methods = methods, }; DISABLE_WARNING_MISSING_PROTOTYPES; @@ -142,7 +141,7 @@ PyMODINIT_FUNC PyInit__journal(void) { PyObject *m; m = PyModule_Create(&module); - if (m == NULL) + if (!m) return NULL; if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { diff --git a/systemd/_reader.c b/systemd/_reader.c index 5b7e191..4e8f47f 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -18,7 +18,12 @@ along with python-systemd; If not, see . ***/ +#define PY_SSIZE_T_CLEAN +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" #include +#pragma GCC diagnostic pop + #include #include #include @@ -32,22 +37,28 @@ #include "strv.h" #if defined(LIBSYSTEMD_VERSION) || LIBSYSTEMD_JOURNAL_VERSION > 204 -# define HAVE_JOURNAL_OPEN_FILES +# define HAVE_JOURNAL_OPEN_FILES 1 #else # define SD_JOURNAL_SYSTEM (1 << 2) # define SD_JOURNAL_CURRENT_USER (1 << 3) +# define HAVE_JOURNAL_OPEN_FILES 0 #endif -#if LIBSYSTEMD_VERSION >= 229 -# define HAVE_ENUMERATE_FIELDS -# define HAVE_HAS_RUNTIME_FILES -# define HAVE_HAS_PERSISTENT_FILES +#define HAVE_ENUMERATE_FIELDS (LIBSYSTEMD_VERSION >= 229) +#define HAVE_HAS_RUNTIME_FILES (LIBSYSTEMD_VERSION >= 229) +#define HAVE_HAS_PERSISTENT_FILES (LIBSYSTEMD_VERSION >= 229) + +#if LIBSYSTEMD_VERSION >= 245 +# define HAVE_JOURNAL_OPEN_NAMESPACE 1 +#else +# define HAVE_JOURNAL_OPEN_NAMESPACE 0 #endif #if LIBSYSTEMD_VERSION >= 230 -# define HAVE_JOURNAL_OPEN_DIRECTORY_FD +# define HAVE_JOURNAL_OPEN_DIRECTORY_FD 1 #else # define SD_JOURNAL_OS_ROOT (1 << 4) +# define HAVE_JOURNAL_OPEN_DIRECTORY_FD 0 #endif typedef struct { @@ -84,17 +95,17 @@ static PyStructSequence_Desc Monotonic_desc = { * Convert a str or bytes object into a C-string path. * Returns NULL on error. */ -static char* convert_path(PyObject *path, PyObject **bytes) { +static char* str_converter(PyObject *str, PyObject **bytes) { #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 int r; - r = PyUnicode_FSConverter(path, bytes); + r = PyUnicode_FSConverter(str, bytes); if (r == 0) return NULL; return PyBytes_AsString(*bytes); #else - return PyString_AsString(path); + return PyString_AsString(str); #endif } @@ -141,7 +152,7 @@ static int strv_converter(PyObject* obj, void *_result) { char *s; item = PySequence_ITEM(obj, i); - s = convert_path(item, &bytes); + s = str_converter(item, &bytes); if (!s) goto cleanup; @@ -220,7 +231,7 @@ static void Reader_dealloc(Reader* self) { } PyDoc_STRVAR(Reader__doc__, - "_Reader([flags | path | files]) -> ...\n\n" + "_Reader([flags | path | files | namespace]) -> ...\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" @@ -231,23 +242,25 @@ PyDoc_STRVAR(Reader__doc__, "OS_ROOT is used to open the journal from directories relative to the specified\n" "directory path or file descriptor.\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" + "If `namespace` argument is specified, the specific journal namespace will be open\n" + "(supported since systemd v245). Instead of opening the system journal, argument\n" + "`path` may specify a directory which contains the journal. It maybe be either\n" + "a file system path (a string), or a file descriptor (an integer). Alternatively,\n" + "argument `files` may specify a list of journal file names. Note that `flags`, `path`,\n" + "`files`, `directory_fd`, `namespace` are 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) { unsigned flags = SD_JOURNAL_LOCAL_ONLY; - PyObject *_path = NULL, *_files = NULL; + PyObject *_path = NULL, *_files = NULL, *_namespace = NULL; int r; - static const char* const kwlist[] = {"flags", "path", "files", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iO&O&:__init__", (char**) kwlist, + static const char* const kwlist[] = {"flags", "path", "files", "namespace", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iO&O&O&:__init__", (char**) kwlist, &flags, null_converter, &_path, - null_converter, &_files)) + null_converter, &_files, + null_converter, &_namespace)) return -1; if (!!_path + !!_files > 1) { @@ -263,7 +276,7 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { if (long_as_fd(_path, &fd) < 0) return -1; -#ifdef HAVE_JOURNAL_OPEN_DIRECTORY_FD +#if HAVE_JOURNAL_OPEN_DIRECTORY_FD Py_BEGIN_ALLOW_THREADS r = sd_journal_open_directory_fd(&self->j, (int) fd, flags); Py_END_ALLOW_THREADS @@ -274,7 +287,7 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { char *path = NULL; _cleanup_Py_DECREF_ PyObject *path_bytes = NULL; - path = convert_path(_path, &path_bytes); + path = str_converter(_path, &path_bytes); if (!path) return -1; @@ -286,13 +299,13 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { _cleanup_Py_DECREF_ PyObject *item0 = NULL; item0 = PySequence_GetItem(_files, 0); - if (item0 == NULL || !PyLong_Check(item0)) { + if (!item0 || !PyLong_Check(item0)) { _cleanup_strv_free_ char **files = NULL; if (!strv_converter(_files, &files)) return -1; -#ifdef HAVE_JOURNAL_OPEN_FILES +#if HAVE_JOURNAL_OPEN_FILES Py_BEGIN_ALLOW_THREADS r = sd_journal_open_files(&self->j, (const char**) files, flags); Py_END_ALLOW_THREADS @@ -306,7 +319,7 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { if (!intlist_converter(_files, &fds, &n_fds)) return -1; -#ifdef HAVE_JOURNAL_OPEN_DIRECTORY_FD +#if HAVE_JOURNAL_OPEN_DIRECTORY_FD Py_BEGIN_ALLOW_THREADS r = sd_journal_open_files_fd(&self->j, fds, n_fds, flags); Py_END_ALLOW_THREADS @@ -314,6 +327,20 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { r = -ENOSYS; #endif } + } else if (_namespace) { +#if HAVE_JOURNAL_OPEN_NAMESPACE + char *namespace = NULL; + _cleanup_Py_DECREF_ PyObject *ns_bytes = NULL; + namespace = str_converter(_namespace, &ns_bytes); + if (!namespace) + return -1; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_open_namespace(&self->j, namespace, flags); + Py_END_ALLOW_THREADS +#else + r = -ENOSYS; +#endif } else { Py_BEGIN_ALLOW_THREADS r = sd_journal_open(&self->j, flags); @@ -327,7 +354,10 @@ PyDoc_STRVAR(Reader_fileno__doc__, "fileno() -> int\n\n" "Get a file descriptor to poll for changes in the journal.\n" "This method invokes sd_journal_get_fd().\n" - "See man:sd_journal_get_fd(3)."); + "See :manpage:`sd_journal_get_fd(3)`.\n\n" + "When the file descriptor returned by this function is used a poll\n" + "loop, .process() should be used to process events and reset the readability\n" + "state of the file descriptor."); static PyObject* Reader_fileno(Reader *self, PyObject *args) { int fd; @@ -342,7 +372,7 @@ PyDoc_STRVAR(Reader_reliable_fd__doc__, "reliable_fd() -> bool\n\n" "Returns True iff the journal can be polled reliably.\n" "This method invokes sd_journal_reliable_fd().\n" - "See man:sd_journal_reliable_fd(3)."); + "See :manpage:`sd_journal_reliable_fd(3)`."); static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) { int r; @@ -356,7 +386,7 @@ PyDoc_STRVAR(Reader_get_events__doc__, "get_events() -> int\n\n" "Returns a mask of poll() events to wait for on the file\n" "descriptor returned by .fileno().\n\n" - "See man:sd_journal_get_events(3) for further discussion."); + "See :manpage:`sd_journal_get_events(3)` for further discussion."); static PyObject* Reader_get_events(Reader *self, PyObject *args) { int r; @@ -373,7 +403,7 @@ PyDoc_STRVAR(Reader_get_timeout__doc__, "is necessary.\n\n" "The return value must be converted to a relative timeout in\n" "milliseconds if it is to be used as an argument for poll().\n" - "See man:sd_journal_get_timeout(3) for further discussion."); + "See :manpage:`sd_journal_get_timeout(3)` for further discussion."); static PyObject* Reader_get_timeout(Reader *self, PyObject *args) { int r; uint64_t t; @@ -409,7 +439,7 @@ PyDoc_STRVAR(Reader_close__doc__, "close() -> None\n\n" "Free resources allocated by this Reader object.\n" "This method invokes sd_journal_close().\n" - "See man:sd_journal_close(3)."); + "See :manpage:`sd_journal_close(3)`."); static PyObject* Reader_close(Reader *self, PyObject *args) { assert(self); assert(!args); @@ -427,7 +457,7 @@ PyDoc_STRVAR(Reader_get_usage__doc__, "the size of journal files of the local host, otherwise\n" "of all hosts.\n\n" "This method invokes sd_journal_get_usage().\n" - "See man:sd_journal_get_usage(3)."); + "See :manpage:`sd_journal_get_usage(3)`."); static PyObject* Reader_get_usage(Reader *self, PyObject *args) { int r; uint64_t bytes; @@ -638,7 +668,7 @@ PyDoc_STRVAR(Reader_get_realtime__doc__, "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)."); + "See :manpage:`sd_journal_get_realtime_usec(3)`."); static PyObject* Reader_get_realtime(Reader *self, PyObject *args) { uint64_t timestamp; int r; @@ -659,7 +689,7 @@ PyDoc_STRVAR(Reader_get_monotonic__doc__, "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)."); + "See :manpage:`sd_journal_get_monotonic_usec(3)`."); static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) { uint64_t timestamp; sd_id128_t id; @@ -707,11 +737,17 @@ PyDoc_STRVAR(Reader_add_match__doc__, "Match is a string of the form \"FIELD=value\"."); static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) { char *match; - int match_len, r; + Py_ssize_t match_len; + int r; if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) return NULL; - r = sd_journal_add_match(self->j, match, match_len); + if (match_len > INT_MAX) { + set_error(-ENOBUFS, NULL, NULL); + return NULL; + } + + r = sd_journal_add_match(self->j, match, (int) match_len); if (set_error(r, NULL, "Invalid match") < 0) return NULL; @@ -723,7 +759,7 @@ PyDoc_STRVAR(Reader_add_disjunction__doc__, "Inserts a logical OR between matches added since previous\n" "add_disjunction() or add_conjunction() and the next\n" "add_disjunction() or add_conjunction().\n\n" - "See man:sd_journal_add_disjunction(3) for explanation."); + "See :manpage:`sd_journal_add_disjunction(3)` for explanation."); static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); @@ -737,7 +773,7 @@ PyDoc_STRVAR(Reader_add_conjunction__doc__, "Inserts a logical AND between matches added since previous\n" "add_disjunction() or add_conjunction() and the next\n" "add_disjunction() or add_conjunction().\n\n" - "See man:sd_journal_add_disjunction(3) for explanation."); + "See :manpage:`sd_journal_add_disjunction(3)` for explanation."); static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_conjunction(self->j); @@ -758,7 +794,7 @@ PyDoc_STRVAR(Reader_seek_head__doc__, "seek_head() -> None\n\n" "Jump to the beginning of the journal.\n" "This method invokes sd_journal_seek_head().\n" - "See man:sd_journal_seek_head(3)."); + "See :manpage:`sd_journal_seek_head(3)`."); static PyObject* Reader_seek_head(Reader *self, PyObject *args) { int r; Py_BEGIN_ALLOW_THREADS @@ -775,7 +811,7 @@ PyDoc_STRVAR(Reader_seek_tail__doc__, "seek_tail() -> None\n\n" "Jump to the end of the journal.\n" "This method invokes sd_journal_seek_tail().\n" - "See man:sd_journal_seek_tail(3)."); + "See :manpage:`sd_journal_seek_tail(3)`."); static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { int r; @@ -847,15 +883,57 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { Py_RETURN_NONE; } +PyDoc_STRVAR(Reader_get_start__doc__, + "get_start() -> int\n\n" + "Return the realtime timestamp of the first journal entry\n\n" + "in microseconds.\n\n" + "Wraps sd_journal_get_cutoff_realtime_usec().\n" + "See :manpage:`sd_journal_get_cutoff_realtime_usec(3)`."); +static PyObject* Reader_get_start(Reader *self, PyObject *args) { + uint64_t start; + int r; + + assert(self); + assert(!args); + + r = sd_journal_get_cutoff_realtime_usec(self->j, &start, NULL); + if (set_error(r, NULL, NULL) < 0) + return NULL; + + assert_cc(sizeof(unsigned long long) == sizeof(start)); + return PyLong_FromUnsignedLongLong(start); +} + +PyDoc_STRVAR(Reader_get_end__doc__, + "get_end() -> int\n\n" + "Return the realtime timestamp of the last journal entry\n\n" + "in microseconds.\n\n" + "Wraps sd_journal_get_cutoff_realtime_usec().\n" + "See :manpage:`sd_journal_get_cutoff_realtime_usec(3)`."); +static PyObject* Reader_get_end(Reader *self, PyObject *args) { + uint64_t end; + int r; + + assert(self); + assert(!args); + + r = sd_journal_get_cutoff_realtime_usec(self->j, NULL, &end); + if (set_error(r, NULL, NULL) < 0) + return NULL; + + assert_cc(sizeof(unsigned long long) == sizeof(end)); + return PyLong_FromUnsignedLongLong(end); +} + PyDoc_STRVAR(Reader_process__doc__, "process() -> state change (integer)\n\n" - "Process events and reset the readable state of the file\n" + "Process events and reset the readability state of the file\n" "descriptor returned by .fileno().\n\n" "Will return constants: NOP if no change; APPEND if new\n" "entries have been added to the end of the journal; and\n" "INVALIDATE if journal files have been added or removed.\n\n" - "See man:sd_journal_process(3) for further discussion."); + "See :manpage:`sd_journal_process(3)` for further discussion."); static PyObject* Reader_process(Reader *self, PyObject *args) { int r; @@ -879,10 +957,10 @@ PyDoc_STRVAR(Reader_wait__doc__, "Will return constants: NOP if no change; APPEND if new\n" "entries have been added to the end of the journal; and\n" "INVALIDATE if journal files have been added or removed.\n\n" - "See man:sd_journal_wait(3) for further discussion."); + "See :manpage:`sd_journal_wait(3)` for further discussion."); static PyObject* Reader_wait(Reader *self, PyObject *args) { int r; - int64_t timeout; + int64_t timeout = -1; if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) return NULL; @@ -920,7 +998,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { PyDoc_STRVAR(Reader_get_cursor__doc__, "get_cursor() -> str\n\n" "Return a cursor string for the current journal entry.\n\n" - "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3)."); + "Wraps sd_journal_get_cursor(). See :manpage:`sd_journal_get_cursor(3)`."); static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { _cleanup_free_ char *cursor = NULL; int r; @@ -938,7 +1016,7 @@ static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { PyDoc_STRVAR(Reader_test_cursor__doc__, "test_cursor(str) -> bool\n\n" "Test whether the cursor string matches current journal entry.\n\n" - "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3)."); + "Wraps sd_journal_test_cursor(). See :manpage:`sd_journal_test_cursor(3)`."); static PyObject* Reader_test_cursor(Reader *self, PyObject *args) { const char *cursor; int r; @@ -1016,7 +1094,7 @@ PyDoc_STRVAR(Reader_enumerate_fields__doc__, "Return a set of field names appearing in the journal.\n" "See sd_journal_enumerate_fields(3)."); static PyObject* Reader_enumerate_fields(Reader *self, PyObject *args) { -#ifdef HAVE_ENUMERATE_FIELDS +#if HAVE_ENUMERATE_FIELDS _cleanup_Py_DECREF_ PyObject *_value_set = NULL; PyObject *value_set; int r; @@ -1056,9 +1134,9 @@ static PyObject* Reader_enumerate_fields(Reader *self, PyObject *args) { PyDoc_STRVAR(Reader_has_runtime_files__doc__, "has_runtime_files(str) -> bool\n\n" "Returns true if runtime journal files have been found.\n\n" - "See man:sd_journal_test_cursor(3)."); + "See :manpage:`sd_journal_test_cursor(3)`."); static PyObject* Reader_has_runtime_files(Reader *self, PyObject *args) { -#ifdef HAVE_ENUMERATE_FIELDS +#if HAVE_ENUMERATE_FIELDS int r; assert(self); @@ -1077,9 +1155,9 @@ static PyObject* Reader_has_runtime_files(Reader *self, PyObject *args) { PyDoc_STRVAR(Reader_has_persistent_files__doc__, "has_persistent_files(str) -> bool\n\n" "Returns true if persistent journal files have been found.\n\n" - "See man:sd_journal_test_cursor(3)."); + "See :manpage:`sd_journal_test_cursor(3)`."); static PyObject* Reader_has_persistent_files(Reader *self, PyObject *args) { -#ifdef HAVE_ENUMERATE_FIELDS +#if HAVE_ENUMERATE_FIELDS int r; assert(self); @@ -1101,7 +1179,7 @@ PyDoc_STRVAR(Reader_get_catalog__doc__, "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)."); + "Wraps :manpage:`sd_journal_get_catalog(3)`."); static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { int r; _cleanup_free_ char *msg = NULL; @@ -1139,7 +1217,7 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { PyDoc_STRVAR(get_catalog__doc__, "get_catalog(id128) -> str\n\n" "Retrieve a message catalog entry for the given id.\n" - "Wraps man:sd_journal_get_catalog_for_message_id(3)."); + "Wraps :manpage:`sd_journal_get_catalog_for_message_id(3)`."); static PyObject* get_catalog(PyObject *self, PyObject *args) { int r; char *id_ = NULL; @@ -1184,7 +1262,7 @@ static PyObject* Reader_get_data_threshold(Reader *self, void *closure) { static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) { int r; - if (value == NULL) { + if (!value) { PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); return -1; } @@ -1201,7 +1279,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 == NULL); + return PyBool_FromLong(!self->j); } static PyGetSetDef Reader_getsetters[] = { @@ -1242,6 +1320,8 @@ static PyMethodDef Reader_methods[] = { {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__}, {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__}, {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__}, + {"_get_start", (PyCFunction) Reader_get_start, METH_NOARGS, Reader_get_start__doc__}, + {"_get_end", (PyCFunction) Reader_get_end, METH_NOARGS, Reader_get_end__doc__}, {"process", (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__}, {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, @@ -1276,10 +1356,10 @@ static PyMethodDef methods[] = { #if PY_MAJOR_VERSION >= 3 static PyModuleDef module = { PyModuleDef_HEAD_INIT, - "_reader", - module__doc__, - -1, - methods, + .m_name = "_reader", + .m_doc = module__doc__, + .m_size = -1, + .m_methods = methods, }; #endif @@ -1309,7 +1389,7 @@ init_reader(void) #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&module); - if (m == NULL) + if (!m) return NULL; if (!initialized) { @@ -1318,7 +1398,7 @@ init_reader(void) } #else m = Py_InitModule3("_reader", methods, module__doc__); - if (m == NULL) + if (!m) return; #endif @@ -1336,7 +1416,7 @@ init_reader(void) PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) || PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) || PyModule_AddIntConstant(m, "SYSTEM", SD_JOURNAL_SYSTEM) || - PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) || + PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM) || PyModule_AddIntConstant(m, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) || PyModule_AddIntConstant(m, "OS_ROOT", SD_JOURNAL_OS_ROOT) || PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { diff --git a/systemd/daemon.py b/systemd/daemon.py index 625219c..168e55d 100644 --- a/systemd/daemon.py +++ b/systemd/daemon.py @@ -4,6 +4,7 @@ booted, notify, _listen_fds, + _listen_fds_with_names, _is_fifo, _is_socket, _is_socket_inet, @@ -58,7 +59,7 @@ def listen_fds(unset_environment=True): Example:: (in primary window) - $ systemd-activate -l 2000 python3 -c \\ + $ systemd-socket-activate -l 2000 python3 -c \\ 'from systemd.daemon import listen_fds; print(listen_fds())' (in another window) $ telnet localhost 2000 @@ -69,3 +70,24 @@ def listen_fds(unset_environment=True): """ num = _listen_fds(unset_environment) return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num)) + +def listen_fds_with_names(unset_environment=True): + """Return a dictionary of socket activated descriptors as {fd: name} + + Example:: + + (in primary window) + $ systemd-socket-activate -l 2000 -l 4000 --fdname=2K:4K python3 -c \\ + 'from systemd.daemon import listen_fds_with_names; print(listen_fds_with_names())' + (in another window) + $ telnet localhost 2000 + (in primary window) + ... + Execing python3 (...) + [3] + """ + composite = _listen_fds_with_names(unset_environment) + retval = {} + for i in range(0, composite[0]): + retval[i+LISTEN_FDS_START] = composite[1+i] + return retval diff --git a/systemd/id128-constants.h b/systemd/id128-constants.h index 6de8d82..a1d215c 100644 --- a/systemd/id128-constants.h +++ b/systemd/id128-constants.h @@ -2,10 +2,13 @@ add_id(m, "SD_MESSAGE_BACKTRACE", SD_MESSAGE_BACKTRACE) JOINER add_id(m, "SD_MESSAGE_BOOTCHART", SD_MESSAGE_BOOTCHART) JOINER add_id(m, "SD_MESSAGE_CONFIG_ERROR", SD_MESSAGE_CONFIG_ERROR) JOINER add_id(m, "SD_MESSAGE_COREDUMP", SD_MESSAGE_COREDUMP) JOINER +add_id(m, "SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE", SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE) JOINER add_id(m, "SD_MESSAGE_DNSSEC_DOWNGRADE", SD_MESSAGE_DNSSEC_DOWNGRADE) JOINER add_id(m, "SD_MESSAGE_DNSSEC_FAILURE", SD_MESSAGE_DNSSEC_FAILURE) JOINER add_id(m, "SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED", SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED) JOINER +add_id(m, "SD_MESSAGE_FACTORY_RESET", SD_MESSAGE_FACTORY_RESET) JOINER add_id(m, "SD_MESSAGE_FORWARD_SYSLOG_MISSED", SD_MESSAGE_FORWARD_SYSLOG_MISSED) JOINER +add_id(m, "SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS", SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS) JOINER add_id(m, "SD_MESSAGE_HIBERNATE_KEY", SD_MESSAGE_HIBERNATE_KEY) JOINER add_id(m, "SD_MESSAGE_INVALID_CONFIGURATION", SD_MESSAGE_INVALID_CONFIGURATION) JOINER add_id(m, "SD_MESSAGE_JOURNAL_DROPPED", SD_MESSAGE_JOURNAL_DROPPED) JOINER @@ -17,8 +20,13 @@ add_id(m, "SD_MESSAGE_LID_CLOSED", SD_MESSAGE_LID_CLOSED) JOINER add_id(m, "SD_MESSAGE_LID_OPENED", SD_MESSAGE_LID_OPENED) JOINER add_id(m, "SD_MESSAGE_MACHINE_START", SD_MESSAGE_MACHINE_START) JOINER add_id(m, "SD_MESSAGE_MACHINE_STOP", SD_MESSAGE_MACHINE_STOP) JOINER +add_id(m, "SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE", SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE) JOINER +add_id(m, "SD_MESSAGE_NOBODY_USER_UNSUITABLE", SD_MESSAGE_NOBODY_USER_UNSUITABLE) JOINER add_id(m, "SD_MESSAGE_OVERMOUNTING", SD_MESSAGE_OVERMOUNTING) JOINER +add_id(m, "SD_MESSAGE_POWER_KEY_LONG_PRESS", SD_MESSAGE_POWER_KEY_LONG_PRESS) JOINER add_id(m, "SD_MESSAGE_POWER_KEY", SD_MESSAGE_POWER_KEY) JOINER +add_id(m, "SD_MESSAGE_REBOOT_KEY_LONG_PRESS", SD_MESSAGE_REBOOT_KEY_LONG_PRESS) JOINER +add_id(m, "SD_MESSAGE_REBOOT_KEY", SD_MESSAGE_REBOOT_KEY) JOINER add_id(m, "SD_MESSAGE_SEAT_START", SD_MESSAGE_SEAT_START) JOINER add_id(m, "SD_MESSAGE_SEAT_STOP", SD_MESSAGE_SEAT_STOP) JOINER add_id(m, "SD_MESSAGE_SESSION_START", SD_MESSAGE_SESSION_START) JOINER @@ -28,17 +36,30 @@ add_id(m, "SD_MESSAGE_SLEEP_START", SD_MESSAGE_SLEEP_START) JOINER add_id(m, "SD_MESSAGE_SLEEP_STOP", SD_MESSAGE_SLEEP_STOP) JOINER add_id(m, "SD_MESSAGE_SPAWN_FAILED", SD_MESSAGE_SPAWN_FAILED) JOINER add_id(m, "SD_MESSAGE_STARTUP_FINISHED", SD_MESSAGE_STARTUP_FINISHED) JOINER +add_id(m, "SD_MESSAGE_SUSPEND_KEY_LONG_PRESS", SD_MESSAGE_SUSPEND_KEY_LONG_PRESS) JOINER add_id(m, "SD_MESSAGE_SUSPEND_KEY", SD_MESSAGE_SUSPEND_KEY) JOINER add_id(m, "SD_MESSAGE_SYSTEM_DOCKED", SD_MESSAGE_SYSTEM_DOCKED) JOINER +add_id(m, "SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED", SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED) JOINER add_id(m, "SD_MESSAGE_SYSTEM_UNDOCKED", SD_MESSAGE_SYSTEM_UNDOCKED) JOINER +add_id(m, "SD_MESSAGE_TAINTED", SD_MESSAGE_TAINTED) JOINER add_id(m, "SD_MESSAGE_TIME_CHANGE", SD_MESSAGE_TIME_CHANGE) JOINER +add_id(m, "SD_MESSAGE_TIME_SYNC", SD_MESSAGE_TIME_SYNC) JOINER add_id(m, "SD_MESSAGE_TIMEZONE_CHANGE", SD_MESSAGE_TIMEZONE_CHANGE) JOINER add_id(m, "SD_MESSAGE_TRUNCATED_CORE", SD_MESSAGE_TRUNCATED_CORE) JOINER add_id(m, "SD_MESSAGE_UNIT_FAILED", SD_MESSAGE_UNIT_FAILED) JOINER +add_id(m, "SD_MESSAGE_UNIT_FAILURE_RESULT", SD_MESSAGE_UNIT_FAILURE_RESULT) JOINER +add_id(m, "SD_MESSAGE_UNIT_OOMD_KILL", SD_MESSAGE_UNIT_OOMD_KILL) JOINER +add_id(m, "SD_MESSAGE_UNIT_OUT_OF_MEMORY", SD_MESSAGE_UNIT_OUT_OF_MEMORY) JOINER +add_id(m, "SD_MESSAGE_UNIT_PROCESS_EXIT", SD_MESSAGE_UNIT_PROCESS_EXIT) JOINER add_id(m, "SD_MESSAGE_UNIT_RELOADED", SD_MESSAGE_UNIT_RELOADED) JOINER add_id(m, "SD_MESSAGE_UNIT_RELOADING", SD_MESSAGE_UNIT_RELOADING) JOINER +add_id(m, "SD_MESSAGE_UNIT_RESOURCES", SD_MESSAGE_UNIT_RESOURCES) JOINER +add_id(m, "SD_MESSAGE_UNIT_RESTART_SCHEDULED", SD_MESSAGE_UNIT_RESTART_SCHEDULED) JOINER +add_id(m, "SD_MESSAGE_UNIT_SKIPPED", SD_MESSAGE_UNIT_SKIPPED) JOINER add_id(m, "SD_MESSAGE_UNIT_STARTED", SD_MESSAGE_UNIT_STARTED) JOINER add_id(m, "SD_MESSAGE_UNIT_STARTING", SD_MESSAGE_UNIT_STARTING) JOINER add_id(m, "SD_MESSAGE_UNIT_STOPPED", SD_MESSAGE_UNIT_STOPPED) JOINER add_id(m, "SD_MESSAGE_UNIT_STOPPING", SD_MESSAGE_UNIT_STOPPING) JOINER +add_id(m, "SD_MESSAGE_UNIT_SUCCESS", SD_MESSAGE_UNIT_SUCCESS) JOINER +add_id(m, "SD_MESSAGE_UNSAFE_USER_NAME", SD_MESSAGE_UNSAFE_USER_NAME) JOINER add_id(m, "SD_MESSAGE_USER_STARTUP_FINISHED", SD_MESSAGE_USER_STARTUP_FINISHED) JOINER diff --git a/systemd/id128-defines.h b/systemd/id128-defines.h index a8d06ef..178de83 100644 --- a/systemd/id128-defines.h +++ b/systemd/id128-defines.h @@ -2,10 +2,13 @@ #define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18) #define SD_MESSAGE_CONFIG_ERROR SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) #define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa) #define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) #define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) #define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_FACTORY_RESET SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c) #define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) +#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45) #define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) #define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) #define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) @@ -17,8 +20,13 @@ #define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) #define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) #define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) +#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93) +#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c) #define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) +#define SD_MESSAGE_POWER_KEY_LONG_PRESS SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b) #define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75) +#define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0) #define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) #define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) #define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) @@ -28,17 +36,30 @@ #define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) #define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) #define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) +#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8) #define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) #define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33) #define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_TAINTED SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b) #define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIME_SYNC SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37) #define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) #define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) #define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) +#define SD_MESSAGE_UNIT_OOMD_KILL SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed) +#define SD_MESSAGE_UNIT_OUT_OF_MEMORY SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef) +#define SD_MESSAGE_UNIT_PROCESS_EXIT SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15) #define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) #define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) +#define SD_MESSAGE_UNIT_RESTART_SCHEDULED SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3) +#define SD_MESSAGE_UNIT_SKIPPED SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73) #define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) #define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) #define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) #define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48) +#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) #define SD_MESSAGE_USER_STARTUP_FINISHED SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) diff --git a/systemd/id128.c b/systemd/id128.c index f5d6aa6..891c658 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -30,10 +30,11 @@ #include "id128-defines.h" #include - #include "pyutil.h" #include "macro.h" +#define HAVE_SD_ID128_GET_MACHINE_APP_SPECIFIC (LIBSYSTEMD_VERSION >= 240) + PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-id128 library.\n\n" "Provides SD_MESSAGE_* constants and functions to query and generate\n" @@ -52,6 +53,12 @@ PyDoc_STRVAR(get_machine__doc__, "Wraps sd_id128_get_machine(3)." ); +PyDoc_STRVAR(get_machine_app_specific__doc__, + "get_machine_app_specific(UUID) -> UUID\n\n" + "Return a 128-bit unique identifier for this application and machine.\n" + "Wraps sd_id128_get_machine_app_specific(3)." +); + PyDoc_STRVAR(get_boot__doc__, "get_boot() -> UUID\n\n" "Return a 128-bit unique identifier for this boot.\n" @@ -85,7 +92,7 @@ static PyObject* make_uuid(sd_id128_t id) { sd_id128_t id; \ int r; \ \ - assert(args == NULL); \ + assert(!args); \ \ r = sd_id128_##name(&id); \ if (r < 0) { \ @@ -100,11 +107,48 @@ helper(randomize) helper(get_machine) helper(get_boot) +static PyObject *get_machine_app_specific(PyObject *self, PyObject *args) { + _cleanup_Py_DECREF_ PyObject *uuid_bytes = NULL; + + uuid_bytes = PyObject_GetAttrString(args, "bytes"); + if (!uuid_bytes) + return NULL; + +#if HAVE_SD_ID128_GET_MACHINE_APP_SPECIFIC + Py_buffer buffer; + sd_id128_t app_id; + int r; + + r = PyObject_GetBuffer(uuid_bytes, &buffer, 0); + if (r == -1) + return NULL; + + if (buffer.len != sizeof(sd_id128_t)) { + PyBuffer_Release(&buffer); + return NULL; + } + + r = sd_id128_get_machine_app_specific(*(sd_id128_t*)buffer.buf, &app_id); + PyBuffer_Release(&buffer); + if (r < 0) { + errno = -r; + return PyErr_SetFromErrno(PyExc_IOError); + } + + return make_uuid(app_id); + +#else + set_error(-ENOSYS, NULL, "Compiled without support for sd_id128_get_machine_app_specific"); + return NULL; +#endif +} + static PyMethodDef methods[] = { { "randomize", randomize, METH_NOARGS, randomize__doc__}, { "get_machine", get_machine, METH_NOARGS, get_machine__doc__}, + { "get_machine_app_specific", get_machine_app_specific, METH_O, get_machine_app_specific__doc__}, { "get_boot", get_boot, METH_NOARGS, get_boot__doc__}, - { NULL, NULL, 0, NULL } /* Sentinel */ + {} /* Sentinel */ }; static int add_id(PyObject *module, const char* name, sd_id128_t id) { @@ -124,7 +168,7 @@ PyMODINIT_FUNC initid128(void) { PyObject *m; m = Py_InitModule3("id128", methods, module__doc__); - if (m == NULL) + if (!m) return; /* a series of lines like 'add_id() ;' follow */ @@ -139,10 +183,10 @@ REENABLE_WARNING; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, - "id128", /* name of module */ - module__doc__, /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module */ - methods + .m_name = "id128", /* name of module */ + .m_doc = module__doc__, /* module documentation */ + .m_size = -1, /* size of per-interpreter state of the module */ + .m_methods = methods, }; DISABLE_WARNING_MISSING_PROTOTYPES; @@ -150,7 +194,7 @@ PyMODINIT_FUNC PyInit_id128(void) { PyObject *m; m = PyModule_Create(&module); - if (m == NULL) + if (!m) return NULL; if ( /* a series of lines like 'add_id() ||' follow */ diff --git a/systemd/journal.py b/systemd/journal.py index cc0b8aa..9d33fa8 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -28,8 +28,6 @@ import logging as _logging from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) -if _sys.version_info >= (3,3): - from collections import ChainMap as _ChainMap from ._journal import __version__, sendv, stream_fd from ._reader import (_Reader, NOP, APPEND, INVALIDATE, @@ -53,13 +51,16 @@ def _convert_monotonic(m): def _convert_source_monotonic(s): return _datetime.timedelta(microseconds=int(s)) +try: + _LOCAL_TIMEZONE = _datetime.datetime.now().astimezone().tzinfo +except TypeError: + _LOCAL_TIMEZONE = None def _convert_realtime(t): - return _datetime.datetime.fromtimestamp(t / 1000000) - + return _datetime.datetime.fromtimestamp(t / 1000000, _LOCAL_TIMEZONE) def _convert_timestamp(s): - return _datetime.datetime.fromtimestamp(int(s) / 1000000) + return _datetime.datetime.fromtimestamp(int(s) / 1000000, _LOCAL_TIMEZONE) def _convert_trivial(x): @@ -140,7 +141,7 @@ class Reader(_Reader): journal. """ - def __init__(self, flags=None, path=None, files=None, converters=None): + def __init__(self, flags=None, path=None, files=None, converters=None, namespace=None): """Create a new Reader. Argument `flags` defines the open flags of the journal, which can be one @@ -149,8 +150,8 @@ def __init__(self, flags=None, path=None, files=None, converters=None): and SYSTEM_ONLY opens only journal files of system services and the kernel. 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. + path or a file descriptor. Specify `namespace` to read from specific journal + namespace. Note that `flags`, `path`, `files` and `namespace` are exclusive. Argument `converters` is a dictionary which updates the DEFAULT_CONVERTERS to convert journal field values. Field names are used @@ -171,16 +172,10 @@ def __init__(self, flags=None, path=None, files=None, converters=None): else: flags = 0 - super(Reader, self).__init__(flags, path, files) - if _sys.version_info >= (3, 3): - self.converters = _ChainMap() - if converters is not None: - self.converters.maps.append(converters) - self.converters.maps.append(DEFAULT_CONVERTERS) - else: - self.converters = DEFAULT_CONVERTERS.copy() - if converters is not None: - self.converters.update(converters) + super(Reader, self).__init__(flags, path, files, namespace) + self.converters = DEFAULT_CONVERTERS.copy() + if converters is not None: + self.converters.update(converters) def _convert_field(self, key, value): """Convert value using self.converters[key]. @@ -321,11 +316,25 @@ def seek_realtime(self, realtime): >>> j.seek_realtime(yesterday) """ if isinstance(realtime, _datetime.datetime): + try: + realtime = realtime.astimezone() + except TypeError: + # With python2: Required argument 'tz' (pos 1) not found + pass + realtime = int(float(realtime.strftime("%s.%f")) * 1000000) elif not isinstance(realtime, int): realtime = int(realtime * 1000000) return super(Reader, self).seek_realtime(realtime) + def get_start(self): + start = super(Reader, self)._get_start() + return _convert_realtime(start) + + def get_end(self): + end = super(Reader, self)._get_end() + return _convert_realtime(end) + def seek_monotonic(self, monotonic, bootid=None): """Seek to a matching journal entry nearest to `monotonic` time. @@ -565,6 +574,20 @@ def __init__(self, level=_logging.NOTSET, sender_function=send, **kwargs): self.send = sender_function self._extra = kwargs + @classmethod + def with_args(cls, config=None): + """Create a JournalHandler with a configuration dictionary + + This creates a JournalHandler instance, but accepts the parameters through + a dictionary that can be specified as a positional argument. This is useful + in contexts like logging.config.fileConfig, where the syntax does not allow + for positional arguments. + + >>> JournalHandler.with_args({'SYSLOG_IDENTIFIER':'my-cool-app'}) + <...JournalHandler ...> + """ + return cls(**(config or {})) + def emit(self, record): """Write `record` as a journal event. diff --git a/systemd/login.c b/systemd/login.c index ff74dc7..b029229 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -38,7 +38,7 @@ static PyObject* name(PyObject *self, PyObject *args) { \ int r; \ PyObject *ans; \ \ - assert(args == NULL); \ + assert(!args); \ \ r = sd_get_##name(&list); \ if (r < 0) { \ @@ -73,7 +73,7 @@ static PyObject* uids(PyObject *self, PyObject *args) { int r; PyObject *ans; - assert(args == NULL); + assert(!args); r = sd_get_uids(&list); if (r < 0) { @@ -148,7 +148,7 @@ PyDoc_STRVAR(Monitor__doc__, "Monitor may be used to monitor login sessions, users, seats, and virtual\n" "machines/containers. Monitor provides a file descriptor which can be\n" "integrated in an external event loop.\n\n" - "See man:sd_login_monitor_new(3) for the details about what can be monitored."); + "See :manpage:`sd_login_monitor_new(3)` for the details about what can be monitored."); static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) { const char *category = NULL; int r; @@ -183,7 +183,7 @@ PyDoc_STRVAR(Monitor_get_events__doc__, "get_events() -> int\n\n" "Returns a mask of poll() events to wait for on the file descriptor returned\n" "by .fileno().\n\n" - "See man:sd_login_monitor_get_events(3) for further discussion."); + "See :manpage:`sd_login_monitor_get_events(3)` for further discussion."); static PyObject* Monitor_get_events(Monitor *self, PyObject *args) { int r = sd_login_monitor_get_events(self->monitor); set_error(r, NULL, NULL); @@ -200,7 +200,7 @@ PyDoc_STRVAR(Monitor_get_timeout__doc__, "is necessary.\n\n" "The return value must be converted to a relative timeout in\n" "milliseconds if it is to be used as an argument for poll().\n" - "See man:sd_login_monitor_get_timeout(3) for further discussion."); + "See :manpage:`sd_login_monitor_get_timeout(3)` for further discussion."); static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) { int r; uint64_t t; @@ -240,7 +240,7 @@ PyDoc_STRVAR(Monitor_close__doc__, "close() -> None\n\n" "Free resources allocated by this Monitor object.\n" "This method invokes sd_login_monitor_unref().\n" - "See man:sd_login_monitor_unref(3)."); + "See :manpage:`sd_login_monitor_unref(3)`."); static PyObject* Monitor_close(Monitor *self, PyObject *args) { assert(self); assert(!args); @@ -255,7 +255,7 @@ PyDoc_STRVAR(Monitor_flush__doc__, "flush() -> None\n\n" "Reset the wakeup state of the monitor object.\n" "This method invokes sd_login_monitor_flush().\n" - "See man:sd_login_monitor_flush(3)."); + "See :manpage:`sd_login_monitor_flush(3)`."); static PyObject* Monitor_flush(Monitor *self, PyObject *args) { assert(self); assert(!args); @@ -323,7 +323,7 @@ PyMODINIT_FUNC initlogin(void) { return; m = Py_InitModule3("login", methods, module__doc__); - if (m == NULL) + if (!m) return; PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); @@ -351,7 +351,7 @@ PyMODINIT_FUNC PyInit_login(void) { return NULL; m = PyModule_Create(&module); - if (m == NULL) + if (!m) return NULL; if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { diff --git a/systemd/test/test_daemon.py b/systemd/test/test_daemon.py index 1ddb55e..d2eb10f 100644 --- a/systemd/test/test_daemon.py +++ b/systemd/test/test_daemon.py @@ -11,7 +11,7 @@ is_socket_unix, _is_socket_unix, is_socket_sockaddr, _is_socket_sockaddr, is_mq, _is_mq, - listen_fds, + listen_fds, listen_fds_with_names, notify) import pytest @@ -256,7 +256,49 @@ def test_listen_fds_default_unset(): assert listen_fds() == [3] assert listen_fds() == [] +def test_listen_fds_with_names_nothing(): + # make sure we have no fds to listen to, no names + os.unsetenv('LISTEN_FDS') + os.unsetenv('LISTEN_PID') + os.unsetenv('LISTEN_FDNAMES') + + assert listen_fds_with_names() == {} + assert listen_fds_with_names(True) == {} + assert listen_fds_with_names(False) == {} + +def test_listen_fds_with_names_no_names(): + # make sure we have no fds to listen to, no names + os.environ['LISTEN_FDS'] = '1' + os.environ['LISTEN_PID'] = str(os.getpid()) + os.unsetenv('LISTEN_FDNAMES') + + assert listen_fds_with_names(False) == {3: 'unknown'} + assert listen_fds_with_names(True) == {3: 'unknown'} + assert listen_fds_with_names() == {} + +def test_listen_fds_with_names_single(): + # make sure we have no fds to listen to, no names + os.environ['LISTEN_FDS'] = '1' + os.environ['LISTEN_PID'] = str(os.getpid()) + os.environ['LISTEN_FDNAMES'] = 'cmds' + + assert listen_fds_with_names(False) == {3: 'cmds'} + assert listen_fds_with_names() == {3: 'cmds'} + assert listen_fds_with_names(True) == {} + +def test_listen_fds_with_names_multiple(): + # make sure we have no fds to listen to, no names + os.environ['LISTEN_FDS'] = '3' + os.environ['LISTEN_PID'] = str(os.getpid()) + os.environ['LISTEN_FDNAMES'] = 'cmds:data:errs' + + assert listen_fds_with_names(False) == {3: 'cmds', 4: 'data', 5: 'errs'} + assert listen_fds_with_names(True) == {3: 'cmds', 4: 'data', 5: 'errs'} + assert listen_fds_with_names() == {} + def test_notify_no_socket(): + os.environ.pop('NOTIFY_SOCKET', None) + assert notify('READY=1') is False with skip_enosys(): assert notify('FDSTORE=1', fds=[]) is False @@ -302,3 +344,16 @@ def test_notify_with_socket(tmpdir): assert notify('FDSTORE=1', fds=[1, 2]) assert notify('FDSTORE=1', pid=os.getpid()) assert notify('FDSTORE=1', pid=os.getpid(), fds=(1,)) + +def test_daemon_notify_memleak(): + # https://github.com/systemd/python-systemd/pull/51 + fd = 1 + fds = [fd] + ref_cnt = sys.getrefcount(fd) + + try: + notify('', True, 0, fds) + except connection_error: + pass + + assert sys.getrefcount(fd) <= ref_cnt, 'leak' diff --git a/systemd/test/test_id128.py b/systemd/test/test_id128.py new file mode 100644 index 0000000..146ec73 --- /dev/null +++ b/systemd/test/test_id128.py @@ -0,0 +1,46 @@ +import contextlib +import errno +import uuid +import pytest + +from systemd import id128 + +@contextlib.contextmanager +def skip_oserror(code): + try: + yield + except (OSError, IOError) as e: + if e.errno == code: + pytest.skip() + raise + + +def test_randomize(): + u1 = id128.randomize() + u2 = id128.randomize() + assert u1 != u2 + +def test_get_machine(): + u1 = id128.get_machine() + u2 = id128.get_machine() + assert u1 == u2 + +def test_get_machine_app_specific(): + a1 = uuid.uuid1() + a2 = uuid.uuid1() + + with skip_oserror(errno.ENOSYS): + u1 = id128.get_machine_app_specific(a1) + + u2 = id128.get_machine_app_specific(a2) + u3 = id128.get_machine_app_specific(a1) + u4 = id128.get_machine_app_specific(a2) + + assert u1 != u2 + assert u1 == u3 + assert u2 == u4 + +def test_get_boot(): + u1 = id128.get_boot() + u2 = id128.get_boot() + assert u1 == u2 diff --git a/systemd/test/test_journal.py b/systemd/test/test_journal.py index 49e4279..c192136 100644 --- a/systemd/test/test_journal.py +++ b/systemd/test/test_journal.py @@ -6,7 +6,8 @@ import os import time import uuid -import traceback as _traceback +import sys +import traceback from systemd import journal, id128 from systemd.journal import _make_line @@ -30,7 +31,7 @@ def send(self, MESSAGE, MESSAGE_ID=None, args.append('MESSAGE_ID=' + id) if CODE_LINE is CODE_FILE is CODE_FUNC is None: - CODE_FILE, CODE_LINE, CODE_FUNC = _traceback.extract_stack(limit=2)[0][:3] + CODE_FILE, CODE_LINE, CODE_FUNC = traceback.extract_stack(limit=2)[0][:3] if CODE_FILE is not None: args.append('CODE_FILE=' + CODE_FILE) if CODE_LINE is not None: @@ -82,10 +83,14 @@ def test_journalhandler_init_exception(): kw = {' X ':3} with pytest.raises(ValueError): journal.JournalHandler(**kw) + with pytest.raises(ValueError): + journal.JournalHandler.with_args(kw) def test_journalhandler_init(): kw = {'X':3, 'X3':4} journal.JournalHandler(logging.INFO, **kw) + kw['level'] = logging.INFO + journal.JournalHandler.with_args(kw) def test_journalhandler_info(): record = logging.LogRecord('test-logger', logging.INFO, 'testpath', 1, 'test', None, None) @@ -98,6 +103,16 @@ def test_journalhandler_info(): assert 'X=3' in sender.buf[0] assert 'X3=4' in sender.buf[0] + sender = MockSender() + handler = journal.JournalHandler.with_args({'level':logging.INFO, 'X':3, 'X3':4, 'sender_function':sender.send}) + handler.emit(record) + assert len(sender.buf) == 1 + assert 'X=3' in sender.buf[0] + assert 'X3=4' in sender.buf[0] + + # just check that args==None doesn't cause an error + journal.JournalHandler.with_args() + def test_journalhandler_no_message_id(): record = logging.LogRecord('test-logger', logging.INFO, 'testpath', 1, 'test', None, None) sender = MockSender() @@ -276,6 +291,21 @@ def test_reader_convert_entry(tmpdir): 'x2' : ['YYY', 'YYY'], 'y2' : [b'\200\200', b'\200\201']} +def test_reader_convert_timestamps(tmpdir): + j = journal.Reader(path=tmpdir.strpath) + + val = j._convert_field('_SOURCE_REALTIME_TIMESTAMP', 1641651559324187) + if sys.version_info >= (3,): + assert val.tzinfo is not None + + val = j._convert_field('__REALTIME_TIMESTAMP', 1641651559324187) + if sys.version_info >= (3,): + assert val.tzinfo is not None + + val = j._convert_field('COREDUMP_TIMESTAMP', 1641651559324187) + if sys.version_info >= (3,): + assert val.tzinfo is not None + def test_seek_realtime(tmpdir): j = journal.Reader(path=tmpdir.strpath) diff --git a/systemd/util.c b/systemd/util.c index e02c825..a8ca299 100644 --- a/systemd/util.c +++ b/systemd/util.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include