1 /* $OpenBSD: newsyslog.c,v 1.37 2001/07/09 07:04:50 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Todd C. Miller <Todd.Miller@courtesan.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * Copyright (c) 1997, Jason Downs. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by Jason Downs for the 44 * OpenBSD system. 45 * 4. Neither the name(s) of the author(s) nor the name OpenBSD 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 50 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 51 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 52 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 53 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 54 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 55 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 56 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 /* 63 * This file contains changes from the Open Software Foundation. 64 */ 65 66 /* 67 68 Copyright 1988, 1989 by the Massachusetts Institute of Technology 69 70 Permission to use, copy, modify, and distribute this software 71 and its documentation for any purpose and without fee is 72 hereby granted, provided that the above copyright notice 73 appear in all copies and that both that copyright notice and 74 this permission notice appear in supporting documentation, 75 and that the names of M.I.T. and the M.I.T. S.I.P.B. not be 76 used in advertising or publicity pertaining to distribution 77 of the software without specific, written prior permission. 78 M.I.T. and the M.I.T. S.I.P.B. make no representations about 79 the suitability of this software for any purpose. It is 80 provided "as is" without express or implied warranty. 81 82 */ 83 84 /* 85 * newsyslog - roll over selected logs at the appropriate time, 86 * keeping the a specified number of backup files around. 87 * 88 */ 89 90 #ifndef lint 91 static char rcsid[] = "$OpenBSD: newsyslog.c,v 1.37 2001/07/09 07:04:50 deraadt Exp $"; 92 #endif /* not lint */ 93 94 #ifndef CONF 95 #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */ 96 #endif 97 #ifndef PIDFILE 98 #define PIDFILE "/etc/syslog.pid" 99 #endif 100 #ifndef COMPRESS 101 #define COMPRESS "/usr/ucb/compress" /* File compression program */ 102 #endif 103 #ifndef COMPRESS_POSTFIX 104 #define COMPRESS_POSTFIX ".Z" 105 #endif 106 #ifndef STATS_DIR 107 #define STATS_DIR "/etc" 108 #endif 109 #ifndef SENDMAIL 110 #define SENDMAIL "/usr/lib/sendmail" 111 #endif 112 113 #include <stdio.h> 114 #include <sys/types.h> 115 #include <sys/time.h> 116 #include <sys/stat.h> 117 #include <sys/param.h> 118 #include <sys/wait.h> 119 #include <stdlib.h> 120 #include <string.h> 121 #include <ctype.h> 122 #include <signal.h> 123 #include <fcntl.h> 124 #include <pwd.h> 125 #include <grp.h> 126 #include <unistd.h> 127 #include <err.h> 128 129 #define CE_ROTATED 0x01 /* Log file has been rotated */ 130 #define CE_COMPACT 0x02 /* Compact the achived log files */ 131 #define CE_BINARY 0x04 /* Logfile is in binary, don't add */ 132 /* status messages */ 133 #define CE_MONITOR 0x08 /* Monitory for changes */ 134 #define NONE -1 135 136 struct conf_entry { 137 char *log; /* Name of the log */ 138 uid_t uid; /* Owner of log */ 139 gid_t gid; /* Group of log */ 140 int numlogs; /* Number of logs to keep */ 141 int size; /* Size cutoff to trigger trimming the log */ 142 int hours; /* Hours between log trimming */ 143 int permissions; /* File permissions on the log */ 144 int signal; /* Signal to send (defaults to SIGHUP) */ 145 int flags; /* Flags (CE_COMPACT & CE_BINARY) */ 146 char *whom; /* Whom to notify if logfile changes */ 147 char *pidfile; /* Path to file containg pid to signal */ 148 char *runcmd; /* Command to run instead of sending a signal */ 149 struct conf_entry *next; /* Linked list pointer */ 150 }; 151 152 struct pidinfo { 153 char *file; 154 int signal; 155 }; 156 157 int verbose = 0; /* Print out what's going on */ 158 int needroot = 1; /* Root privs are necessary */ 159 int noaction = 0; /* Don't do anything, just show it */ 160 int monitormode = 0; /* Don't do monitoring by default */ 161 char *conf = CONF; /* Configuration file to use */ 162 time_t timenow; 163 #define MIN_PID 4 164 char hostname[MAXHOSTNAMELEN]; /* hostname */ 165 char *daytime; /* timenow in human readable form */ 166 167 168 void do_entry __P((struct conf_entry *)); 169 void PRS __P((int, char **)); 170 void usage __P((void)); 171 struct conf_entry *parse_file __P((int *)); 172 char *missing_field __P((char *, char *)); 173 void dotrim __P((char *, int, int, int, uid_t, gid_t)); 174 int log_trim __P((char *)); 175 void compress_log __P((char *)); 176 int sizefile __P((char *)); 177 int age_old_log __P((char *)); 178 char *sob __P((char *)); 179 char *son __P((char *)); 180 int isnumberstr __P((char *)); 181 void domonitor __P((char *, char *)); 182 FILE *openmail __P((void)); 183 void closemail __P((FILE *)); 184 void child_killer __P((int)); 185 void run_command __P((char *)); 186 void send_signal __P((char *, int)); 187 188 int 189 main(argc, argv) 190 int argc; 191 char **argv; 192 { 193 struct conf_entry *p, *q; 194 struct pidinfo *pidlist, *pl; 195 int status, listlen; 196 197 PRS(argc, argv); 198 if (needroot && getuid() && geteuid()) 199 errx(1, "You must be root."); 200 p = q = parse_file(&listlen); 201 signal(SIGCHLD, child_killer); 202 203 pidlist = (struct pidinfo *)calloc(sizeof(struct pidinfo), listlen + 1); 204 if (pidlist == NULL) 205 err(1, "calloc"); 206 207 /* Step 1, rotate all log files */ 208 while (q) { 209 do_entry(q); 210 q = q->next; 211 } 212 213 /* Step 2, make a list of unique pid files */ 214 for (q = p, pl = pidlist; q; ) { 215 if (q->flags & CE_ROTATED) { 216 struct pidinfo *pltmp; 217 218 for (pltmp = pidlist; pltmp < pl; pltmp++) { 219 if ((strcmp(pltmp->file, q->pidfile) == 0 && 220 pltmp->signal == q->signal) || (q->runcmd && 221 strcmp(q->runcmd, pltmp->file) == 0)) 222 break; 223 } 224 if (pltmp == pl) { /* unique entry */ 225 if (q->runcmd) { 226 pl->file = q->runcmd; 227 pl->signal = -1; 228 } else { 229 pl->file = q->pidfile; 230 pl->signal = q->signal; 231 } 232 pl++; 233 } 234 } 235 q = q->next; 236 } 237 238 /* Step 3, send a signal or run a command */ 239 for (pl = pidlist; pl->file; pl++) { 240 if (pl->signal == -1) 241 run_command(pl->file); 242 else 243 send_signal(pl->file, pl->signal); 244 } 245 if (!noaction) 246 sleep(5); 247 248 /* Step 4, compress the log.0 file if configured to do so and free */ 249 while (p) { 250 if ((p->flags & CE_COMPACT) && (p->flags & CE_ROTATED)) 251 compress_log(p->log); 252 q = p; 253 p = p->next; 254 free(q); 255 } 256 257 /* Wait for children to finish, then exit */ 258 while (waitpid(-1, &status, 0) != -1) 259 ; 260 exit(0); 261 } 262 263 void 264 do_entry(ent) 265 struct conf_entry *ent; 266 267 { 268 int modtime, size; 269 270 if (verbose) 271 printf("%s <%d%s>: ", ent->log, ent->numlogs, 272 (ent->flags & CE_COMPACT) ? "Z" : ""); 273 size = sizefile(ent->log); 274 modtime = age_old_log(ent->log); 275 if (size < 0) { 276 if (verbose) 277 printf("does not exist.\n"); 278 } else { 279 if (verbose && (ent->size > 0)) 280 printf("size (Kb): %d [%d] ", size, ent->size); 281 if (verbose && (ent->hours > 0)) 282 printf(" age (hr): %d [%d] ", modtime, ent->hours); 283 if (monitormode && ent->flags & CE_MONITOR) 284 domonitor(ent->log, ent->whom); 285 if (!monitormode && (((ent->size > 0) && (size >= ent->size)) || 286 ((ent->hours > 0) && ((modtime >= ent->hours) 287 || (modtime < 0))))) { 288 if (verbose) 289 printf("--> trimming log....\n"); 290 if (noaction && !verbose) 291 printf("%s <%d%s>: ", ent->log, ent->numlogs, 292 (ent->flags & CE_COMPACT) ? "Z" : ""); 293 dotrim(ent->log, ent->numlogs, ent->flags, 294 ent->permissions, ent->uid, ent->gid); 295 ent->flags |= CE_ROTATED; 296 } else if (verbose) 297 printf("--> skipping\n"); 298 } 299 } 300 301 /* Run the specified command */ 302 void 303 run_command(cmd) 304 char *cmd; 305 { 306 307 if (noaction) 308 (void)printf("run %s\n", cmd); 309 else 310 system(cmd); 311 } 312 313 /* Send a signal to the pid specified by pidfile */ 314 void 315 send_signal(pidfile, signal) 316 char *pidfile; 317 int signal; 318 { 319 FILE *f; 320 char line[BUFSIZ]; 321 pid_t pid = 0; 322 323 if ((f = fopen(pidfile, "r")) == NULL) { 324 warn("can't open %s", pidfile); 325 return; 326 } 327 328 if (fgets(line, sizeof(line), f)) 329 pid = atoi(line); 330 (void)fclose(f); 331 332 if (noaction) 333 (void)printf("kill -%s %d\n", sys_signame[signal], pid); 334 else if (pid == 0) 335 warnx("empty pid file: %s", pidfile); 336 else if (pid < MIN_PID) 337 warnx("preposterous process number: %d", pid); 338 else if (kill(pid, signal)) 339 warnx("warning - could not send SIG%s to daemon", 340 sys_signame[signal]); 341 } 342 343 void 344 PRS(argc, argv) 345 int argc; 346 char **argv; 347 { 348 int c; 349 char *p; 350 351 timenow = time(NULL); 352 daytime = ctime(&timenow) + 4; 353 daytime[15] = '\0'; 354 355 /* Let's get our hostname */ 356 (void)gethostname(hostname, sizeof(hostname)); 357 358 /* Truncate domain */ 359 p = strchr(hostname, '.'); 360 if (p) 361 *p = '\0'; 362 363 optind = 1; /* Start options parsing */ 364 while ((c = getopt(argc, argv, "nrvmf:")) != -1) { 365 switch (c) { 366 case 'n': 367 noaction++; /* This implies needroot as off */ 368 /* fall through */ 369 case 'r': 370 needroot = 0; 371 break; 372 case 'v': 373 verbose++; 374 break; 375 case 'f': 376 conf = optarg; 377 break; 378 case 'm': 379 monitormode++; 380 break; 381 default: 382 usage(); 383 } 384 } 385 } 386 387 void 388 usage() 389 { 390 extern const char *__progname; 391 392 (void)fprintf(stderr, "usage: %s [-mnrv] [-f config_file]\n", 393 __progname); 394 exit(1); 395 } 396 397 /* Parse a configuration file and return a linked list of all the logs 398 * to process 399 */ 400 struct conf_entry * 401 parse_file(nentries) 402 int *nentries; 403 { 404 FILE *f; 405 char line[BUFSIZ], *parse, *q; 406 char *errline, *group, *tmp; 407 struct conf_entry *first = NULL; 408 struct conf_entry *working = NULL; 409 struct passwd *pass; 410 struct group *grp; 411 412 if (strcmp(conf, "-") == 0) 413 f = stdin; 414 else { 415 if ((f = fopen(conf, "r")) == NULL) 416 err(1, "can't open %s", conf); 417 } 418 419 *nentries = 0; 420 while (fgets(line, sizeof(line), f)) { 421 if ((line[0] == '\n') || (line[0] == '#')) 422 continue; 423 errline = strdup(line); 424 if (errline == NULL) 425 err(1, "strdup"); 426 (*nentries)++; 427 if (!first) { 428 working = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 429 if (working == NULL) 430 err(1, "malloc"); 431 first = working; 432 } else { 433 working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 434 if (working->next == NULL) 435 err(1, "malloc"); 436 working = working->next; 437 } 438 439 q = parse = missing_field(sob(line), errline); 440 *(parse = son(line)) = '\0'; 441 working->log = strdup(q); 442 if (working->log == NULL) 443 err(1, "strdup"); 444 445 q = parse = missing_field(sob(++parse), errline); 446 *(parse = son(parse)) = '\0'; 447 if ((group = strchr(q, '.')) != NULL) { 448 *group++ = '\0'; 449 if (*q) { 450 if (!(isnumberstr(q))) { 451 if ((pass = getpwnam(q)) == NULL) 452 errx(1, "Error in config file; unknown user: %s", q); 453 working->uid = pass->pw_uid; 454 } else 455 working->uid = atoi(q); 456 } else 457 working->uid = NONE; 458 459 q = group; 460 if (*q) { 461 if (!(isnumberstr(q))) { 462 if ((grp = getgrnam(q)) == NULL) 463 errx(1, "Error in config file; unknown group: %s", q); 464 working->gid = grp->gr_gid; 465 } else 466 working->gid = atoi(q); 467 } else 468 working->gid = NONE; 469 470 q = parse = missing_field(sob(++parse), errline); 471 *(parse = son(parse)) = '\0'; 472 } else 473 working->uid = working->gid = NONE; 474 475 if (!sscanf(q, "%o", &working->permissions)) 476 errx(1, "Error in config file; bad permissions: %s", q); 477 478 q = parse = missing_field(sob(++parse), errline); 479 *(parse = son(parse)) = '\0'; 480 if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0) 481 errx(1, "Error in config file; bad number: %s", q); 482 483 q = parse = missing_field(sob(++parse), errline); 484 *(parse = son(parse)) = '\0'; 485 if (isdigit(*q)) 486 working->size = atoi(q); 487 else 488 working->size = -1; 489 490 q = parse = missing_field(sob(++parse), errline); 491 *(parse = son(parse)) = '\0'; 492 if (isdigit(*q)) 493 working->hours = atoi(q); 494 else 495 working->hours = -1; 496 497 working->flags = 0; 498 q = sob(++parse); /* Optional field */ 499 if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' || 500 *q == 'M' || *q == 'm') { 501 *(parse = son(q)) = '\0'; 502 while (*q) { 503 switch (*q) { 504 case 'Z': 505 case 'z': 506 working->flags |= CE_COMPACT; 507 break; 508 case 'B': 509 case 'b': 510 working->flags |= CE_BINARY; 511 break; 512 case 'M': 513 case 'm': 514 working->flags |= CE_MONITOR; 515 break; 516 default: 517 errx(1, "Illegal flag in config file: %c", *q); 518 break; 519 } 520 q++; 521 } 522 } else 523 parse--; /* no flags so undo */ 524 525 working->whom = NULL; 526 if (working->flags & CE_MONITOR) { /* Optional field */ 527 q = parse = sob(++parse); 528 *(parse = son(parse)) = '\0'; 529 530 working->whom = strdup(q); 531 if (working->log == NULL) 532 err(1, "strdup"); 533 } 534 535 working->pidfile = PIDFILE; 536 working->signal = SIGHUP; 537 working->runcmd = NULL; 538 for (;;) { 539 q = parse = sob(++parse); /* Optional field */ 540 if (q == NULL || *q == '\0') 541 break; 542 if (*q == '/') { 543 *(parse = son(parse)) = '\0'; 544 if (strlen(q) >= MAXPATHLEN) 545 errx(1, "%s: pathname too long", q); 546 working->pidfile = strdup(q); 547 if (working->pidfile == NULL) 548 err(1, "strdup"); 549 } else if (*q == '"' && (tmp = strchr(q + 1, '"'))) { 550 *(parse = tmp) = '\0'; 551 working->runcmd = strdup(++q); 552 if (working->runcmd == NULL) 553 err(1, "strdup"); 554 } else if (strncmp(q, "SIG", 3) == 0) { 555 int i; 556 557 *(parse = son(parse)) = '\0'; 558 for (i = 1; i < NSIG; i++) { 559 if (!strcmp(sys_signame[i], q + 3)) { 560 working->signal = i; 561 break; 562 } 563 } 564 if (i == NSIG) 565 errx(1, "unknown signal: %s", q); 566 } else 567 errx(1, "unrecognized field: %s", q); 568 } 569 570 /* Make sure we can't oflow MAXPATHLEN */ 571 if (asprintf(&tmp, "%s.%d%s", working->log, working->numlogs, 572 COMPRESS_POSTFIX) >= MAXPATHLEN) 573 errx(1, "%s: pathname too long", working->log); 574 575 if (tmp) 576 free(tmp); 577 free(errline); 578 } 579 if (working) 580 working->next = NULL; 581 (void)fclose(f); 582 return(first); 583 } 584 585 char * 586 missing_field(p, errline) 587 char *p; 588 char *errline; 589 { 590 if (!p || !*p) { 591 warnx("Missing field in config file line:"); 592 fputs(errline, stderr); 593 exit(1); 594 } 595 return(p); 596 } 597 598 void 599 dotrim(log, numdays, flags, perm, owner_uid, group_gid) 600 char *log; 601 int numdays; 602 int flags; 603 int perm; 604 uid_t owner_uid; 605 gid_t group_gid; 606 { 607 char file1[MAXPATHLEN], file2[MAXPATHLEN]; 608 char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; 609 int fd; 610 struct stat st; 611 int days = numdays; 612 613 /* Remove oldest log (may not exist) */ 614 (void)snprintf(file1, sizeof file1, "%s.%d", log, numdays); 615 (void)snprintf(zfile1, sizeof zfile1, "%s.%d%s", log, numdays, 616 COMPRESS_POSTFIX); 617 618 if (noaction) { 619 printf("rm -f %s %s\n", file1, zfile1); 620 } else { 621 (void)unlink(file1); 622 (void)unlink(zfile1); 623 } 624 625 /* Move down log files */ 626 while (numdays--) { 627 (void)strlcpy(file2, file1, sizeof file2); 628 (void)snprintf(file1, sizeof file1, "%s.%d", log, numdays); 629 (void)strlcpy(zfile1, file1, sizeof zfile1); 630 (void)strlcpy(zfile2, file2, sizeof zfile2); 631 if (lstat(file1, &st)) { 632 (void)strlcat(zfile1, COMPRESS_POSTFIX, sizeof zfile1); 633 (void)strlcat(zfile2, COMPRESS_POSTFIX, sizeof zfile2); 634 if (lstat(zfile1, &st)) 635 continue; 636 } 637 if (noaction) { 638 printf("mv %s %s\n", zfile1, zfile2); 639 printf("chmod %o %s\n", perm, zfile2); 640 printf("chown %d:%d %s\n", 641 owner_uid, group_gid, zfile2); 642 } else { 643 if (rename(zfile1, zfile2)) 644 warn("can't mv %s to %s", zfile1, zfile2); 645 if (chmod(zfile2, perm)) 646 warn("can't chmod %s", zfile2); 647 if (chown(zfile2, owner_uid, group_gid)) 648 warn("can't chown %s", zfile2); 649 } 650 } 651 if (!noaction && !(flags & CE_BINARY)) 652 (void)log_trim(log); /* Report the trimming to the old log */ 653 654 (void)snprintf(file2, sizeof(file2), "%s.XXXXXXXXXX", log); 655 if (noaction) { 656 printf("Create new log file...\n"); 657 } else { 658 if ((fd = mkstemp(file2)) < 0) 659 err(1, "can't start '%s' log", file2); 660 if (fchown(fd, owner_uid, group_gid)) 661 err(1, "can't chown '%s' log file", file2); 662 if (fchmod(fd, perm)) 663 err(1, "can't chmod '%s' log file", file2); 664 (void)close(fd); 665 /* Add status message */ 666 if (!(flags & CE_BINARY) && log_trim(file2)) 667 err(1, "can't add status message to log '%s'", file2); 668 } 669 670 if (days == 0) { 671 if (noaction) 672 printf("rm %s\n", log); 673 else if (unlink(log)) 674 warn("can't rm %s", log); 675 } else { 676 if (noaction) 677 printf("mv %s to %s\n", log, file1); 678 else if (rename(log, file1)) 679 warn("can't to mv %s to %s", log, file1); 680 } 681 682 /* Now move the new log file into place */ 683 if (noaction) 684 printf("mv %s to %s\n", file2, log); 685 else if (rename(file2, log)) 686 warn("can't to mv %s to %s", file2, log); 687 } 688 689 /* Log the fact that the logs were turned over */ 690 int 691 log_trim(log) 692 char *log; 693 { 694 FILE *f; 695 696 if ((f = fopen(log, "a")) == NULL) 697 return(-1); 698 (void)fprintf(f, "%s %s newsyslog[%d]: logfile turned over\n", 699 daytime, hostname, getpid()); 700 if (fclose(f) == EOF) 701 err(1, "log_trim: fclose"); 702 return(0); 703 } 704 705 /* Fork off compress or gzip to compress the old log file */ 706 void 707 compress_log(log) 708 char *log; 709 { 710 pid_t pid; 711 char *base; 712 char tmp[MAXPATHLEN]; 713 714 if ((base = strrchr(COMPRESS, '/')) == NULL) 715 base = COMPRESS; 716 else 717 base++; 718 if (noaction) { 719 printf("%s %s.0\n", base, log); 720 return; 721 } 722 pid = fork(); 723 (void)snprintf(tmp, sizeof tmp, "%s.0", log); 724 if (pid < 0) { 725 err(1, "fork"); 726 } else if (!pid) { 727 (void)execl(COMPRESS, base, "-f", tmp, (char *)NULL); 728 warn(COMPRESS); 729 _exit(1); 730 } 731 } 732 733 /* Return size in kilobytes of a file */ 734 int 735 sizefile(file) 736 char *file; 737 { 738 struct stat sb; 739 740 if (stat(file, &sb) < 0) 741 return(-1); 742 return(sb.st_blocks / (1024.0 / DEV_BSIZE)); 743 } 744 745 /* Return the age (in hours) of old log file (file.0), or -1 if none */ 746 int 747 age_old_log(file) 748 char *file; 749 { 750 struct stat sb; 751 char tmp[MAXPATHLEN]; 752 753 (void)strlcpy(tmp, file, sizeof tmp); 754 strlcat(tmp, ".0", sizeof tmp); 755 if (stat(tmp, &sb) < 0) { 756 strlcat(tmp, COMPRESS_POSTFIX, sizeof tmp); 757 if (stat(tmp, &sb) < 0) 758 return(-1); 759 } 760 return( (int) (timenow - sb.st_mtime + 1800) / 3600); 761 } 762 763 /* Skip Over Blanks */ 764 char * 765 sob(p) 766 register char *p; 767 { 768 while (p && *p && isspace(*p)) 769 p++; 770 return(p); 771 } 772 773 /* Skip Over Non-Blanks */ 774 char * 775 son(p) 776 register char *p; 777 { 778 while (p && *p && !isspace(*p)) 779 p++; 780 return(p); 781 } 782 783 /* Check if string is actually a number */ 784 int 785 isnumberstr(string) 786 char *string; 787 { 788 while (*string) { 789 if (!isdigit(*string++)) 790 return(0); 791 } 792 return(1); 793 } 794 795 void 796 domonitor(log, whom) 797 char *log, *whom; 798 { 799 struct stat sb, tsb; 800 char fname[MAXPATHLEN], *flog, *p, *rb = NULL; 801 FILE *fp; 802 off_t osize; 803 int rd; 804 805 if (stat(log, &sb) < 0) 806 return; 807 808 flog = strdup(log); 809 if (flog == NULL) 810 err(1, "strdup"); 811 812 for (p = flog; *p != '\0'; p++) { 813 if (*p == '/') 814 *p = '_'; 815 } 816 snprintf(fname, sizeof fname, "%s/newsyslog.%s.size", 817 STATS_DIR, flog); 818 819 /* ..if it doesn't exist, simply record the current size. */ 820 if ((sb.st_size == 0) || stat(fname, &tsb) < 0) 821 goto update; 822 823 fp = fopen(fname, "r"); 824 if (fp == NULL) { 825 warn("%s", fname); 826 goto cleanup; 827 } 828 #ifdef QUAD_OFF_T 829 if (fscanf(fp, "%qd\n", &osize) != 1) { 830 #else 831 if (fscanf(fp, "%ld\n", &osize) != 1) { 832 #endif /* QUAD_OFF_T */ 833 fclose(fp); 834 goto update; 835 } 836 837 fclose(fp); 838 839 /* If the file is smaller, mark the entire thing as changed. */ 840 if (sb.st_size < osize) 841 osize = 0; 842 843 /* Now see if current size is larger. */ 844 if (sb.st_size > osize) { 845 rb = (char *) malloc(sb.st_size - osize); 846 if (rb == NULL) 847 err(1, "malloc"); 848 849 /* Open logfile, seek. */ 850 fp = fopen(log, "r"); 851 if (fp == NULL) { 852 warn("%s", log); 853 goto cleanup; 854 } 855 fseek(fp, osize, SEEK_SET); 856 rd = fread(rb, 1, sb.st_size - osize, fp); 857 if (rd < 1) { 858 warn("fread"); 859 fclose(fp); 860 goto cleanup; 861 } 862 863 /* Send message. */ 864 fclose(fp); 865 866 fp = openmail(); 867 if (fp == NULL) { 868 warn("openmail"); 869 goto cleanup; 870 } 871 fprintf(fp, "To: %s\nSubject: LOGFILE NOTIFICATION: %s\n\n\n", 872 whom, log); 873 fwrite(rb, 1, rd, fp); 874 fputs("\n\n", fp); 875 876 closemail(fp); 877 } 878 update: 879 /* Reopen for writing and update file. */ 880 fp = fopen(fname, "w"); 881 if (fp == NULL) { 882 warn("%s", fname); 883 goto cleanup; 884 } 885 #ifdef QUAD_OFF_T 886 fprintf(fp, "%qd\n", sb.st_size); 887 #else 888 fprintf(fp, "%ld\n", sb.st_size); 889 #endif /* QUAD_OFF_T */ 890 fclose(fp); 891 892 cleanup: 893 free(flog); 894 if (rb != NULL) 895 free(rb); 896 } 897 898 FILE * 899 openmail() 900 { 901 char *cmdbuf = NULL; 902 FILE *ret; 903 904 asprintf(&cmdbuf, "%s -t", SENDMAIL); 905 if (cmdbuf) { 906 ret = popen(cmdbuf, "w"); 907 free(cmdbuf); 908 return (ret); 909 } 910 return (NULL); 911 } 912 913 void 914 closemail(pfp) 915 FILE *pfp; 916 { 917 pclose(pfp); 918 } 919 920 void 921 child_killer(signum) 922 int signum; 923 { 924 int status; 925 926 while (waitpid(-1, &status, WNOHANG) > 0) 927 ; 928 } 929