Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 1.19
1.19 ! kstailey 1: /* $OpenBSD: newsyslog.c,v 1.18 1999/03/08 03:16:34 millert 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.19 ! kstailey 64: static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.18 1999/03/08 03:16:34 millert 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:
1.19 ! kstailey 196: if (verbose)
! 197: printf("%s <%d%s>: ", ent->log, ent->numlogs,
! 198: (ent->flags & CE_COMPACT) ? "Z" : "");
1.1 deraadt 199: size = sizefile(ent->log);
200: modtime = age_old_log(ent->log);
201: if (size < 0) {
202: if (verbose)
203: printf("does not exist.\n");
204: } else {
205: if (verbose && (ent->size > 0))
206: printf("size (Kb): %d [%d] ", size, ent->size);
207: if (verbose && (ent->hours > 0))
208: printf(" age (hr): %d [%d] ", modtime, ent->hours);
1.10 downsj 209: if (monitor && ent->flags & CE_MONITOR)
210: domonitor(ent->log, ent->whom);
211: if (!monitor && ((ent->size > 0) && (size >= ent->size)) ||
1.1 deraadt 212: ((ent->hours > 0) && ((modtime >= ent->hours)
213: || (modtime < 0)))) {
214: if (verbose)
215: printf("--> trimming log....\n");
1.19 ! kstailey 216: if (noaction && !verbose)
! 217: printf("%s <%d%s>: ", ent->log, ent->numlogs,
! 218: (ent->flags & CE_COMPACT) ? "Z" : "");
1.1 deraadt 219: dotrim(ent->log, ent->numlogs, ent->flags,
1.14 millert 220: ent->permissions, ent->uid, ent->gid, pid);
1.1 deraadt 221: } else {
222: if (verbose)
223: printf("--> skipping\n");
224: }
225: }
226: }
227:
1.9 downsj 228: void PRS(argc, argv)
1.1 deraadt 229: int argc;
230: char **argv;
231: {
232: int c;
233: char *p;
234:
1.12 kstailey 235: timenow = time(NULL);
1.1 deraadt 236: daytime = ctime(&timenow) + 4;
1.2 deraadt 237: daytime[15] = '\0';
1.1 deraadt 238:
239: /* Let's get our hostname */
240: (void) gethostname(hostname, sizeof(hostname));
241:
242: /* Truncate domain */
1.9 downsj 243: p = strchr(hostname, '.');
244: if (p)
1.1 deraadt 245: *p = '\0';
246:
247: optind = 1; /* Start options parsing */
1.10 downsj 248: while ((c = getopt(argc,argv,"nrvmf:t:")) != -1) {
1.1 deraadt 249: switch (c) {
250: case 'n':
251: noaction++; /* This implies needroot as off */
252: /* fall through */
253: case 'r':
254: needroot = 0;
255: break;
256: case 'v':
257: verbose++;
258: break;
259: case 'f':
260: conf = optarg;
261: break;
1.10 downsj 262: case 'm':
263: monitor++;
264: break;
1.1 deraadt 265: default:
266: usage();
267: }
268: }
1.9 downsj 269: }
1.1 deraadt 270:
1.9 downsj 271: void usage()
1.1 deraadt 272: {
1.14 millert 273: fprintf(stderr, "usage: %s [-nrvm] [-f config-file]\n", __progname);
274: exit(1);
1.1 deraadt 275: }
276:
277: /* Parse a configuration file and return a linked list of all the logs
278: * to process
279: */
280: struct conf_entry *parse_file()
281: {
282: FILE *f;
283: char line[BUFSIZ], *parse, *q;
284: char *errline, *group;
285: struct conf_entry *first = NULL;
286: struct conf_entry *working;
287: struct passwd *pass;
288: struct group *grp;
289:
290: if (strcmp(conf,"-"))
291: f = fopen(conf,"r");
292: else
293: f = stdin;
1.11 downsj 294: if (!f)
295: err(1, conf);
296:
1.1 deraadt 297: while (fgets(line,BUFSIZ,f)) {
298: if ((line[0]== '\n') || (line[0] == '#'))
299: continue;
300: errline = strdup(line);
1.11 downsj 301: if (errline == NULL)
302: err(1, "strdup");
1.1 deraadt 303: if (!first) {
304: working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
1.11 downsj 305: if (working == NULL)
306: err(1, "malloc");
1.1 deraadt 307: first = working;
308: } else {
309: working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
1.11 downsj 310: if (working->next == NULL)
311: err(1, "malloc");
1.1 deraadt 312: working = working->next;
313: }
314:
315: q = parse = missing_field(sob(line),errline);
316: *(parse = son(line)) = '\0';
317: working->log = strdup(q);
1.11 downsj 318: if (working->log == NULL)
319: err(1, "strdup");
1.1 deraadt 320:
321: q = parse = missing_field(sob(++parse),errline);
322: *(parse = son(parse)) = '\0';
323: if ((group = strchr(q, '.')) != NULL) {
324: *group++ = '\0';
325: if (*q) {
1.10 downsj 326: if (!(isnumberstr(q))) {
1.11 downsj 327: if ((pass = getpwnam(q)) == NULL)
328: errx(1, "Error in config file; unknown user: %s", q);
1.1 deraadt 329: working->uid = pass->pw_uid;
330: } else
331: working->uid = atoi(q);
332: } else
333: working->uid = NONE;
334:
335: q = group;
336: if (*q) {
1.10 downsj 337: if (!(isnumberstr(q))) {
1.11 downsj 338: if ((grp = getgrnam(q)) == NULL)
339: errx(1, "Error in config file; unknown group: %s", q);
1.1 deraadt 340: working->gid = grp->gr_gid;
341: } else
342: working->gid = atoi(q);
343: } else
344: working->gid = NONE;
345:
346: q = parse = missing_field(sob(++parse),errline);
347: *(parse = son(parse)) = '\0';
1.11 downsj 348: } else
1.1 deraadt 349: working->uid = working->gid = NONE;
350:
1.11 downsj 351: if (!sscanf(q,"%o",&working->permissions))
352: errx(1, "Error in config file; bad permissions: %s", q);
1.1 deraadt 353:
354: q = parse = missing_field(sob(++parse),errline);
355: *(parse = son(parse)) = '\0';
1.18 millert 356: if (!sscanf(q,"%d",&working->numlogs) || working->numlogs < 0)
1.11 downsj 357: errx(1, "Error in config file; bad number: %s", q);
1.1 deraadt 358:
359: q = parse = missing_field(sob(++parse),errline);
360: *(parse = son(parse)) = '\0';
361: if (isdigit(*q))
362: working->size = atoi(q);
363: else
364: working->size = -1;
365:
366: q = parse = missing_field(sob(++parse),errline);
367: *(parse = son(parse)) = '\0';
368: if (isdigit(*q))
369: working->hours = atoi(q);
370: else
371: working->hours = -1;
372:
373: q = parse = sob(++parse); /* Optional field */
374: *(parse = son(parse)) = '\0';
375: working->flags = 0;
376: while (q && *q && !isspace(*q)) {
377: if ((*q == 'Z') || (*q == 'z'))
378: working->flags |= CE_COMPACT;
379: else if ((*q == 'B') || (*q == 'b'))
380: working->flags |= CE_BINARY;
1.10 downsj 381: else if ((*q == 'M') || (*q == 'm'))
382: working->flags |= CE_MONITOR;
1.11 downsj 383: else
384: errx(1, "Illegal flag in config file: %c", *q);
1.1 deraadt 385: q++;
386: }
1.10 downsj 387:
388: working->whom = NULL;
389: if (working->flags & CE_MONITOR) { /* Optional field */
390: q = parse = sob(++parse);
391: *(parse = son(parse)) = '\0';
392:
393: working->whom = strdup(q);
1.11 downsj 394: if (working->log == NULL)
395: err(1, "strdup");
1.10 downsj 396: }
1.14 millert 397:
398: working->pidfile = PIDFILE;
399: q = parse = sob(++parse); /* Optional field */
400: *(parse = son(parse)) = '\0';
401: if (q && *q != '\0') {
402: working->pidfile = strdup(q);
403: if (working->pidfile == NULL)
404: err(1, "strdup");
405: }
1.1 deraadt 406:
407: free(errline);
408: }
409: if (working)
1.12 kstailey 410: working->next = NULL;
1.1 deraadt 411: (void) fclose(f);
412: return(first);
413: }
414:
1.9 downsj 415: char *missing_field(p, errline)
1.1 deraadt 416: char *p,*errline;
417: {
418: if (!p || !*p) {
1.14 millert 419: warnx("Missing field in config file line:");
1.11 downsj 420: fputs(errline, stderr);
1.1 deraadt 421: exit(1);
422: }
423: return(p);
424: }
425:
1.14 millert 426: void dotrim(log, numdays, flags, perm, owner_uid, group_gid, daemon_pid)
1.1 deraadt 427: char *log;
428: int numdays;
429: int flags;
430: int perm;
431: int owner_uid;
432: int group_gid;
1.14 millert 433: int daemon_pid;
1.1 deraadt 434: {
1.7 deraadt 435: char file1[MAXPATHLEN], file2[MAXPATHLEN];
436: char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
1.1 deraadt 437: int fd;
438: struct stat st;
1.6 tholo 439: int days = numdays;
1.1 deraadt 440:
441: /* Remove oldest log */
442: (void) sprintf(file1,"%s.%d",log,numdays);
443: (void) strcpy(zfile1, file1);
444: (void) strcat(zfile1, COMPRESS_POSTFIX);
445:
446: if (noaction) {
447: printf("rm -f %s\n", file1);
448: printf("rm -f %s\n", zfile1);
449: } else {
450: (void) unlink(file1);
451: (void) unlink(zfile1);
452: }
453:
454: /* Move down log files */
455: while (numdays--) {
456: (void) strcpy(file2,file1);
457: (void) sprintf(file1,"%s.%d",log,numdays);
458: (void) strcpy(zfile1, file1);
459: (void) strcpy(zfile2, file2);
460: if (lstat(file1, &st)) {
461: (void) strcat(zfile1, COMPRESS_POSTFIX);
462: (void) strcat(zfile2, COMPRESS_POSTFIX);
463: if (lstat(zfile1, &st)) continue;
464: }
465: if (noaction) {
466: printf("mv %s %s\n",zfile1,zfile2);
467: printf("chmod %o %s\n", perm, zfile2);
468: printf("chown %d.%d %s\n",
469: owner_uid, group_gid, zfile2);
470: } else {
471: (void) rename(zfile1, zfile2);
472: (void) chmod(zfile2, perm);
473: (void) chown(zfile2, owner_uid, group_gid);
474: }
475: }
476: if (!noaction && !(flags & CE_BINARY))
477: (void) log_trim(log); /* Report the trimming to the old log */
478:
1.6 tholo 479: if (days == 0) {
1.5 deraadt 480: if (noaction)
481: printf("rm %s\n",log);
482: else
483: (void) unlink(log);
484: } else {
485: if (noaction)
486: printf("mv %s to %s\n",log,file1);
487: else
488: (void) rename(log,file1);
489: }
490:
1.1 deraadt 491: if (noaction)
492: printf("Start new log...");
493: else {
494: fd = creat(log,perm);
1.11 downsj 495: if (fd < 0)
1.13 mickey 496: err(1, "can't start \'%s\' log", log);
1.11 downsj 497: if (fchown(fd, owner_uid, group_gid))
1.13 mickey 498: err(1, "can't chown \'%s\' log file", log);
1.1 deraadt 499: (void) close(fd);
500: if (!(flags & CE_BINARY))
1.11 downsj 501: if (log_trim(log)) /* Add status message */
1.13 mickey 502: err(1, "can't add status message to log \'%s\'", log);
1.1 deraadt 503: }
504: if (noaction)
505: printf("chmod %o %s...",perm,log);
506: else
507: (void) chmod(log,perm);
508: if (noaction)
1.14 millert 509: printf("kill -HUP %d\n",daemon_pid);
1.17 deraadt 510: else if (daemon_pid < MIN_PID)
1.14 millert 511: warnx("preposterous process number: %d", daemon_pid);
512: else if (kill(daemon_pid,SIGHUP))
1.17 deraadt 513: warnx("warning - could not HUP daemon");
514: if (flags & CE_COMPACT) {
515: if (noaction)
516: printf("Compress %s.0\n",log);
517: else
518: compress_log(log);
519: }
1.1 deraadt 520: }
521:
522: /* Log the fact that the logs were turned over */
1.9 downsj 523: int log_trim(log)
1.17 deraadt 524: char *log;
1.1 deraadt 525: {
526: FILE *f;
527: if ((f = fopen(log,"a")) == NULL)
528: return(-1);
529: fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
530: daytime, hostname, getpid());
531: if (fclose(f) == EOF) {
1.11 downsj 532: err(1, "log_trim: fclose");
1.1 deraadt 533: }
534: return(0);
535: }
536:
1.16 millert 537: /* Fork off compress or gzip to compress the old log file */
1.9 downsj 538: void compress_log(log)
1.1 deraadt 539: char *log;
540: {
541: int pid;
1.7 deraadt 542: char tmp[MAXPATHLEN];
1.1 deraadt 543:
544: pid = fork();
545: (void) sprintf(tmp,"%s.0",log);
546: if (pid < 0) {
1.11 downsj 547: err(1, "fork");
1.1 deraadt 548: } else if (!pid) {
549: (void) execl(COMPRESS,"compress","-f",tmp,0);
1.16 millert 550: warn(COMPRESS);
551: _exit(1);
1.1 deraadt 552: }
553: }
554:
555: /* Return size in kilobytes of a file */
556: int sizefile(file)
557: char *file;
558: {
559: struct stat sb;
560:
561: if (stat(file,&sb) < 0)
562: return(-1);
563: return(kbytes(dbtob(sb.st_blocks)));
564: }
565:
566: /* Return the age of old log file (file.0) */
567: int age_old_log(file)
568: char *file;
569: {
570: struct stat sb;
1.7 deraadt 571: char tmp[MAXPATHLEN];
1.1 deraadt 572:
573: (void) strcpy(tmp,file);
574: if (stat(strcat(tmp,".0"),&sb) < 0)
575: if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
576: return(-1);
577: return( (int) (timenow - sb.st_mtime + 1800) / 3600);
578: }
579:
580: /* Skip Over Blanks */
581: char *sob(p)
582: register char *p;
583: {
584: while (p && *p && isspace(*p))
585: p++;
586: return(p);
587: }
588:
589: /* Skip Over Non-Blanks */
590: char *son(p)
591: register char *p;
592: {
593: while (p && *p && !isspace(*p))
594: p++;
595: return(p);
596: }
597:
598:
599: /* Check if string is actually a number */
600:
1.10 downsj 601: int isnumberstr(string)
602: char *string;
1.1 deraadt 603: {
604: while (*string != '\0') {
1.9 downsj 605: if (!isdigit(*string++))
606: return(0);
1.1 deraadt 607: }
608: return(1);
1.10 downsj 609: }
610:
611: void domonitor(log, whom)
612: char *log, *whom;
613: {
614: struct stat sb, tsb;
615: char *fname, *flog, *p, *rb = NULL;
616: FILE *fp;
617: off_t osize;
618: int rd;
619:
620: if (stat(log, &sb) < 0)
621: return;
622:
623: flog = strdup(log);
1.11 downsj 624: if (flog == NULL)
625: err(1, "strdup");
626:
1.10 downsj 627: for (p = flog; *p != '\0'; p++) {
628: if (*p == '/')
629: *p = '_';
630: }
631: fname = (char *) malloc(strlen(STATS_DIR) + strlen(flog) + 17);
1.11 downsj 632: if (fname == NULL)
633: err(1, "malloc");
634:
1.10 downsj 635: sprintf(fname, "%s/newsyslog.%s.size", STATS_DIR, flog);
636:
637: /* ..if it doesn't exist, simply record the current size. */
638: if ((sb.st_size == 0) || stat(fname, &tsb) < 0)
639: goto update;
640:
641: fp = fopen(fname, "r");
642: if (fp == NULL) {
1.11 downsj 643: warn(fname);
1.10 downsj 644: goto cleanup;
645: }
646: #ifdef QUAD_OFF_T
647: if (fscanf(fp, "%qd\n", &osize) != 1) {
648: #else
649: if (fscanf(fp, "%ld\n", &osize) != 1) {
650: #endif /* QUAD_OFF_T */
651: fclose(fp);
652: goto update;
653: }
654:
655: fclose(fp);
656:
657: /* If the file is smaller, mark the entire thing as changed. */
658: if (sb.st_size < osize)
659: osize = 0;
660:
661: /* Now see if current size is larger. */
662: if (sb.st_size > osize) {
663: rb = (char *) malloc(sb.st_size - osize);
1.11 downsj 664: if (rb == NULL)
665: err(1, "malloc");
1.10 downsj 666:
667: /* Open logfile, seek. */
668: fp = fopen(log, "r");
669: if (fp == NULL) {
1.11 downsj 670: warn(log);
1.10 downsj 671: goto cleanup;
672: }
673: fseek(fp, osize, SEEK_SET);
674: rd = fread(rb, 1, sb.st_size - osize, fp);
675: if (rd < 1) {
1.11 downsj 676: warn("fread");
1.10 downsj 677: fclose(fp);
678: goto cleanup;
679: }
680:
681: /* Send message. */
682: fclose(fp);
683:
684: fp = openmail();
685: if (fp == NULL) {
1.11 downsj 686: warn("openmail");
1.10 downsj 687: goto cleanup;
688: }
689: fprintf(fp, "To: %s\nSubject: LOGFILE NOTIFICATION: %s\n\n\n",
690: whom, log);
691: fwrite(rb, 1, rd, fp);
692: fputs("\n\n", fp);
693:
694: closemail(fp);
695: }
696: update:
697: /* Reopen for writing and update file. */
698: fp = fopen(fname, "w");
699: if (fp == NULL) {
1.11 downsj 700: warn(fname);
1.10 downsj 701: goto cleanup;
702: }
703: #ifdef QUAD_OFF_T
704: fprintf(fp, "%qd\n", sb.st_size);
705: #else
706: fprintf(fp, "%ld\n", sb.st_size);
707: #endif /* QUAD_OFF_T */
708: fclose(fp);
709:
710: cleanup:
711: free(flog);
712: free(fname);
713: if (rb != NULL)
714: free(rb);
715: }
716:
717: FILE *openmail()
718: {
719: char *cmdbuf;
720: FILE *ret;
721:
722: cmdbuf = (char *) malloc(strlen(SENDMAIL) + 3);
723: if (cmdbuf == NULL)
724: return(NULL);
725:
726: sprintf(cmdbuf, "%s -t", SENDMAIL);
727: ret = popen(cmdbuf, "w");
728:
729: free(cmdbuf);
730: return(ret);
731: }
732:
733: void closemail(pfp)
734: FILE *pfp;
735: {
736: pclose(pfp);
1.16 millert 737: }
738:
739: void child_killer(signum)
740: int signum;
741: {
742: int status;
743:
744: while (waitpid(-1, &status, WNOHANG) > 0)
745: ;
1.1 deraadt 746: }