From 683fd2a640db4f973ff5f975f013f302db26e82a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:01:18 +0200 Subject: python: integrate David Strauss' python-systemd package --- systemd/_journal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/systemd/_journal.c b/systemd/_journal.c index b766ec6..3e10981 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -1,7 +1,10 @@ #include + #define SD_JOURNAL_SUPPRESS_LOCATION #include +#include "macro.h" + PyDoc_STRVAR(journal_sendv__doc__, "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" "Send an entry to the journal." -- cgit v1.2.1 From 5db22f490dbe06a98fed3564f82c9e92ef46b9e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:05:28 +0200 Subject: python: change license to LGPL 2.1 The original license has been MIT for this code, but David Strauss (its original author) agreed to relicense it to LGPL 2.1 for inclusion in systemd. --- systemd/__init__.py | 18 ++++++++++++++++++ systemd/_journal.c | 21 +++++++++++++++++++++ systemd/journal.py | 19 +++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/systemd/__init__.py b/systemd/__init__.py index e69de29..0d56b99 100644 --- a/systemd/__init__.py +++ b/systemd/__init__.py @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 David Strauss +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . diff --git a/systemd/_journal.c b/systemd/_journal.c index 3e10981..64310a7 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -1,3 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 David Strauss + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + #include #define SD_JOURNAL_SUPPRESS_LOCATION diff --git a/systemd/journal.py b/systemd/journal.py index 53e992b..0f8a330 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -1,3 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 David Strauss +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + import traceback as _traceback import os as _os from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, -- cgit v1.2.1 From 3bb03b4e4d7a3651ddc8bf91018a4de74d16752d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:16:10 +0200 Subject: python: reindent to follow coding style --- systemd/_journal.c | 186 ++++++++++++++++++++++++++--------------------------- systemd/journal.py | 122 ++++++++++++++++++----------------- 2 files changed, 153 insertions(+), 155 deletions(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index 64310a7..c305b77 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -24,135 +24,129 @@ #define SD_JOURNAL_SUPPRESS_LOCATION #include -#include "macro.h" - PyDoc_STRVAR(journal_sendv__doc__, "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" "Send an entry to the journal." - ); - -static PyObject * -journal_sendv(PyObject *self, PyObject *args) { - struct iovec *iov = NULL; - int argc = PyTuple_Size(args); - int i, r; - PyObject *ret = NULL; - - PyObject **encoded = calloc(argc, sizeof(PyObject*)); - if (!encoded) { - ret = PyErr_NoMemory(); - goto out1; - } - - // Allocate sufficient iovector space for the arguments. - iov = malloc(argc * sizeof(struct iovec)); - if (!iov) { - ret = PyErr_NoMemory(); - goto out; - } - - // Iterate through the Python arguments and fill the iovector. - for (i = 0; i < argc; ++i) { - PyObject *item = PyTuple_GetItem(args, i); - char *stritem; - Py_ssize_t length; - - if (PyUnicode_Check(item)) { - encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict"); - if (encoded[i] == NULL) - goto out; - item = encoded[i]; - } - if (PyBytes_AsStringAndSize(item, &stritem, &length)) - goto out; +); - iov[i].iov_base = stritem; - iov[i].iov_len = length; - } +static PyObject *journal_sendv(PyObject *self, PyObject *args) { + struct iovec *iov = NULL; + int argc; + int i, r; + PyObject *ret = NULL; + PyObject **encoded; - // Clear errno, because sd_journal_sendv will not set it by - // itself, unless an error occurs in one of the system calls. - errno = 0; + argc = PyTuple_Size(args); - // Send the iovector to the journal. - r = sd_journal_sendv(iov, argc); + encoded = calloc(argc, sizeof(PyObject*)); + if (!encoded) { + ret = PyErr_NoMemory(); + goto out1; + } - if (r) { - if (errno) - PyErr_SetFromErrno(PyExc_IOError); - else - PyErr_SetString(PyExc_ValueError, "invalid message format"); - goto out; - } + /* Allocate sufficient iovector space for the arguments. */ + iov = malloc(argc * sizeof(struct iovec)); + if (!iov) { + ret = PyErr_NoMemory(); + goto out; + } - // End with success. - Py_INCREF(Py_None); - ret = Py_None; + /* Iterate through the Python arguments and fill the iovector. */ + for (i = 0; i < argc; ++i) { + PyObject *item = PyTuple_GetItem(args, i); + char *stritem; + Py_ssize_t length; + + if (PyUnicode_Check(item)) { + encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict"); + if (encoded[i] == NULL) + goto out; + item = encoded[i]; + } + if (PyBytes_AsStringAndSize(item, &stritem, &length)) + goto out; + + iov[i].iov_base = stritem; + iov[i].iov_len = length; + } + + /* Clear errno, because sd_journal_sendv will not set it by + itself, unless an error occurs in one of the system calls. */ + errno = 0; + + /* Send the iovector to the journal. */ + r = sd_journal_sendv(iov, argc); + if (r) { + if (errno) + PyErr_SetFromErrno(PyExc_IOError); + else + PyErr_SetString(PyExc_ValueError, "invalid message format"); + goto out; + } + + /* End with success. */ + Py_INCREF(Py_None); + ret = Py_None; out: - for (i = 0; i < argc; ++i) - Py_XDECREF(encoded[i]); + for (i = 0; i < argc; ++i) + Py_XDECREF(encoded[i]); - free(encoded); + free(encoded); out1: - // Free the iovector. The actual strings - // are already managed by Python. - free(iov); + /* Free the iovector. The actual strings + are already managed by Python. */ + free(iov); - return ret; + return ret; } PyDoc_STRVAR(journal_stream_fd__doc__, "stream_fd(identifier, priority, level_prefix) -> fd\n\n" "Open a stream to journal by calling sd_journal_stream_fd(3)." - ); - -static PyObject* -journal_stream_fd(PyObject *self, PyObject *args) { - const char* identifier; - int priority, level_prefix; - int fd; - if (!PyArg_ParseTuple(args, "sii:stream_fd", - &identifier, &priority, &level_prefix)) - return NULL; - - fd = sd_journal_stream_fd(identifier, priority, level_prefix); - if (fd < 0) - return PyErr_SetFromErrno(PyExc_IOError); - - return PyLong_FromLong(fd); +); + +static PyObject* journal_stream_fd(PyObject *self, PyObject *args) { + const char* identifier; + int priority, level_prefix; + int fd; + + if (!PyArg_ParseTuple(args, "sii:stream_fd", + &identifier, &priority, &level_prefix)) + return NULL; + + fd = sd_journal_stream_fd(identifier, priority, level_prefix); + if (fd < 0) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyLong_FromLong(fd); } 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 */ + { "sendv", journal_sendv, METH_VARARGS, journal_sendv__doc__ }, + { "stream_fd", journal_stream_fd, METH_VARARGS, journal_stream_fd__doc__ }, + { NULL, NULL, 0, NULL } /* Sentinel */ }; #if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC -init_journal(void) -{ - (void) Py_InitModule("_journal", methods); +PyMODINIT_FUNC init_journal(void) { + (void) Py_InitModule("_journal", methods); } #else static struct PyModuleDef module = { - PyModuleDef_HEAD_INIT, - "_journal", /* name of module */ - NULL, /* module documentation, may be NULL */ - 0, /* size of per-interpreter state of the module */ - methods + PyModuleDef_HEAD_INIT, + "_journal", /* name of module */ + NULL, /* module documentation, may be NULL */ + 0, /* size of per-interpreter state of the module */ + methods }; -PyMODINIT_FUNC -PyInit__journal(void) -{ - return PyModule_Create(&module); +PyMODINIT_FUNC PyInit__journal(void) { + return PyModule_Create(&module); } #endif diff --git a/systemd/journal.py b/systemd/journal.py index 0f8a330..760d2db 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -24,89 +24,93 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, from ._journal import sendv, stream_fd def _make_line(field, value): - if isinstance(value, bytes): - return field.encode('utf-8') + b'=' + value - else: - return field + '=' + value + if isinstance(value, bytes): + return field.encode('utf-8') + b'=' + value + else: + return field + '=' + value def send(MESSAGE, MESSAGE_ID=None, CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None, **kwargs): - r"""Send a message to journald. + r"""Send a message to journald. - >>> journal.send('Hello world') - >>> journal.send('Hello, again, world', FIELD2='Greetings!') - >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') + >>> journal.send('Hello world') + >>> journal.send('Hello, again, world', FIELD2='Greetings!') + >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') - Value of the MESSAGE argument will be used for the MESSAGE= field. + Value of the MESSAGE argument will be used for the MESSAGE= + field. - MESSAGE_ID can be given to uniquely identify the type of message. + MESSAGE_ID can be given to uniquely identify the type of + message. - Other parts of the message can be specified as keyword arguments. + Other parts of the message can be specified as keyword + arguments. - Both MESSAGE and MESSAGE_ID, if present, must be strings, and will - be sent as UTF-8 to journal. Other arguments can be bytes, in - which case they will be sent as-is to journal. + Both MESSAGE and MESSAGE_ID, if present, must be strings, and + will be sent as UTF-8 to journal. Other arguments can be + bytes, in which case they will be sent as-is to journal. - CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to identify - the caller. Unless at least on of the three is given, values are - extracted from the stack frame of the caller of send(). CODE_FILE - and CODE_FUNC must be strings, CODE_LINE must be an integer. + CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to + identify the caller. Unless at least on of the three is given, + values are extracted from the stack frame of the caller of + send(). CODE_FILE and CODE_FUNC must be strings, CODE_LINE + must be an integer. - Other useful fields include PRIORITY, SYSLOG_FACILITY, - SYSLOG_IDENTIFIER, SYSLOG_PID. - """ + Other useful fields include PRIORITY, SYSLOG_FACILITY, + SYSLOG_IDENTIFIER, SYSLOG_PID. + """ - args = ['MESSAGE=' + MESSAGE] + args = ['MESSAGE=' + MESSAGE] - if MESSAGE_ID is not None: - args.append('MESSAGE_ID=' + MESSAGE_ID) + if MESSAGE_ID is not None: + args.append('MESSAGE_ID=' + MESSAGE_ID) - if CODE_LINE == CODE_FILE == CODE_FUNC == None: - 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: - args.append('CODE_LINE={:d}'.format(CODE_LINE)) - if CODE_FUNC is not None: - args.append('CODE_FUNC=' + CODE_FUNC) + if CODE_LINE == CODE_FILE == CODE_FUNC == None: + 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: + args.append('CODE_LINE={:d}'.format(CODE_LINE)) + if CODE_FUNC is not None: + args.append('CODE_FUNC=' + CODE_FUNC) - args.extend(_make_line(key, val) for key, val in kwargs.items()) - return sendv(*args) + args.extend(_make_line(key, val) for key, val in kwargs.items()) + return sendv(*args) def stream(identifier, priority=LOG_DEBUG, level_prefix=False): - r"""Return a file object wrapping a stream to journal. + r"""Return a file object wrapping a stream to journal. - Log messages written to this file as simple newline sepearted - text strings are written to the journal. + Log messages written to this file as simple newline sepearted + text strings are written to the journal. - The file will be line buffered, so messages are actually sent - after a newline character is written. + The file will be line buffered, so messages are actually sent + after a newline character is written. - >>> stream = journal.stream('myapp') - >>> stream - ', mode 'w' at 0x...> - >>> stream.write('message...\n') + >>> stream = journal.stream('myapp') + >>> stream + ', mode 'w' at 0x...> + >>> stream.write('message...\n') - will produce the following message in the journal: + will produce the following message in the journal: - PRIORITY=7 - SYSLOG_IDENTIFIER=myapp - MESSAGE=message... + PRIORITY=7 + SYSLOG_IDENTIFIER=myapp + MESSAGE=message... - Using the interface with print might be more convinient: + Using the interface with print might be more convinient: - >>> from __future__ import print_function - >>> print('message...', file=stream) + >>> from __future__ import print_function + >>> print('message...', file=stream) - priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, - LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. + priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, + LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. - level_prefix is a boolean. If true, kernel-style log priority - level prefixes (such as '<1>') are interpreted. See sd-daemon(3) - for more information. - """ + level_prefix is a boolean. If true, kernel-style log priority + level prefixes (such as '<1>') are interpreted. See + sd-daemon(3) for more information. + """ - fd = stream_fd(identifier, priority, level_prefix) - return _os.fdopen(fd, 'w', 1) + fd = stream_fd(identifier, priority, level_prefix) + return _os.fdopen(fd, 'w', 1) -- cgit v1.2.1 From da42130ac5906d8d5d5ed315aeca72e4bbfb8201 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:25:59 +0200 Subject: python: fix error handling, and allocate argument array on the stack --- systemd/_journal.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index c305b77..eab9c29 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -21,6 +21,8 @@ #include +#include + #define SD_JOURNAL_SUPPRESS_LOCATION #include @@ -36,20 +38,13 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { PyObject *ret = NULL; PyObject **encoded; + /* Allocate an array for the argument strings */ argc = PyTuple_Size(args); - - encoded = calloc(argc, sizeof(PyObject*)); - if (!encoded) { - ret = PyErr_NoMemory(); - goto out1; - } + encoded = alloca(argc * sizeof(PyObject*)); + memset(encoded, 0, argc * sizeof(PyObject*)); /* Allocate sufficient iovector space for the arguments. */ - iov = malloc(argc * sizeof(struct iovec)); - if (!iov) { - ret = PyErr_NoMemory(); - goto out; - } + iov = alloca(argc * sizeof(struct iovec)); /* Iterate through the Python arguments and fill the iovector. */ for (i = 0; i < argc; ++i) { @@ -70,17 +65,11 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { iov[i].iov_len = length; } - /* Clear errno, because sd_journal_sendv will not set it by - itself, unless an error occurs in one of the system calls. */ - errno = 0; - /* Send the iovector to the journal. */ r = sd_journal_sendv(iov, argc); - if (r) { - if (errno) - PyErr_SetFromErrno(PyExc_IOError); - else - PyErr_SetString(PyExc_ValueError, "invalid message format"); + if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_IOError); goto out; } @@ -92,13 +81,6 @@ out: for (i = 0; i < argc; ++i) Py_XDECREF(encoded[i]); - free(encoded); - -out1: - /* Free the iovector. The actual strings - are already managed by Python. */ - free(iov); - return ret; } @@ -117,8 +99,10 @@ static PyObject* journal_stream_fd(PyObject *self, PyObject *args) { return NULL; fd = sd_journal_stream_fd(identifier, priority, level_prefix); - if (fd < 0) + if (fd < 0) { + errno = -fd; return PyErr_SetFromErrno(PyExc_IOError); + } return PyLong_FromLong(fd); } -- cgit v1.2.1 From 059247a62f40c7e045181d41f0e28f5d83a47436 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 19:34:09 +0200 Subject: python: make gcc shut up --- systemd/_journal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/systemd/_journal.c b/systemd/_journal.c index eab9c29..d27178d 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -113,6 +113,9 @@ static PyMethodDef methods[] = { { NULL, NULL, 0, NULL } /* Sentinel */ }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_journal(void) { @@ -133,4 +136,6 @@ PyMODINIT_FUNC PyInit__journal(void) { return PyModule_Create(&module); } +#pragma GCC diagnostic pop + #endif -- cgit v1.2.1 From 4b22c017938e3f23b03bdd2c1561e8504ee83854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 1 Nov 2012 00:10:47 +0100 Subject: systemd-python: fix nesting of #ifs and #pragmas --- systemd/_journal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index d27178d..0bdf709 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -136,6 +136,6 @@ PyMODINIT_FUNC PyInit__journal(void) { return PyModule_Create(&module); } -#pragma GCC diagnostic pop - #endif + +#pragma GCC diagnostic pop -- cgit v1.2.1 From 603876167a7ea78d0a71d70766f65979618ca423 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Tue, 9 Oct 2012 18:12:02 +0300 Subject: python: add journal backend for the logging framework Supports Python versions 2.6 through 3.3 (tested on 2.7 and 3.2). See JournalHandler docstring for usage details. [zj: - use send() instead of using sendv() directly - do exception handling like in the logging module - bumped min version to python2.6, since the module does not work with python2.5 anyway ] --- systemd/journal.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index 760d2db..516ca1a 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -19,6 +19,7 @@ import traceback as _traceback import os as _os +import logging as _logging from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd @@ -114,3 +115,87 @@ def stream(identifier, priority=LOG_DEBUG, level_prefix=False): fd = stream_fd(identifier, priority, level_prefix) return _os.fdopen(fd, 'w', 1) + +class JournalHandler(_logging.Handler): + """Journal handler class for the Python logging framework. + + Please see the Python logging module documentation for an + overview: http://docs.python.org/library/logging.html + + To create a custom logger whose messages go only to journal: + + >>> log = logging.getLogger('custom_logger_name') + >>> log.propagate = False + >>> log.addHandler(journal.JournalHandler()) + >>> log.warn("Some message: %s", detail) + + Note that by default, message levels INFO and DEBUG are ignored + by the logging framework. To enable those log levels: + + >>> log.setLevel(logging.DEBUG) + + To attach journal MESSAGE_ID, an extra field is supported: + + >>> log.warn("Message with ID", + >>> extra={'MESSAGE_ID': '22bb01335f724c959ac4799627d1cb61'}) + + To redirect all logging messages to journal regardless of where + they come from, attach it to the root logger: + + >>> logging.root.addHandler(journal.JournalHandler()) + + For more complex configurations when using dictConfig or + fileConfig, specify 'systemd.journal.JournalHandler' as the + handler class. Only standard handler configuration options + are supported: level, formatter, filters. + + The following journal fields will be sent: + + MESSAGE, PRIORITY, THREAD_NAME, CODE_FILE, CODE_LINE, + CODE_FUNC, LOGGER (name as supplied to getLogger call), + MESSAGE_ID (optional, see above). + """ + + def emit(self, record): + """Write record as journal event. + + MESSAGE is taken from the message provided by the + user, and PRIORITY, LOGGER, THREAD_NAME, + CODE_{FILE,LINE,FUNC} fields are appended + automatically. In addition, record.MESSAGE_ID will be + used if present. + """ + try: + msg = self.format(record) + pri = self.mapPriority(record.levelno) + mid = getattr(record, 'MESSAGE_ID', None) + send(msg, + MESSAGE_ID=mid, + PRIORITY=format(pri), + LOGGER=record.name, + THREAD_NAME=record.threadName, + CODE_FILE=record.pathname, + CODE_LINE=record.lineno, + CODE_FUNC=record.funcName) + except Exception: + self.handleError(record) + + @staticmethod + def mapPriority(levelno): + """Map logging levels to journald priorities. + + Since Python log level numbers are "sparse", we have + to map numbers in between the standard levels too. + """ + if levelno <= _logging.DEBUG: + return LOG_DEBUG + elif levelno <= _logging.INFO: + return LOG_INFO + elif levelno <= _logging.WARNING: + return LOG_WARNING + elif levelno <= _logging.ERROR: + return LOG_ERR + elif levelno <= _logging.CRITICAL: + return LOG_CRIT + else: + return LOG_ALERT -- cgit v1.2.1 From 37f8da33632600f8255ef2767bbdb9fca0c4cb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 5 Feb 2013 21:44:46 -0500 Subject: python: add systemd.id128 module uuid.UUIDs are utilized to hold UUID values. --- systemd/.gitignore | 1 + systemd/_journal.c | 2 +- systemd/id128.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ systemd/journal.py | 8 ++- 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 systemd/.gitignore create mode 100644 systemd/id128.c diff --git a/systemd/.gitignore b/systemd/.gitignore new file mode 100644 index 0000000..ed3a500 --- /dev/null +++ b/systemd/.gitignore @@ -0,0 +1 @@ +/id128-constants.h diff --git a/systemd/_journal.c b/systemd/_journal.c index 0bdf709..ced52b2 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright 2012 David Strauss + Copyright 2012 David Strauss systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/systemd/id128.c b/systemd/id128.c new file mode 100644 index 0000000..f82b0af --- /dev/null +++ b/systemd/id128.c @@ -0,0 +1,157 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) + +static void cleanup_Py_DECREFp(PyObject **p) { + if (!*p) + return; + + Py_DECREF(*p); +} + +PyDoc_STRVAR(module__doc__, + "Python interface to the libsystemd-id128 library.\n\n" + "Provides SD_MESSAGE_* constants and functions to query and generate\n" + "128bit unique identifiers." +); + +PyDoc_STRVAR(randomize__doc__, + "randomize() -> UUID\n\n" + "Return a new random 128bit unique identifier.\n" + "Wraps sd_id128_randomize(3)." +); + +PyDoc_STRVAR(get_machine__doc__, + "get_machine() -> UUID\n\n" + "Return a 128bit unique identifier for this machine.\n" + "Wraps sd_id128_get_machine(3)." +); + +PyDoc_STRVAR(get_boot__doc__, + "get_boot() -> UUID\n\n" + "Return a 128bit unique identifier for this boot.\n" + "Wraps sd_id128_get_boot(3)." +); + +static PyObject* make_uuid(sd_id128_t id) { + PyObject _cleanup_Py_DECREF_ + *uuid = NULL, *UUID = NULL, *bytes = NULL, + *args = NULL, *kwargs = NULL, *obj = NULL; + + uuid = PyImport_ImportModule("uuid"); + if (!uuid) + return NULL; + + UUID = PyObject_GetAttrString(uuid, "UUID"); + bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); + args = Py_BuildValue("()"); + kwargs = PyDict_New(); + if (!UUID || !bytes || !args || !kwargs) + return NULL; + + if (PyDict_SetItemString(kwargs, "bytes", bytes) < 0) + return NULL; + + return PyObject_Call(UUID, args, kwargs); +} + +#define helper(name) \ + static PyObject *name(PyObject *self, PyObject *args) { \ + sd_id128_t id; \ + int r; \ + \ + assert(args == NULL); \ + \ + r = sd_id128_##name(&id); \ + if (r < 0) { \ + errno = -r; \ + return PyErr_SetFromErrno(PyExc_IOError); \ + } \ + \ + return make_uuid(id); \ + } + +helper(randomize) +helper(get_machine) +helper(get_boot) + +static PyMethodDef methods[] = { + { "randomize", randomize, METH_NOARGS, randomize__doc__}, + { "get_machine", get_machine, METH_NOARGS, get_machine__doc__}, + { "get_boot", get_boot, METH_NOARGS, get_boot__doc__}, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +static int add_id(PyObject *module, const char* name, sd_id128_t id) { + PyObject _cleanup_Py_DECREF_ *obj; + + obj = make_uuid(id); + if (!obj) + return -1; + + return PyObject_SetAttrString(module, name, obj); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC initid128(void) { + PyObject *m; + + m = Py_InitModule3("id128", methods, module__doc__); + if (m == NULL) + return; + +#include "id128-constants.h" +} + +#else + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "id128", /* name of module */ + module__doc__, /* module documentation, may be NULL */ + 0, /* size of per-interpreter state of the module */ + methods +}; + +PyMODINIT_FUNC PyInit_id128(void) { + PyObject *m; + + m = PyModule_Create(&module); + if (m == NULL) + return NULL; + +#include "id128-constants.h" + + return m; +} + +#endif + +#pragma GCC diagnostic pop diff --git a/systemd/journal.py b/systemd/journal.py index 516ca1a..fc57437 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -1,8 +1,10 @@ -# -*- Mode: python; indent-tabs-mode: nil -*- */ +# -*- Mode: python; coding:utf-8; indent-tabs-mode: nil -*- */ # # This file is part of systemd. # -# Copyright 2012 David Strauss +# Copyright 2012 David Strauss +# Copyright 2012 Zbigniew Jędrzejewski-Szmek +# Copyright 2012 Marti Raudsepp # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by @@ -120,7 +122,7 @@ class JournalHandler(_logging.Handler): """Journal handler class for the Python logging framework. Please see the Python logging module documentation for an - overview: http://docs.python.org/library/logging.html + overview: http://docs.python.org/library/logging.html. To create a custom logger whose messages go only to journal: -- cgit v1.2.1 From d6cd30aa2e85fa2db9821303cd696c54eb8e63bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 9 Feb 2013 12:20:05 -0500 Subject: python: utilize uuid.UUID in logging --- systemd/journal.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index fc57437..d610b47 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -35,24 +35,18 @@ def _make_line(field, value): def send(MESSAGE, MESSAGE_ID=None, CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None, **kwargs): - r"""Send a message to journald. + r"""Send a message to the journal. >>> journal.send('Hello world') >>> journal.send('Hello, again, world', FIELD2='Greetings!') >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') Value of the MESSAGE argument will be used for the MESSAGE= - field. + field. MESSAGE must be a string and will be sent as UTF-8 to + the journal. MESSAGE_ID can be given to uniquely identify the type of - message. - - Other parts of the message can be specified as keyword - arguments. - - Both MESSAGE and MESSAGE_ID, if present, must be strings, and - will be sent as UTF-8 to journal. Other arguments can be - bytes, in which case they will be sent as-is to journal. + message. It must be a string or a uuid.UUID object. CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to identify the caller. Unless at least on of the three is given, @@ -60,6 +54,11 @@ def send(MESSAGE, MESSAGE_ID=None, send(). CODE_FILE and CODE_FUNC must be strings, CODE_LINE must be an integer. + Additional fields for the journal entry can only be specified + as keyword arguments. The payload can be either a string or + bytes. A string will be sent as UTF-8, and bytes will be sent + as-is to the journal. + Other useful fields include PRIORITY, SYSLOG_FACILITY, SYSLOG_IDENTIFIER, SYSLOG_PID. """ @@ -67,7 +66,8 @@ def send(MESSAGE, MESSAGE_ID=None, args = ['MESSAGE=' + MESSAGE] if MESSAGE_ID is not None: - args.append('MESSAGE_ID=' + MESSAGE_ID) + id = getattr(MESSAGE_ID, 'hex', MESSAGE_ID) + args.append('MESSAGE_ID=' + id) if CODE_LINE == CODE_FILE == CODE_FUNC == None: CODE_FILE, CODE_LINE, CODE_FUNC = \ @@ -138,8 +138,9 @@ class JournalHandler(_logging.Handler): To attach journal MESSAGE_ID, an extra field is supported: - >>> log.warn("Message with ID", - >>> extra={'MESSAGE_ID': '22bb01335f724c959ac4799627d1cb61'}) + >>> import uuid + >>> mid = uuid.UUID('0123456789ABCDEF0123456789ABCDEF') + >>> log.warn("Message with ID", extra={'MESSAGE_ID': mid}) To redirect all logging messages to journal regardless of where they come from, attach it to the root logger: -- cgit v1.2.1 From 0c023a2c03776f6876c4779e90ccfcb3142babe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 9 Feb 2013 15:37:35 -0500 Subject: python: build html docs using sphinx Build instructions: make make DESTIDIR=/tmp/... install make DESTIDIR=/tmp/... sphinx-html sphinx-man sphinx-epub ... --- systemd/.gitignore | 1 + systemd/docs/conf.py | 288 +++++++++++++++++++++++++++++++++++++++++++++++ systemd/docs/id128.rst | 7 ++ systemd/docs/index.rst | 22 ++++ systemd/docs/journal.rst | 11 ++ systemd/journal.py | 30 ++--- 6 files changed, 344 insertions(+), 15 deletions(-) create mode 100644 systemd/docs/conf.py create mode 100644 systemd/docs/id128.rst create mode 100644 systemd/docs/index.rst create mode 100644 systemd/docs/journal.rst diff --git a/systemd/.gitignore b/systemd/.gitignore index ed3a500..4124b7a 100644 --- a/systemd/.gitignore +++ b/systemd/.gitignore @@ -1 +1,2 @@ /id128-constants.h +*.py[oc] diff --git a/systemd/docs/conf.py b/systemd/docs/conf.py new file mode 100644 index 0000000..4a55778 --- /dev/null +++ b/systemd/docs/conf.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# +# python-systemd documentation build configuration file, created by +# sphinx-quickstart on Sat Feb 9 13:49:42 2013. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'python-systemd' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '197' +# The full version, including alpha/beta/rc tags. +release = '197' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'python-systemddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'python-systemd.tex', u'python-systemd Documentation', + None, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'python-systemd', u'python-systemd Documentation', + [], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'python-systemd', u'python-systemd Documentation', + u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks', 'python-systemd', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# -- Options for Epub output --------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'python-systemd' +epub_author = u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks' +epub_publisher = u'David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks' +epub_copyright = u'2013, David Strauss, Zbigniew Jędrzejewski-Szmek, Marti Raudsepp, Steven Hiscocks' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +#epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/systemd/docs/id128.rst b/systemd/docs/id128.rst new file mode 100644 index 0000000..e817d80 --- /dev/null +++ b/systemd/docs/id128.rst @@ -0,0 +1,7 @@ +`systemd.id128` module +====================== + +.. automodule:: systemd.id128 + :members: + :undoc-members: + :inherited-members: diff --git a/systemd/docs/index.rst b/systemd/docs/index.rst new file mode 100644 index 0000000..f04d5a1 --- /dev/null +++ b/systemd/docs/index.rst @@ -0,0 +1,22 @@ +.. python-systemd documentation master file, created by + sphinx-quickstart on Sat Feb 9 13:49:42 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to python-systemd's documentation! +========================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + journal + id128 + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst new file mode 100644 index 0000000..036250a --- /dev/null +++ b/systemd/docs/journal.rst @@ -0,0 +1,11 @@ +`systemd.journal` module +======================== + +.. automodule:: systemd.journal + :members: send, sendv, stream, stream_fd + :undoc-members: + +`JournalHandler` class +---------------------- + +.. autoclass:: JournalHandler diff --git a/systemd/journal.py b/systemd/journal.py index d610b47..47849a3 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -96,19 +96,20 @@ def stream(identifier, priority=LOG_DEBUG, level_prefix=False): ', mode 'w' at 0x...> >>> stream.write('message...\n') - will produce the following message in the journal: + will produce the following message in the journal:: - PRIORITY=7 - SYSLOG_IDENTIFIER=myapp - MESSAGE=message... + PRIORITY=7 + SYSLOG_IDENTIFIER=myapp + MESSAGE=message... Using the interface with print might be more convinient: >>> from __future__ import print_function >>> print('message...', file=stream) - priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, - LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. + priority is the syslog priority, one of `LOG_EMERG`, + `LOG_ALERT`, `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, + `LOG_NOTICE`, `LOG_INFO`, `LOG_DEBUG`. level_prefix is a boolean. If true, kernel-style log priority level prefixes (such as '<1>') are interpreted. See @@ -131,8 +132,8 @@ class JournalHandler(_logging.Handler): >>> log.addHandler(journal.JournalHandler()) >>> log.warn("Some message: %s", detail) - Note that by default, message levels INFO and DEBUG are ignored - by the logging framework. To enable those log levels: + Note that by default, message levels `INFO` and `DEBUG` are + ignored by the logging framework. To enable those log levels: >>> log.setLevel(logging.DEBUG) @@ -147,16 +148,15 @@ class JournalHandler(_logging.Handler): >>> logging.root.addHandler(journal.JournalHandler()) - For more complex configurations when using dictConfig or - fileConfig, specify 'systemd.journal.JournalHandler' as the + For more complex configurations when using `dictConfig` or + `fileConfig`, specify `systemd.journal.JournalHandler` as the handler class. Only standard handler configuration options - are supported: level, formatter, filters. + are supported: `level`, `formatter`, `filters`. The following journal fields will be sent: - - MESSAGE, PRIORITY, THREAD_NAME, CODE_FILE, CODE_LINE, - CODE_FUNC, LOGGER (name as supplied to getLogger call), - MESSAGE_ID (optional, see above). + `MESSAGE`, `PRIORITY`, `THREAD_NAME`, `CODE_FILE`, `CODE_LINE`, + `CODE_FUNC`, `LOGGER` (name as supplied to getLogger call), + `MESSAGE_ID` (optional, see above). """ def emit(self, record): -- cgit v1.2.1 From 3503342886daf5d4bfa802ce176da50ad886b2c8 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 8 Feb 2013 19:41:21 +0000 Subject: systemd-python: add Journal class for reading journal --- systemd/_reader.c | 1163 ++++++++++++++++++++++++++++++++++++++++++++++++++++ systemd/journal.py | 2 + 2 files changed, 1165 insertions(+) create mode 100644 systemd/_reader.c diff --git a/systemd/_reader.c b/systemd/_reader.c new file mode 100644 index 0000000..963da11 --- /dev/null +++ b/systemd/_reader.c @@ -0,0 +1,1163 @@ +/* +_reader - Python module that reads systemd journal similar to journalctl +Copyright (C) 2012 Steven Hiscocks + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include + +#include +#include +#include + +typedef struct { + PyObject_HEAD + sd_journal *j; + PyObject *default_call; + PyObject *call_dict; +} Journal; +static PyTypeObject JournalType; + +static void +Journal_dealloc(Journal* self) +{ + sd_journal_close(self->j); + Py_XDECREF(self->default_call); + Py_XDECREF(self->call_dict); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +Journal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + Journal *self; + + self = (Journal *)type->tp_alloc(type, 0); + if (self != NULL) { + PyObject *globals, *temp; + + globals = PyEval_GetBuiltins(); + temp = PyImport_ImportModule("functools"); + PyDict_SetItemString(globals, "functools", temp); + Py_DECREF(temp); + temp = PyImport_ImportModule("datetime"); + PyDict_SetItemString(globals, "datetime", temp); + Py_DECREF(temp); + +#if PY_MAJOR_VERSION >=3 + self->default_call = PyRun_String("functools.partial(str, encoding='utf-8')", Py_eval_input, globals, NULL); +#else + self->default_call = PyRun_String("functools.partial(unicode, encoding='utf-8')", Py_eval_input, globals, NULL); +#endif + + self->call_dict = PyRun_String("{" + "'PRIORITY': int," + "'LEADER': int," + "'SESSION_ID': int," + "'USERSPACE_USEC': int," + "'INITRD_USEC': int," + "'KERNEL_USEC': int," + "'_UID': int," + "'_GID': int," + "'_PID': int," + "'SYSLOG_FACILITY': int," + "'SYSLOG_PID': int," + "'_AUDIT_SESSION': int," + "'_AUDIT_LOGINUID': int," + "'_SYSTEMD_SESSION': int," + "'_SYSTEMD_OWNER_UID': int," + "'CODE_LINE': int," + "'ERRNO': int," + "'EXIT_STATUS': int," + "'_SOURCE_REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," + "'__REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," + "'_SOURCE_MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x))," + "'__MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x))," +#if PY_MAJOR_VERSION >=3 + "'COREDUMP': bytes," +#else + "'COREDUMP': str," +#endif + "'COREDUMP_PID': int," + "'COREDUMP_UID': int," + "'COREDUMP_GID': int," + "'COREDUMP_SESSION': int," + "'COREDUMP_SIGNAL': int," + "'COREDUMP_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," + "}", Py_eval_input, globals, NULL); + } + + return (PyObject *) self; +} + +PyDoc_STRVAR(Journal__doc__, +"Journal([flags][, default_call][, call_dict][,path]) -> ...\n" +"Journal instance\n\n" +"Returns instance of Journal, which allows filtering and return\n" +"of journal entries.\n" +"Argument `flags` sets open flags of the journal, which can be one\n" +"of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" +"journal on local machine only; RUNTIME_ONLY opens only\n" +"volatile journal files; and SYSTEM_ONLY opens only\n" +"journal files of system services and the kernel.\n" +"Argument `default_call` must be a callable that accepts one\n" +"argument which is string/bytes value of a field and returns\n" +"python object.\n" +"Argument `call_dict` is a dictionary where the key represents\n" +"a field name, and value is a callable as per `default_call`.\n" +"A set of sane defaults for `default_call` and `call_dict` are\n" +"present.\n" +"Argument `path` is the directory of journal files. Note that\n" +"currently flags are ignored when `path` is present as they are\n" +" not relevant."); +static int +Journal_init(Journal *self, PyObject *args, PyObject *keywds) +{ + int flags=SD_JOURNAL_LOCAL_ONLY; + char *path=NULL; + PyObject *default_call=NULL, *call_dict=NULL; + + static char *kwlist[] = {"flags", "default_call", "call_dict", "path", NULL}; + if (! PyArg_ParseTupleAndKeywords(args, keywds, "|iOOs", kwlist, + &flags, &default_call, &call_dict, &path)) + return 1; + + if (default_call) { + if (PyCallable_Check(default_call) || default_call == Py_None) { + Py_DECREF(self->default_call); + self->default_call = default_call; + Py_INCREF(self->default_call); + }else{ + PyErr_SetString(PyExc_TypeError, "Default call not callable"); + return 1; + } + } + + if (call_dict) { + if (PyDict_Check(call_dict)) { + Py_DECREF(self->call_dict); + self->call_dict = call_dict; + Py_INCREF(self->call_dict); + }else if (call_dict == Py_None) { + Py_DECREF(self->call_dict); + self->call_dict = PyDict_New(); + }else{ + PyErr_SetString(PyExc_TypeError, "Call dictionary must be dict type"); + return 1; + } + } + + int r; + if (path) { + r = sd_journal_open_directory(&self->j, path, 0); + }else{ + Py_BEGIN_ALLOW_THREADS + r = sd_journal_open(&self->j, flags); + Py_END_ALLOW_THREADS + } + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid flags or path"); + return -1; + }else if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return 1; + }else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error opening journal"); + return 1; + } + + return 0; +} + +static PyObject * +Journal___process_field(Journal *self, PyObject *key, const void *value, ssize_t value_len) +{ + PyObject *callable=NULL, *return_value=NULL; + if (PyDict_Check(self->call_dict)) + callable = PyDict_GetItem(self->call_dict, key); + + if (PyCallable_Check(callable)) { +#if PY_MAJOR_VERSION >=3 + return_value = PyObject_CallFunction(callable, "y#", value, value_len); +#else + return_value = PyObject_CallFunction(callable, "s#", value, value_len); +#endif + if (!return_value) + PyErr_Clear(); + } + if (!return_value && PyCallable_Check(self->default_call)) +#if PY_MAJOR_VERSION >=3 + return_value = PyObject_CallFunction(self->default_call, "y#", value, value_len); +#else + return_value = PyObject_CallFunction(self->default_call, "s#", value, value_len); +#endif + if (!return_value) { + PyErr_Clear(); +#if PY_MAJOR_VERSION >=3 + return_value = PyBytes_FromStringAndSize(value, value_len); +#else + return_value = PyString_FromStringAndSize(value, value_len); +#endif + } + if (!return_value) { + return_value = Py_None; + } + return return_value; +} + +PyDoc_STRVAR(Journal_get_next__doc__, +"get_next([skip]) -> dict\n\n" +"Return dictionary of the next log entry. Optional skip value will\n" +"return the `skip`th log entry."); +static PyObject * +Journal_get_next(Journal *self, PyObject *args) +{ + int64_t skip=1LL; + if (! PyArg_ParseTuple(args, "|L", &skip)) + return NULL; + + int r; + if (skip == 1LL) { + Py_BEGIN_ALLOW_THREADS + r = sd_journal_next(self->j); + Py_END_ALLOW_THREADS + }else if (skip == -1LL) { + Py_BEGIN_ALLOW_THREADS + r = sd_journal_previous(self->j); + Py_END_ALLOW_THREADS + }else if (skip > 1LL) { + Py_BEGIN_ALLOW_THREADS + r = sd_journal_next_skip(self->j, skip); + Py_END_ALLOW_THREADS + }else if (skip < -1LL) { + Py_BEGIN_ALLOW_THREADS + r = sd_journal_previous_skip(self->j, -skip); + Py_END_ALLOW_THREADS + }else{ + PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer"); + return NULL; + } + + if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error getting next message"); + return NULL; + }else if ( r == 0) { //EOF + return PyDict_New(); + } + + PyObject *dict; + dict = PyDict_New(); + + const void *msg; + size_t msg_len; + const char *delim_ptr; + PyObject *key, *value, *cur_value, *tmp_list; + + SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { + delim_ptr = memchr(msg, '=', msg_len); +#if PY_MAJOR_VERSION >=3 + key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); +#else + key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg); +#endif + value = Journal___process_field(self, key, delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) ); + if (PyDict_Contains(dict, key)) { + cur_value = PyDict_GetItem(dict, key); + if (PyList_CheckExact(cur_value) && PyList_Size(cur_value) > 1) { + PyList_Append(cur_value, value); + }else{ + tmp_list = PyList_New(0); + PyList_Append(tmp_list, cur_value); + PyList_Append(tmp_list, value); + PyDict_SetItem(dict, key, tmp_list); + Py_DECREF(tmp_list); + } + }else{ + PyDict_SetItem(dict, key, value); + } + Py_DECREF(key); + Py_DECREF(value); + } + + uint64_t realtime; + if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) { + char realtime_str[20]; + sprintf(realtime_str, "%llu", (long long unsigned) realtime); + +#if PY_MAJOR_VERSION >=3 + key = PyUnicode_FromString("__REALTIME_TIMESTAMP"); +#else + key = PyString_FromString("__REALTIME_TIMESTAMP"); +#endif + value = Journal___process_field(self, key, realtime_str, strlen(realtime_str)); + PyDict_SetItem(dict, key, value); + Py_DECREF(key); + Py_DECREF(value); + } + + sd_id128_t sd_id; + uint64_t monotonic; + if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) { + char monotonic_str[20]; + sprintf(monotonic_str, "%llu", (long long unsigned) monotonic); +#if PY_MAJOR_VERSION >=3 + key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP"); +#else + key = PyString_FromString("__MONOTONIC_TIMESTAMP"); +#endif + value = Journal___process_field(self, key, monotonic_str, strlen(monotonic_str)); + + PyDict_SetItem(dict, key, value); + Py_DECREF(key); + Py_DECREF(value); + } + + char *cursor; + if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0... +#if PY_MAJOR_VERSION >=3 + key = PyUnicode_FromString("__CURSOR"); +#else + key = PyString_FromString("__CURSOR"); +#endif + value = Journal___process_field(self, key, cursor, strlen(cursor)); + PyDict_SetItem(dict, key, value); + free(cursor); + Py_DECREF(key); + Py_DECREF(value); + } + + return dict; +} + +PyDoc_STRVAR(Journal_get_previous__doc__, +"get_previous([skip]) -> dict\n\n" +"Return dictionary of the previous log entry. Optional skip value\n" +"will return the -`skip`th log entry. Equivalent to get_next(-skip)."); +static PyObject * +Journal_get_previous(Journal *self, PyObject *args) +{ + int64_t skip=1LL; + if (! PyArg_ParseTuple(args, "|L", &skip)) + return NULL; + + PyObject *dict, *arg; + arg = Py_BuildValue("(L)", -skip); + dict = Journal_get_next(self, arg); + Py_DECREF(arg); + return dict; +} + +PyDoc_STRVAR(Journal_add_match__doc__, +"add_match(match, ..., field=value, ...) -> None\n\n" +"Add a match to filter journal log entries. All matches of different\n" +"field are combined in logical AND, and matches of the same field\n" +"are automatically combined in logical OR.\n" +"Matches can be passed as strings \"field=value\", or keyword\n" +"arguments field=\"value\"."); +static PyObject * +Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) +{ + Py_ssize_t arg_match_len; + char *arg_match; + int i, r; + for (i = 0; i < PySequence_Size(args); i++) { +#if PY_MAJOR_VERSION >=3 + PyObject *arg; + arg = PySequence_Fast_GET_ITEM(args, i); + if (PyUnicode_Check(arg)) { +#if PY_MINOR_VERSION >=3 + arg_match = PyUnicode_AsUTF8AndSize(arg, &arg_match_len); +#else + PyObject *temp; + temp = PyUnicode_AsUTF8String(arg); + PyBytes_AsStringAndSize(temp, &arg_match, &arg_match_len); + Py_DECREF(temp); +#endif + }else if (PyBytes_Check(arg)) { + PyBytes_AsStringAndSize(arg, &arg_match, &arg_match_len); + }else{ + PyErr_SetString(PyExc_TypeError, "expected bytes or string"); + } +#else + PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(args, i), &arg_match, &arg_match_len); +#endif + if (PyErr_Occurred()) + return NULL; + r = sd_journal_add_match(self->j, arg_match, arg_match_len); + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid match"); + return NULL; + }else if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return NULL; + }else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error adding match"); + return NULL; + } + } + + if (! keywds) + Py_RETURN_NONE; + + PyObject *key, *value; + Py_ssize_t pos=0, match_key_len, match_value_len; + int match_len; + char *match_key, *match_value; + void *match; + while (PyDict_Next(keywds, &pos, &key, &value)) { +#if PY_MAJOR_VERSION >=3 + if (PyUnicode_Check(key)) { +#if PY_MINOR_VERSION >=3 + match_key = PyUnicode_AsUTF8AndSize(key, &match_key_len); +#else + PyObject *temp2; + temp2 = PyUnicode_AsUTF8String(key); + PyBytes_AsStringAndSize(temp2, &match_key, &match_key_len); + Py_DECREF(temp2); +#endif + }else if (PyBytes_Check(key)) { + PyBytes_AsStringAndSize(key, &match_key, &match_key_len); + }else{ + PyErr_SetString(PyExc_TypeError, "expected bytes or string"); + } + if (PyUnicode_Check(value)) { +#if PY_MINOR_VERSION >=3 + match_value = PyUnicode_AsUTF8AndSize(value, &match_value_len); +#else + PyObject *temp3; + temp3 = PyUnicode_AsUTF8String(value); + PyBytes_AsStringAndSize(temp3, &match_value, &match_value_len); + Py_DECREF(temp3); +#endif + }else if (PyBytes_Check(value)) { + PyBytes_AsStringAndSize(value, &match_value, &match_value_len); + }else{ + PyErr_SetString(PyExc_TypeError, "expected bytes or string"); + } +#else + PyString_AsStringAndSize(key, &match_key, &match_key_len); + PyString_AsStringAndSize(value, &match_value, &match_value_len); +#endif + if (PyErr_Occurred()) + return NULL; + + match_len = match_key_len + 1 + match_value_len; + match = malloc(match_len); + memcpy(match, match_key, match_key_len); + memcpy(match + match_key_len, "=", 1); + memcpy(match + match_key_len + 1, match_value, match_value_len); + + r = sd_journal_add_match(self->j, match, match_len); + free(match); + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid match"); + return NULL; + }else if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return NULL; + }else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error adding match"); + return NULL; + } + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_add_disjunction__doc__, +"add_disjunction() -> None\n\n" +"Once called, all matches before and after are combined in logical\n" +"OR."); +static PyObject * +Journal_add_disjunction(Journal *self, PyObject *args) +{ + int r; + r = sd_journal_add_disjunction(self->j); + if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return NULL; + }else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error adding disjunction"); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_flush_matches__doc__, +"flush_matches() -> None\n\n" +"Clears all current match filters."); +static PyObject * +Journal_flush_matches(Journal *self, PyObject *args) +{ + sd_journal_flush_matches(self->j); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_seek__doc__, +"seek(offset[, whence]) -> None\n\n" +"Seek through journal by `offset` number of entries. Argument\n" +"`whence` defines what the offset is relative to:\n" +"os.SEEK_SET (default) from first match in journal;\n" +"os.SEEK_CUR from current position in journal;\n" +"and os.SEEK_END is from last match in journal."); +static PyObject * +Journal_seek(Journal *self, PyObject *args, PyObject *keywds) +{ + int64_t offset; + int whence=SEEK_SET; + static char *kwlist[] = {"offset", "whence", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist, + &offset, &whence)) + return NULL; + + PyObject *arg; + if (whence == SEEK_SET){ + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_head(self->j); + Py_END_ALLOW_THREADS + if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error seeking to head"); + return NULL; + } + if (offset > 0LL) { + arg = Py_BuildValue("(L)", offset); + Py_DECREF(Journal_get_next(self, arg)); + Py_DECREF(arg); + } + }else if (whence == SEEK_CUR){ + arg = Py_BuildValue("(L)", offset); + Py_DECREF(Journal_get_next(self, arg)); + Py_DECREF(arg); + }else if (whence == SEEK_END){ + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_tail(self->j); + Py_END_ALLOW_THREADS + if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail"); + return NULL; + } + arg = Py_BuildValue("(L)", -1LL); + Py_DECREF(Journal_get_next(self, arg)); + Py_DECREF(arg); + if (offset < 0LL) { + arg = Py_BuildValue("(L)", offset); + Py_DECREF(Journal_get_next(self, arg)); + Py_DECREF(arg); + } + }else{ + PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_seek_realtime__doc__, +"seek_realtime(realtime) -> None\n\n" +"Seek to nearest matching journal entry to `realtime`. Argument\n" +"`realtime` can be an integer unix timestamp in usecs or a " +"datetime instance."); +static PyObject * +Journal_seek_realtime(Journal *self, PyObject *args) +{ + PyObject *arg; + if (! PyArg_ParseTuple(args, "O", &arg)) + return NULL; + + uint64_t timestamp=-1LL; + if (PyDateTime_Check(arg)) { + PyObject *temp; + char *timestamp_str; + temp = PyObject_CallMethod(arg, "strftime", "s", "%s%f"); +#if PY_MAJOR_VERSION >=3 + PyObject *temp2; + temp2 = PyUnicode_AsUTF8String(temp); + timestamp_str = PyBytes_AsString(temp2); + Py_DECREF(temp2); +#else + timestamp_str = PyString_AsString(temp); +#endif + Py_DECREF(temp); + timestamp = strtoull(timestamp_str, NULL, 10); + }else if (PyLong_Check(arg)) { + timestamp = PyLong_AsUnsignedLongLong(arg); +#if PY_MAJOR_VERSION <3 + }else if (PyInt_Check(arg)) { + timestamp = PyInt_AsUnsignedLongLongMask(arg); +#endif + } + if ((int64_t) timestamp < 0LL) { + PyErr_SetString(PyExc_ValueError, "Time must be positive integer or datetime instance"); + return NULL; + } + + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_realtime_usec(self->j, timestamp); + Py_END_ALLOW_THREADS + if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error seek to time"); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_seek_monotonic__doc__, +"seek_monotonic(monotonic[, bootid]) -> None\n\n" +"Seek to nearest matching journal entry to `monotonic`. Argument\n" +"`monotonic` is an timestamp from boot in secs, or a\n" +"timedelta instance.\n" +"Argument `bootid` is a string representing which boot the\n" +"monotonic time is reference to. Defaults to current bootid."); +static PyObject * +Journal_seek_monotonic(Journal *self, PyObject *args) +{ + PyObject *arg; + char *bootid=NULL; + if (! PyArg_ParseTuple(args, "O|s", &arg, &bootid)) + return NULL; + + uint64_t timestamp=-1LL; + if PyDelta_Check(arg) { + PyObject *temp; + temp = PyObject_CallMethod(arg, "total_seconds", NULL); + timestamp = (uint64_t) (PyFloat_AsDouble(temp) * 1E6); + Py_DECREF(temp); + }else if (PyFloat_Check(arg)) { + timestamp = (uint64_t) (PyFloat_AsDouble(arg) * 1E6); + }else if (PyLong_Check(arg)) { + timestamp = PyLong_AsUnsignedLongLong(arg) * (uint64_t) 1E6; +#if PY_MAJOR_VERSION <3 + }else if (PyInt_Check(arg)) { + timestamp = PyInt_AsUnsignedLongLongMask(arg) * (uint64_t) 1E6; +#endif + + } + + if ((int64_t) timestamp < 0LL) { + PyErr_SetString(PyExc_ValueError, "Time must be positive number or timedelta instance"); + return NULL; + } + + sd_id128_t sd_id; + int r; + if (bootid) { + r = sd_id128_from_string(bootid, &sd_id); + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid bootid"); + return NULL; + } else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error processing bootid"); + return NULL; + } + }else{ + r = sd_id128_get_boot(&sd_id); + if (r == -EIO) { + PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); + return NULL; + } else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); + return NULL; + } + } + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp); + Py_END_ALLOW_THREADS + if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error seek to time"); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_wait__doc__, +"wait([timeout]) -> Change state (integer)\n\n" +"Waits until there is a change in the journal. Argument `timeout`\n" +"is the maximum number of seconds to wait before returning\n" +"regardless if journal has changed. If `timeout` is not given or is\n" +"0, then it will block forever.\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."); +static PyObject * +Journal_wait(Journal *self, PyObject *args, PyObject *keywds) +{ + int64_t timeout=0LL; + if (! PyArg_ParseTuple(args, "|L", &timeout)) + return NULL; + + int r; + if ( timeout == 0LL) { + Py_BEGIN_ALLOW_THREADS + r = sd_journal_wait(self->j, (uint64_t) -1); + Py_END_ALLOW_THREADS + }else{ + Py_BEGIN_ALLOW_THREADS + r = sd_journal_wait(self->j, timeout * 1E6); + Py_END_ALLOW_THREADS + } +#if PY_MAJOR_VERSION >=3 + return PyLong_FromLong(r); +#else + return PyInt_FromLong(r); +#endif +} + +PyDoc_STRVAR(Journal_seek_cursor__doc__, +"seek_cursor(cursor) -> None\n\n" +"Seeks to journal entry by given unique reference `cursor`."); +static PyObject * +Journal_seek_cursor(Journal *self, PyObject *args) +{ + const char *cursor; + if (! PyArg_ParseTuple(args, "s", &cursor)) + return NULL; + + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_cursor(self->j, cursor); + Py_END_ALLOW_THREADS + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid cursor"); + return NULL; + }else if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return NULL; + }else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error seeking to cursor"); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +Journal_iter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +Journal_iternext(PyObject *self) +{ + Journal *iter = (Journal *)self; + PyObject *dict, *arg; + Py_ssize_t dict_size; + + arg = Py_BuildValue("()"); + dict = Journal_get_next(iter, arg); + Py_DECREF(arg); + dict_size = PyDict_Size(dict); + if ((int64_t) dict_size > 0LL) { + return dict; + }else{ + Py_DECREF(dict); + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } +} + +#ifdef SD_JOURNAL_FOREACH_UNIQUE +PyDoc_STRVAR(Journal_query_unique__doc__, +"query_unique(field) -> a set of values\n\n" +"Returns a set of unique values in journal for given `field`.\n" +"Note this does not respect any journal matches."); +static PyObject * +Journal_query_unique(Journal *self, PyObject *args) +{ + char *query; + if (! PyArg_ParseTuple(args, "s", &query)) + return NULL; + + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_query_unique(self->j, query); + Py_END_ALLOW_THREADS + if (r == -EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid field name"); + return NULL; + } else if (r == -ENOMEM) { + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + return NULL; + } else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error querying journal"); + return NULL; + } + + const void *uniq; + size_t uniq_len; + const char *delim_ptr; + PyObject *value_set, *key, *value; + value_set = PySet_New(0); + +#if PY_MAJOR_VERSION >=3 + key = PyUnicode_FromString(query); +#else + key = PyString_FromString(query); +#endif + + SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { + delim_ptr = memchr(uniq, '=', uniq_len); + value = Journal___process_field(self, key, delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1)); + PySet_Add(value_set, value); + Py_DECREF(value); + } + Py_DECREF(key); + return value_set; +} +#endif //def SD_JOURNAL_FOREACH_UNIQUE + +PyDoc_STRVAR(Journal_log_level__doc__, +"log_level(level) -> None\n\n" +"Sets maximum log level by setting matches for PRIORITY."); +static PyObject * +Journal_log_level(Journal *self, PyObject *args) +{ + int level; + if (! PyArg_ParseTuple(args, "i", &level)) + return NULL; + + if (level < 0 || level > 7) { + PyErr_SetString(PyExc_ValueError, "Log level should be 0 <= level <= 7"); + return NULL; + } + int i; + char level_str[2]; + PyObject *arg, *keywds; + for(i = 0; i <= level; i++) { + sprintf(level_str, "%i", i); + arg = PyTuple_New(0); + keywds = Py_BuildValue("{s:s}", "PRIORITY", level_str); + Journal_add_match(self, arg, keywds); + Py_DECREF(arg); + Py_DECREF(keywds); + if (PyErr_Occurred()) + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_this_boot__doc__, +"this_boot() -> None\n\n" +"Sets match filter for the current _BOOT_ID."); +static PyObject * +Journal_this_boot(Journal *self, PyObject *args) +{ + sd_id128_t sd_id; + int r; + r = sd_id128_get_boot(&sd_id); + if (r == -EIO) { + PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); + return NULL; + } else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); + return NULL; + } + + char bootid[33]; + sd_id128_to_string(sd_id, bootid); + + PyObject *arg, *keywds; + arg = PyTuple_New(0); + keywds = Py_BuildValue("{s:s}", "_BOOT_ID", bootid); + Journal_add_match(self, arg, keywds); + Py_DECREF(arg); + Py_DECREF(keywds); + if (PyErr_Occurred()) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Journal_this_machine__doc__, +"this_machine() -> None\n\n" +"Sets match filter for the current _MACHINE_ID."); +static PyObject * +Journal_this_machine(Journal *self, PyObject *args) +{ + sd_id128_t sd_id; + int r; + r = sd_id128_get_machine(&sd_id); + if (r == -EIO) { + PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); + return NULL; + } else if (r < 0) { + PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); + return NULL; + } + + char machineid[33]; + sd_id128_to_string(sd_id, machineid); + + PyObject *arg, *keywds; + arg = PyTuple_New(0); + keywds = Py_BuildValue("{s:s}", "_MACHINE_ID", machineid); + Journal_add_match(self, arg, keywds); + Py_DECREF(arg); + Py_DECREF(keywds); + if (PyErr_Occurred()) + return NULL; + + Py_RETURN_NONE; +} + +static PyObject * +Journal_get_default_call(Journal *self, void *closure) +{ + Py_INCREF(self->default_call); + return self->default_call; +} + +static int +Journal_set_default_call(Journal *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete default_call"); + return -1; + } + if (! PyCallable_Check(value)) { + PyErr_SetString(PyExc_TypeError, "default_call must be callable"); + return -1; + } + Py_DECREF(self->default_call); + Py_INCREF(value); + self->default_call = value; + + return 0; +} + +static PyObject * +Journal_get_call_dict(Journal *self, void *closure) +{ + Py_INCREF(self->call_dict); + return self->call_dict; +} + +static int +Journal_set_call_dict(Journal *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete call_dict"); + return -1; + } + if (! PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, "call_dict must be dict type"); + return -1; + } + Py_DECREF(self->call_dict); + Py_INCREF(value); + self->call_dict = value; + + return 0; +} + +static PyObject * +Journal_get_data_threshold(Journal *self, void *closure) +{ + size_t cvalue; + PyObject *value; + int r; + + r = sd_journal_get_data_threshold(self->j, &cvalue); + if (r < 0){ + PyErr_SetString(PyExc_RuntimeError, "Error getting data threshold"); + return NULL; + } + +#if PY_MAJOR_VERSION >=3 + value = PyLong_FromSize_t(cvalue); +#else + value = PyInt_FromSize_t(cvalue); +#endif + return value; +} + +static int +Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold"); + return -1; + } +#if PY_MAJOR_VERSION >=3 + if (! PyLong_Check(value)){ +#else + if (! PyInt_Check(value)){ +#endif + PyErr_SetString(PyExc_TypeError, "Data threshold must be int"); + return -1; + } + int r; +#if PY_MAJOR_VERSION >=3 + r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value)); +#else + r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value)); +#endif + if (r < 0){ + PyErr_SetString(PyExc_RuntimeError, "Error setting data threshold"); + return -1; + } + return 0; +} + +static PyGetSetDef Journal_getseters[] = { + {"data_threshold", + (getter)Journal_get_data_threshold, + (setter)Journal_set_data_threshold, + "data threshold", + NULL}, + {"call_dict", + (getter)Journal_get_call_dict, + (setter)Journal_set_call_dict, + "dictionary of calls for each field", + NULL}, + {"default_call", + (getter)Journal_get_default_call, + (setter)Journal_set_default_call, + "default call for values for fields", + NULL}, + {NULL} +}; + +static PyMethodDef Journal_methods[] = { + {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS, + Journal_get_next__doc__}, + {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS, + Journal_get_previous__doc__}, + {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS, + Journal_add_match__doc__}, + {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS, + Journal_add_disjunction__doc__}, + {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS, + Journal_flush_matches__doc__}, + {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS, + Journal_seek__doc__}, + {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS, + Journal_seek_realtime__doc__}, + {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS, + Journal_seek_monotonic__doc__}, + {"wait", (PyCFunction)Journal_wait, METH_VARARGS, + Journal_wait__doc__}, + {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS, + Journal_seek_cursor__doc__}, +#ifdef SD_JOURNAL_FOREACH_UNIQUE + {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS, + Journal_query_unique__doc__}, +#endif + {"log_level", (PyCFunction)Journal_log_level, METH_VARARGS, + Journal_log_level__doc__}, + {"this_boot", (PyCFunction)Journal_this_boot, METH_NOARGS, + Journal_this_boot__doc__}, + {"this_machine", (PyCFunction)Journal_this_machine, METH_NOARGS, + Journal_this_machine__doc__}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject JournalType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_reader.Journal", /*tp_name*/ + sizeof(Journal), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Journal_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/ + Journal__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + Journal_iter, /* tp_iter */ + Journal_iternext, /* tp_iternext */ + Journal_methods, /* tp_methods */ + 0, /* tp_members */ + Journal_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Journal_init, /* tp_init */ + 0, /* tp_alloc */ + Journal_new, /* tp_new */ +}; + +#if PY_MAJOR_VERSION >= 3 +static PyModuleDef _reader_module = { + PyModuleDef_HEAD_INIT, + "_reader", + "Module that reads systemd journal similar to journalctl.", + -1, + NULL, NULL, NULL, NULL, NULL +}; +#endif + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION >= 3 +PyInit__reader(void) +#else +init_reader(void) +#endif +{ + PyObject* m; + + PyDateTime_IMPORT; + + if (PyType_Ready(&JournalType) < 0) +#if PY_MAJOR_VERSION >= 3 + return NULL; +#else + return; +#endif + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&_reader_module); + if (m == NULL) + return NULL; +#else + m = Py_InitModule3("_reader", NULL, + "Module that reads systemd journal similar to journalctl."); + if (m == NULL) + return; +#endif + + Py_INCREF(&JournalType); + PyModule_AddObject(m, "Journal", (PyObject *)&JournalType); + PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP); + PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND); + PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE); + PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY); + PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY); + PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY); + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif +} diff --git a/systemd/journal.py b/systemd/journal.py index 47849a3..a1543b8 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -25,6 +25,8 @@ import logging as _logging from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd +from ._reader import (Journal, NOP, APPEND, INVALIDATE, + LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) def _make_line(field, value): if isinstance(value, bytes): -- cgit v1.2.1 From 5ec357b0714df671970fb5f6716793f63702c6ac Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 8 Feb 2013 22:04:42 +0000 Subject: systemd-python: moved PyRun_String to journal.py code --- systemd/_reader.c | 55 +++--------------------------------------------------- systemd/journal.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 963da11..69c6d02 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -46,57 +46,8 @@ Journal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self = (Journal *)type->tp_alloc(type, 0); if (self != NULL) { - PyObject *globals, *temp; - - globals = PyEval_GetBuiltins(); - temp = PyImport_ImportModule("functools"); - PyDict_SetItemString(globals, "functools", temp); - Py_DECREF(temp); - temp = PyImport_ImportModule("datetime"); - PyDict_SetItemString(globals, "datetime", temp); - Py_DECREF(temp); - -#if PY_MAJOR_VERSION >=3 - self->default_call = PyRun_String("functools.partial(str, encoding='utf-8')", Py_eval_input, globals, NULL); -#else - self->default_call = PyRun_String("functools.partial(unicode, encoding='utf-8')", Py_eval_input, globals, NULL); -#endif - - self->call_dict = PyRun_String("{" - "'PRIORITY': int," - "'LEADER': int," - "'SESSION_ID': int," - "'USERSPACE_USEC': int," - "'INITRD_USEC': int," - "'KERNEL_USEC': int," - "'_UID': int," - "'_GID': int," - "'_PID': int," - "'SYSLOG_FACILITY': int," - "'SYSLOG_PID': int," - "'_AUDIT_SESSION': int," - "'_AUDIT_LOGINUID': int," - "'_SYSTEMD_SESSION': int," - "'_SYSTEMD_OWNER_UID': int," - "'CODE_LINE': int," - "'ERRNO': int," - "'EXIT_STATUS': int," - "'_SOURCE_REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," - "'__REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," - "'_SOURCE_MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x))," - "'__MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x))," -#if PY_MAJOR_VERSION >=3 - "'COREDUMP': bytes," -#else - "'COREDUMP': str," -#endif - "'COREDUMP_PID': int," - "'COREDUMP_UID': int," - "'COREDUMP_GID': int," - "'COREDUMP_SESSION': int," - "'COREDUMP_SIGNAL': int," - "'COREDUMP_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6)," - "}", Py_eval_input, globals, NULL); + self->call_dict = PyDict_New(); + self->default_call = Py_None; } return (PyObject *) self; @@ -1149,7 +1100,7 @@ init_reader(void) #endif Py_INCREF(&JournalType); - PyModule_AddObject(m, "Journal", (PyObject *)&JournalType); + PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType); PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP); PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND); PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE); diff --git a/systemd/journal.py b/systemd/journal.py index a1543b8..1f0aafc 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -19,15 +19,61 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . +import datetime +import functools +import sys import traceback as _traceback import os as _os import logging as _logging from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd -from ._reader import (Journal, NOP, APPEND, INVALIDATE, +from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) +class Journal(_Journal): + def __new__(cls, *args, **kwargs): + self = _Journal.__new__(cls, *args, **kwargs) + if sys.version_info[0] >= 3: + self.default_call = functools.partial(str, encoding='utf-8') + else: + self.default_call = functools.partial(unicode, encoding='utf-8') + self.call_dict = { + 'PRIORITY': int, + 'LEADER': int, + 'SESSION_ID': int, + 'USERSPACE_USEC': int, + 'INITRD_USEC': int, + 'KERNEL_USEC': int, + '_UID': int, + '_GID': int, + '_PID': int, + 'SYSLOG_FACILITY': int, + 'SYSLOG_PID': int, + '_AUDIT_SESSION': int, + '_AUDIT_LOGINUID': int, + '_SYSTEMD_SESSION': int, + '_SYSTEMD_OWNER_UID': int, + 'CODE_LINE': int, + 'ERRNO': int, + 'EXIT_STATUS': int, + '_SOURCE_REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), + '__REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), + '_SOURCE_MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x)), + '__MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x)), + 'COREDUMP_PID': int, + 'COREDUMP_UID': int, + 'COREDUMP_GID': int, + 'COREDUMP_SESSION': int, + 'COREDUMP_SIGNAL': int, + 'COREDUMP_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), + } + if sys.version_info[0] >= 3: + self.call_dict['COREDUMP'] = bytes + else: + self.call_dict['COREDUMP'] = str + return self + def _make_line(field, value): if isinstance(value, bytes): return field.encode('utf-8') + b'=' + value -- cgit v1.2.1 From 98da3b4ec42e12f137644f783171f19ad10744eb Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 8 Feb 2013 22:14:18 +0000 Subject: systemd-python: MESSAGE_ID as UUID for Journal --- systemd/journal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index 1f0aafc..fafddaa 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -22,6 +22,7 @@ import datetime import functools import sys +import uuid import traceback as _traceback import os as _os import logging as _logging @@ -39,6 +40,7 @@ class Journal(_Journal): else: self.default_call = functools.partial(unicode, encoding='utf-8') self.call_dict = { + 'MESSAGE_ID': uuid.UUID, 'PRIORITY': int, 'LEADER': int, 'SESSION_ID': int, -- cgit v1.2.1 From 7d270338de3c8fadaed116018195ec306949ad1c Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 15 Feb 2013 16:59:50 +0000 Subject: systemd-python: move default call dicts from C to python --- systemd/_reader.c | 196 +++++------------------------------------------------ systemd/journal.py | 121 ++++++++++++++++++++++----------- 2 files changed, 97 insertions(+), 220 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 69c6d02..f047ab9 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -25,8 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA typedef struct { PyObject_HEAD sd_journal *j; - PyObject *default_call; - PyObject *call_dict; } Journal; static PyTypeObject JournalType; @@ -34,27 +32,11 @@ static void Journal_dealloc(Journal* self) { sd_journal_close(self->j); - Py_XDECREF(self->default_call); - Py_XDECREF(self->call_dict); Py_TYPE(self)->tp_free((PyObject*)self); } -static PyObject * -Journal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - Journal *self; - - self = (Journal *)type->tp_alloc(type, 0); - if (self != NULL) { - self->call_dict = PyDict_New(); - self->default_call = Py_None; - } - - return (PyObject *) self; -} - PyDoc_STRVAR(Journal__doc__, -"Journal([flags][, default_call][, call_dict][,path]) -> ...\n" +"Journal([flags][,path]) -> ...\n" "Journal instance\n\n" "Returns instance of Journal, which allows filtering and return\n" "of journal entries.\n" @@ -63,13 +45,6 @@ PyDoc_STRVAR(Journal__doc__, "journal on local machine only; RUNTIME_ONLY opens only\n" "volatile journal files; and SYSTEM_ONLY opens only\n" "journal files of system services and the kernel.\n" -"Argument `default_call` must be a callable that accepts one\n" -"argument which is string/bytes value of a field and returns\n" -"python object.\n" -"Argument `call_dict` is a dictionary where the key represents\n" -"a field name, and value is a callable as per `default_call`.\n" -"A set of sane defaults for `default_call` and `call_dict` are\n" -"present.\n" "Argument `path` is the directory of journal files. Note that\n" "currently flags are ignored when `path` is present as they are\n" " not relevant."); @@ -78,38 +53,12 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) { int flags=SD_JOURNAL_LOCAL_ONLY; char *path=NULL; - PyObject *default_call=NULL, *call_dict=NULL; - static char *kwlist[] = {"flags", "default_call", "call_dict", "path", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, keywds, "|iOOs", kwlist, - &flags, &default_call, &call_dict, &path)) + static char *kwlist[] = {"flags", NULL}; + if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist, + &flags, &path)) return 1; - if (default_call) { - if (PyCallable_Check(default_call) || default_call == Py_None) { - Py_DECREF(self->default_call); - self->default_call = default_call; - Py_INCREF(self->default_call); - }else{ - PyErr_SetString(PyExc_TypeError, "Default call not callable"); - return 1; - } - } - - if (call_dict) { - if (PyDict_Check(call_dict)) { - Py_DECREF(self->call_dict); - self->call_dict = call_dict; - Py_INCREF(self->call_dict); - }else if (call_dict == Py_None) { - Py_DECREF(self->call_dict); - self->call_dict = PyDict_New(); - }else{ - PyErr_SetString(PyExc_TypeError, "Call dictionary must be dict type"); - return 1; - } - } - int r; if (path) { r = sd_journal_open_directory(&self->j, path, 0); @@ -132,42 +81,6 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) return 0; } -static PyObject * -Journal___process_field(Journal *self, PyObject *key, const void *value, ssize_t value_len) -{ - PyObject *callable=NULL, *return_value=NULL; - if (PyDict_Check(self->call_dict)) - callable = PyDict_GetItem(self->call_dict, key); - - if (PyCallable_Check(callable)) { -#if PY_MAJOR_VERSION >=3 - return_value = PyObject_CallFunction(callable, "y#", value, value_len); -#else - return_value = PyObject_CallFunction(callable, "s#", value, value_len); -#endif - if (!return_value) - PyErr_Clear(); - } - if (!return_value && PyCallable_Check(self->default_call)) -#if PY_MAJOR_VERSION >=3 - return_value = PyObject_CallFunction(self->default_call, "y#", value, value_len); -#else - return_value = PyObject_CallFunction(self->default_call, "s#", value, value_len); -#endif - if (!return_value) { - PyErr_Clear(); -#if PY_MAJOR_VERSION >=3 - return_value = PyBytes_FromStringAndSize(value, value_len); -#else - return_value = PyString_FromStringAndSize(value, value_len); -#endif - } - if (!return_value) { - return_value = Py_None; - } - return return_value; -} - PyDoc_STRVAR(Journal_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" @@ -223,10 +136,10 @@ Journal_get_next(Journal *self, PyObject *args) #else key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg); #endif - value = Journal___process_field(self, key, delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) ); + value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) ); if (PyDict_Contains(dict, key)) { cur_value = PyDict_GetItem(dict, key); - if (PyList_CheckExact(cur_value) && PyList_Size(cur_value) > 1) { + if (PyList_CheckExact(cur_value)) { PyList_Append(cur_value, value); }else{ tmp_list = PyList_New(0); @@ -252,7 +165,7 @@ Journal_get_next(Journal *self, PyObject *args) #else key = PyString_FromString("__REALTIME_TIMESTAMP"); #endif - value = Journal___process_field(self, key, realtime_str, strlen(realtime_str)); + value = PyBytes_FromString(realtime_str); PyDict_SetItem(dict, key, value); Py_DECREF(key); Py_DECREF(value); @@ -268,7 +181,7 @@ Journal_get_next(Journal *self, PyObject *args) #else key = PyString_FromString("__MONOTONIC_TIMESTAMP"); #endif - value = Journal___process_field(self, key, monotonic_str, strlen(monotonic_str)); + value = PyBytes_FromString(monotonic_str); PyDict_SetItem(dict, key, value); Py_DECREF(key); @@ -282,7 +195,7 @@ Journal_get_next(Journal *self, PyObject *args) #else key = PyString_FromString("__CURSOR"); #endif - value = Journal___process_field(self, key, cursor, strlen(cursor)); + value = PyBytes_FromString(cursor); PyDict_SetItem(dict, key, value); free(cursor); Py_DECREF(key); @@ -303,11 +216,7 @@ Journal_get_previous(Journal *self, PyObject *args) if (! PyArg_ParseTuple(args, "|L", &skip)) return NULL; - PyObject *dict, *arg; - arg = Py_BuildValue("(L)", -skip); - dict = Journal_get_next(self, arg); - Py_DECREF(arg); - return dict; + return PyObject_CallMethod((PyObject *)self, "get_next", "L", -skip); } PyDoc_STRVAR(Journal_add_match__doc__, @@ -485,14 +394,10 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) return NULL; } if (offset > 0LL) { - arg = Py_BuildValue("(L)", offset); - Py_DECREF(Journal_get_next(self, arg)); - Py_DECREF(arg); + Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); } }else if (whence == SEEK_CUR){ - arg = Py_BuildValue("(L)", offset); - Py_DECREF(Journal_get_next(self, arg)); - Py_DECREF(arg); + Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); }else if (whence == SEEK_END){ int r; Py_BEGIN_ALLOW_THREADS @@ -502,13 +407,9 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail"); return NULL; } - arg = Py_BuildValue("(L)", -1LL); - Py_DECREF(Journal_get_next(self, arg)); - Py_DECREF(arg); + Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL)); if (offset < 0LL) { - arg = Py_BuildValue("(L)", offset); - Py_DECREF(Journal_get_next(self, arg)); - Py_DECREF(arg); + Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); } }else{ PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); @@ -706,13 +607,10 @@ Journal_iter(PyObject *self) static PyObject * Journal_iternext(PyObject *self) { - Journal *iter = (Journal *)self; PyObject *dict, *arg; Py_ssize_t dict_size; - arg = Py_BuildValue("()"); - dict = Journal_get_next(iter, arg); - Py_DECREF(arg); + dict = PyObject_CallMethod(self, "get_next", ""); dict_size = PyDict_Size(dict); if ((int64_t) dict_size > 0LL) { return dict; @@ -764,7 +662,7 @@ Journal_query_unique(Journal *self, PyObject *args) SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { delim_ptr = memchr(uniq, '=', uniq_len); - value = Journal___process_field(self, key, delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1)); + value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1)); PySet_Add(value_set, value); Py_DECREF(value); } @@ -867,56 +765,6 @@ Journal_this_machine(Journal *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -Journal_get_default_call(Journal *self, void *closure) -{ - Py_INCREF(self->default_call); - return self->default_call; -} - -static int -Journal_set_default_call(Journal *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete default_call"); - return -1; - } - if (! PyCallable_Check(value)) { - PyErr_SetString(PyExc_TypeError, "default_call must be callable"); - return -1; - } - Py_DECREF(self->default_call); - Py_INCREF(value); - self->default_call = value; - - return 0; -} - -static PyObject * -Journal_get_call_dict(Journal *self, void *closure) -{ - Py_INCREF(self->call_dict); - return self->call_dict; -} - -static int -Journal_set_call_dict(Journal *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete call_dict"); - return -1; - } - if (! PyDict_Check(value)) { - PyErr_SetString(PyExc_TypeError, "call_dict must be dict type"); - return -1; - } - Py_DECREF(self->call_dict); - Py_INCREF(value); - self->call_dict = value; - - return 0; -} - static PyObject * Journal_get_data_threshold(Journal *self, void *closure) { @@ -972,16 +820,6 @@ static PyGetSetDef Journal_getseters[] = { (setter)Journal_set_data_threshold, "data threshold", NULL}, - {"call_dict", - (getter)Journal_get_call_dict, - (setter)Journal_set_call_dict, - "dictionary of calls for each field", - NULL}, - {"default_call", - (getter)Journal_get_default_call, - (setter)Journal_set_default_call, - "default call for values for fields", - NULL}, {NULL} }; @@ -1057,7 +895,7 @@ static PyTypeObject JournalType = { 0, /* tp_dictoffset */ (initproc)Journal_init, /* tp_init */ 0, /* tp_alloc */ - Journal_new, /* tp_new */ + PyType_GenericNew, /* tp_new */ }; #if PY_MAJOR_VERSION >= 3 diff --git a/systemd/journal.py b/systemd/journal.py index fafddaa..8a688f9 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -32,49 +32,88 @@ from ._journal import sendv, stream_fd from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) +_MONOTONIC_CONVERTER = lambda x: datetime.timedelta(microseconds=float(x)) +_REALTIME_CONVERTER = lambda x: datetime.datetime.fromtimestamp(float(x)/1E6) +DEFAULT_CONVERTERS = { + 'MESSAGE_ID': uuid.UUID, + 'PRIORITY': int, + 'LEADER': int, + 'SESSION_ID': int, + 'USERSPACE_USEC': int, + 'INITRD_USEC': int, + 'KERNEL_USEC': int, + '_UID': int, + '_GID': int, + '_PID': int, + 'SYSLOG_FACILITY': int, + 'SYSLOG_PID': int, + '_AUDIT_SESSION': int, + '_AUDIT_LOGINUID': int, + '_SYSTEMD_SESSION': int, + '_SYSTEMD_OWNER_UID': int, + 'CODE_LINE': int, + 'ERRNO': int, + 'EXIT_STATUS': int, + '_SOURCE_REALTIME_TIMESTAMP': _REALTIME_CONVERTER, + '__REALTIME_TIMESTAMP': _REALTIME_CONVERTER, + '_SOURCE_MONOTONIC_TIMESTAMP': _MONOTONIC_CONVERTER, + '__MONOTONIC_TIMESTAMP': _MONOTONIC_CONVERTER, + 'COREDUMP': bytes, + 'COREDUMP_PID': int, + 'COREDUMP_UID': int, + 'COREDUMP_GID': int, + 'COREDUMP_SESSION': int, + 'COREDUMP_SIGNAL': int, + 'COREDUMP_TIMESTAMP': _REALTIME_CONVERTER, +} + +if sys.version_info >= (3,): + _convert_unicode = functools.partial(str, encoding='utf-8') +else: + _convert_unicode = functools.partial(unicode, encoding='utf-8') + class Journal(_Journal): - def __new__(cls, *args, **kwargs): - self = _Journal.__new__(cls, *args, **kwargs) - if sys.version_info[0] >= 3: - self.default_call = functools.partial(str, encoding='utf-8') - else: - self.default_call = functools.partial(unicode, encoding='utf-8') - self.call_dict = { - 'MESSAGE_ID': uuid.UUID, - 'PRIORITY': int, - 'LEADER': int, - 'SESSION_ID': int, - 'USERSPACE_USEC': int, - 'INITRD_USEC': int, - 'KERNEL_USEC': int, - '_UID': int, - '_GID': int, - '_PID': int, - 'SYSLOG_FACILITY': int, - 'SYSLOG_PID': int, - '_AUDIT_SESSION': int, - '_AUDIT_LOGINUID': int, - '_SYSTEMD_SESSION': int, - '_SYSTEMD_OWNER_UID': int, - 'CODE_LINE': int, - 'ERRNO': int, - 'EXIT_STATUS': int, - '_SOURCE_REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), - '__REALTIME_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), - '_SOURCE_MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x)), - '__MONOTONIC_TIMESTAMP': lambda x: datetime.timedelta(microseconds=float(x)), - 'COREDUMP_PID': int, - 'COREDUMP_UID': int, - 'COREDUMP_GID': int, - 'COREDUMP_SESSION': int, - 'COREDUMP_SIGNAL': int, - 'COREDUMP_TIMESTAMP': lambda x: datetime.datetime.fromtimestamp(float(x)/1E6), - } - if sys.version_info[0] >= 3: - self.call_dict['COREDUMP'] = bytes + def __init__(self, converters=None, *args, **kwargs): + super(Journal, self).__init__(*args, **kwargs) + 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.call_dict['COREDUMP'] = str - return self + # suitable fallback, e.g. + self.converters = DEFAULT_CONVERTERS.copy() + if converters is not None: + self.converters.update(converters) + + def _convert_field(self, key, value): + try: + result = self.converters[key](value) + except KeyError: + # Default conversion in unicode + try: + result = _convert_unicode(value) + except: + # Leave in default bytes + result = value + return result + + def _convert_entry(self, entry): + result = {} + for key, value in entry.iteritems(): + if isinstance(value, list): + result[key] = [self._convert_field(key, val) for val in value] + else: + result[key] = self._convert_field(key, value) + return result + + def get_next(self, *args, **kwargs): + return self._convert_entry( + super(Journal, self).get_next(*args, **kwargs)) + + def query_unique(self, key, *args, **kwargs): + return set(self._convert_field(key, value) + for value in super(Journal, self).query_unique(key, *args, **kwargs)) def _make_line(field, value): if isinstance(value, bytes): -- cgit v1.2.1 From 108e4de1e41aa3b4b48654a9caab988fdb2d224f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 16 Feb 2013 20:28:34 -0500 Subject: sphinx: document Journal class too --- systemd/docs/journal.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 036250a..89728a2 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -9,3 +9,16 @@ ---------------------- .. autoclass:: JournalHandler + +Accessing the Journal +--------------------- + +.. autoclass:: _Journal + :undoc-members: + :inherited-members: + +.. autoclass:: Journal + :undoc-members: + :inherited-members: + + .. automethod:: __init__ -- cgit v1.2.1 From 87531b51f5530583c916a645790208f5216e4089 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 15 Feb 2013 17:09:47 +0000 Subject: systemd-python: Journal log_level moved to python --- systemd/_reader.c | 32 -------------------------------- systemd/journal.py | 8 ++++++++ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index f047ab9..ce66317 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -671,36 +671,6 @@ Journal_query_unique(Journal *self, PyObject *args) } #endif //def SD_JOURNAL_FOREACH_UNIQUE -PyDoc_STRVAR(Journal_log_level__doc__, -"log_level(level) -> None\n\n" -"Sets maximum log level by setting matches for PRIORITY."); -static PyObject * -Journal_log_level(Journal *self, PyObject *args) -{ - int level; - if (! PyArg_ParseTuple(args, "i", &level)) - return NULL; - - if (level < 0 || level > 7) { - PyErr_SetString(PyExc_ValueError, "Log level should be 0 <= level <= 7"); - return NULL; - } - int i; - char level_str[2]; - PyObject *arg, *keywds; - for(i = 0; i <= level; i++) { - sprintf(level_str, "%i", i); - arg = PyTuple_New(0); - keywds = Py_BuildValue("{s:s}", "PRIORITY", level_str); - Journal_add_match(self, arg, keywds); - Py_DECREF(arg); - Py_DECREF(keywds); - if (PyErr_Occurred()) - return NULL; - } - Py_RETURN_NONE; -} - PyDoc_STRVAR(Journal_this_boot__doc__, "this_boot() -> None\n\n" "Sets match filter for the current _BOOT_ID."); @@ -848,8 +818,6 @@ static PyMethodDef Journal_methods[] = { {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS, Journal_query_unique__doc__}, #endif - {"log_level", (PyCFunction)Journal_log_level, METH_VARARGS, - Journal_log_level__doc__}, {"this_boot", (PyCFunction)Journal_this_boot, METH_NOARGS, Journal_this_boot__doc__}, {"this_machine", (PyCFunction)Journal_this_machine, METH_NOARGS, diff --git a/systemd/journal.py b/systemd/journal.py index 8a688f9..40e40c3 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -115,6 +115,14 @@ class Journal(_Journal): return set(self._convert_field(key, value) for value in super(Journal, self).query_unique(key, *args, **kwargs)) + def log_level(self, level): + """Sets maximum log level by setting matches for PRIORITY.""" + if 0 <= level <= 7: + for i in range(level+1): + self.add_match(PRIORITY="%s" % i) + else: + raise ValueError("Log level must be 0 <= level <= 7") + def _make_line(field, value): if isinstance(value, bytes): return field.encode('utf-8') + b'=' + value -- cgit v1.2.1 From 55c89e4680e58245438c5abd6147151f3d3a4050 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Fri, 15 Feb 2013 17:16:56 +0000 Subject: systemd-python: implement this_boot/this_machine in Python --- systemd/_reader.c | 68 ------------------------------------------------------ systemd/journal.py | 9 ++++++++ 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index ce66317..06bdf16 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -671,70 +671,6 @@ Journal_query_unique(Journal *self, PyObject *args) } #endif //def SD_JOURNAL_FOREACH_UNIQUE -PyDoc_STRVAR(Journal_this_boot__doc__, -"this_boot() -> None\n\n" -"Sets match filter for the current _BOOT_ID."); -static PyObject * -Journal_this_boot(Journal *self, PyObject *args) -{ - sd_id128_t sd_id; - int r; - r = sd_id128_get_boot(&sd_id); - if (r == -EIO) { - PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); - return NULL; - } else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); - return NULL; - } - - char bootid[33]; - sd_id128_to_string(sd_id, bootid); - - PyObject *arg, *keywds; - arg = PyTuple_New(0); - keywds = Py_BuildValue("{s:s}", "_BOOT_ID", bootid); - Journal_add_match(self, arg, keywds); - Py_DECREF(arg); - Py_DECREF(keywds); - if (PyErr_Occurred()) - return NULL; - - Py_RETURN_NONE; -} - -PyDoc_STRVAR(Journal_this_machine__doc__, -"this_machine() -> None\n\n" -"Sets match filter for the current _MACHINE_ID."); -static PyObject * -Journal_this_machine(Journal *self, PyObject *args) -{ - sd_id128_t sd_id; - int r; - r = sd_id128_get_machine(&sd_id); - if (r == -EIO) { - PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); - return NULL; - } else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); - return NULL; - } - - char machineid[33]; - sd_id128_to_string(sd_id, machineid); - - PyObject *arg, *keywds; - arg = PyTuple_New(0); - keywds = Py_BuildValue("{s:s}", "_MACHINE_ID", machineid); - Journal_add_match(self, arg, keywds); - Py_DECREF(arg); - Py_DECREF(keywds); - if (PyErr_Occurred()) - return NULL; - - Py_RETURN_NONE; -} - static PyObject * Journal_get_data_threshold(Journal *self, void *closure) { @@ -818,10 +754,6 @@ static PyMethodDef Journal_methods[] = { {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS, Journal_query_unique__doc__}, #endif - {"this_boot", (PyCFunction)Journal_this_boot, METH_NOARGS, - Journal_this_boot__doc__}, - {"this_machine", (PyCFunction)Journal_this_machine, METH_NOARGS, - Journal_this_machine__doc__}, {NULL} /* Sentinel */ }; diff --git a/systemd/journal.py b/systemd/journal.py index 40e40c3..533a875 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -31,6 +31,7 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, from ._journal import sendv, stream_fd from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) +from . import id128 as _id128 _MONOTONIC_CONVERTER = lambda x: datetime.timedelta(microseconds=float(x)) _REALTIME_CONVERTER = lambda x: datetime.datetime.fromtimestamp(float(x)/1E6) @@ -123,6 +124,14 @@ class Journal(_Journal): else: raise ValueError("Log level must be 0 <= level <= 7") + def this_boot(self): + """Add match for _BOOT_ID equal to current boot ID.""" + self.add_match(_BOOT_ID=_id128.get_boot().hex) + + def this_machine(self): + """Add match for _MACHINE_ID equal to the ID of this machine.""" + self.add_match(_MACHINE_ID=_id128.get_machine().hex) + def _make_line(field, value): if isinstance(value, bytes): return field.encode('utf-8') + b'=' + value -- cgit v1.2.1 From 461dbcafe920d60b94344efacbfe858143b12fc6 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 13:40:44 +0000 Subject: systemd-python: some python3 and bug fixes --- systemd/_reader.c | 19 ++++++++++++------- systemd/journal.py | 11 +++++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 06bdf16..ae3d77c 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -383,7 +383,7 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) &offset, &whence)) return NULL; - PyObject *arg; + PyObject *result=NULL; if (whence == SEEK_SET){ int r; Py_BEGIN_ALLOW_THREADS @@ -394,10 +394,10 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) return NULL; } if (offset > 0LL) { - Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); + result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); } }else if (whence == SEEK_CUR){ - Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); + result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); }else if (whence == SEEK_END){ int r; Py_BEGIN_ALLOW_THREADS @@ -407,14 +407,19 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail"); return NULL; } - Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL)); if (offset < 0LL) { - Py_DECREF(PyObject_CallMethod((PyObject *)self, "get_next", "L", offset)); + result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); + }else{ + result = PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL); } }else{ PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); - return NULL; } + + if (result) + Py_DECREF(result); + if (PyErr_Occurred()) + return NULL; Py_RETURN_NONE; } @@ -607,7 +612,7 @@ Journal_iter(PyObject *self) static PyObject * Journal_iternext(PyObject *self) { - PyObject *dict, *arg; + PyObject *dict; Py_ssize_t dict_size; dict = PyObject_CallMethod(self, "get_next", ""); diff --git a/systemd/journal.py b/systemd/journal.py index 533a875..6e82a46 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -26,6 +26,8 @@ import uuid import traceback as _traceback import os as _os import logging as _logging +if sys.version_info >= (3,): + from collections import ChainMap from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd @@ -90,7 +92,7 @@ class Journal(_Journal): def _convert_field(self, key, value): try: result = self.converters[key](value) - except KeyError: + except: # Default conversion in unicode try: result = _convert_unicode(value) @@ -101,13 +103,18 @@ class Journal(_Journal): def _convert_entry(self, entry): result = {} - for key, value in entry.iteritems(): + for key, value in entry.items(): if isinstance(value, list): result[key] = [self._convert_field(key, val) for val in value] else: result[key] = self._convert_field(key, value) return result + def add_match(self, *args, **kwargs): + args = list(args) + args.extend(_make_line(key, val) for key, val in kwargs.items()) + super(Journal, self).add_match(*args) + def get_next(self, *args, **kwargs): return self._convert_entry( super(Journal, self).get_next(*args, **kwargs)) -- cgit v1.2.1 From c5ba02c0a8a82e22d918cb70b2f7afb0e8e5a398 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 17:29:57 +0000 Subject: systemd-python: Tidy up _reader error handling --- systemd/_reader.c | 140 +++++++++++++++++++++++++++-------------------------- systemd/journal.py | 8 +-- 2 files changed, 75 insertions(+), 73 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index ae3d77c..5d743eb 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -54,28 +54,26 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) int flags=SD_JOURNAL_LOCAL_ONLY; char *path=NULL; - static char *kwlist[] = {"flags", NULL}; + static char *kwlist[] = {"flags", "path", NULL}; if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist, &flags, &path)) return 1; int r; + Py_BEGIN_ALLOW_THREADS if (path) { r = sd_journal_open_directory(&self->j, path, 0); }else{ - Py_BEGIN_ALLOW_THREADS r = sd_journal_open(&self->j, flags); - Py_END_ALLOW_THREADS } - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid flags or path"); + Py_END_ALLOW_THREADS + if (r < 0) { + errno = -r; + PyObject *errtype = r == -EINVAL ? PyExc_ValueError : + r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrnoWithFilename(errtype, path); return -1; - }else if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return 1; - }else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error opening journal"); - return 1; } return 0; @@ -92,30 +90,27 @@ Journal_get_next(Journal *self, PyObject *args) if (! PyArg_ParseTuple(args, "|L", &skip)) return NULL; + if (skip == 0LL) { + PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer"); + return NULL; + } + int r; + Py_BEGIN_ALLOW_THREADS if (skip == 1LL) { - Py_BEGIN_ALLOW_THREADS r = sd_journal_next(self->j); - Py_END_ALLOW_THREADS }else if (skip == -1LL) { - Py_BEGIN_ALLOW_THREADS r = sd_journal_previous(self->j); - Py_END_ALLOW_THREADS }else if (skip > 1LL) { - Py_BEGIN_ALLOW_THREADS r = sd_journal_next_skip(self->j, skip); - Py_END_ALLOW_THREADS }else if (skip < -1LL) { - Py_BEGIN_ALLOW_THREADS r = sd_journal_previous_skip(self->j, -skip); - Py_END_ALLOW_THREADS - }else{ - PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer"); - return NULL; } + Py_END_ALLOW_THREADS if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error getting next message"); + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; }else if ( r == 0) { //EOF return PyDict_New(); @@ -256,14 +251,12 @@ Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) if (PyErr_Occurred()) return NULL; r = sd_journal_add_match(self->j, arg_match, arg_match_len); - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid match"); - return NULL; - }else if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return NULL; - }else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error adding match"); + if (r < 0) { + errno = -r; + PyObject *errtype = r == -EINVAL ? PyExc_ValueError : + r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); return NULL; } } @@ -345,11 +338,11 @@ Journal_add_disjunction(Journal *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); - if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return NULL; - }else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error adding disjunction"); + if (r < 0) { + errno = -r; + PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); return NULL; } Py_RETURN_NONE; @@ -390,7 +383,8 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) r = sd_journal_seek_head(self->j); Py_END_ALLOW_THREADS if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error seeking to head"); + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } if (offset > 0LL) { @@ -404,7 +398,8 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) r = sd_journal_seek_tail(self->j); Py_END_ALLOW_THREADS if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error seeking to tail"); + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } if (offset < 0LL) { @@ -467,7 +462,8 @@ Journal_seek_realtime(Journal *self, PyObject *args) r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error seek to time"); + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -517,8 +513,9 @@ Journal_seek_monotonic(Journal *self, PyObject *args) if (r == -EINVAL) { PyErr_SetString(PyExc_ValueError, "Invalid bootid"); return NULL; - } else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error processing bootid"); + }else if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } }else{ @@ -526,8 +523,9 @@ Journal_seek_monotonic(Journal *self, PyObject *args) if (r == -EIO) { PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); return NULL; - } else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error getting current boot ID"); + }else if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } } @@ -536,7 +534,8 @@ Journal_seek_monotonic(Journal *self, PyObject *args) r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp); Py_END_ALLOW_THREADS if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error seek to time"); + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -559,14 +558,19 @@ Journal_wait(Journal *self, PyObject *args, PyObject *keywds) return NULL; int r; + Py_BEGIN_ALLOW_THREADS if ( timeout == 0LL) { - Py_BEGIN_ALLOW_THREADS r = sd_journal_wait(self->j, (uint64_t) -1); - Py_END_ALLOW_THREADS }else{ - Py_BEGIN_ALLOW_THREADS r = sd_journal_wait(self->j, timeout * 1E6); - Py_END_ALLOW_THREADS + } + Py_END_ALLOW_THREADS + if (r < 0) { + errno = -r; + PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); + return NULL; } #if PY_MAJOR_VERSION >=3 return PyLong_FromLong(r); @@ -589,14 +593,12 @@ Journal_seek_cursor(Journal *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_cursor(self->j, cursor); Py_END_ALLOW_THREADS - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid cursor"); - return NULL; - }else if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return NULL; - }else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error seeking to cursor"); + if (r < 0) { + errno = -r; + PyObject *errtype = r == -EINVAL ? PyExc_ValueError : + r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); return NULL; } Py_RETURN_NONE; @@ -642,14 +644,12 @@ Journal_query_unique(Journal *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_query_unique(self->j, query); Py_END_ALLOW_THREADS - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid field name"); - return NULL; - } else if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return NULL; - } else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error querying journal"); + if (r < 0) { + errno = -r; + PyObject *errtype = r == -EINVAL ? PyExc_ValueError : + r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); return NULL; } @@ -684,8 +684,9 @@ Journal_get_data_threshold(Journal *self, void *closure) int r; r = sd_journal_get_data_threshold(self->j, &cvalue); - if (r < 0){ - PyErr_SetString(PyExc_RuntimeError, "Error getting data threshold"); + if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -718,9 +719,10 @@ Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) #else r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value)); #endif - if (r < 0){ - PyErr_SetString(PyExc_RuntimeError, "Error setting data threshold"); - return -1; + if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; } return 0; } diff --git a/systemd/journal.py b/systemd/journal.py index 6e82a46..db35ba2 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -115,13 +115,13 @@ class Journal(_Journal): args.extend(_make_line(key, val) for key, val in kwargs.items()) super(Journal, self).add_match(*args) - def get_next(self, *args, **kwargs): + def get_next(self, skip=1): return self._convert_entry( - super(Journal, self).get_next(*args, **kwargs)) + super(Journal, self).get_next(skip)) - def query_unique(self, key, *args, **kwargs): + def query_unique(self, key): return set(self._convert_field(key, value) - for value in super(Journal, self).query_unique(key, *args, **kwargs)) + for value in super(Journal, self).query_unique(key)) def log_level(self, level): """Sets maximum log level by setting matches for PRIORITY.""" -- cgit v1.2.1 From 716351a180e2d2fb6d9874d35f6c6af646402cca Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 17:31:18 +0000 Subject: systemd-python: Moved _reader datetime usage to python --- systemd/_reader.c | 52 ++++++++-------------------------------------------- systemd/journal.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 5d743eb..f1c3175 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -426,34 +426,12 @@ PyDoc_STRVAR(Journal_seek_realtime__doc__, static PyObject * Journal_seek_realtime(Journal *self, PyObject *args) { - PyObject *arg; - if (! PyArg_ParseTuple(args, "O", &arg)) + uint64_t timestamp; + if (! PyArg_ParseTuple(args, "K", ×tamp)) return NULL; - uint64_t timestamp=-1LL; - if (PyDateTime_Check(arg)) { - PyObject *temp; - char *timestamp_str; - temp = PyObject_CallMethod(arg, "strftime", "s", "%s%f"); -#if PY_MAJOR_VERSION >=3 - PyObject *temp2; - temp2 = PyUnicode_AsUTF8String(temp); - timestamp_str = PyBytes_AsString(temp2); - Py_DECREF(temp2); -#else - timestamp_str = PyString_AsString(temp); -#endif - Py_DECREF(temp); - timestamp = strtoull(timestamp_str, NULL, 10); - }else if (PyLong_Check(arg)) { - timestamp = PyLong_AsUnsignedLongLong(arg); -#if PY_MAJOR_VERSION <3 - }else if (PyInt_Check(arg)) { - timestamp = PyInt_AsUnsignedLongLongMask(arg); -#endif - } if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be positive integer or datetime instance"); + PyErr_SetString(PyExc_ValueError, "Time must be positive integer"); return NULL; } @@ -479,30 +457,16 @@ PyDoc_STRVAR(Journal_seek_monotonic__doc__, static PyObject * Journal_seek_monotonic(Journal *self, PyObject *args) { - PyObject *arg; + double timedouble; char *bootid=NULL; - if (! PyArg_ParseTuple(args, "O|s", &arg, &bootid)) + if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) return NULL; - uint64_t timestamp=-1LL; - if PyDelta_Check(arg) { - PyObject *temp; - temp = PyObject_CallMethod(arg, "total_seconds", NULL); - timestamp = (uint64_t) (PyFloat_AsDouble(temp) * 1E6); - Py_DECREF(temp); - }else if (PyFloat_Check(arg)) { - timestamp = (uint64_t) (PyFloat_AsDouble(arg) * 1E6); - }else if (PyLong_Check(arg)) { - timestamp = PyLong_AsUnsignedLongLong(arg) * (uint64_t) 1E6; -#if PY_MAJOR_VERSION <3 - }else if (PyInt_Check(arg)) { - timestamp = PyInt_AsUnsignedLongLongMask(arg) * (uint64_t) 1E6; -#endif - - } + uint64_t timestamp; + timestamp = (uint64_t) (timedouble * 1.0E6); if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be positive number or timedelta instance"); + PyErr_SetString(PyExc_ValueError, "Time must be positive number"); return NULL; } diff --git a/systemd/journal.py b/systemd/journal.py index db35ba2..46affce 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -123,6 +123,16 @@ class Journal(_Journal): return set(self._convert_field(key, value) for value in super(Journal, self).query_unique(key)) + def seek_realtime(self, timestamp): + if isinstance(timestamp, datetime.datetime): + timestamp = int(timestamp.strftime("%s%f")) + return super(Journal, self).seek_realtime(timestamp) + + def seek_monotonic(self, timestamp, bootid=None): + if isinstance(timestamp, datetime.timedelta): + timestamp = timestamp.totalseconds() + return super(Journal, self).seek_monotonic(timestamp, bootid) + def log_level(self, level): """Sets maximum log level by setting matches for PRIORITY.""" if 0 <= level <= 7: -- cgit v1.2.1 From ab67680240974f3810003c90e5aa96802c7dbe98 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 17:32:05 +0000 Subject: systemd-python: updated _reader header to standard license --- systemd/_reader.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index f1c3175..9d7ce2d 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1,21 +1,23 @@ -/* -_reader - Python module that reads systemd journal similar to journalctl -Copyright (C) 2012 Steven Hiscocks - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Steven Hiscocks, Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ #include #include -- cgit v1.2.1 From 7258e1a44ac14fb57a86a877d6969d62ffaa4750 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 18:23:24 +0000 Subject: systemd-python: correct data_threshold error return value --- systemd/_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 9d7ce2d..d2f738b 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -688,7 +688,7 @@ Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) if (r < 0) { errno = -r; PyErr_SetFromErrno(PyExc_OSError); - return NULL; + return -1; } return 0; } -- cgit v1.2.1 From 097bcda6ce689720a58f17dfd855decd80c8d4ef Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 18:53:52 +0000 Subject: systemd-python: _reader add_match takes single string python code now takes care of multiple matches --- systemd/_reader.c | 112 +++++++---------------------------------------------- systemd/journal.py | 3 +- 2 files changed, 15 insertions(+), 100 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index d2f738b..8f21678 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -97,7 +97,7 @@ Journal_get_next(Journal *self, PyObject *args) return NULL; } - int r; + int r = -EINVAL; Py_BEGIN_ALLOW_THREADS if (skip == 1LL) { r = sd_journal_next(self->j); @@ -226,106 +226,20 @@ PyDoc_STRVAR(Journal_add_match__doc__, static PyObject * Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) { - Py_ssize_t arg_match_len; - char *arg_match; - int i, r; - for (i = 0; i < PySequence_Size(args); i++) { -#if PY_MAJOR_VERSION >=3 - PyObject *arg; - arg = PySequence_Fast_GET_ITEM(args, i); - if (PyUnicode_Check(arg)) { -#if PY_MINOR_VERSION >=3 - arg_match = PyUnicode_AsUTF8AndSize(arg, &arg_match_len); -#else - PyObject *temp; - temp = PyUnicode_AsUTF8String(arg); - PyBytes_AsStringAndSize(temp, &arg_match, &arg_match_len); - Py_DECREF(temp); -#endif - }else if (PyBytes_Check(arg)) { - PyBytes_AsStringAndSize(arg, &arg_match, &arg_match_len); - }else{ - PyErr_SetString(PyExc_TypeError, "expected bytes or string"); - } -#else - PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(args, i), &arg_match, &arg_match_len); -#endif - if (PyErr_Occurred()) - return NULL; - r = sd_journal_add_match(self->j, arg_match, arg_match_len); - if (r < 0) { - errno = -r; - PyObject *errtype = r == -EINVAL ? PyExc_ValueError : - r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); - return NULL; - } - } - - if (! keywds) - Py_RETURN_NONE; - - PyObject *key, *value; - Py_ssize_t pos=0, match_key_len, match_value_len; + char *match; int match_len; - char *match_key, *match_value; - void *match; - while (PyDict_Next(keywds, &pos, &key, &value)) { -#if PY_MAJOR_VERSION >=3 - if (PyUnicode_Check(key)) { -#if PY_MINOR_VERSION >=3 - match_key = PyUnicode_AsUTF8AndSize(key, &match_key_len); -#else - PyObject *temp2; - temp2 = PyUnicode_AsUTF8String(key); - PyBytes_AsStringAndSize(temp2, &match_key, &match_key_len); - Py_DECREF(temp2); -#endif - }else if (PyBytes_Check(key)) { - PyBytes_AsStringAndSize(key, &match_key, &match_key_len); - }else{ - PyErr_SetString(PyExc_TypeError, "expected bytes or string"); - } - if (PyUnicode_Check(value)) { -#if PY_MINOR_VERSION >=3 - match_value = PyUnicode_AsUTF8AndSize(value, &match_value_len); -#else - PyObject *temp3; - temp3 = PyUnicode_AsUTF8String(value); - PyBytes_AsStringAndSize(temp3, &match_value, &match_value_len); - Py_DECREF(temp3); -#endif - }else if (PyBytes_Check(value)) { - PyBytes_AsStringAndSize(value, &match_value, &match_value_len); - }else{ - PyErr_SetString(PyExc_TypeError, "expected bytes or string"); - } -#else - PyString_AsStringAndSize(key, &match_key, &match_key_len); - PyString_AsStringAndSize(value, &match_value, &match_value_len); -#endif - if (PyErr_Occurred()) - return NULL; - - match_len = match_key_len + 1 + match_value_len; - match = malloc(match_len); - memcpy(match, match_key, match_key_len); - memcpy(match + match_key_len, "=", 1); - memcpy(match + match_key_len + 1, match_value, match_value_len); + if (! PyArg_ParseTuple(args, "s#", &match, &match_len)) + return NULL; - r = sd_journal_add_match(self->j, match, match_len); - free(match); - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid match"); - return NULL; - }else if (r == -ENOMEM) { - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - return NULL; - }else if (r < 0) { - PyErr_SetString(PyExc_RuntimeError, "Error adding match"); - return NULL; - } + int r; + r = sd_journal_add_match(self->j, match, match_len); + if (r < 0) { + errno = -r; + PyObject *errtype = r == -EINVAL ? PyExc_ValueError : + r == -ENOMEM ? PyExc_MemoryError : + PyExc_OSError; + PyErr_SetFromErrno(errtype); + return NULL; } Py_RETURN_NONE; diff --git a/systemd/journal.py b/systemd/journal.py index 46affce..279662e 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -113,7 +113,8 @@ class Journal(_Journal): def add_match(self, *args, **kwargs): args = list(args) args.extend(_make_line(key, val) for key, val in kwargs.items()) - super(Journal, self).add_match(*args) + for arg in args: + super(Journal, self).add_match(arg) def get_next(self, skip=1): return self._convert_entry( -- cgit v1.2.1 From 029ac59922161565cccbe40e0aeb21af119d4b00 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 19:06:46 +0000 Subject: systemd-python: Update _reader docstrings --- systemd/_reader.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 8f21678..7aa638b 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -217,12 +217,11 @@ Journal_get_previous(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_add_match__doc__, -"add_match(match, ..., field=value, ...) -> None\n\n" +"add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" -"field are combined in logical AND, and matches of the same field\n" +"fields are combined in logical AND, and matches of the same field\n" "are automatically combined in logical OR.\n" -"Matches can be passed as strings \"field=value\", or keyword\n" -"arguments field=\"value\"."); +"Match is string of form \"field=value\"."); static PyObject * Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) { @@ -337,8 +336,7 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) PyDoc_STRVAR(Journal_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" -"`realtime` can be an integer unix timestamp in usecs or a " -"datetime instance."); +"`realtime` can must be an integer unix timestamp in usecs."); static PyObject * Journal_seek_realtime(Journal *self, PyObject *args) { @@ -366,8 +364,7 @@ Journal_seek_realtime(Journal *self, PyObject *args) PyDoc_STRVAR(Journal_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" "Seek to nearest matching journal entry to `monotonic`. Argument\n" -"`monotonic` is an timestamp from boot in secs, or a\n" -"timedelta instance.\n" +"`monotonic` is an timestamp from boot in seconds.\n" "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); static PyObject * -- cgit v1.2.1 From 445332cb69386e8696332403aca55c60c1df5de7 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 19:10:49 +0000 Subject: systemd-python: _reader now takes unix timestamp in seconds --- systemd/_reader.c | 9 ++++++--- systemd/journal.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 7aa638b..95e6094 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -336,14 +336,17 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) PyDoc_STRVAR(Journal_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" -"`realtime` can must be an integer unix timestamp in usecs."); +"`realtime` can must be an integer unix timestamp."); static PyObject * Journal_seek_realtime(Journal *self, PyObject *args) { - uint64_t timestamp; - if (! PyArg_ParseTuple(args, "K", ×tamp)) + double timedouble; + if (! PyArg_ParseTuple(args, "d", &timedouble)) return NULL; + uint64_t timestamp; + timestamp = (uint64_t) (timedouble * 1.0E6); + if ((int64_t) timestamp < 0LL) { PyErr_SetString(PyExc_ValueError, "Time must be positive integer"); return NULL; diff --git a/systemd/journal.py b/systemd/journal.py index 279662e..ab8661e 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -126,7 +126,7 @@ class Journal(_Journal): def seek_realtime(self, timestamp): if isinstance(timestamp, datetime.datetime): - timestamp = int(timestamp.strftime("%s%f")) + timestamp = float(timestamp.strftime("%s.%f")) return super(Journal, self).seek_realtime(timestamp) def seek_monotonic(self, timestamp, bootid=None): -- cgit v1.2.1 From 9d405a0928e3a293da662111409cfb6d00d03b72 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 16 Feb 2013 19:16:09 +0000 Subject: systemd-python: remove unneeded ifdef for query_unique --- systemd/_reader.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 95e6094..c16cbdc 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -508,7 +508,6 @@ Journal_iternext(PyObject *self) } } -#ifdef SD_JOURNAL_FOREACH_UNIQUE PyDoc_STRVAR(Journal_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Returns a set of unique values in journal for given `field`.\n" @@ -554,7 +553,6 @@ Journal_query_unique(Journal *self, PyObject *args) Py_DECREF(key); return value_set; } -#endif //def SD_JOURNAL_FOREACH_UNIQUE static PyObject * Journal_get_data_threshold(Journal *self, void *closure) @@ -637,10 +635,8 @@ static PyMethodDef Journal_methods[] = { Journal_wait__doc__}, {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS, Journal_seek_cursor__doc__}, -#ifdef SD_JOURNAL_FOREACH_UNIQUE {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS, Journal_query_unique__doc__}, -#endif {NULL} /* Sentinel */ }; -- cgit v1.2.1 From 2f5a35a755f5ca046ecb04b2d01dfbe19a21447d Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 17 Feb 2013 11:48:13 +0000 Subject: systemd-python: Journal this_boot/machine now accepts ID --- systemd/journal.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index ab8661e..60c8111 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -142,13 +142,28 @@ class Journal(_Journal): else: raise ValueError("Log level must be 0 <= level <= 7") - def this_boot(self): - """Add match for _BOOT_ID equal to current boot ID.""" - self.add_match(_BOOT_ID=_id128.get_boot().hex) + def this_boot(self, bootid=None): + """Add match for _BOOT_ID equal to current boot ID or the specified boot ID. + + bootid should be either a UUID or a 32 digit hex number. + """ + if bootid is None: + bootid = _id128.get_boot().hex + else: + bootid = getattr(bootid, 'hex', bootid) + self.add_match(_BOOT_ID=bootid) + + def this_machine(self, machineid=None): + """Add match for _MACHINE_ID equal to the ID of this machine. + + bootid should be either a UUID or a 32 digit hex number. + """ + if machineid is None: + machineid = _id128.get_machine().hex + else: + machineid = getattr(machineid, 'hex', machineid) + self.add_match(_MACHINE_ID=machineid) - def this_machine(self): - """Add match for _MACHINE_ID equal to the ID of this machine.""" - self.add_match(_MACHINE_ID=_id128.get_machine().hex) def _make_line(field, value): if isinstance(value, bytes): -- cgit v1.2.1 From 8d5a8d36545fd118d3966607a3fe5e5d1059fab3 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 17 Feb 2013 11:53:09 +0000 Subject: systemd-python: tidy up import names in journal --- systemd/_reader.c | 2 +- systemd/journal.py | 30 +++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index c16cbdc..481da24 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -642,7 +642,7 @@ static PyMethodDef Journal_methods[] = { static PyTypeObject JournalType = { PyVarObject_HEAD_INIT(NULL, 0) - "_reader.Journal", /*tp_name*/ + "_reader._Journal", /*tp_name*/ sizeof(Journal), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Journal_dealloc, /*tp_dealloc*/ diff --git a/systemd/journal.py b/systemd/journal.py index 60c8111..a50922c 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -19,14 +19,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . -import datetime -import functools -import sys -import uuid +import sys as _sys +import datetime as _datetime +import functools as _functools +import uuid as _uuid import traceback as _traceback import os as _os +from os import SEEK_SET, SEEK_CUR, SEEK_END import logging as _logging -if sys.version_info >= (3,): +if _sys.version_info >= (3,): from collections import ChainMap from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) @@ -35,10 +36,12 @@ from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) from . import id128 as _id128 -_MONOTONIC_CONVERTER = lambda x: datetime.timedelta(microseconds=float(x)) -_REALTIME_CONVERTER = lambda x: datetime.datetime.fromtimestamp(float(x)/1E6) +_MONOTONIC_CONVERTER = lambda x: _datetime.timedelta(microseconds=float(x)) +_REALTIME_CONVERTER = lambda x: _datetime.datetime.fromtimestamp(float(x)/1E6) DEFAULT_CONVERTERS = { - 'MESSAGE_ID': uuid.UUID, + 'MESSAGE_ID': _uuid.UUID, + '_MACHINE_ID': _uuid.UUID, + '_BOOT_ID': _uuid.UUID, 'PRIORITY': int, 'LEADER': int, 'SESSION_ID': int, @@ -70,15 +73,16 @@ DEFAULT_CONVERTERS = { 'COREDUMP_TIMESTAMP': _REALTIME_CONVERTER, } -if sys.version_info >= (3,): - _convert_unicode = functools.partial(str, encoding='utf-8') +if _sys.version_info >= (3,): + _convert_unicode = _functools.partial(str, encoding='utf-8') else: - _convert_unicode = functools.partial(unicode, encoding='utf-8') + _convert_unicode = _functools.partial(unicode, encoding='utf-8') class Journal(_Journal): def __init__(self, converters=None, *args, **kwargs): super(Journal, self).__init__(*args, **kwargs) if sys.version_info >= (3,3): + if _sys.version_info >= (3,3): self.converters = ChainMap() if converters is not None: self.converters.maps.append(converters) @@ -125,12 +129,12 @@ class Journal(_Journal): for value in super(Journal, self).query_unique(key)) def seek_realtime(self, timestamp): - if isinstance(timestamp, datetime.datetime): + if isinstance(realtime, _datetime.datetime): timestamp = float(timestamp.strftime("%s.%f")) return super(Journal, self).seek_realtime(timestamp) def seek_monotonic(self, timestamp, bootid=None): - if isinstance(timestamp, datetime.timedelta): + if isinstance(monotonic, _datetime.timedelta): timestamp = timestamp.totalseconds() return super(Journal, self).seek_monotonic(timestamp, bootid) -- cgit v1.2.1 From 3813a8ab261dbab1b9667323b53cac01db11df4e Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 17 Feb 2013 11:55:45 +0000 Subject: systemd-python: update Journal python docstrings --- systemd/_reader.c | 2 +- systemd/journal.py | 77 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 481da24..70676ac 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -57,7 +57,7 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) char *path=NULL; static char *kwlist[] = {"flags", "path", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, keywds, "|iz", kwlist, &flags, &path)) return 1; diff --git a/systemd/journal.py b/systemd/journal.py index a50922c..4f42928 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -79,21 +79,34 @@ else: _convert_unicode = _functools.partial(unicode, encoding='utf-8') class Journal(_Journal): - def __init__(self, converters=None, *args, **kwargs): - super(Journal, self).__init__(*args, **kwargs) - if sys.version_info >= (3,3): + def __init__(self, converters=None, flags=LOCAL_ONLY, path=None): + """Creates instance of Journal, which allows filtering and + return of journal entries. + Argument `converters` is a dictionary which updates the + DEFAULT_CONVERTERS to convert journal field values. + Argument `flags` sets open flags of the journal, which can be one + of, or ORed combination of constants: LOCAL_ONLY (default) opens + journal on local machine only; RUNTIME_ONLY opens only + volatile journal files; and SYSTEM_ONLY opens only + journal files of system services and the kernel. + Argument `path` is the directory of journal files. Note that + currently flags are ignored when `path` is present as they are + currently not relevant. + """ + super(Journal, self).__init__(flags, path) 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: - # suitable fallback, e.g. self.converters = DEFAULT_CONVERTERS.copy() if converters is not None: self.converters.update(converters) def _convert_field(self, key, value): + """ Convert value based on callable from self.converters + based of field/key""" try: result = self.converters[key](value) except: @@ -106,6 +119,7 @@ class Journal(_Journal): return result def _convert_entry(self, entry): + """ Convert entire journal entry utilising _covert_field""" result = {} for key, value in entry.items(): if isinstance(value, list): @@ -115,31 +129,54 @@ class Journal(_Journal): return result def add_match(self, *args, **kwargs): + """Add one or more matches to the filter journal log entries. + All matches of different field are combined in a logical AND, + and matches of the smae field are automatically combined in a + logical OR. + Matches can be passed as strings of form "field=value", or + keyword arguments field="value".""" args = list(args) args.extend(_make_line(key, val) for key, val in kwargs.items()) for arg in args: super(Journal, self).add_match(arg) def get_next(self, skip=1): + """Return dictionary of the next log entry. Optional skip value + will return the `skip`th log entry. + Returned will be journal entry dictionary processed with + converters.""" return self._convert_entry( super(Journal, self).get_next(skip)) - def query_unique(self, key): - return set(self._convert_field(key, value) - for value in super(Journal, self).query_unique(key)) - - def seek_realtime(self, timestamp): + def query_unique(self, field): + """Returns a set of unique values in journal for given `field`, + processed with converters. + Note this does not respect any journal matches.""" + return set(self._convert_field(field, value) + for value in super(Journal, self).query_unique(field)) + + def seek_realtime(self, realtime): + """Seek to nearest matching journal entry to `realtime`. + Argument `realtime` can must be either an integer unix timestamp + or datetime.datetime instance.""" if isinstance(realtime, _datetime.datetime): - timestamp = float(timestamp.strftime("%s.%f")) - return super(Journal, self).seek_realtime(timestamp) - - def seek_monotonic(self, timestamp, bootid=None): + realtime = float(realtime.strftime("%s.%f")) + return super(Journal, self).seek_realtime(realtime) + + def seek_monotonic(self, monotonic, bootid=None): + """Seek to nearest matching journal entry to `monotonic`. + Argument `monotonic` is a timestamp from boot in either seconds + or a datetime.timedelta instance. + Argument `bootid` is a string or UUID representing which boot the + monotonic time is reference to. Defaults to current bootid.""" if isinstance(monotonic, _datetime.timedelta): - timestamp = timestamp.totalseconds() - return super(Journal, self).seek_monotonic(timestamp, bootid) + monotonic = monotonic.totalseconds() + if isinstance(bootid, _uuid.UUID): + bootid = bootid.get_hex() + return super(Journal, self).seek_monotonic(monotonic, bootid) def log_level(self, level): - """Sets maximum log level by setting matches for PRIORITY.""" + """Sets maximum log `level` by setting matches for PRIORITY.""" if 0 <= level <= 7: for i in range(level+1): self.add_match(PRIORITY="%s" % i) @@ -149,7 +186,9 @@ class Journal(_Journal): def this_boot(self, bootid=None): """Add match for _BOOT_ID equal to current boot ID or the specified boot ID. - bootid should be either a UUID or a 32 digit hex number. + If specified, bootid should be either a UUID or a 32 digit hex number. + + Equivalent to add_match(_BOOT_ID='bootid'). """ if bootid is None: bootid = _id128.get_boot().hex @@ -160,7 +199,9 @@ class Journal(_Journal): def this_machine(self, machineid=None): """Add match for _MACHINE_ID equal to the ID of this machine. - bootid should be either a UUID or a 32 digit hex number. + If specified, machineid should be either a UUID or a 32 digit hex number. + + Equivalent to add_match(_MACHINE_ID='machineid'). """ if machineid is None: machineid = _id128.get_machine().hex -- cgit v1.2.1 From 82ff2498de678de19076bb3f3cf34de210164c0f Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 17 Feb 2013 14:27:59 +0000 Subject: systemd-python: fix memory leak in _reader and minor bugs iternext now checks for error from get_next, and changed a DECREF to XDECREF rather than NULL check --- systemd/_reader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 70676ac..b754014 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -326,8 +326,7 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); } - if (result) - Py_DECREF(result); + Py_XDECREF(result); if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; @@ -498,6 +497,8 @@ Journal_iternext(PyObject *self) Py_ssize_t dict_size; dict = PyObject_CallMethod(self, "get_next", ""); + if (PyErr_Occurred()) + return NULL; dict_size = PyDict_Size(dict); if ((int64_t) dict_size > 0LL) { return dict; -- cgit v1.2.1 From d21d05648df0093f77363fe891bdb2cc11d036e0 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 17 Feb 2013 17:46:01 +0000 Subject: systemd-python: add Journal method to add MESSAGE_ID match --- systemd/journal.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index 4f42928..d61c30e 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -183,6 +183,15 @@ class Journal(_Journal): else: raise ValueError("Log level must be 0 <= level <= 7") + def messageid_match(self, messageid): + """Sets match filter for log entries for specified `messageid`. + `messageid` can be string or UUID instance. + Standard message IDs can be found in systemd.id128 + Equivalent to add_match(MESSAGE_ID=`messageid`).""" + if isinstance(messageid, _uuid.UUID): + messageid = messageid.get_hex() + self.add_match(MESSAGE_ID=messageid) + def this_boot(self, bootid=None): """Add match for _BOOT_ID equal to current boot ID or the specified boot ID. -- cgit v1.2.1 From 7df62d3a00d5345369ea5ccb2d410fa970f4bebe Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Tue, 19 Feb 2013 20:37:55 +0000 Subject: systemd-python: Journal convert_unicode exception handling change Rather than catch all, is now limited to UnicodeDecodeError --- systemd/journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/journal.py b/systemd/journal.py index d61c30e..5c5f5ca 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -113,7 +113,7 @@ class Journal(_Journal): # Default conversion in unicode try: result = _convert_unicode(value) - except: + except UnicodeDecodeError: # Leave in default bytes result = value return result -- cgit v1.2.1 From 31d2aff90e92cf79d2bb154b4c8db5255410f39c Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Tue, 19 Feb 2013 20:39:45 +0000 Subject: systemd-python: Added doc string for Journal --- systemd/journal.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index 5c5f5ca..d63722b 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -79,6 +79,24 @@ else: _convert_unicode = _functools.partial(unicode, encoding='utf-8') class Journal(_Journal): + """Journal allows the access and filtering of systemd journal + entries. Note that in order to access the system journal, a + non-root user must be in the `adm` group. + + Example usage to print out all error or higher level messages + for systemd-udevd for the boot: + + >>> myjournal = journal.Journal() + >>> myjournal.add_boot_match(journal.CURRENT_BOOT) + >>> myjournal.add_loglevel_matches(journal.LOG_ERR) + >>> myjournal.add_match(_SYSTEMD_UNIT="systemd-udevd.service") + >>> from __future__ import print_function + >>> for entry in myjournal: + ... print(entry['MESSAGE']) + + See man page "systemd.journal-fields" for more info on + typical fields found in the journal. + """ def __init__(self, converters=None, flags=LOCAL_ONLY, path=None): """Creates instance of Journal, which allows filtering and return of journal entries. -- cgit v1.2.1 From 544fbf3b3a4accda699a1da11b9ac8e2c159e5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 17 Feb 2013 11:26:10 -0500 Subject: systemd-python: introduce error setting helper --- systemd/_reader.c | 137 +++++++++++++++++------------------------------------- 1 file changed, 42 insertions(+), 95 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index b754014..65ca06b 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -30,6 +30,20 @@ typedef struct { } Journal; static PyTypeObject JournalType; +static int set_error(int r, const char* path, const char* invalid_message) { + if (r >= 0) + return r; + if (r == -EINVAL && invalid_message) + PyErr_SetString(PyExc_ValueError, invalid_message); + else if (r == -ENOMEM) + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + else { + errno = -r; + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + } + return 1; +} + static void Journal_dealloc(Journal* self) { @@ -69,16 +83,8 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) r = sd_journal_open(&self->j, flags); } Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyObject *errtype = r == -EINVAL ? PyExc_ValueError : - r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrnoWithFilename(errtype, path); - return -1; - } - return 0; + return set_error(r, path, "Invalid flags or path"); } PyDoc_STRVAR(Journal_get_next__doc__, @@ -110,13 +116,11 @@ Journal_get_next(Journal *self, PyObject *args) } Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + set_error(r, NULL, NULL); + if (r < 0) return NULL; - }else if ( r == 0) { //EOF + else if (r == 0) /* EOF */ return PyDict_New(); - } PyObject *dict; dict = PyDict_New(); @@ -232,14 +236,9 @@ Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) int r; r = sd_journal_add_match(self->j, match, match_len); - if (r < 0) { - errno = -r; - PyObject *errtype = r == -EINVAL ? PyExc_ValueError : - r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); - return NULL; - } + set_error(r, NULL, "Invalid match"); + if (r < 0) + return NULL; Py_RETURN_NONE; } @@ -253,13 +252,9 @@ Journal_add_disjunction(Journal *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); - if (r < 0) { - errno = -r; - PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); + set_error(r, NULL, NULL); + if (r < 0) return NULL; - } Py_RETURN_NONE; } @@ -297,11 +292,9 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_head(self->j); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + if (set_error(r, NULL, NULL)) return NULL; - } + if (offset > 0LL) { result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); } @@ -312,11 +305,9 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_tail(self->j); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + if (set_error(r, NULL, NULL)) return NULL; - } + if (offset < 0LL) { result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); }else{ @@ -355,11 +346,8 @@ Journal_seek_realtime(Journal *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + if (set_error(r, NULL, NULL)) return NULL; - } Py_RETURN_NONE; } @@ -389,37 +377,24 @@ Journal_seek_monotonic(Journal *self, PyObject *args) int r; if (bootid) { r = sd_id128_from_string(bootid, &sd_id); - if (r == -EINVAL) { - PyErr_SetString(PyExc_ValueError, "Invalid bootid"); + if (set_error(r, NULL, "Invalid bootid")) return NULL; - }else if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - }else{ + } else { + Py_BEGIN_ALLOW_THREADS r = sd_id128_get_boot(&sd_id); - if (r == -EIO) { - PyErr_SetString(PyExc_IOError, "Error getting current boot ID"); - return NULL; - }else if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) return NULL; - } } Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + if (set_error(r, NULL, NULL)) return NULL; - } Py_RETURN_NONE; } - + PyDoc_STRVAR(Journal_wait__doc__, "wait([timeout]) -> Change state (integer)\n\n" "Waits until there is a change in the journal. Argument `timeout`\n" @@ -438,19 +413,11 @@ Journal_wait(Journal *self, PyObject *args, PyObject *keywds) int r; Py_BEGIN_ALLOW_THREADS - if ( timeout == 0LL) { - r = sd_journal_wait(self->j, (uint64_t) -1); - }else{ - r = sd_journal_wait(self->j, timeout * 1E6); - } + r = sd_journal_wait(self->j, timeout ==0 ? (uint64_t) -1 : timeout * 1E6); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); + if (set_error(r, NULL, NULL)) return NULL; - } + #if PY_MAJOR_VERSION >=3 return PyLong_FromLong(r); #else @@ -472,14 +439,8 @@ Journal_seek_cursor(Journal *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_cursor(self->j, cursor); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyObject *errtype = r == -EINVAL ? PyExc_ValueError : - r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); + if (set_error(r, NULL, "Invalid cursor")) return NULL; - } Py_RETURN_NONE; } @@ -524,14 +485,8 @@ Journal_query_unique(Journal *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_query_unique(self->j, query); Py_END_ALLOW_THREADS - if (r < 0) { - errno = -r; - PyObject *errtype = r == -EINVAL ? PyExc_ValueError : - r == -ENOMEM ? PyExc_MemoryError : - PyExc_OSError; - PyErr_SetFromErrno(errtype); + if (set_error(r, NULL, "Invalid field name")) return NULL; - } const void *uniq; size_t uniq_len; @@ -563,11 +518,8 @@ Journal_get_data_threshold(Journal *self, void *closure) int r; r = sd_journal_get_data_threshold(self->j, &cvalue); - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); + if (set_error(r, NULL, NULL)) return NULL; - } #if PY_MAJOR_VERSION >=3 value = PyLong_FromSize_t(cvalue); @@ -598,12 +550,7 @@ Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) #else r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value)); #endif - if (r < 0) { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - return 0; + return set_error(r, NULL, NULL); } static PyGetSetDef Journal_getseters[] = { -- cgit v1.2.1 From 2d3c2201a4e6abb636418df36434d8822172c0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 16 Feb 2013 21:54:09 -0500 Subject: systemd-python: wrap some python differences using macros --- systemd/_reader.c | 74 +++++++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 48 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 65ca06b..6504396 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -24,6 +24,23 @@ #include #include +#if PY_MAJOR_VERSION >=3 +# define unicode_FromStringAndSize PyUnicode_FromStringAndSize +# define unicode_FromString PyUnicode_FromString +# define long_FromLong PyLong_FromLong +# define long_FromSize_t PyLong_FromSize_t +# define long_Check PyLong_Check +# define long_AsLong PyLong_AsLong +#else +/* Python 3 type naming convention is used */ +# define unicode_FromStringAndSize PyString_FromStringAndSize +# define unicode_FromString PyString_FromString +# define long_FromLong PyInt_FromLong +# define long_FromSize_t PyInt_FromSize_t +# define long_Check PyInt_Check +# define long_AsLong PyInt_AsLong +#endif + typedef struct { PyObject_HEAD sd_journal *j; @@ -132,11 +149,7 @@ Journal_get_next(Journal *self, PyObject *args) SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { delim_ptr = memchr(msg, '=', msg_len); -#if PY_MAJOR_VERSION >=3 - key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); -#else - key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg); -#endif + key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) ); if (PyDict_Contains(dict, key)) { cur_value = PyDict_GetItem(dict, key); @@ -160,12 +173,7 @@ Journal_get_next(Journal *self, PyObject *args) if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) { char realtime_str[20]; sprintf(realtime_str, "%llu", (long long unsigned) realtime); - -#if PY_MAJOR_VERSION >=3 - key = PyUnicode_FromString("__REALTIME_TIMESTAMP"); -#else - key = PyString_FromString("__REALTIME_TIMESTAMP"); -#endif + key = unicode_FromString("__REALTIME_TIMESTAMP"); value = PyBytes_FromString(realtime_str); PyDict_SetItem(dict, key, value); Py_DECREF(key); @@ -177,11 +185,7 @@ Journal_get_next(Journal *self, PyObject *args) if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) { char monotonic_str[20]; sprintf(monotonic_str, "%llu", (long long unsigned) monotonic); -#if PY_MAJOR_VERSION >=3 - key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP"); -#else - key = PyString_FromString("__MONOTONIC_TIMESTAMP"); -#endif + key = unicode_FromString("__MONOTONIC_TIMESTAMP"); value = PyBytes_FromString(monotonic_str); PyDict_SetItem(dict, key, value); @@ -191,11 +195,7 @@ Journal_get_next(Journal *self, PyObject *args) char *cursor; if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0... -#if PY_MAJOR_VERSION >=3 - key = PyUnicode_FromString("__CURSOR"); -#else - key = PyString_FromString("__CURSOR"); -#endif + key = unicode_FromString("__CURSOR"); value = PyBytes_FromString(cursor); PyDict_SetItem(dict, key, value); free(cursor); @@ -418,11 +418,7 @@ Journal_wait(Journal *self, PyObject *args, PyObject *keywds) if (set_error(r, NULL, NULL)) return NULL; -#if PY_MAJOR_VERSION >=3 - return PyLong_FromLong(r); -#else - return PyInt_FromLong(r); -#endif + return long_FromLong(r); } PyDoc_STRVAR(Journal_seek_cursor__doc__, @@ -493,12 +489,7 @@ Journal_query_unique(Journal *self, PyObject *args) const char *delim_ptr; PyObject *value_set, *key, *value; value_set = PySet_New(0); - -#if PY_MAJOR_VERSION >=3 - key = PyUnicode_FromString(query); -#else - key = PyString_FromString(query); -#endif + key = unicode_FromString(query); SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { delim_ptr = memchr(uniq, '=', uniq_len); @@ -521,12 +512,7 @@ Journal_get_data_threshold(Journal *self, void *closure) if (set_error(r, NULL, NULL)) return NULL; -#if PY_MAJOR_VERSION >=3 - value = PyLong_FromSize_t(cvalue); -#else - value = PyInt_FromSize_t(cvalue); -#endif - return value; + return long_FromSize_t(cvalue); } static int @@ -536,20 +522,12 @@ Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold"); return -1; } -#if PY_MAJOR_VERSION >=3 - if (! PyLong_Check(value)){ -#else - if (! PyInt_Check(value)){ -#endif + if (!long_Check(value)){ PyErr_SetString(PyExc_TypeError, "Data threshold must be int"); return -1; } int r; -#if PY_MAJOR_VERSION >=3 - r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value)); -#else - r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value)); -#endif + r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value)); return set_error(r, NULL, NULL); } -- cgit v1.2.1 From b8b5706a1d7055bbefc695fd32a45df44602c00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 19 Feb 2013 21:58:54 -0500 Subject: systemd-python: add casts and fix unused variable warnings in _reader --- systemd/_reader.c | 58 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 6504396..c6f29f5 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -84,12 +84,12 @@ PyDoc_STRVAR(Journal__doc__, static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) { - int flags=SD_JOURNAL_LOCAL_ONLY; - char *path=NULL; + int flags = SD_JOURNAL_LOCAL_ONLY; + char *path = NULL; - static char *kwlist[] = {"flags", "path", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, keywds, "|iz", kwlist, - &flags, &path)) + static const char* const kwlist[] = {"flags", "path", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist, + &flags, &path)) return 1; int r; @@ -217,7 +217,8 @@ Journal_get_previous(Journal *self, PyObject *args) if (! PyArg_ParseTuple(args, "|L", &skip)) return NULL; - return PyObject_CallMethod((PyObject *)self, "get_next", "L", -skip); + return PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", -skip); } PyDoc_STRVAR(Journal_add_match__doc__, @@ -280,10 +281,10 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) { int64_t offset; int whence=SEEK_SET; - static char *kwlist[] = {"offset", "whence", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist, - &offset, &whence)) + static const char* const kwlist[] = {"offset", "whence", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist, + &offset, &whence)) return NULL; PyObject *result=NULL; @@ -296,10 +297,12 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) return NULL; if (offset > 0LL) { - result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset); } }else if (whence == SEEK_CUR){ - result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset); }else if (whence == SEEK_END){ int r; Py_BEGIN_ALLOW_THREADS @@ -309,9 +312,11 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) return NULL; if (offset < 0LL) { - result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset); + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset); }else{ - result = PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL); + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", -1LL); } }else{ PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); @@ -407,11 +412,12 @@ PyDoc_STRVAR(Journal_wait__doc__, static PyObject * Journal_wait(Journal *self, PyObject *args, PyObject *keywds) { - int64_t timeout=0LL; - if (! PyArg_ParseTuple(args, "|L", &timeout)) + int r; + int64_t timeout = 0LL; + + if (!PyArg_ParseTuple(args, "|L", &timeout)) return NULL; - int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_wait(self->j, timeout ==0 ? (uint64_t) -1 : timeout * 1E6); Py_END_ALLOW_THREADS @@ -453,7 +459,7 @@ Journal_iternext(PyObject *self) PyObject *dict; Py_ssize_t dict_size; - dict = PyObject_CallMethod(self, "get_next", ""); + dict = PyObject_CallMethod(self, (char*) "get_next", (char*) ""); if (PyErr_Occurred()) return NULL; dict_size = PyDict_Size(dict); @@ -505,7 +511,6 @@ static PyObject * Journal_get_data_threshold(Journal *self, void *closure) { size_t cvalue; - PyObject *value; int r; r = sd_journal_get_data_threshold(self->j, &cvalue); @@ -532,11 +537,11 @@ Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) } static PyGetSetDef Journal_getseters[] = { - {"data_threshold", - (getter)Journal_get_data_threshold, - (setter)Journal_set_data_threshold, - "data threshold", - NULL}, + {(char*) "data_threshold", + (getter)Journal_get_data_threshold, + (setter)Journal_set_data_threshold, + (char*) "data threshold", + NULL}, {NULL} }; @@ -617,11 +622,14 @@ static PyModuleDef _reader_module = { }; #endif +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 PyInit__reader(void) #else -init_reader(void) +init_reader(void) #endif { PyObject* m; @@ -659,3 +667,5 @@ init_reader(void) return m; #endif } + +#pragma GCC diagnostic pop -- cgit v1.2.1 From 79931a5520d810eefebd82b818c38166b1cd6d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 19 Feb 2013 22:11:02 -0500 Subject: systemd-python: downgrade _reader.c to C89 --- systemd/_reader.c | 167 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index c6f29f5..d0ccb91 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -84,7 +84,7 @@ PyDoc_STRVAR(Journal__doc__, static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) { - int flags = SD_JOURNAL_LOCAL_ONLY; + int flags = SD_JOURNAL_LOCAL_ONLY, r; char *path = NULL; static const char* const kwlist[] = {"flags", "path", NULL}; @@ -92,13 +92,11 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) &flags, &path)) return 1; - int r; Py_BEGIN_ALLOW_THREADS - if (path) { + if (path) r = sd_journal_open_directory(&self->j, path, 0); - }else{ + else r = sd_journal_open(&self->j, flags); - } Py_END_ALLOW_THREADS return set_error(r, path, "Invalid flags or path"); @@ -111,26 +109,30 @@ PyDoc_STRVAR(Journal_get_next__doc__, static PyObject * Journal_get_next(Journal *self, PyObject *args) { - int64_t skip=1LL; + PyObject *dict; + const void *msg; + size_t msg_len; + const char *delim_ptr; + PyObject *key, *value, *cur_value, *tmp_list; + + int64_t skip = 1LL, r = -EINVAL; if (! PyArg_ParseTuple(args, "|L", &skip)) return NULL; if (skip == 0LL) { - PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer"); + PyErr_SetString(PyExc_ValueError, "skip must be nonzero"); return NULL; } - int r = -EINVAL; Py_BEGIN_ALLOW_THREADS - if (skip == 1LL) { + if (skip == 1LL) r = sd_journal_next(self->j); - }else if (skip == -1LL) { + else if (skip == -1LL) r = sd_journal_previous(self->j); - }else if (skip > 1LL) { + else if (skip > 1LL) r = sd_journal_next_skip(self->j, skip); - }else if (skip < -1LL) { + else if (skip < -1LL) r = sd_journal_previous_skip(self->j, -skip); - } Py_END_ALLOW_THREADS set_error(r, NULL, NULL); @@ -139,14 +141,8 @@ Journal_get_next(Journal *self, PyObject *args) else if (r == 0) /* EOF */ return PyDict_New(); - PyObject *dict; dict = PyDict_New(); - const void *msg; - size_t msg_len; - const char *delim_ptr; - PyObject *key, *value, *cur_value, *tmp_list; - SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { delim_ptr = memchr(msg, '=', msg_len); key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); @@ -169,38 +165,44 @@ Journal_get_next(Journal *self, PyObject *args) Py_DECREF(value); } - uint64_t realtime; - if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) { - char realtime_str[20]; - sprintf(realtime_str, "%llu", (long long unsigned) realtime); - key = unicode_FromString("__REALTIME_TIMESTAMP"); - value = PyBytes_FromString(realtime_str); - PyDict_SetItem(dict, key, value); - Py_DECREF(key); - Py_DECREF(value); + { + uint64_t realtime; + if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) { + char realtime_str[20]; + sprintf(realtime_str, "%llu", (long long unsigned) realtime); + key = unicode_FromString("__REALTIME_TIMESTAMP"); + value = PyBytes_FromString(realtime_str); + PyDict_SetItem(dict, key, value); + Py_DECREF(key); + Py_DECREF(value); + } } - sd_id128_t sd_id; - uint64_t monotonic; - if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) { - char monotonic_str[20]; - sprintf(monotonic_str, "%llu", (long long unsigned) monotonic); - key = unicode_FromString("__MONOTONIC_TIMESTAMP"); - value = PyBytes_FromString(monotonic_str); - - PyDict_SetItem(dict, key, value); - Py_DECREF(key); - Py_DECREF(value); + { + sd_id128_t sd_id; + uint64_t monotonic; + if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) { + char monotonic_str[20]; + sprintf(monotonic_str, "%llu", (long long unsigned) monotonic); + key = unicode_FromString("__MONOTONIC_TIMESTAMP"); + value = PyBytes_FromString(monotonic_str); + + PyDict_SetItem(dict, key, value); + Py_DECREF(key); + Py_DECREF(value); + } } - char *cursor; - if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0... - key = unicode_FromString("__CURSOR"); - value = PyBytes_FromString(cursor); - PyDict_SetItem(dict, key, value); - free(cursor); - Py_DECREF(key); - Py_DECREF(value); + { + char *cursor; + if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0... + key = unicode_FromString("__CURSOR"); + value = PyBytes_FromString(cursor); + PyDict_SetItem(dict, key, value); + free(cursor); + Py_DECREF(key); + Py_DECREF(value); + } } return dict; @@ -231,11 +233,10 @@ static PyObject * Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) { char *match; - int match_len; - if (! PyArg_ParseTuple(args, "s#", &match, &match_len)) + int match_len, r; + if (!PyArg_ParseTuple(args, "s#", &match, &match_len)) return NULL; - int r; r = sd_journal_add_match(self->j, match, match_len); set_error(r, NULL, "Invalid match"); if (r < 0) @@ -280,15 +281,16 @@ static PyObject * Journal_seek(Journal *self, PyObject *args, PyObject *keywds) { int64_t offset; - int whence=SEEK_SET; + int whence = SEEK_SET; + PyObject *result = NULL; static const char* const kwlist[] = {"offset", "whence", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist, &offset, &whence)) return NULL; - PyObject *result=NULL; - if (whence == SEEK_SET){ + switch(whence) { + case SEEK_SET: { int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_head(self->j); @@ -296,14 +298,16 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) if (set_error(r, NULL, NULL)) return NULL; - if (offset > 0LL) { + if (offset > 0LL) result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", (char*) "L", offset); - } - }else if (whence == SEEK_CUR){ + break; + } + case SEEK_CUR: result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", (char*) "L", offset); - }else if (whence == SEEK_END){ + break; + case SEEK_END: { int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_tail(self->j); @@ -311,14 +315,11 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) if (set_error(r, NULL, NULL)) return NULL; - if (offset < 0LL) { - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset); - }else{ - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", -1LL); - } - }else{ + result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", + (char*) "L", offset < 0LL ? offset : -1LL); + break; + } + default: PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); } @@ -336,18 +337,18 @@ static PyObject * Journal_seek_realtime(Journal *self, PyObject *args) { double timedouble; - if (! PyArg_ParseTuple(args, "d", &timedouble)) + uint64_t timestamp; + int r; + + if (!PyArg_ParseTuple(args, "d", &timedouble)) return NULL; - uint64_t timestamp; timestamp = (uint64_t) (timedouble * 1.0E6); - if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be positive integer"); + PyErr_SetString(PyExc_ValueError, "Time must be a positive integer"); return NULL; } - int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS @@ -367,10 +368,13 @@ Journal_seek_monotonic(Journal *self, PyObject *args) { double timedouble; char *bootid=NULL; + uint64_t timestamp; + sd_id128_t sd_id; + int r; + if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) return NULL; - uint64_t timestamp; timestamp = (uint64_t) (timedouble * 1.0E6); if ((int64_t) timestamp < 0LL) { @@ -378,8 +382,6 @@ Journal_seek_monotonic(Journal *self, PyObject *args) return NULL; } - sd_id128_t sd_id; - int r; if (bootid) { r = sd_id128_from_string(bootid, &sd_id); if (set_error(r, NULL, "Invalid bootid")) @@ -434,10 +436,11 @@ static PyObject * Journal_seek_cursor(Journal *self, PyObject *args) { const char *cursor; + int r; + if (! PyArg_ParseTuple(args, "s", &cursor)) return NULL; - int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_cursor(self->j, cursor); Py_END_ALLOW_THREADS @@ -480,24 +483,26 @@ static PyObject * Journal_query_unique(Journal *self, PyObject *args) { char *query; + int r; + const void *uniq; + size_t uniq_len; + PyObject *value_set, *key, *value; + if (! PyArg_ParseTuple(args, "s", &query)) return NULL; - int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_query_unique(self->j, query); Py_END_ALLOW_THREADS if (set_error(r, NULL, "Invalid field name")) return NULL; - const void *uniq; - size_t uniq_len; - const char *delim_ptr; - PyObject *value_set, *key, *value; value_set = PySet_New(0); key = unicode_FromString(query); SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { + const char *delim_ptr; + delim_ptr = memchr(uniq, '=', uniq_len); value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1)); PySet_Add(value_set, value); @@ -523,15 +528,15 @@ Journal_get_data_threshold(Journal *self, void *closure) static int Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) { + int r; if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold"); return -1; } if (!long_Check(value)){ - PyErr_SetString(PyExc_TypeError, "Data threshold must be int"); + PyErr_SetString(PyExc_TypeError, "Data threshold must be an int"); return -1; } - int r; r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value)); return set_error(r, NULL, NULL); } -- cgit v1.2.1 From 2575a15b6104c835d9482813cd08f6c9a57c863e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 19 Feb 2013 22:22:03 -0500 Subject: systemd-python: indenation and style tweaks --- systemd/_reader.c | 177 ++++++++++++++++++++++++------------------------------ 1 file changed, 80 insertions(+), 97 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index d0ccb91..1bca116 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ +/*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -61,28 +61,26 @@ static int set_error(int r, const char* path, const char* invalid_message) { return 1; } -static void -Journal_dealloc(Journal* self) +static void Journal_dealloc(Journal* self) { sd_journal_close(self->j); Py_TYPE(self)->tp_free((PyObject*)self); } PyDoc_STRVAR(Journal__doc__, -"Journal([flags][,path]) -> ...\n" -"Journal instance\n\n" -"Returns instance of Journal, which allows filtering and return\n" -"of journal entries.\n" -"Argument `flags` sets open flags of the journal, which can be one\n" -"of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" -"journal on local machine only; RUNTIME_ONLY opens only\n" -"volatile journal files; and SYSTEM_ONLY opens only\n" -"journal files of system services and the kernel.\n" -"Argument `path` is the directory of journal files. Note that\n" -"currently flags are ignored when `path` is present as they are\n" -" not relevant."); -static int -Journal_init(Journal *self, PyObject *args, PyObject *keywds) + "Journal([flags][,path]) -> ...\n" + "Journal instance\n\n" + "Returns instance of Journal, which allows filtering and return\n" + "of journal entries.\n" + "Argument `flags` sets open flags of the journal, which can be one\n" + "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" + "journal on local machine only; RUNTIME_ONLY opens only\n" + "volatile journal files; and SYSTEM_ONLY opens only\n" + "journal files of system services and the kernel.\n" + "Argument `path` is the directory of journal files. Note that\n" + "currently flags are ignored when `path` is present as they are\n" + " not relevant."); +static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) { int flags = SD_JOURNAL_LOCAL_ONLY, r; char *path = NULL; @@ -103,11 +101,10 @@ Journal_init(Journal *self, PyObject *args, PyObject *keywds) } PyDoc_STRVAR(Journal_get_next__doc__, -"get_next([skip]) -> dict\n\n" -"Return dictionary of the next log entry. Optional skip value will\n" -"return the `skip`th log entry."); -static PyObject * -Journal_get_next(Journal *self, PyObject *args) + "get_next([skip]) -> dict\n\n" + "Return dictionary of the next log entry. Optional skip value will\n" + "return the `skip`th log entry."); +static PyObject* Journal_get_next(Journal *self, PyObject *args) { PyObject *dict; const void *msg; @@ -116,7 +113,7 @@ Journal_get_next(Journal *self, PyObject *args) PyObject *key, *value, *cur_value, *tmp_list; int64_t skip = 1LL, r = -EINVAL; - if (! PyArg_ParseTuple(args, "|L", &skip)) + if (!PyArg_ParseTuple(args, "|L", &skip)) return NULL; if (skip == 0LL) { @@ -209,14 +206,13 @@ Journal_get_next(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_get_previous__doc__, -"get_previous([skip]) -> dict\n\n" -"Return dictionary of the previous log entry. Optional skip value\n" -"will return the -`skip`th log entry. Equivalent to get_next(-skip)."); -static PyObject * -Journal_get_previous(Journal *self, PyObject *args) + "get_previous([skip]) -> dict\n\n" + "Return dictionary of the previous log entry. Optional skip value\n" + "will return the -`skip`th log entry. Equivalent to get_next(-skip)."); +static PyObject* Journal_get_previous(Journal *self, PyObject *args) { - int64_t skip=1LL; - if (! PyArg_ParseTuple(args, "|L", &skip)) + int64_t skip = 1LL; + if (!PyArg_ParseTuple(args, "|L", &skip)) return NULL; return PyObject_CallMethod((PyObject *)self, (char*) "get_next", @@ -224,13 +220,12 @@ Journal_get_previous(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_add_match__doc__, -"add_match(match) -> None\n\n" -"Add a match to filter journal log entries. All matches of different\n" -"fields are combined in logical AND, and matches of the same field\n" -"are automatically combined in logical OR.\n" -"Match is string of form \"field=value\"."); -static PyObject * -Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) + "add_match(match) -> None\n\n" + "Add a match to filter journal log entries. All matches of different\n" + "fields are combined in logical AND, and matches of the same field\n" + "are automatically combined in logical OR.\n" + "Match is a string of the form \"FIELD=value\"."); +static PyObject* Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) { char *match; int match_len, r; @@ -246,11 +241,10 @@ Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) } PyDoc_STRVAR(Journal_add_disjunction__doc__, -"add_disjunction() -> None\n\n" -"Once called, all matches before and after are combined in logical\n" -"OR."); -static PyObject * -Journal_add_disjunction(Journal *self, PyObject *args) + "add_disjunction() -> None\n\n" + "Once called, all matches before and after are combined in logical\n" + "OR."); +static PyObject* Journal_add_disjunction(Journal *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); @@ -261,24 +255,22 @@ Journal_add_disjunction(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_flush_matches__doc__, -"flush_matches() -> None\n\n" -"Clears all current match filters."); -static PyObject * -Journal_flush_matches(Journal *self, PyObject *args) + "flush_matches() -> None\n\n" + "Clears all current match filters."); +static PyObject* Journal_flush_matches(Journal *self, PyObject *args) { sd_journal_flush_matches(self->j); Py_RETURN_NONE; } PyDoc_STRVAR(Journal_seek__doc__, -"seek(offset[, whence]) -> None\n\n" -"Seek through journal by `offset` number of entries. Argument\n" -"`whence` defines what the offset is relative to:\n" -"os.SEEK_SET (default) from first match in journal;\n" -"os.SEEK_CUR from current position in journal;\n" -"and os.SEEK_END is from last match in journal."); -static PyObject * -Journal_seek(Journal *self, PyObject *args, PyObject *keywds) + "seek(offset[, whence]) -> None\n\n" + "Seek through journal by `offset` number of entries. Argument\n" + "`whence` defines what the offset is relative to:\n" + "os.SEEK_SET (default) from first match in journal;\n" + "os.SEEK_CUR from current position in journal;\n" + "and os.SEEK_END is from last match in journal."); +static PyObject* Journal_seek(Journal *self, PyObject *args, PyObject *keywds) { int64_t offset; int whence = SEEK_SET; @@ -330,11 +322,10 @@ Journal_seek(Journal *self, PyObject *args, PyObject *keywds) } PyDoc_STRVAR(Journal_seek_realtime__doc__, -"seek_realtime(realtime) -> None\n\n" -"Seek to nearest matching journal entry to `realtime`. Argument\n" -"`realtime` can must be an integer unix timestamp."); -static PyObject * -Journal_seek_realtime(Journal *self, PyObject *args) + "seek_realtime(realtime) -> None\n\n" + "Seek to nearest matching journal entry to `realtime`. Argument\n" + "`realtime` can must be an integer unix timestamp."); +static PyObject* Journal_seek_realtime(Journal *self, PyObject *args) { double timedouble; uint64_t timestamp; @@ -358,21 +349,20 @@ Journal_seek_realtime(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_seek_monotonic__doc__, -"seek_monotonic(monotonic[, bootid]) -> None\n\n" -"Seek to nearest matching journal entry to `monotonic`. Argument\n" -"`monotonic` is an timestamp from boot in seconds.\n" -"Argument `bootid` is a string representing which boot the\n" -"monotonic time is reference to. Defaults to current bootid."); -static PyObject * -Journal_seek_monotonic(Journal *self, PyObject *args) + "seek_monotonic(monotonic[, bootid]) -> None\n\n" + "Seek to nearest matching journal entry to `monotonic`. Argument\n" + "`monotonic` is an timestamp from boot in seconds.\n" + "Argument `bootid` is a string representing which boot the\n" + "monotonic time is reference to. Defaults to current bootid."); +static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) { double timedouble; - char *bootid=NULL; + char *bootid = NULL; uint64_t timestamp; sd_id128_t sd_id; int r; - if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) + if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) return NULL; timestamp = (uint64_t) (timedouble * 1.0E6); @@ -403,16 +393,15 @@ Journal_seek_monotonic(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_wait__doc__, -"wait([timeout]) -> Change state (integer)\n\n" -"Waits until there is a change in the journal. Argument `timeout`\n" -"is the maximum number of seconds to wait before returning\n" -"regardless if journal has changed. If `timeout` is not given or is\n" -"0, then it will block forever.\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."); -static PyObject * -Journal_wait(Journal *self, PyObject *args, PyObject *keywds) + "wait([timeout]) -> Change state (integer)\n\n" + "Waits until there is a change in the journal. Argument `timeout`\n" + "is the maximum number of seconds to wait before returning\n" + "regardless if journal has changed. If `timeout` is not given or is\n" + "0, then it will block forever.\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."); +static PyObject* Journal_wait(Journal *self, PyObject *args, PyObject *keywds) { int r; int64_t timeout = 0LL; @@ -430,15 +419,14 @@ Journal_wait(Journal *self, PyObject *args, PyObject *keywds) } PyDoc_STRVAR(Journal_seek_cursor__doc__, -"seek_cursor(cursor) -> None\n\n" -"Seeks to journal entry by given unique reference `cursor`."); -static PyObject * -Journal_seek_cursor(Journal *self, PyObject *args) + "seek_cursor(cursor) -> None\n\n" + "Seeks to journal entry by given unique reference `cursor`."); +static PyObject* Journal_seek_cursor(Journal *self, PyObject *args) { const char *cursor; int r; - if (! PyArg_ParseTuple(args, "s", &cursor)) + if (!PyArg_ParseTuple(args, "s", &cursor)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -449,15 +437,13 @@ Journal_seek_cursor(Journal *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -Journal_iter(PyObject *self) +static PyObject* Journal_iter(PyObject *self) { Py_INCREF(self); return self; } -static PyObject * -Journal_iternext(PyObject *self) +static PyObject* Journal_iternext(PyObject *self) { PyObject *dict; Py_ssize_t dict_size; @@ -476,11 +462,10 @@ Journal_iternext(PyObject *self) } PyDoc_STRVAR(Journal_query_unique__doc__, -"query_unique(field) -> a set of values\n\n" -"Returns a set of unique values in journal for given `field`.\n" -"Note this does not respect any journal matches."); -static PyObject * -Journal_query_unique(Journal *self, PyObject *args) + "query_unique(field) -> a set of values\n\n" + "Returns a set of unique values in journal for given `field`.\n" + "Note this does not respect any journal matches."); +static PyObject* Journal_query_unique(Journal *self, PyObject *args) { char *query; int r; @@ -488,7 +473,7 @@ Journal_query_unique(Journal *self, PyObject *args) size_t uniq_len; PyObject *value_set, *key, *value; - if (! PyArg_ParseTuple(args, "s", &query)) + if (!PyArg_ParseTuple(args, "s", &query)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -512,8 +497,7 @@ Journal_query_unique(Journal *self, PyObject *args) return value_set; } -static PyObject * -Journal_get_data_threshold(Journal *self, void *closure) +static PyObject* Journal_get_data_threshold(Journal *self, void *closure) { size_t cvalue; int r; @@ -525,8 +509,7 @@ Journal_get_data_threshold(Journal *self, void *closure) return long_FromSize_t(cvalue); } -static int -Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) +static int Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) { int r; if (value == NULL) { -- cgit v1.2.1 From c18311bd54fe55a20bcf90209fc0e3a1424ec538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 19 Feb 2013 23:03:32 -0500 Subject: systemd-python: polish the docstrings --- systemd/_reader.c | 54 ++++++++++++++++++++++------------------- systemd/id128.c | 8 +++--- systemd/journal.py | 71 +++++++++++++++++++++++++++++++++--------------------- 3 files changed, 76 insertions(+), 57 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 1bca116..207b9e7 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -68,10 +68,8 @@ static void Journal_dealloc(Journal* self) } PyDoc_STRVAR(Journal__doc__, - "Journal([flags][,path]) -> ...\n" - "Journal instance\n\n" - "Returns instance of Journal, which allows filtering and return\n" - "of journal entries.\n" + "Journal([flags][,path]) -> ...\n\n" + "Journal allows filtering and retrieval of Journal entries.\n" "Argument `flags` sets open flags of the journal, which can be one\n" "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" "journal on local machine only; RUNTIME_ONLY opens only\n" @@ -79,7 +77,7 @@ PyDoc_STRVAR(Journal__doc__, "journal files of system services and the kernel.\n" "Argument `path` is the directory of journal files. Note that\n" "currently flags are ignored when `path` is present as they are\n" - " not relevant."); + "not relevant."); static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) { int flags = SD_JOURNAL_LOCAL_ONLY, r; @@ -103,7 +101,7 @@ static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) PyDoc_STRVAR(Journal_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" - "return the `skip`th log entry."); + "return the `skip`\\-th log entry."); static PyObject* Journal_get_next(Journal *self, PyObject *args) { PyObject *dict; @@ -208,7 +206,7 @@ static PyObject* Journal_get_next(Journal *self, PyObject *args) PyDoc_STRVAR(Journal_get_previous__doc__, "get_previous([skip]) -> dict\n\n" "Return dictionary of the previous log entry. Optional skip value\n" - "will return the -`skip`th log entry. Equivalent to get_next(-skip)."); + "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip)."); static PyObject* Journal_get_previous(Journal *self, PyObject *args) { int64_t skip = 1LL; @@ -222,8 +220,8 @@ static PyObject* Journal_get_previous(Journal *self, PyObject *args) PyDoc_STRVAR(Journal_add_match__doc__, "add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" - "fields are combined in logical AND, and matches of the same field\n" - "are automatically combined in logical OR.\n" + "fields are combined with logical AND, and matches of the same field\n" + "are automatically combined with logical OR.\n" "Match is a string of the form \"FIELD=value\"."); static PyObject* Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) { @@ -242,8 +240,7 @@ static PyObject* Journal_add_match(Journal *self, PyObject *args, PyObject *keyw PyDoc_STRVAR(Journal_add_disjunction__doc__, "add_disjunction() -> None\n\n" - "Once called, all matches before and after are combined in logical\n" - "OR."); + "Inserts a logical OR between matches added before and afterwards."); static PyObject* Journal_add_disjunction(Journal *self, PyObject *args) { int r; @@ -256,7 +253,7 @@ static PyObject* Journal_add_disjunction(Journal *self, PyObject *args) PyDoc_STRVAR(Journal_flush_matches__doc__, "flush_matches() -> None\n\n" - "Clears all current match filters."); + "Clear all current match filters."); static PyObject* Journal_flush_matches(Journal *self, PyObject *args) { sd_journal_flush_matches(self->j); @@ -265,7 +262,7 @@ static PyObject* Journal_flush_matches(Journal *self, PyObject *args) PyDoc_STRVAR(Journal_seek__doc__, "seek(offset[, whence]) -> None\n\n" - "Seek through journal by `offset` number of entries. Argument\n" + "Jump `offset` entries in the journal. Argument\n" "`whence` defines what the offset is relative to:\n" "os.SEEK_SET (default) from first match in journal;\n" "os.SEEK_CUR from current position in journal;\n" @@ -393,11 +390,11 @@ static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) } PyDoc_STRVAR(Journal_wait__doc__, - "wait([timeout]) -> Change state (integer)\n\n" - "Waits until there is a change in the journal. Argument `timeout`\n" - "is the maximum number of seconds to wait before returning\n" - "regardless if journal has changed. If `timeout` is not given or is\n" - "0, then it will block forever.\n" + "wait([timeout]) -> state change (integer)\n\n" + "Wait for a change in the journal. Argument `timeout` specifies\n" + "the maximum number of seconds to wait before returning\n" + "regardless of wheter the journal has changed. If `timeout` is not given\n" + "or is 0, then block forever.\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."); @@ -420,7 +417,7 @@ static PyObject* Journal_wait(Journal *self, PyObject *args, PyObject *keywds) PyDoc_STRVAR(Journal_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" - "Seeks to journal entry by given unique reference `cursor`."); + "Seek to journal entry by given unique reference `cursor`."); static PyObject* Journal_seek_cursor(Journal *self, PyObject *args) { const char *cursor; @@ -463,8 +460,8 @@ static PyObject* Journal_iternext(PyObject *self) PyDoc_STRVAR(Journal_query_unique__doc__, "query_unique(field) -> a set of values\n\n" - "Returns a set of unique values in journal for given `field`.\n" - "Note this does not respect any journal matches."); + "Return a set of unique values appearing in journal for the\n" + "given `field`. Note this does not respect any journal matches."); static PyObject* Journal_query_unique(Journal *self, PyObject *args) { char *query; @@ -497,6 +494,11 @@ static PyObject* Journal_query_unique(Journal *self, PyObject *args) return value_set; } +PyDoc_STRVAR(data_threshold__doc__, + "Threshold for field size truncation.\n\n" + "Fields longer than this will be truncated to the threshold size.\n" + "Defaults to 64Kb."); + static PyObject* Journal_get_data_threshold(Journal *self, void *closure) { size_t cvalue; @@ -528,7 +530,7 @@ static PyGetSetDef Journal_getseters[] = { {(char*) "data_threshold", (getter)Journal_get_data_threshold, (setter)Journal_set_data_threshold, - (char*) "data threshold", + (char*) data_threshold__doc__, NULL}, {NULL} }; @@ -600,11 +602,14 @@ static PyTypeObject JournalType = { PyType_GenericNew, /* tp_new */ }; +#define SUMMARY \ + "Module that reads the systemd journal similar to journalctl." + #if PY_MAJOR_VERSION >= 3 static PyModuleDef _reader_module = { PyModuleDef_HEAD_INIT, "_reader", - "Module that reads systemd journal similar to journalctl.", + SUMMARY, -1, NULL, NULL, NULL, NULL, NULL }; @@ -636,8 +641,7 @@ init_reader(void) if (m == NULL) return NULL; #else - m = Py_InitModule3("_reader", NULL, - "Module that reads systemd journal similar to journalctl."); + m = Py_InitModule3("_reader", NULL, SUMMARY); if (m == NULL) return; #endif diff --git a/systemd/id128.c b/systemd/id128.c index f82b0af..04db786 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -35,24 +35,24 @@ static void cleanup_Py_DECREFp(PyObject **p) { PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-id128 library.\n\n" "Provides SD_MESSAGE_* constants and functions to query and generate\n" - "128bit unique identifiers." + "128-bit unique identifiers." ); PyDoc_STRVAR(randomize__doc__, "randomize() -> UUID\n\n" - "Return a new random 128bit unique identifier.\n" + "Return a new random 128-bit unique identifier.\n" "Wraps sd_id128_randomize(3)." ); PyDoc_STRVAR(get_machine__doc__, "get_machine() -> UUID\n\n" - "Return a 128bit unique identifier for this machine.\n" + "Return a 128-bit unique identifier for this machine.\n" "Wraps sd_id128_get_machine(3)." ); PyDoc_STRVAR(get_boot__doc__, "get_boot() -> UUID\n\n" - "Return a 128bit unique identifier for this boot.\n" + "Return a 128-bit unique identifier for this boot.\n" "Wraps sd_id128_get_boot(3)." ); diff --git a/systemd/journal.py b/systemd/journal.py index d63722b..892a56f 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -90,12 +90,11 @@ class Journal(_Journal): >>> myjournal.add_boot_match(journal.CURRENT_BOOT) >>> myjournal.add_loglevel_matches(journal.LOG_ERR) >>> myjournal.add_match(_SYSTEMD_UNIT="systemd-udevd.service") - >>> from __future__ import print_function >>> for entry in myjournal: ... print(entry['MESSAGE']) - See man page "systemd.journal-fields" for more info on - typical fields found in the journal. + See systemd.journal-fields(7) for more info on typical fields + found in the journal. """ def __init__(self, converters=None, flags=LOCAL_ONLY, path=None): """Creates instance of Journal, which allows filtering and @@ -123,7 +122,7 @@ class Journal(_Journal): self.converters.update(converters) def _convert_field(self, key, value): - """ Convert value based on callable from self.converters + """Convert value based on callable from self.converters based of field/key""" try: result = self.converters[key](value) @@ -137,7 +136,7 @@ class Journal(_Journal): return result def _convert_entry(self, entry): - """ Convert entire journal entry utilising _covert_field""" + """Convert entire journal entry utilising _covert_field""" result = {} for key, value in entry.items(): if isinstance(value, list): @@ -149,44 +148,56 @@ class Journal(_Journal): def add_match(self, *args, **kwargs): """Add one or more matches to the filter journal log entries. All matches of different field are combined in a logical AND, - and matches of the smae field are automatically combined in a + and matches of the same field are automatically combined in a logical OR. - Matches can be passed as strings of form "field=value", or - keyword arguments field="value".""" + Matches can be passed as strings of form "FIELD=value", or + keyword arguments FIELD="value". + """ args = list(args) args.extend(_make_line(key, val) for key, val in kwargs.items()) for arg in args: super(Journal, self).add_match(arg) def get_next(self, skip=1): - """Return dictionary of the next log entry. Optional skip value - will return the `skip`th log entry. - Returned will be journal entry dictionary processed with - converters.""" + """Return the next log entry as a dictionary of fields. + + Optional skip value will return the `skip`\-th log entry. + + Entries will be processed with converters specified during + Journal creation. + """ return self._convert_entry( super(Journal, self).get_next(skip)) def query_unique(self, field): - """Returns a set of unique values in journal for given `field`, - processed with converters. - Note this does not respect any journal matches.""" + """Return unique values appearing in the Journal for given `field`. + + Note this does not respect any journal matches. + + Entries will be processed with converters specified during + Journal creation. + """ return set(self._convert_field(field, value) for value in super(Journal, self).query_unique(field)) def seek_realtime(self, realtime): - """Seek to nearest matching journal entry to `realtime`. - Argument `realtime` can must be either an integer unix timestamp - or datetime.datetime instance.""" + """Seek to a matching journal entry nearest to `realtime` time. + + Argument `realtime` must be either an integer unix timestamp + or datetime.datetime instance. + """ if isinstance(realtime, _datetime.datetime): realtime = float(realtime.strftime("%s.%f")) return super(Journal, self).seek_realtime(realtime) def seek_monotonic(self, monotonic, bootid=None): - """Seek to nearest matching journal entry to `monotonic`. - Argument `monotonic` is a timestamp from boot in either seconds - or a datetime.timedelta instance. - Argument `bootid` is a string or UUID representing which boot the - monotonic time is reference to. Defaults to current bootid.""" + """Seek to a matching journal entry nearest to `monotonic` time. + + Argument `monotonic` is a timestamp from boot in either + seconds or a datetime.timedelta instance. Argument `bootid` + is a string or UUID representing which boot the monotonic time + is reference to. Defaults to current bootid. + """ if isinstance(monotonic, _datetime.timedelta): monotonic = monotonic.totalseconds() if isinstance(bootid, _uuid.UUID): @@ -194,7 +205,8 @@ class Journal(_Journal): return super(Journal, self).seek_monotonic(monotonic, bootid) def log_level(self, level): - """Sets maximum log `level` by setting matches for PRIORITY.""" + """Set maximum log `level` by setting matches for PRIORITY. + """ if 0 <= level <= 7: for i in range(level+1): self.add_match(PRIORITY="%s" % i) @@ -202,10 +214,13 @@ class Journal(_Journal): raise ValueError("Log level must be 0 <= level <= 7") def messageid_match(self, messageid): - """Sets match filter for log entries for specified `messageid`. - `messageid` can be string or UUID instance. - Standard message IDs can be found in systemd.id128 - Equivalent to add_match(MESSAGE_ID=`messageid`).""" + """Add match for log entries with specified `messageid`. + + `messageid` can be string of hexadicimal digits or a UUID + instance. Standard message IDs can be found in systemd.id128. + + Equivalent to add_match(MESSAGE_ID=`messageid`). + """ if isinstance(messageid, _uuid.UUID): messageid = messageid.get_hex() self.add_match(MESSAGE_ID=messageid) -- cgit v1.2.1 From ec05eb1aef266447dd75b70e92121c6b0cb4bb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 21 Feb 2013 14:23:57 +0100 Subject: systemd-python: use PyModule_AddObject in id128 --- systemd/id128.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/id128.c b/systemd/id128.c index 04db786..42f247d 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -106,13 +106,13 @@ static PyMethodDef methods[] = { }; static int add_id(PyObject *module, const char* name, sd_id128_t id) { - PyObject _cleanup_Py_DECREF_ *obj; + PyObject *obj; obj = make_uuid(id); if (!obj) return -1; - return PyObject_SetAttrString(module, name, obj); + return PyModule_AddObject(module, name, obj); } #pragma GCC diagnostic push -- cgit v1.2.1 From 8d697bfd136f6ba8e37aedf9ba06bf8f86845d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 21 Feb 2013 15:07:39 +0100 Subject: systemd-python: document attributes In id128 it would be better to add everything automatically, but sphinx cannot do this right now. --- systemd/docs/id128.rst | 31 +++++++++++++++++++++++++++++++ systemd/docs/journal.rst | 23 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/systemd/docs/id128.rst b/systemd/docs/id128.rst index e817d80..12c28f3 100644 --- a/systemd/docs/id128.rst +++ b/systemd/docs/id128.rst @@ -5,3 +5,34 @@ :members: :undoc-members: :inherited-members: + + .. autoattribute:: systemd.id128.SD_MESSAGE_COREDUMP + .. autoattribute:: systemd.id128.SD_MESSAGE_FORWARD_SYSLOG_MISSED + .. autoattribute:: systemd.id128.SD_MESSAGE_HIBERNATE_KEY + .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_DROPPED + .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_MISSED + .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_START + .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_STOP + .. autoattribute:: systemd.id128.SD_MESSAGE_LID_CLOSED + .. autoattribute:: systemd.id128.SD_MESSAGE_LID_OPENED + .. autoattribute:: systemd.id128.SD_MESSAGE_OVERMOUNTING + .. autoattribute:: systemd.id128.SD_MESSAGE_POWER_KEY + .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_START + .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_STOP + .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_START + .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_STOP + .. autoattribute:: systemd.id128.SD_MESSAGE_SHUTDOWN + .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_START + .. 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 + .. autoattribute:: systemd.id128.SD_MESSAGE_TIMEZONE_CHANGE + .. autoattribute:: systemd.id128.SD_MESSAGE_TIME_CHANGE + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_FAILED + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADED + .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADING + .. 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 diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 89728a2..9d627ce 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -22,3 +22,26 @@ Accessing the Journal :inherited-members: .. automethod:: __init__ + +.. autoattribute:: systemd.journal.DEFAULT_CONVERTERS + +Whence constants +~~~~~~~~~~~~~~~~ + +.. autoattribute:: systemd.journal.SEEK_SET +.. autoattribute:: systemd.journal.SEEK_CUR +.. autoattribute:: systemd.journal.SEEK_END + +Journal access types +~~~~~~~~~~~~~~~~~~~~ + +.. autoattribute:: systemd.journal.LOCAL_ONLY +.. autoattribute:: systemd.journal.RUNTIME_ONLY +.. autoattribute:: systemd.journal.SYSTEM_ONLY + +Journal event types +~~~~~~~~~~~~~~~~~~~ + +.. autoattribute:: systemd.journal.NOP +.. autoattribute:: systemd.journal.APPEND +.. autoattribute:: systemd.journal.INVALIDATE -- cgit v1.2.1 From cedd0fd2498929600f408c28969b2f744d65cc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 21 Feb 2013 15:08:01 +0100 Subject: systemd-python: hide ChainMap import --- systemd/journal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index 892a56f..4d71564 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -28,7 +28,7 @@ import os as _os from os import SEEK_SET, SEEK_CUR, SEEK_END import logging as _logging if _sys.version_info >= (3,): - from collections import ChainMap + from collections import ChainMap as _ChainMap from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd @@ -112,7 +112,7 @@ class Journal(_Journal): """ super(Journal, self).__init__(flags, path) if _sys.version_info >= (3,3): - self.converters = ChainMap() + self.converters = _ChainMap() if converters is not None: self.converters.maps.append(converters) self.converters.maps.append(DEFAULT_CONVERTERS) -- cgit v1.2.1 From 7db007ce6ce565ae3091e16b61174ec393588705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 22 Feb 2013 13:33:06 +0100 Subject: python-systemd: check all errors and use automatic cleanup __REALTIME_TIMESTAMP and __MONOTONIC_TIMESTAMP return ints. It doesn't make sense to convert to string, just to convert back to a number later on. Also try to follow systemd rules for indentation. --- systemd/_reader.c | 293 ++++++++++++++++++++++++++++++++--------------------- systemd/id128.c | 21 ++-- systemd/journal.py | 6 +- systemd/pyutil.c | 30 ++++++ systemd/pyutil.h | 29 ++++++ 5 files changed, 253 insertions(+), 126 deletions(-) create mode 100644 systemd/pyutil.c create mode 100644 systemd/pyutil.h diff --git a/systemd/_reader.c b/systemd/_reader.c index 207b9e7..9262c89 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -18,11 +18,17 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ -#include #include #include #include +#include + +#include + +#include "pyutil.h" +#include "macro.h" +#include "util.h" #if PY_MAJOR_VERSION >=3 # define unicode_FromStringAndSize PyUnicode_FromStringAndSize @@ -107,10 +113,9 @@ static PyObject* Journal_get_next(Journal *self, PyObject *args) PyObject *dict; const void *msg; size_t msg_len; - const char *delim_ptr; - PyObject *key, *value, *cur_value, *tmp_list; + int64_t skip = 1LL; + int r; - int64_t skip = 1LL, r = -EINVAL; if (!PyArg_ParseTuple(args, "|L", &skip)) return NULL; @@ -128,6 +133,8 @@ static PyObject* Journal_get_next(Journal *self, PyObject *args) r = sd_journal_next_skip(self->j, skip); else if (skip < -1LL) r = sd_journal_previous_skip(self->j, -skip); + else + assert_not_reached("should not be here"); Py_END_ALLOW_THREADS set_error(r, NULL, NULL); @@ -137,70 +144,128 @@ static PyObject* Journal_get_next(Journal *self, PyObject *args) return PyDict_New(); dict = PyDict_New(); + if (!dict) + return NULL; SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + const char *delim_ptr; + delim_ptr = memchr(msg, '=', msg_len); + if (!delim_ptr) { + PyErr_SetString(PyExc_OSError, + "journal gave us a field without '='"); + goto error; + } + key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); - value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) ); + if (!key) + goto error; + + value = PyBytes_FromStringAndSize( + delim_ptr + 1, + (const char*) msg + msg_len - (delim_ptr + 1) ); + if (!value) + goto error; + if (PyDict_Contains(dict, key)) { - cur_value = PyDict_GetItem(dict, key); + PyObject *cur_value = PyDict_GetItem(dict, key); + if (PyList_CheckExact(cur_value)) { - PyList_Append(cur_value, value); - }else{ - tmp_list = PyList_New(0); - PyList_Append(tmp_list, cur_value); - PyList_Append(tmp_list, value); + r = PyList_Append(cur_value, value); + if (r < 0) + goto error; + } else { + PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0); + if (!tmp_list) + goto error; + + r = PyList_Append(tmp_list, cur_value); + if (r < 0) + goto error; + + r = PyList_Append(tmp_list, value); + if (r < 0) + goto error; + PyDict_SetItem(dict, key, tmp_list); - Py_DECREF(tmp_list); + if (r < 0) + goto error; } - }else{ - PyDict_SetItem(dict, key, value); + } else { + r = PyDict_SetItem(dict, key, value); + if (r < 0) + goto error; } - Py_DECREF(key); - Py_DECREF(value); } { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; uint64_t realtime; - if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) { - char realtime_str[20]; - sprintf(realtime_str, "%llu", (long long unsigned) realtime); - key = unicode_FromString("__REALTIME_TIMESTAMP"); - value = PyBytes_FromString(realtime_str); - PyDict_SetItem(dict, key, value); - Py_DECREF(key); - Py_DECREF(value); - } + + r = sd_journal_get_realtime_usec(self->j, &realtime); + if (set_error(r, NULL, NULL)) + goto error; + + key = unicode_FromString("__REALTIME_TIMESTAMP"); + if (!key) + goto error; + + assert_cc(sizeof(unsigned long long) == sizeof(realtime)); + value = PyLong_FromUnsignedLongLong(realtime); + if (!value) + goto error; + + if (PyDict_SetItem(dict, key, value)) + goto error; } { + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; sd_id128_t sd_id; uint64_t monotonic; - if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) { - char monotonic_str[20]; - sprintf(monotonic_str, "%llu", (long long unsigned) monotonic); - key = unicode_FromString("__MONOTONIC_TIMESTAMP"); - value = PyBytes_FromString(monotonic_str); - - PyDict_SetItem(dict, key, value); - Py_DECREF(key); - Py_DECREF(value); - } + + r = sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id); + if (set_error(r, NULL, NULL)) + goto error; + + key = unicode_FromString("__MONOTONIC_TIMESTAMP"); + if (!key) + goto error; + + assert_cc(sizeof(unsigned long long) == sizeof(monotonic)); + value = PyLong_FromUnsignedLongLong(monotonic); + if (!value) + goto error; + + if (PyDict_SetItem(dict, key, value)) + goto error; } { - char *cursor; - if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0... - key = unicode_FromString("__CURSOR"); - value = PyBytes_FromString(cursor); - PyDict_SetItem(dict, key, value); - free(cursor); - Py_DECREF(key); - Py_DECREF(value); - } + PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + char _cleanup_free_ *cursor = NULL; + + r = sd_journal_get_cursor(self->j, &cursor); + if (set_error(r, NULL, NULL)) + goto error; + + key = unicode_FromString("__CURSOR"); + if (!key) + goto error; + + value = PyBytes_FromString(cursor); + if (!value) + goto error; + + if (PyDict_SetItem(dict, key, value)) + goto error; } return dict; +error: + Py_DECREF(dict); + return NULL; } PyDoc_STRVAR(Journal_get_previous__doc__, @@ -451,7 +516,7 @@ static PyObject* Journal_iternext(PyObject *self) dict_size = PyDict_Size(dict); if ((int64_t) dict_size > 0LL) { return dict; - }else{ + } else { Py_DECREF(dict); PyErr_SetNone(PyExc_StopIteration); return NULL; @@ -486,7 +551,9 @@ static PyObject* Journal_query_unique(Journal *self, PyObject *args) const char *delim_ptr; delim_ptr = memchr(uniq, '=', uniq_len); - value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1)); + value = PyBytes_FromStringAndSize( + delim_ptr + 1, + (const char*) uniq + uniq_len - (delim_ptr + 1)); PySet_Add(value_set, value); Py_DECREF(value); } @@ -495,7 +562,7 @@ static PyObject* Journal_query_unique(Journal *self, PyObject *args) } PyDoc_STRVAR(data_threshold__doc__, - "Threshold for field size truncation.\n\n" + "Threshold for field size truncation in bytes.\n\n" "Fields longer than this will be truncated to the threshold size.\n" "Defaults to 64Kb."); @@ -515,7 +582,7 @@ static int Journal_set_data_threshold(Journal *self, PyObject *value, void *clos { int r; if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold"); + PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); return -1; } if (!long_Check(value)){ @@ -528,78 +595,67 @@ static int Journal_set_data_threshold(Journal *self, PyObject *value, void *clos static PyGetSetDef Journal_getseters[] = { {(char*) "data_threshold", - (getter)Journal_get_data_threshold, - (setter)Journal_set_data_threshold, + (getter) Journal_get_data_threshold, + (setter) Journal_set_data_threshold, (char*) data_threshold__doc__, NULL}, {NULL} }; static PyMethodDef Journal_methods[] = { - {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS, - Journal_get_next__doc__}, - {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS, - Journal_get_previous__doc__}, - {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS, - Journal_add_match__doc__}, - {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS, - Journal_add_disjunction__doc__}, - {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS, - Journal_flush_matches__doc__}, - {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS, - Journal_seek__doc__}, - {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS, - Journal_seek_realtime__doc__}, - {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS, - Journal_seek_monotonic__doc__}, - {"wait", (PyCFunction)Journal_wait, METH_VARARGS, - Journal_wait__doc__}, - {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS, - Journal_seek_cursor__doc__}, - {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS, - Journal_query_unique__doc__}, + {"get_next", (PyCFunction) Journal_get_next, METH_VARARGS, Journal_get_next__doc__}, + {"get_previous", (PyCFunction) Journal_get_previous, METH_VARARGS, Journal_get_previous__doc__}, + {"add_match", (PyCFunction) Journal_add_match, METH_VARARGS|METH_KEYWORDS, Journal_add_match__doc__}, + {"add_disjunction", (PyCFunction) Journal_add_disjunction, METH_NOARGS, Journal_add_disjunction__doc__}, + {"flush_matches", (PyCFunction) Journal_flush_matches, METH_NOARGS, Journal_flush_matches__doc__}, + {"seek", (PyCFunction) Journal_seek, METH_VARARGS | METH_KEYWORDS, Journal_seek__doc__}, + {"seek_realtime", (PyCFunction) Journal_seek_realtime, METH_VARARGS, Journal_seek_realtime__doc__}, + {"seek_monotonic", (PyCFunction) Journal_seek_monotonic, METH_VARARGS, Journal_seek_monotonic__doc__}, + {"wait", (PyCFunction) Journal_wait, METH_VARARGS, Journal_wait__doc__}, + {"seek_cursor", (PyCFunction) Journal_seek_cursor, METH_VARARGS, Journal_seek_cursor__doc__}, + {"query_unique", (PyCFunction) Journal_query_unique, METH_VARARGS, Journal_query_unique__doc__}, {NULL} /* Sentinel */ }; static PyTypeObject JournalType = { PyVarObject_HEAD_INIT(NULL, 0) - "_reader._Journal", /*tp_name*/ - sizeof(Journal), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Journal_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/ - Journal__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - Journal_iter, /* tp_iter */ - Journal_iternext, /* tp_iternext */ - Journal_methods, /* tp_methods */ - 0, /* tp_members */ - Journal_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Journal_init, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + "_reader._Journal", /*tp_name*/ + sizeof(Journal), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Journal_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Journal__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + Journal_iter, /* tp_iter */ + Journal_iternext, /* tp_iternext */ + Journal_methods, /* tp_methods */ + 0, /* tp_members */ + Journal_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc) Journal_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ }; #define SUMMARY \ @@ -647,13 +703,18 @@ init_reader(void) #endif Py_INCREF(&JournalType); - PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType); - PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP); - PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND); - PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE); - PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY); - PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY); - PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY); + if (PyModule_AddObject(m, "_Journal", (PyObject *) &JournalType) || + PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) || + PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) || + PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || + PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) || + PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) || + PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) { +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(m); + return NULL; +#endif + } #if PY_MAJOR_VERSION >= 3 return m; diff --git a/systemd/id128.c b/systemd/id128.c index 42f247d..a6711a5 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -19,18 +19,13 @@ along with systemd; If not, see . ***/ +#include + #include #include -#define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) - -static void cleanup_Py_DECREFp(PyObject **p) { - if (!*p) - return; - - Py_DECREF(*p); -} +#include "pyutil.h" PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-id128 library.\n\n" @@ -127,7 +122,10 @@ PyMODINIT_FUNC initid128(void) { if (m == NULL) return; + /* a series of lines like 'add_id() ;' follow */ +#define JOINER ; #include "id128-constants.h" +#undef JOINER } #else @@ -147,7 +145,14 @@ PyMODINIT_FUNC PyInit_id128(void) { if (m == NULL) return NULL; + if ( /* a series of lines like 'add_id() ||' follow */ +#define JOINER || #include "id128-constants.h" +#undef JOINER + false) { + Py_DECREF(m); + return NULL; + } return m; } diff --git a/systemd/journal.py b/systemd/journal.py index 4d71564..d94934c 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . +from __future__ import division + import sys as _sys import datetime as _datetime import functools as _functools @@ -36,8 +38,8 @@ from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) from . import id128 as _id128 -_MONOTONIC_CONVERTER = lambda x: _datetime.timedelta(microseconds=float(x)) -_REALTIME_CONVERTER = lambda x: _datetime.datetime.fromtimestamp(float(x)/1E6) +_MONOTONIC_CONVERTER = lambda x: _datetime.timedelta(microseconds=x) +_REALTIME_CONVERTER = lambda x: _datetime.datetime.fromtimestamp(x / 1E6) DEFAULT_CONVERTERS = { 'MESSAGE_ID': _uuid.UUID, '_MACHINE_ID': _uuid.UUID, diff --git a/systemd/pyutil.c b/systemd/pyutil.c new file mode 100644 index 0000000..79065a1 --- /dev/null +++ b/systemd/pyutil.c @@ -0,0 +1,30 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include "pyutil.h" + +void cleanup_Py_DECREFp(PyObject **p) { + if (!*p) + return; + + Py_DECREF(*p); +} diff --git a/systemd/pyutil.h b/systemd/pyutil.h new file mode 100644 index 0000000..3b7bc58 --- /dev/null +++ b/systemd/pyutil.h @@ -0,0 +1,29 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifndef Py_TYPE +/* avoid duplication warnings from errors in Python 2.7 headers */ +# include +#endif + +void cleanup_Py_DECREFp(PyObject **p); + +#define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) -- cgit v1.2.1 From 4a3c9f4286768502d060d27a64e0478ca2ad5e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 23 Feb 2013 01:11:36 +0100 Subject: systemd-python: return both parts of sd_journal_get_monotonic_usec In Python 3, a named tuple is used. In Python 2, a simple tuple is used. In either case, the pair is (timestamp, bootid). --- systemd/_reader.c | 74 ++++++++++++++++++++++++++++++++++++++++-------- systemd/docs/journal.rst | 2 ++ systemd/journal.py | 8 +++++- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 9262c89..7645cb9 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -67,6 +67,26 @@ static int set_error(int r, const char* path, const char* invalid_message) { return 1; } +#if PY_MAJOR_VERSION >= 3 +static PyTypeObject MonotonicType; + +PyDoc_STRVAR(MonotonicType__doc__, + "A tuple of (timestamp, bootid) for holding monotonic timestamps"); + +static PyStructSequence_Field MonotonicType_fields[] = { + {(char*) "timestamp", (char*) "Time"}, + {(char*) "bootid", (char*) "Unique identifier of the boot"}, + {NULL, NULL} +}; + +static PyStructSequence_Desc Monotonic_desc = { + (char*) "journal.Monotonic", + MonotonicType__doc__, + MonotonicType_fields, + 2, +}; +#endif + static void Journal_dealloc(Journal* self) { sd_journal_close(self->j); @@ -221,22 +241,37 @@ static PyObject* Journal_get_next(Journal *self, PyObject *args) } { - PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; - sd_id128_t sd_id; + PyObject _cleanup_Py_DECREF_ + *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL; + sd_id128_t id; uint64_t monotonic; - r = sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id); + r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id); if (set_error(r, NULL, NULL)) goto error; + assert_cc(sizeof(unsigned long long) == sizeof(monotonic)); key = unicode_FromString("__MONOTONIC_TIMESTAMP"); - if (!key) + timestamp = PyLong_FromUnsignedLongLong(monotonic); + bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); +#if PY_MAJOR_VERSION >= 3 + value = PyStructSequence_New(&MonotonicType); +#else + value = PyTuple_New(2); +#endif + if (!key || !timestamp || !bytes || !value) goto error; - assert_cc(sizeof(unsigned long long) == sizeof(monotonic)); - value = PyLong_FromUnsignedLongLong(monotonic); - if (!value) - goto error; + Py_INCREF(timestamp); + Py_INCREF(bytes); + +#if PY_MAJOR_VERSION >= 3 + PyStructSequence_SET_ITEM(value, 0, timestamp); + PyStructSequence_SET_ITEM(value, 1, bytes); +#else + PyTuple_SET_ITEM(value, 0, timestamp); + PyTuple_SET_ITEM(value, 1, bytes); +#endif if (PyDict_SetItem(dict, key, value)) goto error; @@ -421,7 +456,7 @@ static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) double timedouble; char *bootid = NULL; uint64_t timestamp; - sd_id128_t sd_id; + sd_id128_t id; int r; if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) @@ -435,19 +470,19 @@ static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) } if (bootid) { - r = sd_id128_from_string(bootid, &sd_id); + r = sd_id128_from_string(bootid, &id); if (set_error(r, NULL, "Invalid bootid")) return NULL; } else { Py_BEGIN_ALLOW_THREADS - r = sd_id128_get_boot(&sd_id); + r = sd_id128_get_boot(&id); Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL)) return NULL; } Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp); + r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL)) return NULL; @@ -671,6 +706,10 @@ static PyModuleDef _reader_module = { }; #endif +#if PY_MAJOR_VERSION >= 3 +static bool initialized = false; +#endif + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -696,6 +735,11 @@ init_reader(void) m = PyModule_Create(&_reader_module); if (m == NULL) return NULL; + + if (!initialized) { + PyStructSequence_InitType(&MonotonicType, &Monotonic_desc); + initialized = true; + } #else m = Py_InitModule3("_reader", NULL, SUMMARY); if (m == NULL) @@ -703,7 +747,13 @@ init_reader(void) #endif Py_INCREF(&JournalType); +#if PY_MAJOR_VERSION >= 3 + Py_INCREF(&MonotonicType); +#endif if (PyModule_AddObject(m, "_Journal", (PyObject *) &JournalType) || +#if PY_MAJOR_VERSION >= 3 + PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) || +#endif PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) || PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) || PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 9d627ce..38ab57e 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -23,6 +23,8 @@ Accessing the Journal .. automethod:: __init__ +.. autoclass:: Monotonic + .. autoattribute:: systemd.journal.DEFAULT_CONVERTERS Whence constants diff --git a/systemd/journal.py b/systemd/journal.py index d94934c..a5641e9 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -38,7 +38,13 @@ from ._reader import (_Journal, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) from . import id128 as _id128 -_MONOTONIC_CONVERTER = lambda x: _datetime.timedelta(microseconds=x) +if _sys.version_info >= (3,): + from ._reader import Monotonic +else: + Monotonic = tuple + +_MONOTONIC_CONVERTER = lambda p: Monotonic((_datetime.timedelta(microseconds=p[0]), + _uuid.UUID(bytes=p[1]))) _REALTIME_CONVERTER = lambda x: _datetime.datetime.fromtimestamp(x / 1E6) DEFAULT_CONVERTERS = { 'MESSAGE_ID': _uuid.UUID, -- cgit v1.2.1 From 3223d0b21fdb35829a6ba206eddb6339bf9ba73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 28 Feb 2013 19:32:31 -0500 Subject: python-systemd: rename Journal to Reader It seems inevitable that we'll also grow a writing interface, and then it'll be cumbersome to have a "Journal" for reading, and a "Writer" for writing. --- systemd/_reader.c | 122 +++++++++++++++++++++++------------------------ systemd/docs/journal.rst | 4 +- systemd/journal.py | 28 +++++------ 3 files changed, 77 insertions(+), 77 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 7645cb9..7f200d5 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -50,8 +50,8 @@ typedef struct { PyObject_HEAD sd_journal *j; -} Journal; -static PyTypeObject JournalType; +} Reader; +static PyTypeObject ReaderType; static int set_error(int r, const char* path, const char* invalid_message) { if (r >= 0) @@ -87,15 +87,15 @@ static PyStructSequence_Desc Monotonic_desc = { }; #endif -static void Journal_dealloc(Journal* self) +static void Reader_dealloc(Reader* self) { sd_journal_close(self->j); Py_TYPE(self)->tp_free((PyObject*)self); } -PyDoc_STRVAR(Journal__doc__, - "Journal([flags][,path]) -> ...\n\n" - "Journal allows filtering and retrieval of Journal entries.\n" +PyDoc_STRVAR(Reader__doc__, + "Reader([flags][,path]) -> ...\n\n" + "Reader allows filtering and retrieval of Journal entries.\n" "Argument `flags` sets open flags of the journal, which can be one\n" "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" "journal on local machine only; RUNTIME_ONLY opens only\n" @@ -104,7 +104,7 @@ PyDoc_STRVAR(Journal__doc__, "Argument `path` is the directory of journal files. Note that\n" "currently flags are ignored when `path` is present as they are\n" "not relevant."); -static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) +static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { int flags = SD_JOURNAL_LOCAL_ONLY, r; char *path = NULL; @@ -124,11 +124,11 @@ static int Journal_init(Journal *self, PyObject *args, PyObject *keywds) return set_error(r, path, "Invalid flags or path"); } -PyDoc_STRVAR(Journal_get_next__doc__, +PyDoc_STRVAR(Reader_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" "return the `skip`\\-th log entry."); -static PyObject* Journal_get_next(Journal *self, PyObject *args) +static PyObject* Reader_get_next(Reader *self, PyObject *args) { PyObject *dict; const void *msg; @@ -303,11 +303,11 @@ error: return NULL; } -PyDoc_STRVAR(Journal_get_previous__doc__, +PyDoc_STRVAR(Reader_get_previous__doc__, "get_previous([skip]) -> dict\n\n" "Return dictionary of the previous log entry. Optional skip value\n" "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip)."); -static PyObject* Journal_get_previous(Journal *self, PyObject *args) +static PyObject* Reader_get_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; if (!PyArg_ParseTuple(args, "|L", &skip)) @@ -317,13 +317,13 @@ static PyObject* Journal_get_previous(Journal *self, PyObject *args) (char*) "L", -skip); } -PyDoc_STRVAR(Journal_add_match__doc__, +PyDoc_STRVAR(Reader_add_match__doc__, "add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" "fields are combined with logical AND, and matches of the same field\n" "are automatically combined with logical OR.\n" "Match is a string of the form \"FIELD=value\"."); -static PyObject* Journal_add_match(Journal *self, PyObject *args, PyObject *keywds) +static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) { char *match; int match_len, r; @@ -338,10 +338,10 @@ static PyObject* Journal_add_match(Journal *self, PyObject *args, PyObject *keyw Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_add_disjunction__doc__, +PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() -> None\n\n" "Inserts a logical OR between matches added before and afterwards."); -static PyObject* Journal_add_disjunction(Journal *self, PyObject *args) +static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); @@ -351,23 +351,23 @@ static PyObject* Journal_add_disjunction(Journal *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_flush_matches__doc__, +PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); -static PyObject* Journal_flush_matches(Journal *self, PyObject *args) +static PyObject* Reader_flush_matches(Reader *self, PyObject *args) { sd_journal_flush_matches(self->j); Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_seek__doc__, +PyDoc_STRVAR(Reader_seek__doc__, "seek(offset[, whence]) -> None\n\n" "Jump `offset` entries in the journal. Argument\n" "`whence` defines what the offset is relative to:\n" "os.SEEK_SET (default) from first match in journal;\n" "os.SEEK_CUR from current position in journal;\n" "and os.SEEK_END is from last match in journal."); -static PyObject* Journal_seek(Journal *self, PyObject *args, PyObject *keywds) +static PyObject* Reader_seek(Reader *self, PyObject *args, PyObject *keywds) { int64_t offset; int whence = SEEK_SET; @@ -418,11 +418,11 @@ static PyObject* Journal_seek(Journal *self, PyObject *args, PyObject *keywds) Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_seek_realtime__doc__, +PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" "`realtime` can must be an integer unix timestamp."); -static PyObject* Journal_seek_realtime(Journal *self, PyObject *args) +static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { double timedouble; uint64_t timestamp; @@ -445,13 +445,13 @@ static PyObject* Journal_seek_realtime(Journal *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_seek_monotonic__doc__, +PyDoc_STRVAR(Reader_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" "Seek to nearest matching journal entry to `monotonic`. Argument\n" "`monotonic` is an timestamp from boot in seconds.\n" "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); -static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) +static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { double timedouble; char *bootid = NULL; @@ -489,7 +489,7 @@ static PyObject* Journal_seek_monotonic(Journal *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(Journal_wait__doc__, +PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" "the maximum number of seconds to wait before returning\n" @@ -498,7 +498,7 @@ PyDoc_STRVAR(Journal_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."); -static PyObject* Journal_wait(Journal *self, PyObject *args, PyObject *keywds) +static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) { int r; int64_t timeout = 0LL; @@ -515,10 +515,10 @@ static PyObject* Journal_wait(Journal *self, PyObject *args, PyObject *keywds) return long_FromLong(r); } -PyDoc_STRVAR(Journal_seek_cursor__doc__, +PyDoc_STRVAR(Reader_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" "Seek to journal entry by given unique reference `cursor`."); -static PyObject* Journal_seek_cursor(Journal *self, PyObject *args) +static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { const char *cursor; int r; @@ -534,13 +534,13 @@ static PyObject* Journal_seek_cursor(Journal *self, PyObject *args) Py_RETURN_NONE; } -static PyObject* Journal_iter(PyObject *self) +static PyObject* Reader_iter(PyObject *self) { Py_INCREF(self); return self; } -static PyObject* Journal_iternext(PyObject *self) +static PyObject* Reader_iternext(PyObject *self) { PyObject *dict; Py_ssize_t dict_size; @@ -558,11 +558,11 @@ static PyObject* Journal_iternext(PyObject *self) } } -PyDoc_STRVAR(Journal_query_unique__doc__, +PyDoc_STRVAR(Reader_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Return a set of unique values appearing in journal for the\n" "given `field`. Note this does not respect any journal matches."); -static PyObject* Journal_query_unique(Journal *self, PyObject *args) +static PyObject* Reader_query_unique(Reader *self, PyObject *args) { char *query; int r; @@ -601,7 +601,7 @@ PyDoc_STRVAR(data_threshold__doc__, "Fields longer than this will be truncated to the threshold size.\n" "Defaults to 64Kb."); -static PyObject* Journal_get_data_threshold(Journal *self, void *closure) +static PyObject* Reader_get_data_threshold(Reader *self, void *closure) { size_t cvalue; int r; @@ -613,7 +613,7 @@ static PyObject* Journal_get_data_threshold(Journal *self, void *closure) return long_FromSize_t(cvalue); } -static int Journal_set_data_threshold(Journal *self, PyObject *value, void *closure) +static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) { int r; if (value == NULL) { @@ -628,36 +628,36 @@ static int Journal_set_data_threshold(Journal *self, PyObject *value, void *clos return set_error(r, NULL, NULL); } -static PyGetSetDef Journal_getseters[] = { +static PyGetSetDef Reader_getseters[] = { {(char*) "data_threshold", - (getter) Journal_get_data_threshold, - (setter) Journal_set_data_threshold, + (getter) Reader_get_data_threshold, + (setter) Reader_set_data_threshold, (char*) data_threshold__doc__, NULL}, {NULL} }; -static PyMethodDef Journal_methods[] = { - {"get_next", (PyCFunction) Journal_get_next, METH_VARARGS, Journal_get_next__doc__}, - {"get_previous", (PyCFunction) Journal_get_previous, METH_VARARGS, Journal_get_previous__doc__}, - {"add_match", (PyCFunction) Journal_add_match, METH_VARARGS|METH_KEYWORDS, Journal_add_match__doc__}, - {"add_disjunction", (PyCFunction) Journal_add_disjunction, METH_NOARGS, Journal_add_disjunction__doc__}, - {"flush_matches", (PyCFunction) Journal_flush_matches, METH_NOARGS, Journal_flush_matches__doc__}, - {"seek", (PyCFunction) Journal_seek, METH_VARARGS | METH_KEYWORDS, Journal_seek__doc__}, - {"seek_realtime", (PyCFunction) Journal_seek_realtime, METH_VARARGS, Journal_seek_realtime__doc__}, - {"seek_monotonic", (PyCFunction) Journal_seek_monotonic, METH_VARARGS, Journal_seek_monotonic__doc__}, - {"wait", (PyCFunction) Journal_wait, METH_VARARGS, Journal_wait__doc__}, - {"seek_cursor", (PyCFunction) Journal_seek_cursor, METH_VARARGS, Journal_seek_cursor__doc__}, - {"query_unique", (PyCFunction) Journal_query_unique, METH_VARARGS, Journal_query_unique__doc__}, +static PyMethodDef Reader_methods[] = { + {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, + {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, + {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, + {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, + {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, + {"seek", (PyCFunction) Reader_seek, METH_VARARGS | METH_KEYWORDS, Reader_seek__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__}, + {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, + {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, + {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, {NULL} /* Sentinel */ }; -static PyTypeObject JournalType = { +static PyTypeObject ReaderType = { PyVarObject_HEAD_INIT(NULL, 0) - "_reader._Journal", /*tp_name*/ - sizeof(Journal), /*tp_basicsize*/ + "_reader._Reader", /*tp_name*/ + sizeof(Reader), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)Journal_dealloc, /*tp_dealloc*/ + (destructor)Reader_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -673,22 +673,22 @@ static PyTypeObject JournalType = { 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - Journal__doc__, /* tp_doc */ + Reader__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - Journal_iter, /* tp_iter */ - Journal_iternext, /* tp_iternext */ - Journal_methods, /* tp_methods */ + Reader_iter, /* tp_iter */ + Reader_iternext, /* tp_iternext */ + Reader_methods, /* tp_methods */ 0, /* tp_members */ - Journal_getseters, /* tp_getset */ + Reader_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc) Journal_init, /* tp_init */ + (initproc) Reader_init, /* tp_init */ 0, /* tp_alloc */ PyType_GenericNew, /* tp_new */ }; @@ -724,7 +724,7 @@ init_reader(void) PyDateTime_IMPORT; - if (PyType_Ready(&JournalType) < 0) + if (PyType_Ready(&ReaderType) < 0) #if PY_MAJOR_VERSION >= 3 return NULL; #else @@ -746,11 +746,11 @@ init_reader(void) return; #endif - Py_INCREF(&JournalType); + Py_INCREF(&ReaderType); #if PY_MAJOR_VERSION >= 3 Py_INCREF(&MonotonicType); #endif - if (PyModule_AddObject(m, "_Journal", (PyObject *) &JournalType) || + if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) || #if PY_MAJOR_VERSION >= 3 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) || #endif diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 38ab57e..78b831a 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -13,11 +13,11 @@ Accessing the Journal --------------------- -.. autoclass:: _Journal +.. autoclass:: _Reader :undoc-members: :inherited-members: -.. autoclass:: Journal +.. autoclass:: Reader :undoc-members: :inherited-members: diff --git a/systemd/journal.py b/systemd/journal.py index a5641e9..23e1d65 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -34,7 +34,7 @@ if _sys.version_info >= (3,): from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd -from ._reader import (_Journal, NOP, APPEND, INVALIDATE, +from ._reader import (_Reader, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) from . import id128 as _id128 @@ -86,15 +86,15 @@ if _sys.version_info >= (3,): else: _convert_unicode = _functools.partial(unicode, encoding='utf-8') -class Journal(_Journal): - """Journal allows the access and filtering of systemd journal +class Reader(_Reader): + """Reader allows the access and filtering of systemd journal entries. Note that in order to access the system journal, a non-root user must be in the `adm` group. Example usage to print out all error or higher level messages for systemd-udevd for the boot: - >>> myjournal = journal.Journal() + >>> myjournal = journal.Reader() >>> myjournal.add_boot_match(journal.CURRENT_BOOT) >>> myjournal.add_loglevel_matches(journal.LOG_ERR) >>> myjournal.add_match(_SYSTEMD_UNIT="systemd-udevd.service") @@ -105,7 +105,7 @@ class Journal(_Journal): found in the journal. """ def __init__(self, converters=None, flags=LOCAL_ONLY, path=None): - """Creates instance of Journal, which allows filtering and + """Create an instance of Reader, which allows filtering and return of journal entries. Argument `converters` is a dictionary which updates the DEFAULT_CONVERTERS to convert journal field values. @@ -118,7 +118,7 @@ class Journal(_Journal): currently flags are ignored when `path` is present as they are currently not relevant. """ - super(Journal, self).__init__(flags, path) + super(Reader, self).__init__(flags, path) if _sys.version_info >= (3,3): self.converters = _ChainMap() if converters is not None: @@ -164,7 +164,7 @@ class Journal(_Journal): args = list(args) args.extend(_make_line(key, val) for key, val in kwargs.items()) for arg in args: - super(Journal, self).add_match(arg) + super(Reader, self).add_match(arg) def get_next(self, skip=1): """Return the next log entry as a dictionary of fields. @@ -172,21 +172,21 @@ class Journal(_Journal): Optional skip value will return the `skip`\-th log entry. Entries will be processed with converters specified during - Journal creation. + Reader creation. """ return self._convert_entry( - super(Journal, self).get_next(skip)) + super(Reader, self).get_next(skip)) def query_unique(self, field): - """Return unique values appearing in the Journal for given `field`. + """Return unique values appearing in the journal for given `field`. Note this does not respect any journal matches. Entries will be processed with converters specified during - Journal creation. + Reader creation. """ return set(self._convert_field(field, value) - for value in super(Journal, self).query_unique(field)) + for value in super(Reader, self).query_unique(field)) def seek_realtime(self, realtime): """Seek to a matching journal entry nearest to `realtime` time. @@ -196,7 +196,7 @@ class Journal(_Journal): """ if isinstance(realtime, _datetime.datetime): realtime = float(realtime.strftime("%s.%f")) - return super(Journal, self).seek_realtime(realtime) + return super(Reader, self).seek_realtime(realtime) def seek_monotonic(self, monotonic, bootid=None): """Seek to a matching journal entry nearest to `monotonic` time. @@ -210,7 +210,7 @@ class Journal(_Journal): monotonic = monotonic.totalseconds() if isinstance(bootid, _uuid.UUID): bootid = bootid.get_hex() - return super(Journal, self).seek_monotonic(monotonic, bootid) + return super(Reader, self).seek_monotonic(monotonic, bootid) def log_level(self, level): """Set maximum log `level` by setting matches for PRIORITY. -- cgit v1.2.1 From 5ed6b60c1567b732db33784096ae4b4f731185a4 Mon Sep 17 00:00:00 2001 From: Lukas Nykryn Date: Fri, 1 Mar 2013 18:29:57 +0100 Subject: systemd-python: add missing check for return of PyDict_SetItem in _reader.c --- systemd/_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 7f200d5..d3d45cc 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -208,7 +208,7 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) if (r < 0) goto error; - PyDict_SetItem(dict, key, tmp_list); + r = PyDict_SetItem(dict, key, tmp_list); if (r < 0) goto error; } -- cgit v1.2.1 From 68bdaeba7af98ad7af0cf757c29f8fd9baf6738b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 5 Mar 2013 23:23:09 -0500 Subject: systemd-python: fix error check in _Reader.wait() --- systemd/_reader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index d3d45cc..7013fcf 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -507,9 +507,10 @@ static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) return NULL; Py_BEGIN_ALLOW_THREADS - r = sd_journal_wait(self->j, timeout ==0 ? (uint64_t) -1 : timeout * 1E6); + r = sd_journal_wait(self->j, + timeout == 0 ? (uint64_t) -1 : timeout * 1E6); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return long_FromLong(r); -- cgit v1.2.1 From 0fe854e001b9f14d481f5a512dcc13f5c3531d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 6 Mar 2013 22:15:46 -0500 Subject: systemd-python: catch only ValueErrors in conversion code First of all, 'try: ... except: ...' (with no exception specified) is always a no-no, since it catches all BaseExceptions, which includes ^C and other stuff which should almost never be caught. Now the conversion is stricter, and only one conversion is attempted, and only a ValueEror is caught. It seems reasonable to catch ValueErrors, since the entries in the journal are not verified, and any erroneous application might log a field which cannot be converted. The consumer of events must only check if a field is an instance of bytes and can otherwise assume that the conversion was performed correctly. Order of arguments in Reader.__init__ has been changed to match order in _Reader.__init__. Conversions have been updated to work under Python 2 and 3. --- systemd/journal.py | 98 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index 23e1d65..80299e6 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -43,13 +43,29 @@ if _sys.version_info >= (3,): else: Monotonic = tuple -_MONOTONIC_CONVERTER = lambda p: Monotonic((_datetime.timedelta(microseconds=p[0]), - _uuid.UUID(bytes=p[1]))) -_REALTIME_CONVERTER = lambda x: _datetime.datetime.fromtimestamp(x / 1E6) +def _convert_monotonic(m): + return Monotonic((_datetime.timedelta(microseconds=m[0]), + _uuid.UUID(bytes=m[1]))) + +def _convert_source_monotonic(s): + return _datetime.timedelta(microseconds=int(s)) + +def _convert_realtime(t): + return _datetime.datetime.fromtimestamp(t / 1E6) + +def _convert_timestamp(s): + return _datetime.datetime.fromtimestamp(int(s) / 1E6) + +if _sys.version_info >= (3,): + def _convert_uuid(s): + return _uuid.UUID(s.decode()) +else: + _convert_uuid = _uuid.UUID + DEFAULT_CONVERTERS = { - 'MESSAGE_ID': _uuid.UUID, - '_MACHINE_ID': _uuid.UUID, - '_BOOT_ID': _uuid.UUID, + 'MESSAGE_ID': _convert_uuid, + '_MACHINE_ID': _convert_uuid, + '_BOOT_ID': _convert_uuid, 'PRIORITY': int, 'LEADER': int, 'SESSION_ID': int, @@ -68,55 +84,60 @@ DEFAULT_CONVERTERS = { 'CODE_LINE': int, 'ERRNO': int, 'EXIT_STATUS': int, - '_SOURCE_REALTIME_TIMESTAMP': _REALTIME_CONVERTER, - '__REALTIME_TIMESTAMP': _REALTIME_CONVERTER, - '_SOURCE_MONOTONIC_TIMESTAMP': _MONOTONIC_CONVERTER, - '__MONOTONIC_TIMESTAMP': _MONOTONIC_CONVERTER, + '_SOURCE_REALTIME_TIMESTAMP': _convert_timestamp, + '__REALTIME_TIMESTAMP': _convert_realtime, + '_SOURCE_MONOTONIC_TIMESTAMP': _convert_source_monotonic, + '__MONOTONIC_TIMESTAMP': _convert_monotonic, 'COREDUMP': bytes, 'COREDUMP_PID': int, 'COREDUMP_UID': int, 'COREDUMP_GID': int, 'COREDUMP_SESSION': int, 'COREDUMP_SIGNAL': int, - 'COREDUMP_TIMESTAMP': _REALTIME_CONVERTER, + 'COREDUMP_TIMESTAMP': _convert_timestamp, } -if _sys.version_info >= (3,): - _convert_unicode = _functools.partial(str, encoding='utf-8') -else: - _convert_unicode = _functools.partial(unicode, encoding='utf-8') - class Reader(_Reader): """Reader allows the access and filtering of systemd journal entries. Note that in order to access the system journal, a non-root user must be in the `adm` group. - Example usage to print out all error or higher level messages - for systemd-udevd for the boot: + Example usage to print out all informational or higher level + messages for systemd-udevd for this boot: - >>> myjournal = journal.Reader() - >>> myjournal.add_boot_match(journal.CURRENT_BOOT) - >>> myjournal.add_loglevel_matches(journal.LOG_ERR) - >>> myjournal.add_match(_SYSTEMD_UNIT="systemd-udevd.service") - >>> for entry in myjournal: + >>> j = journal.Reader() + >>> j.this_boot() + >>> j.log_level(journal.LOG_INFO) + >>> j.add_match(_SYSTEMD_UNIT="systemd-udevd.service") + >>> for entry in j: ... print(entry['MESSAGE']) See systemd.journal-fields(7) for more info on typical fields found in the journal. """ - def __init__(self, converters=None, flags=LOCAL_ONLY, path=None): + def __init__(self, flags=LOCAL_ONLY, path=None, converters=None): """Create an instance of Reader, which allows filtering and return of journal entries. - Argument `converters` is a dictionary which updates the - DEFAULT_CONVERTERS to convert journal field values. + Argument `flags` sets open flags of the journal, which can be one of, or ORed combination of constants: LOCAL_ONLY (default) opens journal on local machine only; RUNTIME_ONLY opens only volatile journal files; and SYSTEM_ONLY opens only journal files of system services and the kernel. + Argument `path` is the directory of journal files. Note that currently flags are ignored when `path` is present as they are currently not relevant. + + Argument `converters` is a dictionary which updates the + DEFAULT_CONVERTERS to convert journal field values. Field + names are used as keys into this dictionary. The values must + be single argument functions, which take a `bytes` object and + return a converted value. When there's no entry for a field + name, then the default UTF-8 decoding will be attempted. If + the conversion fails with a ValueError, unconverted bytes + object will be returned. (Note that ValueEror is a superclass + of UnicodeDecodeError). """ super(Reader, self).__init__(flags, path) if _sys.version_info >= (3,3): @@ -130,18 +151,19 @@ class Reader(_Reader): self.converters.update(converters) def _convert_field(self, key, value): - """Convert value based on callable from self.converters - based of field/key""" + """Convert value using self.converters[key] + + If `key` is not present in self.converters, a standard unicode + decoding will be attempted. If the conversion (either + key-specific or the default one) fails with a ValueError, the + original bytes object will be returned. + """ + convert = self.converters.get(key, bytes.decode) try: - result = self.converters[key](value) - except: - # Default conversion in unicode - try: - result = _convert_unicode(value) - except UnicodeDecodeError: - # Leave in default bytes - result = value - return result + return convert(value) + except ValueError: + # Leave in default bytes + return value def _convert_entry(self, entry): """Convert entire journal entry utilising _covert_field""" @@ -217,7 +239,7 @@ class Reader(_Reader): """ if 0 <= level <= 7: for i in range(level+1): - self.add_match(PRIORITY="%s" % i) + self.add_match(PRIORITY="%d" % i) else: raise ValueError("Log level must be 0 <= level <= 7") -- cgit v1.2.1 From cbbe7879266d15b6936b2c766b1c4d9481798145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 00:26:24 -0500 Subject: systemd-python: split .seek() into .seek_head() and .seek_tail() This way python code follows the original interface more closely. Also, .seek(0, journal.SEEK_END) was just to much to type. --- systemd/_reader.c | 79 +++++++++++++++++------------------------------- systemd/docs/journal.rst | 5 --- systemd/journal.py | 1 - 3 files changed, 27 insertions(+), 58 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 7013fcf..e5733f0 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -360,60 +360,34 @@ static PyObject* Reader_flush_matches(Reader *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(Reader_seek__doc__, - "seek(offset[, whence]) -> None\n\n" - "Jump `offset` entries in the journal. Argument\n" - "`whence` defines what the offset is relative to:\n" - "os.SEEK_SET (default) from first match in journal;\n" - "os.SEEK_CUR from current position in journal;\n" - "and os.SEEK_END is from last match in journal."); -static PyObject* Reader_seek(Reader *self, PyObject *args, PyObject *keywds) +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)."); +static PyObject* Reader_seek_head(Reader *self, PyObject *args) { - int64_t offset; - int whence = SEEK_SET; - PyObject *result = NULL; - - static const char* const kwlist[] = {"offset", "whence", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist, - &offset, &whence)) + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_head(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) return NULL; + Py_RETURN_NONE; +} - switch(whence) { - case SEEK_SET: { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_head(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) - return NULL; - - if (offset > 0LL) - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset); - break; - } - case SEEK_CUR: - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset); - break; - case SEEK_END: { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_tail(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) - return NULL; - - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset < 0LL ? offset : -1LL); - break; - } - default: - PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); - } - - Py_XDECREF(result); - if (PyErr_Occurred()) +PyDoc_STRVAR(Reader_seek_tail__doc__, + "seek_tail() -> None\n\n" + "Jump to the beginning of the journal.\n" + "This method invokes sd_journal_seek_tail().\n" + "See man:sd_journal_seek_tail(3)."); +static PyObject* Reader_seek_tail(Reader *self, PyObject *args) +{ + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_tail(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) return NULL; Py_RETURN_NONE; } @@ -644,7 +618,8 @@ static PyMethodDef Reader_methods[] = { {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, - {"seek", (PyCFunction) Reader_seek, METH_VARARGS | METH_KEYWORDS, Reader_seek__doc__}, + {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__}, + {"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__}, {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 78b831a..faa2707 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -27,12 +27,7 @@ Accessing the Journal .. autoattribute:: systemd.journal.DEFAULT_CONVERTERS -Whence constants -~~~~~~~~~~~~~~~~ -.. autoattribute:: systemd.journal.SEEK_SET -.. autoattribute:: systemd.journal.SEEK_CUR -.. autoattribute:: systemd.journal.SEEK_END Journal access types ~~~~~~~~~~~~~~~~~~~~ diff --git a/systemd/journal.py b/systemd/journal.py index 80299e6..e9c09e8 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -27,7 +27,6 @@ import functools as _functools import uuid as _uuid import traceback as _traceback import os as _os -from os import SEEK_SET, SEEK_CUR, SEEK_END import logging as _logging if _sys.version_info >= (3,): from collections import ChainMap as _ChainMap -- cgit v1.2.1 From 000525db3e22d76131adbb17bc87b1c7c6426481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 00:35:28 -0500 Subject: systemd-python: export sd_j_get_fd, sd_j_reliable_fd, sd_j_close sd_journal_get_fd(j) is called j.fileno(), for compatiblity with Python conventions for file-like objects. More importantly, those new .seek_head() and .seek_tail() do not call .get_next(). This is better, if one wants to skip before retrieving an entry. --- systemd/_reader.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ systemd/docs/journal.rst | 16 ++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index e5733f0..c435dad 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -124,6 +124,47 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) return set_error(r, path, "Invalid flags or path"); } +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)."); +static PyObject* Reader_fileno(Reader *self, PyObject *args) +{ + int r; + r = sd_journal_get_fd(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + return long_FromLong(r); +} + +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)."); +static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) +{ + int r; + r = sd_journal_reliable_fd(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + return PyBool_FromLong(r); +} + +PyDoc_STRVAR(Reader_close__doc__, + "reliable_fd() -> None\n\n" + "Free resources allocated by this Reader object.\n" + "This method invokes sd_journal_close().\n" + "See man:sd_journal_close(3)."); +static PyObject* Reader_close(Reader *self, PyObject *args) +{ + sd_journal_close(self->j); + Py_RETURN_NONE; +} + PyDoc_STRVAR(Reader_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" @@ -613,6 +654,9 @@ static PyGetSetDef Reader_getseters[] = { }; static PyMethodDef Reader_methods[] = { + {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, + {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, + {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index faa2707..9dc495f 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -27,6 +27,22 @@ Accessing the Journal .. autoattribute:: systemd.journal.DEFAULT_CONVERTERS +Example: polling for journal events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example shows that journal events can be waited for (using +e.g. `poll`). This makes it easy to integrate Reader in an external +event loop: + + >>> import select + >>> from systemd import journal + >>> j = journal.Reader() + >>> j.seek_tail() + >>> p = select.poll() + >>> p.register(j, select.POLLIN) + >>> p.poll() + [(3, 1)] + >>> j.get_next() Journal access types -- cgit v1.2.1 From 67bdb044d89e04ee9e71741b20f797bb32458b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 11:28:44 -0500 Subject: systemd-python: refuse path and flags together in __init__ It's better to explictly check, instead of just documenting it. The return value from init is changed from 1 to -1 on error. Python seems to ignore 1 every second time. Looks like a bug in Python, but the return value doesn't seem to be documented anywhere, and -1 works as expected... so let's just use that. --- systemd/_reader.c | 23 ++++++++++++++++------- systemd/journal.py | 5 ++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index c435dad..42029ab 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -64,7 +64,7 @@ static int set_error(int r, const char* path, const char* invalid_message) { errno = -r; PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); } - return 1; + return -1; } #if PY_MAJOR_VERSION >= 3 @@ -94,25 +94,34 @@ static void Reader_dealloc(Reader* self) } PyDoc_STRVAR(Reader__doc__, - "Reader([flags][,path]) -> ...\n\n" + "Reader([flags | path]) -> ...\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" "Argument `flags` sets open flags of the journal, which can be one\n" "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" "journal on local machine only; RUNTIME_ONLY opens only\n" "volatile journal files; and SYSTEM_ONLY opens only\n" - "journal files of system services and the kernel.\n" + "journal files of system services and the kernel.\n\n" "Argument `path` is the directory of journal files. Note that\n" - "currently flags are ignored when `path` is present as they are\n" - "not relevant."); + "`flags` and `path` are exclusive.\n"); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { - int flags = SD_JOURNAL_LOCAL_ONLY, r; + int flags = 0, r; char *path = NULL; static const char* const kwlist[] = {"flags", "path", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist, &flags, &path)) - return 1; + return -1; + + if (!flags) + flags = SD_JOURNAL_LOCAL_ONLY; + else + if (path) { + PyErr_SetString(PyExc_ValueError, "cannot use both flags and path"); + return -1; + } Py_BEGIN_ALLOW_THREADS if (path) diff --git a/systemd/journal.py b/systemd/journal.py index e9c09e8..1ed726c 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -114,7 +114,7 @@ class Reader(_Reader): See systemd.journal-fields(7) for more info on typical fields found in the journal. """ - def __init__(self, flags=LOCAL_ONLY, path=None, converters=None): + def __init__(self, flags=0, path=None, converters=None): """Create an instance of Reader, which allows filtering and return of journal entries. @@ -125,8 +125,7 @@ class Reader(_Reader): journal files of system services and the kernel. Argument `path` is the directory of journal files. Note that - currently flags are ignored when `path` is present as they are - currently not relevant. + `flags` and `path` are exclusive. Argument `converters` is a dictionary which updates the DEFAULT_CONVERTERS to convert journal field values. Field -- cgit v1.2.1 From 7b732ed75dbc4b6cdcac0d4d729fc92cde178656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 11:29:01 -0500 Subject: systemd-python: update documentation for new systemd-journal group --- systemd/journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/journal.py b/systemd/journal.py index 1ed726c..146f59f 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -99,7 +99,7 @@ DEFAULT_CONVERTERS = { class Reader(_Reader): """Reader allows the access and filtering of systemd journal entries. Note that in order to access the system journal, a - non-root user must be in the `adm` group. + non-root user must be in the `systemd-journal` group. Example usage to print out all informational or higher level messages for systemd-udevd for this boot: -- cgit v1.2.1 From 70f8e1a1950099b05a7edae0ab068e81d86472d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 15:27:30 -0500 Subject: systemd-python: fix typos --- systemd/_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 42029ab..52e6b1c 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -164,7 +164,7 @@ static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) } PyDoc_STRVAR(Reader_close__doc__, - "reliable_fd() -> None\n\n" + "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)."); @@ -428,7 +428,7 @@ static PyObject* Reader_seek_head(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_seek_tail__doc__, "seek_tail() -> None\n\n" - "Jump to the beginning of the journal.\n" + "Jump to the end of the journal.\n" "This method invokes sd_journal_seek_tail().\n" "See man:sd_journal_seek_tail(3)."); static PyObject* Reader_seek_tail(Reader *self, PyObject *args) -- cgit v1.2.1 From df25f1d61024d2881d24b2480104a00c682d371b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 7 Mar 2013 15:32:33 -0500 Subject: systemd-python: fix segfault on double close --- systemd/_reader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index 52e6b1c..160ab69 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -171,6 +171,7 @@ PyDoc_STRVAR(Reader_close__doc__, static PyObject* Reader_close(Reader *self, PyObject *args) { sd_journal_close(self->j); + self->j = NULL; Py_RETURN_NONE; } -- cgit v1.2.1 From 3f1377a8e19d5a16bc833e2acfceaed9861eb145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 8 Mar 2013 11:45:37 -0500 Subject: systemd-python: provide version info to sphinx --- systemd/docs/conf.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/systemd/docs/conf.py b/systemd/docs/conf.py index 4a55778..ad3f002 100644 --- a/systemd/docs/conf.py +++ b/systemd/docs/conf.py @@ -42,15 +42,6 @@ master_doc = 'index' # General information about the project. project = u'python-systemd' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '197' -# The full version, including alpha/beta/rc tags. -release = '197' - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None -- cgit v1.2.1 From 531c97c27b13aefd7435b8e0a4ebc558a4b5054a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 8 Mar 2013 13:46:21 -0500 Subject: html: make python docs look more like the rest The result is ugly enough, I hope, to motivate someone with design skills to fix it. --- systemd/docs/conf.py | 4 +- systemd/docs/default.css | 196 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 systemd/docs/default.css diff --git a/systemd/docs/conf.py b/systemd/docs/conf.py index ad3f002..1919170 100644 --- a/systemd/docs/conf.py +++ b/systemd/docs/conf.py @@ -28,7 +28,7 @@ import sys, os extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ['.'] # The suffix of source filenames. source_suffix = '.rst' @@ -110,7 +110,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] +html_static_path = ['.'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. diff --git a/systemd/docs/default.css b/systemd/docs/default.css new file mode 100644 index 0000000..7c097d6 --- /dev/null +++ b/systemd/docs/default.css @@ -0,0 +1,196 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: #ffffff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-color: #133f52; + line-height: 30px; + color: #ffffff; +} + +div.related a { + color: #ffffff; +} + +div.sphinxsidebar { + background-color: #dddddd; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; +} + +div.sphinxsidebar input { + border: 1px solid #000000; + font-family: sans-serif; + font-size: 1em; +} + + + +/* -- hyperlink styles ------------------------------------------------------ */ + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + + + +/* -- body styles ----------------------------------------------------------- */ + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Trebuchet MS', sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p { + margin-bottom: 5px; +} + +div.admonition pre { + margin-bottom: 5px; +} + +div.admonition ul, div.admonition ol { + margin-bottom: 5px; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +th { + background-color: #ede; +} + +.warning tt { + background: #efc2c2; +} + +.note tt { + background: #d6d6d6; +} + +.viewcode-back { + font-family: sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} -- cgit v1.2.1 From 98d3fa4753dcc36c95ca6cd53ce999812cbe866f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 9 Mar 2013 09:55:03 -0500 Subject: python/docs: use the same links on top as in man pages I forgot to commit the layout file, because it was gitignored. Fixed now. --- systemd/docs/.gitignore | 1 + systemd/docs/layout.html | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 systemd/docs/.gitignore create mode 100644 systemd/docs/layout.html diff --git a/systemd/docs/.gitignore b/systemd/docs/.gitignore new file mode 100644 index 0000000..b06a965 --- /dev/null +++ b/systemd/docs/.gitignore @@ -0,0 +1 @@ +!layout.html diff --git a/systemd/docs/layout.html b/systemd/docs/layout.html new file mode 100644 index 0000000..7898914 --- /dev/null +++ b/systemd/docs/layout.html @@ -0,0 +1,17 @@ +{% extends "!layout.html" %} + +{% block relbar1 %} + Index · + Directives · + Python · + libudev · + gudev + systemd v. {{release}} +
+{% endblock %} + +{# remove the lower relbar #} +{% block relbar2 %} {% endblock %} + +{# remove the footer #} +{% block footer %} {% endblock %} -- cgit v1.2.1 From 961f238f8f449b0ecc2945264d26099afb60940b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 12 Mar 2013 23:57:09 -0400 Subject: man,html: say 'systemd 198' in the header This should help readers of the man or HTML pages know if the documentation is out of date. An alternative to use a date generated from 'git log' was considered, but since we try to keep user visible documentation up to date, showing the project version should be enough. --- systemd/docs/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/docs/layout.html b/systemd/docs/layout.html index 7898914..be5ff98 100644 --- a/systemd/docs/layout.html +++ b/systemd/docs/layout.html @@ -6,7 +6,7 @@ Python · libudev · gudev - systemd v. {{release}} + systemd {{release}}
{% endblock %} -- cgit v1.2.1 From d583bb0f53df19ef889d1d2575ac4c7eaacb8795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 15 Mar 2013 19:01:10 -0400 Subject: systemd-python: add systemd.daemon wrapping sd-daemon Please see the documentation (e.g. pydoc3 systemd.daemon) for full description. As usual, systemd._daemon wraps the raw interface, while systemd.daemon provides the more pythonic API. sd_listen_fds, sd_booted, sd_is_fifo, sd_is_socket, sd_is_socket_unix, sd_is_socket_inet, sd_is_mq, and SD_LISTEN_FDS_START are currently wrapped. --- systemd/_daemon.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++ systemd/_reader.c | 17 --- systemd/daemon.py | 53 ++++++++ systemd/docs/daemon.rst | 16 +++ systemd/docs/index.rst | 1 + systemd/pyutil.h | 19 +++ 6 files changed, 412 insertions(+), 17 deletions(-) create mode 100644 systemd/_daemon.c create mode 100644 systemd/daemon.py create mode 100644 systemd/docs/daemon.rst diff --git a/systemd/_daemon.c b/systemd/_daemon.c new file mode 100644 index 0000000..8f93d91 --- /dev/null +++ b/systemd/_daemon.c @@ -0,0 +1,323 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with 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 + +#include +#include "pyutil.h" + +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" + "useful for socket activation and checking if the system is\n" + "running under systemd." +); + +static PyObject* set_error(int r, const char* invalid_message) { + assert (r < 0); + + if (r == -EINVAL && invalid_message) + PyErr_SetString(PyExc_ValueError, invalid_message); + else if (r == -ENOMEM) + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + else { + errno = -r; + PyErr_SetFromErrno(PyExc_OSError); + } + + return NULL; +} + + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 +static int Unicode_FSConverter(PyObject* obj, void *_result) { + PyObject **result = _result; + + assert(result); + + if (!obj) + /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so + * we can assume that it was PyUnicode_FSConverter. */ + return PyUnicode_FSConverter(obj, result); + + if (obj == Py_None) { + *result = NULL; + return 1; + } + + return PyUnicode_FSConverter(obj, result); +} +#endif + + +PyDoc_STRVAR(booted__doc__, + "booted() -> bool\n\n" + "Return True iff this system is running under systemd.\n" + "Wraps sd_daemon_booted(3)." +); + +static PyObject* booted(PyObject *self, PyObject *args) { + int r; + assert(args == NULL); + + r = sd_booted(); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(listen_fds__doc__, + "_listen_fds(unset_environment=True) -> int\n\n" + "Return the number of descriptors passed to this process by the init system\n" + "as part of the socket-based activation logic.\n" + "Wraps sd_listen_fds(3)." +); + +static PyObject* listen_fds(PyObject *self, PyObject *args) { + int r; + int unset = true; + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3 + if (!PyArg_ParseTuple(args, "|p:_listen_fds", &unset)) + return NULL; +#else + PyObject *obj = NULL; + if (!PyArg_ParseTuple(args, "|O:_listen_fds", &obj)) + return NULL; + if (obj != NULL) + unset = PyObject_IsTrue(obj); + if (unset < 0) + return NULL; +#endif + + r = sd_listen_fds(unset); + if (r < 0) + return set_error(r, NULL); + + return long_FromLong(r); +} + +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" + "Wraps sd_is_fifo(3)." +); + + +static PyObject* is_fifo(PyObject *self, PyObject *args) { + int r; + int fd; + const char *path = NULL; + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 + if (!PyArg_ParseTuple(args, "i|O&:_is_fifo", + &fd, Unicode_FSConverter, &path)) + return NULL; +#else + if (!PyArg_ParseTuple(args, "i|z:_is_fifo", &fd, &path)) + return NULL; +#endif + + r = sd_is_fifo(fd, path); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(is_mq__doc__, + "_is_mq(fd, path) -> bool\n\n" + "Returns True iff the descriptor refers to a POSIX message queue.\n" + "Wraps sd_is_mq(3)." +); + +static PyObject* is_mq(PyObject *self, PyObject *args) { + int r; + int fd; + const char *path = NULL; + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 + if (!PyArg_ParseTuple(args, "i|O&:_is_mq", + &fd, Unicode_FSConverter, &path)) + return NULL; +#else + if (!PyArg_ParseTuple(args, "i|z:_is_mq", &fd, &path)) + return NULL; +#endif + + r = sd_is_mq(fd, path); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + + +PyDoc_STRVAR(is_socket__doc__, + "_is_socket(fd, family=AF_UNSPEC, type=0, listening=-1) -> bool\n\n" + "Returns True iff the descriptor refers to a socket.\n" + "Wraps sd_is_socket(3).\n\n" + "Constants for `family` are defined in the socket module." +); + +static PyObject* is_socket(PyObject *self, PyObject *args) { + int r; + int fd, family = AF_UNSPEC, type = 0, listening = -1; + + if (!PyArg_ParseTuple(args, "i|iii:_is_socket", + &fd, &family, &type, &listening)) + return NULL; + + r = sd_is_socket(fd, family, type, listening); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(is_socket_inet__doc__, + "_is_socket_inet(fd, family=AF_UNSPEC, type=0, listening=-1, port=0) -> bool\n\n" + "Wraps sd_is_socket_inet(3).\n\n" + "Constants for `family` are defined in the socket module." +); + +static PyObject* is_socket_inet(PyObject *self, PyObject *args) { + int r; + int fd, family = AF_UNSPEC, type = 0, listening = -1, port = 0; + + if (!PyArg_ParseTuple(args, "i|iiii:_is_socket_inet", + &fd, &family, &type, &listening, &port)) + return NULL; + + if (port < 0 || port > INT16_MAX) + return set_error(-EINVAL, "port must fit into uint16_t"); + + r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(is_socket_unix__doc__, + "_is_socket_unix(fd, type, listening, path) -> bool\n\n" + "Wraps sd_is_socket_unix(3)." +); + +static PyObject* is_socket_unix(PyObject *self, PyObject *args) { + int r; + int fd, type = 0, listening = -1; + char* path = NULL; + Py_ssize_t length = 0; + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 + PyObject _cleanup_Py_DECREF_ *_path = NULL; + if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix", + &fd, &type, &listening, Unicode_FSConverter, &_path)) + return NULL; + if (_path) { + assert(PyBytes_Check(_path)); + if (PyBytes_AsStringAndSize(_path, &path, &length)) + return NULL; + } +#else + if (!PyArg_ParseTuple(args, "i|iiz#:_is_socket_unix", + &fd, &type, &listening, &path, &length)) + return NULL; +#endif + + r = sd_is_socket_unix(fd, type, listening, path, length); + if (r < 0) + return set_error(r, NULL); + + return PyBool_FromLong(r); +} + + +static PyMethodDef methods[] = { + { "booted", booted, METH_NOARGS, booted__doc__}, + { "_listen_fds", listen_fds, METH_VARARGS, listen_fds__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__}, + { "_is_socket_inet", is_socket_inet, METH_VARARGS, is_socket_inet__doc__}, + { "_is_socket_unix", is_socket_unix, METH_VARARGS, is_socket_unix__doc__}, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC init_daemon(void) { + PyObject *m; + + m = Py_InitModule3("_daemon", methods, module__doc__); + if (m == NULL) + return; + + PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START); +} + +#else + +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 +}; + +PyMODINIT_FUNC PyInit__daemon(void) { + PyObject *m; + + m = PyModule_Create(&module); + if (m == NULL) + return NULL; + + if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START)) { + Py_DECREF(m); + return NULL; + } + + return m; +} + +#endif + +#pragma GCC diagnostic pop diff --git a/systemd/_reader.c b/systemd/_reader.c index 160ab69..96634a1 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -30,23 +30,6 @@ #include "macro.h" #include "util.h" -#if PY_MAJOR_VERSION >=3 -# define unicode_FromStringAndSize PyUnicode_FromStringAndSize -# define unicode_FromString PyUnicode_FromString -# define long_FromLong PyLong_FromLong -# define long_FromSize_t PyLong_FromSize_t -# define long_Check PyLong_Check -# define long_AsLong PyLong_AsLong -#else -/* Python 3 type naming convention is used */ -# define unicode_FromStringAndSize PyString_FromStringAndSize -# define unicode_FromString PyString_FromString -# define long_FromLong PyInt_FromLong -# define long_FromSize_t PyInt_FromSize_t -# define long_Check PyInt_Check -# define long_AsLong PyInt_AsLong -#endif - typedef struct { PyObject_HEAD sd_journal *j; diff --git a/systemd/daemon.py b/systemd/daemon.py new file mode 100644 index 0000000..4a02204 --- /dev/null +++ b/systemd/daemon.py @@ -0,0 +1,53 @@ +from ._daemon import (booted, + _listen_fds, + _is_fifo, + _is_socket, + _is_socket_inet, + _is_socket_unix, + _is_mq, + LISTEN_FDS_START) +from socket import AF_UNSPEC as _AF_UNSPEC + +def _convert_fileobj(fileobj): + try: + return fileobj.fileno() + except AttributeError: + return fileobj + +def is_fifo(fileobj, path=None): + fd = _convert_fileobj(fileobj) + return _is_fifo(fd, path) + +def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1): + fd = _convert_fileobj(fileobj) + return _is_socket(fd, family, type, listening) + +def is_socket_inet(fileobj, family=_AF_UNSPEC, type=0, listening=-1, port=0): + fd = _convert_fileobj(fileobj) + return _is_socket_inet(fd, family, type, listening) + +def is_socket_unix(fileobj, type=0, listening=-1, path=None): + fd = _convert_fileobj(fileobj) + return _is_socket_unix(fd, type, listening, path) + +def is_mq(fileobj, path=None): + fd = _convert_fileobj(fileobj) + return _is_mq(fd, path) + +def listen_fds(unset_environment=True): + """Return a list of socket activated descriptors + + Example:: + + (in primary window) + $ systemd-activate -l 2000 python3 -c \\ + 'from systemd.daemon import listen_fds; print(listen_fds())' + (in another window) + $ telnet localhost 2000 + (in primary window) + ... + Execing python3 (...) + [3] + """ + num = _listen_fds(unset_environment) + return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num)) diff --git a/systemd/docs/daemon.rst b/systemd/docs/daemon.rst new file mode 100644 index 0000000..72280ca --- /dev/null +++ b/systemd/docs/daemon.rst @@ -0,0 +1,16 @@ +`systemd.daemon` module +======================= + +.. automodule:: systemd.daemon + :members: + :undoc-members: + :inherited-members: + + .. autoattribute:: systemd.daemon.LISTEN_FDS_START + + .. autofunction:: _listen_fds + .. autofunction:: _is_fifo + .. autofunction:: _is_socket + .. autofunction:: _is_socket_unix + .. autofunction:: _is_socket_inet + .. autofunction:: _is_mq diff --git a/systemd/docs/index.rst b/systemd/docs/index.rst index f04d5a1..8a94d07 100644 --- a/systemd/docs/index.rst +++ b/systemd/docs/index.rst @@ -13,6 +13,7 @@ Contents: journal id128 + daemon Indices and tables ================== diff --git a/systemd/pyutil.h b/systemd/pyutil.h index 3b7bc58..2163fda 100644 --- a/systemd/pyutil.h +++ b/systemd/pyutil.h @@ -1,5 +1,7 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ +#pragma once + /*** This file is part of systemd. @@ -27,3 +29,20 @@ void cleanup_Py_DECREFp(PyObject **p); #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) + +#if PY_MAJOR_VERSION >=3 +# define unicode_FromStringAndSize PyUnicode_FromStringAndSize +# define unicode_FromString PyUnicode_FromString +# define long_FromLong PyLong_FromLong +# define long_FromSize_t PyLong_FromSize_t +# define long_Check PyLong_Check +# define long_AsLong PyLong_AsLong +#else +/* Python 3 type naming convention is used */ +# define unicode_FromStringAndSize PyString_FromStringAndSize +# define unicode_FromString PyString_FromString +# define long_FromLong PyInt_FromLong +# define long_FromSize_t PyInt_FromSize_t +# define long_Check PyInt_Check +# define long_AsLong PyInt_AsLong +#endif -- cgit v1.2.1 From 4ddd8feaa98ba0e83b5931e3cffc1330cdf540e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 18 Mar 2013 01:12:25 -0400 Subject: systemd-python: allow Reader to be used as a context manager --- systemd/_reader.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index 96634a1..6759555 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -153,6 +153,35 @@ PyDoc_STRVAR(Reader_close__doc__, "See man:sd_journal_close(3)."); static PyObject* Reader_close(Reader *self, PyObject *args) { + assert(self); + assert(!args); + + sd_journal_close(self->j); + self->j = NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Reader___enter____doc__, + "__enter__() -> self\n\n" + "Part of the context manager protocol.\n" + "Returns self.\n"); +static PyObject* Reader___enter__(PyObject *self, PyObject *args) +{ + assert(self); + assert(!args); + + Py_INCREF(self); + return self; +} + +PyDoc_STRVAR(Reader___exit____doc__, + "__exit__(type, value, traceback) -> None\n\n" + "Part of the context manager protocol.\n" + "Closes the journal.\n"); +static PyObject* Reader___exit__(Reader *self, PyObject *args) +{ + assert(self); + sd_journal_close(self->j); self->j = NULL; Py_RETURN_NONE; @@ -650,6 +679,9 @@ static PyMethodDef Reader_methods[] = { {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, + {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, + {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, + {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, -- cgit v1.2.1 From b7010f5dbc0e828b9c59c79af861a2464cf27e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 15 Mar 2013 18:10:51 -0400 Subject: systemd-python: add _Reader.closed attribute This should make the file interface of _Reader complete. --- systemd/_reader.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 6759555..67358e3 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -666,12 +666,24 @@ static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closur return set_error(r, NULL, NULL); } -static PyGetSetDef Reader_getseters[] = { +PyDoc_STRVAR(closed__doc__, + "True iff journal is closed"); +static PyObject* Reader_get_closed(Reader *self, void *closure) +{ + return PyBool_FromLong(self->j == NULL); +} + +static PyGetSetDef Reader_getsetters[] = { {(char*) "data_threshold", (getter) Reader_get_data_threshold, (setter) Reader_set_data_threshold, (char*) data_threshold__doc__, NULL}, + {(char*) "closed", + (getter) Reader_get_closed, + NULL, + (char*) closed__doc__, + NULL}, {NULL} }; @@ -727,7 +739,7 @@ static PyTypeObject ReaderType = { Reader_iternext, /* tp_iternext */ Reader_methods, /* tp_methods */ 0, /* tp_members */ - Reader_getseters, /* tp_getset */ + Reader_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ -- cgit v1.2.1 From 150dc5bde2962145c7ad64c2e85be2c0e1e0fa26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 15 Mar 2013 18:10:51 -0400 Subject: systemd-python: add _Reader.get_catalog() This one wraps sd_journaal_get_catalog. --- systemd/_reader.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index 67358e3..a257757 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -634,6 +634,29 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) return value_set; } + +PyDoc_STRVAR(Reader_get_catalog__doc__, + "get_catalog() -> str\n\n" + "Retrieve a message catalog entry for the current journal entry.\n" + "Wraps man:sd_journal_get_catalog(3)."); +static PyObject* Reader_get_catalog(Reader *self, PyObject *args) +{ + int r; + char _cleanup_free_ *msg = NULL; + + assert(self); + assert(!args); + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog(self->j, &msg); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + return unicode_FromString(msg); +} + + PyDoc_STRVAR(data_threshold__doc__, "Threshold for field size truncation in bytes.\n\n" "Fields longer than this will be truncated to the threshold size.\n" @@ -706,6 +729,7 @@ static PyMethodDef Reader_methods[] = { {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, + {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, {NULL} /* Sentinel */ }; -- cgit v1.2.1 From 69669a975b593bfd177420b4647e8a784b167de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 15 Mar 2013 18:10:51 -0400 Subject: systemd-python: add journal.get_catalog() This one wraps sd_journal_get_catalog_from_message_id. Thanks to Python namespacing, we can stick to a shorter name. --- systemd/_reader.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- systemd/journal.py | 3 ++- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index a257757..2feb091 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -50,6 +50,11 @@ static int set_error(int r, const char* path, const char* invalid_message) { return -1; } + +PyDoc_STRVAR(module__doc__, + "Class to reads the systemd journal similar to journalctl."); + + #if PY_MAJOR_VERSION >= 3 static PyTypeObject MonotonicType; @@ -657,6 +662,37 @@ 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)."); +static PyObject* get_catalog(PyObject *self, PyObject *args) +{ + int r; + char *id_ = NULL; + sd_id128_t id; + char _cleanup_free_ *msg = NULL; + + assert(!self); + assert(args); + + if (!PyArg_ParseTuple(args, "z", &id_)) + return NULL; + + r = sd_id128_from_string(id_, &id); + if (set_error(r, NULL, "Invalid id128")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog_for_message_id(id, &msg); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + return unicode_FromString(msg); +} + + PyDoc_STRVAR(data_threshold__doc__, "Threshold for field size truncation in bytes.\n\n" "Fields longer than this will be truncated to the threshold size.\n" @@ -774,16 +810,19 @@ static PyTypeObject ReaderType = { PyType_GenericNew, /* tp_new */ }; -#define SUMMARY \ - "Module that reads the systemd journal similar to journalctl." +static PyMethodDef methods[] = { + { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; #if PY_MAJOR_VERSION >= 3 -static PyModuleDef _reader_module = { +static PyModuleDef module = { PyModuleDef_HEAD_INIT, "_reader", - SUMMARY, + module__doc__, -1, - NULL, NULL, NULL, NULL, NULL + methods, + NULL, NULL, NULL, NULL }; #endif @@ -813,7 +852,7 @@ init_reader(void) #endif #if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&_reader_module); + m = PyModule_Create(&module); if (m == NULL) return NULL; @@ -822,7 +861,7 @@ init_reader(void) initialized = true; } #else - m = Py_InitModule3("_reader", NULL, SUMMARY); + m = Py_InitModule3("_reader", methods, module__doc__); if (m == NULL) return; #endif diff --git a/systemd/journal.py b/systemd/journal.py index 146f59f..fee39c9 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -34,7 +34,8 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd from ._reader import (_Reader, NOP, APPEND, INVALIDATE, - LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY) + LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY, + get_catalog) from . import id128 as _id128 if _sys.version_info >= (3,): -- cgit v1.2.1 From 9d63a706d7b76f53ff9f72336c2dce9ee91884c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 15 Mar 2013 18:10:51 -0400 Subject: systemd-python: small cleanups - separate methods with two empty lines for clarity - avoid malloc(0) by specyfing private data size as -1 - add method name in error messages --- systemd/_journal.c | 2 +- systemd/_reader.c | 38 +++++++++++++++++++++++++++++--------- systemd/id128.c | 2 +- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index ced52b2..2de0d4f 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -128,7 +128,7 @@ static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "_journal", /* name of module */ NULL, /* module documentation, may be NULL */ - 0, /* size of per-interpreter state of the module */ + -1, /* size of per-interpreter state of the module */ methods }; diff --git a/systemd/_reader.c b/systemd/_reader.c index 2feb091..908d911 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -75,6 +75,7 @@ static PyStructSequence_Desc Monotonic_desc = { }; #endif + static void Reader_dealloc(Reader* self) { sd_journal_close(self->j); @@ -121,6 +122,7 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) return set_error(r, path, "Invalid flags or path"); } + PyDoc_STRVAR(Reader_fileno__doc__, "fileno() -> int\n\n" "Get a file descriptor to poll for changes in the journal.\n" @@ -136,6 +138,7 @@ static PyObject* Reader_fileno(Reader *self, PyObject *args) return long_FromLong(r); } + PyDoc_STRVAR(Reader_reliable_fd__doc__, "reliable_fd() -> bool\n\n" "Returns True iff the journal can be polled reliably.\n" @@ -151,6 +154,7 @@ static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) return PyBool_FromLong(r); } + PyDoc_STRVAR(Reader_close__doc__, "close() -> None\n\n" "Free resources allocated by this Reader object.\n" @@ -166,6 +170,7 @@ static PyObject* Reader_close(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader___enter____doc__, "__enter__() -> self\n\n" "Part of the context manager protocol.\n" @@ -192,6 +197,7 @@ static PyObject* Reader___exit__(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" @@ -204,7 +210,7 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) int64_t skip = 1LL; int r; - if (!PyArg_ParseTuple(args, "|L", &skip)) + if (!PyArg_ParseTuple(args, "|L:_Reader.get_next", &skip)) return NULL; if (skip == 0LL) { @@ -371,6 +377,7 @@ error: return NULL; } + PyDoc_STRVAR(Reader_get_previous__doc__, "get_previous([skip]) -> dict\n\n" "Return dictionary of the previous log entry. Optional skip value\n" @@ -378,13 +385,14 @@ PyDoc_STRVAR(Reader_get_previous__doc__, static PyObject* Reader_get_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L", &skip)) + if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip)) return NULL; return PyObject_CallMethod((PyObject *)self, (char*) "get_next", (char*) "L", -skip); } + PyDoc_STRVAR(Reader_add_match__doc__, "add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" @@ -395,7 +403,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds { char *match; int match_len, r; - if (!PyArg_ParseTuple(args, "s#", &match, &match_len)) + if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len)) return NULL; r = sd_journal_add_match(self->j, match, match_len); @@ -406,6 +414,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() -> None\n\n" "Inserts a logical OR between matches added before and afterwards."); @@ -419,6 +428,7 @@ static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); @@ -428,6 +438,7 @@ static PyObject* Reader_flush_matches(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_head__doc__, "seek_head() -> None\n\n" "Jump to the beginning of the journal.\n" @@ -444,6 +455,7 @@ static PyObject* Reader_seek_head(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_tail__doc__, "seek_tail() -> None\n\n" "Jump to the end of the journal.\n" @@ -460,6 +472,7 @@ static PyObject* Reader_seek_tail(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" @@ -470,7 +483,7 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) uint64_t timestamp; int r; - if (!PyArg_ParseTuple(args, "d", &timedouble)) + if (!PyArg_ParseTuple(args, "d:_Reader.seek_realtime", &timedouble)) return NULL; timestamp = (uint64_t) (timedouble * 1.0E6); @@ -487,6 +500,7 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" "Seek to nearest matching journal entry to `monotonic`. Argument\n" @@ -501,7 +515,7 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) sd_id128_t id; int r; - if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) + if (!PyArg_ParseTuple(args, "d|z:_Reader.seek_monotonic", &timedouble, &bootid)) return NULL; timestamp = (uint64_t) (timedouble * 1.0E6); @@ -531,6 +545,7 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" @@ -545,7 +560,7 @@ static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) int r; int64_t timeout = 0LL; - if (!PyArg_ParseTuple(args, "|L", &timeout)) + if (!PyArg_ParseTuple(args, "|L:_Reader.wait", &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -558,6 +573,7 @@ static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) return long_FromLong(r); } + PyDoc_STRVAR(Reader_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" "Seek to journal entry by given unique reference `cursor`."); @@ -566,7 +582,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) const char *cursor; int r; - if (!PyArg_ParseTuple(args, "s", &cursor)) + if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -577,6 +593,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) Py_RETURN_NONE; } + static PyObject* Reader_iter(PyObject *self) { Py_INCREF(self); @@ -601,6 +618,7 @@ static PyObject* Reader_iternext(PyObject *self) } } + PyDoc_STRVAR(Reader_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Return a set of unique values appearing in journal for the\n" @@ -613,7 +631,7 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) size_t uniq_len; PyObject *value_set, *key, *value; - if (!PyArg_ParseTuple(args, "s", &query)) + if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -676,7 +694,7 @@ static PyObject* get_catalog(PyObject *self, PyObject *args) assert(!self); assert(args); - if (!PyArg_ParseTuple(args, "z", &id_)) + if (!PyArg_ParseTuple(args, "z:get_catalog", &id_)) return NULL; r = sd_id128_from_string(id_, &id); @@ -725,6 +743,7 @@ static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closur return set_error(r, NULL, NULL); } + PyDoc_STRVAR(closed__doc__, "True iff journal is closed"); static PyObject* Reader_get_closed(Reader *self, void *closure) @@ -732,6 +751,7 @@ static PyObject* Reader_get_closed(Reader *self, void *closure) return PyBool_FromLong(self->j == NULL); } + static PyGetSetDef Reader_getsetters[] = { {(char*) "data_threshold", (getter) Reader_get_data_threshold, diff --git a/systemd/id128.c b/systemd/id128.c index a6711a5..865cc3c 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -134,7 +134,7 @@ static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "id128", /* name of module */ module__doc__, /* module documentation, may be NULL */ - 0, /* size of per-interpreter state of the module */ + -1, /* size of per-interpreter state of the module */ methods }; -- cgit v1.2.1 From 26b763337c48e7cd4481f7d7eabce34904d20e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 18:25:35 -0400 Subject: Remove some unused variables --- systemd/id128.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/id128.c b/systemd/id128.c index 865cc3c..a9611c4 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -54,7 +54,7 @@ PyDoc_STRVAR(get_boot__doc__, static PyObject* make_uuid(sd_id128_t id) { PyObject _cleanup_Py_DECREF_ *uuid = NULL, *UUID = NULL, *bytes = NULL, - *args = NULL, *kwargs = NULL, *obj = NULL; + *args = NULL, *kwargs = NULL; uuid = PyImport_ImportModule("uuid"); if (!uuid) -- cgit v1.2.1 From f55e2d9049f79834a35164545616faac8fea07fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 18:30:10 -0400 Subject: systemd-python: export sd_journal_get_usage --- systemd/_reader.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 908d911..17c0015 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -130,12 +130,12 @@ PyDoc_STRVAR(Reader_fileno__doc__, "See man:sd_journal_get_fd(3)."); static PyObject* Reader_fileno(Reader *self, PyObject *args) { - int r; - r = sd_journal_get_fd(self->j); - set_error(r, NULL, NULL); - if (r < 0) + int fd; + fd = sd_journal_get_fd(self->j); + set_error(fd, NULL, NULL); + if (fd < 0) return NULL; - return long_FromLong(r); + return long_FromLong(fd); } @@ -171,6 +171,29 @@ static PyObject* Reader_close(Reader *self, PyObject *args) } +PyDoc_STRVAR(Reader_get_usage__doc__, + "get_usage() -> int\n\n" + "Returns the total disk space currently used by journal" + "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was" + "passed when opening the journal this value will only reflect" + "the size of journal files of the local host, otherwise" + "of all hosts.\n\n" + "This method invokes sd_journal_get_usage().\n" + "See man:sd_journal_get_usage(3)."); +static PyObject* Reader_get_usage(Reader *self, PyObject *args) +{ + int r; + uint64_t bytes; + + r = sd_journal_get_usage(self->j, &bytes); + if (set_error(r, NULL, NULL)) + return NULL; + + assert_cc(sizeof(unsigned long long) == sizeof(bytes)); + return PyLong_FromUnsignedLongLong(bytes); +} + + PyDoc_STRVAR(Reader___enter____doc__, "__enter__() -> self\n\n" "Part of the context manager protocol.\n" @@ -770,9 +793,9 @@ static PyMethodDef Reader_methods[] = { {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, + {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, - {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, -- cgit v1.2.1 From 8057e1c7e7213011157a05a2e6ee467a204c0b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 18:40:05 -0400 Subject: systemd-python: cleanup up usec_t handling The behaviour wrt. seconds vs. microseconds was inconsistent. Now _Reader always uses native units (us), while Reader always uses seconds and accepts both floats and ints. This way the conversion is always done in the Python layer, and the lower level API allows access to the journal API without the potentially lossy conversion between double and uint64_t. --- systemd/_reader.c | 39 ++++++++++++--------------------------- systemd/journal.py | 20 ++++++++++++++++---- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 17c0015..4fe7847 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -499,22 +499,15 @@ static PyObject* Reader_seek_tail(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" - "`realtime` can must be an integer unix timestamp."); + "`realtime` in specified in seconds."); static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { - double timedouble; uint64_t timestamp; int r; - if (!PyArg_ParseTuple(args, "d:_Reader.seek_realtime", &timedouble)) + if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) return NULL; - timestamp = (uint64_t) (timedouble * 1.0E6); - if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be a positive integer"); - return NULL; - } - Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS @@ -527,26 +520,18 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" "Seek to nearest matching journal entry to `monotonic`. Argument\n" - "`monotonic` is an timestamp from boot in seconds.\n" + "`monotonic` is an timestamp from boot in microseconds.\n" "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { - double timedouble; char *bootid = NULL; uint64_t timestamp; sd_id128_t id; int r; - if (!PyArg_ParseTuple(args, "d|z:_Reader.seek_monotonic", &timedouble, &bootid)) - return NULL; - - timestamp = (uint64_t) (timedouble * 1.0E6); - - if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be positive number"); + if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", ×tamp, &bootid)) return NULL; - } if (bootid) { r = sd_id128_from_string(bootid, &id); @@ -565,6 +550,7 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL)) return NULL; + Py_RETURN_NONE; } @@ -572,23 +558,22 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" - "the maximum number of seconds to wait before returning\n" - "regardless of wheter the journal has changed. If `timeout` is not given\n" - "or is 0, then block forever.\n" + "the maximum number of microseconds to wait before returning\n" + "regardless of wheter the journal has changed. If `timeout` is -1,\n" + "then block forever.\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."); -static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) +static PyObject* Reader_wait(Reader *self, PyObject *args) { int r; - int64_t timeout = 0LL; + int64_t timeout; - if (!PyArg_ParseTuple(args, "|L:_Reader.wait", &timeout)) + if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS - r = sd_journal_wait(self->j, - timeout == 0 ? (uint64_t) -1 : timeout * 1E6); + r = sd_journal_wait(self->j, timeout); Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL) < 0) return NULL; diff --git a/systemd/journal.py b/systemd/journal.py index fee39c9..a522aec 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -51,10 +51,10 @@ def _convert_source_monotonic(s): return _datetime.timedelta(microseconds=int(s)) def _convert_realtime(t): - return _datetime.datetime.fromtimestamp(t / 1E6) + return _datetime.datetime.fromtimestamp(t / 1000000) def _convert_timestamp(s): - return _datetime.datetime.fromtimestamp(int(s) / 1E6) + return _datetime.datetime.fromtimestamp(int(s) / 1000000) if _sys.version_info >= (3,): def _convert_uuid(s): @@ -209,6 +209,17 @@ class Reader(_Reader): return set(self._convert_field(field, value) for value in super(Reader, self).query_unique(field)) + def wait(self, timeout=None): + """Wait for a change in the journal. `timeout` is the maximum + time in seconds to wait, or None, to wait forever. + + Returns one of NOP (no change), APPEND (new entries have been + added to the end of the journal), or INVALIDATE (journal files + have been added or removed). + """ + us = -1 if timeout is None else int(timeout * 1000000) + return super(Reader, self).wait(timeout) + def seek_realtime(self, realtime): """Seek to a matching journal entry nearest to `realtime` time. @@ -216,8 +227,8 @@ class Reader(_Reader): or datetime.datetime instance. """ if isinstance(realtime, _datetime.datetime): - realtime = float(realtime.strftime("%s.%f")) - return super(Reader, self).seek_realtime(realtime) + realtime = float(realtime.strftime("%s.%f")) * 1000000 + return super(Reader, self).seek_realtime(int(realtime)) def seek_monotonic(self, monotonic, bootid=None): """Seek to a matching journal entry nearest to `monotonic` time. @@ -229,6 +240,7 @@ class Reader(_Reader): """ if isinstance(monotonic, _datetime.timedelta): monotonic = monotonic.totalseconds() + monotonic = int(monotonic * 1000000) if isinstance(bootid, _uuid.UUID): bootid = bootid.get_hex() return super(Reader, self).seek_monotonic(monotonic, bootid) -- cgit v1.2.1 From 3507ff227bcf29d67b8f7b35d409619bdec7099e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 19:00:37 -0400 Subject: systemd-python: implement _Reader.test_cursor Getting the cursor is split out from .get_next() into .get_cursor(). This mirrors the C API more closely, and also makes things a bit faster if the cursor is not needed. --- systemd/_reader.c | 66 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 4fe7847..b74d2be 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -374,26 +374,6 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) goto error; } - { - PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; - char _cleanup_free_ *cursor = NULL; - - r = sd_journal_get_cursor(self->j, &cursor); - if (set_error(r, NULL, NULL)) - goto error; - - key = unicode_FromString("__CURSOR"); - if (!key) - goto error; - - value = PyBytes_FromString(cursor); - if (!value) - goto error; - - if (PyDict_SetItem(dict, key, value)) - goto error; - } - return dict; error: Py_DECREF(dict); @@ -602,6 +582,50 @@ 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)."); +static PyObject* Reader_get_cursor(Reader *self, PyObject *args) +{ + char _cleanup_free_ *cursor = NULL; + int r; + + assert(self); + assert(!args); + + r = sd_journal_get_cursor(self->j, &cursor); + if (set_error(r, NULL, NULL)) + return NULL; + + return unicode_FromString(cursor); +} + + +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)."); +static PyObject* Reader_test_cursor(Reader *self, PyObject *args) +{ + const char *cursor; + int r; + + assert(self); + assert(args); + + if (!PyArg_ParseTuple(args, "s:_Reader.get_cursor", &cursor)) + return NULL; + + r = sd_journal_test_cursor(self->j, cursor); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + + return PyBool_FromLong(r); +} + + static PyObject* Reader_iter(PyObject *self) { Py_INCREF(self); @@ -792,6 +816,8 @@ static PyMethodDef Reader_methods[] = { {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__}, {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, + {"get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, + {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__}, {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, {NULL} /* Sentinel */ -- cgit v1.2.1 From 530a0dcbc3e57e85c1e7596586b39edd5bd205bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 19:12:27 -0400 Subject: systemd-python: split out realtime and monotonic into separate functions This matches the C API more closely, and also enables the user to get just partial information, should she desire to do so. Functions names in error messages are modified to not include the class name, because Python uses just the function name into functions declared as METH_NOARGS, and error messages were inconsistent. --- systemd/_reader.c | 164 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 99 insertions(+), 65 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index b74d2be..c128cf3 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -83,8 +83,8 @@ static void Reader_dealloc(Reader* self) } PyDoc_STRVAR(Reader__doc__, - "Reader([flags | path]) -> ...\n\n" - "Reader allows filtering and retrieval of Journal entries.\n" + "_Reader([flags | path]) -> ...\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" "Argument `flags` sets open flags of the journal, which can be one\n" @@ -93,7 +93,9 @@ PyDoc_STRVAR(Reader__doc__, "volatile journal files; and SYSTEM_ONLY opens only\n" "journal files of system services and the kernel.\n\n" "Argument `path` is the directory of journal files. Note that\n" - "`flags` and `path` are exclusive.\n"); + "`flags` and `path` are exclusive.\n\n" + "_Reader implements the context manager protocol: the journal\n" + "will be closed when exiting the block."); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { int flags = 0, r; @@ -221,19 +223,17 @@ static PyObject* Reader___exit__(Reader *self, PyObject *args) } -PyDoc_STRVAR(Reader_get_next__doc__, - "get_next([skip]) -> dict\n\n" - "Return dictionary of the next log entry. Optional skip value will\n" - "return the `skip`\\-th log entry."); -static PyObject* Reader_get_next(Reader *self, PyObject *args) +PyDoc_STRVAR(Reader_next__doc__, + "next([skip]) -> bool\n\n" + "Go to the next log entry. Optional skip value means to go to\n" + "the `skip`\\-th log entry.\n" + "Returns False if at end of file, True otherwise."); +static PyObject* Reader_next(Reader *self, PyObject *args) { - PyObject *dict; - const void *msg; - size_t msg_len; int64_t skip = 1LL; int r; - if (!PyArg_ParseTuple(args, "|L:_Reader.get_next", &skip)) + if (!PyArg_ParseTuple(args, "|L:next", &skip)) return NULL; if (skip == 0LL) { @@ -257,7 +257,26 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) set_error(r, NULL, NULL); if (r < 0) return NULL; - else if (r == 0) /* EOF */ + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(Reader_get_next__doc__, + "get_next([skip]) -> dict\n\n" + "Return dictionary of the next log entry. Optional skip value will\n" + "return the `skip`\\-th log entry. Returns an empty dict on EOF."); +static PyObject* Reader_get_next(Reader *self, PyObject *args) +{ + PyObject _cleanup_Py_DECREF_ *tmp = NULL; + PyObject *dict; + const void *msg; + size_t msg_len; + int r; + + tmp = Reader_next(self, args); + if (!tmp) + return NULL; + if (tmp == Py_False) /* EOF */ return PyDict_New(); dict = PyDict_New(); @@ -316,68 +335,80 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) } } - { - PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; - uint64_t realtime; + return dict; +error: + Py_DECREF(dict); + return NULL; +} - r = sd_journal_get_realtime_usec(self->j, &realtime); - if (set_error(r, NULL, NULL)) - goto error; - key = unicode_FromString("__REALTIME_TIMESTAMP"); - if (!key) - goto error; +PyDoc_STRVAR(Reader_get_realtime__doc__, + "get_realtime() -> int\n\n" + "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)."); +static PyObject* Reader_get_realtime(Reader *self, PyObject *args) +{ + uint64_t timestamp; + int r; - assert_cc(sizeof(unsigned long long) == sizeof(realtime)); - value = PyLong_FromUnsignedLongLong(realtime); - if (!value) - goto error; + assert(self); + assert(!args); - if (PyDict_SetItem(dict, key, value)) - goto error; - } + r = sd_journal_get_realtime_usec(self->j, ×tamp); + if (set_error(r, NULL, NULL)) + return NULL; - { - PyObject _cleanup_Py_DECREF_ - *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL; - sd_id128_t id; - uint64_t monotonic; + assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); + return PyLong_FromUnsignedLongLong(timestamp); +} - r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id); - if (set_error(r, NULL, NULL)) - goto error; - assert_cc(sizeof(unsigned long long) == sizeof(monotonic)); - key = unicode_FromString("__MONOTONIC_TIMESTAMP"); - timestamp = PyLong_FromUnsignedLongLong(monotonic); - bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); +PyDoc_STRVAR(Reader_get_monotonic__doc__, + "get_monotonic() -> (timestamp, bootid)\n\n" + "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)."); +static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) +{ + uint64_t timestamp; + sd_id128_t id; + PyObject *monotonic, *bootid, *tuple; + int r; + + assert(self); + assert(!args); + + r = sd_journal_get_monotonic_usec(self->j, ×tamp, &id); + if (set_error(r, NULL, NULL)) + return NULL; + + assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); + monotonic = PyLong_FromUnsignedLongLong(timestamp); + bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); #if PY_MAJOR_VERSION >= 3 - value = PyStructSequence_New(&MonotonicType); + tuple = PyStructSequence_New(&MonotonicType); #else - value = PyTuple_New(2); + tuple = PyTuple_New(2); #endif - if (!key || !timestamp || !bytes || !value) - goto error; - - Py_INCREF(timestamp); - Py_INCREF(bytes); + if (!monotonic || !bootid || !tuple) { + Py_XDECREF(monotonic); + Py_XDECREF(bootid); + Py_XDECREF(tuple); + return NULL; + } #if PY_MAJOR_VERSION >= 3 - PyStructSequence_SET_ITEM(value, 0, timestamp); - PyStructSequence_SET_ITEM(value, 1, bytes); + PyStructSequence_SET_ITEM(tuple, 0, monotonic); + PyStructSequence_SET_ITEM(tuple, 1, bootid); #else - PyTuple_SET_ITEM(value, 0, timestamp); - PyTuple_SET_ITEM(value, 1, bytes); + PyTuple_SET_ITEM(tuple, 0, monotonic); + PyTuple_SET_ITEM(tuple, 1, bootid); #endif - if (PyDict_SetItem(dict, key, value)) - goto error; - } - - return dict; -error: - Py_DECREF(dict); - return NULL; + return tuple; } @@ -388,7 +419,7 @@ PyDoc_STRVAR(Reader_get_previous__doc__, static PyObject* Reader_get_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip)) + if (!PyArg_ParseTuple(args, "|L:get_previous", &skip)) return NULL; return PyObject_CallMethod((PyObject *)self, (char*) "get_next", @@ -406,7 +437,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds { char *match; int match_len, r; - if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len)) + if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) return NULL; r = sd_journal_add_match(self->j, match, match_len); @@ -570,7 +601,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) const char *cursor; int r; - if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor)) + if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -614,7 +645,7 @@ static PyObject* Reader_test_cursor(Reader *self, PyObject *args) assert(self); assert(args); - if (!PyArg_ParseTuple(args, "s:_Reader.get_cursor", &cursor)) + if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor)) return NULL; r = sd_journal_test_cursor(self->j, cursor); @@ -663,7 +694,7 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) size_t uniq_len; PyObject *value_set, *key, *value; - if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query)) + if (!PyArg_ParseTuple(args, "s:query_unique", &query)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -805,8 +836,11 @@ static PyMethodDef Reader_methods[] = { {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, + {"next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, + {"get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, + {"get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, -- cgit v1.2.1 From 0c9613000ae845cdb61008f819b641ffa454aa5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 20 Mar 2013 23:01:32 -0400 Subject: systemd-python: allow retrieval of single fields This can give huge efficiency gains, e.g. if only MESSAGE is required and all other fields can be ignored. --- systemd/_reader.c | 109 +++++++++++++++++++++++++++++++++++++++-------- systemd/docs/journal.rst | 3 ++ systemd/journal.py | 10 ++++- 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index c128cf3..d1188a1 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -261,6 +261,73 @@ static PyObject* Reader_next(Reader *self, PyObject *args) } +static int extract(const char* msg, size_t msg_len, + PyObject **key, PyObject **value) { + PyObject *k = NULL, *v; + const char *delim_ptr; + + delim_ptr = memchr(msg, '=', msg_len); + if (!delim_ptr) { + PyErr_SetString(PyExc_OSError, + "journal gave us a field without '='"); + return -1; + } + + if (key) { + k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); + if (!k) + return -1; + } + + if (value) { + v = PyBytes_FromStringAndSize(delim_ptr + 1, + (const char*) msg + msg_len - (delim_ptr + 1)); + if (!v) { + Py_XDECREF(k); + return -1; + } + + *value = v; + } + + if (key) + *key = k; + + return 0; +} + +PyDoc_STRVAR(Reader_get__doc__, + "get(str) -> str\n\n" + "Return data associated with this key in current log entry.\n" + "Throws KeyError is the data is not available."); +static PyObject* Reader_get(Reader *self, PyObject *args) +{ + const char* field; + const void* msg; + size_t msg_len; + PyObject *value; + int r; + + assert(self); + assert(args); + + if (!PyArg_ParseTuple(args, "s:get", &field)) + return NULL; + + r = sd_journal_get_data(self->j, field, &msg, &msg_len); + if (r == -ENOENT) { + PyErr_SetString(PyExc_KeyError, field); + return NULL; + } else if (set_error(r, NULL, "field name is not valid")) + return NULL; + + r = extract(msg, msg_len, NULL, &value); + if (r < 0) + return NULL; + return value; +} + + PyDoc_STRVAR(Reader_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" @@ -285,23 +352,9 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; - const char *delim_ptr; - - delim_ptr = memchr(msg, '=', msg_len); - if (!delim_ptr) { - PyErr_SetString(PyExc_OSError, - "journal gave us a field without '='"); - goto error; - } - - key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); - if (!key) - goto error; - value = PyBytes_FromStringAndSize( - delim_ptr + 1, - (const char*) msg + msg_len - (delim_ptr + 1) ); - if (!value) + r = extract(msg, msg_len, &key, &value); + if (r < 0) goto error; if (PyDict_Contains(dict, key)) { @@ -336,6 +389,7 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) } return dict; + error: Py_DECREF(dict); return NULL; @@ -724,6 +778,9 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_get_catalog__doc__, "get_catalog() -> str\n\n" "Retrieve a message catalog entry for the current journal entry.\n" + "Will throw IndexError if the entry has no MESSAGE_ID\n" + "and KeyError is the id is specified, but hasn't been found\n" + "in the catalog.\n\n" "Wraps man:sd_journal_get_catalog(3)."); static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { @@ -736,7 +793,22 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) Py_BEGIN_ALLOW_THREADS r = sd_journal_get_catalog(self->j, &msg); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (r == -ENOENT) { + const void* mid; + size_t mid_len; + + r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len); + if (r == 0) { + const int l = sizeof("MESSAGE_ID"); + assert(mid_len > l); + PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l, + (const char*) mid + l); + } else if (r == -ENOENT) + PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field"); + else + set_error(r, NULL, NULL); + return NULL; + } else if (set_error(r, NULL, NULL)) return NULL; return unicode_FromString(msg); @@ -837,6 +909,7 @@ static PyMethodDef Reader_methods[] = { {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, {"next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, + {"get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, {"get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, @@ -899,7 +972,7 @@ static PyTypeObject ReaderType = { }; static PyMethodDef methods[] = { - { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, + { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, { NULL, NULL, 0, NULL } /* Sentinel */ }; diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 9dc495f..08756b9 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -23,6 +23,9 @@ Accessing the Journal .. automethod:: __init__ +.. autofunction:: _get_catalog +.. autofunction:: get_catalog + .. autoclass:: Monotonic .. autoattribute:: systemd.journal.DEFAULT_CONVERTERS diff --git a/systemd/journal.py b/systemd/journal.py index a522aec..c918c43 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -35,7 +35,7 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, from ._journal import sendv, stream_fd from ._reader import (_Reader, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY, - get_catalog) + _get_catalog) from . import id128 as _id128 if _sys.version_info >= (3,): @@ -137,6 +137,9 @@ class Reader(_Reader): the conversion fails with a ValueError, unconverted bytes object will be returned. (Note that ValueEror is a superclass of UnicodeDecodeError). + + Reader implements the context manager protocol: the journal + will be closed when exiting the block. """ super(Reader, self).__init__(flags, path) if _sys.version_info >= (3,3): @@ -293,6 +296,11 @@ class Reader(_Reader): self.add_match(_MACHINE_ID=machineid) +def get_catalog(mid): + if isinstance(mid, _uuid.UUID): + mid = mid.get_hex() + return _get_catalog(mid) + def _make_line(field, value): if isinstance(value, bytes): return field.encode('utf-8') + b'=' + value -- cgit v1.2.1 From 06e0c9012f1cc4552fcbd9fe484361974f9576a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 24 Mar 2013 19:59:00 -0400 Subject: Use initalization instead of explicit zeroing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before, we would initialize many fields twice: first by filling the structure with zeros, and then a second time with the real values. We can let the compiler do the job for us, avoiding one copy. A downside of this patch is that text gets slightly bigger. This is because all zero() calls are effectively inlined: $ size build/.libs/systemd text data bss dec hex filename before 897737 107300 2560 1007597 f5fed build/.libs/systemd after 897873 107300 2560 1007733 f6075 build/.libs/systemd … actually less than 1‰. A few asserts that the parameter is not null had to be removed. I don't think this changes much, because first, it is quite unlikely for the assert to fail, and second, an immediate SEGV is almost as good as an assert. --- systemd/_reader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index d1188a1..a49527f 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -64,7 +64,7 @@ PyDoc_STRVAR(MonotonicType__doc__, static PyStructSequence_Field MonotonicType_fields[] = { {(char*) "timestamp", (char*) "Time"}, {(char*) "bootid", (char*) "Unique identifier of the boot"}, - {NULL, NULL} + {} /* Sentinel */ }; static PyStructSequence_Desc Monotonic_desc = { @@ -898,7 +898,7 @@ static PyGetSetDef Reader_getsetters[] = { NULL, (char*) closed__doc__, NULL}, - {NULL} + {} /* Sentinel */ }; static PyMethodDef Reader_methods[] = { @@ -927,7 +927,7 @@ static PyMethodDef Reader_methods[] = { {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__}, {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, - {NULL} /* Sentinel */ + {} /* Sentinel */ }; static PyTypeObject ReaderType = { -- cgit v1.2.1 From d9dc73a9235af51b7ca703737d8059c2f6340f80 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sat, 6 Apr 2013 18:46:44 -0400 Subject: systemd-python: fix wait bug --- systemd/journal.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index c918c43..48f57ac 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -23,7 +23,6 @@ from __future__ import division import sys as _sys import datetime as _datetime -import functools as _functools import uuid as _uuid import traceback as _traceback import os as _os @@ -221,7 +220,7 @@ class Reader(_Reader): have been added or removed). """ us = -1 if timeout is None else int(timeout * 1000000) - return super(Reader, self).wait(timeout) + return super(Reader, self).wait(us) def seek_realtime(self, realtime): """Seek to a matching journal entry nearest to `realtime` time. -- cgit v1.2.1 From f805090312790c4104ddb712acd5bbe128ceade9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 14 Apr 2013 18:37:38 -0400 Subject: systemd-python: fix formatting in docstring --- systemd/_reader.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index a49527f..3b36634 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -175,10 +175,10 @@ static PyObject* Reader_close(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_get_usage__doc__, "get_usage() -> int\n\n" - "Returns the total disk space currently used by journal" - "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was" - "passed when opening the journal this value will only reflect" - "the size of journal files of the local host, otherwise" + "Returns the total disk space currently used by journal\n" + "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n" + "passed when opening the journal this value will only reflect\n" + "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)."); @@ -628,7 +628,8 @@ PyDoc_STRVAR(Reader_wait__doc__, "then block forever.\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."); + "INVALIDATE if journal files have been added or removed.\n\n" + "See man:sd_journal_wait(3) for further discussion."); static PyObject* Reader_wait(Reader *self, PyObject *args) { int r; -- cgit v1.2.1 From 81d7790578f22673e0567a6a7cd37408248f7d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 14 Apr 2013 18:37:03 -0400 Subject: systemd-python: export new sd_journal_{process,get_events,get_timeout} get_timeout_ms is added as a convenience function, since it is abysmally hard to call clock_gettime() in Python versions lower than 3.3. And even for Python 3.3 users it saves a few lines. --- systemd/_reader.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 3b36634..fd423b3 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -132,8 +133,7 @@ PyDoc_STRVAR(Reader_fileno__doc__, "See man:sd_journal_get_fd(3)."); static PyObject* Reader_fileno(Reader *self, PyObject *args) { - int fd; - fd = sd_journal_get_fd(self->j); + int fd = sd_journal_get_fd(self->j); set_error(fd, NULL, NULL); if (fd < 0) return NULL; @@ -148,8 +148,7 @@ PyDoc_STRVAR(Reader_reliable_fd__doc__, "See man:sd_journal_reliable_fd(3)."); static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) { - int r; - r = sd_journal_reliable_fd(self->j); + int r = sd_journal_reliable_fd(self->j); set_error(r, NULL, NULL); if (r < 0) return NULL; @@ -157,6 +156,78 @@ static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) } +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."); +static PyObject* Reader_get_events(Reader *self, PyObject *args) +{ + int r = sd_journal_get_events(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + return long_FromLong(r); +} + + +PyDoc_STRVAR(Reader_get_timeout__doc__, + "get_timeout() -> int or None\n\n" + "Returns a timeout value for usage in poll(), the time since the\n" + "epoch of clock_gettime(2) in microseconds, or None if no timeout\n" + "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."); +static PyObject* Reader_get_timeout(Reader *self, PyObject *args) +{ + int r; + uint64_t t; + + r = sd_journal_get_timeout(self->j, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + + if (t == (uint64_t) -1) + Py_RETURN_NONE; + + assert_cc(sizeof(unsigned long long) == sizeof(t)); + return PyLong_FromUnsignedLongLong(t); +} + + +PyDoc_STRVAR(Reader_get_timeout_ms__doc__, + "get_timeout_ms() -> int\n\n" + "Returns a timeout value suitable for usage in poll(), the value\n" + "returned by .get_timeout() converted to relative ms, or -1 if\n" + "no timeout is necessary."); +static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) +{ + int r; + uint64_t t; + + r = sd_journal_get_timeout(self->j, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + + if (t == (uint64_t) -1) + return PyLong_FromLong(-1); + else { + struct timespec ts; + uint64_t n; + int msec; + + clock_gettime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; + + return PyLong_FromLong(msec); + } +} + + PyDoc_STRVAR(Reader_close__doc__, "close() -> None\n\n" "Free resources allocated by this Reader object.\n" @@ -620,6 +691,30 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) } +PyDoc_STRVAR(Reader_process__doc__, + "process() -> state change (integer)\n\n" + "Process events and reset the readable 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."); +static PyObject* Reader_process(Reader *self, PyObject *args) +{ + int r; + + assert(!args); + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_process(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL) < 0) + return NULL; + + return long_FromLong(r); +} + + PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" @@ -905,6 +1000,9 @@ static PyGetSetDef Reader_getsetters[] = { static PyMethodDef Reader_methods[] = { {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, + {"get_events", (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__}, + {"get_timeout", (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__}, + {"get_timeout_ms", (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__}, {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, @@ -922,6 +1020,7 @@ 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__}, + {"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__}, {"get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, -- cgit v1.2.1 From 6238295ba705b0e2aba4326d33527c762328f79e Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Sun, 14 Apr 2013 20:55:08 +0100 Subject: python-systemd: Reader return special fields and _Reader changes Changes to _Reader make it match closer to C API, by removing `get_next` and `get_previous`. A `get_all` method added, which returns dictionary of fields using C API SD_JOURNAL_FOREACH_DATA macro, which can be used in conjunction with `next`. _Reader `get`, `next`, `get_{realtime,monotonic,cursor}` and new `previous` methods are made private. This is so the traversal and getting of journal fields can be made transparent in the python interface. Reader now solely implements `get_next` and `get_previous`, returning a standard dictionary (future: other mapping types?) with all standard and special fields through the converters. This makes the output the same as journalctl json/export format output. Iterator methods also moved to Reader, as they do not function as intend with changes to _Reader. These changes also mean that more optimised journal interfaces can be made more easily from _Reader, by avoiding getting of unrequired fields by using the `_get` method, and avoiding field conversions. --- systemd/_reader.c | 91 +++++++++++++++++------------------------------------- systemd/journal.py | 45 +++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 66 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index fd423b3..14f8a40 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -331,6 +331,21 @@ static PyObject* Reader_next(Reader *self, PyObject *args) return PyBool_FromLong(r); } +PyDoc_STRVAR(Reader_previous__doc__, + "previous([skip]) -> bool\n\n" + "Go to the previous log entry. Optional skip value means to \n" + "go to the `skip`\\-th previous log entry.\n" + "Returns False if at start of file, True otherwise."); +static PyObject* Reader_previous(Reader *self, PyObject *args) +{ + int64_t skip = 1LL; + if (!PyArg_ParseTuple(args, "|L:previous", &skip)) + return NULL; + + return PyObject_CallMethod((PyObject *)self, (char*) "_next", + (char*) "L", -skip); +} + static int extract(const char* msg, size_t msg_len, PyObject **key, PyObject **value) { @@ -399,24 +414,16 @@ static PyObject* Reader_get(Reader *self, PyObject *args) } -PyDoc_STRVAR(Reader_get_next__doc__, - "get_next([skip]) -> dict\n\n" - "Return dictionary of the next log entry. Optional skip value will\n" - "return the `skip`\\-th log entry. Returns an empty dict on EOF."); -static PyObject* Reader_get_next(Reader *self, PyObject *args) +PyDoc_STRVAR(Reader_get_all__doc__, + "_get_all() -> dict\n\n" + "Return dictionary of the current log entry."); +static PyObject* Reader_get_all(Reader *self, PyObject *args) { - PyObject _cleanup_Py_DECREF_ *tmp = NULL; PyObject *dict; const void *msg; size_t msg_len; int r; - tmp = Reader_next(self, args); - if (!tmp) - return NULL; - if (tmp == Py_False) /* EOF */ - return PyDict_New(); - dict = PyDict_New(); if (!dict) return NULL; @@ -536,22 +543,6 @@ static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) return tuple; } - -PyDoc_STRVAR(Reader_get_previous__doc__, - "get_previous([skip]) -> dict\n\n" - "Return dictionary of the previous log entry. Optional skip value\n" - "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip)."); -static PyObject* Reader_get_previous(Reader *self, PyObject *args) -{ - int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L:get_previous", &skip)) - return NULL; - - return PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", -skip); -} - - PyDoc_STRVAR(Reader_add_match__doc__, "add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" @@ -806,32 +797,6 @@ static PyObject* Reader_test_cursor(Reader *self, PyObject *args) return PyBool_FromLong(r); } - -static PyObject* Reader_iter(PyObject *self) -{ - Py_INCREF(self); - return self; -} - -static PyObject* Reader_iternext(PyObject *self) -{ - PyObject *dict; - Py_ssize_t dict_size; - - dict = PyObject_CallMethod(self, (char*) "get_next", (char*) ""); - if (PyErr_Occurred()) - return NULL; - dict_size = PyDict_Size(dict); - if ((int64_t) dict_size > 0LL) { - return dict; - } else { - Py_DECREF(dict); - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } -} - - PyDoc_STRVAR(Reader_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Return a set of unique values appearing in journal for the\n" @@ -1007,12 +972,12 @@ static PyMethodDef Reader_methods[] = { {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, - {"next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, - {"get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, - {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, - {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, - {"get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, - {"get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, + {"_next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, + {"_previous", (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__}, + {"_get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, + {"_get_all", (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__}, + {"_get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, + {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, @@ -1023,7 +988,7 @@ static PyMethodDef Reader_methods[] = { {"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__}, - {"get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, + {"_get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__}, {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, @@ -1056,8 +1021,8 @@ static PyTypeObject ReaderType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - Reader_iter, /* tp_iter */ - Reader_iternext, /* tp_iternext */ + 0, /* tp_iter */ + 0, /* tp_iternext */ Reader_methods, /* tp_methods */ 0, /* tp_members */ Reader_getsetters, /* tp_getset */ diff --git a/systemd/journal.py b/systemd/journal.py index 48f57ac..6c740b0 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -176,6 +176,25 @@ class Reader(_Reader): result[key] = self._convert_field(key, value) return result + def __iter__(self): + """Part of iterator protocol. + Returns self. + """ + return self + + if _sys.version_info >= (3,): + def __next__(self): + """Part of iterator protocol. + Returns self.get_next(). + """ + return self.get_next() + else: + def next(self): + """Part of iterator protocol. + Returns self.get_next(). + """ + return self.get_next() + def add_match(self, *args, **kwargs): """Add one or more matches to the filter journal log entries. All matches of different field are combined in a logical AND, @@ -190,15 +209,35 @@ class Reader(_Reader): super(Reader, self).add_match(arg) def get_next(self, skip=1): - """Return the next log entry as a dictionary of fields. + """Return the next log entry as a mapping type, currently + a standard dictionary of fields. Optional skip value will return the `skip`\-th log entry. Entries will be processed with converters specified during Reader creation. """ - return self._convert_entry( - super(Reader, self).get_next(skip)) + if super(Reader, self)._next(skip): + entry = super(Reader, self)._get_all() + if entry: + entry['__REALTIME_TIMESTAMP'] = self._get_realtime() + entry['__MONOTONIC_TIMESTAMP'] = self._get_monotonic() + entry['__CURSOR'] = self._get_cursor() + return self._convert_entry(entry) + return dict() + + def get_previous(self, skip=1): + """Return the previous log entry as a mapping type, + currently a standard dictionary of fields. + + Optional skip value will return the -`skip`\-th log entry. + + Entries will be processed with converters specified during + Reader creation. + + Equivilent to get_next(-skip). + """ + return self.get_next(-skip) def query_unique(self, field): """Return unique values appearing in the journal for given `field`. -- cgit v1.2.1 From 0035bc19b18f0163f5d868debce185a912950cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 15 Apr 2013 22:25:58 -0400 Subject: Report about syntax errors with metadata The information about the unit for which files are being parsed is passed all the way down. This way messages land in the journal with proper UNIT=... or USER_UNIT=... attribution. 'systemctl status' and 'journalctl -u' not displaying those messages has been a source of confusion for users, since the journal entry for a misspelt setting was often logged quite a bit earlier than the failure to start a unit. Based-on-a-patch-by: Oleksii Shevchuk --- systemd/docs/id128.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/systemd/docs/id128.rst b/systemd/docs/id128.rst index 12c28f3..e43776a 100644 --- a/systemd/docs/id128.rst +++ b/systemd/docs/id128.rst @@ -36,3 +36,4 @@ .. 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_CONFIG_ERROR -- cgit v1.2.1 From 68db467eea78b2b2f02fd29641b3dfa3889fd60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 15 Apr 2013 22:42:27 -0400 Subject: sd-messages.h: add new bootchart message id --- systemd/docs/id128.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/systemd/docs/id128.rst b/systemd/docs/id128.rst index e43776a..89c37f3 100644 --- a/systemd/docs/id128.rst +++ b/systemd/docs/id128.rst @@ -37,3 +37,4 @@ .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPED .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPING .. autoattribute:: systemd.id128.SD_MESSAGE_CONFIG_ERROR + .. autoattribute:: systemd.id128.SD_MESSAGE_BOOTCHART -- cgit v1.2.1 From fdb41b9f7088ae4fcb1d5b87362c7a16ff51861f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 18 Apr 2013 09:11:22 +0200 Subject: move _cleanup_ attribute in front of the type http://lists.freedesktop.org/archives/systemd-devel/2013-April/010510.html --- systemd/_daemon.c | 2 +- systemd/_reader.c | 10 +++++----- systemd/id128.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index 8f93d91..ce20649 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -244,7 +244,7 @@ static PyObject* is_socket_unix(PyObject *self, PyObject *args) { Py_ssize_t length = 0; #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 - PyObject _cleanup_Py_DECREF_ *_path = NULL; + _cleanup_Py_DECREF_ PyObject *_path = NULL; if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix", &fd, &type, &listening, Unicode_FSConverter, &_path)) return NULL; diff --git a/systemd/_reader.c b/systemd/_reader.c index 14f8a40..05993b3 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -429,7 +429,7 @@ static PyObject* Reader_get_all(Reader *self, PyObject *args) return NULL; SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { - PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL; + _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL; r = extract(msg, msg_len, &key, &value); if (r < 0) @@ -443,7 +443,7 @@ static PyObject* Reader_get_all(Reader *self, PyObject *args) if (r < 0) goto error; } else { - PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0); + _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0); if (!tmp_list) goto error; @@ -760,7 +760,7 @@ PyDoc_STRVAR(Reader_get_cursor__doc__, "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3)."); static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { - char _cleanup_free_ *cursor = NULL; + _cleanup_free_ char *cursor = NULL; int r; assert(self); @@ -846,7 +846,7 @@ PyDoc_STRVAR(Reader_get_catalog__doc__, static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { int r; - char _cleanup_free_ *msg = NULL; + _cleanup_free_ char *msg = NULL; assert(self); assert(!args); @@ -885,7 +885,7 @@ static PyObject* get_catalog(PyObject *self, PyObject *args) int r; char *id_ = NULL; sd_id128_t id; - char _cleanup_free_ *msg = NULL; + _cleanup_free_ char *msg = NULL; assert(!self); assert(args); diff --git a/systemd/id128.c b/systemd/id128.c index a9611c4..1c2fe5d 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -52,7 +52,7 @@ PyDoc_STRVAR(get_boot__doc__, ); static PyObject* make_uuid(sd_id128_t id) { - PyObject _cleanup_Py_DECREF_ + _cleanup_Py_DECREF_ PyObject *uuid = NULL, *UUID = NULL, *bytes = NULL, *args = NULL, *kwargs = NULL; -- cgit v1.2.1 From 756d44fc44e1c8f38c0bc32a6019bc1d2a61c73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 18 Apr 2013 19:37:26 -0400 Subject: systemd-python: wrap sd_journal_add_conjunction --- systemd/_reader.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 05993b3..b836597 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -567,7 +567,10 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() -> None\n\n" - "Inserts a logical OR between matches added before and afterwards."); + "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."); static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) { int r; @@ -579,6 +582,23 @@ static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) } +PyDoc_STRVAR(Reader_add_conjunction__doc__, + "add_conjunction() -> None\n\n" + "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."); +static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) +{ + int r; + r = sd_journal_add_conjunction(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + Py_RETURN_NONE; +} + + PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); @@ -980,6 +1000,7 @@ static PyMethodDef Reader_methods[] = { {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, + {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__}, {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__}, {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__}, -- cgit v1.2.1 From dbf3584a15cabe33d3c40d3430faf9c9fcccfa21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 22 Apr 2013 22:07:18 -0400 Subject: systemd-python: attach fields to JournalHandler, add SYSLOG_IDENTIFIER Arbitrary fields can be attached at the level of the handler, and they'll be sent with all messages from this handler. This facility is used to attach SYSLOG_IDENTIFIER to all messages, since otherwise journald attaches SYSLOG_IDENTIFIER=python or something similar, which is completely useless. --- systemd/journal.py | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index 6c740b0..7d42525 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -27,7 +27,7 @@ import uuid as _uuid import traceback as _traceback import os as _os import logging as _logging -if _sys.version_info >= (3,): +if _sys.version_info >= (3,3): from collections import ChainMap as _ChainMap from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) @@ -96,6 +96,11 @@ DEFAULT_CONVERTERS = { 'COREDUMP_TIMESTAMP': _convert_timestamp, } +_IDENT_LETTER = set('ABCDEFGHIJKLMNOPQRTSUVWXYZ_') + +def _valid_field_name(s): + return not (set(s) - _IDENT_LETTER) + class Reader(_Reader): """Reader allows the access and filtering of systemd journal entries. Note that in order to access the system journal, a @@ -450,12 +455,6 @@ class JournalHandler(_logging.Handler): >>> log.setLevel(logging.DEBUG) - To attach journal MESSAGE_ID, an extra field is supported: - - >>> import uuid - >>> mid = uuid.UUID('0123456789ABCDEF0123456789ABCDEF') - >>> log.warn("Message with ID", extra={'MESSAGE_ID': mid}) - To redirect all logging messages to journal regardless of where they come from, attach it to the root logger: @@ -466,12 +465,36 @@ class JournalHandler(_logging.Handler): handler class. Only standard handler configuration options are supported: `level`, `formatter`, `filters`. + To attach journal MESSAGE_ID, an extra field is supported: + + >>> import uuid + >>> mid = uuid.UUID('0123456789ABCDEF0123456789ABCDEF') + >>> log.warn("Message with ID", extra={'MESSAGE_ID': mid}) + + Fields to be attached to all messages sent through this + handler can be specified as keyword arguments. This probably + makes sense only for SYSLOG_IDENTIFIER and similar fields + which are constant for the whole program: + + >>> journal.JournalHandler(SYSLOG_IDENTIFIER='my-cool-app') + The following journal fields will be sent: `MESSAGE`, `PRIORITY`, `THREAD_NAME`, `CODE_FILE`, `CODE_LINE`, `CODE_FUNC`, `LOGGER` (name as supplied to getLogger call), - `MESSAGE_ID` (optional, see above). + `MESSAGE_ID` (optional, see above), `SYSLOG_IDENTIFIER` (defaults + to sys.argv[0]). """ + def __init__(self, level=_logging.NOTSET, **kwargs): + super(JournalHandler, self).__init__(level) + + for name in kwargs: + if not _valid_field_name(name): + raise ValueError('Invalid field name: ' + name) + if 'SYSLOG_IDENTIFIER' not in kwargs: + kwargs['SYSLOG_IDENTIFIER'] = _sys.argv[0] + self._extra = kwargs + def emit(self, record): """Write record as journal event. @@ -492,7 +515,8 @@ class JournalHandler(_logging.Handler): THREAD_NAME=record.threadName, CODE_FILE=record.pathname, CODE_LINE=record.lineno, - CODE_FUNC=record.funcName) + CODE_FUNC=record.funcName, + **self._extra) except Exception: self.handleError(record) -- cgit v1.2.1 From cfcc1870673b08d67af2e6d2de94959fb5df8660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Fri, 3 May 2013 23:43:11 +0300 Subject: Spelling fixes --- systemd/journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/journal.py b/systemd/journal.py index 7d42525..9e40cbc 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -240,7 +240,7 @@ class Reader(_Reader): Entries will be processed with converters specified during Reader creation. - Equivilent to get_next(-skip). + Equivalent to get_next(-skip). """ return self.get_next(-skip) -- cgit v1.2.1 From 7b3e876ddef01e9f17ef56ea371074be2ee90139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 May 2013 21:08:14 -0400 Subject: systemd-python: add __version__ strings --- systemd/_daemon.c | 4 +++- systemd/_journal.c | 21 +++++++++++++++++++-- systemd/_reader.c | 4 +++- systemd/daemon.py | 3 ++- systemd/id128.c | 5 ++--- systemd/journal.py | 2 +- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index ce20649..d3b4807 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -291,6 +291,7 @@ PyMODINIT_FUNC init_daemon(void) { return; PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START); + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } #else @@ -310,7 +311,8 @@ PyMODINIT_FUNC PyInit__daemon(void) { if (m == NULL) return NULL; - if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START)) { + if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START) || + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { Py_DECREF(m); return NULL; } diff --git a/systemd/_journal.c b/systemd/_journal.c index 2de0d4f..f8e0b4f 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -119,7 +119,13 @@ static PyMethodDef methods[] = { #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_journal(void) { - (void) Py_InitModule("_journal", methods); + PyObject *m; + + m = Py_InitModule("_journal", methods); + if (m == NULL) + return; + + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } #else @@ -133,7 +139,18 @@ static struct PyModuleDef module = { }; PyMODINIT_FUNC PyInit__journal(void) { - return PyModule_Create(&module); + PyObject *m; + + m = PyModule_Create(&module); + if (m == NULL) + return NULL; + + if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { + Py_DECREF(m); + return NULL; + } + + return m; } #endif diff --git a/systemd/_reader.c b/systemd/_reader.c index b836597..50ad889 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -30,6 +30,7 @@ #include "pyutil.h" #include "macro.h" #include "util.h" +#include "build.h" typedef struct { PyObject_HEAD @@ -1126,7 +1127,8 @@ init_reader(void) PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) || PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) || - PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) { + PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) || + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { #if PY_MAJOR_VERSION >= 3 Py_DECREF(m); return NULL; diff --git a/systemd/daemon.py b/systemd/daemon.py index 4a02204..e2829d1 100644 --- a/systemd/daemon.py +++ b/systemd/daemon.py @@ -1,4 +1,5 @@ -from ._daemon import (booted, +from ._daemon import (__version__, + booted, _listen_fds, _is_fifo, _is_socket, diff --git a/systemd/id128.c b/systemd/id128.c index 1c2fe5d..ec1d9fb 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -19,8 +19,6 @@ along with systemd; If not, see . ***/ -#include - #include #include @@ -126,6 +124,7 @@ PyMODINIT_FUNC initid128(void) { #define JOINER ; #include "id128-constants.h" #undef JOINER + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } #else @@ -149,7 +148,7 @@ PyMODINIT_FUNC PyInit_id128(void) { #define JOINER || #include "id128-constants.h" #undef JOINER - false) { + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { Py_DECREF(m); return NULL; } diff --git a/systemd/journal.py b/systemd/journal.py index 9e40cbc..9ef1ede 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -31,7 +31,7 @@ if _sys.version_info >= (3,3): from collections import ChainMap as _ChainMap from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) -from ._journal import sendv, stream_fd +from ._journal import __version__, sendv, stream_fd from ._reader import (_Reader, NOP, APPEND, INVALIDATE, LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY, _get_catalog) -- cgit v1.2.1 From 466a9b7fe1a2eb91d6007c67eeead6484917e64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 8 May 2013 19:46:49 -0400 Subject: systemd-python: add wrappers for easy functions in sd-login sd_get_uids, sd_get_seats, sd_get_sessions, and sd_get_machine_names. --- systemd/_reader.c | 14 +--- systemd/docs/index.rst | 1 + systemd/docs/login.rst | 5 ++ systemd/login.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++ systemd/pyutil.c | 16 +++++ systemd/pyutil.h | 1 + 6 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 systemd/docs/login.rst create mode 100644 systemd/login.c diff --git a/systemd/_reader.c b/systemd/_reader.c index 50ad889..d20c58d 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -213,19 +213,7 @@ static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) if (r < 0) return NULL; - if (t == (uint64_t) -1) - return PyLong_FromLong(-1); - else { - struct timespec ts; - uint64_t n; - int msec; - - clock_gettime(CLOCK_MONOTONIC, &ts); - n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; - msec = t > n ? (int) ((t - n + 999) / 1000) : 0; - - return PyLong_FromLong(msec); - } + return absolute_timeout(t); } diff --git a/systemd/docs/index.rst b/systemd/docs/index.rst index 8a94d07..e78d966 100644 --- a/systemd/docs/index.rst +++ b/systemd/docs/index.rst @@ -14,6 +14,7 @@ Contents: journal id128 daemon + login Indices and tables ================== diff --git a/systemd/docs/login.rst b/systemd/docs/login.rst new file mode 100644 index 0000000..2cd9d8c --- /dev/null +++ b/systemd/docs/login.rst @@ -0,0 +1,5 @@ +`systemd.login` module +======================= + +.. automodule:: systemd.login + :members: diff --git a/systemd/login.c b/systemd/login.c new file mode 100644 index 0000000..1dbe5ac --- /dev/null +++ b/systemd/login.c @@ -0,0 +1,176 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with 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 "systemd/sd-login.h" +#include "pyutil.h" +#include "util.h" +#include "strv.h" + +PyDoc_STRVAR(module__doc__, + "Python interface to the libsystemd-login library." +); + +#define helper(name) \ +static PyObject* name(PyObject *self, PyObject *args) { \ + _cleanup_strv_free_ char **list = NULL; \ + int r; \ + PyObject *ans; \ + \ + assert(args == NULL); \ + \ + r = sd_get_##name(&list); \ + if (r < 0) { \ + errno = -r; \ + return PyErr_SetFromErrno(PyExc_IOError); \ + } \ + \ + ans = PyList_New(r); \ + if (!ans) \ + return NULL; \ + \ + for (r--; r >= 0; r--) { \ + PyObject *s = unicode_FromString(list[r]); \ + if (!s) { \ + Py_DECREF(ans); \ + return NULL; \ + } \ + \ + PyList_SetItem(ans, r, s); \ + } \ + \ + return ans; \ +} + +helper(seats) +helper(sessions) +helper(machine_names) +#undef helper + +static PyObject* uids(PyObject *self, PyObject *args) { + _cleanup_free_ uid_t *list = NULL; + int r; + PyObject *ans; + + assert(args == NULL); + + r = sd_get_uids(&list); + if (r < 0) { + errno = -r; + return PyErr_SetFromErrno(PyExc_IOError); + } + + ans = PyList_New(r); + if (!ans) + return NULL; + + for (r--; r >= 0; r--) { + PyObject *s = long_FromLong(list[r]); + if (!s) { + Py_DECREF(ans); + return NULL; + } + + PyList_SetItem(ans, r, s); + } + + return ans; +} + +PyDoc_STRVAR(seats__doc__, + "seats() -> list\n\n" + "Returns a list of currently available local seats.\n" + "Wraps sd_get_seats(3)." +); + +PyDoc_STRVAR(sessions__doc__, + "sessions() -> list\n\n" + "Returns a list of current login sessions.\n" + "Wraps sd_get_sessions(3)." +); + +PyDoc_STRVAR(machine_names__doc__, + "machine_names() -> list\n\n" + "Returns a list of currently running virtual machines\n" + "and containers on the system.\n" + "Wraps sd_get_machine_names(3)." +); + +PyDoc_STRVAR(uids__doc__, + "uids() -> list\n\n" + "Returns a list of uids of users who currently have login sessions.\n" + "Wraps sd_get_uids(3)." +); + +static PyMethodDef methods[] = { + { "seats", seats, METH_NOARGS, seats__doc__}, + { "sessions", sessions, METH_NOARGS, sessions__doc__}, + { "machine_names", machine_names, METH_NOARGS, machine_names__doc__}, + { "uids", uids, METH_NOARGS, uids__doc__}, + {} /* Sentinel */ +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC initlogin(void) { + PyObject *m; + + m = Py_InitModule3("login", methods, module__doc__); + if (m == NULL) + return; + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); +} +#else + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "login", /* name of module */ + module__doc__, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module */ + methods +}; + +PyMODINIT_FUNC PyInit_login(void) { + PyObject *m; + + m = PyModule_Create(&module); + if (m == NULL) + return NULL; + + if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { + Py_DECREF(m); + return NULL; + } + + return m; +} + +#endif + +#pragma GCC diagnostic pop diff --git a/systemd/pyutil.c b/systemd/pyutil.c index 79065a1..9510acd 100644 --- a/systemd/pyutil.c +++ b/systemd/pyutil.c @@ -28,3 +28,19 @@ void cleanup_Py_DECREFp(PyObject **p) { Py_DECREF(*p); } + +PyObject* absolute_timeout(uint64_t t) { + if (t == (uint64_t) -1) + return PyLong_FromLong(-1); + else { + struct timespec ts; + uint64_t n; + int msec; + + clock_gettime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; + + return PyLong_FromLong(msec); + } +} diff --git a/systemd/pyutil.h b/systemd/pyutil.h index 2163fda..5c7ea37 100644 --- a/systemd/pyutil.h +++ b/systemd/pyutil.h @@ -27,6 +27,7 @@ #endif void cleanup_Py_DECREFp(PyObject **p); +PyObject* absolute_timeout(uint64_t t); #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) -- cgit v1.2.1 From 3ac545a83dd6427c1ade27c6e8b2f54e820c9486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 9 May 2013 18:10:30 -0400 Subject: systemd-python: wrap sd_login_monitor --- systemd/_daemon.c | 49 ++++------- systemd/_reader.c | 22 +---- systemd/docs/journal.rst | 2 +- systemd/docs/login.rst | 23 ++++++ systemd/login.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++ systemd/pyutil.c | 14 ++++ systemd/pyutil.h | 1 + 7 files changed, 268 insertions(+), 52 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index d3b4807..c6b14d4 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -40,21 +40,6 @@ PyDoc_STRVAR(module__doc__, "running under systemd." ); -static PyObject* set_error(int r, const char* invalid_message) { - assert (r < 0); - - if (r == -EINVAL && invalid_message) - PyErr_SetString(PyExc_ValueError, invalid_message); - else if (r == -ENOMEM) - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - else { - errno = -r; - PyErr_SetFromErrno(PyExc_OSError); - } - - return NULL; -} - #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 static int Unicode_FSConverter(PyObject* obj, void *_result) { @@ -88,8 +73,8 @@ static PyObject* booted(PyObject *self, PyObject *args) { assert(args == NULL); r = sd_booted(); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, NULL, NULL)) + return NULL; return PyBool_FromLong(r); } @@ -120,8 +105,8 @@ static PyObject* listen_fds(PyObject *self, PyObject *args) { #endif r = sd_listen_fds(unset); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, NULL, NULL)) + return NULL; return long_FromLong(r); } @@ -148,8 +133,8 @@ static PyObject* is_fifo(PyObject *self, PyObject *args) { #endif r = sd_is_fifo(fd, path); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, path, NULL)) + return NULL; return PyBool_FromLong(r); } @@ -176,8 +161,8 @@ static PyObject* is_mq(PyObject *self, PyObject *args) { #endif r = sd_is_mq(fd, path); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, path, NULL)) + return NULL; return PyBool_FromLong(r); } @@ -200,8 +185,8 @@ static PyObject* is_socket(PyObject *self, PyObject *args) { return NULL; r = sd_is_socket(fd, family, type, listening); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, NULL, NULL)) + return NULL; return PyBool_FromLong(r); } @@ -221,12 +206,14 @@ static PyObject* is_socket_inet(PyObject *self, PyObject *args) { &fd, &family, &type, &listening, &port)) return NULL; - if (port < 0 || port > INT16_MAX) - return set_error(-EINVAL, "port must fit into uint16_t"); + if (port < 0 || port > INT16_MAX) { + set_error(-EINVAL, NULL, "port must fit into uint16_t"); + return NULL; + } r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, NULL, NULL)) + return NULL; return PyBool_FromLong(r); } @@ -260,8 +247,8 @@ static PyObject* is_socket_unix(PyObject *self, PyObject *args) { #endif r = sd_is_socket_unix(fd, type, listening, path, length); - if (r < 0) - return set_error(r, NULL); + if (set_error(r, path, NULL)) + return NULL; return PyBool_FromLong(r); } diff --git a/systemd/_reader.c b/systemd/_reader.c index d20c58d..c4c4fdf 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -38,20 +38,6 @@ typedef struct { } Reader; static PyTypeObject ReaderType; -static int set_error(int r, const char* path, const char* invalid_message) { - if (r >= 0) - return r; - if (r == -EINVAL && invalid_message) - PyErr_SetString(PyExc_ValueError, invalid_message); - else if (r == -ENOMEM) - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - else { - errno = -r; - PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); - } - return -1; -} - PyDoc_STRVAR(module__doc__, "Class to reads the systemd journal similar to journalctl."); @@ -177,7 +163,7 @@ PyDoc_STRVAR(Reader_get_timeout__doc__, "Returns a timeout value for usage in poll(), the time since the\n" "epoch of clock_gettime(2) in microseconds, or None if no timeout\n" "is necessary.\n\n" - "The return value must be converted to a relative timeout in \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."); static PyObject* Reader_get_timeout(Reader *self, PyObject *args) @@ -275,11 +261,7 @@ PyDoc_STRVAR(Reader___exit____doc__, "Closes the journal.\n"); static PyObject* Reader___exit__(Reader *self, PyObject *args) { - assert(self); - - sd_journal_close(self->j); - self->j = NULL; - Py_RETURN_NONE; + return Reader_close(self, args); } diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index 08756b9..e6c4206 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -42,7 +42,7 @@ event loop: >>> j = journal.Reader() >>> j.seek_tail() >>> p = select.poll() - >>> p.register(j, select.POLLIN) + >>> p.register(j, j.get_events()) >>> p.poll() [(3, 1)] >>> j.get_next() diff --git a/systemd/docs/login.rst b/systemd/docs/login.rst index 2cd9d8c..6b4de64 100644 --- a/systemd/docs/login.rst +++ b/systemd/docs/login.rst @@ -3,3 +3,26 @@ .. automodule:: systemd.login :members: + +.. autoclass:: Monitor + :undoc-members: + :inherited-members: + +Example: polling for events +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example shows that session/uid/seat/machine events can be waited +for (using e.g. `poll`). This makes it easy to integrate Monitor in an +external event loop: + + >>> import select + >>> from systemd import login + >>> m = login.Monitor("machine") + >>> p = select.poll() + >>> p.register(m, m.get_events()) + >>> login.machine_names() + [] + >>> p.poll() + [(3, 1)] + >>> login.machine_names() + ['fedora-19.nspawn'] diff --git a/systemd/login.c b/systemd/login.c index 1dbe5ac..57dc080 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -133,6 +133,198 @@ static PyMethodDef methods[] = { {} /* Sentinel */ }; + +typedef struct { + PyObject_HEAD + sd_login_monitor *monitor; +} Monitor; +static PyTypeObject MonitorType; + +static void Monitor_dealloc(Monitor* self) +{ + sd_login_monitor_unref(self->monitor); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyDoc_STRVAR(Monitor__doc__, + "Monitor([category]) -> ...\n\n" + "Monitor may be used to monitor login sessions, users, seats,\n" + "and virtual machines/containers. Monitor provides a file\n" + "descriptor which can be integrated in an external event loop.\n" + "See man:sd_login_monitor_new(3) for the details about what\n" + "can be monitored."); +static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) +{ + const char *category = NULL; + int r; + + static const char* const kwlist[] = {"category", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|z", (char**) kwlist, + &category)) + return -1; + + Py_BEGIN_ALLOW_THREADS + r = sd_login_monitor_new(category, &self->monitor); + Py_END_ALLOW_THREADS + + return set_error(r, NULL, "Invalid category"); +} + + +PyDoc_STRVAR(Monitor_fileno__doc__, + "fileno() -> int\n\n" + "Get a file descriptor to poll for events.\n" + "This method wraps sd_login_monitor_get_fd(3)."); +static PyObject* Monitor_fileno(Monitor *self, PyObject *args) +{ + int fd = sd_login_monitor_get_fd(self->monitor); + set_error(fd, NULL, NULL); + if (fd < 0) + return NULL; + return long_FromLong(fd); +} + + +PyDoc_STRVAR(Monitor_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_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); + if (r < 0) + return NULL; + return long_FromLong(r); +} + + +PyDoc_STRVAR(Monitor_get_timeout__doc__, + "get_timeout() -> int or None\n\n" + "Returns a timeout value for usage in poll(), the time since the\n" + "epoch of clock_gettime(2) in microseconds, or None if no timeout\n" + "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."); +static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) +{ + int r; + uint64_t t; + + r = sd_login_monitor_get_timeout(self->monitor, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + + if (t == (uint64_t) -1) + Py_RETURN_NONE; + + assert_cc(sizeof(unsigned long long) == sizeof(t)); + return PyLong_FromUnsignedLongLong(t); +} + + +PyDoc_STRVAR(Monitor_get_timeout_ms__doc__, + "get_timeout_ms() -> int\n\n" + "Returns a timeout value suitable for usage in poll(), the value\n" + "returned by .get_timeout() converted to relative ms, or -1 if\n" + "no timeout is necessary."); +static PyObject* Monitor_get_timeout_ms(Monitor *self, PyObject *args) +{ + int r; + uint64_t t; + + r = sd_login_monitor_get_timeout(self->monitor, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + + return absolute_timeout(t); +} + + +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)."); +static PyObject* Monitor_close(Monitor *self, PyObject *args) +{ + assert(self); + assert(!args); + + sd_login_monitor_unref(self->monitor); + self->monitor = NULL; + Py_RETURN_NONE; +} + + +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)."); +static PyObject* Monitor_flush(Monitor *self, PyObject *args) +{ + assert(self); + assert(!args); + + sd_login_monitor_flush(self->monitor); + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Monitor___enter____doc__, + "__enter__() -> self\n\n" + "Part of the context manager protocol.\n" + "Returns self.\n"); +static PyObject* Monitor___enter__(PyObject *self, PyObject *args) +{ + assert(self); + assert(!args); + + Py_INCREF(self); + return self; +} + + +PyDoc_STRVAR(Monitor___exit____doc__, + "__exit__(type, value, traceback) -> None\n\n" + "Part of the context manager protocol.\n" + "Closes the monitor..\n"); +static PyObject* Monitor___exit__(Monitor *self, PyObject *args) +{ + return Monitor_close(self, args); +} + + +static PyMethodDef Monitor_methods[] = { + {"fileno", (PyCFunction) Monitor_fileno, METH_NOARGS, Monitor_fileno__doc__}, + {"get_events", (PyCFunction) Monitor_get_events, METH_NOARGS, Monitor_get_events__doc__}, + {"get_timeout", (PyCFunction) Monitor_get_timeout, METH_NOARGS, Monitor_get_timeout__doc__}, + {"get_timeout_ms", (PyCFunction) Monitor_get_timeout_ms, METH_NOARGS, Monitor_get_timeout_ms__doc__}, + {"close", (PyCFunction) Monitor_close, METH_NOARGS, Monitor_close__doc__}, + {"flush", (PyCFunction) Monitor_flush, METH_NOARGS, Monitor_flush__doc__}, + {"__enter__", (PyCFunction) Monitor___enter__, METH_NOARGS, Monitor___enter____doc__}, + {"__exit__", (PyCFunction) Monitor___exit__, METH_VARARGS, Monitor___exit____doc__}, + {} /* Sentinel */ +}; + +static PyTypeObject MonitorType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "login.Monitor", + .tp_basicsize = sizeof(Monitor), + .tp_dealloc = (destructor) Monitor_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Monitor__doc__, + .tp_methods = Monitor_methods, + .tp_init = (initproc) Monitor_init, + .tp_new = PyType_GenericNew, +}; + + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -141,10 +333,17 @@ static PyMethodDef methods[] = { PyMODINIT_FUNC initlogin(void) { PyObject *m; + if (PyType_Ready(&MonitorType) < 0) + return; + m = Py_InitModule3("login", methods, module__doc__); if (m == NULL) return; + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); + + Py_INCREF(&MonitorType); + PyModule_AddObject(m, "Monitor", (PyObject *) &MonitorType); } #else @@ -159,6 +358,9 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_login(void) { PyObject *m; + if (PyType_Ready(&MonitorType) < 0) + return NULL; + m = PyModule_Create(&module); if (m == NULL) return NULL; @@ -168,6 +370,13 @@ PyMODINIT_FUNC PyInit_login(void) { return NULL; } + Py_INCREF(&MonitorType); + if (PyModule_AddObject(m, "Monitor", (PyObject *) &MonitorType)) { + Py_DECREF(&MonitorType); + Py_DECREF(m); + return NULL; + } + return m; } diff --git a/systemd/pyutil.c b/systemd/pyutil.c index 9510acd..2f047e6 100644 --- a/systemd/pyutil.c +++ b/systemd/pyutil.c @@ -44,3 +44,17 @@ PyObject* absolute_timeout(uint64_t t) { return PyLong_FromLong(msec); } } + +int set_error(int r, const char* path, const char* invalid_message) { + if (r >= 0) + return r; + if (r == -EINVAL && invalid_message) + PyErr_SetString(PyExc_ValueError, invalid_message); + else if (r == -ENOMEM) + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + else { + errno = -r; + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + } + return -1; +} diff --git a/systemd/pyutil.h b/systemd/pyutil.h index 5c7ea37..ea88840 100644 --- a/systemd/pyutil.h +++ b/systemd/pyutil.h @@ -28,6 +28,7 @@ void cleanup_Py_DECREFp(PyObject **p); PyObject* absolute_timeout(uint64_t t); +int set_error(int r, const char* path, const char* invalid_message); #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) -- cgit v1.2.1 From cba4e76b2fc7c3524da42ccca33d47a13cc41c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 9 May 2013 18:25:54 -0400 Subject: systemd-python: use consistent indentation --- systemd/login.c | 92 ++++++++++++++++++++++++++++---------------------------- systemd/pyutil.c | 48 ++++++++++++++--------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/systemd/login.c b/systemd/login.c index 57dc080..b5cb811 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -142,8 +142,8 @@ static PyTypeObject MonitorType; static void Monitor_dealloc(Monitor* self) { - sd_login_monitor_unref(self->monitor); - Py_TYPE(self)->tp_free((PyObject*)self); + sd_login_monitor_unref(self->monitor); + Py_TYPE(self)->tp_free((PyObject*)self); } PyDoc_STRVAR(Monitor__doc__, @@ -192,11 +192,11 @@ PyDoc_STRVAR(Monitor_get_events__doc__, "See man: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); - if (r < 0) - return NULL; - return long_FromLong(r); + int r = sd_login_monitor_get_events(self->monitor); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + return long_FromLong(r); } @@ -210,19 +210,19 @@ PyDoc_STRVAR(Monitor_get_timeout__doc__, "See man:sd_login_monitor_get_timeout(3) for further discussion."); static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) { - int r; - uint64_t t; + int r; + uint64_t t; - r = sd_login_monitor_get_timeout(self->monitor, &t); - set_error(r, NULL, NULL); - if (r < 0) - return NULL; + r = sd_login_monitor_get_timeout(self->monitor, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; - if (t == (uint64_t) -1) - Py_RETURN_NONE; + if (t == (uint64_t) -1) + Py_RETURN_NONE; - assert_cc(sizeof(unsigned long long) == sizeof(t)); - return PyLong_FromUnsignedLongLong(t); + assert_cc(sizeof(unsigned long long) == sizeof(t)); + return PyLong_FromUnsignedLongLong(t); } @@ -233,15 +233,15 @@ PyDoc_STRVAR(Monitor_get_timeout_ms__doc__, "no timeout is necessary."); static PyObject* Monitor_get_timeout_ms(Monitor *self, PyObject *args) { - int r; - uint64_t t; + int r; + uint64_t t; - r = sd_login_monitor_get_timeout(self->monitor, &t); - set_error(r, NULL, NULL); - if (r < 0) - return NULL; + r = sd_login_monitor_get_timeout(self->monitor, &t); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; - return absolute_timeout(t); + return absolute_timeout(t); } @@ -282,11 +282,11 @@ PyDoc_STRVAR(Monitor___enter____doc__, "Returns self.\n"); static PyObject* Monitor___enter__(PyObject *self, PyObject *args) { - assert(self); - assert(!args); + assert(self); + assert(!args); - Py_INCREF(self); - return self; + Py_INCREF(self); + return self; } @@ -301,27 +301,27 @@ static PyObject* Monitor___exit__(Monitor *self, PyObject *args) static PyMethodDef Monitor_methods[] = { - {"fileno", (PyCFunction) Monitor_fileno, METH_NOARGS, Monitor_fileno__doc__}, - {"get_events", (PyCFunction) Monitor_get_events, METH_NOARGS, Monitor_get_events__doc__}, - {"get_timeout", (PyCFunction) Monitor_get_timeout, METH_NOARGS, Monitor_get_timeout__doc__}, - {"get_timeout_ms", (PyCFunction) Monitor_get_timeout_ms, METH_NOARGS, Monitor_get_timeout_ms__doc__}, - {"close", (PyCFunction) Monitor_close, METH_NOARGS, Monitor_close__doc__}, - {"flush", (PyCFunction) Monitor_flush, METH_NOARGS, Monitor_flush__doc__}, - {"__enter__", (PyCFunction) Monitor___enter__, METH_NOARGS, Monitor___enter____doc__}, - {"__exit__", (PyCFunction) Monitor___exit__, METH_VARARGS, Monitor___exit____doc__}, - {} /* Sentinel */ + {"fileno", (PyCFunction) Monitor_fileno, METH_NOARGS, Monitor_fileno__doc__}, + {"get_events", (PyCFunction) Monitor_get_events, METH_NOARGS, Monitor_get_events__doc__}, + {"get_timeout", (PyCFunction) Monitor_get_timeout, METH_NOARGS, Monitor_get_timeout__doc__}, + {"get_timeout_ms", (PyCFunction) Monitor_get_timeout_ms, METH_NOARGS, Monitor_get_timeout_ms__doc__}, + {"close", (PyCFunction) Monitor_close, METH_NOARGS, Monitor_close__doc__}, + {"flush", (PyCFunction) Monitor_flush, METH_NOARGS, Monitor_flush__doc__}, + {"__enter__", (PyCFunction) Monitor___enter__, METH_NOARGS, Monitor___enter____doc__}, + {"__exit__", (PyCFunction) Monitor___exit__, METH_VARARGS, Monitor___exit____doc__}, + {} /* Sentinel */ }; static PyTypeObject MonitorType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "login.Monitor", - .tp_basicsize = sizeof(Monitor), - .tp_dealloc = (destructor) Monitor_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = Monitor__doc__, - .tp_methods = Monitor_methods, - .tp_init = (initproc) Monitor_init, - .tp_new = PyType_GenericNew, + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "login.Monitor", + .tp_basicsize = sizeof(Monitor), + .tp_dealloc = (destructor) Monitor_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Monitor__doc__, + .tp_methods = Monitor_methods, + .tp_init = (initproc) Monitor_init, + .tp_new = PyType_GenericNew, }; diff --git a/systemd/pyutil.c b/systemd/pyutil.c index 2f047e6..42e7ba7 100644 --- a/systemd/pyutil.c +++ b/systemd/pyutil.c @@ -30,31 +30,31 @@ void cleanup_Py_DECREFp(PyObject **p) { } PyObject* absolute_timeout(uint64_t t) { - if (t == (uint64_t) -1) - return PyLong_FromLong(-1); - else { - struct timespec ts; - uint64_t n; - int msec; - - clock_gettime(CLOCK_MONOTONIC, &ts); - n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; - msec = t > n ? (int) ((t - n + 999) / 1000) : 0; - - return PyLong_FromLong(msec); - } + if (t == (uint64_t) -1) + return PyLong_FromLong(-1); + else { + struct timespec ts; + uint64_t n; + int msec; + + clock_gettime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; + + return PyLong_FromLong(msec); + } } int set_error(int r, const char* path, const char* invalid_message) { - if (r >= 0) - return r; - if (r == -EINVAL && invalid_message) - PyErr_SetString(PyExc_ValueError, invalid_message); - else if (r == -ENOMEM) - PyErr_SetString(PyExc_MemoryError, "Not enough memory"); - else { - errno = -r; - PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); - } - return -1; + if (r >= 0) + return r; + if (r == -EINVAL && invalid_message) + PyErr_SetString(PyExc_ValueError, invalid_message); + else if (r == -ENOMEM) + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + else { + errno = -r; + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + } + return -1; } -- cgit v1.2.1 From d9e0a12c2a9f7bf8843d8a9b969fb30f558df66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 9 May 2013 18:28:15 -0400 Subject: systemd-python: allow threads around flush flush() is potentially costly. --- systemd/login.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/systemd/login.c b/systemd/login.c index b5cb811..1e86193 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -271,7 +271,9 @@ static PyObject* Monitor_flush(Monitor *self, PyObject *args) assert(self); assert(!args); + Py_BEGIN_ALLOW_THREADS sd_login_monitor_flush(self->monitor); + Py_END_ALLOW_THREADS Py_RETURN_NONE; } -- cgit v1.2.1 From 98f187ea6486ea110bec62212d946325c589f996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 16 May 2013 00:38:39 -0400 Subject: systemd-python: do not attempt to convert str to bytes Bug-spotted-by: Steven Hiscocks --- systemd/journal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index 9ef1ede..8fd1bb3 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -55,6 +55,9 @@ def _convert_realtime(t): def _convert_timestamp(s): return _datetime.datetime.fromtimestamp(int(s) / 1000000) +def _convert_trivial(x): + return x + if _sys.version_info >= (3,): def _convert_uuid(s): return _uuid.UUID(s.decode()) @@ -87,6 +90,7 @@ DEFAULT_CONVERTERS = { '__REALTIME_TIMESTAMP': _convert_realtime, '_SOURCE_MONOTONIC_TIMESTAMP': _convert_source_monotonic, '__MONOTONIC_TIMESTAMP': _convert_monotonic, + '__CURSOR': _convert_trivial, 'COREDUMP': bytes, 'COREDUMP_PID': int, 'COREDUMP_UID': int, -- cgit v1.2.1 From 0a3463cd5b35b7dce8b68bbc7064f6dd98a57b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 4 Jun 2013 22:31:05 -0400 Subject: journal: add ability to filter by current user This is the just the library part. SD_JOURNAL_CURRENT_USER flags is added to sd_j_open(), to open files from current user. SD_JOURNAL_SYSTEM_ONLY is renamed to SD_JOURNAL_SYSTEM, and changed to mean to (also) open system files. This way various flags can be combined, which gives them nicer semantics, especially if other ones are added later. Backwards compatibility is kept, because SD_JOURNAL_SYSTEM_ONLY is equivalent to SD_JOURNAL_SYSTEM if used alone, and before there we no other flags. --- systemd/_reader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index c4c4fdf..2c69963 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1097,6 +1097,7 @@ init_reader(void) PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || 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_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { #if PY_MAJOR_VERSION >= 3 -- cgit v1.2.1 From d9a07b11ea52bb0c9a52b980394bf851fcc8767e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Jul 2013 22:15:54 -0400 Subject: systemd-python: wrap sd_notify _listen_fds() is modified to accept unset_environment arg as keyword, to match new notify(). --- systemd/_daemon.c | 49 +++++++++++++++++++++++++++++++++++++++++++++---- systemd/daemon.py | 1 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index c6b14d4..bd4e73e 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -79,6 +79,43 @@ static PyObject* booted(PyObject *self, PyObject *args) { return PyBool_FromLong(r); } +PyDoc_STRVAR(notify__doc__, + "notify(status, unset_environment=False) -> bool\n\n" + "Send a message to the init system about a status change.\n" + "Wraps sd_notify(3)."); + +static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { + int r; + const char* msg; + int unset = false; + + static const char* const kwlist[] = { + "status", + "unset_environment", + NULL, + }; +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3 + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|p:notify", + (char**) kwlist, &msg, &unset)) + return NULL; +#else + PyObject *obj = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|O:notify", + (char**) kwlist, &msg, &obj)) + return NULL; + if (obj != NULL) + unset = PyObject_IsTrue(obj); + if (unset < 0) + return NULL; +#endif + + r = sd_notify(unset, msg); + if (set_error(r, NULL, NULL)) + return NULL; + + return PyBool_FromLong(r); +} + PyDoc_STRVAR(listen_fds__doc__, "_listen_fds(unset_environment=True) -> int\n\n" @@ -87,16 +124,19 @@ PyDoc_STRVAR(listen_fds__doc__, "Wraps sd_listen_fds(3)." ); -static PyObject* listen_fds(PyObject *self, PyObject *args) { +static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) { int r; int unset = true; + static const char* const kwlist[] = {"unset_environment", NULL}; #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3 - if (!PyArg_ParseTuple(args, "|p:_listen_fds", &unset)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|p:_listen_fds", + (char**) kwlist, &unset)) return NULL; #else PyObject *obj = NULL; - if (!PyArg_ParseTuple(args, "|O:_listen_fds", &obj)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds", + (char**) kwlist, &unset, &obj)) return NULL; if (obj != NULL) unset = PyObject_IsTrue(obj); @@ -256,7 +296,8 @@ static PyObject* is_socket_unix(PyObject *self, PyObject *args) { static PyMethodDef methods[] = { { "booted", booted, METH_NOARGS, booted__doc__}, - { "_listen_fds", listen_fds, METH_VARARGS, listen_fds__doc__}, + { "notify", (PyCFunction) notify, METH_VARARGS | METH_KEYWORDS, notify__doc__}, + { "_listen_fds", (PyCFunction) listen_fds, METH_VARARGS | METH_KEYWORDS, listen_fds__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__}, diff --git a/systemd/daemon.py b/systemd/daemon.py index e2829d1..1c386bb 100644 --- a/systemd/daemon.py +++ b/systemd/daemon.py @@ -1,5 +1,6 @@ from ._daemon import (__version__, booted, + notify, _listen_fds, _is_fifo, _is_socket, -- cgit v1.2.1 From 2c0317be82aa85911fe7665719d0139ee4330211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 17 Jul 2013 12:50:43 -0400 Subject: systemd-python: fix iteration Back in 6a58bf4135 raising stop iteration was removed from the C code, but wasn't added in the Python counterpart. --- systemd/journal.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index 8fd1bb3..adcc844 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -191,18 +191,18 @@ class Reader(_Reader): """ return self - if _sys.version_info >= (3,): - def __next__(self): - """Part of iterator protocol. - Returns self.get_next(). - """ - return self.get_next() - else: - def next(self): - """Part of iterator protocol. - Returns self.get_next(). - """ - return self.get_next() + def __next__(self): + """Part of iterator protocol. + Returns self.get_next() or raises StopIteration. + """ + ans = self.get_next() + if ans: + return ans + else: + raise StopIteration() + + if _sys.version_info < (3,): + next = __next__ def add_match(self, *args, **kwargs): """Add one or more matches to the filter journal log entries. -- cgit v1.2.1 From 2bfb849c70ac2328d37755f1850fc7cfde1d6b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 17 Jul 2013 12:50:13 -0400 Subject: systemd-python: add support for sd_j_open_files Also export missing flags. --- systemd/_daemon.c | 22 -------------- systemd/_reader.c | 84 +++++++++++++++++++++++++++++++++++++++++++++--------- systemd/journal.py | 7 +++-- systemd/pyutil.c | 20 +++++++++++++ systemd/pyutil.h | 4 +++ 5 files changed, 99 insertions(+), 38 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index bd4e73e..6b84fb8 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -40,28 +40,6 @@ PyDoc_STRVAR(module__doc__, "running under systemd." ); - -#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 -static int Unicode_FSConverter(PyObject* obj, void *_result) { - PyObject **result = _result; - - assert(result); - - if (!obj) - /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so - * we can assume that it was PyUnicode_FSConverter. */ - return PyUnicode_FSConverter(obj, result); - - if (obj == Py_None) { - *result = NULL; - return 1; - } - - return PyUnicode_FSConverter(obj, result); -} -#endif - - PyDoc_STRVAR(booted__doc__, "booted() -> bool\n\n" "Return True iff this system is running under systemd.\n" diff --git a/systemd/_reader.c b/systemd/_reader.c index 2c69963..6ac2f20 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -30,6 +30,7 @@ #include "pyutil.h" #include "macro.h" #include "util.h" +#include "strv.h" #include "build.h" typedef struct { @@ -63,6 +64,57 @@ static PyStructSequence_Desc Monotonic_desc = { }; #endif +static int strv_converter(PyObject* obj, void *_result) { + char ***result = _result; + Py_ssize_t i, len; + + assert(result); + + if (!obj) + goto cleanup; + + if (!PySequence_Check(obj)) + return 0; + + len = PySequence_Length(obj); + *result = new0(char*, len + 1); + + for (i = 0; i < len; i++) { + PyObject *item; +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 + int r; + PyObject *bytes; +#endif + char *s, *s2; + + item = PySequence_ITEM(obj, i); +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 + r = PyUnicode_FSConverter(item, &bytes); + if (r == 0) + goto cleanup; + + s = PyBytes_AsString(bytes); +#else + s = PyString_AsString(item); +#endif + if (!s) + goto cleanup; + + s2 = strdup(s); + if (!s2) + log_oom(); + + (*result)[i] = s2; + } + + return 1; + +cleanup: + strv_free(*result); + *result = NULL; + + return 0; +} static void Reader_dealloc(Reader* self) { @@ -71,40 +123,45 @@ static void Reader_dealloc(Reader* self) } PyDoc_STRVAR(Reader__doc__, - "_Reader([flags | path]) -> ...\n\n" + "_Reader([flags | path | files]) -> ...\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" "Argument `flags` sets open flags of the journal, which can be one\n" "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" "journal on local machine only; RUNTIME_ONLY opens only\n" - "volatile journal files; and SYSTEM_ONLY opens only\n" - "journal files of system services and the kernel.\n\n" - "Argument `path` is the directory of journal files. Note that\n" - "`flags` and `path` are exclusive.\n\n" + "volatile journal files; and SYSTEM opens journal files of\n" + "system services and the kernel, and CURRENT_USER opens files\n" + "of the current user.\n\n" + "Argument `path` is the directory of journal files.\n" + "Argument `files` is a list of files. Note that\n" + "`flags`, `path`, and `files` are exclusive.\n\n" "_Reader implements the context manager protocol: the journal\n" "will be closed when exiting the block."); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { int flags = 0, r; char *path = NULL; + char **files = NULL; - static const char* const kwlist[] = {"flags", "path", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist, - &flags, &path)) + static const char* const kwlist[] = {"flags", "path", "files", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&", (char**) kwlist, + &flags, &path, strv_converter, &files)) return -1; + if (!!flags + !!path + !!files > 1) { + PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files"); + return -1; + } + if (!flags) flags = SD_JOURNAL_LOCAL_ONLY; - else - if (path) { - PyErr_SetString(PyExc_ValueError, "cannot use both flags and path"); - return -1; - } Py_BEGIN_ALLOW_THREADS if (path) r = sd_journal_open_directory(&self->j, path, 0); + else if (files) + r = sd_journal_open_files(&self->j, (const char**) files, 0); else r = sd_journal_open(&self->j, flags); Py_END_ALLOW_THREADS @@ -1099,6 +1156,7 @@ init_reader(void) 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, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) || PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { #if PY_MAJOR_VERSION >= 3 Py_DECREF(m); diff --git a/systemd/journal.py b/systemd/journal.py index adcc844..d0bcd24 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -33,7 +33,8 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import __version__, sendv, stream_fd from ._reader import (_Reader, NOP, APPEND, INVALIDATE, - LOCAL_ONLY, RUNTIME_ONLY, SYSTEM_ONLY, + LOCAL_ONLY, RUNTIME_ONLY, + SYSTEM, SYSTEM_ONLY, CURRENT_USER, _get_catalog) from . import id128 as _id128 @@ -123,7 +124,7 @@ class Reader(_Reader): See systemd.journal-fields(7) for more info on typical fields found in the journal. """ - def __init__(self, flags=0, path=None, converters=None): + def __init__(self, flags=0, path=None, files=None, converters=None): """Create an instance of Reader, which allows filtering and return of journal entries. @@ -149,7 +150,7 @@ class Reader(_Reader): Reader implements the context manager protocol: the journal will be closed when exiting the block. """ - super(Reader, self).__init__(flags, path) + super(Reader, self).__init__(flags, path, files) if _sys.version_info >= (3,3): self.converters = _ChainMap() if converters is not None: diff --git a/systemd/pyutil.c b/systemd/pyutil.c index 42e7ba7..722c4f5 100644 --- a/systemd/pyutil.c +++ b/systemd/pyutil.c @@ -58,3 +58,23 @@ int set_error(int r, const char* path, const char* invalid_message) { } return -1; } + +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 +int Unicode_FSConverter(PyObject* obj, void *_result) { + PyObject **result = _result; + + assert(result); + + if (!obj) + /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so + * we can assume that it was PyUnicode_FSConverter. */ + return PyUnicode_FSConverter(obj, result); + + if (obj == Py_None) { + *result = NULL; + return 1; + } + + return PyUnicode_FSConverter(obj, result); +} +#endif diff --git a/systemd/pyutil.h b/systemd/pyutil.h index ea88840..1477e7b 100644 --- a/systemd/pyutil.h +++ b/systemd/pyutil.h @@ -30,6 +30,10 @@ void cleanup_Py_DECREFp(PyObject **p); PyObject* absolute_timeout(uint64_t t); int set_error(int r, const char* path, const char* invalid_message); +#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 +int Unicode_FSConverter(PyObject* obj, void *_result); +#endif + #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp))) #if PY_MAJOR_VERSION >=3 -- cgit v1.2.1 From c0d8b4e8109f48ed1a21cc677edff0f1462d2f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 18 Jul 2013 00:03:09 -0400 Subject: systemd-python: also update the documentation sphinx, oh sphinx, why do you require manual ficksups all the time? --- systemd/docs/journal.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/systemd/docs/journal.rst b/systemd/docs/journal.rst index e6c4206..ea74cf8 100644 --- a/systemd/docs/journal.rst +++ b/systemd/docs/journal.rst @@ -53,7 +53,8 @@ Journal access types .. autoattribute:: systemd.journal.LOCAL_ONLY .. autoattribute:: systemd.journal.RUNTIME_ONLY -.. autoattribute:: systemd.journal.SYSTEM_ONLY +.. autoattribute:: systemd.journal.SYSTEM +.. autoattribute:: systemd.journal.CURRENT_USER Journal event types ~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.1 From 7583998db70626720e201ede56ccba56d4520d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 25 Jul 2013 18:55:08 -0400 Subject: systemd-python: use modern C --- systemd/_reader.c | 49 ++++++++++--------------------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 6ac2f20..98a99aa 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1046,48 +1046,20 @@ static PyMethodDef Reader_methods[] = { static PyTypeObject ReaderType = { PyVarObject_HEAD_INIT(NULL, 0) - "_reader._Reader", /*tp_name*/ - sizeof(Reader), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Reader_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - Reader__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Reader_methods, /* tp_methods */ - 0, /* tp_members */ - Reader_getsetters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc) Reader_init, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + .tp_name = "_reader._Reader", + .tp_basicsize = sizeof(Reader), + .tp_dealloc = (destructor) Reader_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Reader__doc__, + .tp_methods = Reader_methods, + .tp_getset = Reader_getsetters, + .tp_init = (initproc) Reader_init, + .tp_new = PyType_GenericNew, }; static PyMethodDef methods[] = { { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, - { NULL, NULL, 0, NULL } /* Sentinel */ + {} /* Sentinel */ }; #if PY_MAJOR_VERSION >= 3 @@ -1097,7 +1069,6 @@ static PyModuleDef module = { module__doc__, -1, methods, - NULL, NULL, NULL, NULL }; #endif -- cgit v1.2.1 From ac8b2b3eb3097b9079d8c5db1f09fcedffab246e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 26 Jul 2013 11:02:27 -0400 Subject: systemd-python: fix gcc warning src/python-systemd/_reader.c: In function Reader_get_catalog: src/python-systemd/_reader.c:912:53: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] assert(mid_len > l); ^ --- systemd/_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 98a99aa..a678f69 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -908,9 +908,9 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len); if (r == 0) { - const int l = sizeof("MESSAGE_ID"); + const size_t l = sizeof("MESSAGE_ID"); assert(mid_len > l); - PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l, + PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l), (const char*) mid + l); } else if (r == -ENOENT) PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field"); -- cgit v1.2.1 From ec9078e45e25d7484b5f4e08dd69020f57e4d668 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Thu, 15 Aug 2013 12:50:32 -0400 Subject: systemd-python: fix initialization of _Reader objects https://bugzilla.redhat.com/show_bug.cgi?id=995575 --- systemd/_reader.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/systemd/_reader.c b/systemd/_reader.c index a678f69..3b1003b 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -64,6 +64,10 @@ static PyStructSequence_Desc Monotonic_desc = { }; #endif +/** + * Convert a Python sequence object into a strv (char**), and + * None into a NULL pointer. + */ static int strv_converter(PyObject* obj, void *_result) { char ***result = _result; Py_ssize_t i, len; @@ -73,6 +77,11 @@ static int strv_converter(PyObject* obj, void *_result) { if (!obj) goto cleanup; + if (obj == Py_None) { + *result = NULL; + return 1; + } + if (!PySequence_Check(obj)) return 0; -- cgit v1.2.1 From 39970127f2f05b39dc38f073dd3d60657edc8508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 15 Aug 2013 12:51:20 -0400 Subject: systemd-python: check for oom, give nicer error messages --- systemd/_reader.c | 8 ++++++-- systemd/login.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 3b1003b..bc5db19 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -75,7 +75,7 @@ static int strv_converter(PyObject* obj, void *_result) { assert(result); if (!obj) - goto cleanup; + return 0; if (obj == Py_None) { *result = NULL; @@ -87,6 +87,10 @@ static int strv_converter(PyObject* obj, void *_result) { len = PySequence_Length(obj); *result = new0(char*, len + 1); + if (!*result) { + set_error(-ENOMEM, NULL, NULL); + return 0; + } for (i = 0; i < len; i++) { PyObject *item; @@ -154,7 +158,7 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) char **files = NULL; static const char* const kwlist[] = {"flags", "path", "files", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&", (char**) kwlist, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist, &flags, &path, strv_converter, &files)) return -1; diff --git a/systemd/login.c b/systemd/login.c index 1e86193..dd2edbc 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -159,7 +159,7 @@ static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) int r; static const char* const kwlist[] = {"category", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|z", (char**) kwlist, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|z:__init__", (char**) kwlist, &category)) return -1; -- cgit v1.2.1 From 7bb40a26e0a228b0a6ad4871661838a8facd7eb5 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Tue, 5 Nov 2013 15:41:20 +0100 Subject: systemd-python: convert keyword value to string Allows using journal.send('msg', PRIORITY=journal.LOG_CRIT) Before this commit this results in TypeError: cannot concatenate 'str' and 'int' objects and requires passing PRIORITY value as string to work. --- systemd/journal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/systemd/journal.py b/systemd/journal.py index d0bcd24..9c7e004 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -352,6 +352,8 @@ def get_catalog(mid): def _make_line(field, value): if isinstance(value, bytes): return field.encode('utf-8') + b'=' + value + elif isinstance(value, int): + return field + '=' + str(value) else: return field + '=' + value -- cgit v1.2.1 From 07c1d9a144e492c13ef225f0b928d1edb8bf856c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 11 Nov 2013 19:53:59 -0500 Subject: systemd-python: fix booted() and add two functions to docs For some reason sphinx doesn't want to show inherited C functions. --- systemd/_daemon.c | 2 +- systemd/docs/daemon.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index 6b84fb8..f0ab16f 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -51,7 +51,7 @@ static PyObject* booted(PyObject *self, PyObject *args) { assert(args == NULL); r = sd_booted(); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); diff --git a/systemd/docs/daemon.rst b/systemd/docs/daemon.rst index 72280ca..0ad11ed 100644 --- a/systemd/docs/daemon.rst +++ b/systemd/docs/daemon.rst @@ -14,3 +14,5 @@ .. autofunction:: _is_socket_unix .. autofunction:: _is_socket_inet .. autofunction:: _is_mq + .. autofunction:: notify + .. autofunction:: booted -- cgit v1.2.1 From 2ebe9bf6c3a08fd33f83a30c1893b3a768eb9322 Mon Sep 17 00:00:00 2001 From: Thomas Hindoe Paaboel Andersen Date: Tue, 3 Dec 2013 22:27:45 +0100 Subject: trivial coding style clean ups - Add space between if/for and the opening parentheses - Place the opening brace on same line as the function (not for udev) From the CODING_STYLE Try to use this: void foo() { } instead of this: void foo() { } --- systemd/_reader.c | 108 ++++++++++++++++++------------------------------------ systemd/login.c | 30 +++++---------- 2 files changed, 46 insertions(+), 92 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index bc5db19..1012606 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -129,8 +129,7 @@ cleanup: return 0; } -static void Reader_dealloc(Reader* self) -{ +static void Reader_dealloc(Reader* self) { sd_journal_close(self->j); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -151,8 +150,7 @@ PyDoc_STRVAR(Reader__doc__, "`flags`, `path`, and `files` are exclusive.\n\n" "_Reader implements the context manager protocol: the journal\n" "will be closed when exiting the block."); -static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) -{ +static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { int flags = 0, r; char *path = NULL; char **files = NULL; @@ -188,8 +186,7 @@ PyDoc_STRVAR(Reader_fileno__doc__, "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)."); -static PyObject* Reader_fileno(Reader *self, PyObject *args) -{ +static PyObject* Reader_fileno(Reader *self, PyObject *args) { int fd = sd_journal_get_fd(self->j); set_error(fd, NULL, NULL); if (fd < 0) @@ -203,8 +200,7 @@ PyDoc_STRVAR(Reader_reliable_fd__doc__, "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)."); -static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) -{ +static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) { int r = sd_journal_reliable_fd(self->j); set_error(r, NULL, NULL); if (r < 0) @@ -218,8 +214,7 @@ PyDoc_STRVAR(Reader_get_events__doc__, "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."); -static PyObject* Reader_get_events(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_events(Reader *self, PyObject *args) { int r = sd_journal_get_events(self->j); set_error(r, NULL, NULL); if (r < 0) @@ -236,8 +231,7 @@ PyDoc_STRVAR(Reader_get_timeout__doc__, "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."); -static PyObject* Reader_get_timeout(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_timeout(Reader *self, PyObject *args) { int r; uint64_t t; @@ -259,8 +253,7 @@ PyDoc_STRVAR(Reader_get_timeout_ms__doc__, "Returns a timeout value suitable for usage in poll(), the value\n" "returned by .get_timeout() converted to relative ms, or -1 if\n" "no timeout is necessary."); -static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) { int r; uint64_t t; @@ -278,8 +271,7 @@ PyDoc_STRVAR(Reader_close__doc__, "Free resources allocated by this Reader object.\n" "This method invokes sd_journal_close().\n" "See man:sd_journal_close(3)."); -static PyObject* Reader_close(Reader *self, PyObject *args) -{ +static PyObject* Reader_close(Reader *self, PyObject *args) { assert(self); assert(!args); @@ -298,8 +290,7 @@ PyDoc_STRVAR(Reader_get_usage__doc__, "of all hosts.\n\n" "This method invokes sd_journal_get_usage().\n" "See man:sd_journal_get_usage(3)."); -static PyObject* Reader_get_usage(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_usage(Reader *self, PyObject *args) { int r; uint64_t bytes; @@ -316,8 +307,7 @@ PyDoc_STRVAR(Reader___enter____doc__, "__enter__() -> self\n\n" "Part of the context manager protocol.\n" "Returns self.\n"); -static PyObject* Reader___enter__(PyObject *self, PyObject *args) -{ +static PyObject* Reader___enter__(PyObject *self, PyObject *args) { assert(self); assert(!args); @@ -329,8 +319,7 @@ PyDoc_STRVAR(Reader___exit____doc__, "__exit__(type, value, traceback) -> None\n\n" "Part of the context manager protocol.\n" "Closes the journal.\n"); -static PyObject* Reader___exit__(Reader *self, PyObject *args) -{ +static PyObject* Reader___exit__(Reader *self, PyObject *args) { return Reader_close(self, args); } @@ -340,8 +329,7 @@ PyDoc_STRVAR(Reader_next__doc__, "Go to the next log entry. Optional skip value means to go to\n" "the `skip`\\-th log entry.\n" "Returns False if at end of file, True otherwise."); -static PyObject* Reader_next(Reader *self, PyObject *args) -{ +static PyObject* Reader_next(Reader *self, PyObject *args) { int64_t skip = 1LL; int r; @@ -377,8 +365,7 @@ PyDoc_STRVAR(Reader_previous__doc__, "Go to the previous log entry. Optional skip value means to \n" "go to the `skip`\\-th previous log entry.\n" "Returns False if at start of file, True otherwise."); -static PyObject* Reader_previous(Reader *self, PyObject *args) -{ +static PyObject* Reader_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; if (!PyArg_ParseTuple(args, "|L:previous", &skip)) return NULL; @@ -427,8 +414,7 @@ PyDoc_STRVAR(Reader_get__doc__, "get(str) -> str\n\n" "Return data associated with this key in current log entry.\n" "Throws KeyError is the data is not available."); -static PyObject* Reader_get(Reader *self, PyObject *args) -{ +static PyObject* Reader_get(Reader *self, PyObject *args) { const char* field; const void* msg; size_t msg_len; @@ -458,8 +444,7 @@ static PyObject* Reader_get(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_get_all__doc__, "_get_all() -> dict\n\n" "Return dictionary of the current log entry."); -static PyObject* Reader_get_all(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_all(Reader *self, PyObject *args) { PyObject *dict; const void *msg; size_t msg_len; @@ -521,8 +506,7 @@ PyDoc_STRVAR(Reader_get_realtime__doc__, "in microseconds.\n\n" "Wraps sd_journal_get_realtime_usec().\n" "See man:sd_journal_get_realtime_usec(3)."); -static PyObject* Reader_get_realtime(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_realtime(Reader *self, PyObject *args) { uint64_t timestamp; int r; @@ -544,8 +528,7 @@ PyDoc_STRVAR(Reader_get_monotonic__doc__, "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)."); -static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) { uint64_t timestamp; sd_id128_t id; PyObject *monotonic, *bootid, *tuple; @@ -590,8 +573,7 @@ PyDoc_STRVAR(Reader_add_match__doc__, "fields are combined with logical AND, and matches of the same field\n" "are automatically combined with logical OR.\n" "Match is a string of the form \"FIELD=value\"."); -static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) -{ +static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) { char *match; int match_len, r; if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) @@ -612,8 +594,7 @@ PyDoc_STRVAR(Reader_add_disjunction__doc__, "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."); -static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) -{ +static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); set_error(r, NULL, NULL); @@ -629,8 +610,7 @@ PyDoc_STRVAR(Reader_add_conjunction__doc__, "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."); -static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) -{ +static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_conjunction(self->j); set_error(r, NULL, NULL); @@ -643,8 +623,7 @@ static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); -static PyObject* Reader_flush_matches(Reader *self, PyObject *args) -{ +static PyObject* Reader_flush_matches(Reader *self, PyObject *args) { sd_journal_flush_matches(self->j); Py_RETURN_NONE; } @@ -655,8 +634,7 @@ PyDoc_STRVAR(Reader_seek_head__doc__, "Jump to the beginning of the journal.\n" "This method invokes sd_journal_seek_head().\n" "See man:sd_journal_seek_head(3)."); -static PyObject* Reader_seek_head(Reader *self, PyObject *args) -{ +static PyObject* Reader_seek_head(Reader *self, PyObject *args) { int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_head(self->j); @@ -672,8 +650,7 @@ PyDoc_STRVAR(Reader_seek_tail__doc__, "Jump to the end of the journal.\n" "This method invokes sd_journal_seek_tail().\n" "See man:sd_journal_seek_tail(3)."); -static PyObject* Reader_seek_tail(Reader *self, PyObject *args) -{ +static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { int r; Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_tail(self->j); @@ -688,8 +665,7 @@ PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" "`realtime` in specified in seconds."); -static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) -{ +static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { uint64_t timestamp; int r; @@ -711,8 +687,7 @@ PyDoc_STRVAR(Reader_seek_monotonic__doc__, "`monotonic` is an timestamp from boot in microseconds.\n" "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); -static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) -{ +static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { char *bootid = NULL; uint64_t timestamp; sd_id128_t id; @@ -751,8 +726,7 @@ PyDoc_STRVAR(Reader_process__doc__, "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."); -static PyObject* Reader_process(Reader *self, PyObject *args) -{ +static PyObject* Reader_process(Reader *self, PyObject *args) { int r; assert(!args); @@ -777,8 +751,7 @@ PyDoc_STRVAR(Reader_wait__doc__, "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."); -static PyObject* Reader_wait(Reader *self, PyObject *args) -{ +static PyObject* Reader_wait(Reader *self, PyObject *args) { int r; int64_t timeout; @@ -798,8 +771,7 @@ static PyObject* Reader_wait(Reader *self, PyObject *args) PyDoc_STRVAR(Reader_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" "Seek to journal entry by given unique reference `cursor`."); -static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) -{ +static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { const char *cursor; int r; @@ -819,8 +791,7 @@ 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)."); -static PyObject* Reader_get_cursor(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { _cleanup_free_ char *cursor = NULL; int r; @@ -839,8 +810,7 @@ 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)."); -static PyObject* Reader_test_cursor(Reader *self, PyObject *args) -{ +static PyObject* Reader_test_cursor(Reader *self, PyObject *args) { const char *cursor; int r; @@ -862,8 +832,7 @@ PyDoc_STRVAR(Reader_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Return a set of unique values appearing in journal for the\n" "given `field`. Note this does not respect any journal matches."); -static PyObject* Reader_query_unique(Reader *self, PyObject *args) -{ +static PyObject* Reader_query_unique(Reader *self, PyObject *args) { char *query; int r; const void *uniq; @@ -904,8 +873,7 @@ PyDoc_STRVAR(Reader_get_catalog__doc__, "and KeyError is the id is specified, but hasn't been found\n" "in the catalog.\n\n" "Wraps man:sd_journal_get_catalog(3)."); -static PyObject* Reader_get_catalog(Reader *self, PyObject *args) -{ +static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { int r; _cleanup_free_ char *msg = NULL; @@ -941,8 +909,7 @@ 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)."); -static PyObject* get_catalog(PyObject *self, PyObject *args) -{ +static PyObject* get_catalog(PyObject *self, PyObject *args) { int r; char *id_ = NULL; sd_id128_t id; @@ -973,8 +940,7 @@ PyDoc_STRVAR(data_threshold__doc__, "Fields longer than this will be truncated to the threshold size.\n" "Defaults to 64Kb."); -static PyObject* Reader_get_data_threshold(Reader *self, void *closure) -{ +static PyObject* Reader_get_data_threshold(Reader *self, void *closure) { size_t cvalue; int r; @@ -985,8 +951,7 @@ static PyObject* Reader_get_data_threshold(Reader *self, void *closure) return long_FromSize_t(cvalue); } -static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) -{ +static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) { int r; if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); @@ -1003,8 +968,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) -{ +static PyObject* Reader_get_closed(Reader *self, void *closure) { return PyBool_FromLong(self->j == NULL); } diff --git a/systemd/login.c b/systemd/login.c index dd2edbc..43f7819 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -140,8 +140,7 @@ typedef struct { } Monitor; static PyTypeObject MonitorType; -static void Monitor_dealloc(Monitor* self) -{ +static void Monitor_dealloc(Monitor* self) { sd_login_monitor_unref(self->monitor); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -153,8 +152,7 @@ PyDoc_STRVAR(Monitor__doc__, "descriptor which can be integrated in an external event loop.\n" "See man:sd_login_monitor_new(3) for the details about what\n" "can be monitored."); -static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) -{ +static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) { const char *category = NULL; int r; @@ -175,8 +173,7 @@ PyDoc_STRVAR(Monitor_fileno__doc__, "fileno() -> int\n\n" "Get a file descriptor to poll for events.\n" "This method wraps sd_login_monitor_get_fd(3)."); -static PyObject* Monitor_fileno(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_fileno(Monitor *self, PyObject *args) { int fd = sd_login_monitor_get_fd(self->monitor); set_error(fd, NULL, NULL); if (fd < 0) @@ -190,8 +187,7 @@ PyDoc_STRVAR(Monitor_get_events__doc__, "Returns a mask of poll() events to wait for on the file\n" "descriptor returned by .fileno().\n\n" "See man:sd_login_monitor_get_events(3) for further discussion."); -static PyObject* Monitor_get_events(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_get_events(Monitor *self, PyObject *args) { int r = sd_login_monitor_get_events(self->monitor); set_error(r, NULL, NULL); if (r < 0) @@ -208,8 +204,7 @@ PyDoc_STRVAR(Monitor_get_timeout__doc__, "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."); -static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) { int r; uint64_t t; @@ -231,8 +226,7 @@ PyDoc_STRVAR(Monitor_get_timeout_ms__doc__, "Returns a timeout value suitable for usage in poll(), the value\n" "returned by .get_timeout() converted to relative ms, or -1 if\n" "no timeout is necessary."); -static PyObject* Monitor_get_timeout_ms(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_get_timeout_ms(Monitor *self, PyObject *args) { int r; uint64_t t; @@ -250,8 +244,7 @@ PyDoc_STRVAR(Monitor_close__doc__, "Free resources allocated by this Monitor object.\n" "This method invokes sd_login_monitor_unref().\n" "See man:sd_login_monitor_unref(3)."); -static PyObject* Monitor_close(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_close(Monitor *self, PyObject *args) { assert(self); assert(!args); @@ -266,8 +259,7 @@ PyDoc_STRVAR(Monitor_flush__doc__, "Reset the wakeup state of the monitor object.\n" "This method invokes sd_login_monitor_flush().\n" "See man:sd_login_monitor_flush(3)."); -static PyObject* Monitor_flush(Monitor *self, PyObject *args) -{ +static PyObject* Monitor_flush(Monitor *self, PyObject *args) { assert(self); assert(!args); @@ -282,8 +274,7 @@ PyDoc_STRVAR(Monitor___enter____doc__, "__enter__() -> self\n\n" "Part of the context manager protocol.\n" "Returns self.\n"); -static PyObject* Monitor___enter__(PyObject *self, PyObject *args) -{ +static PyObject* Monitor___enter__(PyObject *self, PyObject *args) { assert(self); assert(!args); @@ -296,8 +287,7 @@ PyDoc_STRVAR(Monitor___exit____doc__, "__exit__(type, value, traceback) -> None\n\n" "Part of the context manager protocol.\n" "Closes the monitor..\n"); -static PyObject* Monitor___exit__(Monitor *self, PyObject *args) -{ +static PyObject* Monitor___exit__(Monitor *self, PyObject *args) { return Monitor_close(self, args); } -- cgit v1.2.1 From 73f327ce219453c4d055805c78ccdaa1d625843d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 29 Dec 2013 23:39:28 -0500 Subject: systemd-python: fix setting of exception codes The return value of 0 would be treated as failure by mistake, resulting in " SystemError: error return without exception set". The way that set_error() is used is changed to be the same everywhere. --- systemd/_daemon.c | 14 ++++++------- systemd/_reader.c | 63 +++++++++++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index f0ab16f..c6db69f 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -88,7 +88,7 @@ static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) { #endif r = sd_notify(unset, msg); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -123,7 +123,7 @@ static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) { #endif r = sd_listen_fds(unset); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return long_FromLong(r); @@ -151,7 +151,7 @@ static PyObject* is_fifo(PyObject *self, PyObject *args) { #endif r = sd_is_fifo(fd, path); - if (set_error(r, path, NULL)) + if (set_error(r, path, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -179,7 +179,7 @@ static PyObject* is_mq(PyObject *self, PyObject *args) { #endif r = sd_is_mq(fd, path); - if (set_error(r, path, NULL)) + if (set_error(r, path, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -203,7 +203,7 @@ static PyObject* is_socket(PyObject *self, PyObject *args) { return NULL; r = sd_is_socket(fd, family, type, listening); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -230,7 +230,7 @@ static PyObject* is_socket_inet(PyObject *self, PyObject *args) { } r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -265,7 +265,7 @@ static PyObject* is_socket_unix(PyObject *self, PyObject *args) { #endif r = sd_is_socket_unix(fd, type, listening, path, length); - if (set_error(r, path, NULL)) + if (set_error(r, path, NULL) < 0) return NULL; return PyBool_FromLong(r); diff --git a/systemd/_reader.c b/systemd/_reader.c index 1012606..ee25c49 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -202,8 +202,7 @@ PyDoc_STRVAR(Reader_reliable_fd__doc__, "See man:sd_journal_reliable_fd(3)."); static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) { int r = sd_journal_reliable_fd(self->j); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); } @@ -216,8 +215,7 @@ PyDoc_STRVAR(Reader_get_events__doc__, "See man:sd_journal_get_events(3) for further discussion."); static PyObject* Reader_get_events(Reader *self, PyObject *args) { int r = sd_journal_get_events(self->j); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; return long_FromLong(r); } @@ -236,8 +234,7 @@ static PyObject* Reader_get_timeout(Reader *self, PyObject *args) { uint64_t t; r = sd_journal_get_timeout(self->j, &t); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; if (t == (uint64_t) -1) @@ -258,8 +255,7 @@ static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) { uint64_t t; r = sd_journal_get_timeout(self->j, &t); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; return absolute_timeout(t); @@ -295,7 +291,7 @@ static PyObject* Reader_get_usage(Reader *self, PyObject *args) { uint64_t bytes; r = sd_journal_get_usage(self->j, &bytes); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; assert_cc(sizeof(unsigned long long) == sizeof(bytes)); @@ -354,8 +350,7 @@ static PyObject* Reader_next(Reader *self, PyObject *args) { assert_not_reached("should not be here"); Py_END_ALLOW_THREADS - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); } @@ -431,7 +426,8 @@ static PyObject* Reader_get(Reader *self, PyObject *args) { if (r == -ENOENT) { PyErr_SetString(PyExc_KeyError, field); return NULL; - } else if (set_error(r, NULL, "field name is not valid")) + } + if (set_error(r, NULL, "field name is not valid") < 0) return NULL; r = extract(msg, msg_len, NULL, &value); @@ -514,7 +510,7 @@ static PyObject* Reader_get_realtime(Reader *self, PyObject *args) { assert(!args); r = sd_journal_get_realtime_usec(self->j, ×tamp); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); @@ -538,7 +534,7 @@ static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) { assert(!args); r = sd_journal_get_monotonic_usec(self->j, ×tamp, &id); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); @@ -580,8 +576,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds return NULL; r = sd_journal_add_match(self->j, match, match_len); - set_error(r, NULL, "Invalid match"); - if (r < 0) + if (set_error(r, NULL, "Invalid match") < 0) return NULL; Py_RETURN_NONE; @@ -597,8 +592,7 @@ PyDoc_STRVAR(Reader_add_disjunction__doc__, static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_disjunction(self->j); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; } @@ -613,8 +607,7 @@ PyDoc_STRVAR(Reader_add_conjunction__doc__, static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) { int r; r = sd_journal_add_conjunction(self->j); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; } @@ -639,7 +632,7 @@ static PyObject* Reader_seek_head(Reader *self, PyObject *args) { Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_head(self->j); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; } @@ -655,7 +648,7 @@ static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_tail(self->j); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; } @@ -675,7 +668,7 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; } @@ -698,20 +691,20 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { if (bootid) { r = sd_id128_from_string(bootid, &id); - if (set_error(r, NULL, "Invalid bootid")) + if (set_error(r, NULL, "Invalid bootid") < 0) return NULL; } else { Py_BEGIN_ALLOW_THREADS r = sd_id128_get_boot(&id); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; } Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; Py_RETURN_NONE; @@ -781,7 +774,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_cursor(self->j, cursor); Py_END_ALLOW_THREADS - if (set_error(r, NULL, "Invalid cursor")) + if (set_error(r, NULL, "Invalid cursor") < 0) return NULL; Py_RETURN_NONE; } @@ -799,7 +792,7 @@ static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { assert(!args); r = sd_journal_get_cursor(self->j, &cursor); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return unicode_FromString(cursor); @@ -821,8 +814,7 @@ static PyObject* Reader_test_cursor(Reader *self, PyObject *args) { return NULL; r = sd_journal_test_cursor(self->j, cursor); - set_error(r, NULL, NULL); - if (r < 0) + if (set_error(r, NULL, NULL) < 0) return NULL; return PyBool_FromLong(r); @@ -845,7 +837,7 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) { Py_BEGIN_ALLOW_THREADS r = sd_journal_query_unique(self->j, query); Py_END_ALLOW_THREADS - if (set_error(r, NULL, "Invalid field name")) + if (set_error(r, NULL, "Invalid field name") < 0) return NULL; value_set = PySet_New(0); @@ -898,7 +890,8 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { else set_error(r, NULL, NULL); return NULL; - } else if (set_error(r, NULL, NULL)) + } + if (set_error(r, NULL, NULL) < 0) return NULL; return unicode_FromString(msg); @@ -922,13 +915,13 @@ static PyObject* get_catalog(PyObject *self, PyObject *args) { return NULL; r = sd_id128_from_string(id_, &id); - if (set_error(r, NULL, "Invalid id128")) + if (set_error(r, NULL, "Invalid id128") < 0) return NULL; Py_BEGIN_ALLOW_THREADS r = sd_journal_get_catalog_for_message_id(id, &msg); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return unicode_FromString(msg); @@ -945,7 +938,7 @@ static PyObject* Reader_get_data_threshold(Reader *self, void *closure) { int r; r = sd_journal_get_data_threshold(self->j, &cvalue); - if (set_error(r, NULL, NULL)) + if (set_error(r, NULL, NULL) < 0) return NULL; return long_FromSize_t(cvalue); -- cgit v1.2.1 From d109c7035f17d158b0c79e3f907db02379c2bba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 30 Dec 2013 00:01:00 -0500 Subject: systemd-python: fix listen_fds under Python 2 --- systemd/_daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index c6db69f..3982e85 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -114,7 +114,7 @@ static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) { #else PyObject *obj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds", - (char**) kwlist, &unset, &obj)) + (char**) kwlist, &obj)) return NULL; if (obj != NULL) unset = PyObject_IsTrue(obj); -- cgit v1.2.1 From 5cf9be60caba7604038d167e9d0e2deea2a2676b Mon Sep 17 00:00:00 2001 From: Greg KH Date: Fri, 31 Jan 2014 06:51:32 +0100 Subject: use memzero(foo, length); for all memset(foo, 0, length); calls In trying to track down a stupid linker bug, I noticed a bunch of memset() calls that should be using memzero() to make it more "obvious" that the options are correct (i.e. 0 is not the length, but the data to set). So fix up all current calls to memset(foo, 0, length) to memzero(foo, length). --- systemd/_journal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index f8e0b4f..669c22c 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -22,6 +22,7 @@ #include #include +#include "util.h" #define SD_JOURNAL_SUPPRESS_LOCATION #include @@ -41,7 +42,7 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { /* Allocate an array for the argument strings */ argc = PyTuple_Size(args); encoded = alloca(argc * sizeof(PyObject*)); - memset(encoded, 0, argc * sizeof(PyObject*)); + memzero(encoded, argc * sizeof(PyObject*)); /* Allocate sufficient iovector space for the arguments. */ iov = alloca(argc * sizeof(struct iovec)); -- cgit v1.2.1 From 5e1d42a18032bff6863784d18671a3cb65cae0c3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 31 Jan 2014 12:27:35 +0100 Subject: util: use alloca0() intead of alloca() + memzero() --- systemd/_journal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/systemd/_journal.c b/systemd/_journal.c index 669c22c..8cc6d3e 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -41,8 +41,7 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { /* Allocate an array for the argument strings */ argc = PyTuple_Size(args); - encoded = alloca(argc * sizeof(PyObject*)); - memzero(encoded, argc * sizeof(PyObject*)); + encoded = alloca0(argc * sizeof(PyObject*)); /* Allocate sufficient iovector space for the arguments. */ iov = alloca(argc * sizeof(struct iovec)); -- cgit v1.2.1 From ce4e46ae66761931f386cfff0065ad056c528717 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Feb 2014 18:35:03 +0100 Subject: macro: introduce nice macro for disabling -Wmissing-prototypes warnigs --- systemd/_daemon.c | 10 +++++----- systemd/_journal.c | 9 ++++----- systemd/_reader.c | 5 ++--- systemd/id128.c | 12 +++++++----- systemd/login.c | 11 +++++------ 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index 3982e85..7756a78 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -31,6 +31,7 @@ #include #include "pyutil.h" +#include "macro.h" PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-daemon library.\n\n" @@ -284,11 +285,9 @@ static PyMethodDef methods[] = { { NULL, NULL, 0, NULL } /* Sentinel */ }; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - #if PY_MAJOR_VERSION < 3 +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC init_daemon(void) { PyObject *m; @@ -299,6 +298,7 @@ PyMODINIT_FUNC init_daemon(void) { PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START); PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } +REENABLE_WARNING; #else @@ -310,6 +310,7 @@ static struct PyModuleDef module = { methods }; +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC PyInit__daemon(void) { PyObject *m; @@ -325,7 +326,6 @@ PyMODINIT_FUNC PyInit__daemon(void) { return m; } +REENABLE_WARNING; #endif - -#pragma GCC diagnostic pop diff --git a/systemd/_journal.c b/systemd/_journal.c index 8cc6d3e..cbc661d 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -113,11 +113,9 @@ static PyMethodDef methods[] = { { NULL, NULL, 0, NULL } /* Sentinel */ }; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - #if PY_MAJOR_VERSION < 3 +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC init_journal(void) { PyObject *m; @@ -127,6 +125,7 @@ PyMODINIT_FUNC init_journal(void) { PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } +REENABLE_WARNING; #else @@ -138,6 +137,7 @@ static struct PyModuleDef module = { methods }; +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC PyInit__journal(void) { PyObject *m; @@ -152,7 +152,6 @@ PyMODINIT_FUNC PyInit__journal(void) { return m; } +REENABLE_WARNING; #endif - -#pragma GCC diagnostic pop diff --git a/systemd/_reader.c b/systemd/_reader.c index ee25c49..224a916 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1046,8 +1046,7 @@ static PyModuleDef module = { static bool initialized = false; #endif -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-prototypes" +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 @@ -1110,4 +1109,4 @@ init_reader(void) #endif } -#pragma GCC diagnostic pop +REENABLE_WARNING; diff --git a/systemd/id128.c b/systemd/id128.c index ec1d9fb..6dadf7b 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -24,6 +24,9 @@ #include #include "pyutil.h" +#include "log.h" +#include "util.h" +#include "macro.h" PyDoc_STRVAR(module__doc__, "Python interface to the libsystemd-id128 library.\n\n" @@ -108,11 +111,9 @@ static int add_id(PyObject *module, const char* name, sd_id128_t id) { return PyModule_AddObject(module, name, obj); } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - #if PY_MAJOR_VERSION < 3 +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC initid128(void) { PyObject *m; @@ -126,6 +127,7 @@ PyMODINIT_FUNC initid128(void) { #undef JOINER PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION); } +REENABLE_WARNING; #else @@ -137,6 +139,7 @@ static struct PyModuleDef module = { methods }; +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC PyInit_id128(void) { PyObject *m; @@ -155,7 +158,6 @@ PyMODINIT_FUNC PyInit_id128(void) { return m; } +REENABLE_WARNING; #endif - -#pragma GCC diagnostic pop diff --git a/systemd/login.c b/systemd/login.c index 43f7819..e844f5f 100644 --- a/systemd/login.c +++ b/systemd/login.c @@ -316,12 +316,9 @@ static PyTypeObject MonitorType = { .tp_new = PyType_GenericNew, }; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - #if PY_MAJOR_VERSION < 3 +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC initlogin(void) { PyObject *m; @@ -337,6 +334,8 @@ PyMODINIT_FUNC initlogin(void) { Py_INCREF(&MonitorType); PyModule_AddObject(m, "Monitor", (PyObject *) &MonitorType); } +REENABLE_WARNING; + #else static struct PyModuleDef module = { @@ -347,6 +346,7 @@ static struct PyModuleDef module = { methods }; +DISABLE_WARNING_MISSING_PROTOTYPES; PyMODINIT_FUNC PyInit_login(void) { PyObject *m; @@ -371,7 +371,6 @@ PyMODINIT_FUNC PyInit_login(void) { return m; } +REENABLE_WARNING; #endif - -#pragma GCC diagnostic pop -- cgit v1.2.1 From f7cf47eb64f10c58323e814f25e1f89a38e1d25e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 20 Feb 2014 19:14:52 +0100 Subject: python: reindent _reader.c All files should follow our coding style, and that means 8ch indenting. Let's correct that. --- systemd/_reader.c | 1159 ++++++++++++++++++++++++++--------------------------- 1 file changed, 577 insertions(+), 582 deletions(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 224a916..059b904 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -34,12 +34,11 @@ #include "build.h" typedef struct { - PyObject_HEAD - sd_journal *j; + PyObject_HEAD + sd_journal *j; } Reader; static PyTypeObject ReaderType; - PyDoc_STRVAR(module__doc__, "Class to reads the systemd journal similar to journalctl."); @@ -51,16 +50,16 @@ PyDoc_STRVAR(MonotonicType__doc__, "A tuple of (timestamp, bootid) for holding monotonic timestamps"); static PyStructSequence_Field MonotonicType_fields[] = { - {(char*) "timestamp", (char*) "Time"}, - {(char*) "bootid", (char*) "Unique identifier of the boot"}, - {} /* Sentinel */ + {(char*) "timestamp", (char*) "Time"}, + {(char*) "bootid", (char*) "Unique identifier of the boot"}, + {} /* Sentinel */ }; static PyStructSequence_Desc Monotonic_desc = { - (char*) "journal.Monotonic", - MonotonicType__doc__, - MonotonicType_fields, - 2, + (char*) "journal.Monotonic", + MonotonicType__doc__, + MonotonicType_fields, + 2, }; #endif @@ -75,49 +74,49 @@ static int strv_converter(PyObject* obj, void *_result) { assert(result); if (!obj) - return 0; + return 0; if (obj == Py_None) { - *result = NULL; - return 1; + *result = NULL; + return 1; } if (!PySequence_Check(obj)) - return 0; + return 0; len = PySequence_Length(obj); *result = new0(char*, len + 1); if (!*result) { - set_error(-ENOMEM, NULL, NULL); - return 0; + set_error(-ENOMEM, NULL, NULL); + return 0; } for (i = 0; i < len; i++) { - PyObject *item; + PyObject *item; #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 - int r; - PyObject *bytes; + int r; + PyObject *bytes; #endif - char *s, *s2; + char *s, *s2; - item = PySequence_ITEM(obj, i); + item = PySequence_ITEM(obj, i); #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1 - r = PyUnicode_FSConverter(item, &bytes); - if (r == 0) - goto cleanup; + r = PyUnicode_FSConverter(item, &bytes); + if (r == 0) + goto cleanup; - s = PyBytes_AsString(bytes); + s = PyBytes_AsString(bytes); #else - s = PyString_AsString(item); + s = PyString_AsString(item); #endif - if (!s) - goto cleanup; + if (!s) + goto cleanup; - s2 = strdup(s); - if (!s2) - log_oom(); + s2 = strdup(s); + if (!s2) + log_oom(); - (*result)[i] = s2; + (*result)[i] = s2; } return 1; @@ -130,8 +129,8 @@ cleanup: } static void Reader_dealloc(Reader* self) { - sd_journal_close(self->j); - Py_TYPE(self)->tp_free((PyObject*)self); + sd_journal_close(self->j); + Py_TYPE(self)->tp_free((PyObject*)self); } PyDoc_STRVAR(Reader__doc__, @@ -151,35 +150,34 @@ PyDoc_STRVAR(Reader__doc__, "_Reader implements the context manager protocol: the journal\n" "will be closed when exiting the block."); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { - int flags = 0, r; - char *path = NULL; - char **files = NULL; - - static const char* const kwlist[] = {"flags", "path", "files", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist, - &flags, &path, strv_converter, &files)) - return -1; - - if (!!flags + !!path + !!files > 1) { - PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files"); - return -1; - } - - if (!flags) - flags = SD_JOURNAL_LOCAL_ONLY; - - Py_BEGIN_ALLOW_THREADS - if (path) - r = sd_journal_open_directory(&self->j, path, 0); - else if (files) - r = sd_journal_open_files(&self->j, (const char**) files, 0); - else - r = sd_journal_open(&self->j, flags); - Py_END_ALLOW_THREADS - - return set_error(r, path, "Invalid flags or path"); -} + int flags = 0, r; + char *path = NULL; + char **files = NULL; + + static const char* const kwlist[] = {"flags", "path", "files", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist, + &flags, &path, strv_converter, &files)) + return -1; + + if (!!flags + !!path + !!files > 1) { + PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files"); + return -1; + } + if (!flags) + flags = SD_JOURNAL_LOCAL_ONLY; + + Py_BEGIN_ALLOW_THREADS + if (path) + r = sd_journal_open_directory(&self->j, path, 0); + else if (files) + r = sd_journal_open_files(&self->j, (const char**) files, 0); + else + r = sd_journal_open(&self->j, flags); + Py_END_ALLOW_THREADS + + return set_error(r, path, "Invalid flags or path"); +} PyDoc_STRVAR(Reader_fileno__doc__, "fileno() -> int\n\n" @@ -187,13 +185,14 @@ PyDoc_STRVAR(Reader_fileno__doc__, "This method invokes sd_journal_get_fd().\n" "See man:sd_journal_get_fd(3)."); static PyObject* Reader_fileno(Reader *self, PyObject *args) { - int fd = sd_journal_get_fd(self->j); - set_error(fd, NULL, NULL); - if (fd < 0) - return NULL; - return long_FromLong(fd); -} + int fd; + fd = sd_journal_get_fd(self->j); + set_error(fd, NULL, NULL); + if (fd < 0) + return NULL; + return long_FromLong(fd); +} PyDoc_STRVAR(Reader_reliable_fd__doc__, "reliable_fd() -> bool\n\n" @@ -201,12 +200,13 @@ PyDoc_STRVAR(Reader_reliable_fd__doc__, "This method invokes sd_journal_reliable_fd().\n" "See man:sd_journal_reliable_fd(3)."); static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) { - int r = sd_journal_reliable_fd(self->j); - if (set_error(r, NULL, NULL) < 0) - return NULL; - return PyBool_FromLong(r); -} + int r; + r = sd_journal_reliable_fd(self->j); + if (set_error(r, NULL, NULL) < 0) + return NULL; + return PyBool_FromLong(r); +} PyDoc_STRVAR(Reader_get_events__doc__, "get_events() -> int\n\n" @@ -214,12 +214,13 @@ PyDoc_STRVAR(Reader_get_events__doc__, "descriptor returned by .fileno().\n\n" "See man:sd_journal_get_events(3) for further discussion."); static PyObject* Reader_get_events(Reader *self, PyObject *args) { - int r = sd_journal_get_events(self->j); - if (set_error(r, NULL, NULL) < 0) - return NULL; - return long_FromLong(r); -} + int r; + r = sd_journal_get_events(self->j); + if (set_error(r, NULL, NULL) < 0) + return NULL; + return long_FromLong(r); +} PyDoc_STRVAR(Reader_get_timeout__doc__, "get_timeout() -> int or None\n\n" @@ -230,53 +231,50 @@ PyDoc_STRVAR(Reader_get_timeout__doc__, "milliseconds if it is to be used as an argument for poll().\n" "See man:sd_journal_get_timeout(3) for further discussion."); static PyObject* Reader_get_timeout(Reader *self, PyObject *args) { - int r; - uint64_t t; + int r; + uint64_t t; - r = sd_journal_get_timeout(self->j, &t); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_timeout(self->j, &t); + if (set_error(r, NULL, NULL) < 0) + return NULL; - if (t == (uint64_t) -1) - Py_RETURN_NONE; + if (t == (uint64_t) -1) + Py_RETURN_NONE; - assert_cc(sizeof(unsigned long long) == sizeof(t)); - return PyLong_FromUnsignedLongLong(t); + assert_cc(sizeof(unsigned long long) == sizeof(t)); + return PyLong_FromUnsignedLongLong(t); } - PyDoc_STRVAR(Reader_get_timeout_ms__doc__, "get_timeout_ms() -> int\n\n" "Returns a timeout value suitable for usage in poll(), the value\n" "returned by .get_timeout() converted to relative ms, or -1 if\n" "no timeout is necessary."); static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) { - int r; - uint64_t t; + int r; + uint64_t t; - r = sd_journal_get_timeout(self->j, &t); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_timeout(self->j, &t); + if (set_error(r, NULL, NULL) < 0) + return NULL; - return absolute_timeout(t); + return absolute_timeout(t); } - 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)."); static PyObject* Reader_close(Reader *self, PyObject *args) { - assert(self); - assert(!args); + assert(self); + assert(!args); - sd_journal_close(self->j); - self->j = NULL; - Py_RETURN_NONE; + sd_journal_close(self->j); + self->j = NULL; + Py_RETURN_NONE; } - PyDoc_STRVAR(Reader_get_usage__doc__, "get_usage() -> int\n\n" "Returns the total disk space currently used by journal\n" @@ -287,28 +285,27 @@ PyDoc_STRVAR(Reader_get_usage__doc__, "This method invokes sd_journal_get_usage().\n" "See man:sd_journal_get_usage(3)."); static PyObject* Reader_get_usage(Reader *self, PyObject *args) { - int r; - uint64_t bytes; + int r; + uint64_t bytes; - r = sd_journal_get_usage(self->j, &bytes); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_usage(self->j, &bytes); + if (set_error(r, NULL, NULL) < 0) + return NULL; - assert_cc(sizeof(unsigned long long) == sizeof(bytes)); - return PyLong_FromUnsignedLongLong(bytes); + assert_cc(sizeof(unsigned long long) == sizeof(bytes)); + return PyLong_FromUnsignedLongLong(bytes); } - PyDoc_STRVAR(Reader___enter____doc__, "__enter__() -> self\n\n" "Part of the context manager protocol.\n" "Returns self.\n"); static PyObject* Reader___enter__(PyObject *self, PyObject *args) { - assert(self); - assert(!args); + assert(self); + assert(!args); - Py_INCREF(self); - return self; + Py_INCREF(self); + return self; } PyDoc_STRVAR(Reader___exit____doc__, @@ -316,43 +313,42 @@ PyDoc_STRVAR(Reader___exit____doc__, "Part of the context manager protocol.\n" "Closes the journal.\n"); static PyObject* Reader___exit__(Reader *self, PyObject *args) { - return Reader_close(self, args); + return Reader_close(self, args); } - PyDoc_STRVAR(Reader_next__doc__, "next([skip]) -> bool\n\n" "Go to the next log entry. Optional skip value means to go to\n" "the `skip`\\-th log entry.\n" "Returns False if at end of file, True otherwise."); static PyObject* Reader_next(Reader *self, PyObject *args) { - int64_t skip = 1LL; - int r; + int64_t skip = 1LL; + int r; - if (!PyArg_ParseTuple(args, "|L:next", &skip)) - return NULL; + if (!PyArg_ParseTuple(args, "|L:next", &skip)) + return NULL; - if (skip == 0LL) { - PyErr_SetString(PyExc_ValueError, "skip must be nonzero"); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - if (skip == 1LL) - r = sd_journal_next(self->j); - else if (skip == -1LL) - r = sd_journal_previous(self->j); - else if (skip > 1LL) - r = sd_journal_next_skip(self->j, skip); - else if (skip < -1LL) - r = sd_journal_previous_skip(self->j, -skip); - else - assert_not_reached("should not be here"); - Py_END_ALLOW_THREADS - - if (set_error(r, NULL, NULL) < 0) - return NULL; - return PyBool_FromLong(r); + if (skip == 0LL) { + PyErr_SetString(PyExc_ValueError, "skip must be nonzero"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (skip == 1LL) + r = sd_journal_next(self->j); + else if (skip == -1LL) + r = sd_journal_previous(self->j); + else if (skip > 1LL) + r = sd_journal_next_skip(self->j, skip); + else if (skip < -1LL) + r = sd_journal_previous_skip(self->j, -skip); + else + assert_not_reached("should not be here"); + Py_END_ALLOW_THREADS + + if (set_error(r, NULL, NULL) < 0) + return NULL; + return PyBool_FromLong(r); } PyDoc_STRVAR(Reader_previous__doc__, @@ -361,48 +357,47 @@ PyDoc_STRVAR(Reader_previous__doc__, "go to the `skip`\\-th previous log entry.\n" "Returns False if at start of file, True otherwise."); static PyObject* Reader_previous(Reader *self, PyObject *args) { - int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L:previous", &skip)) - return NULL; + int64_t skip = 1LL; + if (!PyArg_ParseTuple(args, "|L:previous", &skip)) + return NULL; - return PyObject_CallMethod((PyObject *)self, (char*) "_next", - (char*) "L", -skip); + return PyObject_CallMethod((PyObject *)self, (char*) "_next", + (char*) "L", -skip); } - static int extract(const char* msg, size_t msg_len, PyObject **key, PyObject **value) { - PyObject *k = NULL, *v; - const char *delim_ptr; - - delim_ptr = memchr(msg, '=', msg_len); - if (!delim_ptr) { - PyErr_SetString(PyExc_OSError, - "journal gave us a field without '='"); - return -1; - } - - if (key) { - k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); - if (!k) - return -1; - } - - if (value) { - v = PyBytes_FromStringAndSize(delim_ptr + 1, - (const char*) msg + msg_len - (delim_ptr + 1)); - if (!v) { - Py_XDECREF(k); - return -1; + PyObject *k = NULL, *v; + const char *delim_ptr; + + delim_ptr = memchr(msg, '=', msg_len); + if (!delim_ptr) { + PyErr_SetString(PyExc_OSError, + "journal gave us a field without '='"); + return -1; } - *value = v; - } + if (key) { + k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg); + if (!k) + return -1; + } - if (key) - *key = k; + if (value) { + v = PyBytes_FromStringAndSize(delim_ptr + 1, + (const char*) msg + msg_len - (delim_ptr + 1)); + if (!v) { + Py_XDECREF(k); + return -1; + } - return 0; + *value = v; + } + + if (key) + *key = k; + + return 0; } PyDoc_STRVAR(Reader_get__doc__, @@ -410,92 +405,90 @@ PyDoc_STRVAR(Reader_get__doc__, "Return data associated with this key in current log entry.\n" "Throws KeyError is the data is not available."); static PyObject* Reader_get(Reader *self, PyObject *args) { - const char* field; - const void* msg; - size_t msg_len; - PyObject *value; - int r; - - assert(self); - assert(args); - - if (!PyArg_ParseTuple(args, "s:get", &field)) - return NULL; - - r = sd_journal_get_data(self->j, field, &msg, &msg_len); - if (r == -ENOENT) { - PyErr_SetString(PyExc_KeyError, field); - return NULL; - } - if (set_error(r, NULL, "field name is not valid") < 0) - return NULL; + const char* field; + const void* msg; + size_t msg_len; + PyObject *value; + int r; + + assert(self); + assert(args); + + if (!PyArg_ParseTuple(args, "s:get", &field)) + return NULL; + + r = sd_journal_get_data(self->j, field, &msg, &msg_len); + if (r == -ENOENT) { + PyErr_SetString(PyExc_KeyError, field); + return NULL; + } + if (set_error(r, NULL, "field name is not valid") < 0) + return NULL; - r = extract(msg, msg_len, NULL, &value); - if (r < 0) - return NULL; - return value; + r = extract(msg, msg_len, NULL, &value); + if (r < 0) + return NULL; + return value; } - PyDoc_STRVAR(Reader_get_all__doc__, "_get_all() -> dict\n\n" "Return dictionary of the current log entry."); static PyObject* Reader_get_all(Reader *self, PyObject *args) { - PyObject *dict; - const void *msg; - size_t msg_len; - int r; + PyObject *dict; + const void *msg; + size_t msg_len; + int r; - dict = PyDict_New(); - if (!dict) - return NULL; + dict = PyDict_New(); + if (!dict) + return NULL; - SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { - _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL; + SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) { + _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL; - r = extract(msg, msg_len, &key, &value); - if (r < 0) - goto error; - - if (PyDict_Contains(dict, key)) { - PyObject *cur_value = PyDict_GetItem(dict, key); - - if (PyList_CheckExact(cur_value)) { - r = PyList_Append(cur_value, value); - if (r < 0) - goto error; - } else { - _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0); - if (!tmp_list) - goto error; - - r = PyList_Append(tmp_list, cur_value); - if (r < 0) - goto error; - - r = PyList_Append(tmp_list, value); - if (r < 0) - goto error; - - r = PyDict_SetItem(dict, key, tmp_list); + r = extract(msg, msg_len, &key, &value); if (r < 0) - goto error; - } - } else { - r = PyDict_SetItem(dict, key, value); - if (r < 0) - goto error; + goto error; + + if (PyDict_Contains(dict, key)) { + PyObject *cur_value = PyDict_GetItem(dict, key); + + if (PyList_CheckExact(cur_value)) { + r = PyList_Append(cur_value, value); + if (r < 0) + goto error; + } else { + _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0); + if (!tmp_list) + goto error; + + r = PyList_Append(tmp_list, cur_value); + if (r < 0) + goto error; + + r = PyList_Append(tmp_list, value); + if (r < 0) + goto error; + + r = PyDict_SetItem(dict, key, tmp_list); + if (r < 0) + goto error; + } + } else { + r = PyDict_SetItem(dict, key, value); + if (r < 0) + goto error; + } } - } - return dict; + return dict; error: - Py_DECREF(dict); - return NULL; + Py_DECREF(dict); + return NULL; } - PyDoc_STRVAR(Reader_get_realtime__doc__, "get_realtime() -> int\n\n" "Return the realtime timestamp for the current journal entry\n" @@ -503,21 +496,20 @@ PyDoc_STRVAR(Reader_get_realtime__doc__, "Wraps sd_journal_get_realtime_usec().\n" "See man:sd_journal_get_realtime_usec(3)."); static PyObject* Reader_get_realtime(Reader *self, PyObject *args) { - uint64_t timestamp; - int r; + uint64_t timestamp; + int r; - assert(self); - assert(!args); + assert(self); + assert(!args); - r = sd_journal_get_realtime_usec(self->j, ×tamp); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_realtime_usec(self->j, ×tamp); + if (set_error(r, NULL, NULL) < 0) + return NULL; - assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); - return PyLong_FromUnsignedLongLong(timestamp); + assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); + return PyLong_FromUnsignedLongLong(timestamp); } - PyDoc_STRVAR(Reader_get_monotonic__doc__, "get_monotonic() -> (timestamp, bootid)\n\n" "Return the monotonic timestamp for the current journal entry\n" @@ -525,42 +517,42 @@ PyDoc_STRVAR(Reader_get_monotonic__doc__, "Wraps sd_journal_get_monotonic_usec().\n" "See man:sd_journal_get_monotonic_usec(3)."); static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) { - uint64_t timestamp; - sd_id128_t id; - PyObject *monotonic, *bootid, *tuple; - int r; + uint64_t timestamp; + sd_id128_t id; + PyObject *monotonic, *bootid, *tuple; + int r; - assert(self); - assert(!args); + assert(self); + assert(!args); - r = sd_journal_get_monotonic_usec(self->j, ×tamp, &id); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_monotonic_usec(self->j, ×tamp, &id); + if (set_error(r, NULL, NULL) < 0) + return NULL; - assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); - monotonic = PyLong_FromUnsignedLongLong(timestamp); - bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); + assert_cc(sizeof(unsigned long long) == sizeof(timestamp)); + monotonic = PyLong_FromUnsignedLongLong(timestamp); + bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes)); #if PY_MAJOR_VERSION >= 3 - tuple = PyStructSequence_New(&MonotonicType); + tuple = PyStructSequence_New(&MonotonicType); #else - tuple = PyTuple_New(2); + tuple = PyTuple_New(2); #endif - if (!monotonic || !bootid || !tuple) { - Py_XDECREF(monotonic); - Py_XDECREF(bootid); - Py_XDECREF(tuple); - return NULL; - } + if (!monotonic || !bootid || !tuple) { + Py_XDECREF(monotonic); + Py_XDECREF(bootid); + Py_XDECREF(tuple); + return NULL; + } #if PY_MAJOR_VERSION >= 3 - PyStructSequence_SET_ITEM(tuple, 0, monotonic); - PyStructSequence_SET_ITEM(tuple, 1, bootid); + PyStructSequence_SET_ITEM(tuple, 0, monotonic); + PyStructSequence_SET_ITEM(tuple, 1, bootid); #else - PyTuple_SET_ITEM(tuple, 0, monotonic); - PyTuple_SET_ITEM(tuple, 1, bootid); + PyTuple_SET_ITEM(tuple, 0, monotonic); + PyTuple_SET_ITEM(tuple, 1, bootid); #endif - return tuple; + return tuple; } PyDoc_STRVAR(Reader_add_match__doc__, @@ -570,19 +562,18 @@ PyDoc_STRVAR(Reader_add_match__doc__, "are automatically combined with logical OR.\n" "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; - if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) - return NULL; + char *match; + int match_len, r; + if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len)) + return NULL; - r = sd_journal_add_match(self->j, match, match_len); - if (set_error(r, NULL, "Invalid match") < 0) - return NULL; + r = sd_journal_add_match(self->j, match, match_len); + if (set_error(r, NULL, "Invalid match") < 0) + return NULL; - Py_RETURN_NONE; + Py_RETURN_NONE; } - PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() -> None\n\n" "Inserts a logical OR between matches added since previous\n" @@ -590,14 +581,13 @@ PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() or add_conjunction().\n\n" "See man: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); - if (set_error(r, NULL, NULL) < 0) - return NULL; - Py_RETURN_NONE; + int r; + r = sd_journal_add_disjunction(self->j); + if (set_error(r, NULL, NULL) < 0) + return NULL; + Py_RETURN_NONE; } - PyDoc_STRVAR(Reader_add_conjunction__doc__, "add_conjunction() -> None\n\n" "Inserts a logical AND between matches added since previous\n" @@ -605,38 +595,37 @@ PyDoc_STRVAR(Reader_add_conjunction__doc__, "add_disjunction() or add_conjunction().\n\n" "See man: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); - if (set_error(r, NULL, NULL) < 0) - return NULL; - Py_RETURN_NONE; + int r; + r = sd_journal_add_conjunction(self->j); + if (set_error(r, NULL, NULL) < 0) + return NULL; + Py_RETURN_NONE; } - PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); static PyObject* Reader_flush_matches(Reader *self, PyObject *args) { - sd_journal_flush_matches(self->j); - Py_RETURN_NONE; + sd_journal_flush_matches(self->j); + Py_RETURN_NONE; } - 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)."); static PyObject* Reader_seek_head(Reader *self, PyObject *args) { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_head(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; - Py_RETURN_NONE; -} + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_head(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL) < 0) + return NULL; + + Py_RETURN_NONE; +} PyDoc_STRVAR(Reader_seek_tail__doc__, "seek_tail() -> None\n\n" @@ -644,35 +633,37 @@ PyDoc_STRVAR(Reader_seek_tail__doc__, "This method invokes sd_journal_seek_tail().\n" "See man:sd_journal_seek_tail(3)."); static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_tail(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; - Py_RETURN_NONE; -} + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_tail(self->j); + Py_END_ALLOW_THREADS + + if (set_error(r, NULL, NULL) < 0) + return NULL; + Py_RETURN_NONE; +} PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" "`realtime` in specified in seconds."); static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { - uint64_t timestamp; - int r; + uint64_t timestamp; + int r; - if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) - return NULL; + if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) + return NULL; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_realtime_usec(self->j, timestamp); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; - Py_RETURN_NONE; -} + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_realtime_usec(self->j, timestamp); + Py_END_ALLOW_THREADS + + if (set_error(r, NULL, NULL) < 0) + return NULL; + Py_RETURN_NONE; +} PyDoc_STRVAR(Reader_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" @@ -681,33 +672,35 @@ PyDoc_STRVAR(Reader_seek_monotonic__doc__, "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { - char *bootid = NULL; - uint64_t timestamp; - sd_id128_t id; - int r; + char *bootid = NULL; + uint64_t timestamp; + sd_id128_t id; + int r; + + if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", ×tamp, &bootid)) + return NULL; + + if (bootid) { + r = sd_id128_from_string(bootid, &id); + if (set_error(r, NULL, "Invalid bootid") < 0) + return NULL; + } else { + Py_BEGIN_ALLOW_THREADS + r = sd_id128_get_boot(&id); + Py_END_ALLOW_THREADS - if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", ×tamp, &bootid)) - return NULL; + if (set_error(r, NULL, NULL) < 0) + return NULL; + } - if (bootid) { - r = sd_id128_from_string(bootid, &id); - if (set_error(r, NULL, "Invalid bootid") < 0) - return NULL; - } else { Py_BEGIN_ALLOW_THREADS - r = sd_id128_get_boot(&id); + r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; - } - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; + if (set_error(r, NULL, NULL) < 0) + return NULL; - Py_RETURN_NONE; + Py_RETURN_NONE; } @@ -720,20 +713,19 @@ PyDoc_STRVAR(Reader_process__doc__, "INVALIDATE if journal files have been added or removed.\n\n" "See man:sd_journal_process(3) for further discussion."); static PyObject* Reader_process(Reader *self, PyObject *args) { - int r; + int r; - assert(!args); + assert(!args); - Py_BEGIN_ALLOW_THREADS - r = sd_journal_process(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_process(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL) < 0) + return NULL; - return long_FromLong(r); + return long_FromLong(r); } - PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" @@ -745,79 +737,79 @@ PyDoc_STRVAR(Reader_wait__doc__, "INVALIDATE if journal files have been added or removed.\n\n" "See man:sd_journal_wait(3) for further discussion."); static PyObject* Reader_wait(Reader *self, PyObject *args) { - int r; - int64_t timeout; + int r; + int64_t timeout; - if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) - return NULL; + if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) + return NULL; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_wait(self->j, timeout); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_wait(self->j, timeout); + Py_END_ALLOW_THREADS - return long_FromLong(r); -} + if (set_error(r, NULL, NULL) < 0) + return NULL; + return long_FromLong(r); +} PyDoc_STRVAR(Reader_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" "Seek to journal entry by given unique reference `cursor`."); static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { - const char *cursor; - int r; + const char *cursor; + int r; - if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) - return NULL; + if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) + return NULL; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_cursor(self->j, cursor); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, "Invalid cursor") < 0) - return NULL; - Py_RETURN_NONE; -} + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_cursor(self->j, cursor); + Py_END_ALLOW_THREADS + + if (set_error(r, NULL, "Invalid cursor") < 0) + return NULL; + Py_RETURN_NONE; +} 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)."); static PyObject* Reader_get_cursor(Reader *self, PyObject *args) { - _cleanup_free_ char *cursor = NULL; - int r; + _cleanup_free_ char *cursor = NULL; + int r; - assert(self); - assert(!args); + assert(self); + assert(!args); - r = sd_journal_get_cursor(self->j, &cursor); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_cursor(self->j, &cursor); + if (set_error(r, NULL, NULL) < 0) + return NULL; - return unicode_FromString(cursor); + return unicode_FromString(cursor); } - 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)."); static PyObject* Reader_test_cursor(Reader *self, PyObject *args) { - const char *cursor; - int r; + const char *cursor; + int r; - assert(self); - assert(args); + assert(self); + assert(args); - if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor)) - return NULL; + if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor)) + return NULL; - r = sd_journal_test_cursor(self->j, cursor); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_test_cursor(self->j, cursor); + if (set_error(r, NULL, NULL) < 0) + return NULL; - return PyBool_FromLong(r); + return PyBool_FromLong(r); } PyDoc_STRVAR(Reader_query_unique__doc__, @@ -825,38 +817,39 @@ PyDoc_STRVAR(Reader_query_unique__doc__, "Return a set of unique values appearing in journal for the\n" "given `field`. Note this does not respect any journal matches."); static PyObject* Reader_query_unique(Reader *self, PyObject *args) { - char *query; - int r; - const void *uniq; - size_t uniq_len; - PyObject *value_set, *key, *value; + char *query; + int r; + const void *uniq; + size_t uniq_len; + PyObject *value_set, *key, *value; - if (!PyArg_ParseTuple(args, "s:query_unique", &query)) - return NULL; + if (!PyArg_ParseTuple(args, "s:query_unique", &query)) + return NULL; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_query_unique(self->j, query); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, "Invalid field name") < 0) - return NULL; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_query_unique(self->j, query); + Py_END_ALLOW_THREADS - value_set = PySet_New(0); - key = unicode_FromString(query); + if (set_error(r, NULL, "Invalid field name") < 0) + return NULL; - SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { - const char *delim_ptr; + value_set = PySet_New(0); + key = unicode_FromString(query); - delim_ptr = memchr(uniq, '=', uniq_len); - value = PyBytes_FromStringAndSize( - delim_ptr + 1, - (const char*) uniq + uniq_len - (delim_ptr + 1)); - PySet_Add(value_set, value); - Py_DECREF(value); - } - Py_DECREF(key); - return value_set; -} + SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) { + const char *delim_ptr; + delim_ptr = memchr(uniq, '=', uniq_len); + value = PyBytes_FromStringAndSize( + delim_ptr + 1, + (const char*) uniq + uniq_len - (delim_ptr + 1)); + PySet_Add(value_set, value); + Py_DECREF(value); + } + + Py_DECREF(key); + return value_set; +} PyDoc_STRVAR(Reader_get_catalog__doc__, "get_catalog() -> str\n\n" @@ -866,67 +859,68 @@ PyDoc_STRVAR(Reader_get_catalog__doc__, "in the catalog.\n\n" "Wraps man:sd_journal_get_catalog(3)."); static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { - int r; - _cleanup_free_ char *msg = NULL; - - assert(self); - assert(!args); - - Py_BEGIN_ALLOW_THREADS - r = sd_journal_get_catalog(self->j, &msg); - Py_END_ALLOW_THREADS - if (r == -ENOENT) { - const void* mid; - size_t mid_len; - - r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len); - if (r == 0) { - const size_t l = sizeof("MESSAGE_ID"); - assert(mid_len > l); - PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l), - (const char*) mid + l); - } else if (r == -ENOENT) - PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field"); - else - set_error(r, NULL, NULL); - return NULL; - } - if (set_error(r, NULL, NULL) < 0) - return NULL; + int r; + _cleanup_free_ char *msg = NULL; - return unicode_FromString(msg); -} + assert(self); + assert(!args); + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog(self->j, &msg); + Py_END_ALLOW_THREADS + + if (r == -ENOENT) { + const void* mid; + size_t mid_len; + + r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len); + if (r == 0) { + const size_t l = sizeof("MESSAGE_ID"); + assert(mid_len > l); + PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l), + (const char*) mid + l); + } else if (r == -ENOENT) + PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field"); + else + set_error(r, NULL, NULL); + return NULL; + } + if (set_error(r, NULL, NULL) < 0) + return NULL; + + return unicode_FromString(msg); +} 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)."); static PyObject* get_catalog(PyObject *self, PyObject *args) { - int r; - char *id_ = NULL; - sd_id128_t id; - _cleanup_free_ char *msg = NULL; + int r; + char *id_ = NULL; + sd_id128_t id; + _cleanup_free_ char *msg = NULL; - assert(!self); - assert(args); + assert(!self); + assert(args); - if (!PyArg_ParseTuple(args, "z:get_catalog", &id_)) - return NULL; + if (!PyArg_ParseTuple(args, "z:get_catalog", &id_)) + return NULL; - r = sd_id128_from_string(id_, &id); - if (set_error(r, NULL, "Invalid id128") < 0) - return NULL; + r = sd_id128_from_string(id_, &id); + if (set_error(r, NULL, "Invalid id128") < 0) + return NULL; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_get_catalog_for_message_id(id, &msg); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL) < 0) - return NULL; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog_for_message_id(id, &msg); + Py_END_ALLOW_THREADS - return unicode_FromString(msg); -} + if (set_error(r, NULL, NULL) < 0) + return NULL; + return unicode_FromString(msg); +} PyDoc_STRVAR(data_threshold__doc__, "Threshold for field size truncation in bytes.\n\n" @@ -934,97 +928,98 @@ PyDoc_STRVAR(data_threshold__doc__, "Defaults to 64Kb."); static PyObject* Reader_get_data_threshold(Reader *self, void *closure) { - size_t cvalue; - int r; + size_t cvalue; + int r; - r = sd_journal_get_data_threshold(self->j, &cvalue); - if (set_error(r, NULL, NULL) < 0) - return NULL; + r = sd_journal_get_data_threshold(self->j, &cvalue); + if (set_error(r, NULL, NULL) < 0) + return NULL; - return long_FromSize_t(cvalue); + return long_FromSize_t(cvalue); } static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) { - int r; - if (value == NULL) { - PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); - return -1; - } - if (!long_Check(value)){ - PyErr_SetString(PyExc_TypeError, "Data threshold must be an int"); - return -1; - } - r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value)); - return set_error(r, NULL, NULL); -} + int r; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold"); + return -1; + } + + if (!long_Check(value)){ + PyErr_SetString(PyExc_TypeError, "Data threshold must be an int"); + return -1; + } + + r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value)); + return set_error(r, NULL, NULL); +} 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 == NULL); } - static PyGetSetDef Reader_getsetters[] = { - {(char*) "data_threshold", - (getter) Reader_get_data_threshold, - (setter) Reader_set_data_threshold, - (char*) data_threshold__doc__, - NULL}, - {(char*) "closed", - (getter) Reader_get_closed, - NULL, - (char*) closed__doc__, - NULL}, - {} /* Sentinel */ + { (char*) "data_threshold", + (getter) Reader_get_data_threshold, + (setter) Reader_set_data_threshold, + (char*) data_threshold__doc__, + NULL }, + { (char*) "closed", + (getter) Reader_get_closed, + NULL, + (char*) closed__doc__, + NULL }, + {} /* Sentinel */ }; static PyMethodDef Reader_methods[] = { - {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, - {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, - {"get_events", (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__}, - {"get_timeout", (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__}, - {"get_timeout_ms", (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__}, - {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, - {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, - {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, - {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, - {"_next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, - {"_previous", (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__}, - {"_get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, - {"_get_all", (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__}, - {"_get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, - {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, - {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, - {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, - {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__}, - {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, - {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__}, - {"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__}, - {"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__}, - {"_get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, - {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__}, - {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, - {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, - {} /* Sentinel */ + {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, + {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, + {"get_events", (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__}, + {"get_timeout", (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__}, + {"get_timeout_ms", (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__}, + {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, + {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, + {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, + {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, + {"_next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__}, + {"_previous", (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__}, + {"_get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__}, + {"_get_all", (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__}, + {"_get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__}, + {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__}, + {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, + {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, + {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__}, + {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, + {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__}, + {"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__}, + {"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__}, + {"_get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__}, + {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__}, + {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, + {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, + {} /* Sentinel */ }; static PyTypeObject ReaderType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "_reader._Reader", - .tp_basicsize = sizeof(Reader), - .tp_dealloc = (destructor) Reader_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = Reader__doc__, - .tp_methods = Reader_methods, - .tp_getset = Reader_getsetters, - .tp_init = (initproc) Reader_init, - .tp_new = PyType_GenericNew, + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "_reader._Reader", + .tp_basicsize = sizeof(Reader), + .tp_dealloc = (destructor) Reader_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Reader__doc__, + .tp_methods = Reader_methods, + .tp_getset = Reader_getsetters, + .tp_init = (initproc) Reader_init, + .tp_new = PyType_GenericNew, }; static PyMethodDef methods[] = { @@ -1034,11 +1029,11 @@ static PyMethodDef methods[] = { #if PY_MAJOR_VERSION >= 3 static PyModuleDef module = { - PyModuleDef_HEAD_INIT, - "_reader", - module__doc__, - -1, - methods, + PyModuleDef_HEAD_INIT, + "_reader", + module__doc__, + -1, + methods, }; #endif @@ -1055,57 +1050,57 @@ PyInit__reader(void) init_reader(void) #endif { - PyObject* m; + PyObject* m; - PyDateTime_IMPORT; + PyDateTime_IMPORT; - if (PyType_Ready(&ReaderType) < 0) + if (PyType_Ready(&ReaderType) < 0) #if PY_MAJOR_VERSION >= 3 - return NULL; + return NULL; #else - return; + return; #endif #if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&module); - if (m == NULL) - return NULL; + m = PyModule_Create(&module); + if (m == NULL) + return NULL; - if (!initialized) { - PyStructSequence_InitType(&MonotonicType, &Monotonic_desc); - initialized = true; - } + if (!initialized) { + PyStructSequence_InitType(&MonotonicType, &Monotonic_desc); + initialized = true; + } #else - m = Py_InitModule3("_reader", methods, module__doc__); - if (m == NULL) - return; + m = Py_InitModule3("_reader", methods, module__doc__); + if (m == NULL) + return; #endif - Py_INCREF(&ReaderType); + Py_INCREF(&ReaderType); #if PY_MAJOR_VERSION >= 3 - Py_INCREF(&MonotonicType); + Py_INCREF(&MonotonicType); #endif - if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) || + if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) || #if PY_MAJOR_VERSION >= 3 - PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) || + PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) || #endif - PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) || - PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) || - PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || - 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, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) || - PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { + PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) || + PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) || + PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) || + 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, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) || + PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) { #if PY_MAJOR_VERSION >= 3 - Py_DECREF(m); - return NULL; + Py_DECREF(m); + return NULL; #endif - } + } #if PY_MAJOR_VERSION >= 3 - return m; + return m; #endif } -- cgit v1.2.1 From 583f1a94417bcfba3f32ad4969eddb5fb025bb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 5 Apr 2014 13:23:25 -0400 Subject: systemd-python: use .hex instead of .get_hex() It turns out the latter got removed in Python 3. https://bugs.freedesktop.org/show_bug.cgi?id=77086 --- systemd/journal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/systemd/journal.py b/systemd/journal.py index 9c7e004..dd1f229 100644 --- a/systemd/journal.py +++ b/systemd/journal.py @@ -293,7 +293,7 @@ class Reader(_Reader): monotonic = monotonic.totalseconds() monotonic = int(monotonic * 1000000) if isinstance(bootid, _uuid.UUID): - bootid = bootid.get_hex() + bootid = bootid.hex return super(Reader, self).seek_monotonic(monotonic, bootid) def log_level(self, level): @@ -314,7 +314,7 @@ class Reader(_Reader): Equivalent to add_match(MESSAGE_ID=`messageid`). """ if isinstance(messageid, _uuid.UUID): - messageid = messageid.get_hex() + messageid = messageid.hex self.add_match(MESSAGE_ID=messageid) def this_boot(self, bootid=None): @@ -346,7 +346,7 @@ class Reader(_Reader): def get_catalog(mid): if isinstance(mid, _uuid.UUID): - mid = mid.get_hex() + mid = mid.hex return _get_catalog(mid) def _make_line(field, value): -- cgit v1.2.1 From 4398250261f189dace230eceb18209fc6e37aef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 5 Apr 2014 13:29:50 -0400 Subject: systemd-python: fix failing assert A parameter which was always null before, now get's set to the module. --- systemd/_reader.c | 1 - 1 file changed, 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index 059b904..9a19a10 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -902,7 +902,6 @@ static PyObject* get_catalog(PyObject *self, PyObject *args) { sd_id128_t id; _cleanup_free_ char *msg = NULL; - assert(!self); assert(args); if (!PyArg_ParseTuple(args, "z:get_catalog", &id_)) -- cgit v1.2.1 From a68a57ed71e89c124d781bdae49f8adf8b900227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 31 Jul 2014 04:15:29 -0400 Subject: Always prefer our headers to system headers In practice this shouldn't make much difference, but sometimes our headers might be newer, and we want to test them. --- systemd/_daemon.c | 2 +- systemd/_journal.c | 2 +- systemd/_reader.c | 2 +- systemd/id128.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index 7756a78..65cfec7 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -29,7 +29,7 @@ #include #include -#include +#include "systemd/sd-daemon.h" #include "pyutil.h" #include "macro.h" diff --git a/systemd/_journal.c b/systemd/_journal.c index cbc661d..456e4a2 100644 --- a/systemd/_journal.c +++ b/systemd/_journal.c @@ -25,7 +25,7 @@ #include "util.h" #define SD_JOURNAL_SUPPRESS_LOCATION -#include +#include "systemd/sd-journal.h" PyDoc_STRVAR(journal_sendv__doc__, "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" diff --git a/systemd/_reader.c b/systemd/_reader.c index 9a19a10..d17aa83 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -25,7 +25,7 @@ #include #include -#include +#include "systemd/sd-journal.h" #include "pyutil.h" #include "macro.h" diff --git a/systemd/id128.c b/systemd/id128.c index 6dadf7b..5ec7309 100644 --- a/systemd/id128.c +++ b/systemd/id128.c @@ -21,7 +21,7 @@ #include -#include +#include "systemd/sd-messages.h" #include "pyutil.h" #include "log.h" -- cgit v1.2.1 From 7ea37c0d40bb23e44777925207dd4338037861f7 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 14 Oct 2014 07:54:56 -0400 Subject: python-systemd: avoid hitting assert in __exit__ Reader_close() asserts that 'args' is always NULL, but the __exit__ function forwards a non-NULL args. --- systemd/_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemd/_reader.c b/systemd/_reader.c index d17aa83..3a56126 100644 --- a/systemd/_reader.c +++ b/systemd/_reader.c @@ -313,7 +313,7 @@ PyDoc_STRVAR(Reader___exit____doc__, "Part of the context manager protocol.\n" "Closes the journal.\n"); static PyObject* Reader___exit__(Reader *self, PyObject *args) { - return Reader_close(self, args); + return Reader_close(self, NULL); } PyDoc_STRVAR(Reader_next__doc__, -- cgit v1.2.1 From d6c4257815a7d231aa55d4fa88d6cb374d98c3e8 Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Wed, 25 Mar 2015 17:00:09 +0000 Subject: python-systemd: fix is_socket_inet to cope with ports Just a couple of trivial oversights. --- systemd/_daemon.c | 2 +- systemd/daemon.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/_daemon.c b/systemd/_daemon.c index 65cfec7..7c5f1b2 100644 --- a/systemd/_daemon.c +++ b/systemd/_daemon.c @@ -225,7 +225,7 @@ static PyObject* is_socket_inet(PyObject *self, PyObject *args) { &fd, &family, &type, &listening, &port)) return NULL; - if (port < 0 || port > INT16_MAX) { + if (port < 0 || port > UINT16_MAX) { set_error(-EINVAL, NULL, "port must fit into uint16_t"); return NULL; } diff --git a/systemd/daemon.py b/systemd/daemon.py index 1c386bb..82011ca 100644 --- a/systemd/daemon.py +++ b/systemd/daemon.py @@ -26,7 +26,7 @@ def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1): def is_socket_inet(fileobj, family=_AF_UNSPEC, type=0, listening=-1, port=0): fd = _convert_fileobj(fileobj) - return _is_socket_inet(fd, family, type, listening) + return _is_socket_inet(fd, family, type, listening, port) def is_socket_unix(fileobj, type=0, listening=-1, path=None): fd = _convert_fileobj(fileobj) -- cgit v1.2.1 From 2a3fc158a5160e8da77852fc71ee49c6f359fd56 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 3 Jun 2015 00:09:23 +0200 Subject: remove gudev and gtk-doc The library moved to: https://git.gnome.org/browse/libgudev/ --- systemd/docs/layout.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/systemd/docs/layout.html b/systemd/docs/layout.html index be5ff98..930a6a7 100644 --- a/systemd/docs/layout.html +++ b/systemd/docs/layout.html @@ -4,8 +4,6 @@ Index · Directives · Python · - libudev · - gudev systemd {{release}}
{% endblock %} -- cgit v1.2.1