Home | History | Annotate | Download | only in misc
      1 /*
      2  * logsave.c --- A program which saves the output of a program until
      3  *	/var/log is mounted.
      4  *
      5  * Copyright (C) 2003 Theodore Ts'o.
      6  *
      7  * %Begin-Header%
      8  * This file may be redistributed under the terms of the GNU Public
      9  * License.
     10  * %End-Header%
     11  */
     12 
     13 #define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
     14 
     15 #include "config.h"
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <unistd.h>
     19 #include <string.h>
     20 #include <sys/types.h>
     21 #include <sys/wait.h>
     22 #include <fcntl.h>
     23 #include <time.h>
     24 #include <errno.h>
     25 #ifdef HAVE_SIGNAL_H
     26 #include <signal.h>
     27 #endif
     28 #ifdef HAVE_GETOPT_H
     29 #include <getopt.h>
     30 #else
     31 extern char *optarg;
     32 extern int optind;
     33 #endif
     34 
     35 static int	outfd = -1;
     36 static int	outbufsize = 0;
     37 static void	*outbuf = 0;
     38 static int	verbose = 0;
     39 static int	do_skip = 0;
     40 static int	skip_mode = 0;
     41 static pid_t	child_pid = -1;
     42 
     43 static void usage(char *progname)
     44 {
     45 	printf("Usage: %s [-asv] logfile program\n", progname);
     46 	exit(1);
     47 }
     48 
     49 #define SEND_LOG	0x01
     50 #define SEND_CONSOLE	0x02
     51 #define SEND_BOTH	0x03
     52 
     53 /*
     54  * Helper function that does the right thing if write returns a
     55  * partial write, or an EGAIN/EINTR error.
     56  */
     57 static int write_all(int fd, const char *buf, size_t count)
     58 {
     59 	ssize_t ret;
     60 	int c = 0;
     61 
     62 	while (count > 0) {
     63 		ret = write(fd, buf, count);
     64 		if (ret < 0) {
     65 			if ((errno == EAGAIN) || (errno == EINTR))
     66 				continue;
     67 			return -1;
     68 		}
     69 		count -= ret;
     70 		buf += ret;
     71 		c += ret;
     72 	}
     73 	return c;
     74 }
     75 
     76 static void send_output(const char *buffer, int c, int flag)
     77 {
     78 	const char	*cp;
     79 	char		*n;
     80 	int		cnt, d, del;
     81 
     82 	if (c == 0)
     83 		c = strlen(buffer);
     84 
     85 	if (flag & SEND_CONSOLE) {
     86 		cnt = c;
     87 		cp = buffer;
     88 		while (cnt) {
     89 			del = 0;
     90 			for (d=0; d < cnt; d++) {
     91 				if (skip_mode &&
     92 				    (cp[d] == '\001' || cp[d] == '\002')) {
     93 					del = 1;
     94 					break;
     95 				}
     96 			}
     97 			write_all(1, cp, d);
     98 			if (del)
     99 				d++;
    100 			cnt -= d;
    101 			cp += d;
    102 		}
    103 	}
    104 	if (!(flag & SEND_LOG))
    105 		return;
    106 	if (outfd > 0)
    107 		write_all(outfd, buffer, c);
    108 	else {
    109 		n = realloc(outbuf, outbufsize + c);
    110 		if (n) {
    111 			outbuf = n;
    112 			memcpy(((char *)outbuf)+outbufsize, buffer, c);
    113 			outbufsize += c;
    114 		}
    115 	}
    116 }
    117 
    118 static int do_read(int fd)
    119 {
    120 	int	c;
    121 	char	buffer[4096], *cp, *sep;
    122 
    123 	c = read(fd, buffer, sizeof(buffer)-1);
    124 	if (c <= 0)
    125 		return c;
    126 	if (do_skip) {
    127 		send_output(buffer, c, SEND_CONSOLE);
    128 		buffer[c] = 0;
    129 		cp = buffer;
    130 		while (*cp) {
    131 			if (skip_mode) {
    132 				cp = strchr(cp, '\002');
    133 				if (!cp)
    134 					return 0;
    135 				cp++;
    136 				skip_mode = 0;
    137 				continue;
    138 			}
    139 			sep = strchr(cp, '\001');
    140 			if (sep)
    141 				*sep = 0;
    142 			send_output(cp, 0, SEND_LOG);
    143 			if (sep) {
    144 				cp = sep + 1;
    145 				skip_mode = 1;
    146 			} else
    147 				break;
    148 		}
    149 	} else
    150 		send_output(buffer, c, SEND_BOTH);
    151 	return c;
    152 }
    153 
    154 static void signal_term(int sig)
    155 {
    156 	if (child_pid > 0)
    157 		kill(child_pid, sig);
    158 }
    159 
    160 static int run_program(char **argv)
    161 {
    162 	int	fds[2];
    163 	int	status, rc, pid;
    164 	char	buffer[80];
    165 #ifdef HAVE_SIGNAL_H
    166 	struct sigaction	sa;
    167 #endif
    168 
    169 	if (pipe(fds) < 0) {
    170 		perror("pipe");
    171 		exit(1);
    172 	}
    173 
    174 #ifdef HAVE_SIGNAL_H
    175 	memset(&sa, 0, sizeof(struct sigaction));
    176 	sa.sa_handler = signal_term;
    177 	sigaction(SIGINT, &sa, 0);
    178 	sigaction(SIGTERM, &sa, 0);
    179 #ifdef SA_RESTART
    180 	sa.sa_flags = SA_RESTART;
    181 #endif
    182 #endif
    183 
    184 	pid = fork();
    185 	if (pid < 0) {
    186 		perror("vfork");
    187 		exit(1);
    188 	}
    189 	if (pid == 0) {
    190 		dup2(fds[1],1);		/* fds[1] replaces stdout */
    191 		dup2(fds[1],2);  	/* fds[1] replaces stderr */
    192 		close(fds[0]);	/* don't need this here */
    193 		close(fds[1]);
    194 
    195 		execvp(argv[0], argv);
    196 		perror(argv[0]);
    197 		exit(1);
    198 	}
    199 	child_pid = pid;
    200 	close(fds[1]);
    201 
    202 	while (!(waitpid(pid, &status, WNOHANG ))) {
    203 		do_read(fds[0]);
    204 	}
    205 	child_pid = -1;
    206 	do_read(fds[0]);
    207 	close(fds[0]);
    208 
    209 	if ( WIFEXITED(status) ) {
    210 		rc = WEXITSTATUS(status);
    211 		if (rc) {
    212 			send_output(argv[0], 0, SEND_BOTH);
    213 			sprintf(buffer, " exited with status code %d\n", rc);
    214 			send_output(buffer, 0, SEND_BOTH);
    215 		}
    216 	} else {
    217 		if (WIFSIGNALED(status)) {
    218 			send_output(argv[0], 0, SEND_BOTH);
    219 			sprintf(buffer, "died with signal %d\n",
    220 				WTERMSIG(status));
    221 			send_output(buffer, 0, SEND_BOTH);
    222 			return 1;
    223 		}
    224 		rc = 0;
    225 	}
    226 	return rc;
    227 }
    228 
    229 static int copy_from_stdin(void)
    230 {
    231 	int	c, bad_read = 0;
    232 
    233 	while (1) {
    234 		c = do_read(0);
    235 		if ((c == 0 ) ||
    236 		    ((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) {
    237 			if (bad_read++ > 3)
    238 				break;
    239 			continue;
    240 		}
    241 		if (c < 0) {
    242 			perror("read");
    243 			exit(1);
    244 		}
    245 		bad_read = 0;
    246 	}
    247 	return 0;
    248 }
    249 
    250 
    251 
    252 int main(int argc, char **argv)
    253 {
    254 	int	c, pid, rc;
    255 	char	*outfn, **cpp;
    256 	int	openflags = O_CREAT|O_WRONLY|O_TRUNC;
    257 	int	send_flag = SEND_LOG;
    258 	int	do_stdin;
    259 	time_t	t;
    260 
    261 	while ((c = getopt(argc, argv, "+asv")) != EOF) {
    262 		switch (c) {
    263 		case 'a':
    264 			openflags &= ~O_TRUNC;
    265 			openflags |= O_APPEND;
    266 			break;
    267 		case 's':
    268 			do_skip = 1;
    269 			break;
    270 		case 'v':
    271 			verbose++;
    272 			send_flag |= SEND_CONSOLE;
    273 			break;
    274 		}
    275 	}
    276 	if (optind == argc || optind+1 == argc)
    277 		usage(argv[0]);
    278 	outfn = argv[optind];
    279 	optind++;
    280 	argv += optind;
    281 	argc -= optind;
    282 
    283 	outfd = open(outfn, openflags, 0644);
    284 	do_stdin = !strcmp(argv[0], "-");
    285 
    286 	send_output("Log of ", 0, send_flag);
    287 	if (do_stdin)
    288 		send_output("stdin", 0, send_flag);
    289 	else {
    290 		for (cpp = argv; *cpp; cpp++) {
    291 			send_output(*cpp, 0, send_flag);
    292 			send_output(" ", 0, send_flag);
    293 		}
    294 	}
    295 	send_output("\n", 0, send_flag);
    296 	t = time(0);
    297 	send_output(ctime(&t), 0, send_flag);
    298 	send_output("\n", 0, send_flag);
    299 
    300 	if (do_stdin)
    301 		rc = copy_from_stdin();
    302 	else
    303 		rc = run_program(argv);
    304 
    305 	send_output("\n", 0, send_flag);
    306 	t = time(0);
    307 	send_output(ctime(&t), 0, send_flag);
    308 	send_output("----------------\n", 0, send_flag);
    309 
    310 	if (outbuf) {
    311 		pid = fork();
    312 		if (pid < 0) {
    313 			perror("fork");
    314 			exit(1);
    315 		}
    316 		if (pid) {
    317 			if (verbose)
    318 				printf("Backgrounding to save %s later\n",
    319 				       outfn);
    320 			exit(rc);
    321 		}
    322 		setsid();	/* To avoid getting killed by init */
    323 		while (outfd < 0) {
    324 			outfd = open(outfn, openflags, 0644);
    325 			sleep(1);
    326 		}
    327 		write_all(outfd, outbuf, outbufsize);
    328 		free(outbuf);
    329 	}
    330 	if (outfd >= 0)
    331 		close(outfd);
    332 
    333 	exit(rc);
    334 }
    335