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
50ade2cf0Sdg199075 * Common Development and Distribution License (the "License").
60ade2cf0Sdg199075 * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22e9a193fcSJohn.Zolnowsky@Sun.COM * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23d39070fcSBryan Cantrill * Copyright (c) 2013, Joyent, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate *
257c478bd9Sstevel@tonic-gate * logadm/main.c -- main routines for logadm
267c478bd9Sstevel@tonic-gate *
277c478bd9Sstevel@tonic-gate * this program is 90% argument processing, 10% actions...
287c478bd9Sstevel@tonic-gate */
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <strings.h>
347c478bd9Sstevel@tonic-gate #include <libintl.h>
357c478bd9Sstevel@tonic-gate #include <locale.h>
36fbe4944aSPetr Sumbera #include <fcntl.h>
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/stat.h>
397c478bd9Sstevel@tonic-gate #include <sys/wait.h>
407c478bd9Sstevel@tonic-gate #include <sys/filio.h>
41d39070fcSBryan Cantrill #include <sys/sysmacros.h>
427c478bd9Sstevel@tonic-gate #include <time.h>
43fbe4944aSPetr Sumbera #include <utime.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);
600ade2cf0Sdg199075 static void do_delayed_gzip(const char *lhs, void *rhs, void *arg);
617c478bd9Sstevel@tonic-gate static void expirefiles(struct fn *fnp, struct opts *opts);
627c478bd9Sstevel@tonic-gate static void dorm(struct opts *opts, const char *msg, struct fn *fnp);
637c478bd9Sstevel@tonic-gate static void docmd(struct opts *opts, const char *msg, const char *cmd,
647c478bd9Sstevel@tonic-gate const char *arg1, const char *arg2, const char *arg3);
65fbe4944aSPetr Sumbera static void docopytruncate(struct opts *opts, const char *file,
66fbe4944aSPetr Sumbera const char *file_copy);
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate /* our configuration file, unless otherwise specified by -f */
697c478bd9Sstevel@tonic-gate static char *Default_conffile = "/etc/logadm.conf";
70e9a193fcSJohn.Zolnowsky@Sun.COM /* our timestamps file, unless otherwise specified by -F */
71e9a193fcSJohn.Zolnowsky@Sun.COM static char *Default_timestamps = "/var/logadm/timestamps";
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate /* default pathnames to the commands we invoke */
747c478bd9Sstevel@tonic-gate static char *Sh = "/bin/sh";
757c478bd9Sstevel@tonic-gate static char *Mv = "/bin/mv";
767c478bd9Sstevel@tonic-gate static char *Rm = "/bin/rm";
777c478bd9Sstevel@tonic-gate static char *Touch = "/bin/touch";
787c478bd9Sstevel@tonic-gate static char *Chmod = "/bin/chmod";
797c478bd9Sstevel@tonic-gate static char *Chown = "/bin/chown";
807c478bd9Sstevel@tonic-gate static char *Gzip = "/bin/gzip";
817c478bd9Sstevel@tonic-gate static char *Mkdir = "/bin/mkdir";
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate /* return from time(0), gathered early on to avoid slewed timestamps */
847c478bd9Sstevel@tonic-gate time_t Now;
857c478bd9Sstevel@tonic-gate
867c478bd9Sstevel@tonic-gate /* list of before commands that have been executed */
877c478bd9Sstevel@tonic-gate static struct lut *Beforecmds;
887c478bd9Sstevel@tonic-gate
897c478bd9Sstevel@tonic-gate /* list of after commands to execute before exiting */
907c478bd9Sstevel@tonic-gate static struct lut *Aftercmds;
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate /* list of conffile entry names that are considered "done" */
937c478bd9Sstevel@tonic-gate static struct lut *Donenames;
947c478bd9Sstevel@tonic-gate
950ade2cf0Sdg199075 /* A list of names of files to be gzipped */
960ade2cf0Sdg199075 static struct lut *Gzipnames = NULL;
970ade2cf0Sdg199075
987c478bd9Sstevel@tonic-gate /*
99e9a193fcSJohn.Zolnowsky@Sun.COM * only the "FfhnVv" options are allowed in the first form of this command,
1007c478bd9Sstevel@tonic-gate * so this defines the list of options that are an error in they appear
1017c478bd9Sstevel@tonic-gate * in the first form. In other words, it is not allowed to run logadm
1027c478bd9Sstevel@tonic-gate * with any of these options unless at least one logname is also provided.
1037c478bd9Sstevel@tonic-gate */
104636deb66Sgm149974 #define OPTIONS_NOT_FIRST_FORM "eNrwpPsabcglmoRtzACEST"
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate /* text that we spew with the -h flag */
1077c478bd9Sstevel@tonic-gate #define HELP1 \
1087c478bd9Sstevel@tonic-gate "Usage: logadm [options]\n"\
1097c478bd9Sstevel@tonic-gate " (processes all entries in /etc/logadm.conf or conffile given by -f)\n"\
1107c478bd9Sstevel@tonic-gate " or: logadm [options] logname...\n"\
1117c478bd9Sstevel@tonic-gate " (processes the given lognames)\n"\
1127c478bd9Sstevel@tonic-gate "\n"\
1137c478bd9Sstevel@tonic-gate "General options:\n"\
1147c478bd9Sstevel@tonic-gate " -e mailaddr mail errors to given address\n"\
115e9a193fcSJohn.Zolnowsky@Sun.COM " -F timestamps use timestamps instead of /var/logadm/timestamps\n"\
1167c478bd9Sstevel@tonic-gate " -f conffile use conffile instead of /etc/logadm.conf\n"\
1177c478bd9Sstevel@tonic-gate " -h display help\n"\
1187c478bd9Sstevel@tonic-gate " -N not an error if log file nonexistent\n"\
1197c478bd9Sstevel@tonic-gate " -n show actions, don't perform them\n"\
1207c478bd9Sstevel@tonic-gate " -r remove logname entry from conffile\n"\
1217c478bd9Sstevel@tonic-gate " -V ensure conffile entries exist, correct\n"\
1227c478bd9Sstevel@tonic-gate " -v print info about actions happening\n"\
1237c478bd9Sstevel@tonic-gate " -w entryname write entry to config file\n"\
1247c478bd9Sstevel@tonic-gate "\n"\
1257c478bd9Sstevel@tonic-gate "Options which control when a logfile is rotated:\n"\
1267c478bd9Sstevel@tonic-gate "(default is: -s1b -p1w if no -s or -p)\n"\
1277c478bd9Sstevel@tonic-gate " -p period only rotate if period passed since last rotate\n"\
1287c478bd9Sstevel@tonic-gate " -P timestamp used to store rotation date in conffile\n"\
1297c478bd9Sstevel@tonic-gate " -s size only rotate if given size or greater\n"\
1307c478bd9Sstevel@tonic-gate "\n"
1317c478bd9Sstevel@tonic-gate #define HELP2 \
1327c478bd9Sstevel@tonic-gate "Options which control how a logfile is rotated:\n"\
1337c478bd9Sstevel@tonic-gate "(default is: -t '$file.$n', owner/group/mode taken from log file)\n"\
1347c478bd9Sstevel@tonic-gate " -a cmd execute cmd after taking actions\n"\
1357c478bd9Sstevel@tonic-gate " -b cmd execute cmd before taking actions\n"\
1367c478bd9Sstevel@tonic-gate " -c copy & truncate logfile, don't rename\n"\
1377c478bd9Sstevel@tonic-gate " -g group new empty log file group\n"\
138636deb66Sgm149974 " -l rotate log file with local time rather than UTC\n"\
1397c478bd9Sstevel@tonic-gate " -m mode new empty log file mode\n"\
1407c478bd9Sstevel@tonic-gate " -M cmd execute cmd to rotate the log file\n"\
1417c478bd9Sstevel@tonic-gate " -o owner new empty log file owner\n"\
1427c478bd9Sstevel@tonic-gate " -R cmd run cmd on file after rotate\n"\
1437c478bd9Sstevel@tonic-gate " -t template template for naming old logs\n"\
1447c478bd9Sstevel@tonic-gate " -z count gzip old logs except most recent count\n"\
1457c478bd9Sstevel@tonic-gate "\n"\
1467c478bd9Sstevel@tonic-gate "Options which control the expiration of old logfiles:\n"\
1477c478bd9Sstevel@tonic-gate "(default is: -C10 if no -A, -C, or -S)\n"\
1487c478bd9Sstevel@tonic-gate " -A age expire logs older than age\n"\
1497c478bd9Sstevel@tonic-gate " -C count expire old logs until count remain\n"\
1507c478bd9Sstevel@tonic-gate " -E cmd run cmd on file to expire\n"\
1517c478bd9Sstevel@tonic-gate " -S size expire until space used is below size \n"\
1527c478bd9Sstevel@tonic-gate " -T pattern pattern for finding old logs\n"
1537c478bd9Sstevel@tonic-gate
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate * main -- where it all begins
1567c478bd9Sstevel@tonic-gate */
1577c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1587c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])1597c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
1607c478bd9Sstevel@tonic-gate {
1617c478bd9Sstevel@tonic-gate struct opts *clopts; /* from parsing command line */
1627c478bd9Sstevel@tonic-gate const char *conffile; /* our configuration file */
163e9a193fcSJohn.Zolnowsky@Sun.COM const char *timestamps; /* our timestamps file */
1647c478bd9Sstevel@tonic-gate struct fn_list *lognames; /* list of lognames we're processing */
1657c478bd9Sstevel@tonic-gate struct fn *fnp;
1667c478bd9Sstevel@tonic-gate char *val;
167b493790cSbasabi char *buf;
168e9a193fcSJohn.Zolnowsky@Sun.COM int status;
1697c478bd9Sstevel@tonic-gate
1707c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
1717c478bd9Sstevel@tonic-gate
1727c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1737c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* only used if Makefiles don't define it */
1747c478bd9Sstevel@tonic-gate #endif
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
1777c478bd9Sstevel@tonic-gate
178e9a193fcSJohn.Zolnowsky@Sun.COM /* we only print times into the timestamps file, so make them uniform */
1797c478bd9Sstevel@tonic-gate (void) setlocale(LC_TIME, "C");
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate /* give our name to error routines & skip it for arg parsing */
1827c478bd9Sstevel@tonic-gate err_init(*argv++);
1837c478bd9Sstevel@tonic-gate (void) setlinebuf(stdout);
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate if (putenv("PATH=/bin"))
1867c478bd9Sstevel@tonic-gate err(EF_SYS, "putenv PATH");
1877c478bd9Sstevel@tonic-gate if (putenv("TZ=UTC"))
1887c478bd9Sstevel@tonic-gate err(EF_SYS, "putenv TZ");
1897c478bd9Sstevel@tonic-gate tzset();
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate (void) umask(0);
1927c478bd9Sstevel@tonic-gate
1937c478bd9Sstevel@tonic-gate Now = time(0);
1947c478bd9Sstevel@tonic-gate
1957c478bd9Sstevel@tonic-gate /* check for (undocumented) debugging environment variables */
1967c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_DEFAULT_CONFFILE"))
1977c478bd9Sstevel@tonic-gate Default_conffile = val;
198e9a193fcSJohn.Zolnowsky@Sun.COM if (val = getenv("_LOGADM_DEFAULT_TIMESTAMPS"))
199e9a193fcSJohn.Zolnowsky@Sun.COM Default_timestamps = val;
2007c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_DEBUG"))
2017c478bd9Sstevel@tonic-gate Debug = atoi(val);
2027c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_SH"))
2037c478bd9Sstevel@tonic-gate Sh = val;
2047c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_MV"))
2057c478bd9Sstevel@tonic-gate Mv = val;
2067c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_RM"))
2077c478bd9Sstevel@tonic-gate Rm = val;
2087c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_TOUCH"))
2097c478bd9Sstevel@tonic-gate Touch = val;
2107c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_CHMOD"))
2117c478bd9Sstevel@tonic-gate Chmod = val;
2127c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_CHOWN"))
2137c478bd9Sstevel@tonic-gate Chown = val;
2147c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_GZIP"))
2157c478bd9Sstevel@tonic-gate Gzip = val;
2167c478bd9Sstevel@tonic-gate if (val = getenv("_LOGADM_MKDIR"))
2177c478bd9Sstevel@tonic-gate Mkdir = val;
2187c478bd9Sstevel@tonic-gate
219e9a193fcSJohn.Zolnowsky@Sun.COM opts_init(Opttable, Opttable_cnt);
2207c478bd9Sstevel@tonic-gate
2217c478bd9Sstevel@tonic-gate /* parse command line arguments */
2227c478bd9Sstevel@tonic-gate if (SETJMP)
2237c478bd9Sstevel@tonic-gate usage("bailing out due to command line errors");
2247c478bd9Sstevel@tonic-gate else
225e9a193fcSJohn.Zolnowsky@Sun.COM clopts = opts_parse(NULL, argv, OPTF_CLI);
2267c478bd9Sstevel@tonic-gate
2277c478bd9Sstevel@tonic-gate if (Debug) {
2287c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "command line opts:");
2297c478bd9Sstevel@tonic-gate opts_print(clopts, stderr, NULL);
2307c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate
2337c478bd9Sstevel@tonic-gate /*
2347c478bd9Sstevel@tonic-gate * There are many moods of logadm:
2357c478bd9Sstevel@tonic-gate *
2367c478bd9Sstevel@tonic-gate * 1. "-h" for help was given. We spew a canned help
2377c478bd9Sstevel@tonic-gate * message and exit, regardless of any other options given.
2387c478bd9Sstevel@tonic-gate *
2397c478bd9Sstevel@tonic-gate * 2. "-r" or "-w" asking us to write to the conffile. Lots
2407c478bd9Sstevel@tonic-gate * of argument checking, then we make the change to conffile
2417c478bd9Sstevel@tonic-gate * and exit. (-r processing actually happens in dologname().)
2427c478bd9Sstevel@tonic-gate *
2437c478bd9Sstevel@tonic-gate * 3. "-V" to search/verify the conffile was given. We do
2447c478bd9Sstevel@tonic-gate * the appropriate run through the conffile and exit.
2457c478bd9Sstevel@tonic-gate * (-V processing actually happens in dologname().)
2467c478bd9Sstevel@tonic-gate *
2477c478bd9Sstevel@tonic-gate * 4. No lognames were given, so we're being asked to go through
2487c478bd9Sstevel@tonic-gate * every entry in conffile. We verify that only the options
2497c478bd9Sstevel@tonic-gate * that make sense for this form of the command are present
2507c478bd9Sstevel@tonic-gate * and fall into the main processing loop below.
2517c478bd9Sstevel@tonic-gate *
2527c478bd9Sstevel@tonic-gate * 5. lognames were given, so we fall into the main processing
2537c478bd9Sstevel@tonic-gate * loop below to work our way through them.
2547c478bd9Sstevel@tonic-gate *
2557c478bd9Sstevel@tonic-gate * The last two cases are where the option processing gets more
2567c478bd9Sstevel@tonic-gate * complex. Each time around the main processing loop, we're
2577c478bd9Sstevel@tonic-gate * in one of these cases:
2587c478bd9Sstevel@tonic-gate *
2597c478bd9Sstevel@tonic-gate * A. No cmdargs were found (we're in case 4), the entry
2607c478bd9Sstevel@tonic-gate * in conffile supplies no log file names, so the entry
2617c478bd9Sstevel@tonic-gate * name itself is the logfile name (or names, if it globs
2627c478bd9Sstevel@tonic-gate * to multiple file names).
2637c478bd9Sstevel@tonic-gate *
2647c478bd9Sstevel@tonic-gate * B. No cmdargs were found (we're in case 4), the entry
2657c478bd9Sstevel@tonic-gate * in conffile gives log file names that we then loop
2667c478bd9Sstevel@tonic-gate * through and rotate/expire. In this case, the entry
2677c478bd9Sstevel@tonic-gate * name is specifically NOT one of the log file names.
2687c478bd9Sstevel@tonic-gate *
2697c478bd9Sstevel@tonic-gate * C. We're going through the cmdargs (we're in case 5),
2707c478bd9Sstevel@tonic-gate * the entry in conffile either doesn't exist or it exists
2717c478bd9Sstevel@tonic-gate * but supplies no log file names, so the cmdarg itself
2727c478bd9Sstevel@tonic-gate * is the log file name.
2737c478bd9Sstevel@tonic-gate *
2747c478bd9Sstevel@tonic-gate * D. We're going through the cmdargs (we're in case 5),
2757c478bd9Sstevel@tonic-gate * a matching entry in conffile supplies log file names
2767c478bd9Sstevel@tonic-gate * that we then loop through and rotate/expire. In this
2777c478bd9Sstevel@tonic-gate * case the entry name is specifically NOT one of the log
2787c478bd9Sstevel@tonic-gate * file names.
2797c478bd9Sstevel@tonic-gate *
2807c478bd9Sstevel@tonic-gate * As we're doing all this, any options given on the command line
2817c478bd9Sstevel@tonic-gate * override any found in the conffile, and we apply the defaults
2827c478bd9Sstevel@tonic-gate * for rotation conditions and expiration conditions, etc. at the
2837c478bd9Sstevel@tonic-gate * last opportunity, when we're sure they haven't been overridden
2847c478bd9Sstevel@tonic-gate * by an option somewhere along the way.
2857c478bd9Sstevel@tonic-gate *
2867c478bd9Sstevel@tonic-gate */
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate /* help option overrides anything else */
2897c478bd9Sstevel@tonic-gate if (opts_count(clopts, "h")) {
2907c478bd9Sstevel@tonic-gate (void) fputs(HELP1, stderr);
2917c478bd9Sstevel@tonic-gate (void) fputs(HELP2, stderr);
2927c478bd9Sstevel@tonic-gate err_done(0);
2937c478bd9Sstevel@tonic-gate /*NOTREACHED*/
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate
2967c478bd9Sstevel@tonic-gate /* detect illegal option combinations */
2977c478bd9Sstevel@tonic-gate if (opts_count(clopts, "rwV") > 1)
2987c478bd9Sstevel@tonic-gate usage("Only one of -r, -w, or -V may be used at a time.");
2997c478bd9Sstevel@tonic-gate if (opts_count(clopts, "cM") > 1)
3007c478bd9Sstevel@tonic-gate usage("Only one of -c or -M may be used at a time.");
3017c478bd9Sstevel@tonic-gate
3027c478bd9Sstevel@tonic-gate /* arrange for error output to be mailed if clopts includes -e */
3037c478bd9Sstevel@tonic-gate if (opts_count(clopts, "e"))
3047c478bd9Sstevel@tonic-gate err_mailto(opts_optarg(clopts, "e"));
3057c478bd9Sstevel@tonic-gate
306e9a193fcSJohn.Zolnowsky@Sun.COM /* this implements the default conffile and timestamps */
3077c478bd9Sstevel@tonic-gate if ((conffile = opts_optarg(clopts, "f")) == NULL)
3087c478bd9Sstevel@tonic-gate conffile = Default_conffile;
309e9a193fcSJohn.Zolnowsky@Sun.COM if ((timestamps = opts_optarg(clopts, "F")) == NULL)
310e9a193fcSJohn.Zolnowsky@Sun.COM timestamps = Default_timestamps;
3117c478bd9Sstevel@tonic-gate if (opts_count(clopts, "v"))
3127c478bd9Sstevel@tonic-gate (void) out("# loading %s\n", conffile);
313e9a193fcSJohn.Zolnowsky@Sun.COM status = conf_open(conffile, timestamps, clopts);
314e9a193fcSJohn.Zolnowsky@Sun.COM if (!status && opts_count(clopts, "V"))
315e9a193fcSJohn.Zolnowsky@Sun.COM err_done(0);
3167c478bd9Sstevel@tonic-gate
3177c478bd9Sstevel@tonic-gate /* handle conffile write option */
3187c478bd9Sstevel@tonic-gate if (opts_count(clopts, "w")) {
3197c478bd9Sstevel@tonic-gate if (Debug)
3207c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3217c478bd9Sstevel@tonic-gate "main: add/replace conffile entry: <%s>\n",
3227c478bd9Sstevel@tonic-gate opts_optarg(clopts, "w"));
3237c478bd9Sstevel@tonic-gate conf_replace(opts_optarg(clopts, "w"), clopts);
3247c478bd9Sstevel@tonic-gate conf_close(clopts);
3257c478bd9Sstevel@tonic-gate err_done(0);
3267c478bd9Sstevel@tonic-gate /*NOTREACHED*/
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate
3297c478bd9Sstevel@tonic-gate /*
3307c478bd9Sstevel@tonic-gate * lognames is either a list supplied on the command line,
3317c478bd9Sstevel@tonic-gate * or every entry in the conffile if none were supplied.
3327c478bd9Sstevel@tonic-gate */
3337c478bd9Sstevel@tonic-gate lognames = opts_cmdargs(clopts);
3347c478bd9Sstevel@tonic-gate if (fn_list_empty(lognames)) {
3357c478bd9Sstevel@tonic-gate /*
3367c478bd9Sstevel@tonic-gate * being asked to do all entries in conffile
3377c478bd9Sstevel@tonic-gate *
3387c478bd9Sstevel@tonic-gate * check to see if any options were given that only
3397c478bd9Sstevel@tonic-gate * make sense when lognames are given specifically
3407c478bd9Sstevel@tonic-gate * on the command line.
3417c478bd9Sstevel@tonic-gate */
3427c478bd9Sstevel@tonic-gate if (opts_count(clopts, OPTIONS_NOT_FIRST_FORM))
3437c478bd9Sstevel@tonic-gate usage("some options require logname argument");
3447c478bd9Sstevel@tonic-gate if (Debug)
3457c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3467c478bd9Sstevel@tonic-gate "main: run all entries in conffile\n");
3477c478bd9Sstevel@tonic-gate lognames = conf_entries();
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate
3507c478bd9Sstevel@tonic-gate /* foreach logname... */
3517c478bd9Sstevel@tonic-gate fn_list_rewind(lognames);
3527c478bd9Sstevel@tonic-gate while ((fnp = fn_list_next(lognames)) != NULL) {
353b493790cSbasabi buf = fn_s(fnp);
354b493790cSbasabi if (buf != NULL && lut_lookup(Donenames, buf) != NULL) {
3557c478bd9Sstevel@tonic-gate if (Debug)
3567c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3577c478bd9Sstevel@tonic-gate "main: logname already done: <%s>\n",
358b493790cSbasabi buf);
3597c478bd9Sstevel@tonic-gate continue;
3607c478bd9Sstevel@tonic-gate }
361b493790cSbasabi if (buf != NULL && SETJMP)
3627c478bd9Sstevel@tonic-gate err(EF_FILE, "bailing out on logname \"%s\" "
363b493790cSbasabi "due to errors", buf);
3647c478bd9Sstevel@tonic-gate else
3657c478bd9Sstevel@tonic-gate dologname(fnp, clopts);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate /* execute any after commands */
3697c478bd9Sstevel@tonic-gate lut_walk(Aftercmds, doaftercmd, clopts);
3707c478bd9Sstevel@tonic-gate
3710ade2cf0Sdg199075 /* execute any gzip commands */
3720ade2cf0Sdg199075 lut_walk(Gzipnames, do_delayed_gzip, clopts);
3730ade2cf0Sdg199075
3747c478bd9Sstevel@tonic-gate /* write out any conffile changes */
3757c478bd9Sstevel@tonic-gate conf_close(clopts);
3767c478bd9Sstevel@tonic-gate
3777c478bd9Sstevel@tonic-gate err_done(0);
3787c478bd9Sstevel@tonic-gate /*NOTREACHED*/
3797c478bd9Sstevel@tonic-gate return (0); /* for lint's little mind */
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate
3827c478bd9Sstevel@tonic-gate /* spew a message, then a usage message, then exit */
3837c478bd9Sstevel@tonic-gate static void
usage(const char * msg)3847c478bd9Sstevel@tonic-gate usage(const char *msg)
3857c478bd9Sstevel@tonic-gate {
3867c478bd9Sstevel@tonic-gate if (msg)
3877c478bd9Sstevel@tonic-gate err(0, "%s\nUse \"logadm -h\" for help.", msg);
3887c478bd9Sstevel@tonic-gate else
3897c478bd9Sstevel@tonic-gate err(EF_RAW, "Use \"logadm -h\" for help.\n");
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate
3927c478bd9Sstevel@tonic-gate /* helper function used by doaftercmd() to join mail addrs with commas */
3937c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
3947c478bd9Sstevel@tonic-gate static void
commajoin(const char * lhs,void * rhs,void * arg)3957c478bd9Sstevel@tonic-gate commajoin(const char *lhs, void *rhs, void *arg)
3967c478bd9Sstevel@tonic-gate {
3977c478bd9Sstevel@tonic-gate struct fn *fnp = (struct fn *)arg;
398b493790cSbasabi char *buf;
3997c478bd9Sstevel@tonic-gate
400b493790cSbasabi buf = fn_s(fnp);
401b493790cSbasabi if (buf != NULL && *buf)
4027c478bd9Sstevel@tonic-gate fn_putc(fnp, ',');
4037c478bd9Sstevel@tonic-gate fn_puts(fnp, lhs);
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate
4067c478bd9Sstevel@tonic-gate /* helper function used by main() to run "after" commands */
4077c478bd9Sstevel@tonic-gate static void
doaftercmd(const char * lhs,void * rhs,void * arg)4087c478bd9Sstevel@tonic-gate doaftercmd(const char *lhs, void *rhs, void *arg)
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate struct opts *opts = (struct opts *)arg;
4117c478bd9Sstevel@tonic-gate struct lut *addrs = (struct lut *)rhs;
4127c478bd9Sstevel@tonic-gate
4137c478bd9Sstevel@tonic-gate if (addrs) {
4147c478bd9Sstevel@tonic-gate struct fn *fnp = fn_new(NULL);
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate /*
4177c478bd9Sstevel@tonic-gate * addrs contains list of email addrs that should get
4187c478bd9Sstevel@tonic-gate * the error output when this after command is executed.
4197c478bd9Sstevel@tonic-gate */
4207c478bd9Sstevel@tonic-gate lut_walk(addrs, commajoin, fnp);
4217c478bd9Sstevel@tonic-gate err_mailto(fn_s(fnp));
4227c478bd9Sstevel@tonic-gate }
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gate docmd(opts, "-a cmd", Sh, "-c", lhs, NULL);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate
4270ade2cf0Sdg199075 /* perform delayed gzip */
4280ade2cf0Sdg199075
4290ade2cf0Sdg199075 static void
do_delayed_gzip(const char * lhs,void * rhs,void * arg)4300ade2cf0Sdg199075 do_delayed_gzip(const char *lhs, void *rhs, void *arg)
4310ade2cf0Sdg199075 {
4320ade2cf0Sdg199075 struct opts *opts = (struct opts *)arg;
4330ade2cf0Sdg199075
4340ade2cf0Sdg199075 if (rhs == NULL) {
4350ade2cf0Sdg199075 if (Debug) {
4360ade2cf0Sdg199075 (void) fprintf(stderr, "do_delayed_gzip: not gzipping "
4370ade2cf0Sdg199075 "expired file <%s>\n", lhs);
4380ade2cf0Sdg199075 }
4390ade2cf0Sdg199075 return;
4400ade2cf0Sdg199075 }
4410ade2cf0Sdg199075 docmd(opts, "compress old log (-z flag)", Gzip, "-f", lhs, NULL);
4420ade2cf0Sdg199075 }
4430ade2cf0Sdg199075
4440ade2cf0Sdg199075
4457c478bd9Sstevel@tonic-gate /* main logname processing */
4467c478bd9Sstevel@tonic-gate static void
dologname(struct fn * fnp,struct opts * clopts)4477c478bd9Sstevel@tonic-gate dologname(struct fn *fnp, struct opts *clopts)
4487c478bd9Sstevel@tonic-gate {
4497c478bd9Sstevel@tonic-gate const char *logname = fn_s(fnp);
4507c478bd9Sstevel@tonic-gate struct opts *cfopts;
4517c478bd9Sstevel@tonic-gate struct opts *allopts;
4527c478bd9Sstevel@tonic-gate struct fn_list *logfiles;
4537c478bd9Sstevel@tonic-gate struct fn_list *globbedfiles;
4547c478bd9Sstevel@tonic-gate struct fn *nextfnp;
4557c478bd9Sstevel@tonic-gate
4567c478bd9Sstevel@tonic-gate /* look up options set by config file */
4577c478bd9Sstevel@tonic-gate cfopts = conf_opts(logname);
4587c478bd9Sstevel@tonic-gate
4597c478bd9Sstevel@tonic-gate if (opts_count(clopts, "v"))
4607c478bd9Sstevel@tonic-gate (void) out("# processing logname: %s\n", logname);
4617c478bd9Sstevel@tonic-gate
4627c478bd9Sstevel@tonic-gate if (Debug) {
463b493790cSbasabi if (logname != NULL)
464b493790cSbasabi (void) fprintf(stderr, "dologname: logname <%s>\n",
465b493790cSbasabi logname);
4667c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "conffile opts:");
4677c478bd9Sstevel@tonic-gate opts_print(cfopts, stderr, NULL);
4687c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate
4717c478bd9Sstevel@tonic-gate /* handle conffile lookup option */
4727c478bd9Sstevel@tonic-gate if (opts_count(clopts, "V")) {
4737c478bd9Sstevel@tonic-gate /* lookup an entry in conffile */
4747c478bd9Sstevel@tonic-gate if (Debug)
4757c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4767c478bd9Sstevel@tonic-gate "dologname: lookup conffile entry\n");
4777c478bd9Sstevel@tonic-gate if (conf_lookup(logname)) {
4787c478bd9Sstevel@tonic-gate opts_printword(logname, stdout);
4797c478bd9Sstevel@tonic-gate opts_print(cfopts, stdout, NULL);
4807c478bd9Sstevel@tonic-gate (void) out("\n");
4817c478bd9Sstevel@tonic-gate } else
4827c478bd9Sstevel@tonic-gate err_exitcode(1);
4837c478bd9Sstevel@tonic-gate return;
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate
4867c478bd9Sstevel@tonic-gate /* handle conffile removal option */
4877c478bd9Sstevel@tonic-gate if (opts_count(clopts, "r")) {
4887c478bd9Sstevel@tonic-gate if (Debug)
4897c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4907c478bd9Sstevel@tonic-gate "dologname: remove conffile entry\n");
4917c478bd9Sstevel@tonic-gate if (conf_lookup(logname))
4927c478bd9Sstevel@tonic-gate conf_replace(logname, NULL);
4937c478bd9Sstevel@tonic-gate else
4947c478bd9Sstevel@tonic-gate err_exitcode(1);
4957c478bd9Sstevel@tonic-gate return;
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate
4987c478bd9Sstevel@tonic-gate /* generate combined options */
4997c478bd9Sstevel@tonic-gate allopts = opts_merge(cfopts, clopts);
5007c478bd9Sstevel@tonic-gate
5017c478bd9Sstevel@tonic-gate /* arrange for error output to be mailed if allopts includes -e */
5027c478bd9Sstevel@tonic-gate if (opts_count(allopts, "e"))
5037c478bd9Sstevel@tonic-gate err_mailto(opts_optarg(allopts, "e"));
5047c478bd9Sstevel@tonic-gate else
5057c478bd9Sstevel@tonic-gate err_mailto(NULL);
5067c478bd9Sstevel@tonic-gate
5077c478bd9Sstevel@tonic-gate /* this implements the default rotation rules */
5087c478bd9Sstevel@tonic-gate if (opts_count(allopts, "sp") == 0) {
5097c478bd9Sstevel@tonic-gate if (opts_count(clopts, "v"))
5107c478bd9Sstevel@tonic-gate (void) out(
5117c478bd9Sstevel@tonic-gate "# using default rotate rules: -s1b -p1w\n");
5127c478bd9Sstevel@tonic-gate (void) opts_set(allopts, "s", "1b");
5137c478bd9Sstevel@tonic-gate (void) opts_set(allopts, "p", "1w");
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate
5167c478bd9Sstevel@tonic-gate /* this implements the default expiration rules */
5177c478bd9Sstevel@tonic-gate if (opts_count(allopts, "ACS") == 0) {
5187c478bd9Sstevel@tonic-gate if (opts_count(clopts, "v"))
5197c478bd9Sstevel@tonic-gate (void) out("# using default expire rule: -C10\n");
5207c478bd9Sstevel@tonic-gate (void) opts_set(allopts, "C", "10");
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate /* this implements the default template */
5247c478bd9Sstevel@tonic-gate if (opts_count(allopts, "t") == 0) {
5257c478bd9Sstevel@tonic-gate if (opts_count(clopts, "v"))
5267c478bd9Sstevel@tonic-gate (void) out("# using default template: $file.$n\n");
5277c478bd9Sstevel@tonic-gate (void) opts_set(allopts, "t", "$file.$n");
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate if (Debug) {
5317c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "merged opts:");
5327c478bd9Sstevel@tonic-gate opts_print(allopts, stderr, NULL);
5337c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate
5367c478bd9Sstevel@tonic-gate /*
5377c478bd9Sstevel@tonic-gate * if the conffile entry supplied log file names, then
5387c478bd9Sstevel@tonic-gate * logname is NOT one of the log file names (it was just
5397c478bd9Sstevel@tonic-gate * the entry name in conffile).
5407c478bd9Sstevel@tonic-gate */
5417c478bd9Sstevel@tonic-gate logfiles = opts_cmdargs(cfopts);
5427c478bd9Sstevel@tonic-gate if (Debug) {
543b493790cSbasabi char *buf;
5447c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "dologname: logfiles from cfopts:\n");
5457c478bd9Sstevel@tonic-gate fn_list_rewind(logfiles);
5467c478bd9Sstevel@tonic-gate while ((nextfnp = fn_list_next(logfiles)) != NULL)
547b493790cSbasabi buf = fn_s(nextfnp);
548b493790cSbasabi if (buf != NULL)
549b493790cSbasabi (void) fprintf(stderr, " <%s>\n", buf);
5507c478bd9Sstevel@tonic-gate }
5517c478bd9Sstevel@tonic-gate if (fn_list_empty(logfiles))
5527c478bd9Sstevel@tonic-gate globbedfiles = glob_glob(fnp);
5537c478bd9Sstevel@tonic-gate else
5547c478bd9Sstevel@tonic-gate globbedfiles = glob_glob_list(logfiles);
5557c478bd9Sstevel@tonic-gate
5567c478bd9Sstevel@tonic-gate /* go through the list produced by glob expansion */
5577c478bd9Sstevel@tonic-gate fn_list_rewind(globbedfiles);
5587c478bd9Sstevel@tonic-gate while ((nextfnp = fn_list_next(globbedfiles)) != NULL)
5597c478bd9Sstevel@tonic-gate if (rotatelog(nextfnp, allopts))
5607c478bd9Sstevel@tonic-gate expirefiles(nextfnp, allopts);
5617c478bd9Sstevel@tonic-gate
5627c478bd9Sstevel@tonic-gate fn_list_free(globbedfiles);
5637c478bd9Sstevel@tonic-gate opts_free(allopts);
5647c478bd9Sstevel@tonic-gate }
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate
5677c478bd9Sstevel@tonic-gate /* absurdly long buffer lengths for holding user/group/mode strings */
5687c478bd9Sstevel@tonic-gate #define TIMESTRMAX 100
5697c478bd9Sstevel@tonic-gate #define MAXATTR 100
5707c478bd9Sstevel@tonic-gate
5717c478bd9Sstevel@tonic-gate /* rotate a log file if necessary, returns true if ok to go on to expire step */
5727c478bd9Sstevel@tonic-gate static boolean_t
rotatelog(struct fn * fnp,struct opts * opts)5737c478bd9Sstevel@tonic-gate rotatelog(struct fn *fnp, struct opts *opts)
5747c478bd9Sstevel@tonic-gate {
5757c478bd9Sstevel@tonic-gate char *fname = fn_s(fnp);
5767c478bd9Sstevel@tonic-gate struct stat stbuf;
5777c478bd9Sstevel@tonic-gate char nowstr[TIMESTRMAX];
5787c478bd9Sstevel@tonic-gate struct fn *recentlog = fn_new(NULL); /* for -R cmd */
5797c478bd9Sstevel@tonic-gate char ownerbuf[MAXATTR];
5807c478bd9Sstevel@tonic-gate char groupbuf[MAXATTR];
5817c478bd9Sstevel@tonic-gate char modebuf[MAXATTR];
5827c478bd9Sstevel@tonic-gate const char *owner;
5837c478bd9Sstevel@tonic-gate const char *group;
5847c478bd9Sstevel@tonic-gate const char *mode;
5857c478bd9Sstevel@tonic-gate
586b493790cSbasabi if (Debug && fname != NULL)
5877c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "rotatelog: fname <%s>\n", fname);
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate if (opts_count(opts, "p") && opts_optarg_int(opts, "p") == OPTP_NEVER)
5907c478bd9Sstevel@tonic-gate return (B_TRUE); /* "-p never" forced no rotate */
5917c478bd9Sstevel@tonic-gate
5927c478bd9Sstevel@tonic-gate /* prepare the keywords */
5937c478bd9Sstevel@tonic-gate kw_init(fnp, NULL);
5947c478bd9Sstevel@tonic-gate if (Debug > 1) {
5957c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "rotatelog keywords:\n");
5967c478bd9Sstevel@tonic-gate kw_print(stderr);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate
5997c478bd9Sstevel@tonic-gate if (lstat(fname, &stbuf) < 0) {
6007c478bd9Sstevel@tonic-gate if (opts_count(opts, "N"))
6017c478bd9Sstevel@tonic-gate return (1);
6027c478bd9Sstevel@tonic-gate err(EF_WARN|EF_SYS, "%s", fname);
6037c478bd9Sstevel@tonic-gate return (B_FALSE);
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate
6067c478bd9Sstevel@tonic-gate if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
6077c478bd9Sstevel@tonic-gate err(EF_WARN, "%s is a symlink", fname);
6087c478bd9Sstevel@tonic-gate return (B_FALSE);
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
6127c478bd9Sstevel@tonic-gate err(EF_WARN, "%s is not a regular file", fname);
6137c478bd9Sstevel@tonic-gate return (B_FALSE);
6147c478bd9Sstevel@tonic-gate }
6157c478bd9Sstevel@tonic-gate
6164e93ab4aSmh138676 /* even if size condition is not met, this entry is "done" */
6174e93ab4aSmh138676 if (opts_count(opts, "s") &&
6184e93ab4aSmh138676 stbuf.st_size < opts_optarg_int(opts, "s")) {
6194e93ab4aSmh138676 Donenames = lut_add(Donenames, fname, "1");
6207c478bd9Sstevel@tonic-gate return (B_TRUE);
6214e93ab4aSmh138676 }
6227c478bd9Sstevel@tonic-gate
6237c478bd9Sstevel@tonic-gate /* see if age condition is present, and return if not met */
6247c478bd9Sstevel@tonic-gate if (opts_count(opts, "p")) {
625404720a7Sbasabi off_t when = opts_optarg_int(opts, "p");
6267c478bd9Sstevel@tonic-gate struct opts *cfopts;
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate /* unless rotate forced by "-p now", see if period has passed */
6297c478bd9Sstevel@tonic-gate if (when != OPTP_NOW) {
6307c478bd9Sstevel@tonic-gate /*
6317c478bd9Sstevel@tonic-gate * "when" holds the number of seconds that must have
6327c478bd9Sstevel@tonic-gate * passed since the last time this log was rotated.
6337c478bd9Sstevel@tonic-gate * of course, running logadm can take a little time
6347c478bd9Sstevel@tonic-gate * (typically a second or two, but longer if the
6357c478bd9Sstevel@tonic-gate * conffile has lots of stuff in it) and that amount
6367c478bd9Sstevel@tonic-gate * of time is variable, depending on system load, etc.
6377c478bd9Sstevel@tonic-gate * so we want to allow a little "slop" in the value of
6387c478bd9Sstevel@tonic-gate * "when". this way, if a log should be rotated every
6397c478bd9Sstevel@tonic-gate * week, and the number of seconds passed is really a
6407c478bd9Sstevel@tonic-gate * few seconds short of a week, we'll go ahead and
6417c478bd9Sstevel@tonic-gate * rotate the log as expected.
6427c478bd9Sstevel@tonic-gate *
6437c478bd9Sstevel@tonic-gate */
6447c478bd9Sstevel@tonic-gate if (when >= 60 * 60)
6457c478bd9Sstevel@tonic-gate when -= 59;
6467c478bd9Sstevel@tonic-gate
6477c478bd9Sstevel@tonic-gate /*
6487c478bd9Sstevel@tonic-gate * last rotation is recorded as argument to -P,
6497c478bd9Sstevel@tonic-gate * but if logname isn't the same as log file name
6507c478bd9Sstevel@tonic-gate * then the timestamp would be recorded on a
651*34969e74SJerry Jelinek * separate line in the timestamp file. so if we
6527c478bd9Sstevel@tonic-gate * haven't seen a -P already, we check to see if
6537c478bd9Sstevel@tonic-gate * it is part of a specific entry for the log
6547c478bd9Sstevel@tonic-gate * file name. this handles the case where the
6557c478bd9Sstevel@tonic-gate * logname is "apache", it supplies a log file
6567c478bd9Sstevel@tonic-gate * name like "/var/apache/logs/[a-z]*_log",
6577c478bd9Sstevel@tonic-gate * which expands to multiple file names. if one
6587c478bd9Sstevel@tonic-gate * of the file names is "/var/apache/logs/access_log"
6597c478bd9Sstevel@tonic-gate * the the -P will be attached to a line with that
660*34969e74SJerry Jelinek * logname in the timestamp file.
6617c478bd9Sstevel@tonic-gate */
6627c478bd9Sstevel@tonic-gate if (opts_count(opts, "P")) {
663404720a7Sbasabi off_t last = opts_optarg_int(opts, "P");
6647c478bd9Sstevel@tonic-gate
6657c478bd9Sstevel@tonic-gate /* return if not enough time has passed */
6667c478bd9Sstevel@tonic-gate if (Now - last < when)
6677c478bd9Sstevel@tonic-gate return (B_TRUE);
6687c478bd9Sstevel@tonic-gate } else if ((cfopts = conf_opts(fname)) != NULL &&
6697c478bd9Sstevel@tonic-gate opts_count(cfopts, "P")) {
670404720a7Sbasabi off_t last = opts_optarg_int(cfopts, "P");
6717c478bd9Sstevel@tonic-gate
6727c478bd9Sstevel@tonic-gate /*
6737c478bd9Sstevel@tonic-gate * just checking this means this entry
6747c478bd9Sstevel@tonic-gate * is now "done" if we're going through
6757c478bd9Sstevel@tonic-gate * the entire conffile
6767c478bd9Sstevel@tonic-gate */
6777c478bd9Sstevel@tonic-gate Donenames = lut_add(Donenames, fname, "1");
6787c478bd9Sstevel@tonic-gate
6797c478bd9Sstevel@tonic-gate /* return if not enough time has passed */
6807c478bd9Sstevel@tonic-gate if (Now - last < when)
6817c478bd9Sstevel@tonic-gate return (B_TRUE);
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate }
6847c478bd9Sstevel@tonic-gate }
6857c478bd9Sstevel@tonic-gate
6867c478bd9Sstevel@tonic-gate if (Debug)
6877c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "rotatelog: conditions met\n");
688636deb66Sgm149974 if (opts_count(opts, "l")) {
689636deb66Sgm149974 /* Change the time zone to local time zone */
690636deb66Sgm149974 if (putenv("TZ="))
691636deb66Sgm149974 err(EF_SYS, "putenv TZ");
692636deb66Sgm149974 tzset();
693636deb66Sgm149974 Now = time(0);
6947c478bd9Sstevel@tonic-gate
6957c478bd9Sstevel@tonic-gate /* rename the log file */
6967c478bd9Sstevel@tonic-gate rotateto(fnp, opts, 0, recentlog, B_FALSE);
6977c478bd9Sstevel@tonic-gate
698636deb66Sgm149974 /* Change the time zone to UTC */
699636deb66Sgm149974 if (putenv("TZ=UTC"))
700636deb66Sgm149974 err(EF_SYS, "putenv TZ");
701636deb66Sgm149974 tzset();
702636deb66Sgm149974 Now = time(0);
703636deb66Sgm149974 } else {
704636deb66Sgm149974 /* rename the log file */
705636deb66Sgm149974 rotateto(fnp, opts, 0, recentlog, B_FALSE);
706636deb66Sgm149974 }
707636deb66Sgm149974
7087c478bd9Sstevel@tonic-gate /* determine owner, group, mode for empty log file */
7097c478bd9Sstevel@tonic-gate if (opts_count(opts, "o"))
7107c478bd9Sstevel@tonic-gate (void) strlcpy(ownerbuf, opts_optarg(opts, "o"), MAXATTR);
7117c478bd9Sstevel@tonic-gate else {
7127c478bd9Sstevel@tonic-gate (void) snprintf(ownerbuf, MAXATTR, "%ld", stbuf.st_uid);
7137c478bd9Sstevel@tonic-gate }
7147c478bd9Sstevel@tonic-gate owner = ownerbuf;
7157c478bd9Sstevel@tonic-gate if (opts_count(opts, "g"))
7167c478bd9Sstevel@tonic-gate group = opts_optarg(opts, "g");
7177c478bd9Sstevel@tonic-gate else {
7187c478bd9Sstevel@tonic-gate (void) snprintf(groupbuf, MAXATTR, "%ld", stbuf.st_gid);
7197c478bd9Sstevel@tonic-gate group = groupbuf;
7207c478bd9Sstevel@tonic-gate }
7217c478bd9Sstevel@tonic-gate (void) strlcat(ownerbuf, ":", MAXATTR - strlen(ownerbuf));
7227c478bd9Sstevel@tonic-gate (void) strlcat(ownerbuf, group, MAXATTR - strlen(ownerbuf));
7237c478bd9Sstevel@tonic-gate if (opts_count(opts, "m"))
7247c478bd9Sstevel@tonic-gate mode = opts_optarg(opts, "m");
7257c478bd9Sstevel@tonic-gate else {
7267c478bd9Sstevel@tonic-gate (void) snprintf(modebuf, MAXATTR,
7277c478bd9Sstevel@tonic-gate "%03lo", stbuf.st_mode & 0777);
7287c478bd9Sstevel@tonic-gate mode = modebuf;
7297c478bd9Sstevel@tonic-gate }
7307c478bd9Sstevel@tonic-gate
7317c478bd9Sstevel@tonic-gate /* create the empty log file */
7327c478bd9Sstevel@tonic-gate docmd(opts, NULL, Touch, fname, NULL, NULL);
7337c478bd9Sstevel@tonic-gate docmd(opts, NULL, Chown, owner, fname, NULL);
7347c478bd9Sstevel@tonic-gate docmd(opts, NULL, Chmod, mode, fname, NULL);
7357c478bd9Sstevel@tonic-gate
7367c478bd9Sstevel@tonic-gate /* execute post-rotation command */
7377c478bd9Sstevel@tonic-gate if (opts_count(opts, "R")) {
7387c478bd9Sstevel@tonic-gate struct fn *rawcmd = fn_new(opts_optarg(opts, "R"));
7397c478bd9Sstevel@tonic-gate struct fn *cmd = fn_new(NULL);
7407c478bd9Sstevel@tonic-gate
7417c478bd9Sstevel@tonic-gate kw_init(recentlog, NULL);
7427c478bd9Sstevel@tonic-gate (void) kw_expand(rawcmd, cmd, 0, B_FALSE);
7437c478bd9Sstevel@tonic-gate docmd(opts, "-R cmd", Sh, "-c", fn_s(cmd), NULL);
7447c478bd9Sstevel@tonic-gate fn_free(rawcmd);
7457c478bd9Sstevel@tonic-gate fn_free(cmd);
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate fn_free(recentlog);
7487c478bd9Sstevel@tonic-gate
7497c478bd9Sstevel@tonic-gate /*
7507c478bd9Sstevel@tonic-gate * add "after" command to list of after commands. we also record
7517c478bd9Sstevel@tonic-gate * the email address, if any, where the error output of the after
7527c478bd9Sstevel@tonic-gate * command should be sent. if the after command is already on
7537c478bd9Sstevel@tonic-gate * our list, add the email addr to the list the email addrs for
7547c478bd9Sstevel@tonic-gate * that command (the after command will only be executed once,
7557c478bd9Sstevel@tonic-gate * so the error output gets mailed to every address we've come
7567c478bd9Sstevel@tonic-gate * across associated with this command).
7577c478bd9Sstevel@tonic-gate */
7587c478bd9Sstevel@tonic-gate if (opts_count(opts, "a")) {
7597c478bd9Sstevel@tonic-gate const char *cmd = opts_optarg(opts, "a");
7607c478bd9Sstevel@tonic-gate struct lut *addrs = (struct lut *)lut_lookup(Aftercmds, cmd);
7617c478bd9Sstevel@tonic-gate if (opts_count(opts, "e"))
7627c478bd9Sstevel@tonic-gate addrs = lut_add(addrs, opts_optarg(opts, "e"), NULL);
7637c478bd9Sstevel@tonic-gate Aftercmds = lut_add(Aftercmds, opts_optarg(opts, "a"), addrs);
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate
7667c478bd9Sstevel@tonic-gate /* record the rotation date */
7677c478bd9Sstevel@tonic-gate (void) strftime(nowstr, sizeof (nowstr),
7687c478bd9Sstevel@tonic-gate "%a %b %e %T %Y", gmtime(&Now));
769b493790cSbasabi if (opts_count(opts, "v") && fname != NULL)
7707c478bd9Sstevel@tonic-gate (void) out("# recording rotation date %s for %s\n",
7717c478bd9Sstevel@tonic-gate nowstr, fname);
7727c478bd9Sstevel@tonic-gate conf_set(fname, "P", STRDUP(nowstr));
7737c478bd9Sstevel@tonic-gate Donenames = lut_add(Donenames, fname, "1");
7747c478bd9Sstevel@tonic-gate return (B_TRUE);
7757c478bd9Sstevel@tonic-gate }
7767c478bd9Sstevel@tonic-gate
7777c478bd9Sstevel@tonic-gate /* rotate files "up" according to current template */
7787c478bd9Sstevel@tonic-gate static void
rotateto(struct fn * fnp,struct opts * opts,int n,struct fn * recentlog,boolean_t isgz)7797c478bd9Sstevel@tonic-gate rotateto(struct fn *fnp, struct opts *opts, int n, struct fn *recentlog,
7807c478bd9Sstevel@tonic-gate boolean_t isgz)
7817c478bd9Sstevel@tonic-gate {
7827c478bd9Sstevel@tonic-gate struct fn *template = fn_new(opts_optarg(opts, "t"));
7837c478bd9Sstevel@tonic-gate struct fn *newfile = fn_new(NULL);
7847c478bd9Sstevel@tonic-gate struct fn *dirname;
7857c478bd9Sstevel@tonic-gate int hasn;
7867c478bd9Sstevel@tonic-gate struct stat stbuf;
787b493790cSbasabi char *buf1;
788b493790cSbasabi char *buf2;
7897c478bd9Sstevel@tonic-gate
7907c478bd9Sstevel@tonic-gate /* expand template to figure out new filename */
7917c478bd9Sstevel@tonic-gate hasn = kw_expand(template, newfile, n, isgz);
7927c478bd9Sstevel@tonic-gate
793b493790cSbasabi buf1 = fn_s(fnp);
794b493790cSbasabi buf2 = fn_s(newfile);
7957c478bd9Sstevel@tonic-gate
796b493790cSbasabi if (Debug)
797b493790cSbasabi if (buf1 != NULL && buf2 != NULL) {
798b493790cSbasabi (void) fprintf(stderr, "rotateto: %s -> %s (%d)\n",
799b493790cSbasabi buf1, buf2, n);
800b493790cSbasabi }
8017c478bd9Sstevel@tonic-gate /* if filename is there already, rotate "up" */
802b493790cSbasabi if (hasn && lstat(buf2, &stbuf) != -1)
8037c478bd9Sstevel@tonic-gate rotateto(newfile, opts, n + 1, recentlog, isgz);
8047c478bd9Sstevel@tonic-gate else if (hasn && opts_count(opts, "z")) {
8057c478bd9Sstevel@tonic-gate struct fn *gzfnp = fn_dup(newfile);
8067c478bd9Sstevel@tonic-gate /*
8077c478bd9Sstevel@tonic-gate * since we're compressing old files, see if we
8087c478bd9Sstevel@tonic-gate * about to rotate into one.
8097c478bd9Sstevel@tonic-gate */
8107c478bd9Sstevel@tonic-gate fn_puts(gzfnp, ".gz");
8117c478bd9Sstevel@tonic-gate if (lstat(fn_s(gzfnp), &stbuf) != -1)
8127c478bd9Sstevel@tonic-gate rotateto(gzfnp, opts, n + 1, recentlog, B_TRUE);
8137c478bd9Sstevel@tonic-gate fn_free(gzfnp);
8147c478bd9Sstevel@tonic-gate }
8157c478bd9Sstevel@tonic-gate
8167c478bd9Sstevel@tonic-gate /* first time through run "before" cmd if not run already */
8177c478bd9Sstevel@tonic-gate if (n == 0 && opts_count(opts, "b")) {
8187c478bd9Sstevel@tonic-gate const char *cmd = opts_optarg(opts, "b");
8197c478bd9Sstevel@tonic-gate
8207c478bd9Sstevel@tonic-gate if (lut_lookup(Beforecmds, cmd) == NULL) {
8217c478bd9Sstevel@tonic-gate docmd(opts, "-b cmd", Sh, "-c", cmd, NULL);
8227c478bd9Sstevel@tonic-gate Beforecmds = lut_add(Beforecmds, cmd, "1");
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate
8267c478bd9Sstevel@tonic-gate /* ensure destination directory exists */
8277c478bd9Sstevel@tonic-gate dirname = fn_dirname(newfile);
8287c478bd9Sstevel@tonic-gate docmd(opts, "verify directory exists", Mkdir, "-p",
8297c478bd9Sstevel@tonic-gate fn_s(dirname), NULL);
8307c478bd9Sstevel@tonic-gate fn_free(dirname);
8317c478bd9Sstevel@tonic-gate
8327c478bd9Sstevel@tonic-gate /* do the rename */
8334bc1f5b2SBryan Cantrill if (n == 0 && opts_count(opts, "c") != NULL) {
834fbe4944aSPetr Sumbera docopytruncate(opts, fn_s(fnp), fn_s(newfile));
8357c478bd9Sstevel@tonic-gate } else if (n == 0 && opts_count(opts, "M")) {
8367c478bd9Sstevel@tonic-gate struct fn *rawcmd = fn_new(opts_optarg(opts, "M"));
8377c478bd9Sstevel@tonic-gate struct fn *cmd = fn_new(NULL);
8387c478bd9Sstevel@tonic-gate
8397c478bd9Sstevel@tonic-gate /* use specified command to mv the log file */
8407c478bd9Sstevel@tonic-gate kw_init(fnp, newfile);
8417c478bd9Sstevel@tonic-gate (void) kw_expand(rawcmd, cmd, 0, B_FALSE);
8427c478bd9Sstevel@tonic-gate docmd(opts, "-M cmd", Sh, "-c", fn_s(cmd), NULL);
8437c478bd9Sstevel@tonic-gate fn_free(rawcmd);
8447c478bd9Sstevel@tonic-gate fn_free(cmd);
8457c478bd9Sstevel@tonic-gate } else
8467c478bd9Sstevel@tonic-gate /* common case: we call "mv" to handle the actual rename */
8477c478bd9Sstevel@tonic-gate docmd(opts, "rotate log file", Mv, "-f",
8487c478bd9Sstevel@tonic-gate fn_s(fnp), fn_s(newfile));
8497c478bd9Sstevel@tonic-gate
8507c478bd9Sstevel@tonic-gate /* first time through, gather interesting info for caller */
8517c478bd9Sstevel@tonic-gate if (n == 0)
8527c478bd9Sstevel@tonic-gate fn_renew(recentlog, fn_s(newfile));
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate
8557c478bd9Sstevel@tonic-gate /* expire phase of logname processing */
8567c478bd9Sstevel@tonic-gate static void
expirefiles(struct fn * fnp,struct opts * opts)8577c478bd9Sstevel@tonic-gate expirefiles(struct fn *fnp, struct opts *opts)
8587c478bd9Sstevel@tonic-gate {
8597c478bd9Sstevel@tonic-gate char *fname = fn_s(fnp);
8607c478bd9Sstevel@tonic-gate struct fn *template;
8617c478bd9Sstevel@tonic-gate struct fn *pattern;
8627c478bd9Sstevel@tonic-gate struct fn_list *files;
8637c478bd9Sstevel@tonic-gate struct fn *nextfnp;
864404720a7Sbasabi off_t count;
865404720a7Sbasabi off_t size;
8667c478bd9Sstevel@tonic-gate
867b493790cSbasabi if (Debug && fname != NULL)
8687c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "expirefiles: fname <%s>\n", fname);
8697c478bd9Sstevel@tonic-gate
8707c478bd9Sstevel@tonic-gate /* return if no potential expire conditions */
871bf8770b4Snakanon if (opts_count(opts, "zAS") == 0 && opts_optarg_int(opts, "C") == 0)
8727c478bd9Sstevel@tonic-gate return;
8737c478bd9Sstevel@tonic-gate
8747c478bd9Sstevel@tonic-gate kw_init(fnp, NULL);
8757c478bd9Sstevel@tonic-gate if (Debug > 1) {
8767c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "expirefiles keywords:\n");
8777c478bd9Sstevel@tonic-gate kw_print(stderr);
8787c478bd9Sstevel@tonic-gate }
8797c478bd9Sstevel@tonic-gate
8807c478bd9Sstevel@tonic-gate /* see if pattern was supplied by user */
8817c478bd9Sstevel@tonic-gate if (opts_count(opts, "T")) {
8827c478bd9Sstevel@tonic-gate template = fn_new(opts_optarg(opts, "T"));
8837c478bd9Sstevel@tonic-gate pattern = glob_to_reglob(template);
8847c478bd9Sstevel@tonic-gate } else {
8857c478bd9Sstevel@tonic-gate /* nope, generate pattern based on rotation template */
8867c478bd9Sstevel@tonic-gate template = fn_new(opts_optarg(opts, "t"));
8877c478bd9Sstevel@tonic-gate pattern = fn_new(NULL);
8887c478bd9Sstevel@tonic-gate (void) kw_expand(template, pattern, -1,
8897c478bd9Sstevel@tonic-gate opts_count(opts, "z") != 0);
8907c478bd9Sstevel@tonic-gate }
8917c478bd9Sstevel@tonic-gate
8927c478bd9Sstevel@tonic-gate /* match all old log files (hopefully not any others as well!) */
8937c478bd9Sstevel@tonic-gate files = glob_reglob(pattern);
8947c478bd9Sstevel@tonic-gate
8957c478bd9Sstevel@tonic-gate if (Debug) {
896b493790cSbasabi char *buf;
897b493790cSbasabi
898b493790cSbasabi buf = fn_s(pattern);
899b493790cSbasabi if (buf != NULL) {
9007c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "expirefiles: pattern <%s>\n",
901b493790cSbasabi buf);
902b493790cSbasabi }
9037c478bd9Sstevel@tonic-gate fn_list_rewind(files);
9047c478bd9Sstevel@tonic-gate while ((nextfnp = fn_list_next(files)) != NULL)
905b493790cSbasabi buf = fn_s(nextfnp);
906b493790cSbasabi if (buf != NULL)
907b493790cSbasabi (void) fprintf(stderr, " <%s>\n", buf);
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate
9107c478bd9Sstevel@tonic-gate /* see if count causes expiration */
9117c478bd9Sstevel@tonic-gate if ((count = opts_optarg_int(opts, "C")) > 0) {
9127c478bd9Sstevel@tonic-gate int needexpire = fn_list_count(files) - count;
9137c478bd9Sstevel@tonic-gate
9147c478bd9Sstevel@tonic-gate if (Debug)
9157c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "expirefiles: needexpire %d\n",
9167c478bd9Sstevel@tonic-gate needexpire);
9177c478bd9Sstevel@tonic-gate
9187c478bd9Sstevel@tonic-gate while (needexpire > 0 &&
9197c478bd9Sstevel@tonic-gate ((nextfnp = fn_list_popoldest(files)) != NULL)) {
9207c478bd9Sstevel@tonic-gate dorm(opts, "expire by count rule", nextfnp);
9217c478bd9Sstevel@tonic-gate fn_free(nextfnp);
9227c478bd9Sstevel@tonic-gate needexpire--;
9237c478bd9Sstevel@tonic-gate }
9247c478bd9Sstevel@tonic-gate }
9257c478bd9Sstevel@tonic-gate
9267c478bd9Sstevel@tonic-gate /* see if total size causes expiration */
9277c478bd9Sstevel@tonic-gate if (opts_count(opts, "S") && (size = opts_optarg_int(opts, "S")) > 0) {
9287c478bd9Sstevel@tonic-gate while (fn_list_totalsize(files) > size &&
9297c478bd9Sstevel@tonic-gate ((nextfnp = fn_list_popoldest(files)) != NULL)) {
9307c478bd9Sstevel@tonic-gate dorm(opts, "expire by size rule", nextfnp);
9317c478bd9Sstevel@tonic-gate fn_free(nextfnp);
9327c478bd9Sstevel@tonic-gate }
9337c478bd9Sstevel@tonic-gate }
9347c478bd9Sstevel@tonic-gate
9357c478bd9Sstevel@tonic-gate /* see if age causes expiration */
9367c478bd9Sstevel@tonic-gate if (opts_count(opts, "A")) {
937404720a7Sbasabi int mtime = (int)time(0) - (int)opts_optarg_int(opts, "A");
9387c478bd9Sstevel@tonic-gate
939bf8770b4Snakanon while ((nextfnp = fn_list_popoldest(files)) != NULL) {
9407c478bd9Sstevel@tonic-gate if (fn_getstat(nextfnp)->st_mtime < mtime) {
9417c478bd9Sstevel@tonic-gate dorm(opts, "expire by age rule", nextfnp);
9427c478bd9Sstevel@tonic-gate fn_free(nextfnp);
9437c478bd9Sstevel@tonic-gate } else {
944b0b46606Snakanon fn_list_addfn(files, nextfnp);
9457c478bd9Sstevel@tonic-gate break;
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate }
948bf8770b4Snakanon }
949bf8770b4Snakanon
950bf8770b4Snakanon /* record old log files to be gzip'ed according to -z count */
951bf8770b4Snakanon if (opts_count(opts, "z")) {
952bf8770b4Snakanon int zcount = (int)opts_optarg_int(opts, "z");
953bf8770b4Snakanon int fcount = fn_list_count(files);
954bf8770b4Snakanon
955bf8770b4Snakanon while (fcount > zcount &&
956bf8770b4Snakanon (nextfnp = fn_list_popoldest(files)) != NULL) {
957bf8770b4Snakanon if (!fn_isgz(nextfnp)) {
958bf8770b4Snakanon /*
959bf8770b4Snakanon * Don't gzip the old log file yet -
960bf8770b4Snakanon * it takes too long. Just remember that we
961bf8770b4Snakanon * need to gzip.
962bf8770b4Snakanon */
963bf8770b4Snakanon if (Debug) {
964bf8770b4Snakanon (void) fprintf(stderr,
965bf8770b4Snakanon "will compress %s count %d\n",
966bf8770b4Snakanon fn_s(nextfnp), fcount);
967bf8770b4Snakanon }
968bf8770b4Snakanon Gzipnames = lut_add(Gzipnames,
969bf8770b4Snakanon fn_s(nextfnp), "1");
970bf8770b4Snakanon }
971bf8770b4Snakanon fn_free(nextfnp);
972bf8770b4Snakanon fcount--;
973bf8770b4Snakanon }
974bf8770b4Snakanon }
9757c478bd9Sstevel@tonic-gate
9767c478bd9Sstevel@tonic-gate fn_free(template);
9777c478bd9Sstevel@tonic-gate fn_list_free(files);
9787c478bd9Sstevel@tonic-gate }
9797c478bd9Sstevel@tonic-gate
9807c478bd9Sstevel@tonic-gate /* execute a command to remove an expired log file */
9817c478bd9Sstevel@tonic-gate static void
dorm(struct opts * opts,const char * msg,struct fn * fnp)9827c478bd9Sstevel@tonic-gate dorm(struct opts *opts, const char *msg, struct fn *fnp)
9837c478bd9Sstevel@tonic-gate {
9847c478bd9Sstevel@tonic-gate if (opts_count(opts, "E")) {
9857c478bd9Sstevel@tonic-gate struct fn *rawcmd = fn_new(opts_optarg(opts, "E"));
9867c478bd9Sstevel@tonic-gate struct fn *cmd = fn_new(NULL);
9877c478bd9Sstevel@tonic-gate
9887c478bd9Sstevel@tonic-gate /* user supplied cmd, expand $file */
9897c478bd9Sstevel@tonic-gate kw_init(fnp, NULL);
9907c478bd9Sstevel@tonic-gate (void) kw_expand(rawcmd, cmd, 0, B_FALSE);
9917c478bd9Sstevel@tonic-gate docmd(opts, msg, Sh, "-c", fn_s(cmd), NULL);
9927c478bd9Sstevel@tonic-gate fn_free(rawcmd);
9937c478bd9Sstevel@tonic-gate fn_free(cmd);
9947c478bd9Sstevel@tonic-gate } else
9957c478bd9Sstevel@tonic-gate docmd(opts, msg, Rm, "-f", fn_s(fnp), NULL);
9960ade2cf0Sdg199075 Gzipnames = lut_add(Gzipnames, fn_s(fnp), NULL);
9977c478bd9Sstevel@tonic-gate }
9987c478bd9Sstevel@tonic-gate
9997c478bd9Sstevel@tonic-gate /* execute a command, producing -n and -v output as necessary */
10007c478bd9Sstevel@tonic-gate static void
docmd(struct opts * opts,const char * msg,const char * cmd,const char * arg1,const char * arg2,const char * arg3)10017c478bd9Sstevel@tonic-gate docmd(struct opts *opts, const char *msg, const char *cmd,
10027c478bd9Sstevel@tonic-gate const char *arg1, const char *arg2, const char *arg3)
10037c478bd9Sstevel@tonic-gate {
10047c478bd9Sstevel@tonic-gate int pid;
10057c478bd9Sstevel@tonic-gate int errpipe[2];
10067c478bd9Sstevel@tonic-gate
10077c478bd9Sstevel@tonic-gate /* print info about command if necessary */
10087c478bd9Sstevel@tonic-gate if (opts_count(opts, "vn")) {
10097c478bd9Sstevel@tonic-gate const char *simplecmd;
10107c478bd9Sstevel@tonic-gate
10117c478bd9Sstevel@tonic-gate if ((simplecmd = strrchr(cmd, '/')) == NULL)
10127c478bd9Sstevel@tonic-gate simplecmd = cmd;
10137c478bd9Sstevel@tonic-gate else
10147c478bd9Sstevel@tonic-gate simplecmd++;
10157c478bd9Sstevel@tonic-gate (void) out("%s", simplecmd);
10167c478bd9Sstevel@tonic-gate if (arg1)
10177c478bd9Sstevel@tonic-gate (void) out(" %s", arg1);
10187c478bd9Sstevel@tonic-gate if (arg2)
10197c478bd9Sstevel@tonic-gate (void) out(" %s", arg2);
10207c478bd9Sstevel@tonic-gate if (arg3)
10217c478bd9Sstevel@tonic-gate (void) out(" %s", arg3);
10227c478bd9Sstevel@tonic-gate if (msg)
10237c478bd9Sstevel@tonic-gate (void) out(" # %s", msg);
10247c478bd9Sstevel@tonic-gate (void) out("\n");
10257c478bd9Sstevel@tonic-gate }
10267c478bd9Sstevel@tonic-gate
10277c478bd9Sstevel@tonic-gate if (opts_count(opts, "n"))
10287c478bd9Sstevel@tonic-gate return; /* -n means don't really do it */
10297c478bd9Sstevel@tonic-gate
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate * run the cmd and see if it failed. this function is *not* a
10327c478bd9Sstevel@tonic-gate * generic command runner -- we depend on some knowledge we
10337c478bd9Sstevel@tonic-gate * have about the commands we run. first of all, we expect
10347c478bd9Sstevel@tonic-gate * errors to spew something to stderr, and that something is
10357c478bd9Sstevel@tonic-gate * typically short enough to fit into a pipe so we can wait()
10367c478bd9Sstevel@tonic-gate * for the command to complete and then fetch the error text
10377c478bd9Sstevel@tonic-gate * from the pipe. we also expect the exit codes to make sense.
10387c478bd9Sstevel@tonic-gate * notice also that we only allow a command name which is an
10397c478bd9Sstevel@tonic-gate * absolute pathname, and two args must be supplied (the
10407c478bd9Sstevel@tonic-gate * second may be NULL, or they may both be NULL).
10417c478bd9Sstevel@tonic-gate */
10427c478bd9Sstevel@tonic-gate if (pipe(errpipe) < 0)
10437c478bd9Sstevel@tonic-gate err(EF_SYS, "pipe");
10447c478bd9Sstevel@tonic-gate
10457c478bd9Sstevel@tonic-gate if ((pid = fork()) < 0)
10467c478bd9Sstevel@tonic-gate err(EF_SYS, "fork");
10477c478bd9Sstevel@tonic-gate else if (pid) {
10487c478bd9Sstevel@tonic-gate int wstat;
10497c478bd9Sstevel@tonic-gate int count;
10507c478bd9Sstevel@tonic-gate
10517c478bd9Sstevel@tonic-gate /* parent */
10527c478bd9Sstevel@tonic-gate (void) close(errpipe[1]);
10537c478bd9Sstevel@tonic-gate if (waitpid(pid, &wstat, 0) < 0)
10547c478bd9Sstevel@tonic-gate err(EF_SYS, "waitpid");
10557c478bd9Sstevel@tonic-gate
10567c478bd9Sstevel@tonic-gate /* check for stderr output */
10577c478bd9Sstevel@tonic-gate if (ioctl(errpipe[0], FIONREAD, &count) >= 0 && count) {
10587c478bd9Sstevel@tonic-gate err(EF_WARN, "command failed: %s%s%s%s%s%s%s",
10597c478bd9Sstevel@tonic-gate cmd,
10607c478bd9Sstevel@tonic-gate (arg1) ? " " : "",
10617c478bd9Sstevel@tonic-gate (arg1) ? arg1 : "",
10627c478bd9Sstevel@tonic-gate (arg2) ? " " : "",
10637c478bd9Sstevel@tonic-gate (arg2) ? arg2 : "",
10647c478bd9Sstevel@tonic-gate (arg3) ? " " : "",
10657c478bd9Sstevel@tonic-gate (arg3) ? arg3 : "");
10667c478bd9Sstevel@tonic-gate err_fromfd(errpipe[0]);
10677c478bd9Sstevel@tonic-gate } else if (WIFSIGNALED(wstat))
10687c478bd9Sstevel@tonic-gate err(EF_WARN,
10697c478bd9Sstevel@tonic-gate "command died, signal %d: %s%s%s%s%s%s%s",
10707c478bd9Sstevel@tonic-gate WTERMSIG(wstat),
10717c478bd9Sstevel@tonic-gate cmd,
10727c478bd9Sstevel@tonic-gate (arg1) ? " " : "",
10737c478bd9Sstevel@tonic-gate (arg1) ? arg1 : "",
10747c478bd9Sstevel@tonic-gate (arg2) ? " " : "",
10757c478bd9Sstevel@tonic-gate (arg2) ? arg2 : "",
10767c478bd9Sstevel@tonic-gate (arg3) ? " " : "",
10777c478bd9Sstevel@tonic-gate (arg3) ? arg3 : "");
10787c478bd9Sstevel@tonic-gate else if (WIFEXITED(wstat) && WEXITSTATUS(wstat))
10797c478bd9Sstevel@tonic-gate err(EF_WARN,
10807c478bd9Sstevel@tonic-gate "command error, exit %d: %s%s%s%s%s%s%s",
10817c478bd9Sstevel@tonic-gate WEXITSTATUS(wstat),
10827c478bd9Sstevel@tonic-gate cmd,
10837c478bd9Sstevel@tonic-gate (arg1) ? " " : "",
10847c478bd9Sstevel@tonic-gate (arg1) ? arg1 : "",
10857c478bd9Sstevel@tonic-gate (arg2) ? " " : "",
10867c478bd9Sstevel@tonic-gate (arg2) ? arg2 : "",
10877c478bd9Sstevel@tonic-gate (arg3) ? " " : "",
10887c478bd9Sstevel@tonic-gate (arg3) ? arg3 : "");
10897c478bd9Sstevel@tonic-gate
10907c478bd9Sstevel@tonic-gate (void) close(errpipe[0]);
10917c478bd9Sstevel@tonic-gate } else {
10927c478bd9Sstevel@tonic-gate /* child */
10937c478bd9Sstevel@tonic-gate (void) dup2(errpipe[1], fileno(stderr));
10947c478bd9Sstevel@tonic-gate (void) close(errpipe[0]);
10957c478bd9Sstevel@tonic-gate (void) execl(cmd, cmd, arg1, arg2, arg3, 0);
10967c478bd9Sstevel@tonic-gate perror(cmd);
10977c478bd9Sstevel@tonic-gate _exit(1);
10987c478bd9Sstevel@tonic-gate }
10997c478bd9Sstevel@tonic-gate }
1100fbe4944aSPetr Sumbera
1101fbe4944aSPetr Sumbera /* do internal atomic file copy and truncation */
1102fbe4944aSPetr Sumbera static void
docopytruncate(struct opts * opts,const char * file,const char * file_copy)1103fbe4944aSPetr Sumbera docopytruncate(struct opts *opts, const char *file, const char *file_copy)
1104fbe4944aSPetr Sumbera {
1105d39070fcSBryan Cantrill int fi, fo;
1106d39070fcSBryan Cantrill char buf[128 * 1024];
1107fbe4944aSPetr Sumbera struct stat s;
1108fbe4944aSPetr Sumbera struct utimbuf times;
1109d39070fcSBryan Cantrill off_t written = 0, rem, last = 0, thresh = 1024 * 1024;
1110d39070fcSBryan Cantrill ssize_t len;
1111fbe4944aSPetr Sumbera
1112fbe4944aSPetr Sumbera /* print info if necessary */
1113fbe4944aSPetr Sumbera if (opts_count(opts, "vn") != NULL) {
1114fbe4944aSPetr Sumbera (void) out("# log rotation via atomic copy and truncation"
1115fbe4944aSPetr Sumbera " (-c flag):\n");
1116fbe4944aSPetr Sumbera (void) out("# copy %s to %s\n", file, file_copy);
1117fbe4944aSPetr Sumbera (void) out("# truncate %s\n", file);
1118fbe4944aSPetr Sumbera }
1119fbe4944aSPetr Sumbera
1120fbe4944aSPetr Sumbera if (opts_count(opts, "n"))
1121fbe4944aSPetr Sumbera return; /* -n means don't really do it */
1122fbe4944aSPetr Sumbera
1123fbe4944aSPetr Sumbera /* open log file to be rotated and remember its chmod mask */
1124fbe4944aSPetr Sumbera if ((fi = open(file, O_RDWR)) < 0) {
1125fbe4944aSPetr Sumbera err(EF_SYS, "cannot open file %s", file);
1126fbe4944aSPetr Sumbera return;
1127fbe4944aSPetr Sumbera }
1128fbe4944aSPetr Sumbera
1129fbe4944aSPetr Sumbera if (fstat(fi, &s) < 0) {
1130fbe4944aSPetr Sumbera err(EF_SYS, "cannot access: %s", file);
1131fbe4944aSPetr Sumbera (void) close(fi);
1132fbe4944aSPetr Sumbera return;
1133fbe4944aSPetr Sumbera }
1134fbe4944aSPetr Sumbera
1135fbe4944aSPetr Sumbera /* create new file for copy destination with correct attributes */
1136d39070fcSBryan Cantrill if ((fo = open(file_copy, O_CREAT|O_TRUNC|O_WRONLY, s.st_mode)) < 0) {
1137fbe4944aSPetr Sumbera err(EF_SYS, "cannot create file: %s", file_copy);
1138fbe4944aSPetr Sumbera (void) close(fi);
1139fbe4944aSPetr Sumbera return;
1140fbe4944aSPetr Sumbera }
1141fbe4944aSPetr Sumbera
1142fbe4944aSPetr Sumbera (void) fchown(fo, s.st_uid, s.st_gid);
1143fbe4944aSPetr Sumbera
1144d39070fcSBryan Cantrill /*
1145d39070fcSBryan Cantrill * Now we'll loop, reading the log file and writing it to our copy
1146d39070fcSBryan Cantrill * until the bytes remaining are beneath our atomicity threshold -- at
1147d39070fcSBryan Cantrill * which point we'll lock the file and copy the remainder atomically.
1148d39070fcSBryan Cantrill * The body of this loop is non-atomic with respect to writers, the
1149d39070fcSBryan Cantrill * rationale being that total atomicity (that is, locking the file for
1150d39070fcSBryan Cantrill * the entire duration of the copy) comes at too great a cost for a
1151d39070fcSBryan Cantrill * large log file, as the writer (i.e., the daemon whose log is being
1152d39070fcSBryan Cantrill * rolled) can be blocked for an unacceptable duration. (For one
1153d39070fcSBryan Cantrill * particularly loquacious daemon, this period was observed to be
1154d39070fcSBryan Cantrill * several minutes in length -- a time so long that it induced
1155d39070fcSBryan Cantrill * additional failures in dependent components.) Note that this means
1156d39070fcSBryan Cantrill * that if the log file is not always appended to -- if it is opened
1157d39070fcSBryan Cantrill * without O_APPEND or otherwise truncated outside of logadm -- this
1158d39070fcSBryan Cantrill * will result in our log snapshot being incorrect. But of course, in
1159d39070fcSBryan Cantrill * either of these cases, the use of logadm at all is itself
1160d39070fcSBryan Cantrill * suspect...
1161d39070fcSBryan Cantrill */
1162d39070fcSBryan Cantrill do {
1163d39070fcSBryan Cantrill if (fstat(fi, &s) < 0) {
1164d39070fcSBryan Cantrill err(EF_SYS, "cannot stat: %s", file);
1165d39070fcSBryan Cantrill (void) close(fi);
1166d39070fcSBryan Cantrill (void) close(fo);
1167d39070fcSBryan Cantrill (void) remove(file_copy);
1168d39070fcSBryan Cantrill return;
1169d39070fcSBryan Cantrill }
1170d39070fcSBryan Cantrill
1171d39070fcSBryan Cantrill if ((rem = s.st_size - written) < thresh) {
1172d39070fcSBryan Cantrill if (rem >= 0)
1173d39070fcSBryan Cantrill break;
1174d39070fcSBryan Cantrill
1175d39070fcSBryan Cantrill /*
1176d39070fcSBryan Cantrill * If the file became smaller, something fishy is going
1177d39070fcSBryan Cantrill * on; we'll truncate our copy, reset our seek offset
1178d39070fcSBryan Cantrill * and break into the atomic copy.
1179d39070fcSBryan Cantrill */
1180d39070fcSBryan Cantrill (void) ftruncate(fo, 0);
1181d39070fcSBryan Cantrill (void) lseek(fo, 0, SEEK_SET);
1182d39070fcSBryan Cantrill (void) lseek(fi, 0, SEEK_SET);
1183d39070fcSBryan Cantrill break;
1184d39070fcSBryan Cantrill }
1185d39070fcSBryan Cantrill
1186d39070fcSBryan Cantrill if (written != 0 && rem > last) {
1187d39070fcSBryan Cantrill /*
1188d39070fcSBryan Cantrill * We're falling behind -- this file is getting bigger
1189d39070fcSBryan Cantrill * faster than we're able to write it; break out and
1190d39070fcSBryan Cantrill * lock the file to block the writer.
1191d39070fcSBryan Cantrill */
1192d39070fcSBryan Cantrill break;
1193d39070fcSBryan Cantrill }
1194d39070fcSBryan Cantrill
1195d39070fcSBryan Cantrill last = rem;
1196d39070fcSBryan Cantrill
1197d39070fcSBryan Cantrill while (rem > 0) {
1198d39070fcSBryan Cantrill if ((len = read(fi, buf, MIN(sizeof (buf), rem))) <= 0)
1199d39070fcSBryan Cantrill break;
1200d39070fcSBryan Cantrill
1201d39070fcSBryan Cantrill if (write(fo, buf, len) == len) {
1202d39070fcSBryan Cantrill rem -= len;
1203d39070fcSBryan Cantrill written += len;
1204d39070fcSBryan Cantrill continue;
1205d39070fcSBryan Cantrill }
1206d39070fcSBryan Cantrill
1207d39070fcSBryan Cantrill err(EF_SYS, "cannot write into file %s", file_copy);
1208d39070fcSBryan Cantrill (void) close(fi);
1209d39070fcSBryan Cantrill (void) close(fo);
1210d39070fcSBryan Cantrill (void) remove(file_copy);
1211d39070fcSBryan Cantrill return;
1212d39070fcSBryan Cantrill }
1213d39070fcSBryan Cantrill } while (len >= 0);
1214d39070fcSBryan Cantrill
1215fbe4944aSPetr Sumbera /* lock log file so that nobody can write into it before we are done */
1216fbe4944aSPetr Sumbera if (fchmod(fi, s.st_mode|S_ISGID) < 0)
1217fbe4944aSPetr Sumbera err(EF_SYS, "cannot set mandatory lock bit for: %s", file);
1218fbe4944aSPetr Sumbera
1219fbe4944aSPetr Sumbera if (lockf(fi, F_LOCK, 0) == -1)
1220fbe4944aSPetr Sumbera err(EF_SYS, "cannot lock file %s", file);
1221fbe4944aSPetr Sumbera
1222fbe4944aSPetr Sumbera /* do atomic copy and truncation */
1223fbe4944aSPetr Sumbera while ((len = read(fi, buf, sizeof (buf))) > 0)
1224fbe4944aSPetr Sumbera if (write(fo, buf, len) != len) {
1225fbe4944aSPetr Sumbera err(EF_SYS, "cannot write into file %s", file_copy);
1226fbe4944aSPetr Sumbera (void) lockf(fi, F_ULOCK, 0);
1227fbe4944aSPetr Sumbera (void) fchmod(fi, s.st_mode);
1228fbe4944aSPetr Sumbera (void) close(fi);
1229fbe4944aSPetr Sumbera (void) close(fo);
1230fbe4944aSPetr Sumbera (void) remove(file_copy);
1231fbe4944aSPetr Sumbera return;
1232fbe4944aSPetr Sumbera }
1233fbe4944aSPetr Sumbera
1234fbe4944aSPetr Sumbera (void) ftruncate(fi, 0);
1235fbe4944aSPetr Sumbera
1236fbe4944aSPetr Sumbera /* unlock log file */
1237fbe4944aSPetr Sumbera if (lockf(fi, F_ULOCK, 0) == -1)
1238fbe4944aSPetr Sumbera err(EF_SYS, "cannot unlock file %s", file);
1239fbe4944aSPetr Sumbera
1240fbe4944aSPetr Sumbera if (fchmod(fi, s.st_mode) < 0)
1241fbe4944aSPetr Sumbera err(EF_SYS, "cannot reset mandatory lock bit for: %s", file);
1242fbe4944aSPetr Sumbera
1243fbe4944aSPetr Sumbera (void) close(fi);
1244fbe4944aSPetr Sumbera (void) close(fo);
1245fbe4944aSPetr Sumbera
1246fbe4944aSPetr Sumbera /* keep times from original file */
1247fbe4944aSPetr Sumbera times.actime = s.st_atime;
1248fbe4944aSPetr Sumbera times.modtime = s.st_mtime;
1249fbe4944aSPetr Sumbera (void) utime(file_copy, ×);
1250fbe4944aSPetr Sumbera }
1251