--- /dev/null
+// -*- 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 <poll.h>
+#include <signal.h>
+#include <sstream>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#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 <executable>` "
+ << "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 <errno.h>
+
+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 "<unknown>";
+ }
+ // 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 "<unknown>";
+ }
+ 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);
+}
+
+
+