// -*- 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); }