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 int	outfd = -1;
     35 int	outbufsize = 0;
     36 void	*outbuf = 0;
     37 int	verbose = 0;
     38 int	do_skip = 0;
     39 int	skip_mode = 0;
     40 pid_t	child_pid = -1;
     41 
     42 static void usage(char *progname)
     43 {
     44 	printf("Usage: %s [-v] [-d dir] 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 
    193 		execvp(argv[0], argv);
    194 		perror(argv[0]);
    195 		exit(1);
    196 	}
    197 	child_pid = pid;
    198 	close(fds[1]);
    199 
    200 	while (!(waitpid(pid, &status, WNOHANG ))) {
    201 		do_read(fds[0]);
    202 	}
    203 	child_pid = -1;
    204 	do_read(fds[0]);
    205 	close(fds[0]);
    206 
    207 	if ( WIFEXITED(status) ) {
    208 		rc = WEXITSTATUS(status);
    209 		if (rc) {
    210 			send_output(argv[0], 0, SEND_BOTH);
    211 			sprintf(buffer, " died with exit status %d\n", rc);
    212 			send_output(buffer, 0, SEND_BOTH);
    213 		}
    214 	} else {
    215 		if (WIFSIGNALED(status)) {
    216 			send_output(argv[0], 0, SEND_BOTH);
    217 			sprintf(buffer, "died with signal %d\n",
    218 				WTERMSIG(status));
    219 			send_output(buffer, 0, SEND_BOTH);
    220 			rc = 1;
    221 		}
    222 		rc = 0;
    223 	}
    224 	return rc;
    225 }
    226 
    227 static int copy_from_stdin(void)
    228 {
    229 	int	c, bad_read = 0;
    230 
    231 	while (1) {
    232 		c = do_read(0);
    233 		if ((c == 0 ) ||
    234 		    ((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) {
    235 			if (bad_read++ > 3)
    236 				break;
    237 			continue;
    238 		}
    239 		if (c < 0) {
    240 			perror("read");
    241 			exit(1);
    242 		}
    243 		bad_read = 0;
    244 	}
    245 	return 0;
    246 }
    247 
    248 
    249 
    250 int main(int argc, char **argv)
    251 {
    252 	int	c, pid, rc;
    253 	char	*outfn, **cpp;
    254 	int	openflags = O_CREAT|O_WRONLY|O_TRUNC;
    255 	int	send_flag = SEND_LOG;
    256 	int	do_stdin;
    257 	time_t	t;
    258 
    259 	while ((c = getopt(argc, argv, "+asv")) != EOF) {
    260 		switch (c) {
    261 		case 'a':
    262 			openflags &= ~O_TRUNC;
    263 			openflags |= O_APPEND;
    264 			break;
    265 		case 's':
    266 			do_skip = 1;
    267 			break;
    268 		case 'v':
    269 			verbose++;
    270 			send_flag |= SEND_CONSOLE;
    271 			break;
    272 		}
    273 	}
    274 	if (optind == argc || optind+1 == argc)
    275 		usage(argv[0]);
    276 	outfn = argv[optind];
    277 	optind++;
    278 	argv += optind;
    279 	argc -= optind;
    280 
    281 	outfd = open(outfn, openflags, 0644);
    282 	do_stdin = !strcmp(argv[0], "-");
    283 
    284 	send_output("Log of ", 0, send_flag);
    285 	if (do_stdin)
    286 		send_output("stdin", 0, send_flag);
    287 	else {
    288 		for (cpp = argv; *cpp; cpp++) {
    289 			send_output(*cpp, 0, send_flag);
    290 			send_output(" ", 0, send_flag);
    291 		}
    292 	}
    293 	send_output("\n", 0, send_flag);
    294 	t = time(0);
    295 	send_output(ctime(&t), 0, send_flag);
    296 	send_output("\n", 0, send_flag);
    297 
    298 	if (do_stdin)
    299 		rc = copy_from_stdin();
    300 	else
    301 		rc = run_program(argv);
    302 
    303 	send_output("\n", 0, send_flag);
    304 	t = time(0);
    305 	send_output(ctime(&t), 0, send_flag);
    306 	send_output("----------------\n", 0, send_flag);
    307 
    308 	if (outbuf) {
    309 		pid = fork();
    310 		if (pid < 0) {
    311 			perror("fork");
    312 			exit(1);
    313 		}
    314 		if (pid) {
    315 			if (verbose)
    316 				printf("Backgrounding to save %s later\n",
    317 				       outfn);
    318 			exit(rc);
    319 		}
    320 		setsid();	/* To avoid getting killed by init */
    321 		while (outfd < 0) {
    322 			outfd = open(outfn, openflags, 0644);
    323 			sleep(1);
    324 		}
    325 		write_all(outfd, outbuf, outbufsize);
    326 		free(outbuf);
    327 	}
    328 	close(outfd);
    329 
    330 	exit(rc);
    331 }
    332