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