2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-27 15:18:06 +00:00
Files
openvswitch/lib/fatal-signal.c
Jesse Gross d8b3070205 fatal-signal: Run signal hooks outside of actual signal handlers.
Rather than running signal hooks directly from the actual signal
handler, simply record the fact that the signal occured and run
the hook next time around the poll loop.  This allows significantly
more freedom as to what can actually be done in the signal hooks.
2010-01-06 09:11:58 -05:00

265 lines
6.7 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2008, 2009 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "fatal-signal.h"
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "poll-loop.h"
#include "shash.h"
#include "socket-util.h"
#include "util.h"
#define THIS_MODULE VLM_fatal_signal
#include "vlog.h"
/* Signals to catch. */
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
/* Signals to catch as a sigset_t. */
static sigset_t fatal_signal_set;
/* Hooks to call upon catching a signal */
struct hook {
void (*func)(void *aux);
void *aux;
bool run_at_exit;
};
#define MAX_HOOKS 32
static struct hook hooks[MAX_HOOKS];
static size_t n_hooks;
static int signal_fds[2];
static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX;
/* Disabled by fatal_signal_fork()? */
static bool disabled;
static void fatal_signal_init(void);
static void atexit_handler(void);
static void call_hooks(int sig_nr);
static void
fatal_signal_init(void)
{
static bool inited = false;
if (!inited) {
size_t i;
inited = true;
if (pipe(signal_fds)) {
ovs_fatal(errno, "could not create pipe");
}
set_nonblocking(signal_fds[0]);
set_nonblocking(signal_fds[1]);
sigemptyset(&fatal_signal_set);
for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
int sig_nr = fatal_signals[i];
struct sigaction old_sa;
sigaddset(&fatal_signal_set, sig_nr);
if (sigaction(sig_nr, NULL, &old_sa)) {
ovs_fatal(errno, "sigaction");
}
if (old_sa.sa_handler == SIG_DFL
&& signal(sig_nr, fatal_signal_handler) == SIG_ERR) {
ovs_fatal(errno, "signal");
}
}
atexit(atexit_handler);
}
}
/* Registers 'hook' to be called when a process termination signal is raised.
* If 'run_at_exit' is true, 'hook' is also called during normal process
* termination, e.g. when exit() is called or when main() returns.
*
* The hook is not called immediately from the signal handler but rather the
* next time the poll loop iterates, so it is freed from the usual restrictions
* on signal handler functions. */
void
fatal_signal_add_hook(void (*func)(void *aux), void *aux, bool run_at_exit)
{
fatal_signal_init();
assert(n_hooks < MAX_HOOKS);
hooks[n_hooks].func = func;
hooks[n_hooks].aux = aux;
hooks[n_hooks].run_at_exit = run_at_exit;
n_hooks++;
}
/* Handles fatal signal number 'sig_nr'.
*
* Ordinarily this is the actual signal handler. When other code needs to
* handle one of our signals, however, it can register for that signal and, if
* and when necessary, call this function to do fatal signal processing for it
* and terminate the process. Currently only timeval.c does this, for SIGALRM.
* (It is not important whether the other code sets up its signal handler
* before or after this file, because this file will only set up a signal
* handler in the case where the signal has its default handling.) */
void
fatal_signal_handler(int sig_nr)
{
ignore(write(signal_fds[1], "", 1));
stored_sig_nr = sig_nr;
}
void
fatal_signal_run(void)
{
int sig_nr = stored_sig_nr;
if (sig_nr != SIG_ATOMIC_MAX) {
call_hooks(sig_nr);
/* Re-raise the signal with the default handling so that the program
* termination status reflects that we were killed by this signal */
signal(sig_nr, SIG_DFL);
raise(sig_nr);
}
}
void
fatal_signal_wait(void)
{
poll_fd_wait(signal_fds[0], POLLIN);
}
static void
atexit_handler(void)
{
if (!disabled) {
call_hooks(0);
}
}
static void
call_hooks(int sig_nr)
{
static volatile sig_atomic_t recurse = 0;
if (!recurse) {
size_t i;
recurse = 1;
for (i = 0; i < n_hooks; i++) {
struct hook *h = &hooks[i];
if (sig_nr || h->run_at_exit) {
h->func(h->aux);
}
}
}
}
static struct shash files = SHASH_INITIALIZER(&files);
static void unlink_files(void *aux);
static void do_unlink_files(void);
/* Registers 'file' to be unlinked when the program terminates via exit() or a
* fatal signal. */
void
fatal_signal_add_file_to_unlink(const char *file)
{
static bool added_hook = false;
if (!added_hook) {
added_hook = true;
fatal_signal_add_hook(unlink_files, NULL, true);
}
if (!shash_find(&files, file)) {
shash_add(&files, file, NULL);
}
}
/* Unregisters 'file' from being unlinked when the program terminates via
* exit() or a fatal signal. */
void
fatal_signal_remove_file_to_unlink(const char *file)
{
struct shash_node *node;
node = shash_find(&files, file);
if (node) {
shash_delete(&files, node);
}
}
/* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'.
* Returns 0 if successful, otherwise a positive errno value. */
int
fatal_signal_unlink_file_now(const char *file)
{
int error = unlink(file) ? errno : 0;
if (error) {
VLOG_WARN("could not unlink \"%s\" (%s)", file, strerror(error));
}
fatal_signal_remove_file_to_unlink(file);
return error;
}
static void
unlink_files(void *aux UNUSED)
{
do_unlink_files();
}
static void
do_unlink_files(void)
{
struct shash_node *node;
SHASH_FOR_EACH (node, &files) {
unlink(node->name);
}
}
/* Disables the fatal signal hook mechanism. Following a fork, one of the
* resulting processes can call this function to allow it to terminate without
* triggering fatal signal processing or removing files. Fatal signal
* processing is still enabled in the other process. */
void
fatal_signal_fork(void)
{
size_t i;
disabled = true;
for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
int sig_nr = fatal_signals[i];
if (signal(sig_nr, SIG_DFL) == SIG_IGN) {
signal(sig_nr, SIG_IGN);
}
}
/* Raise any signals that we have already received with the default
* handler. */
if (stored_sig_nr != SIG_ATOMIC_MAX) {
raise(stored_sig_nr);
}
}