[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.9

1.9     ! downsj      1: /*     $OpenBSD: newsyslog.c,v 1.8 1997/01/15 23:42:56 millert 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.9     ! downsj     32: static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.8 1997/01/15 23:42:56 millert 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>
1.9     ! downsj     49: #include <sys/types.h>
        !            50: #include <sys/time.h>
        !            51: #include <sys/stat.h>
        !            52: #include <sys/param.h>
        !            53: #include <sys/wait.h>
1.1       deraadt    54: #include <stdlib.h>
                     55: #include <string.h>
                     56: #include <ctype.h>
                     57: #include <signal.h>
1.9     ! downsj     58: #include <fcntl.h>
1.1       deraadt    59: #include <pwd.h>
                     60: #include <grp.h>
1.9     ! downsj     61: #include <unistd.h>
1.1       deraadt    62:
                     63: #define kbytes(size)  (((size) + 1023) >> 10)
                     64:
                     65: #define CE_COMPACT 1            /* Compact the achived log files */
                     66: #define CE_BINARY 2             /* Logfile is in binary, don't add */
                     67:                                 /* status messages */
                     68: #define NONE -1
                     69:
                     70: struct conf_entry {
                     71:         char    *log;           /* Name of the log */
                     72:         int     uid;            /* Owner of log */
                     73:         int     gid;            /* Group of log */
                     74:         int     numlogs;        /* Number of logs to keep */
                     75:         int     size;           /* Size cutoff to trigger trimming the log */
                     76:         int     hours;          /* Hours between log trimming */
                     77:         int     permissions;    /* File permissions on the log */
                     78:         int     flags;          /* Flags (CE_COMPACT & CE_BINARY)  */
                     79:         struct conf_entry       *next; /* Linked list pointer */
                     80: };
                     81:
                     82: char    *progname;              /* contains argv[0] */
                     83: int     verbose = 0;            /* Print out what's going on */
                     84: int     needroot = 1;           /* Root privs are necessary */
                     85: int     noaction = 0;           /* Don't do anything, just show it */
                     86: char    *conf = CONF;           /* Configuration file to use */
                     87: time_t  timenow;
                     88: int     syslog_pid;             /* read in from /etc/syslog.pid */
                     89: #define MIN_PID                3
                     90: #define MAX_PID                65534
1.7       deraadt    91: char    hostname[MAXHOSTNAMELEN]; /* hostname */
1.1       deraadt    92: char    *daytime;               /* timenow in human readable form */
                     93:
                     94:
1.9     ! downsj     95: void do_entry __P((struct conf_entry *));
        !            96: void PRS __P((int, char **));
        !            97: void usage __P((void));
        !            98: struct conf_entry *parse_file __P((void));
        !            99: char *missing_field __P((char *, char *));
        !           100: void dotrim __P((char *, int, int, int, int, int));
        !           101: int log_trim __P((char *));
        !           102: void compress_log __P((char *));
        !           103: int sizefile __P((char *));
        !           104: int age_old_log __P((char *));
        !           105: char *sob __P((char *));
        !           106: char *son __P((char *));
        !           107: int isnumber __P((char *));
1.1       deraadt   108:
1.9     ! downsj    109: int main(argc, argv)
1.1       deraadt   110:         int argc;
                    111:         char **argv;
                    112: {
                    113:         struct conf_entry *p, *q;
                    114:
                    115:         PRS(argc,argv);
                    116:         if (needroot && getuid() && geteuid()) {
                    117:                 fprintf(stderr,"%s: must have root privs\n",progname);
                    118:                 exit(1);
                    119:         }
                    120:         p = q = parse_file();
                    121:         while (p) {
                    122:                 do_entry(p);
                    123:                 p=p->next;
                    124:                 free((char *) q);
                    125:                 q=p;
                    126:         }
                    127:         exit(0);
                    128: }
                    129:
1.9     ! downsj    130: void do_entry(ent)
1.1       deraadt   131:         struct conf_entry       *ent;
                    132:
                    133: {
                    134:         int     size, modtime;
                    135:
                    136:         if (verbose) {
                    137:                 if (ent->flags & CE_COMPACT)
                    138:                         printf("%s <%dZ>: ",ent->log,ent->numlogs);
                    139:                 else
                    140:                         printf("%s <%d>: ",ent->log,ent->numlogs);
                    141:         }
                    142:         size = sizefile(ent->log);
                    143:         modtime = age_old_log(ent->log);
                    144:         if (size < 0) {
                    145:                 if (verbose)
                    146:                         printf("does not exist.\n");
                    147:         } else {
                    148:                 if (verbose && (ent->size > 0))
                    149:                         printf("size (Kb): %d [%d] ", size, ent->size);
                    150:                 if (verbose && (ent->hours > 0))
                    151:                         printf(" age (hr): %d [%d] ", modtime, ent->hours);
                    152:                 if (((ent->size > 0) && (size >= ent->size)) ||
                    153:                     ((ent->hours > 0) && ((modtime >= ent->hours)
                    154:                                         || (modtime < 0)))) {
                    155:                         if (verbose)
                    156:                                 printf("--> trimming log....\n");
                    157:                         if (noaction && !verbose) {
                    158:                                 if (ent->flags & CE_COMPACT)
                    159:                                         printf("%s <%dZ>: trimming",
                    160:                                                ent->log,ent->numlogs);
                    161:                                 else
                    162:                                         printf("%s <%d>: trimming",
                    163:                                                ent->log,ent->numlogs);
                    164:                         }
                    165:                         dotrim(ent->log, ent->numlogs, ent->flags,
                    166:                                ent->permissions, ent->uid, ent->gid);
                    167:                 } else {
                    168:                         if (verbose)
                    169:                                 printf("--> skipping\n");
                    170:                 }
                    171:         }
                    172: }
                    173:
1.9     ! downsj    174: void PRS(argc, argv)
1.1       deraadt   175:         int argc;
                    176:         char **argv;
                    177: {
                    178:         int     c;
                    179:         FILE    *f;
                    180:         char    line[BUFSIZ];
                    181:        char    *p;
                    182:
                    183:         progname = argv[0];
                    184:         timenow = time((time_t *) 0);
                    185:         daytime = ctime(&timenow) + 4;
1.2       deraadt   186:         daytime[15] = '\0';
1.1       deraadt   187:
                    188:         /* Let's find the pid of syslogd */
                    189:         syslog_pid = 0;
                    190:         f = fopen(PIDFILE,"r");
                    191:         if (f && fgets(line,BUFSIZ,f))
                    192:                 syslog_pid = atoi(line);
                    193:        if (f)
                    194:                (void)fclose(f);
                    195:
                    196:         /* Let's get our hostname */
                    197:         (void) gethostname(hostname, sizeof(hostname));
                    198:
                    199:        /* Truncate domain */
1.9     ! downsj    200:        p = strchr(hostname, '.');
        !           201:        if (p)
1.1       deraadt   202:                *p = '\0';
                    203:
                    204:         optind = 1;             /* Start options parsing */
1.9     ! downsj    205:         while ((c = getopt(argc,argv,"nrvf:t:")) != -1) {
1.1       deraadt   206:                 switch (c) {
                    207:                 case 'n':
                    208:                         noaction++; /* This implies needroot as off */
                    209:                         /* fall through */
                    210:                 case 'r':
                    211:                         needroot = 0;
                    212:                         break;
                    213:                 case 'v':
                    214:                         verbose++;
                    215:                         break;
                    216:                 case 'f':
                    217:                         conf = optarg;
                    218:                         break;
                    219:                 default:
                    220:                         usage();
                    221:                 }
                    222:         }
1.9     ! downsj    223: }
1.1       deraadt   224:
1.9     ! downsj    225: void usage()
1.1       deraadt   226: {
                    227:         fprintf(stderr,
                    228:                 "Usage: %s <-nrv> <-f config-file>\n", progname);
                    229:         exit(1);
                    230: }
                    231:
                    232: /* Parse a configuration file and return a linked list of all the logs
                    233:  * to process
                    234:  */
                    235: struct conf_entry *parse_file()
                    236: {
                    237:         FILE    *f;
                    238:         char    line[BUFSIZ], *parse, *q;
                    239:         char    *errline, *group;
                    240:         struct conf_entry *first = NULL;
                    241:         struct conf_entry *working;
                    242:         struct passwd *pass;
                    243:         struct group *grp;
                    244:
                    245:         if (strcmp(conf,"-"))
                    246:                 f = fopen(conf,"r");
                    247:         else
                    248:                 f = stdin;
                    249:         if (!f) {
                    250:                 (void) fprintf(stderr,"%s: ",progname);
                    251:                 perror(conf);
                    252:                 exit(1);
                    253:         }
                    254:         while (fgets(line,BUFSIZ,f)) {
                    255:                 if ((line[0]== '\n') || (line[0] == '#'))
                    256:                         continue;
                    257:                 errline = strdup(line);
                    258:                 if (!first) {
                    259:                         working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
                    260:                         first = working;
                    261:                 } else {
                    262:                         working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
                    263:                         working = working->next;
                    264:                 }
                    265:
                    266:                 q = parse = missing_field(sob(line),errline);
                    267:                 *(parse = son(line)) = '\0';
                    268:                 working->log = strdup(q);
                    269:
                    270:                 q = parse = missing_field(sob(++parse),errline);
                    271:                 *(parse = son(parse)) = '\0';
                    272:                 if ((group = strchr(q, '.')) != NULL) {
                    273:                     *group++ = '\0';
                    274:                     if (*q) {
                    275:                         if (!(isnumber(q))) {
                    276:                             if ((pass = getpwnam(q)) == NULL) {
                    277:                                 fprintf(stderr,
                    278:                                     "Error in config file; unknown user:\n");
                    279:                                 fputs(errline,stderr);
                    280:                                 exit(1);
                    281:                             }
                    282:                             working->uid = pass->pw_uid;
                    283:                         } else
                    284:                             working->uid = atoi(q);
                    285:                     } else
                    286:                         working->uid = NONE;
                    287:
                    288:                     q = group;
                    289:                     if (*q) {
                    290:                         if (!(isnumber(q))) {
                    291:                             if ((grp = getgrnam(q)) == NULL) {
                    292:                                 fprintf(stderr,
                    293:                                     "Error in config file; unknown group:\n");
                    294:                                 fputs(errline,stderr);
                    295:                                 exit(1);
                    296:                             }
                    297:                             working->gid = grp->gr_gid;
                    298:                         } else
                    299:                             working->gid = atoi(q);
                    300:                     } else
                    301:                         working->gid = NONE;
                    302:
                    303:                     q = parse = missing_field(sob(++parse),errline);
                    304:                     *(parse = son(parse)) = '\0';
                    305:                 }
                    306:                 else
                    307:                     working->uid = working->gid = NONE;
                    308:
                    309:                 if (!sscanf(q,"%o",&working->permissions)) {
                    310:                         fprintf(stderr,
                    311:                                 "Error in config file; bad permissions:\n");
                    312:                         fputs(errline,stderr);
                    313:                         exit(1);
                    314:                 }
                    315:
                    316:                 q = parse = missing_field(sob(++parse),errline);
                    317:                 *(parse = son(parse)) = '\0';
                    318:                 if (!sscanf(q,"%d",&working->numlogs)) {
                    319:                         fprintf(stderr,
                    320:                                 "Error in config file; bad number:\n");
                    321:                         fputs(errline,stderr);
                    322:                         exit(1);
                    323:                 }
                    324:
                    325:                 q = parse = missing_field(sob(++parse),errline);
                    326:                 *(parse = son(parse)) = '\0';
                    327:                 if (isdigit(*q))
                    328:                         working->size = atoi(q);
                    329:                 else
                    330:                         working->size = -1;
                    331:
                    332:                 q = parse = missing_field(sob(++parse),errline);
                    333:                 *(parse = son(parse)) = '\0';
                    334:                 if (isdigit(*q))
                    335:                         working->hours = atoi(q);
                    336:                 else
                    337:                         working->hours = -1;
                    338:
                    339:                 q = parse = sob(++parse); /* Optional field */
                    340:                 *(parse = son(parse)) = '\0';
                    341:                 working->flags = 0;
                    342:                 while (q && *q && !isspace(*q)) {
                    343:                         if ((*q == 'Z') || (*q == 'z'))
                    344:                                 working->flags |= CE_COMPACT;
                    345:                         else if ((*q == 'B') || (*q == 'b'))
                    346:                                 working->flags |= CE_BINARY;
                    347:                         else {
                    348:                                 fprintf(stderr,
                    349:                                         "Illegal flag in config file -- %c\n",
                    350:                                         *q);
                    351:                                 exit(1);
                    352:                         }
                    353:                         q++;
                    354:                 }
                    355:
                    356:                 free(errline);
                    357:         }
                    358:         if (working)
                    359:                 working->next = (struct conf_entry *) NULL;
                    360:         (void) fclose(f);
                    361:         return(first);
                    362: }
                    363:
1.9     ! downsj    364: char *missing_field(p, errline)
1.1       deraadt   365:         char    *p,*errline;
                    366: {
                    367:         if (!p || !*p) {
                    368:                 fprintf(stderr,"Missing field in config file:\n");
                    369:                 fputs(errline,stderr);
                    370:                 exit(1);
                    371:         }
                    372:         return(p);
                    373: }
                    374:
1.9     ! downsj    375: void dotrim(log, numdays, flags, perm, owner_uid, group_gid)
1.1       deraadt   376:         char    *log;
                    377:         int     numdays;
                    378:         int     flags;
                    379:         int     perm;
                    380:         int     owner_uid;
                    381:         int     group_gid;
                    382: {
1.7       deraadt   383:         char    file1[MAXPATHLEN], file2[MAXPATHLEN];
                    384:         char    zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
1.1       deraadt   385:         int     fd;
                    386:         struct  stat st;
1.6       tholo     387:        int     days = numdays;
1.1       deraadt   388:
                    389:         /* Remove oldest log */
                    390:         (void) sprintf(file1,"%s.%d",log,numdays);
                    391:         (void) strcpy(zfile1, file1);
                    392:         (void) strcat(zfile1, COMPRESS_POSTFIX);
                    393:
                    394:         if (noaction) {
                    395:                 printf("rm -f %s\n", file1);
                    396:                 printf("rm -f %s\n", zfile1);
                    397:         } else {
                    398:                 (void) unlink(file1);
                    399:                 (void) unlink(zfile1);
                    400:         }
                    401:
                    402:         /* Move down log files */
                    403:         while (numdays--) {
                    404:                 (void) strcpy(file2,file1);
                    405:                 (void) sprintf(file1,"%s.%d",log,numdays);
                    406:                 (void) strcpy(zfile1, file1);
                    407:                 (void) strcpy(zfile2, file2);
                    408:                 if (lstat(file1, &st)) {
                    409:                         (void) strcat(zfile1, COMPRESS_POSTFIX);
                    410:                         (void) strcat(zfile2, COMPRESS_POSTFIX);
                    411:                         if (lstat(zfile1, &st)) continue;
                    412:                 }
                    413:                 if (noaction) {
                    414:                         printf("mv %s %s\n",zfile1,zfile2);
                    415:                         printf("chmod %o %s\n", perm, zfile2);
                    416:                         printf("chown %d.%d %s\n",
                    417:                                owner_uid, group_gid, zfile2);
                    418:                 } else {
                    419:                         (void) rename(zfile1, zfile2);
                    420:                         (void) chmod(zfile2, perm);
                    421:                         (void) chown(zfile2, owner_uid, group_gid);
                    422:                 }
                    423:         }
                    424:         if (!noaction && !(flags & CE_BINARY))
                    425:                 (void) log_trim(log);  /* Report the trimming to the old log */
                    426:
1.6       tholo     427:        if (days == 0) {
1.5       deraadt   428:                if (noaction)
                    429:                        printf("rm %s\n",log);
                    430:                else
                    431:                        (void) unlink(log);
                    432:        } else {
                    433:                if (noaction)
                    434:                        printf("mv %s to %s\n",log,file1);
                    435:                else
                    436:                        (void) rename(log,file1);
                    437:        }
                    438:
1.1       deraadt   439:         if (noaction)
                    440:                 printf("Start new log...");
                    441:         else {
                    442:                 fd = creat(log,perm);
                    443:                 if (fd < 0) {
                    444:                         perror("can't start new log");
                    445:                         exit(1);
                    446:                 }
                    447:                 if (fchown(fd, owner_uid, group_gid)) {
                    448:                         perror("can't chmod new log file");
                    449:                         exit(1);
                    450:                 }
                    451:                 (void) close(fd);
                    452:                 if (!(flags & CE_BINARY))
                    453:                         if (log_trim(log)) {    /* Add status message */
                    454:                                 perror("can't add status message to log");
                    455:                                 exit(1);
                    456:                         }
                    457:         }
                    458:         if (noaction)
                    459:                 printf("chmod %o %s...",perm,log);
                    460:         else
                    461:                 (void) chmod(log,perm);
                    462:         if (noaction)
                    463:                 printf("kill -HUP %d (syslogd)\n",syslog_pid);
                    464:         else
                    465:        if (syslog_pid < MIN_PID || syslog_pid > MAX_PID) {
                    466:                fprintf(stderr,"%s: preposterous process number: %d\n",
                    467:                                progname, syslog_pid);
                    468:         } else if (kill(syslog_pid,SIGHUP)) {
                    469:                         fprintf(stderr,"%s: ",progname);
                    470:                         perror("warning - could not restart syslogd");
                    471:                 }
                    472:         if (flags & CE_COMPACT) {
                    473:                 if (noaction)
                    474:                         printf("Compress %s.0\n",log);
                    475:                 else
                    476:                         compress_log(log);
                    477:         }
                    478: }
                    479:
                    480: /* Log the fact that the logs were turned over */
1.9     ! downsj    481: int log_trim(log)
1.1       deraadt   482:         char    *log;
                    483: {
                    484:         FILE    *f;
                    485:         if ((f = fopen(log,"a")) == NULL)
                    486:                 return(-1);
                    487:         fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
                    488:                 daytime, hostname, getpid());
                    489:         if (fclose(f) == EOF) {
                    490:                 perror("log_trim: fclose:");
                    491:                 exit(1);
                    492:         }
                    493:         return(0);
                    494: }
                    495:
                    496: /* Fork of /usr/ucb/compress to compress the old log file */
