Index: nbd-3.10/nbd-server.c =================================================================== --- nbd-3.10.orig/nbd-server.c +++ nbd-3.10/nbd-server.c @@ -168,6 +168,16 @@ char default_authname[] = SYSCONFDIR "/n #include +static volatile sig_atomic_t is_sigchld_caught; /**< Flag set by + SIGCHLD handler + to mark a child + exit */ + +static volatile sig_atomic_t is_sigterm_caught; /**< Flag set by + SIGTERM handler + to mark a exit + request */ + static volatile sig_atomic_t is_sighup_caught; /**< Flag set by SIGHUP handler to mark a reconfiguration @@ -930,27 +940,16 @@ GArray* parse_cfile(gchar* f, struct gen } /** - * Signal handler for SIGCHLD + * Handle SIGCHLD by setting atomically a flag which will be evaluated in the + * main loop of the root server process. This allows us to separate the signal + * catching from th actual task triggered by SIGCHLD and hence processing in the + * interrupt context is kept as minimial as possible. + * * @param s the signal we're handling (must be SIGCHLD, or something * is severely wrong) **/ -void sigchld_handler(int s) { - int status; - int* i; - pid_t pid; - - while((pid=waitpid(-1, &status, WNOHANG)) > 0) { - if(WIFEXITED(status)) { - msg(LOG_INFO, "Child exited with %d", WEXITSTATUS(status)); - } - i=g_hash_table_lookup(children, &pid); - if(!i) { - msg(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid); - } else { - DEBUG("Removing %d from the list of children", pid); - g_hash_table_remove(children, &pid); - } - } +static void sigchld_handler(const int s G_GNUC_UNUSED) { + is_sigchld_caught = 1; } /** @@ -968,15 +967,16 @@ void killchild(gpointer key, gpointer va } /** - * Handle SIGTERM and dispatch it to our children + * Handle SIGTERM by setting atomically a flag which will be evaluated in the + * main loop of the root server process. This allows us to separate the signal + * catching from th actual task triggered by SIGTERM and hence processing in the + * interrupt context is kept as minimial as possible. + * * @param s the signal we're handling (must be SIGTERM, or something * is severely wrong). **/ -void sigterm_handler(int s) { - g_hash_table_foreach(children, killchild, NULL); - unlink(pidfname); - - exit(EXIT_SUCCESS); +static void sigterm_handler(const int s G_GNUC_UNUSED) { + is_sigterm_caught = 1; } /** @@ -2066,9 +2066,12 @@ spawn_child() goto out; } /* Child */ + + /* Child's signal disposition is reset to default. */ signal(SIGCHLD, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGHUP, SIG_DFL); + sigemptyset(&oldset); out: sigprocmask(SIG_SETMASK, &oldset, NULL); return pid; @@ -2262,9 +2265,12 @@ handle_oldstyle_connection(GArray *const goto handle_connection_out; } /* child */ + + /* Child's signal disposition is reset to default. */ signal(SIGCHLD, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGHUP, SIG_DFL); + sigemptyset(&oldset); sigprocmask(SIG_SETMASK, &oldset, NULL); g_hash_table_destroy(children); @@ -2368,6 +2374,8 @@ void serveloop(GArray* servers) { int max; fd_set mset; fd_set rset; + sigset_t blocking_mask; + sigset_t original_mask; /* * Set up the master fd_set. The set of descriptors we need @@ -2390,7 +2398,56 @@ void serveloop(GArray* servers) { FD_SET(sock, &mset); max=sock>max?sock:max; } + + /* Construct a signal mask which is used to make signal testing and + * receiving an atomic operation to ensure no signal is received between + * tests and blocking pselect(). */ + if (sigemptyset(&blocking_mask) == -1) + err("failed to initialize blocking_mask: %m"); + + if (sigaddset(&blocking_mask, SIGCHLD) == -1) + err("failed to add SIGCHLD to blocking_mask: %m"); + + if (sigaddset(&blocking_mask, SIGHUP) == -1) + err("failed to add SIGHUP to blocking_mask: %m"); + + if (sigaddset(&blocking_mask, SIGTERM) == -1) + err("failed to add SIGTERM to blocking_mask: %m"); + + if (sigprocmask(SIG_BLOCK, &blocking_mask, &original_mask) == -1) + err("failed to block signals: %m"); + for(;;) { + if (is_sigterm_caught) { + is_sigterm_caught = 0; + + g_hash_table_foreach(children, killchild, NULL); + unlink(pidfname); + + exit(EXIT_SUCCESS); + } + + if (is_sigchld_caught) { + int status; + int* i; + pid_t pid; + + is_sigchld_caught = 0; + + while ((pid=waitpid(-1, &status, WNOHANG)) > 0) { + if (WIFEXITED(status)) { + msg(LOG_INFO, "Child exited with %d", WEXITSTATUS(status)); + } + i = g_hash_table_lookup(children, &pid); + if (!i) { + msg(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid); + } else { + DEBUG("Removing %d from the list of children", pid); + g_hash_table_remove(children, &pid); + } + } + } + /* SIGHUP causes the root server process to reconfigure * itself and add new export servers for each newly * found export configuration group, i.e. spawn new @@ -2425,8 +2482,7 @@ void serveloop(GArray* servers) { } memcpy(&rset, &mset, sizeof(fd_set)); - if(select(max+1, &rset, NULL, NULL, NULL)>0) { - + if (pselect(max + 1, &rset, NULL, NULL, NULL, &original_mask) > 0) { DEBUG("accept, "); for(i=0; i < modernsocks->len; i++) { int sock = g_array_index(modernsocks, int, i);