From 3f2b4a5704bb2e362933847e466a9468544705d4 Mon Sep 17 00:00:00 2001 From: Jesse Smith Date: Mon, 31 Dec 2018 16:31:34 -0400 Subject: [PATCH] Added logsave.c and logsave.8 manual page from e2fsprogs to make sure logsave is available to initscripts. Updated src/Makefile to make sure bootlogd compiles with Clang. --- doc/Changelog | 3 + man/logsave.8 | 61 +++++++++ src/Makefile | 17 ++- src/bootlogd.c | 6 - src/logsave.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 410 insertions(+), 10 deletions(-) create mode 100644 man/logsave.8 create mode 100644 src/logsave.c diff --git a/doc/Changelog b/doc/Changelog index ba64343..36601ec 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -18,6 +18,9 @@ sysvinit (2.94) unreleased; urgency=low * Increased the size of the kernel command line buffer in bootlogd from 256 characters to 4096. This size is defined in KERNEL_COMMAND_LENGTH for easy modification downstream. + * Added logsave.c and logsave.8 manual page from e2fsprogs to make + sure logsave is available to initscripts. + * Updated src/Makefile to make sure bootlogd compiles with Clang. sysvinit (2.93) released; urgency=low diff --git a/man/logsave.8 b/man/logsave.8 new file mode 100644 index 0000000..cc3ffde --- /dev/null +++ b/man/logsave.8 @@ -0,0 +1,61 @@ +.\" -*- nroff -*- +.\" Copyright 2003 by Theodore Ts'o. All Rights Reserved. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH LOGSAVE 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@" +.SH NAME +logsave \- save the output of a command in a logfile +.SH SYNOPSIS +.B logsave +[ +.B \-asv +] +.I logfile cmd_prog [ ... ] +.SH DESCRIPTION +The +.B logsave +program will execute +.I cmd_prog +with the specified argument(s), and save a copy of its output to +.IR logfile . +If the containing directory for +.I logfile +does not exist, +.B logsave +will accumulate the output in memory until it can be written out. +A copy of the output will also be written to standard output. +.PP +If +.I cmd_prog +is a single hyphen ('-'), then instead of executing a program, +.B logsave +will take its input from standard input and save it in +.I logfile +.PP +.B logsave +is useful for saving the output of initial boot scripts +until the /var partition is mounted, so the output can be written to +/var/log. +.SH OPTIONS +.TP +.B \-a +This option will cause the output to be appended to +.IR logfile , +instead of replacing its current contents. +.TP +.B \-s +This option will cause +.B logsave +to skip writing to the log file text which is bracketed with a control-A +(ASCII 001 or Start of Header) and control-B (ASCII 002 or Start of +Text). This allows progress bar information to be visible to the user +on the console, while not being written to the log file. +.TP +.B \-v +This option will make +.B logsave +to be more verbose in its output to the user. +.SH AUTHOR +Theodore Ts'o (tytso@mit.edu) +.SH SEE ALSO +.BR fsck (8) diff --git a/src/Makefile b/src/Makefile index a345e0e..e54c051 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,13 +23,13 @@ MNTPOINT= # For some known distributions we do not build all programs, otherwise we do. BIN = -SBIN = init halt shutdown runlevel killall5 fstab-decode +SBIN = init halt shutdown runlevel killall5 fstab-decode logsave USRBIN = last mesg readbootlog MAN1 = last.1 lastb.1 mesg.1 readbootlog.1 MAN5 = initscript.5 inittab.5 initctl.5 MAN8 = halt.8 init.8 killall5.8 pidof.8 poweroff.8 reboot.8 runlevel.8 -MAN8 += shutdown.8 telinit.8 fstab-decode.8 +MAN8 += shutdown.8 telinit.8 fstab-decode.8 logsave.8 ifeq ($(DISTRO),) SBIN += sulogin bootlogd @@ -115,6 +115,9 @@ halt: halt.o ifdown.o hddown.o utmp.o runlevellog.o last: LDLIBS += $(STATIC) last: last.o +logsave: LDLIBS += $(STATIC) +logsave: logsave.o + mesg: LDLIBS += $(STATIC) mesg: mesg.o @@ -137,10 +140,10 @@ shutdown: LDLIBS += $(STATIC) shutdown: dowall.o shutdown.o utmp.o bootlogd: LDLIBS += -lutil $(STATIC) -bootlogd: bootlogd.o bootlogd.h +bootlogd: bootlogd.o readbootlog: LDLIBS += $(STATIC) -readbootlog: readbootlog.o bootlogd.h +readbootlog: readbootlog.o fstab-decode: LDLIBS += $(STATIC) fstab-decode: fstab-decode.o @@ -159,6 +162,10 @@ init_utmp.o: CPPFLAGS += -DINIT_MAIN init_utmp.o: utmp.c init.h initreq.h paths.h $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< +bootlogd.o: bootlogd.c bootlogd.h + +readbootlog.o: readbootlog.c + utmpdump.o: utmpdump.c oldutmp.h shutdown.o: shutdown.c paths.h reboot.h initreq.h init.h @@ -167,6 +174,8 @@ halt.o: halt.c reboot.h paths.h runlevellog.c runlevellog.h last.o: last.c oldutmp.h +logsave.o: logsave.c + consoles.o: consoles.c consoles.h cleanobjs: diff --git a/src/bootlogd.c b/src/bootlogd.c index 66057ee..f046a01 100644 --- a/src/bootlogd.c +++ b/src/bootlogd.c @@ -24,12 +24,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * *NOTE* *NOTE* *NOTE* - * This is a PROOF OF CONCEPT IMPLEMENTATION - * - * I have bigger plans for Debian, but for now - * this has to do ;) - * */ #include diff --git a/src/logsave.c b/src/logsave.c new file mode 100644 index 0000000..c027032 --- /dev/null +++ b/src/logsave.c @@ -0,0 +1,333 @@ +/* + * 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% + */ + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#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); +}