Home | History | Annotate | Line # | Download | only in newsyslog
newsyslog.c revision 1.11
      1 /*
      2  * This file contains changes from the Open Software Foundation.
      3  */
      4 
      5 /*
      6 
      7 Copyright 1988, 1989 by the Massachusetts Institute of Technology
      8 
      9 Permission to use, copy, modify, and distribute this software
     10 and its documentation for any purpose and without fee is
     11 hereby granted, provided that the above copyright notice
     12 appear in all copies and that both that copyright notice and
     13 this permission notice appear in supporting documentation,
     14 and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
     15 used in advertising or publicity pertaining to distribution
     16 of the software without specific, written prior permission.
     17 M.I.T. and the M.I.T. S.I.P.B. make no representations about
     18 the suitability of this software for any purpose.  It is
     19 provided "as is" without express or implied warranty.
     20 
     21 */
     22 
     23 /*
     24  *      newsyslog - roll over selected logs at the appropriate time,
     25  *              keeping the a specified number of backup files around.
     26  *
     27  *      $Source: /tank/opengrok/rsync2/NetBSD/src/usr.bin/newsyslog/newsyslog.c,v $
     28  *      $Author: thorpej $
     29  */
     30 
     31 #ifndef lint
     32 static char rcsid[] = "$Id: newsyslog.c,v 1.11 1996/09/27 01:55:26 thorpej Exp $";
     33 #endif /* not lint */
     34 
     35 #ifndef CONF
     36 #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */
     37 #endif
     38 #ifndef PIDFILE
     39 #define PIDFILE "/etc/syslog.pid"
     40 #endif
     41 #ifndef COMPRESS
     42 #define COMPRESS "/usr/ucb/compress" /* File compression program */
     43 #endif
     44 #ifndef COMPRESS_POSTFIX
     45 #define COMPRESS_POSTFIX ".Z"
     46 #endif
     47 
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <ctype.h>
     52 #include <signal.h>
     53 #include <pwd.h>
     54 #include <grp.h>
     55 #include <sys/types.h>
     56 #include <sys/time.h>
     57 #include <sys/stat.h>
     58 #include <sys/param.h>
     59 #include <sys/wait.h>
     60 
     61 #define kbytes(size)  (((size) + 1023) >> 10)
     62 #ifdef _IBMR2
     63 /* Calculates (db * DEV_BSIZE) */
     64 #define dbtob(db)  ((unsigned)(db) << UBSHIFT)
     65 #endif
     66 
     67 #define CE_COMPACT 1            /* Compact the achived log files */
     68 #define CE_BINARY 2             /* Logfile is in binary, don't add */
     69                                 /* status messages */
     70 #define NONE -1
     71 
     72 struct conf_entry {
     73         char    *log;           /* Name of the log */
     74         int     uid;            /* Owner of log */
     75         int     gid;            /* Group of log */
     76         int     numlogs;        /* Number of logs to keep */
     77         int     size;           /* Size cutoff to trigger trimming the log */
     78         int     hours;          /* Hours between log trimming */
     79         int     permissions;    /* File permissions on the log */
     80         int     flags;          /* Flags (CE_COMPACT & CE_BINARY)  */
     81         struct conf_entry       *next; /* Linked list pointer */
     82 };
     83 
     84 extern int      optind;
     85 extern char     *optarg;
     86 extern char *malloc();
     87 extern uid_t getuid(),geteuid();
     88 extern time_t time();
     89 
     90 char    *progname;              /* contains argv[0] */
     91 int     verbose = 0;            /* Print out what's going on */
     92 int     needroot = 1;           /* Root privs are necessary */
     93 int     noaction = 0;           /* Don't do anything, just show it */
     94 char    *conf = CONF;           /* Configuration file to use */
     95 time_t  timenow;
     96 int     syslog_pid;             /* read in from /etc/syslog.pid */
     97 #define MIN_PID		3
     98 #define MAX_PID		65534
     99 char    hostname[64];           /* hostname */
    100 char    *daytime;               /* timenow in human readable form */
    101 
    102 
    103 struct conf_entry *parse_file();
    104 char *sob(), *son(), *strdup(), *missing_field();
    105 
    106 main(argc,argv)
    107         int argc;
    108         char **argv;
    109 {
    110         struct conf_entry *p, *q;
    111 
    112         PRS(argc,argv);
    113         if (needroot && getuid() && geteuid()) {
    114                 fprintf(stderr,"%s: must have root privs\n",progname);
    115                 exit(1);
    116         }
    117         p = q = parse_file();
    118         while (p) {
    119                 do_entry(p);
    120                 p=p->next;
    121                 free((char *) q);
    122                 q=p;
    123         }
    124         exit(0);
    125 }
    126 
    127 do_entry(ent)
    128         struct conf_entry       *ent;
    129 
    130 {
    131         int     size, modtime;
    132 
    133         if (verbose) {
    134                 if (ent->flags & CE_COMPACT)
    135                         printf("%s <%dZ>: ",ent->log,ent->numlogs);
    136                 else
    137                         printf("%s <%d>: ",ent->log,ent->numlogs);
    138         }
    139         size = sizefile(ent->log);
    140         modtime = age_old_log(ent->log);
    141         if (size < 0) {
    142                 if (verbose)
    143                         printf("does not exist.\n");
    144         } else {
    145                 if (verbose && (ent->size > 0))
    146                         printf("size (Kb): %d [%d] ", size, ent->size);
    147                 if (verbose && (ent->hours > 0))
    148                         printf(" age (hr): %d [%d] ", modtime, ent->hours);
    149                 if (((ent->size > 0) && (size >= ent->size)) ||
    150                     ((ent->hours > 0) && ((modtime >= ent->hours)
    151                                         || (modtime < 0)))) {
    152                         if (verbose)
    153                                 printf("--> trimming log....\n");
    154                         if (noaction && !verbose) {
    155                                 if (ent->flags & CE_COMPACT)
    156                                         printf("%s <%dZ>: trimming",
    157                                                ent->log,ent->numlogs);
    158                                 else
    159                                         printf("%s <%d>: trimming",
    160                                                ent->log,ent->numlogs);
    161                         }
    162                         dotrim(ent->log, ent->numlogs, ent->flags,
    163                                ent->permissions, ent->uid, ent->gid);
    164                 } else {
    165                         if (verbose)
    166                                 printf("--> skipping\n");
    167                 }
    168         }
    169 }
    170 
    171 PRS(argc,argv)
    172         int argc;
    173         char **argv;
    174 {
    175         int     c;
    176         FILE    *f;
    177         char    line[BUFSIZ];
    178 	char	*p;
    179 
    180         progname = argv[0];
    181         timenow = time((time_t *) 0);
    182         daytime = ctime(&timenow) + 4;
    183         daytime[15] = '\0';
    184 
    185         /* Let's find the pid of syslogd */
    186         syslog_pid = 0;
    187         f = fopen(PIDFILE,"r");
    188         if (f && fgets(line,BUFSIZ,f))
    189                 syslog_pid = atoi(line);
    190 	if (f)
    191 		(void)fclose(f);
    192 
    193         /* Let's get our hostname */
    194         (void) gethostname(hostname, sizeof(hostname));
    195 
    196 	/* Truncate domain */
    197 	if (p = strchr(hostname, '.')) {
    198 		*p = '\0';
    199 	}
    200 
    201         optind = 1;             /* Start options parsing */
    202         while ((c=getopt(argc,argv,"nrvf:t:")) != EOF)
    203                 switch (c) {
    204                 case 'n':
    205                         noaction++; /* This implies needroot as off */
    206                         /* fall through */
    207                 case 'r':
    208                         needroot = 0;
    209                         break;
    210                 case 'v':
    211                         verbose++;
    212                         break;
    213                 case 'f':
    214                         conf = optarg;
    215                         break;
    216                 default:
    217                         usage();
    218                 }
    219         }
    220 
    221 usage()
    222 {
    223         fprintf(stderr,
    224                 "Usage: %s <-nrv> <-f config-file>\n", progname);
    225         exit(1);
    226 }
    227 
    228 /* Parse a configuration file and return a linked list of all the logs
    229  * to process
    230  */
    231 struct conf_entry *parse_file()
    232 {
    233         FILE    *f;
    234         char    line[BUFSIZ], *parse, *q;
    235         char    *errline, *group;
    236         struct conf_entry *first = NULL;
    237         struct conf_entry *working;
    238         struct passwd *pass;
    239         struct group *grp;
    240 
    241         if (strcmp(conf,"-"))
    242                 f = fopen(conf,"r");
    243         else
    244                 f = stdin;
    245         if (!f) {
    246                 (void) fprintf(stderr,"%s: ",progname);
    247                 perror(conf);
    248                 exit(1);
    249         }
    250         while (fgets(line,BUFSIZ,f)) {
    251                 if ((line[0]== '\n') || (line[0] == '#'))
    252                         continue;
    253                 errline = strdup(line);
    254                 if (!first) {
    255                         working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
    256                         first = working;
    257                 } else {
    258                         working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
    259                         working = working->next;
    260                 }
    261 
    262                 q = parse = missing_field(sob(line),errline);
    263                 *(parse = son(line)) = '\0';
    264                 working->log = strdup(q);
    265 
    266                 q = parse = missing_field(sob(++parse),errline);
    267                 *(parse = son(parse)) = '\0';
    268                 if ((group = strchr(q, '.')) != NULL) {
    269                     *group++ = '\0';
    270                     if (*q) {
    271                         if (!(isnumber(q))) {
    272                             if ((pass = getpwnam(q)) == NULL) {
    273                                 fprintf(stderr,
    274                                     "Error in config file; unknown user:\n");
    275                                 fputs(errline,stderr);
    276                                 exit(1);
    277                             }
    278                             working->uid = pass->pw_uid;
    279                         } else
    280                             working->uid = atoi(q);
    281                     } else
    282                         working->uid = NONE;
    283 
    284                     q = group;
    285                     if (*q) {
    286                         if (!(isnumber(q))) {
    287                             if ((grp = getgrnam(q)) == NULL) {
    288                                 fprintf(stderr,
    289                                     "Error in config file; unknown group:\n");
    290                                 fputs(errline,stderr);
    291                                 exit(1);
    292                             }
    293                             working->gid = grp->gr_gid;
    294                         } else
    295                             working->gid = atoi(q);
    296                     } else
    297                         working->gid = NONE;
    298 
    299                     q = parse = missing_field(sob(++parse),errline);
    300                     *(parse = son(parse)) = '\0';
    301                 }
    302                 else
    303                     working->uid = working->gid = NONE;
    304 
    305                 if (!sscanf(q,"%o",&working->permissions)) {
    306                         fprintf(stderr,
    307                                 "Error in config file; bad permissions:\n");
    308                         fputs(errline,stderr);
    309                         exit(1);
    310                 }
    311 
    312                 q = parse = missing_field(sob(++parse),errline);
    313                 *(parse = son(parse)) = '\0';
    314                 if (!sscanf(q,"%d",&working->numlogs)) {
    315                         fprintf(stderr,
    316                                 "Error in config file; bad number:\n");
    317                         fputs(errline,stderr);
    318                         exit(1);
    319                 }
    320 
    321                 q = parse = missing_field(sob(++parse),errline);
    322                 *(parse = son(parse)) = '\0';
    323                 if (isdigit(*q))
    324                         working->size = atoi(q);
    325                 else
    326                         working->size = -1;
    327 
    328                 q = parse = missing_field(sob(++parse),errline);
    329                 *(parse = son(parse)) = '\0';
    330                 if (isdigit(*q))
    331                         working->hours = atoi(q);
    332                 else
    333                         working->hours = -1;
    334 
    335                 q = parse = sob(++parse); /* Optional field */
    336                 *(parse = son(parse)) = '\0';
    337                 working->flags = 0;
    338                 while (q && *q && !isspace(*q)) {
    339                         if ((*q == 'Z') || (*q == 'z'))
    340                                 working->flags |= CE_COMPACT;
    341                         else if ((*q == 'B') || (*q == 'b'))
    342                                 working->flags |= CE_BINARY;
    343                         else {
    344                                 fprintf(stderr,
    345                                         "Illegal flag in config file -- %c\n",
    346                                         *q);
    347                                 exit(1);
    348                         }
    349                         q++;
    350                 }
    351 
    352                 free(errline);
    353         }
    354         if (working)
    355                 working->next = (struct conf_entry *) NULL;
    356         (void) fclose(f);
    357         return(first);
    358 }
    359 
    360 char *missing_field(p,errline)
    361         char    *p,*errline;
    362 {
    363         if (!p || !*p) {
    364                 fprintf(stderr,"Missing field in config file:\n");
    365                 fputs(errline,stderr);
    366                 exit(1);
    367         }
    368         return(p);
    369 }
    370 
    371 dotrim(log,numdays,flags,perm,owner_uid,group_gid)
    372         char    *log;
    373         int     numdays;
    374         int     flags;
    375         int     perm;
    376         int     owner_uid;
    377         int     group_gid;
    378 {
    379         char    file1[128], file2[128];
    380         char    zfile1[128], zfile2[128];
    381         int     fd;
    382         struct  stat st;
    383         int     ngen = numdays;
    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 
    431 	if (ngen == 0)
    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         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 }
    590