Whamcloud - gitweb
iscan: fix the test program iscan so it builds again
[tools/e2fsprogs.git] / misc / logsave.c
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 EAGAIN/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 }