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