Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 1.1.1.1
1.1 deraadt 1: /*
2: * This file contains changes from the Open Software Foundation.
3: */
4:
5: /*
6:
7: Copyright 1988, 1989 by the Massachusetts Institute of Technology
8:
9: Permission to use, copy, modify, and distribute this software
10: and its documentation for any purpose and without fee is
11: hereby granted, provided that the above copyright notice
12: appear in all copies and that both that copyright notice and
13: this permission notice appear in supporting documentation,
14: and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
15: used in advertising or publicity pertaining to distribution
16: of the software without specific, written prior permission.
17: M.I.T. and the M.I.T. S.I.P.B. make no representations about
18: the suitability of this software for any purpose. It is
19: provided "as is" without express or implied warranty.
20:
21: */
22:
23: /*
24: * newsyslog - roll over selected logs at the appropriate time,
25: * keeping the a specified number of backup files around.
26: *
27: * $Source: /a/cvsroot/src/usr.bin/newsyslog/newsyslog.c,v $
28: * $Author: jtc $
29: */
30:
31: #ifndef lint
32: static char rcsid[] = "$Id: newsyslog.c,v 1.9 1995/01/21 21:53:46 jtc Exp $";
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;
183: daytime[16] = '\0';
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:
430: if (noaction)
431: printf("mv %s to %s\n",log,file1);
432: else
433: (void) rename(log,file1);
434: if (noaction)
435: printf("Start new log...");
436: else {
437: fd = creat(log,perm);
438: if (fd < 0) {
439: perror("can't start new log");
440: exit(1);
441: }
442: if (fchown(fd, owner_uid, group_gid)) {
443: perror("can't chmod new log file");
444: exit(1);
445: }
446: (void) close(fd);
447: if (!(flags & CE_BINARY))
448: if (log_trim(log)) { /* Add status message */
449: perror("can't add status message to log");
450: exit(1);
451: }
452: }
453: if (noaction)
454: printf("chmod %o %s...",perm,log);
455: else
456: (void) chmod(log,perm);
457: if (noaction)
458: printf("kill -HUP %d (syslogd)\n",syslog_pid);
459: else
460: if (syslog_pid < MIN_PID || syslog_pid > MAX_PID) {
461: fprintf(stderr,"%s: preposterous process number: %d\n",
462: progname, syslog_pid);
463: } else if (kill(syslog_pid,SIGHUP)) {
464: fprintf(stderr,"%s: ",progname);
465: perror("warning - could not restart syslogd");
466: }
467: if (flags & CE_COMPACT) {
468: if (noaction)
469: printf("Compress %s.0\n",log);
470: else
471: compress_log(log);
472: }
473: }
474:
475: /* Log the fact that the logs were turned over */
476: log_trim(log)
477: char *log;
478: {
479: FILE *f;
480: if ((f = fopen(log,"a")) == NULL)
481: return(-1);
482: fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
483: daytime, hostname, getpid());
484: if (fclose(f) == EOF) {
485: perror("log_trim: fclose:");
486: exit(1);
487: }
488: return(0);
489: }
490:
491: /* Fork of /usr/ucb/compress to compress the old log file */
492: compress_log(log)
493: char *log;
494: {
495: int pid;
496: char tmp[128];
497:
498: pid = fork();
499: (void) sprintf(tmp,"%s.0",log);
500: if (pid < 0) {
501: fprintf(stderr,"%s: ",progname);
502: perror("fork");
503: exit(1);
504: } else if (!pid) {
505: (void) execl(COMPRESS,"compress","-f",tmp,0);
506: fprintf(stderr,"%s: ",progname);
507: perror(COMPRESS);
508: exit(1);
509: }
510: }
511:
512: /* Return size in kilobytes of a file */
513: int sizefile(file)
514: char *file;
515: {
516: struct stat sb;
517:
518: if (stat(file,&sb) < 0)
519: return(-1);
520: return(kbytes(dbtob(sb.st_blocks)));
521: }
522:
523: /* Return the age of old log file (file.0) */
524: int age_old_log(file)
525: char *file;
526: {
527: struct stat sb;
528: char tmp[MAXPATHLEN+3];
529:
530: (void) strcpy(tmp,file);
531: if (stat(strcat(tmp,".0"),&sb) < 0)
532: if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
533: return(-1);
534: return( (int) (timenow - sb.st_mtime + 1800) / 3600);
535: }
536:
537:
538: #ifndef OSF
539: /* Duplicate a string using malloc */
540:
541: char *strdup(strp)
542: register char *strp;
543: {
544: register char *cp;
545:
546: if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL)
547: abort();
548: return(strcpy (cp, strp));
549: }
550: #endif
551:
552: /* Skip Over Blanks */
553: char *sob(p)
554: register char *p;
555: {
556: while (p && *p && isspace(*p))
557: p++;
558: return(p);
559: }
560:
561: /* Skip Over Non-Blanks */
562: char *son(p)
563: register char *p;
564: {
565: while (p && *p && !isspace(*p))
566: p++;
567: return(p);
568: }
569:
570:
571: /* Check if string is actually a number */
572:
573: isnumber(string)
574: char *string;
575: {
576: while (*string != '\0') {
577: if (*string < '0' || *string > '9') return(0);
578: string++;
579: }
580: return(1);
581: }