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