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