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