Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 1.6
1.6 ! tholo 1: /* $OpenBSD: newsyslog.c,v 1.5 1996/08/31 14:20:36 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.6 ! tholo 32: static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.5 1996/08/31 14:20:36 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;
1.6 ! tholo 383: int days = numdays;
1.1 deraadt 384:
385: #ifdef _IBMR2
386: /* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */
387: /* change it to be owned by uid -1, instead of leaving it as is, as it is */
388: /* supposed to. */
389: if (owner_uid == -1)
390: owner_uid = geteuid();
391: #endif
392:
393: /* Remove oldest log */
394: (void) sprintf(file1,"%s.%d",log,numdays);
395: (void) strcpy(zfile1, file1);
396: (void) strcat(zfile1, COMPRESS_POSTFIX);
397:
398: if (noaction) {
399: printf("rm -f %s\n", file1);
400: printf("rm -f %s\n", zfile1);
401: } else {
402: (void) unlink(file1);
403: (void) unlink(zfile1);
404: }
405:
406: /* Move down log files */
407: while (numdays--) {
408: (void) strcpy(file2,file1);
409: (void) sprintf(file1,"%s.%d",log,numdays);
410: (void) strcpy(zfile1, file1);
411: (void) strcpy(zfile2, file2);
412: if (lstat(file1, &st)) {
413: (void) strcat(zfile1, COMPRESS_POSTFIX);
414: (void) strcat(zfile2, COMPRESS_POSTFIX);
415: if (lstat(zfile1, &st)) continue;
416: }
417: if (noaction) {
418: printf("mv %s %s\n",zfile1,zfile2);
419: printf("chmod %o %s\n", perm, zfile2);
420: printf("chown %d.%d %s\n",
421: owner_uid, group_gid, zfile2);
422: } else {
423: (void) rename(zfile1, zfile2);
424: (void) chmod(zfile2, perm);
425: (void) chown(zfile2, owner_uid, group_gid);
426: }
427: }
428: if (!noaction && !(flags & CE_BINARY))
429: (void) log_trim(log); /* Report the trimming to the old log */
430:
1.6 ! tholo 431: if (days == 0) {
1.5 deraadt 432: if (noaction)
433: printf("rm %s\n",log);
434: else
435: (void) unlink(log);
436: } else {
437: if (noaction)
438: printf("mv %s to %s\n",log,file1);
439: else
440: (void) rename(log,file1);
441: }
442:
1.1 deraadt 443: if (noaction)
444: printf("Start new log...");
445: else {
446: fd = creat(log,perm);
447: if (fd < 0) {
448: perror("can't start new log");
449: exit(1);
450: }
451: if (fchown(fd, owner_uid, group_gid)) {
452: perror("can't chmod new log file");
453: exit(1);
454: }
455: (void) close(fd);
456: if (!(flags & CE_BINARY))
457: if (log_trim(log)) { /* Add status message */
458: perror("can't add status message to log");
459: exit(1);
460: }
461: }
462: if (noaction)
463: printf("chmod %o %s...",perm,log);
464: else
465: (void) chmod(log,perm);
466: if (noaction)
467: printf("kill -HUP %d (syslogd)\n",syslog_pid);
468: else
469: if (syslog_pid < MIN_PID || syslog_pid > MAX_PID) {
470: fprintf(stderr,"%s: preposterous process number: %d\n",
471: progname, syslog_pid);
472: } else if (kill(syslog_pid,SIGHUP)) {
473: fprintf(stderr,"%s: ",progname);
474: perror("warning - could not restart syslogd");
475: }
476: if (flags & CE_COMPACT) {
477: if (noaction)
478: printf("Compress %s.0\n",log);
479: else
480: compress_log(log);
481: }
482: }
483:
484: /* Log the fact that the logs were turned over */
485: log_trim(log)
486: char *log;
487: {
488: FILE *f;
489: if ((f = fopen(log,"a")) == NULL)
490: return(-1);
491: fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
492: daytime, hostname, getpid());
493: if (fclose(f) == EOF) {
494: perror("log_trim: fclose:");
495: exit(1);
496: }
497: return(0);
498: }
499:
500: /* Fork of /usr/ucb/compress to compress the old log file */
501: compress_log(log)
502: char *log;
503: {
504: int pid;
505: char tmp[128];
506:
507: pid = fork();
508: (void) sprintf(tmp,"%s.0",log);
509: if (pid < 0) {
510: fprintf(stderr,"%s: ",progname);
511: perror("fork");
512: exit(1);
513: } else if (!pid) {
514: (void) execl(COMPRESS,"compress","-f",tmp,0);
515: fprintf(stderr,"%s: ",progname);
516: perror(COMPRESS);
517: exit(1);
518: }
519: }
520:
521: /* Return size in kilobytes of a file */
522: int sizefile(file)
523: char *file;
524: {
525: struct stat sb;
526:
527: if (stat(file,&sb) < 0)
528: return(-1);
529: return(kbytes(dbtob(sb.st_blocks)));
530: }
531:
532: /* Return the age of old log file (file.0) */
533: int age_old_log(file)
534: char *file;
535: {
536: struct stat sb;
537: char tmp[MAXPATHLEN+3];
538:
539: (void) strcpy(tmp,file);
540: if (stat(strcat(tmp,".0"),&sb) < 0)
541: if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
542: return(-1);
543: return( (int) (timenow - sb.st_mtime + 1800) / 3600);
544: }
545:
546:
547: #ifndef OSF
548: /* Duplicate a string using malloc */
549:
550: char *strdup(strp)
551: register char *strp;
552: {
553: register char *cp;
554:
555: if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL)
556: abort();
557: return(strcpy (cp, strp));
558: }
559: #endif
560:
561: /* Skip Over Blanks */
562: char *sob(p)
563: register char *p;
564: {
565: while (p && *p && isspace(*p))
566: p++;
567: return(p);
568: }
569:
570: /* Skip Over Non-Blanks */
571: char *son(p)
572: register char *p;
573: {
574: while (p && *p && !isspace(*p))
575: p++;
576: return(p);
577: }
578:
579:
580: /* Check if string is actually a number */
581:
582: isnumber(string)
583: char *string;
584: {
585: while (*string != '\0') {
586: if (*string < '0' || *string > '9') return(0);
587: string++;
588: }
589: return(1);
590: }