Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 1.5
1.5 ! deraadt 1: /* $OpenBSD: newsyslog.c,v 1.4 1996/07/22 10:09:17 deraadt Exp $ */
1.3 deraadt 2:
1.1 deraadt 3: /*
4: * This file contains changes from the Open Software Foundation.
5: */
6:
7: /*
8:
9: Copyright 1988, 1989 by the Massachusetts Institute of Technology
10:
11: Permission to use, copy, modify, and distribute this software
12: and its documentation for any purpose and without fee is
13: hereby granted, provided that the above copyright notice
14: appear in all copies and that both that copyright notice and
15: this permission notice appear in supporting documentation,
16: and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
17: used in advertising or publicity pertaining to distribution
18: of the software without specific, written prior permission.
19: M.I.T. and the M.I.T. S.I.P.B. make no representations about
20: the suitability of this software for any purpose. It is
21: provided "as is" without express or implied warranty.
22:
23: */
24:
25: /*
26: * newsyslog - roll over selected logs at the appropriate time,
27: * keeping the a specified number of backup files around.
28: *
29: */
30:
31: #ifndef lint
1.5 ! deraadt 32: static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.4 1996/07/22 10:09:17 deraadt Exp $";
1.1 deraadt 33: #endif /* not lint */
34:
35: #ifndef CONF
36: #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */
37: #endif
38: #ifndef PIDFILE
39: #define PIDFILE "/etc/syslog.pid"
40: #endif
41: #ifndef COMPRESS
42: #define COMPRESS "/usr/ucb/compress" /* File compression program */
43: #endif
44: #ifndef COMPRESS_POSTFIX
45: #define COMPRESS_POSTFIX ".Z"
46: #endif
47:
48: #include <stdio.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include <ctype.h>
52: #include <signal.h>
53: #include <pwd.h>
54: #include <grp.h>
55: #include <sys/types.h>
56: #include <sys/time.h>
57: #include <sys/stat.h>
58: #include <sys/param.h>
59: #include <sys/wait.h>
60:
61: #define kbytes(size) (((size) + 1023) >> 10)
62: #ifdef _IBMR2
63: /* Calculates (db * DEV_BSIZE) */
64: #define dbtob(db) ((unsigned)(db) << UBSHIFT)
65: #endif
66:
67: #define CE_COMPACT 1 /* Compact the achived log files */
68: #define CE_BINARY 2 /* Logfile is in binary, don't add */
69: /* status messages */
70: #define NONE -1
71:
72: struct conf_entry {
73: char *log; /* Name of the log */
74: int uid; /* Owner of log */
75: int gid; /* Group of log */
76: int numlogs; /* Number of logs to keep */
77: int size; /* Size cutoff to trigger trimming the log */
78: int hours; /* Hours between log trimming */
79: int permissions; /* File permissions on the log */
80: int flags; /* Flags (CE_COMPACT & CE_BINARY) */
81: struct conf_entry *next; /* Linked list pointer */
82: };
83:
84: extern int optind;
85: extern char *optarg;
86: extern char *malloc();
87: extern uid_t getuid(),geteuid();
88: extern time_t time();
89:
90: char *progname; /* contains argv[0] */
91: int verbose = 0; /* Print out what's going on */
92: int needroot = 1; /* Root privs are necessary */
93: int noaction = 0; /* Don't do anything, just show it */
94: char *conf = CONF; /* Configuration file to use */
95: time_t timenow;
96: int syslog_pid; /* read in from /etc/syslog.pid */
97: #define MIN_PID 3
98: #define MAX_PID 65534
99: char hostname[64]; /* hostname */
100: char *daytime; /* timenow in human readable form */
101:
102:
103: struct conf_entry *parse_file();
104: char *sob(), *son(), *strdup(), *missing_field();
105:
106: main(argc,argv)
107: int argc;
108: char **argv;
109: {
110: struct conf_entry *p, *q;
111:
112: PRS(argc,argv);
113: if (needroot && getuid() && geteuid()) {
114: fprintf(stderr,"%s: must have root privs\n",progname);
115: exit(1);
116: }
117: p = q = parse_file();
118: while (p) {
119: do_entry(p);
120: p=p->next;
121: free((char *) q);
122: q=p;
123: }
124: exit(0);
125: }
126:
127: do_entry(ent)
128: struct conf_entry *ent;
129:
130: {
131: int size, modtime;
132:
133: if (verbose) {
134: if (ent->flags & CE_COMPACT)
135: printf("%s <%dZ>: ",ent->log,ent->numlogs);
136: else
137: printf("%s <%d>: ",ent->log,ent->numlogs);
138: }
139: size = sizefile(ent->log);
140: modtime = age_old_log(ent->log);
141: if (size < 0) {
142: if (verbose)
143: printf("does not exist.\n");
144: } else {
145: if (verbose && (ent->size > 0))
146: printf("size (Kb): %d [%d] ", size, ent->size);
147: if (verbose && (ent->hours > 0))
148: printf(" age (hr): %d [%d] ", modtime, ent->hours);
149: if (((ent->size > 0) && (size >= ent->size)) ||
150: ((ent->hours > 0) && ((modtime >= ent->hours)
151: || (modtime < 0)))) {
152: if (verbose)
153: printf("--> trimming log....\n");
154: if (noaction && !verbose) {
155: if (ent->flags & CE_COMPACT)
156: printf("%s <%dZ>: trimming",
157: ent->log,ent->numlogs);
158: else
159: printf("%s <%d>: trimming",
160: ent->log,ent->numlogs);
161: }
162: dotrim(ent->log, ent->numlogs, ent->flags,
163: ent->permissions, ent->uid, ent->gid);
164: } else {
165: if (verbose)
166: printf("--> skipping\n");
167: }
168: }
169: }
170:
171: PRS(argc,argv)
172: int argc;
173: char **argv;
174: {
175: int c;
176: FILE *f;
177: char line[BUFSIZ];
178: char *p;
179:
180: progname = argv[0];
181: timenow = time((time_t *) 0);
182: daytime = ctime(&timenow) + 4;
1.2 deraadt 183: daytime[15] = '\0';
1.1 deraadt 184:
185: /* Let's find the pid of syslogd */
186: syslog_pid = 0;
187: f = fopen(PIDFILE,"r");
188: if (f && fgets(line,BUFSIZ,f))
189: syslog_pid = atoi(line);
190: if (f)
191: (void)fclose(f);
192:
193: /* Let's get our hostname */
194: (void) gethostname(hostname, sizeof(hostname));
195:
196: /* Truncate domain */
197: if (p = strchr(hostname, '.')) {
198: *p = '\0';
199: }
200:
201: optind = 1; /* Start options parsing */
202: while ((c=getopt(argc,argv,"nrvf:t:")) != EOF)
203: switch (c) {
204: case 'n':
205: noaction++; /* This implies needroot as off */
206: /* fall through */
207: case 'r':
208: needroot = 0;
209: break;
210: case 'v':
211: verbose++;
212: break;
213: case 'f':
214: conf = optarg;
215: break;
216: default:
217: usage();
218: }
219: }
220:
221: usage()
222: {
223: fprintf(stderr,
224: "Usage: %s <-nrv> <-f config-file>\n", progname);
225: exit(1);
226: }
227:
228: /* Parse a configuration file and return a linked list of all the logs
229: * to process
230: */
231: struct conf_entry *parse_file()
232: {
233: FILE *f;
234: char line[BUFSIZ], *parse, *q;
235: char *errline, *group;
236: struct conf_entry *first = NULL;
237: struct conf_entry *working;
238: struct passwd *pass;
239: struct group *grp;
240:
241: if (strcmp(conf,"-"))
242: f = fopen(conf,"r");
243: else
244: f = stdin;
245: if (!f) {
246: (void) fprintf(stderr,"%s: ",progname);
247: perror(conf);
248: exit(1);
249: }
250: while (fgets(line,BUFSIZ,f)) {
251: if ((line[0]== '\n') || (line[0] == '#'))
252: continue;
253: errline = strdup(line);
254: if (!first) {
255: working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
256: first = working;
257: } else {
258: working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
259: working = working->next;
260: }
261:
262: q = parse = missing_field(sob(line),errline);
263: *(parse = son(line)) = '\0';
264: working->log = strdup(q);
265:
266: q = parse = missing_field(sob(++parse),errline);
267: *(parse = son(parse)) = '\0';
268: if ((group = strchr(q, '.')) != NULL) {
269: *group++ = '\0';
270: if (*q) {
271: if (!(isnumber(q))) {
272: if ((pass = getpwnam(q)) == NULL) {
273: fprintf(stderr,
274: "Error in config file; unknown user:\n");
275: fputs(errline,stderr);
276: exit(1);
277: }
278: working->uid = pass->pw_uid;
279: } else
280: working->uid = atoi(q);
281: } else
282: working->uid = NONE;
283:
284: q = group;
285: if (*q) {
286: if (!(isnumber(q))) {
287: if ((grp = getgrnam(q)) == NULL) {
288: fprintf(stderr,
289: "Error in config file; unknown group:\n");
290: fputs(errline,stderr);
291: exit(1);
292: }
293: working->gid = grp->gr_gid;
294: } else
295: working->gid = atoi(q);
296: } else
297: working->gid = NONE;
298:
299: q = parse = missing_field(sob(++parse),errline);
300: *(parse = son(parse)) = '\0';
301: }
302: else
303: working->uid = working->gid = NONE;
304:
305: if (!sscanf(q,"%o",&working->permissions)) {
306: fprintf(stderr,
307: "Error in config file; bad permissions:\n");
308: fputs(errline,stderr);
309: exit(1);
310: }
311:
312: q = parse = missing_field(sob(++parse),errline);
313: *(parse = son(parse)) = '\0';
314: if (!sscanf(q,"%d",&working->numlogs)) {
315: fprintf(stderr,
316: "Error in config file; bad number:\n");
317: fputs(errline,stderr);
318: exit(1);
319: }
320:
321: q = parse = missing_field(sob(++parse),errline);
322: *(parse = son(parse)) = '\0';
323: if (isdigit(*q))
324: working->size = atoi(q);
325: else
326: working->size = -1;
327:
328: q = parse = missing_field(sob(++parse),errline);
329: *(parse = son(parse)) = '\0';
330: if (isdigit(*q))
331: working->hours = atoi(q);
332: else
333: working->hours = -1;
334:
335: q = parse = sob(++parse); /* Optional field */
336: *(parse = son(parse)) = '\0';
337: working->flags = 0;
338: while (q && *q && !isspace(*q)) {
339: if ((*q == 'Z') || (*q == 'z'))
340: working->flags |= CE_COMPACT;
341: else if ((*q == 'B') || (*q == 'b'))
342: working->flags |= CE_BINARY;
343: else {
344: fprintf(stderr,
345: "Illegal flag in config file -- %c\n",
346: *q);
347: exit(1);
348: }
349: q++;
350: }
351:
352: free(errline);
353: }
354: if (working)
355: working->next = (struct conf_entry *) NULL;
356: (void) fclose(f);
357: return(first);
358: }
359:
360: char *missing_field(p,errline)
361: char *p,*errline;
362: {
363: if (!p || !*p) {
364: fprintf(stderr,"Missing field in config file:\n");
365: fputs(errline,stderr);
366: exit(1);
367: }
368: return(p);
369: }
370:
371: dotrim(log,numdays,flags,perm,owner_uid,group_gid)
372: char *log;
373: int numdays;
374: int flags;
375: int perm;
376: int owner_uid;
377: int group_gid;
378: {
379: char file1[128], file2[128];
380: char zfile1[128], zfile2[128];
381: int fd;
382: struct stat st;
383:
384: #ifdef _IBMR2
385: /* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */
386: /* change it to be owned by uid -1, instead of leaving it as is, as it is */
387: /* supposed to. */
388: if (owner_uid == -1)
389: owner_uid = geteuid();
390: #endif
391:
392: /* Remove oldest log */
393: (void) sprintf(file1,"%s.%d",log,numdays);
394: (void) strcpy(zfile1, file1);
395: (void) strcat(zfile1, COMPRESS_POSTFIX);
396:
397: if (noaction) {
398: printf("rm -f %s\n", file1);
399: printf("rm -f %s\n", zfile1);
400: } else {
401: (void) unlink(file1);
402: (void) unlink(zfile1);
403: }
404:
405: /* Move down log files */
406: while (numdays--) {
407: (void) strcpy(file2,file1);
408: (void) sprintf(file1,"%s.%d",log,numdays);
409: (void) strcpy(zfile1, file1);
410: (void) strcpy(zfile2, file2);
411: if (lstat(file1, &st)) {
412: (void) strcat(zfile1, COMPRESS_POSTFIX);
413: (void) strcat(zfile2, COMPRESS_POSTFIX);
414: if (lstat(zfile1, &st)) continue;
415: }
416: if (noaction) {
417: printf("mv %s %s\n",zfile1,zfile2);
418: printf("chmod %o %s\n", perm, zfile2);
419: printf("chown %d.%d %s\n",
420: owner_uid, group_gid, zfile2);
421: } else {
422: (void) rename(zfile1, zfile2);
423: (void) chmod(zfile2, perm);
424: (void) chown(zfile2, owner_uid, group_gid);
425: }
426: }
427: if (!noaction && !(flags & CE_BINARY))
428: (void) log_trim(log); /* Report the trimming to the old log */
429:
1.5 ! deraadt 430: if (numdays == -1) {
! 431: if (noaction)
! 432: printf("rm %s\n",log);
! 433: else
! 434: (void) unlink(log);
! 435: } else {
! 436: if (noaction)
! 437: printf("mv %s to %s\n",log,file1);
! 438: else
! 439: (void) rename(log,file1);
! 440: }
! 441:
1.1 deraadt 442: if (noaction)
443: printf("Start new log...");
444: else {
445: fd = creat(log,perm);
446: if (fd < 0) {
447: perror("can't start new log");
448: exit(1);
449: }
450: if (fchown(fd, owner_uid, group_gid)) {
451: perror("can't chmod new log file");
452: exit(1);
453: }
454: (void) close(fd);
455: if (!(flags & CE_BINARY))
456: if (log_trim(log)) { /* Add status message */
457: perror("can't add status message to log");
458: exit(1);
459: }
460: }
461: if (noaction)
462: printf("chmod %o %s...",perm,log);
463: else
464: (void) chmod(log,perm);
465: if (noaction)
466: printf("kill -HUP %d (syslogd)\n",syslog_pid);
467: else
468: if (syslog_pid < MIN_PID || syslog_pid > MAX_PID) {
469: fprintf(stderr,"%s: preposterous process number: %d\n",
470: progname, syslog_pid);
471: } else if (kill(syslog_pid,SIGHUP)) {
472: fprintf(stderr,"%s: ",progname);
473: perror("warning - could not restart syslogd");
474: }
475: if (flags & CE_COMPACT) {
476: if (noaction)
477: printf("Compress %s.0\n",log);
478: else
479: compress_log(log);
480: }
481: }
482:
483: /* Log the fact that the logs were turned over */
484: log_trim(log)
485: char *log;
486: {
487: FILE *f;
488: if ((f = fopen(log,"a")) == NULL)
489: return(-1);
490: fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
491: daytime, hostname, getpid());
492: if (fclose(f) == EOF) {
493: perror("log_trim: fclose:");
494: exit(1);
495: }
496: return(0);
497: }
498:
499: /* Fork of /usr/ucb/compress to compress the old log file */
500: compress_log(log)
501: char *log;
502: {
503: int pid;
504: char tmp[128];
505:
506: pid = fork();
507: (void) sprintf(tmp,"%s.0",log);
508: if (pid < 0) {
509: fprintf(stderr,"%s: ",progname);
510: perror("fork");
511: exit(1);
512: } else if (!pid) {
513: (void) execl(COMPRESS,"compress","-f",tmp,0);
514: fprintf(stderr,"%s: ",progname);
515: perror(COMPRESS);
516: exit(1);
517: }
518: }
519:
520: /* Return size in kilobytes of a file */
521: int sizefile(file)
522: char *file;
523: {
524: struct stat sb;
525:
526: if (stat(file,&sb) < 0)
527: return(-1);
528: return(kbytes(dbtob(sb.st_blocks)));
529: }
530:
531: /* Return the age of old log file (file.0) */
532: int age_old_log(file)
533: char *file;
534: {
535: struct stat sb;
536: char tmp[MAXPATHLEN+3];
537:
538: (void) strcpy(tmp,file);
539: if (stat(strcat(tmp,".0"),&sb) < 0)
540: if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
541: return(-1);
542: return( (int) (timenow - sb.st_mtime + 1800) / 3600);
543: }
544:
545:
546: #ifndef OSF
547: /* Duplicate a string using malloc */
548:
549: char *strdup(strp)
550: register char *strp;
551: {
552: register char *cp;
553:
554: if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL)
555: abort();
556: return(strcpy (cp, strp));
557: }
558: #endif
559:
560: /* Skip Over Blanks */
561: char *sob(p)
562: register char *p;
563: {
564: while (p && *p && isspace(*p))
565: p++;
566: return(p);
567: }
568:
569: /* Skip Over Non-Blanks */
570: char *son(p)
571: register char *p;
572: {
573: while (p && *p && !isspace(*p))
574: p++;
575: return(p);
576: }
577:
578:
579: /* Check if string is actually a number */
580:
581: isnumber(string)
582: char *string;
583: {
584: while (*string != '\0') {
585: if (*string < '0' || *string > '9') return(0);
586: string++;
587: }
588: return(1);
589: }