Make signal handling save.
Only call reentrant functions in signal handlers. Block signals except on the main thread. Quit savely only on functions specified for this purpose. Optimize main loop. Restore signals for external plugins. Ignore SIGPIPE
Simon Nagl committed
May 10, 2016 at 19:59 UTC
a7a0057309c7b101c66e9969753dd3484637a372
4 files changed
+73
-92
src/daemon.c
+1
-47
@@ -30,49 +30,7 @@ int pidfd = -1;
30
31
void sig_handler(int signo)
32
{
33
- switch(signo) {
34
- case SIGILL:
35
- case SIGABRT:
36
- case SIGFPE:
37
- case SIGSEGV:
38
- case SIGBUS:
39
- case SIGSYS:
40
- case SIGTRAP:
41
- case SIGXCPU:
42
- case SIGXFSZ:
43
- infoerr("Death signaled exit (signal %d).", signo);
44
- signal(signo, SIG_DFL);
45
- break;
46
-
47
- case SIGKILL:
48
- case SIGTERM:
49
- case SIGQUIT:
50
- case SIGINT:
51
- case SIGHUP:
52
- case SIGUSR1:
53
- case SIGUSR2:
54
- infoerr("Signaled exit (signal %d).", signo);
55
- signal(SIGPIPE, SIG_IGN);
56
- signal(SIGTERM, SIG_IGN);
57
- signal(SIGQUIT, SIG_IGN);
58
- signal(SIGHUP, SIG_IGN);
59
- signal(SIGINT, SIG_IGN);
60
- signal(SIGCHLD, SIG_IGN);
61
- netdata_cleanup_and_exit(1);
62
- break;
63
-
64
- case SIGPIPE:
65
- infoerr("Signaled PIPE (signal %d).", signo);
66
- // this is received when web clients send a reset
67
- // no need to log it.
68
- // infoerr("Ignoring signal %d.", signo);
69
- break;
70
-
71
- default:
72
- info("Signal %d received. Falling back to default action for it.", signo);
73
- signal(signo, SIG_DFL);
74
- break;
75
- }
33
+ netdata_exit = 1;
34
}
35
36
int become_user(const char *username)
@@ -247,10 +205,6 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch
205
}
206
}
207
250
- signal(SIGCHLD, SIG_IGN);
251
- signal(SIGHUP, SIG_IGN);
252
- signal(SIGWINCH, SIG_IGN);
253
-
208
// fork() again
209
if(!dont_fork) {
210
int i = fork();
src/main.c
+51
-42
@@ -39,7 +39,7 @@
39
40
extern void *cgroups_main(void *ptr);
41
42
-int netdata_exit = 0;
42
+volatile sig_atomic_t netdata_exit = 0;
43
44
void netdata_cleanup_and_exit(int ret)
45
{
@@ -122,19 +122,7 @@ int killpid(pid_t pid, int sig)
122
}
123
else {
124
errno = 0;
125
-
126
- void (*old)(int);
127
- old = signal(sig, SIG_IGN);
128
- if(old == SIG_ERR) {
129
- error("Cannot overwrite signal handler for signal %d", sig);
130
- old = sig_handler;
131
- }
132
-
125
ret = kill(pid, sig);
134
-
135
- if(signal(sig, old) == SIG_ERR)
136
- error("Cannot restore signal handler for signal %d", sig);
137
-
126
if(ret == -1) {
127
switch(errno) {
128
case ESRCH:
@@ -410,6 +398,41 @@ int main(int argc, char **argv)
398
399
// --------------------------------------------------------------------
400
401
+ // block signals while initializing threads.
402
+ // this causes the threads to block signals.
403
+ sigset_t sigset;
404
+ sigfillset(&sigset);
405
+
406
+ if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
407
+ error("Could not block signals for threads");
408
+ }
409
+
410
+ // Catch signals which we want to use to quit savely
411
+ struct sigaction sa;
412
+ sigemptyset(&sa.sa_mask);
413
+ sigaddset(&sa.sa_mask, SIGHUP);
414
+ sigaddset(&sa.sa_mask, SIGINT);
415
+ sigaddset(&sa.sa_mask, SIGTERM);
416
+ sa.sa_handler = sig_handler;
417
+ if(sigaction(SIGHUP, &sa, NULL) == -1) {
418
+ error("Failed to change signal handler for SIGHUP");
419
+ }
420
+ if(sigaction(SIGINT, &sa, NULL) == -1) {
421
+ error("Failed to change signal handler for SIGINT");
422
+ }
423
+ if(sigaction(SIGTERM, &sa, NULL) == -1) {
424
+ error("Failed to change signal handler for SIGTERM");
425
+ }
426
+ // Ignore SIGPIPE completely.
427
+ // INFO: If we add signals here we have to unblock them
428
+ // at popen.c when running a external plugin.
429
+ sa.sa_handler = SIG_IGN;
430
+ if(sigaction(SIGPIPE, &sa, NULL) == -1) {
431
+ error("Failed to change signal handler for SIGTERM");
432
+ }
433
+
434
+ // --------------------------------------------------------------------
435
+
436
i = pthread_attr_init(&attr);
437
if(i != 0)
438
fatal("pthread_attr_init() failed with code %d.", i);
@@ -490,24 +513,6 @@ int main(int argc, char **argv)
513
info("NetData started on pid %d", getpid());
514
515
493
- // catch all signals
494
- for (i = 1 ; i < 65 ;i++) {
495
- switch(i) {
496
- case SIGKILL: // not catchable
497
- case SIGSTOP: // not catchable
498
- break;
499
-
500
- case SIGSEGV:
501
- case SIGFPE:
502
- case SIGCHLD:
503
- signal(i, SIG_DFL);
504
- break;
505
-
506
- default:
507
- signal(i, sig_handler);
508
- break;
509
- }
510
- }
516
517
// ------------------------------------------------------------------------
518
// get default pthread stack size
@@ -542,18 +547,22 @@ int main(int argc, char **argv)
547
else info("Not starting thread %s.", st->name);
548
}
549
545
- // for future use - the main thread
546
- while(1) {
547
- if(netdata_exit != 0) {
548
- netdata_exit++;
550
+ // ------------------------------------------------------------------------
551
+ // block signals while initializing threads.
552
+ sigset_t sigset;
553
+ sigfillset(&sigset);
554
550
- if(netdata_exit > 5) {
551
- netdata_cleanup_and_exit(0);
552
- exit(0);
553
- }
554
- }
555
- sleep(2);
555
+ if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
556
+ error("Could not unblock signals for threads");
557
}
558
558
- exit(0);
559
+ // Handle flags set in the signal handler.
560
+ while(1) {
561
+ pause();
562
+ if(netdata_exit) {
563
+ info("Exit main loop of netdata.");
564
+ netdata_cleanup_and_exit(0);
565
+ exit(0);
566
+ }
567
+ }
568
}
src/main.h
+3
-1
@@ -1,7 +1,9 @@
1
#ifndef NETDATA_MAIN_H
2
#define NETDATA_MAIN_H 1
3
4
-extern int netdata_exit;
4
+#include <signal.h>
5
+
6
+extern volatile sig_atomic_t netdata_exit;
7
8
extern void kill_childs(void);
9
extern int killpid(pid_t pid, int signal);
src/popen.c
+18
-2
@@ -114,10 +114,26 @@ FILE *mypopen(const char *command, pid_t *pidptr)
114
#endif
115
116
// reset all signals
117
- for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
117
+ {
118
+ sigset_t sigset;
119
+ sigfillset(&sigset);
120
+
121
+ if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
122
+ error("Could not block signals for threads");
123
+ }
124
+ // We only need to reset ignored signals.
125
+ // Signals with signal handlers are reset by default.
126
+ struct sigaction sa;
127
+ sigemptyset(&sa.sa_mask);
128
+ sa.sa_handler = SIG_DFL;
129
+ if(sigaction(SIGPIPE, &sa, NULL) == -1) {
130
+ error("Failed to change signal handler for SIGTERM");
131
+ }
132
+ }
133
+
134
135
info("executing command: '%s' on pid %d.", command, getpid());
120
- execl("/bin/sh", "sh", "-c", command, NULL);
136
+ execl("/bin/sh", "sh", "-c", command, NULL);
137
exit(1);
138
}
139