OSDN Git Service

e2freefrag: fix "Illegal block number" errors with bigalloc file systems
[android-x86/external-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 int     outfd = -1;
36 int     outbufsize = 0;
37 void    *outbuf = 0;
38 int     verbose = 0;
39 int     do_skip = 0;
40 int     skip_mode = 0;
41 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
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 }