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