coredump-md/coredump.c

332 lines
8.7 KiB
C

// vim: set noet sw=2 ts=2 sts=2:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/sendfile.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
typedef struct argv_t {
char *pid; // %p
char *global_pid; // %P
char *tid; // %i
char *global_tid; // %I
char *uid; // %u
char *gid; // %g
char *dump_mode; // %d
char *signal; // %s
char *ts; // %t
char *hostname; // %h
char *exefn_; // %e
char *exefn; // %f
char *exepath; // %E
char *max_size; // %c
char *cpu; // %C
} argv_t;
const char const expected_pattern[] = "|%s [-d DESTDIR] [-a EXEPATH] %%p %%P %%i %%I %%u %%g %%d %%s %%t %%h %%e %%f %%E %%c %%C";
const unsigned int const expected_argc = 15;
typedef char *path_t;
typedef struct args_t {
path_t _self;
path_t coredir;
pid_t pid;
pid_t global_pid;
uint tid;
uint global_tid;
uid_t uid;
gid_t gid;
uint dump_mode;
uint signal;
uint ts;
char* hostname;
path_t exefn_;
path_t exefn;
path_t exepath;
size_t max_size;
uint cpu;
path_t after;
char corefile[1024];
} args_t;
// if max_size is 0 or bigger than MAX_SIZE, then MAX_SIZE (1GiB):
#define MAX_SIZE 1073741824
int prsint( long unsigned int *dst, char *src) {
*dst = 0;
for( uint i = 0; src[i]; i++) {
if( '0' <= src[i] && src[i] <= '9')
*dst = 10*(*dst) + src[i]-'0';
else
return -1;
}
return 0;
}
#define CHK_EXST( key) \
if( NULL == v->key) { \
fprintf( stderr, "ERROR: Missing value for: %s\n", #key); \
return -1; \
}
#define TOSTR( key) CHK_EXST( key) \
s->key = v->key;
#define TOINT( key) CHK_EXST( key) \
if( -1 == prsint( &b, v->key)) { \
fprintf( stderr, "ERROR: Invalid value in: %s=%s\n", #key, v->key); \
return -1; \
} \
s->key = b;
int argv2args( argv_t *v, args_t *s) {
long unsigned int b;
TOINT( pid);
TOINT( global_pid);
TOINT( tid);
TOINT( global_tid);
TOINT( uid);
TOINT( gid);
TOINT( dump_mode);
TOINT( signal);
TOINT( ts);
TOSTR( hostname);
TOSTR( exefn_);
TOSTR( exefn);
TOSTR( exepath);
TOINT( max_size);
TOINT( cpu);
}
int proc_filename( char *dest, size_t len, args_t *args, char* fn) {
size_t destl = snprintf( dest, len, "/proc/%i/%s", args->pid, fn);
if( destl < 0) {
fprintf( stderr, "ERROR: gen path for %s: %s (%i)\n", fn, strerror( errno), errno);
return 1;
}
if( len <= destl) {
fprintf( stderr, "ERROR: gen path for %s: /proc/-path to long: destl=%lu\n", fn, destl);
return 1;
}
return 0;
}
int core_filename( char *dest, size_t len, args_t *args, char* ext) {
size_t destl = snprintf( dest, len, "%s/core%s.%u", args->coredir, ext, args->pid);
if( destl < 0) {
fprintf( stderr, "ERROR: gen path for core%s: %s (%i)\n", ext, strerror( errno), errno);
return 1;
}
if( len <= destl) {
fprintf( stderr, "ERROR: gen path for core%s: /proc/-path to long: destl=%lu\n", ext, destl);
return 1;
}
return 0;
}
void show_opened_files() {
pid_t selfpid = getpid();
pid_t child = fork();
switch( child) {
case -1:
fprintf( stderr, "ERROR: error while forking: %s (%i)\n", strerror( errno), errno);
break;
case 0:
char dir[1024];
snprintf( dir, sizeof dir, "/proc/%i/fd/", selfpid);
execl( "/usr/bin/env", "env", "ls", "-la", dir, NULL);
fprintf( stderr, "ERROR: Executing ls failed: %s (%i)\n", strerror( errno), errno);
exit( 2);
default:
int ws;
waitpid( child, &ws, 0);
}
}
int dump_core( args_t *args) {
// Concept: Once used, not necessary anymore, we cen reuse the space again.
int wfd; // write file handler.
struct stat stat; // Needed for determine size of file.
// copy stdin -> core.pid
if( core_filename( args->corefile, sizeof args->corefile, args, ""))
return 1;
if( -1 == (wfd = open( args->corefile, O_WRONLY | O_CREAT | O_TRUNC, 0600))) {
fprintf( stderr, "ERROR: Cannot open file %s: %s (%i)\n", args->corefile, strerror( errno), errno);
return 1;
}
if( -1 == splice(
STDIN_FILENO, 0,
wfd, 0,
(args->max_size || MAX_SIZE < args->max_size) ? args->max_size : MAX_SIZE, 0)) {
fprintf( stderr, "ERROR: Cannot write core to file %s: %s (%i)\n", args->corefile, strerror( errno), errno);
return 1;
}
close( wfd);
return 0;
}
int copy_fd_to_fd( int infd, int outfd) {
char buf[1024];
size_t left;
while( 0 < (left = read( infd, buf, sizeof buf))) {
char *wb = buf;
ssize_t wr;
while( left > (wr = write( outfd, wb, left))) {
if( -1 == wr) {
return -1;
}
left -= wr;
}
}
}
int dump_cmdline( args_t *args) {
// Concept: Once used, not necessary anymore, we cen reuse the space again.
char path[1024]; // For any paths.
int rfd; // read file handler.
int wfd; // write file handler.
struct stat stat; // Needed for determine size of file.
// copy /proc/pid/cmdline -> core-cmdline.pid
if( proc_filename( path, sizeof path, args, "cmdline")) return 1;
if( -1 == (rfd = open( path, O_RDONLY, 0644))) {
fprintf( stderr, "ERROR: Cannot open file %s: %s (%i)\n", path, strerror( errno), errno);
return 1;
}
if( -1 == fstat( rfd, &stat)) {
fprintf( stderr, "ERROR: Cannot stat of file %s: %s (%i)\n", path, strerror( errno), errno);
return 1;
}
if( core_filename( path, sizeof path, args, "-cmdline")) return 1;
if( -1 == (wfd = open( path, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
fprintf( stderr, "ERROR: Cannot open file %s: %s (%i)\n", path, strerror( errno), errno);
return 1;
}
//show_opened_files();
//if( -1 == sendfile( wfd, rfd, NULL, 1024*1024)) {
if( -1 == copy_fd_to_fd( rfd, wfd)) {
fprintf( stderr, "ERROR: Cannot write to file %s: %s (%i)\n", path, strerror( errno), errno);
return 1;
}
close( rfd);
close( wfd);
return 0;
}
#define P( s, m) if( -1 == fprintf( wfh, "%s=%s\n", #s, argv->m)) { \
fprintf( stderr, "ERROR: Cannot write to file %s: %s (%i)\n", path, strerror( errno), errno); \
return 1; \
}
int dump_md( argv_t *argv, args_t *args) {
// Concept: Once used, not necessary anymore, we cen reuse the space again.
char path[1024]; // For any paths.
FILE *wfh;
// write argv -> core-md.pid
if( core_filename( path, sizeof path, args, "-md")) return 1;
umask( 0133);
if( NULL == (wfh = fopen( path, "w"))) {
fprintf( stderr, "ERROR: Cannot open file %s: %s (%i)\n", path, strerror( errno), errno);
return 1;
}
P(p, pid)
P(P, global_pid)
P(i, tid)
P(I, global_tid)
P(u, uid)
P(g, gid)
P(d, dump_mode)
P(s, signal)
P(t, ts)
P(h, hostname)
P(e, exefn_)
P(f, exefn)
P(E, exepath)
P(c, max_size)
P(C, cpu)
fclose( wfh);
return 0;
}
int usage( args_t *args) {
fprintf( stderr, "Usage in kernel.core_pattern=\"");
fprintf( stderr, expected_pattern, args->_self);
fprintf( stderr, "\"\n");
if( '/' != args->_self[0])
fprintf( stderr, "\tAttention: Absolut path instead of %s should be used!\n", args->_self);
fprintf( stderr, "\n -a EXEPATH\tExecutes EXEPATH after dumping core as child process, before itself exits.\n");
fprintf( stderr, "\t\tAs arguments, the path to core-file will be provided.\n");
fprintf( stderr, " -d DESTDIR\tDestination directory for core-files and additional files. (default: /var/cores)\n");
exit(2);
}
void run_after( args_t *args) {
if( args->after) {
pid_t child = fork();
switch( child) {
case -1:
fprintf( stderr, "ERROR: error while forking: %s (%i)\n", strerror( errno), errno);
break;
case 0:
execl( args->after, args->after, args->corefile, NULL);
fprintf( stderr, "ERROR: Executing after-program failed: %s (%i)\n", strerror( errno), errno);
exit( 2);
}
}
}
void parse_args( size_t argc, char** _argv, argv_t **argv, args_t *args) {
args->after = NULL;
args->coredir = "/var/cores";
args->_self = realpath( _argv[0], NULL);
char opt;
while( -1 != (opt = getopt( argc, _argv, "hd:a:"))) {
switch( opt) {
case 'h': usage( args); break;
case 'a': args->after = optarg; break;
case 'd': args->coredir = optarg; break;
default:
fprintf( stderr, "ERROR: Unknown option: -%c\n", opt);
usage( args);
}
}
if( expected_argc > argc-optind+1) {
fprintf( stderr, "ERROR: %u arguments expected. Given: %li\n", expected_argc, argc-optind+1);
usage( args);
exit(2);
}
*argv = (argv_t*) (_argv + optind);
argv2args( *argv, args);
}
int main( size_t argc, char **_argv) {
argv_t *argv = malloc( sizeof (argv_t));
args_t *args = malloc( sizeof (args_t));
parse_args( argc, _argv, &argv, args);
//fprintf( stderr, "dump core...\n");
dump_core( args);
// That's all, core dump written. :)
// But, hold on, now the funny things will follow, which are the differences to a simple core-dump.
//fprintf( stderr, "dump cmdline...\n");
dump_cmdline( args);
//fprintf( stderr, "dump metadata...\n");
dump_md( argv, args);
//fprintf( stderr, "run after program...\n");
run_after( args);
//fprintf( stderr, "exit...\n");
return 0;
}