• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
usage(char * progname)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  */
write_all(int fd,const char * buf,size_t count)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 
send_output(const char * buffer,int c,int flag)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 
do_read(int fd)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 
signal_term(int sig)154 static void signal_term(int sig)
155 {
156 	if (child_pid > 0)
157 		kill(child_pid, sig);
158 }
159 
run_program(char ** argv)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 
copy_from_stdin(void)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 
main(int argc,char ** argv)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