]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/python-systemd/_daemon.c
systemd-python: wrap sd_notify
[thirdparty/systemd.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