Really enable core dumps in Linux
[obnox/samba-ctdb.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 #if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN)
196
197 /**
198  * Get the Linux corepath.
199  *
200  * On Linux the contents of /proc/sys/kernel/core_pattern indicates the
201  * location of the core path.
202  */
203 static char *get_linux_corepath(void)
204 {
205         char *end;
206         int fd;
207         char *result;
208
209         fd = open("/proc/sys/kernel/core_pattern", O_RDONLY, 0);
210         if (fd == -1) {
211                 return NULL;
212         }
213
214         result = afdgets(fd, NULL, 0);
215         close(fd);
216
217         if (result == NULL) {
218                 return NULL;
219         }
220
221         if (result[0] != '/') {
222                 /*
223                  * No absolute path, use the default (cwd)
224                  */
225                 TALLOC_FREE(result);
226                 return NULL;
227         }
228         /* Strip off the common filename expansion */
229
230         end = strrchr_m(result, '/');
231
232         if ((end != result) /* this would be the only / */
233             && (end != NULL)) {
234                 *end = '\0';
235         }
236         return result;
237 }
238 #endif
239
240
241 /**
242  * Try getting system-specific corepath if one exists.
243  *
244  * If the system doesn't define a corepath, then the default is used.
245  */
246 static char *get_corepath(const char *logbase, const char *progname)
247 {
248 #if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME))
249         char *tmp_corepath = NULL;
250         tmp_corepath = get_freebsd_corepath();
251
252         /* If this has been set correctly, we're done. */
253         if (tmp_corepath) {
254                 return tmp_corepath;
255         }
256 #endif
257
258 #if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN)
259         char *tmp_corepath = NULL;
260         tmp_corepath = get_linux_corepath();
261
262         /* If this has been set correctly, we're done. */
263         if (tmp_corepath) {
264                 return tmp_corepath;
265         }
266 #endif
267
268         /* Fall back to the default. */
269         return get_default_corepath(logbase, progname);
270 }
271
272 /*******************************************************************
273 make all the preparations to safely dump a core file
274 ********************************************************************/
275
276 void dump_core_setup(const char *progname)
277 {
278         char *logbase = NULL;
279         char *end = NULL;
280
281         if (lp_logfile() && *lp_logfile()) {
282                 if (asprintf(&logbase, "%s", lp_logfile()) < 0) {
283                         return;
284                 }
285                 if ((end = strrchr_m(logbase, '/'))) {
286                         *end = '\0';
287                 }
288         } else {
289                 /* We will end up here if the log file is given on the command
290                  * line by the -l option but the "log file" option is not set
291                  * in smb.conf.
292                  */
293                 if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) {
294                         return;
295                 }
296         }
297
298         SMB_ASSERT(progname != NULL);
299
300         corepath = get_corepath(logbase, progname);
301         if (!corepath) {
302                 DEBUG(0, ("Unable to setup corepath for %s: %s\n", progname,
303                           strerror(errno)));
304                 goto out;
305         }
306
307
308 #ifdef HAVE_GETRLIMIT
309 #ifdef RLIMIT_CORE
310         {
311                 struct rlimit rlp;
312                 getrlimit(RLIMIT_CORE, &rlp);
313                 rlp.rlim_cur = MAX(16*1024*1024,rlp.rlim_cur);
314                 setrlimit(RLIMIT_CORE, &rlp);
315                 getrlimit(RLIMIT_CORE, &rlp);
316                 DEBUG(3,("Maximum core file size limits now %d(soft) %d(hard)\n",
317                          (int)rlp.rlim_cur,(int)rlp.rlim_max));
318         }
319 #endif
320 #endif
321
322         /* FIXME: if we have a core-plus-pid facility, configurably set
323          * this up here.
324          */
325  out:
326         SAFE_FREE(logbase);
327 }
328
329  void dump_core(void)
330 {
331         static bool called;
332
333         if (called) {
334                 DEBUG(0, ("dump_core() called recursive\n"));
335                 exit(1);
336         }
337         called = true;
338
339         /* Note that even if core dumping has been disabled, we still set up
340          * the core path. This is to handle the case where core dumping is
341          * turned on in smb.conf and the relevant daemon is not restarted.
342          */
343         if (!lp_enable_core_files()) {
344                 DEBUG(0, ("Exiting on internal error (core file administratively disabled)\n"));
345                 exit(1);
346         }
347
348 #if DUMP_CORE
349         /* If we're running as non root we might not be able to dump the core
350          * file to the corepath.  There must not be an unbecome_root() before
351          * we call abort(). */
352         if (geteuid() != 0) {
353                 become_root();
354         }
355
356         if (corepath == NULL) {
357                 DEBUG(0, ("Can not dump core: corepath not set up\n"));
358                 exit(1);
359         }
360
361         if (*corepath != '\0') {
362                 /* The chdir might fail if we dump core before we finish
363                  * processing the config file.
364                  */
365                 if (chdir(corepath) != 0) {
366                         DEBUG(0, ("unable to change to %s\n", corepath));
367                         DEBUGADD(0, ("refusing to dump core\n"));
368                         exit(1);
369                 }
370
371                 DEBUG(0,("dumping core in %s\n", corepath));
372         }
373
374         umask(~(0700));
375         dbgflush();
376
377 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
378         /* On Linux we lose the ability to dump core when we change our user
379          * ID. We know how to dump core safely, so let's make sure we have our
380          * dumpable flag set.
381          */
382         prctl(PR_SET_DUMPABLE, 1);
383 #endif
384
385         /* Ensure we don't have a signal handler for abort. */
386 #ifdef SIGABRT
387         CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
388 #endif
389
390         abort();
391
392 #else /* DUMP_CORE */
393         exit(1);
394 #endif /* DUMP_CORE */
395 }
396