From 7ecec4705c0cacb1446af0eb7a4aee66c00d058f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 9 May 2013 18:10:30 -0400 Subject: [PATCH] systemd-python: wrap sd_login_monitor --- man/sd_login_monitor_new.xml | 2 +- src/python-systemd/_daemon.c | 49 +++---- src/python-systemd/_reader.c | 22 +-- src/python-systemd/docs/journal.rst | 2 +- src/python-systemd/docs/login.rst | 23 +++ src/python-systemd/login.c | 209 ++++++++++++++++++++++++++++ src/python-systemd/pyutil.c | 14 ++ src/python-systemd/pyutil.h | 1 + 8 files changed, 269 insertions(+), 53 deletions(-) diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml index 26af0ea24..261ef1d9d 100644 --- a/man/sd_login_monitor_new.xml +++ b/man/sd_login_monitor_new.xml @@ -96,7 +96,7 @@ Description sd_login_monitor_new() may - be used to monitor login sessions, users, seats and + be used to monitor login sessions, users, seats, and virtual machines/containers. Via a monitor object a file descriptor can be integrated into an application defined event loop which is woken up each time a user diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c index d3b480736..c6b14d466 100644 --- a/src/python-systemd/_daemon.c +++ b/src/python-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/src/python-systemd/_reader.c b/src/python-systemd/_reader.c index d20c58d2a..c4c4fdfe1 100644 --- a/src/python-systemd/_reader.c +++ b/src/python-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/src/python-systemd/docs/journal.rst b/src/python-systemd/docs/journal.rst index 08756b99b..e6c42061f 100644 --- a/src/python-systemd/docs/journal.rst +++ b/src/python-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/src/python-systemd/docs/login.rst b/src/python-systemd/docs/login.rst index 2cd9d8cbe..6b4de64c5 100644 --- a/src/python-systemd/docs/login.rst +++ b/src/python-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/src/python-systemd/login.c b/src/python-systemd/login.c index 1dbe5ac5b..57dc08021 100644 --- a/src/python-systemd/login.c +++ b/src/python-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/src/python-systemd/pyutil.c b/src/python-systemd/pyutil.c index 9510acddd..2f047e643 100644 --- a/src/python-systemd/pyutil.c +++ b/src/python-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/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h index 5c7ea37cd..ea88840fa 100644 --- a/src/python-systemd/pyutil.h +++ b/src/python-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))) -- 2.30.2