Skip to content

Commit

Permalink
Make signal handling async-signal-safe
Browse files Browse the repository at this point in the history
Use a pipe and glib io watch to transfer signals from async-signal-safe
signal handler for processing in mainloop context.

[usb-moded] Make signal handling async-signal-safe. Fixes NB#17368

Signed-off-by: Simo Piiroinen <simo.piiroinen@jollamobile.com>
  • Loading branch information
spiiroin committed Mar 26, 2014
1 parent c933fd9 commit 8a6191f
Showing 1 changed file with 162 additions and 3 deletions.
165 changes: 162 additions & 3 deletions src/usb_moded.c
Expand Up @@ -664,6 +664,163 @@ static void send_supported_modes_signal(void)
g_free(mode_list);
}

/** Pipe fd for transferring signals to mainloop context */
static int sigpipe_fd = -1;

/** Glib io watch callback for reading signals from signal pipe
*
* @param channel glib io channel
* @param condition wakeup reason
* @param data user data (unused)
*
* @return TRUE to keep the iowatch, or FALSE to disable it
*/
static gboolean sigpipe_read_signal_cb(GIOChannel *channel,
GIOCondition condition,
gpointer data)
{
gboolean keep_watch = FALSE;

int fd, rc, sig;

(void)data;

/* Should never happen, but we must disable the io watch
* if the pipe fd still goes into unexpected state ... */
if( condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) )
goto EXIT;

if( (fd = g_io_channel_unix_get_fd(channel)) == -1 )
goto EXIT;

/* If the actual read fails, terminate with core dump */
rc = TEMP_FAILURE_RETRY(read(fd, &sig, sizeof sig));
if( rc != (int)sizeof sig )
abort();

/* handle the signal */
log_warning("handle signal: %s\n", strsignal(sig));
sigint_handler(sig);

keep_watch = TRUE;

EXIT:
if( !keep_watch )
log_crit("disabled signal handler io watch\n");

return keep_watch;
}

/** Async signal handler for writing signals to signal pipe
*
* @param sig the signal number to pass to mainloop via pipe
*/
static void sigpipe_write_signal_cb(int sig)
{
/* NOTE: This function *MUST* be kept async-signal-safe! */

static volatile int exit_tries = 0;

int rc;

/* Restore signal handler */
signal(sig, sigpipe_write_signal_cb);

switch( sig )
{
case SIGINT:
case SIGQUIT:
case SIGTERM:
/* If we receive multiple signals that should have
* caused the process to exit, assume that mainloop
* is stuck and terminate with core dump. */
if( ++exit_tries >= 2 )
abort();
break;

default:
break;
}

/* Transfer the signal to mainloop via pipe ... */
rc = TEMP_FAILURE_RETRY(write(sigpipe_fd, &sig, sizeof sig));

/* ... or terminate with core dump in case of failures */
if( rc != (int)sizeof sig )
abort();
}

/** Create a pipe and io watch for handling signal from glib mainloop
*
* @return TRUE on success, or FALSE in case of errors
*/
static gboolean sigpipe_crate_pipe(void)
{
gboolean res = FALSE;
GIOChannel *chn = 0;
int pfd[2] = { -1, -1 };

if( pipe2(pfd, O_CLOEXEC) == -1 )
goto EXIT;

if( (chn = g_io_channel_unix_new(pfd[0])) == 0 )
goto EXIT;

if( !g_io_add_watch(chn, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
sigpipe_read_signal_cb, 0) )
goto EXIT;

g_io_channel_set_close_on_unref(chn, TRUE), pfd[0] = -1;
sigpipe_fd = pfd[1], pfd[1] = -1;

res = TRUE;

EXIT:
if( chn ) g_io_channel_unref(chn);
if( pfd[0] != -1 ) close(pfd[0]);
if( pfd[1] != -1 ) close(pfd[1]);

return res;
}

/** Install async signal handlers
*/
static void sigpipe_trap_signals(void)
{
static const int sig[] =
{
SIGINT,
SIGQUIT,
SIGTERM,
SIGHUP,
-1
};

for( size_t i = 0; sig[i] != -1; ++i )
{
signal(sig[i], sigpipe_write_signal_cb);
}
}

/** Initialize signal trapping
*
* @return TRUE on success, or FALSE in case of errors
*/
static gboolean sigpipe_init(void)
{
gboolean success = FALSE;

if( !sigpipe_crate_pipe() )
goto EXIT;

sigpipe_trap_signals();

success = TRUE;

EXIT:
return success;
}

int main(int argc, char* argv[])
{
int result = EXIT_FAILURE;
Expand Down Expand Up @@ -769,9 +926,11 @@ int main(int argc, char* argv[])
#endif /* MEEGOLOCK */

/* signal handling */
signal(SIGINT, sigint_handler);
signal(SIGTERM, sigint_handler);
signal(SIGHUP, sigint_handler);
if( !sigpipe_init() )
{
log_crit("signal handler init failed\n");
goto EXIT;
}

#ifdef SYSTEMD
/* Tell systemd that we have started up */
Expand Down

0 comments on commit 8a6191f

Please sign in to comment.