[BACK]Return to newsyslog.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / newsyslog

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: }