1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/prctl.h>
26 #include <sys/types.h>
27 #include <attr/xattr.h>
29 #include <systemd/sd-journal.h>
30 #include <systemd/sd-login.h>
37 #include "cgroup-util.h"
38 #include "journald-native.h"
39 #include "conf-parser.h"
47 /* The maximum size up to which we process coredumps */
48 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
50 /* The maximum size up to which we leave the coredump around on
52 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
54 /* The maximum size up to which we store the coredump in the
56 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
58 /* Make sure to not make this larger than the maximum journal entry
59 * size. See ENTRY_SIZE_MAX in journald-native.c. */
60 assert_cc(JOURNAL_SIZE_MAX
<= ENTRY_SIZE_MAX
);
72 typedef enum CoredumpStorage
{
73 COREDUMP_STORAGE_NONE
,
74 COREDUMP_STORAGE_EXTERNAL
,
75 COREDUMP_STORAGE_JOURNAL
,
76 COREDUMP_STORAGE_BOTH
,
77 _COREDUMP_STORAGE_MAX
,
78 _COREDUMP_STORAGE_INVALID
= -1
81 static CoredumpStorage arg_storage
= COREDUMP_STORAGE_EXTERNAL
;
82 static off_t arg_process_size_max
= PROCESS_SIZE_MAX
;
83 static off_t arg_external_size_max
= EXTERNAL_SIZE_MAX
;
84 static size_t arg_journal_size_max
= JOURNAL_SIZE_MAX
;
86 static const char* const coredump_storage_table
[_COREDUMP_STORAGE_MAX
] = {
87 [COREDUMP_STORAGE_NONE
] = "none",
88 [COREDUMP_STORAGE_EXTERNAL
] = "external",
89 [COREDUMP_STORAGE_JOURNAL
] = "journal",
90 [COREDUMP_STORAGE_BOTH
] = "both",
93 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage
, CoredumpStorage
);
94 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage
, coredump_storage
, CoredumpStorage
, "Failed to parse storage setting");
96 static int parse_config(void) {
98 static const ConfigTableItem items
[] = {
99 { "Coredump", "ProcessSizeMax", config_parse_iec_off
, 0, &arg_process_size_max
},
100 { "Coredump", "ExternalSizeMax", config_parse_iec_off
, 0, &arg_external_size_max
},
101 { "Coredump", "JournalSizeMax", config_parse_iec_size
, 0, &arg_journal_size_max
},
102 { "Coredump", "Storage", config_parse_coredump_storage
, 0, &arg_storage
},
108 "/etc/systemd/coredump.conf",
111 config_item_table_lookup
,
118 static int fix_acl(int fd
, uid_t uid
) {
121 _cleanup_(acl_freep
) acl_t acl
= NULL
;
123 acl_permset_t permset
;
125 if (uid
<= SYSTEM_UID_MAX
)
128 /* Make sure normal users can read (but not write or delete)
129 * their own coredumps */
131 acl
= acl_get_fd(fd
);
133 log_error("Failed to get ACL: %m");
137 if (acl_create_entry(&acl
, &entry
) < 0 ||
138 acl_set_tag_type(entry
, ACL_USER
) < 0 ||
139 acl_set_qualifier(entry
, &uid
) < 0) {
140 log_error("Failed to patch ACL: %m");
144 if (acl_get_permset(entry
, &permset
) < 0 ||
145 acl_add_perm(permset
, ACL_READ
) < 0 ||
146 calc_acl_mask_if_needed(&acl
) < 0) {
147 log_warning("Failed to patch ACL: %m");
151 if (acl_set_fd(fd
, acl
) < 0) {
152 log_error("Failed to apply ACL: %m");
160 static int fix_xattr(int fd
, char *argv
[]) {
163 /* Attach some metadate to coredumps via extended
164 * attributes. Just because we can. */
166 if (!isempty(argv
[ARG_PID
]))
167 if (fsetxattr(fd
, "user.coredump.pid", argv
[ARG_PID
], strlen(argv
[ARG_PID
]), XATTR_CREATE
) < 0)
170 if (!isempty(argv
[ARG_UID
]))
171 if (fsetxattr(fd
, "user.coredump.uid", argv
[ARG_UID
], strlen(argv
[ARG_UID
]), XATTR_CREATE
) < 0)
174 if (!isempty(argv
[ARG_GID
]))
175 if (fsetxattr(fd
, "user.coredump.gid", argv
[ARG_GID
], strlen(argv
[ARG_GID
]), XATTR_CREATE
) < 0)
178 if (!isempty(argv
[ARG_SIGNAL
]))
179 if (fsetxattr(fd
, "user.coredump.signal", argv
[ARG_SIGNAL
], strlen(argv
[ARG_SIGNAL
]), XATTR_CREATE
) < 0)
182 if (!isempty(argv
[ARG_TIMESTAMP
]))
183 if (fsetxattr(fd
, "user.coredump.timestamp", argv
[ARG_TIMESTAMP
], strlen(argv
[ARG_TIMESTAMP
]), XATTR_CREATE
) < 0)
186 if (!isempty(argv
[ARG_COMM
]))
187 if (fsetxattr(fd
, "user.coredump.comm", argv
[ARG_COMM
], strlen(argv
[ARG_COMM
]), XATTR_CREATE
) < 0)
193 #define filename_escape(s) xescape((s), "./")
195 static int save_external_coredump(char **argv
, uid_t uid
, char **ret_filename
, int *ret_fd
, off_t
*ret_size
) {
196 _cleanup_free_
char *p
= NULL
, *t
= NULL
, *c
= NULL
, *fn
= NULL
, *tmp
= NULL
;
197 _cleanup_close_
int fd
= -1;
203 assert(ret_filename
);
207 c
= filename_escape(argv
[ARG_COMM
]);
211 p
= filename_escape(argv
[ARG_PID
]);
215 t
= filename_escape(argv
[ARG_TIMESTAMP
]);
219 r
= sd_id128_get_boot(&boot
);
221 log_error("Failed to determine boot ID: %s", strerror(-r
));
226 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR
".%s.%s000000",
228 SD_ID128_FORMAT_VAL(boot
),
234 tmp
= tempfn_random(fn
);
238 mkdir_p_label("/var/lib/systemd/coredump", 0755);
240 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, 0640);
242 log_error("Failed to create coredump file: %m");
246 r
= copy_bytes(STDIN_FILENO
, fd
);
248 log_error("Failed to dump coredump to file: %s", strerror(-r
));
252 /* Ignore errors on these */
258 log_error("Failed to sync coredump: %m");
263 if (fstat(fd
, &st
) < 0) {
264 log_error("Failed to fstat coredump: %m");
269 if (rename(tmp
, fn
) < 0) {
270 log_error("Failed to rename coredump: %m");
277 *ret_size
= st
.st_size
;
289 static int allocate_journal_field(int fd
, size_t size
, char **ret
, size_t *ret_size
) {
290 _cleanup_free_
char *field
= NULL
;
296 if (lseek(fd
, 0, SEEK_SET
) == (off_t
) -1) {
297 log_warning("Failed to seek: %m");
301 field
= malloc(9 + size
);
303 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
307 memcpy(field
, "COREDUMP=", 9);
309 n
= read(fd
, field
+ 9, size
);
311 log_error("Failed to read core data: %s", strerror(-n
));
314 if ((size_t) n
< size
) {
315 log_error("Core data too short.");
320 *ret_size
= size
+ 9;
327 static int maybe_remove_external_coredump(const char *filename
, off_t size
) {
332 if (IN_SET(arg_storage
, COREDUMP_STORAGE_EXTERNAL
, COREDUMP_STORAGE_BOTH
) &&
333 size
<= arg_external_size_max
)
336 if (unlink(filename
) < 0) {
337 log_error("Failed to unlink %s: %m", filename
);
344 int main(int argc
, char* argv
[]) {
346 _cleanup_free_
char *core_pid
= NULL
, *core_uid
= NULL
, *core_gid
= NULL
, *core_signal
= NULL
,
347 *core_timestamp
= NULL
, *core_comm
= NULL
, *core_exe
= NULL
, *core_unit
= NULL
,
348 *core_session
= NULL
, *core_message
= NULL
, *core_cmdline
= NULL
, *coredump_data
= NULL
,
349 *coredump_filename
= NULL
;
351 _cleanup_close_
int coredump_fd
= -1;
353 struct iovec iovec
[14];
361 /* Make sure we never enter a loop */
362 prctl(PR_SET_DUMPABLE
, 0);
364 /* First, log to a safe place, since we don't know what
365 * crashed and it might be journald which we'd rather not log
367 log_set_target(LOG_TARGET_KMSG
);
368 log_set_max_level(LOG_DEBUG
);
371 if (argc
!= _ARG_MAX
) {
372 log_error("Invalid number of arguments passed from kernel.");
377 /* Ignore all parse errors */
379 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage
));
381 r
= parse_uid(argv
[ARG_UID
], &uid
);
383 log_error("Failed to parse UID.");
387 r
= parse_pid(argv
[ARG_PID
], &pid
);
389 log_error("Failed to parse PID.");
393 r
= parse_gid(argv
[ARG_GID
], &gid
);
395 log_error("Failed to parse GID.");
399 if (cg_pid_get_unit(pid
, &t
) >= 0) {
401 if (streq(t
, SPECIAL_JOURNALD_SERVICE
)) {
403 /* If we are journald, we cut things short,
404 * don't write to the journal, but still
405 * create a coredump. */
407 if (arg_storage
!= COREDUMP_STORAGE_NONE
)
408 arg_storage
= COREDUMP_STORAGE_EXTERNAL
;
410 r
= save_external_coredump(argv
, uid
, &coredump_filename
, &coredump_fd
, &coredump_size
);
414 r
= maybe_remove_external_coredump(coredump_filename
, coredump_size
);
418 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename
);
422 core_unit
= strappend("COREDUMP_UNIT=", t
);
423 } else if (cg_pid_get_user_unit(pid
, &t
) >= 0)
424 core_unit
= strappend("COREDUMP_USER_UNIT=", t
);
427 IOVEC_SET_STRING(iovec
[j
++], core_unit
);
429 /* OK, now we know it's not the journal, hence we can make use
431 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG
);
434 core_pid
= strappend("COREDUMP_PID=", argv
[ARG_PID
]);
436 IOVEC_SET_STRING(iovec
[j
++], core_pid
);
438 core_uid
= strappend("COREDUMP_UID=", argv
[ARG_UID
]);
440 IOVEC_SET_STRING(iovec
[j
++], core_uid
);
442 core_gid
= strappend("COREDUMP_GID=", argv
[ARG_GID
]);
444 IOVEC_SET_STRING(iovec
[j
++], core_gid
);
446 core_signal
= strappend("COREDUMP_SIGNAL=", argv
[ARG_SIGNAL
]);
448 IOVEC_SET_STRING(iovec
[j
++], core_signal
);
450 core_comm
= strappend("COREDUMP_COMM=", argv
[ARG_COMM
]);
452 IOVEC_SET_STRING(iovec
[j
++], core_comm
);
454 if (sd_pid_get_session(pid
, &t
) >= 0) {
455 core_session
= strappend("COREDUMP_SESSION=", t
);
459 IOVEC_SET_STRING(iovec
[j
++], core_session
);
462 if (get_process_exe(pid
, &t
) >= 0) {
463 core_exe
= strappend("COREDUMP_EXE=", t
);
467 IOVEC_SET_STRING(iovec
[j
++], core_exe
);
470 if (get_process_cmdline(pid
, 0, false, &t
) >= 0) {
471 core_cmdline
= strappend("COREDUMP_CMDLINE=", t
);
475 IOVEC_SET_STRING(iovec
[j
++], core_cmdline
);
478 core_timestamp
= strjoin("COREDUMP_TIMESTAMP=", argv
[ARG_TIMESTAMP
], "000000", NULL
);
480 IOVEC_SET_STRING(iovec
[j
++], core_timestamp
);
482 IOVEC_SET_STRING(iovec
[j
++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
483 IOVEC_SET_STRING(iovec
[j
++], "PRIORITY=2");
485 core_message
= strjoin("MESSAGE=Process ", argv
[ARG_PID
], " (", argv
[ARG_COMM
], ") dumped core.", NULL
);
487 IOVEC_SET_STRING(iovec
[j
++], core_message
);
489 /* Always stream the coredump to disk, if that's possible */
490 r
= save_external_coredump(argv
, uid
, &coredump_filename
, &coredump_fd
, &coredump_size
);
494 /* If we don't want to keep the coredump on disk, remove it
495 * now, as later on we will lack the privileges for it. */
496 r
= maybe_remove_external_coredump(coredump_filename
, coredump_size
);
500 /* Now, let's drop privileges to become the user who owns the
501 * segfaulted process and allocate the coredump memory under
502 * his uid. This also ensures that the credentials journald
503 * will see are the ones of the coredumping user, thus making
504 * sure the user himself gets access to the core dump. */
505 if (setresgid(gid
, gid
, gid
) < 0 ||
506 setresuid(uid
, uid
, uid
) < 0) {
507 log_error("Failed to drop privileges: %m");
512 /* Optionally store the entire coredump in the journal */
513 if (IN_SET(arg_storage
, COREDUMP_STORAGE_JOURNAL
, COREDUMP_STORAGE_BOTH
) &&
514 coredump_size
<= (off_t
) arg_journal_size_max
) {
517 /* Store the coredump itself in the journal */
519 r
= allocate_journal_field(coredump_fd
, (size_t) coredump_size
, &coredump_data
, &sz
);
521 iovec
[j
].iov_base
= coredump_data
;
522 iovec
[j
].iov_len
= sz
;
527 r
= sd_journal_sendv(iovec
, j
);
529 log_error("Failed to log coredump: %s", strerror(-r
));
532 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;