1.9     ! downsj    497: void compress_log(log)
1.1       deraadt   498:         char    *log;
                    499: {
                    500:         int     pid;
1.7       deraadt   501:         char    tmp[MAXPATHLEN];
1.1       deraadt   502:
                    503:         pid = fork();
                    504:         (void) sprintf(tmp,"%s.0",log);
                    505:         if (pid < 0) {
                    506:                 fprintf(stderr,"%s: ",progname);
                    507:                 perror("fork");
                    508:                 exit(1);
                    509:         } else if (!pid) {
                    510:                 (void) execl(COMPRESS,"compress","-f",tmp,0);
                    511:                 fprintf(stderr,"%s: ",progname);
                    512:                 perror(COMPRESS);
                    513:                 exit(1);
                    514:         }
                    515: }
                    516:
                    517: /* Return size in kilobytes of a file */
                    518: int sizefile(file)
                    519:         char    *file;
                    520: {
                    521:         struct stat sb;
                    522:
                    523:         if (stat(file,&sb) < 0)
                    524:                 return(-1);
                    525:         return(kbytes(dbtob(sb.st_blocks)));
                    526: }
                    527:
                    528: /* Return the age of old log file (file.0) */
                    529: int age_old_log(file)
                    530:         char    *file;
                    531: {
                    532:         struct stat sb;
1.7       deraadt   533:         char tmp[MAXPATHLEN];
1.1       deraadt   534:
                    535:         (void) strcpy(tmp,file);
                    536:         if (stat(strcat(tmp,".0"),&sb) < 0)
                    537:             if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
                    538:                 return(-1);
                    539:         return( (int) (timenow - sb.st_mtime + 1800) / 3600);
                    540: }
                    541:
                    542: /* Skip Over Blanks */
                    543: char *sob(p)
                    544:         register char   *p;
                    545: {
                    546:         while (p && *p && isspace(*p))
                    547:                 p++;
                    548:         return(p);
                    549: }
                    550:
                    551: /* Skip Over Non-Blanks */
                    552: char *son(p)
                    553:         register char   *p;
                    554: {
                    555:         while (p && *p && !isspace(*p))
                    556:                 p++;
                    557:         return(p);
                    558: }
                    559:
                    560:
                    561: /* Check if string is actually a number */
                    562:
1.9     ! downsj    563: int isnumber(string)
1.1       deraadt   564: char *string;
                    565: {
                    566:         while (*string != '\0') {
1.9     ! downsj    567:             if (!isdigit(*string++))
        !           568:                return(0);
1.1       deraadt   569:         }
                    570:         return(1);
                    571: }