chiark / gitweb /
bd4e73e9be33a329f0099440bd406484a35cd981
[elogind.git] / src / python-systemd / _daemon.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #define PY_SSIZE_T_CLEAN
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wredundant-decls"
25 #include <Python.h>
26 #pragma GCC diagnostic pop
27
28 #include <stdbool.h>
29 #include <assert.h>
30 #include <sys/socket.h>
31
32 #include <systemd/sd-daemon.h>
33 #include "pyutil.h"
34
35 PyDoc_STRVAR(module__doc__,
36         "Python interface to the libsystemd-daemon library.\n\n"
37         "Provides _listen_fds, notify, booted, and is_* functions\n"
38         "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n"
39         "useful for socket activation and checking if the system is\n"
40         "running under systemd."
41 );
42
43
44 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
45 static int Unicode_FSConverter(PyObject* obj, void *_result) {
46         PyObject **result = _result;
47
48         assert(result);
49
50         if (!obj)
51                 /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
52                  * we can assume that it was PyUnicode_FSConverter. */
53                 return PyUnicode_FSConverter(obj, result);
54
55         if (obj == Py_None) {
56                 *result = NULL;
57                 return 1;
58         }
59
60         return PyUnicode_FSConverter(obj, result);
61 }
62 #endif
63
64
65 PyDoc_STRVAR(booted__doc__,
66              "booted() -> bool\n\n"
67              "Return True iff this system is running under systemd.\n"
68              "Wraps sd_daemon_booted(3)."
69 );
70
71 static PyObject* booted(PyObject *self, PyObject *args) {
72         int r;
73         assert(args == NULL);
74
75         r = sd_booted();
76         if (set_error(r, NULL, NULL))
77                 return NULL;
78
79         return PyBool_FromLong(r);
80 }
81
82 PyDoc_STRVAR(notify__doc__,
83              "notify(status, unset_environment=False) -> bool\n\n"
84              "Send a message to the init system about a status change.\n"
85              "Wraps sd_notify(3).");
86
87 static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) {
88         int r;
89         const char* msg;
90         int unset = false;
91
92         static const char* const kwlist[] = {
93                 "status",
94                 "unset_environment",
95                 NULL,
96         };
97 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
98         if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|p:notify",
99                                          (char**) kwlist, &msg, &unset))
100                 return NULL;
101 #else
102         PyObject *obj = NULL;
103         if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|O:notify",
104                                          (char**) kwlist, &msg, &obj))
105                 return NULL;
106         if (obj != NULL)
107                 unset = PyObject_IsTrue(obj);
108         if (unset < 0)
109                 return NULL;
110 #endif
111
112         r = sd_notify(unset, msg);
113         if (set_error(r, NULL, NULL))
114                 return NULL;
115
116         return PyBool_FromLong(r);
117 }
118
119
120 PyDoc_STRVAR(listen_fds__doc__,
121              "_listen_fds(unset_environment=True) -> int\n\n"
122              "Return the number of descriptors passed to this process by the init system\n"
123              "as part of the socket-based activation logic.\n"
124              "Wraps sd_listen_fds(3)."
125 );
126
127 static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) {
128         int r;
129         int unset = true;
130
131         static const char* const kwlist[] = {"unset_environment", NULL};
132 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
133         if (!PyArg_ParseTupleAndKeywords(args, keywds, "|p:_listen_fds",
134                                          (char**) kwlist, &unset))
135                 return NULL;
136 #else
137         PyObject *obj = NULL;
138         if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds",
139                                          (char**) kwlist, &unset, &obj))
140                 return NULL;
141         if (obj != NULL)
142                 unset = PyObject_IsTrue(obj);
143         if (unset < 0)
144                 return NULL;
145 #endif
146
147         r = sd_listen_fds(unset);
148         if (set_error(r, NULL, NULL))
149                 return NULL;
150
151         return long_FromLong(r);
152 }
153
154 PyDoc_STRVAR(is_fifo__doc__,
155              "_is_fifo(fd, path) -> bool\n\n"
156              "Returns True iff the descriptor refers to a FIFO or a pipe.\n"
157              "Wraps sd_is_fifo(3)."
158 );
159
160
161 static PyObject* is_fifo(PyObject *self, PyObject *args) {
162         int r;
163         int fd;
164         const char *path = NULL;
165
166 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
167         if (!PyArg_ParseTuple(args, "i|O&:_is_fifo",
168                               &fd, Unicode_FSConverter, &path))
169                 return NULL;
170 #else
171         if (!PyArg_ParseTuple(args, "i|z:_is_fifo", &fd, &path))
172                 return NULL;
173 #endif
174
175         r = sd_is_fifo(fd, path);
176         if (set_error(r, path, NULL))
177                 return NULL;
178
179         return PyBool_FromLong(r);
180 }
181
182
183 PyDoc_STRVAR(is_mq__doc__,
184              "_is_mq(fd, path) -> bool\n\n"
185              "Returns True iff the descriptor refers to a POSIX message queue.\n"
186              "Wraps sd_is_mq(3)."
187 );
188
189 static PyObject* is_mq(PyObject *self, PyObject *args) {
190         int r;
191         int fd;
192         const char *path = NULL;
193
194 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
195         if (!PyArg_ParseTuple(args, "i|O&:_is_mq",
196                               &fd, Unicode_FSConverter, &path))
197                 return NULL;
198 #else
199         if (!PyArg_ParseTuple(args, "i|z:_is_mq", &fd, &path))
200                 return NULL;
201 #endif
202
203         r = sd_is_mq(fd, path);
204         if (set_error(r, path, NULL))
205                 return NULL;
206
207         return PyBool_FromLong(r);
208 }
209
210
211
212 PyDoc_STRVAR(is_socket__doc__,
213              "_is_socket(fd, family=AF_UNSPEC, type=0, listening=-1) -> bool\n\n"
214              "Returns True iff the descriptor refers to a socket.\n"
215              "Wraps sd_is_socket(3).\n\n"
216              "Constants for `family` are defined in the socket module."
217 );
218
219 static PyObject* is_socket(PyObject *self, PyObject *args) {
220         int r;
221         int fd, family = AF_UNSPEC, type = 0, listening = -1;
222
223         if (!PyArg_ParseTuple(args, "i|iii:_is_socket",
224                               &fd, &family, &type, &listening))
225                 return NULL;
226
227         r = sd_is_socket(fd, family, type, listening);
228         if (set_error(r, NULL, NULL))
229                 return NULL;
230
231         return PyBool_FromLong(r);
232 }
233
234
235 PyDoc_STRVAR(is_socket_inet__doc__,
236              "_is_socket_inet(fd, family=AF_UNSPEC, type=0, listening=-1, port=0) -> bool\n\n"
237              "Wraps sd_is_socket_inet(3).\n\n"
238              "Constants for `family` are defined in the socket module."
239 );
240
241 static PyObject* is_socket_inet(PyObject *self, PyObject *args) {
242         int r;
243         int fd, family = AF_UNSPEC, type = 0, listening = -1, port = 0;
244
245         if (!PyArg_ParseTuple(args, "i|iiii:_is_socket_inet",
246                               &fd, &family, &type, &listening, &port))
247                 return NULL;
248
249         if (port < 0 || port > INT16_MAX) {
250                 set_error(-EINVAL, NULL, "port must fit into uint16_t");
251                 return NULL;
252         }
253
254         r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port);
255         if (set_error(r, NULL, NULL))
256                 return NULL;
257
258         return PyBool_FromLong(r);
259 }
260
261
262 PyDoc_STRVAR(is_socket_unix__doc__,
263              "_is_socket_unix(fd, type, listening, path) -> bool\n\n"
264              "Wraps sd_is_socket_unix(3)."
265 );
266
267 static PyObject* is_socket_unix(PyObject *self, PyObject *args) {
268         int r;
269         int fd, type = 0, listening = -1;
270         char* path = NULL;
271         Py_ssize_t length = 0;
272
273 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
274         _cleanup_Py_DECREF_ PyObject *_path = NULL;
275         if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix",
276                               &fd, &type, &listening, Unicode_FSConverter, &_path))
277                 return NULL;
278         if (_path) {
279                 assert(PyBytes_Check(_path));
280                 if (PyBytes_AsStringAndSize(_path, &path, &length))
281                         return NULL;
282         }
283 #else
284         if (!PyArg_ParseTuple(args, "i|iiz#:_is_socket_unix",
285                               &fd, &type, &listening, &path, &length))
286                 return NULL;
287 #endif
288
289         r = sd_is_socket_unix(fd, type, listening, path, length);
290         if (set_error(r, path, NULL))
291                 return NULL;
292
293         return PyBool_FromLong(r);
294 }
295
296
297 static PyMethodDef methods[] = {
298         { "booted", booted, METH_NOARGS, booted__doc__},
299         { "notify", (PyCFunction) notify, METH_VARARGS | METH_KEYWORDS, notify__doc__},
300         { "_listen_fds", (PyCFunction) listen_fds, METH_VARARGS | METH_KEYWORDS, listen_fds__doc__},
301         { "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__},
302         { "_is_mq", is_mq, METH_VARARGS, is_mq__doc__},
303         { "_is_socket", is_socket, METH_VARARGS, is_socket__doc__},
304         { "_is_socket_inet", is_socket_inet, METH_VARARGS, is_socket_inet__doc__},
305         { "_is_socket_unix", is_socket_unix, METH_VARARGS, is_socket_unix__doc__},
306         { NULL, NULL, 0, NULL }        /* Sentinel */
307 };
308
309 #pragma GCC diagnostic push
310 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
311
312 #if PY_MAJOR_VERSION < 3
313
314 PyMODINIT_FUNC init_daemon(void) {
315         PyObject *m;
316
317         m = Py_InitModule3("_daemon", methods, module__doc__);
318         if (m == NULL)
319                 return;
320
321         PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START);
322         PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
323 }
324
325 #else
326
327 static struct PyModuleDef module = {
328         PyModuleDef_HEAD_INIT,
329         "_daemon", /* name of module */
330         module__doc__, /* module documentation, may be NULL */
331         0, /* size of per-interpreter state of the module */
332         methods
333 };
334
335 PyMODINIT_FUNC PyInit__daemon(void) {
336         PyObject *m;
337
338         m = PyModule_Create(&module);
339         if (m == NULL)
340                 return NULL;
341
342         if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START) ||
343             PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
344                 Py_DECREF(m);
345                 return NULL;
346         }
347
348         return m;
349 }
350
351 #endif
352
353 #pragma GCC diagnostic pop