Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 1.18
1.18 ! millert 1: /* $OpenBSD: newsyslog.c,v 1.17 1999/01/05 00:43:44 deraadt Exp $ */
1.10 downsj 2:
3: /*
4: * Copyright (c) 1997, Jason Downs. All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: * 3. All advertising materials mentioning features or use of this software
15: * must display the following acknowledgement:
16: * This product includes software developed by Jason Downs for the
17: * OpenBSD system.
18: * 4. Neither the name(s) of the author(s) nor the name OpenBSD
19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
23: * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
26: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
1.3 deraadt 34:
1.1 deraadt 35: /*
36: * This file contains changes from the Open Software Foundation.
37: */
38:
39: /*
40:
41: Copyright 1988, 1989 by the Massachusetts Institute of Technology
42:
43: Permission to use, copy, modify, and distribute this software
44: and its documentation for any purpose and without fee is
45: hereby granted, provided that the above copyright notice
46: appear in all copies and that both that copyright notice and
47: this permission notice appear in supporting documentation,
48: and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
49: used in advertising or publicity pertaining to distribution
50: of the software without specific, written prior permission.
51: M.I.T. and the M.I.T. S.I.P.B. make no representations about
52: the suitability of this software for any purpose. It is
53: provided "as is" without express or implied warranty.
54:
55: */
56:
57: /*
58: * newsyslog - roll over selected logs at the appropriate time,
59: * keeping the a specified number of backup files around.
60: *
61: */
62:
63: #ifndef lint
1.18 ! millert 64: static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.17 1999/01/05 00:43:44 deraadt Exp $";
1.1 deraadt 65: #endif /* not lint */
66:
67: #ifndef CONF
68: #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */
69: #endif
70: #ifndef PIDFILE
71: #define PIDFILE "/etc/syslog.pid"
72: #endif
73: #ifndef COMPRESS
74: #define COMPRESS "/usr/ucb/compress" /* File compression program */
75: #endif
76: #ifndef COMPRESS_POSTFIX
77: #define COMPRESS_POSTFIX ".Z"
78: #endif
1.10 downsj 79: #ifndef STATS_DIR
80: #define STATS_DIR "/etc"
81: #endif
82: #ifndef SENDMAIL
83: #define SENDMAIL "/usr/lib/sendmail"
84: #endif
1.1 deraadt 85:
86: #include <stdio.h>
1.9 downsj 87: #include <sys/types.h>
88: #include <sys/time.h>
89: #include <sys/stat.h>
90: #include <sys/param.h>
91: #include <sys/wait.h>
1.1 deraadt 92: #include <stdlib.h>
93: #include <string.h>
94: #include <ctype.h>
95: #include <signal.h>
1.9 downsj 96: #include <fcntl.h>
1.1 deraadt 97: #include <pwd.h>
98: #include <grp.h>
1.9 downsj 99: #include <unistd.h>
1.11 downsj 100: #include <err.h>
1.1 deraadt 101:
102: #define kbytes(size) (((size) + 1023) >> 10)
103:
1.10 downsj 104: #define CE_COMPACT 0x01 /* Compact the achived log files */
105: #define CE_BINARY 0x02 /* Logfile is in binary, don't add */
106: /* status messages */
107: #define CE_MONITOR 0x04 /* Monitory for changes */
1.1 deraadt 108: #define NONE -1
109:
110: struct conf_entry {
111: char *log; /* Name of the log */
112: int uid; /* Owner of log */
113: int gid; /* Group of log */
114: int numlogs; /* Number of logs to keep */
115: int size; /* Size cutoff to trigger trimming the log */
116: int hours; /* Hours between log trimming */
117: int permissions; /* File permissions on the log */
118: int flags; /* Flags (CE_COMPACT & CE_BINARY) */
1.10 downsj 119: char *whom; /* Whom to notify if logfile changes */
1.14 millert 120: char *pidfile; /* Path to file containg pid to HUP */
1.1 deraadt 121: struct conf_entry *next; /* Linked list pointer */
122: };
123:
1.11 downsj 124: extern const char *__progname;
125:
1.1 deraadt 126: int verbose = 0; /* Print out what's going on */
127: int needroot = 1; /* Root privs are necessary */
128: int noaction = 0; /* Don't do anything, just show it */
1.10 downsj 129: int monitor = 0; /* Don't do monitoring by default */
1.1 deraadt 130: char *conf = CONF; /* Configuration file to use */
131: time_t timenow;
1.16 millert 132: #define MIN_PID 4
1.7 deraadt 133: char hostname[MAXHOSTNAMELEN]; /* hostname */
1.1 deraadt 134: char *daytime; /* timenow in human readable form */
135:
136:
1.9 downsj 137: void do_entry __P((struct conf_entry *));
138: void PRS __P((int, char **));
139: void usage __P((void));
140: struct conf_entry *parse_file __P((void));
141: char *missing_field __P((char *, char *));
1.14 millert 142: void dotrim __P((char *, int, int, int, int, int, int));
1.9 downsj 143: int log_trim __P((char *));
144: void compress_log __P((char *));
145: int sizefile __P((char *));
146: int age_old_log __P((char *));
147: char *sob __P((char *));
148: char *son __P((char *));
1.10 downsj 149: int isnumberstr __P((char *));
150: void domonitor __P((char *, char *));
151: FILE *openmail __P((void));
152: void closemail __P((FILE *));
1.16 millert 153: void child_killer __P((int));
1.1 deraadt 154:
1.9 downsj 155: int main(argc, argv)
1.1 deraadt 156: int argc;
157: char **argv;
158: {
159: struct conf_entry *p, *q;
1.16 millert 160: int status;
1.1 deraadt 161:
162: PRS(argc,argv);
1.11 downsj 163: if (needroot && getuid() && geteuid())
164: errx(1, "You must be root.");
1.1 deraadt 165: p = q = parse_file();
1.16 millert 166: signal(SIGCHLD, child_killer);
1.1 deraadt 167: while (p) {
168: do_entry(p);
169: p=p->next;
1.11 downsj 170: free(q);
1.1 deraadt 171: q=p;
172: }
1.16 millert 173:
174: /* Wait for children to finish, then exit */
175: while (waitpid(-1, &status, 0) != -1)
176: ;
1.1 deraadt 177: exit(0);
178: }
179:
1.9 downsj 180: void do_entry(ent)
1.1 deraadt 181: struct conf_entry *ent;
182:
183: {
1.14 millert 184: int size, modtime, pid;
185: char line[BUFSIZ];
186: FILE *f;
187:
188: /* First find the pid to HUP */
189: pid = -1;
190: if ((f = fopen(ent->pidfile,"r")) != NULL) {
191: if (fgets(line,BUFSIZ,f))
192: pid = atoi(line);
193: (void)fclose(f);
194: }
1.1 deraadt 195:
196: if (verbose) {
197: if (ent->flags & CE_COMPACT)
198: printf("%s <%dZ>: ",ent->log,ent->numlogs);
199: else
200: printf("%s <%d>: ",ent->log,ent->numlogs);
201: }
202: size = sizefile(ent->log);
203: modtime = age_old_log(ent->log);
204: if (size < 0) {
205: if (verbose)
206: printf("does not exist.\n");
207: } else {
208: if (verbose && (ent->size > 0))
209: printf("size (Kb): %d [%d] ", size, ent->size);
210: if (verbose && (ent->hours > 0))
211: printf(" age (hr): %d [%d] ", modtime, ent->hours);
1.10 downsj 212: if (monitor && ent->flags & CE_MONITOR)
213: domonitor(ent->log, ent->whom);
214: if (!monitor && ((ent->size > 0) && (size >= ent->size)) ||
1.1 deraadt 215: ((ent->hours > 0) && ((modtime >= ent->hours)
216: || (modtime < 0)))) {
217: if (verbose)
218: printf("--> trimming log....\n");
219: if (noaction && !verbose) {
220: if (ent->flags & CE_COMPACT)
221: printf("%s <%dZ>: trimming",
222: ent->log,ent->numlogs);
223: else
224: printf("%s <%d>: trimming",
225: ent->log,ent->numlogs);
226: }
227: dotrim(ent->log, ent->numlogs, ent->flags,
1.14 millert 228: ent->permissions, ent->uid, ent->gid, pid);
1.1 deraadt 229: } else {
230: if (verbose)
231: printf("--> skipping\n");
232: }
233: }
234: }
235:
1.9 downsj 236: void PRS(argc, argv)
1.1 deraadt 237: int argc;
238: char **argv;
239: {
240: int c;
241: char *p;
242:
1.12 kstailey 243: timenow = time(NULL);
1.1 deraadt 244: daytime = ctime(&timenow) + 4;
1.2 deraadt 245: daytime[15] = '\0';
1.1 deraadt 246:
247: /* Let's get our hostname */
248: (void) gethostname(hostname, sizeof(hostname));
249:
250: /* Truncate domain */
1.9 downsj 251: p = strchr(hostname, '.');
252: if (p)
1.1 deraadt 253: *p = '\0';
254:
255: optind = 1; /* Start options parsing */
1.10 downsj 256: while ((c = getopt(argc,argv,"nrvmf:t:")) != -1) {
1.1 deraadt 257: switch (c) {
258: case 'n':
259: noaction++; /* This implies needroot as off */
260: /* fall through */
261: case 'r':
262: needroot = 0;
263: break;
264: case 'v':
265: verbose++;
266: break;
267: case 'f':
268: conf = optarg;
269: break;
1.10 downsj 270: case 'm':
271: monitor++;
272: break;
1.1 deraadt 273: default:
274: usage();
275: }
276: }
1.9 downsj 277: }
1.1 deraadt 278:
1.9 downsj 279: void usage()
1.1 deraadt 280: {
1.14 millert 281: fprintf(stderr, "usage: %s [-nrvm] [-f config-file]\n", __progname);
282: exit(1);
1.1 deraadt 283: }
284:
285: /* Parse a configuration file and return a linked list of all the logs
286: * to process
287: */
288: struct conf_entry *parse_file()
289: {
290: FILE *f;
291: char line[BUFSIZ], *parse, *q;
292: char *errline, *group;
293: struct conf_entry *first = NULL;
294: struct conf_entry *working;
295: struct passwd *pass;
296: struct group *grp;
297:
298: if (strcmp(conf,"-"))
299: f = fopen(conf,"r");
300: else
301: f = stdin;
1.11 downsj 302: if (!f)
303: err(1, conf);
304:
1.1 deraadt 305: while (fgets(line,BUFSIZ,f)) {
306: if ((line[0]== '\n') || (line[0] == '#'))
307: continue;
308: errline = strdup(line);
1.11 downsj 309: if (errline == NULL)
310: err(1, "strdup");
1.1 deraadt 311: if (!first) {
312: working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
1.11 downsj 313: if (working == NULL)
314: err(1, "malloc");
1.1 deraadt 315: first = working;
316: } else {
317: working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
1.11 downsj 318: if (working->next == NULL)
319: err(1, "malloc");
1.1 deraadt 320: working = working->next;
321: }
322:
323: q = parse = missing_field(sob(line),errline);
324: *(parse = son(line)) = '\0';
325: working->log = strdup(q);
1.11 downsj 326: if (working->log == NULL)
327: err(1, "strdup");
1.1 deraadt 328:
329: q = parse = missing_field(sob(++parse),errline);
330: *(parse = son(parse)) = '\0';
331: if ((group = strchr(q, '.')) != NULL) {
332: *group++ = '\0';
333: if (*q) {
1.10 downsj 334: if (!(isnumberstr(q))) {
1.11 downsj 335: if ((pass = getpwnam(q)) == NULL)
336: errx(1, "Error in config file; unknown user: %s", q);
1.1 deraadt 337: working->uid = pass->pw_uid;
338: } else
339: working->uid = atoi(q);
340: } else
341: working->uid = NONE;
342:
343: q = group;
344: if (*q) {
1.10 downsj 345: if (!(isnumberstr(q))) {
1.11 downsj 346: if ((grp = getgrnam(q)) == NULL)
347: errx(1, "Error in config file; unknown group: %s", q);
1.1 deraadt 348: working->gid = grp->gr_gid;
349: } else
350: working->gid = atoi(q);
351: } else
352: working->gid = NONE;
353:
354: q = parse = missing_field(sob(++parse),errline);
355: *(parse = son(parse)) = '\0';
1.11 downsj 356: } else
1.1 deraadt 357: working->uid = working->gid = NONE;
358:
1.11 downsj 359: if (!sscanf(q,"%o",&working->permissions))
360: errx(1, "Error in config file; bad permissions: %s", q);
1.1 deraadt 361:
362: q = parse = missing_field(sob(++parse),errline);
363: *(parse = son(parse)) = '\0';
1.18 ! millert 364: if (!sscanf(q,"%d",&working->numlogs) || working->numlogs < 0)
1.11 downsj 365: errx(1, "Error in config file; bad number: %s", q);
1.1 deraadt 366:
367: q = parse = missing_field(sob(++parse),errline);
368: *(parse = son(parse)) = '\0';
369: if (isdigit(*q))
370: working->size = atoi(q);
371: else
372: working->size = -1;
373:
374: q = parse = missing_field(sob(++parse),errline);
375: *(parse = son(parse)) = '\0';
376: if (isdigit(*q))
377: working->hours = atoi(q);
378: else
379: working->hours = -1;
380:
381: q = parse = sob(++parse); /* Optional field */
382: *(parse = son(parse)) = '\0';
383: working->flags = 0;
384: while (q && *q && !isspace(*q)) {
385: if ((*q == 'Z') || (*q == 'z'))
386: working->flags |= CE_COMPACT;
387: else if ((*q == 'B') || (*q == 'b'))
388: working->flags |= CE_BINARY;
1.10 downsj 389: else if ((*q == 'M') || (*q == 'm'))
390: working->flags |= CE_MONITOR;
1.11 downsj 391: else
392: errx(1, "Illegal flag in config file: %c", *q);
1.1 deraadt 393: q++;
394: }
1.10 downsj 395:
396: working->whom = NULL;
397: if (working->flags & CE_MONITOR) { /* Optional field */
398: q = parse = sob(++parse);
399: *(parse = son(parse)) = '\0';
400:
401: working->whom = strdup(q);
1.11 downsj 402: if (working->log == NULL)
403: err(1, "strdup");
1.10 downsj 404: }
1.14 millert 405:
406: working->pidfile = PIDFILE;
407: q = parse = sob(++parse); /* Optional field */
408: *(parse = son(parse)) = '\0';
409: if (q && *q != '\0') {
410: working->pidfile = strdup(q);
411: if (working->pidfile == NULL)
412: err(1, "strdup");
413: }
1.1 deraadt 414:
415: free(errline);
416: }
417: if (working)
1.12 kstailey 418: working->next = NULL;
1.1 deraadt 419: (void) fclose(f);
420: return(first);
421: }
422:
1.9 downsj 423: char *missing_field(p, errline)
1.1 deraadt 424: char *p,*errline;
425: {
426: if (!p || !*p) {
1.14 millert 427: warnx("Missing field in config file line:");
1.11 downsj 428: fputs(errline, stderr);
1.1 deraadt 429: exit(1);
430: }
431: return(p);
432: }
433:
1.14 millert 434: void dotrim(log, numdays, flags, perm, owner_uid, group_gid, daemon_pid)
1.1 deraadt 435: char *log;
436: int numdays;
437: int flags;
438: int perm;
439: int owner_uid;
440: int group_gid;
1.14 millert 441: int daemon_pid;
1.1 deraadt 442: {
1.7 deraadt 443: char file1[MAXPATHLEN], file2[MAXPATHLEN];
444: char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
1.1 deraadt 445: int fd;
446: struct stat st;
1.6 tholo 447: int days = numdays;
1.1 deraadt 448:
449: /* Remove oldest log */
450: (void) sprintf(file1,"%s.%d",log,numdays);
451: (void) strcpy(zfile1, file1);
452: (void) strcat(zfile1, COMPRESS_POSTFIX);
453:
454: if (noaction) {
455: printf("rm -f %s\n", file1);
456: printf("rm -f %s\n", zfile1);
457: } else {
458: (void) unlink(file1);
459: (void) unlink(zfile1);
460: }
461:
462: /* Move down log files */
463: while (numdays--) {
464: (void) strcpy(file2,file1);
465: (void) sprintf(file1,"%s.%d",log,numdays);
466: (void) strcpy(zfile1, file1);
467: (void) strcpy(zfile2, file2);
468: if (lstat(file1, &st)) {
469: (void) strcat(zfile1, COMPRESS_POSTFIX);
470: (void) strcat(zfile2, COMPRESS_POSTFIX);
471: if (lstat(zfile1, &st)) continue;
472: }
473: if (noaction) {
474: printf("mv %s %s\n",zfile1,zfile2);
475: printf("chmod %o %s\n", perm, zfile2);
476: printf("chown %d.%d %s\n",
477: owner_uid, group_gid, zfile2);
478: } else {
479: (void) rename(zfile1, zfile2);
480: (void) chmod(zfile2, perm);
481: (void) chown(zfile2, owner_uid, group_gid);
482: }
483: }
484: if (!noaction && !(flags & CE_BINARY))
485: (void) log_trim(log); /* Report the trimming to the old log */
486:
1.6 tholo 487: if (days == 0) {
1.5 deraadt 488: if (noaction)
489: printf("rm %s\n",log);
490: else
491: (void) unlink(log);
492: } else {
493: if (noaction)
494: printf("mv %s to %s\n",log,file1);
495: else
496: (void) rename(log,file1);
497: }
498:
1.1 deraadt 499: if (noaction)
500: printf("Start new log...");
501: else {
502: fd = creat(log,perm);
1.11 downsj 503: if (fd < 0)
1.13 mickey 504: err(1, "can't start \'%s\' log", log);
1.11 downsj 505: if (fchown(fd, owner_uid, group_gid))
1.13 mickey 506: err(1, "can't chown \'%s\' log file", log);
1.1 deraadt 507: (void) close(fd);
508: if (!(flags & CE_BINARY))
1.11 downsj 509: if (log_trim(log)) /* Add status message */
1.13 mickey 510: err(1, "can't add status message to log \'%s\'", log);
1.1 deraadt 511: }
512: if (noaction)
513: printf("chmod %o %s...",perm,log);
514: else
515: (void) chmod(log,perm);
516: if (noaction)
1.14 millert 517: printf("kill -HUP %d\n",daemon_pid);
1.17 deraadt 518: else if (daemon_pid < MIN_PID)
1.14 millert 519: warnx("preposterous process number: %d", daemon_pid);
520: else if (kill(daemon_pid,SIGHUP))
1.17 deraadt 521: warnx("warning - could not HUP daemon");
522: if (flags & CE_COMPACT) {
523: if (noaction)
524: printf("Compress %s.0\n",log);
525: else
526: compress_log(log);
527: }
1.1 deraadt 528: }
529:
530: /* Log the fact that the logs were turned over */
1.9 downsj 531: int log_trim(log)
1.17 deraadt 532: char *log;
1.1 deraadt 533: {
534: FILE *f;
535: if ((f = fopen(log,"a")) == NULL)
536: return(-1);
537: fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
538: daytime, hostname, getpid());
539: if (fclose(f) == EOF) {
1.11 downsj 540: err(1, "log_trim: fclose");
1.1 deraadt 541: }
542: return(0);
543: }
544:
1.16 millert 545: /* Fork off compress or gzip to compress the old log file */
1.9 downsj 546: void compress_log(log)
1.1 deraadt 547: char *log;
548: {
549: int pid;
1.7 deraadt 550: char tmp[MAXPATHLEN];
1.1 deraadt 551:
552: pid = fork();
553: (void) sprintf(tmp,"%s.0",log);
554: if (pid < 0) {
1.11 downsj 555: err(1, "fork");
1.1 deraadt 556: } else if (!pid) {
557: (void) execl(COMPRESS,"compress","-f",tmp,0);
1.16 millert 558: warn(COMPRESS);
559: _exit(1);
1.1 deraadt 560: }
561: }
562:
563: /* Return size in kilobytes of a file */
564: int sizefile(file)
565: char *file;
566: {
567: struct stat sb;
568:
569: if (stat(file,&sb) < 0)
570: return(-1);
571: return(kbytes(dbtob(sb.st_blocks)));
572: }
573:
574: /* Return the age of old log file (file.0) */
575: int age_old_log(file)
576: char *file;
577: {
578: struct stat sb;
1.7 deraadt 579: char tmp[MAXPATHLEN];
1.1 deraadt 580:
581: (void) strcpy(tmp,file);
582: if (stat(strcat(tmp,".0"),&sb) < 0)
583: if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
584: return(-1);
585: return( (int) (timenow - sb.st_mtime + 1800) / 3600);
586: }
587:
588: /* Skip Over Blanks */
589: char *sob(p)
590: register char *p;
591: {
592: while (p && *p && isspace(*p))
593: p++;
594: return(p);
595: }
596:
597: /* Skip Over Non-Blanks */
598: char *son(p)
599: register char *p;
600: {
601: while (p && *p && !isspace(*p))
602: p++;
603: return(p);
604: }
605:
606:
607: /* Check if string is actually a number */
608:
1.10 downsj 609: int isnumberstr(string)
610: char *string;
1.1 deraadt 611: {
612: while (*string != '\0') {
1.9 downsj 613: if (!isdigit(*string++))
614: return(0);
1.1 deraadt 615: }
616: return(1);
1.10 downsj 617: }
618:
619: void domonitor(log, whom)
620: char *log, *whom;
621: {
622: struct stat sb, tsb;
623: char *fname, *flog, *p, *rb = NULL;
624: FILE *fp;
625: off_t osize;
626: int rd;
627:
628: if (stat(log, &sb) < 0)
629: return;
630:
631: flog = strdup(log);
1.11 downsj 632: if (flog == NULL)
633: err(1, "strdup");
634:
1.10 downsj 635: for (p = flog; *p != '\0'; p++) {
636: if (*p == '/')
637: *p = '_';
638: }
639: fname = (char *) malloc(strlen(STATS_DIR) + strlen(flog) + 17);
1.11 downsj 640: if (fname == NULL)
641: err(1, "malloc");
642:
1.10 downsj 643: sprintf(fname, "%s/newsyslog.%s.size", STATS_DIR, flog);
644:
645: /* ..if it doesn't exist, simply record the current size. */
646: if ((sb.st_size == 0) || stat(fname, &tsb) < 0)
647: goto update;
648:
649: fp = fopen(fname, "r");
650: if (fp == NULL) {
1.11 downsj 651: warn(fname);
1.10 downsj 652: goto cleanup;
653: }
654: #ifdef QUAD_OFF_T
655: if (fscanf(fp, "%qd\n", &osize) != 1) {
656: #else
657: if (fscanf(fp, "%ld\n", &osize) != 1) {
658: #endif /* QUAD_OFF_T */
659: fclose(fp);
660: goto update;
661: }
662:
663: fclose(fp);
664:
665: /* If the file is smaller, mark the entire thing as changed. */
666: if (sb.st_size < osize)
667: osize = 0;
668:
669: /* Now see if current size is larger. */
670: if (sb.st_size > osize) {
671: rb = (char *) malloc(sb.st_size - osize);
1.11 downsj 672: if (rb == NULL)
673: err(1, "malloc");
1.10 downsj 674:
675: /* Open logfile, seek. */
676: fp = fopen(log, "r");
677: if (fp == NULL) {
1.11 downsj 678: warn(log);
1.10 downsj 679: goto cleanup;
680: }
681: fseek(fp, osize, SEEK_SET);
682: rd = fread(rb, 1, sb.st_size - osize, fp);
683: if (rd < 1) {
1.11 downsj 684: warn("fread");
1.10 downsj 685: fclose(fp);
686: goto cleanup;
687: }
688:
689: /* Send message. */
690: fclose(fp);
691:
692: fp = openmail();
693: if (fp == NULL) {
1.11 downsj 694: warn("openmail");
1.10 downsj 695: goto cleanup;
696: }
697: fprintf(fp, "To: %s\nSubject: LOGFILE NOTIFICATION: %s\n\n\n",
698: whom, log);
699: fwrite(rb, 1, rd, fp);
700: fputs("\n\n", fp);
701:
702: closemail(fp);
703: }
704: update:
705: /* Reopen for writing and update file. */
706: fp = fopen(fname, "w");
707: if (fp == NULL) {
1.11 downsj 708: warn(fname);
1.10 downsj 709: goto cleanup;
710: }
711: #ifdef QUAD_OFF_T
712: fprintf(fp, "%qd\n", sb.st_size);
713: #else
714: fprintf(fp, "%ld\n", sb.st_size);
715: #endif /* QUAD_OFF_T */
716: fclose(fp);
717:
718: cleanup:
719: free(flog);
720: free(fname);
721: if (rb != NULL)
722: free(rb);
723: }
724:
725: FILE *openmail()
726: {
727: char *cmdbuf;
728: FILE *ret;
729:
730: cmdbuf = (char *) malloc(strlen(SENDMAIL) + 3);
731: if (cmdbuf == NULL)
732: return(NULL);
733:
734: sprintf(cmdbuf, "%s -t", SENDMAIL);
735: ret = popen(cmdbuf, "w");
736:
737: free(cmdbuf);
738: return(ret);
739: }
740:
741: void closemail(pfp)
742: FILE *pfp;
743: {
744: pclose(pfp);
1.16 millert 745: }
746:
747: void child_killer(signum)
748: int signum;
749: {
750: int status;
751:
752: while (waitpid(-1, &status, WNOHANG) > 0)
753: ;
1.1 deraadt 754: }