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#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; 33int do_skip = 0; 34int skip_mode = 0; 35 36static void usage(char *progname) 37{ 38 printf("Usage: %s [-v] [-d dir] logfile program\n", progname); 39 exit(1); 40} 41 42#define SEND_LOG 0x01 43#define SEND_CONSOLE 0x02 44#define SEND_BOTH 0x03 45 46static void send_output(const char *buffer, int c, int flag) 47{ 48 char *n; 49 50 if (c == 0) 51 c = strlen(buffer); 52 53 if (flag & SEND_CONSOLE) 54 write(1, buffer, c); 55 if (!(flag & SEND_LOG)) 56 return; 57 if (outfd > 0) 58 write(outfd, buffer, c); 59 else { 60 n = realloc(outbuf, outbufsize + c); 61 if (n) { 62 outbuf = n; 63 memcpy(((char *)outbuf)+outbufsize, buffer, c); 64 outbufsize += c; 65 } 66 } 67} 68 69static int do_read(int fd) 70{ 71 int c; 72 char buffer[4096], *cp, *sep; 73 74 c = read(fd, buffer, sizeof(buffer)-1); 75 if (c <= 0) 76 return c; 77 if (do_skip) { 78 send_output(buffer, c, SEND_CONSOLE); 79 buffer[c] = 0; 80 cp = buffer; 81 while (*cp) { 82 if (skip_mode) { 83 cp = strchr(cp, '\002'); 84 if (!cp) 85 return 0; 86 cp++; 87 skip_mode = 0; 88 continue; 89 } 90 sep = strchr(cp, '\001'); 91 if (sep) 92 *sep = 0; 93 send_output(cp, 0, SEND_LOG); 94 if (sep) { 95 cp = sep + 1; 96 skip_mode = 1; 97 } else 98 break; 99 } 100 } else 101 send_output(buffer, c, SEND_BOTH); 102 return c; 103} 104 105static int run_program(char **argv) 106{ 107 int fds[2]; 108 int status, rc, pid; 109 char buffer[80]; 110 111 if (pipe(fds) < 0) { 112 perror("pipe"); 113 exit(1); 114 } 115 116 pid = fork(); 117 if (pid < 0) { 118 perror("vfork"); 119 exit(1); 120 } 121 if (pid == 0) { 122 dup2(fds[1],1); /* fds[1] replaces stdout */ 123 dup2(fds[1],2); /* fds[1] replaces stderr */ 124 close(fds[0]); /* don't need this here */ 125 126 execvp(argv[0], argv); 127 perror(argv[0]); 128 exit(1); 129 } 130 close(fds[1]); 131 132 while (!(waitpid(pid, &status, WNOHANG ))) { 133 do_read(fds[0]); 134 } 135 do_read(fds[0]); 136 close(fds[0]); 137 138 if ( WIFEXITED(status) ) { 139 rc = WEXITSTATUS(status); 140 if (rc) { 141 send_output(argv[0], 0, SEND_BOTH); 142 sprintf(buffer, " died with exit status %d\n", rc); 143 send_output(buffer, 0, SEND_BOTH); 144 } 145 } else { 146 if (WIFSIGNALED(status)) { 147 send_output(argv[0], 0, SEND_BOTH); 148 sprintf(buffer, "died with signal %d\n", 149 WTERMSIG(status)); 150 send_output(buffer, 0, SEND_BOTH); 151 rc = 1; 152 } 153 rc = 0; 154 } 155 return rc; 156} 157 158static int copy_from_stdin(void) 159{ 160 int c, bad_read = 0; 161 162 while (1) { 163 c = do_read(0); 164 if ((c == 0 ) || 165 ((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) { 166 if (bad_read++ > 3) 167 break; 168 continue; 169 } 170 if (c < 0) { 171 perror("read"); 172 exit(1); 173 } 174 bad_read = 0; 175 } 176 return 0; 177} 178 179 180 181int main(int argc, char **argv) 182{ 183 int c, pid, rc; 184 char *outfn, **cpp; 185 int openflags = O_CREAT|O_WRONLY|O_TRUNC; 186 int send_flag = SEND_LOG; 187 int do_stdin; 188 time_t t; 189 190 while ((c = getopt(argc, argv, "+asv")) != EOF) { 191 switch (c) { 192 case 'a': 193 openflags &= ~O_TRUNC; 194 openflags |= O_APPEND; 195 break; 196 case 's': 197 do_skip = 1; 198 break; 199 case 'v': 200 verbose++; 201 send_flag |= SEND_CONSOLE; 202 break; 203 } 204 } 205 if (optind == argc || optind+1 == argc) 206 usage(argv[0]); 207 outfn = argv[optind]; 208 optind++; 209 argv += optind; 210 argc -= optind; 211 212 outfd = open(outfn, openflags, 0644); 213 do_stdin = !strcmp(argv[0], "-"); 214 215 send_output("Log of ", 0, send_flag); 216 if (do_stdin) 217 send_output("stdin", 0, send_flag); 218 else { 219 for (cpp = argv; *cpp; cpp++) { 220 send_output(*cpp, 0, send_flag); 221 send_output(" ", 0, send_flag); 222 } 223 } 224 send_output("\n", 0, send_flag); 225 t = time(0); 226 send_output(ctime(&t), 0, send_flag); 227 send_output("\n", 0, send_flag); 228 229 if (do_stdin) 230 rc = copy_from_stdin(); 231 else 232 rc = run_program(argv); 233 234 send_output("\n", 0, send_flag); 235 t = time(0); 236 send_output(ctime(&t), 0, send_flag); 237 send_output("----------------\n", 0, send_flag); 238 239 if (outbuf) { 240 pid = fork(); 241 if (pid < 0) { 242 perror("fork"); 243 exit(1); 244 } 245 if (pid) { 246 if (verbose) 247 printf("Backgrounding to save %s later\n", 248 outfn); 249 exit(rc); 250 } 251 setsid(); /* To avoid getting killed by init */ 252 while (outfd < 0) { 253 outfd = open(outfn, openflags, 0644); 254 sleep(1); 255 } 256 write(outfd, outbuf, outbufsize); 257 free(outbuf); 258 } 259 close(outfd); 260 261 exit(rc); 262} 263