2012-01-14 00:37:35 +01:00
/***
This file is part of systemd .
Copyright 2012 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 00:20:58 +02:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2012-01-14 00:37:35 +01:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 00:20:58 +02:00
Lesser General Public License for more details .
2012-01-14 00:37:35 +01:00
2012-04-12 00:20:58 +02:00
You should have received a copy of the GNU Lesser General Public License
2012-01-14 00:37:35 +01:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
2012-02-02 01:22:49 +01:00
# include <stdio.h>
# include <sys/prctl.h>
2014-06-19 20:18:36 +02:00
# include <sys/xattr.h>
2015-10-23 18:52:53 +02:00
# include <unistd.h>
2012-01-14 00:37:35 +01:00
2014-07-08 16:16:14 +02:00
# ifdef HAVE_ELFUTILS
2016-02-08 21:16:08 +01:00
# include <dwarf.h>
# include <elfutils/libdwfl.h>
2014-07-08 16:16:14 +02:00
# endif
2015-01-29 02:47:29 +01:00
# include "sd-journal.h"
# include "sd-login.h"
2016-02-08 21:16:08 +01:00
# include "sd-daemon.h"
2015-10-23 18:52:53 +02:00
# include "acl-util.h"
2015-10-27 03:01:06 +01:00
# include "alloc-util.h"
2015-10-26 23:32:16 +01:00
# include "capability-util.h"
2012-05-30 22:25:01 +02:00
# include "cgroup-util.h"
2015-10-23 18:52:53 +02:00
# include "compress.h"
2014-06-18 22:02:18 +02:00
# include "conf-parser.h"
# include "copy.h"
2015-01-29 02:47:29 +01:00
# include "coredump-vacuum.h"
2015-10-26 20:07:55 +01:00
# include "dirent-util.h"
2015-10-23 18:52:53 +02:00
# include "escape.h"
2015-10-25 13:14:12 +01:00
# include "fd-util.h"
2015-10-23 18:52:53 +02:00
# include "fileio.h"
2015-10-26 21:16:26 +01:00
# include "fs-util.h"
2015-10-27 01:02:30 +01:00
# include "io-util.h"
2015-10-23 18:52:53 +02:00
# include "journald-native.h"
# include "log.h"
# include "macro.h"
2016-04-19 16:59:47 +02:00
# include "missing.h"
2015-10-23 18:52:53 +02:00
# include "mkdir.h"
2015-10-26 16:18:16 +01:00
# include "parse-util.h"
2015-04-10 19:10:00 +02:00
# include "process-util.h"
2016-02-08 21:16:08 +01:00
# include "socket-util.h"
2015-10-23 18:52:53 +02:00
# include "special.h"
# include "stacktrace.h"
2015-10-26 22:31:05 +01:00
# include "string-table.h"
2015-10-24 22:58:24 +02:00
# include "string-util.h"
2015-10-23 18:52:53 +02:00
# include "strv.h"
2015-10-25 22:32:30 +01:00
# include "user-util.h"
2015-10-23 18:52:53 +02:00
# include "util.h"
2014-06-26 02:53:40 +02:00
2014-06-18 22:02:18 +02:00
/* The maximum size up to which we process coredumps */
2015-09-10 18:16:18 +02:00
# define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU))
2014-06-18 22:02:18 +02:00
2016-02-08 22:08:49 +01:00
/* The maximum size up to which we leave the coredump around on disk */
2014-06-18 22:02:18 +02:00
# define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
2016-02-08 22:08:49 +01:00
/* The maximum size up to which we store the coredump in the journal */
2014-06-18 22:02:18 +02:00
# define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
2012-01-14 00:37:35 +01:00
2013-04-08 20:32:03 +02:00
/* Make sure to not make this larger than the maximum journal entry
2014-07-14 22:53:23 +02:00
* size . See DATA_SIZE_MAX in journald - native . c . */
assert_cc ( JOURNAL_SIZE_MAX < = DATA_SIZE_MAX ) ;
2012-01-14 00:37:35 +01:00
enum {
2016-02-08 21:16:08 +01:00
/* We use this as array indexes for a couple of special fields we use for naming coredumping files, and
* attaching xattrs */
CONTEXT_PID ,
CONTEXT_UID ,
CONTEXT_GID ,
CONTEXT_SIGNAL ,
CONTEXT_TIMESTAMP ,
2016-02-08 22:08:49 +01:00
CONTEXT_RLIMIT ,
2016-02-08 21:16:08 +01:00
CONTEXT_COMM ,
CONTEXT_EXE ,
_CONTEXT_MAX
2012-01-14 00:37:35 +01:00
} ;
2014-06-18 22:02:18 +02:00
typedef enum CoredumpStorage {
COREDUMP_STORAGE_NONE ,
COREDUMP_STORAGE_EXTERNAL ,
COREDUMP_STORAGE_JOURNAL ,
COREDUMP_STORAGE_BOTH ,
_COREDUMP_STORAGE_MAX ,
_COREDUMP_STORAGE_INVALID = - 1
} CoredumpStorage ;
static const char * const coredump_storage_table [ _COREDUMP_STORAGE_MAX ] = {
[ COREDUMP_STORAGE_NONE ] = " none " ,
[ COREDUMP_STORAGE_EXTERNAL ] = " external " ,
[ COREDUMP_STORAGE_JOURNAL ] = " journal " ,
[ COREDUMP_STORAGE_BOTH ] = " both " ,
} ;
DEFINE_PRIVATE_STRING_TABLE_LOOKUP ( coredump_storage , CoredumpStorage ) ;
2014-06-27 19:09:22 +02:00
static DEFINE_CONFIG_PARSE_ENUM ( config_parse_coredump_storage , coredump_storage , CoredumpStorage , " Failed to parse storage setting " ) ;
2014-06-26 02:53:40 +02:00
static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL ;
2014-06-27 19:09:22 +02:00
static bool arg_compress = true ;
2015-09-10 18:16:18 +02:00
static uint64_t arg_process_size_max = PROCESS_SIZE_MAX ;
static uint64_t arg_external_size_max = EXTERNAL_SIZE_MAX ;
2014-06-26 02:53:40 +02:00
static size_t arg_journal_size_max = JOURNAL_SIZE_MAX ;
2015-09-10 18:16:18 +02:00
static uint64_t arg_keep_free = ( uint64_t ) - 1 ;
static uint64_t arg_max_use = ( uint64_t ) - 1 ;
2014-06-18 22:02:18 +02:00
static int parse_config ( void ) {
static const ConfigTableItem items [ ] = {
2014-06-27 19:09:22 +02:00
{ " Coredump " , " Storage " , config_parse_coredump_storage , 0 , & arg_storage } ,
{ " Coredump " , " Compress " , config_parse_bool , 0 , & arg_compress } ,
2015-09-10 18:16:18 +02:00
{ " Coredump " , " ProcessSizeMax " , config_parse_iec_uint64 , 0 , & arg_process_size_max } ,
{ " Coredump " , " ExternalSizeMax " , config_parse_iec_uint64 , 0 , & arg_external_size_max } ,
2014-06-27 19:09:22 +02:00
{ " Coredump " , " JournalSizeMax " , config_parse_iec_size , 0 , & arg_journal_size_max } ,
2015-09-10 18:16:18 +02:00
{ " Coredump " , " KeepFree " , config_parse_iec_uint64 , 0 , & arg_keep_free } ,
{ " Coredump " , " MaxUse " , config_parse_iec_uint64 , 0 , & arg_max_use } ,
2014-06-18 22:02:18 +02:00
{ }
} ;
2015-11-10 15:57:21 +01:00
return config_parse_many ( PKGSYSCONFDIR " /coredump.conf " ,
CONF_PATHS_NULSTR ( " systemd/coredump.conf.d " ) ,
2014-11-29 10:06:48 +01:00
" Coredump \0 " ,
config_item_table_lookup , items ,
false , NULL ) ;
2014-06-18 22:02:18 +02:00
}
static int fix_acl ( int fd , uid_t uid ) {
# ifdef HAVE_ACL
_cleanup_ ( acl_freep ) acl_t acl = NULL ;
acl_entry_t entry ;
acl_permset_t permset ;
2015-11-05 13:44:06 +01:00
int r ;
2014-06-18 22:02:18 +02:00
2014-06-27 19:32:14 +02:00
assert ( fd > = 0 ) ;
2014-06-18 22:02:18 +02:00
if ( uid < = SYSTEM_UID_MAX )
return 0 ;
/* Make sure normal users can read (but not write or delete)
* their own coredumps */
acl = acl_get_fd ( fd ) ;
2014-11-28 19:57:32 +01:00
if ( ! acl )
return log_error_errno ( errno , " Failed to get ACL: %m " ) ;
2014-06-18 22:02:18 +02:00
if ( acl_create_entry ( & acl , & entry ) < 0 | |
acl_set_tag_type ( entry , ACL_USER ) < 0 | |
acl_set_qualifier ( entry , & uid ) < 0 ) {
2014-11-28 19:29:59 +01:00
log_error_errno ( errno , " Failed to patch ACL: %m " ) ;
2014-06-18 22:02:18 +02:00
return - errno ;
}
if ( acl_get_permset ( entry , & permset ) < 0 | |
2015-11-05 13:44:06 +01:00
acl_add_perm ( permset , ACL_READ ) < 0 )
return log_warning_errno ( errno , " Failed to patch ACL: %m " ) ;
r = calc_acl_mask_if_needed ( & acl ) ;
if ( r < 0 )
return log_warning_errno ( r , " Failed to patch ACL: %m " ) ;
2014-06-18 22:02:18 +02:00
2014-11-28 19:57:32 +01:00
if ( acl_set_fd ( fd , acl ) < 0 )
return log_error_errno ( errno , " Failed to apply ACL: %m " ) ;
2014-06-18 22:02:18 +02:00
# endif
return 0 ;
}
2016-02-08 21:16:08 +01:00
static int fix_xattr ( int fd , const char * context [ _CONTEXT_MAX ] ) {
2014-06-19 12:13:09 +02:00
2016-02-08 21:16:08 +01:00
static const char * const xattrs [ _CONTEXT_MAX ] = {
[ CONTEXT_PID ] = " user.coredump.pid " ,
[ CONTEXT_UID ] = " user.coredump.uid " ,
[ CONTEXT_GID ] = " user.coredump.gid " ,
[ CONTEXT_SIGNAL ] = " user.coredump.signal " ,
[ CONTEXT_TIMESTAMP ] = " user.coredump.timestamp " ,
[ CONTEXT_COMM ] = " user.coredump.comm " ,
[ CONTEXT_EXE ] = " user.coredump.exe " ,
2014-06-19 12:13:09 +02:00
} ;
2014-06-18 22:02:18 +02:00
int r = 0 ;
2014-06-19 12:13:09 +02:00
unsigned i ;
2014-06-18 22:02:18 +02:00
2014-06-27 19:32:14 +02:00
assert ( fd > = 0 ) ;
2014-06-24 02:18:16 +02:00
/* Attach some metadata to coredumps via extended
2014-06-18 22:02:18 +02:00
* attributes . Just because we can . */
2016-02-08 21:16:08 +01:00
for ( i = 0 ; i < _CONTEXT_MAX ; i + + ) {
2014-06-24 02:18:16 +02:00
int k ;
2016-02-08 21:16:08 +01:00
if ( isempty ( context [ i ] ) | | ! xattrs [ i ] )
2014-06-19 12:13:09 +02:00
continue ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
k = fsetxattr ( fd , xattrs [ i ] , context [ i ] , strlen ( context [ i ] ) , XATTR_CREATE ) ;
2014-06-24 02:18:16 +02:00
if ( k < 0 & & r = = 0 )
2014-06-18 22:02:18 +02:00
r = - errno ;
2014-06-19 12:13:09 +02:00
}
2014-06-18 22:02:18 +02:00
return r ;
}
2014-06-23 12:39:53 +02:00
# define filename_escape(s) xescape((s), ". / ")
2014-06-18 22:02:18 +02:00
2016-04-19 16:59:47 +02:00
static inline const char * coredump_tmpfile_name ( const char * s ) {
return s ? s : " (unnamed temporary file) " ;
}
2014-06-27 19:32:14 +02:00
static int fix_permissions (
int fd ,
const char * filename ,
const char * target ,
2016-02-08 21:16:08 +01:00
const char * context [ _CONTEXT_MAX ] ,
2014-06-27 19:32:14 +02:00
uid_t uid ) {
assert ( fd > = 0 ) ;
assert ( target ) ;
2016-02-08 21:16:08 +01:00
assert ( context ) ;
2014-06-25 07:03:03 +02:00
/* Ignore errors on these */
2016-02-08 21:16:08 +01:00
( void ) fchmod ( fd , 0640 ) ;
( void ) fix_acl ( fd , uid ) ;
( void ) fix_xattr ( fd , context ) ;
2014-06-25 07:03:03 +02:00
2014-11-28 19:57:32 +01:00
if ( fsync ( fd ) < 0 )
2016-04-19 16:59:47 +02:00
return log_error_errno ( errno , " Failed to sync coredump %s: %m " , coredump_tmpfile_name ( filename ) ) ;
2014-06-25 07:03:03 +02:00
2016-04-19 16:59:47 +02:00
if ( filename ) {
if ( rename ( filename , target ) < 0 )
return log_error_errno ( errno , " Failed to rename coredump %s -> %s: %m " , filename , target ) ;
} else {
_cleanup_free_ char * proc_fd_path = NULL ;
if ( asprintf ( & proc_fd_path , " /proc/self/fd/%d " , fd ) < 0 )
return log_oom ( ) ;
if ( linkat ( AT_FDCWD , proc_fd_path , AT_FDCWD , target , AT_SYMLINK_FOLLOW ) < 0 )
return log_error_errno ( errno , " Failed to create coredump %s: %m " , target ) ;
}
2014-06-25 07:03:03 +02:00
return 0 ;
}
2015-09-10 18:16:18 +02:00
static int maybe_remove_external_coredump ( const char * filename , uint64_t size ) {
2014-06-25 07:03:03 +02:00
2014-06-27 19:32:14 +02:00
/* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
2014-06-25 07:03:03 +02:00
if ( IN_SET ( arg_storage , COREDUMP_STORAGE_EXTERNAL , COREDUMP_STORAGE_BOTH ) & &
size < = arg_external_size_max )
return 0 ;
if ( ! filename )
return 1 ;
2014-11-28 19:57:32 +01:00
if ( unlink ( filename ) < 0 & & errno ! = ENOENT )
return log_error_errno ( errno , " Failed to unlink %s: %m " , filename ) ;
2014-06-25 07:03:03 +02:00
return 1 ;
}
2016-02-08 21:16:08 +01:00
static int make_filename ( const char * context [ _CONTEXT_MAX ] , char * * ret ) {
2014-06-27 19:32:14 +02:00
_cleanup_free_ char * c = NULL , * u = NULL , * p = NULL , * t = NULL ;
2015-03-27 12:02:49 +01:00
sd_id128_t boot = { } ;
2014-06-18 22:02:18 +02:00
int r ;
2016-02-08 21:16:08 +01:00
assert ( context ) ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
c = filename_escape ( context [ CONTEXT_COMM ] ) ;
2014-06-18 22:02:18 +02:00
if ( ! c )
2014-06-27 19:32:14 +02:00
return - ENOMEM ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
u = filename_escape ( context [ CONTEXT_UID ] ) ;
2014-06-27 18:57:24 +02:00
if ( ! u )
2014-06-27 19:32:14 +02:00
return - ENOMEM ;
2014-06-18 22:02:18 +02:00
r = sd_id128_get_boot ( & boot ) ;
2014-06-27 19:32:14 +02:00
if ( r < 0 )
2014-06-18 22:02:18 +02:00
return r ;
2016-02-08 21:16:08 +01:00
p = filename_escape ( context [ CONTEXT_PID ] ) ;
2014-06-27 19:32:14 +02:00
if ( ! p )
return - ENOMEM ;
2016-02-08 21:16:08 +01:00
t = filename_escape ( context [ CONTEXT_TIMESTAMP ] ) ;
2014-06-27 19:32:14 +02:00
if ( ! t )
return - ENOMEM ;
if ( asprintf ( ret ,
2014-06-27 18:57:24 +02:00
" /var/lib/systemd/coredump/core.%s.%s. " SD_ID128_FORMAT_STR " .%s.%s000000 " ,
2014-06-18 22:02:18 +02:00
c ,
2014-06-27 18:57:24 +02:00
u ,
2014-06-18 22:02:18 +02:00
SD_ID128_FORMAT_VAL ( boot ) ,
p ,
2014-06-27 19:32:14 +02:00
t ) < 0 )
return - ENOMEM ;
return 0 ;
}
2016-04-19 16:59:47 +02:00
static int open_coredump_tmpfile ( const char * target , char * * ret_filename ) {
_cleanup_free_ char * tmp = NULL ;
int fd ;
int r ;
assert ( target ) ;
assert ( ret_filename ) ;
fd = open ( " /var/lib/systemd/coredump " , O_TMPFILE | O_CLOEXEC | O_NOCTTY | O_RDWR , 0640 ) ;
if ( fd < 0 ) {
log_debug_errno ( errno , " Failed to use O_TMPFILE: %m " ) ;
r = tempfn_random ( target , NULL , & tmp ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to determine temporary file name: %m " ) ;
fd = open ( tmp , O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW , 0640 ) ;
if ( fd < 0 )
return log_error_errno ( errno , " Failed to create coredump file %s: %m " , tmp ) ;
}
* ret_filename = tmp ;
tmp = NULL ;
return fd ;
}
2014-06-27 19:32:14 +02:00
static int save_external_coredump (
2016-02-08 21:16:08 +01:00
const char * context [ _CONTEXT_MAX ] ,
int input_fd ,
2014-06-27 19:32:14 +02:00
char * * ret_filename ,
2015-12-23 19:59:31 +01:00
int * ret_node_fd ,
int * ret_data_fd ,
2015-09-10 18:16:18 +02:00
uint64_t * ret_size ) {
2014-06-27 19:32:14 +02:00
_cleanup_free_ char * fn = NULL , * tmp = NULL ;
_cleanup_close_ int fd = - 1 ;
2016-02-08 22:08:49 +01:00
uint64_t rlimit , max_size ;
2014-06-27 19:32:14 +02:00
struct stat st ;
2016-02-08 21:16:08 +01:00
uid_t uid ;
2014-06-27 19:32:14 +02:00
int r ;
2016-02-08 21:16:08 +01:00
assert ( context ) ;
2014-06-27 19:32:14 +02:00
assert ( ret_filename ) ;
2015-12-23 19:59:31 +01:00
assert ( ret_node_fd ) ;
assert ( ret_data_fd ) ;
2014-06-27 19:32:14 +02:00
assert ( ret_size ) ;
2016-02-08 21:16:08 +01:00
r = parse_uid ( context [ CONTEXT_UID ] , & uid ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse UID: %m " ) ;
2016-02-08 22:08:49 +01:00
r = safe_atou64 ( context [ CONTEXT_RLIMIT ] , & rlimit ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse resource limit: %s " , context [ CONTEXT_RLIMIT ] ) ;
if ( rlimit < = 0 ) {
/* Is coredumping disabled? Then don't bother saving/processing the coredump */
log_info ( " Core Dumping has been disabled for process %s (%s). " , context [ CONTEXT_PID ] , context [ CONTEXT_COMM ] ) ;
return - EBADSLT ;
}
/* Never store more than the process configured, or than we actually shall keep or process */
max_size = MIN ( rlimit , MAX ( arg_process_size_max , arg_external_size_max ) ) ;
2016-02-08 21:16:08 +01:00
r = make_filename ( context , & fn ) ;
2014-11-28 18:23:20 +01:00
if ( r < 0 )
return log_error_errno ( r , " Failed to determine coredump file name: %m " ) ;
2014-06-18 22:02:18 +02:00
2012-05-31 12:40:20 +02:00
mkdir_p_label ( " /var/lib/systemd/coredump " , 0755 ) ;
2012-02-02 01:22:49 +01:00
2016-04-19 16:59:47 +02:00
fd = open_coredump_tmpfile ( fn , & tmp ) ;
2014-11-28 19:57:32 +01:00
if ( fd < 0 )
2016-04-19 16:59:47 +02:00
return fd ;
2012-02-02 01:22:49 +01:00
2016-02-08 22:08:49 +01:00
r = copy_bytes ( input_fd , fd , max_size , false ) ;
2014-11-06 21:20:32 +01:00
if ( r = = - EFBIG ) {
2016-02-08 21:16:08 +01:00
log_error ( " Coredump of %s (%s) is larger than configured processing limit, refusing. " , context [ CONTEXT_PID ] , context [ CONTEXT_COMM ] ) ;
2014-06-23 16:28:05 +02:00
goto fail ;
} else if ( IN_SET ( r , - EDQUOT , - ENOSPC ) ) {
2016-02-08 21:16:08 +01:00
log_error ( " Not enough disk space for coredump of %s (%s), refusing. " , context [ CONTEXT_PID ] , context [ CONTEXT_COMM ] ) ;
2014-06-23 16:28:05 +02:00
goto fail ;
} else if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_error_errno ( r , " Failed to dump coredump to file: %m " ) ;
2014-06-18 22:02:18 +02:00
goto fail ;
}
2012-02-02 01:22:49 +01:00
2014-06-18 22:02:18 +02:00
if ( fstat ( fd , & st ) < 0 ) {
2016-04-19 16:59:47 +02:00
log_error_errno ( errno , " Failed to fstat coredump %s: %m " , coredump_tmpfile_name ( tmp ) ) ;
2014-06-18 22:02:18 +02:00
goto fail ;
}
2014-06-27 00:07:39 +02:00
if ( lseek ( fd , 0 , SEEK_SET ) = = ( off_t ) - 1 ) {
2016-04-19 16:59:47 +02:00
log_error_errno ( errno , " Failed to seek on %s: %m " , coredump_tmpfile_name ( tmp ) ) ;
2014-06-27 19:32:14 +02:00
goto fail ;
2014-06-27 00:07:39 +02:00
}
2014-07-04 04:42:22 +02:00
# if defined(HAVE_XZ) || defined(HAVE_LZ4)
2014-06-25 07:03:03 +02:00
/* If we will remove the coredump anyway, do not compress. */
2014-06-26 02:53:40 +02:00
if ( maybe_remove_external_coredump ( NULL , st . st_size ) = = 0
2014-06-27 19:09:22 +02:00
& & arg_compress ) {
2014-06-25 07:03:03 +02:00
2014-06-27 19:32:14 +02:00
_cleanup_free_ char * fn_compressed = NULL , * tmp_compressed = NULL ;
_cleanup_close_ int fd_compressed = - 1 ;
2014-06-25 07:03:03 +02:00
2014-07-04 04:42:22 +02:00
fn_compressed = strappend ( fn , COMPRESSED_EXT ) ;
2014-06-27 19:32:14 +02:00
if ( ! fn_compressed ) {
2014-07-04 04:42:22 +02:00
log_oom ( ) ;
2014-06-25 07:03:03 +02:00
goto uncompressed ;
}
2016-04-19 16:59:47 +02:00
fd_compressed = open_coredump_tmpfile ( fn_compressed , & tmp_compressed ) ;
if ( fd_compressed < 0 )
2014-06-27 19:32:14 +02:00
goto uncompressed ;
2014-06-25 07:03:03 +02:00
2014-07-04 04:42:22 +02:00
r = compress_stream ( fd , fd_compressed , - 1 ) ;
2014-06-27 19:32:14 +02:00
if ( r < 0 ) {
2016-04-19 16:59:47 +02:00
log_error_errno ( r , " Failed to compress %s: %m " , coredump_tmpfile_name ( tmp_compressed ) ) ;
2014-06-27 19:32:14 +02:00
goto fail_compressed ;
}
2016-02-08 21:16:08 +01:00
r = fix_permissions ( fd_compressed , tmp_compressed , fn_compressed , context , uid ) ;
2014-06-25 07:03:03 +02:00
if ( r < 0 )
2014-06-27 19:32:14 +02:00
goto fail_compressed ;
/* OK, this worked, we can get rid of the uncompressed version now */
2016-04-19 16:59:47 +02:00
if ( tmp )
unlink_noerrno ( tmp ) ;
2014-06-25 07:03:03 +02:00
2015-09-10 18:16:18 +02:00
* ret_filename = fn_compressed ; /* compressed */
2015-12-23 19:59:31 +01:00
* ret_node_fd = fd_compressed ; /* compressed */
* ret_data_fd = fd ; /* uncompressed */
2015-09-10 18:16:18 +02:00
* ret_size = ( uint64_t ) st . st_size ; /* uncompressed */
2014-06-25 07:03:03 +02:00
2014-06-27 19:32:14 +02:00
fn_compressed = NULL ;
2015-12-23 19:59:31 +01:00
fd = fd_compressed = - 1 ;
2014-06-25 07:03:03 +02:00
return 0 ;
2014-06-27 19:32:14 +02:00
fail_compressed :
2016-04-19 16:59:47 +02:00
if ( tmp_compressed )
( void ) unlink ( tmp_compressed ) ;
2014-06-18 22:02:18 +02:00
}
2014-06-25 07:03:03 +02:00
uncompressed :
2014-07-11 16:42:06 +02:00
# endif
2015-12-23 19:59:31 +01:00
2016-02-08 21:16:08 +01:00
r = fix_permissions ( fd , tmp , fn , context , uid ) ;
2014-06-25 07:03:03 +02:00
if ( r < 0 )
goto fail ;
2014-06-18 22:02:18 +02:00
* ret_filename = fn ;
2015-12-23 19:59:31 +01:00
* ret_data_fd = fd ;
* ret_node_fd = - 1 ;
2015-09-10 18:16:18 +02:00
* ret_size = ( uint64_t ) st . st_size ;
2014-06-18 22:02:18 +02:00
fn = NULL ;
fd = - 1 ;
return 0 ;
fail :
2016-04-19 16:59:47 +02:00
if ( tmp )
( void ) unlink ( tmp ) ;
2014-06-18 22:02:18 +02:00
return r ;
}
static int allocate_journal_field ( int fd , size_t size , char * * ret , size_t * ret_size ) {
_cleanup_free_ char * field = NULL ;
ssize_t n ;
2014-06-19 12:07:12 +02:00
assert ( fd > = 0 ) ;
2014-06-18 22:02:18 +02:00
assert ( ret ) ;
assert ( ret_size ) ;
2014-11-28 19:57:32 +01:00
if ( lseek ( fd , 0 , SEEK_SET ) = = ( off_t ) - 1 )
return log_warning_errno ( errno , " Failed to seek: %m " ) ;
2012-02-02 01:22:49 +01:00
2014-06-18 22:02:18 +02:00
field = malloc ( 9 + size ) ;
if ( ! field ) {
2014-06-25 07:03:03 +02:00
log_warning ( " Failed to allocate memory for coredump, coredump will not be stored. " ) ;
2014-06-18 22:02:18 +02:00
return - ENOMEM ;
}
memcpy ( field , " COREDUMP= " , 9 ) ;
n = read ( fd , field + 9 , size ) ;
2014-11-28 18:23:20 +01:00
if ( n < 0 )
return log_error_errno ( ( int ) n , " Failed to read core data: %m " ) ;
2014-06-18 22:02:18 +02:00
if ( ( size_t ) n < size ) {
log_error ( " Core data too short. " ) ;
return - EIO ;
}
* ret = field ;
* ret_size = size + 9 ;
field = NULL ;
return 0 ;
}
2012-02-02 01:22:49 +01:00
2014-11-25 07:37:48 +01:00
/* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
* 0 : / dev / pts / 23
* pos : 0
* flags : 0100002
*
* 1 : / dev / pts / 23
* pos : 0
* flags : 0100002
*
* 2 : / dev / pts / 23
* pos : 0
* flags : 0100002
* EOF
*/
static int compose_open_fds ( pid_t pid , char * * open_fds ) {
2014-11-28 20:15:18 +01:00
_cleanup_closedir_ DIR * proc_fd_dir = NULL ;
_cleanup_close_ int proc_fdinfo_fd = - 1 ;
_cleanup_free_ char * buffer = NULL ;
2014-11-25 07:37:48 +01:00
_cleanup_fclose_ FILE * stream = NULL ;
2014-11-27 05:31:35 +01:00
const char * fddelim = " " , * path ;
2014-11-25 07:37:48 +01:00
struct dirent * dent = NULL ;
2014-11-28 20:15:18 +01:00
size_t size = 0 ;
2014-11-25 07:37:48 +01:00
int r = 0 ;
assert ( pid > = 0 ) ;
assert ( open_fds ! = NULL ) ;
2014-11-27 05:31:35 +01:00
path = procfs_file_alloca ( pid , " fd " ) ;
2014-11-25 07:37:48 +01:00
proc_fd_dir = opendir ( path ) ;
2014-11-27 05:31:35 +01:00
if ( ! proc_fd_dir )
return - errno ;
2014-11-25 07:37:48 +01:00
2014-11-28 20:15:18 +01:00
proc_fdinfo_fd = openat ( dirfd ( proc_fd_dir ) , " ../fdinfo " , O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC | O_PATH ) ;
2014-11-27 05:31:35 +01:00
if ( proc_fdinfo_fd < 0 )
return - errno ;
2014-11-25 07:37:48 +01:00
2014-11-28 20:15:18 +01:00
stream = open_memstream ( & buffer , & size ) ;
2014-11-25 07:37:48 +01:00
if ( ! stream )
return - ENOMEM ;
2014-11-28 20:15:18 +01:00
FOREACH_DIRENT ( dent , proc_fd_dir , return - errno ) {
2014-11-25 07:37:48 +01:00
_cleanup_fclose_ FILE * fdinfo = NULL ;
2014-11-28 20:15:18 +01:00
_cleanup_free_ char * fdname = NULL ;
2014-11-27 05:31:35 +01:00
char line [ LINE_MAX ] ;
2014-11-28 20:15:18 +01:00
int fd ;
2014-11-25 07:37:48 +01:00
2014-11-27 05:31:35 +01:00
r = readlinkat_malloc ( dirfd ( proc_fd_dir ) , dent - > d_name , & fdname ) ;
2014-11-25 07:37:48 +01:00
if ( r < 0 )
return r ;
fprintf ( stream , " %s%s:%s \n " , fddelim , dent - > d_name , fdname ) ;
fddelim = " \n " ;
/* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
2014-11-27 05:31:35 +01:00
fd = openat ( proc_fdinfo_fd , dent - > d_name , O_NOFOLLOW | O_CLOEXEC | O_RDONLY ) ;
if ( fd < 0 )
2014-11-25 07:37:48 +01:00
continue ;
2014-11-27 05:31:35 +01:00
fdinfo = fdopen ( fd , " re " ) ;
if ( fdinfo = = NULL ) {
close ( fd ) ;
2014-11-25 07:37:48 +01:00
continue ;
2014-11-27 05:31:35 +01:00
}
2014-11-25 07:37:48 +01:00
2014-11-28 20:15:18 +01:00
FOREACH_LINE ( line , fdinfo , break ) {
fputs ( line , stream ) ;
if ( ! endswith ( line , " \n " ) )
fputc ( ' \n ' , stream ) ;
}
2014-11-25 07:37:48 +01:00
}
2014-11-28 20:15:18 +01:00
errno = 0 ;
2015-09-09 15:20:10 +02:00
stream = safe_fclose ( stream ) ;
2014-11-28 20:15:18 +01:00
2016-01-11 20:31:14 +01:00
if ( errno > 0 )
2014-11-28 20:15:18 +01:00
return - errno ;
* open_fds = buffer ;
buffer = NULL ;
2014-11-25 07:37:48 +01:00
return 0 ;
}
2016-02-08 21:16:08 +01:00
static int change_uid_gid ( const char * context [ ] ) {
uid_t uid ;
gid_t gid ;
int r ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
r = parse_uid ( context [ CONTEXT_UID ] , & uid ) ;
if ( r < 0 )
return r ;
2014-11-28 20:29:24 +01:00
2016-02-08 23:35:24 +01:00
if ( uid < = SYSTEM_UID_MAX ) {
const char * user = " systemd-coredump " ;
r = get_user_creds ( & user , & uid , & gid , NULL , NULL ) ;
if ( r < 0 ) {
log_warning_errno ( r , " Cannot resolve %s user. Proceeding to dump core as root: %m " , user ) ;
uid = gid = 0 ;
}
} else {
r = parse_gid ( context [ CONTEXT_GID ] , & gid ) ;
if ( r < 0 )
return r ;
}
2016-02-08 21:16:08 +01:00
return drop_privileges ( uid , gid , 0 ) ;
}
2014-11-28 20:29:24 +01:00
2016-02-08 21:16:08 +01:00
static int submit_coredump (
const char * context [ _CONTEXT_MAX ] ,
struct iovec * iovec ,
size_t n_iovec_allocated ,
size_t n_iovec ,
int input_fd ) {
2014-06-18 22:02:18 +02:00
2015-12-23 19:59:31 +01:00
_cleanup_close_ int coredump_fd = - 1 , coredump_node_fd = - 1 ;
2016-02-08 21:16:08 +01:00
_cleanup_free_ char * core_message = NULL , * filename = NULL , * coredump_data = NULL ;
2015-09-10 18:16:18 +02:00
uint64_t coredump_size ;
2016-02-08 21:16:08 +01:00
int r ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
assert ( context ) ;
assert ( iovec ) ;
assert ( n_iovec_allocated > = n_iovec + 3 ) ;
assert ( input_fd > = 0 ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
/* Vacuum before we write anything again */
( void ) coredump_vacuum ( - 1 , arg_keep_free , arg_max_use ) ;
2012-02-02 01:22:49 +01:00
2016-02-08 21:16:08 +01:00
/* Always stream the coredump to disk, if that's possible */
r = save_external_coredump ( context , input_fd , & filename , & coredump_node_fd , & coredump_fd , & coredump_size ) ;
if ( r < 0 )
/* Skip whole core dumping part */
goto log ;
/* If we don't want to keep the coredump on disk, remove it now, as later on we will lack the privileges for
* it . However , we keep the fd to it , so that we can still process it and log it . */
r = maybe_remove_external_coredump ( filename , coredump_size ) ;
if ( r < 0 )
return r ;
if ( r = = 0 ) {
const char * coredump_filename ;
coredump_filename = strjoina ( " COREDUMP_FILENAME= " , filename ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , coredump_filename ) ;
2012-01-14 00:37:35 +01:00
}
2016-02-08 21:16:08 +01:00
/* Vacuum again, but exclude the coredump we just created */
( void ) coredump_vacuum ( coredump_node_fd > = 0 ? coredump_node_fd : coredump_fd , arg_keep_free , arg_max_use ) ;
2014-06-27 19:09:22 +02:00
2016-02-08 21:16:08 +01:00
/* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the coredump
* memory under the user ' s uid . This also ensures that the credentials journald will see are the ones of the
* coredumping user , thus making sure the user gets access to the core dump . Let ' s also get rid of all
* capabilities , if we run as root , we won ' t need them anymore . */
r = change_uid_gid ( context ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to drop privileges: %m " ) ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
# ifdef HAVE_ELFUTILS
/* Try to get a strack trace if we can */
if ( coredump_size < = arg_process_size_max ) {
_cleanup_free_ char * stacktrace = NULL ;
r = coredump_make_stack_trace ( coredump_fd , context [ CONTEXT_EXE ] , & stacktrace ) ;
if ( r > = 0 )
core_message = strjoin ( " MESSAGE=Process " , context [ CONTEXT_PID ] , " ( " , context [ CONTEXT_COMM ] , " ) of user " , context [ CONTEXT_UID ] , " dumped core. \n \n " , stacktrace , NULL ) ;
else if ( r = = - EINVAL )
log_warning ( " Failed to generate stack trace: %s " , dwfl_errmsg ( dwfl_errno ( ) ) ) ;
else
log_warning_errno ( r , " Failed to generate stack trace: %m " ) ;
2014-06-18 22:02:18 +02:00
}
2012-02-02 01:22:49 +01:00
2016-02-08 21:16:08 +01:00
if ( ! core_message )
# endif
log :
core_message = strjoin ( " MESSAGE=Process " , context [ CONTEXT_PID ] , " ( " , context [ CONTEXT_COMM ] , " ) of user " , context [ CONTEXT_UID ] , " dumped core. " , NULL ) ;
if ( core_message )
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_message ) ;
/* Optionally store the entire coredump in the journal */
if ( IN_SET ( arg_storage , COREDUMP_STORAGE_JOURNAL , COREDUMP_STORAGE_BOTH ) & &
coredump_size < = arg_journal_size_max ) {
size_t sz = 0 ;
/* Store the coredump itself in the journal */
r = allocate_journal_field ( coredump_fd , ( size_t ) coredump_size , & coredump_data , & sz ) ;
if ( r > = 0 ) {
iovec [ n_iovec ] . iov_base = coredump_data ;
iovec [ n_iovec ] . iov_len = sz ;
n_iovec + + ;
}
2012-01-14 00:37:35 +01:00
}
2016-02-08 21:16:08 +01:00
assert ( n_iovec < = n_iovec_allocated ) ;
r = sd_journal_sendv ( iovec , n_iovec ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to log coredump: %m " ) ;
return 0 ;
}
static void map_context_fields ( const struct iovec * iovec , const char * context [ ] ) {
static const char * const context_field_names [ _CONTEXT_MAX ] = {
[ CONTEXT_PID ] = " COREDUMP_PID= " ,
[ CONTEXT_UID ] = " COREDUMP_UID= " ,
[ CONTEXT_GID ] = " COREDUMP_GID= " ,
[ CONTEXT_SIGNAL ] = " COREDUMP_SIGNAL= " ,
[ CONTEXT_TIMESTAMP ] = " COREDUMP_TIMESTAMP= " ,
[ CONTEXT_COMM ] = " COREDUMP_COMM= " ,
[ CONTEXT_EXE ] = " COREDUMP_EXE= " ,
2016-02-08 22:08:49 +01:00
[ CONTEXT_RLIMIT ] = " COREDUMP_RLIMIT= " ,
2016-02-08 21:16:08 +01:00
} ;
unsigned i ;
assert ( iovec ) ;
assert ( context ) ;
for ( i = 0 ; i < _CONTEXT_MAX ; i + + ) {
size_t l ;
l = strlen ( context_field_names [ i ] ) ;
if ( iovec - > iov_len < l )
continue ;
if ( memcmp ( iovec - > iov_base , context_field_names [ i ] , l ) ! = 0 )
continue ;
/* Note that these strings are NUL terminated, because we made sure that a trailing NUL byte is in the
* buffer , though not included in the iov_len count . ( see below ) */
context [ i ] = ( char * ) iovec - > iov_base + l ;
break ;
}
}
static int process_socket ( int fd ) {
_cleanup_close_ int coredump_fd = - 1 ;
struct iovec * iovec = NULL ;
size_t n_iovec = 0 , n_iovec_allocated = 0 , i ;
const char * context [ _CONTEXT_MAX ] = { } ;
int r ;
assert ( fd > = 0 ) ;
log_set_target ( LOG_TARGET_AUTO ) ;
log_parse_environment ( ) ;
log_open ( ) ;
for ( ; ; ) {
union {
struct cmsghdr cmsghdr ;
uint8_t buf [ CMSG_SPACE ( sizeof ( int ) ) ] ;
} control = { } ;
struct msghdr mh = {
. msg_control = & control ,
. msg_controllen = sizeof ( control ) ,
. msg_iovlen = 1 ,
} ;
ssize_t n ;
int l ;
if ( ! GREEDY_REALLOC ( iovec , n_iovec_allocated , n_iovec + 3 ) ) {
r = log_oom ( ) ;
goto finish ;
}
if ( ioctl ( fd , FIONREAD , & l ) < 0 ) {
r = log_error_errno ( errno , " FIONREAD failed: %m " ) ;
goto finish ;
}
assert ( l > = 0 ) ;
iovec [ n_iovec ] . iov_len = l ;
iovec [ n_iovec ] . iov_base = malloc ( l + 1 ) ;
if ( ! iovec [ n_iovec ] . iov_base ) {
r = log_oom ( ) ;
goto finish ;
}
mh . msg_iov = iovec + n_iovec ;
n = recvmsg ( fd , & mh , MSG_NOSIGNAL | MSG_CMSG_CLOEXEC ) ;
if ( n < 0 ) {
free ( iovec [ n_iovec ] . iov_base ) ;
r = log_error_errno ( errno , " Failed to receive datagram: %m " ) ;
goto finish ;
}
if ( n = = 0 ) {
struct cmsghdr * cmsg , * found = NULL ;
/* The final zero-length datagram carries the file descriptor and tells us that we're done. */
free ( iovec [ n_iovec ] . iov_base ) ;
CMSG_FOREACH ( cmsg , & mh ) {
if ( cmsg - > cmsg_level = = SOL_SOCKET & &
cmsg - > cmsg_type = = SCM_RIGHTS & &
cmsg - > cmsg_len = = CMSG_LEN ( sizeof ( int ) ) ) {
assert ( ! found ) ;
found = cmsg ;
}
}
if ( ! found ) {
log_error ( " Coredump file descriptor missing. " ) ;
r = - EBADMSG ;
goto finish ;
}
assert ( coredump_fd < 0 ) ;
coredump_fd = * ( int * ) CMSG_DATA ( found ) ;
break ;
}
/* Add trailing NUL byte, in case these are strings */
( ( char * ) iovec [ n_iovec ] . iov_base ) [ n ] = 0 ;
iovec [ n_iovec ] . iov_len = ( size_t ) n ;
cmsg_close_all ( & mh ) ;
map_context_fields ( iovec + n_iovec , context ) ;
n_iovec + + ;
}
if ( ! GREEDY_REALLOC ( iovec , n_iovec_allocated , n_iovec + 3 ) ) {
r = log_oom ( ) ;
2014-06-18 22:02:18 +02:00
goto finish ;
}
2016-02-08 21:16:08 +01:00
/* Make sure we we got all data we really need */
assert ( context [ CONTEXT_PID ] ) ;
assert ( context [ CONTEXT_UID ] ) ;
assert ( context [ CONTEXT_GID ] ) ;
assert ( context [ CONTEXT_SIGNAL ] ) ;
assert ( context [ CONTEXT_TIMESTAMP ] ) ;
2016-02-08 22:08:49 +01:00
assert ( context [ CONTEXT_RLIMIT ] ) ;
2016-02-08 21:16:08 +01:00
assert ( context [ CONTEXT_COMM ] ) ;
assert ( coredump_fd > = 0 ) ;
r = submit_coredump ( context , iovec , n_iovec_allocated , n_iovec , coredump_fd ) ;
finish :
for ( i = 0 ; i < n_iovec ; i + + )
free ( iovec [ i ] . iov_base ) ;
free ( iovec ) ;
return r ;
}
static int send_iovec ( const struct iovec iovec [ ] , size_t n_iovec , int input_fd ) {
static const union sockaddr_union sa = {
. un . sun_family = AF_UNIX ,
. un . sun_path = " /run/systemd/coredump " ,
} ;
_cleanup_close_ int fd = - 1 ;
size_t i ;
int r ;
assert ( iovec | | n_iovec < = 0 ) ;
assert ( input_fd > = 0 ) ;
fd = socket ( AF_UNIX , SOCK_SEQPACKET | SOCK_CLOEXEC , 0 ) ;
if ( fd < 0 )
return log_error_errno ( errno , " Failed to create coredump socket: %m " ) ;
if ( connect ( fd , & sa . sa , offsetof ( union sockaddr_union , un . sun_path ) + strlen ( sa . un . sun_path ) ) < 0 )
return log_error_errno ( errno , " Failed to connect to coredump service: %m " ) ;
for ( i = 0 ; i < n_iovec ; i + + ) {
ssize_t n ;
assert ( iovec [ i ] . iov_len > 0 ) ;
n = send ( fd , iovec [ i ] . iov_base , iovec [ i ] . iov_len , MSG_NOSIGNAL ) ;
if ( n < 0 )
return log_error_errno ( errno , " Failed to send coredump datagram: %m " ) ;
2014-06-24 02:18:16 +02:00
}
2016-02-08 21:16:08 +01:00
r = send_one_fd ( fd , input_fd , 0 ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to send coredump fd: %m " ) ;
2014-06-24 02:18:16 +02:00
2016-02-08 21:16:08 +01:00
return 0 ;
}
2014-06-24 02:18:16 +02:00
2016-02-08 21:16:08 +01:00
static int process_journald_crash ( const char * context [ ] , int input_fd ) {
_cleanup_close_ int coredump_fd = - 1 , coredump_node_fd = - 1 ;
_cleanup_free_ char * filename = NULL ;
uint64_t coredump_size ;
int r ;
2012-02-02 01:22:49 +01:00
2016-02-08 21:16:08 +01:00
assert ( context ) ;
assert ( input_fd > = 0 ) ;
2012-02-02 01:22:49 +01:00
2016-02-08 21:16:08 +01:00
/* If we are journald, we cut things short, don't write to the journal, but still create a coredump. */
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
if ( arg_storage ! = COREDUMP_STORAGE_NONE )
arg_storage = COREDUMP_STORAGE_EXTERNAL ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
r = save_external_coredump ( context , input_fd , & filename , & coredump_node_fd , & coredump_fd , & coredump_size ) ;
if ( r < 0 )
return r ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
r = maybe_remove_external_coredump ( filename , coredump_size ) ;
if ( r < 0 )
return r ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
log_info ( " Detected coredump of the journal daemon itself, diverted to %s. " , filename ) ;
return 0 ;
}
static int process_kernel ( int argc , char * argv [ ] ) {
/* The small core field we allocate on the stack, to keep things simple */
char
* core_pid = NULL , * core_uid = NULL , * core_gid = NULL , * core_signal = NULL ,
* core_session = NULL , * core_exe = NULL , * core_comm = NULL , * core_cmdline = NULL ,
* core_cgroup = NULL , * core_cwd = NULL , * core_root = NULL , * core_unit = NULL ,
2016-02-08 22:08:49 +01:00
* core_user_unit = NULL , * core_slice = NULL , * core_timestamp = NULL , * core_rlimit = NULL ;
2016-02-08 21:16:08 +01:00
/* The larger ones we allocate on the heap */
_cleanup_free_ char
* core_owner_uid = NULL , * core_open_fds = NULL , * core_proc_status = NULL ,
* core_proc_maps = NULL , * core_proc_limits = NULL , * core_proc_cgroup = NULL , * core_environ = NULL ;
_cleanup_free_ char * exe = NULL , * comm = NULL ;
const char * context [ _CONTEXT_MAX ] ;
2016-02-08 22:08:49 +01:00
struct iovec iovec [ 25 ] ;
2016-02-08 21:16:08 +01:00
size_t n_iovec = 0 ;
uid_t owner_uid ;
const char * p ;
pid_t pid ;
char * t ;
int r ;
if ( argc < CONTEXT_COMM + 1 ) {
log_error ( " Not enough arguments passed from kernel (%i, expected %i). " , argc - 1 , CONTEXT_COMM + 1 - 1 ) ;
return - EINVAL ;
}
r = parse_pid ( argv [ CONTEXT_PID + 1 ] , & pid ) ;
if ( r < 0 )
return log_error_errno ( r , " Failed to parse PID. " ) ;
r = get_process_comm ( pid , & comm ) ;
if ( r < 0 ) {
log_warning_errno ( r , " Failed to get COMM, falling back to the command line: %m " ) ;
comm = strv_join ( argv + CONTEXT_COMM + 1 , " " ) ;
if ( ! comm )
return log_oom ( ) ;
}
r = get_process_exe ( pid , & exe ) ;
if ( r < 0 )
log_warning_errno ( r , " Failed to get EXE, ignoring: %m " ) ;
context [ CONTEXT_PID ] = argv [ CONTEXT_PID + 1 ] ;
context [ CONTEXT_UID ] = argv [ CONTEXT_UID + 1 ] ;
context [ CONTEXT_GID ] = argv [ CONTEXT_GID + 1 ] ;
context [ CONTEXT_SIGNAL ] = argv [ CONTEXT_SIGNAL + 1 ] ;
context [ CONTEXT_TIMESTAMP ] = argv [ CONTEXT_TIMESTAMP + 1 ] ;
2016-02-08 22:08:49 +01:00
context [ CONTEXT_RLIMIT ] = argv [ CONTEXT_RLIMIT + 1 ] ;
2016-02-08 21:16:08 +01:00
context [ CONTEXT_COMM ] = comm ;
context [ CONTEXT_EXE ] = exe ;
if ( cg_pid_get_unit ( pid , & t ) > = 0 ) {
if ( streq ( t , SPECIAL_JOURNALD_SERVICE ) ) {
free ( t ) ;
return process_journald_crash ( context , STDIN_FILENO ) ;
2012-02-02 01:22:49 +01:00
}
2015-02-03 02:05:59 +01:00
core_unit = strjoina ( " COREDUMP_UNIT= " , t ) ;
2014-11-28 20:29:24 +01:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_unit ) ;
2014-11-28 20:29:24 +01:00
}
2012-02-02 01:22:49 +01:00
2016-02-08 21:16:08 +01:00
/* OK, now we know it's not the journal, hence we can make use of it now. */
2012-02-02 01:22:49 +01:00
log_set_target ( LOG_TARGET_JOURNAL_OR_KMSG ) ;
log_open ( ) ;
2016-02-08 21:16:08 +01:00
if ( cg_pid_get_user_unit ( pid , & t ) > = 0 ) {
core_user_unit = strjoina ( " COREDUMP_USER_UNIT= " , t ) ;
free ( t ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_user_unit ) ;
}
core_pid = strjoina ( " COREDUMP_PID= " , context [ CONTEXT_PID ] ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_pid ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
core_uid = strjoina ( " COREDUMP_UID= " , context [ CONTEXT_UID ] ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_uid ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
core_gid = strjoina ( " COREDUMP_GID= " , context [ CONTEXT_GID ] ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_gid ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
core_signal = strjoina ( " COREDUMP_SIGNAL= " , context [ CONTEXT_SIGNAL ] ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_signal ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 22:08:49 +01:00
core_rlimit = strjoina ( " COREDUMP_RLIMIT= " , context [ CONTEXT_RLIMIT ] ) ;
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_rlimit ) ;
2012-01-14 00:37:35 +01:00
if ( sd_pid_get_session ( pid , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_session = strjoina ( " COREDUMP_SESSION= " , t ) ;
2012-01-14 00:37:35 +01:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_session ) ;
2012-01-14 00:37:35 +01:00
}
2014-06-18 23:55:36 +02:00
if ( sd_pid_get_owner_uid ( pid , & owner_uid ) > = 0 ) {
2016-02-08 21:16:08 +01:00
r = asprintf ( & core_owner_uid , " COREDUMP_OWNER_UID= " UID_FMT , owner_uid ) ;
2014-07-25 15:38:31 +02:00
if ( r > 0 )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_owner_uid ) ;
2014-06-18 23:55:36 +02:00
}
if ( sd_pid_get_slice ( pid , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_slice = strjoina ( " COREDUMP_SLICE= " , t ) ;
2014-06-18 23:55:36 +02:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_slice ) ;
2014-06-18 23:55:36 +02:00
}
2014-06-24 02:18:16 +02:00
if ( comm ) {
2015-02-03 02:05:59 +01:00
core_comm = strjoina ( " COREDUMP_COMM= " , comm ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_comm ) ;
2014-06-24 02:18:16 +02:00
}
if ( exe ) {
2015-02-03 02:05:59 +01:00
core_exe = strjoina ( " COREDUMP_EXE= " , exe ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_exe ) ;
2012-01-14 00:37:35 +01:00
}
2013-01-14 18:16:50 +01:00
if ( get_process_cmdline ( pid , 0 , false , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_cmdline = strjoina ( " COREDUMP_CMDLINE= " , t ) ;
2012-01-14 00:37:35 +01:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_cmdline ) ;
2012-01-14 00:37:35 +01:00
}
2014-06-18 23:55:36 +02:00
if ( cg_pid_get_path_shifted ( pid , NULL , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_cgroup = strjoina ( " COREDUMP_CGROUP= " , t ) ;
2014-06-18 23:55:36 +02:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_cgroup ) ;
2014-06-18 23:55:36 +02:00
}
2014-11-25 07:37:48 +01:00
if ( compose_open_fds ( pid , & t ) > = 0 ) {
core_open_fds = strappend ( " COREDUMP_OPEN_FDS= " , t ) ;
free ( t ) ;
if ( core_open_fds )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_open_fds ) ;
2014-11-25 07:37:48 +01:00
}
p = procfs_file_alloca ( pid , " status " ) ;
if ( read_full_file ( p , & t , NULL ) > = 0 ) {
core_proc_status = strappend ( " COREDUMP_PROC_STATUS= " , t ) ;
free ( t ) ;
if ( core_proc_status )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_proc_status ) ;
2014-11-25 07:37:48 +01:00
}
p = procfs_file_alloca ( pid , " maps " ) ;
if ( read_full_file ( p , & t , NULL ) > = 0 ) {
core_proc_maps = strappend ( " COREDUMP_PROC_MAPS= " , t ) ;
free ( t ) ;
if ( core_proc_maps )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_proc_maps ) ;
2014-11-25 07:37:48 +01:00
}
p = procfs_file_alloca ( pid , " limits " ) ;
if ( read_full_file ( p , & t , NULL ) > = 0 ) {
core_proc_limits = strappend ( " COREDUMP_PROC_LIMITS= " , t ) ;
free ( t ) ;
if ( core_proc_limits )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_proc_limits ) ;
2014-11-25 07:37:48 +01:00
}
p = procfs_file_alloca ( pid , " cgroup " ) ;
if ( read_full_file ( p , & t , NULL ) > = 0 ) {
core_proc_cgroup = strappend ( " COREDUMP_PROC_CGROUP= " , t ) ;
free ( t ) ;
if ( core_proc_cgroup )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_proc_cgroup ) ;
2014-11-25 07:37:48 +01:00
}
if ( get_process_cwd ( pid , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_cwd = strjoina ( " COREDUMP_CWD= " , t ) ;
2014-11-25 07:37:48 +01:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_cwd ) ;
2014-11-25 07:37:48 +01:00
}
if ( get_process_root ( pid , & t ) > = 0 ) {
2015-02-03 02:05:59 +01:00
core_root = strjoina ( " COREDUMP_ROOT= " , t ) ;
2014-11-25 07:37:48 +01:00
free ( t ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_root ) ;
2014-11-25 07:37:48 +01:00
}
if ( get_process_environ ( pid , & t ) > = 0 ) {
core_environ = strappend ( " COREDUMP_ENVIRON= " , t ) ;
free ( t ) ;
if ( core_environ )
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_environ ) ;
2014-11-25 07:37:48 +01:00
}
2016-04-09 20:04:09 +02:00
core_timestamp = strjoina ( " COREDUMP_TIMESTAMP= " , context [ CONTEXT_TIMESTAMP ] , " 000000 " ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , core_timestamp ) ;
2012-01-14 00:37:35 +01:00
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , " MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1 " ) ;
2016-01-26 13:48:57 +01:00
assert_cc ( 2 = = LOG_CRIT ) ;
2016-02-08 21:16:08 +01:00
IOVEC_SET_STRING ( iovec [ n_iovec + + ] , " PRIORITY=2 " ) ;
2014-06-27 18:57:24 +02:00
2016-02-08 21:16:08 +01:00
assert ( n_iovec < = ELEMENTSOF ( iovec ) ) ;
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
return send_iovec ( iovec , n_iovec , STDIN_FILENO ) ;
}
2014-06-18 22:02:18 +02:00
2016-02-08 21:16:08 +01:00
int main ( int argc , char * argv [ ] ) {
int r ;
2012-01-14 03:37:32 +01:00
2016-02-08 21:16:08 +01:00
/* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not
* log to then . */
2014-06-19 12:07:12 +02:00
2016-02-08 21:16:08 +01:00
log_set_target ( LOG_TARGET_KMSG ) ;
log_open ( ) ;
2014-06-19 12:07:12 +02:00
2016-02-08 21:16:08 +01:00
/* Make sure we never enter a loop */
( void ) prctl ( PR_SET_DUMPABLE , 0 ) ;
2014-06-19 12:07:12 +02:00
2016-02-08 21:16:08 +01:00
/* Ignore all parse errors */
( void ) parse_config ( ) ;
2012-01-14 03:37:32 +01:00
2016-02-08 21:16:08 +01:00
log_debug ( " Selected storage '%s'. " , coredump_storage_to_string ( arg_storage ) ) ;
log_debug ( " Selected compression %s. " , yes_no ( arg_compress ) ) ;
2012-01-14 03:37:32 +01:00
2016-02-08 21:16:08 +01:00
r = sd_listen_fds ( false ) ;
if ( r < 0 ) {
log_error_errno ( r , " Failed to determine number of file descriptor: %m " ) ;
goto finish ;
2012-01-14 03:37:32 +01:00
}
2016-02-08 21:16:08 +01:00
/* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as
* coredump handler */
if ( r = = 0 )
r = process_kernel ( argc , argv ) ;
else if ( r = = 1 )
r = process_socket ( SD_LISTEN_FDS_START ) ;
else {
log_error ( " Received unexpected number of file descriptors. " ) ;
r = - EINVAL ;
}
2012-01-14 00:37:35 +01:00
finish :
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
}