summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Waldvogel <marcel.waldvogel@uni-konstanz.de>2018-06-24 11:58:15 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-06-16 16:27:11 +0200
commitb1608daf27a38f223afa4dda1930d4999c183178 (patch)
tree2c57a4aa08ca3a5fdd8e7bbead78eb864b70b053
parenta2471e236906503a2079fe9806303ff31e832cbd (diff)
downloadpython-systemd-b1608daf27a38f223afa4dda1930d4999c183178.tar.gz
Added support for listen_fds_with_names()
-rw-r--r--NEWS3
-rw-r--r--systemd/_daemon.c81
-rw-r--r--systemd/daemon.py22
-rw-r--r--systemd/test/test_daemon.py42
4 files changed, 145 insertions, 3 deletions
diff --git a/NEWS b/NEWS
index b6d208d..9cf35f4 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,9 @@ CHANGES WITH 235:
* Adapt the rename of systemd-activate to systemd-socket-activate
performed in systemd 230
+ * Support for the new sd_listen_fds_with_names added in systemd 227
+ is added.
+
CHANGES WITH 234:
* Support for the new sd_is_socket_sockaddr added in systemd 233
diff --git a/systemd/_daemon.c b/systemd/_daemon.c
index f41095a..9119f57 100644
--- a/systemd/_daemon.c
+++ b/systemd/_daemon.c
@@ -41,14 +41,18 @@
# define HAVE_PID_NOTIFY_WITH_FDS
#endif
+#if LIBSYSTEMD_VERSION >= 227
+# define HAVE_SD_LISTEN_FDS_WITH_NAMES
+#endif
+
#if LIBSYSTEMD_VERSION >= 233
# define HAVE_IS_SOCKET_SOCKADDR
#endif
PyDoc_STRVAR(module__doc__,
"Python interface to the libsystemd-daemon library.\n\n"
- "Provides _listen_fds, notify, booted, and is_* functions\n"
- "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n"
+ "Provides _listen_fds*, notify, booted, and is_* functions\n"
+ "which wrap sd_listen_fds*, sd_notify, sd_booted, sd_is_*;\n"
"useful for socket activation and checking if the system is\n"
"running under systemd."
);
@@ -204,6 +208,77 @@ static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) {
return long_FromLong(r);
}
+PyDoc_STRVAR(listen_fds_with_names__doc__,
+ "_listen_fds_with_names(unset_environment=True) -> (int, str...)\n\n"
+ "Return the number of descriptors passed to this process by the init system\n"
+ "and their names as part of the socket-based activation logic.\n"
+ "Wraps sd_listen_fds_with_names(3).\n"
+ "Raises RunTimeError if compiled under systemd < 227."
+);
+
+static void free_names(char **names) {
+ if (names == NULL)
+ return;
+ for (char **n = names; *n != NULL; n++)
+ free(*n);
+ free(names);
+}
+static PyObject* listen_fds_with_names(PyObject *self, PyObject *args, PyObject *keywds) {
+ int r;
+ int unset = false;
+ char **names = NULL;
+ PyObject *tpl, *item;
+
+ static const char* const kwlist[] = {"unset_environment", NULL};
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|p:_listen_fds_with_names",
+ (char**) kwlist, &unset))
+ return NULL;
+#else
+ PyObject *obj = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds_with_names",
+ (char**) kwlist, &obj))
+ return NULL;
+ if (obj != NULL)
+ unset = PyObject_IsTrue(obj);
+ if (unset < 0)
+ return NULL;
+#endif
+
+#ifdef HAVE_SD_LISTEN_FDS_WITH_NAMES
+ r = sd_listen_fds_with_names(unset, &names);
+ if (set_error(r, NULL, NULL) < 0)
+ return NULL;
+
+ tpl = PyTuple_New(r+1);
+ if (tpl == NULL)
+ return NULL;
+
+ item = long_FromLong(r);
+ if (item == NULL) {
+ Py_DECREF(tpl);
+ return NULL;
+ }
+ if (PyTuple_SetItem(tpl, 0, item) < 0) {
+ Py_DECREF(tpl);
+ return NULL;
+ }
+ for (int i = 0; i < r && names[i] != NULL; i++) {
+ item = unicode_FromString(names[i]);
+ if (PyTuple_SetItem(tpl, 1+i, item) < 0) {
+ Py_DECREF(tpl);
+ free_names(names);
+ return NULL;
+ }
+ }
+ free_names(names);
+ return tpl;
+#else /* !HAVE_SD_LISTEN_FDS_WITH_NAMES */
+ set_error(-ENOSYS, NULL, "Compiled without support for sd_listen_fds_with_names");
+ return NULL;
+#endif /* HAVE_SD_LISTEN_FDS_WITH_NAMES */
+}
+
PyDoc_STRVAR(is_fifo__doc__,
"_is_fifo(fd, path) -> bool\n\n"
"Returns True iff the descriptor refers to a FIFO or a pipe.\n"
@@ -411,6 +486,8 @@ static PyMethodDef methods[] = {
{ "booted", booted, METH_NOARGS, booted__doc__},
{ "notify", (PyCFunction) notify, METH_VARARGS | METH_KEYWORDS, notify__doc__},
{ "_listen_fds", (PyCFunction) listen_fds, METH_VARARGS | METH_KEYWORDS, listen_fds__doc__},
+ { "_listen_fds_with_names", (PyCFunction) listen_fds_with_names,
+ METH_VARARGS | METH_KEYWORDS, listen_fds_with_names__doc__},
{ "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__},
{ "_is_mq", is_mq, METH_VARARGS, is_mq__doc__},
{ "_is_socket", is_socket, METH_VARARGS, is_socket__doc__},
diff --git a/systemd/daemon.py b/systemd/daemon.py
index 217b595..168e55d 100644
--- a/systemd/daemon.py
+++ b/systemd/daemon.py
@@ -4,6 +4,7 @@ from ._daemon import (__version__,
booted,
notify,
_listen_fds,
+ _listen_fds_with_names,
_is_fifo,
_is_socket,
_is_socket_inet,
@@ -69,3 +70,24 @@ def listen_fds(unset_environment=True):
"""
num = _listen_fds(unset_environment)
return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num))
+
+def listen_fds_with_names(unset_environment=True):
+ """Return a dictionary of socket activated descriptors as {fd: name}
+
+ Example::
+
+ (in primary window)
+ $ systemd-socket-activate -l 2000 -l 4000 --fdname=2K:4K python3 -c \\
+ 'from systemd.daemon import listen_fds_with_names; print(listen_fds_with_names())'
+ (in another window)
+ $ telnet localhost 2000
+ (in primary window)
+ ...
+ Execing python3 (...)
+ [3]
+ """
+ composite = _listen_fds_with_names(unset_environment)
+ retval = {}
+ for i in range(0, composite[0]):
+ retval[i+LISTEN_FDS_START] = composite[1+i]
+ return retval
diff --git a/systemd/test/test_daemon.py b/systemd/test/test_daemon.py
index 9f4db7d..ea680a1 100644
--- a/systemd/test/test_daemon.py
+++ b/systemd/test/test_daemon.py
@@ -11,7 +11,7 @@ from systemd.daemon import (booted,
is_socket_unix, _is_socket_unix,
is_socket_sockaddr, _is_socket_sockaddr,
is_mq, _is_mq,
- listen_fds,
+ listen_fds, listen_fds_with_names,
notify)
import pytest
@@ -256,6 +256,46 @@ def test_listen_fds_default_unset():
assert listen_fds() == [3]
assert listen_fds() == []
+def test_listen_fds_with_names_nothing():
+ # make sure we have no fds to listen to, no names
+ os.unsetenv('LISTEN_FDS')
+ os.unsetenv('LISTEN_PID')
+ os.unsetenv('LISTEN_FDNAMES')
+
+ assert listen_fds_with_names() == {}
+ assert listen_fds_with_names(True) == {}
+ assert listen_fds_with_names(False) == {}
+
+def test_listen_fds_with_names_no_names():
+ # make sure we have no fds to listen to, no names
+ os.environ['LISTEN_FDS'] = '1'
+ os.environ['LISTEN_PID'] = str(os.getpid())
+ os.unsetenv('LISTEN_FDNAMES')
+
+ assert listen_fds_with_names(False) == {3: 'unknown'}
+ assert listen_fds_with_names(True) == {3: 'unknown'}
+ assert listen_fds_with_names() == {}
+
+def test_listen_fds_with_names_single():
+ # make sure we have no fds to listen to, no names
+ os.environ['LISTEN_FDS'] = '1'
+ os.environ['LISTEN_PID'] = str(os.getpid())
+ os.environ['LISTEN_FDNAMES'] = 'cmds'
+
+ assert listen_fds_with_names(False) == {3: 'cmds'}
+ assert listen_fds_with_names() == {3: 'cmds'}
+ assert listen_fds_with_names(True) == {}
+
+def test_listen_fds_with_names_multiple():
+ # make sure we have no fds to listen to, no names
+ os.environ['LISTEN_FDS'] = '3'
+ os.environ['LISTEN_PID'] = str(os.getpid())
+ os.environ['LISTEN_FDNAMES'] = 'cmds:data:errs'
+
+ assert listen_fds_with_names(False) == {3: 'cmds', 4: 'data', 5: 'errs'}
+ assert listen_fds_with_names(True) == {3: 'cmds', 4: 'data', 5: 'errs'}
+ assert listen_fds_with_names() == {}
+
def test_notify_no_socket():
assert notify('READY=1') is False
with skip_enosys():