2012-10-19 14:29:46 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2012 Zbigniew Jędrzejewski - Szmek
systemd is free software ; you can redistribute it and / or modify it
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
( 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2012-11-12 23:16:07 +04:00
# include <locale.h>
2012-10-19 14:29:46 +04:00
# include <stdio.h>
# include <string.h>
# include <getopt.h>
2012-10-27 03:19:47 +04:00
# include <fcntl.h>
# include <unistd.h>
2012-10-19 14:29:46 +04:00
2014-07-31 12:15:29 +04:00
# include "systemd/sd-journal.h"
2012-10-19 14:29:46 +04:00
# include "build.h"
# include "set.h"
# include "util.h"
# include "log.h"
# include "path-util.h"
# include "pager.h"
2013-03-18 07:36:25 +04:00
# include "macro.h"
2013-04-16 07:25:57 +04:00
# include "journal-internal.h"
2014-06-19 01:05:15 +04:00
# include "copy.h"
2014-06-26 07:38:49 +04:00
# include "compress.h"
2012-10-19 14:29:46 +04:00
static enum {
ACTION_NONE ,
2014-06-19 01:34:59 +04:00
ACTION_INFO ,
2012-10-19 14:29:46 +04:00
ACTION_LIST ,
ACTION_DUMP ,
2012-10-27 03:19:47 +04:00
ACTION_GDB ,
2012-10-19 14:29:46 +04:00
} arg_action = ACTION_LIST ;
2014-06-19 01:34:59 +04:00
static const char * arg_field = NULL ;
2012-10-26 22:23:28 +04:00
static int arg_no_pager = false ;
2012-10-30 12:45:19 +04:00
static int arg_no_legend = false ;
2014-06-19 14:24:00 +04:00
static int arg_one = false ;
static FILE * output = NULL ;
2012-10-19 14:29:46 +04:00
static Set * new_matches ( void ) {
Set * set ;
char * tmp ;
int r ;
2014-08-13 03:00:18 +04:00
set = set_new ( NULL ) ;
2012-10-19 14:29:46 +04:00
if ( ! set ) {
log_oom ( ) ;
return NULL ;
}
tmp = strdup ( " MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1 " ) ;
if ( ! tmp ) {
log_oom ( ) ;
2012-10-26 16:11:37 +04:00
set_free ( set ) ;
2012-10-19 14:29:46 +04:00
return NULL ;
}
2013-04-23 07:12:15 +04:00
r = set_consume ( set , tmp ) ;
2012-10-19 14:29:46 +04:00
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " failed to add to set: %m " ) ;
2012-10-26 16:11:37 +04:00
set_free ( set ) ;
2012-10-19 14:29:46 +04:00
return NULL ;
}
return set ;
}
static int add_match ( Set * set , const char * match ) {
int r = - ENOMEM ;
unsigned pid ;
const char * prefix ;
char * pattern = NULL ;
2013-04-18 11:11:22 +04:00
_cleanup_free_ char * p = NULL ;
2012-10-19 14:29:46 +04:00
if ( strchr ( match , ' = ' ) )
prefix = " " ;
else if ( strchr ( match , ' / ' ) ) {
p = path_make_absolute_cwd ( match ) ;
if ( ! p )
goto fail ;
match = p ;
prefix = " COREDUMP_EXE= " ;
}
else if ( safe_atou ( match , & pid ) = = 0 )
prefix = " COREDUMP_PID= " ;
else
prefix = " COREDUMP_COMM= " ;
pattern = strjoin ( prefix , match , NULL ) ;
if ( ! pattern )
goto fail ;
2013-04-23 07:12:15 +04:00
log_debug ( " Adding pattern: %s " , pattern ) ;
2014-07-18 22:01:13 +04:00
r = set_consume ( set , pattern ) ;
2012-10-19 14:29:46 +04:00
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to add pattern: %m " ) ;
2012-10-19 14:29:46 +04:00
goto fail ;
}
return 0 ;
fail :
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to add match: %m " ) ;
2012-10-19 14:29:46 +04:00
return r ;
}
2014-08-02 19:12:21 +04:00
static void help ( void ) {
printf ( " %s [OPTIONS...] \n \n "
" List or retrieve coredumps from the journal. \n \n "
" Flags: \n "
" -h --help Show this help \n "
" --version Print version string \n "
" --no-pager Do not pipe output into a pager \n "
" --no-legend Do not print the column headers. \n "
" -1 Show information about most recent entry only \n "
" -F --field=FIELD List all values a certain field takes \n "
" -o --output=FILE Write output to FILE \n \n "
" Commands: \n "
" list [MATCHES...] List available coredumps (default) \n "
" info [MATCHES...] Show detailed information about one or more coredumps \n "
" dump [MATCHES...] Print first matching coredump to stdout \n "
" gdb [MATCHES...] Start gdb for the first matching coredump \n "
, program_invocation_short_name ) ;
}
2013-03-18 07:36:25 +04:00
static int parse_argv ( int argc , char * argv [ ] , Set * matches ) {
2012-10-19 14:29:46 +04:00
enum {
ARG_VERSION = 0x100 ,
ARG_NO_PAGER ,
2012-10-30 12:45:19 +04:00
ARG_NO_LEGEND ,
2012-10-19 14:29:46 +04:00
} ;
int r , c ;
static const struct option options [ ] = {
2012-10-30 12:35:53 +04:00
{ " help " , no_argument , NULL , ' h ' } ,
{ " version " , no_argument , NULL , ARG_VERSION } ,
{ " no-pager " , no_argument , NULL , ARG_NO_PAGER } ,
2012-10-30 12:45:19 +04:00
{ " no-legend " , no_argument , NULL , ARG_NO_LEGEND } ,
2012-10-30 12:35:53 +04:00
{ " output " , required_argument , NULL , ' o ' } ,
2012-10-30 14:15:24 +04:00
{ " field " , required_argument , NULL , ' F ' } ,
2013-11-06 21:28:39 +04:00
{ }
2012-10-19 14:29:46 +04:00
} ;
assert ( argc > = 0 ) ;
assert ( argv ) ;
2014-06-19 14:24:00 +04:00
while ( ( c = getopt_long ( argc , argv , " ho:F:1 " , options , NULL ) ) > = 0 )
2012-10-19 14:29:46 +04:00
switch ( c ) {
2013-11-06 21:28:39 +04:00
2012-10-19 14:29:46 +04:00
case ' h ' :
arg_action = ACTION_NONE ;
2014-08-02 19:12:21 +04:00
help ( ) ;
return 0 ;
2012-10-19 14:29:46 +04:00
case ARG_VERSION :
2013-11-06 21:28:39 +04:00
arg_action = ACTION_NONE ;
2012-10-19 14:29:46 +04:00
puts ( PACKAGE_STRING ) ;
puts ( SYSTEMD_FEATURES ) ;
return 0 ;
case ARG_NO_PAGER :
arg_no_pager = true ;
break ;
2012-10-30 12:45:19 +04:00
case ARG_NO_LEGEND :
arg_no_legend = true ;
break ;
2012-10-19 14:29:46 +04:00
case ' o ' :
if ( output ) {
log_error ( " cannot set output more than once " ) ;
return - EINVAL ;
}
output = fopen ( optarg , " we " ) ;
if ( ! output ) {
log_error ( " writing to '%s': %m " , optarg ) ;
return - errno ;
}
break ;
2012-10-30 12:35:53 +04:00
2012-10-30 14:15:24 +04:00
case ' F ' :
2014-06-19 01:05:15 +04:00
if ( arg_field ) {
2012-10-30 14:15:24 +04:00
log_error ( " cannot use --field/-F more than once " ) ;
return - EINVAL ;
}
2014-06-19 01:05:15 +04:00
arg_field = optarg ;
2012-10-30 14:15:24 +04:00
break ;
2014-06-19 14:24:00 +04:00
case ' 1 ' :
arg_one = true ;
break ;
2012-10-30 12:35:53 +04:00
case ' ? ' :
return - EINVAL ;
2012-10-19 14:29:46 +04:00
default :
2013-11-06 21:28:39 +04:00
assert_not_reached ( " Unhandled option " ) ;
2012-10-19 14:29:46 +04:00
}
if ( optind < argc ) {
const char * cmd = argv [ optind + + ] ;
2013-12-04 01:27:45 +04:00
if ( streq ( cmd , " list " ) )
2012-10-19 14:29:46 +04:00
arg_action = ACTION_LIST ;
else if ( streq ( cmd , " dump " ) )
arg_action = ACTION_DUMP ;
2012-10-27 03:19:47 +04:00
else if ( streq ( cmd , " gdb " ) )
arg_action = ACTION_GDB ;
2014-06-19 01:34:59 +04:00
else if ( streq ( cmd , " info " ) )
arg_action = ACTION_INFO ;
2012-10-19 14:29:46 +04:00
else {
log_error ( " Unknown action '%s' " , cmd ) ;
return - EINVAL ;
}
}
2014-06-19 01:05:15 +04:00
if ( arg_field & & arg_action ! = ACTION_LIST ) {
2012-10-30 14:15:24 +04:00
log_error ( " Option --field/-F only makes sense with list " ) ;
return - EINVAL ;
}
2012-10-19 14:29:46 +04:00
while ( optind < argc ) {
r = add_match ( matches , argv [ optind ] ) ;
if ( r ! = 0 )
return r ;
optind + + ;
}
return 0 ;
}
2012-10-26 22:25:10 +04:00
static int retrieve ( const void * data ,
size_t len ,
const char * name ,
2014-06-19 01:05:15 +04:00
char * * var ) {
2012-10-19 14:29:46 +04:00
2012-10-30 14:15:24 +04:00
size_t ident ;
2014-06-19 01:05:15 +04:00
char * v ;
2012-10-26 22:25:10 +04:00
2012-10-30 14:15:24 +04:00
ident = strlen ( name ) + 1 ; /* name + "=" */
2012-10-26 22:25:10 +04:00
2012-10-30 14:15:24 +04:00
if ( len < ident )
2012-10-26 22:25:10 +04:00
return 0 ;
2012-10-19 14:29:46 +04:00
2012-10-30 14:15:24 +04:00
if ( memcmp ( data , name , ident - 1 ) ! = 0 )
2012-10-26 22:25:10 +04:00
return 0 ;
2012-10-30 14:15:24 +04:00
if ( ( ( const char * ) data ) [ ident - 1 ] ! = ' = ' )
2012-10-26 22:25:10 +04:00
return 0 ;
2012-10-19 14:29:46 +04:00
2014-06-19 01:05:15 +04:00
v = strndup ( ( const char * ) data + ident , len - ident ) ;
if ( ! v )
2012-10-19 14:29:46 +04:00
return log_oom ( ) ;
2014-06-19 01:05:15 +04:00
free ( * var ) ;
* var = v ;
2012-10-19 14:29:46 +04:00
return 0 ;
}
2012-10-30 14:15:24 +04:00
static void print_field ( FILE * file , sd_journal * j ) {
2014-06-19 01:05:15 +04:00
_cleanup_free_ char * value = NULL ;
2012-10-30 14:15:24 +04:00
const void * d ;
size_t l ;
2014-06-19 01:34:59 +04:00
assert ( file ) ;
assert ( j ) ;
2014-06-19 01:05:15 +04:00
assert ( arg_field ) ;
2012-10-30 14:15:24 +04:00
SD_JOURNAL_FOREACH_DATA ( j , d , l )
2014-06-19 01:05:15 +04:00
retrieve ( d , l , arg_field , & value ) ;
2012-10-30 14:15:24 +04:00
if ( value )
fprintf ( file , " %s \n " , value ) ;
}
2014-06-19 01:34:59 +04:00
static int print_list ( FILE * file , sd_journal * j , int had_legend ) {
2014-06-19 01:05:15 +04:00
_cleanup_free_ char
2012-10-19 14:29:46 +04:00
* pid = NULL , * uid = NULL , * gid = NULL ,
2014-06-26 07:38:49 +04:00
* sgnl = NULL , * exe = NULL , * comm = NULL , * cmdline = NULL ,
* filename = NULL ;
2012-10-26 22:25:10 +04:00
const void * d ;
size_t l ;
2012-10-26 22:34:39 +04:00
usec_t t ;
char buf [ FORMAT_TIMESTAMP_MAX ] ;
int r ;
2014-06-26 07:38:49 +04:00
bool present ;
2012-10-26 22:25:10 +04:00
2014-06-19 01:34:59 +04:00
assert ( file ) ;
assert ( j ) ;
2012-10-26 22:25:10 +04:00
SD_JOURNAL_FOREACH_DATA ( j , d , l ) {
retrieve ( d , l , " COREDUMP_PID " , & pid ) ;
retrieve ( d , l , " COREDUMP_UID " , & uid ) ;
retrieve ( d , l , " COREDUMP_GID " , & gid ) ;
retrieve ( d , l , " COREDUMP_SIGNAL " , & sgnl ) ;
retrieve ( d , l , " COREDUMP_EXE " , & exe ) ;
2014-06-19 01:05:15 +04:00
retrieve ( d , l , " COREDUMP_COMM " , & comm ) ;
retrieve ( d , l , " COREDUMP_CMDLINE " , & cmdline ) ;
2014-06-26 07:38:49 +04:00
retrieve ( d , l , " COREDUMP_FILENAME " , & filename ) ;
2012-10-26 22:25:10 +04:00
}
2012-10-19 14:29:46 +04:00
2014-06-26 07:38:49 +04:00
if ( ! pid & & ! uid & & ! gid & & ! sgnl & & ! exe & & ! comm & & ! cmdline & & ! filename ) {
2012-10-26 22:34:39 +04:00
log_warning ( " Empty coredump log entry " ) ;
return - EINVAL ;
}
r = sd_journal_get_realtime_usec ( j , & t ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to get realtime timestamp: %m " ) ;
2012-10-26 22:34:39 +04:00
return r ;
2012-10-19 14:29:46 +04:00
}
2012-10-26 22:34:39 +04:00
format_timestamp ( buf , sizeof ( buf ) , t ) ;
2014-06-26 07:38:49 +04:00
present = filename & & access ( filename , F_OK ) = = 0 ;
2012-10-26 22:34:39 +04:00
2012-10-30 12:45:19 +04:00
if ( ! had_legend & & ! arg_no_legend )
2014-06-26 07:38:49 +04:00
fprintf ( file , " %-*s %*s %*s %*s %*s %*s %s \n " ,
2014-06-19 19:29:39 +04:00
FORMAT_TIMESTAMP_WIDTH , " TIME " ,
2012-10-19 14:29:46 +04:00
6 , " PID " ,
5 , " UID " ,
5 , " GID " ,
2012-10-26 22:34:39 +04:00
3 , " SIG " ,
2014-06-26 07:38:49 +04:00
1 , " PRESENT " ,
2012-10-26 22:34:39 +04:00
" EXE " ) ;
2012-10-19 14:29:46 +04:00
2014-06-26 07:38:49 +04:00
fprintf ( file , " %-*s %*s %*s %*s %*s %*s %s \n " ,
2014-06-19 19:29:39 +04:00
FORMAT_TIMESTAMP_WIDTH , buf ,
2014-06-19 01:05:15 +04:00
6 , strna ( pid ) ,
5 , strna ( uid ) ,
5 , strna ( gid ) ,
3 , strna ( sgnl ) ,
2014-06-26 07:38:49 +04:00
1 , present ? " * " : " " ,
2014-06-19 01:05:15 +04:00
strna ( exe ? : ( comm ? : cmdline ) ) ) ;
2012-10-26 22:34:39 +04:00
return 0 ;
2012-10-19 14:29:46 +04:00
}
2014-06-19 01:34:59 +04:00
static int print_info ( FILE * file , sd_journal * j , bool need_space ) {
_cleanup_free_ char
* pid = NULL , * uid = NULL , * gid = NULL ,
* sgnl = NULL , * exe = NULL , * comm = NULL , * cmdline = NULL ,
* unit = NULL , * user_unit = NULL , * session = NULL ,
2014-06-19 01:55:36 +04:00
* boot_id = NULL , * machine_id = NULL , * hostname = NULL ,
2014-06-26 07:38:49 +04:00
* slice = NULL , * cgroup = NULL , * owner_uid = NULL ,
* message = NULL , * timestamp = NULL , * filename = NULL ;
2014-06-19 01:34:59 +04:00
const void * d ;
size_t l ;
2014-06-23 17:51:09 +04:00
int r ;
2014-06-19 01:34:59 +04:00
assert ( file ) ;
assert ( j ) ;
SD_JOURNAL_FOREACH_DATA ( j , d , l ) {
retrieve ( d , l , " COREDUMP_PID " , & pid ) ;
retrieve ( d , l , " COREDUMP_UID " , & uid ) ;
retrieve ( d , l , " COREDUMP_GID " , & gid ) ;
retrieve ( d , l , " COREDUMP_SIGNAL " , & sgnl ) ;
retrieve ( d , l , " COREDUMP_EXE " , & exe ) ;
retrieve ( d , l , " COREDUMP_COMM " , & comm ) ;
retrieve ( d , l , " COREDUMP_CMDLINE " , & cmdline ) ;
retrieve ( d , l , " COREDUMP_UNIT " , & unit ) ;
retrieve ( d , l , " COREDUMP_USER_UNIT " , & user_unit ) ;
retrieve ( d , l , " COREDUMP_SESSION " , & session ) ;
2014-06-19 01:55:36 +04:00
retrieve ( d , l , " COREDUMP_OWNER_UID " , & owner_uid ) ;
retrieve ( d , l , " COREDUMP_SLICE " , & slice ) ;
retrieve ( d , l , " COREDUMP_CGROUP " , & cgroup ) ;
2014-06-23 17:51:09 +04:00
retrieve ( d , l , " COREDUMP_TIMESTAMP " , & timestamp ) ;
2014-06-26 07:38:49 +04:00
retrieve ( d , l , " COREDUMP_FILENAME " , & filename ) ;
2014-06-19 01:34:59 +04:00
retrieve ( d , l , " _BOOT_ID " , & boot_id ) ;
retrieve ( d , l , " _MACHINE_ID " , & machine_id ) ;
retrieve ( d , l , " _HOSTNAME " , & hostname ) ;
2014-06-19 14:07:12 +04:00
retrieve ( d , l , " MESSAGE " , & message ) ;
2014-06-19 01:34:59 +04:00
}
if ( need_space )
fputs ( " \n " , file ) ;
2014-06-23 17:55:24 +04:00
if ( comm )
fprintf ( file ,
" PID: %s%s%s (%s) \n " ,
ansi_highlight ( ) , strna ( pid ) , ansi_highlight_off ( ) , comm ) ;
else
fprintf ( file ,
" PID: %s%s%s \n " ,
ansi_highlight ( ) , strna ( pid ) , ansi_highlight_off ( ) ) ;
2014-06-19 01:55:36 +04:00
if ( uid ) {
uid_t n ;
if ( parse_uid ( uid , & n ) > = 0 ) {
_cleanup_free_ char * u = NULL ;
u = uid_to_name ( n ) ;
fprintf ( file ,
" UID: %s (%s) \n " ,
uid , u ) ;
} else {
fprintf ( file ,
" UID: %s \n " ,
uid ) ;
}
}
if ( gid ) {
gid_t n ;
if ( parse_gid ( gid , & n ) > = 0 ) {
_cleanup_free_ char * g = NULL ;
g = gid_to_name ( n ) ;
fprintf ( file ,
" GID: %s (%s) \n " ,
gid , g ) ;
} else {
fprintf ( file ,
" GID: %s \n " ,
gid ) ;
}
}
2014-06-19 01:34:59 +04:00
if ( sgnl ) {
int sig ;
if ( safe_atoi ( sgnl , & sig ) > = 0 )
fprintf ( file , " Signal: %s (%s) \n " , sgnl , signal_to_string ( sig ) ) ;
else
fprintf ( file , " Signal: %s \n " , sgnl ) ;
}
2014-06-23 17:51:09 +04:00
if ( timestamp ) {
usec_t u ;
r = safe_atou64 ( timestamp , & u ) ;
if ( r > = 0 ) {
char absolute [ FORMAT_TIMESTAMP_MAX ] , relative [ FORMAT_TIMESPAN_MAX ] ;
fprintf ( file ,
" Timestamp: %s (%s) \n " ,
format_timestamp ( absolute , sizeof ( absolute ) , u ) ,
format_timestamp_relative ( relative , sizeof ( relative ) , u ) ) ;
} else
fprintf ( file , " Timestamp: %s \n " , timestamp ) ;
}
2014-06-19 01:34:59 +04:00
if ( cmdline )
fprintf ( file , " Command Line: %s \n " , cmdline ) ;
2014-06-23 17:55:24 +04:00
if ( exe )
fprintf ( file , " Executable: %s%s%s \n " , ansi_highlight ( ) , exe , ansi_highlight_off ( ) ) ;
2014-06-19 01:55:36 +04:00
if ( cgroup )
fprintf ( file , " Control Group: %s \n " , cgroup ) ;
2014-06-19 01:34:59 +04:00
if ( unit )
fprintf ( file , " Unit: %s \n " , unit ) ;
if ( user_unit )
fprintf ( file , " User Unit: %s \n " , unit ) ;
2014-06-19 01:55:36 +04:00
if ( slice )
fprintf ( file , " Slice: %s \n " , slice ) ;
2014-06-19 01:34:59 +04:00
if ( session )
fprintf ( file , " Session: %s \n " , session ) ;
2014-06-19 01:55:36 +04:00
if ( owner_uid ) {
uid_t n ;
if ( parse_uid ( owner_uid , & n ) > = 0 ) {
_cleanup_free_ char * u = NULL ;
u = uid_to_name ( n ) ;
fprintf ( file ,
" Owner UID: %s (%s) \n " ,
owner_uid , u ) ;
} else {
fprintf ( file ,
" Owner UID: %s \n " ,
owner_uid ) ;
}
}
2014-06-19 01:34:59 +04:00
if ( boot_id )
fprintf ( file , " Boot ID: %s \n " , boot_id ) ;
if ( machine_id )
fprintf ( file , " Machine ID: %s \n " , machine_id ) ;
if ( hostname )
fprintf ( file , " Hostname: %s \n " , hostname ) ;
2014-06-26 07:38:49 +04:00
if ( filename & & access ( filename , F_OK ) = = 0 )
fprintf ( file , " Coredump: %s \n " , filename ) ;
2014-06-19 01:34:59 +04:00
2014-06-19 14:07:12 +04:00
if ( message ) {
_cleanup_free_ char * m = NULL ;
m = strreplace ( message , " \n " , " \n " ) ;
fprintf ( file , " Message: %s \n " , strstrip ( m ? : message ) ) ;
}
2014-06-19 01:34:59 +04:00
return 0 ;
}
2012-10-27 03:19:47 +04:00
static int focus ( sd_journal * j ) {
2012-10-19 14:29:46 +04:00
int r ;
r = sd_journal_seek_tail ( j ) ;
if ( r = = 0 )
r = sd_journal_previous ( j ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to search journal: %m " ) ;
2012-10-19 14:29:46 +04:00
return r ;
}
2012-10-26 22:25:10 +04:00
if ( r = = 0 ) {
2014-06-19 14:24:00 +04:00
log_error ( " No match found. " ) ;
2012-10-26 22:25:10 +04:00
return - ESRCH ;
}
2012-10-27 03:19:47 +04:00
return r ;
}
2012-10-19 14:29:46 +04:00
2014-06-19 14:24:00 +04:00
static void print_entry ( sd_journal * j , unsigned n_found ) {
assert ( j ) ;
if ( arg_action = = ACTION_INFO )
print_info ( stdout , j , n_found ) ;
else if ( arg_field )
print_field ( stdout , j ) ;
else
print_list ( stdout , j , n_found ) ;
}
static int dump_list ( sd_journal * j ) {
unsigned n_found = 0 ;
int r ;
assert ( j ) ;
/* The coredumps are likely to compressed, and for just
* listing them we don ' t need to decompress them , so let ' s
* pick a fairly low data threshold here */
sd_journal_set_data_threshold ( j , 4096 ) ;
if ( arg_one ) {
r = focus ( j ) ;
if ( r < 0 )
return r ;
print_entry ( j , 0 ) ;
} else {
SD_JOURNAL_FOREACH ( j )
print_entry ( j , n_found + + ) ;
if ( ! arg_field & & n_found < = 0 ) {
log_notice ( " No coredumps found. " ) ;
return - ESRCH ;
}
}
return 0 ;
}
2014-06-26 07:38:49 +04:00
static int save_core ( sd_journal * j , int fd , char * * path , bool * unlink_temp ) {
const char * data ;
_cleanup_free_ char * filename = NULL ;
size_t len ;
2012-10-27 03:19:47 +04:00
int r ;
2014-06-26 07:38:49 +04:00
assert ( ( fd > = 0 ) ! = ! ! path ) ;
assert ( ! ! path = = ! ! unlink_temp ) ;
2012-10-27 03:19:47 +04:00
2014-06-26 07:38:49 +04:00
/* Prefer uncompressed file to journal (probably cached) to
* compressed file ( probably uncached ) . */
r = sd_journal_get_data ( j , " COREDUMP_FILENAME " , ( const void * * ) & data , & len ) ;
if ( r < 0 & & r ! = - ENOENT )
2014-11-28 15:19:16 +03:00
log_warning_errno ( r , " Failed to retrieve COREDUMP_FILENAME: %m " ) ;
2014-06-26 07:38:49 +04:00
else if ( r = = 0 )
retrieve ( data , len , " COREDUMP_FILENAME " , & filename ) ;
2012-11-21 03:28:00 +04:00
2014-06-26 07:38:49 +04:00
if ( filename & & access ( filename , R_OK ) < 0 ) {
2014-07-07 02:35:46 +04:00
log_full ( errno = = ENOENT ? LOG_DEBUG : LOG_WARNING ,
" File %s is not readable: %m " , filename ) ;
2014-06-26 07:38:49 +04:00
free ( filename ) ;
filename = NULL ;
2012-10-19 14:29:46 +04:00
}
2014-07-04 06:42:22 +04:00
if ( filename & & ! endswith ( filename , " .xz " ) & & ! endswith ( filename , " .lz4 " ) ) {
2014-06-27 02:17:22 +04:00
if ( path ) {
* path = filename ;
filename = NULL ;
}
2014-06-19 01:05:15 +04:00
2014-06-26 07:38:49 +04:00
return 0 ;
} else {
_cleanup_close_ int fdt = - 1 ;
char * temp = NULL ;
2014-06-19 01:05:15 +04:00
if ( fd < 0 ) {
2014-06-26 07:38:49 +04:00
temp = strdup ( " /var/tmp/coredump-XXXXXX " ) ;
if ( ! temp )
return log_oom ( ) ;
fdt = mkostemp_safe ( temp , O_WRONLY | O_CLOEXEC ) ;
if ( fdt < 0 ) {
log_error ( " Failed to create temporary file: %m " ) ;
return - errno ;
}
log_debug ( " Created temporary file %s " , temp ) ;
fd = fdt ;
}
r = sd_journal_get_data ( j , " COREDUMP " , ( const void * * ) & data , & len ) ;
if ( r = = 0 ) {
ssize_t sz ;
assert ( len > = 9 ) ;
data + = 9 ;
len - = 9 ;
sz = write ( fdt , data , len ) ;
if ( sz < 0 ) {
log_error ( " Failed to write temporary file: %m " ) ;
r = - errno ;
goto error ;
}
if ( sz ! = ( ssize_t ) len ) {
log_error ( " Short write to temporary file. " ) ;
r = - EIO ;
goto error ;
}
} else if ( filename ) {
2014-07-04 06:42:22 +04:00
# if defined(HAVE_XZ) || defined(HAVE_LZ4)
2014-06-26 07:38:49 +04:00
_cleanup_close_ int fdf ;
fdf = open ( filename , O_RDONLY | O_CLOEXEC ) ;
if ( fdf < 0 ) {
log_error ( " Failed to open %s: %m " , filename ) ;
r = - errno ;
goto error ;
}
2014-07-04 06:42:22 +04:00
r = decompress_stream ( filename , fdf , fd , - 1 ) ;
2014-06-26 07:38:49 +04:00
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to decompress %s: %m " , filename ) ;
2014-06-26 07:38:49 +04:00
goto error ;
}
2014-06-26 12:31:23 +04:00
# else
2014-07-04 06:42:22 +04:00
log_error ( " Cannot decompress file. Compiled without compression support. " ) ;
2014-06-26 12:31:23 +04:00
r = - ENOTSUP ;
goto error ;
# endif
2014-06-26 07:38:49 +04:00
} else {
if ( r = = - ENOENT )
2014-07-07 02:35:46 +04:00
log_error ( " Cannot retrieve coredump from journal nor disk. " ) ;
2014-06-19 01:05:15 +04:00
else
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to retrieve COREDUMP field: %m " ) ;
2014-06-26 07:38:49 +04:00
goto error ;
}
2014-06-19 01:05:15 +04:00
2014-06-26 07:38:49 +04:00
if ( temp ) {
* path = temp ;
* unlink_temp = true ;
2014-06-19 01:05:15 +04:00
}
2014-06-26 07:38:49 +04:00
return 0 ;
error :
if ( temp ) {
unlink ( temp ) ;
log_debug ( " Removed temporary file %s " , temp ) ;
2014-06-19 01:05:15 +04:00
}
2014-06-26 07:38:49 +04:00
return r ;
}
}
2014-06-19 01:05:15 +04:00
2014-06-26 07:38:49 +04:00
static int dump_core ( sd_journal * j ) {
int r ;
assert ( j ) ;
r = focus ( j ) ;
if ( r < 0 )
2012-10-27 03:19:47 +04:00
return r ;
2014-06-26 07:38:49 +04:00
print_info ( output ? stdout : stderr , j , false ) ;
if ( on_tty ( ) & & ! output ) {
log_error ( " Refusing to dump core to tty. " ) ;
return - ENOTTY ;
}
r = save_core ( j , output ? fileno ( output ) : STDOUT_FILENO , NULL , NULL ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Coredump retrieval failed: %m " ) ;
2014-06-26 07:38:49 +04:00
return r ;
2012-10-19 14:29:46 +04:00
}
r = sd_journal_previous ( j ) ;
if ( r > = 0 )
2013-12-24 19:39:37 +04:00
log_warning ( " More than one entry matches, ignoring rest. " ) ;
2012-10-19 14:29:46 +04:00
return 0 ;
}
2012-10-27 03:19:47 +04:00
static int run_gdb ( sd_journal * j ) {
2014-06-27 01:16:24 +04:00
_cleanup_free_ char * exe = NULL , * path = NULL ;
2014-06-26 07:38:49 +04:00
bool unlink_path = false ;
const char * data ;
2014-06-19 01:05:15 +04:00
siginfo_t st ;
2012-10-27 03:19:47 +04:00
size_t len ;
pid_t pid ;
int r ;
assert ( j ) ;
r = focus ( j ) ;
if ( r < 0 )
return r ;
2014-06-19 01:34:59 +04:00
print_info ( stdout , j , false ) ;
fputs ( " \n " , stdout ) ;
2012-10-27 03:19:47 +04:00
r = sd_journal_get_data ( j , " COREDUMP_EXE " , ( const void * * ) & data , & len ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to retrieve COREDUMP_EXE field: %m " ) ;
2012-10-27 03:19:47 +04:00
return r ;
}
2014-06-26 07:38:49 +04:00
assert ( len > strlen ( " COREDUMP_EXE= " ) ) ;
data + = strlen ( " COREDUMP_EXE= " ) ;
len - = strlen ( " COREDUMP_EXE= " ) ;
2012-10-27 03:19:47 +04:00
exe = strndup ( data , len ) ;
if ( ! exe )
return log_oom ( ) ;
if ( endswith ( exe , " (deleted) " ) ) {
log_error ( " Binary already deleted. " ) ;
return - ENOENT ;
}
2014-06-19 01:05:15 +04:00
if ( ! path_is_absolute ( exe ) ) {
log_error ( " Binary is not an absolute path. " ) ;
return - ENOENT ;
}
2014-06-26 07:38:49 +04:00
r = save_core ( j , - 1 , & path , & unlink_path ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to retrieve core: %m " ) ;
2012-10-27 03:19:47 +04:00
return r ;
2014-06-19 01:05:15 +04:00
}
2012-10-27 03:19:47 +04:00
pid = fork ( ) ;
if ( pid < 0 ) {
log_error ( " Failed to fork(): %m " ) ;
r = - errno ;
goto finish ;
}
if ( pid = = 0 ) {
execlp ( " gdb " , " gdb " , exe , path , NULL ) ;
2014-06-19 01:05:15 +04:00
2012-10-27 03:19:47 +04:00
log_error ( " Failed to invoke gdb: %m " ) ;
_exit ( 1 ) ;
}
r = wait_for_terminate ( pid , & st ) ;
if ( r < 0 ) {
log_error ( " Failed to wait for gdb: %m " ) ;
goto finish ;
}
r = st . si_code = = CLD_EXITED ? st . si_status : 255 ;
finish :
2014-06-26 07:38:49 +04:00
if ( unlink_path ) {
log_debug ( " Removed temporary file %s " , path ) ;
unlink ( path ) ;
}
2014-06-19 01:05:15 +04:00
2012-10-27 03:19:47 +04:00
return r ;
}
2012-10-19 14:29:46 +04:00
int main ( int argc , char * argv [ ] ) {
2013-04-18 11:11:22 +04:00
_cleanup_journal_close_ sd_journal * j = NULL ;
2012-10-19 14:29:46 +04:00
const char * match ;
Iterator it ;
int r = 0 ;
2013-04-18 11:11:22 +04:00
_cleanup_set_free_free_ Set * matches = NULL ;
2012-10-19 14:29:46 +04:00
2012-11-12 23:16:07 +04:00
setlocale ( LC_ALL , " " ) ;
2012-10-19 14:29:46 +04:00
log_parse_environment ( ) ;
log_open ( ) ;
matches = new_matches ( ) ;
2012-10-30 12:44:32 +04:00
if ( ! matches ) {
r = - ENOMEM ;
2012-10-19 14:29:46 +04:00
goto end ;
2012-10-30 12:44:32 +04:00
}
2012-10-19 14:29:46 +04:00
2013-03-18 07:36:25 +04:00
r = parse_argv ( argc , argv , matches ) ;
2012-10-30 12:44:32 +04:00
if ( r < 0 )
2012-10-19 14:29:46 +04:00
goto end ;
if ( arg_action = = ACTION_NONE )
goto end ;
r = sd_journal_open ( & j , SD_JOURNAL_LOCAL_ONLY ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to open journal: %m " ) ;
2012-10-19 14:29:46 +04:00
goto end ;
}
2014-06-26 07:38:49 +04:00
/* We want full data, nothing truncated. */
sd_journal_set_data_threshold ( j , 0 ) ;
2012-10-19 14:29:46 +04:00
SET_FOREACH ( match , matches , it ) {
r = sd_journal_add_match ( j , match , strlen ( match ) ) ;
if ( r ! = 0 ) {
log_error ( " Failed to add match '%s': %s " ,
match , strerror ( - r ) ) ;
goto end ;
}
}
2013-08-02 15:58:26 +04:00
if ( _unlikely_ ( log_get_max_level ( ) > = LOG_PRI ( LOG_DEBUG ) ) ) {
_cleanup_free_ char * filter ;
filter = journal_make_match_string ( j ) ;
log_debug ( " Journal filter: %s " , filter ) ;
}
2012-10-19 14:29:46 +04:00
switch ( arg_action ) {
2012-10-27 03:19:47 +04:00
2012-10-19 14:29:46 +04:00
case ACTION_LIST :
2014-06-19 01:34:59 +04:00
case ACTION_INFO :
2012-10-19 14:29:46 +04:00
if ( ! arg_no_pager )
2013-03-07 23:44:35 +04:00
pager_open ( false ) ;
2012-10-19 14:29:46 +04:00
r = dump_list ( j ) ;
break ;
2012-10-27 03:19:47 +04:00
2012-10-19 14:29:46 +04:00
case ACTION_DUMP :
r = dump_core ( j ) ;
break ;
2012-10-27 03:19:47 +04:00
case ACTION_GDB :
r = run_gdb ( j ) ;
break ;
default :
2012-10-19 14:29:46 +04:00
assert_not_reached ( " Shouldn't be here " ) ;
}
end :
pager_close ( ) ;
if ( output )
fclose ( output ) ;
2012-10-27 03:19:47 +04:00
return r > = 0 ? r : EXIT_FAILURE ;
2012-10-19 14:29:46 +04:00
}