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