diff options
Diffstat (limited to 'misc/logsave.c')
-rw-r--r-- | misc/logsave.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/misc/logsave.c b/misc/logsave.c new file mode 100644 index 0000000..96b6d8e --- /dev/null +++ b/misc/logsave.c @@ -0,0 +1,334 @@ +/* + * logsave.c --- A program which saves the output of a program until + * /var/log is mounted. + * + * Copyright (C) 2003 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif + +static int outfd = -1; +static int outbufsize = 0; +static void *outbuf = 0; +static int verbose = 0; +static int do_skip = 0; +static int skip_mode = 0; +static pid_t child_pid = -1; + +static void usage(char *progname) +{ + printf("Usage: %s [-asv] logfile program\n", progname); + exit(1); +} + +#define SEND_LOG 0x01 +#define SEND_CONSOLE 0x02 +#define SEND_BOTH 0x03 + +/* + * Helper function that does the right thing if write returns a + * partial write, or an EAGAIN/EINTR error. + */ +static int write_all(int fd, const char *buf, size_t count) +{ + ssize_t ret; + int c = 0; + + while (count > 0) { + ret = write(fd, buf, count); + if (ret < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + return -1; + } + count -= ret; + buf += ret; + c += ret; + } + return c; +} + +static void send_output(const char *buffer, int c, int flag) +{ + const char *cp; + char *n; + int cnt, d, del; + + if (c == 0) + c = strlen(buffer); + + if (flag & SEND_CONSOLE) { + cnt = c; + cp = buffer; + while (cnt) { + del = 0; + for (d=0; d < cnt; d++) { + if (skip_mode && + (cp[d] == '\001' || cp[d] == '\002')) { + del = 1; + break; + } + } + write_all(1, cp, d); + if (del) + d++; + cnt -= d; + cp += d; + } + } + if (!(flag & SEND_LOG)) + return; + if (outfd > 0) + write_all(outfd, buffer, c); + else { + n = realloc(outbuf, outbufsize + c); + if (n) { + outbuf = n; + memcpy(((char *)outbuf)+outbufsize, buffer, c); + outbufsize += c; + } + } +} + +static int do_read(int fd) +{ + int c; + char buffer[4096], *cp, *sep; + + c = read(fd, buffer, sizeof(buffer)-1); + if (c <= 0) + return c; + if (do_skip) { + send_output(buffer, c, SEND_CONSOLE); + buffer[c] = 0; + cp = buffer; + while (*cp) { + if (skip_mode) { + cp = strchr(cp, '\002'); + if (!cp) + return 0; + cp++; + skip_mode = 0; + continue; + } + sep = strchr(cp, '\001'); + if (sep) + *sep = 0; + send_output(cp, 0, SEND_LOG); + if (sep) { + cp = sep + 1; + skip_mode = 1; + } else + break; + } + } else + send_output(buffer, c, SEND_BOTH); + return c; +} + +static void signal_term(int sig) +{ + if (child_pid > 0) + kill(child_pid, sig); +} + +static int run_program(char **argv) +{ + int fds[2]; + int status, rc, pid; + char buffer[80]; +#ifdef HAVE_SIGNAL_H + struct sigaction sa; +#endif + + if (pipe(fds) < 0) { + perror("pipe"); + exit(1); + } + +#ifdef HAVE_SIGNAL_H + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = signal_term; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); +#ifdef SA_RESTART + sa.sa_flags = SA_RESTART; +#endif +#endif + + pid = fork(); + if (pid < 0) { + perror("vfork"); + exit(1); + } + if (pid == 0) { + dup2(fds[1],1); /* fds[1] replaces stdout */ + dup2(fds[1],2); /* fds[1] replaces stderr */ + close(fds[0]); /* don't need this here */ + close(fds[1]); + + execvp(argv[0], argv); + perror(argv[0]); + exit(1); + } + child_pid = pid; + close(fds[1]); + + while (!(waitpid(pid, &status, WNOHANG ))) { + do_read(fds[0]); + } + child_pid = -1; + do_read(fds[0]); + close(fds[0]); + + if ( WIFEXITED(status) ) { + rc = WEXITSTATUS(status); + if (rc) { + send_output(argv[0], 0, SEND_BOTH); + sprintf(buffer, " exited with status code %d\n", rc); + send_output(buffer, 0, SEND_BOTH); + } + } else { + if (WIFSIGNALED(status)) { + send_output(argv[0], 0, SEND_BOTH); + sprintf(buffer, "died with signal %d\n", + WTERMSIG(status)); + send_output(buffer, 0, SEND_BOTH); + return 1; + } + rc = 0; + } + return rc; +} + +static int copy_from_stdin(void) +{ + int c, bad_read = 0; + + while (1) { + c = do_read(0); + if ((c == 0 ) || + ((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) { + if (bad_read++ > 3) + break; + continue; + } + if (c < 0) { + perror("read"); + exit(1); + } + bad_read = 0; + } + return 0; +} + + + +int main(int argc, char **argv) +{ + int c, pid, rc; + char *outfn, **cpp; + int openflags = O_CREAT|O_WRONLY|O_TRUNC; + int send_flag = SEND_LOG; + int do_stdin; + time_t t; + + while ((c = getopt(argc, argv, "+asv")) != EOF) { + switch (c) { + case 'a': + openflags &= ~O_TRUNC; + openflags |= O_APPEND; + break; + case 's': + do_skip = 1; + break; + case 'v': + verbose++; + send_flag |= SEND_CONSOLE; + break; + } + } + if (optind == argc || optind+1 == argc) + usage(argv[0]); + outfn = argv[optind]; + optind++; + argv += optind; + argc -= optind; + + outfd = open(outfn, openflags, 0644); + do_stdin = !strcmp(argv[0], "-"); + + send_output("Log of ", 0, send_flag); + if (do_stdin) + send_output("stdin", 0, send_flag); + else { + for (cpp = argv; *cpp; cpp++) { + send_output(*cpp, 0, send_flag); + send_output(" ", 0, send_flag); + } + } + send_output("\n", 0, send_flag); + t = time(0); + send_output(ctime(&t), 0, send_flag); + send_output("\n", 0, send_flag); + + if (do_stdin) + rc = copy_from_stdin(); + else + rc = run_program(argv); + + send_output("\n", 0, send_flag); + t = time(0); + send_output(ctime(&t), 0, send_flag); + send_output("----------------\n", 0, send_flag); + + if (outbuf) { + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (pid) { + if (verbose) + printf("Backgrounding to save %s later\n", + outfn); + exit(rc); + } + setsid(); /* To avoid getting killed by init */ + while (outfd < 0) { + outfd = open(outfn, openflags, 0644); + sleep(1); + } + write_all(outfd, outbuf, outbufsize); + free(outbuf); + } + if (outfd >= 0) + close(outfd); + + exit(rc); +} |