initial code repo
[stor4nfv.git] / src / ceph / src / global / signal_handler.cc
diff --git a/src/ceph/src/global/signal_handler.cc b/src/ceph/src/global/signal_handler.cc
new file mode 100644 (file)
index 0000000..d4099e1
--- /dev/null
@@ -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 <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);
+}
+
+
+