xref: /titanic_52/usr/src/cmd/logadm/main.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  *
26*7c478bd9Sstevel@tonic-gate  * logadm/main.c -- main routines for logadm
27*7c478bd9Sstevel@tonic-gate  *
28*7c478bd9Sstevel@tonic-gate  * this program is 90% argument processing, 10% actions...
29*7c478bd9Sstevel@tonic-gate  */
30*7c478bd9Sstevel@tonic-gate 
31*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate #include <stdio.h>
34*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
35*7c478bd9Sstevel@tonic-gate #include <unistd.h>
36*7c478bd9Sstevel@tonic-gate #include <strings.h>
37*7c478bd9Sstevel@tonic-gate #include <libintl.h>
38*7c478bd9Sstevel@tonic-gate #include <locale.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/wait.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/filio.h>
43*7c478bd9Sstevel@tonic-gate #include <time.h>
44*7c478bd9Sstevel@tonic-gate #include "err.h"
45*7c478bd9Sstevel@tonic-gate #include "lut.h"
46*7c478bd9Sstevel@tonic-gate #include "fn.h"
47*7c478bd9Sstevel@tonic-gate #include "opts.h"
48*7c478bd9Sstevel@tonic-gate #include "conf.h"
49*7c478bd9Sstevel@tonic-gate #include "glob.h"
50*7c478bd9Sstevel@tonic-gate #include "kw.h"
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate /* forward declarations for functions in this file */
53*7c478bd9Sstevel@tonic-gate static void usage(const char *msg);
54*7c478bd9Sstevel@tonic-gate static void commajoin(const char *lhs, void *rhs, void *arg);
55*7c478bd9Sstevel@tonic-gate static void doaftercmd(const char *lhs, void *rhs, void *arg);
56*7c478bd9Sstevel@tonic-gate static void dologname(struct fn *fnp, struct opts *clopts);
57*7c478bd9Sstevel@tonic-gate static boolean_t rotatelog(struct fn *fnp, struct opts *opts);
58*7c478bd9Sstevel@tonic-gate static void rotateto(struct fn *fnp, struct opts *opts, int n,
59*7c478bd9Sstevel@tonic-gate     struct fn *recentlog, boolean_t isgz);
60*7c478bd9Sstevel@tonic-gate static void expirefiles(struct fn *fnp, struct opts *opts);
61*7c478bd9Sstevel@tonic-gate static void dorm(struct opts *opts, const char *msg, struct fn *fnp);
62*7c478bd9Sstevel@tonic-gate static void docmd(struct opts *opts, const char *msg, const char *cmd,
63*7c478bd9Sstevel@tonic-gate     const char *arg1, const char *arg2, const char *arg3);
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate /* our configuration file, unless otherwise specified by -f */
66*7c478bd9Sstevel@tonic-gate static char *Default_conffile = "/etc/logadm.conf";
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate /* default pathnames to the commands we invoke */
69*7c478bd9Sstevel@tonic-gate static char *Sh = "/bin/sh";
70*7c478bd9Sstevel@tonic-gate static char *Mv = "/bin/mv";
71*7c478bd9Sstevel@tonic-gate static char *Cp = "/bin/cp";
72*7c478bd9Sstevel@tonic-gate static char *Rm = "/bin/rm";
73*7c478bd9Sstevel@tonic-gate static char *Touch = "/bin/touch";
74*7c478bd9Sstevel@tonic-gate static char *Chmod = "/bin/chmod";
75*7c478bd9Sstevel@tonic-gate static char *Chown = "/bin/chown";
76*7c478bd9Sstevel@tonic-gate static char *Gzip = "/bin/gzip";
77*7c478bd9Sstevel@tonic-gate static char *Mkdir = "/bin/mkdir";
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate /* return from time(0), gathered early on to avoid slewed timestamps */
80*7c478bd9Sstevel@tonic-gate time_t Now;
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate /* list of before commands that have been executed */
83*7c478bd9Sstevel@tonic-gate static struct lut *Beforecmds;
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate /* list of after commands to execute before exiting */
86*7c478bd9Sstevel@tonic-gate static struct lut *Aftercmds;
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate /* list of conffile entry names that are considered "done" */
89*7c478bd9Sstevel@tonic-gate static struct lut *Donenames;
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate /* table that drives argument parsing */
92*7c478bd9Sstevel@tonic-gate static struct optinfo Opttable[] = {
93*7c478bd9Sstevel@tonic-gate 	{ "e", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
94*7c478bd9Sstevel@tonic-gate 	{ "f", OPTTYPE_STRING,	NULL,			OPTF_CLI },
95*7c478bd9Sstevel@tonic-gate 	{ "h", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
96*7c478bd9Sstevel@tonic-gate 	{ "N", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI|OPTF_CONF },
97*7c478bd9Sstevel@tonic-gate 	{ "n", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
98*7c478bd9Sstevel@tonic-gate 	{ "r", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
99*7c478bd9Sstevel@tonic-gate 	{ "V", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
100*7c478bd9Sstevel@tonic-gate 	{ "v", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
101*7c478bd9Sstevel@tonic-gate 	{ "w", OPTTYPE_STRING,	NULL,			OPTF_CLI },
102*7c478bd9Sstevel@tonic-gate 	{ "p", OPTTYPE_INT,	opts_parse_seconds,	OPTF_CLI|OPTF_CONF },
103*7c478bd9Sstevel@tonic-gate 	{ "P", OPTTYPE_INT,	opts_parse_ctime,	OPTF_CLI|OPTF_CONF },
104*7c478bd9Sstevel@tonic-gate 	{ "s", OPTTYPE_INT,	opts_parse_bytes,	OPTF_CLI|OPTF_CONF },
105*7c478bd9Sstevel@tonic-gate 	{ "a", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
106*7c478bd9Sstevel@tonic-gate 	{ "b", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
107*7c478bd9Sstevel@tonic-gate 	{ "c", OPTTYPE_BOOLEAN, NULL,			OPTF_CLI|OPTF_CONF },
108*7c478bd9Sstevel@tonic-gate 	{ "g", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
109*7c478bd9Sstevel@tonic-gate 	{ "m", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
110*7c478bd9Sstevel@tonic-gate 	{ "M", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
111*7c478bd9Sstevel@tonic-gate 	{ "o", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
112*7c478bd9Sstevel@tonic-gate 	{ "R", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
113*7c478bd9Sstevel@tonic-gate 	{ "t", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
114*7c478bd9Sstevel@tonic-gate 	{ "z", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
115*7c478bd9Sstevel@tonic-gate 	{ "A", OPTTYPE_INT,	opts_parse_seconds,	OPTF_CLI|OPTF_CONF },
116*7c478bd9Sstevel@tonic-gate 	{ "C", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
117*7c478bd9Sstevel@tonic-gate 	{ "E", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
118*7c478bd9Sstevel@tonic-gate 	{ "S", OPTTYPE_INT,	opts_parse_bytes,	OPTF_CLI|OPTF_CONF },
119*7c478bd9Sstevel@tonic-gate 	{ "T", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
120*7c478bd9Sstevel@tonic-gate };
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * only the "fhnVv" options are allowed in the first form of this command,
124*7c478bd9Sstevel@tonic-gate  * so this defines the list of options that are an error in they appear
125*7c478bd9Sstevel@tonic-gate  * in the first form.  In other words, it is not allowed to run logadm
126*7c478bd9Sstevel@tonic-gate  * with any of these options unless at least one logname is also provided.
127*7c478bd9Sstevel@tonic-gate  */
128*7c478bd9Sstevel@tonic-gate #define	OPTIONS_NOT_FIRST_FORM	"eNrwpPsabcgmoRtzACEST"
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate /* text that we spew with the -h flag */
131*7c478bd9Sstevel@tonic-gate #define	HELP1 \
132*7c478bd9Sstevel@tonic-gate "Usage: logadm [options]\n"\
133*7c478bd9Sstevel@tonic-gate "       (processes all entries in /etc/logadm.conf or conffile given by -f)\n"\
134*7c478bd9Sstevel@tonic-gate "   or: logadm [options] logname...\n"\
135*7c478bd9Sstevel@tonic-gate "       (processes the given lognames)\n"\
136*7c478bd9Sstevel@tonic-gate "\n"\
137*7c478bd9Sstevel@tonic-gate "General options:\n"\
138*7c478bd9Sstevel@tonic-gate "        -e mailaddr     mail errors to given address\n"\
139*7c478bd9Sstevel@tonic-gate "        -f conffile     use conffile instead of /etc/logadm.conf\n"\
140*7c478bd9Sstevel@tonic-gate "        -h              display help\n"\
141*7c478bd9Sstevel@tonic-gate "        -N              not an error if log file nonexistent\n"\
142*7c478bd9Sstevel@tonic-gate "        -n              show actions, don't perform them\n"\
143*7c478bd9Sstevel@tonic-gate "        -r              remove logname entry from conffile\n"\
144*7c478bd9Sstevel@tonic-gate "        -V              ensure conffile entries exist, correct\n"\
145*7c478bd9Sstevel@tonic-gate "        -v              print info about actions happening\n"\
146*7c478bd9Sstevel@tonic-gate "        -w entryname    write entry to config file\n"\
147*7c478bd9Sstevel@tonic-gate "\n"\
148*7c478bd9Sstevel@tonic-gate "Options which control when a logfile is rotated:\n"\
149*7c478bd9Sstevel@tonic-gate "(default is: -s1b -p1w if no -s or -p)\n"\
150*7c478bd9Sstevel@tonic-gate "        -p period       only rotate if period passed since last rotate\n"\
151*7c478bd9Sstevel@tonic-gate "        -P timestamp    used to store rotation date in conffile\n"\
152*7c478bd9Sstevel@tonic-gate "        -s size         only rotate if given size or greater\n"\
153*7c478bd9Sstevel@tonic-gate "\n"
154*7c478bd9Sstevel@tonic-gate #define	HELP2 \
155*7c478bd9Sstevel@tonic-gate "Options which control how a logfile is rotated:\n"\
156*7c478bd9Sstevel@tonic-gate "(default is: -t '$file.$n', owner/group/mode taken from log file)\n"\
157*7c478bd9Sstevel@tonic-gate "        -a cmd          execute cmd after taking actions\n"\
158*7c478bd9Sstevel@tonic-gate "        -b cmd          execute cmd before taking actions\n"\
159*7c478bd9Sstevel@tonic-gate "        -c              copy & truncate logfile, don't rename\n"\
160*7c478bd9Sstevel@tonic-gate "        -g group        new empty log file group\n"\
161*7c478bd9Sstevel@tonic-gate "        -m mode         new empty log file mode\n"\
162*7c478bd9Sstevel@tonic-gate "        -M cmd          execute cmd to rotate the log file\n"\
163*7c478bd9Sstevel@tonic-gate "        -o owner        new empty log file owner\n"\
164*7c478bd9Sstevel@tonic-gate "        -R cmd          run cmd on file after rotate\n"\
165*7c478bd9Sstevel@tonic-gate "        -t template     template for naming old logs\n"\
166*7c478bd9Sstevel@tonic-gate "        -z count        gzip old logs except most recent count\n"\
167*7c478bd9Sstevel@tonic-gate "\n"\
168*7c478bd9Sstevel@tonic-gate "Options which control the expiration of old logfiles:\n"\
169*7c478bd9Sstevel@tonic-gate "(default is: -C10 if no -A, -C, or -S)\n"\
170*7c478bd9Sstevel@tonic-gate "        -A age          expire logs older than age\n"\
171*7c478bd9Sstevel@tonic-gate "        -C count        expire old logs until count remain\n"\
172*7c478bd9Sstevel@tonic-gate "        -E cmd          run cmd on file to expire\n"\
173*7c478bd9Sstevel@tonic-gate "        -S size         expire until space used is below size \n"\
174*7c478bd9Sstevel@tonic-gate "        -T pattern      pattern for finding old logs\n"
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /*
177*7c478bd9Sstevel@tonic-gate  * main -- where it all begins
178*7c478bd9Sstevel@tonic-gate  */
179*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
180*7c478bd9Sstevel@tonic-gate int
181*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
182*7c478bd9Sstevel@tonic-gate {
183*7c478bd9Sstevel@tonic-gate 	struct opts *clopts;		/* from parsing command line */
184*7c478bd9Sstevel@tonic-gate 	const char *conffile;		/* our configuration file */
185*7c478bd9Sstevel@tonic-gate 	struct fn_list *lognames;	/* list of lognames we're processing */
186*7c478bd9Sstevel@tonic-gate 	struct fn *fnp;
187*7c478bd9Sstevel@tonic-gate 	char *val;
188*7c478bd9Sstevel@tonic-gate 
189*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
192*7c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* only used if Makefiles don't define it */
193*7c478bd9Sstevel@tonic-gate #endif
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	/* we only print times into the conffile, so make them uniform */
198*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_TIME, "C");
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate 	/* give our name to error routines & skip it for arg parsing */
201*7c478bd9Sstevel@tonic-gate 	err_init(*argv++);
202*7c478bd9Sstevel@tonic-gate 	(void) setlinebuf(stdout);
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 	if (putenv("PATH=/bin"))
205*7c478bd9Sstevel@tonic-gate 		err(EF_SYS, "putenv PATH");
206*7c478bd9Sstevel@tonic-gate 	if (putenv("TZ=UTC"))
207*7c478bd9Sstevel@tonic-gate 		err(EF_SYS, "putenv TZ");
208*7c478bd9Sstevel@tonic-gate 	tzset();
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	(void) umask(0);
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	Now = time(0);
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate 	/* check for (undocumented) debugging environment variables */
215*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_DEFAULT_CONFFILE"))
216*7c478bd9Sstevel@tonic-gate 		Default_conffile = val;
217*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_DEBUG"))
218*7c478bd9Sstevel@tonic-gate 		Debug = atoi(val);
219*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_SH"))
220*7c478bd9Sstevel@tonic-gate 		Sh = val;
221*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_MV"))
222*7c478bd9Sstevel@tonic-gate 		Mv = val;
223*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CP"))
224*7c478bd9Sstevel@tonic-gate 		Cp = val;
225*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_RM"))
226*7c478bd9Sstevel@tonic-gate 		Rm = val;
227*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_TOUCH"))
228*7c478bd9Sstevel@tonic-gate 		Touch = val;
229*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CHMOD"))
230*7c478bd9Sstevel@tonic-gate 		Chmod = val;
231*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CHOWN"))
232*7c478bd9Sstevel@tonic-gate 		Chown = val;
233*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_GZIP"))
234*7c478bd9Sstevel@tonic-gate 		Gzip = val;
235*7c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_MKDIR"))
236*7c478bd9Sstevel@tonic-gate 		Mkdir = val;
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate 	opts_init(Opttable, sizeof (Opttable) / sizeof (struct optinfo));
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 	/* parse command line arguments */
241*7c478bd9Sstevel@tonic-gate 	if (SETJMP)
242*7c478bd9Sstevel@tonic-gate 		usage("bailing out due to command line errors");
243*7c478bd9Sstevel@tonic-gate 	else
244*7c478bd9Sstevel@tonic-gate 		clopts = opts_parse(argv, OPTF_CLI);
245*7c478bd9Sstevel@tonic-gate 
246*7c478bd9Sstevel@tonic-gate 	if (Debug) {
247*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "command line opts:");
248*7c478bd9Sstevel@tonic-gate 		opts_print(clopts, stderr, NULL);
249*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
250*7c478bd9Sstevel@tonic-gate 	}
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	/*
253*7c478bd9Sstevel@tonic-gate 	 * There are many moods of logadm:
254*7c478bd9Sstevel@tonic-gate 	 *
255*7c478bd9Sstevel@tonic-gate 	 *	1. "-h" for help was given.  We spew a canned help
256*7c478bd9Sstevel@tonic-gate 	 *	   message and exit, regardless of any other options given.
257*7c478bd9Sstevel@tonic-gate 	 *
258*7c478bd9Sstevel@tonic-gate 	 *	2. "-r" or "-w" asking us to write to the conffile.  Lots
259*7c478bd9Sstevel@tonic-gate 	 *	   of argument checking, then we make the change to conffile
260*7c478bd9Sstevel@tonic-gate 	 *	   and exit.  (-r processing actually happens in dologname().)
261*7c478bd9Sstevel@tonic-gate 	 *
262*7c478bd9Sstevel@tonic-gate 	 *	3. "-V" to search/verify the conffile was given.  We do
263*7c478bd9Sstevel@tonic-gate 	 *	   the appropriate run through the conffile and exit.
264*7c478bd9Sstevel@tonic-gate 	 *	   (-V processing actually happens in dologname().)
265*7c478bd9Sstevel@tonic-gate 	 *
266*7c478bd9Sstevel@tonic-gate 	 *	4. No lognames were given, so we're being asked to go through
267*7c478bd9Sstevel@tonic-gate 	 *	   every entry in conffile.  We verify that only the options
268*7c478bd9Sstevel@tonic-gate 	 *	   that make sense for this form of the command are present
269*7c478bd9Sstevel@tonic-gate 	 *	   and fall into the main processing loop below.
270*7c478bd9Sstevel@tonic-gate 	 *
271*7c478bd9Sstevel@tonic-gate 	 *	5. lognames were given, so we fall into the main processing
272*7c478bd9Sstevel@tonic-gate 	 *	   loop below to work our way through them.
273*7c478bd9Sstevel@tonic-gate 	 *
274*7c478bd9Sstevel@tonic-gate 	 * The last two cases are where the option processing gets more
275*7c478bd9Sstevel@tonic-gate 	 * complex.  Each time around the main processing loop, we're
276*7c478bd9Sstevel@tonic-gate 	 * in one of these cases:
277*7c478bd9Sstevel@tonic-gate 	 *
278*7c478bd9Sstevel@tonic-gate 	 *	A. No cmdargs were found (we're in case 4), the entry
279*7c478bd9Sstevel@tonic-gate 	 *	   in conffile supplies no log file names, so the entry
280*7c478bd9Sstevel@tonic-gate 	 *	   name itself is the logfile name (or names, if it globs
281*7c478bd9Sstevel@tonic-gate 	 *	   to multiple file names).
282*7c478bd9Sstevel@tonic-gate 	 *
283*7c478bd9Sstevel@tonic-gate 	 *	B. No cmdargs were found (we're in case 4), the entry
284*7c478bd9Sstevel@tonic-gate 	 *	   in conffile gives log file names that we then loop
285*7c478bd9Sstevel@tonic-gate 	 *	   through and rotate/expire.  In this case, the entry
286*7c478bd9Sstevel@tonic-gate 	 *	   name is specifically NOT one of the log file names.
287*7c478bd9Sstevel@tonic-gate 	 *
288*7c478bd9Sstevel@tonic-gate 	 *	C. We're going through the cmdargs (we're in case 5),
289*7c478bd9Sstevel@tonic-gate 	 *	   the entry in conffile either doesn't exist or it exists
290*7c478bd9Sstevel@tonic-gate 	 *	   but supplies no log file names, so the cmdarg itself
291*7c478bd9Sstevel@tonic-gate 	 *	   is the log file name.
292*7c478bd9Sstevel@tonic-gate 	 *
293*7c478bd9Sstevel@tonic-gate 	 *	D. We're going through the cmdargs (we're in case 5),
294*7c478bd9Sstevel@tonic-gate 	 *	   a matching entry in conffile supplies log file names
295*7c478bd9Sstevel@tonic-gate 	 *	   that we then loop through and rotate/expire.  In this
296*7c478bd9Sstevel@tonic-gate 	 *	   case the entry name is specifically NOT one of the log
297*7c478bd9Sstevel@tonic-gate 	 *	   file names.
298*7c478bd9Sstevel@tonic-gate 	 *
299*7c478bd9Sstevel@tonic-gate 	 * As we're doing all this, any options given on the command line
300*7c478bd9Sstevel@tonic-gate 	 * override any found in the conffile, and we apply the defaults
301*7c478bd9Sstevel@tonic-gate 	 * for rotation conditions and expiration conditions, etc. at the
302*7c478bd9Sstevel@tonic-gate 	 * last opportunity, when we're sure they haven't been overridden
303*7c478bd9Sstevel@tonic-gate 	 * by an option somewhere along the way.
304*7c478bd9Sstevel@tonic-gate 	 *
305*7c478bd9Sstevel@tonic-gate 	 */
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	/* help option overrides anything else */
308*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "h")) {
309*7c478bd9Sstevel@tonic-gate 		(void) fputs(HELP1, stderr);
310*7c478bd9Sstevel@tonic-gate 		(void) fputs(HELP2, stderr);
311*7c478bd9Sstevel@tonic-gate 		err_done(0);
312*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
313*7c478bd9Sstevel@tonic-gate 	}
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 	/* detect illegal option combinations */
316*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "rwV") > 1)
317*7c478bd9Sstevel@tonic-gate 		usage("Only one of -r, -w, or -V may be used at a time.");
318*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "cM") > 1)
319*7c478bd9Sstevel@tonic-gate 		usage("Only one of -c or -M may be used at a time.");
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* arrange for error output to be mailed if clopts includes -e */
322*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "e"))
323*7c478bd9Sstevel@tonic-gate 		err_mailto(opts_optarg(clopts, "e"));
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 	/* this implements the default conffile */
326*7c478bd9Sstevel@tonic-gate 	if ((conffile = opts_optarg(clopts, "f")) == NULL)
327*7c478bd9Sstevel@tonic-gate 		conffile = Default_conffile;
328*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "v"))
329*7c478bd9Sstevel@tonic-gate 		(void) out("# loading %s\n", conffile);
330*7c478bd9Sstevel@tonic-gate 	conf_open(conffile, opts_count(clopts, "Vn") == 0);
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	/* handle conffile write option */
333*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "w")) {
334*7c478bd9Sstevel@tonic-gate 		if (Debug)
335*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
336*7c478bd9Sstevel@tonic-gate 			    "main: add/replace conffile entry: <%s>\n",
337*7c478bd9Sstevel@tonic-gate 			    opts_optarg(clopts, "w"));
338*7c478bd9Sstevel@tonic-gate 		conf_replace(opts_optarg(clopts, "w"), clopts);
339*7c478bd9Sstevel@tonic-gate 		conf_close(clopts);
340*7c478bd9Sstevel@tonic-gate 		err_done(0);
341*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
342*7c478bd9Sstevel@tonic-gate 	}
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	/*
345*7c478bd9Sstevel@tonic-gate 	 * lognames is either a list supplied on the command line,
346*7c478bd9Sstevel@tonic-gate 	 * or every entry in the conffile if none were supplied.
347*7c478bd9Sstevel@tonic-gate 	 */
348*7c478bd9Sstevel@tonic-gate 	lognames = opts_cmdargs(clopts);
349*7c478bd9Sstevel@tonic-gate 	if (fn_list_empty(lognames)) {
350*7c478bd9Sstevel@tonic-gate 		/*
351*7c478bd9Sstevel@tonic-gate 		 * being asked to do all entries in conffile
352*7c478bd9Sstevel@tonic-gate 		 *
353*7c478bd9Sstevel@tonic-gate 		 * check to see if any options were given that only
354*7c478bd9Sstevel@tonic-gate 		 * make sense when lognames are given specifically
355*7c478bd9Sstevel@tonic-gate 		 * on the command line.
356*7c478bd9Sstevel@tonic-gate 		 */
357*7c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, OPTIONS_NOT_FIRST_FORM))
358*7c478bd9Sstevel@tonic-gate 			usage("some options require logname argument");
359*7c478bd9Sstevel@tonic-gate 		if (Debug)
360*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
361*7c478bd9Sstevel@tonic-gate 			    "main: run all entries in conffile\n");
362*7c478bd9Sstevel@tonic-gate 		lognames = conf_entries();
363*7c478bd9Sstevel@tonic-gate 	}
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	/* foreach logname... */
366*7c478bd9Sstevel@tonic-gate 	fn_list_rewind(lognames);
367*7c478bd9Sstevel@tonic-gate 	while ((fnp = fn_list_next(lognames)) != NULL) {
368*7c478bd9Sstevel@tonic-gate 		if (lut_lookup(Donenames, fn_s(fnp)) != NULL) {
369*7c478bd9Sstevel@tonic-gate 			if (Debug)
370*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
371*7c478bd9Sstevel@tonic-gate 				    "main: logname already done: <%s>\n",
372*7c478bd9Sstevel@tonic-gate 				    fn_s(fnp));
373*7c478bd9Sstevel@tonic-gate 			continue;
374*7c478bd9Sstevel@tonic-gate 		}
375*7c478bd9Sstevel@tonic-gate 		if (SETJMP)
376*7c478bd9Sstevel@tonic-gate 			err(EF_FILE, "bailing out on logname \"%s\" "
377*7c478bd9Sstevel@tonic-gate 			    "due to errors", fn_s(fnp));
378*7c478bd9Sstevel@tonic-gate 		else
379*7c478bd9Sstevel@tonic-gate 			dologname(fnp, clopts);
380*7c478bd9Sstevel@tonic-gate 	}
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	/* execute any after commands */
383*7c478bd9Sstevel@tonic-gate 	lut_walk(Aftercmds, doaftercmd, clopts);
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate 	/* write out any conffile changes */
386*7c478bd9Sstevel@tonic-gate 	conf_close(clopts);
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate 	err_done(0);
389*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
390*7c478bd9Sstevel@tonic-gate 	return (0);	/* for lint's little mind */
391*7c478bd9Sstevel@tonic-gate }
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate /* spew a message, then a usage message, then exit */
394*7c478bd9Sstevel@tonic-gate static void
395*7c478bd9Sstevel@tonic-gate usage(const char *msg)
396*7c478bd9Sstevel@tonic-gate {
397*7c478bd9Sstevel@tonic-gate 	if (msg)
398*7c478bd9Sstevel@tonic-gate 		err(0, "%s\nUse \"logadm -h\" for help.", msg);
399*7c478bd9Sstevel@tonic-gate 	else
400*7c478bd9Sstevel@tonic-gate 		err(EF_RAW, "Use \"logadm -h\" for help.\n");
401*7c478bd9Sstevel@tonic-gate }
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate /* helper function used by doaftercmd() to join mail addrs with commas */
404*7c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
405*7c478bd9Sstevel@tonic-gate static void
406*7c478bd9Sstevel@tonic-gate commajoin(const char *lhs, void *rhs, void *arg)
407*7c478bd9Sstevel@tonic-gate {
408*7c478bd9Sstevel@tonic-gate 	struct fn *fnp = (struct fn *)arg;
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 	if (*fn_s(fnp))
411*7c478bd9Sstevel@tonic-gate 		fn_putc(fnp, ',');
412*7c478bd9Sstevel@tonic-gate 	fn_puts(fnp, lhs);
413*7c478bd9Sstevel@tonic-gate }
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate /* helper function used by main() to run "after" commands */
416*7c478bd9Sstevel@tonic-gate static void
417*7c478bd9Sstevel@tonic-gate doaftercmd(const char *lhs, void *rhs, void *arg)
418*7c478bd9Sstevel@tonic-gate {
419*7c478bd9Sstevel@tonic-gate 	struct opts *opts = (struct opts *)arg;
420*7c478bd9Sstevel@tonic-gate 	struct lut *addrs = (struct lut *)rhs;
421*7c478bd9Sstevel@tonic-gate 
422*7c478bd9Sstevel@tonic-gate 	if (addrs) {
423*7c478bd9Sstevel@tonic-gate 		struct fn *fnp = fn_new(NULL);
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 		/*
426*7c478bd9Sstevel@tonic-gate 		 * addrs contains list of email addrs that should get
427*7c478bd9Sstevel@tonic-gate 		 * the error output when this after command is executed.
428*7c478bd9Sstevel@tonic-gate 		 */
429*7c478bd9Sstevel@tonic-gate 		lut_walk(addrs, commajoin, fnp);
430*7c478bd9Sstevel@tonic-gate 		err_mailto(fn_s(fnp));
431*7c478bd9Sstevel@tonic-gate 	}
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 	docmd(opts, "-a cmd", Sh, "-c", lhs, NULL);
434*7c478bd9Sstevel@tonic-gate }
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate /* main logname processing */
437*7c478bd9Sstevel@tonic-gate static void
438*7c478bd9Sstevel@tonic-gate dologname(struct fn *fnp, struct opts *clopts)
439*7c478bd9Sstevel@tonic-gate {
440*7c478bd9Sstevel@tonic-gate 	const char *logname = fn_s(fnp);
441*7c478bd9Sstevel@tonic-gate 	struct opts *cfopts;
442*7c478bd9Sstevel@tonic-gate 	struct opts *allopts;
443*7c478bd9Sstevel@tonic-gate 	struct fn_list *logfiles;
444*7c478bd9Sstevel@tonic-gate 	struct fn_list *globbedfiles;
445*7c478bd9Sstevel@tonic-gate 	struct fn *nextfnp;
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	/* look up options set by config file */
448*7c478bd9Sstevel@tonic-gate 	cfopts = conf_opts(logname);
449*7c478bd9Sstevel@tonic-gate 
450*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "v"))
451*7c478bd9Sstevel@tonic-gate 		(void) out("# processing logname: %s\n", logname);
452*7c478bd9Sstevel@tonic-gate 
453*7c478bd9Sstevel@tonic-gate 	if (Debug) {
454*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "dologname: logname <%s>\n", logname);
455*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "conffile opts:");
456*7c478bd9Sstevel@tonic-gate 		opts_print(cfopts, stderr, NULL);
457*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
458*7c478bd9Sstevel@tonic-gate 	}
459*7c478bd9Sstevel@tonic-gate 
460*7c478bd9Sstevel@tonic-gate 	/* handle conffile lookup option */
461*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "V")) {
462*7c478bd9Sstevel@tonic-gate 		/* lookup an entry in conffile */
463*7c478bd9Sstevel@tonic-gate 		if (Debug)
464*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
465*7c478bd9Sstevel@tonic-gate 			    "dologname: lookup conffile entry\n");
466*7c478bd9Sstevel@tonic-gate 		if (conf_lookup(logname)) {
467*7c478bd9Sstevel@tonic-gate 			opts_printword(logname, stdout);
468*7c478bd9Sstevel@tonic-gate 			opts_print(cfopts, stdout, NULL);
469*7c478bd9Sstevel@tonic-gate 			(void) out("\n");
470*7c478bd9Sstevel@tonic-gate 		} else
471*7c478bd9Sstevel@tonic-gate 			err_exitcode(1);
472*7c478bd9Sstevel@tonic-gate 		return;
473*7c478bd9Sstevel@tonic-gate 	}
474*7c478bd9Sstevel@tonic-gate 
475*7c478bd9Sstevel@tonic-gate 	/* handle conffile removal option */
476*7c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "r")) {
477*7c478bd9Sstevel@tonic-gate 		if (Debug)
478*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
479*7c478bd9Sstevel@tonic-gate 			    "dologname: remove conffile entry\n");
480*7c478bd9Sstevel@tonic-gate 		if (conf_lookup(logname))
481*7c478bd9Sstevel@tonic-gate 			conf_replace(logname, NULL);
482*7c478bd9Sstevel@tonic-gate 		else
483*7c478bd9Sstevel@tonic-gate 			err_exitcode(1);
484*7c478bd9Sstevel@tonic-gate 		return;
485*7c478bd9Sstevel@tonic-gate 	}
486*7c478bd9Sstevel@tonic-gate 
487*7c478bd9Sstevel@tonic-gate 	/* generate combined options */
488*7c478bd9Sstevel@tonic-gate 	allopts = opts_merge(cfopts, clopts);
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	/* arrange for error output to be mailed if allopts includes -e */
491*7c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "e"))
492*7c478bd9Sstevel@tonic-gate 		err_mailto(opts_optarg(allopts, "e"));
493*7c478bd9Sstevel@tonic-gate 	else
494*7c478bd9Sstevel@tonic-gate 		err_mailto(NULL);
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 	/* this implements the default rotation rules */
497*7c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "sp") == 0) {
498*7c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
499*7c478bd9Sstevel@tonic-gate 			(void) out(
500*7c478bd9Sstevel@tonic-gate 			    "#     using default rotate rules: -s1b -p1w\n");
501*7c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "s", "1b");
502*7c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "p", "1w");
503*7c478bd9Sstevel@tonic-gate 	}
504*7c478bd9Sstevel@tonic-gate 
505*7c478bd9Sstevel@tonic-gate 	/* this implements the default expiration rules */
506*7c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "ACS") == 0) {
507*7c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
508*7c478bd9Sstevel@tonic-gate 			(void) out("#     using default expire rule: -C10\n");
509*7c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "C", "10");
510*7c478bd9Sstevel@tonic-gate 	}
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 	/* this implements the default template */
513*7c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "t") == 0) {
514*7c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
515*7c478bd9Sstevel@tonic-gate 			(void) out("#     using default template: $file.$n\n");
516*7c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "t", "$file.$n");
517*7c478bd9Sstevel@tonic-gate 	}
518*7c478bd9Sstevel@tonic-gate 
519*7c478bd9Sstevel@tonic-gate 	if (Debug) {
520*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "merged opts:");
521*7c478bd9Sstevel@tonic-gate 		opts_print(allopts, stderr, NULL);
522*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
523*7c478bd9Sstevel@tonic-gate 	}
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	/*
526*7c478bd9Sstevel@tonic-gate 	 * if the conffile entry supplied log file names, then
527*7c478bd9Sstevel@tonic-gate 	 * logname is NOT one of the log file names (it was just
528*7c478bd9Sstevel@tonic-gate 	 * the entry name in conffile).
529*7c478bd9Sstevel@tonic-gate 	 */
530*7c478bd9Sstevel@tonic-gate 	logfiles = opts_cmdargs(cfopts);
531*7c478bd9Sstevel@tonic-gate 	if (Debug) {
532*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "dologname: logfiles from cfopts:\n");
533*7c478bd9Sstevel@tonic-gate 		fn_list_rewind(logfiles);
534*7c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_next(logfiles)) != NULL)
535*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "    <%s>\n", fn_s(nextfnp));
536*7c478bd9Sstevel@tonic-gate 	}
537*7c478bd9Sstevel@tonic-gate 	if (fn_list_empty(logfiles))
538*7c478bd9Sstevel@tonic-gate 		globbedfiles = glob_glob(fnp);
539*7c478bd9Sstevel@tonic-gate 	else
540*7c478bd9Sstevel@tonic-gate 		globbedfiles = glob_glob_list(logfiles);
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	/* go through the list produced by glob expansion */
543*7c478bd9Sstevel@tonic-gate 	fn_list_rewind(globbedfiles);
544*7c478bd9Sstevel@tonic-gate 	while ((nextfnp = fn_list_next(globbedfiles)) != NULL)
545*7c478bd9Sstevel@tonic-gate 		if (rotatelog(nextfnp, allopts))
546*7c478bd9Sstevel@tonic-gate 			expirefiles(nextfnp, allopts);
547*7c478bd9Sstevel@tonic-gate 
548*7c478bd9Sstevel@tonic-gate 	fn_list_free(globbedfiles);
549*7c478bd9Sstevel@tonic-gate 	opts_free(allopts);
550*7c478bd9Sstevel@tonic-gate }
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate /* absurdly long buffer lengths for holding user/group/mode strings */
554*7c478bd9Sstevel@tonic-gate #define	TIMESTRMAX	100
555*7c478bd9Sstevel@tonic-gate #define	MAXATTR		100
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate /* rotate a log file if necessary, returns true if ok to go on to expire step */
558*7c478bd9Sstevel@tonic-gate static boolean_t
559*7c478bd9Sstevel@tonic-gate rotatelog(struct fn *fnp, struct opts *opts)
560*7c478bd9Sstevel@tonic-gate {
561*7c478bd9Sstevel@tonic-gate 	char *fname = fn_s(fnp);
562*7c478bd9Sstevel@tonic-gate 	struct stat stbuf;
563*7c478bd9Sstevel@tonic-gate 	char nowstr[TIMESTRMAX];
564*7c478bd9Sstevel@tonic-gate 	struct fn *recentlog = fn_new(NULL);	/* for -R cmd */
565*7c478bd9Sstevel@tonic-gate 	char ownerbuf[MAXATTR];
566*7c478bd9Sstevel@tonic-gate 	char groupbuf[MAXATTR];
567*7c478bd9Sstevel@tonic-gate 	char modebuf[MAXATTR];
568*7c478bd9Sstevel@tonic-gate 	const char *owner;
569*7c478bd9Sstevel@tonic-gate 	const char *group;
570*7c478bd9Sstevel@tonic-gate 	const char *mode;
571*7c478bd9Sstevel@tonic-gate 
572*7c478bd9Sstevel@tonic-gate 	if (Debug)
573*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog: fname <%s>\n", fname);
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "p") && opts_optarg_int(opts, "p") == OPTP_NEVER)
576*7c478bd9Sstevel@tonic-gate 		return (B_TRUE);	/* "-p never" forced no rotate */
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 	/* prepare the keywords */
579*7c478bd9Sstevel@tonic-gate 	kw_init(fnp, NULL);
580*7c478bd9Sstevel@tonic-gate 	if (Debug > 1) {
581*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog keywords:\n");
582*7c478bd9Sstevel@tonic-gate 		kw_print(stderr);
583*7c478bd9Sstevel@tonic-gate 	}
584*7c478bd9Sstevel@tonic-gate 
585*7c478bd9Sstevel@tonic-gate 	if (lstat(fname, &stbuf) < 0) {
586*7c478bd9Sstevel@tonic-gate 		if (opts_count(opts, "N"))
587*7c478bd9Sstevel@tonic-gate 			return (1);
588*7c478bd9Sstevel@tonic-gate 		err(EF_WARN|EF_SYS, "%s", fname);
589*7c478bd9Sstevel@tonic-gate 		return (B_FALSE);
590*7c478bd9Sstevel@tonic-gate 	}
591*7c478bd9Sstevel@tonic-gate 
592*7c478bd9Sstevel@tonic-gate 	if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
593*7c478bd9Sstevel@tonic-gate 		err(EF_WARN, "%s is a symlink", fname);
594*7c478bd9Sstevel@tonic-gate 		return (B_FALSE);
595*7c478bd9Sstevel@tonic-gate 	}
596*7c478bd9Sstevel@tonic-gate 
597*7c478bd9Sstevel@tonic-gate 	if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
598*7c478bd9Sstevel@tonic-gate 		err(EF_WARN, "%s is not a regular file", fname);
599*7c478bd9Sstevel@tonic-gate 		return (B_FALSE);
600*7c478bd9Sstevel@tonic-gate 	}
601*7c478bd9Sstevel@tonic-gate 
602*7c478bd9Sstevel@tonic-gate 	/* see if size condition is present, and return if not met */
603*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "s") && stbuf.st_size < opts_optarg_int(opts, "s"))
604*7c478bd9Sstevel@tonic-gate 		return (B_TRUE);
605*7c478bd9Sstevel@tonic-gate 
606*7c478bd9Sstevel@tonic-gate 	/* see if age condition is present, and return if not met */
607*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "p")) {
608*7c478bd9Sstevel@tonic-gate 		int when = opts_optarg_int(opts, "p");
609*7c478bd9Sstevel@tonic-gate 		struct opts *cfopts;
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate 		/* unless rotate forced by "-p now", see if period has passed */
612*7c478bd9Sstevel@tonic-gate 		if (when != OPTP_NOW) {
613*7c478bd9Sstevel@tonic-gate 			/*
614*7c478bd9Sstevel@tonic-gate 			 * "when" holds the number of seconds that must have
615*7c478bd9Sstevel@tonic-gate 			 * passed since the last time this log was rotated.
616*7c478bd9Sstevel@tonic-gate 			 * of course, running logadm can take a little time
617*7c478bd9Sstevel@tonic-gate 			 * (typically a second or two, but longer if the
618*7c478bd9Sstevel@tonic-gate 			 * conffile has lots of stuff in it) and that amount
619*7c478bd9Sstevel@tonic-gate 			 * of time is variable, depending on system load, etc.
620*7c478bd9Sstevel@tonic-gate 			 * so we want to allow a little "slop" in the value of
621*7c478bd9Sstevel@tonic-gate 			 * "when".  this way, if a log should be rotated every
622*7c478bd9Sstevel@tonic-gate 			 * week, and the number of seconds passed is really a
623*7c478bd9Sstevel@tonic-gate 			 * few seconds short of a week, we'll go ahead and
624*7c478bd9Sstevel@tonic-gate 			 * rotate the log as expected.
625*7c478bd9Sstevel@tonic-gate 			 *
626*7c478bd9Sstevel@tonic-gate 			 */
627*7c478bd9Sstevel@tonic-gate 			if (when >= 60 * 60)
628*7c478bd9Sstevel@tonic-gate 				when -= 59;
629*7c478bd9Sstevel@tonic-gate 
630*7c478bd9Sstevel@tonic-gate 			/*
631*7c478bd9Sstevel@tonic-gate 			 * last rotation is recorded as argument to -P,
632*7c478bd9Sstevel@tonic-gate 			 * but if logname isn't the same as log file name
633*7c478bd9Sstevel@tonic-gate 			 * then the timestamp would be recorded on a
634*7c478bd9Sstevel@tonic-gate 			 * separate line in the conf file.  so if we
635*7c478bd9Sstevel@tonic-gate 			 * haven't seen a -P already, we check to see if
636*7c478bd9Sstevel@tonic-gate 			 * it is part of a specific entry for the log
637*7c478bd9Sstevel@tonic-gate 			 * file name.  this handles the case where the
638*7c478bd9Sstevel@tonic-gate 			 * logname is "apache", it supplies a log file
639*7c478bd9Sstevel@tonic-gate 			 * name like "/var/apache/logs/[a-z]*_log",
640*7c478bd9Sstevel@tonic-gate 			 * which expands to multiple file names.  if one
641*7c478bd9Sstevel@tonic-gate 			 * of the file names is "/var/apache/logs/access_log"
642*7c478bd9Sstevel@tonic-gate 			 * the the -P will be attached to a line with that
643*7c478bd9Sstevel@tonic-gate 			 * logname in the conf file.
644*7c478bd9Sstevel@tonic-gate 			 */
645*7c478bd9Sstevel@tonic-gate 			if (opts_count(opts, "P")) {
646*7c478bd9Sstevel@tonic-gate 				int last = opts_optarg_int(opts, "P");
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate 				/* return if not enough time has passed */
649*7c478bd9Sstevel@tonic-gate 				if (Now - last < when)
650*7c478bd9Sstevel@tonic-gate 					return (B_TRUE);
651*7c478bd9Sstevel@tonic-gate 			} else if ((cfopts = conf_opts(fname)) != NULL &&
652*7c478bd9Sstevel@tonic-gate 			    opts_count(cfopts, "P")) {
653*7c478bd9Sstevel@tonic-gate 				int last = opts_optarg_int(cfopts, "P");
654*7c478bd9Sstevel@tonic-gate 
655*7c478bd9Sstevel@tonic-gate 				/*
656*7c478bd9Sstevel@tonic-gate 				 * just checking this means this entry
657*7c478bd9Sstevel@tonic-gate 				 * is now "done" if we're going through
658*7c478bd9Sstevel@tonic-gate 				 * the entire conffile
659*7c478bd9Sstevel@tonic-gate 				 */
660*7c478bd9Sstevel@tonic-gate 				Donenames = lut_add(Donenames, fname, "1");
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate 				/* return if not enough time has passed */
663*7c478bd9Sstevel@tonic-gate 				if (Now - last < when)
664*7c478bd9Sstevel@tonic-gate 					return (B_TRUE);
665*7c478bd9Sstevel@tonic-gate 			}
666*7c478bd9Sstevel@tonic-gate 		}
667*7c478bd9Sstevel@tonic-gate 	}
668*7c478bd9Sstevel@tonic-gate 
669*7c478bd9Sstevel@tonic-gate 	if (Debug)
670*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog: conditions met\n");
671*7c478bd9Sstevel@tonic-gate 
672*7c478bd9Sstevel@tonic-gate 	/* rename the log file */
673*7c478bd9Sstevel@tonic-gate 	rotateto(fnp, opts, 0, recentlog, B_FALSE);
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate 	/* determine owner, group, mode for empty log file */
676*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "o"))
677*7c478bd9Sstevel@tonic-gate 		(void) strlcpy(ownerbuf, opts_optarg(opts, "o"), MAXATTR);
678*7c478bd9Sstevel@tonic-gate 	else {
679*7c478bd9Sstevel@tonic-gate 		(void) snprintf(ownerbuf, MAXATTR, "%ld", stbuf.st_uid);
680*7c478bd9Sstevel@tonic-gate 	}
681*7c478bd9Sstevel@tonic-gate 	owner = ownerbuf;
682*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "g"))
683*7c478bd9Sstevel@tonic-gate 		group = opts_optarg(opts, "g");
684*7c478bd9Sstevel@tonic-gate 	else {
685*7c478bd9Sstevel@tonic-gate 		(void) snprintf(groupbuf, MAXATTR, "%ld", stbuf.st_gid);
686*7c478bd9Sstevel@tonic-gate 		group = groupbuf;
687*7c478bd9Sstevel@tonic-gate 	}
688*7c478bd9Sstevel@tonic-gate 	(void) strlcat(ownerbuf, ":", MAXATTR - strlen(ownerbuf));
689*7c478bd9Sstevel@tonic-gate 	(void) strlcat(ownerbuf, group, MAXATTR - strlen(ownerbuf));
690*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "m"))
691*7c478bd9Sstevel@tonic-gate 		mode = opts_optarg(opts, "m");
692*7c478bd9Sstevel@tonic-gate 	else {
693*7c478bd9Sstevel@tonic-gate 		(void) snprintf(modebuf, MAXATTR,
694*7c478bd9Sstevel@tonic-gate 		    "%03lo", stbuf.st_mode & 0777);
695*7c478bd9Sstevel@tonic-gate 		mode = modebuf;
696*7c478bd9Sstevel@tonic-gate 	}
697*7c478bd9Sstevel@tonic-gate 
698*7c478bd9Sstevel@tonic-gate 	/* create the empty log file */
699*7c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Touch, fname, NULL, NULL);
700*7c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Chown, owner, fname, NULL);
701*7c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Chmod, mode, fname, NULL);
702*7c478bd9Sstevel@tonic-gate 
703*7c478bd9Sstevel@tonic-gate 	/* execute post-rotation command */
704*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "R")) {
705*7c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "R"));
706*7c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate 		kw_init(recentlog, NULL);
709*7c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
710*7c478bd9Sstevel@tonic-gate 		docmd(opts, "-R cmd", Sh, "-c", fn_s(cmd), NULL);
711*7c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
712*7c478bd9Sstevel@tonic-gate 		fn_free(cmd);
713*7c478bd9Sstevel@tonic-gate 	}
714*7c478bd9Sstevel@tonic-gate 	fn_free(recentlog);
715*7c478bd9Sstevel@tonic-gate 
716*7c478bd9Sstevel@tonic-gate 	/*
717*7c478bd9Sstevel@tonic-gate 	 * add "after" command to list of after commands.  we also record
718*7c478bd9Sstevel@tonic-gate 	 * the email address, if any, where the error output of the after
719*7c478bd9Sstevel@tonic-gate 	 * command should be sent.  if the after command is already on
720*7c478bd9Sstevel@tonic-gate 	 * our list, add the email addr to the list the email addrs for
721*7c478bd9Sstevel@tonic-gate 	 * that command (the after command will only be executed once,
722*7c478bd9Sstevel@tonic-gate 	 * so the error output gets mailed to every address we've come
723*7c478bd9Sstevel@tonic-gate 	 * across associated with this command).
724*7c478bd9Sstevel@tonic-gate 	 */
725*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "a")) {
726*7c478bd9Sstevel@tonic-gate 		const char *cmd = opts_optarg(opts, "a");
727*7c478bd9Sstevel@tonic-gate 		struct lut *addrs = (struct lut *)lut_lookup(Aftercmds, cmd);
728*7c478bd9Sstevel@tonic-gate 		if (opts_count(opts, "e"))
729*7c478bd9Sstevel@tonic-gate 			addrs = lut_add(addrs, opts_optarg(opts, "e"), NULL);
730*7c478bd9Sstevel@tonic-gate 		Aftercmds = lut_add(Aftercmds, opts_optarg(opts, "a"), addrs);
731*7c478bd9Sstevel@tonic-gate 	}
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 	/* record the rotation date */
734*7c478bd9Sstevel@tonic-gate 	(void) strftime(nowstr, sizeof (nowstr),
735*7c478bd9Sstevel@tonic-gate 	    "%a %b %e %T %Y", gmtime(&Now));
736*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "v"))
737*7c478bd9Sstevel@tonic-gate 		(void) out("#     recording rotation date %s for %s\n",
738*7c478bd9Sstevel@tonic-gate 		    nowstr, fname);
739*7c478bd9Sstevel@tonic-gate 	conf_set(fname, "P", STRDUP(nowstr));
740*7c478bd9Sstevel@tonic-gate 	Donenames = lut_add(Donenames, fname, "1");
741*7c478bd9Sstevel@tonic-gate 	return (B_TRUE);
742*7c478bd9Sstevel@tonic-gate }
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate /* rotate files "up" according to current template */
745*7c478bd9Sstevel@tonic-gate static void
746*7c478bd9Sstevel@tonic-gate rotateto(struct fn *fnp, struct opts *opts, int n, struct fn *recentlog,
747*7c478bd9Sstevel@tonic-gate     boolean_t isgz)
748*7c478bd9Sstevel@tonic-gate {
749*7c478bd9Sstevel@tonic-gate 	struct fn *template = fn_new(opts_optarg(opts, "t"));
750*7c478bd9Sstevel@tonic-gate 	struct fn *newfile = fn_new(NULL);
751*7c478bd9Sstevel@tonic-gate 	struct fn *dirname;
752*7c478bd9Sstevel@tonic-gate 	int hasn;
753*7c478bd9Sstevel@tonic-gate 	struct stat stbuf;
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 	/* expand template to figure out new filename */
756*7c478bd9Sstevel@tonic-gate 	hasn = kw_expand(template, newfile, n, isgz);
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	if (Debug)
759*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotateto: %s -> %s (%d)\n", fn_s(fnp),
760*7c478bd9Sstevel@tonic-gate 		    fn_s(newfile), n);
761*7c478bd9Sstevel@tonic-gate 
762*7c478bd9Sstevel@tonic-gate 	/* if filename is there already, rotate "up" */
763*7c478bd9Sstevel@tonic-gate 	if (hasn && lstat(fn_s(newfile), &stbuf) != -1)
764*7c478bd9Sstevel@tonic-gate 		rotateto(newfile, opts, n + 1, recentlog, isgz);
765*7c478bd9Sstevel@tonic-gate 	else if (hasn && opts_count(opts, "z")) {
766*7c478bd9Sstevel@tonic-gate 		struct fn *gzfnp = fn_dup(newfile);
767*7c478bd9Sstevel@tonic-gate 		/*
768*7c478bd9Sstevel@tonic-gate 		 * since we're compressing old files, see if we
769*7c478bd9Sstevel@tonic-gate 		 * about to rotate into one.
770*7c478bd9Sstevel@tonic-gate 		 */
771*7c478bd9Sstevel@tonic-gate 		fn_puts(gzfnp, ".gz");
772*7c478bd9Sstevel@tonic-gate 		if (lstat(fn_s(gzfnp), &stbuf) != -1)
773*7c478bd9Sstevel@tonic-gate 			rotateto(gzfnp, opts, n + 1, recentlog, B_TRUE);
774*7c478bd9Sstevel@tonic-gate 		fn_free(gzfnp);
775*7c478bd9Sstevel@tonic-gate 	}
776*7c478bd9Sstevel@tonic-gate 
777*7c478bd9Sstevel@tonic-gate 	/* first time through run "before" cmd if not run already */
778*7c478bd9Sstevel@tonic-gate 	if (n == 0 && opts_count(opts, "b")) {
779*7c478bd9Sstevel@tonic-gate 		const char *cmd = opts_optarg(opts, "b");
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 		if (lut_lookup(Beforecmds, cmd) == NULL) {
782*7c478bd9Sstevel@tonic-gate 			docmd(opts, "-b cmd", Sh, "-c", cmd, NULL);
783*7c478bd9Sstevel@tonic-gate 			Beforecmds = lut_add(Beforecmds, cmd, "1");
784*7c478bd9Sstevel@tonic-gate 		}
785*7c478bd9Sstevel@tonic-gate 	}
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 	/* ensure destination directory exists */
788*7c478bd9Sstevel@tonic-gate 	dirname = fn_dirname(newfile);
789*7c478bd9Sstevel@tonic-gate 	docmd(opts, "verify directory exists", Mkdir, "-p",
790*7c478bd9Sstevel@tonic-gate 	    fn_s(dirname), NULL);
791*7c478bd9Sstevel@tonic-gate 	fn_free(dirname);
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate 	/* do the rename */
794*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "c")) {
795*7c478bd9Sstevel@tonic-gate 		docmd(opts, "rotate log file via copy (-c flag)",
796*7c478bd9Sstevel@tonic-gate 		    Cp, "-fp", fn_s(fnp), fn_s(newfile));
797*7c478bd9Sstevel@tonic-gate 		docmd(opts, "truncate log file (-c flag)",
798*7c478bd9Sstevel@tonic-gate 		    Cp, "-f", "/dev/null", fn_s(fnp));
799*7c478bd9Sstevel@tonic-gate 	} else if (n == 0 && opts_count(opts, "M")) {
800*7c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "M"));
801*7c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
802*7c478bd9Sstevel@tonic-gate 
803*7c478bd9Sstevel@tonic-gate 		/* use specified command to mv the log file */
804*7c478bd9Sstevel@tonic-gate 		kw_init(fnp, newfile);
805*7c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
806*7c478bd9Sstevel@tonic-gate 		docmd(opts, "-M cmd", Sh, "-c", fn_s(cmd), NULL);
807*7c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
808*7c478bd9Sstevel@tonic-gate 		fn_free(cmd);
809*7c478bd9Sstevel@tonic-gate 	} else
810*7c478bd9Sstevel@tonic-gate 		/* common case: we call "mv" to handle the actual rename */
811*7c478bd9Sstevel@tonic-gate 		docmd(opts, "rotate log file", Mv, "-f",
812*7c478bd9Sstevel@tonic-gate 		    fn_s(fnp), fn_s(newfile));
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate 	/* gzip the old log file  according to -z count */
815*7c478bd9Sstevel@tonic-gate 	if (!isgz && opts_count(opts, "z")) {
816*7c478bd9Sstevel@tonic-gate 		int count = opts_optarg_int(opts, "z");
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate 		if (Debug)
819*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "rotateto z count %d\n", count);
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 		if (count <= n) {
822*7c478bd9Sstevel@tonic-gate 			docmd(opts, "compress old log (-z flag)", Gzip,
823*7c478bd9Sstevel@tonic-gate 			    "-f", fn_s(newfile), NULL);
824*7c478bd9Sstevel@tonic-gate 			fn_puts(newfile, ".gz");
825*7c478bd9Sstevel@tonic-gate 		}
826*7c478bd9Sstevel@tonic-gate 	}
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	/* first time through, gather interesting info for caller */
829*7c478bd9Sstevel@tonic-gate 	if (n == 0)
830*7c478bd9Sstevel@tonic-gate 		fn_renew(recentlog, fn_s(newfile));
831*7c478bd9Sstevel@tonic-gate }
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate /* expire phase of logname processing */
834*7c478bd9Sstevel@tonic-gate static void
835*7c478bd9Sstevel@tonic-gate expirefiles(struct fn *fnp, struct opts *opts)
836*7c478bd9Sstevel@tonic-gate {
837*7c478bd9Sstevel@tonic-gate 	char *fname = fn_s(fnp);
838*7c478bd9Sstevel@tonic-gate 	struct fn *template;
839*7c478bd9Sstevel@tonic-gate 	struct fn *pattern;
840*7c478bd9Sstevel@tonic-gate 	struct fn_list *files;
841*7c478bd9Sstevel@tonic-gate 	struct fn *nextfnp;
842*7c478bd9Sstevel@tonic-gate 	int count;
843*7c478bd9Sstevel@tonic-gate 	size_t size;
844*7c478bd9Sstevel@tonic-gate 
845*7c478bd9Sstevel@tonic-gate 	if (Debug)
846*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles: fname <%s>\n", fname);
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 	/* return if no potential expire conditions */
849*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "AS") == 0 && opts_optarg_int(opts, "C") == 0)
850*7c478bd9Sstevel@tonic-gate 		return;
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate 	kw_init(fnp, NULL);
853*7c478bd9Sstevel@tonic-gate 	if (Debug > 1) {
854*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles keywords:\n");
855*7c478bd9Sstevel@tonic-gate 		kw_print(stderr);
856*7c478bd9Sstevel@tonic-gate 	}
857*7c478bd9Sstevel@tonic-gate 
858*7c478bd9Sstevel@tonic-gate 	/* see if pattern was supplied by user */
859*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "T")) {
860*7c478bd9Sstevel@tonic-gate 		template = fn_new(opts_optarg(opts, "T"));
861*7c478bd9Sstevel@tonic-gate 		pattern = glob_to_reglob(template);
862*7c478bd9Sstevel@tonic-gate 	} else {
863*7c478bd9Sstevel@tonic-gate 		/* nope, generate pattern based on rotation template */
864*7c478bd9Sstevel@tonic-gate 		template = fn_new(opts_optarg(opts, "t"));
865*7c478bd9Sstevel@tonic-gate 		pattern = fn_new(NULL);
866*7c478bd9Sstevel@tonic-gate 		(void) kw_expand(template, pattern, -1,
867*7c478bd9Sstevel@tonic-gate 		    opts_count(opts, "z") != 0);
868*7c478bd9Sstevel@tonic-gate 	}
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate 	/* match all old log files (hopefully not any others as well!) */
871*7c478bd9Sstevel@tonic-gate 	files = glob_reglob(pattern);
872*7c478bd9Sstevel@tonic-gate 
873*7c478bd9Sstevel@tonic-gate 	if (Debug) {
874*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles: pattern <%s>\n",
875*7c478bd9Sstevel@tonic-gate 		    fn_s(pattern));
876*7c478bd9Sstevel@tonic-gate 		fn_list_rewind(files);
877*7c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_next(files)) != NULL)
878*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "    <%s>\n", fn_s(nextfnp));
879*7c478bd9Sstevel@tonic-gate 	}
880*7c478bd9Sstevel@tonic-gate 
881*7c478bd9Sstevel@tonic-gate 	/* see if count causes expiration */
882*7c478bd9Sstevel@tonic-gate 	if ((count = opts_optarg_int(opts, "C")) > 0) {
883*7c478bd9Sstevel@tonic-gate 		int needexpire = fn_list_count(files) - count;
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate 		if (Debug)
886*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "expirefiles: needexpire %d\n",
887*7c478bd9Sstevel@tonic-gate 			    needexpire);
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 		while (needexpire > 0 &&
890*7c478bd9Sstevel@tonic-gate 		    ((nextfnp = fn_list_popoldest(files)) != NULL)) {
891*7c478bd9Sstevel@tonic-gate 			dorm(opts, "expire by count rule", nextfnp);
892*7c478bd9Sstevel@tonic-gate 			fn_free(nextfnp);
893*7c478bd9Sstevel@tonic-gate 			needexpire--;
894*7c478bd9Sstevel@tonic-gate 		}
895*7c478bd9Sstevel@tonic-gate 	}
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 	/* see if total size causes expiration */
898*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "S") && (size = opts_optarg_int(opts, "S")) > 0) {
899*7c478bd9Sstevel@tonic-gate 		while (fn_list_totalsize(files) > size &&
900*7c478bd9Sstevel@tonic-gate 		    ((nextfnp = fn_list_popoldest(files)) != NULL)) {
901*7c478bd9Sstevel@tonic-gate 				dorm(opts, "expire by size rule", nextfnp);
902*7c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
903*7c478bd9Sstevel@tonic-gate 		}
904*7c478bd9Sstevel@tonic-gate 	}
905*7c478bd9Sstevel@tonic-gate 
906*7c478bd9Sstevel@tonic-gate 	/* see if age causes expiration */
907*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "A")) {
908*7c478bd9Sstevel@tonic-gate 		int mtime = (int)time(0) - opts_optarg_int(opts, "A");
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_popoldest(files)) != NULL)
911*7c478bd9Sstevel@tonic-gate 			if (fn_getstat(nextfnp)->st_mtime < mtime) {
912*7c478bd9Sstevel@tonic-gate 				dorm(opts, "expire by age rule", nextfnp);
913*7c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
914*7c478bd9Sstevel@tonic-gate 			} else {
915*7c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
916*7c478bd9Sstevel@tonic-gate 				break;
917*7c478bd9Sstevel@tonic-gate 			}
918*7c478bd9Sstevel@tonic-gate 	}
919*7c478bd9Sstevel@tonic-gate 
920*7c478bd9Sstevel@tonic-gate 	fn_free(template);
921*7c478bd9Sstevel@tonic-gate 	fn_list_free(files);
922*7c478bd9Sstevel@tonic-gate }
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate /* execute a command to remove an expired log file */
925*7c478bd9Sstevel@tonic-gate static void
926*7c478bd9Sstevel@tonic-gate dorm(struct opts *opts, const char *msg, struct fn *fnp)
927*7c478bd9Sstevel@tonic-gate {
928*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "E")) {
929*7c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "E"));
930*7c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
931*7c478bd9Sstevel@tonic-gate 
932*7c478bd9Sstevel@tonic-gate 		/* user supplied cmd, expand $file */
933*7c478bd9Sstevel@tonic-gate 		kw_init(fnp, NULL);
934*7c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
935*7c478bd9Sstevel@tonic-gate 		docmd(opts, msg, Sh, "-c", fn_s(cmd), NULL);
936*7c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
937*7c478bd9Sstevel@tonic-gate 		fn_free(cmd);
938*7c478bd9Sstevel@tonic-gate 	} else
939*7c478bd9Sstevel@tonic-gate 		docmd(opts, msg, Rm, "-f", fn_s(fnp), NULL);
940*7c478bd9Sstevel@tonic-gate }
941*7c478bd9Sstevel@tonic-gate 
942*7c478bd9Sstevel@tonic-gate /* execute a command, producing -n and -v output as necessary */
943*7c478bd9Sstevel@tonic-gate static void
944*7c478bd9Sstevel@tonic-gate docmd(struct opts *opts, const char *msg, const char *cmd,
945*7c478bd9Sstevel@tonic-gate     const char *arg1, const char *arg2, const char *arg3)
946*7c478bd9Sstevel@tonic-gate {
947*7c478bd9Sstevel@tonic-gate 	int pid;
948*7c478bd9Sstevel@tonic-gate 	int errpipe[2];
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate 	/* print info about command if necessary */
951*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "vn")) {
952*7c478bd9Sstevel@tonic-gate 		const char *simplecmd;
953*7c478bd9Sstevel@tonic-gate 
954*7c478bd9Sstevel@tonic-gate 		if ((simplecmd = strrchr(cmd, '/')) == NULL)
955*7c478bd9Sstevel@tonic-gate 			simplecmd = cmd;
956*7c478bd9Sstevel@tonic-gate 		else
957*7c478bd9Sstevel@tonic-gate 			simplecmd++;
958*7c478bd9Sstevel@tonic-gate 		(void) out("%s", simplecmd);
959*7c478bd9Sstevel@tonic-gate 		if (arg1)
960*7c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg1);
961*7c478bd9Sstevel@tonic-gate 		if (arg2)
962*7c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg2);
963*7c478bd9Sstevel@tonic-gate 		if (arg3)
964*7c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg3);
965*7c478bd9Sstevel@tonic-gate 		if (msg)
966*7c478bd9Sstevel@tonic-gate 			(void) out(" # %s", msg);
967*7c478bd9Sstevel@tonic-gate 		(void) out("\n");
968*7c478bd9Sstevel@tonic-gate 	}
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "n"))
971*7c478bd9Sstevel@tonic-gate 		return;		/* -n means don't really do it */
972*7c478bd9Sstevel@tonic-gate 
973*7c478bd9Sstevel@tonic-gate 	/*
974*7c478bd9Sstevel@tonic-gate 	 * run the cmd and see if it failed.  this function is *not* a
975*7c478bd9Sstevel@tonic-gate 	 * generic command runner -- we depend on some knowledge we
976*7c478bd9Sstevel@tonic-gate 	 * have about the commands we run.  first of all, we expect
977*7c478bd9Sstevel@tonic-gate 	 * errors to spew something to stderr, and that something is
978*7c478bd9Sstevel@tonic-gate 	 * typically short enough to fit into a pipe so we can wait()
979*7c478bd9Sstevel@tonic-gate 	 * for the command to complete and then fetch the error text
980*7c478bd9Sstevel@tonic-gate 	 * from the pipe.  we also expect the exit codes to make sense.
981*7c478bd9Sstevel@tonic-gate 	 * notice also that we only allow a command name which is an
982*7c478bd9Sstevel@tonic-gate 	 * absolute pathname, and two args must be supplied (the
983*7c478bd9Sstevel@tonic-gate 	 * second may be NULL, or they may both be NULL).
984*7c478bd9Sstevel@tonic-gate 	 */
985*7c478bd9Sstevel@tonic-gate 	if (pipe(errpipe) < 0)
986*7c478bd9Sstevel@tonic-gate 		err(EF_SYS, "pipe");
987*7c478bd9Sstevel@tonic-gate 
988*7c478bd9Sstevel@tonic-gate 	if ((pid = fork()) < 0)
989*7c478bd9Sstevel@tonic-gate 		err(EF_SYS, "fork");
990*7c478bd9Sstevel@tonic-gate 	else if (pid) {
991*7c478bd9Sstevel@tonic-gate 		int wstat;
992*7c478bd9Sstevel@tonic-gate 		int count;
993*7c478bd9Sstevel@tonic-gate 
994*7c478bd9Sstevel@tonic-gate 		/* parent */
995*7c478bd9Sstevel@tonic-gate 		(void) close(errpipe[1]);
996*7c478bd9Sstevel@tonic-gate 		if (waitpid(pid, &wstat, 0) < 0)
997*7c478bd9Sstevel@tonic-gate 			err(EF_SYS, "waitpid");
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 		/* check for stderr output */
1000*7c478bd9Sstevel@tonic-gate 		if (ioctl(errpipe[0], FIONREAD, &count) >= 0 && count) {
1001*7c478bd9Sstevel@tonic-gate 			err(EF_WARN, "command failed: %s%s%s%s%s%s%s",
1002*7c478bd9Sstevel@tonic-gate 				cmd,
1003*7c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
1004*7c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
1005*7c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
1006*7c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
1007*7c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
1008*7c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
1009*7c478bd9Sstevel@tonic-gate 			err_fromfd(errpipe[0]);
1010*7c478bd9Sstevel@tonic-gate 		} else if (WIFSIGNALED(wstat))
1011*7c478bd9Sstevel@tonic-gate 			err(EF_WARN,
1012*7c478bd9Sstevel@tonic-gate 			    "command died, signal %d: %s%s%s%s%s%s%s",
1013*7c478bd9Sstevel@tonic-gate 				WTERMSIG(wstat),
1014*7c478bd9Sstevel@tonic-gate 				cmd,
1015*7c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
1016*7c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
1017*7c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
1018*7c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
1019*7c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
1020*7c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
1021*7c478bd9Sstevel@tonic-gate 		else if (WIFEXITED(wstat) && WEXITSTATUS(wstat))
1022*7c478bd9Sstevel@tonic-gate 			err(EF_WARN,
1023*7c478bd9Sstevel@tonic-gate 			    "command error, exit %d: %s%s%s%s%s%s%s",
1024*7c478bd9Sstevel@tonic-gate 				WEXITSTATUS(wstat),
1025*7c478bd9Sstevel@tonic-gate 				cmd,
1026*7c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
1027*7c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
1028*7c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
1029*7c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
1030*7c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
1031*7c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
1032*7c478bd9Sstevel@tonic-gate 
1033*7c478bd9Sstevel@tonic-gate 		(void) close(errpipe[0]);
1034*7c478bd9Sstevel@tonic-gate 	} else {
1035*7c478bd9Sstevel@tonic-gate 		/* child */
1036*7c478bd9Sstevel@tonic-gate 		(void) dup2(errpipe[1], fileno(stderr));
1037*7c478bd9Sstevel@tonic-gate 		(void) close(errpipe[0]);
1038*7c478bd9Sstevel@tonic-gate 		(void) execl(cmd, cmd, arg1, arg2, arg3, 0);
1039*7c478bd9Sstevel@tonic-gate 		perror(cmd);
1040*7c478bd9Sstevel@tonic-gate 		_exit(1);
1041*7c478bd9Sstevel@tonic-gate 	}
1042*7c478bd9Sstevel@tonic-gate }
1043