No more manifest files
[yaz-moved-to-github.git] / src / backtrace.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file
8  * \brief get information for abnormal terminated, crashes, etc
9  */
10
11
12 #if HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include <signal.h>
17 #include <string.h>
18 #include <errno.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #include <stdlib.h>
23 #if HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26 #include <yaz/log.h>
27 #include <yaz/snprintf.h>
28 #include <yaz/backtrace.h>
29 #include <yaz/nmem.h>
30
31 #if HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #if HAVE_EXECINFO_H
36 #include <execinfo.h>
37 #endif
38
39 #define BACKTRACE_SZ 100
40
41 static char static_progname[256];
42 #if HAVE_EXECINFO_H
43 static int yaz_panic_fd = -1;
44
45 static void yaz_invoke_gdb(void)
46 {
47     int fd = yaz_panic_fd;
48     pid_t pid;
49     int fds[2];
50     if (pipe(fds) == -1)
51     {
52         const char *cp = "backtrace: pipe failed\n";
53         write(fd, cp, strlen(cp));
54         return;
55     }
56     pid = fork();
57     if (pid == (pid_t) (-1))
58     {   /* error */
59         const char *cp = "backtrace: fork failure\n";
60         write(fd, cp, strlen(cp));
61     }
62     else if (pid == 0)
63     {   /* child */
64         char *arg[20];
65         int arg_no = 0;
66         char pidstr[40];
67         const char *cp = "backtrace: could not exec gdb\n";
68
69         close(fds[1]);
70         close(0);
71         dup(fds[0]);
72         if (fd != 1)
73         {
74             close(1);
75             dup(fd);
76         }
77         if (fd != 2)
78         {
79             close(2);
80             dup(fd);
81         }
82         arg[arg_no++] = "/usr/bin/gdb";
83         arg[arg_no++] = "-n";
84         arg[arg_no++] = "-batch";
85         arg[arg_no++] = "-ex";
86         arg[arg_no++] = "info threads";
87         arg[arg_no++] = "-ex";
88         arg[arg_no++] = "thread apply all bt";
89         arg[arg_no++] = static_progname;
90         sprintf(pidstr, NMEM_INT_PRINTF, (nmem_int_t) getppid());
91         arg[arg_no++] = pidstr;
92         arg[arg_no] = 0;
93         execv(arg[0], arg);
94         write(2, cp, strlen(cp)); /* exec failure if we make it this far */
95         _exit(1);
96     }
97     else
98     {  /* parent */
99         int sec = 0;
100
101         close(fds[0]);
102         write(fds[1], "quit\n", 5);
103         while (1)
104         {
105             int status;
106             pid_t s = waitpid(pid, &status, WNOHANG);
107             if (s != 0)
108                 break;
109             if (sec == 9)
110                 kill(pid, SIGTERM);
111             if (sec == 10)
112                 kill(pid, SIGKILL);
113             if (sec == 11)
114                 break;
115             if (sec > 3)
116                 write(fds[1], "quit\n", 5);
117             sleep(1);
118             sec++;
119         }
120         close(fds[1]);
121     }
122 }
123
124 static void yaz_panic_alarm(int sig)
125 {
126     const char *cp = "backtrace: backtrace hangs\n";
127
128     write(yaz_panic_fd, cp, strlen(cp));
129     yaz_invoke_gdb();
130     abort();
131 }
132
133 static void yaz_invoke_backtrace(void)
134 {
135     int fd = yaz_panic_fd;
136     void *backtrace_info[BACKTRACE_SZ];
137     int sz = BACKTRACE_SZ;
138
139     signal(SIGALRM, yaz_panic_alarm);
140     alarm(1);
141     sz = backtrace(backtrace_info, sz);
142     backtrace_symbols_fd(backtrace_info, sz, fd);
143 }
144
145 static void yaz_panic_sig_handler(int sig)
146 {
147     char buf[512];
148     FILE *file;
149
150     signal(SIGABRT, SIG_DFL);
151     signal(SIGSEGV, SIG_DFL);
152     signal(SIGFPE, SIG_DFL);
153     signal(SIGBUS, SIG_DFL);
154     strcpy(buf, "\nYAZ panic received ");
155     switch (sig)
156     {
157     case SIGSEGV:
158         strcat(buf, "SIGSEGV");
159         break;
160     case SIGABRT:
161         strcat(buf, "SIGABRT");
162         break;
163     case SIGFPE:
164         strcat(buf, "SIGFPE");
165         break;
166     case SIGBUS:
167         strcat(buf, "SIGBUS");
168         break;
169     default:
170         yaz_snprintf(buf + strlen(buf), sizeof buf, "signo=%d", sig);
171         break;
172     }
173     yaz_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
174                  " PID=" NMEM_INT_PRINTF "\n", (nmem_int_t) getpid());
175
176     file = yaz_log_file();
177     /* static variable to be used in the following + handlers */
178     yaz_panic_fd = fileno(file);
179
180     write(yaz_panic_fd, buf, strlen(buf));
181     yaz_invoke_backtrace();
182     yaz_invoke_gdb();
183     abort();
184 }
185 #endif
186
187 void yaz_enable_panic_backtrace(const char *progname)
188 {
189     strncpy(static_progname, progname, sizeof(static_progname) - 1);
190     static_progname[sizeof(static_progname) - 1] = '\0';
191 #if HAVE_EXECINFO_H
192     signal(SIGABRT, yaz_panic_sig_handler);
193     signal(SIGSEGV, yaz_panic_sig_handler);
194     signal(SIGFPE, yaz_panic_sig_handler);
195     signal(SIGBUS, yaz_panic_sig_handler);
196 #endif
197 }
198
199 /*
200  * Local variables:
201  * c-basic-offset: 4
202  * c-file-style: "Stroustrup"
203  * indent-tabs-mode: nil
204  * End:
205  * vim: shiftwidth=4 tabstop=8 expandtab
206  */
207