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