]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/signal.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / lib / signal.c
index 51fe13a83d4d99a12fcf45ffec358bc13848a7b6..0ca3af99033fec351e2bbf1320c189d321ca3c8e 100644 (file)
@@ -1,37 +1,26 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
-   License as published by the Free Software Foundation and included
-   in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA.
-
-   Bacula® is a registered trademark of Kern Sibbald.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is 
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  *  Signal handlers for Bacula daemons
  *
  *   Kern Sibbald, April 2000
  *
- *   Version $Id$
- *
  * Note, we probably should do a core dump for the serious
  * signals such as SIGBUS, SIGPFE, ...
  * Also, for SIGHUP and SIGUSR1, we should re-read the
 #endif
 
 extern char my_name[];
+extern char fail_time[];
 extern char *exepath;
 extern char *exename;
+extern bool prt_kaboom;
 
 static const char *sig_names[BA_NSIG+1];
 
@@ -71,41 +62,104 @@ const char *get_signal_name(int sig)
    }
 }
 
+/* defined in jcr.c */
+extern void dbg_print_jcr(FILE *fp);
+/* defined in plugins.c */
+extern void dbg_print_plugin(FILE *fp);
+/* defined in lockmgr.c */
+extern void dbg_print_lock(FILE *fp);
+
+#define MAX_DBG_HOOK 10
+static dbg_hook_t *dbg_hooks[MAX_DBG_HOOK];
+static int dbg_handler_count=0;
+
+void dbg_add_hook(dbg_hook_t *hook)
+{
+   ASSERT(dbg_handler_count < MAX_DBG_HOOK);
+   dbg_hooks[dbg_handler_count++] = hook;
+}
+
+/*
+ * !!! WARNING !!!
+ *
+ * This function should be used ONLY after a violent signal. We walk through the
+ * JCR chain without locking, Bacula should not be running.
+ */
+static void dbg_print_bacula()
+{
+   char buf[512];
+
+   snprintf(buf, sizeof(buf), "%s/bacula.%d.traceback", working_directory, main_pid);
+   FILE *fp = fopen(buf, "a+") ;
+   if (!fp) {
+      fp = stderr;
+   }
+
+   fprintf(stderr, "LockDump: %s\n", buf);
+
+   /* Print also BDB and RWLOCK structure
+    * Can add more info about JCR with dbg_jcr_add_hook()
+    */
+   dbg_print_lock(fp);
+   dbg_print_jcr(fp);
+   dbg_print_plugin(fp);
+
+   for(int i=0; i < dbg_handler_count ; i++) {
+      dbg_hooks[i](fp);
+   }
+
+   if (fp != stderr) {
+      fclose(fp);
+   }
+}
+
 /*
  * Handle signals here
  */
 extern "C" void signal_handler(int sig)
 {
    static int already_dead = 0;
+   int chld_status=-1;
+   utime_t now;
 
-   /* If we come back more than once, get out fast! */
-   if (already_dead) {
-      exit(1);
-   }
    Dmsg2(900, "sig=%d %s\n", sig, sig_names[sig]);
    /* Ignore certain signals -- SIGUSR2 used to interrupt threads */
    if (sig == SIGCHLD || sig == SIGUSR2) {
       return;
    }
+   /* FreeBSD seems to generate a signal of 0, which is of course undefined */
+   if (sig == 0) {
+      return;
+   }
+   /* If we come back more than once, get out fast! */
+   if (already_dead) {
+      exit(1);
+   }
    already_dead++;
-   if (sig == SIGTERM) {
-//    Emsg1(M_TERM, -1, "Shutting down Bacula service: %s ...\n", my_name);
+   /* Don't use Emsg here as it may lock and thus block us */
+   if (sig == SIGTERM || sig == SIGINT) {
+       syslog(LOG_DAEMON|LOG_ERR, "Shutting down Bacula service: %s ...\n", my_name);
    } else {
-      Emsg2(M_FATAL, -1, _("Bacula interrupted by signal %d: %s\n"), sig, get_signal_name(sig));
+      fprintf(stderr, _("Bacula interrupted by signal %d: %s\n"), sig, get_signal_name(sig));
+      syslog(LOG_DAEMON|LOG_ERR,
+         _("Bacula interrupted by signal %d: %s\n"), sig, get_signal_name(sig));
+      /* Edit current time for showing in the dump */
+      now = time(NULL);
+      bstrftimes(fail_time, 30, now);
    }
 
 #ifdef TRACEBACK
-   if (sig != SIGTERM) {
+   if (sig != SIGTERM && sig != SIGINT) {
       struct sigaction sigdefault;
-      static char *argv[4];
+      static char *argv[5];
       static char pid_buf[20];
       static char btpath[400];
-      char buf[100];
+      char buf[400];
       pid_t pid;
       int exelen = strlen(exepath);
 
-      fprintf(stderr, _("Kaboom! %s, %s got signal %d - %s. Attempting traceback.\n"),
-              exename, my_name, sig, get_signal_name(sig));
+      fprintf(stderr, _("Kaboom! %s, %s got signal %d - %s at %s. Attempting traceback.\n"),
+              exename, my_name, sig, get_signal_name(sig), fail_time);
       fprintf(stderr, _("Kaboom! exepath=%s\n"), exepath);
 
       if (exelen + 12 > (int)sizeof(btpath)) {
@@ -134,7 +188,16 @@ extern "C" void signal_handler(int sig)
          strcpy((char *)working_directory, "/tmp/");
       }
       unlink("./core");               /* get rid of any old core file */
+
       sprintf(pid_buf, "%d", (int)main_pid);
+      snprintf(buf, sizeof(buf), "%s/bacula.%s.traceback", working_directory, pid_buf);
+      unlink(buf);              /* Remove the previous backtrace file if exist */
+
+#ifdef DEVELOPER /* When DEVELOPER not set, this is done below */
+      /* print information about the current state into working/<file>.traceback */
+      dbg_print_bacula();
+#endif
+
       Dmsg1(300, "Working=%s\n", working_directory);
       Dmsg1(300, "btpath=%s\n", btpath);
       Dmsg1(300, "exepath=%s\n", exepath);
@@ -146,8 +209,10 @@ extern "C" void signal_handler(int sig)
          argv[0] = btpath;            /* path to btraceback */
          argv[1] = exepath;           /* path to exe */
          argv[2] = pid_buf;
-         argv[3] = (char *)NULL;
-         fprintf(stderr, _("Calling: %s %s %s\n"), btpath, exepath, pid_buf);
+         argv[3] = (char *)working_directory;
+         argv[4] = (char *)NULL;
+         fprintf(stderr, _("Calling: %s %s %s %s\n"), btpath, exepath, pid_buf,
+            working_directory);
          if (execv(btpath, argv) != 0) {
             berrno be;
             printf(_("execv: %s failed: ERR=%s\n"), btpath, be.bstrerror());
@@ -156,6 +221,7 @@ extern "C" void signal_handler(int sig)
       default:                        /* parent */
          break;
       }
+
       /* Parent continue here, waiting for child */
       sigdefault.sa_flags = 0;
       sigdefault.sa_handler = SIG_DFL;
@@ -164,20 +230,52 @@ extern "C" void signal_handler(int sig)
       sigaction(sig,  &sigdefault, NULL);
       if (pid > 0) {
          Dmsg0(500, "Doing waitpid\n");
-         waitpid(pid, NULL, 0);       /* wait for child to produce dump */
-         fprintf(stderr, _("Traceback complete, attempting cleanup ...\n"));
+         waitpid(pid, &chld_status, 0);   /* wait for child to produce dump */
          Dmsg0(500, "Done waitpid\n");
-         exit_handler(sig);           /* clean up if possible */
-         Dmsg0(500, "Done exit_handler\n");
       } else {
          Dmsg0(500, "Doing sleep\n");
          bmicrosleep(30, 0);
       }
-      fprintf(stderr, _("It looks like the traceback worked ...\n"));
-   }
+      if (WEXITSTATUS(chld_status) == 0) {
+         fprintf(stderr, _("It looks like the traceback worked...\n"));
+      } else {
+         fprintf(stderr, _("The btraceback call returned %d\n"),
+                           WEXITSTATUS(chld_status));
+      }
+
+#ifndef DEVELOPER /* When DEVELOPER set, this is done above */
+      /* print information about the current state into working/<file>.traceback */
+      dbg_print_bacula();
 #endif
 
+      /* If we want it printed, do so */
+#ifdef direct_print
+      if (prt_kaboom) {
+         FILE *fd;
+         snprintf(buf, sizeof(buf), "%s/bacula.%s.traceback", working_directory, pid_buf);
+         fd = fopen(buf, "r");
+         if (fd != NULL) {
+            printf("\n\n ==== Traceback output ====\n\n");
+            while (fgets(buf, (int)sizeof(buf), fd) != NULL) {
+               printf("%s", buf);
+            }
+            fclose(fd);
+            printf(" ==== End traceback output ====\n\n");
+         }
+      }
+#else
+      if (prt_kaboom) {
+         snprintf(buf, sizeof(buf), "/bin/cat %s/bacula.%s.traceback", working_directory, pid_buf);
+         fprintf(stderr, "\n\n ==== Traceback output ====\n\n");
+         system(buf);
+         fprintf(stderr, " ==== End traceback output ====\n\n");
+      }
+#endif
+
+   }
+#endif
    exit_handler(sig);
+   Dmsg0(500, "Done exit_handler\n");
 }
 
 /*
@@ -289,7 +387,7 @@ void init_signals(void terminate(int sig))
    sigaction(SIGWINCH,  &sigignore, NULL);
    sigaction(SIGIO,     &sighandle, NULL);
 
-   sigaction(SIGINT,    &sigdefault, NULL);
+   sigaction(SIGINT,    &sighandle, NULL);
    sigaction(SIGXCPU,   &sigdefault, NULL);
    sigaction(SIGXFSZ,   &sigdefault, NULL);
 
@@ -302,7 +400,7 @@ void init_signals(void terminate(int sig))
    sigaction(SIGEMT,    &sighandle, NULL);
 #endif
 #ifdef SIGIOT
-   sigaction(SIGIOT,    &sighandle, NULL);                     
+   sigaction(SIGIOT,    &sighandle, NULL);
 #endif
    sigaction(SIGBUS,    &sighandle, NULL);
    sigaction(SIGFPE,    &sighandle, NULL);