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