2ffd5dbd6d17dc1d1033681d333f3516b8018aef
[samba.git] / lib / util / 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 char *corepath;
33
34 /*******************************************************************
35 report a fault
36 ********************************************************************/
37 static void fault_report(int sig)
38 {
39         static int counter;
40
41         if (counter) _exit(1);
42
43         counter++;
44
45         DEBUGSEP(0);
46         DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)sys_getpid(),samba_version_string()));
47         DEBUG(0,("\nPlease read the Trouble-Shooting section of the Samba3-HOWTO\n"));
48         DEBUG(0,("\nFrom: http://www.samba.org/samba/docs/Samba3-HOWTO.pdf\n"));
49         DEBUGSEP(0);
50
51         smb_panic("internal error");
52
53         /* smb_panic() never returns, so this is really redundent */
54         exit(1);
55 }
56
57 /****************************************************************************
58 catch serious errors
59 ****************************************************************************/
60 static void sig_fault(int sig)
61 {
62         fault_report(sig);
63 }
64
65 /*******************************************************************
66 setup our fault handlers
67 ********************************************************************/
68 void fault_setup(void)
69 {
70 #ifdef SIGSEGV
71         CatchSignal(SIGSEGV, sig_fault);
72 #endif
73 #ifdef SIGBUS
74         CatchSignal(SIGBUS, sig_fault);
75 #endif
76 #ifdef SIGABRT
77         CatchSignal(SIGABRT, sig_fault);
78 #endif
79 }
80
81 /**
82  * Build up the default corepath as "<logbase>/cores/<progname>"
83  */
84 static char *get_default_corepath(const char *logbase, const char *progname)
85 {
86         char *tmp_corepath;
87
88         /* Setup core dir in logbase. */
89         tmp_corepath = talloc_asprintf(NULL, "%s/cores", logbase);
90         if (!tmp_corepath)
91                 return NULL;
92
93         if ((mkdir(tmp_corepath, 0700) == -1) && errno != EEXIST)
94                 goto err_out;
95
96         if (chmod(tmp_corepath, 0700) == -1)
97                 goto err_out;
98
99         talloc_free(tmp_corepath);
100
101         /* Setup progname-specific core subdir */
102         tmp_corepath = talloc_asprintf(NULL, "%s/cores/%s", logbase, progname);
103         if (!tmp_corepath)
104                 return NULL;
105
106         if (mkdir(tmp_corepath, 0700) == -1 && errno != EEXIST)
107                 goto err_out;
108
109         if (chown(tmp_corepath, getuid(), getgid()) == -1)
110                 goto err_out;
111
112         if (chmod(tmp_corepath, 0700) == -1)
113                 goto err_out;
114
115         return tmp_corepath;
116
117  err_out:
118         talloc_free(tmp_corepath);
119         return NULL;
120 }
121
122 /**
123  * Get the FreeBSD corepath.
124  *
125  * On FreeBSD the current working directory is ignored when creating a core
126  * file.  Instead the core directory is controlled via sysctl.  This consults
127  * the value of "kern.corefile" so the correct corepath can be printed out
128  * before dump_core() calls abort.
129  */
130 #if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME))
131 static char *get_freebsd_corepath(void)
132 {
133         char *tmp_corepath = NULL;
134         char *end = NULL;
135         size_t len = 128;
136         int ret;
137
138         /* Loop with increasing sizes so we don't allocate too much. */
139         do {
140                 if (len > 1024)  {
141                         goto err_out;
142                 }
143
144                 tmp_corepath = (char *)talloc_realloc(NULL, tmp_corepath,
145                                                       char, len);
146                 if (!tmp_corepath) {
147                         return NULL;
148                 }
149
150                 ret = sysctlbyname("kern.corefile", tmp_corepath, &len, NULL,
151                                    0);
152                 if (ret == -1) {
153                         if (errno != ENOMEM) {
154                                 DEBUG(0, ("sysctlbyname failed getting "
155                                           "kern.corefile %s\n",
156                                           strerror(errno)));
157                                 goto err_out;
158                         }
159
160                         /* Not a large enough array, try a bigger one. */
161                         len = len << 1;
162                 }
163         } while (ret == -1);
164
165         /* Strip off the common filename expansion */
166         if ((end = strrchr_m(tmp_corepath, '/'))) {
167                 *end = '\0';
168         }
169
170         return tmp_corepath;
171
172  err_out:
173         if (tmp_corepath) {
174                 talloc_free(tmp_corepath);
175         }
176         return NULL;
177 }
178 #endif
179
180 #if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN)
181
182 /**
183  * Get the Linux corepath.
184  *
185  * On Linux the contents of /proc/sys/kernel/core_pattern indicates the
186  * location of the core path.
187  */
188 static char *get_linux_corepath(void)
189 {
190         char *end;
191         int fd;
192         char *result;
193
194         fd = open("/proc/sys/kernel/core_pattern", O_RDONLY, 0);
195         if (fd == -1) {
196                 return NULL;
197         }
198
199         result = afdgets(fd, NULL, 0);
200         close(fd);
201
202         if (result == NULL) {
203                 return NULL;
204         }
205
206         if (result[0] != '/') {
207                 /*
208                  * No absolute path, use the default (cwd)
209                  */
210                 TALLOC_FREE(result);
211                 return NULL;
212         }
213         /* Strip off the common filename expansion */
214
215         end = strrchr_m(result, '/');
216
217         if ((end != result) /* this would be the only / */
218             && (end != NULL)) {
219                 *end = '\0';
220         }
221         return result;
222 }
223 #endif
224
225
226 /**
227  * Try getting system-specific corepath if one exists.
228  *
229  * If the system doesn't define a corepath, then the default is used.
230  */
231 static char *get_corepath(const char *logbase, const char *progname)
232 {
233 #if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME))
234         char *tmp_corepath = NULL;
235         tmp_corepath = get_freebsd_corepath();
236
237         /* If this has been set correctly, we're done. */
238         if (tmp_corepath) {
239                 return tmp_corepath;
240         }
241 #endif
242
243 #if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN)
244         char *tmp_corepath = NULL;
245         tmp_corepath = get_linux_corepath();
246
247         /* If this has been set correctly, we're done. */
248         if (tmp_corepath) {
249                 return tmp_corepath;
250         }
251 #endif
252
253         /* Fall back to the default. */
254         return get_default_corepath(logbase, progname);
255 }
256
257 /*******************************************************************
258 make all the preparations to safely dump a core file
259 ********************************************************************/
260
261 void dump_core_setup(const char *progname)
262 {
263         char *logbase = NULL;
264         char *end = NULL;
265
266         if (lp_logfile() && *lp_logfile()) {
267                 if (asprintf(&logbase, "%s", lp_logfile()) < 0) {
268                         return;
269                 }
270                 if ((end = strrchr_m(logbase, '/'))) {
271                         *end = '\0';
272                 }
273         } else {
274                 /* We will end up here if the log file is given on the command
275                  * line by the -l option but the "log file" option is not set
276                  * in smb.conf.
277                  */
278                 if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) {
279                         return;
280                 }
281         }
282
283         SMB_ASSERT(progname != NULL);
284
285         corepath = get_corepath(logbase, progname);
286         if (!corepath) {
287                 DEBUG(0, ("Unable to setup corepath for %s: %s\n", progname,
288                           strerror(errno)));
289                 goto out;
290         }
291
292
293 #ifdef HAVE_GETRLIMIT
294 #ifdef RLIMIT_CORE
295         {
296                 struct rlimit rlp;
297                 getrlimit(RLIMIT_CORE, &rlp);
298                 rlp.rlim_cur = MAX(16*1024*1024,rlp.rlim_cur);
299                 setrlimit(RLIMIT_CORE, &rlp);
300                 getrlimit(RLIMIT_CORE, &rlp);
301                 DEBUG(3,("Maximum core file size limits now %d(soft) %d(hard)\n",
302                          (int)rlp.rlim_cur,(int)rlp.rlim_max));
303         }
304 #endif
305 #endif
306
307         /* FIXME: if we have a core-plus-pid facility, configurably set
308          * this up here.
309          */
310  out:
311         SAFE_FREE(logbase);
312 }
313
314  void dump_core(void)
315 {
316         static bool called;
317
318         if (called) {
319                 DEBUG(0, ("dump_core() called recursive\n"));
320                 exit(1);
321         }
322         called = true;
323
324         /* Note that even if core dumping has been disabled, we still set up
325          * the core path. This is to handle the case where core dumping is
326          * turned on in smb.conf and the relevant daemon is not restarted.
327          */
328         if (!lp_enable_core_files()) {
329                 DEBUG(0, ("Exiting on internal error (core file administratively disabled)\n"));
330                 exit(1);
331         }
332
333 #if DUMP_CORE
334         /* If we're running as non root we might not be able to dump the core
335          * file to the corepath.  There must not be an unbecome_root() before
336          * we call abort(). */
337         if (geteuid() != sec_initial_uid()) {
338                 become_root();
339         }
340
341         if (corepath == NULL) {
342                 DEBUG(0, ("Can not dump core: corepath not set up\n"));
343                 exit(1);
344         }
345
346         if (*corepath != '\0') {
347                 /* The chdir might fail if we dump core before we finish
348                  * processing the config file.
349                  */
350                 if (chdir(corepath) != 0) {
351                         DEBUG(0, ("unable to change to %s\n", corepath));
352                         DEBUGADD(0, ("refusing to dump core\n"));
353                         exit(1);
354                 }
355
356                 DEBUG(0,("dumping core in %s\n", corepath));
357         }
358
359         umask(~(0700));
360         dbgflush();
361
362 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
363         /* On Linux we lose the ability to dump core when we change our user
364          * ID. We know how to dump core safely, so let's make sure we have our
365          * dumpable flag set.
366          */
367         prctl(PR_SET_DUMPABLE, 1);
368 #endif
369
370         /* Ensure we don't have a signal handler for abort. */
371 #ifdef SIGABRT
372         CatchSignal(SIGABRT, SIG_DFL);
373 #endif
374
375         abort();
376
377 #else /* DUMP_CORE */
378         exit(1);
379 #endif /* DUMP_CORE */
380 }