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