Annotation of src/usr.bin/newsyslog/newsyslog.c, Revision 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: }