diff options
| author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2013-03-15 19:01:10 -0400 | 
|---|---|---|
| committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2013-03-15 22:55:24 -0400 | 
| commit | b04c8c83e8d5670b0923c7cd7d6ea622b0187289 (patch) | |
| tree | 9faeb93a6d086f1cb213535be5397fefe17b1581 | |
| parent | 5e65c93a433447b15180249166f7b3944c3e6156 (diff) | |
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.
| -rw-r--r-- | Makefile.am | 23 | ||||
| -rw-r--r-- | TODO | 1 | ||||
| -rw-r--r-- | src/python-systemd/_daemon.c | 323 | ||||
| -rw-r--r-- | src/python-systemd/_reader.c | 17 | ||||
| -rw-r--r-- | src/python-systemd/daemon.py | 53 | ||||
| -rw-r--r-- | src/python-systemd/docs/daemon.rst | 16 | ||||
| -rw-r--r-- | src/python-systemd/docs/index.rst | 1 | ||||
| -rw-r--r-- | src/python-systemd/pyutil.h | 19 | 
8 files changed, 436 insertions, 17 deletions
| diff --git a/Makefile.am b/Makefile.am index f687eca0cc..7e9cdfdb26 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3484,6 +3484,7 @@ if HAVE_PYTHON_DEVEL  pkgpyexec_LTLIBRARIES = \  	_journal.la \  	id128.la \ +	_daemon.la \  	_reader.la  _journal_la_SOURCES = \ @@ -3526,6 +3527,27 @@ id128_la_LIBADD = \  	$(PYTHON_LIBS) \  	libsystemd-id128.la +_daemon_la_SOURCES = \ +	src/python-systemd/_daemon.c \ +	src/python-systemd/pyutil.c \ +	src/python-systemd/pyutil.h + +_daemon_la_CFLAGS = \ +	$(AM_CFLAGS) \ +        -fvisibility=default \ +	$(PYTHON_CFLAGS) \ +	-I$(top_builddir)/src/python-systemd + +_daemon_la_LDFLAGS = \ +	$(AM_LDFLAGS) \ +	-shared \ +	-module \ +	-avoid-version + +_daemon_la_LIBADD = \ +	$(PYTHON_LIBS) \ +	libsystemd-daemon.la +  _reader_la_SOURCES = \  	src/python-systemd/_reader.c \  	src/python-systemd/pyutil.c \ @@ -3550,6 +3572,7 @@ _reader_la_LIBADD = \  dist_pkgpyexec_PYTHON = \  	src/python-systemd/journal.py \ +	src/python-systemd/daemon.py \  	src/python-systemd/__init__.py  src/python-systemd/id128-constants.h: src/systemd/sd-messages.h Makefile @@ -587,6 +587,7 @@ Features:       be just "return self->j != NULL")     - figure out a simple way to wait for journal events in a way that       works with ^C +   - add documentation to systemd.daemon  External: diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c new file mode 100644 index 0000000000..8f93d91b48 --- /dev/null +++ b/src/python-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 <zbyszek@in.waw.pl> + +  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 <http://www.gnu.org/licenses/>. +***/ + +#define PY_SSIZE_T_CLEAN +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#include <Python.h> +#pragma GCC diagnostic pop + +#include <stdbool.h> +#include <assert.h> +#include <sys/socket.h> + +#include <systemd/sd-daemon.h> +#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/src/python-systemd/_reader.c b/src/python-systemd/_reader.c index 160ab69a3c..96634a19c3 100644 --- a/src/python-systemd/_reader.c +++ b/src/python-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/src/python-systemd/daemon.py b/src/python-systemd/daemon.py new file mode 100644 index 0000000000..4a02204433 --- /dev/null +++ b/src/python-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/src/python-systemd/docs/daemon.rst b/src/python-systemd/docs/daemon.rst new file mode 100644 index 0000000000..72280ca2f0 --- /dev/null +++ b/src/python-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/src/python-systemd/docs/index.rst b/src/python-systemd/docs/index.rst index f04d5a181c..8a94d0750b 100644 --- a/src/python-systemd/docs/index.rst +++ b/src/python-systemd/docs/index.rst @@ -13,6 +13,7 @@ Contents:     journal     id128 +   daemon  Indices and tables  ================== diff --git a/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h index 3b7bc580df..2163fda9ef 100644 --- a/src/python-systemd/pyutil.h +++ b/src/python-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 | 
