From: Colin Patrick McCabe Date: Fri, 13 May 2011 00:10:59 +0000 (-0700) Subject: Add code X-Git-Url: http://club.cc.cmu.edu/~cmccabe/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5c23331a561fdaa4b79070654462ad50a7f0b17a;p=handle_core Add code Signed-off-by: Colin McCabe --- 5c23331a561fdaa4b79070654462ad50a7f0b17a diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9415613 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CC=gcc + +CFLAGS=-Wall + +all: handle_core + +handle_core: handle_core.o + $(CC) $(CFLAGS) handle_core.o -o $@ + +clean: + rm -rf *o handle_core diff --git a/handle_core.c b/handle_core.c new file mode 100644 index 0000000..afd6daf --- /dev/null +++ b/handle_core.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 1024 +#define CORE_PATTERN "core.*" + +/* + * Core file handler + * + * Example usage: + * echo "|/usr/bin/handle_core -e %p -d /home/core -m 10" > \ + * /proc/sys/kernel/core_pattern + */ + +/* Count the number of core files we have. */ +static int count_core_files(const char *core_dir) +{ + struct dirent *de; + int count = 0; + DIR *dp = opendir(core_dir); + if (!dp) { + int err = errno; + return -err; + } + + while (1) { + de = readdir(dp); + if (!de) + break; + if (fnmatch(CORE_PATTERN, de->d_name, 0) == 0) { + count++; + } + } + closedir(dp); + return count; +} + +/* Unlink the core which sorts the first in the glob sort order. */ +static int unlink_core(const char *core_dir) +{ + int ret; + char path[PATH_MAX]; + glob_t g; + + snprintf(path, PATH_MAX, "%s/%s", core_dir, CORE_PATTERN); + ret = glob(path, GLOB_ERR, NULL, &g); + if (ret != 0) { + return EIO; + } + if (g.gl_pathc == 0) + return ENOENT; + if (unlink(g.gl_pathv[0])) { + globfree(&g); + return errno; + } + globfree(&g); + return 0; +} + +/* Print the new core name into a buffer of size PATH_MAX */ +static void get_core_name(const char *core_dir, char *core_name) +{ + struct tm *tm; + struct tm tm_buf; + time_t now; + time(&now); + tm = localtime_r(&now, &tm_buf); + snprintf(core_name, PATH_MAX, "%s/core.%d-%lld-%lld_%lld", core_dir, + tm->tm_year, (long long)tm->tm_mon, (long long)tm->tm_mday, + (long long)now); +} + +static void usage(void) +{ + fprintf(stderr, "handle_core: userspace core-file handler for Linux\n\ +-d Directory to write core files into\n\ +-e Name of the executable that is core dumping\n\ +-h This help message\n\ +-m This maximum number of core files to allow\n\ + before deleting older core files.\n\ +"); +} + +static void parse_options(int argc, char **argv, int *max_cores, + char **exe_name, char **core_dir) +{ + int c; + *max_cores = 10; + *exe_name = NULL; + *core_dir = "/var/core"; + while ((c = getopt(argc, argv, "d:hm:")) != -1) { + switch (c) { + case 'd': + *core_dir = optarg; + break; + case 'e': + *exe_name = optarg; + break; + case 'h': + usage(); + exit(0); + break; + case 'm': + *max_cores = atoi(optarg); + if (*max_cores == 0) { + fprintf(stderr, "handle_core: invalid argument " + "for max_cores: %s. Please give a number " + "greater than 0.\n", optarg); + exit(1); + } + break; + default: + fprintf(stderr, "handle_core: invalid usage\n\n"); + usage(); + exit(1); + break; + } + } + if (*exe_name == NULL) { + fprintf(stderr, "handle_core: you must supply the executable " + "name with -e. Try -h for help.\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + int done = 0; + char *exe_name, *core_dir; + char core_name[PATH_MAX]; + FILE *fp; + int num_coref, num_deleted; + int max_cores; + + /* Write the core to a file */ + parse_options(argc, argv, &max_cores, &exe_name, &core_dir); + get_core_name(core_dir, core_name); + fp = fopen(core_name, "w"); + if (!fp) { + int err = errno; + syslog(LOG_USER | LOG_ERR, "unable to open %s: " + "error %d (%s)\n", core_name, err, strerror(err)); + return err; + } + do { + size_t res; + char buf[BUF_SIZE]; + size_t nread = fread(buf, 1, BUF_SIZE, stdin); + if (nread != BUF_SIZE) { + if (ferror(fp)) { + int err = errno; + syslog(LOG_USER | LOG_ERR, "error reading core " + "file from stdin: %d (%s)", err, strerror(err)); + fclose(fp); + return err; + } + else { + done = 1; + } + } + res = fwrite(buf, 1, nread, fp); + if (res != nread) { + int err = errno; + syslog(LOG_USER | LOG_ERR, "error writing core " + "file to %s: %d (%s)", core_name, err, strerror(err)); + fclose(fp); + return err; + } + } while (!done); + fclose(fp); + + /* Make sure we don't have too many cores sitting around. */ + num_coref = count_core_files(core_dir); + num_deleted = 0; + if (num_coref < 0) { + syslog(LOG_USER | LOG_ERR, "count_core_files failed with error " + "code %d\n", num_coref); + return num_coref; + } + while (num_coref > max_cores) { + int ret = unlink_core(core_dir); + if (ret) { + syslog(LOG_USER | LOG_ERR, "unlink_core failed with error " + "code %d\n", ret); + } + num_coref--; + num_deleted++; + } + + syslog(LOG_USER | LOG_ERR, "wrote core %s. Deleted %d extra core%s\n", + core_name, num_deleted, ((num_deleted == 1) ? "" : "s")); + return 0; +}