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