newsyslog.c revision 95999
113244Sgraichen/*
213244Sgraichen * This file contains changes from the Open Software Foundation.
313244Sgraichen */
413244Sgraichen
513244Sgraichen/*
659004Shm * Copyright 1988, 1989 by the Massachusetts Institute of Technology
759004Shm *
859004Shm * Permission to use, copy, modify, and distribute this software and its
959004Shm * documentation for any purpose and without fee is hereby granted, provided
1059004Shm * that the above copyright notice appear in all copies and that both that
1159004Shm * copyright notice and this permission notice appear in supporting
1259004Shm * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
1359004Shm * used in advertising or publicity pertaining to distribution of the
1459004Shm * software without specific, written prior permission. M.I.T. and the M.I.T.
1559004Shm * S.I.P.B. make no representations about the suitability of this software
1659004Shm * for any purpose.  It is provided "as is" without express or implied
1759004Shm * warranty.
1859004Shm *
1959004Shm */
2013244Sgraichen
2113244Sgraichen/*
2259004Shm * newsyslog - roll over selected logs at the appropriate time, keeping the a
2359004Shm * specified number of backup files around.
2413244Sgraichen */
2513244Sgraichen
2613244Sgraichen#ifndef lint
2730160Scharnierstatic const char rcsid[] =
2859003Shm"$FreeBSD: head/usr.sbin/newsyslog/newsyslog.c 95999 2002-05-03 20:53:37Z maxim $";
2959004Shm#endif	/* not lint */
3013244Sgraichen
3143071Swollman#define OSF
3213244Sgraichen#ifndef COMPRESS_POSTFIX
3343071Swollman#define COMPRESS_POSTFIX ".gz"
3413244Sgraichen#endif
3580638Sobrien#ifndef	BZCOMPRESS_POSTFIX
3680638Sobrien#define	BZCOMPRESS_POSTFIX ".bz2"
3780638Sobrien#endif
3813244Sgraichen
3930160Scharnier#include <ctype.h>
4030160Scharnier#include <err.h>
4195999Smaxim#include <errno.h>
4230160Scharnier#include <fcntl.h>
4330160Scharnier#include <grp.h>
4443071Swollman#include <paths.h>
4530160Scharnier#include <pwd.h>
4630160Scharnier#include <signal.h>
4713244Sgraichen#include <stdio.h>
4813244Sgraichen#include <stdlib.h>
4913244Sgraichen#include <string.h>
5043071Swollman#include <time.h>
5116240Salex#include <unistd.h>
5213244Sgraichen#include <sys/types.h>
5313244Sgraichen#include <sys/stat.h>
5413244Sgraichen#include <sys/param.h>
5513244Sgraichen#include <sys/wait.h>
5613244Sgraichen
5743071Swollman#include "pathnames.h"
5843071Swollman
5913244Sgraichen#define kbytes(size)  (((size) + 1023) >> 10)
6059004Shm
6113244Sgraichen#ifdef _IBMR2
6213244Sgraichen/* Calculates (db * DEV_BSIZE) */
6359003Shm#define dbtob(db)  ((unsigned)(db) << UBSHIFT)
6413244Sgraichen#endif
6513244Sgraichen
6659003Shm#define CE_COMPACT 1		/* Compact the achived log files */
6759003Shm#define CE_BINARY  2		/* Logfile is in binary, don't add */
6880638Sobrien#define CE_BZCOMPACT 8		/* Compact the achived log files with bzip2 */
6959004Shm				/*  status messages */
7043071Swollman#define	CE_TRIMAT  4		/* trim at a specific time */
7143071Swollman
7213244Sgraichen#define NONE -1
7359003Shm
7413244Sgraichenstruct conf_entry {
7559003Shm	char *log;		/* Name of the log */
7659003Shm	char *pid_file;		/* PID file */
7759003Shm	int uid;		/* Owner of log */
7859003Shm	int gid;		/* Group of log */
7959003Shm	int numlogs;		/* Number of logs to keep */
8059003Shm	int size;		/* Size cutoff to trigger trimming the log */
8159003Shm	int hours;		/* Hours between log trimming */
8259003Shm	time_t trim_at;		/* Specific time to do trimming */
8359003Shm	int permissions;	/* File permissions on the log */
8480646Sobrien	int flags;		/* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */
8559003Shm	int sig;		/* Signal to send */
8659003Shm	struct conf_entry *next;/* Linked list pointer */
8713244Sgraichen};
8813244Sgraichen
8959004Shmint archtodir = 0;		/* Archive old logfiles to other directory */
9059003Shmint verbose = 0;		/* Print out what's going on */
9159003Shmint needroot = 1;		/* Root privs are necessary */
9259003Shmint noaction = 0;		/* Don't do anything, just show it */
9359003Shmint force = 0;			/* Force the trim no matter what */
9459004Shmchar *archdirname;		/* Directory path to old logfiles archive */
9580640Sobrienconst char *conf = _PATH_CONF;	/* Configuration file to use */
9659003Shmtime_t timenow;
9759003Shm
9825443Sache#define MIN_PID         5
9959003Shm#define MAX_PID		99999	/* was lower, see /usr/include/sys/proc.h */
10071299Sjedgarchar hostname[MAXHOSTNAMELEN];	/* hostname */
10159003Shmchar *daytime;			/* timenow in human readable form */
10213244Sgraichen
10360373Sdesstatic struct conf_entry *parse_file(char **files);
10416240Salexstatic char *sob(char *p);
10516240Salexstatic char *son(char *p);
10659003Shmstatic char *missing_field(char *p, char *errline);
10759003Shmstatic void do_entry(struct conf_entry * ent);
10859003Shmstatic void PRS(int argc, char **argv);
10980640Sobrienstatic void usage(void);
11080646Sobrienstatic void dotrim(char *log, const char *pid_file, int numdays, int falgs,
11180646Sobrien		int perm, int owner_uid, int group_gid, int sig);
11216240Salexstatic int log_trim(char *log);
11316240Salexstatic void compress_log(char *log);
11480638Sobrienstatic void bzcompress_log(char *log);
11516240Salexstatic int sizefile(char *file);
11616240Salexstatic int age_old_log(char *file);
11780640Sobrienstatic pid_t get_pid(const char *pid_file);
11893659Scjcstatic time_t parse8601(char *s, char *errline);
11980646Sobrienstatic void movefile(char *from, char *to, int perm, int owner_uid,
12080646Sobrien		int group_gid);
12159004Shmstatic void createdir(char *dirpart);
12293659Scjcstatic time_t parseDWM(char *s, char *errline);
12313244Sgraichen
12459004Shmint
12559004Shmmain(int argc, char **argv)
12613244Sgraichen{
12759003Shm	struct conf_entry *p, *q;
12825443Sache
12959003Shm	PRS(argc, argv);
13059003Shm	if (needroot && getuid() && geteuid())
13159003Shm		errx(1, "must have root privs");
13260373Sdes	p = q = parse_file(argv + optind);
13359003Shm
13459003Shm	while (p) {
13559003Shm		do_entry(p);
13659003Shm		p = p->next;
13759003Shm		free((char *) q);
13859003Shm		q = p;
13959003Shm	}
14095999Smaxim	while (wait(NULL) > 0 || errno == EINTR)
14195999Smaxim		;
14259003Shm	return (0);
14313244Sgraichen}
14413244Sgraichen
14559004Shmstatic void
14659004Shmdo_entry(struct conf_entry * ent)
14713244Sgraichen{
14859003Shm	int size, modtime;
14980640Sobrien	const char *pid_file;
15059003Shm
15159003Shm	if (verbose) {
15259003Shm		if (ent->flags & CE_COMPACT)
15359003Shm			printf("%s <%dZ>: ", ent->log, ent->numlogs);
15480638Sobrien		else if (ent->flags & CE_BZCOMPACT)
15580638Sobrien			printf("%s <%dJ>: ", ent->log, ent->numlogs);
15659003Shm		else
15759003Shm			printf("%s <%d>: ", ent->log, ent->numlogs);
15859003Shm	}
15959003Shm	size = sizefile(ent->log);
16059003Shm	modtime = age_old_log(ent->log);
16159003Shm	if (size < 0) {
16259003Shm		if (verbose)
16359003Shm			printf("does not exist.\n");
16459003Shm	} else {
16590240Sroam		if (ent->flags & CE_TRIMAT && !force) {
16643071Swollman			if (timenow < ent->trim_at
16759003Shm			    || difftime(timenow, ent->trim_at) >= 60 * 60) {
16843071Swollman				if (verbose)
16943071Swollman					printf("--> will trim at %s",
17059003Shm					    ctime(&ent->trim_at));
17143071Swollman				return;
17243071Swollman			} else if (verbose && ent->hours <= 0) {
17343071Swollman				printf("--> time is up\n");
17443071Swollman			}
17543071Swollman		}
17659003Shm		if (verbose && (ent->size > 0))
17759003Shm			printf("size (Kb): %d [%d] ", size, ent->size);
17859003Shm		if (verbose && (ent->hours > 0))
17959003Shm			printf(" age (hr): %d [%d] ", modtime, ent->hours);
18059003Shm		if (force || ((ent->size > 0) && (size >= ent->size)) ||
18143071Swollman		    (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) ||
18259003Shm		    ((ent->hours > 0) && ((modtime >= ent->hours)
18359003Shm			    || (modtime < 0)))) {
18459003Shm			if (verbose)
18559003Shm				printf("--> trimming log....\n");
18659003Shm			if (noaction && !verbose) {
18759003Shm				if (ent->flags & CE_COMPACT)
18859003Shm					printf("%s <%dZ>: trimming\n",
18959003Shm					    ent->log, ent->numlogs);
19080638Sobrien				else if (ent->flags & CE_BZCOMPACT)
19180638Sobrien					printf("%s <%dJ>: trimming\n",
19280638Sobrien					    ent->log, ent->numlogs);
19359003Shm				else
19459003Shm					printf("%s <%d>: trimming\n",
19559003Shm					    ent->log, ent->numlogs);
19659003Shm			}
19735920Shoek			if (ent->pid_file) {
19835920Shoek				pid_file = ent->pid_file;
19935920Shoek			} else {
20035920Shoek				/* Only try to notify syslog if we are root */
20135920Shoek				if (needroot)
20243071Swollman					pid_file = _PATH_SYSLOGPID;
20335920Shoek				else
20435920Shoek					pid_file = NULL;
20535920Shoek			}
20659003Shm			dotrim(ent->log, pid_file, ent->numlogs,
20780646Sobrien			    ent->flags, ent->permissions, ent->uid, ent->gid,
20880646Sobrien			    ent->sig);
20959003Shm		} else {
21059003Shm			if (verbose)
21159003Shm				printf("--> skipping\n");
21259003Shm		}
21359003Shm	}
21413244Sgraichen}
21513244Sgraichen
21659004Shmstatic void
21759004ShmPRS(int argc, char **argv)
21813244Sgraichen{
21959003Shm	int c;
22059003Shm	char *p;
22113244Sgraichen
22259003Shm	timenow = time((time_t *) 0);
22359003Shm	daytime = ctime(&timenow) + 4;
22459003Shm	daytime[15] = '\0';
22513244Sgraichen
22659003Shm	/* Let's get our hostname */
22759003Shm	(void) gethostname(hostname, sizeof(hostname));
22813244Sgraichen
22913244Sgraichen	/* Truncate domain */
23016240Salex	if ((p = strchr(hostname, '.'))) {
23113244Sgraichen		*p = '\0';
23213244Sgraichen	}
23359004Shm	while ((c = getopt(argc, argv, "nrvFf:a:t:")) != -1)
23459003Shm		switch (c) {
23559003Shm		case 'n':
23659003Shm			noaction++;
23735920Shoek			break;
23859004Shm		case 'a':
23959004Shm			archtodir++;
24059004Shm			archdirname = optarg;
24159004Shm			break;
24259003Shm		case 'r':
24359003Shm			needroot = 0;
24459003Shm			break;
24559003Shm		case 'v':
24659003Shm			verbose++;
24759003Shm			break;
24859003Shm		case 'f':
24959003Shm			conf = optarg;
25059003Shm			break;
25134584Spst		case 'F':
25234584Spst			force++;
25334584Spst			break;
25459003Shm		default:
25559003Shm			usage();
25659003Shm		}
25743071Swollman}
25813244Sgraichen
25959004Shmstatic void
26059004Shmusage(void)
26113244Sgraichen{
26280646Sobrien
26380646Sobrien	fprintf(stderr,
26480646Sobrien	    "usage: newsyslog [-Fnrv] [-f config-file] [-a directory]\n");
26559003Shm	exit(1);
26613244Sgraichen}
26713244Sgraichen
26859004Shm/*
26959004Shm * Parse a configuration file and return a linked list of all the logs to
27059004Shm * process
27113244Sgraichen */
27259003Shmstatic struct conf_entry *
27360373Sdesparse_file(char **files)
27413244Sgraichen{
27559003Shm	FILE *f;
27659003Shm	char line[BUFSIZ], *parse, *q;
27759003Shm	char *errline, *group;
27860373Sdes	char **p;
27980646Sobrien	struct conf_entry *first, *working;
28059003Shm	struct passwd *pass;
28159003Shm	struct group *grp;
28225518Sbrian	int eol;
28313244Sgraichen
28480646Sobrien	first = working = NULL;
28580646Sobrien
28659003Shm	if (strcmp(conf, "-"))
28759003Shm		f = fopen(conf, "r");
28859003Shm	else
28959003Shm		f = stdin;
29059003Shm	if (!f)
29159003Shm		err(1, "%s", conf);
29259003Shm	while (fgets(line, BUFSIZ, f)) {
29359003Shm		if ((line[0] == '\n') || (line[0] == '#'))
29459003Shm			continue;
29559003Shm		errline = strdup(line);
29660373Sdes
29760373Sdes		q = parse = missing_field(sob(line), errline);
29860373Sdes		parse = son(line);
29960373Sdes		if (!*parse)
30080646Sobrien			errx(1, "malformed line (missing fields):\n%s",
30180646Sobrien			    errline);
30260373Sdes		*parse = '\0';
30360373Sdes
30460373Sdes		if (*files) {
30560373Sdes			for (p = files; *p; ++p)
30660373Sdes				if (strcmp(*p, q) == 0)
30760373Sdes					break;
30860373Sdes			if (!*p)
30960373Sdes				continue;
31060373Sdes		}
31160373Sdes
31259003Shm		if (!first) {
31380646Sobrien			if ((working = malloc(sizeof(struct conf_entry))) ==
31480646Sobrien			    NULL)
31571299Sjedgar				err(1, "malloc");
31659003Shm			first = working;
31759003Shm		} else {
31880646Sobrien			if ((working->next = malloc(sizeof(struct conf_entry)))
31980646Sobrien			    == NULL)
32071299Sjedgar				err(1, "malloc");
32159003Shm			working = working->next;
32259003Shm		}
32371299Sjedgar		if ((working->log = strdup(q)) == NULL)
32471299Sjedgar			err(1, "strdup");
32513244Sgraichen
32659003Shm		q = parse = missing_field(sob(++parse), errline);
32759003Shm		parse = son(parse);
32825518Sbrian		if (!*parse)
32980646Sobrien			errx(1, "malformed line (missing fields):\n%s",
33080646Sobrien			    errline);
33159003Shm		*parse = '\0';
33259003Shm		if ((group = strchr(q, ':')) != NULL ||
33359003Shm		    (group = strrchr(q, '.')) != NULL) {
33459003Shm			*group++ = '\0';
33559003Shm			if (*q) {
33659003Shm				if (!(isnumber(*q))) {
33759003Shm					if ((pass = getpwnam(q)) == NULL)
33859003Shm						errx(1,
33980646Sobrien				     "error in config file; unknown user:\n%s",
34059003Shm						    errline);
34159003Shm					working->uid = pass->pw_uid;
34259003Shm				} else
34359003Shm					working->uid = atoi(q);
34459003Shm			} else
34559003Shm				working->uid = NONE;
34613244Sgraichen
34759003Shm			q = group;
34859003Shm			if (*q) {
34959003Shm				if (!(isnumber(*q))) {
35059003Shm					if ((grp = getgrnam(q)) == NULL)
35159003Shm						errx(1,
35280646Sobrien				    "error in config file; unknown group:\n%s",
35359003Shm						    errline);
35459003Shm					working->gid = grp->gr_gid;
35559003Shm				} else
35659003Shm					working->gid = atoi(q);
35759003Shm			} else
35859003Shm				working->gid = NONE;
35913244Sgraichen
36059003Shm			q = parse = missing_field(sob(++parse), errline);
36159003Shm			parse = son(parse);
36259003Shm			if (!*parse)
36380646Sobrien				errx(1, "malformed line (missing fields):\n%s",
36480646Sobrien				    errline);
36559003Shm			*parse = '\0';
36659003Shm		} else
36759003Shm			working->uid = working->gid = NONE;
36859003Shm
36959003Shm		if (!sscanf(q, "%o", &working->permissions))
37059003Shm			errx(1, "error in config file; bad permissions:\n%s",
37159003Shm			    errline);
37259003Shm
37359003Shm		q = parse = missing_field(sob(++parse), errline);
37459003Shm		parse = son(parse);
37525518Sbrian		if (!*parse)
37680646Sobrien			errx(1, "malformed line (missing fields):\n%s",
37780646Sobrien			    errline);
37859003Shm		*parse = '\0';
37959003Shm		if (!sscanf(q, "%d", &working->numlogs))
38059003Shm			errx(1, "error in config file; bad number:\n%s",
38159003Shm			    errline);
38213244Sgraichen
38359003Shm		q = parse = missing_field(sob(++parse), errline);
38459003Shm		parse = son(parse);
38525518Sbrian		if (!*parse)
38680646Sobrien			errx(1, "malformed line (missing fields):\n%s",
38780646Sobrien			    errline);
38859003Shm		*parse = '\0';
38959003Shm		if (isdigit(*q))
39059003Shm			working->size = atoi(q);
39159003Shm		else
39259003Shm			working->size = -1;
39359003Shm
39459003Shm		working->flags = 0;
39559003Shm		q = parse = missing_field(sob(++parse), errline);
39659003Shm		parse = son(parse);
39725518Sbrian		eol = !*parse;
39859003Shm		*parse = '\0';
39943071Swollman		{
40059003Shm			char *ep;
40159003Shm			u_long ul;
40213244Sgraichen
40343071Swollman			ul = strtoul(q, &ep, 10);
40443071Swollman			if (ep == q)
40543071Swollman				working->hours = 0;
40643071Swollman			else if (*ep == '*')
40743071Swollman				working->hours = -1;
40843071Swollman			else if (ul > INT_MAX)
40943071Swollman				errx(1, "interval is too large:\n%s", errline);
41043071Swollman			else
41143071Swollman				working->hours = ul;
41243071Swollman
41380646Sobrien			if (*ep != '\0' && *ep != '@' && *ep != '*' &&
41480646Sobrien			    *ep != '$')
41543071Swollman				errx(1, "malformed interval/at:\n%s", errline);
41643071Swollman			if (*ep == '@') {
41793659Scjc				if ((working->trim_at = parse8601(ep + 1, errline))
41859003Shm				    == (time_t) - 1)
41943071Swollman					errx(1, "malformed at:\n%s", errline);
42043071Swollman				working->flags |= CE_TRIMAT;
42159004Shm			} else if (*ep == '$') {
42293659Scjc				if ((working->trim_at = parseDWM(ep + 1, errline))
42359004Shm				    == (time_t) - 1)
42459004Shm					errx(1, "malformed at:\n%s", errline);
42559004Shm				working->flags |= CE_TRIMAT;
42643071Swollman			}
42743071Swollman		}
42843071Swollman
42925518Sbrian		if (eol)
43059003Shm			q = NULL;
43125518Sbrian		else {
43259003Shm			q = parse = sob(++parse);	/* Optional field */
43359003Shm			parse = son(parse);
43459003Shm			if (!*parse)
43559003Shm				eol = 1;
43659003Shm			*parse = '\0';
43725518Sbrian		}
43825443Sache
43959003Shm		while (q && *q && !isspace(*q)) {
44059003Shm			if ((*q == 'Z') || (*q == 'z'))
44159003Shm				working->flags |= CE_COMPACT;
44280638Sobrien			else if ((*q == 'J') || (*q == 'j'))
44380638Sobrien				working->flags |= CE_BZCOMPACT;
44459003Shm			else if ((*q == 'B') || (*q == 'b'))
44559003Shm				working->flags |= CE_BINARY;
44659003Shm			else if (*q != '-')
44780646Sobrien				errx(1, "illegal flag in config file -- %c",
44880646Sobrien				    *q);
44959003Shm			q++;
45059003Shm		}
45159003Shm
45225518Sbrian		if (eol)
45359003Shm			q = NULL;
45425518Sbrian		else {
45559003Shm			q = parse = sob(++parse);	/* Optional field */
45659003Shm			parse = son(parse);
45759003Shm			if (!*parse)
45859003Shm				eol = 1;
45959003Shm			*parse = '\0';
46025518Sbrian		}
46125443Sache
46225443Sache		working->pid_file = NULL;
46325443Sache		if (q && *q) {
46425443Sache			if (*q == '/')
46525443Sache				working->pid_file = strdup(q);
46636817Sache			else if (isdigit(*q))
46736817Sache				goto got_sig;
46859003Shm			else
46980646Sobrien				errx(1,
47080646Sobrien			"illegal pid file or signal number in config file:\n%s",
47180646Sobrien				    errline);
47225443Sache		}
47336817Sache		if (eol)
47459003Shm			q = NULL;
47536817Sache		else {
47659003Shm			q = parse = sob(++parse);	/* Optional field */
47759003Shm			*(parse = son(parse)) = '\0';
47836817Sache		}
47936817Sache
48036817Sache		working->sig = SIGHUP;
48136817Sache		if (q && *q) {
48236817Sache			if (isdigit(*q)) {
48359003Shm		got_sig:
48436817Sache				working->sig = atoi(q);
48536817Sache			} else {
48659003Shm		err_sig:
48780646Sobrien				errx(1,
48880646Sobrien				    "illegal signal number in config file:\n%s",
48980646Sobrien				    errline);
49036817Sache			}
49136817Sache			if (working->sig < 1 || working->sig >= NSIG)
49236817Sache				goto err_sig;
49336817Sache		}
49459003Shm		free(errline);
49559003Shm	}
49659003Shm	if (working)
49759003Shm		working->next = (struct conf_entry *) NULL;
49859003Shm	(void) fclose(f);
49959003Shm	return (first);
50013244Sgraichen}
50113244Sgraichen
50259003Shmstatic char *
50359004Shmmissing_field(char *p, char *errline)
50413244Sgraichen{
50580646Sobrien
50659003Shm	if (!p || !*p)
50759003Shm		errx(1, "missing field in config file:\n%s", errline);
50859003Shm	return (p);
50913244Sgraichen}
51013244Sgraichen
51159004Shmstatic void
51280640Sobriendotrim(char *log, const char *pid_file, int numdays, int flags, int perm,
51359004Shm    int owner_uid, int group_gid, int sig)
51413244Sgraichen{
51571299Sjedgar	char dirpart[MAXPATHLEN], namepart[MAXPATHLEN];
51671299Sjedgar	char file1[MAXPATHLEN], file2[MAXPATHLEN];
51771299Sjedgar	char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
51880638Sobrien	char jfile1[MAXPATHLEN];
51994352Ssheldonh	char tfile[MAXPATHLEN];
52059003Shm	int notified, need_notification, fd, _numdays;
52159003Shm	struct stat st;
52259003Shm	pid_t pid;
52313244Sgraichen
52413244Sgraichen#ifdef _IBMR2
52559004Shm	/*
52659004Shm	 * AIX 3.1 has a broken fchown- if the owner_uid is -1, it will
52759004Shm	 * actually change it to be owned by uid -1, instead of leaving it
52859004Shm	 * as is, as it is supposed to.
52959004Shm	 */
53059003Shm	if (owner_uid == -1)
53159003Shm		owner_uid = geteuid();
53213244Sgraichen#endif
53313244Sgraichen
53459004Shm	if (archtodir) {
53559004Shm		char *p;
53613244Sgraichen
53759004Shm		/* build complete name of archive directory into dirpart */
53859004Shm		if (*archdirname == '/') {	/* absolute */
53971299Sjedgar			strlcpy(dirpart, archdirname, sizeof(dirpart));
54059004Shm		} else {	/* relative */
54159004Shm			/* get directory part of logfile */
54271299Sjedgar			strlcpy(dirpart, log, sizeof(dirpart));
54359004Shm			if ((p = rindex(dirpart, '/')) == NULL)
54459004Shm				dirpart[0] = '\0';
54559004Shm			else
54659004Shm				*(p + 1) = '\0';
54771299Sjedgar			strlcat(dirpart, archdirname, sizeof(dirpart));
54859004Shm		}
54959004Shm
55059004Shm		/* check if archive directory exists, if not, create it */
55159004Shm		if (lstat(dirpart, &st))
55259004Shm			createdir(dirpart);
55359004Shm
55459004Shm		/* get filename part of logfile */
55559004Shm		if ((p = rindex(log, '/')) == NULL)
55671299Sjedgar			strlcpy(namepart, log, sizeof(namepart));
55759004Shm		else
55871299Sjedgar			strlcpy(namepart, p + 1, sizeof(namepart));
55959004Shm
56059004Shm		/* name of oldest log */
56180646Sobrien		(void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart,
56280646Sobrien		    namepart, numdays);
56371299Sjedgar		(void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
56471299Sjedgar		    COMPRESS_POSTFIX);
56580638Sobrien		snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
56680638Sobrien		    BZCOMPRESS_POSTFIX);
56759004Shm	} else {
56859004Shm		/* name of oldest log */
56971299Sjedgar		(void) snprintf(file1, sizeof(file1), "%s.%d", log, numdays);
57071299Sjedgar		(void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
57171299Sjedgar		    COMPRESS_POSTFIX);
57280638Sobrien		snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
57380638Sobrien		    BZCOMPRESS_POSTFIX);
57459004Shm	}
57559004Shm
57659003Shm	if (noaction) {
57759003Shm		printf("rm -f %s\n", file1);
57859003Shm		printf("rm -f %s\n", zfile1);
57980638Sobrien		printf("rm -f %s\n", jfile1);
58059003Shm	} else {
58159003Shm		(void) unlink(file1);
58259003Shm		(void) unlink(zfile1);
58380638Sobrien		(void) unlink(jfile1);
58459003Shm	}
58513244Sgraichen
58659003Shm	/* Move down log files */
58718188Sjkh	_numdays = numdays;	/* preserve */
58859003Shm	while (numdays--) {
58959004Shm
59071299Sjedgar		(void) strlcpy(file2, file1, sizeof(file2));
59159004Shm
59259004Shm		if (archtodir)
59380646Sobrien			(void) snprintf(file1, sizeof(file1), "%s/%s.%d",
59480646Sobrien			    dirpart, namepart, numdays);
59559004Shm		else
59680646Sobrien			(void) snprintf(file1, sizeof(file1), "%s.%d", log,
59780646Sobrien			    numdays);
59859004Shm
59971299Sjedgar		(void) strlcpy(zfile1, file1, sizeof(zfile1));
60071299Sjedgar		(void) strlcpy(zfile2, file2, sizeof(zfile2));
60159003Shm		if (lstat(file1, &st)) {
60280646Sobrien			(void) strlcat(zfile1, COMPRESS_POSTFIX,
60380646Sobrien			    sizeof(zfile1));
60480646Sobrien			(void) strlcat(zfile2, COMPRESS_POSTFIX,
60580646Sobrien			    sizeof(zfile2));
60680638Sobrien			if (lstat(zfile1, &st)) {
60780638Sobrien				strlcpy(zfile1, file1, sizeof(zfile1));
60880638Sobrien				strlcpy(zfile2, file2, sizeof(zfile2));
60980638Sobrien				strlcat(zfile1, BZCOMPRESS_POSTFIX,
61080638Sobrien				    sizeof(zfile1));
61180638Sobrien				strlcat(zfile2, BZCOMPRESS_POSTFIX,
61280638Sobrien				    sizeof(zfile2));
61380638Sobrien				if (lstat(zfile1, &st))
61480638Sobrien					continue;
61580638Sobrien			}
61659003Shm		}
61759003Shm		if (noaction) {
61859003Shm			printf("mv %s %s\n", zfile1, zfile2);
61959003Shm			printf("chmod %o %s\n", perm, zfile2);
62080684Sobrien			printf("chown %d:%d %s\n",
62159003Shm			    owner_uid, group_gid, zfile2);
62259003Shm		} else {
62359003Shm			(void) rename(zfile1, zfile2);
62459003Shm			(void) chmod(zfile2, perm);
62559003Shm			(void) chown(zfile2, owner_uid, group_gid);
62659003Shm		}
62759003Shm	}
62859003Shm	if (!noaction && !(flags & CE_BINARY))
62959003Shm		(void) log_trim(log);	/* Report the trimming to the old log */
63013244Sgraichen
63118188Sjkh	if (!_numdays) {
63218075Sjkh		if (noaction)
63359003Shm			printf("rm %s\n", log);
63418075Sjkh		else
63559003Shm			(void) unlink(log);
63659003Shm	} else {
63718075Sjkh		if (noaction)
63859003Shm			printf("mv %s to %s\n", log, file1);
63959004Shm		else {
64059004Shm			if (archtodir)
64180646Sobrien				movefile(log, file1, perm, owner_uid,
64280646Sobrien				    group_gid);
64359004Shm			else
64459004Shm				(void) rename(log, file1);
64559004Shm		}
64618075Sjkh	}
64718075Sjkh
64859003Shm	if (noaction)
64959003Shm		printf("Start new log...");
65059003Shm	else {
65194352Ssheldonh		strlcpy(tfile, log, sizeof(tfile));
65294352Ssheldonh		strlcat(tfile, ".XXXXXX", sizeof(tfile));
65394352Ssheldonh		mkstemp(tfile);
65494352Ssheldonh		fd = creat(tfile, perm);
65559003Shm		if (fd < 0)
65659003Shm			err(1, "can't start new log");
65759003Shm		if (fchown(fd, owner_uid, group_gid))
65859003Shm			err(1, "can't chmod new log file");
65959003Shm		(void) close(fd);
66059003Shm		if (!(flags & CE_BINARY))
66194352Ssheldonh			if (log_trim(tfile))	/* Add status message */
66259003Shm				err(1, "can't add status message to log");
66359003Shm	}
66459003Shm	if (noaction)
66559003Shm		printf("chmod %o %s...\n", perm, log);
66694352Ssheldonh	else {
66794352Ssheldonh		(void) chmod(tfile, perm);
66894352Ssheldonh		if (rename(tfile, log) < 0) {
66994352Ssheldonh			err(1, "can't start new log");
67094352Ssheldonh			(void) unlink(tfile);
67194352Ssheldonh		}
67294352Ssheldonh	}
67325443Sache
67425443Sache	pid = 0;
67525443Sache	need_notification = notified = 0;
67625443Sache	if (pid_file != NULL) {
67725443Sache		need_notification = 1;
67825443Sache		pid = get_pid(pid_file);
67925443Sache	}
68025443Sache	if (pid) {
68125443Sache		if (noaction) {
68225443Sache			notified = 1;
68359003Shm			printf("kill -%d %d\n", sig, (int) pid);
68459003Shm		} else if (kill(pid, sig))
68559003Shm			warn("can't notify daemon, pid %d", (int) pid);
68625443Sache		else {
68725443Sache			notified = 1;
68825443Sache			if (verbose)
68959003Shm				printf("daemon pid %d notified\n", (int) pid);
69025443Sache		}
69125443Sache	}
69280638Sobrien	if ((flags & CE_COMPACT) || (flags & CE_BZCOMPACT)) {
69325443Sache		if (need_notification && !notified)
69480646Sobrien			warnx(
69580646Sobrien			    "log %s not compressed because daemon not notified",
69680646Sobrien			    log);
69725443Sache		else if (noaction)
69859003Shm			printf("Compress %s.0\n", log);
69925443Sache		else {
70025443Sache			if (notified) {
70125443Sache				if (verbose)
70225443Sache					printf("small pause to allow daemon to close log\n");
70331460Sache				sleep(10);
70425443Sache			}
70559004Shm			if (archtodir) {
70680646Sobrien				(void) snprintf(file1, sizeof(file1), "%s/%s",
70780646Sobrien				    dirpart, namepart);
70880638Sobrien				if (flags & CE_COMPACT)
70980638Sobrien					compress_log(file1);
71080638Sobrien				else if (flags & CE_BZCOMPACT)
71180638Sobrien					bzcompress_log(file1);
71259004Shm			} else {
71380638Sobrien				if (flags & CE_COMPACT)
71480638Sobrien					compress_log(log);
71580638Sobrien				else if (flags & CE_BZCOMPACT)
71680638Sobrien					bzcompress_log(log);
71759004Shm			}
71825443Sache		}
71959003Shm	}
72013244Sgraichen}
72113244Sgraichen
72213244Sgraichen/* Log the fact that the logs were turned over */
72359004Shmstatic int
72459004Shmlog_trim(char *log)
72513244Sgraichen{
72659003Shm	FILE *f;
72759003Shm
72859003Shm	if ((f = fopen(log, "a")) == NULL)
72959003Shm		return (-1);
73059003Shm	fprintf(f, "%s %s newsyslog[%d]: logfile turned over\n",
73159003Shm	    daytime, hostname, (int) getpid());
73259003Shm	if (fclose(f) == EOF)
73359003Shm		err(1, "log_trim: fclose:");
73459003Shm	return (0);
73513244Sgraichen}
73613244Sgraichen
73759004Shm/* Fork of gzip to compress the old log file */
73859004Shmstatic void
73959004Shmcompress_log(char *log)
74013244Sgraichen{
74159003Shm	pid_t pid;
74271299Sjedgar	char tmp[MAXPATHLEN];
74359003Shm
74471299Sjedgar	(void) snprintf(tmp, sizeof(tmp), "%s.0", log);
74525443Sache	pid = fork();
74659003Shm	if (pid < 0)
74780638Sobrien		err(1, "gzip fork");
74859003Shm	else if (!pid) {
74979452Sbrian		(void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, (char *)0);
75043071Swollman		err(1, _PATH_GZIP);
75159003Shm	}
75213244Sgraichen}
75313244Sgraichen
75480638Sobrien/* Fork of bzip2 to compress the old log file */
75580638Sobrienstatic void
75680638Sobrienbzcompress_log(char *log)
75780638Sobrien{
75880638Sobrien	pid_t pid;
75980638Sobrien	char tmp[MAXPATHLEN];
76080638Sobrien
76180638Sobrien	snprintf(tmp, sizeof(tmp), "%s.0", log);
76280638Sobrien	pid = fork();
76380638Sobrien	if (pid < 0)
76480638Sobrien		err(1, "bzip2 fork");
76580638Sobrien	else if (!pid) {
76686360Sobrien		execl(_PATH_BZIP2, _PATH_BZIP2, "-f", tmp, (char *)0);
76780638Sobrien		err(1, _PATH_BZIP2);
76880638Sobrien	}
76980638Sobrien}
77080638Sobrien
77113244Sgraichen/* Return size in kilobytes of a file */
77259004Shmstatic int
77359004Shmsizefile(char *file)
77413244Sgraichen{
77559003Shm	struct stat sb;
77613244Sgraichen
77759003Shm	if (stat(file, &sb) < 0)
77859003Shm		return (-1);
77959003Shm	return (kbytes(dbtob(sb.st_blocks)));
78013244Sgraichen}
78113244Sgraichen
78213244Sgraichen/* Return the age of old log file (file.0) */
78359004Shmstatic int
78459004Shmage_old_log(char *file)
78513244Sgraichen{
78659003Shm	struct stat sb;
78759003Shm	char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1];
78813244Sgraichen
78959004Shm	if (archtodir) {
79059004Shm		char *p;
79159004Shm
79259004Shm		/* build name of archive directory into tmp */
79359004Shm		if (*archdirname == '/') {	/* absolute */
79471299Sjedgar			strlcpy(tmp, archdirname, sizeof(tmp));
79559004Shm		} else {	/* relative */
79659004Shm			/* get directory part of logfile */
79771299Sjedgar			strlcpy(tmp, file, sizeof(tmp));
79859004Shm			if ((p = rindex(tmp, '/')) == NULL)
79959004Shm				tmp[0] = '\0';
80059004Shm			else
80159004Shm				*(p + 1) = '\0';
80271299Sjedgar			strlcat(tmp, archdirname, sizeof(tmp));
80359004Shm		}
80459004Shm
80571299Sjedgar		strlcat(tmp, "/", sizeof(tmp));
80659004Shm
80759004Shm		/* get filename part of logfile */
80859004Shm		if ((p = rindex(file, '/')) == NULL)
80971299Sjedgar			strlcat(tmp, file, sizeof(tmp));
81059004Shm		else
81171299Sjedgar			strlcat(tmp, p + 1, sizeof(tmp));
81259004Shm	} else {
81371299Sjedgar		(void) strlcpy(tmp, file, sizeof(tmp));
81459004Shm	}
81559004Shm
81659003Shm	if (stat(strcat(tmp, ".0"), &sb) < 0)
81759003Shm		if (stat(strcat(tmp, COMPRESS_POSTFIX), &sb) < 0)
81859003Shm			return (-1);
81959003Shm	return ((int) (timenow - sb.st_mtime + 1800) / 3600);
82013244Sgraichen}
82113244Sgraichen
82259004Shmstatic pid_t
82380640Sobrienget_pid(const char *pid_file)
82425443Sache{
82525443Sache	FILE *f;
82659003Shm	char line[BUFSIZ];
82725443Sache	pid_t pid = 0;
82813244Sgraichen
82959003Shm	if ((f = fopen(pid_file, "r")) == NULL)
83025443Sache		warn("can't open %s pid file to restart a daemon",
83159003Shm		    pid_file);
83225443Sache	else {
83359003Shm		if (fgets(line, BUFSIZ, f)) {
83425443Sache			pid = atol(line);
83525443Sache			if (pid < MIN_PID || pid > MAX_PID) {
83680646Sobrien				warnx("preposterous process number: %d",
83780646Sobrien				   (int)pid);
83825443Sache				pid = 0;
83925443Sache			}
84025443Sache		} else
84125443Sache			warn("can't read %s pid file to restart a daemon",
84259003Shm			    pid_file);
84359003Shm		(void) fclose(f);
84425443Sache	}
84525443Sache	return pid;
84625443Sache}
84725443Sache
84813244Sgraichen/* Skip Over Blanks */
84959003Shmchar *
85059004Shmsob(char *p)
85113244Sgraichen{
85259003Shm	while (p && *p && isspace(*p))
85359003Shm		p++;
85459003Shm	return (p);
85513244Sgraichen}
85613244Sgraichen
85713244Sgraichen/* Skip Over Non-Blanks */
85859003Shmchar *
85959004Shmson(char *p)
86013244Sgraichen{
86159003Shm	while (p && *p && !isspace(*p))
86259003Shm		p++;
86359003Shm	return (p);
86413244Sgraichen}
86543071Swollman
86643071Swollman/*
86759004Shm * Parse a limited subset of ISO 8601. The specific format is as follows:
86843071Swollman *
86959004Shm * [CC[YY[MM[DD]]]][THH[MM[SS]]]	(where `T' is the literal letter)
87043071Swollman *
87159004Shm * We don't accept a timezone specification; missing fields (including timezone)
87259004Shm * are defaulted to the current date but time zero.
87343071Swollman */
87443071Swollmanstatic time_t
87593659Scjcparse8601(char *s, char *errline)
87643071Swollman{
87759003Shm	char *t;
87893659Scjc	time_t tsecs;
87959003Shm	struct tm tm, *tmp;
88059003Shm	u_long ul;
88143071Swollman
88243071Swollman	tmp = localtime(&timenow);
88343071Swollman	tm = *tmp;
88443071Swollman
88543071Swollman	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
88643071Swollman
88743071Swollman	ul = strtoul(s, &t, 10);
88843071Swollman	if (*t != '\0' && *t != 'T')
88943071Swollman		return -1;
89043071Swollman
89143071Swollman	/*
89259003Shm	 * Now t points either to the end of the string (if no time was
89359003Shm	 * provided) or to the letter `T' which separates date and time in
89459003Shm	 * ISO 8601.  The pointer arithmetic is the same for either case.
89543071Swollman	 */
89643071Swollman	switch (t - s) {
89743071Swollman	case 8:
89843071Swollman		tm.tm_year = ((ul / 1000000) - 19) * 100;
89943071Swollman		ul = ul % 1000000;
90043071Swollman	case 6:
90180666Swollman		tm.tm_year -= tm.tm_year % 100;
90243071Swollman		tm.tm_year += ul / 10000;
90343071Swollman		ul = ul % 10000;
90443071Swollman	case 4:
90543071Swollman		tm.tm_mon = (ul / 100) - 1;
90643071Swollman		ul = ul % 100;
90743071Swollman	case 2:
90843071Swollman		tm.tm_mday = ul;
90943071Swollman	case 0:
91043071Swollman		break;
91143071Swollman	default:
91243071Swollman		return -1;
91343071Swollman	}
91443071Swollman
91543071Swollman	/* sanity check */
91643071Swollman	if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12
91743071Swollman	    || tm.tm_mday < 1 || tm.tm_mday > 31)
91843071Swollman		return -1;
91943071Swollman
92043071Swollman	if (*t != '\0') {
92143071Swollman		s = ++t;
92243071Swollman		ul = strtoul(s, &t, 10);
92343071Swollman		if (*t != '\0' && !isspace(*t))
92443071Swollman			return -1;
92543071Swollman
92643071Swollman		switch (t - s) {
92743071Swollman		case 6:
92843071Swollman			tm.tm_sec = ul % 100;
92943071Swollman			ul /= 100;
93043071Swollman		case 4:
93143071Swollman			tm.tm_min = ul % 100;
93243071Swollman			ul /= 100;
93343071Swollman		case 2:
93443071Swollman			tm.tm_hour = ul;
93543071Swollman		case 0:
93643071Swollman			break;
93743071Swollman		default:
93843071Swollman			return -1;
93943071Swollman		}
94043071Swollman
94143071Swollman		/* sanity check */
94243071Swollman		if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
94343071Swollman		    || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
94443071Swollman			return -1;
94543071Swollman	}
94693659Scjc	if ((tsecs = mktime(&tm)) == -1)
94793659Scjc		errx(1, "nonexistent time:\n%s", errline);
94893659Scjc	return tsecs;
94943071Swollman}
95059004Shm
95159004Shm/* physically move file */
95259004Shmstatic void
95359004Shmmovefile(char *from, char *to, int perm, int owner_uid, int group_gid)
95459004Shm{
95559004Shm	FILE *src, *dst;
95659004Shm	int c;
95759004Shm
95859004Shm	if ((src = fopen(from, "r")) == NULL)
95959004Shm		err(1, "can't fopen %s for reading", from);
96059004Shm	if ((dst = fopen(to, "w")) == NULL)
96159004Shm		err(1, "can't fopen %s for writing", to);
96259004Shm	if (fchown(fileno(dst), owner_uid, group_gid))
96359004Shm		err(1, "can't fchown %s", to);
96459004Shm	if (fchmod(fileno(dst), perm))
96559004Shm		err(1, "can't fchmod %s", to);
96659004Shm
96759004Shm	while ((c = getc(src)) != EOF) {
96859004Shm		if ((putc(c, dst)) == EOF)
96959004Shm			err(1, "error writing to %s", to);
97059004Shm	}
97159004Shm
97259004Shm	if (ferror(src))
97359004Shm		err(1, "error reading from %s", from);
97459004Shm	if ((fclose(src)) != 0)
97559004Shm		err(1, "can't fclose %s", to);
97659004Shm	if ((fclose(dst)) != 0)
97759004Shm		err(1, "can't fclose %s", from);
97859004Shm	if ((unlink(from)) != 0)
97959004Shm		err(1, "can't unlink %s", from);
98059004Shm}
98159004Shm
98259004Shm/* create one or more directory components of a path */
98359004Shmstatic void
98459004Shmcreatedir(char *dirpart)
98559004Shm{
98659004Shm	char *s, *d;
98771299Sjedgar	char mkdirpath[MAXPATHLEN];
98859004Shm	struct stat st;
98959004Shm
99059004Shm	s = dirpart;
99159004Shm	d = mkdirpath;
99259004Shm
99359004Shm	for (;;) {
99459004Shm		*d++ = *s++;
99559004Shm		if (*s == '/' || *s == '\0') {
99659004Shm			*d = '\0';
99759004Shm			if (lstat(mkdirpath, &st))
99859004Shm				mkdir(mkdirpath, 0755);
99959004Shm		}
100059004Shm		if (*s == '\0')
100159004Shm			break;
100259004Shm	}
100359004Shm}
100459004Shm
100559004Shm/*-
100659004Shm * Parse a cyclic time specification, the format is as follows:
100759004Shm *
100859004Shm *	[Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
100959004Shm *
101059004Shm * to rotate a logfile cyclic at
101159004Shm *
101259004Shm *	- every day (D) within a specific hour (hh)	(hh = 0...23)
101359004Shm *	- once a week (W) at a specific day (d)     OR	(d = 0..6, 0 = Sunday)
101459004Shm *	- once a month (M) at a specific day (d)	(d = 1..31,l|L)
101559004Shm *
101659004Shm * We don't accept a timezone specification; missing fields
101759004Shm * are defaulted to the current date but time zero.
101859004Shm */
101959004Shmstatic time_t
102093659ScjcparseDWM(char *s, char *errline)
102159004Shm{
102259004Shm	char *t;
102393659Scjc	time_t tsecs;
102459004Shm	struct tm tm, *tmp;
102580742Sobrien	long l;
102659004Shm	int nd;
102759004Shm	static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
102859004Shm	int WMseen = 0;
102959004Shm	int Dseen = 0;
103059004Shm
103159004Shm	tmp = localtime(&timenow);
103259004Shm	tm = *tmp;
103359004Shm
103459004Shm	/* set no. of days per month */
103559004Shm
103659004Shm	nd = mtab[tm.tm_mon];
103759004Shm
103859004Shm	if (tm.tm_mon == 1) {
103959004Shm		if (((tm.tm_year + 1900) % 4 == 0) &&
104059004Shm		    ((tm.tm_year + 1900) % 100 != 0) &&
104159004Shm		    ((tm.tm_year + 1900) % 400 == 0)) {
104259004Shm			nd++;	/* leap year, 29 days in february */
104359004Shm		}
104459004Shm	}
104559004Shm	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
104659004Shm
104759004Shm	for (;;) {
104859004Shm		switch (*s) {
104959004Shm		case 'D':
105059004Shm			if (Dseen)
105159004Shm				return -1;
105259004Shm			Dseen++;
105359004Shm			s++;
105480742Sobrien			l = strtol(s, &t, 10);
105580742Sobrien			if (l < 0 || l > 23)
105659004Shm				return -1;
105780742Sobrien			tm.tm_hour = l;
105859004Shm			break;
105959004Shm
106059004Shm		case 'W':
106159004Shm			if (WMseen)
106259004Shm				return -1;
106359004Shm			WMseen++;
106459004Shm			s++;
106580742Sobrien			l = strtol(s, &t, 10);
106680742Sobrien			if (l < 0 || l > 6)
106759004Shm				return -1;
106880742Sobrien			if (l != tm.tm_wday) {
106959004Shm				int save;
107059004Shm
107180742Sobrien				if (l < tm.tm_wday) {
107259004Shm					save = 6 - tm.tm_wday;
107380742Sobrien					save += (l + 1);
107459004Shm				} else {
107580742Sobrien					save = l - tm.tm_wday;
107659004Shm				}
107759004Shm
107859004Shm				tm.tm_mday += save;
107959004Shm
108059004Shm				if (tm.tm_mday > nd) {
108159004Shm					tm.tm_mon++;
108259004Shm					tm.tm_mday = tm.tm_mday - nd;
108359004Shm				}
108459004Shm			}
108559004Shm			break;
108659004Shm
108759004Shm		case 'M':
108859004Shm			if (WMseen)
108959004Shm				return -1;
109059004Shm			WMseen++;
109159004Shm			s++;
109259004Shm			if (tolower(*s) == 'l') {
109359004Shm				tm.tm_mday = nd;
109459004Shm				s++;
109559004Shm				t = s;
109659004Shm			} else {
109780742Sobrien				l = strtol(s, &t, 10);
109880742Sobrien				if (l < 1 || l > 31)
109959004Shm					return -1;
110059004Shm
110180742Sobrien				if (l > nd)
110259004Shm					return -1;
110380742Sobrien				tm.tm_mday = l;
110459004Shm			}
110559004Shm			break;
110659004Shm
110759004Shm		default:
110859004Shm			return (-1);
110959004Shm			break;
111059004Shm		}
111159004Shm
111259004Shm		if (*t == '\0' || isspace(*t))
111359004Shm			break;
111459004Shm		else
111559004Shm			s = t;
111659004Shm	}
111793659Scjc	if ((tsecs = mktime(&tm)) == -1)
111893659Scjc		errx(1, "nonexistent time:\n%s", errline);
111993659Scjc	return tsecs;
112059004Shm}
1121