bf61bb26c43ca8fa7e0bf3b4ba12b6c41872d8a6
[samba.git] / source3 / lib / fault.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Critical Fault handling
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Tim Prouty 2009
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 #ifdef HAVE_SYS_SYSCTL_H
24 #include <sys/sysctl.h>
25 #endif
26
27
28 #ifdef HAVE_SYS_PRCTL_H
29 #include <sys/prctl.h>
30 #endif
31
32 static void (*cont_fn)(void *);
33 static char *corepath;
34
35 /*******************************************************************
36 report a fault
37 ********************************************************************/
38 static void fault_report(int sig)
39 {
40         static int counter;
41
42         if (counter) _exit(1);
43
44         counter++;
45
46         DEBUGSEP(0);
47         DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)sys_getpid(),samba_version_string()));
48         DEBUG(0,("\nPlease read the Trouble-Shooting section of the Samba3-HOWTO\n"));
49         DEBUG(0,("\nFrom: http://www.samba.org/samba/docs/Samba3-HOWTO.pdf\n"));
50         DEBUGSEP(0);
51   
52         smb_panic("internal error");
53
54         if (cont_fn) {
55                 cont_fn(NULL);
56 #ifdef SIGSEGV
57                 CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL);
58 #endif
59 #ifdef SIGBUS
60                 CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL);
61 #endif
62 #ifdef SIGABRT
63                 CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
64 #endif
65                 return; /* this should cause a core dump */
66         }
67         exit(1);
68 }
69
70 /****************************************************************************
71 catch serious errors
72 ****************************************************************************/
73 static void sig_fault(int sig)
74 {
75         fault_report(sig);
76 }
77
78 /*******************************************************************
79 setup our fault handlers
80 ********************************************************************/
81 void fault_setup(void (*fn)(void *))
82 {
83         cont_fn = fn;
84
85 #ifdef SIGSEGV
86         CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault);
87 #endif
88 #ifdef SIGBUS
89         CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
90 #endif
91 #ifdef SIGABRT
92         CatchSignal(SIGABRT,SIGNAL_CAST sig_fault);
93 #endif
94 }
95
96 /**
97  * Build up the default corepath as "<logbase>/cores/<progname>"
98  */
99 static char *get_default_corepath(const char *logbase, const char *progname)
100 {
101         char *tmp_corepath;
102
103         /* Setup core dir in logbase. */
104         tmp_corepath = talloc_asprintf(NULL, "%s/cores", logbase);
105         if (!tmp_corepath)
106                 return NULL;
107
108         if ((mkdir(tmp_corepath, 0700) == -1) && errno != EEXIST)
109                 goto err_out;
110
111         if (chmod(tmp_corepath, 0700) == -1)
112                 goto err_out;
113
114         talloc_free(tmp_corepath);
115
116         /* Setup progname-specific core subdir */
117         tmp_corepath = talloc_asprintf(NULL, "%s/cores/%s", logbase, progname);
118         if (!tmp_corepath)
119                 return NULL;
120
121         if (mkdir(tmp_corepath, 0700) == -1 && errno != EEXIST)
122                 goto err_out;
123
124         if (chown(tmp_corepath, getuid(), getgid()) == -1)
125                 goto err_out;
126
127         if (chmod(tmp_corepath, 0700) == -1)
128                 goto err_out;
129
130         return tmp_corepath;
131
132  err_out:
133         talloc_free(tmp_corepath);
134         return NULL;
135 }
136
137 /**
138  * Get the FreeBSD corepath.
139  *
140  * On FreeBSD the current working directory is ignored when creating a core
141  * file.  Instead the core directory is controlled via sysctl.  This consults
142  * the value of "kern.corefile" so the correct corepath can be printed out
143  * before dump_core() calls abort.
144  */
145 #if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME))
146 static char *get_freebsd_corepath(void)
147 {
148         char *tmp_corepath = NULL;
149         char *end = NULL;
150         size_t len = 128;
151         int ret;
152
153         /* Loop with increasing sizes so we don't allocate too much. */
154         do {
155                 if (len > 1024)  {
156                         goto err_out;
157                 }
158
159                 tmp_corepath = (char *)talloc_realloc(NULL, tmp_corepath,
160                                                       char, len);
161                 if (!tmp_corepath) {
162                         return NULL;
163                 }
164
165                 ret = sysctlbyname("kern.corefile", tmp_corepath, &len, NULL,
166                                    0);
167                 if (ret == -1) {
168                         if (errno != ENOMEM) {
169                                 DEBUG(0, ("sysctlbyname failed getting "
170                                           "kern.corefile %s\n",
171                                           strerror(errno)));
172                                 goto err_out;
173                         }
174
175                         /* Not a large enough array, try a bigger one. */
176                         len = len << 1;
177                 }
178         } while (ret == -1);
179
180         /* Strip off the common filename expansion */
181         if ((end = strrchr_m(tmp_corepath, '/'))) {
182                 *end = '\0';
183         }
184
185         return tmp_corepath;
186
187  err_out:
188         if (tmp_corepath) {
189                 talloc_free(tmp_corepath);
190         }
191         return NULL;
192 }
193 #endif
194
195 /**
196  * Try getting system-specific corepath if one exists.
197  *
198  * If the system doesn't define a corepath, then the default is used.
199  */
200 static char *get_corepath(const char *logbase, const char *progname)
201 {
202 #if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME))
203
204         /* @todo: Add support for the linux corepath. */
205
206         char *tmp_corepath = NULL;
207         tmp_corepath = get_freebsd_corepath();
208
209         /* If this has been set correctly, we're done. */
210         if (tmp_corepath) {
211                 return tmp_corepath;
212         }
213 #endif
214
215         /* Fall back to the default. */
216         return get_default_corepath(logbase, progname);
217 }
218
219 /*******************************************************************
220 make all the preparations to safely dump a core file
221 ********************************************************************/
222
223 void dump_core_setup(const char *progname)
224 {
225         char *logbase = NULL;
226         char *end = NULL;
227
228         if (lp_logfile() && *lp_logfile()) {
229                 if (asprintf(&logbase, "%s", lp_logfile()) < 0) {
230                         return;
231                 }
232                 if ((end = strrchr_m(logbase, '/'))) {
233                         *end = '\0';
234                 }
235         } else {
236                 /* We will end up here if the log file is given on the command
237                  * line by the -l option but the "log file" option is not set
238                  * in smb.conf.
239                  */
240                 if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) {
241                         return;
242                 }
243         }
244
245         SMB_ASSERT(progname != NULL);
246
247         corepath = get_corepath(logbase, progname);
248         if (!corepath) {
249                 DEBUG(0, ("Unable to setup corepath for %s: %s\n", progname,
250                           strerror(errno)));
251                 goto out;
252         }
253
254
255 #ifdef HAVE_GETRLIMIT
256 #ifdef RLIMIT_CORE
257         {
258                 struct rlimit rlp;
259                 getrlimit(RLIMIT_CORE, &rlp);
260                 rlp.rlim_cur = MAX(16*1024*1024,rlp.rlim_cur);
261                 setrlimit(RLIMIT_CORE, &rlp);
262                 getrlimit(RLIMIT_CORE, &rlp);
263                 DEBUG(3,("Maximum core file size limits now %d(soft) %d(hard)\n",
264                          (int)rlp.rlim_cur,(int)rlp.rlim_max));
265         }
266 #endif
267 #endif
268
269 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
270         /* On Linux we lose the ability to dump core when we change our user
271          * ID. We know how to dump core safely, so let's make sure we have our
272          * dumpable flag set.
273          */
274         prctl(PR_SET_DUMPABLE, 1);
275 #endif
276
277         /* FIXME: if we have a core-plus-pid facility, configurably set
278          * this up here.
279          */
280  out:
281         SAFE_FREE(logbase);
282 }
283
284  void dump_core(void)
285 {
286         static bool called;
287
288         if (called) {
289                 DEBUG(0, ("dump_core() called recursive\n"));
290                 exit(1);
291         }
292         called = true;
293
294         /* Note that even if core dumping has been disabled, we still set up
295          * the core path. This is to handle the case where core dumping is
296          * turned on in smb.conf and the relevant daemon is not restarted.
297          */
298         if (!lp_enable_core_files()) {
299                 DEBUG(0, ("Exiting on internal error (core file administratively disabled)\n"));
300                 exit(1);
301         }
302
303 #if DUMP_CORE
304         /* If we're running as non root we might not be able to dump the core
305          * file to the corepath.  There must not be an unbecome_root() before
306          * we call abort(). */
307         if (geteuid() != 0) {
308                 become_root();
309         }
310
311         if (corepath == NULL) {
312                 DEBUG(0, ("Can not dump core: corepath not set up\n"));
313                 exit(1);
314         }
315
316         if (*corepath != '\0') {
317                 /* The chdir might fail if we dump core before we finish
318                  * processing the config file.
319                  */
320                 if (chdir(corepath) != 0) {
321                         DEBUG(0, ("unable to change to %s\n", corepath));
322                         DEBUGADD(0, ("refusing to dump core\n"));
323                         exit(1);
324                 }
325
326                 DEBUG(0,("dumping core in %s\n", corepath));
327         }
328
329         umask(~(0700));
330         dbgflush();
331
332         /* Ensure we don't have a signal handler for abort. */
333 #ifdef SIGABRT
334         CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
335 #endif
336
337         abort();
338
339 #else /* DUMP_CORE */
340         exit(1);
341 #endif /* DUMP_CORE */
342 }
343