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