newsyslog.c revision 49145
11844Swollman/* 250476Speter * This file contains changes from the Open Software Foundation. 31844Swollman */ 41638Srgrimes 594940Sru/* 61638Srgrimes 742915SjdpCopyright 1988, 1989 by the Massachusetts Institute of Technology 842915Sjdp 942915SjdpPermission to use, copy, modify, and distribute this software 1042915Sjdpand its documentation for any purpose and without fee is 11139106Sruhereby granted, provided that the above copyright notice 1242915Sjdpappear in all copies and that both that copyright notice and 1342915Sjdpthis permission notice appear in supporting documentation, 1442915Sjdpand that the names of M.I.T. and the M.I.T. S.I.P.B. not be 15129024Sdesused in advertising or publicity pertaining to distribution 16129024Sdesof the software without specific, written prior permission. 1729141SpeterM.I.T. and the M.I.T. S.I.P.B. make no representations about 18129024Sdesthe suitability of this software for any purpose. It is 19129024Sdesprovided "as is" without express or implied warranty. 20129024Sdes 21125119Sru*/ 22100332Sru 23100332Sru/* 2442915Sjdp * newsyslog - roll over selected logs at the appropriate time, 2542915Sjdp * keeping the a specified number of backup files around. 2629141Speter */ 27119607Sru 28117034Sgordon#ifndef lint 29119607Srustatic const char rcsid[] = 30117034Sgordon "$Id: newsyslog.c,v 1.23 1999/06/28 03:15:02 obrien Exp $"; 312827Sjkh#endif /* not lint */ 322827Sjkh 332827Sjkh#define OSF 342827Sjkh#ifndef COMPRESS_POSTFIX 352827Sjkh#define COMPRESS_POSTFIX ".gz" 361638Srgrimes#endif 372827Sjkh 381638Srgrimes#include <ctype.h> 3918529Sbde#include <err.h> 4018529Sbde#include <fcntl.h> 411638Srgrimes#include <grp.h> 4242450Sjdp#include <paths.h> 431638Srgrimes#include <pwd.h> 44117173Sru#include <signal.h> 451638Srgrimes#include <stdio.h> 4696512Sru#include <stdlib.h> 4796512Sru#include <string.h> 4896512Sru#include <time.h> 4996512Sru#include <unistd.h> 5096512Sru#include <sys/types.h> 5196512Sru#include <sys/stat.h> 5296512Sru#include <sys/param.h> 5396512Sru#include <sys/wait.h> 54126890Strhodes 55126890Strhodes#include "pathnames.h" 56126890Strhodes 57126890Strhodes#define kbytes(size) (((size) + 1023) >> 10) 58126890Strhodes#ifdef _IBMR2 59126890Strhodes/* Calculates (db * DEV_BSIZE) */ 601638Srgrimes#define dbtob(db) ((unsigned)(db) << UBSHIFT) 61126890Strhodes#endif 621638Srgrimes 6342450Sjdp#define CE_COMPACT 1 /* Compact the achived log files */ 641844Swollman#define CE_BINARY 2 /* Logfile is in binary, don't add */ 651844Swollman /* status messages */ 6636673Sdt#define CE_TRIMAT 4 /* trim at a specific time */ 67126890Strhodes 681844Swollman#define NONE -1 6942450Sjdp 701844Swollmanstruct conf_entry { 711844Swollman char *log; /* Name of the log */ 721844Swollman char *pid_file; /* PID file */ 73127027Strhodes int uid; /* Owner of log */ 741844Swollman int gid; /* Group of log */ 7542450Sjdp int numlogs; /* Number of logs to keep */ 761844Swollman int size; /* Size cutoff to trigger trimming the log */ 771844Swollman int hours; /* Hours between log trimming */ 7836054Sbde time_t trim_at; /* Specific time to do trimming */ 7936054Sbde int permissions; /* File permissions on the log */ 8036054Sbde int flags; /* Flags (CE_COMPACT & CE_BINARY) */ 8142450Sjdp int sig; /* Signal to send */ 8236054Sbde struct conf_entry *next; /* Linked list pointer */ 8336054Sbde}; 84117173Sru 85117159Sruint verbose = 0; /* Print out what's going on */ 861638Srgrimesint needroot = 1; /* Root privs are necessary */ 87117173Sruint noaction = 0; /* Don't do anything, just show it */ 88117173Sruint force = 0; /* Force the trim no matter what*/ 89117173Sruchar *conf = _PATH_CONF; /* Configuration file to use */ 90117173Srutime_t timenow; 91117173Sru#define MIN_PID 5 92117173Sru#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ 93117173Sruchar hostname[MAXHOSTNAMELEN+1]; /* hostname */ 941844Swollmanchar *daytime; /* timenow in human readable form */ 95117122Sru 961844Swollmanstatic struct conf_entry *parse_file(); 9742450Sjdpstatic char *sob(char *p); 98117122Srustatic char *son(char *p); 991844Swollmanstatic char *missing_field(char *p,char *errline); 10096512Srustatic void do_entry(struct conf_entry *ent); 1011638Srgrimesstatic void PRS(int argc,char **argv); 102156772Sdeischenstatic void usage(); 103156772Sdeischenstatic void dotrim(char *log,char *pid_file,int numdays,int falgs,int perm,int owner_uid,int group_gid,int sig); 104156772Sdeischenstatic int log_trim(char *log); 105156772Sdeischenstatic void compress_log(char *log); 106156772Sdeischenstatic int sizefile(char *file); 107156772Sdeischenstatic int age_old_log(char *file); 108156772Sdeischenstatic pid_t get_pid(char *pid_file); 109156772Sdeischenstatic time_t parse8601(const char *s); 110156772Sdeischen 111156772Sdeischenint main(argc,argv) 112156772Sdeischen int argc; 113156772Sdeischen char **argv; 114156772Sdeischen{ 115156772Sdeischen struct conf_entry *p, *q; 116156772Sdeischen 117156772Sdeischen PRS(argc,argv); 118156772Sdeischen if (needroot && getuid() && geteuid()) 119156772Sdeischen errx(1, "must have root privs"); 120156772Sdeischen p = q = parse_file(); 121156772Sdeischen 122156772Sdeischen while (p) { 123156772Sdeischen do_entry(p); 124156772Sdeischen p=p->next; 125156772Sdeischen free((char *) q); 126156772Sdeischen q=p; 127156772Sdeischen } 128156772Sdeischen return(0); 129156772Sdeischen} 130157054Sdes 131156772Sdeischenstatic void do_entry(ent) 132156772Sdeischen struct conf_entry *ent; 133156772Sdeischen 134156772Sdeischen{ 135156772Sdeischen int size, modtime; 136156772Sdeischen char *pid_file; 137156772Sdeischen 138156772Sdeischen if (verbose) { 139156772Sdeischen if (ent->flags & CE_COMPACT) 140156772Sdeischen printf("%s <%dZ>: ",ent->log,ent->numlogs); 141156772Sdeischen else 14299362Sru printf("%s <%d>: ",ent->log,ent->numlogs); 14399362Sru } 14499362Sru size = sizefile(ent->log); 14599362Sru modtime = age_old_log(ent->log); 14696512Sru if (size < 0) { 14796512Sru if (verbose) 1481638Srgrimes printf("does not exist.\n"); 14996512Sru } else { 15096512Sru if (ent->flags & CE_TRIMAT) { 15196512Sru if (timenow < ent->trim_at 15296512Sru || difftime(timenow, ent->trim_at) >= 60*60) { 15396512Sru if (verbose) 15499362Sru printf("--> will trim at %s", 1551638Srgrimes ctime(&ent->trim_at)); 15696512Sru return; 15795114Sobrien } else if (verbose && ent->hours <= 0) { 158156854Sru printf("--> time is up\n"); 15996512Sru } 16096512Sru } 16195306Sru if (verbose && (ent->size > 0)) 16296512Sru printf("size (Kb): %d [%d] ", size, ent->size); 16396512Sru if (verbose && (ent->hours > 0)) 16496512Sru printf(" age (hr): %d [%d] ", modtime, ent->hours); 16596512Sru if (force || ((ent->size > 0) && (size >= ent->size)) || 16696512Sru (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) || 16774805Sru ((ent->hours > 0) && ((modtime >= ent->hours) 1681844Swollman || (modtime < 0)))) { 16999362Sru if (verbose) 17099362Sru printf("--> trimming log....\n"); 17196512Sru if (noaction && !verbose) { 17299362Sru if (ent->flags & CE_COMPACT) 1731844Swollman printf("%s <%dZ>: trimming\n", 17496512Sru ent->log,ent->numlogs); 17596512Sru else 1761638Srgrimes printf("%s <%d>: trimming\n", 17742915Sjdp ent->log,ent->numlogs); 17842915Sjdp } 17996512Sru if (ent->pid_file) { 18042915Sjdp pid_file = ent->pid_file; 18196512Sru } else { 18242915Sjdp /* Only try to notify syslog if we are root */ 18396343Sobrien if (needroot) 18496512Sru pid_file = _PATH_SYSLOGPID; 18591011Sru else 18628945Speter pid_file = NULL; 1871844Swollman } 188156813Sru dotrim(ent->log, pid_file, ent->numlogs, 18996512Sru ent->flags, ent->permissions, ent->uid, ent->gid, ent->sig); 19096512Sru } else { 19196512Sru if (verbose) 1922353Sbde printf("--> skipping\n"); 19396512Sru } 19496512Sru } 19596512Sru} 1963859Sbde 1971844Swollmanstatic void PRS(argc,argv) 198139106Sru int argc; 19996512Sru char **argv; 20096512Sru{ 20196512Sru int c; 20296512Sru char *p; 20392491Smarkm 20496512Sru timenow = time((time_t *) 0); 20596512Sru daytime = ctime(&timenow) + 4; 20692491Smarkm daytime[15] = '\0'; 20792491Smarkm 2081638Srgrimes /* Let's get our hostname */ 209144893Sharti (void) gethostname(hostname, sizeof(hostname)); 21096512Sru 21196512Sru /* Truncate domain */ 21296512Sru if ((p = strchr(hostname, '.'))) { 213156813Sru *p = '\0'; 21496512Sru } 2151638Srgrimes 2161638Srgrimes optind = 1; /* Start options parsing */ 21734179Sbde while ((c=getopt(argc,argv,"nrvFf:t:")) != -1) 21824750Sbde switch (c) { 21942450Sjdp case 'n': 22024750Sbde noaction++; 22124750Sbde break; 222139107Sru case 'r': 22331809Sbde needroot = 0; 22442915Sjdp break; 22527910Sasami case 'v': 22628945Speter verbose++; 2271638Srgrimes break; 2281638Srgrimes case 'f': 2291638Srgrimes conf = optarg; 230136019Sru break; 231139111Sru case 'F': 2322298Swollman force++; 2332298Swollman break; 234136019Sru default: 235136019Sru usage(); 2362298Swollman } 23749328Shoek} 23849328Shoek 23949328Shoekstatic void usage() 24049328Shoek{ 24156971Sru fprintf(stderr, "usage: newsyslog [-Fnrv] [-f config-file]\n"); 24249328Shoek exit(1); 24349328Shoek} 24449328Shoek 24549328Shoek/* Parse a configuration file and return a linked list of all the logs 24699362Sru * to process 24795306Sru */ 24899343Srustatic struct conf_entry *parse_file() 24995306Sru{ 250139110Sru FILE *f; 25192980Sdes char line[BUFSIZ], *parse, *q; 25249328Shoek char *errline, *group; 25396512Sru struct conf_entry *first = NULL; 254156854Sru struct conf_entry *working = NULL; 25592980Sdes struct passwd *pass; 25649328Shoek struct group *grp; 2571638Srgrimes int eol; 258116144Sobrien 259100872Sru if (strcmp(conf,"-")) 26049328Shoek f = fopen(conf,"r"); 26142915Sjdp else 26242915Sjdp f = stdin; 263119846Sru if (!f) 264119846Sru err(1, "%s", conf); 265119846Sru while (fgets(line,BUFSIZ,f)) { 266119846Sru if ((line[0]== '\n') || (line[0] == '#')) 267119730Speter continue; 268119846Sru errline = strdup(line); 269119846Sru if (!first) { 270119846Sru working = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 2711844Swollman first = working; 27228945Speter } else { 273119730Speter working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry)); 274119846Sru working = working->next; 275156813Sru } 276100872Sru 27749328Shoek q = parse = missing_field(sob(line),errline); 2781844Swollman parse = son(line); 279139106Sru if (!*parse) 280100872Sru errx(1, "malformed line (missing fields):\n%s", errline); 28196462Sru *parse = '\0'; 28296462Sru working->log = strdup(q); 283144893Sharti 28496462Sru q = parse = missing_field(sob(++parse),errline); 285141503Sphantom parse = son(parse); 28697769Sru if (!*parse) 28796668Sru errx(1, "malformed line (missing fields):\n%s", errline); 28899256Sru *parse = '\0'; 28996462Sru if ((group = strchr(q, ':')) != NULL || 290156813Sru (group = strrchr(q, '.')) != NULL) { 29196164Sru *group++ = '\0'; 29299343Sru if (*q) { 29396162Sru if (!(isnumber(*q))) { 29496162Sru if ((pass = getpwnam(q)) == NULL) 2951638Srgrimes errx(1, 2961638Srgrimes "error in config file; unknown user:\n%s", 2971638Srgrimes errline); 29895306Sru working->uid = pass->pw_uid; 299103713Smarkm } else 3001638Srgrimes working->uid = atoi(q); 3011638Srgrimes } else 302156813Sru working->uid = NONE; 3031638Srgrimes 30474842Sru q = group; 3051844Swollman if (*q) { 3061844Swollman if (!(isnumber(*q))) { 30734092Sbde if ((grp = getgrnam(q)) == NULL) 30899362Sru errx(1, 30996512Sru "error in config file; unknown group:\n%s", 31099362Sru errline); 311124637Sru working->gid = grp->gr_gid; 312124637Sru } else 313124637Sru working->gid = atoi(q); 31434092Sbde } else 31599362Sru working->gid = NONE; 31699362Sru 31799362Sru q = parse = missing_field(sob(++parse),errline); 318124637Sru parse = son(parse); 319124637Sru if (!*parse) 320124637Sru errx(1, "malformed line (missing fields):\n%s", errline); 32196512Sru *parse = '\0'; 32299362Sru } 32334092Sbde else 324100457Sru working->uid = working->gid = NONE; 325100457Sru 326100457Sru if (!sscanf(q,"%o",&working->permissions)) 327100457Sru errx(1, "error in config file; bad permissions:\n%s", 328100457Sru errline); 329100457Sru 330100457Sru q = parse = missing_field(sob(++parse),errline); 331100457Sru parse = son(parse); 332100457Sru if (!*parse) 333156854Sru errx(1, "malformed line (missing fields):\n%s", errline); 334100457Sru *parse = '\0'; 335100457Sru if (!sscanf(q,"%d",&working->numlogs)) 336100457Sru errx(1, "error in config file; bad number:\n%s", 337100457Sru errline); 338100457Sru 339100457Sru q = parse = missing_field(sob(++parse),errline); 340100457Sru parse = son(parse); 341100457Sru if (!*parse) 342100457Sru errx(1, "malformed line (missing fields):\n%s", errline); 343100457Sru *parse = '\0'; 344100457Sru if (isdigit(*q)) 345100457Sru working->size = atoi(q); 346100457Sru else 347100457Sru working->size = -1; 348100457Sru 349100457Sru working->flags = 0; 350100457Sru q = parse = missing_field(sob(++parse),errline); 351144893Sharti parse = son(parse); 352100457Sru eol = !*parse; 353100457Sru *parse = '\0'; 354100457Sru { 355100457Sru char *ep; 356100457Sru u_long ul; 357100457Sru 358157054Sdes ul = strtoul(q, &ep, 10); 359157054Sdes if (ep == q) 360100457Sru working->hours = 0; 361157054Sdes else if (*ep == '*') 362100457Sru working->hours = -1; 36316663Sjkh else if (ul > INT_MAX) 36476861Skris errx(1, "interval is too large:\n%s", errline); 36576861Skris else 366 working->hours = ul; 367 368 if (*ep != '\0' && *ep != '@' && *ep != '*') 369 errx(1, "malformed interval/at:\n%s", errline); 370 if (*ep == '@') { 371 if ((working->trim_at = parse8601(ep + 1)) 372 == (time_t)-1) 373 errx(1, "malformed at:\n%s", errline); 374 working->flags |= CE_TRIMAT; 375 } 376 } 377 378 if (eol) 379 q = NULL; 380 else { 381 q = parse = sob(++parse); /* Optional field */ 382 parse = son(parse); 383 if (!*parse) 384 eol = 1; 385 *parse = '\0'; 386 } 387 388 while (q && *q && !isspace(*q)) { 389 if ((*q == 'Z') || (*q == 'z')) 390 working->flags |= CE_COMPACT; 391 else if ((*q == 'B') || (*q == 'b')) 392 working->flags |= CE_BINARY; 393 else if (*q != '-') 394 errx(1, "illegal flag in config file -- %c", *q); 395 q++; 396 } 397 398 if (eol) 399 q = NULL; 400 else { 401 q = parse = sob(++parse); /* Optional field */ 402 parse = son(parse); 403 if (!*parse) 404 eol = 1; 405 *parse = '\0'; 406 } 407 408 working->pid_file = NULL; 409 if (q && *q) { 410 if (*q == '/') 411 working->pid_file = strdup(q); 412 else if (isdigit(*q)) 413 goto got_sig; 414 else 415 errx(1, "illegal pid file or signal number in config file:\n%s", errline); 416 } 417 418 if (eol) 419 q = NULL; 420 else { 421 q = parse = sob(++parse); /* Optional field */ 422 *(parse = son(parse)) = '\0'; 423 } 424 425 working->sig = SIGHUP; 426 if (q && *q) { 427 if (isdigit(*q)) { 428 got_sig: 429 working->sig = atoi(q); 430 } else { 431 err_sig: 432 errx(1, "illegal signal number in config file:\n%s", errline); 433 } 434 if (working->sig < 1 || working->sig >= NSIG) 435 goto err_sig; 436 } 437 438 free(errline); 439 } 440 if (working) 441 working->next = (struct conf_entry *) NULL; 442 (void) fclose(f); 443 return(first); 444} 445 446static char *missing_field(p,errline) 447 char *p,*errline; 448{ 449 if (!p || !*p) 450 errx(1, "missing field in config file:\n%s", errline); 451 return(p); 452} 453 454static void dotrim(log,pid_file,numdays,flags,perm,owner_uid,group_gid,sig) 455 char *log; 456 char *pid_file; 457 int numdays; 458 int flags; 459 int perm; 460 int owner_uid; 461 int group_gid; 462 int sig; 463{ 464 char file1 [MAXPATHLEN+1], file2 [MAXPATHLEN+1]; 465 char zfile1[MAXPATHLEN+1], zfile2[MAXPATHLEN+1]; 466 int notified, need_notification, fd, _numdays; 467 struct stat st; 468 pid_t pid; 469 470#ifdef _IBMR2 471/* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */ 472/* change it to be owned by uid -1, instead of leaving it as is, as it is */ 473/* supposed to. */ 474 if (owner_uid == -1) 475 owner_uid = geteuid(); 476#endif 477 478 /* Remove oldest log */ 479 (void) sprintf(file1,"%s.%d",log,numdays); 480 (void) strcpy(zfile1, file1); 481 (void) strcat(zfile1, COMPRESS_POSTFIX); 482 483 if (noaction) { 484 printf("rm -f %s\n", file1); 485 printf("rm -f %s\n", zfile1); 486 } else { 487 (void) unlink(file1); 488 (void) unlink(zfile1); 489 } 490 491 /* Move down log files */ 492 _numdays = numdays; /* preserve */ 493 while (numdays--) { 494 (void) strcpy(file2,file1); 495 (void) sprintf(file1,"%s.%d",log,numdays); 496 (void) strcpy(zfile1, file1); 497 (void) strcpy(zfile2, file2); 498 if (lstat(file1, &st)) { 499 (void) strcat(zfile1, COMPRESS_POSTFIX); 500 (void) strcat(zfile2, COMPRESS_POSTFIX); 501 if (lstat(zfile1, &st)) continue; 502 } 503 if (noaction) { 504 printf("mv %s %s\n",zfile1,zfile2); 505 printf("chmod %o %s\n", perm, zfile2); 506 printf("chown %d.%d %s\n", 507 owner_uid, group_gid, zfile2); 508 } else { 509 (void) rename(zfile1, zfile2); 510 (void) chmod(zfile2, perm); 511 (void) chown(zfile2, owner_uid, group_gid); 512 } 513 } 514 if (!noaction && !(flags & CE_BINARY)) 515 (void) log_trim(log); /* Report the trimming to the old log */ 516 517 if (!_numdays) { 518 if (noaction) 519 printf("rm %s\n",log); 520 else 521 (void)unlink(log); 522 } 523 else { 524 if (noaction) 525 printf("mv %s to %s\n",log,file1); 526 else 527 (void)rename(log, file1); 528 } 529 530 if (noaction) 531 printf("Start new log..."); 532 else { 533 fd = creat(log,perm); 534 if (fd < 0) 535 err(1, "can't start new log"); 536 if (fchown(fd, owner_uid, group_gid)) 537 err(1, "can't chmod new log file"); 538 (void) close(fd); 539 if (!(flags & CE_BINARY)) 540 if (log_trim(log)) /* Add status message */ 541 err(1, "can't add status message to log"); 542 } 543 if (noaction) 544 printf("chmod %o %s...\n", perm, log); 545 else 546 (void) chmod(log,perm); 547 548 pid = 0; 549 need_notification = notified = 0; 550 if (pid_file != NULL) { 551 need_notification = 1; 552 pid = get_pid(pid_file); 553 } 554 555 if (pid) { 556 if (noaction) { 557 notified = 1; 558 printf("kill -%d %d\n", sig, (int)pid); 559 } else if (kill(pid,sig)) 560 warn("can't notify daemon, pid %d", (int)pid); 561 else { 562 notified = 1; 563 if (verbose) 564 printf("daemon pid %d notified\n", (int)pid); 565 } 566 } 567 568 if ((flags & CE_COMPACT)) { 569 if (need_notification && !notified) 570 warnx("log not compressed because daemon not notified"); 571 else if (noaction) 572 printf("Compress %s.0\n",log); 573 else { 574 if (notified) { 575 if (verbose) 576 printf("small pause to allow daemon to close log\n"); 577 sleep(10); 578 } 579 compress_log(log); 580 } 581 } 582} 583 584/* Log the fact that the logs were turned over */ 585static int log_trim(log) 586 char *log; 587{ 588 FILE *f; 589 if ((f = fopen(log,"a")) == NULL) 590 return(-1); 591 fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n", 592 daytime, hostname, (int)getpid()); 593 if (fclose(f) == EOF) 594 err(1, "log_trim: fclose:"); 595 return(0); 596} 597 598/* Fork of /usr/ucb/compress to compress the old log file */ 599static void compress_log(log) 600 char *log; 601{ 602 pid_t pid; 603 char tmp[MAXPATHLEN+1]; 604 605 (void) sprintf(tmp,"%s.0",log); 606 pid = fork(); 607 if (pid < 0) 608 err(1, "fork"); 609 else if (!pid) { 610 (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, 0); 611 err(1, _PATH_GZIP); 612 } 613} 614 615/* Return size in kilobytes of a file */ 616static int sizefile(file) 617 char *file; 618{ 619 struct stat sb; 620 621 if (stat(file,&sb) < 0) 622 return(-1); 623 return(kbytes(dbtob(sb.st_blocks))); 624} 625 626/* Return the age of old log file (file.0) */ 627static int age_old_log(file) 628 char *file; 629{ 630 struct stat sb; 631 char tmp[MAXPATHLEN+sizeof(".0")+sizeof(COMPRESS_POSTFIX)+1]; 632 633 (void) strcpy(tmp,file); 634 if (stat(strcat(tmp,".0"),&sb) < 0) 635 if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0) 636 return(-1); 637 return( (int) (timenow - sb.st_mtime + 1800) / 3600); 638} 639 640static pid_t get_pid(pid_file) 641 char *pid_file; 642{ 643 FILE *f; 644 char line[BUFSIZ]; 645 pid_t pid = 0; 646 647 if ((f = fopen(pid_file,"r")) == NULL) 648 warn("can't open %s pid file to restart a daemon", 649 pid_file); 650 else { 651 if (fgets(line,BUFSIZ,f)) { 652 pid = atol(line); 653 if (pid < MIN_PID || pid > MAX_PID) { 654 warnx("preposterous process number: %d", (int)pid); 655 pid = 0; 656 } 657 } else 658 warn("can't read %s pid file to restart a daemon", 659 pid_file); 660 (void)fclose(f); 661 } 662 return pid; 663} 664 665/* Skip Over Blanks */ 666char *sob(p) 667 register char *p; 668{ 669 while (p && *p && isspace(*p)) 670 p++; 671 return(p); 672} 673 674/* Skip Over Non-Blanks */ 675char *son(p) 676 register char *p; 677{ 678 while (p && *p && !isspace(*p)) 679 p++; 680 return(p); 681} 682 683/* 684 * Parse a limited subset of ISO 8601. 685 * The specific format is as follows: 686 * 687 * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 688 * 689 * We don't accept a timezone specification; missing fields (including 690 * timezone) are defaulted to the current date but time zero. 691 */ 692static time_t 693parse8601(const char *s) 694{ 695 char *t; 696 struct tm tm, *tmp; 697 u_long ul; 698 699 tmp = localtime(&timenow); 700 tm = *tmp; 701 702 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 703 704 ul = strtoul(s, &t, 10); 705 if (*t != '\0' && *t != 'T') 706 return -1; 707 708 /* 709 * Now t points either to the end of the string (if no time 710 * was provided) or to the letter `T' which separates date 711 * and time in ISO 8601. The pointer arithmetic is the same for 712 * either case. 713 */ 714 switch (t - s) { 715 case 8: 716 tm.tm_year = ((ul / 1000000) - 19) * 100; 717 ul = ul % 1000000; 718 case 6: 719 tm.tm_year = tm.tm_year - (tm.tm_year % 100); 720 tm.tm_year += ul / 10000; 721 ul = ul % 10000; 722 case 4: 723 tm.tm_mon = (ul / 100) - 1; 724 ul = ul % 100; 725 case 2: 726 tm.tm_mday = ul; 727 case 0: 728 break; 729 default: 730 return -1; 731 } 732 733 /* sanity check */ 734 if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 735 || tm.tm_mday < 1 || tm.tm_mday > 31) 736 return -1; 737 738 if (*t != '\0') { 739 s = ++t; 740 ul = strtoul(s, &t, 10); 741 if (*t != '\0' && !isspace(*t)) 742 return -1; 743 744 switch (t - s) { 745 case 6: 746 tm.tm_sec = ul % 100; 747 ul /= 100; 748 case 4: 749 tm.tm_min = ul % 100; 750 ul /= 100; 751 case 2: 752 tm.tm_hour = ul; 753 case 0: 754 break; 755 default: 756 return -1; 757 } 758 759 /* sanity check */ 760 if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 761 || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 762 return -1; 763 } 764 765 return mktime(&tm); 766} 767 768 769