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 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 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 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 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 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 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 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 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 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 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 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 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