From 647834f82039e395acbb948fb2778ba799fa640d Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Fri, 3 Jun 2016 00:52:06 +0200 Subject: [PATCH hurd 2/2] trans/crash: xxx core file templates --- hurd/crash.defs | 6 +- trans/crash.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 4 deletions(-) diff --git a/hurd/crash.defs b/hurd/crash.defs index 442957d..aead8ee 100644 --- a/hurd/crash.defs +++ b/hurd/crash.defs @@ -25,8 +25,10 @@ subsystem crash 32000; /* Handle a crashing task, whose task control port is TASK. - FILE is a file port open for writing. The caller will link it to "core" - (or whatever name) if the RPC returns success. + FILE is a file port open for writing. The caller will link it to + "core" (or whatever name) if the RPC returns success. If EEXIST is + returned, the caller assumes that a core file has been written to a + different file, and that FILE can be discarded. SIGNO, SIGCODE, and SIGERROR indicate the signal that killed the process. EXC is zero for a software signal; otherwise EXC, CODE, and diff --git a/trans/crash.c b/trans/crash.c index e60ce7e..0fe9b24 100644 --- a/trans/crash.c +++ b/trans/crash.c @@ -70,7 +70,105 @@ enum crash_action #define CRASH_ORPHANS_DEFAULT crash_corefile static enum crash_action crash_how, crash_orphans_how; +static char *corefile_template; + + +/* Template parsing. */ +static int +template_valid (const char *template, const char **errp) +{ + int valid = 0; + const char *t; + int specifier = 0; + + for (t = template; *t; t++) + { + if (specifier) + switch (*t) + { + case '%': + case 'p': + case 's': + case 't': + specifier = 0; + break; + default: + goto out; + } + else if (*t == '%') + specifier = 1; + } + + out: + valid = ! specifier; + *errp = valid? NULL: t; + return valid; +} + +static char * +template_make_file_name (const char *template, + task_t task, + int signo) +{ + const char *t; + char *file_name = NULL; + size_t file_name_len = 0; + FILE *stream; + int specifier = 0; + + if (! template_valid (template, &t)) + { + errno = EINVAL; + return NULL; + } + + stream = open_memstream (&file_name, &file_name_len); + if (stream == NULL) + return NULL; + + for (t = template; *t; t++) + { + if (specifier) + { + switch (*t) + { + case '%': + fprintf (stream, "%%"); + break; + + case 'p': + fprintf (stream, "%d", task2pid (task)); + break; + + case 's': + fprintf (stream, "%d", signo); + break; + + case 't': + fprintf (stream, "%d", time (NULL)); + break; + + default: + assert (!"reached!"); + } + specifier = 0; + } + else if (*t == '%') + specifier = 1; + else + fprintf (stream, "%c", *t); + } + + assert (! specifier); + + fprintf (stream, "%c", 0); + fclose (stream); + + return file_name; +} + + /* This is defined in ../exec/elfcore.c, or we could have different implementations for other formats. */ @@ -237,10 +335,42 @@ S_crash_dump_task (mach_port_t port, err = task_suspend (task); if (!err) { - err = dump_core (task, core_file, + file_t sink = core_file; + if (corefile_template) + { + char *file_name; + + file_name = template_make_file_name (corefile_template, + task, signo); + if (file_name == NULL) + error (0, errno, "template_make_file_name"); + else + { + sink = file_name_lookup (file_name, O_WRONLY|O_CREAT, + S_IRUSR); + if (! MACH_PORT_VALID (sink)) + { + error (0, errno, "%s", file_name); + sink = core_file; + } + free (file_name); + } + } + + err = dump_core (task, sink, (off_t) -1, /* XXX should get core limit in RPC */ signo, sigcode, sigerror); task_resume (task); + + if (sink != core_file) + { + mach_port_deallocate (mach_task_self (), sink); + + /* We return an error so that the libc discards + CORE_FILE. */ + if (! err) + err = EEXIST; + } } break; @@ -450,13 +580,25 @@ static const struct argp_option options[] = {"kill", 'k', 0, 0, "Kill the process", 2}, {"core-file", 'c', 0, 0, "Dump a core file", 2}, {"dump-core", 0, 0, OPTION_ALIAS }, + {"core-file-name", 'C', "TEMPLATE", 0, + "Specify core file name (see below)", 2}, {0} }; static const char doc[] = "Server to handle crashing tasks and dump core files or equivalent.\v" "The ACTION values can be `suspend', `kill', or `core-file'.\n\n" "If `--orphan-action' is not specified, the `--action' value is used for " -"orphans. The default is `--action=suspend --orphan-action=core-file'."; +"orphans. The default is `--action=suspend --orphan-action=core-file'.\n" +"\n" +"The core file is either written to the file provided by the " +"crashing process, or if a TEMPLATE value is given, to the file " +"with the name constructed by expanding TEMPLATE value. " +"TEMPLATE may contain % specifiers:\n" +"\n" +"\t%% just %\n" +"\t%p the process' PID\n" +"\t%s the signal number that caused the dump\n" +"\t%t time of crash in seconds since the EPOCH\n"; static error_t parse_opt (int opt, char *arg, struct argp_state *state) @@ -494,6 +636,14 @@ parse_opt (int opt, char *arg, struct argp_state *state) case 's': crash_how = crash_suspend; break; case 'k': crash_how = crash_kill; break; case 'c': crash_how = crash_corefile; break; + case 'C': + { + char *errp; + if (! template_valid (arg, &errp)) + error (1, 0, "Invalid template: ...'%s'", errp); + } + corefile_template = arg; + break; case ARGP_KEY_SUCCESS: if (crash_orphans_how == crash_unspecified) @@ -536,6 +686,18 @@ trivfs_append_args (struct trivfs_control *fsys, err = argz_add (argz, argz_len, opt); } + if (!err && corefile_template) + { + char *template; + if (asprintf (&template, "--core-file-name=%s", corefile_template) < 0) + err = errno; + else + { + err = argz_add (argz, argz_len, template); + free (template); + } + } + return err; } -- 2.1.4