newsyslog.c revision 363238
1/*-
2 * ------+---------+---------+-------- + --------+---------+---------+---------*
3 * This file includes significant modifications done by:
4 * Copyright (c) 2003, 2004  - Garance Alistair Drosehn <gad@FreeBSD.org>.
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * ------+---------+---------+-------- + --------+---------+---------+---------*
29 */
30
31/*
32 * This file contains changes from the Open Software Foundation.
33 */
34
35/*
36 * Copyright 1988, 1989 by the Massachusetts Institute of Technology
37 *
38 * Permission to use, copy, modify, and distribute this software and its
39 * documentation for any purpose and without fee is hereby granted, provided
40 * that the above copyright notice appear in all copies and that both that
41 * copyright notice and this permission notice appear in supporting
42 * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
43 * used in advertising or publicity pertaining to distribution of the
44 * software without specific, written prior permission. M.I.T. and the M.I.T.
45 * S.I.P.B. make no representations about the suitability of this software
46 * for any purpose.  It is provided "as is" without express or implied
47 * warranty.
48 *
49 */
50
51/*
52 * newsyslog - roll over selected logs at the appropriate time, keeping the a
53 * specified number of backup files around.
54 */
55
56#include <sys/cdefs.h>
57__FBSDID("$FreeBSD: stable/11/usr.sbin/newsyslog/newsyslog.c 363238 2020-07-15 22:42:50Z eugen $");
58
59#define	OSF
60
61#include <sys/param.h>
62#include <sys/queue.h>
63#include <sys/stat.h>
64#include <sys/wait.h>
65
66#include <assert.h>
67#include <ctype.h>
68#include <err.h>
69#include <errno.h>
70#include <dirent.h>
71#include <fcntl.h>
72#include <fnmatch.h>
73#include <glob.h>
74#include <grp.h>
75#include <paths.h>
76#include <pwd.h>
77#include <signal.h>
78#include <stdio.h>
79#include <libgen.h>
80#include <stdlib.h>
81#include <string.h>
82#include <syslog.h>
83#include <time.h>
84#include <unistd.h>
85
86#include "pathnames.h"
87#include "extern.h"
88
89/*
90 * Compression suffixes
91 */
92#ifndef	COMPRESS_SUFFIX_GZ
93#define	COMPRESS_SUFFIX_GZ	".gz"
94#endif
95
96#ifndef	COMPRESS_SUFFIX_BZ2
97#define	COMPRESS_SUFFIX_BZ2	".bz2"
98#endif
99
100#ifndef	COMPRESS_SUFFIX_XZ
101#define	COMPRESS_SUFFIX_XZ	".xz"
102#endif
103
104#define	COMPRESS_SUFFIX_MAXLEN	MAX(MAX(sizeof(COMPRESS_SUFFIX_GZ),sizeof(COMPRESS_SUFFIX_BZ2)),sizeof(COMPRESS_SUFFIX_XZ))
105
106/*
107 * Compression types
108 */
109#define	COMPRESS_TYPES  4	/* Number of supported compression types */
110
111#define	COMPRESS_NONE	0
112#define	COMPRESS_GZIP	1
113#define	COMPRESS_BZIP2	2
114#define	COMPRESS_XZ	3
115
116/*
117 * Bit-values for the 'flags' parsed from a config-file entry.
118 */
119#define	CE_BINARY	0x0008	/* Logfile is in binary, do not add status */
120				/*    messages to logfile(s) when rotating. */
121#define	CE_NOSIGNAL	0x0010	/* There is no process to signal when */
122				/*    trimming this file. */
123#define	CE_TRIMAT	0x0020	/* trim file at a specific time. */
124#define	CE_GLOB		0x0040	/* name of the log is file name pattern. */
125#define	CE_SIGNALGROUP	0x0080	/* Signal a process-group instead of a single */
126				/*    process when trimming this file. */
127#define	CE_CREATE	0x0100	/* Create the log file if it does not exist. */
128#define	CE_NODUMP	0x0200	/* Set 'nodump' on newly created log file. */
129#define	CE_PID2CMD	0x0400	/* Replace PID file with a shell command.*/
130#define	CE_PLAIN0	0x0800	/* Do not compress zero'th history file */
131#define	CE_RFC5424	0x1000	/* Use RFC5424 format rotation message */
132
133#define	MIN_PID         5	/* Don't touch pids lower than this */
134#define	MAX_PID		99999	/* was lower, see /usr/include/sys/proc.h */
135
136#define	kbytes(size)  (((size) + 1023) >> 10)
137
138#define	DEFAULT_MARKER	"<default>"
139#define	DEBUG_MARKER	"<debug>"
140#define	INCLUDE_MARKER	"<include>"
141#define	DEFAULT_TIMEFNAME_FMT	"%Y%m%dT%H%M%S"
142
143#define	MAX_OLDLOGS 65536	/* Default maximum number of old logfiles */
144
145struct compress_types {
146	const char *flag;	/* Flag in configuration file */
147	const char *suffix;	/* Compression suffix */
148	const char *path;	/* Path to compression program */
149};
150
151static const struct compress_types compress_type[COMPRESS_TYPES] = {
152	{ "", "", "" },					/* no compression */
153	{ "Z", COMPRESS_SUFFIX_GZ, _PATH_GZIP },	/* gzip compression */
154	{ "J", COMPRESS_SUFFIX_BZ2, _PATH_BZIP2 },	/* bzip2 compression */
155	{ "X", COMPRESS_SUFFIX_XZ, _PATH_XZ }		/* xz compression */
156};
157
158struct conf_entry {
159	STAILQ_ENTRY(conf_entry) cf_nextp;
160	char *log;		/* Name of the log */
161	char *pid_cmd_file;		/* PID or command file */
162	char *r_reason;		/* The reason this file is being rotated */
163	int firstcreate;	/* Creating log for the first time (-C). */
164	int rotate;		/* Non-zero if this file should be rotated */
165	int fsize;		/* size found for the log file */
166	uid_t uid;		/* Owner of log */
167	gid_t gid;		/* Group of log */
168	int numlogs;		/* Number of logs to keep */
169	int trsize;		/* Size cutoff to trigger trimming the log */
170	int hours;		/* Hours between log trimming */
171	struct ptime_data *trim_at;	/* Specific time to do trimming */
172	unsigned int permissions;	/* File permissions on the log */
173	int flags;		/* CE_BINARY */
174	int compress;		/* Compression */
175	int sig;		/* Signal to send */
176	int def_cfg;		/* Using the <default> rule for this file */
177};
178
179struct sigwork_entry {
180	SLIST_ENTRY(sigwork_entry) sw_nextp;
181	int	 sw_signum;		/* the signal to send */
182	int	 sw_pidok;		/* true if pid value is valid */
183	pid_t	 sw_pid;		/* the process id from the PID file */
184	const char *sw_pidtype;		/* "daemon" or "process group" */
185	int	 sw_runcmd;		/* run command or send PID to signal */
186	char	 sw_fname[1];		/* file the PID was read from or shell cmd */
187};
188
189struct zipwork_entry {
190	SLIST_ENTRY(zipwork_entry) zw_nextp;
191	const struct conf_entry *zw_conf;	/* for chown/perm/flag info */
192	const struct sigwork_entry *zw_swork;	/* to know success of signal */
193	int	 zw_fsize;		/* size of the file to compress */
194	char	 zw_fname[1];		/* the file to compress */
195};
196
197struct include_entry {
198	STAILQ_ENTRY(include_entry) inc_nextp;
199	const char *file;	/* Name of file to process */
200};
201
202struct oldlog_entry {
203	char *fname;		/* Filename of the log file */
204	time_t t;		/* Parsed timestamp of the logfile */
205};
206
207typedef enum {
208	FREE_ENT, KEEP_ENT
209}	fk_entry;
210
211STAILQ_HEAD(cflist, conf_entry);
212static SLIST_HEAD(swlisthead, sigwork_entry) swhead =
213    SLIST_HEAD_INITIALIZER(swhead);
214static SLIST_HEAD(zwlisthead, zipwork_entry) zwhead =
215    SLIST_HEAD_INITIALIZER(zwhead);
216STAILQ_HEAD(ilist, include_entry);
217
218int dbg_at_times;		/* -D Show details of 'trim_at' code */
219
220static int archtodir = 0;	/* Archive old logfiles to other directory */
221static int createlogs;		/* Create (non-GLOB) logfiles which do not */
222				/*    already exist.  1=='for entries with */
223				/*    C flag', 2=='for all entries'. */
224int verbose = 0;		/* Print out what's going on */
225static int needroot = 1;	/* Root privs are necessary */
226int noaction = 0;		/* Don't do anything, just show it */
227static int norotate = 0;	/* Don't rotate */
228static int nosignal;		/* Do not send any signals */
229static int enforcepid = 0;	/* If PID file does not exist or empty, do nothing */
230static int force = 0;		/* Force the trim no matter what */
231static int rotatereq = 0;	/* -R = Always rotate the file(s) as given */
232				/*    on the command (this also requires   */
233				/*    that a list of files *are* given on  */
234				/*    the run command). */
235static char *requestor;		/* The name given on a -R request */
236static char *timefnamefmt = NULL;/* Use time based filenames instead of .0 */
237static char *archdirname;	/* Directory path to old logfiles archive */
238static char *destdir = NULL;	/* Directory to treat at root for logs */
239static const char *conf;	/* Configuration file to use */
240
241struct ptime_data *dbg_timenow;	/* A "timenow" value set via -D option */
242static struct ptime_data *timenow; /* The time to use for checking at-fields */
243
244#define	DAYTIME_LEN	16
245static char daytime[DAYTIME_LEN];/* The current time in human readable form,
246				  * used for rotation-tracking messages. */
247
248/* Another buffer to hold the current time in RFC5424 format. Fractional
249 * seconds are allowed by the RFC, but are not included in the
250 * rotation-tracking messages written by newsyslog and so are not accounted for
251 * in the length below.
252 */
253#define	DAYTIME_RFC5424_LEN	sizeof("YYYY-MM-DDTHH:MM:SS+00:00")
254static char daytime_rfc5424[DAYTIME_RFC5424_LEN];
255
256static char hostname[MAXHOSTNAMELEN]; /* hostname */
257static size_t hostname_shortlen;
258
259static const char *path_syslogpid = _PATH_SYSLOGPID;
260
261static struct cflist *get_worklist(char **files);
262static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
263		    struct conf_entry **defconf, struct ilist *inclist);
264static void add_to_queue(const char *fname, struct ilist *inclist);
265static char *sob(char *p);
266static char *son(char *p);
267static int isnumberstr(const char *);
268static int isglobstr(const char *);
269static char *missing_field(char *p, char *errline);
270static void	 change_attrs(const char *, const struct conf_entry *);
271static const char *get_logfile_suffix(const char *logfile);
272static fk_entry	 do_entry(struct conf_entry *);
273static fk_entry	 do_rotate(const struct conf_entry *);
274static void	 do_sigwork(struct sigwork_entry *);
275static void	 do_zipwork(struct zipwork_entry *);
276static struct sigwork_entry *
277		 save_sigwork(const struct conf_entry *);
278static struct zipwork_entry *
279		 save_zipwork(const struct conf_entry *, const struct
280		    sigwork_entry *, int, const char *);
281static void	 set_swpid(struct sigwork_entry *, const struct conf_entry *);
282static int	 sizefile(const char *);
283static void expand_globs(struct cflist *work_p, struct cflist *glob_p);
284static void free_clist(struct cflist *list);
285static void free_entry(struct conf_entry *ent);
286static struct conf_entry *init_entry(const char *fname,
287		struct conf_entry *src_entry);
288static void parse_args(int argc, char **argv);
289static int parse_doption(const char *doption);
290static void usage(void);
291static int log_trim(const char *logname, const struct conf_entry *log_ent);
292static int age_old_log(const char *file);
293static void savelog(char *from, char *to);
294static void createdir(const struct conf_entry *ent, char *dirpart);
295static void createlog(const struct conf_entry *ent);
296static int parse_signal(const char *str);
297
298/*
299 * All the following take a parameter of 'int', but expect values in the
300 * range of unsigned char.  Define wrappers which take values of type 'char',
301 * whether signed or unsigned, and ensure they end up in the right range.
302 */
303#define	isdigitch(Anychar) isdigit((u_char)(Anychar))
304#define	isprintch(Anychar) isprint((u_char)(Anychar))
305#define	isspacech(Anychar) isspace((u_char)(Anychar))
306#define	tolowerch(Anychar) tolower((u_char)(Anychar))
307
308int
309main(int argc, char **argv)
310{
311	struct cflist *worklist;
312	struct conf_entry *p;
313	struct sigwork_entry *stmp;
314	struct zipwork_entry *ztmp;
315
316	SLIST_INIT(&swhead);
317	SLIST_INIT(&zwhead);
318
319	parse_args(argc, argv);
320	argc -= optind;
321	argv += optind;
322
323	if (needroot && getuid() && geteuid())
324		errx(1, "must have root privs");
325	worklist = get_worklist(argv);
326
327	/*
328	 * Rotate all the files which need to be rotated.  Note that
329	 * some users have *hundreds* of entries in newsyslog.conf!
330	 */
331	while (!STAILQ_EMPTY(worklist)) {
332		p = STAILQ_FIRST(worklist);
333		STAILQ_REMOVE_HEAD(worklist, cf_nextp);
334		if (do_entry(p) == FREE_ENT)
335			free_entry(p);
336	}
337
338	/*
339	 * Send signals to any processes which need a signal to tell
340	 * them to close and re-open the log file(s) we have rotated.
341	 * Note that zipwork_entries include pointers to these
342	 * sigwork_entry's, so we can not free the entries here.
343	 */
344	if (!SLIST_EMPTY(&swhead)) {
345		if (noaction || verbose)
346			printf("Signal all daemon process(es)...\n");
347		SLIST_FOREACH(stmp, &swhead, sw_nextp)
348			do_sigwork(stmp);
349		if (!(rotatereq && nosignal)) {
350			if (noaction)
351				printf("\tsleep 10\n");
352			else {
353				if (verbose)
354					printf("Pause 10 seconds to allow "
355					    "daemon(s) to close log file(s)\n");
356				sleep(10);
357			}
358		}
359	}
360	/*
361	 * Compress all files that we're expected to compress, now
362	 * that all processes should have closed the files which
363	 * have been rotated.
364	 */
365	if (!SLIST_EMPTY(&zwhead)) {
366		if (noaction || verbose)
367			printf("Compress all rotated log file(s)...\n");
368		while (!SLIST_EMPTY(&zwhead)) {
369			ztmp = SLIST_FIRST(&zwhead);
370			do_zipwork(ztmp);
371			SLIST_REMOVE_HEAD(&zwhead, zw_nextp);
372			free(ztmp);
373		}
374	}
375	/* Now free all the sigwork entries. */
376	while (!SLIST_EMPTY(&swhead)) {
377		stmp = SLIST_FIRST(&swhead);
378		SLIST_REMOVE_HEAD(&swhead, sw_nextp);
379		free(stmp);
380	}
381
382	while (wait(NULL) > 0 || errno == EINTR)
383		;
384	return (0);
385}
386
387static struct conf_entry *
388init_entry(const char *fname, struct conf_entry *src_entry)
389{
390	struct conf_entry *tempwork;
391
392	if (verbose > 4)
393		printf("\t--> [creating entry for %s]\n", fname);
394
395	tempwork = malloc(sizeof(struct conf_entry));
396	if (tempwork == NULL)
397		err(1, "malloc of conf_entry for %s", fname);
398
399	if (destdir == NULL || fname[0] != '/')
400		tempwork->log = strdup(fname);
401	else
402		asprintf(&tempwork->log, "%s%s", destdir, fname);
403	if (tempwork->log == NULL)
404		err(1, "strdup for %s", fname);
405
406	if (src_entry != NULL) {
407		tempwork->pid_cmd_file = NULL;
408		if (src_entry->pid_cmd_file)
409			tempwork->pid_cmd_file = strdup(src_entry->pid_cmd_file);
410		tempwork->r_reason = NULL;
411		tempwork->firstcreate = 0;
412		tempwork->rotate = 0;
413		tempwork->fsize = -1;
414		tempwork->uid = src_entry->uid;
415		tempwork->gid = src_entry->gid;
416		tempwork->numlogs = src_entry->numlogs;
417		tempwork->trsize = src_entry->trsize;
418		tempwork->hours = src_entry->hours;
419		tempwork->trim_at = NULL;
420		if (src_entry->trim_at != NULL)
421			tempwork->trim_at = ptime_init(src_entry->trim_at);
422		tempwork->permissions = src_entry->permissions;
423		tempwork->flags = src_entry->flags;
424		tempwork->compress = src_entry->compress;
425		tempwork->sig = src_entry->sig;
426		tempwork->def_cfg = src_entry->def_cfg;
427	} else {
428		/* Initialize as a "do-nothing" entry */
429		tempwork->pid_cmd_file = NULL;
430		tempwork->r_reason = NULL;
431		tempwork->firstcreate = 0;
432		tempwork->rotate = 0;
433		tempwork->fsize = -1;
434		tempwork->uid = (uid_t)-1;
435		tempwork->gid = (gid_t)-1;
436		tempwork->numlogs = 1;
437		tempwork->trsize = -1;
438		tempwork->hours = -1;
439		tempwork->trim_at = NULL;
440		tempwork->permissions = 0;
441		tempwork->flags = 0;
442		tempwork->compress = COMPRESS_NONE;
443		tempwork->sig = SIGHUP;
444		tempwork->def_cfg = 0;
445	}
446
447	return (tempwork);
448}
449
450static void
451free_entry(struct conf_entry *ent)
452{
453
454	if (ent == NULL)
455		return;
456
457	if (ent->log != NULL) {
458		if (verbose > 4)
459			printf("\t--> [freeing entry for %s]\n", ent->log);
460		free(ent->log);
461		ent->log = NULL;
462	}
463
464	if (ent->pid_cmd_file != NULL) {
465		free(ent->pid_cmd_file);
466		ent->pid_cmd_file = NULL;
467	}
468
469	if (ent->r_reason != NULL) {
470		free(ent->r_reason);
471		ent->r_reason = NULL;
472	}
473
474	if (ent->trim_at != NULL) {
475		ptime_free(ent->trim_at);
476		ent->trim_at = NULL;
477	}
478
479	free(ent);
480}
481
482static void
483free_clist(struct cflist *list)
484{
485	struct conf_entry *ent;
486
487	while (!STAILQ_EMPTY(list)) {
488		ent = STAILQ_FIRST(list);
489		STAILQ_REMOVE_HEAD(list, cf_nextp);
490		free_entry(ent);
491	}
492
493	free(list);
494	list = NULL;
495}
496
497static fk_entry
498do_entry(struct conf_entry * ent)
499{
500#define	REASON_MAX	80
501	int modtime;
502	fk_entry free_or_keep;
503	double diffsecs;
504	char temp_reason[REASON_MAX];
505	int oversized;
506
507	free_or_keep = FREE_ENT;
508	if (verbose)
509		printf("%s <%d%s>: ", ent->log, ent->numlogs,
510		    compress_type[ent->compress].flag);
511	ent->fsize = sizefile(ent->log);
512	oversized = ((ent->trsize > 0) && (ent->fsize >= ent->trsize));
513	modtime = age_old_log(ent->log);
514	ent->rotate = 0;
515	ent->firstcreate = 0;
516	if (ent->fsize < 0) {
517		/*
518		 * If either the C flag or the -C option was specified,
519		 * and if we won't be creating the file, then have the
520		 * verbose message include a hint as to why the file
521		 * will not be created.
522		 */
523		temp_reason[0] = '\0';
524		if (createlogs > 1)
525			ent->firstcreate = 1;
526		else if ((ent->flags & CE_CREATE) && createlogs)
527			ent->firstcreate = 1;
528		else if (ent->flags & CE_CREATE)
529			strlcpy(temp_reason, " (no -C option)", REASON_MAX);
530		else if (createlogs)
531			strlcpy(temp_reason, " (no C flag)", REASON_MAX);
532
533		if (ent->firstcreate) {
534			if (verbose)
535				printf("does not exist -> will create.\n");
536			createlog(ent);
537		} else if (verbose) {
538			printf("does not exist, skipped%s.\n", temp_reason);
539		}
540	} else {
541		if (ent->flags & CE_TRIMAT && !force && !rotatereq &&
542		    !oversized) {
543			diffsecs = ptimeget_diff(timenow, ent->trim_at);
544			if (diffsecs < 0.0) {
545				/* trim_at is some time in the future. */
546				if (verbose) {
547					ptime_adjust4dst(ent->trim_at,
548					    timenow);
549					printf("--> will trim at %s",
550					    ptimeget_ctime(ent->trim_at));
551				}
552				return (free_or_keep);
553			} else if (diffsecs >= 3600.0) {
554				/*
555				 * trim_at is more than an hour in the past,
556				 * so find the next valid trim_at time, and
557				 * tell the user what that will be.
558				 */
559				if (verbose && dbg_at_times)
560					printf("\n\t--> prev trim at %s\t",
561					    ptimeget_ctime(ent->trim_at));
562				if (verbose) {
563					ptimeset_nxtime(ent->trim_at);
564					printf("--> will trim at %s",
565					    ptimeget_ctime(ent->trim_at));
566				}
567				return (free_or_keep);
568			} else if (verbose && noaction && dbg_at_times) {
569				/*
570				 * If we are just debugging at-times, then
571				 * a detailed message is helpful.  Also
572				 * skip "doing" any commands, since they
573				 * would all be turned off by no-action.
574				 */
575				printf("\n\t--> timematch at %s",
576				    ptimeget_ctime(ent->trim_at));
577				return (free_or_keep);
578			} else if (verbose && ent->hours <= 0) {
579				printf("--> time is up\n");
580			}
581		}
582		if (verbose && (ent->trsize > 0))
583			printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize);
584		if (verbose && (ent->hours > 0))
585			printf(" age (hr): %d [%d] ", modtime, ent->hours);
586
587		/*
588		 * Figure out if this logfile needs to be rotated.
589		 */
590		temp_reason[0] = '\0';
591		if (rotatereq) {
592			ent->rotate = 1;
593			snprintf(temp_reason, REASON_MAX, " due to -R from %s",
594			    requestor);
595		} else if (force) {
596			ent->rotate = 1;
597			snprintf(temp_reason, REASON_MAX, " due to -F request");
598		} else if (oversized) {
599			ent->rotate = 1;
600			snprintf(temp_reason, REASON_MAX, " due to size>%dK",
601			    ent->trsize);
602		} else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) {
603			ent->rotate = 1;
604		} else if ((ent->hours > 0) && ((modtime >= ent->hours) ||
605		    (modtime < 0))) {
606			ent->rotate = 1;
607		}
608
609		/*
610		 * If the file needs to be rotated, then rotate it.
611		 */
612		if (ent->rotate && !norotate) {
613			if (temp_reason[0] != '\0')
614				ent->r_reason = strdup(temp_reason);
615			if (verbose)
616				printf("--> trimming log....\n");
617			if (noaction && !verbose)
618				printf("%s <%d%s>: trimming\n", ent->log,
619				    ent->numlogs,
620				    compress_type[ent->compress].flag);
621			free_or_keep = do_rotate(ent);
622		} else {
623			if (verbose)
624				printf("--> skipping\n");
625		}
626	}
627	return (free_or_keep);
628#undef REASON_MAX
629}
630
631static void
632parse_args(int argc, char **argv)
633{
634	int ch;
635	char *p;
636
637	timenow = ptime_init(NULL);
638	ptimeset_time(timenow, time(NULL));
639	strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN);
640	ptimeget_ctime_rfc5424(timenow, daytime_rfc5424, DAYTIME_RFC5424_LEN);
641
642	/* Let's get our hostname */
643	(void)gethostname(hostname, sizeof(hostname));
644	hostname_shortlen = strcspn(hostname, ".");
645
646	/* Parse command line options. */
647	while ((ch = getopt(argc, argv, "a:d:f:nrst:vCD:FNPR:S:")) != -1)
648		switch (ch) {
649		case 'a':
650			archtodir++;
651			archdirname = optarg;
652			break;
653		case 'd':
654			destdir = optarg;
655			break;
656		case 'f':
657			conf = optarg;
658			break;
659		case 'n':
660			noaction++;
661			/* FALLTHROUGH */
662		case 'r':
663			needroot = 0;
664			break;
665		case 's':
666			nosignal = 1;
667			break;
668		case 't':
669			if (optarg[0] == '\0' ||
670			    strcmp(optarg, "DEFAULT") == 0)
671				timefnamefmt = strdup(DEFAULT_TIMEFNAME_FMT);
672			else
673				timefnamefmt = strdup(optarg);
674			break;
675		case 'v':
676			verbose++;
677			break;
678		case 'C':
679			/* Useful for things like rc.diskless... */
680			createlogs++;
681			break;
682		case 'D':
683			/*
684			 * Set some debugging option.  The specific option
685			 * depends on the value of optarg.  These options
686			 * may come and go without notice or documentation.
687			 */
688			if (parse_doption(optarg))
689				break;
690			usage();
691			/* NOTREACHED */
692		case 'F':
693			force++;
694			break;
695		case 'N':
696			norotate++;
697			break;
698		case 'P':
699			enforcepid++;
700			break;
701		case 'R':
702			rotatereq++;
703			requestor = strdup(optarg);
704			break;
705		case 'S':
706			path_syslogpid = optarg;
707			break;
708		case 'm':	/* Used by OpenBSD for "monitor mode" */
709		default:
710			usage();
711			/* NOTREACHED */
712		}
713
714	if (force && norotate) {
715		warnx("Only one of -F and -N may be specified.");
716		usage();
717		/* NOTREACHED */
718	}
719
720	if (rotatereq) {
721		if (optind == argc) {
722			warnx("At least one filename must be given when -R is specified.");
723			usage();
724			/* NOTREACHED */
725		}
726		/* Make sure "requestor" value is safe for a syslog message. */
727		for (p = requestor; *p != '\0'; p++) {
728			if (!isprintch(*p) && (*p != '\t'))
729				*p = '.';
730		}
731	}
732
733	if (dbg_timenow) {
734		/*
735		 * Note that the 'daytime' variable is not changed.
736		 * That is only used in messages that track when a
737		 * logfile is rotated, and if a file *is* rotated,
738		 * then it will still rotated at the "real now" time.
739		 */
740		ptime_free(timenow);
741		timenow = dbg_timenow;
742		fprintf(stderr, "Debug: Running as if TimeNow is %s",
743		    ptimeget_ctime(dbg_timenow));
744	}
745
746}
747
748/*
749 * These debugging options are mainly meant for developer use, such
750 * as writing regression-tests.  They would not be needed by users
751 * during normal operation of newsyslog...
752 */
753static int
754parse_doption(const char *doption)
755{
756	const char TN[] = "TN=";
757	int res;
758
759	if (strncmp(doption, TN, sizeof(TN) - 1) == 0) {
760		/*
761		 * The "TimeNow" debugging option.  This might be off
762		 * by an hour when crossing a timezone change.
763		 */
764		dbg_timenow = ptime_init(NULL);
765		res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601,
766		    time(NULL), doption + sizeof(TN) - 1);
767		if (res == -2) {
768			warnx("Non-existent time specified on -D %s", doption);
769			return (0);			/* failure */
770		} else if (res < 0) {
771			warnx("Malformed time given on -D %s", doption);
772			return (0);			/* failure */
773		}
774		return (1);			/* successfully parsed */
775
776	}
777
778	if (strcmp(doption, "ats") == 0) {
779		dbg_at_times++;
780		return (1);			/* successfully parsed */
781	}
782
783	/* XXX - This check could probably be dropped. */
784	if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder")
785	    == 0)) {
786		warnx("NOTE: newsyslog always uses 'neworder'.");
787		return (1);			/* successfully parsed */
788	}
789
790	warnx("Unknown -D (debug) option: '%s'", doption);
791	return (0);				/* failure */
792}
793
794static void
795usage(void)
796{
797
798	fprintf(stderr,
799	    "usage: newsyslog [-CFNPnrsv] [-a directory] [-d directory] [-f config_file]\n"
800	    "                 [-S pidfile] [-t timefmt] [[-R tagname] file ...]\n");
801	exit(1);
802}
803
804/*
805 * Parse a configuration file and return a linked list of all the logs
806 * which should be processed.
807 */
808static struct cflist *
809get_worklist(char **files)
810{
811	FILE *f;
812	char **given;
813	struct cflist *cmdlist, *filelist, *globlist;
814	struct conf_entry *defconf, *dupent, *ent;
815	struct ilist inclist;
816	struct include_entry *inc;
817	int gmatch, fnres;
818
819	defconf = NULL;
820	STAILQ_INIT(&inclist);
821
822	filelist = malloc(sizeof(struct cflist));
823	if (filelist == NULL)
824		err(1, "malloc of filelist");
825	STAILQ_INIT(filelist);
826	globlist = malloc(sizeof(struct cflist));
827	if (globlist == NULL)
828		err(1, "malloc of globlist");
829	STAILQ_INIT(globlist);
830
831	inc = malloc(sizeof(struct include_entry));
832	if (inc == NULL)
833		err(1, "malloc of inc");
834	inc->file = conf;
835	if (inc->file == NULL)
836		inc->file = _PATH_CONF;
837	STAILQ_INSERT_TAIL(&inclist, inc, inc_nextp);
838
839	STAILQ_FOREACH(inc, &inclist, inc_nextp) {
840		if (strcmp(inc->file, "-") != 0)
841			f = fopen(inc->file, "r");
842		else {
843			f = stdin;
844			inc->file = "<stdin>";
845		}
846		if (!f)
847			err(1, "%s", inc->file);
848
849		if (verbose)
850			printf("Processing %s\n", inc->file);
851		parse_file(f, filelist, globlist, &defconf, &inclist);
852		(void) fclose(f);
853	}
854
855	/*
856	 * All config-file information has been read in and turned into
857	 * a filelist and a globlist.  If there were no specific files
858	 * given on the run command, then the only thing left to do is to
859	 * call a routine which finds all files matched by the globlist
860	 * and adds them to the filelist.  Then return the worklist.
861	 */
862	if (*files == NULL) {
863		expand_globs(filelist, globlist);
864		free_clist(globlist);
865		if (defconf != NULL)
866			free_entry(defconf);
867		return (filelist);
868	}
869
870	/*
871	 * If newsyslog was given a specific list of files to process,
872	 * it may be that some of those files were not listed in any
873	 * config file.  Those unlisted files should get the default
874	 * rotation action.  First, create the default-rotation action
875	 * if none was found in a system config file.
876	 */
877	if (defconf == NULL) {
878		defconf = init_entry(DEFAULT_MARKER, NULL);
879		defconf->numlogs = 3;
880		defconf->trsize = 50;
881		defconf->permissions = S_IRUSR|S_IWUSR;
882	}
883
884	/*
885	 * If newsyslog was run with a list of specific filenames,
886	 * then create a new worklist which has only those files in
887	 * it, picking up the rotation-rules for those files from
888	 * the original filelist.
889	 *
890	 * XXX - Note that this will copy multiple rules for a single
891	 *	logfile, if multiple entries are an exact match for
892	 *	that file.  That matches the historic behavior, but do
893	 *	we want to continue to allow it?  If so, it should
894	 *	probably be handled more intelligently.
895	 */
896	cmdlist = malloc(sizeof(struct cflist));
897	if (cmdlist == NULL)
898		err(1, "malloc of cmdlist");
899	STAILQ_INIT(cmdlist);
900
901	for (given = files; *given; ++given) {
902		/*
903		 * First try to find exact-matches for this given file.
904		 */
905		gmatch = 0;
906		STAILQ_FOREACH(ent, filelist, cf_nextp) {
907			if (strcmp(ent->log, *given) == 0) {
908				gmatch++;
909				dupent = init_entry(*given, ent);
910				STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
911			}
912		}
913		if (gmatch) {
914			if (verbose > 2)
915				printf("\t+ Matched entry %s\n", *given);
916			continue;
917		}
918
919		/*
920		 * There was no exact-match for this given file, so look
921		 * for a "glob" entry which does match.
922		 */
923		gmatch = 0;
924		if (verbose > 2)
925			printf("\t+ Checking globs for %s\n", *given);
926		STAILQ_FOREACH(ent, globlist, cf_nextp) {
927			fnres = fnmatch(ent->log, *given, FNM_PATHNAME);
928			if (verbose > 2)
929				printf("\t+    = %d for pattern %s\n", fnres,
930				    ent->log);
931			if (fnres == 0) {
932				gmatch++;
933				dupent = init_entry(*given, ent);
934				/* This new entry is not a glob! */
935				dupent->flags &= ~CE_GLOB;
936				STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
937				/* Only allow a match to one glob-entry */
938				break;
939			}
940		}
941		if (gmatch) {
942			if (verbose > 2)
943				printf("\t+ Matched %s via %s\n", *given,
944				    ent->log);
945			continue;
946		}
947
948		/*
949		 * This given file was not found in any config file, so
950		 * add a worklist item based on the default entry.
951		 */
952		if (verbose > 2)
953			printf("\t+ No entry matched %s  (will use %s)\n",
954			    *given, DEFAULT_MARKER);
955		dupent = init_entry(*given, defconf);
956		/* Mark that it was *not* found in a config file */
957		dupent->def_cfg = 1;
958		STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp);
959	}
960
961	/*
962	 * Free all the entries in the original work list, the list of
963	 * glob entries, and the default entry.
964	 */
965	free_clist(filelist);
966	free_clist(globlist);
967	free_entry(defconf);
968
969	/* And finally, return a worklist which matches the given files. */
970	return (cmdlist);
971}
972
973/*
974 * Expand the list of entries with filename patterns, and add all files
975 * which match those glob-entries onto the worklist.
976 */
977static void
978expand_globs(struct cflist *work_p, struct cflist *glob_p)
979{
980	int gmatch, gres;
981	size_t i;
982	char *mfname;
983	struct conf_entry *dupent, *ent, *globent;
984	glob_t pglob;
985	struct stat st_fm;
986
987	/*
988	 * The worklist contains all fully-specified (non-GLOB) names.
989	 *
990	 * Now expand the list of filename-pattern (GLOB) entries into
991	 * a second list, which (by definition) will only match files
992	 * that already exist.  Do not add a glob-related entry for any
993	 * file which already exists in the fully-specified list.
994	 */
995	STAILQ_FOREACH(globent, glob_p, cf_nextp) {
996		gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob);
997		if (gres != 0) {
998			warn("cannot expand pattern (%d): %s", gres,
999			    globent->log);
1000			continue;
1001		}
1002
1003		if (verbose > 2)
1004			printf("\t+ Expanding pattern %s\n", globent->log);
1005		for (i = 0; i < pglob.gl_matchc; i++) {
1006			mfname = pglob.gl_pathv[i];
1007
1008			/* See if this file already has a specific entry. */
1009			gmatch = 0;
1010			STAILQ_FOREACH(ent, work_p, cf_nextp) {
1011				if (strcmp(mfname, ent->log) == 0) {
1012					gmatch++;
1013					break;
1014				}
1015			}
1016			if (gmatch)
1017				continue;
1018
1019			/* Make sure the named matched is a file. */
1020			gres = lstat(mfname, &st_fm);
1021			if (gres != 0) {
1022				/* Error on a file that glob() matched?!? */
1023				warn("Skipping %s - lstat() error", mfname);
1024				continue;
1025			}
1026			if (!S_ISREG(st_fm.st_mode)) {
1027				/* We only rotate files! */
1028				if (verbose > 2)
1029					printf("\t+  . skipping %s (!file)\n",
1030					    mfname);
1031				continue;
1032			}
1033
1034			if (verbose > 2)
1035				printf("\t+  . add file %s\n", mfname);
1036			dupent = init_entry(mfname, globent);
1037			/* This new entry is not a glob! */
1038			dupent->flags &= ~CE_GLOB;
1039
1040			/* Add to the worklist. */
1041			STAILQ_INSERT_TAIL(work_p, dupent, cf_nextp);
1042		}
1043		globfree(&pglob);
1044		if (verbose > 2)
1045			printf("\t+ Done with pattern %s\n", globent->log);
1046	}
1047}
1048
1049/*
1050 * Parse a configuration file and update a linked list of all the logs to
1051 * process.
1052 */
1053static void
1054parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
1055    struct conf_entry **defconf_p, struct ilist *inclist)
1056{
1057	char line[BUFSIZ], *parse, *q;
1058	char *cp, *errline, *group;
1059	struct conf_entry *working;
1060	struct passwd *pwd;
1061	struct group *grp;
1062	glob_t pglob;
1063	int eol, ptm_opts, res, special;
1064	size_t i;
1065
1066	errline = NULL;
1067	while (fgets(line, BUFSIZ, cf)) {
1068		if ((line[0] == '\n') || (line[0] == '#') ||
1069		    (strlen(line) == 0))
1070			continue;
1071		if (errline != NULL)
1072			free(errline);
1073		errline = strdup(line);
1074		for (cp = line + 1; *cp != '\0'; cp++) {
1075			if (*cp != '#')
1076				continue;
1077			if (*(cp - 1) == '\\') {
1078				strcpy(cp - 1, cp);
1079				cp--;
1080				continue;
1081			}
1082			*cp = '\0';
1083			break;
1084		}
1085
1086		q = parse = missing_field(sob(line), errline);
1087		parse = son(line);
1088		if (!*parse) {
1089			warnx("malformed line (missing fields):\n%s",
1090			    errline);
1091			continue;
1092		}
1093		*parse = '\0';
1094
1095		/*
1096		 * Allow people to set debug options via the config file.
1097		 * (NOTE: debug options are undocumented, and may disappear
1098		 * at any time, etc).
1099		 */
1100		if (strcasecmp(DEBUG_MARKER, q) == 0) {
1101			q = parse = missing_field(sob(parse + 1), errline);
1102			parse = son(parse);
1103			if (!*parse)
1104				warnx("debug line specifies no option:\n%s",
1105				    errline);
1106			else {
1107				*parse = '\0';
1108				parse_doption(q);
1109			}
1110			continue;
1111		} else if (strcasecmp(INCLUDE_MARKER, q) == 0) {
1112			if (verbose)
1113				printf("Found: %s", errline);
1114			q = parse = missing_field(sob(parse + 1), errline);
1115			parse = son(parse);
1116			if (!*parse) {
1117				warnx("include line missing argument:\n%s",
1118				    errline);
1119				continue;
1120			}
1121
1122			*parse = '\0';
1123
1124			if (isglobstr(q)) {
1125				res = glob(q, GLOB_NOCHECK, NULL, &pglob);
1126				if (res != 0) {
1127					warn("cannot expand pattern (%d): %s",
1128					    res, q);
1129					continue;
1130				}
1131
1132				if (verbose > 2)
1133					printf("\t+ Expanding pattern %s\n", q);
1134
1135				for (i = 0; i < pglob.gl_matchc; i++)
1136					add_to_queue(pglob.gl_pathv[i],
1137					    inclist);
1138				globfree(&pglob);
1139			} else
1140				add_to_queue(q, inclist);
1141			continue;
1142		}
1143
1144#define badline(msg, ...) do {		\
1145	warnx(msg, __VA_ARGS__);	\
1146	goto cleanup;			\
1147} while (0)
1148
1149		special = 0;
1150		working = init_entry(q, NULL);
1151		if (strcasecmp(DEFAULT_MARKER, q) == 0) {
1152			special = 1;
1153			if (*defconf_p != NULL)
1154				badline("Ignoring duplicate entry for %s!", q);
1155			*defconf_p = working;
1156		}
1157
1158		q = parse = missing_field(sob(parse + 1), errline);
1159		parse = son(parse);
1160		if (!*parse)
1161			badline("malformed line (missing fields):\n%s",
1162			    errline);
1163		*parse = '\0';
1164		if ((group = strchr(q, ':')) != NULL ||
1165		    (group = strrchr(q, '.')) != NULL) {
1166			*group++ = '\0';
1167			if (*q) {
1168				if (!(isnumberstr(q))) {
1169					if ((pwd = getpwnam(q)) == NULL)
1170						badline(
1171				     "error in config file; unknown user:\n%s",
1172						    errline);
1173					working->uid = pwd->pw_uid;
1174				} else
1175					working->uid = atoi(q);
1176			} else
1177				working->uid = (uid_t)-1;
1178
1179			q = group;
1180			if (*q) {
1181				if (!(isnumberstr(q))) {
1182					if ((grp = getgrnam(q)) == NULL)
1183						badline(
1184				    "error in config file; unknown group:\n%s",
1185						    errline);
1186					working->gid = grp->gr_gid;
1187				} else
1188					working->gid = atoi(q);
1189			} else
1190				working->gid = (gid_t)-1;
1191
1192			q = parse = missing_field(sob(parse + 1), errline);
1193			parse = son(parse);
1194			if (!*parse)
1195				badline("malformed line (missing fields):\n%s",
1196				    errline);
1197			*parse = '\0';
1198		} else {
1199			working->uid = (uid_t)-1;
1200			working->gid = (gid_t)-1;
1201		}
1202
1203		if (!sscanf(q, "%o", &working->permissions))
1204			badline("error in config file; bad permissions:\n%s",
1205			    errline);
1206
1207		q = parse = missing_field(sob(parse + 1), errline);
1208		parse = son(parse);
1209		if (!*parse)
1210			badline("malformed line (missing fields):\n%s",
1211			    errline);
1212		*parse = '\0';
1213		if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0)
1214			badline("error in config file; bad value for count of logs to save:\n%s",
1215			    errline);
1216
1217		q = parse = missing_field(sob(parse + 1), errline);
1218		parse = son(parse);
1219		if (!*parse)
1220			badline("malformed line (missing fields):\n%s",
1221			    errline);
1222		*parse = '\0';
1223		if (isdigitch(*q))
1224			working->trsize = atoi(q);
1225		else if (strcmp(q, "*") == 0)
1226			working->trsize = -1;
1227		else {
1228			warnx("Invalid value of '%s' for 'size' in line:\n%s",
1229			    q, errline);
1230			working->trsize = -1;
1231		}
1232
1233		working->flags = 0;
1234		working->compress = COMPRESS_NONE;
1235		q = parse = missing_field(sob(parse + 1), errline);
1236		parse = son(parse);
1237		eol = !*parse;
1238		*parse = '\0';
1239		{
1240			char *ep;
1241			u_long ul;
1242
1243			ul = strtoul(q, &ep, 10);
1244			if (ep == q)
1245				working->hours = 0;
1246			else if (*ep == '*')
1247				working->hours = -1;
1248			else if (ul > INT_MAX)
1249				badline("interval is too large:\n%s", errline);
1250			else
1251				working->hours = ul;
1252
1253			if (*ep == '\0' || strcmp(ep, "*") == 0)
1254				goto no_trimat;
1255			if (*ep != '@' && *ep != '$')
1256				badline("malformed interval/at:\n%s", errline);
1257
1258			working->flags |= CE_TRIMAT;
1259			working->trim_at = ptime_init(NULL);
1260			ptm_opts = PTM_PARSE_ISO8601;
1261			if (*ep == '$')
1262				ptm_opts = PTM_PARSE_DWM;
1263			ptm_opts |= PTM_PARSE_MATCHDOM;
1264			res = ptime_relparse(working->trim_at, ptm_opts,
1265			    ptimeget_secs(timenow), ep + 1);
1266			if (res == -2)
1267				badline("nonexistent time for 'at' value:\n%s",
1268				    errline);
1269			else if (res < 0)
1270				badline("malformed 'at' value:\n%s", errline);
1271		}
1272no_trimat:
1273
1274		if (eol)
1275			q = NULL;
1276		else {
1277			q = parse = sob(parse + 1);	/* Optional field */
1278			parse = son(parse);
1279			if (!*parse)
1280				eol = 1;
1281			*parse = '\0';
1282		}
1283
1284		for (; q && *q && !isspacech(*q); q++) {
1285			switch (tolowerch(*q)) {
1286			case 'b':
1287				working->flags |= CE_BINARY;
1288				break;
1289			case 'c':
1290				working->flags |= CE_CREATE;
1291				break;
1292			case 'd':
1293				working->flags |= CE_NODUMP;
1294				break;
1295			case 'g':
1296				working->flags |= CE_GLOB;
1297				break;
1298			case 'j':
1299				working->compress = COMPRESS_BZIP2;
1300				break;
1301			case 'n':
1302				working->flags |= CE_NOSIGNAL;
1303				break;
1304			case 'p':
1305				working->flags |= CE_PLAIN0;
1306				break;
1307			case 'r':
1308				working->flags |= CE_PID2CMD;
1309				break;
1310			case 't':
1311				working->flags |= CE_RFC5424;
1312				break;
1313			case 'u':
1314				working->flags |= CE_SIGNALGROUP;
1315				break;
1316			case 'w':
1317				/* Deprecated flag - keep for compatibility purposes */
1318				break;
1319			case 'x':
1320				working->compress = COMPRESS_XZ;
1321				break;
1322			case 'z':
1323				working->compress = COMPRESS_GZIP;
1324				break;
1325			case '-':
1326				break;
1327			case 'f':	/* Used by OpenBSD for "CE_FOLLOW" */
1328			case 'm':	/* Used by OpenBSD for "CE_MONITOR" */
1329			default:
1330				badline("illegal flag in config file -- %c",
1331				    *q);
1332			}
1333		}
1334
1335		if (eol)
1336			q = NULL;
1337		else {
1338			q = parse = sob(parse + 1);	/* Optional field */
1339			parse = son(parse);
1340			if (!*parse)
1341				eol = 1;
1342			*parse = '\0';
1343		}
1344
1345		working->pid_cmd_file = NULL;
1346		if (q && *q) {
1347			if (*q == '/')
1348				working->pid_cmd_file = strdup(q);
1349			else if (isalnum(*q))
1350				goto got_sig;
1351			else {
1352				badline(
1353			"illegal pid file or signal in config file:\n%s",
1354				    errline);
1355			}
1356		}
1357		if (eol)
1358			q = NULL;
1359		else {
1360			q = parse = sob(parse + 1);	/* Optional field */
1361			parse = son(parse);
1362			*parse = '\0';
1363		}
1364
1365		working->sig = SIGHUP;
1366		if (q && *q) {
1367got_sig:
1368			working->sig = parse_signal(q);
1369			if (working->sig < 1 || working->sig >= sys_nsig) {
1370				badline(
1371				    "illegal signal in config file:\n%s",
1372				    errline);
1373			}
1374		}
1375
1376		/*
1377		 * Finish figuring out what pid-file to use (if any) in
1378		 * later processing if this logfile needs to be rotated.
1379		 */
1380		if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) {
1381			/*
1382			 * This config-entry specified 'n' for nosignal,
1383			 * see if it also specified an explicit pid_cmd_file.
1384			 * This would be a pretty pointless combination.
1385			 */
1386			if (working->pid_cmd_file != NULL) {
1387				warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s",
1388				    working->pid_cmd_file, errline);
1389				free(working->pid_cmd_file);
1390				working->pid_cmd_file = NULL;
1391			}
1392		} else if (working->pid_cmd_file == NULL) {
1393			/*
1394			 * This entry did not specify the 'n' flag, which
1395			 * means it should signal syslogd unless it had
1396			 * specified some other pid-file (and obviously the
1397			 * syslog pid-file will not be for a process-group).
1398			 * Also, we should only try to notify syslog if we
1399			 * are root.
1400			 */
1401			if (working->flags & CE_SIGNALGROUP) {
1402				warnx("Ignoring flag 'U' in line:\n%s",
1403				    errline);
1404				working->flags &= ~CE_SIGNALGROUP;
1405			}
1406			if (needroot)
1407				working->pid_cmd_file = strdup(path_syslogpid);
1408		}
1409
1410		/*
1411		 * Add this entry to the appropriate list of entries, unless
1412		 * it was some kind of special entry (eg: <default>).
1413		 */
1414		if (special) {
1415			;			/* Do not add to any list */
1416		} else if (working->flags & CE_GLOB) {
1417			STAILQ_INSERT_TAIL(glob_p, working, cf_nextp);
1418		} else {
1419			STAILQ_INSERT_TAIL(work_p, working, cf_nextp);
1420		}
1421		continue;
1422cleanup:
1423		free_entry(working);
1424#undef badline
1425	} /* while (fgets(line, BUFSIZ, cf)) */
1426	if (errline != NULL)
1427		free(errline);
1428}
1429
1430static char *
1431missing_field(char *p, char *errline)
1432{
1433
1434	if (!p || !*p)
1435		errx(1, "missing field in config file:\n%s", errline);
1436	return (p);
1437}
1438
1439/*
1440 * In our sort we return it in the reverse of what qsort normally
1441 * would do, as we want the newest files first.  If we have two
1442 * entries with the same time we don't really care about order.
1443 *
1444 * Support function for qsort() in delete_oldest_timelog().
1445 */
1446static int
1447oldlog_entry_compare(const void *a, const void *b)
1448{
1449	const struct oldlog_entry *ola = a, *olb = b;
1450
1451	if (ola->t > olb->t)
1452		return (-1);
1453	else if (ola->t < olb->t)
1454		return (1);
1455	else
1456		return (0);
1457}
1458
1459/*
1460 * Check whether the file corresponding to dp is an archive of the logfile
1461 * logfname, based on the timefnamefmt format string. Return true and fill out
1462 * tm if this is the case; otherwise return false.
1463 */
1464static int
1465validate_old_timelog(int fd, const struct dirent *dp, const char *logfname,
1466    struct tm *tm)
1467{
1468	struct stat sb;
1469	size_t logfname_len;
1470	char *s;
1471	int c;
1472
1473	logfname_len = strlen(logfname);
1474
1475	if (dp->d_type != DT_REG) {
1476		/*
1477		 * Some filesystems (e.g. NFS) don't fill out the d_type field
1478		 * and leave it set to DT_UNKNOWN; in this case we must obtain
1479		 * the file type ourselves.
1480		 */
1481		if (dp->d_type != DT_UNKNOWN ||
1482		    fstatat(fd, dp->d_name, &sb, AT_SYMLINK_NOFOLLOW) != 0 ||
1483		    !S_ISREG(sb.st_mode))
1484			return (0);
1485	}
1486	/* Ignore everything but files with our logfile prefix. */
1487	if (strncmp(dp->d_name, logfname, logfname_len) != 0)
1488		return (0);
1489	/* Ignore the actual non-rotated logfile. */
1490	if (dp->d_namlen == logfname_len)
1491		return (0);
1492
1493	/*
1494	 * Make sure we created have found a logfile, so the
1495	 * postfix is valid, IE format is: '.<time>(.[bgx]z)?'.
1496	 */
1497	if (dp->d_name[logfname_len] != '.') {
1498		if (verbose)
1499			printf("Ignoring %s which has unexpected "
1500			    "extension '%s'\n", dp->d_name,
1501			    &dp->d_name[logfname_len]);
1502		return (0);
1503	}
1504	memset(tm, 0, sizeof(*tm));
1505	if ((s = strptime(&dp->d_name[logfname_len + 1],
1506	    timefnamefmt, tm)) == NULL) {
1507		/*
1508		 * We could special case "old" sequentially named logfiles here,
1509		 * but we do not as that would require special handling to
1510		 * decide which one was the oldest compared to "new" time based
1511		 * logfiles.
1512		 */
1513		if (verbose)
1514			printf("Ignoring %s which does not "
1515			    "match time format\n", dp->d_name);
1516		return (0);
1517	}
1518
1519	for (c = 0; c < COMPRESS_TYPES; c++)
1520		if (strcmp(s, compress_type[c].suffix) == 0)
1521			/* We're done. */
1522			return (1);
1523
1524	if (verbose)
1525		printf("Ignoring %s which has unexpected extension '%s'\n",
1526		    dp->d_name, s);
1527
1528	return (0);
1529}
1530
1531/*
1532 * Delete the oldest logfiles, when using time based filenames.
1533 */
1534static void
1535delete_oldest_timelog(const struct conf_entry *ent, const char *archive_dir)
1536{
1537	char *logfname, *s, *dir, errbuf[80];
1538	int dir_fd, i, logcnt, max_logcnt;
1539	struct oldlog_entry *oldlogs;
1540	struct dirent *dp;
1541	const char *cdir;
1542	struct tm tm;
1543	DIR *dirp;
1544
1545	oldlogs = malloc(MAX_OLDLOGS * sizeof(struct oldlog_entry));
1546	max_logcnt = MAX_OLDLOGS;
1547	logcnt = 0;
1548
1549	if (archive_dir != NULL && archive_dir[0] != '\0')
1550		cdir = archive_dir;
1551	else
1552		if ((cdir = dirname(ent->log)) == NULL)
1553			err(1, "dirname()");
1554	if ((dir = strdup(cdir)) == NULL)
1555		err(1, "strdup()");
1556
1557	if ((s = basename(ent->log)) == NULL)
1558		err(1, "basename()");
1559	if ((logfname = strdup(s)) == NULL)
1560		err(1, "strdup()");
1561	if (strcmp(logfname, "/") == 0)
1562		errx(1, "Invalid log filename - became '/'");
1563
1564	if (verbose > 2)
1565		printf("Searching for old logs in %s\n", dir);
1566
1567	/* First we create a 'list' of all archived logfiles */
1568	if ((dirp = opendir(dir)) == NULL)
1569		err(1, "Cannot open log directory '%s'", dir);
1570	dir_fd = dirfd(dirp);
1571	while ((dp = readdir(dirp)) != NULL) {
1572		if (validate_old_timelog(dir_fd, dp, logfname, &tm) == 0)
1573			continue;
1574
1575		/*
1576		 * We should now have old an old rotated logfile, so
1577		 * add it to the 'list'.
1578		 */
1579		if ((oldlogs[logcnt].t = timegm(&tm)) == -1)
1580			err(1, "Could not convert time string to time value");
1581		if ((oldlogs[logcnt].fname = strdup(dp->d_name)) == NULL)
1582			err(1, "strdup()");
1583		logcnt++;
1584
1585		/*
1586		 * It is very unlikely we ever run out of space in the
1587		 * logfile array from the default size, but lets
1588		 * handle it anyway...
1589		 */
1590		if (logcnt >= max_logcnt) {
1591			max_logcnt *= 4;
1592			/* Detect integer overflow */
1593			if (max_logcnt < logcnt)
1594				errx(1, "Too many old logfiles found");
1595			oldlogs = realloc(oldlogs,
1596			    max_logcnt * sizeof(struct oldlog_entry));
1597			if (oldlogs == NULL)
1598				err(1, "realloc()");
1599		}
1600	}
1601
1602	/* Second, if needed we delete oldest archived logfiles */
1603	if (logcnt > 0 && logcnt >= ent->numlogs && ent->numlogs > 1) {
1604		oldlogs = realloc(oldlogs, logcnt *
1605		    sizeof(struct oldlog_entry));
1606		if (oldlogs == NULL)
1607			err(1, "realloc()");
1608
1609		/*
1610		 * We now sort the logs in the order of newest to
1611		 * oldest.  That way we can simply skip over the
1612		 * number of records we want to keep.
1613		 */
1614		qsort(oldlogs, logcnt, sizeof(struct oldlog_entry),
1615		    oldlog_entry_compare);
1616		for (i = ent->numlogs - 1; i < logcnt; i++) {
1617			if (noaction)
1618				printf("\trm -f %s/%s\n", dir,
1619				    oldlogs[i].fname);
1620			else if (unlinkat(dir_fd, oldlogs[i].fname, 0) != 0) {
1621				snprintf(errbuf, sizeof(errbuf),
1622				    "Could not delete old logfile '%s'",
1623				    oldlogs[i].fname);
1624				perror(errbuf);
1625			}
1626		}
1627	} else if (verbose > 1)
1628		printf("No old logs to delete for logfile %s\n", ent->log);
1629
1630	/* Third, cleanup */
1631	closedir(dirp);
1632	for (i = 0; i < logcnt; i++) {
1633		assert(oldlogs[i].fname != NULL);
1634		free(oldlogs[i].fname);
1635	}
1636	free(oldlogs);
1637	free(logfname);
1638	free(dir);
1639}
1640
1641/*
1642 * Generate a log filename, when using classic filenames.
1643 */
1644static void
1645gen_classiclog_fname(char *fname, size_t fname_sz, const char *archive_dir,
1646    const char *namepart, int numlogs_c)
1647{
1648
1649	if (archive_dir[0] != '\0')
1650		(void) snprintf(fname, fname_sz, "%s/%s.%d", archive_dir,
1651		    namepart, numlogs_c);
1652	else
1653		(void) snprintf(fname, fname_sz, "%s.%d", namepart, numlogs_c);
1654}
1655
1656/*
1657 * Delete a rotated logfile, when using classic filenames.
1658 */
1659static void
1660delete_classiclog(const char *archive_dir, const char *namepart, int numlog_c)
1661{
1662	char file1[MAXPATHLEN], zfile1[MAXPATHLEN];
1663	int c;
1664
1665	gen_classiclog_fname(file1, sizeof(file1), archive_dir, namepart,
1666	    numlog_c);
1667
1668	for (c = 0; c < COMPRESS_TYPES; c++) {
1669		(void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
1670		    compress_type[c].suffix);
1671		if (noaction)
1672			printf("\trm -f %s\n", zfile1);
1673		else
1674			(void) unlink(zfile1);
1675	}
1676}
1677
1678/*
1679 * Only add to the queue if the file hasn't already been added. This is
1680 * done to prevent circular include loops.
1681 */
1682static void
1683add_to_queue(const char *fname, struct ilist *inclist)
1684{
1685	struct include_entry *inc;
1686
1687	STAILQ_FOREACH(inc, inclist, inc_nextp) {
1688		if (strcmp(fname, inc->file) == 0) {
1689			warnx("duplicate include detected: %s", fname);
1690			return;
1691		}
1692	}
1693
1694	inc = malloc(sizeof(struct include_entry));
1695	if (inc == NULL)
1696		err(1, "malloc of inc");
1697	inc->file = strdup(fname);
1698
1699	if (verbose > 2)
1700		printf("\t+ Adding %s to the processing queue.\n", fname);
1701
1702	STAILQ_INSERT_TAIL(inclist, inc, inc_nextp);
1703}
1704
1705/*
1706 * Search for logfile and return its compression suffix (if supported)
1707 * The suffix detection is first-match in the order of compress_types
1708 *
1709 * Note: if logfile without suffix exists (uncompressed, COMPRESS_NONE)
1710 * a zero-length string is returned
1711 */
1712static const char *
1713get_logfile_suffix(const char *logfile)
1714{
1715	struct stat st;
1716	char zfile[MAXPATHLEN];
1717	int c;
1718
1719	for (c = 0; c < COMPRESS_TYPES; c++) {
1720		(void) strlcpy(zfile, logfile, MAXPATHLEN);
1721		(void) strlcat(zfile, compress_type[c].suffix, MAXPATHLEN);
1722		if (lstat(zfile, &st) == 0)
1723			return (compress_type[c].suffix);
1724	}
1725	return (NULL);
1726}
1727
1728static fk_entry
1729do_rotate(const struct conf_entry *ent)
1730{
1731	char dirpart[MAXPATHLEN], namepart[MAXPATHLEN];
1732	char file1[MAXPATHLEN], file2[MAXPATHLEN];
1733	char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
1734	const char *logfile_suffix;
1735	char datetimestr[30];
1736	int flags, numlogs_c;
1737	fk_entry free_or_keep;
1738	struct sigwork_entry *swork;
1739	struct stat st;
1740	struct tm tm;
1741	time_t now;
1742
1743	flags = ent->flags;
1744	free_or_keep = FREE_ENT;
1745
1746	if (archtodir) {
1747		char *p;
1748
1749		/* build complete name of archive directory into dirpart */
1750		if (*archdirname == '/') {	/* absolute */
1751			strlcpy(dirpart, archdirname, sizeof(dirpart));
1752		} else {	/* relative */
1753			/* get directory part of logfile */
1754			strlcpy(dirpart, ent->log, sizeof(dirpart));
1755			if ((p = strrchr(dirpart, '/')) == NULL)
1756				dirpart[0] = '\0';
1757			else
1758				*(p + 1) = '\0';
1759			strlcat(dirpart, archdirname, sizeof(dirpart));
1760		}
1761
1762		/* check if archive directory exists, if not, create it */
1763		if (lstat(dirpart, &st))
1764			createdir(ent, dirpart);
1765
1766		/* get filename part of logfile */
1767		if ((p = strrchr(ent->log, '/')) == NULL)
1768			strlcpy(namepart, ent->log, sizeof(namepart));
1769		else
1770			strlcpy(namepart, p + 1, sizeof(namepart));
1771	} else {
1772		/*
1773		 * Tell utility functions we are not using an archive
1774		 * dir.
1775		 */
1776		dirpart[0] = '\0';
1777		strlcpy(namepart, ent->log, sizeof(namepart));
1778	}
1779
1780	/* Delete old logs */
1781	if (timefnamefmt != NULL)
1782		delete_oldest_timelog(ent, dirpart);
1783	else {
1784		/*
1785		 * Handle cleaning up after legacy newsyslog where we
1786		 * kept ent->numlogs + 1 files.  This code can go away
1787		 * at some point in the future.
1788		 */
1789		delete_classiclog(dirpart, namepart, ent->numlogs);
1790
1791		if (ent->numlogs > 0)
1792			delete_classiclog(dirpart, namepart, ent->numlogs - 1);
1793
1794	}
1795
1796	if (timefnamefmt != NULL) {
1797		/* If time functions fails we can't really do any sensible */
1798		if (time(&now) == (time_t)-1 ||
1799		    localtime_r(&now, &tm) == NULL)
1800			bzero(&tm, sizeof(tm));
1801
1802		strftime(datetimestr, sizeof(datetimestr), timefnamefmt, &tm);
1803		if (archtodir)
1804			(void) snprintf(file1, sizeof(file1), "%s/%s.%s",
1805			    dirpart, namepart, datetimestr);
1806		else
1807			(void) snprintf(file1, sizeof(file1), "%s.%s",
1808			    ent->log, datetimestr);
1809
1810		/* Don't run the code to move down logs */
1811		numlogs_c = -1;
1812	} else {
1813		gen_classiclog_fname(file1, sizeof(file1), dirpart, namepart,
1814		    ent->numlogs - 1);
1815		numlogs_c = ent->numlogs - 2;		/* copy for countdown */
1816	}
1817
1818	/* Move down log files */
1819	for (; numlogs_c >= 0; numlogs_c--) {
1820		(void) strlcpy(file2, file1, sizeof(file2));
1821
1822		gen_classiclog_fname(file1, sizeof(file1), dirpart, namepart,
1823		    numlogs_c);
1824
1825		logfile_suffix = get_logfile_suffix(file1);
1826		if (logfile_suffix == NULL)
1827			continue;
1828		(void) strlcpy(zfile1, file1, MAXPATHLEN);
1829		(void) strlcpy(zfile2, file2, MAXPATHLEN);
1830		(void) strlcat(zfile1, logfile_suffix, MAXPATHLEN);
1831		(void) strlcat(zfile2, logfile_suffix, MAXPATHLEN);
1832
1833		if (noaction)
1834			printf("\tmv %s %s\n", zfile1, zfile2);
1835		else {
1836			/* XXX - Ought to be checking for failure! */
1837			(void)rename(zfile1, zfile2);
1838			change_attrs(zfile2, ent);
1839			if (ent->compress && !strlen(logfile_suffix)) {
1840				/* compress old rotation */
1841				struct zipwork_entry zwork;
1842
1843				memset(&zwork, 0, sizeof(zwork));
1844				zwork.zw_conf = ent;
1845				zwork.zw_fsize = sizefile(zfile2);
1846				strcpy(zwork.zw_fname, zfile2);
1847				do_zipwork(&zwork);
1848			}
1849		}
1850	}
1851
1852	if (ent->numlogs > 0) {
1853		if (noaction) {
1854			/*
1855			 * Note that savelog() may succeed with using link()
1856			 * for the archtodir case, but there is no good way
1857			 * of knowing if it will when doing "noaction", so
1858			 * here we claim that it will have to do a copy...
1859			 */
1860			if (archtodir)
1861				printf("\tcp %s %s\n", ent->log, file1);
1862			else
1863				printf("\tln %s %s\n", ent->log, file1);
1864			printf("\ttouch %s\t\t"
1865			    "# Update mtime for 'when'-interval processing\n",
1866			    file1);
1867		} else {
1868			if (!(flags & CE_BINARY)) {
1869				/* Report the trimming to the old log */
1870				log_trim(ent->log, ent);
1871			}
1872			savelog(ent->log, file1);
1873			/*
1874			 * Interval-based rotations are done using the mtime of
1875			 * the most recently archived log, so make sure it gets
1876			 * updated during a rotation.
1877			 */
1878			utimes(file1, NULL);
1879		}
1880		change_attrs(file1, ent);
1881	}
1882
1883	/* Create the new log file and move it into place */
1884	if (noaction)
1885		printf("Start new log...\n");
1886	createlog(ent);
1887
1888	/*
1889	 * Save all signalling and file-compression to be done after log
1890	 * files from all entries have been rotated.  This way any one
1891	 * process will not be sent the same signal multiple times when
1892	 * multiple log files had to be rotated.
1893	 */
1894	swork = NULL;
1895	if (ent->pid_cmd_file != NULL)
1896		swork = save_sigwork(ent);
1897	if (ent->numlogs > 0 && ent->compress > COMPRESS_NONE) {
1898		if (!(ent->flags & CE_PLAIN0) ||
1899		    strcmp(&file1[strlen(file1) - 2], ".0") != 0) {
1900			/*
1901			 * The zipwork_entry will include a pointer to this
1902			 * conf_entry, so the conf_entry should not be freed.
1903			 */
1904			free_or_keep = KEEP_ENT;
1905			save_zipwork(ent, swork, ent->fsize, file1);
1906		}
1907	}
1908
1909	return (free_or_keep);
1910}
1911
1912static void
1913do_sigwork(struct sigwork_entry *swork)
1914{
1915	struct sigwork_entry *nextsig;
1916	int kres, secs;
1917	char *tmp;
1918
1919	if (swork->sw_runcmd == 0 && (!(swork->sw_pidok) || swork->sw_pid == 0))
1920		return;			/* no work to do... */
1921
1922	/*
1923	 * If nosignal (-s) was specified, then do not signal any process.
1924	 * Note that a nosignal request triggers a warning message if the
1925	 * rotated logfile needs to be compressed, *unless* -R was also
1926	 * specified.  We assume that an `-sR' request came from a process
1927	 * which writes to the logfile, and as such, we assume that process
1928	 * has already made sure the logfile is not presently in use.  This
1929	 * just sets swork->sw_pidok to a special value, and do_zipwork
1930	 * will print any necessary warning(s).
1931	 */
1932	if (nosignal) {
1933		if (!rotatereq)
1934			swork->sw_pidok = -1;
1935		return;
1936	}
1937
1938	/*
1939	 * Compute the pause between consecutive signals.  Use a longer
1940	 * sleep time if we will be sending two signals to the same
1941	 * daemon or process-group.
1942	 */
1943	secs = 0;
1944	nextsig = SLIST_NEXT(swork, sw_nextp);
1945	if (nextsig != NULL) {
1946		if (swork->sw_pid == nextsig->sw_pid)
1947			secs = 10;
1948		else
1949			secs = 1;
1950	}
1951
1952	if (noaction) {
1953		if (swork->sw_runcmd)
1954			printf("\tsh -c '%s %d'\n", swork->sw_fname,
1955			    swork->sw_signum);
1956		else {
1957			printf("\tkill -%d %d \t\t# %s\n", swork->sw_signum,
1958			    (int)swork->sw_pid, swork->sw_fname);
1959			if (secs > 0)
1960				printf("\tsleep %d\n", secs);
1961		}
1962		return;
1963	}
1964
1965	if (swork->sw_runcmd) {
1966		asprintf(&tmp, "%s %d", swork->sw_fname, swork->sw_signum);
1967		if (tmp == NULL) {
1968			warn("can't allocate memory to run %s",
1969			    swork->sw_fname);
1970			return;
1971		}
1972		if (verbose)
1973			printf("Run command: %s\n", tmp);
1974		kres = system(tmp);
1975		if (kres) {
1976			warnx("%s: returned non-zero exit code: %d",
1977			    tmp, kres);
1978		}
1979		free(tmp);
1980		return;
1981	}
1982
1983	kres = kill(swork->sw_pid, swork->sw_signum);
1984	if (kres != 0) {
1985		/*
1986		 * Assume that "no such process" (ESRCH) is something
1987		 * to warn about, but is not an error.  Presumably the
1988		 * process which writes to the rotated log file(s) is
1989		 * gone, in which case we should have no problem with
1990		 * compressing the rotated log file(s).
1991		 */
1992		if (errno != ESRCH)
1993			swork->sw_pidok = 0;
1994		warn("can't notify %s, pid %d = %s", swork->sw_pidtype,
1995		    (int)swork->sw_pid, swork->sw_fname);
1996	} else {
1997		if (verbose)
1998			printf("Notified %s pid %d = %s\n", swork->sw_pidtype,
1999			    (int)swork->sw_pid, swork->sw_fname);
2000		if (secs > 0) {
2001			if (verbose)
2002				printf("Pause %d second(s) between signals\n",
2003				    secs);
2004			sleep(secs);
2005		}
2006	}
2007}
2008
2009static void
2010do_zipwork(struct zipwork_entry *zwork)
2011{
2012	const char *pgm_name, *pgm_path;
2013	int errsav, fcount, zstatus;
2014	pid_t pidzip, wpid;
2015	char zresult[MAXPATHLEN];
2016	int c;
2017
2018	assert(zwork != NULL);
2019	pgm_path = NULL;
2020	strlcpy(zresult, zwork->zw_fname, sizeof(zresult));
2021	if (zwork->zw_conf != NULL &&
2022	    zwork->zw_conf->compress > COMPRESS_NONE)
2023		for (c = 1; c < COMPRESS_TYPES; c++) {
2024			if (zwork->zw_conf->compress == c) {
2025				pgm_path = compress_type[c].path;
2026				(void) strlcat(zresult,
2027				    compress_type[c].suffix, sizeof(zresult));
2028				break;
2029			}
2030		}
2031	if (pgm_path == NULL) {
2032		warnx("invalid entry for %s in do_zipwork", zwork->zw_fname);
2033		return;
2034	}
2035	pgm_name = strrchr(pgm_path, '/');
2036	if (pgm_name == NULL)
2037		pgm_name = pgm_path;
2038	else
2039		pgm_name++;
2040
2041	if (zwork->zw_swork != NULL && zwork->zw_swork->sw_runcmd == 0 &&
2042	    zwork->zw_swork->sw_pidok <= 0) {
2043		warnx(
2044		    "log %s not compressed because daemon(s) not notified",
2045		    zwork->zw_fname);
2046		change_attrs(zwork->zw_fname, zwork->zw_conf);
2047		return;
2048	}
2049
2050	if (noaction) {
2051		printf("\t%s %s\n", pgm_name, zwork->zw_fname);
2052		change_attrs(zresult, zwork->zw_conf);
2053		return;
2054	}
2055
2056	fcount = 1;
2057	pidzip = fork();
2058	while (pidzip < 0) {
2059		/*
2060		 * The fork failed.  If the failure was due to a temporary
2061		 * problem, then wait a short time and try it again.
2062		 */
2063		errsav = errno;
2064		warn("fork() for `%s %s'", pgm_name, zwork->zw_fname);
2065		if (errsav != EAGAIN || fcount > 5)
2066			errx(1, "Exiting...");
2067		sleep(fcount * 12);
2068		fcount++;
2069		pidzip = fork();
2070	}
2071	if (!pidzip) {
2072		/* The child process executes the compression command */
2073		execl(pgm_path, pgm_path, "-f", zwork->zw_fname, (char *)0);
2074		err(1, "execl(`%s -f %s')", pgm_path, zwork->zw_fname);
2075	}
2076
2077	wpid = waitpid(pidzip, &zstatus, 0);
2078	if (wpid == -1) {
2079		/* XXX - should this be a fatal error? */
2080		warn("%s: waitpid(%d)", pgm_path, pidzip);
2081		return;
2082	}
2083	if (!WIFEXITED(zstatus)) {
2084		warnx("`%s -f %s' did not terminate normally", pgm_name,
2085		    zwork->zw_fname);
2086		return;
2087	}
2088	if (WEXITSTATUS(zstatus)) {
2089		warnx("`%s -f %s' terminated with a non-zero status (%d)",
2090		    pgm_name, zwork->zw_fname, WEXITSTATUS(zstatus));
2091		return;
2092	}
2093
2094	/* Compression was successful, set file attributes on the result. */
2095	change_attrs(zresult, zwork->zw_conf);
2096}
2097
2098/*
2099 * Save information on any process we need to signal.  Any single
2100 * process may need to be sent different signal-values for different
2101 * log files, but usually a single signal-value will cause the process
2102 * to close and re-open all of it's log files.
2103 */
2104static struct sigwork_entry *
2105save_sigwork(const struct conf_entry *ent)
2106{
2107	struct sigwork_entry *sprev, *stmp;
2108	int ndiff;
2109	size_t tmpsiz;
2110
2111	sprev = NULL;
2112	ndiff = 1;
2113	SLIST_FOREACH(stmp, &swhead, sw_nextp) {
2114		ndiff = strcmp(ent->pid_cmd_file, stmp->sw_fname);
2115		if (ndiff > 0)
2116			break;
2117		if (ndiff == 0) {
2118			if (ent->sig == stmp->sw_signum)
2119				break;
2120			if (ent->sig > stmp->sw_signum) {
2121				ndiff = 1;
2122				break;
2123			}
2124		}
2125		sprev = stmp;
2126	}
2127	if (stmp != NULL && ndiff == 0)
2128		return (stmp);
2129
2130	tmpsiz = sizeof(struct sigwork_entry) + strlen(ent->pid_cmd_file) + 1;
2131	stmp = malloc(tmpsiz);
2132
2133	stmp->sw_runcmd = 0;
2134	/* If this is a command to run we just set the flag and run command */
2135	if (ent->flags & CE_PID2CMD) {
2136		stmp->sw_pid = -1;
2137		stmp->sw_pidok = 0;
2138		stmp->sw_runcmd = 1;
2139	} else {
2140		set_swpid(stmp, ent);
2141	}
2142	stmp->sw_signum = ent->sig;
2143	strcpy(stmp->sw_fname, ent->pid_cmd_file);
2144	if (sprev == NULL)
2145		SLIST_INSERT_HEAD(&swhead, stmp, sw_nextp);
2146	else
2147		SLIST_INSERT_AFTER(sprev, stmp, sw_nextp);
2148	return (stmp);
2149}
2150
2151/*
2152 * Save information on any file we need to compress.  We may see the same
2153 * file multiple times, so check the full list to avoid duplicates.  The
2154 * list itself is sorted smallest-to-largest, because that's the order we
2155 * want to compress the files.  If the partition is very low on disk space,
2156 * then the smallest files are the most likely to compress, and compressing
2157 * them first will free up more space for the larger files.
2158 */
2159static struct zipwork_entry *
2160save_zipwork(const struct conf_entry *ent, const struct sigwork_entry *swork,
2161    int zsize, const char *zipfname)
2162{
2163	struct zipwork_entry *zprev, *ztmp;
2164	int ndiff;
2165	size_t tmpsiz;
2166
2167	/* Compute the size if the caller did not know it. */
2168	if (zsize < 0)
2169		zsize = sizefile(zipfname);
2170
2171	zprev = NULL;
2172	ndiff = 1;
2173	SLIST_FOREACH(ztmp, &zwhead, zw_nextp) {
2174		ndiff = strcmp(zipfname, ztmp->zw_fname);
2175		if (ndiff == 0)
2176			break;
2177		if (zsize > ztmp->zw_fsize)
2178			zprev = ztmp;
2179	}
2180	if (ztmp != NULL && ndiff == 0)
2181		return (ztmp);
2182
2183	tmpsiz = sizeof(struct zipwork_entry) + strlen(zipfname) + 1;
2184	ztmp = malloc(tmpsiz);
2185	ztmp->zw_conf = ent;
2186	ztmp->zw_swork = swork;
2187	ztmp->zw_fsize = zsize;
2188	strcpy(ztmp->zw_fname, zipfname);
2189	if (zprev == NULL)
2190		SLIST_INSERT_HEAD(&zwhead, ztmp, zw_nextp);
2191	else
2192		SLIST_INSERT_AFTER(zprev, ztmp, zw_nextp);
2193	return (ztmp);
2194}
2195
2196/* Send a signal to the pid specified by pidfile */
2197static void
2198set_swpid(struct sigwork_entry *swork, const struct conf_entry *ent)
2199{
2200	FILE *f;
2201	long minok, maxok, rval;
2202	char *endp, *linep, line[BUFSIZ];
2203
2204	minok = MIN_PID;
2205	maxok = MAX_PID;
2206	swork->sw_pidok = 0;
2207	swork->sw_pid = 0;
2208	swork->sw_pidtype = "daemon";
2209	if (ent->flags & CE_SIGNALGROUP) {
2210		/*
2211		 * If we are expected to signal a process-group when
2212		 * rotating this logfile, then the value read in should
2213		 * be the negative of a valid process ID.
2214		 */
2215		minok = -MAX_PID;
2216		maxok = -MIN_PID;
2217		swork->sw_pidtype = "process-group";
2218	}
2219
2220	f = fopen(ent->pid_cmd_file, "r");
2221	if (f == NULL) {
2222		if (errno == ENOENT && enforcepid == 0) {
2223			/*
2224			 * Warn if the PID file doesn't exist, but do
2225			 * not consider it an error.  Most likely it
2226			 * means the process has been terminated,
2227			 * so it should be safe to rotate any log
2228			 * files that the process would have been using.
2229			 */
2230			swork->sw_pidok = 1;
2231			warnx("pid file doesn't exist: %s", ent->pid_cmd_file);
2232		} else
2233			warn("can't open pid file: %s", ent->pid_cmd_file);
2234		return;
2235	}
2236
2237	if (fgets(line, BUFSIZ, f) == NULL) {
2238		/*
2239		 * Warn if the PID file is empty, but do not consider
2240		 * it an error.  Most likely it means the process has
2241		 * has terminated, so it should be safe to rotate any
2242		 * log files that the process would have been using.
2243		 */
2244		if (feof(f) && enforcepid == 0) {
2245			swork->sw_pidok = 1;
2246			warnx("pid/cmd file is empty: %s", ent->pid_cmd_file);
2247		} else
2248			warn("can't read from pid file: %s", ent->pid_cmd_file);
2249		(void)fclose(f);
2250		return;
2251	}
2252	(void)fclose(f);
2253
2254	errno = 0;
2255	linep = line;
2256	while (*linep == ' ')
2257		linep++;
2258	rval = strtol(linep, &endp, 10);
2259	if (*endp != '\0' && !isspacech(*endp)) {
2260		warnx("pid file does not start with a valid number: %s",
2261		    ent->pid_cmd_file);
2262	} else if (rval < minok || rval > maxok) {
2263		warnx("bad value '%ld' for process number in %s",
2264		    rval, ent->pid_cmd_file);
2265		if (verbose)
2266			warnx("\t(expecting value between %ld and %ld)",
2267			    minok, maxok);
2268	} else {
2269		swork->sw_pidok = 1;
2270		swork->sw_pid = rval;
2271	}
2272
2273	return;
2274}
2275
2276/* Log the fact that the logs were turned over */
2277static int
2278log_trim(const char *logname, const struct conf_entry *log_ent)
2279{
2280	FILE *f;
2281	const char *xtra;
2282
2283	if ((f = fopen(logname, "a")) == NULL)
2284		return (-1);
2285	xtra = "";
2286	if (log_ent->def_cfg)
2287		xtra = " using <default> rule";
2288	if (log_ent->flags & CE_RFC5424) {
2289		if (log_ent->firstcreate) {
2290			fprintf(f, "<%d>1 %s %s newsyslog %d - - %s%s\n",
2291			    LOG_MAKEPRI(LOG_USER, LOG_INFO),
2292			    daytime_rfc5424, hostname, getpid(),
2293			    "logfile first created", xtra);
2294		} else if (log_ent->r_reason != NULL) {
2295			fprintf(f, "<%d>1 %s %s newsyslog %d - - %s%s%s\n",
2296			    LOG_MAKEPRI(LOG_USER, LOG_INFO),
2297			    daytime_rfc5424, hostname, getpid(),
2298			    "logfile turned over", log_ent->r_reason, xtra);
2299		} else {
2300			fprintf(f, "<%d>1 %s %s newsyslog %d - - %s%s\n",
2301			    LOG_MAKEPRI(LOG_USER, LOG_INFO),
2302			    daytime_rfc5424, hostname, getpid(),
2303			    "logfile turned over", xtra);
2304		}
2305	} else {
2306		if (log_ent->firstcreate)
2307			fprintf(f,
2308			    "%s %.*s newsyslog[%d]: logfile first created%s\n",
2309			    daytime, (int)hostname_shortlen, hostname, getpid(),
2310			    xtra);
2311		else if (log_ent->r_reason != NULL)
2312			fprintf(f,
2313			    "%s %.*s newsyslog[%d]: logfile turned over%s%s\n",
2314			    daytime, (int)hostname_shortlen, hostname, getpid(),
2315			    log_ent->r_reason, xtra);
2316		else
2317			fprintf(f,
2318			    "%s %.*s newsyslog[%d]: logfile turned over%s\n",
2319			    daytime, (int)hostname_shortlen, hostname, getpid(),
2320			    xtra);
2321	}
2322	if (fclose(f) == EOF)
2323		err(1, "log_trim: fclose");
2324	return (0);
2325}
2326
2327/* Return size in kilobytes of a file */
2328static int
2329sizefile(const char *file)
2330{
2331	struct stat sb;
2332
2333	if (stat(file, &sb) < 0)
2334		return (-1);
2335	return (kbytes(sb.st_size));
2336}
2337
2338/*
2339 * Return the mtime of the most recent archive of the logfile, using timestamp
2340 * based filenames.
2341 */
2342static time_t
2343mtime_old_timelog(const char *file)
2344{
2345	struct stat sb;
2346	struct tm tm;
2347	int dir_fd;
2348	time_t t;
2349	struct dirent *dp;
2350	DIR *dirp;
2351	char *s, *logfname, *dir;
2352
2353	t = -1;
2354
2355	if ((dir = dirname(file)) == NULL) {
2356		warn("dirname() of '%s'", file);
2357		return (t);
2358	}
2359	if ((s = basename(file)) == NULL) {
2360		warn("basename() of '%s'", file);
2361		return (t);
2362	} else if (s[0] == '/') {
2363		warnx("Invalid log filename '%s'", s);
2364		return (t);
2365	} else if ((logfname = strdup(s)) == NULL)
2366		err(1, "strdup()");
2367
2368	if ((dirp = opendir(dir)) == NULL) {
2369		warn("Cannot open log directory '%s'", dir);
2370		return (t);
2371	}
2372	dir_fd = dirfd(dirp);
2373	/* Open the archive dir and find the most recent archive of logfname. */
2374	while ((dp = readdir(dirp)) != NULL) {
2375		if (validate_old_timelog(dir_fd, dp, logfname, &tm) == 0)
2376			continue;
2377
2378		if (fstatat(dir_fd, dp->d_name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
2379			warn("Cannot stat '%s'", file);
2380			continue;
2381		}
2382		if (t < sb.st_mtime)
2383			t = sb.st_mtime;
2384	}
2385	closedir(dirp);
2386
2387	return (t);
2388}
2389
2390/* Return the age in hours of the most recent archive of the logfile. */
2391static int
2392age_old_log(const char *file)
2393{
2394	struct stat sb;
2395	const char *logfile_suffix;
2396	char tmp[MAXPATHLEN + sizeof(".0") + COMPRESS_SUFFIX_MAXLEN + 1];
2397	time_t mtime;
2398
2399	if (archtodir) {
2400		char *p;
2401
2402		/* build name of archive directory into tmp */
2403		if (*archdirname == '/') {	/* absolute */
2404			strlcpy(tmp, archdirname, sizeof(tmp));
2405		} else {	/* relative */
2406			/* get directory part of logfile */
2407			strlcpy(tmp, file, sizeof(tmp));
2408			if ((p = strrchr(tmp, '/')) == NULL)
2409				tmp[0] = '\0';
2410			else
2411				*(p + 1) = '\0';
2412			strlcat(tmp, archdirname, sizeof(tmp));
2413		}
2414
2415		strlcat(tmp, "/", sizeof(tmp));
2416
2417		/* get filename part of logfile */
2418		if ((p = strrchr(file, '/')) == NULL)
2419			strlcat(tmp, file, sizeof(tmp));
2420		else
2421			strlcat(tmp, p + 1, sizeof(tmp));
2422	} else {
2423		(void) strlcpy(tmp, file, sizeof(tmp));
2424	}
2425
2426	if (timefnamefmt != NULL) {
2427		mtime = mtime_old_timelog(tmp);
2428		if (mtime == -1)
2429			return (-1);
2430	} else {
2431		strlcat(tmp, ".0", sizeof(tmp));
2432		logfile_suffix = get_logfile_suffix(tmp);
2433		if (logfile_suffix == NULL)
2434			return (-1);
2435		(void) strlcat(tmp, logfile_suffix, sizeof(tmp));
2436		if (stat(tmp, &sb) < 0)
2437			return (-1);
2438		mtime = sb.st_mtime;
2439	}
2440
2441	return ((int)(ptimeget_secs(timenow) - mtime + 1800) / 3600);
2442}
2443
2444/* Skip Over Blanks */
2445static char *
2446sob(char *p)
2447{
2448	while (p && *p && isspace(*p))
2449		p++;
2450	return (p);
2451}
2452
2453/* Skip Over Non-Blanks */
2454static char *
2455son(char *p)
2456{
2457	while (p && *p && !isspace(*p))
2458		p++;
2459	return (p);
2460}
2461
2462/* Check if string is actually a number */
2463static int
2464isnumberstr(const char *string)
2465{
2466	while (*string) {
2467		if (!isdigitch(*string++))
2468			return (0);
2469	}
2470	return (1);
2471}
2472
2473/* Check if string contains a glob */
2474static int
2475isglobstr(const char *string)
2476{
2477	char chr;
2478
2479	while ((chr = *string++)) {
2480		if (chr == '*' || chr == '?' || chr == '[')
2481			return (1);
2482	}
2483	return (0);
2484}
2485
2486/*
2487 * Save the active log file under a new name.  A link to the new name
2488 * is the quick-and-easy way to do this.  If that fails (which it will
2489 * if the destination is on another partition), then make a copy of
2490 * the file to the new location.
2491 */
2492static void
2493savelog(char *from, char *to)
2494{
2495	FILE *src, *dst;
2496	int c, res;
2497
2498	res = link(from, to);
2499	if (res == 0)
2500		return;
2501
2502	if ((src = fopen(from, "r")) == NULL)
2503		err(1, "can't fopen %s for reading", from);
2504	if ((dst = fopen(to, "w")) == NULL)
2505		err(1, "can't fopen %s for writing", to);
2506
2507	while ((c = getc(src)) != EOF) {
2508		if ((putc(c, dst)) == EOF)
2509			err(1, "error writing to %s", to);
2510	}
2511
2512	if (ferror(src))
2513		err(1, "error reading from %s", from);
2514	if ((fclose(src)) != 0)
2515		err(1, "can't fclose %s", to);
2516	if ((fclose(dst)) != 0)
2517		err(1, "can't fclose %s", from);
2518}
2519
2520/* create one or more directory components of a path */
2521static void
2522createdir(const struct conf_entry *ent, char *dirpart)
2523{
2524	int res;
2525	char *s, *d;
2526	char mkdirpath[MAXPATHLEN];
2527	struct stat st;
2528
2529	s = dirpart;
2530	d = mkdirpath;
2531
2532	for (;;) {
2533		*d++ = *s++;
2534		if (*s != '/' && *s != '\0')
2535			continue;
2536		*d = '\0';
2537		res = lstat(mkdirpath, &st);
2538		if (res != 0) {
2539			if (noaction) {
2540				printf("\tmkdir %s\n", mkdirpath);
2541			} else {
2542				res = mkdir(mkdirpath, 0755);
2543				if (res != 0)
2544					err(1, "Error on mkdir(\"%s\") for -a",
2545					    mkdirpath);
2546			}
2547		}
2548		if (*s == '\0')
2549			break;
2550	}
2551	if (verbose) {
2552		if (ent->firstcreate)
2553			printf("Created directory '%s' for new %s\n",
2554			    dirpart, ent->log);
2555		else
2556			printf("Created directory '%s' for -a\n", dirpart);
2557	}
2558}
2559
2560/*
2561 * Create a new log file, destroying any currently-existing version
2562 * of the log file in the process.  If the caller wants a backup copy
2563 * of the file to exist, they should call 'link(logfile,logbackup)'
2564 * before calling this routine.
2565 */
2566void
2567createlog(const struct conf_entry *ent)
2568{
2569	int fd, failed;
2570	struct stat st;
2571	char *realfile, *slash, tempfile[MAXPATHLEN];
2572
2573	fd = -1;
2574	realfile = ent->log;
2575
2576	/*
2577	 * If this log file is being created for the first time (-C option),
2578	 * then it may also be true that the parent directory does not exist
2579	 * yet.  Check, and create that directory if it is missing.
2580	 */
2581	if (ent->firstcreate) {
2582		strlcpy(tempfile, realfile, sizeof(tempfile));
2583		slash = strrchr(tempfile, '/');
2584		if (slash != NULL) {
2585			*slash = '\0';
2586			failed = stat(tempfile, &st);
2587			if (failed && errno != ENOENT)
2588				err(1, "Error on stat(%s)", tempfile);
2589			if (failed)
2590				createdir(ent, tempfile);
2591			else if (!S_ISDIR(st.st_mode))
2592				errx(1, "%s exists but is not a directory",
2593				    tempfile);
2594		}
2595	}
2596
2597	/*
2598	 * First create an unused filename, so it can be chown'ed and
2599	 * chmod'ed before it is moved into the real location.  mkstemp
2600	 * will create the file mode=600 & owned by us.  Note that all
2601	 * temp files will have a suffix of '.z<something>'.
2602	 */
2603	strlcpy(tempfile, realfile, sizeof(tempfile));
2604	strlcat(tempfile, ".zXXXXXX", sizeof(tempfile));
2605	if (noaction)
2606		printf("\tmktemp %s\n", tempfile);
2607	else {
2608		fd = mkstemp(tempfile);
2609		if (fd < 0)
2610			err(1, "can't mkstemp logfile %s", tempfile);
2611
2612		/*
2613		 * Add status message to what will become the new log file.
2614		 */
2615		if (!(ent->flags & CE_BINARY)) {
2616			if (log_trim(tempfile, ent))
2617				err(1, "can't add status message to log");
2618		}
2619	}
2620
2621	/* Change the owner/group, if we are supposed to */
2622	if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) {
2623		if (noaction)
2624			printf("\tchown %u:%u %s\n", ent->uid, ent->gid,
2625			    tempfile);
2626		else {
2627			failed = fchown(fd, ent->uid, ent->gid);
2628			if (failed)
2629				err(1, "can't fchown temp file %s", tempfile);
2630		}
2631	}
2632
2633	/* Turn on NODUMP if it was requested in the config-file. */
2634	if (ent->flags & CE_NODUMP) {
2635		if (noaction)
2636			printf("\tchflags nodump %s\n", tempfile);
2637		else {
2638			failed = fchflags(fd, UF_NODUMP);
2639			if (failed) {
2640				warn("log_trim: fchflags(NODUMP)");
2641			}
2642		}
2643	}
2644
2645	/*
2646	 * Note that if the real logfile still exists, and if the call
2647	 * to rename() fails, then "neither the old file nor the new
2648	 * file shall be changed or created" (to quote the standard).
2649	 * If the call succeeds, then the file will be replaced without
2650	 * any window where some other process might find that the file
2651	 * did not exist.
2652	 * XXX - ? It may be that for some error conditions, we could
2653	 *	retry by first removing the realfile and then renaming.
2654	 */
2655	if (noaction) {
2656		printf("\tchmod %o %s\n", ent->permissions, tempfile);
2657		printf("\tmv %s %s\n", tempfile, realfile);
2658	} else {
2659		failed = fchmod(fd, ent->permissions);
2660		if (failed)
2661			err(1, "can't fchmod temp file '%s'", tempfile);
2662		failed = rename(tempfile, realfile);
2663		if (failed)
2664			err(1, "can't mv %s to %s", tempfile, realfile);
2665	}
2666
2667	if (fd >= 0)
2668		close(fd);
2669}
2670
2671/*
2672 * Change the attributes of a given filename to what was specified in
2673 * the newsyslog.conf entry.  This routine is only called for files
2674 * that newsyslog expects that it has created, and thus it is a fatal
2675 * error if this routine finds that the file does not exist.
2676 */
2677static void
2678change_attrs(const char *fname, const struct conf_entry *ent)
2679{
2680	int failed;
2681
2682	if (noaction) {
2683		printf("\tchmod %o %s\n", ent->permissions, fname);
2684
2685		if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1)
2686			printf("\tchown %u:%u %s\n",
2687			    ent->uid, ent->gid, fname);
2688
2689		if (ent->flags & CE_NODUMP)
2690			printf("\tchflags nodump %s\n", fname);
2691		return;
2692	}
2693
2694	failed = chmod(fname, ent->permissions);
2695	if (failed) {
2696		if (errno != EPERM)
2697			err(1, "chmod(%s) in change_attrs", fname);
2698		warn("change_attrs couldn't chmod(%s)", fname);
2699	}
2700
2701	if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) {
2702		failed = chown(fname, ent->uid, ent->gid);
2703		if (failed)
2704			warn("can't chown %s", fname);
2705	}
2706
2707	if (ent->flags & CE_NODUMP) {
2708		failed = chflags(fname, UF_NODUMP);
2709		if (failed)
2710			warn("can't chflags %s NODUMP", fname);
2711	}
2712}
2713
2714/*
2715 * Parse a signal number or signal name. Returns the signal number parsed or -1
2716 * on failure.
2717 */
2718static int
2719parse_signal(const char *str)
2720{
2721	int sig, i;
2722	const char *errstr;
2723
2724	sig = strtonum(str, 1, sys_nsig - 1, &errstr);
2725
2726	if (errstr == NULL)
2727		return (sig);
2728	if (strncasecmp(str, "SIG", 3) == 0)
2729		str += 3;
2730
2731	for (i = 1; i < sys_nsig; i++) {
2732		if (strcasecmp(str, sys_signame[i]) == 0)
2733			return (i);
2734	}
2735
2736	return (-1);
2737}
2738