Contents of /trunk/cups/patches/cups-1.4.7-systemd-socket.patch
Parent Directory | Revision Log
Revision 1422 -
(show annotations)
(download)
Mon Jul 18 12:24:57 2011 UTC (13 years, 2 months ago) by niro
File size: 35856 byte(s)
Mon Jul 18 12:24:57 2011 UTC (13 years, 2 months ago) by niro
File size: 35856 byte(s)
-added fedoras systemd socket patch
1 | From 1db0a48375836c7e93ac30649007c2391be8b3b2 Mon Sep 17 00:00:00 2001 |
2 | From: Lennart Poettering <lennart@poettering.net> |
3 | Date: Mon, 4 Jul 2011 01:37:01 +0200 |
4 | Subject: [PATCH] systemd: add systemd socket activation and unit files |
5 | |
6 | On newer Linux machines systemd supports launchd-style socket |
7 | activation. This patch adds support for this mechanism and also adds |
8 | systemd unit files as appropriate to hook this up with systemd. |
9 | |
10 | This includes an MIT-licensed drop-in file that implements the most |
11 | basic interfaces needed to get the file descriptors from systemd. Most |
12 | of the systemd and Linux-specific code compiles to NOPs on other |
13 | systems. |
14 | |
15 | Three triggers to start CUPS are defined: a) if new printer hardware is |
16 | plugged in, b) when a client accesses the CUPS socket and c) when a |
17 | spool file resides in /var/spool/cups. |
18 | --- |
19 | Makedefs.in | 2 + |
20 | config-scripts/cups-systemd.m4 | 25 ++ |
21 | configure.in | 4 + |
22 | data/Makefile | 9 + |
23 | data/cups.path.in | 8 + |
24 | data/cups.service.in | 9 + |
25 | data/cups.socket.in | 9 + |
26 | scheduler/Makefile | 9 + |
27 | scheduler/main.c | 107 ++++++++ |
28 | scheduler/sd-daemon.c | 520 ++++++++++++++++++++++++++++++++++++++++ |
29 | scheduler/sd-daemon.h | 277 +++++++++++++++++++++ |
30 | 11 files changed, 979 insertions(+), 0 deletions(-) |
31 | create mode 100644 config-scripts/cups-systemd.m4 |
32 | create mode 100644 data/cups.path.in |
33 | create mode 100644 data/cups.service.in |
34 | create mode 100644 data/cups.socket.in |
35 | create mode 100644 scheduler/sd-daemon.c |
36 | create mode 100644 scheduler/sd-daemon.h |
37 | |
38 | diff --git a/Makedefs.in b/Makedefs.in |
39 | index 654eea4..02c43b6 100644 |
40 | --- a/Makedefs.in |
41 | +++ b/Makedefs.in |
42 | @@ -143,6 +143,7 @@ CXXFLAGS = @CPPFLAGS@ @CXXFLAGS@ |
43 | CXXLIBS = @CXXLIBS@ |
44 | DBUS_NOTIFIER = @DBUS_NOTIFIER@ |
45 | DBUS_NOTIFIERLIBS = @DBUS_NOTIFIERLIBS@ |
46 | +SYSTEMD_UNITS = @SYSTEMD_UNITS@ |
47 | DNSSD_BACKEND = @DNSSD_BACKEND@ |
48 | DSOFLAGS = -L../cups @DSOFLAGS@ |
49 | DSOLIBS = @DSOLIBS@ $(COMMONLIBS) |
50 | @@ -267,6 +268,7 @@ PAMFILE = @PAMFILE@ |
51 | |
52 | DEFAULT_LAUNCHD_CONF = @DEFAULT_LAUNCHD_CONF@ |
53 | DBUSDIR = @DBUSDIR@ |
54 | +SYSTEMDUNITDIR = $(BUILDROOT)@systemdsystemunitdir@ |
55 | |
56 | |
57 | # |
58 | diff --git a/config-scripts/cups-systemd.m4 b/config-scripts/cups-systemd.m4 |
59 | new file mode 100644 |
60 | index 0000000..fead8a9 |
61 | --- /dev/null |
62 | +++ b/config-scripts/cups-systemd.m4 |
63 | @@ -0,0 +1,25 @@ |
64 | +dnl |
65 | +dnl "$Id$" |
66 | +dnl |
67 | +dnl systemd stuff for CUPS. |
68 | + |
69 | +dnl Find whether systemd is available |
70 | + |
71 | +AC_ARG_WITH([systemdsystemunitdir], |
72 | + AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), |
73 | + [], [with_systemdsystemunitdir=$($PKGCONFIG --variable=systemdsystemunitdir systemd)]) |
74 | +if test "x$with_systemdsystemunitdir" != xno; then |
75 | + AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) |
76 | +fi |
77 | + |
78 | +if test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ; then |
79 | + SYSTEMD_UNITS="cups.service cups.socket cups.path" |
80 | +else |
81 | + SYSTEMD_UNITS="" |
82 | +fi |
83 | + |
84 | +AC_SUBST(SYSTEMD_UNITS) |
85 | + |
86 | +dnl |
87 | +dnl "$Id$" |
88 | +dnl |
89 | diff --git a/configure.in b/configure.in |
90 | index 560f4f8..b0c19a9 100644 |
91 | --- a/configure.in |
92 | +++ b/configure.in |
93 | @@ -37,6 +37,7 @@ sinclude(config-scripts/cups-pam.m4) |
94 | sinclude(config-scripts/cups-largefile.m4) |
95 | sinclude(config-scripts/cups-dnssd.m4) |
96 | sinclude(config-scripts/cups-launchd.m4) |
97 | +sinclude(config-scripts/cups-systemd.m4) |
98 | sinclude(config-scripts/cups-defaults.m4) |
99 | sinclude(config-scripts/cups-pdf.m4) |
100 | sinclude(config-scripts/cups-scripting.m4) |
101 | @@ -71,6 +72,9 @@ AC_OUTPUT(Makedefs |
102 | conf/snmp.conf |
103 | cups-config |
104 | data/testprint |
105 | + data/cups.service |
106 | + data/cups.socket |
107 | + data/cups.path |
108 | desktop/cups.desktop |
109 | doc/help/ref-cupsd-conf.html |
110 | doc/help/standard.html |
111 | diff --git a/data/Makefile b/data/Makefile |
112 | index b9de21a..cccab85 100644 |
113 | --- a/data/Makefile |
114 | +++ b/data/Makefile |
115 | @@ -112,6 +112,12 @@ install-data: |
116 | $(INSTALL_DATA) $$file $(DATADIR)/ppdc; \ |
117 | done |
118 | $(INSTALL_DIR) -m 755 $(DATADIR)/profiles |
119 | + if test "x$(SYSTEMD_UNITS)" != "x" ; then \ |
120 | + $(INSTALL_DIR) -m 755 $(SYSTEMDUNITDIR); \ |
121 | + for file in $(SYSTEMD_UNITS); do \ |
122 | + $(INSTALL_DATA) $$file $(SYSTEMDUNITDIR); \ |
123 | + done; \ |
124 | + fi |
125 | |
126 | |
127 | # |
128 | @@ -159,6 +165,9 @@ uninstall: |
129 | -$(RMDIR) $(DATADIR)/charsets |
130 | -$(RMDIR) $(DATADIR)/banners |
131 | -$(RMDIR) $(DATADIR) |
132 | + for file in $(SYSTEMD_UNITS); do \ |
133 | + $(RM) $(SYSTEMDUNITDIR)/$$file; \ |
134 | + done |
135 | |
136 | |
137 | # |
138 | diff --git a/data/cups.path.in b/data/cups.path.in |
139 | new file mode 100644 |
140 | index 0000000..c99e39b |
141 | --- /dev/null |
142 | +++ b/data/cups.path.in |
143 | @@ -0,0 +1,8 @@ |
144 | +[Unit] |
145 | +Description=CUPS Printer Service Spool |
146 | + |
147 | +[Path] |
148 | +DirectoryNotEmpty=@CUPS_REQUESTS@ |
149 | + |
150 | +[Install] |
151 | +WantedBy=multi-user.target |
152 | diff --git a/data/cups.service.in b/data/cups.service.in |
153 | new file mode 100644 |
154 | index 0000000..007d0e6 |
155 | --- /dev/null |
156 | +++ b/data/cups.service.in |
157 | @@ -0,0 +1,9 @@ |
158 | +[Unit] |
159 | +Description=CUPS Printing Service |
160 | + |
161 | +[Service] |
162 | +ExecStart=@sbindir@/cupsd -f |
163 | + |
164 | +[Install] |
165 | +Also=cups.socket cups.path |
166 | +WantedBy=printer.target |
167 | diff --git a/data/cups.socket.in b/data/cups.socket.in |
168 | new file mode 100644 |
169 | index 0000000..b940096 |
170 | --- /dev/null |
171 | +++ b/data/cups.socket.in |
172 | @@ -0,0 +1,9 @@ |
173 | +[Unit] |
174 | +Description=CUPS Printing Service Sockets |
175 | + |
176 | +[Socket] |
177 | +ListenStream=@CUPS_DEFAULT_DOMAINSOCKET@ |
178 | +ListenStream=631 |
179 | + |
180 | +[Install] |
181 | +WantedBy=sockets.target |
182 | diff --git a/scheduler/Makefile b/scheduler/Makefile |
183 | index 3c7da8e..05152a6 100644 |
184 | --- a/scheduler/Makefile |
185 | +++ b/scheduler/Makefile |
186 | @@ -39,6 +39,7 @@ CUPSDOBJS = \ |
187 | server.o \ |
188 | statbuf.o \ |
189 | subscriptions.o \ |
190 | + sd-daemon.o \ |
191 | sysman.o |
192 | LIBOBJS = \ |
193 | filter.o \ |
194 | @@ -568,6 +569,14 @@ sloc: |
195 | |
196 | |
197 | # |
198 | +# Update sd-daemon.[ch] drop-in file from upstream git |
199 | +# |
200 | + |
201 | +update-systemd: |
202 | + curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c > sd-daemon.c |
203 | + curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h > sd-daemon.h |
204 | + |
205 | +# |
206 | # Dependencies... |
207 | # |
208 | |
209 | diff --git a/scheduler/main.c b/scheduler/main.c |
210 | index baaa3a1..5a4b8e1 100644 |
211 | --- a/scheduler/main.c |
212 | +++ b/scheduler/main.c |
213 | @@ -26,6 +26,8 @@ |
214 | * launchd_checkin() - Check-in with launchd and collect the listening |
215 | * fds. |
216 | * launchd_checkout() - Update the launchd KeepAlive file as needed. |
217 | + * systemd_checkin() - Check-in with systemd and collect the |
218 | + * listening fds. |
219 | * parent_handler() - Catch USR1/CHLD signals... |
220 | * process_children() - Process all dead children... |
221 | * select_timeout() - Calculate the select timeout value. |
222 | @@ -62,6 +64,8 @@ |
223 | # endif /* !LAUNCH_JOBKEY_SERVICEIPC */ |
224 | #endif /* HAVE_LAUNCH_H */ |
225 | |
226 | +#include "sd-daemon.h" |
227 | + |
228 | #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) |
229 | # include <malloc.h> |
230 | #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */ |
231 | @@ -78,6 +82,7 @@ |
232 | static void launchd_checkin(void); |
233 | static void launchd_checkout(void); |
234 | #endif /* HAVE_LAUNCHD */ |
235 | +static void systemd_checkin(void); |
236 | static void parent_handler(int sig); |
237 | static void process_children(void); |
238 | static void sigchld_handler(int sig); |
239 | @@ -520,6 +525,11 @@ main(int argc, /* I - Number of command-line args */ |
240 | #endif /* HAVE_LAUNCHD */ |
241 | |
242 | /* |
243 | + * If we were started by systemd get the listen sockets file descriptors... |
244 | + */ |
245 | + systemd_checkin(); |
246 | + |
247 | + /* |
248 | * Startup the server... |
249 | */ |
250 | |
251 | @@ -731,6 +741,12 @@ main(int argc, /* I - Number of command-line args */ |
252 | #endif /* HAVE_LAUNCHD */ |
253 | |
254 | /* |
255 | + * If we were started by systemd get the listen sockets file descriptors... |
256 | + */ |
257 | + |
258 | + systemd_checkin(); |
259 | + |
260 | + /* |
261 | * Startup the server... |
262 | */ |
263 | |
264 | @@ -1535,6 +1551,97 @@ launchd_checkout(void) |
265 | } |
266 | #endif /* HAVE_LAUNCHD */ |
267 | |
268 | +static void |
269 | +systemd_checkin(void) |
270 | +{ |
271 | + int n, fd; |
272 | + |
273 | + n = sd_listen_fds(1); |
274 | + if (n < 0) |
275 | + { |
276 | + cupsdLogMessage(CUPSD_LOG_ERROR, |
277 | + "systemd_checkin: Failed to acquire sockets from systemd - %s", |
278 | + strerror(-n)); |
279 | + exit(EXIT_FAILURE); |
280 | + return; |
281 | + } |
282 | + |
283 | + if (n == 0) |
284 | + return; |
285 | + |
286 | + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) |
287 | + { |
288 | + http_addr_t addr; |
289 | + socklen_t addrlen; |
290 | + int r; |
291 | + cupsd_listener_t *lis; |
292 | + char s[256]; |
293 | + |
294 | + r = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1); |
295 | + if (r < 0) { |
296 | + cupsdLogMessage(CUPSD_LOG_ERROR, |
297 | + "systemd_checkin: Unable to verify socket type - %s", |
298 | + strerror(-r)); |
299 | + continue; |
300 | + } |
301 | + |
302 | + if (!r) { |
303 | + cupsdLogMessage(CUPSD_LOG_ERROR, |
304 | + "systemd_checkin: Socket not of the right type"); |
305 | + continue; |
306 | + } |
307 | + |
308 | + if (getsockname(fd, (struct sockaddr*) &addr, &addrlen)) |
309 | + { |
310 | + cupsdLogMessage(CUPSD_LOG_ERROR, |
311 | + "systemd_checkin: Unable to get local address - %s", |
312 | + strerror(errno)); |
313 | + continue; |
314 | + } |
315 | + |
316 | + /* |
317 | + * Try to match the systemd socket address to one of the listeners... |
318 | + */ |
319 | + |
320 | + for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
321 | + lis; |
322 | + lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
323 | + if (httpAddrEqual(&lis->address, &addr)) |
324 | + break; |
325 | + |
326 | + if (lis) |
327 | + { |
328 | + cupsdLogMessage(CUPSD_LOG_DEBUG, |
329 | + "systemd_checkin: Matched existing listener %s with fd %d...", |
330 | + httpAddrString(&(lis->address), s, sizeof(s)), fd); |
331 | + } |
332 | + else |
333 | + { |
334 | + cupsdLogMessage(CUPSD_LOG_DEBUG, |
335 | + "systemd_checkin: Adding new listener %s with fd %d...", |
336 | + httpAddrString(&addr, s, sizeof(s)), fd); |
337 | + |
338 | + if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL) |
339 | + { |
340 | + cupsdLogMessage(CUPSD_LOG_ERROR, |
341 | + "systemd_checkin: Unable to allocate listener - " |
342 | + "%s.", strerror(errno)); |
343 | + exit(EXIT_FAILURE); |
344 | + } |
345 | + |
346 | + cupsArrayAdd(Listeners, lis); |
347 | + |
348 | + memcpy(&lis->address, &addr, sizeof(lis->address)); |
349 | + } |
350 | + |
351 | + lis->fd = fd; |
352 | + |
353 | +# ifdef HAVE_SSL |
354 | + if (_httpAddrPort(&(lis->address)) == 443) |
355 | + lis->encryption = HTTP_ENCRYPT_ALWAYS; |
356 | +# endif /* HAVE_SSL */ |
357 | + } |
358 | +} |
359 | |
360 | /* |
361 | * 'parent_handler()' - Catch USR1/CHLD signals... |
362 | diff --git a/scheduler/sd-daemon.c b/scheduler/sd-daemon.c |
363 | new file mode 100644 |
364 | index 0000000..a2ec74c |
365 | --- /dev/null |
366 | +++ b/scheduler/sd-daemon.c |
367 | @@ -0,0 +1,520 @@ |
368 | +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
369 | + |
370 | +/*** |
371 | + Copyright 2010 Lennart Poettering |
372 | + |
373 | + Permission is hereby granted, free of charge, to any person |
374 | + obtaining a copy of this software and associated documentation files |
375 | + (the "Software"), to deal in the Software without restriction, |
376 | + including without limitation the rights to use, copy, modify, merge, |
377 | + publish, distribute, sublicense, and/or sell copies of the Software, |
378 | + and to permit persons to whom the Software is furnished to do so, |
379 | + subject to the following conditions: |
380 | + |
381 | + The above copyright notice and this permission notice shall be |
382 | + included in all copies or substantial portions of the Software. |
383 | + |
384 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
385 | + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
386 | + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
387 | + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
388 | + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
389 | + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
390 | + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
391 | + SOFTWARE. |
392 | +***/ |
393 | + |
394 | +#ifndef _GNU_SOURCE |
395 | +#define _GNU_SOURCE |
396 | +#endif |
397 | + |
398 | +#include <sys/types.h> |
399 | +#include <sys/stat.h> |
400 | +#include <sys/socket.h> |
401 | +#include <sys/un.h> |
402 | +#include <sys/fcntl.h> |
403 | +#include <netinet/in.h> |
404 | +#include <stdlib.h> |
405 | +#include <errno.h> |
406 | +#include <unistd.h> |
407 | +#include <string.h> |
408 | +#include <stdarg.h> |
409 | +#include <stdio.h> |
410 | +#include <stddef.h> |
411 | +#include <limits.h> |
412 | + |
413 | +#if defined(__linux__) |
414 | +#include <mqueue.h> |
415 | +#endif |
416 | + |
417 | +#include "sd-daemon.h" |
418 | + |
419 | +#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS) |
420 | +#define _sd_hidden_ __attribute__ ((visibility("hidden"))) |
421 | +#else |
422 | +#define _sd_hidden_ |
423 | +#endif |
424 | + |
425 | +_sd_hidden_ int sd_listen_fds(int unset_environment) { |
426 | + |
427 | +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) |
428 | + return 0; |
429 | +#else |
430 | + int r, fd; |
431 | + const char *e; |
432 | + char *p = NULL; |
433 | + unsigned long l; |
434 | + |
435 | + if (!(e = getenv("LISTEN_PID"))) { |
436 | + r = 0; |
437 | + goto finish; |
438 | + } |
439 | + |
440 | + errno = 0; |
441 | + l = strtoul(e, &p, 10); |
442 | + |
443 | + if (errno != 0) { |
444 | + r = -errno; |
445 | + goto finish; |
446 | + } |
447 | + |
448 | + if (!p || *p || l <= 0) { |
449 | + r = -EINVAL; |
450 | + goto finish; |
451 | + } |
452 | + |
453 | + /* Is this for us? */ |
454 | + if (getpid() != (pid_t) l) { |
455 | + r = 0; |
456 | + goto finish; |
457 | + } |
458 | + |
459 | + if (!(e = getenv("LISTEN_FDS"))) { |
460 | + r = 0; |
461 | + goto finish; |
462 | + } |
463 | + |
464 | + errno = 0; |
465 | + l = strtoul(e, &p, 10); |
466 | + |
467 | + if (errno != 0) { |
468 | + r = -errno; |
469 | + goto finish; |
470 | + } |
471 | + |
472 | + if (!p || *p) { |
473 | + r = -EINVAL; |
474 | + goto finish; |
475 | + } |
476 | + |
477 | + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { |
478 | + int flags; |
479 | + |
480 | + if ((flags = fcntl(fd, F_GETFD)) < 0) { |
481 | + r = -errno; |
482 | + goto finish; |
483 | + } |
484 | + |
485 | + if (flags & FD_CLOEXEC) |
486 | + continue; |
487 | + |
488 | + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { |
489 | + r = -errno; |
490 | + goto finish; |
491 | + } |
492 | + } |
493 | + |
494 | + r = (int) l; |
495 | + |
496 | +finish: |
497 | + if (unset_environment) { |
498 | + unsetenv("LISTEN_PID"); |
499 | + unsetenv("LISTEN_FDS"); |
500 | + } |
501 | + |
502 | + return r; |
503 | +#endif |
504 | +} |
505 | + |
506 | +_sd_hidden_ int sd_is_fifo(int fd, const char *path) { |
507 | + struct stat st_fd; |
508 | + |
509 | + if (fd < 0) |
510 | + return -EINVAL; |
511 | + |
512 | + memset(&st_fd, 0, sizeof(st_fd)); |
513 | + if (fstat(fd, &st_fd) < 0) |
514 | + return -errno; |
515 | + |
516 | + if (!S_ISFIFO(st_fd.st_mode)) |
517 | + return 0; |
518 | + |
519 | + if (path) { |
520 | + struct stat st_path; |
521 | + |
522 | + memset(&st_path, 0, sizeof(st_path)); |
523 | + if (stat(path, &st_path) < 0) { |
524 | + |
525 | + if (errno == ENOENT || errno == ENOTDIR) |
526 | + return 0; |
527 | + |
528 | + return -errno; |
529 | + } |
530 | + |
531 | + return |
532 | + st_path.st_dev == st_fd.st_dev && |
533 | + st_path.st_ino == st_fd.st_ino; |
534 | + } |
535 | + |
536 | + return 1; |
537 | +} |
538 | + |
539 | +_sd_hidden_ int sd_is_special(int fd, const char *path) { |
540 | + struct stat st_fd; |
541 | + |
542 | + if (fd < 0) |
543 | + return -EINVAL; |
544 | + |
545 | + if (fstat(fd, &st_fd) < 0) |
546 | + return -errno; |
547 | + |
548 | + if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) |
549 | + return 0; |
550 | + |
551 | + if (path) { |
552 | + struct stat st_path; |
553 | + |
554 | + if (stat(path, &st_path) < 0) { |
555 | + |
556 | + if (errno == ENOENT || errno == ENOTDIR) |
557 | + return 0; |
558 | + |
559 | + return -errno; |
560 | + } |
561 | + |
562 | + if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) |
563 | + return |
564 | + st_path.st_dev == st_fd.st_dev && |
565 | + st_path.st_ino == st_fd.st_ino; |
566 | + else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) |
567 | + return st_path.st_rdev == st_fd.st_rdev; |
568 | + else |
569 | + return 0; |
570 | + } |
571 | + |
572 | + return 1; |
573 | +} |
574 | + |
575 | +static int sd_is_socket_internal(int fd, int type, int listening) { |
576 | + struct stat st_fd; |
577 | + |
578 | + if (fd < 0 || type < 0) |
579 | + return -EINVAL; |
580 | + |
581 | + if (fstat(fd, &st_fd) < 0) |
582 | + return -errno; |
583 | + |
584 | + if (!S_ISSOCK(st_fd.st_mode)) |
585 | + return 0; |
586 | + |
587 | + if (type != 0) { |
588 | + int other_type = 0; |
589 | + socklen_t l = sizeof(other_type); |
590 | + |
591 | + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) |
592 | + return -errno; |
593 | + |
594 | + if (l != sizeof(other_type)) |
595 | + return -EINVAL; |
596 | + |
597 | + if (other_type != type) |
598 | + return 0; |
599 | + } |
600 | + |
601 | + if (listening >= 0) { |
602 | + int accepting = 0; |
603 | + socklen_t l = sizeof(accepting); |
604 | + |
605 | + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) |
606 | + return -errno; |
607 | + |
608 | + if (l != sizeof(accepting)) |
609 | + return -EINVAL; |
610 | + |
611 | + if (!accepting != !listening) |
612 | + return 0; |
613 | + } |
614 | + |
615 | + return 1; |
616 | +} |
617 | + |
618 | +union sockaddr_union { |
619 | + struct sockaddr sa; |
620 | + struct sockaddr_in in4; |
621 | + struct sockaddr_in6 in6; |
622 | + struct sockaddr_un un; |
623 | + struct sockaddr_storage storage; |
624 | +}; |
625 | + |
626 | +_sd_hidden_ int sd_is_socket(int fd, int family, int type, int listening) { |
627 | + int r; |
628 | + |
629 | + if (family < 0) |
630 | + return -EINVAL; |
631 | + |
632 | + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) |
633 | + return r; |
634 | + |
635 | + if (family > 0) { |
636 | + union sockaddr_union sockaddr; |
637 | + socklen_t l; |
638 | + |
639 | + memset(&sockaddr, 0, sizeof(sockaddr)); |
640 | + l = sizeof(sockaddr); |
641 | + |
642 | + if (getsockname(fd, &sockaddr.sa, &l) < 0) |
643 | + return -errno; |
644 | + |
645 | + if (l < sizeof(sa_family_t)) |
646 | + return -EINVAL; |
647 | + |
648 | + return sockaddr.sa.sa_family == family; |
649 | + } |
650 | + |
651 | + return 1; |
652 | +} |
653 | + |
654 | +_sd_hidden_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { |
655 | + union sockaddr_union sockaddr; |
656 | + socklen_t l; |
657 | + int r; |
658 | + |
659 | + if (family != 0 && family != AF_INET && family != AF_INET6) |
660 | + return -EINVAL; |
661 | + |
662 | + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) |
663 | + return r; |
664 | + |
665 | + memset(&sockaddr, 0, sizeof(sockaddr)); |
666 | + l = sizeof(sockaddr); |
667 | + |
668 | + if (getsockname(fd, &sockaddr.sa, &l) < 0) |
669 | + return -errno; |
670 | + |
671 | + if (l < sizeof(sa_family_t)) |
672 | + return -EINVAL; |
673 | + |
674 | + if (sockaddr.sa.sa_family != AF_INET && |
675 | + sockaddr.sa.sa_family != AF_INET6) |
676 | + return 0; |
677 | + |
678 | + if (family > 0) |
679 | + if (sockaddr.sa.sa_family != family) |
680 | + return 0; |
681 | + |
682 | + if (port > 0) { |
683 | + if (sockaddr.sa.sa_family == AF_INET) { |
684 | + if (l < sizeof(struct sockaddr_in)) |
685 | + return -EINVAL; |
686 | + |
687 | + return htons(port) == sockaddr.in4.sin_port; |
688 | + } else { |
689 | + if (l < sizeof(struct sockaddr_in6)) |
690 | + return -EINVAL; |
691 | + |
692 | + return htons(port) == sockaddr.in6.sin6_port; |
693 | + } |
694 | + } |
695 | + |
696 | + return 1; |
697 | +} |
698 | + |
699 | +_sd_hidden_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { |
700 | + union sockaddr_union sockaddr; |
701 | + socklen_t l; |
702 | + int r; |
703 | + |
704 | + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) |
705 | + return r; |
706 | + |
707 | + memset(&sockaddr, 0, sizeof(sockaddr)); |
708 | + l = sizeof(sockaddr); |
709 | + |
710 | + if (getsockname(fd, &sockaddr.sa, &l) < 0) |
711 | + return -errno; |
712 | + |
713 | + if (l < sizeof(sa_family_t)) |
714 | + return -EINVAL; |
715 | + |
716 | + if (sockaddr.sa.sa_family != AF_UNIX) |
717 | + return 0; |
718 | + |
719 | + if (path) { |
720 | + if (length <= 0) |
721 | + length = strlen(path); |
722 | + |
723 | + if (length <= 0) |
724 | + /* Unnamed socket */ |
725 | + return l == offsetof(struct sockaddr_un, sun_path); |
726 | + |
727 | + if (path[0]) |
728 | + /* Normal path socket */ |
729 | + return |
730 | + (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && |
731 | + memcmp(path, sockaddr.un.sun_path, length+1) == 0; |
732 | + else |
733 | + /* Abstract namespace socket */ |
734 | + return |
735 | + (l == offsetof(struct sockaddr_un, sun_path) + length) && |
736 | + memcmp(path, sockaddr.un.sun_path, length) == 0; |
737 | + } |
738 | + |
739 | + return 1; |
740 | +} |
741 | + |
742 | +_sd_hidden_ int sd_is_mq(int fd, const char *path) { |
743 | +#if !defined(__linux__) |
744 | + return 0; |
745 | +#else |
746 | + struct mq_attr attr; |
747 | + |
748 | + if (fd < 0) |
749 | + return -EINVAL; |
750 | + |
751 | + if (mq_getattr(fd, &attr) < 0) |
752 | + return -errno; |
753 | + |
754 | + if (path) { |
755 | + char fpath[PATH_MAX]; |
756 | + struct stat a, b; |
757 | + |
758 | + if (path[0] != '/') |
759 | + return -EINVAL; |
760 | + |
761 | + if (fstat(fd, &a) < 0) |
762 | + return -errno; |
763 | + |
764 | + strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); |
765 | + fpath[sizeof(fpath)-1] = 0; |
766 | + |
767 | + if (stat(fpath, &b) < 0) |
768 | + return -errno; |
769 | + |
770 | + if (a.st_dev != b.st_dev || |
771 | + a.st_ino != b.st_ino) |
772 | + return 0; |
773 | + } |
774 | + |
775 | + return 1; |
776 | +#endif |
777 | +} |
778 | + |
779 | +_sd_hidden_ int sd_notify(int unset_environment, const char *state) { |
780 | +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) |
781 | + return 0; |
782 | +#else |
783 | + int fd = -1, r; |
784 | + struct msghdr msghdr; |
785 | + struct iovec iovec; |
786 | + union sockaddr_union sockaddr; |
787 | + const char *e; |
788 | + |
789 | + if (!state) { |
790 | + r = -EINVAL; |
791 | + goto finish; |
792 | + } |
793 | + |
794 | + if (!(e = getenv("NOTIFY_SOCKET"))) |
795 | + return 0; |
796 | + |
797 | + /* Must be an abstract socket, or an absolute path */ |
798 | + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { |
799 | + r = -EINVAL; |
800 | + goto finish; |
801 | + } |
802 | + |
803 | + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { |
804 | + r = -errno; |
805 | + goto finish; |
806 | + } |
807 | + |
808 | + memset(&sockaddr, 0, sizeof(sockaddr)); |
809 | + sockaddr.sa.sa_family = AF_UNIX; |
810 | + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); |
811 | + |
812 | + if (sockaddr.un.sun_path[0] == '@') |
813 | + sockaddr.un.sun_path[0] = 0; |
814 | + |
815 | + memset(&iovec, 0, sizeof(iovec)); |
816 | + iovec.iov_base = (char*) state; |
817 | + iovec.iov_len = strlen(state); |
818 | + |
819 | + memset(&msghdr, 0, sizeof(msghdr)); |
820 | + msghdr.msg_name = &sockaddr; |
821 | + msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); |
822 | + |
823 | + if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) |
824 | + msghdr.msg_namelen = sizeof(struct sockaddr_un); |
825 | + |
826 | + msghdr.msg_iov = &iovec; |
827 | + msghdr.msg_iovlen = 1; |
828 | + |
829 | + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { |
830 | + r = -errno; |
831 | + goto finish; |
832 | + } |
833 | + |
834 | + r = 1; |
835 | + |
836 | +finish: |
837 | + if (unset_environment) |
838 | + unsetenv("NOTIFY_SOCKET"); |
839 | + |
840 | + if (fd >= 0) |
841 | + close(fd); |
842 | + |
843 | + return r; |
844 | +#endif |
845 | +} |
846 | + |
847 | +_sd_hidden_ int sd_notifyf(int unset_environment, const char *format, ...) { |
848 | +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) |
849 | + return 0; |
850 | +#else |
851 | + va_list ap; |
852 | + char *p = NULL; |
853 | + int r; |
854 | + |
855 | + va_start(ap, format); |
856 | + r = vasprintf(&p, format, ap); |
857 | + va_end(ap); |
858 | + |
859 | + if (r < 0 || !p) |
860 | + return -ENOMEM; |
861 | + |
862 | + r = sd_notify(unset_environment, p); |
863 | + free(p); |
864 | + |
865 | + return r; |
866 | +#endif |
867 | +} |
868 | + |
869 | +_sd_hidden_ int sd_booted(void) { |
870 | +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) |
871 | + return 0; |
872 | +#else |
873 | + |
874 | + struct stat a, b; |
875 | + |
876 | + /* We simply test whether the systemd cgroup hierarchy is |
877 | + * mounted */ |
878 | + |
879 | + if (lstat("/sys/fs/cgroup", &a) < 0) |
880 | + return 0; |
881 | + |
882 | + if (lstat("/sys/fs/cgroup/systemd", &b) < 0) |
883 | + return 0; |
884 | + |
885 | + return a.st_dev != b.st_dev; |
886 | +#endif |
887 | +} |
888 | diff --git a/scheduler/sd-daemon.h b/scheduler/sd-daemon.h |
889 | new file mode 100644 |
890 | index 0000000..46dc7fd |
891 | --- /dev/null |
892 | +++ b/scheduler/sd-daemon.h |
893 | @@ -0,0 +1,277 @@ |
894 | +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
895 | + |
896 | +#ifndef foosddaemonhfoo |
897 | +#define foosddaemonhfoo |
898 | + |
899 | +/*** |
900 | + Copyright 2010 Lennart Poettering |
901 | + |
902 | + Permission is hereby granted, free of charge, to any person |
903 | + obtaining a copy of this software and associated documentation files |
904 | + (the "Software"), to deal in the Software without restriction, |
905 | + including without limitation the rights to use, copy, modify, merge, |
906 | + publish, distribute, sublicense, and/or sell copies of the Software, |
907 | + and to permit persons to whom the Software is furnished to do so, |
908 | + subject to the following conditions: |
909 | + |
910 | + The above copyright notice and this permission notice shall be |
911 | + included in all copies or substantial portions of the Software. |
912 | + |
913 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
914 | + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
915 | + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
916 | + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
917 | + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
918 | + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
919 | + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
920 | + SOFTWARE. |
921 | +***/ |
922 | + |
923 | +#include <sys/types.h> |
924 | +#include <inttypes.h> |
925 | + |
926 | +#ifdef __cplusplus |
927 | +extern "C" { |
928 | +#endif |
929 | + |
930 | +/* |
931 | + Reference implementation of a few systemd related interfaces for |
932 | + writing daemons. These interfaces are trivial to implement. To |
933 | + simplify porting we provide this reference implementation. |
934 | + Applications are welcome to reimplement the algorithms described |
935 | + here if they do not want to include these two source files. |
936 | + |
937 | + The following functionality is provided: |
938 | + |
939 | + - Support for logging with log levels on stderr |
940 | + - File descriptor passing for socket-based activation |
941 | + - Daemon startup and status notification |
942 | + - Detection of systemd boots |
943 | + |
944 | + You may compile this with -DDISABLE_SYSTEMD to disable systemd |
945 | + support. This makes all those calls NOPs that are directly related to |
946 | + systemd (i.e. only sd_is_xxx() will stay useful). |
947 | + |
948 | + Since this is drop-in code we don't want any of our symbols to be |
949 | + exported in any case. Hence we declare hidden visibility for all of |
950 | + them. |
951 | + |
952 | + You may find an up-to-date version of these source files online: |
953 | + |
954 | + http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h |
955 | + http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c |
956 | + |
957 | + This should compile on non-Linux systems, too, but with the |
958 | + exception of the sd_is_xxx() calls all functions will become NOPs. |
959 | + |
960 | + See sd-daemon(7) for more information. |
961 | +*/ |
962 | + |
963 | +#ifndef _sd_printf_attr_ |
964 | +#if __GNUC__ >= 4 |
965 | +#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) |
966 | +#else |
967 | +#define _sd_printf_attr_(a,b) |
968 | +#endif |
969 | +#endif |
970 | + |
971 | +/* |
972 | + Log levels for usage on stderr: |
973 | + |
974 | + fprintf(stderr, SD_NOTICE "Hello World!\n"); |
975 | + |
976 | + This is similar to printk() usage in the kernel. |
977 | +*/ |
978 | +#define SD_EMERG "<0>" /* system is unusable */ |
979 | +#define SD_ALERT "<1>" /* action must be taken immediately */ |
980 | +#define SD_CRIT "<2>" /* critical conditions */ |
981 | +#define SD_ERR "<3>" /* error conditions */ |
982 | +#define SD_WARNING "<4>" /* warning conditions */ |
983 | +#define SD_NOTICE "<5>" /* normal but significant condition */ |
984 | +#define SD_INFO "<6>" /* informational */ |
985 | +#define SD_DEBUG "<7>" /* debug-level messages */ |
986 | + |
987 | +/* The first passed file descriptor is fd 3 */ |
988 | +#define SD_LISTEN_FDS_START 3 |
989 | + |
990 | +/* |
991 | + Returns how many file descriptors have been passed, or a negative |
992 | + errno code on failure. Optionally, removes the $LISTEN_FDS and |
993 | + $LISTEN_PID file descriptors from the environment (recommended, but |
994 | + problematic in threaded environments). If r is the return value of |
995 | + this function you'll find the file descriptors passed as fds |
996 | + SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative |
997 | + errno style error code on failure. This function call ensures that |
998 | + the FD_CLOEXEC flag is set for the passed file descriptors, to make |
999 | + sure they are not passed on to child processes. If FD_CLOEXEC shall |
1000 | + not be set, the caller needs to unset it after this call for all file |
1001 | + descriptors that are used. |
1002 | + |
1003 | + See sd_listen_fds(3) for more information. |
1004 | +*/ |
1005 | +int sd_listen_fds(int unset_environment); |
1006 | + |
1007 | +/* |
1008 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1009 | + the file descriptor is a FIFO in the file system stored under the |
1010 | + specified path, 0 otherwise. If path is NULL a path name check will |
1011 | + not be done and the call only verifies if the file descriptor |
1012 | + refers to a FIFO. Returns a negative errno style error code on |
1013 | + failure. |
1014 | + |
1015 | + See sd_is_fifo(3) for more information. |
1016 | +*/ |
1017 | +int sd_is_fifo(int fd, const char *path); |
1018 | + |
1019 | +/* |
1020 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1021 | + the file descriptor is a special character device on the file |
1022 | + system stored under the specified path, 0 otherwise. |
1023 | + If path is NULL a path name check will not be done and the call |
1024 | + only verifies if the file descriptor refers to a special character. |
1025 | + Returns a negative errno style error code on failure. |
1026 | + |
1027 | + See sd_is_special(3) for more information. |
1028 | +*/ |
1029 | +int sd_is_special(int fd, const char *path); |
1030 | + |
1031 | +/* |
1032 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1033 | + the file descriptor is a socket of the specified family (AF_INET, |
1034 | + ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If |
1035 | + family is 0 a socket family check will not be done. If type is 0 a |
1036 | + socket type check will not be done and the call only verifies if |
1037 | + the file descriptor refers to a socket. If listening is > 0 it is |
1038 | + verified that the socket is in listening mode. (i.e. listen() has |
1039 | + been called) If listening is == 0 it is verified that the socket is |
1040 | + not in listening mode. If listening is < 0 no listening mode check |
1041 | + is done. Returns a negative errno style error code on failure. |
1042 | + |
1043 | + See sd_is_socket(3) for more information. |
1044 | +*/ |
1045 | +int sd_is_socket(int fd, int family, int type, int listening); |
1046 | + |
1047 | +/* |
1048 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1049 | + the file descriptor is an Internet socket, of the specified family |
1050 | + (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, |
1051 | + SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version |
1052 | + check is not done. If type is 0 a socket type check will not be |
1053 | + done. If port is 0 a socket port check will not be done. The |
1054 | + listening flag is used the same way as in sd_is_socket(). Returns a |
1055 | + negative errno style error code on failure. |
1056 | + |
1057 | + See sd_is_socket_inet(3) for more information. |
1058 | +*/ |
1059 | +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); |
1060 | + |
1061 | +/* |
1062 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1063 | + the file descriptor is an AF_UNIX socket of the specified type |
1064 | + (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 |
1065 | + a socket type check will not be done. If path is NULL a socket path |
1066 | + check will not be done. For normal AF_UNIX sockets set length to |
1067 | + 0. For abstract namespace sockets set length to the length of the |
1068 | + socket name (including the initial 0 byte), and pass the full |
1069 | + socket path in path (including the initial 0 byte). The listening |
1070 | + flag is used the same way as in sd_is_socket(). Returns a negative |
1071 | + errno style error code on failure. |
1072 | + |
1073 | + See sd_is_socket_unix(3) for more information. |
1074 | +*/ |
1075 | +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); |
1076 | + |
1077 | +/* |
1078 | + Helper call for identifying a passed file descriptor. Returns 1 if |
1079 | + the file descriptor is a POSIX Message Queue of the specified name, |
1080 | + 0 otherwise. If path is NULL a message queue name check is not |
1081 | + done. Returns a negative errno style error code on failure. |
1082 | +*/ |
1083 | +int sd_is_mq(int fd, const char *path); |
1084 | + |
1085 | +/* |
1086 | + Informs systemd about changed daemon state. This takes a number of |
1087 | + newline separated environment-style variable assignments in a |
1088 | + string. The following variables are known: |
1089 | + |
1090 | + READY=1 Tells systemd that daemon startup is finished (only |
1091 | + relevant for services of Type=notify). The passed |
1092 | + argument is a boolean "1" or "0". Since there is |
1093 | + little value in signaling non-readiness the only |
1094 | + value daemons should send is "READY=1". |
1095 | + |
1096 | + STATUS=... Passes a single-line status string back to systemd |
1097 | + that describes the daemon state. This is free-from |
1098 | + and can be used for various purposes: general state |
1099 | + feedback, fsck-like programs could pass completion |
1100 | + percentages and failing programs could pass a human |
1101 | + readable error message. Example: "STATUS=Completed |
1102 | + 66% of file system check..." |
1103 | + |
1104 | + ERRNO=... If a daemon fails, the errno-style error code, |
1105 | + formatted as string. Example: "ERRNO=2" for ENOENT. |
1106 | + |
1107 | + BUSERROR=... If a daemon fails, the D-Bus error-style error |
1108 | + code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" |
1109 | + |
1110 | + MAINPID=... The main pid of a daemon, in case systemd did not |
1111 | + fork off the process itself. Example: "MAINPID=4711" |
1112 | + |
1113 | + Daemons can choose to send additional variables. However, it is |
1114 | + recommended to prefix variable names not listed above with X_. |
1115 | + |
1116 | + Returns a negative errno-style error code on failure. Returns > 0 |
1117 | + if systemd could be notified, 0 if it couldn't possibly because |
1118 | + systemd is not running. |
1119 | + |
1120 | + Example: When a daemon finished starting up, it could issue this |
1121 | + call to notify systemd about it: |
1122 | + |
1123 | + sd_notify(0, "READY=1"); |
1124 | + |
1125 | + See sd_notifyf() for more complete examples. |
1126 | + |
1127 | + See sd_notify(3) for more information. |
1128 | +*/ |
1129 | +int sd_notify(int unset_environment, const char *state); |
1130 | + |
1131 | +/* |
1132 | + Similar to sd_notify() but takes a format string. |
1133 | + |
1134 | + Example 1: A daemon could send the following after initialization: |
1135 | + |
1136 | + sd_notifyf(0, "READY=1\n" |
1137 | + "STATUS=Processing requests...\n" |
1138 | + "MAINPID=%lu", |
1139 | + (unsigned long) getpid()); |
1140 | + |
1141 | + Example 2: A daemon could send the following shortly before |
1142 | + exiting, on failure: |
1143 | + |
1144 | + sd_notifyf(0, "STATUS=Failed to start up: %s\n" |
1145 | + "ERRNO=%i", |
1146 | + strerror(errno), |
1147 | + errno); |
1148 | + |
1149 | + See sd_notifyf(3) for more information. |
1150 | +*/ |
1151 | +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3); |
1152 | + |
1153 | +/* |
1154 | + Returns > 0 if the system was booted with systemd. Returns < 0 on |
1155 | + error. Returns 0 if the system was not booted with systemd. Note |
1156 | + that all of the functions above handle non-systemd boots just |
1157 | + fine. You should NOT protect them with a call to this function. Also |
1158 | + note that this function checks whether the system, not the user |
1159 | + session is controlled by systemd. However the functions above work |
1160 | + for both user and system services. |
1161 | + |
1162 | + See sd_booted(3) for more information. |
1163 | +*/ |
1164 | +int sd_booted(void); |
1165 | + |
1166 | +#ifdef __cplusplus |
1167 | +} |
1168 | +#endif |
1169 | + |
1170 | +#endif |
1171 | -- |
1172 | 1.7.6 |
1173 |