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