2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Various functions to aid in debugging
21 * @copyright 2013 The FreeRADIUS server project
22 * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24 #include <freeradius-devel/libradius.h>
27 * runtime backtrace functions are not POSIX but are included in
28 * glibc, OSX >= 10.5 and various BSDs
30 #ifdef HAVE_EXECINFO_H
31 # include <execinfo.h>
32 # define MAX_BT_FRAMES 128
35 static char panic_action[512];
37 /** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
41 static void NEVER_RETURNS _fr_fault(int sig)
43 char cmd[sizeof(panic_action) + 20];
45 size_t left = sizeof(cmd), ret;
47 char const *p = panic_action;
52 fprintf(stderr, "FATAL SIGNAL: %s\n", strsignal(sig));
55 * Produce a simple backtrace - They've very basic but at least give us an
56 * idea of the area of the code we hit the issue in.
58 #ifdef HAVE_EXECINFO_H
59 size_t frame_count, i;
60 void *stack[MAX_BT_FRAMES];
63 frame_count = backtrace(stack, MAX_BT_FRAMES);
64 frames = backtrace_symbols(stack, frame_count);
66 fprintf(stderr, "Backtrace of last %zu frames:\n", frame_count);
67 for (i = 0; i < frame_count; i++) {
68 fprintf(stderr, "%s\n", frames[i]);
69 /* Leak the backtrace strings, freeing may lead to undefined behaviour... */
73 /* No panic action set... */
74 if (panic_action[0] == '\0') {
75 fprintf(stderr, "No panic action set\n");
79 /* Substitute %p for the current PID (useful for attaching a debugger) */
80 while ((q = strstr(p, "%p"))) {
81 out += ret = snprintf(out, left, "%.*s%d", (int) (q - p), p, (int) getpid());
84 fprintf(stderr, "Panic action too long\n");
90 if (strlen(p) >= left) goto oob;
91 strlcpy(out, p, left);
93 fprintf(stderr, "Calling: %s\n", cmd);
95 fprintf(stderr, "Panic action exited with %i\n", code);
100 /** Registers signal handlers to execute panic_action on fatal signal
102 * May be called multiple time to change the panic_action/program.
104 * @param cmd to execute on fault. If present %p will be substituted
105 * for the parent PID before the command is executed, and %e
106 * will be substituted for the currently running program.
107 * @param program Name of program currently executing (argv[0]).
108 * @return 0 on success -1 on failure.
110 int fr_fault_setup(char const *cmd, char const *program)
112 static int setup = FALSE;
114 char *out = panic_action;
115 size_t left = sizeof(panic_action), ret;
121 /* Substitute %e for the current program */
122 while ((q = strstr(p, "%e"))) {
123 out += ret = snprintf(out, left, "%.*s%s", (int) (q - p), p, program ? program : "");
126 fr_strerror_printf("Panic action too long");
132 if (strlen(p) >= left) goto oob;
133 strlcpy(out, p, left);
135 *panic_action = '\0';
138 /* Unsure what the side effects of changing the signal handler mid execution might be */
141 if (fr_set_signal(SIGSEGV, _fr_fault) < 0) return -1;
144 if (fr_set_signal(SIGBUS, _fr_fault) < 0) return -1;
147 if (fr_set_signal(SIGABRT, _fr_fault) < 0) return -1;
150 if (fr_set_signal(SIGFPE, _fr_fault) < 0) return -1;