// vim: set noet sw=2 ts=2 sts=2: #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include 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; }