diff --git a/.gitignore b/.gitignore index d0256a7..4049918 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/coredump + # ---> Vim # Swap [._]*.s[a-v][a-z] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b49b94a --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +all: coredump + +coredump: coredump.o + gcc -o $@ $^ + +%.o: %.c + gcc -fPIC -g -c -o $@ $< + +clean: + rm -f coredump coredump.o + +.PHONY: clean all diff --git a/coredump.c b/coredump.c new file mode 100644 index 0000000..f24d153 --- /dev/null +++ b/coredump.c @@ -0,0 +1,285 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct argv_t { + char *_self; + char *coredir; + 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 %%p %%P %%i %%I %%u %%g %%d %%s %%t %%h %%e %%f %%E %%c %%C"; + +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; +} 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; + TOSTR( coredir); + 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, argv_t *argv, char* fn) { + size_t destl = snprintf( dest, len, "/proc/%s/%s", argv->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, argv_t *argv, char* ext) { + size_t destl = snprintf( dest, len, "%s/core%s.%s", argv->coredir, ext, argv->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( argv_t *argv, 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 stdin -> core.pid + if( core_filename( path, sizeof path, argv, "")) + return 1; + if( -1 == (wfd = open( path, O_WRONLY | O_CREAT | O_TRUNC, 0600))) { + fprintf( stderr, "ERROR: Cannot open file %s: %s (%i)\n", path, 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", path, 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( argv_t *argv, 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, argv, "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, argv, "-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) { + // 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, argv, "-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 main( size_t argc, argv_t *argv) { + args_t *args = malloc( sizeof (args_t)); + args->_self = realpath( argv->_self, NULL); + + if( (sizeof (argv_t))/(sizeof (char*)) > argc) { + fprintf( stderr, "ERROR: %li arguments expected. Exect value of kernel.core_pattern must be: `", (sizeof (argv_t))/(sizeof (char*))); + fprintf( stderr, expected_pattern, args->_self); + fprintf( stderr, "`. Given: %li\n", argc-1); + if( '/' != args->_self[0]) + fprintf( stderr, "\tAttention: Absolut path instead of %s should be used!\n", args->_self); + return 2; + } + + argv2args( argv, args); + + //fprintf( stderr, "dump core...\n"); + dump_core( argv, 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( argv, args); + //fprintf( stderr, "dump metadata...\n"); + dump_md( argv); + //fprintf( stderr, "exit...\n"); + + return 0; +}