xref: /titanic_52/usr/src/cmd/logadm/main.c (revision 636deb6630259136adfafabed91fd84dfcaae63c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*636deb66Sgm149974  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  *
267c478bd9Sstevel@tonic-gate  * logadm/main.c -- main routines for logadm
277c478bd9Sstevel@tonic-gate  *
287c478bd9Sstevel@tonic-gate  * this program is 90% argument processing, 10% actions...
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <stdlib.h>
357c478bd9Sstevel@tonic-gate #include <unistd.h>
367c478bd9Sstevel@tonic-gate #include <strings.h>
377c478bd9Sstevel@tonic-gate #include <libintl.h>
387c478bd9Sstevel@tonic-gate #include <locale.h>
397c478bd9Sstevel@tonic-gate #include <sys/types.h>
407c478bd9Sstevel@tonic-gate #include <sys/stat.h>
417c478bd9Sstevel@tonic-gate #include <sys/wait.h>
427c478bd9Sstevel@tonic-gate #include <sys/filio.h>
437c478bd9Sstevel@tonic-gate #include <time.h>
447c478bd9Sstevel@tonic-gate #include "err.h"
457c478bd9Sstevel@tonic-gate #include "lut.h"
467c478bd9Sstevel@tonic-gate #include "fn.h"
477c478bd9Sstevel@tonic-gate #include "opts.h"
487c478bd9Sstevel@tonic-gate #include "conf.h"
497c478bd9Sstevel@tonic-gate #include "glob.h"
507c478bd9Sstevel@tonic-gate #include "kw.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /* forward declarations for functions in this file */
537c478bd9Sstevel@tonic-gate static void usage(const char *msg);
547c478bd9Sstevel@tonic-gate static void commajoin(const char *lhs, void *rhs, void *arg);
557c478bd9Sstevel@tonic-gate static void doaftercmd(const char *lhs, void *rhs, void *arg);
567c478bd9Sstevel@tonic-gate static void dologname(struct fn *fnp, struct opts *clopts);
577c478bd9Sstevel@tonic-gate static boolean_t rotatelog(struct fn *fnp, struct opts *opts);
587c478bd9Sstevel@tonic-gate static void rotateto(struct fn *fnp, struct opts *opts, int n,
597c478bd9Sstevel@tonic-gate     struct fn *recentlog, boolean_t isgz);
607c478bd9Sstevel@tonic-gate static void expirefiles(struct fn *fnp, struct opts *opts);
617c478bd9Sstevel@tonic-gate static void dorm(struct opts *opts, const char *msg, struct fn *fnp);
627c478bd9Sstevel@tonic-gate static void docmd(struct opts *opts, const char *msg, const char *cmd,
637c478bd9Sstevel@tonic-gate     const char *arg1, const char *arg2, const char *arg3);
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /* our configuration file, unless otherwise specified by -f */
667c478bd9Sstevel@tonic-gate static char *Default_conffile = "/etc/logadm.conf";
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /* default pathnames to the commands we invoke */
697c478bd9Sstevel@tonic-gate static char *Sh = "/bin/sh";
707c478bd9Sstevel@tonic-gate static char *Mv = "/bin/mv";
717c478bd9Sstevel@tonic-gate static char *Cp = "/bin/cp";
727c478bd9Sstevel@tonic-gate static char *Rm = "/bin/rm";
737c478bd9Sstevel@tonic-gate static char *Touch = "/bin/touch";
747c478bd9Sstevel@tonic-gate static char *Chmod = "/bin/chmod";
757c478bd9Sstevel@tonic-gate static char *Chown = "/bin/chown";
767c478bd9Sstevel@tonic-gate static char *Gzip = "/bin/gzip";
777c478bd9Sstevel@tonic-gate static char *Mkdir = "/bin/mkdir";
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /* return from time(0), gathered early on to avoid slewed timestamps */
807c478bd9Sstevel@tonic-gate time_t Now;
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /* list of before commands that have been executed */
837c478bd9Sstevel@tonic-gate static struct lut *Beforecmds;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /* list of after commands to execute before exiting */
867c478bd9Sstevel@tonic-gate static struct lut *Aftercmds;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /* list of conffile entry names that are considered "done" */
897c478bd9Sstevel@tonic-gate static struct lut *Donenames;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate /* table that drives argument parsing */
927c478bd9Sstevel@tonic-gate static struct optinfo Opttable[] = {
937c478bd9Sstevel@tonic-gate 	{ "e", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
947c478bd9Sstevel@tonic-gate 	{ "f", OPTTYPE_STRING,	NULL,			OPTF_CLI },
957c478bd9Sstevel@tonic-gate 	{ "h", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
96*636deb66Sgm149974 	{ "l", OPTTYPE_BOOLEAN, NULL,			OPTF_CLI },
977c478bd9Sstevel@tonic-gate 	{ "N", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI|OPTF_CONF },
987c478bd9Sstevel@tonic-gate 	{ "n", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
997c478bd9Sstevel@tonic-gate 	{ "r", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
1007c478bd9Sstevel@tonic-gate 	{ "V", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
1017c478bd9Sstevel@tonic-gate 	{ "v", OPTTYPE_BOOLEAN,	NULL,			OPTF_CLI },
1027c478bd9Sstevel@tonic-gate 	{ "w", OPTTYPE_STRING,	NULL,			OPTF_CLI },
1037c478bd9Sstevel@tonic-gate 	{ "p", OPTTYPE_INT,	opts_parse_seconds,	OPTF_CLI|OPTF_CONF },
1047c478bd9Sstevel@tonic-gate 	{ "P", OPTTYPE_INT,	opts_parse_ctime,	OPTF_CLI|OPTF_CONF },
1057c478bd9Sstevel@tonic-gate 	{ "s", OPTTYPE_INT,	opts_parse_bytes,	OPTF_CLI|OPTF_CONF },
1067c478bd9Sstevel@tonic-gate 	{ "a", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1077c478bd9Sstevel@tonic-gate 	{ "b", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1087c478bd9Sstevel@tonic-gate 	{ "c", OPTTYPE_BOOLEAN, NULL,			OPTF_CLI|OPTF_CONF },
1097c478bd9Sstevel@tonic-gate 	{ "g", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1107c478bd9Sstevel@tonic-gate 	{ "m", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
1117c478bd9Sstevel@tonic-gate 	{ "M", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1127c478bd9Sstevel@tonic-gate 	{ "o", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1137c478bd9Sstevel@tonic-gate 	{ "R", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1147c478bd9Sstevel@tonic-gate 	{ "t", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1157c478bd9Sstevel@tonic-gate 	{ "z", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
1167c478bd9Sstevel@tonic-gate 	{ "A", OPTTYPE_INT,	opts_parse_seconds,	OPTF_CLI|OPTF_CONF },
1177c478bd9Sstevel@tonic-gate 	{ "C", OPTTYPE_INT,	opts_parse_atopi,	OPTF_CLI|OPTF_CONF },
1187c478bd9Sstevel@tonic-gate 	{ "E", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1197c478bd9Sstevel@tonic-gate 	{ "S", OPTTYPE_INT,	opts_parse_bytes,	OPTF_CLI|OPTF_CONF },
1207c478bd9Sstevel@tonic-gate 	{ "T", OPTTYPE_STRING,	NULL,			OPTF_CLI|OPTF_CONF },
1217c478bd9Sstevel@tonic-gate };
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /*
1247c478bd9Sstevel@tonic-gate  * only the "fhnVv" options are allowed in the first form of this command,
1257c478bd9Sstevel@tonic-gate  * so this defines the list of options that are an error in they appear
1267c478bd9Sstevel@tonic-gate  * in the first form.  In other words, it is not allowed to run logadm
1277c478bd9Sstevel@tonic-gate  * with any of these options unless at least one logname is also provided.
1287c478bd9Sstevel@tonic-gate  */
129*636deb66Sgm149974 #define	OPTIONS_NOT_FIRST_FORM	"eNrwpPsabcglmoRtzACEST"
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate /* text that we spew with the -h flag */
1327c478bd9Sstevel@tonic-gate #define	HELP1 \
1337c478bd9Sstevel@tonic-gate "Usage: logadm [options]\n"\
1347c478bd9Sstevel@tonic-gate "       (processes all entries in /etc/logadm.conf or conffile given by -f)\n"\
1357c478bd9Sstevel@tonic-gate "   or: logadm [options] logname...\n"\
1367c478bd9Sstevel@tonic-gate "       (processes the given lognames)\n"\
1377c478bd9Sstevel@tonic-gate "\n"\
1387c478bd9Sstevel@tonic-gate "General options:\n"\
1397c478bd9Sstevel@tonic-gate "        -e mailaddr     mail errors to given address\n"\
1407c478bd9Sstevel@tonic-gate "        -f conffile     use conffile instead of /etc/logadm.conf\n"\
1417c478bd9Sstevel@tonic-gate "        -h              display help\n"\
1427c478bd9Sstevel@tonic-gate "        -N              not an error if log file nonexistent\n"\
1437c478bd9Sstevel@tonic-gate "        -n              show actions, don't perform them\n"\
1447c478bd9Sstevel@tonic-gate "        -r              remove logname entry from conffile\n"\
1457c478bd9Sstevel@tonic-gate "        -V              ensure conffile entries exist, correct\n"\
1467c478bd9Sstevel@tonic-gate "        -v              print info about actions happening\n"\
1477c478bd9Sstevel@tonic-gate "        -w entryname    write entry to config file\n"\
1487c478bd9Sstevel@tonic-gate "\n"\
1497c478bd9Sstevel@tonic-gate "Options which control when a logfile is rotated:\n"\
1507c478bd9Sstevel@tonic-gate "(default is: -s1b -p1w if no -s or -p)\n"\
1517c478bd9Sstevel@tonic-gate "        -p period       only rotate if period passed since last rotate\n"\
1527c478bd9Sstevel@tonic-gate "        -P timestamp    used to store rotation date in conffile\n"\
1537c478bd9Sstevel@tonic-gate "        -s size         only rotate if given size or greater\n"\
1547c478bd9Sstevel@tonic-gate "\n"
1557c478bd9Sstevel@tonic-gate #define	HELP2 \
1567c478bd9Sstevel@tonic-gate "Options which control how a logfile is rotated:\n"\
1577c478bd9Sstevel@tonic-gate "(default is: -t '$file.$n', owner/group/mode taken from log file)\n"\
1587c478bd9Sstevel@tonic-gate "        -a cmd          execute cmd after taking actions\n"\
1597c478bd9Sstevel@tonic-gate "        -b cmd          execute cmd before taking actions\n"\
1607c478bd9Sstevel@tonic-gate "        -c              copy & truncate logfile, don't rename\n"\
1617c478bd9Sstevel@tonic-gate "        -g group        new empty log file group\n"\
162*636deb66Sgm149974 "        -l              rotate log file with local time rather than UTC\n"\
1637c478bd9Sstevel@tonic-gate "        -m mode         new empty log file mode\n"\
1647c478bd9Sstevel@tonic-gate "        -M cmd          execute cmd to rotate the log file\n"\
1657c478bd9Sstevel@tonic-gate "        -o owner        new empty log file owner\n"\
1667c478bd9Sstevel@tonic-gate "        -R cmd          run cmd on file after rotate\n"\
1677c478bd9Sstevel@tonic-gate "        -t template     template for naming old logs\n"\
1687c478bd9Sstevel@tonic-gate "        -z count        gzip old logs except most recent count\n"\
1697c478bd9Sstevel@tonic-gate "\n"\
1707c478bd9Sstevel@tonic-gate "Options which control the expiration of old logfiles:\n"\
1717c478bd9Sstevel@tonic-gate "(default is: -C10 if no -A, -C, or -S)\n"\
1727c478bd9Sstevel@tonic-gate "        -A age          expire logs older than age\n"\
1737c478bd9Sstevel@tonic-gate "        -C count        expire old logs until count remain\n"\
1747c478bd9Sstevel@tonic-gate "        -E cmd          run cmd on file to expire\n"\
1757c478bd9Sstevel@tonic-gate "        -S size         expire until space used is below size \n"\
1767c478bd9Sstevel@tonic-gate "        -T pattern      pattern for finding old logs\n"
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /*
1797c478bd9Sstevel@tonic-gate  * main -- where it all begins
1807c478bd9Sstevel@tonic-gate  */
1817c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1827c478bd9Sstevel@tonic-gate int
1837c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
1847c478bd9Sstevel@tonic-gate {
1857c478bd9Sstevel@tonic-gate 	struct opts *clopts;		/* from parsing command line */
1867c478bd9Sstevel@tonic-gate 	const char *conffile;		/* our configuration file */
1877c478bd9Sstevel@tonic-gate 	struct fn_list *lognames;	/* list of lognames we're processing */
1887c478bd9Sstevel@tonic-gate 	struct fn *fnp;
1897c478bd9Sstevel@tonic-gate 	char *val;
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1947c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* only used if Makefiles don't define it */
1957c478bd9Sstevel@tonic-gate #endif
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	/* we only print times into the conffile, so make them uniform */
2007c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_TIME, "C");
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	/* give our name to error routines & skip it for arg parsing */
2037c478bd9Sstevel@tonic-gate 	err_init(*argv++);
2047c478bd9Sstevel@tonic-gate 	(void) setlinebuf(stdout);
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	if (putenv("PATH=/bin"))
2077c478bd9Sstevel@tonic-gate 		err(EF_SYS, "putenv PATH");
2087c478bd9Sstevel@tonic-gate 	if (putenv("TZ=UTC"))
2097c478bd9Sstevel@tonic-gate 		err(EF_SYS, "putenv TZ");
2107c478bd9Sstevel@tonic-gate 	tzset();
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	(void) umask(0);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	Now = time(0);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	/* check for (undocumented) debugging environment variables */
2177c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_DEFAULT_CONFFILE"))
2187c478bd9Sstevel@tonic-gate 		Default_conffile = val;
2197c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_DEBUG"))
2207c478bd9Sstevel@tonic-gate 		Debug = atoi(val);
2217c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_SH"))
2227c478bd9Sstevel@tonic-gate 		Sh = val;
2237c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_MV"))
2247c478bd9Sstevel@tonic-gate 		Mv = val;
2257c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CP"))
2267c478bd9Sstevel@tonic-gate 		Cp = val;
2277c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_RM"))
2287c478bd9Sstevel@tonic-gate 		Rm = val;
2297c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_TOUCH"))
2307c478bd9Sstevel@tonic-gate 		Touch = val;
2317c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CHMOD"))
2327c478bd9Sstevel@tonic-gate 		Chmod = val;
2337c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_CHOWN"))
2347c478bd9Sstevel@tonic-gate 		Chown = val;
2357c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_GZIP"))
2367c478bd9Sstevel@tonic-gate 		Gzip = val;
2377c478bd9Sstevel@tonic-gate 	if (val = getenv("_LOGADM_MKDIR"))
2387c478bd9Sstevel@tonic-gate 		Mkdir = val;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	opts_init(Opttable, sizeof (Opttable) / sizeof (struct optinfo));
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	/* parse command line arguments */
2437c478bd9Sstevel@tonic-gate 	if (SETJMP)
2447c478bd9Sstevel@tonic-gate 		usage("bailing out due to command line errors");
2457c478bd9Sstevel@tonic-gate 	else
2467c478bd9Sstevel@tonic-gate 		clopts = opts_parse(argv, OPTF_CLI);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	if (Debug) {
2497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "command line opts:");
2507c478bd9Sstevel@tonic-gate 		opts_print(clopts, stderr, NULL);
2517c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	/*
2557c478bd9Sstevel@tonic-gate 	 * There are many moods of logadm:
2567c478bd9Sstevel@tonic-gate 	 *
2577c478bd9Sstevel@tonic-gate 	 *	1. "-h" for help was given.  We spew a canned help
2587c478bd9Sstevel@tonic-gate 	 *	   message and exit, regardless of any other options given.
2597c478bd9Sstevel@tonic-gate 	 *
2607c478bd9Sstevel@tonic-gate 	 *	2. "-r" or "-w" asking us to write to the conffile.  Lots
2617c478bd9Sstevel@tonic-gate 	 *	   of argument checking, then we make the change to conffile
2627c478bd9Sstevel@tonic-gate 	 *	   and exit.  (-r processing actually happens in dologname().)
2637c478bd9Sstevel@tonic-gate 	 *
2647c478bd9Sstevel@tonic-gate 	 *	3. "-V" to search/verify the conffile was given.  We do
2657c478bd9Sstevel@tonic-gate 	 *	   the appropriate run through the conffile and exit.
2667c478bd9Sstevel@tonic-gate 	 *	   (-V processing actually happens in dologname().)
2677c478bd9Sstevel@tonic-gate 	 *
2687c478bd9Sstevel@tonic-gate 	 *	4. No lognames were given, so we're being asked to go through
2697c478bd9Sstevel@tonic-gate 	 *	   every entry in conffile.  We verify that only the options
2707c478bd9Sstevel@tonic-gate 	 *	   that make sense for this form of the command are present
2717c478bd9Sstevel@tonic-gate 	 *	   and fall into the main processing loop below.
2727c478bd9Sstevel@tonic-gate 	 *
2737c478bd9Sstevel@tonic-gate 	 *	5. lognames were given, so we fall into the main processing
2747c478bd9Sstevel@tonic-gate 	 *	   loop below to work our way through them.
2757c478bd9Sstevel@tonic-gate 	 *
2767c478bd9Sstevel@tonic-gate 	 * The last two cases are where the option processing gets more
2777c478bd9Sstevel@tonic-gate 	 * complex.  Each time around the main processing loop, we're
2787c478bd9Sstevel@tonic-gate 	 * in one of these cases:
2797c478bd9Sstevel@tonic-gate 	 *
2807c478bd9Sstevel@tonic-gate 	 *	A. No cmdargs were found (we're in case 4), the entry
2817c478bd9Sstevel@tonic-gate 	 *	   in conffile supplies no log file names, so the entry
2827c478bd9Sstevel@tonic-gate 	 *	   name itself is the logfile name (or names, if it globs
2837c478bd9Sstevel@tonic-gate 	 *	   to multiple file names).
2847c478bd9Sstevel@tonic-gate 	 *
2857c478bd9Sstevel@tonic-gate 	 *	B. No cmdargs were found (we're in case 4), the entry
2867c478bd9Sstevel@tonic-gate 	 *	   in conffile gives log file names that we then loop
2877c478bd9Sstevel@tonic-gate 	 *	   through and rotate/expire.  In this case, the entry
2887c478bd9Sstevel@tonic-gate 	 *	   name is specifically NOT one of the log file names.
2897c478bd9Sstevel@tonic-gate 	 *
2907c478bd9Sstevel@tonic-gate 	 *	C. We're going through the cmdargs (we're in case 5),
2917c478bd9Sstevel@tonic-gate 	 *	   the entry in conffile either doesn't exist or it exists
2927c478bd9Sstevel@tonic-gate 	 *	   but supplies no log file names, so the cmdarg itself
2937c478bd9Sstevel@tonic-gate 	 *	   is the log file name.
2947c478bd9Sstevel@tonic-gate 	 *
2957c478bd9Sstevel@tonic-gate 	 *	D. We're going through the cmdargs (we're in case 5),
2967c478bd9Sstevel@tonic-gate 	 *	   a matching entry in conffile supplies log file names
2977c478bd9Sstevel@tonic-gate 	 *	   that we then loop through and rotate/expire.  In this
2987c478bd9Sstevel@tonic-gate 	 *	   case the entry name is specifically NOT one of the log
2997c478bd9Sstevel@tonic-gate 	 *	   file names.
3007c478bd9Sstevel@tonic-gate 	 *
3017c478bd9Sstevel@tonic-gate 	 * As we're doing all this, any options given on the command line
3027c478bd9Sstevel@tonic-gate 	 * override any found in the conffile, and we apply the defaults
3037c478bd9Sstevel@tonic-gate 	 * for rotation conditions and expiration conditions, etc. at the
3047c478bd9Sstevel@tonic-gate 	 * last opportunity, when we're sure they haven't been overridden
3057c478bd9Sstevel@tonic-gate 	 * by an option somewhere along the way.
3067c478bd9Sstevel@tonic-gate 	 *
3077c478bd9Sstevel@tonic-gate 	 */
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	/* help option overrides anything else */
3107c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "h")) {
3117c478bd9Sstevel@tonic-gate 		(void) fputs(HELP1, stderr);
3127c478bd9Sstevel@tonic-gate 		(void) fputs(HELP2, stderr);
3137c478bd9Sstevel@tonic-gate 		err_done(0);
3147c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
3157c478bd9Sstevel@tonic-gate 	}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/* detect illegal option combinations */
3187c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "rwV") > 1)
3197c478bd9Sstevel@tonic-gate 		usage("Only one of -r, -w, or -V may be used at a time.");
3207c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "cM") > 1)
3217c478bd9Sstevel@tonic-gate 		usage("Only one of -c or -M may be used at a time.");
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	/* arrange for error output to be mailed if clopts includes -e */
3247c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "e"))
3257c478bd9Sstevel@tonic-gate 		err_mailto(opts_optarg(clopts, "e"));
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/* this implements the default conffile */
3287c478bd9Sstevel@tonic-gate 	if ((conffile = opts_optarg(clopts, "f")) == NULL)
3297c478bd9Sstevel@tonic-gate 		conffile = Default_conffile;
3307c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "v"))
3317c478bd9Sstevel@tonic-gate 		(void) out("# loading %s\n", conffile);
3327c478bd9Sstevel@tonic-gate 	conf_open(conffile, opts_count(clopts, "Vn") == 0);
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/* handle conffile write option */
3357c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "w")) {
3367c478bd9Sstevel@tonic-gate 		if (Debug)
3377c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3387c478bd9Sstevel@tonic-gate 			    "main: add/replace conffile entry: <%s>\n",
3397c478bd9Sstevel@tonic-gate 			    opts_optarg(clopts, "w"));
3407c478bd9Sstevel@tonic-gate 		conf_replace(opts_optarg(clopts, "w"), clopts);
3417c478bd9Sstevel@tonic-gate 		conf_close(clopts);
3427c478bd9Sstevel@tonic-gate 		err_done(0);
3437c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	/*
3477c478bd9Sstevel@tonic-gate 	 * lognames is either a list supplied on the command line,
3487c478bd9Sstevel@tonic-gate 	 * or every entry in the conffile if none were supplied.
3497c478bd9Sstevel@tonic-gate 	 */
3507c478bd9Sstevel@tonic-gate 	lognames = opts_cmdargs(clopts);
3517c478bd9Sstevel@tonic-gate 	if (fn_list_empty(lognames)) {
3527c478bd9Sstevel@tonic-gate 		/*
3537c478bd9Sstevel@tonic-gate 		 * being asked to do all entries in conffile
3547c478bd9Sstevel@tonic-gate 		 *
3557c478bd9Sstevel@tonic-gate 		 * check to see if any options were given that only
3567c478bd9Sstevel@tonic-gate 		 * make sense when lognames are given specifically
3577c478bd9Sstevel@tonic-gate 		 * on the command line.
3587c478bd9Sstevel@tonic-gate 		 */
3597c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, OPTIONS_NOT_FIRST_FORM))
3607c478bd9Sstevel@tonic-gate 			usage("some options require logname argument");
3617c478bd9Sstevel@tonic-gate 		if (Debug)
3627c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3637c478bd9Sstevel@tonic-gate 			    "main: run all entries in conffile\n");
3647c478bd9Sstevel@tonic-gate 		lognames = conf_entries();
3657c478bd9Sstevel@tonic-gate 	}
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	/* foreach logname... */
3687c478bd9Sstevel@tonic-gate 	fn_list_rewind(lognames);
3697c478bd9Sstevel@tonic-gate 	while ((fnp = fn_list_next(lognames)) != NULL) {
3707c478bd9Sstevel@tonic-gate 		if (lut_lookup(Donenames, fn_s(fnp)) != NULL) {
3717c478bd9Sstevel@tonic-gate 			if (Debug)
3727c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
3737c478bd9Sstevel@tonic-gate 				    "main: logname already done: <%s>\n",
3747c478bd9Sstevel@tonic-gate 				    fn_s(fnp));
3757c478bd9Sstevel@tonic-gate 			continue;
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 		if (SETJMP)
3787c478bd9Sstevel@tonic-gate 			err(EF_FILE, "bailing out on logname \"%s\" "
3797c478bd9Sstevel@tonic-gate 			    "due to errors", fn_s(fnp));
3807c478bd9Sstevel@tonic-gate 		else
3817c478bd9Sstevel@tonic-gate 			dologname(fnp, clopts);
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	/* execute any after commands */
3857c478bd9Sstevel@tonic-gate 	lut_walk(Aftercmds, doaftercmd, clopts);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	/* write out any conffile changes */
3887c478bd9Sstevel@tonic-gate 	conf_close(clopts);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	err_done(0);
3917c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
3927c478bd9Sstevel@tonic-gate 	return (0);	/* for lint's little mind */
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate /* spew a message, then a usage message, then exit */
3967c478bd9Sstevel@tonic-gate static void
3977c478bd9Sstevel@tonic-gate usage(const char *msg)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate 	if (msg)
4007c478bd9Sstevel@tonic-gate 		err(0, "%s\nUse \"logadm -h\" for help.", msg);
4017c478bd9Sstevel@tonic-gate 	else
4027c478bd9Sstevel@tonic-gate 		err(EF_RAW, "Use \"logadm -h\" for help.\n");
4037c478bd9Sstevel@tonic-gate }
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate /* helper function used by doaftercmd() to join mail addrs with commas */
4067c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
4077c478bd9Sstevel@tonic-gate static void
4087c478bd9Sstevel@tonic-gate commajoin(const char *lhs, void *rhs, void *arg)
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate 	struct fn *fnp = (struct fn *)arg;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	if (*fn_s(fnp))
4137c478bd9Sstevel@tonic-gate 		fn_putc(fnp, ',');
4147c478bd9Sstevel@tonic-gate 	fn_puts(fnp, lhs);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /* helper function used by main() to run "after" commands */
4187c478bd9Sstevel@tonic-gate static void
4197c478bd9Sstevel@tonic-gate doaftercmd(const char *lhs, void *rhs, void *arg)
4207c478bd9Sstevel@tonic-gate {
4217c478bd9Sstevel@tonic-gate 	struct opts *opts = (struct opts *)arg;
4227c478bd9Sstevel@tonic-gate 	struct lut *addrs = (struct lut *)rhs;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	if (addrs) {
4257c478bd9Sstevel@tonic-gate 		struct fn *fnp = fn_new(NULL);
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		/*
4287c478bd9Sstevel@tonic-gate 		 * addrs contains list of email addrs that should get
4297c478bd9Sstevel@tonic-gate 		 * the error output when this after command is executed.
4307c478bd9Sstevel@tonic-gate 		 */
4317c478bd9Sstevel@tonic-gate 		lut_walk(addrs, commajoin, fnp);
4327c478bd9Sstevel@tonic-gate 		err_mailto(fn_s(fnp));
4337c478bd9Sstevel@tonic-gate 	}
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	docmd(opts, "-a cmd", Sh, "-c", lhs, NULL);
4367c478bd9Sstevel@tonic-gate }
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate /* main logname processing */
4397c478bd9Sstevel@tonic-gate static void
4407c478bd9Sstevel@tonic-gate dologname(struct fn *fnp, struct opts *clopts)
4417c478bd9Sstevel@tonic-gate {
4427c478bd9Sstevel@tonic-gate 	const char *logname = fn_s(fnp);
4437c478bd9Sstevel@tonic-gate 	struct opts *cfopts;
4447c478bd9Sstevel@tonic-gate 	struct opts *allopts;
4457c478bd9Sstevel@tonic-gate 	struct fn_list *logfiles;
4467c478bd9Sstevel@tonic-gate 	struct fn_list *globbedfiles;
4477c478bd9Sstevel@tonic-gate 	struct fn *nextfnp;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	/* look up options set by config file */
4507c478bd9Sstevel@tonic-gate 	cfopts = conf_opts(logname);
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "v"))
4537c478bd9Sstevel@tonic-gate 		(void) out("# processing logname: %s\n", logname);
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	if (Debug) {
4567c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "dologname: logname <%s>\n", logname);
4577c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "conffile opts:");
4587c478bd9Sstevel@tonic-gate 		opts_print(cfopts, stderr, NULL);
4597c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
4607c478bd9Sstevel@tonic-gate 	}
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	/* handle conffile lookup option */
4637c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "V")) {
4647c478bd9Sstevel@tonic-gate 		/* lookup an entry in conffile */
4657c478bd9Sstevel@tonic-gate 		if (Debug)
4667c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
4677c478bd9Sstevel@tonic-gate 			    "dologname: lookup conffile entry\n");
4687c478bd9Sstevel@tonic-gate 		if (conf_lookup(logname)) {
4697c478bd9Sstevel@tonic-gate 			opts_printword(logname, stdout);
4707c478bd9Sstevel@tonic-gate 			opts_print(cfopts, stdout, NULL);
4717c478bd9Sstevel@tonic-gate 			(void) out("\n");
4727c478bd9Sstevel@tonic-gate 		} else
4737c478bd9Sstevel@tonic-gate 			err_exitcode(1);
4747c478bd9Sstevel@tonic-gate 		return;
4757c478bd9Sstevel@tonic-gate 	}
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	/* handle conffile removal option */
4787c478bd9Sstevel@tonic-gate 	if (opts_count(clopts, "r")) {
4797c478bd9Sstevel@tonic-gate 		if (Debug)
4807c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
4817c478bd9Sstevel@tonic-gate 			    "dologname: remove conffile entry\n");
4827c478bd9Sstevel@tonic-gate 		if (conf_lookup(logname))
4837c478bd9Sstevel@tonic-gate 			conf_replace(logname, NULL);
4847c478bd9Sstevel@tonic-gate 		else
4857c478bd9Sstevel@tonic-gate 			err_exitcode(1);
4867c478bd9Sstevel@tonic-gate 		return;
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/* generate combined options */
4907c478bd9Sstevel@tonic-gate 	allopts = opts_merge(cfopts, clopts);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	/* arrange for error output to be mailed if allopts includes -e */
4937c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "e"))
4947c478bd9Sstevel@tonic-gate 		err_mailto(opts_optarg(allopts, "e"));
4957c478bd9Sstevel@tonic-gate 	else
4967c478bd9Sstevel@tonic-gate 		err_mailto(NULL);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	/* this implements the default rotation rules */
4997c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "sp") == 0) {
5007c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
5017c478bd9Sstevel@tonic-gate 			(void) out(
5027c478bd9Sstevel@tonic-gate 			    "#     using default rotate rules: -s1b -p1w\n");
5037c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "s", "1b");
5047c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "p", "1w");
5057c478bd9Sstevel@tonic-gate 	}
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	/* this implements the default expiration rules */
5087c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "ACS") == 0) {
5097c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
5107c478bd9Sstevel@tonic-gate 			(void) out("#     using default expire rule: -C10\n");
5117c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "C", "10");
5127c478bd9Sstevel@tonic-gate 	}
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	/* this implements the default template */
5157c478bd9Sstevel@tonic-gate 	if (opts_count(allopts, "t") == 0) {
5167c478bd9Sstevel@tonic-gate 		if (opts_count(clopts, "v"))
5177c478bd9Sstevel@tonic-gate 			(void) out("#     using default template: $file.$n\n");
5187c478bd9Sstevel@tonic-gate 		(void) opts_set(allopts, "t", "$file.$n");
5197c478bd9Sstevel@tonic-gate 	}
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	if (Debug) {
5227c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "merged opts:");
5237c478bd9Sstevel@tonic-gate 		opts_print(allopts, stderr, NULL);
5247c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	/*
5287c478bd9Sstevel@tonic-gate 	 * if the conffile entry supplied log file names, then
5297c478bd9Sstevel@tonic-gate 	 * logname is NOT one of the log file names (it was just
5307c478bd9Sstevel@tonic-gate 	 * the entry name in conffile).
5317c478bd9Sstevel@tonic-gate 	 */
5327c478bd9Sstevel@tonic-gate 	logfiles = opts_cmdargs(cfopts);
5337c478bd9Sstevel@tonic-gate 	if (Debug) {
5347c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "dologname: logfiles from cfopts:\n");
5357c478bd9Sstevel@tonic-gate 		fn_list_rewind(logfiles);
5367c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_next(logfiles)) != NULL)
5377c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "    <%s>\n", fn_s(nextfnp));
5387c478bd9Sstevel@tonic-gate 	}
5397c478bd9Sstevel@tonic-gate 	if (fn_list_empty(logfiles))
5407c478bd9Sstevel@tonic-gate 		globbedfiles = glob_glob(fnp);
5417c478bd9Sstevel@tonic-gate 	else
5427c478bd9Sstevel@tonic-gate 		globbedfiles = glob_glob_list(logfiles);
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/* go through the list produced by glob expansion */
5457c478bd9Sstevel@tonic-gate 	fn_list_rewind(globbedfiles);
5467c478bd9Sstevel@tonic-gate 	while ((nextfnp = fn_list_next(globbedfiles)) != NULL)
5477c478bd9Sstevel@tonic-gate 		if (rotatelog(nextfnp, allopts))
5487c478bd9Sstevel@tonic-gate 			expirefiles(nextfnp, allopts);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	fn_list_free(globbedfiles);
5517c478bd9Sstevel@tonic-gate 	opts_free(allopts);
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate /* absurdly long buffer lengths for holding user/group/mode strings */
5567c478bd9Sstevel@tonic-gate #define	TIMESTRMAX	100
5577c478bd9Sstevel@tonic-gate #define	MAXATTR		100
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate /* rotate a log file if necessary, returns true if ok to go on to expire step */
5607c478bd9Sstevel@tonic-gate static boolean_t
5617c478bd9Sstevel@tonic-gate rotatelog(struct fn *fnp, struct opts *opts)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	char *fname = fn_s(fnp);
5647c478bd9Sstevel@tonic-gate 	struct stat stbuf;
5657c478bd9Sstevel@tonic-gate 	char nowstr[TIMESTRMAX];
5667c478bd9Sstevel@tonic-gate 	struct fn *recentlog = fn_new(NULL);	/* for -R cmd */
5677c478bd9Sstevel@tonic-gate 	char ownerbuf[MAXATTR];
5687c478bd9Sstevel@tonic-gate 	char groupbuf[MAXATTR];
5697c478bd9Sstevel@tonic-gate 	char modebuf[MAXATTR];
5707c478bd9Sstevel@tonic-gate 	const char *owner;
5717c478bd9Sstevel@tonic-gate 	const char *group;
5727c478bd9Sstevel@tonic-gate 	const char *mode;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	if (Debug)
5757c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog: fname <%s>\n", fname);
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "p") && opts_optarg_int(opts, "p") == OPTP_NEVER)
5787c478bd9Sstevel@tonic-gate 		return (B_TRUE);	/* "-p never" forced no rotate */
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	/* prepare the keywords */
5817c478bd9Sstevel@tonic-gate 	kw_init(fnp, NULL);
5827c478bd9Sstevel@tonic-gate 	if (Debug > 1) {
5837c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog keywords:\n");
5847c478bd9Sstevel@tonic-gate 		kw_print(stderr);
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	if (lstat(fname, &stbuf) < 0) {
5887c478bd9Sstevel@tonic-gate 		if (opts_count(opts, "N"))
5897c478bd9Sstevel@tonic-gate 			return (1);
5907c478bd9Sstevel@tonic-gate 		err(EF_WARN|EF_SYS, "%s", fname);
5917c478bd9Sstevel@tonic-gate 		return (B_FALSE);
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
5957c478bd9Sstevel@tonic-gate 		err(EF_WARN, "%s is a symlink", fname);
5967c478bd9Sstevel@tonic-gate 		return (B_FALSE);
5977c478bd9Sstevel@tonic-gate 	}
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
6007c478bd9Sstevel@tonic-gate 		err(EF_WARN, "%s is not a regular file", fname);
6017c478bd9Sstevel@tonic-gate 		return (B_FALSE);
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	/* see if size condition is present, and return if not met */
6057c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "s") && stbuf.st_size < opts_optarg_int(opts, "s"))
6067c478bd9Sstevel@tonic-gate 		return (B_TRUE);
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	/* see if age condition is present, and return if not met */
6097c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "p")) {
6107c478bd9Sstevel@tonic-gate 		int when = opts_optarg_int(opts, "p");
6117c478bd9Sstevel@tonic-gate 		struct opts *cfopts;
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 		/* unless rotate forced by "-p now", see if period has passed */
6147c478bd9Sstevel@tonic-gate 		if (when != OPTP_NOW) {
6157c478bd9Sstevel@tonic-gate 			/*
6167c478bd9Sstevel@tonic-gate 			 * "when" holds the number of seconds that must have
6177c478bd9Sstevel@tonic-gate 			 * passed since the last time this log was rotated.
6187c478bd9Sstevel@tonic-gate 			 * of course, running logadm can take a little time
6197c478bd9Sstevel@tonic-gate 			 * (typically a second or two, but longer if the
6207c478bd9Sstevel@tonic-gate 			 * conffile has lots of stuff in it) and that amount
6217c478bd9Sstevel@tonic-gate 			 * of time is variable, depending on system load, etc.
6227c478bd9Sstevel@tonic-gate 			 * so we want to allow a little "slop" in the value of
6237c478bd9Sstevel@tonic-gate 			 * "when".  this way, if a log should be rotated every
6247c478bd9Sstevel@tonic-gate 			 * week, and the number of seconds passed is really a
6257c478bd9Sstevel@tonic-gate 			 * few seconds short of a week, we'll go ahead and
6267c478bd9Sstevel@tonic-gate 			 * rotate the log as expected.
6277c478bd9Sstevel@tonic-gate 			 *
6287c478bd9Sstevel@tonic-gate 			 */
6297c478bd9Sstevel@tonic-gate 			if (when >= 60 * 60)
6307c478bd9Sstevel@tonic-gate 				when -= 59;
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 			/*
6337c478bd9Sstevel@tonic-gate 			 * last rotation is recorded as argument to -P,
6347c478bd9Sstevel@tonic-gate 			 * but if logname isn't the same as log file name
6357c478bd9Sstevel@tonic-gate 			 * then the timestamp would be recorded on a
6367c478bd9Sstevel@tonic-gate 			 * separate line in the conf file.  so if we
6377c478bd9Sstevel@tonic-gate 			 * haven't seen a -P already, we check to see if
6387c478bd9Sstevel@tonic-gate 			 * it is part of a specific entry for the log
6397c478bd9Sstevel@tonic-gate 			 * file name.  this handles the case where the
6407c478bd9Sstevel@tonic-gate 			 * logname is "apache", it supplies a log file
6417c478bd9Sstevel@tonic-gate 			 * name like "/var/apache/logs/[a-z]*_log",
6427c478bd9Sstevel@tonic-gate 			 * which expands to multiple file names.  if one
6437c478bd9Sstevel@tonic-gate 			 * of the file names is "/var/apache/logs/access_log"
6447c478bd9Sstevel@tonic-gate 			 * the the -P will be attached to a line with that
6457c478bd9Sstevel@tonic-gate 			 * logname in the conf file.
6467c478bd9Sstevel@tonic-gate 			 */
6477c478bd9Sstevel@tonic-gate 			if (opts_count(opts, "P")) {
6487c478bd9Sstevel@tonic-gate 				int last = opts_optarg_int(opts, "P");
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 				/* return if not enough time has passed */
6517c478bd9Sstevel@tonic-gate 				if (Now - last < when)
6527c478bd9Sstevel@tonic-gate 					return (B_TRUE);
6537c478bd9Sstevel@tonic-gate 			} else if ((cfopts = conf_opts(fname)) != NULL &&
6547c478bd9Sstevel@tonic-gate 			    opts_count(cfopts, "P")) {
6557c478bd9Sstevel@tonic-gate 				int last = opts_optarg_int(cfopts, "P");
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 				/*
6587c478bd9Sstevel@tonic-gate 				 * just checking this means this entry
6597c478bd9Sstevel@tonic-gate 				 * is now "done" if we're going through
6607c478bd9Sstevel@tonic-gate 				 * the entire conffile
6617c478bd9Sstevel@tonic-gate 				 */
6627c478bd9Sstevel@tonic-gate 				Donenames = lut_add(Donenames, fname, "1");
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 				/* return if not enough time has passed */
6657c478bd9Sstevel@tonic-gate 				if (Now - last < when)
6667c478bd9Sstevel@tonic-gate 					return (B_TRUE);
6677c478bd9Sstevel@tonic-gate 			}
6687c478bd9Sstevel@tonic-gate 		}
6697c478bd9Sstevel@tonic-gate 	}
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	if (Debug)
6727c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotatelog: conditions met\n");
673*636deb66Sgm149974 	if (opts_count(opts, "l")) {
674*636deb66Sgm149974 		/* Change the time zone to local time zone */
675*636deb66Sgm149974 		if (putenv("TZ="))
676*636deb66Sgm149974 			err(EF_SYS, "putenv TZ");
677*636deb66Sgm149974 		tzset();
678*636deb66Sgm149974 		Now = time(0);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 		/* rename the log file */
6817c478bd9Sstevel@tonic-gate 		rotateto(fnp, opts, 0, recentlog, B_FALSE);
6827c478bd9Sstevel@tonic-gate 
683*636deb66Sgm149974 		/* Change the time zone to UTC */
684*636deb66Sgm149974 		if (putenv("TZ=UTC"))
685*636deb66Sgm149974 			err(EF_SYS, "putenv TZ");
686*636deb66Sgm149974 		tzset();
687*636deb66Sgm149974 		Now = time(0);
688*636deb66Sgm149974 	} else {
689*636deb66Sgm149974 		/* rename the log file */
690*636deb66Sgm149974 		rotateto(fnp, opts, 0, recentlog, B_FALSE);
691*636deb66Sgm149974 	}
692*636deb66Sgm149974 
6937c478bd9Sstevel@tonic-gate 	/* determine owner, group, mode for empty log file */
6947c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "o"))
6957c478bd9Sstevel@tonic-gate 		(void) strlcpy(ownerbuf, opts_optarg(opts, "o"), MAXATTR);
6967c478bd9Sstevel@tonic-gate 	else {
6977c478bd9Sstevel@tonic-gate 		(void) snprintf(ownerbuf, MAXATTR, "%ld", stbuf.st_uid);
6987c478bd9Sstevel@tonic-gate 	}
6997c478bd9Sstevel@tonic-gate 	owner = ownerbuf;
7007c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "g"))
7017c478bd9Sstevel@tonic-gate 		group = opts_optarg(opts, "g");
7027c478bd9Sstevel@tonic-gate 	else {
7037c478bd9Sstevel@tonic-gate 		(void) snprintf(groupbuf, MAXATTR, "%ld", stbuf.st_gid);
7047c478bd9Sstevel@tonic-gate 		group = groupbuf;
7057c478bd9Sstevel@tonic-gate 	}
7067c478bd9Sstevel@tonic-gate 	(void) strlcat(ownerbuf, ":", MAXATTR - strlen(ownerbuf));
7077c478bd9Sstevel@tonic-gate 	(void) strlcat(ownerbuf, group, MAXATTR - strlen(ownerbuf));
7087c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "m"))
7097c478bd9Sstevel@tonic-gate 		mode = opts_optarg(opts, "m");
7107c478bd9Sstevel@tonic-gate 	else {
7117c478bd9Sstevel@tonic-gate 		(void) snprintf(modebuf, MAXATTR,
7127c478bd9Sstevel@tonic-gate 		    "%03lo", stbuf.st_mode & 0777);
7137c478bd9Sstevel@tonic-gate 		mode = modebuf;
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	/* create the empty log file */
7177c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Touch, fname, NULL, NULL);
7187c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Chown, owner, fname, NULL);
7197c478bd9Sstevel@tonic-gate 	docmd(opts, NULL, Chmod, mode, fname, NULL);
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 	/* execute post-rotation command */
7227c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "R")) {
7237c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "R"));
7247c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		kw_init(recentlog, NULL);
7277c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
7287c478bd9Sstevel@tonic-gate 		docmd(opts, "-R cmd", Sh, "-c", fn_s(cmd), NULL);
7297c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
7307c478bd9Sstevel@tonic-gate 		fn_free(cmd);
7317c478bd9Sstevel@tonic-gate 	}
7327c478bd9Sstevel@tonic-gate 	fn_free(recentlog);
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	/*
7357c478bd9Sstevel@tonic-gate 	 * add "after" command to list of after commands.  we also record
7367c478bd9Sstevel@tonic-gate 	 * the email address, if any, where the error output of the after
7377c478bd9Sstevel@tonic-gate 	 * command should be sent.  if the after command is already on
7387c478bd9Sstevel@tonic-gate 	 * our list, add the email addr to the list the email addrs for
7397c478bd9Sstevel@tonic-gate 	 * that command (the after command will only be executed once,
7407c478bd9Sstevel@tonic-gate 	 * so the error output gets mailed to every address we've come
7417c478bd9Sstevel@tonic-gate 	 * across associated with this command).
7427c478bd9Sstevel@tonic-gate 	 */
7437c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "a")) {
7447c478bd9Sstevel@tonic-gate 		const char *cmd = opts_optarg(opts, "a");
7457c478bd9Sstevel@tonic-gate 		struct lut *addrs = (struct lut *)lut_lookup(Aftercmds, cmd);
7467c478bd9Sstevel@tonic-gate 		if (opts_count(opts, "e"))
7477c478bd9Sstevel@tonic-gate 			addrs = lut_add(addrs, opts_optarg(opts, "e"), NULL);
7487c478bd9Sstevel@tonic-gate 		Aftercmds = lut_add(Aftercmds, opts_optarg(opts, "a"), addrs);
7497c478bd9Sstevel@tonic-gate 	}
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	/* record the rotation date */
7527c478bd9Sstevel@tonic-gate 	(void) strftime(nowstr, sizeof (nowstr),
7537c478bd9Sstevel@tonic-gate 	    "%a %b %e %T %Y", gmtime(&Now));
7547c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "v"))
7557c478bd9Sstevel@tonic-gate 		(void) out("#     recording rotation date %s for %s\n",
7567c478bd9Sstevel@tonic-gate 		    nowstr, fname);
7577c478bd9Sstevel@tonic-gate 	conf_set(fname, "P", STRDUP(nowstr));
7587c478bd9Sstevel@tonic-gate 	Donenames = lut_add(Donenames, fname, "1");
7597c478bd9Sstevel@tonic-gate 	return (B_TRUE);
7607c478bd9Sstevel@tonic-gate }
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate /* rotate files "up" according to current template */
7637c478bd9Sstevel@tonic-gate static void
7647c478bd9Sstevel@tonic-gate rotateto(struct fn *fnp, struct opts *opts, int n, struct fn *recentlog,
7657c478bd9Sstevel@tonic-gate     boolean_t isgz)
7667c478bd9Sstevel@tonic-gate {
7677c478bd9Sstevel@tonic-gate 	struct fn *template = fn_new(opts_optarg(opts, "t"));
7687c478bd9Sstevel@tonic-gate 	struct fn *newfile = fn_new(NULL);
7697c478bd9Sstevel@tonic-gate 	struct fn *dirname;
7707c478bd9Sstevel@tonic-gate 	int hasn;
7717c478bd9Sstevel@tonic-gate 	struct stat stbuf;
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	/* expand template to figure out new filename */
7747c478bd9Sstevel@tonic-gate 	hasn = kw_expand(template, newfile, n, isgz);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	if (Debug)
7777c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "rotateto: %s -> %s (%d)\n", fn_s(fnp),
7787c478bd9Sstevel@tonic-gate 		    fn_s(newfile), n);
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 	/* if filename is there already, rotate "up" */
7817c478bd9Sstevel@tonic-gate 	if (hasn && lstat(fn_s(newfile), &stbuf) != -1)
7827c478bd9Sstevel@tonic-gate 		rotateto(newfile, opts, n + 1, recentlog, isgz);
7837c478bd9Sstevel@tonic-gate 	else if (hasn && opts_count(opts, "z")) {
7847c478bd9Sstevel@tonic-gate 		struct fn *gzfnp = fn_dup(newfile);
7857c478bd9Sstevel@tonic-gate 		/*
7867c478bd9Sstevel@tonic-gate 		 * since we're compressing old files, see if we
7877c478bd9Sstevel@tonic-gate 		 * about to rotate into one.
7887c478bd9Sstevel@tonic-gate 		 */
7897c478bd9Sstevel@tonic-gate 		fn_puts(gzfnp, ".gz");
7907c478bd9Sstevel@tonic-gate 		if (lstat(fn_s(gzfnp), &stbuf) != -1)
7917c478bd9Sstevel@tonic-gate 			rotateto(gzfnp, opts, n + 1, recentlog, B_TRUE);
7927c478bd9Sstevel@tonic-gate 		fn_free(gzfnp);
7937c478bd9Sstevel@tonic-gate 	}
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	/* first time through run "before" cmd if not run already */
7967c478bd9Sstevel@tonic-gate 	if (n == 0 && opts_count(opts, "b")) {
7977c478bd9Sstevel@tonic-gate 		const char *cmd = opts_optarg(opts, "b");
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		if (lut_lookup(Beforecmds, cmd) == NULL) {
8007c478bd9Sstevel@tonic-gate 			docmd(opts, "-b cmd", Sh, "-c", cmd, NULL);
8017c478bd9Sstevel@tonic-gate 			Beforecmds = lut_add(Beforecmds, cmd, "1");
8027c478bd9Sstevel@tonic-gate 		}
8037c478bd9Sstevel@tonic-gate 	}
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	/* ensure destination directory exists */
8067c478bd9Sstevel@tonic-gate 	dirname = fn_dirname(newfile);
8077c478bd9Sstevel@tonic-gate 	docmd(opts, "verify directory exists", Mkdir, "-p",
8087c478bd9Sstevel@tonic-gate 	    fn_s(dirname), NULL);
8097c478bd9Sstevel@tonic-gate 	fn_free(dirname);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	/* do the rename */
8127c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "c")) {
8137c478bd9Sstevel@tonic-gate 		docmd(opts, "rotate log file via copy (-c flag)",
8147c478bd9Sstevel@tonic-gate 		    Cp, "-fp", fn_s(fnp), fn_s(newfile));
8157c478bd9Sstevel@tonic-gate 		docmd(opts, "truncate log file (-c flag)",
8167c478bd9Sstevel@tonic-gate 		    Cp, "-f", "/dev/null", fn_s(fnp));
8177c478bd9Sstevel@tonic-gate 	} else if (n == 0 && opts_count(opts, "M")) {
8187c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "M"));
8197c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 		/* use specified command to mv the log file */
8227c478bd9Sstevel@tonic-gate 		kw_init(fnp, newfile);
8237c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
8247c478bd9Sstevel@tonic-gate 		docmd(opts, "-M cmd", Sh, "-c", fn_s(cmd), NULL);
8257c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
8267c478bd9Sstevel@tonic-gate 		fn_free(cmd);
8277c478bd9Sstevel@tonic-gate 	} else
8287c478bd9Sstevel@tonic-gate 		/* common case: we call "mv" to handle the actual rename */
8297c478bd9Sstevel@tonic-gate 		docmd(opts, "rotate log file", Mv, "-f",
8307c478bd9Sstevel@tonic-gate 		    fn_s(fnp), fn_s(newfile));
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	/* gzip the old log file  according to -z count */
8337c478bd9Sstevel@tonic-gate 	if (!isgz && opts_count(opts, "z")) {
8347c478bd9Sstevel@tonic-gate 		int count = opts_optarg_int(opts, "z");
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 		if (Debug)
8377c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "rotateto z count %d\n", count);
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 		if (count <= n) {
8407c478bd9Sstevel@tonic-gate 			docmd(opts, "compress old log (-z flag)", Gzip,
8417c478bd9Sstevel@tonic-gate 			    "-f", fn_s(newfile), NULL);
8427c478bd9Sstevel@tonic-gate 			fn_puts(newfile, ".gz");
8437c478bd9Sstevel@tonic-gate 		}
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	/* first time through, gather interesting info for caller */
8477c478bd9Sstevel@tonic-gate 	if (n == 0)
8487c478bd9Sstevel@tonic-gate 		fn_renew(recentlog, fn_s(newfile));
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate /* expire phase of logname processing */
8527c478bd9Sstevel@tonic-gate static void
8537c478bd9Sstevel@tonic-gate expirefiles(struct fn *fnp, struct opts *opts)
8547c478bd9Sstevel@tonic-gate {
8557c478bd9Sstevel@tonic-gate 	char *fname = fn_s(fnp);
8567c478bd9Sstevel@tonic-gate 	struct fn *template;
8577c478bd9Sstevel@tonic-gate 	struct fn *pattern;
8587c478bd9Sstevel@tonic-gate 	struct fn_list *files;
8597c478bd9Sstevel@tonic-gate 	struct fn *nextfnp;
8607c478bd9Sstevel@tonic-gate 	int count;
8617c478bd9Sstevel@tonic-gate 	size_t size;
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	if (Debug)
8647c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles: fname <%s>\n", fname);
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	/* return if no potential expire conditions */
8677c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "AS") == 0 && opts_optarg_int(opts, "C") == 0)
8687c478bd9Sstevel@tonic-gate 		return;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	kw_init(fnp, NULL);
8717c478bd9Sstevel@tonic-gate 	if (Debug > 1) {
8727c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles keywords:\n");
8737c478bd9Sstevel@tonic-gate 		kw_print(stderr);
8747c478bd9Sstevel@tonic-gate 	}
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	/* see if pattern was supplied by user */
8777c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "T")) {
8787c478bd9Sstevel@tonic-gate 		template = fn_new(opts_optarg(opts, "T"));
8797c478bd9Sstevel@tonic-gate 		pattern = glob_to_reglob(template);
8807c478bd9Sstevel@tonic-gate 	} else {
8817c478bd9Sstevel@tonic-gate 		/* nope, generate pattern based on rotation template */
8827c478bd9Sstevel@tonic-gate 		template = fn_new(opts_optarg(opts, "t"));
8837c478bd9Sstevel@tonic-gate 		pattern = fn_new(NULL);
8847c478bd9Sstevel@tonic-gate 		(void) kw_expand(template, pattern, -1,
8857c478bd9Sstevel@tonic-gate 		    opts_count(opts, "z") != 0);
8867c478bd9Sstevel@tonic-gate 	}
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 	/* match all old log files (hopefully not any others as well!) */
8897c478bd9Sstevel@tonic-gate 	files = glob_reglob(pattern);
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 	if (Debug) {
8927c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "expirefiles: pattern <%s>\n",
8937c478bd9Sstevel@tonic-gate 		    fn_s(pattern));
8947c478bd9Sstevel@tonic-gate 		fn_list_rewind(files);
8957c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_next(files)) != NULL)
8967c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "    <%s>\n", fn_s(nextfnp));
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	/* see if count causes expiration */
9007c478bd9Sstevel@tonic-gate 	if ((count = opts_optarg_int(opts, "C")) > 0) {
9017c478bd9Sstevel@tonic-gate 		int needexpire = fn_list_count(files) - count;
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 		if (Debug)
9047c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "expirefiles: needexpire %d\n",
9057c478bd9Sstevel@tonic-gate 			    needexpire);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 		while (needexpire > 0 &&
9087c478bd9Sstevel@tonic-gate 		    ((nextfnp = fn_list_popoldest(files)) != NULL)) {
9097c478bd9Sstevel@tonic-gate 			dorm(opts, "expire by count rule", nextfnp);
9107c478bd9Sstevel@tonic-gate 			fn_free(nextfnp);
9117c478bd9Sstevel@tonic-gate 			needexpire--;
9127c478bd9Sstevel@tonic-gate 		}
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* see if total size causes expiration */
9167c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "S") && (size = opts_optarg_int(opts, "S")) > 0) {
9177c478bd9Sstevel@tonic-gate 		while (fn_list_totalsize(files) > size &&
9187c478bd9Sstevel@tonic-gate 		    ((nextfnp = fn_list_popoldest(files)) != NULL)) {
9197c478bd9Sstevel@tonic-gate 				dorm(opts, "expire by size rule", nextfnp);
9207c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	/* see if age causes expiration */
9257c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "A")) {
9267c478bd9Sstevel@tonic-gate 		int mtime = (int)time(0) - opts_optarg_int(opts, "A");
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 		while ((nextfnp = fn_list_popoldest(files)) != NULL)
9297c478bd9Sstevel@tonic-gate 			if (fn_getstat(nextfnp)->st_mtime < mtime) {
9307c478bd9Sstevel@tonic-gate 				dorm(opts, "expire by age rule", nextfnp);
9317c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
9327c478bd9Sstevel@tonic-gate 			} else {
9337c478bd9Sstevel@tonic-gate 				fn_free(nextfnp);
9347c478bd9Sstevel@tonic-gate 				break;
9357c478bd9Sstevel@tonic-gate 			}
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	fn_free(template);
9397c478bd9Sstevel@tonic-gate 	fn_list_free(files);
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate /* execute a command to remove an expired log file */
9437c478bd9Sstevel@tonic-gate static void
9447c478bd9Sstevel@tonic-gate dorm(struct opts *opts, const char *msg, struct fn *fnp)
9457c478bd9Sstevel@tonic-gate {
9467c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "E")) {
9477c478bd9Sstevel@tonic-gate 		struct fn *rawcmd = fn_new(opts_optarg(opts, "E"));
9487c478bd9Sstevel@tonic-gate 		struct fn *cmd = fn_new(NULL);
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 		/* user supplied cmd, expand $file */
9517c478bd9Sstevel@tonic-gate 		kw_init(fnp, NULL);
9527c478bd9Sstevel@tonic-gate 		(void) kw_expand(rawcmd, cmd, 0, B_FALSE);
9537c478bd9Sstevel@tonic-gate 		docmd(opts, msg, Sh, "-c", fn_s(cmd), NULL);
9547c478bd9Sstevel@tonic-gate 		fn_free(rawcmd);
9557c478bd9Sstevel@tonic-gate 		fn_free(cmd);
9567c478bd9Sstevel@tonic-gate 	} else
9577c478bd9Sstevel@tonic-gate 		docmd(opts, msg, Rm, "-f", fn_s(fnp), NULL);
9587c478bd9Sstevel@tonic-gate }
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate /* execute a command, producing -n and -v output as necessary */
9617c478bd9Sstevel@tonic-gate static void
9627c478bd9Sstevel@tonic-gate docmd(struct opts *opts, const char *msg, const char *cmd,
9637c478bd9Sstevel@tonic-gate     const char *arg1, const char *arg2, const char *arg3)
9647c478bd9Sstevel@tonic-gate {
9657c478bd9Sstevel@tonic-gate 	int pid;
9667c478bd9Sstevel@tonic-gate 	int errpipe[2];
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	/* print info about command if necessary */
9697c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "vn")) {
9707c478bd9Sstevel@tonic-gate 		const char *simplecmd;
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 		if ((simplecmd = strrchr(cmd, '/')) == NULL)
9737c478bd9Sstevel@tonic-gate 			simplecmd = cmd;
9747c478bd9Sstevel@tonic-gate 		else
9757c478bd9Sstevel@tonic-gate 			simplecmd++;
9767c478bd9Sstevel@tonic-gate 		(void) out("%s", simplecmd);
9777c478bd9Sstevel@tonic-gate 		if (arg1)
9787c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg1);
9797c478bd9Sstevel@tonic-gate 		if (arg2)
9807c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg2);
9817c478bd9Sstevel@tonic-gate 		if (arg3)
9827c478bd9Sstevel@tonic-gate 			(void) out(" %s", arg3);
9837c478bd9Sstevel@tonic-gate 		if (msg)
9847c478bd9Sstevel@tonic-gate 			(void) out(" # %s", msg);
9857c478bd9Sstevel@tonic-gate 		(void) out("\n");
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	if (opts_count(opts, "n"))
9897c478bd9Sstevel@tonic-gate 		return;		/* -n means don't really do it */
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	/*
9927c478bd9Sstevel@tonic-gate 	 * run the cmd and see if it failed.  this function is *not* a
9937c478bd9Sstevel@tonic-gate 	 * generic command runner -- we depend on some knowledge we
9947c478bd9Sstevel@tonic-gate 	 * have about the commands we run.  first of all, we expect
9957c478bd9Sstevel@tonic-gate 	 * errors to spew something to stderr, and that something is
9967c478bd9Sstevel@tonic-gate 	 * typically short enough to fit into a pipe so we can wait()
9977c478bd9Sstevel@tonic-gate 	 * for the command to complete and then fetch the error text
9987c478bd9Sstevel@tonic-gate 	 * from the pipe.  we also expect the exit codes to make sense.
9997c478bd9Sstevel@tonic-gate 	 * notice also that we only allow a command name which is an
10007c478bd9Sstevel@tonic-gate 	 * absolute pathname, and two args must be supplied (the
10017c478bd9Sstevel@tonic-gate 	 * second may be NULL, or they may both be NULL).
10027c478bd9Sstevel@tonic-gate 	 */
10037c478bd9Sstevel@tonic-gate 	if (pipe(errpipe) < 0)
10047c478bd9Sstevel@tonic-gate 		err(EF_SYS, "pipe");
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	if ((pid = fork()) < 0)
10077c478bd9Sstevel@tonic-gate 		err(EF_SYS, "fork");
10087c478bd9Sstevel@tonic-gate 	else if (pid) {
10097c478bd9Sstevel@tonic-gate 		int wstat;
10107c478bd9Sstevel@tonic-gate 		int count;
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		/* parent */
10137c478bd9Sstevel@tonic-gate 		(void) close(errpipe[1]);
10147c478bd9Sstevel@tonic-gate 		if (waitpid(pid, &wstat, 0) < 0)
10157c478bd9Sstevel@tonic-gate 			err(EF_SYS, "waitpid");
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 		/* check for stderr output */
10187c478bd9Sstevel@tonic-gate 		if (ioctl(errpipe[0], FIONREAD, &count) >= 0 && count) {
10197c478bd9Sstevel@tonic-gate 			err(EF_WARN, "command failed: %s%s%s%s%s%s%s",
10207c478bd9Sstevel@tonic-gate 				cmd,
10217c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
10227c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
10237c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
10247c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
10257c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
10267c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
10277c478bd9Sstevel@tonic-gate 			err_fromfd(errpipe[0]);
10287c478bd9Sstevel@tonic-gate 		} else if (WIFSIGNALED(wstat))
10297c478bd9Sstevel@tonic-gate 			err(EF_WARN,
10307c478bd9Sstevel@tonic-gate 			    "command died, signal %d: %s%s%s%s%s%s%s",
10317c478bd9Sstevel@tonic-gate 				WTERMSIG(wstat),
10327c478bd9Sstevel@tonic-gate 				cmd,
10337c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
10347c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
10357c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
10367c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
10377c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
10387c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
10397c478bd9Sstevel@tonic-gate 		else if (WIFEXITED(wstat) && WEXITSTATUS(wstat))
10407c478bd9Sstevel@tonic-gate 			err(EF_WARN,
10417c478bd9Sstevel@tonic-gate 			    "command error, exit %d: %s%s%s%s%s%s%s",
10427c478bd9Sstevel@tonic-gate 				WEXITSTATUS(wstat),
10437c478bd9Sstevel@tonic-gate 				cmd,
10447c478bd9Sstevel@tonic-gate 				(arg1) ? " " : "",
10457c478bd9Sstevel@tonic-gate 				(arg1) ? arg1 : "",
10467c478bd9Sstevel@tonic-gate 				(arg2) ? " " : "",
10477c478bd9Sstevel@tonic-gate 				(arg2) ? arg2 : "",
10487c478bd9Sstevel@tonic-gate 				(arg3) ? " " : "",
10497c478bd9Sstevel@tonic-gate 				(arg3) ? arg3 : "");
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 		(void) close(errpipe[0]);
10527c478bd9Sstevel@tonic-gate 	} else {
10537c478bd9Sstevel@tonic-gate 		/* child */
10547c478bd9Sstevel@tonic-gate 		(void) dup2(errpipe[1], fileno(stderr));
10557c478bd9Sstevel@tonic-gate 		(void) close(errpipe[0]);
10567c478bd9Sstevel@tonic-gate 		(void) execl(cmd, cmd, arg1, arg2, arg3, 0);
10577c478bd9Sstevel@tonic-gate 		perror(cmd);
10587c478bd9Sstevel@tonic-gate 		_exit(1);
10597c478bd9Sstevel@tonic-gate 	}
10607c478bd9Sstevel@tonic-gate }
1061