cli: handle POSIX signals in a dedicated thread
For a multihreaded application the safest way to handle unix signals is using a dedicated thread that processes the signals by sigwait() function. All other threads have these signals (processed by the thread) blocked. A few useful links: http://pubs.opengroup.org/onlinepubs/007904975/functions/sigwait.html http://pic.dhe.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.genprogc%2Fdoc%2Fgenprogc%2Fsignal_mgmt.htm http://www.linuxjournal.com/article/2121?page=0,2 http://www.redwoodsoft.com/~dru/unixbook/book.chinaunix.net/special/ebook/addisonWesley/APUE2/0201433079/ch12lev1sec8.html https://www.securecoding.cert.org/confluence/display/seccode/CON37-C.+Do+not+call+signal%28%29+in+a+multithreaded+program
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@@ -55,6 +56,7 @@ typedef struct {
|
|||||||
|
|
||||||
/* --- Global variables --- */
|
/* --- Global variables --- */
|
||||||
GMainLoop *loop = NULL;
|
GMainLoop *loop = NULL;
|
||||||
|
static sigset_t signal_set;
|
||||||
|
|
||||||
|
|
||||||
/* Get an error quark for use with GError */
|
/* Get an error quark for use with GError */
|
||||||
@@ -263,27 +265,67 @@ parse_command_line (NmCli *nmc, int argc, char **argv)
|
|||||||
return nmc->return_value;
|
return nmc->return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void *signal_handling_thread (void *arg);
|
||||||
signal_handler (int signo)
|
/*
|
||||||
{
|
* Thread function waiting for signals and processing them.
|
||||||
if (signo == SIGINT || signo == SIGTERM) {
|
* Wait for signals in signal set. The semantics of sigwait() require that all
|
||||||
g_message (_("Caught signal %d, shutting down..."), signo);
|
* threads (including the thread calling sigwait()) have the signal masked, for
|
||||||
g_main_loop_quit (loop);
|
* reliable operation. Otherwise, a signal that arrives while this thread is
|
||||||
|
* not blocked in sigwait() might be delivered to another thread.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
signal_handling_thread (void *arg) {
|
||||||
|
int signo;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
sigwait (&signal_set, &signo);
|
||||||
|
|
||||||
|
switch (signo) {
|
||||||
|
case SIGINT:
|
||||||
|
case SIGQUIT:
|
||||||
|
case SIGTERM:
|
||||||
|
printf (_("\nError: nmcli terminated by signal %d."), signo);
|
||||||
|
exit (1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/*
|
||||||
|
* Mask the signals we are interested in and create a signal handling thread.
|
||||||
|
* Because all threads inherit the signal mask from their creator, all threads
|
||||||
|
* in the process will have the signals masked. That's why setup_signals() has
|
||||||
|
* to be called before creating other threads.
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
setup_signals (void)
|
setup_signals (void)
|
||||||
{
|
{
|
||||||
struct sigaction action;
|
pthread_t signal_thread_id;
|
||||||
sigset_t mask;
|
int status;
|
||||||
|
|
||||||
sigemptyset (&mask);
|
sigemptyset (&signal_set);
|
||||||
action.sa_handler = signal_handler;
|
sigaddset (&signal_set, SIGINT);
|
||||||
action.sa_mask = mask;
|
sigaddset (&signal_set, SIGQUIT);
|
||||||
action.sa_flags = 0;
|
sigaddset (&signal_set, SIGTERM);
|
||||||
sigaction (SIGTERM, &action, NULL);
|
|
||||||
sigaction (SIGINT, &action, NULL);
|
/* Block all signals of interest. */
|
||||||
|
status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL);
|
||||||
|
if (status != 0) {
|
||||||
|
fprintf (stderr, _("Failed to set signal mask: %d"), status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the signal handling thread. */
|
||||||
|
status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL);
|
||||||
|
if (status != 0) {
|
||||||
|
fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NMClient *
|
static NMClient *
|
||||||
@@ -368,6 +410,10 @@ main (int argc, char *argv[])
|
|||||||
NmCli nmc;
|
NmCli nmc;
|
||||||
ArgsInfo args_info = { &nmc, argc, argv };
|
ArgsInfo args_info = { &nmc, argc, argv };
|
||||||
|
|
||||||
|
/* Set up unix signal handling */
|
||||||
|
if (!setup_signals ())
|
||||||
|
exit (NMC_RESULT_ERROR_UNKNOWN);
|
||||||
|
|
||||||
/* Set locale to use environment variables */
|
/* Set locale to use environment variables */
|
||||||
setlocale (LC_ALL, "");
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
@@ -384,7 +430,6 @@ main (int argc, char *argv[])
|
|||||||
g_idle_add (start, &args_info);
|
g_idle_add (start, &args_info);
|
||||||
|
|
||||||
loop = g_main_loop_new (NULL, FALSE); /* create main loop */
|
loop = g_main_loop_new (NULL, FALSE); /* create main loop */
|
||||||
setup_signals (); /* setup UNIX signals */
|
|
||||||
g_main_loop_run (loop); /* run main loop */
|
g_main_loop_run (loop); /* run main loop */
|
||||||
|
|
||||||
/* Print result descripting text */
|
/* Print result descripting text */
|
||||||
|
Reference in New Issue
Block a user