Lely core libraries 2.3.4
daemon.c
Go to the documentation of this file.
1
24#include "util.h"
25
26#if LELY_NO_STDIO
27#undef LELY_NO_DAEMON
28#define LELY_NO_DAEMON 1
29#endif
30
31#if !LELY_NO_DAEMON
32
33#if !LELY_NO_THREADS
34#include <lely/libc/threads.h>
35#endif
36#include <lely/util/daemon.h>
37#include <lely/util/diag.h>
38
39#include <assert.h>
40#include <signal.h>
41#include <stdio.h>
42#include <stdlib.h>
43
44#ifndef LELY_DAEMON_TIMEOUT
45#define LELY_DAEMON_TIMEOUT 1000
46#endif
47
48static daemon_handler_t *daemon_handler = &default_daemon_handler;
49static void *daemon_handle;
50
51#if _WIN32
52
53#include <winerror.h>
54
55static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
56static void WINAPI Handler(DWORD fdwControl);
57static int ReportStatus(DWORD dwCurrentState);
58
59static const char *daemon_name;
60static int (*daemon_init)(int, char **);
61static void (*daemon_main)(void);
62static void (*daemon_fini)(void);
63
64static SERVICE_STATUS_HANDLE hServiceStatus;
65
66static SERVICE_STATUS ServiceStatus = {
67 .dwServiceType = SERVICE_WIN32_OWN_PROCESS,
68 .dwControlsAccepted = SERVICE_ACCEPT_STOP
69 | SERVICE_ACCEPT_PAUSE_CONTINUE
70 | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PARAMCHANGE,
71 .dwWin32ExitCode = NO_ERROR,
72 .dwWaitHint = 2 * LELY_DAEMON_TIMEOUT
73};
74
75int
76daemon_start(const char *name, int (*init)(int, char **), void (*main)(void),
77 void (*fini)(void), int argc, char *argv[])
78{
79 (void)argc;
80 (void)argv;
81
82 daemon_name = name;
83 daemon_init = init;
84 daemon_main = main;
85 daemon_fini = fini;
86
87 SERVICE_TABLE_ENTRYA ServiceTable[] = {
88 { (LPSTR)daemon_name, &ServiceMain }, { NULL, NULL }
89 };
90 return StartServiceCtrlDispatcherA(ServiceTable) ? 0 : -1;
91}
92
93int
94daemon_signal(int sig)
95{
96 if (sig < 0 || sig > DAEMON_USER_MAX) {
97 SetLastError(ERROR_INVALID_PARAMETER);
98 return -1;
99 }
100
101 if (daemon_handler)
102 daemon_handler(sig, daemon_handle);
103
104 return 0;
105}
106
107int
108daemon_status(int status)
109{
110 DWORD dwCurrentState = 0;
111 switch (status) {
112 case DAEMON_START:
113 case DAEMON_CONTINUE: dwCurrentState = SERVICE_RUNNING; break;
114 case DAEMON_STOP: dwCurrentState = SERVICE_STOPPED; break;
115 case DAEMON_PAUSE: dwCurrentState = SERVICE_PAUSED; break;
116 case DAEMON_RELOAD: break;
117 default: SetLastError(ERROR_INVALID_PARAMETER); return -1;
118 }
119
120 return ReportStatus(dwCurrentState);
121}
122
123static void WINAPI
124ServiceMain(DWORD dwArgc, LPSTR *lpszArgv)
125{
126 assert(lpszArgv);
127
128 hServiceStatus = RegisterServiceCtrlHandlerA(daemon_name, Handler);
129 if (!hServiceStatus)
130 goto error_RegisterServiceCtrlHandlerA;
131 ReportStatus(SERVICE_START_PENDING);
132
133#if !LELY_NO_DIAG
134 diag_handler_t *diag_handler;
135 void *diag_handle;
136 diag_get_handler(&diag_handler, &diag_handle);
137 diag_set_handler(&daemon_diag_handler, (void *)daemon_name);
138
139 diag_at_handler_t *diag_at_handler;
140 void *diag_at_handle;
141 diag_at_get_handler(&diag_at_handler, &diag_at_handle);
142 diag_at_set_handler(&daemon_diag_at_handler, (void *)daemon_name);
143#endif
144
145 if (daemon_init) {
146 // Make sure the argument list is NULL-terminated.
147 char **argv = malloc((dwArgc + 1) * sizeof(char *));
148 if (!argv)
149 goto error_init;
150 for (DWORD i = 0; i < dwArgc; i++)
151 argv[i] = lpszArgv[i];
152 argv[dwArgc] = NULL;
153 if (daemon_init(dwArgc, argv)) {
154 free(argv);
155 goto error_init;
156 }
157 free(argv);
158 }
159
160 ReportStatus(SERVICE_RUNNING);
161
162 assert(daemon_main);
163 daemon_main();
164
165 if (daemon_fini)
166 daemon_fini();
167
168#if !LELY_NO_DIAG
169 diag_at_set_handler(diag_at_handler, diag_at_handle);
170 diag_set_handler(diag_handler, diag_handle);
171#endif
172
173error_init:
174 ReportStatus(SERVICE_STOPPED);
175error_RegisterServiceCtrlHandlerA:
176 daemon_fini = NULL;
177 daemon_main = NULL;
178 daemon_init = NULL;
179 daemon_name = NULL;
180}
181
182static void WINAPI
183Handler(DWORD fdwControl)
184{
185 int sig = -1;
186 switch (fdwControl) {
187 case SERVICE_CONTROL_STOP:
188 case SERVICE_CONTROL_SHUTDOWN:
189 ReportStatus(SERVICE_STOP_PENDING);
190 sig = DAEMON_STOP;
191 break;
192 case SERVICE_CONTROL_PAUSE:
193 ReportStatus(SERVICE_PAUSE_PENDING);
194 sig = DAEMON_PAUSE;
195 break;
196 case SERVICE_CONTROL_CONTINUE:
197 ReportStatus(SERVICE_CONTINUE_PENDING);
198 sig = DAEMON_CONTINUE;
199 break;
200 case SERVICE_CONTROL_PARAMCHANGE: sig = DAEMON_RELOAD; break;
201 default:
202 if (fdwControl >= 128 && fdwControl <= 255)
203 sig = DAEMON_USER_MIN + (fdwControl - 128);
204 break;
205 };
206
207 if (sig != -1)
208 daemon_signal(sig);
209
210 ReportStatus(0);
211}
212
213static int
214ReportStatus(DWORD dwCurrentState)
215{
216 static DWORD dwCheckPoint;
217 if (dwCurrentState)
218 ServiceStatus.dwCurrentState = dwCurrentState;
219 switch (ServiceStatus.dwCurrentState) {
220 case SERVICE_START_PENDING:
221 case SERVICE_STOP_PENDING:
222 case SERVICE_PAUSE_PENDING:
223 case SERVICE_CONTINUE_PENDING:
224 ServiceStatus.dwCheckPoint = ++dwCheckPoint;
225 break;
226 case SERVICE_STOPPED:
227 case SERVICE_RUNNING:
228 case SERVICE_PAUSED: ServiceStatus.dwCheckPoint = 0; break;
229 default: break;
230 }
231
232 return SetServiceStatus(hServiceStatus, &ServiceStatus) ? 0 : -1;
233}
234
235#elif _POSIX_C_SOURCE >= 200112L
236
237#include <fcntl.h>
238#include <poll.h>
239#include <sys/stat.h>
240#include <unistd.h>
241
242static int daemon_proc(void);
243static void daemon_signal_func(int sig);
244#if !LELY_NO_THREADS
245static int daemon_thrd_start(void *arg);
246#endif
247
248static int daemon_pipe[2] = { -1, -1 };
249
250int
251daemon_start(const char *name, int (*init)(int, char **), void (*main)(void),
252 void (*fini)(void), int argc, char *argv[])
253{
254#if LELY_NO_DIAG
255 (void)name;
256#endif
257 assert(main);
258 assert(argc >= 0);
259 assert(argv);
260
261 int result = 0;
262 int errsv = errno;
263
264 if (init && init(argc, argv)) {
265 result = -1;
266 errsv = errno;
267 goto error_init;
268 }
269
270 if (daemon_proc() == -1) {
271 result = -1;
272 errsv = errno;
273 goto error_proc;
274 }
275
276 // Create a non-blocking self-pipe.
277#if (defined(__CYGWIN__) || defined(__linux__)) && defined(_GNU_SOURCE)
278 result = pipe2(daemon_pipe, O_NONBLOCK | O_CLOEXEC);
279 if (result == -1) {
280 result = -1;
281 errsv = errno;
282 goto error_pipe;
283 }
284#else
285 result = pipe(daemon_pipe);
286 if (result == -1) {
287 result = -1;
288 errsv = errno;
289 goto error_pipe;
290 }
291 if (fcntl(daemon_pipe[0], F_SETFD, FD_CLOEXEC) == -1) {
292 result = -1;
293 errsv = errno;
294 goto error_fcntl;
295 }
296 if (fcntl(daemon_pipe[1], F_SETFD, FD_CLOEXEC) == -1) {
297 result = -1;
298 errsv = errno;
299 goto error_fcntl;
300 }
301 int flags;
302 if ((flags = fcntl(daemon_pipe[0], F_GETFL, 0)) == -1) {
303 result = -1;
304 errsv = errno;
305 goto error_fcntl;
306 }
307 if (fcntl(daemon_pipe[0], F_SETFL, flags | O_NONBLOCK) == -1) {
308 result = -1;
309 errsv = errno;
310 goto error_fcntl;
311 }
312 if ((flags = fcntl(daemon_pipe[1], F_GETFL, 0)) == -1) {
313 result = -1;
314 errsv = errno;
315 goto error_fcntl;
316 }
317 if (fcntl(daemon_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) {
318 result = -1;
319 errsv = errno;
320 goto error_fcntl;
321 }
322#endif
323
324 // SIGHUP is interpreted as DAEMON_RELOAD.
325 struct sigaction new_hup, old_hup;
326 new_hup.sa_handler = &daemon_signal_func;
327 sigemptyset(&new_hup.sa_mask);
328 new_hup.sa_flags = 0;
329 if (sigaction(SIGHUP, &new_hup, &old_hup) == -1) {
330 result = -1;
331 errsv = errno;
332 goto error_sighup;
333 }
334
335 // SIGTERM is interpreted as DAEMON_STOP.
336 struct sigaction new_term, old_term;
337 new_term.sa_handler = &daemon_signal_func;
338 sigemptyset(&new_term.sa_mask);
339 new_term.sa_flags = 0;
340 if (sigaction(SIGTERM, &new_term, &old_term) == -1) {
341 result = -1;
342 errsv = errno;
343 goto error_sigterm;
344 }
345
346#if !LELY_NO_THREADS
347 thrd_t thr;
348 if (thrd_create(&thr, &daemon_thrd_start, NULL) != thrd_success) {
349 result = -1;
350 goto error_thrd_create;
351 }
352#endif
353
354#if !LELY_NO_DIAG
355 diag_handler_t *diag_handler;
356 void *diag_handle;
357 diag_get_handler(&diag_handler, &diag_handle);
359
360 diag_at_handler_t *diag_at_handler;
361 void *diag_at_handle;
362 diag_at_get_handler(&diag_at_handler, &diag_at_handle);
364#endif
365
366 main();
367
368 daemon_stop();
369#if !LELY_NO_THREADS
370 thrd_join(thr, NULL);
371#endif
372
373#if !LELY_NO_DIAG
374 diag_at_set_handler(diag_at_handler, diag_at_handle);
375 diag_set_handler(diag_handler, diag_handle);
376#endif
377
378#if !LELY_NO_THREADS
379error_thrd_create:
380#endif
381 sigaction(SIGTERM, &old_term, NULL);
382error_sigterm:
383 sigaction(SIGHUP, &old_hup, NULL);
384error_sighup:
385#if !defined(__CYGWIN__) && !defined(__linux__)
386error_fcntl:
387#endif
388 close(daemon_pipe[1]);
389 daemon_pipe[1] = -1;
390 close(daemon_pipe[0]);
391 daemon_pipe[0] = -1;
392error_pipe:
393error_proc:
394 if (fini)
395 fini();
396error_init:
397 errno = errsv;
398 return result;
399}
400
401int
403{
404 if (sig < 0 || sig > DAEMON_USER_MAX) {
405 errno = EINVAL;
406 return -1;
407 }
408
409 int result;
410 do
411 result = write(daemon_pipe[1], &(unsigned char){ sig }, 1);
412 while (result == -1 && errno == EINTR);
413 return result;
414}
415
416int
417daemon_status(int status)
418{
419 if (status < 0 || status >= DAEMON_USER_MIN) {
420 errno = EINVAL;
421 return -1;
422 }
423
424 return 0;
425}
426
427static int
428daemon_proc(void)
429{
430 // Fork and exit the parent process to make the orphaned child a child
431 // process of init.
432 switch (fork()) {
433 case 0: break;
434 case -1: return -1;
435 default: _Exit(EXIT_SUCCESS);
436 }
437
438 // Change working directory to a known path.
439 if (chdir("/") == -1)
440 return -1;
441
442 // Prevent insecure file privileges.
443 umask(0);
444
445 // Detach the child process from the parent's tty.
446 if (setsid() == -1)
447 return -1;
448
449 // Because of the call to setsid(), we are now session leader, which
450 // means we can acquire another controlling tty. Prevent this by forking
451 // again.
452 switch (fork()) {
453 case 0: break;
454 case -1: return -1;
455 default: _Exit(EXIT_SUCCESS);
456 }
457
458 // Ignore terminal signals we shouldn't be receiving anyway.
459 struct sigaction act;
460 act.sa_handler = SIG_IGN;
461 sigemptyset(&act.sa_mask);
462 act.sa_flags = 0;
463 sigaction(SIGTSTP, &act, NULL);
464 sigaction(SIGTTIN, &act, NULL);
465 sigaction(SIGTTOU, &act, NULL);
466
467 // Redirect the standard streams to /dev/null. Since the link between
468 // file descriptors and streams is implementation-defined, we close and
469 // open both, to be on the safe side.
470
471 fsync(STDIN_FILENO);
472 fclose(stdin);
473 close(STDIN_FILENO);
474#if defined(__CYGWIN__) || defined(__linux__)
475 // cppcheck-suppress leakReturnValNotUsed
476 if (open("/dev/null", O_RDONLY | O_CLOEXEC) != STDIN_FILENO)
477 return -1;
478#else
479 // cppcheck-suppress leakReturnValNotUsed
480 if (open("/dev/null", O_RDONLY) != STDIN_FILENO)
481 return -1;
482 if (fcntl(STDIN_FILENO, F_SETFD, FD_CLOEXEC) == -1)
483 return -1;
484#endif
485 stdin = fdopen(STDIN_FILENO, "r");
486 if (!stdin)
487 return -1;
488
489 fflush(stdout);
490 fsync(STDOUT_FILENO);
491 fclose(stdout);
492 close(STDOUT_FILENO);
493#if defined(__CYGWIN__) || defined(__linux__)
494 // cppcheck-suppress leakReturnValNotUsed
495 if (open("/dev/null", O_WRONLY | O_CLOEXEC) != STDOUT_FILENO)
496 return -1;
497#else
498 // cppcheck-suppress leakReturnValNotUsed
499 if (open("/dev/null", O_WRONLY) != STDOUT_FILENO)
500 return -1;
501 if (fcntl(STDOUT_FILENO, F_SETFD, FD_CLOEXEC) == -1)
502 return -1;
503#endif
504 stdout = fdopen(STDOUT_FILENO, "w");
505 if (!stdout)
506 return -1;
507
508 fflush(stderr);
509 fsync(STDERR_FILENO);
510 fclose(stderr);
511 close(STDERR_FILENO);
512#if defined(__CYGWIN__) || defined(__linux__)
513 // cppcheck-suppress leakReturnValNotUsed
514 if (open("/dev/null", O_RDWR | O_CLOEXEC) != STDERR_FILENO)
515 return -1;
516#else
517 // cppcheck-suppress leakReturnValNotUsed
518 if (open("/dev/null", O_RDWR) != STDERR_FILENO)
519 return -1;
520 if (fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC) == -1)
521 return -1;
522#endif
523 stderr = fdopen(STDERR_FILENO, "rw");
524 if (!stderr)
525 return -1;
526
527 return 0;
528}
529
530static void
531daemon_signal_func(int sig)
532{
533 switch (sig) {
534 case SIGTERM: daemon_stop(); break;
535 case SIGHUP: daemon_reload(); break;
536 default: break;
537 }
538}
539
540#if !LELY_NO_THREADS
541static int
542daemon_thrd_start(void *arg)
543{
544 (void)arg;
545
546 for (;;) {
547 int result;
548 // Monitor the read end of the pipe for incoming data.
549 struct pollfd fds = { .fd = daemon_pipe[0], .events = POLLIN };
550 do
551 result = poll(&fds, 1, LELY_DAEMON_TIMEOUT);
552 while (result == -1 && errno == EINTR);
553 if (result != 1)
554 continue;
555 // Read a single signal value.
556 unsigned char uc = 0;
557 do
558 result = read(daemon_pipe[0], &uc, 1);
559 while (result == -1 && errno == EINTR);
560 if (result < 1)
561 continue;
562 int sig = uc;
563 // Execute the signal handler.
564 if (daemon_handler)
565 daemon_handler(sig, daemon_handle);
566 // Exit if we receive the stop signal.
567 if (sig == DAEMON_STOP)
568 break;
569 }
570
571 return 0;
572}
573#endif
574
575#endif // _WIN32
576
577int
579{
581}
582
583int
585{
587}
588
589int
591{
593}
594
595int
597{
599}
600
601void
602daemon_get_handler(daemon_handler_t **phandler, void **phandle)
603{
604 if (phandler)
605 *phandler = daemon_handler;
606 if (phandle)
607 *phandle = daemon_handle;
608}
609
610void
612{
613 daemon_handler = handler;
614 daemon_handle = handle;
615}
616
617void
618default_daemon_handler(int sig, void *handle)
619{
620 (void)handle;
621
622 switch (sig) {
626 }
627}
628
629#endif // !LELY_NO_DAEMON
int daemon_stop(void)
Sends the stop signal to the daemon handler.
Definition daemon.c:578
int daemon_pause(void)
Sends the pause signal to the daemon handler.
Definition daemon.c:590
void default_daemon_handler(int sig, void *handle)
The default daemon_signal() handler.
Definition daemon.c:618
int daemon_signal(int sig)
Sends a signal to a daemon, triggering the execution of the daemon handler.
Definition daemon.c:402
void daemon_get_handler(daemon_handler_t **phandler, void **phandle)
Retrieves current daemon handler and handle argument.
Definition daemon.c:602
int daemon_reload(void)
Sends the reload signal to the daemon handler.
Definition daemon.c:584
void daemon_set_handler(daemon_handler_t *handler, void *handle)
Sets the current daemon handler and its (optional) handle argument.
Definition daemon.c:611
int daemon_continue(void)
Sends the continue signal to the daemon handler.
Definition daemon.c:596
int daemon_start(const char *name, int(*init)(int, char **), void(*main)(void), void(*fini)(void), int argc, char *argv[])
Executes the supplied function as a POSIX daemon or Windows service.
Definition daemon.c:251
int daemon_status(int status)
Sets the current daemon status (one of DAEMON_START, DAEMON_STOP, DAEMON_PAUSE or DAEMON_CONTINUE).
Definition daemon.c:417
This header file is part of the utilities library; it contains the daemon declarations.
void daemon_handler_t(int sig, void *handle)
The function type of a handler for daemon_signal().
Definition daemon.h:68
@ DAEMON_START
The status indicating the daemon has started.
Definition daemon.h:29
@ DAEMON_CONTINUE
The signal/status indicating the daemon SHOULD continue/has continued normal operation.
Definition daemon.h:41
@ DAEMON_RELOAD
The signal indicating the daemon SHOULD reload its configuration.
Definition daemon.h:43
@ DAEMON_USER_MAX
The largest possible value of a user-defined signal.
Definition daemon.h:51
@ DAEMON_PAUSE
The signal/status indicating the daemon SHOULD pause/has paused.
Definition daemon.h:36
@ DAEMON_STOP
The signal/status indicating the daemon MUST terminate/has terminated.
Definition daemon.h:34
@ DAEMON_USER_MIN
The smallest possible value of a user-defined signal.
Definition daemon.h:45
This header file is part of the utilities library; it contains the diagnostic declarations.
void daemon_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The diag_at() handler for daemons.
Definition diag.c:280
void diag_set_handler(diag_handler_t *handler, void *handle)
Sets the handler function for diag().
Definition diag.c:148
void diag_at_set_handler(diag_at_handler_t *handler, void *handle)
Sets the handler function for diag_at().
Definition diag.c:164
void diag_at_handler_t(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The function type of a handler for diag_at().
Definition diag.h:90
void diag_at_get_handler(diag_at_handler_t **phandler, void **phandle)
Retrieves the handler function for diag_at().
Definition diag.c:155
void daemon_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler for daemons.
Definition diag.c:273
void diag_handler_t(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The function type of a handler for diag().
Definition diag.h:76
void diag_get_handler(diag_handler_t **phandler, void **phandle)
Retrieves the handler function for diag().
Definition diag.c:139
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
Creates a new thread executing func(arg).
int thrd_join(thrd_t thr, int *res)
Joins the thread identified by thr with the current thread by blocking until the other thread has ter...
@ thrd_success
Indicates that the requested operation succeeded.
Definition threads.h:121
This header file is part of the C11 and POSIX compatibility library; it includes <unistd....