Whamcloud - gitweb
filefrag: fix fm_start in filefrag_fiemap loop
[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 <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 }