1 /*
2    Unix SMB/CIFS implementation.
3    Critical Fault handling
4    Copyright (C) Andrew Tridgell 1992-1998
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "includes.h"
22 #include "version.h"
23 #include "system/wait.h"
24 #include "system/filesys.h"
25 
26 /**
27  * @file
28  * @brief Fault handling
29  */
30 
31 /* the registered fault handler */
32 static struct {
33 	const char *name;
34 	void (*fault_handler)(int sig);
35 } fault_handlers;
36 
37 static const char *progname;
38 
39 #ifdef HAVE_BACKTRACE
40 #include <execinfo.h>
41 #elif HAVE_LIBEXC_H
42 #include <libexc.h>
43 #endif
44 
45 /**
46  * Write backtrace to debug log
47  */
call_backtrace(void)48 _PUBLIC_ void call_backtrace(void)
49 {
50 #ifdef HAVE_BACKTRACE
51 #ifndef BACKTRACE_STACK_SIZE
52 #define BACKTRACE_STACK_SIZE 64
53 #endif
54 	void *backtrace_stack[BACKTRACE_STACK_SIZE];
55 	size_t backtrace_size;
56 	char **backtrace_strings;
57 
58 	/* get the backtrace (stack frames) */
59 	backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
60 	backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
61 
62 	DEBUG(0, ("BACKTRACE: %lu stack frames:\n",
63 		  (unsigned long)backtrace_size));
64 
65 	if (backtrace_strings) {
66 		int i;
67 
68 		for (i = 0; i < backtrace_size; i++)
69 			DEBUGADD(0, (" #%u %s\n", i, backtrace_strings[i]));
70 
71 		/* Leak the backtrace_strings, rather than risk what free() might do */
72 	}
73 
74 #elif HAVE_LIBEXC
75 
76 #define NAMESIZE 32 /* Arbitrary */
77 #ifndef BACKTRACE_STACK_SIZE
78 #define BACKTRACE_STACK_SIZE 64
79 #endif
80 
81 	/* The IRIX libexc library provides an API for unwinding the stack. See
82 	 * libexc(3) for details. Apparantly trace_back_stack leaks memory, but
83 	 * since we are about to abort anyway, it hardly matters.
84 	 *
85 	 * Note that if we paniced due to a SIGSEGV or SIGBUS (or similar) this
86 	 * will fail with a nasty message upon failing to open the /proc entry.
87 	 */
88 	{
89 		uint64_t	addrs[BACKTRACE_STACK_SIZE];
90 		char *      	names[BACKTRACE_STACK_SIZE];
91 		char		namebuf[BACKTRACE_STACK_SIZE * NAMESIZE];
92 
93 		int		i;
94 		int		levels;
95 
96 		ZERO_ARRAY(addrs);
97 		ZERO_ARRAY(names);
98 		ZERO_ARRAY(namebuf);
99 
100 		for (i = 0; i < BACKTRACE_STACK_SIZE; i++) {
101 			names[i] = namebuf + (i * NAMESIZE);
102 		}
103 
104 		levels = trace_back_stack(0, addrs, names,
105 				BACKTRACE_STACK_SIZE, NAMESIZE);
106 
107 		DEBUG(0, ("BACKTRACE: %d stack frames:\n", levels));
108 		for (i = 0; i < levels; i++) {
109 			DEBUGADD(0, (" #%d 0x%llx %s\n", i, addrs[i], names[i]));
110 		}
111      }
112 #undef NAMESIZE
113 #endif
114 }
115 
116 _PUBLIC_ const char *panic_action = NULL;
117 
118 /**
119  Something really nasty happened - panic !
120 **/
smb_panic(const char * why)121 _PUBLIC_ void smb_panic(const char *why)
122 {
123 	int result;
124 
125 	if (panic_action && *panic_action) {
126 		char pidstr[20];
127 		char cmdstring[200];
128 		safe_strcpy(cmdstring, panic_action, sizeof(cmdstring));
129 		snprintf(pidstr, sizeof(pidstr), "%u", getpid());
130 		all_string_sub(cmdstring, "%PID%", pidstr, sizeof(cmdstring));
131 		if (progname) {
132 			all_string_sub(cmdstring, "%PROG%", progname, sizeof(cmdstring));
133 		}
134 		DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmdstring));
135 		result = system(cmdstring);
136 
137 		if (result == -1)
138 			DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
139 				  strerror(errno)));
140 		else
141 			DEBUG(0, ("smb_panic(): action returned status %d\n",
142 				  WEXITSTATUS(result)));
143 	}
144 	DEBUG(0,("PANIC: %s\n", why));
145 
146 	call_backtrace();
147 
148 #ifdef SIGABRT
149 	CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
150 #endif
151 	abort();
152 }
153 
154 /**
155 report a fault
156 **/
fault_report(int sig)157 static void fault_report(int sig)
158 {
159 	static int counter;
160 
161 	if (counter) _exit(1);
162 
163 	DEBUG(0,("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"));
164 	DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),SAMBA_VERSION_STRING));
165 	DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n"));
166 	DEBUG(0,("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"));
167 
168 	smb_panic("internal error");
169 
170 	exit(1);
171 }
172 
173 /**
174 catch serious errors
175 **/
sig_fault(int sig)176 static void sig_fault(int sig)
177 {
178 	if (fault_handlers.fault_handler) {
179 		/* we have a fault handler, call it. It may not return. */
180 		fault_handlers.fault_handler(sig);
181 	}
182 	/* If it returns or doean't exist, use regular reporter */
183 	fault_report(sig);
184 }
185 
186 /**
187 setup our fault handlers
188 **/
fault_setup(const char * pname)189 _PUBLIC_ void fault_setup(const char *pname)
190 {
191 	if (progname == NULL) {
192 		progname = pname;
193 	}
194 #ifdef SIGSEGV
195 	CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault);
196 #endif
197 #ifdef SIGBUS
198 	CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
199 #endif
200 #ifdef SIGABRT
201 	CatchSignal(SIGABRT,SIGNAL_CAST sig_fault);
202 #endif
203 #ifdef SIGFPE
204 	CatchSignal(SIGFPE,SIGNAL_CAST sig_fault);
205 #endif
206 }
207 
208 /**
209   register a fault handler.
210   Should only be called once in the execution of smbd.
211 */
register_fault_handler(const char * name,void (* fault_handler)(int sig))212 _PUBLIC_ BOOL register_fault_handler(const char *name, void (*fault_handler)(int sig))
213 {
214 	if (fault_handlers.name != NULL) {
215 		/* it's already registered! */
216 		DEBUG(2,("fault handler '%s' already registered - failed '%s'\n",
217 			 fault_handlers.name, name));
218 		return False;
219 	}
220 
221 	fault_handlers.name = name;
222 	fault_handlers.fault_handler = fault_handler;
223 
224 	DEBUG(2,("fault handler '%s' registered\n", name));
225 	return True;
226 }
227