@cryptotaxi247 / netdata-1 / commits / a7a005730

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