X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fglobal%2Fsignal_handler.cc;fp=src%2Fceph%2Fsrc%2Fglobal%2Fsignal_handler.cc;h=d4099e1e201ee73c5a1dad39c5e8ee2044c4adc5;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/global/signal_handler.cc b/src/ceph/src/global/signal_handler.cc new file mode 100644 index 0000000..d4099e1 --- /dev/null +++ b/src/ceph/src/global/signal_handler.cc @@ -0,0 +1,438 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/compat.h" +#include "pthread.h" + +#include "common/BackTrace.h" +#include "common/debug.h" +#include "global/pidfile.h" +#include "global/signal_handler.h" + +#include +#include +#include +#include +#include +#include +#include "common/errno.h" +#if defined(_AIX) +extern char *sys_siglist[]; +#endif + +#define dout_context g_ceph_context + +void install_sighandler(int signum, signal_handler_t handler, int flags) +{ + int ret; + struct sigaction oldact; + struct sigaction act; + memset(&act, 0, sizeof(act)); + + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = flags; + + ret = sigaction(signum, &act, &oldact); + if (ret != 0) { + char buf[1024]; +#if defined(__sun) + char message[SIG2STR_MAX]; + sig2str(signum,message); + snprintf(buf, sizeof(buf), "install_sighandler: sigaction returned " + "%d when trying to install a signal handler for %s\n", + ret, message); +#else + snprintf(buf, sizeof(buf), "install_sighandler: sigaction returned " + "%d when trying to install a signal handler for %s\n", + ret, sig_str(signum)); +#endif + dout_emergency(buf); + exit(1); + } +} + +void sighup_handler(int signum) +{ + g_ceph_context->reopen_logs(); +} + +static void reraise_fatal(int signum) +{ + // Use default handler to dump core + int ret = raise(signum); + + // Normally, we won't get here. If we do, something is very weird. + char buf[1024]; + if (ret) { + snprintf(buf, sizeof(buf), "reraise_fatal: failed to re-raise " + "signal %d\n", signum); + dout_emergency(buf); + } + else { + snprintf(buf, sizeof(buf), "reraise_fatal: default handler for " + "signal %d didn't terminate the process?\n", signum); + dout_emergency(buf); + } + exit(1); +} + +static void handle_fatal_signal(int signum) +{ + // This code may itself trigger a SIGSEGV if the heap is corrupt. In that + // case, SA_RESETHAND specifies that the default signal handler-- + // presumably dump core-- will handle it. + char buf[1024]; + char pthread_name[16] = {0}; //limited by 16B include terminating null byte. + int r = ceph_pthread_getname(pthread_self(), pthread_name, sizeof(pthread_name)); + (void)r; +#if defined(__sun) + char message[SIG2STR_MAX]; + sig2str(signum,message); + snprintf(buf, sizeof(buf), "*** Caught signal (%s) **\n " + "in thread %llx thread_name:%s\n", message, (unsigned long long)pthread_self(), + pthread_name); +#else + snprintf(buf, sizeof(buf), "*** Caught signal (%s) **\n " + "in thread %llx thread_name:%s\n", sig_str(signum), (unsigned long long)pthread_self(), + pthread_name); +#endif + dout_emergency(buf); + pidfile_remove(); + + // TODO: don't use an ostringstream here. It could call malloc(), which we + // don't want inside a signal handler. + // Also fix the backtrace code not to allocate memory. + BackTrace bt(0); + ostringstream oss; + bt.print(oss); + dout_emergency(oss.str()); + + // avoid recursion back into logging code if that is where + // we got the SEGV. + if (g_ceph_context && + g_ceph_context->_log && + !g_ceph_context->_log->is_inside_log_lock()) { + // dump to log. this uses the heap extensively, but we're better + // off trying than not. + derr << buf << std::endl; + bt.print(*_dout); + *_dout << " NOTE: a copy of the executable, or `objdump -rdS ` " + << "is needed to interpret this.\n" + << dendl; + + g_ceph_context->_log->dump_recent(); + } + + reraise_fatal(signum); +} + +void install_standard_sighandlers(void) +{ + install_sighandler(SIGSEGV, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGABRT, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGBUS, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGILL, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGFPE, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGXCPU, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGXFSZ, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); + install_sighandler(SIGSYS, handle_fatal_signal, SA_RESETHAND | SA_NODEFER); +} + + + +/// --- safe handler --- + +#include "common/Thread.h" +#include + +string get_name_by_pid(pid_t pid) +{ + char proc_pid_path[PATH_MAX] = {0}; + snprintf(proc_pid_path, PATH_MAX, PROCPREFIX "/proc/%d/cmdline", pid); + int fd = open(proc_pid_path, O_RDONLY); + + if (fd < 0) { + fd = -errno; + derr << "Fail to open '" << proc_pid_path + << "' error = " << cpp_strerror(fd) + << dendl; + return ""; + } + // assuming the cmdline length does not exceed PATH_MAX. if it + // really does, it's fine to return a truncated version. + char buf[PATH_MAX] = {0}; + int ret = read(fd, buf, sizeof(buf)); + close(fd); + if (ret < 0) { + ret = -errno; + derr << "Fail to read '" << proc_pid_path + << "' error = " << cpp_strerror(ret) + << dendl; + return ""; + } + std::replace(buf, buf + ret, '\0', ' '); + return string(buf, ret); +} + + +/** + * safe async signal handler / dispatcher + * + * This is an async unix signal handler based on the design from + * + * http://evbergen.home.xs4all.nl/unix-signals.html + * + * Features: + * - no unsafe work is done in the signal handler itself + * - callbacks are called from a regular thread + * - signals are not lost, unless multiple instances of the same signal + * are sent twice in quick succession. + */ +struct SignalHandler : public Thread { + /// to kick the thread, for shutdown, new handlers, etc. + int pipefd[2]; // write to [1], read from [0] + + /// to signal shutdown + bool stop; + + /// for an individual signal + struct safe_handler { + + safe_handler() { + memset(pipefd, 0, sizeof(pipefd)); + memset(&handler, 0, sizeof(handler)); + memset(&info_t, 0, sizeof(info_t)); + } + + siginfo_t info_t; + int pipefd[2]; // write to [1], read from [0] + signal_handler_t handler; + }; + + /// all handlers + safe_handler *handlers[32] = {nullptr}; + + /// to protect the handlers array + Mutex lock; + + SignalHandler() + : stop(false), lock("SignalHandler::lock") + { + // create signal pipe + int r = pipe(pipefd); + assert(r == 0); + r = fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + assert(r == 0); + + // create thread + create("signal_handler"); + } + + ~SignalHandler() override { + shutdown(); + } + + void signal_thread() { + int r = write(pipefd[1], "\0", 1); + assert(r == 1); + } + + void shutdown() { + stop = true; + signal_thread(); + join(); + } + + // thread entry point + void *entry() override { + while (!stop) { + // build fd list + struct pollfd fds[33]; + + lock.Lock(); + int num_fds = 0; + fds[num_fds].fd = pipefd[0]; + fds[num_fds].events = POLLIN | POLLERR; + fds[num_fds].revents = 0; + ++num_fds; + for (unsigned i=0; i<32; i++) { + if (handlers[i]) { + fds[num_fds].fd = handlers[i]->pipefd[0]; + fds[num_fds].events = POLLIN | POLLERR; + fds[num_fds].revents = 0; + ++num_fds; + } + } + lock.Unlock(); + + // wait for data on any of those pipes + int r = poll(fds, num_fds, -1); + if (stop) + break; + if (r > 0) { + char v; + + // consume byte from signal socket, if any. + TEMP_FAILURE_RETRY(read(pipefd[0], &v, 1)); + + lock.Lock(); + for (unsigned signum=0; signum<32; signum++) { + if (handlers[signum]) { + r = read(handlers[signum]->pipefd[0], &v, 1); + if (r == 1) { + siginfo_t * siginfo = &handlers[signum]->info_t; + string task_name = get_name_by_pid(siginfo->si_pid); + derr << "received signal: " << sig_str(signum) + << " from " << " PID: " << siginfo->si_pid + << " task name: " << task_name + << " UID: " << siginfo->si_uid + << dendl; + handlers[signum]->handler(signum); + } + } + } + lock.Unlock(); + } + } + return NULL; + } + + void queue_signal(int signum) { + // If this signal handler is registered, the callback must be + // defined. We can do this without the lock because we will never + // have the signal handler defined without the handlers entry also + // being filled in. + assert(handlers[signum]); + int r = write(handlers[signum]->pipefd[1], " ", 1); + assert(r == 1); + } + + void queue_signal_info(int signum, siginfo_t *siginfo, void * content) { + // If this signal handler is registered, the callback must be + // defined. We can do this without the lock because we will never + // have the signal handler defined without the handlers entry also + // being filled in. + assert(handlers[signum]); + memcpy(&handlers[signum]->info_t, siginfo, sizeof(siginfo_t)); + int r = write(handlers[signum]->pipefd[1], " ", 1); + assert(r == 1); + } + + void register_handler(int signum, signal_handler_t handler, bool oneshot); + void unregister_handler(int signum, signal_handler_t handler); +}; + +static SignalHandler *g_signal_handler = NULL; + +static void handler_signal_hook(int signum, siginfo_t * siginfo, void * content) { + g_signal_handler->queue_signal_info(signum, siginfo, content); +} + +void SignalHandler::register_handler(int signum, signal_handler_t handler, bool oneshot) +{ + int r; + + assert(signum >= 0 && signum < 32); + + safe_handler *h = new safe_handler; + + r = pipe(h->pipefd); + assert(r == 0); + r = fcntl(h->pipefd[0], F_SETFL, O_NONBLOCK); + assert(r == 0); + + h->handler = handler; + lock.Lock(); + handlers[signum] = h; + lock.Unlock(); + + // signal thread so that it sees our new handler + signal_thread(); + + // install our handler + struct sigaction oldact; + struct sigaction act; + memset(&act, 0, sizeof(act)); + + act.sa_handler = (signal_handler_t)handler_signal_hook; + sigfillset(&act.sa_mask); // mask all signals in the handler + act.sa_flags = SA_SIGINFO | (oneshot ? SA_RESETHAND : 0); + int ret = sigaction(signum, &act, &oldact); + assert(ret == 0); +} + +void SignalHandler::unregister_handler(int signum, signal_handler_t handler) +{ + assert(signum >= 0 && signum < 32); + safe_handler *h = handlers[signum]; + assert(h); + assert(h->handler == handler); + + // restore to default + signal(signum, SIG_DFL); + + // _then_ remove our handlers entry + lock.Lock(); + handlers[signum] = NULL; + lock.Unlock(); + + // this will wake up select() so that worker thread sees our handler is gone + close(h->pipefd[0]); + close(h->pipefd[1]); + delete h; +} + + +// ------- + +void init_async_signal_handler() +{ + assert(!g_signal_handler); + g_signal_handler = new SignalHandler; +} + +void shutdown_async_signal_handler() +{ + assert(g_signal_handler); + delete g_signal_handler; + g_signal_handler = NULL; +} + +void queue_async_signal(int signum) +{ + assert(g_signal_handler); + g_signal_handler->queue_signal(signum); +} + +void register_async_signal_handler(int signum, signal_handler_t handler) +{ + assert(g_signal_handler); + g_signal_handler->register_handler(signum, handler, false); +} + +void register_async_signal_handler_oneshot(int signum, signal_handler_t handler) +{ + assert(g_signal_handler); + g_signal_handler->register_handler(signum, handler, true); +} + +void unregister_async_signal_handler(int signum, signal_handler_t handler) +{ + assert(g_signal_handler); + g_signal_handler->unregister_handler(signum, handler); +} + + +