blob: dd2e286f9686633d2368cf42cdf3e6e17a1eb79f [file] [log] [blame]
Theodore Ts'of9ddad52003-04-14 18:05:12 -04001/*
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#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <string.h>
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <fcntl.h>
20#include <time.h>
21#include <errno.h>
22#ifdef HAVE_GETOPT_H
23#include <getopt.h>
24#else
25extern char *optarg;
26extern int optind;
27#endif
28
29int outfd = -1;
30int outbufsize = 0;
31void *outbuf = 0;
32int verbose = 0;
33
34static void usage(char *progname)
35{
36 printf("Usage: %s [-v] [-d dir] logfile program\n", progname);
37 exit(1);
38}
39
40static void process_output(const char *buffer, int c)
41{
42 char *n;
43
44 if (c == 0)
45 c = strlen(buffer);
46
47 write(1, buffer, c);
48 if (outfd > 0)
49 write(outfd, buffer, c);
50 else {
51 n = realloc(outbuf, outbufsize + c);
52 if (n) {
53 outbuf = n;
54 memcpy(((char *)outbuf)+outbufsize, buffer, c);
55 outbufsize += c;
56 }
57 }
58}
59
60static void do_read(int fd)
61{
62 int c;
63 char buffer[4096];
64
65 c = read(fd, buffer, sizeof(buffer));
66 process_output(buffer, c);
67}
68
69static int run_program(char **argv)
70{
71 int fds[2];
72 char **cpp;
73 int status, rc, pid;
74 char buffer[80];
75 time_t t;
76
77 if (pipe(fds) < 0) {
78 perror("pipe");
79 exit(1);
80 }
81
82 if (verbose) {
83 process_output("Log of ", 0);
84 for (cpp = argv; *cpp; cpp++) {
85 process_output(*cpp, 0);
86 process_output(" ", 0);
87 }
88 process_output("\n", 0);
89 t = time(0);
90 process_output(ctime(&t), 0);
91 process_output("\n", 0);
92 }
93
94 pid = fork();
95 if (pid < 0) {
96 perror("vfork");
97 exit(1);
98 }
99 if (pid == 0) {
100 dup2(fds[1],1); /* fds[1] replaces stdout */
101 dup2(fds[1],2); /* fds[1] replaces stderr */
102 close(fds[0]); /* don't need this here */
103
104 execvp(argv[0], argv);
105 perror(argv[0]);
106 exit(1);
107 }
108 close(fds[1]);
109
110 while (!(waitpid(pid, &status, WNOHANG ))) {
111 do_read(fds[0]);
112 }
113 do_read(fds[0]);
114 close(fds[0]);
115
116 if ( WIFEXITED(status) ) {
117 rc = WEXITSTATUS(status);
118 if (rc) {
119 process_output(argv[0], 0);
120 sprintf(buffer, " died with exit status %d", rc);
121 process_output(buffer, 0);
122 }
123 } else {
124 if (WIFSIGNALED(status)) {
125 process_output(argv[0], 0);
126 sprintf(buffer, "died with signal %d",
127 WTERMSIG(status));
128 process_output(buffer, 0);
129 rc = 1;
130 }
131 rc = 0;
132 }
133 return rc;
134}
135
136static int copy_from_stdin(void)
137{
138 char buffer[4096];
139 int c;
140 int bad_read = 0;
141
142 while (1) {
143 c = read(0, buffer, sizeof(buffer));
144 if ((c == 0 ) ||
145 ((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) {
146 if (bad_read++ > 3)
147 break;
148 continue;
149 }
150 if (c < 0) {
151 perror("read");
152 exit(1);
153 }
154 process_output(buffer, c);
155 bad_read = 0;
156 }
157 return 0;
158}
159
160
161
162int main(int argc, char **argv)
163{
164 int c, pid, rc;
165 char *outfn;
166 int openflags = O_CREAT|O_WRONLY|O_TRUNC;
167
168 while ((c = getopt(argc, argv, "+v")) != EOF) {
169 switch (c) {
170 case 'a':
171 openflags &= ~O_TRUNC;
172 openflags |= O_APPEND;
173 break;
174 case 'v':
175 verbose++;
176 break;
177 }
178 }
179 if (optind == argc || optind+1 == argc)
180 usage(argv[0]);
181 outfn = argv[optind];
182 optind++;
183 argv += optind;
184 argc -= optind;
185
186 outfd = open(outfn, O_CREAT|O_WRONLY|O_TRUNC, 0644);
187
188 if (strcmp(argv[0], "-"))
189 rc = run_program(argv);
190 else
191 rc = copy_from_stdin();
192
193 if (outbuf) {
194 pid = fork();
195 if (pid < 0) {
196 perror("fork");
197 exit(1);
198 }
199 if (pid) {
200 if (verbose)
201 printf("Backgrounding to save %s later\n",
202 outfn);
203 exit(rc);
204 }
205 while (outfd < 0) {
206 outfd = open(outfn, O_CREAT|O_WRONLY|O_TRUNC, 0644);
207 sleep(1);
208 }
209 write(outfd, outbuf, outbufsize);
210 free(outbuf);
211 }
212 close(outfd);
213
214 exit(rc);
215}