/*
- 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];
}
}
+/* 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)) {
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);
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());
default: /* parent */
break;
}
+
/* Parent continue here, waiting for child */
sigdefault.sa_flags = 0;
sigdefault.sa_handler = SIG_DFL;
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");
}
/*
sigaction(SIGWINCH, &sigignore, NULL);
sigaction(SIGIO, &sighandle, NULL);
- sigaction(SIGINT, &sigdefault, NULL);
+ sigaction(SIGINT, &sighandle, NULL);
sigaction(SIGXCPU, &sigdefault, NULL);
sigaction(SIGXFSZ, &sigdefault, NULL);
sigaction(SIGEMT, &sighandle, NULL);
#endif
#ifdef SIGIOT
- sigaction(SIGIOT, &sighandle, NULL);
+ sigaction(SIGIOT, &sighandle, NULL);
#endif
sigaction(SIGBUS, &sighandle, NULL);
sigaction(SIGFPE, &sighandle, NULL);