diff options
Diffstat (limited to 'usr.sbin/newsyslog')
-rw-r--r-- | usr.sbin/newsyslog/Makefile | 7 | ||||
-rw-r--r-- | usr.sbin/newsyslog/extern.h | 68 | ||||
-rw-r--r-- | usr.sbin/newsyslog/newsyslog.8 | 299 | ||||
-rw-r--r-- | usr.sbin/newsyslog/newsyslog.c | 2462 | ||||
-rw-r--r-- | usr.sbin/newsyslog/newsyslog.conf.5 | 355 | ||||
-rw-r--r-- | usr.sbin/newsyslog/pathnames.h | 28 | ||||
-rw-r--r-- | usr.sbin/newsyslog/ptimes.c | 618 |
7 files changed, 0 insertions, 3837 deletions
diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile deleted file mode 100644 index ebc297b..0000000 --- a/usr.sbin/newsyslog/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# $FreeBSD$ - -PROG= newsyslog -MAN= newsyslog.8 newsyslog.conf.5 -SRCS= newsyslog.c ptimes.c - -.include <bsd.prog.mk> diff --git a/usr.sbin/newsyslog/extern.h b/usr.sbin/newsyslog/extern.h deleted file mode 100644 index c350226..0000000 --- a/usr.sbin/newsyslog/extern.h +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * ------+---------+---------+---------+---------+---------+---------+---------* - * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the FreeBSD Project. - * - * ------+---------+---------+---------+---------+---------+---------+---------* - * $FreeBSD$ - * ------+---------+---------+---------+---------+---------+---------+---------* - */ - -#include <sys/cdefs.h> -#include <time.h> - -#define PTM_PARSE_ISO8601 0x0001 /* Parse ISO-standard format */ -#define PTM_PARSE_DWM 0x0002 /* Parse Day-Week-Month format */ -#define PTM_PARSE_MATCHDOM 0x0004 /* If the user specifies a day-of-month, - * then the result should be a month - * which actually has that day. Eg: - * the user requests "day 31" when - * the present month is February. */ - -struct ptime_data; - -/* Some global variables from newsyslog.c which might be of interest */ -extern int dbg_at_times; /* cmdline debugging option */ -extern int noaction; /* command-line option */ -extern int verbose; /* command-line option */ -extern struct ptime_data *dbg_timenow; - -__BEGIN_DECLS -struct ptime_data *ptime_init(const struct ptime_data *_optsrc); -int ptime_adjust4dst(struct ptime_data *_ptime, const struct - ptime_data *_dstsrc); -int ptime_free(struct ptime_data *_ptime); -int ptime_relparse(struct ptime_data *_ptime, int _parseopts, - time_t _basetime, const char *_str); -const char *ptimeget_ctime(const struct ptime_data *_ptime); -double ptimeget_diff(const struct ptime_data *_minuend, - const struct ptime_data *_subtrahend); -time_t ptimeget_secs(const struct ptime_data *_ptime); -int ptimeset_nxtime(struct ptime_data *_ptime); -int ptimeset_time(struct ptime_data *_ptime, time_t _secs); -__END_DECLS diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8 deleted file mode 100644 index 312080b..0000000 --- a/usr.sbin/newsyslog/newsyslog.8 +++ /dev/null @@ -1,299 +0,0 @@ -.\" This file contains changes from the Open Software Foundation. -.\" -.\" from: @(#)newsyslog.8 -.\" $FreeBSD$ -.\" -.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology -.\" -.\" Permission to use, copy, modify, and distribute this software -.\" and its documentation for any purpose and without fee is -.\" hereby granted, provided that the above copyright notice -.\" appear in all copies and that both that copyright notice and -.\" this permission notice appear in supporting documentation, -.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be -.\" used in advertising or publicity pertaining to distribution -.\" of the software without specific, written prior permission. -.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about -.\" the suitability of this software for any purpose. It is -.\" provided "as is" without express or implied warranty. -.\" -.Dd July 23, 2010 -.Dt NEWSYSLOG 8 -.Os -.Sh NAME -.Nm newsyslog -.Nd maintain system log files to manageable sizes -.Sh SYNOPSIS -.Nm -.Op Fl CFNPnrsv -.Op Fl R Ar tagname -.Op Fl S Ar pidfile -.Op Fl a Ar directory -.Op Fl d Ar directory -.Op Fl f Ar config_file -.Op Fl t Ar timefmt -.Op Ar -.Sh DESCRIPTION -The -.Nm -utility should be scheduled to run periodically by -.Xr cron 8 . -When it is executed it archives log files if necessary. -If a log file -is determined to require archiving, -.Nm -rearranges the files so that -.Dq Va logfile -is empty, -.Dq Va logfile Ns Li \&.0 -has -the last period's logs in it, -.Dq Va logfile Ns Li \&.1 -has the next to last -period's logs in it, and so on, up to a user-specified number of -archived logs. -It is also possible to let archived log filenames be created using the -time the log file was archived instead of the sequential number using -the -.Fl t -option. -Optionally the archived logs can be compressed to save -space. -.Pp -A log can be archived for three reasons: -.Bl -enum -offset indent -.It -It is larger than the configured size (in kilobytes). -.It -A configured number of hours have elapsed since the log was last -archived. -.It -This is the specific configured hour for rotation of the log. -.El -.Pp -The granularity of -.Nm -is dependent on how often it is scheduled to run by -.Xr cron 8 . -Since the program is quite fast, it may be scheduled to run every hour -without any ill effects, -and mode three (above) assumes that this is so. -.Sh OPTIONS -The following options can be used with -.Nm : -.Bl -tag -width indent -.It Fl f Ar config_file -Instruct -.Nm -to use -.Ar config_file -instead of -.Pa /etc/newsyslog.conf -for its configuration file. -.It Fl a Ar directory -Specify a -.Ar directory -into which archived log files will be written. -If a relative path is given, -it is appended to the path of each log file -and the resulting path is used as the directory -into which the archived log for that log file will be written. -If an absolute path is given, -all archived logs are written into the given -.Ar directory . -If any component of the path -.Ar directory -does not exist, -it will be created when -.Nm -is run. -.It Fl d Ar directory -Specify a -.Ar directory -which all log files will be relative to. -To allow archiving of logs outside the root, the -.Ar directory -passed to the -.Fl a -option is unaffected. -.It Fl v -Place -.Nm -in verbose mode. -In this mode it will print out each log and its -reasons for either trimming that log or skipping it. -.It Fl n -Cause -.Nm -not to trim the logs, but to print out what it would do if this option -were not specified. -.It Fl r -Remove the restriction that -.Nm -must be running as root. -Of course, -.Nm -will not be able to send a HUP signal to -.Xr syslogd 8 -so this option should only be used in debugging. -.It Fl s -Specify that -.Nm -should not send any signals to any daemon processes that it would -normally signal when rotating a log file. -For any log file which is rotated, this option will usually also -mean the rotated log file will not be compressed if there is a -daemon which would have been signalled without this option. -However, this option is most likely to be useful when specified -with the -.Fl R -option, and in that case the compression will be done. -.It Fl t Ar timefmt -If specified -.Nm -will create the -.Dq rotated -logfiles using the specified time format instead of the default -sequential filenames. -The time format is described in the -.Xr strftime 3 -manual page. -If the -.Ar timefmt -argument is set to an empty string or the string -.Dq DEFAULT , -the default built in time format -is used. -If the -.Ar timefmt -string is changed the old files created using the previous time format -will not be be automatically removed (unless the new format is very -similar to the old format). -This is also the case when changing from sequential filenames to time -based file names, and the other way around. -The time format should contain at least year, month, day, and hour to -make sure rotating of old logfiles can select the correct logfiles. -.It Fl C -If specified once, then -.Nm -will create any log files which do not exist, and which have the -.Sy C -flag specified in their config file entry. -If specified multiple times, then -.Nm -will create all log files which do not already exist. -If log files are given on the command-line, then the -.Fl C -or -.Fl CC -will only apply to those specific log files. -.It Fl F -Force -.Nm -to trim the logs, even if the trim conditions have not been met. -This -option is useful for diagnosing system problems by providing you with -fresh logs that contain only the problems. -.It Fl N -Do not perform any rotations. -This option is intended to be used with the -.Fl C -or -.Fl CC -options when creating log files is the only objective. -.It Fl P -Prevent further action if we should send signal but the -.Dq pidfile -is empty or does not exist. -.It Fl R Ar tagname -Specify that -.Nm -should rotate a given list of files, even if trim conditions are not -met for those files. -The -.Ar tagname -is only used in the messages written to the log files which are -rotated. -This differs from the -.Fl F -option in that one or more log files must also be specified, so that -.Nm -will only operate on those specific files. -This option is mainly intended for the daemons or programs which write -some log files, and want to trigger a rotate based on their own criteria. -With this option they can execute -.Nm -to trigger the rotate when they want it to happen, and still give the -system administrator a way to specify the rules of rotation (such as how -many backup copies are kept, and what kind of compression is done). -When a daemon does execute -.Nm -with the -.Fl R -option, it should make sure all of the log files are closed before -calling -.Nm , -and then it should re-open the files after -.Nm -returns. -Usually the calling process will also want to specify the -.Fl s -option, so -.Nm -will not send a signal to the very process which called it to force -the rotate. -Skipping the signal step will also mean that -.Nm -will return faster, since -.Nm -normally waits a few seconds after any signal that is sent. -.It Fl S Ar pidfile -Use -.Ar pidfile -as -.Xr syslogd 8 Ns 's -pidfile. -.El -.Pp -If additional command line arguments are given, -.Nm -will only examine log files that match those arguments; otherwise, it -will examine all files listed in the configuration file. -.Sh FILES -.Bl -tag -width /etc/newsyslog.confxxxx -compact -.It Pa /etc/newsyslog.conf -.Nm -configuration file -.El -.Sh COMPATIBILITY -Previous versions of the -.Nm -utility used the dot (``.'') character to -distinguish the group name. -Beginning with -.Fx 3.3 , -this has been changed to a colon (``:'') character so that user and group -names may contain the dot character. -The dot (``.'') character is still -accepted for backwards compatibility. -.Sh SEE ALSO -.Xr bzip2 1 , -.Xr gzip 1 , -.Xr syslog 3 , -.Xr newsyslog.conf 5 , -.Xr chown 8 , -.Xr syslogd 8 -.Sh HISTORY -The -.Nm -utility originated from -.Nx -and first appeared in -.Fx 2.2 . -.Sh AUTHORS -.An Theodore Ts'o , -MIT Project Athena -.Pp -Copyright 1987, Massachusetts Institute of Technology -.Sh BUGS -Does not yet automatically read the logs to find security breaches. diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c deleted file mode 100644 index 7ba6cb8..0000000 --- a/usr.sbin/newsyslog/newsyslog.c +++ /dev/null @@ -1,2462 +0,0 @@ -/*- - * ------+---------+---------+-------- + --------+---------+---------+---------* - * This file includes significant modifications done by: - * Copyright (c) 2003, 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * ------+---------+---------+-------- + --------+---------+---------+---------* - */ - -/* - * This file contains changes from the Open Software Foundation. - */ - -/* - * Copyright 1988, 1989 by the Massachusetts Institute of Technology - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be - * used in advertising or publicity pertaining to distribution of the - * software without specific, written prior permission. M.I.T. and the M.I.T. - * S.I.P.B. make no representations about the suitability of this software - * for any purpose. It is provided "as is" without express or implied - * warranty. - * - */ - -/* - * newsyslog - roll over selected logs at the appropriate time, keeping the a - * specified number of backup files around. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#define OSF -#ifndef COMPRESS_POSTFIX -#define COMPRESS_POSTFIX ".gz" -#endif -#ifndef BZCOMPRESS_POSTFIX -#define BZCOMPRESS_POSTFIX ".bz2" -#endif - -#include <sys/param.h> -#include <sys/queue.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#include <assert.h> -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <fnmatch.h> -#include <glob.h> -#include <grp.h> -#include <paths.h> -#include <pwd.h> -#include <signal.h> -#include <stdio.h> -#include <libgen.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "pathnames.h" -#include "extern.h" - -/* - * Bit-values for the 'flags' parsed from a config-file entry. - */ -#define CE_COMPACT 0x0001 /* Compact the archived log files with gzip. */ -#define CE_BZCOMPACT 0x0002 /* Compact the archived log files with bzip2. */ -#define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */ - /* messages to logfile(s) when rotating. */ -#define CE_NOSIGNAL 0x0010 /* There is no process to signal when */ - /* trimming this file. */ -#define CE_TRIMAT 0x0020 /* trim file at a specific time. */ -#define CE_GLOB 0x0040 /* name of the log is file name pattern. */ -#define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */ - /* process when trimming this file. */ -#define CE_CREATE 0x0100 /* Create the log file if it does not exist. */ -#define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */ - -#define MIN_PID 5 /* Don't touch pids lower than this */ -#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ - -#define kbytes(size) (((size) + 1023) >> 10) - -#define DEFAULT_MARKER "<default>" -#define DEBUG_MARKER "<debug>" -#define INCLUDE_MARKER "<include>" -#define DEFAULT_TIMEFNAME_FMT "%Y%m%dT%H%M%S" - -#define MAX_OLDLOGS 65536 /* Default maximum number of old logfiles */ - -struct conf_entry { - STAILQ_ENTRY(conf_entry) cf_nextp; - char *log; /* Name of the log */ - char *pid_file; /* PID file */ - char *r_reason; /* The reason this file is being rotated */ - int firstcreate; /* Creating log for the first time (-C). */ - int rotate; /* Non-zero if this file should be rotated */ - int fsize; /* size found for the log file */ - uid_t uid; /* Owner of log */ - gid_t gid; /* Group of log */ - int numlogs; /* Number of logs to keep */ - int trsize; /* Size cutoff to trigger trimming the log */ - int hours; /* Hours between log trimming */ - struct ptime_data *trim_at; /* Specific time to do trimming */ - unsigned int permissions; /* File permissions on the log */ - int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ - int sig; /* Signal to send */ - int def_cfg; /* Using the <default> rule for this file */ -}; - -struct sigwork_entry { - SLIST_ENTRY(sigwork_entry) sw_nextp; - int sw_signum; /* the signal to send */ - int sw_pidok; /* true if pid value is valid */ - pid_t sw_pid; /* the process id from the PID file */ - const char *sw_pidtype; /* "daemon" or "process group" */ - char sw_fname[1]; /* file the PID was read from */ -}; - -struct zipwork_entry { - SLIST_ENTRY(zipwork_entry) zw_nextp; - const struct conf_entry *zw_conf; /* for chown/perm/flag info */ - const struct sigwork_entry *zw_swork; /* to know success of signal */ - int zw_fsize; /* size of the file to compress */ - char zw_fname[1]; /* the file to compress */ -}; - -struct include_entry { - STAILQ_ENTRY(include_entry) inc_nextp; - const char *file; /* Name of file to process */ -}; - -struct oldlog_entry { - char *fname; /* Filename of the log file */ - time_t t; /* Parses timestamp of the logfile */ -}; - -typedef enum { - FREE_ENT, KEEP_ENT -} fk_entry; - -STAILQ_HEAD(cflist, conf_entry); -SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead); -SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead); -STAILQ_HEAD(ilist, include_entry); - -int dbg_at_times; /* -D Show details of 'trim_at' code */ - -int archtodir = 0; /* Archive old logfiles to other directory */ -int createlogs; /* Create (non-GLOB) logfiles which do not */ - /* already exist. 1=='for entries with */ - /* C flag', 2=='for all entries'. */ -int verbose = 0; /* Print out what's going on */ -int needroot = 1; /* Root privs are necessary */ -int noaction = 0; /* Don't do anything, just show it */ -int norotate = 0; /* Don't rotate */ -int nosignal; /* Do not send any signals */ -int enforcepid = 0; /* If PID file does not exist or empty, do nothing */ -int force = 0; /* Force the trim no matter what */ -int rotatereq = 0; /* -R = Always rotate the file(s) as given */ - /* on the command (this also requires */ - /* that a list of files *are* given on */ - /* the run command). */ -char *requestor; /* The name given on a -R request */ -char *timefnamefmt = NULL; /* Use time based filenames instead of .0 etc */ -char *archdirname; /* Directory path to old logfiles archive */ -char *destdir = NULL; /* Directory to treat at root for logs */ -const char *conf; /* Configuration file to use */ - -struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */ -struct ptime_data *timenow; /* The time to use for checking at-fields */ - -#define DAYTIME_LEN 16 -char daytime[DAYTIME_LEN]; /* The current time in human readable form, - * used for rotation-tracking messages. */ -char hostname[MAXHOSTNAMELEN]; /* hostname */ - -const char *path_syslogpid = _PATH_SYSLOGPID; - -static struct cflist *get_worklist(char **files); -static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, - struct conf_entry *defconf_p, struct ilist *inclist); -static void add_to_queue(const char *fname, struct ilist *inclist); -static char *sob(char *p); -static char *son(char *p); -static int isnumberstr(const char *); -static int isglobstr(const char *); -static char *missing_field(char *p, char *errline); -static void change_attrs(const char *, const struct conf_entry *); -static fk_entry do_entry(struct conf_entry *); -static fk_entry do_rotate(const struct conf_entry *); -static void do_sigwork(struct sigwork_entry *); -static void do_zipwork(struct zipwork_entry *); -static struct sigwork_entry * - save_sigwork(const struct conf_entry *); -static struct zipwork_entry * - save_zipwork(const struct conf_entry *, const struct - sigwork_entry *, int, const char *); -static void set_swpid(struct sigwork_entry *, const struct conf_entry *); -static int sizefile(const char *); -static void expand_globs(struct cflist *work_p, struct cflist *glob_p); -static void free_clist(struct cflist *list); -static void free_entry(struct conf_entry *ent); -static struct conf_entry *init_entry(const char *fname, - struct conf_entry *src_entry); -static void parse_args(int argc, char **argv); -static int parse_doption(const char *doption); -static void usage(void); -static int log_trim(const char *logname, const struct conf_entry *log_ent); -static int age_old_log(char *file); -static void savelog(char *from, char *to); -static void createdir(const struct conf_entry *ent, char *dirpart); -static void createlog(const struct conf_entry *ent); - -/* - * All the following take a parameter of 'int', but expect values in the - * range of unsigned char. Define wrappers which take values of type 'char', - * whether signed or unsigned, and ensure they end up in the right range. - */ -#define isdigitch(Anychar) isdigit((u_char)(Anychar)) -#define isprintch(Anychar) isprint((u_char)(Anychar)) -#define isspacech(Anychar) isspace((u_char)(Anychar)) -#define tolowerch(Anychar) tolower((u_char)(Anychar)) - -int -main(int argc, char **argv) -{ - struct cflist *worklist; - struct conf_entry *p; - struct sigwork_entry *stmp; - struct zipwork_entry *ztmp; - - SLIST_INIT(&swhead); - SLIST_INIT(&zwhead); - - parse_args(argc, argv); - argc -= optind; - argv += optind; - - if (needroot && getuid() && geteuid()) - errx(1, "must have root privs"); - worklist = get_worklist(argv); - - /* - * Rotate all the files which need to be rotated. Note that - * some users have *hundreds* of entries in newsyslog.conf! - */ - while (!STAILQ_EMPTY(worklist)) { - p = STAILQ_FIRST(worklist); - STAILQ_REMOVE_HEAD(worklist, cf_nextp); - if (do_entry(p) == FREE_ENT) - free_entry(p); - } - - /* - * Send signals to any processes which need a signal to tell - * them to close and re-open the log file(s) we have rotated. - * Note that zipwork_entries include pointers to these - * sigwork_entry's, so we can not free the entries here. - */ - if (!SLIST_EMPTY(&swhead)) { - if (noaction || verbose) - printf("Signal all daemon process(es)...\n"); - SLIST_FOREACH(stmp, &swhead, sw_nextp) - do_sigwork(stmp); - if (noaction) - printf("\tsleep 10\n"); - else { - if (verbose) - printf("Pause 10 seconds to allow daemon(s)" - " to close log file(s)\n"); - sleep(10); - } - } - /* - * Compress all files that we're expected to compress, now - * that all processes should have closed the files which - * have been rotated. - */ - if (!SLIST_EMPTY(&zwhead)) { - if (noaction || verbose) - printf("Compress all rotated log file(s)...\n"); - while (!SLIST_EMPTY(&zwhead)) { - ztmp = SLIST_FIRST(&zwhead); - do_zipwork(ztmp); - SLIST_REMOVE_HEAD(&zwhead, zw_nextp); - free(ztmp); - } - } - /* Now free all the sigwork entries. */ - while (!SLIST_EMPTY(&swhead)) { - stmp = SLIST_FIRST(&swhead); - SLIST_REMOVE_HEAD(&swhead, sw_nextp); - free(stmp); - } - - while (wait(NULL) > 0 || errno == EINTR) - ; - return (0); -} - -static struct conf_entry * -init_entry(const char *fname, struct conf_entry *src_entry) -{ - struct conf_entry *tempwork; - - if (verbose > 4) - printf("\t--> [creating entry for %s]\n", fname); - - tempwork = malloc(sizeof(struct conf_entry)); - if (tempwork == NULL) - err(1, "malloc of conf_entry for %s", fname); - - if (destdir == NULL || fname[0] != '/') - tempwork->log = strdup(fname); - else - asprintf(&tempwork->log, "%s%s", destdir, fname); - if (tempwork->log == NULL) - err(1, "strdup for %s", fname); - - if (src_entry != NULL) { - tempwork->pid_file = NULL; - if (src_entry->pid_file) - tempwork->pid_file = strdup(src_entry->pid_file); - tempwork->r_reason = NULL; - tempwork->firstcreate = 0; - tempwork->rotate = 0; - tempwork->fsize = -1; - tempwork->uid = src_entry->uid; - tempwork->gid = src_entry->gid; - tempwork->numlogs = src_entry->numlogs; - tempwork->trsize = src_entry->trsize; - tempwork->hours = src_entry->hours; - tempwork->trim_at = NULL; - if (src_entry->trim_at != NULL) - tempwork->trim_at = ptime_init(src_entry->trim_at); - tempwork->permissions = src_entry->permissions; - tempwork->flags = src_entry->flags; - tempwork->sig = src_entry->sig; - tempwork->def_cfg = src_entry->def_cfg; - } else { - /* Initialize as a "do-nothing" entry */ - tempwork->pid_file = NULL; - tempwork->r_reason = NULL; - tempwork->firstcreate = 0; - tempwork->rotate = 0; - tempwork->fsize = -1; - tempwork->uid = (uid_t)-1; - tempwork->gid = (gid_t)-1; - tempwork->numlogs = 1; - tempwork->trsize = -1; - tempwork->hours = -1; - tempwork->trim_at = NULL; - tempwork->permissions = 0; - tempwork->flags = 0; - tempwork->sig = SIGHUP; - tempwork->def_cfg = 0; - } - - return (tempwork); -} - -static void -free_entry(struct conf_entry *ent) -{ - - if (ent == NULL) - return; - - if (ent->log != NULL) { - if (verbose > 4) - printf("\t--> [freeing entry for %s]\n", ent->log); - free(ent->log); - ent->log = NULL; - } - - if (ent->pid_file != NULL) { - free(ent->pid_file); - ent->pid_file = NULL; - } - - if (ent->r_reason != NULL) { - free(ent->r_reason); - ent->r_reason = NULL; - } - - if (ent->trim_at != NULL) { - ptime_free(ent->trim_at); - ent->trim_at = NULL; - } - - free(ent); -} - -static void -free_clist(struct cflist *list) -{ - struct conf_entry *ent; - - while (!STAILQ_EMPTY(list)) { - ent = STAILQ_FIRST(list); - STAILQ_REMOVE_HEAD(list, cf_nextp); - free_entry(ent); - } - - free(list); - list = NULL; -} - -static fk_entry -do_entry(struct conf_entry * ent) -{ -#define REASON_MAX 80 - int modtime; - fk_entry free_or_keep; - double diffsecs; - char temp_reason[REASON_MAX]; - - free_or_keep = FREE_ENT; - if (verbose) { - if (ent->flags & CE_COMPACT) - printf("%s <%dZ>: ", ent->log, ent->numlogs); - else if (ent->flags & CE_BZCOMPACT) - printf("%s <%dJ>: ", ent->log, ent->numlogs); - else - printf("%s <%d>: ", ent->log, ent->numlogs); - } - ent->fsize = sizefile(ent->log); - modtime = age_old_log(ent->log); - ent->rotate = 0; - ent->firstcreate = 0; - if (ent->fsize < 0) { - /* - * If either the C flag or the -C option was specified, - * and if we won't be creating the file, then have the - * verbose message include a hint as to why the file - * will not be created. - */ - temp_reason[0] = '\0'; - if (createlogs > 1) - ent->firstcreate = 1; - else if ((ent->flags & CE_CREATE) && createlogs) - ent->firstcreate = 1; - else if (ent->flags & CE_CREATE) - strlcpy(temp_reason, " (no -C option)", REASON_MAX); - else if (createlogs) - strlcpy(temp_reason, " (no C flag)", REASON_MAX); - - if (ent->firstcreate) { - if (verbose) - printf("does not exist -> will create.\n"); - createlog(ent); - } else if (verbose) { - printf("does not exist, skipped%s.\n", temp_reason); - } - } else { - if (ent->flags & CE_TRIMAT && !force && !rotatereq) { - diffsecs = ptimeget_diff(timenow, ent->trim_at); - if (diffsecs < 0.0) { - /* trim_at is some time in the future. */ - if (verbose) { - ptime_adjust4dst(ent->trim_at, - timenow); - printf("--> will trim at %s", - ptimeget_ctime(ent->trim_at)); - } - return (free_or_keep); - } else if (diffsecs >= 3600.0) { - /* - * trim_at is more than an hour in the past, - * so find the next valid trim_at time, and - * tell the user what that will be. - */ - if (verbose && dbg_at_times) - printf("\n\t--> prev trim at %s\t", - ptimeget_ctime(ent->trim_at)); - if (verbose) { - ptimeset_nxtime(ent->trim_at); - printf("--> will trim at %s", - ptimeget_ctime(ent->trim_at)); - } - return (free_or_keep); - } else if (verbose && noaction && dbg_at_times) { - /* - * If we are just debugging at-times, then - * a detailed message is helpful. Also - * skip "doing" any commands, since they - * would all be turned off by no-action. - */ - printf("\n\t--> timematch at %s", - ptimeget_ctime(ent->trim_at)); - return (free_or_keep); - } else if (verbose && ent->hours <= 0) { - printf("--> time is up\n"); - } - } - if (verbose && (ent->trsize > 0)) - printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize); - if (verbose && (ent->hours > 0)) - printf(" age (hr): %d [%d] ", modtime, ent->hours); - - /* - * Figure out if this logfile needs to be rotated. - */ - temp_reason[0] = '\0'; - if (rotatereq) { - ent->rotate = 1; - snprintf(temp_reason, REASON_MAX, " due to -R from %s", - requestor); - } else if (force) { - ent->rotate = 1; - snprintf(temp_reason, REASON_MAX, " due to -F request"); - } else if ((ent->trsize > 0) && (ent->fsize >= ent->trsize)) { - ent->rotate = 1; - snprintf(temp_reason, REASON_MAX, " due to size>%dK", - ent->trsize); - } else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) { - ent->rotate = 1; - } else if ((ent->hours > 0) && ((modtime >= ent->hours) || - (modtime < 0))) { - ent->rotate = 1; - } - - /* - * If the file needs to be rotated, then rotate it. - */ - if (ent->rotate && !norotate) { - if (temp_reason[0] != '\0') - ent->r_reason = strdup(temp_reason); - if (verbose) - printf("--> trimming log....\n"); - if (noaction && !verbose) { - if (ent->flags & CE_COMPACT) - printf("%s <%dZ>: trimming\n", - ent->log, ent->numlogs); - else if (ent->flags & CE_BZCOMPACT) - printf("%s <%dJ>: trimming\n", - ent->log, ent->numlogs); - else - printf("%s <%d>: trimming\n", - ent->log, ent->numlogs); - } - free_or_keep = do_rotate(ent); - } else { - if (verbose) - printf("--> skipping\n"); - } - } - return (free_or_keep); -#undef REASON_MAX -} - -static void -parse_args(int argc, char **argv) -{ - int ch; - char *p; - - timenow = ptime_init(NULL); - ptimeset_time(timenow, time(NULL)); - strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN); - - /* Let's get our hostname */ - (void)gethostname(hostname, sizeof(hostname)); - - /* Truncate domain */ - if ((p = strchr(hostname, '.')) != NULL) - *p = '\0'; - - /* Parse command line options. */ - while ((ch = getopt(argc, argv, "a:d:f:nrst:vCD:FNPR:")) != -1) - switch (ch) { - case 'a': - archtodir++; - archdirname = optarg; - break; - case 'd': - destdir = optarg; - break; - case 'f': - conf = optarg; - break; - case 'n': - noaction++; - break; - case 'r': - needroot = 0; - break; - case 's': - nosignal = 1; - break; - case 't': - if (optarg[0] == '\0' || - strcmp(optarg, "DEFAULT") == 0) - timefnamefmt = strdup(DEFAULT_TIMEFNAME_FMT); - else - timefnamefmt = strdup(optarg); - break; - case 'v': - verbose++; - break; - case 'C': - /* Useful for things like rc.diskless... */ - createlogs++; - break; - case 'D': - /* - * Set some debugging option. The specific option - * depends on the value of optarg. These options - * may come and go without notice or documentation. - */ - if (parse_doption(optarg)) - break; - usage(); - /* NOTREACHED */ - case 'F': - force++; - break; - case 'N': - norotate++; - break; - case 'P': - enforcepid++; - break; - case 'R': - rotatereq++; - requestor = strdup(optarg); - break; - case 'S': - path_syslogpid = optarg; - break; - case 'm': /* Used by OpenBSD for "monitor mode" */ - default: - usage(); - /* NOTREACHED */ - } - - if (force && norotate) { - warnx("Only one of -F and -N may be specified."); - usage(); - /* NOTREACHED */ - } - - if (rotatereq) { - if (optind == argc) { - warnx("At least one filename must be given when -R is specified."); - usage(); - /* NOTREACHED */ - } - /* Make sure "requestor" value is safe for a syslog message. */ - for (p = requestor; *p != '\0'; p++) { - if (!isprintch(*p) && (*p != '\t')) - *p = '.'; - } - } - - if (dbg_timenow) { - /* - * Note that the 'daytime' variable is not changed. - * That is only used in messages that track when a - * logfile is rotated, and if a file *is* rotated, - * then it will still rotated at the "real now" time. - */ - ptime_free(timenow); - timenow = dbg_timenow; - fprintf(stderr, "Debug: Running as if TimeNow is %s", - ptimeget_ctime(dbg_timenow)); - } - -} - -/* - * These debugging options are mainly meant for developer use, such - * as writing regression-tests. They would not be needed by users - * during normal operation of newsyslog... - */ -static int -parse_doption(const char *doption) -{ - const char TN[] = "TN="; - int res; - - if (strncmp(doption, TN, sizeof(TN) - 1) == 0) { - /* - * The "TimeNow" debugging option. This might be off - * by an hour when crossing a timezone change. - */ - dbg_timenow = ptime_init(NULL); - res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601, - time(NULL), doption + sizeof(TN) - 1); - if (res == -2) { - warnx("Non-existent time specified on -D %s", doption); - return (0); /* failure */ - } else if (res < 0) { - warnx("Malformed time given on -D %s", doption); - return (0); /* failure */ - } - return (1); /* successfully parsed */ - - } - - if (strcmp(doption, "ats") == 0) { - dbg_at_times++; - return (1); /* successfully parsed */ - } - - /* XXX - This check could probably be dropped. */ - if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder") - == 0)) { - warnx("NOTE: newsyslog always uses 'neworder'."); - return (1); /* successfully parsed */ - } - - warnx("Unknown -D (debug) option: '%s'", doption); - return (0); /* failure */ -} - -static void -usage(void) -{ - - fprintf(stderr, - "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f config-file]\n" - " [-S pidfile] [-t timefmt ] [ [-R requestor] filename ... ]\n"); - exit(1); -} - -/* - * Parse a configuration file and return a linked list of all the logs - * which should be processed. - */ -static struct cflist * -get_worklist(char **files) -{ - FILE *f; - char **given; - struct cflist *cmdlist, *filelist, *globlist; - struct conf_entry *defconf, *dupent, *ent; - struct ilist inclist; - struct include_entry *inc; - int gmatch, fnres; - - defconf = NULL; - STAILQ_INIT(&inclist); - - filelist = malloc(sizeof(struct cflist)); - if (filelist == NULL) - err(1, "malloc of filelist"); - STAILQ_INIT(filelist); - globlist = malloc(sizeof(struct cflist)); - if (globlist == NULL) - err(1, "malloc of globlist"); - STAILQ_INIT(globlist); - - inc = malloc(sizeof(struct include_entry)); - if (inc == NULL) - err(1, "malloc of inc"); - inc->file = conf; - if (inc->file == NULL) - inc->file = _PATH_CONF; - STAILQ_INSERT_TAIL(&inclist, inc, inc_nextp); - - STAILQ_FOREACH(inc, &inclist, inc_nextp) { - if (strcmp(inc->file, "-") != 0) - f = fopen(inc->file, "r"); - else { - f = stdin; - inc->file = "<stdin>"; - } - if (!f) - err(1, "%s", inc->file); - - if (verbose) - printf("Processing %s\n", inc->file); - parse_file(f, filelist, globlist, defconf, &inclist); - (void) fclose(f); - } - - /* - * All config-file information has been read in and turned into - * a filelist and a globlist. If there were no specific files - * given on the run command, then the only thing left to do is to - * call a routine which finds all files matched by the globlist - * and adds them to the filelist. Then return the worklist. - */ - if (*files == NULL) { - expand_globs(filelist, globlist); - free_clist(globlist); - if (defconf != NULL) - free_entry(defconf); - return (filelist); - /* NOTREACHED */ - } - - /* - * If newsyslog was given a specific list of files to process, - * it may be that some of those files were not listed in any - * config file. Those unlisted files should get the default - * rotation action. First, create the default-rotation action - * if none was found in a system config file. - */ - if (defconf == NULL) { - defconf = init_entry(DEFAULT_MARKER, NULL); - defconf->numlogs = 3; - defconf->trsize = 50; - defconf->permissions = S_IRUSR|S_IWUSR; - } - - /* - * If newsyslog was run with a list of specific filenames, - * then create a new worklist which has only those files in - * it, picking up the rotation-rules for those files from - * the original filelist. - * - * XXX - Note that this will copy multiple rules for a single - * logfile, if multiple entries are an exact match for - * that file. That matches the historic behavior, but do - * we want to continue to allow it? If so, it should - * probably be handled more intelligently. - */ - cmdlist = malloc(sizeof(struct cflist)); - if (cmdlist == NULL) - err(1, "malloc of cmdlist"); - STAILQ_INIT(cmdlist); - - for (given = files; *given; ++given) { - /* - * First try to find exact-matches for this given file. - */ - gmatch = 0; - STAILQ_FOREACH(ent, filelist, cf_nextp) { - if (strcmp(ent->log, *given) == 0) { - gmatch++; - dupent = init_entry(*given, ent); - STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); - } - } - if (gmatch) { - if (verbose > 2) - printf("\t+ Matched entry %s\n", *given); - continue; - } - - /* - * There was no exact-match for this given file, so look - * for a "glob" entry which does match. - */ - gmatch = 0; - if (verbose > 2 && globlist != NULL) - printf("\t+ Checking globs for %s\n", *given); - STAILQ_FOREACH(ent, globlist, cf_nextp) { - fnres = fnmatch(ent->log, *given, FNM_PATHNAME); - if (verbose > 2) - printf("\t+ = %d for pattern %s\n", fnres, - ent->log); - if (fnres == 0) { - gmatch++; - dupent = init_entry(*given, ent); - /* This new entry is not a glob! */ - dupent->flags &= ~CE_GLOB; - STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); - /* Only allow a match to one glob-entry */ - break; - } - } - if (gmatch) { - if (verbose > 2) - printf("\t+ Matched %s via %s\n", *given, - ent->log); - continue; - } - - /* - * This given file was not found in any config file, so - * add a worklist item based on the default entry. - */ - if (verbose > 2) - printf("\t+ No entry matched %s (will use %s)\n", - *given, DEFAULT_MARKER); - dupent = init_entry(*given, defconf); - /* Mark that it was *not* found in a config file */ - dupent->def_cfg = 1; - STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); - } - - /* - * Free all the entries in the original work list, the list of - * glob entries, and the default entry. - */ - free_clist(filelist); - free_clist(globlist); - free_entry(defconf); - - /* And finally, return a worklist which matches the given files. */ - return (cmdlist); -} - -/* - * Expand the list of entries with filename patterns, and add all files - * which match those glob-entries onto the worklist. - */ -static void -expand_globs(struct cflist *work_p, struct cflist *glob_p) -{ - int gmatch, gres; - size_t i; - char *mfname; - struct conf_entry *dupent, *ent, *globent; - glob_t pglob; - struct stat st_fm; - - /* - * The worklist contains all fully-specified (non-GLOB) names. - * - * Now expand the list of filename-pattern (GLOB) entries into - * a second list, which (by definition) will only match files - * that already exist. Do not add a glob-related entry for any - * file which already exists in the fully-specified list. - */ - STAILQ_FOREACH(globent, glob_p, cf_nextp) { - gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob); - if (gres != 0) { - warn("cannot expand pattern (%d): %s", gres, - globent->log); - continue; - } - - if (verbose > 2) - printf("\t+ Expanding pattern %s\n", globent->log); - for (i = 0; i < pglob.gl_matchc; i++) { - mfname = pglob.gl_pathv[i]; - - /* See if this file already has a specific entry. */ - gmatch = 0; - STAILQ_FOREACH(ent, work_p, cf_nextp) { - if (strcmp(mfname, ent->log) == 0) { - gmatch++; - break; - } - } - if (gmatch) - continue; - - /* Make sure the named matched is a file. */ - gres = lstat(mfname, &st_fm); - if (gres != 0) { - /* Error on a file that glob() matched?!? */ - warn("Skipping %s - lstat() error", mfname); - continue; - } - if (!S_ISREG(st_fm.st_mode)) { - /* We only rotate files! */ - if (verbose > 2) - printf("\t+ . skipping %s (!file)\n", - mfname); - continue; - } - - if (verbose > 2) - printf("\t+ . add file %s\n", mfname); - dupent = init_entry(mfname, globent); - /* This new entry is not a glob! */ - dupent->flags &= ~CE_GLOB; - - /* Add to the worklist. */ - STAILQ_INSERT_TAIL(work_p, dupent, cf_nextp); - } - globfree(&pglob); - if (verbose > 2) - printf("\t+ Done with pattern %s\n", globent->log); - } -} - -/* - * Parse a configuration file and update a linked list of all the logs to - * process. - */ -static void -parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, - struct conf_entry *defconf_p, struct ilist *inclist) -{ - char line[BUFSIZ], *parse, *q; - char *cp, *errline, *group; - struct conf_entry *working; - struct passwd *pwd; - struct group *grp; - glob_t pglob; - int eol, ptm_opts, res, special; - size_t i; - - errline = NULL; - while (fgets(line, BUFSIZ, cf)) { - if ((line[0] == '\n') || (line[0] == '#') || - (strlen(line) == 0)) - continue; - if (errline != NULL) - free(errline); - errline = strdup(line); - for (cp = line + 1; *cp != '\0'; cp++) { - if (*cp != '#') - continue; - if (*(cp - 1) == '\\') { - strcpy(cp - 1, cp); - cp--; - continue; - } - *cp = '\0'; - break; - } - - q = parse = missing_field(sob(line), errline); - parse = son(line); - if (!*parse) - errx(1, "malformed line (missing fields):\n%s", - errline); - *parse = '\0'; - - /* - * Allow people to set debug options via the config file. - * (NOTE: debug options are undocumented, and may disappear - * at any time, etc). - */ - if (strcasecmp(DEBUG_MARKER, q) == 0) { - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) - warnx("debug line specifies no option:\n%s", - errline); - else { - *parse = '\0'; - parse_doption(q); - } - continue; - } else if (strcasecmp(INCLUDE_MARKER, q) == 0) { - if (verbose) - printf("Found: %s", errline); - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) { - warnx("include line missing argument:\n%s", - errline); - continue; - } - - *parse = '\0'; - - if (isglobstr(q)) { - res = glob(q, GLOB_NOCHECK, NULL, &pglob); - if (res != 0) { - warn("cannot expand pattern (%d): %s", - res, q); - continue; - } - - if (verbose > 2) - printf("\t+ Expanding pattern %s\n", q); - - for (i = 0; i < pglob.gl_matchc; i++) - add_to_queue(pglob.gl_pathv[i], - inclist); - globfree(&pglob); - } else - add_to_queue(q, inclist); - continue; - } - - special = 0; - working = init_entry(q, NULL); - if (strcasecmp(DEFAULT_MARKER, q) == 0) { - special = 1; - if (defconf_p != NULL) { - warnx("Ignoring duplicate entry for %s!", q); - free_entry(working); - continue; - } - defconf_p = working; - } - - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) - errx(1, "malformed line (missing fields):\n%s", - errline); - *parse = '\0'; - if ((group = strchr(q, ':')) != NULL || - (group = strrchr(q, '.')) != NULL) { - *group++ = '\0'; - if (*q) { - if (!(isnumberstr(q))) { - if ((pwd = getpwnam(q)) == NULL) - errx(1, - "error in config file; unknown user:\n%s", - errline); - working->uid = pwd->pw_uid; - } else - working->uid = atoi(q); - } else - working->uid = (uid_t)-1; - - q = group; - if (*q) { - if (!(isnumberstr(q))) { - if ((grp = getgrnam(q)) == NULL) - errx(1, - "error in config file; unknown group:\n%s", - errline); - working->gid = grp->gr_gid; - } else - working->gid = atoi(q); - } else - working->gid = (gid_t)-1; - - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) - errx(1, "malformed line (missing fields):\n%s", - errline); - *parse = '\0'; - } else { - working->uid = (uid_t)-1; - working->gid = (gid_t)-1; - } - - if (!sscanf(q, "%o", &working->permissions)) - errx(1, "error in config file; bad permissions:\n%s", - errline); - - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) - errx(1, "malformed line (missing fields):\n%s", - errline); - *parse = '\0'; - if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0) - errx(1, "error in config file; bad value for count of logs to save:\n%s", - errline); - - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - if (!*parse) - errx(1, "malformed line (missing fields):\n%s", - errline); - *parse = '\0'; - if (isdigitch(*q)) - working->trsize = atoi(q); - else if (strcmp(q, "*") == 0) - working->trsize = -1; - else { - warnx("Invalid value of '%s' for 'size' in line:\n%s", - q, errline); - working->trsize = -1; - } - - working->flags = 0; - q = parse = missing_field(sob(++parse), errline); - parse = son(parse); - eol = !*parse; - *parse = '\0'; - { - char *ep; - u_long ul; - - ul = strtoul(q, &ep, 10); - if (ep == q) - working->hours = 0; - else if (*ep == '*') - working->hours = -1; - else if (ul > INT_MAX) - errx(1, "interval is too large:\n%s", errline); - else - working->hours = ul; - - if (*ep == '\0' || strcmp(ep, "*") == 0) - goto no_trimat; - if (*ep != '@' && *ep != '$') - errx(1, "malformed interval/at:\n%s", errline); - - working->flags |= CE_TRIMAT; - working->trim_at = ptime_init(NULL); - ptm_opts = PTM_PARSE_ISO8601; - if (*ep == '$') - ptm_opts = PTM_PARSE_DWM; - ptm_opts |= PTM_PARSE_MATCHDOM; - res = ptime_relparse(working->trim_at, ptm_opts, - ptimeget_secs(timenow), ep + 1); - if (res == -2) - errx(1, "nonexistent time for 'at' value:\n%s", - errline); - else if (res < 0) - errx(1, "malformed 'at' value:\n%s", errline); - } -no_trimat: - - if (eol) - q = NULL; - else { - q = parse = sob(++parse); /* Optional field */ - parse = son(parse); - if (!*parse) - eol = 1; - *parse = '\0'; - } - - for (; q && *q && !isspacech(*q); q++) { - switch (tolowerch(*q)) { - case 'b': - working->flags |= CE_BINARY; - break; - case 'c': - /* - * XXX - Ick! Ugly! Remove ASAP! - * We want `c' and `C' for "create". But we - * will temporarily treat `c' as `g', because - * FreeBSD releases <= 4.8 have a typo of - * checking ('G' || 'c') for CE_GLOB. - */ - if (*q == 'c') { - warnx("Assuming 'g' for 'c' in flags for line:\n%s", - errline); - warnx("The 'c' flag will eventually mean 'CREATE'"); - working->flags |= CE_GLOB; - break; - } - working->flags |= CE_CREATE; - break; - case 'd': - working->flags |= CE_NODUMP; - break; - case 'g': - working->flags |= CE_GLOB; - break; - case 'j': - working->flags |= CE_BZCOMPACT; - break; - case 'n': - working->flags |= CE_NOSIGNAL; - break; - case 'u': - working->flags |= CE_SIGNALGROUP; - break; - case 'w': - /* Depreciated flag - keep for compatibility purposes */ - break; - case 'z': - working->flags |= CE_COMPACT; - break; - case '-': - break; - case 'f': /* Used by OpenBSD for "CE_FOLLOW" */ - case 'm': /* Used by OpenBSD for "CE_MONITOR" */ - case 'p': /* Used by NetBSD for "CE_PLAIN0" */ - default: - errx(1, "illegal flag in config file -- %c", - *q); - } - } - - if (eol) - q = NULL; - else { - q = parse = sob(++parse); /* Optional field */ - parse = son(parse); - if (!*parse) - eol = 1; - *parse = '\0'; - } - - working->pid_file = NULL; - if (q && *q) { - if (*q == '/') - working->pid_file = strdup(q); - else if (isdigit(*q)) - goto got_sig; - else - errx(1, - "illegal pid file or signal number in config file:\n%s", - errline); - } - if (eol) - q = NULL; - else { - q = parse = sob(++parse); /* Optional field */ - *(parse = son(parse)) = '\0'; - } - - working->sig = SIGHUP; - if (q && *q) { - if (isdigit(*q)) { - got_sig: - working->sig = atoi(q); - } else { - err_sig: - errx(1, - "illegal signal number in config file:\n%s", - errline); - } - if (working->sig < 1 || working->sig >= NSIG) - goto err_sig; - } - - /* - * Finish figuring out what pid-file to use (if any) in - * later processing if this logfile needs to be rotated. - */ - if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) { - /* - * This config-entry specified 'n' for nosignal, - * see if it also specified an explicit pid_file. - * This would be a pretty pointless combination. - */ - if (working->pid_file != NULL) { - warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s", - working->pid_file, errline); - free(working->pid_file); - working->pid_file = NULL; - } - } else if (working->pid_file == NULL) { - /* - * This entry did not specify the 'n' flag, which - * means it should signal syslogd unless it had - * specified some other pid-file (and obviously the - * syslog pid-file will not be for a process-group). - * Also, we should only try to notify syslog if we - * are root. - */ - if (working->flags & CE_SIGNALGROUP) { - warnx("Ignoring flag 'U' in line:\n%s", - errline); - working->flags &= ~CE_SIGNALGROUP; - } - if (needroot) - working->pid_file = strdup(path_syslogpid); - } - - /* - * Add this entry to the appropriate list of entries, unless - * it was some kind of special entry (eg: <default>). - */ - if (special) { - ; /* Do not add to any list */ - } else if (working->flags & CE_GLOB) { - STAILQ_INSERT_TAIL(glob_p, working, cf_nextp); - } else { - STAILQ_INSERT_TAIL(work_p, working, cf_nextp); - } - } - if (errline != NULL) - free(errline); -} - -static char * -missing_field(char *p, char *errline) -{ - - if (!p || !*p) - errx(1, "missing field in config file:\n%s", errline); - return (p); -} - -/* - * In our sort we return it in the reverse of what qsort normally - * would do, as we want the newest files first. If we have two - * entries with the same time we don't really care about order. - * - * Support function for qsort() in delete_oldest_timelog(). - */ -static int -oldlog_entry_compare(const void *a, const void *b) -{ - const struct oldlog_entry *ola = a, *olb = b; - - if (ola->t > olb->t) - return (-1); - else if (ola->t < olb->t) - return (1); - else - return (0); -} - -/* - * Delete the oldest logfiles, when using time based filenames. - */ -static void -delete_oldest_timelog(const struct conf_entry *ent, const char *archive_dir) -{ - char *logfname, *s, *dir, errbuf[80]; - int logcnt, max_logcnt, dirfd, i; - struct oldlog_entry *oldlogs; - size_t logfname_len; - struct dirent *dp; - const char *cdir; - struct tm tm; - DIR *dirp; - - oldlogs = malloc(MAX_OLDLOGS * sizeof(struct oldlog_entry)); - max_logcnt = MAX_OLDLOGS; - logcnt = 0; - - if (archive_dir != NULL && archive_dir[0] != '\0') - cdir = archive_dir; - else - if ((cdir = dirname(ent->log)) == NULL) - err(1, "dirname()"); - if ((dir = strdup(cdir)) == NULL) - err(1, "strdup()"); - - if ((s = basename(ent->log)) == NULL) - err(1, "basename()"); - if ((logfname = strdup(s)) == NULL) - err(1, "strdup()"); - logfname_len = strlen(logfname); - if (strcmp(logfname, "/") == 0) - errx(1, "Invalid log filename - became '/'"); - - if (verbose > 2) - printf("Searching for old logs in %s\n", dir); - - /* First we create a 'list' of all archived logfiles */ - if ((dirp = opendir(dir)) == NULL) - err(1, "Cannot open log directory '%s'", dir); - dirfd = dirfd(dirp); - while ((dp = readdir(dirp)) != NULL) { - if (dp->d_type != DT_REG) - continue; - - /* Ignore everything but files with our logfile prefix */ - if (strncmp(dp->d_name, logfname, logfname_len) != 0) - continue; - /* Ignore the actual non-rotated logfile */ - if (dp->d_namlen == logfname_len) - continue; - /* - * Make sure we created have found a logfile, so the - * postfix is valid, IE format is: '.<time>(.[bg]z)?'. - */ - if (dp->d_name[logfname_len] != '.') { - if (verbose) - printf("Ignoring %s which has unexpected " - "extension '%s'\n", dp->d_name, - &dp->d_name[logfname_len]); - continue; - } - if ((s = strptime(&dp->d_name[logfname_len + 1], - timefnamefmt, &tm)) == NULL) { - /* - * We could special case "old" sequentially - * named logfiles here, but we do not as that - * would require special handling to decide - * which one was the oldest compared to "new" - * time based logfiles. - */ - if (verbose) - printf("Ignoring %s which does not " - "match time format\n", dp->d_name); - continue; - } - if (*s != '\0' && !(strcmp(s, BZCOMPRESS_POSTFIX) == 0 || - strcmp(s, COMPRESS_POSTFIX) == 0)) { - if (verbose) - printf("Ignoring %s which has unexpected " - "extension '%s'\n", dp->d_name, s); - continue; - } - - /* - * We should now have old an old rotated logfile, so - * add it to the 'list'. - */ - if ((oldlogs[logcnt].t = timegm(&tm)) == -1) - err(1, "Could not convert time string to time value"); - if ((oldlogs[logcnt].fname = strdup(dp->d_name)) == NULL) - err(1, "strdup()"); - logcnt++; - - /* - * It is very unlikely we ever run out of space in the - * logfile array from the default size, but lets - * handle it anyway... - */ - if (logcnt >= max_logcnt) { - max_logcnt *= 4; - /* Detect integer overflow */ - if (max_logcnt < logcnt) - errx(1, "Too many old logfiles found"); - oldlogs = realloc(oldlogs, - max_logcnt * sizeof(struct oldlog_entry)); - if (oldlogs == NULL) - err(1, "realloc()"); - } - } - - /* Second, if needed we delete oldest archived logfiles */ - if (logcnt > 0 && logcnt >= ent->numlogs && ent->numlogs > 1) { - oldlogs = realloc(oldlogs, logcnt * - sizeof(struct oldlog_entry)); - if (oldlogs == NULL) - err(1, "realloc()"); - - /* - * We now sort the logs in the order of newest to - * oldest. That way we can simply skip over the - * number of records we want to keep. - */ - qsort(oldlogs, logcnt, sizeof(struct oldlog_entry), - oldlog_entry_compare); - for (i = ent->numlogs - 1; i < logcnt; i++) { - if (noaction) - printf("\trm -f %s/%s\n", dir, - oldlogs[i].fname); - else if (unlinkat(dirfd, oldlogs[i].fname, 0) != 0) { - snprintf(errbuf, sizeof(errbuf), - "Could not delet old logfile '%s'", - oldlogs[i].fname); - perror(errbuf); - } - } - } else if (verbose > 1) - printf("No old logs to delete for logfile %s\n", ent->log); - - /* Third, cleanup */ - closedir(dirp); - for (i = 0; i < logcnt; i++) { - assert(oldlogs[i].fname != NULL); - free(oldlogs[i].fname); - } - free(oldlogs); - free(logfname); - free(dir); -} - -/* - * Only add to the queue if the file hasn't already been added. This is - * done to prevent circular include loops. - */ -static void -add_to_queue(const char *fname, struct ilist *inclist) -{ - struct include_entry *inc; - - STAILQ_FOREACH(inc, inclist, inc_nextp) { - if (strcmp(fname, inc->file) == 0) { - warnx("duplicate include detected: %s", fname); - return; - } - } - - inc = malloc(sizeof(struct include_entry)); - if (inc == NULL) - err(1, "malloc of inc"); - inc->file = strdup(fname); - - if (verbose > 2) - printf("\t+ Adding %s to the processing queue.\n", fname); - - STAILQ_INSERT_TAIL(inclist, inc, inc_nextp); -} - -static fk_entry -do_rotate(const struct conf_entry *ent) -{ - char dirpart[MAXPATHLEN], namepart[MAXPATHLEN]; - char file1[MAXPATHLEN], file2[MAXPATHLEN]; - char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN]; - char jfile1[MAXPATHLEN]; - char datetimestr[30]; - int flags, numlogs_c; - fk_entry free_or_keep; - struct sigwork_entry *swork; - struct stat st; - struct tm tm; - time_t now; - - flags = ent->flags; - free_or_keep = FREE_ENT; - - if (archtodir) { - char *p; - - /* build complete name of archive directory into dirpart */ - if (*archdirname == '/') { /* absolute */ - strlcpy(dirpart, archdirname, sizeof(dirpart)); - } else { /* relative */ - /* get directory part of logfile */ - strlcpy(dirpart, ent->log, sizeof(dirpart)); - if ((p = rindex(dirpart, '/')) == NULL) - dirpart[0] = '\0'; - else - *(p + 1) = '\0'; - strlcat(dirpart, archdirname, sizeof(dirpart)); - } - - /* check if archive directory exists, if not, create it */ - if (lstat(dirpart, &st)) - createdir(ent, dirpart); - - /* get filename part of logfile */ - if ((p = rindex(ent->log, '/')) == NULL) - strlcpy(namepart, ent->log, sizeof(namepart)); - else - strlcpy(namepart, p + 1, sizeof(namepart)); - - /* name of oldest log */ - (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart, - namepart, ent->numlogs); - } else { - /* - * Tell delete_oldest_timelog() we are not using an - * archive dir. - */ - dirpart[0] = '\0'; - - /* name of oldest log */ - (void) snprintf(file1, sizeof(file1), "%s.%d", ent->log, - ent->numlogs); - } - - /* Delete old logs */ - if (timefnamefmt != NULL) - delete_oldest_timelog(ent, dirpart); - else { - /* name of oldest log */ - (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1, - COMPRESS_POSTFIX); - snprintf(jfile1, sizeof(jfile1), "%s%s", file1, - BZCOMPRESS_POSTFIX); - - if (noaction) { - printf("\trm -f %s\n", file1); - printf("\trm -f %s\n", zfile1); - printf("\trm -f %s\n", jfile1); - } else { - (void) unlink(file1); - (void) unlink(zfile1); - (void) unlink(jfile1); - } - } - - if (timefnamefmt != NULL) { - /* If time functions fails we can't really do any sensible */ - if (time(&now) == (time_t)-1 || - localtime_r(&now, &tm) == NULL) - bzero(&tm, sizeof(tm)); - - strftime(datetimestr, sizeof(datetimestr), timefnamefmt, &tm); - if (archtodir) - (void) snprintf(file1, sizeof(file1), "%s/%s.%s", - dirpart, namepart, datetimestr); - else - (void) snprintf(file1, sizeof(file1), "%s.%s", - ent->log, datetimestr); - - /* Don't run the code to move down logs */ - numlogs_c = 0; - } else - numlogs_c = ent->numlogs; /* copy for countdown */ - - /* Move down log files */ - while (numlogs_c--) { - - (void) strlcpy(file2, file1, sizeof(file2)); - - if (archtodir) - (void) snprintf(file1, sizeof(file1), "%s/%s.%d", - dirpart, namepart, numlogs_c); - else - (void) snprintf(file1, sizeof(file1), "%s.%d", - ent->log, numlogs_c); - - (void) strlcpy(zfile1, file1, sizeof(zfile1)); - (void) strlcpy(zfile2, file2, sizeof(zfile2)); - if (lstat(file1, &st)) { - (void) strlcat(zfile1, COMPRESS_POSTFIX, - sizeof(zfile1)); - (void) strlcat(zfile2, COMPRESS_POSTFIX, - sizeof(zfile2)); - if (lstat(zfile1, &st)) { - strlcpy(zfile1, file1, sizeof(zfile1)); - strlcpy(zfile2, file2, sizeof(zfile2)); - strlcat(zfile1, BZCOMPRESS_POSTFIX, - sizeof(zfile1)); - strlcat(zfile2, BZCOMPRESS_POSTFIX, - sizeof(zfile2)); - if (lstat(zfile1, &st)) - continue; - } - } - if (noaction) - printf("\tmv %s %s\n", zfile1, zfile2); - else { - /* XXX - Ought to be checking for failure! */ - (void)rename(zfile1, zfile2); - } - change_attrs(zfile2, ent); - } - - if (ent->numlogs > 0) { - if (noaction) { - /* - * Note that savelog() may succeed with using link() - * for the archtodir case, but there is no good way - * of knowing if it will when doing "noaction", so - * here we claim that it will have to do a copy... - */ - if (archtodir) - printf("\tcp %s %s\n", ent->log, file1); - else - printf("\tln %s %s\n", ent->log, file1); - } else { - if (!(flags & CE_BINARY)) { - /* Report the trimming to the old log */ - log_trim(ent->log, ent); - } - savelog(ent->log, file1); - } - change_attrs(file1, ent); - } - - /* Create the new log file and move it into place */ - if (noaction) - printf("Start new log...\n"); - createlog(ent); - - /* - * Save all signalling and file-compression to be done after log - * files from all entries have been rotated. This way any one - * process will not be sent the same signal multiple times when - * multiple log files had to be rotated. - */ - swork = NULL; - if (ent->pid_file != NULL) - swork = save_sigwork(ent); - if (ent->numlogs > 0 && (flags & (CE_COMPACT | CE_BZCOMPACT))) { - /* - * The zipwork_entry will include a pointer to this - * conf_entry, so the conf_entry should not be freed. - */ - free_or_keep = KEEP_ENT; - save_zipwork(ent, swork, ent->fsize, file1); - } - - return (free_or_keep); -} - -static void -do_sigwork(struct sigwork_entry *swork) -{ - struct sigwork_entry *nextsig; - int kres, secs; - - if (!(swork->sw_pidok) || swork->sw_pid == 0) - return; /* no work to do... */ - - /* - * If nosignal (-s) was specified, then do not signal any process. - * Note that a nosignal request triggers a warning message if the - * rotated logfile needs to be compressed, *unless* -R was also - * specified. We assume that an `-sR' request came from a process - * which writes to the logfile, and as such, we assume that process - * has already made sure the logfile is not presently in use. This - * just sets swork->sw_pidok to a special value, and do_zipwork - * will print any necessary warning(s). - */ - if (nosignal) { - if (!rotatereq) - swork->sw_pidok = -1; - return; - } - - /* - * Compute the pause between consecutive signals. Use a longer - * sleep time if we will be sending two signals to the same - * deamon or process-group. - */ - secs = 0; - nextsig = SLIST_NEXT(swork, sw_nextp); - if (nextsig != NULL) { - if (swork->sw_pid == nextsig->sw_pid) - secs = 10; - else - secs = 1; - } - - if (noaction) { - printf("\tkill -%d %d \t\t# %s\n", swork->sw_signum, - (int)swork->sw_pid, swork->sw_fname); - if (secs > 0) - printf("\tsleep %d\n", secs); - return; - } - - kres = kill(swork->sw_pid, swork->sw_signum); - if (kres != 0) { - /* - * Assume that "no such process" (ESRCH) is something - * to warn about, but is not an error. Presumably the - * process which writes to the rotated log file(s) is - * gone, in which case we should have no problem with - * compressing the rotated log file(s). - */ - if (errno != ESRCH) - swork->sw_pidok = 0; - warn("can't notify %s, pid %d", swork->sw_pidtype, - (int)swork->sw_pid); - } else { - if (verbose) - printf("Notified %s pid %d = %s\n", swork->sw_pidtype, - (int)swork->sw_pid, swork->sw_fname); - if (secs > 0) { - if (verbose) - printf("Pause %d second(s) between signals\n", - secs); - sleep(secs); - } - } -} - -static void -do_zipwork(struct zipwork_entry *zwork) -{ - const char *pgm_name, *pgm_path; - int errsav, fcount, zstatus; - pid_t pidzip, wpid; - char zresult[MAXPATHLEN]; - - pgm_path = NULL; - strlcpy(zresult, zwork->zw_fname, sizeof(zresult)); - if (zwork != NULL && zwork->zw_conf != NULL) { - if (zwork->zw_conf->flags & CE_COMPACT) { - pgm_path = _PATH_GZIP; - strlcat(zresult, COMPRESS_POSTFIX, sizeof(zresult)); - } else if (zwork->zw_conf->flags & CE_BZCOMPACT) { - pgm_path = _PATH_BZIP2; - strlcat(zresult, BZCOMPRESS_POSTFIX, sizeof(zresult)); - } - } - if (pgm_path == NULL) { - warnx("invalid entry for %s in do_zipwork", zwork->zw_fname); - return; - } - pgm_name = strrchr(pgm_path, '/'); - if (pgm_name == NULL) - pgm_name = pgm_path; - else - pgm_name++; - - if (zwork->zw_swork != NULL && zwork->zw_swork->sw_pidok <= 0) { - warnx( - "log %s not compressed because daemon(s) not notified", - zwork->zw_fname); - change_attrs(zwork->zw_fname, zwork->zw_conf); - return; - } - - if (noaction) { - printf("\t%s %s\n", pgm_name, zwork->zw_fname); - change_attrs(zresult, zwork->zw_conf); - return; - } - - fcount = 1; - pidzip = fork(); - while (pidzip < 0) { - /* - * The fork failed. If the failure was due to a temporary - * problem, then wait a short time and try it again. - */ - errsav = errno; - warn("fork() for `%s %s'", pgm_name, zwork->zw_fname); - if (errsav != EAGAIN || fcount > 5) - errx(1, "Exiting..."); - sleep(fcount * 12); - fcount++; - pidzip = fork(); - } - if (!pidzip) { - /* The child process executes the compression command */ - execl(pgm_path, pgm_path, "-f", zwork->zw_fname, (char *)0); - err(1, "execl(`%s -f %s')", pgm_path, zwork->zw_fname); - } - - wpid = waitpid(pidzip, &zstatus, 0); - if (wpid == -1) { - /* XXX - should this be a fatal error? */ - warn("%s: waitpid(%d)", pgm_path, pidzip); - return; - } - if (!WIFEXITED(zstatus)) { - warnx("`%s -f %s' did not terminate normally", pgm_name, - zwork->zw_fname); - return; - } - if (WEXITSTATUS(zstatus)) { - warnx("`%s -f %s' terminated with a non-zero status (%d)", - pgm_name, zwork->zw_fname, WEXITSTATUS(zstatus)); - return; - } - - /* Compression was successful, set file attributes on the result. */ - change_attrs(zresult, zwork->zw_conf); -} - -/* - * Save information on any process we need to signal. Any single - * process may need to be sent different signal-values for different - * log files, but usually a single signal-value will cause the process - * to close and re-open all of it's log files. - */ -static struct sigwork_entry * -save_sigwork(const struct conf_entry *ent) -{ - struct sigwork_entry *sprev, *stmp; - int ndiff; - size_t tmpsiz; - - sprev = NULL; - ndiff = 1; - SLIST_FOREACH(stmp, &swhead, sw_nextp) { - ndiff = strcmp(ent->pid_file, stmp->sw_fname); - if (ndiff > 0) - break; - if (ndiff == 0) { - if (ent->sig == stmp->sw_signum) - break; - if (ent->sig > stmp->sw_signum) { - ndiff = 1; - break; - } - } - sprev = stmp; - } - if (stmp != NULL && ndiff == 0) - return (stmp); - - tmpsiz = sizeof(struct sigwork_entry) + strlen(ent->pid_file) + 1; - stmp = malloc(tmpsiz); - set_swpid(stmp, ent); - stmp->sw_signum = ent->sig; - strcpy(stmp->sw_fname, ent->pid_file); - if (sprev == NULL) - SLIST_INSERT_HEAD(&swhead, stmp, sw_nextp); - else - SLIST_INSERT_AFTER(sprev, stmp, sw_nextp); - return (stmp); -} - -/* - * Save information on any file we need to compress. We may see the same - * file multiple times, so check the full list to avoid duplicates. The - * list itself is sorted smallest-to-largest, because that's the order we - * want to compress the files. If the partition is very low on disk space, - * then the smallest files are the most likely to compress, and compressing - * them first will free up more space for the larger files. - */ -static struct zipwork_entry * -save_zipwork(const struct conf_entry *ent, const struct sigwork_entry *swork, - int zsize, const char *zipfname) -{ - struct zipwork_entry *zprev, *ztmp; - int ndiff; - size_t tmpsiz; - - /* Compute the size if the caller did not know it. */ - if (zsize < 0) - zsize = sizefile(zipfname); - - zprev = NULL; - ndiff = 1; - SLIST_FOREACH(ztmp, &zwhead, zw_nextp) { - ndiff = strcmp(zipfname, ztmp->zw_fname); - if (ndiff == 0) - break; - if (zsize > ztmp->zw_fsize) - zprev = ztmp; - } - if (ztmp != NULL && ndiff == 0) - return (ztmp); - - tmpsiz = sizeof(struct zipwork_entry) + strlen(zipfname) + 1; - ztmp = malloc(tmpsiz); - ztmp->zw_conf = ent; - ztmp->zw_swork = swork; - ztmp->zw_fsize = zsize; - strcpy(ztmp->zw_fname, zipfname); - if (zprev == NULL) - SLIST_INSERT_HEAD(&zwhead, ztmp, zw_nextp); - else - SLIST_INSERT_AFTER(zprev, ztmp, zw_nextp); - return (ztmp); -} - -/* Send a signal to the pid specified by pidfile */ -static void -set_swpid(struct sigwork_entry *swork, const struct conf_entry *ent) -{ - FILE *f; - long minok, maxok, rval; - char *endp, *linep, line[BUFSIZ]; - - minok = MIN_PID; - maxok = MAX_PID; - swork->sw_pidok = 0; - swork->sw_pid = 0; - swork->sw_pidtype = "daemon"; - if (ent->flags & CE_SIGNALGROUP) { - /* - * If we are expected to signal a process-group when - * rotating this logfile, then the value read in should - * be the negative of a valid process ID. - */ - minok = -MAX_PID; - maxok = -MIN_PID; - swork->sw_pidtype = "process-group"; - } - - f = fopen(ent->pid_file, "r"); - if (f == NULL) { - if (errno == ENOENT && enforcepid == 0) { - /* - * Warn if the PID file doesn't exist, but do - * not consider it an error. Most likely it - * means the process has been terminated, - * so it should be safe to rotate any log - * files that the process would have been using. - */ - swork->sw_pidok = 1; - warnx("pid file doesn't exist: %s", ent->pid_file); - } else - warn("can't open pid file: %s", ent->pid_file); - return; - } - - if (fgets(line, BUFSIZ, f) == NULL) { - /* - * Warn if the PID file is empty, but do not consider - * it an error. Most likely it means the process has - * has terminated, so it should be safe to rotate any - * log files that the process would have been using. - */ - if (feof(f) && enforcepid == 0) { - swork->sw_pidok = 1; - warnx("pid file is empty: %s", ent->pid_file); - } else - warn("can't read from pid file: %s", ent->pid_file); - (void)fclose(f); - return; - } - (void)fclose(f); - - errno = 0; - linep = line; - while (*linep == ' ') - linep++; - rval = strtol(linep, &endp, 10); - if (*endp != '\0' && !isspacech(*endp)) { - warnx("pid file does not start with a valid number: %s", - ent->pid_file); - } else if (rval < minok || rval > maxok) { - warnx("bad value '%ld' for process number in %s", - rval, ent->pid_file); - if (verbose) - warnx("\t(expecting value between %ld and %ld)", - minok, maxok); - } else { - swork->sw_pidok = 1; - swork->sw_pid = rval; - } - - return; -} - -/* Log the fact that the logs were turned over */ -static int -log_trim(const char *logname, const struct conf_entry *log_ent) -{ - FILE *f; - const char *xtra; - - if ((f = fopen(logname, "a")) == NULL) - return (-1); - xtra = ""; - if (log_ent->def_cfg) - xtra = " using <default> rule"; - if (log_ent->firstcreate) - fprintf(f, "%s %s newsyslog[%d]: logfile first created%s\n", - daytime, hostname, (int) getpid(), xtra); - else if (log_ent->r_reason != NULL) - fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s%s\n", - daytime, hostname, (int) getpid(), log_ent->r_reason, xtra); - else - fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s\n", - daytime, hostname, (int) getpid(), xtra); - if (fclose(f) == EOF) - err(1, "log_trim: fclose"); - return (0); -} - -/* Return size in kilobytes of a file */ -static int -sizefile(const char *file) -{ - struct stat sb; - - if (stat(file, &sb) < 0) - return (-1); - return (kbytes(dbtob(sb.st_blocks))); -} - -/* Return the age of old log file (file.0) */ -static int -age_old_log(char *file) -{ - struct stat sb; - char *endp; - char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + - sizeof(BZCOMPRESS_POSTFIX) + 1]; - - if (archtodir) { - char *p; - - /* build name of archive directory into tmp */ - if (*archdirname == '/') { /* absolute */ - strlcpy(tmp, archdirname, sizeof(tmp)); - } else { /* relative */ - /* get directory part of logfile */ - strlcpy(tmp, file, sizeof(tmp)); - if ((p = rindex(tmp, '/')) == NULL) - tmp[0] = '\0'; - else - *(p + 1) = '\0'; - strlcat(tmp, archdirname, sizeof(tmp)); - } - - strlcat(tmp, "/", sizeof(tmp)); - - /* get filename part of logfile */ - if ((p = rindex(file, '/')) == NULL) - strlcat(tmp, file, sizeof(tmp)); - else - strlcat(tmp, p + 1, sizeof(tmp)); - } else { - (void) strlcpy(tmp, file, sizeof(tmp)); - } - - strlcat(tmp, ".0", sizeof(tmp)); - if (stat(tmp, &sb) < 0) { - /* - * A plain '.0' file does not exist. Try again, first - * with the added suffix of '.gz', then with an added - * suffix of '.bz2' instead of '.gz'. - */ - endp = strchr(tmp, '\0'); - strlcat(tmp, COMPRESS_POSTFIX, sizeof(tmp)); - if (stat(tmp, &sb) < 0) { - *endp = '\0'; /* Remove .gz */ - strlcat(tmp, BZCOMPRESS_POSTFIX, sizeof(tmp)); - if (stat(tmp, &sb) < 0) - return (-1); - } - } - return ((int)(ptimeget_secs(timenow) - sb.st_mtime + 1800) / 3600); -} - -/* Skip Over Blanks */ -static char * -sob(char *p) -{ - while (p && *p && isspace(*p)) - p++; - return (p); -} - -/* Skip Over Non-Blanks */ -static char * -son(char *p) -{ - while (p && *p && !isspace(*p)) - p++; - return (p); -} - -/* Check if string is actually a number */ -static int -isnumberstr(const char *string) -{ - while (*string) { - if (!isdigitch(*string++)) - return (0); - } - return (1); -} - -/* Check if string contains a glob */ -static int -isglobstr(const char *string) -{ - char chr; - - while ((chr = *string++)) { - if (chr == '*' || chr == '?' || chr == '[') - return (1); - } - return (0); -} - -/* - * Save the active log file under a new name. A link to the new name - * is the quick-and-easy way to do this. If that fails (which it will - * if the destination is on another partition), then make a copy of - * the file to the new location. - */ -static void -savelog(char *from, char *to) -{ - FILE *src, *dst; - int c, res; - - res = link(from, to); - if (res == 0) - return; - - if ((src = fopen(from, "r")) == NULL) - err(1, "can't fopen %s for reading", from); - if ((dst = fopen(to, "w")) == NULL) - err(1, "can't fopen %s for writing", to); - - while ((c = getc(src)) != EOF) { - if ((putc(c, dst)) == EOF) - err(1, "error writing to %s", to); - } - - if (ferror(src)) - err(1, "error reading from %s", from); - if ((fclose(src)) != 0) - err(1, "can't fclose %s", to); - if ((fclose(dst)) != 0) - err(1, "can't fclose %s", from); -} - -/* create one or more directory components of a path */ -static void -createdir(const struct conf_entry *ent, char *dirpart) -{ - int res; - char *s, *d; - char mkdirpath[MAXPATHLEN]; - struct stat st; - - s = dirpart; - d = mkdirpath; - - for (;;) { - *d++ = *s++; - if (*s != '/' && *s != '\0') - continue; - *d = '\0'; - res = lstat(mkdirpath, &st); - if (res != 0) { - if (noaction) { - printf("\tmkdir %s\n", mkdirpath); - } else { - res = mkdir(mkdirpath, 0755); - if (res != 0) - err(1, "Error on mkdir(\"%s\") for -a", - mkdirpath); - } - } - if (*s == '\0') - break; - } - if (verbose) { - if (ent->firstcreate) - printf("Created directory '%s' for new %s\n", - dirpart, ent->log); - else - printf("Created directory '%s' for -a\n", dirpart); - } -} - -/* - * Create a new log file, destroying any currently-existing version - * of the log file in the process. If the caller wants a backup copy - * of the file to exist, they should call 'link(logfile,logbackup)' - * before calling this routine. - */ -void -createlog(const struct conf_entry *ent) -{ - int fd, failed; - struct stat st; - char *realfile, *slash, tempfile[MAXPATHLEN]; - - fd = -1; - realfile = ent->log; - - /* - * If this log file is being created for the first time (-C option), - * then it may also be true that the parent directory does not exist - * yet. Check, and create that directory if it is missing. - */ - if (ent->firstcreate) { - strlcpy(tempfile, realfile, sizeof(tempfile)); - slash = strrchr(tempfile, '/'); - if (slash != NULL) { - *slash = '\0'; - failed = stat(tempfile, &st); - if (failed && errno != ENOENT) - err(1, "Error on stat(%s)", tempfile); - if (failed) - createdir(ent, tempfile); - else if (!S_ISDIR(st.st_mode)) - errx(1, "%s exists but is not a directory", - tempfile); - } - } - - /* - * First create an unused filename, so it can be chown'ed and - * chmod'ed before it is moved into the real location. mkstemp - * will create the file mode=600 & owned by us. Note that all - * temp files will have a suffix of '.z<something>'. - */ - strlcpy(tempfile, realfile, sizeof(tempfile)); - strlcat(tempfile, ".zXXXXXX", sizeof(tempfile)); - if (noaction) - printf("\tmktemp %s\n", tempfile); - else { - fd = mkstemp(tempfile); - if (fd < 0) - err(1, "can't mkstemp logfile %s", tempfile); - - /* - * Add status message to what will become the new log file. - */ - if (!(ent->flags & CE_BINARY)) { - if (log_trim(tempfile, ent)) - err(1, "can't add status message to log"); - } - } - - /* Change the owner/group, if we are supposed to */ - if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) { - if (noaction) - printf("\tchown %u:%u %s\n", ent->uid, ent->gid, - tempfile); - else { - failed = fchown(fd, ent->uid, ent->gid); - if (failed) - err(1, "can't fchown temp file %s", tempfile); - } - } - - /* Turn on NODUMP if it was requested in the config-file. */ - if (ent->flags & CE_NODUMP) { - if (noaction) - printf("\tchflags nodump %s\n", tempfile); - else { - failed = fchflags(fd, UF_NODUMP); - if (failed) { - warn("log_trim: fchflags(NODUMP)"); - } - } - } - - /* - * Note that if the real logfile still exists, and if the call - * to rename() fails, then "neither the old file nor the new - * file shall be changed or created" (to quote the standard). - * If the call succeeds, then the file will be replaced without - * any window where some other process might find that the file - * did not exist. - * XXX - ? It may be that for some error conditions, we could - * retry by first removing the realfile and then renaming. - */ - if (noaction) { - printf("\tchmod %o %s\n", ent->permissions, tempfile); - printf("\tmv %s %s\n", tempfile, realfile); - } else { - failed = fchmod(fd, ent->permissions); - if (failed) - err(1, "can't fchmod temp file '%s'", tempfile); - failed = rename(tempfile, realfile); - if (failed) - err(1, "can't mv %s to %s", tempfile, realfile); - } - - if (fd >= 0) - close(fd); -} - -/* - * Change the attributes of a given filename to what was specified in - * the newsyslog.conf entry. This routine is only called for files - * that newsyslog expects that it has created, and thus it is a fatal - * error if this routine finds that the file does not exist. - */ -static void -change_attrs(const char *fname, const struct conf_entry *ent) -{ - int failed; - - if (noaction) { - printf("\tchmod %o %s\n", ent->permissions, fname); - - if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) - printf("\tchown %u:%u %s\n", - ent->uid, ent->gid, fname); - - if (ent->flags & CE_NODUMP) - printf("\tchflags nodump %s\n", fname); - return; - } - - failed = chmod(fname, ent->permissions); - if (failed) { - if (errno != EPERM) - err(1, "chmod(%s) in change_attrs", fname); - warn("change_attrs couldn't chmod(%s)", fname); - } - - if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) { - failed = chown(fname, ent->uid, ent->gid); - if (failed) - warn("can't chown %s", fname); - } - - if (ent->flags & CE_NODUMP) { - failed = chflags(fname, UF_NODUMP); - if (failed) - warn("can't chflags %s NODUMP", fname); - } -} diff --git a/usr.sbin/newsyslog/newsyslog.conf.5 b/usr.sbin/newsyslog/newsyslog.conf.5 deleted file mode 100644 index 5ce1f40..0000000 --- a/usr.sbin/newsyslog/newsyslog.conf.5 +++ /dev/null @@ -1,355 +0,0 @@ -.\" This file was split from the newsyslog(8) manual page by Tom Rhodes -.\" and includes modifications as appropriate. -.\" The original header is included below: -.\" -.\" This file contains changes from the Open Software Foundation. -.\" -.\" from: @(#)newsyslog.8 -.\" $FreeBSD$ -.\" -.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology -.\" -.\" Permission to use, copy, modify, and distribute this software -.\" and its documentation for any purpose and without fee is -.\" hereby granted, provided that the above copyright notice -.\" appear in all copies and that both that copyright notice and -.\" this permission notice appear in supporting documentation, -.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be -.\" used in advertising or publicity pertaining to distribution -.\" of the software without specific, written prior permission. -.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about -.\" the suitability of this software for any purpose. It is -.\" provided "as is" without express or implied warranty. -.\" -.Dd July 23, 2010 -.Dt NEWSYSLOG.CONF 5 -.Os -.Sh NAME -.Nm newsyslog.conf -.Nd -.Xr newsyslog 8 -configuration file -.Sh DESCRIPTION -The -.Nm -file is used to set log file rotation configuration for the -.Xr newsyslog 8 -utility. -Configuration may designate that logs are rotated based on -size, last rotation time, or time of day. -The -.Nm -file can also be used to designate secure permissions to log -files at rotation time. -During initialization, -.Xr newsyslog 8 -reads a configuration file, -normally -.Pa /etc/newsyslog.conf , -to determine which logs may potentially be rotated and archived. -Each line has five mandatory fields and four optional fields, -separated with whitespace. -Blank lines or lines beginning with -.Ql # -are ignored. -If -.Ql # -is placed in the middle of the line, the -.Ql # -character and the rest of the line after it is ignored. -To prevent special meaning, the -.Ql # -character may be escaped with -.Ql \e ; -in this case preceding -.Ql \e -is removed and -.Ql # -is treated as an ordinary character. -The fields of the configuration file are as follows: -.Bl -tag -width indent -.It Ar logfile_name -Name of the system log file to be archived, -or one of the literal strings -.Dq Aq Li default , -or -.Dq Aq Li include . -The special default entry will only be used if a log file -name is given as a command line argument to -.Xr newsyslog 8 , -and if that log file name is not matched by any other -line in the configuration file. -The include entry is used to include other configuration -files and supports globbing. -.It Ar owner : Ns Ar group -This optional field specifies the owner and group for the archive file. -The -.Ql \&: -is essential regardless if the -.Ar owner -or -.Ar group -field is left blank or contains a value. -The field may be numeric, or a name which is present in -.Pa /etc/passwd -or -.Pa /etc/group . -.It Ar mode -Specify the file mode of the log file and archives. -.It Ar count -Specify the maximum number of archive files which may exist. -This does not consider the current log file. -.It Ar size -When the size of the log file reaches -.Ar size -in kilobytes, the log file will be trimmed as described above. -If this field contains an asterisk -.Pq Ql * , -the log file will not be trimmed based on size. -.It Ar when -The -.Ar when -field may consist of an interval, a specific time, or both. -If the -.Ar when -field contains an asterisk -.Pq Ql * , -log rotation will solely depend on the contents of the -.Ar size -field. -Otherwise, the -.Ar when -field consists of an optional interval in hours, usually followed -by an -.So Li \&@ Sc Ns No -sign -and a time in restricted -.Tn ISO 8601 -format. -Additionally, the format may also be constructed with a -.Ql $ -sign along with a rotation time specification of once -a day, once a week, or once a month. -.Pp -If a time is specified, the log file will only be trimmed if -.Xr newsyslog 8 -is run within one hour of the specified time. -If an interval is specified, the log file will be trimmed if that many -hours have passed since the last rotation. -When both a time and an interval are -specified then both conditions must be satisfied for the rotation to -take place. -.Pp -There is no provision for the specification of a timezone. -There is little point in specifying an explicit minutes or -seconds component in the current implementation, since the only comparison is -.Dq within the hour . -.Pp -.Sy ISO 8601 restricted time format : -.Pp -The lead-in character for a restricted -.Tn ISO 8601 -time is an -.Ql @ -sign. -The particular format of the time in restricted -.Tn ISO 8601 -is: -.Sm off -.Oo -.Op Oo Oo Oo Va cc Oc Va yy Oc Va mm Oc Va dd -.Oo -.Li T -.Op Va hh Oo Va mm Oo Va ss Oc Oc Oc -.Oc . -.Sm on -Optional date fields default to the appropriate component of the -current date; optional time fields default to midnight; hence if today -is January 22, 1999, the following date specifications are all -equivalent: -.Pp -.Bl -item -compact -offset indent -.It -.Sq Li 19990122T000000 -.It -.Sq Li 990122T000000 -.It -.Sq Li 0122T000000 -.It -.Sq Li 22T000000 -.It -.Sq Li T000000 -.It -.Sq Li T0000 -.It -.Sq Li T00 -.It -.Sq Li 22T -.It -.Sq Li T -.It -.Sq Li \& -.El -.Pp -.Sy Day, week, and month time format: -.Pp -The lead-in character for day, week, and month specification is a -.Ql $ -sign. -The particular format of day, week, and month specification is: -.Op Li D Ns Va hh , -.Op Li W Ns Va w Ns Op Li D Ns Va hh , -and -.Op Li M Ns Va dd Ns Op Li D Ns Va hh , -respectively. -Optional time fields default to midnight. -The ranges for day and hour specifications are: -.Pp -.Bl -tag -width indent -offset indent -compact -.It Ar hh -hours, range 0..23 -.It Ar w -day of week, range 0..6, 0 = Sunday -.It Ar dd -day of month, range 1..31, or one of the letters -.Ql L -or -.Ql l -to specify the last day of the month. -.El -.Pp -Some examples: -.Pp -.Bl -tag -width indent -offset indent -compact -.It Li $D0 -rotate every night at midnight -(same as -.Li @T00 ) -.It Li $D23 -rotate every day at 23:00 -(same as -.Li @T23 ) -.It Li $W0D23 -rotate every week on Sunday at 23:00 -.It Li $W5D16 -rotate every week on Friday at 16:00 -.It Li $M1D0 -rotate at the first day of every month at midnight -(i.e., the start of the day; same as -.Li @01T00 ) -.It Li $M5D6 -rotate on every 5th day of month at 6:00 -(same as -.Li @05T06 ) -.El -.It Ar flags -This optional field is made up of one or more characters -that specify any special processing to be done for the log -files matched by this line. -The following are valid flags: -.Bl -tag -width indent -.It Cm B -indicates that the log file is a binary file, or has some -special format. -Usually -.Xr newsyslog 8 -inserts an -.Tn ASCII -message into a log file during rotation. -This message is used to indicate -when, and sometimes why the log file was rotated. -If -.Cm B -is specified, then that informational message will not be -inserted into the log file. -.It Cm C -indicates that the log file should be created if it does not -already exist, and if the -.Fl C -option was also specified on the command line. -.It Cm D -indicates that -.Xr newsyslog 8 -should set the -.Dv UF_NODUMP -flag when creating a new version of -this log file. -This option would affect how the -.Xr dump 8 -command treats the log file when making a file system backup. -.It Cm G -indicates that the specified -.Ar logfile_name -is a shell pattern, and that -.Xr newsyslog 8 -should archive all filenames matching that pattern using the -other options on this line. -See -.Xr glob 3 -for details on syntax and matching rules. -.It Cm J -indicates that -.Xr newsyslog 8 -should attempt to save disk space by compressing the rotated -log file using -.Xr bzip2 1 . -.It Cm N -indicates that there is no process which needs to be signaled -when this log file is rotated. -.It Cm U -indicates that the file specified by -.Ar path_to_pid_file -will contain the ID for a process group instead of a process. -This option also requires that the first line in that file -be a negative value to distinguish it from a process ID. -.It Cm Z -indicates that -.Xr newsyslog 8 -should attempt to save disk space by compressing the rotated -log file using -.Xr gzip 1 . -.It Fl -a minus sign will not cause any special processing, but it -can be used as a placeholder to create a -.Ar flags -field when you need to specify any of the following fields. -.El -.It Ar path_to_pid_file -This optional field specifies the file name containing a daemon's -process ID or to find a group process ID if the -.Cm U -flag was specified. -If this field is present, a -.Ar signal_number -is sent to the process ID contained in this file. -If this field is not present and the -.Cm N -flag has not been specified, then a -.Dv SIGHUP -signal will be sent to -.Xr syslogd 8 -or to the process id found in the file specified by -.Xr newsyslog 8 Ns 's -.Fl S -switch. -This field must start with -.Ql / -in order to be recognized properly. -.It Ar signal_number -This optional field specifies the signal number that will be sent -to the daemon process (or to all processes in a process group, if the -.Cm U -flag was specified). -If this field is not present, then a -.Dv SIGHUP -signal will be sent. -.El -.Sh SEE ALSO -.Xr bzip2 1 , -.Xr gzip 1 , -.Xr syslog 3 , -.Xr chown 8 , -.Xr newsyslog 8 , -.Xr syslogd 8 -.Sh HISTORY -This manual page first appeared in -.Fx 4.10 . diff --git a/usr.sbin/newsyslog/pathnames.h b/usr.sbin/newsyslog/pathnames.h deleted file mode 100644 index 3d2c6ff..0000000 --- a/usr.sbin/newsyslog/pathnames.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file contains changes from the Open Software Foundation. - */ - -/* - -Copyright 1988, 1989 by the Massachusetts Institute of Technology - -Permission to use, copy, modify, and distribute this software -and its documentation for any purpose and without fee is -hereby granted, provided that the above copyright notice -appear in all copies and that both that copyright notice and -this permission notice appear in supporting documentation, -and that the names of M.I.T. and the M.I.T. S.I.P.B. not be -used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. -M.I.T. and the M.I.T. S.I.P.B. make no representations about -the suitability of this software for any purpose. It is -provided "as is" without express or implied warranty. - - $FreeBSD$ - -*/ - -#define _PATH_CONF "/etc/newsyslog.conf" -#define _PATH_SYSLOGPID _PATH_VARRUN "syslog.pid" -#define _PATH_BZIP2 "/usr/bin/bzip2" -#define _PATH_GZIP "/usr/bin/gzip" diff --git a/usr.sbin/newsyslog/ptimes.c b/usr.sbin/newsyslog/ptimes.c deleted file mode 100644 index 474c69d..0000000 --- a/usr.sbin/newsyslog/ptimes.c +++ /dev/null @@ -1,618 +0,0 @@ -/*- - * ------+---------+---------+---------+---------+---------+---------+---------* - * Initial version of parse8601 was originally added to newsyslog.c in - * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>. - * Initial version of parseDWM was originally added to newsyslog.c in - * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>. - * - * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the FreeBSD Project. - * - * ------+---------+---------+---------+---------+---------+---------+---------* - * This is intended to be a set of general-purpose routines to process times. - * Right now it probably still has a number of assumptions in it, such that - * it works fine for newsyslog but might not work for other uses. - * ------+---------+---------+---------+---------+---------+---------+---------* - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <ctype.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "extern.h" - -#define SECS_PER_HOUR 3600 - -/* - * Bit-values which indicate which components of time were specified - * by the string given to parse8601 or parseDWM. These are needed to - * calculate what time-in-the-future will match that string. - */ -#define TSPEC_YEAR 0x0001 -#define TSPEC_MONTHOFYEAR 0x0002 -#define TSPEC_LDAYOFMONTH 0x0004 -#define TSPEC_DAYOFMONTH 0x0008 -#define TSPEC_DAYOFWEEK 0x0010 -#define TSPEC_HOUROFDAY 0x0020 - -#define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */ - -struct ptime_data { - time_t basesecs; /* Base point for relative times */ - time_t tsecs; /* Time in seconds */ - struct tm basetm; /* Base Time expanded into fields */ - struct tm tm; /* Time expanded into fields */ - int did_adj4dst; /* Track calls to ptime_adjust4dst */ - int parseopts; /* Options given for parsing */ - int tmspec; /* Indicates which time fields had - * been specified by the user */ -}; - -static int days_pmonth(int month, int year); -static int parse8601(struct ptime_data *ptime, const char *str); -static int parseDWM(struct ptime_data *ptime, const char *str); - -/* - * Simple routine to calculate the number of days in a given month. - */ -static int -days_pmonth(int month, int year) -{ - static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, - 30, 31, 30, 31}; - int ndays; - - ndays = mtab[month]; - - if (month == 1) { - /* - * We are usually called with a 'tm-year' value - * (ie, the value = the number of years past 1900). - */ - if (year < 1900) - year += 1900; - if (year % 4 == 0) { - /* - * This is a leap year, as long as it is not a - * multiple of 100, or if it is a multiple of - * both 100 and 400. - */ - if (year % 100 != 0) - ndays++; /* not multiple of 100 */ - else if (year % 400 == 0) - ndays++; /* is multiple of 100 and 400 */ - } - } - return (ndays); -} - -/*- - * Parse a limited subset of ISO 8601. The specific format is as follows: - * - * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) - * - * We don't accept a timezone specification; missing fields (including timezone) - * are defaulted to the current date but time zero. - */ -static int -parse8601(struct ptime_data *ptime, const char *s) -{ - char *t; - long l; - struct tm tm; - - l = strtol(s, &t, 10); - if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) - return (-1); - - /* - * Now t points either to the end of the string (if no time was - * provided) or to the letter `T' which separates date and time in - * ISO 8601. The pointer arithmetic is the same for either case. - */ - tm = ptime->tm; - ptime->tmspec = TSPEC_HOUROFDAY; - switch (t - s) { - case 8: - tm.tm_year = ((l / 1000000) - 19) * 100; - l = l % 1000000; - case 6: - ptime->tmspec |= TSPEC_YEAR; - tm.tm_year -= tm.tm_year % 100; - tm.tm_year += l / 10000; - l = l % 10000; - case 4: - ptime->tmspec |= TSPEC_MONTHOFYEAR; - tm.tm_mon = (l / 100) - 1; - l = l % 100; - case 2: - ptime->tmspec |= TSPEC_DAYOFMONTH; - tm.tm_mday = l; - case 0: - break; - default: - return (-1); - } - - /* sanity check */ - if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 - || tm.tm_mday < 1 || tm.tm_mday > 31) - return (-1); - - if (*t != '\0') { - s = ++t; - l = strtol(s, &t, 10); - if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t))) - return (-1); - - switch (t - s) { - case 6: - tm.tm_sec = l % 100; - l /= 100; - case 4: - tm.tm_min = l % 100; - l /= 100; - case 2: - ptime->tmspec |= TSPEC_HOUROFDAY; - tm.tm_hour = l; - case 0: - break; - default: - return (-1); - } - - /* sanity check */ - if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 - || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) - return (-1); - } - - ptime->tm = tm; - return (0); -} - -/*- - * Parse a cyclic time specification, the format is as follows: - * - * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] - * - * to rotate a logfile cyclic at - * - * - every day (D) within a specific hour (hh) (hh = 0...23) - * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) - * - once a month (M) at a specific day (d) (d = 1..31,l|L) - * - * We don't accept a timezone specification; missing fields - * are defaulted to the current date but time zero. - */ -static int -parseDWM(struct ptime_data *ptime, const char *s) -{ - int daysmon, Dseen, WMseen; - const char *endval; - char *tmp; - long l; - struct tm tm; - - /* Save away the number of days in this month */ - tm = ptime->tm; - daysmon = days_pmonth(tm.tm_mon, tm.tm_year); - - WMseen = Dseen = 0; - ptime->tmspec = TSPEC_HOUROFDAY; - for (;;) { - endval = NULL; - switch (*s) { - case 'D': - if (Dseen) - return (-1); - Dseen++; - ptime->tmspec |= TSPEC_HOUROFDAY; - s++; - l = strtol(s, &tmp, 10); - if (l < 0 || l > 23) - return (-1); - endval = tmp; - tm.tm_hour = l; - break; - - case 'W': - if (WMseen) - return (-1); - WMseen++; - ptime->tmspec |= TSPEC_DAYOFWEEK; - s++; - l = strtol(s, &tmp, 10); - if (l < 0 || l > 6) - return (-1); - endval = tmp; - if (l != tm.tm_wday) { - int save; - - if (l < tm.tm_wday) { - save = 6 - tm.tm_wday; - save += (l + 1); - } else { - save = l - tm.tm_wday; - } - - tm.tm_mday += save; - - if (tm.tm_mday > daysmon) { - tm.tm_mon++; - tm.tm_mday = tm.tm_mday - daysmon; - } - } - break; - - case 'M': - if (WMseen) - return (-1); - WMseen++; - ptime->tmspec |= TSPEC_DAYOFMONTH; - s++; - if (tolower(*s) == 'l') { - /* User wants the last day of the month. */ - ptime->tmspec |= TSPEC_LDAYOFMONTH; - tm.tm_mday = daysmon; - endval = s + 1; - } else { - l = strtol(s, &tmp, 10); - if (l < 1 || l > 31) - return (-1); - - if (l > daysmon) - return (-1); - endval = tmp; - tm.tm_mday = l; - } - break; - - default: - return (-1); - break; - } - - if (endval == NULL) - return (-1); - else if (*endval == '\0' || isspace(*endval)) - break; - else - s = endval; - } - - ptime->tm = tm; - return (0); -} - -/* - * Initialize a new ptime-related data area. - */ -struct ptime_data * -ptime_init(const struct ptime_data *optsrc) -{ - struct ptime_data *newdata; - - newdata = malloc(sizeof(struct ptime_data)); - if (optsrc != NULL) { - memcpy(newdata, optsrc, sizeof(struct ptime_data)); - } else { - memset(newdata, '\0', sizeof(struct ptime_data)); - newdata->did_adj4dst = TNYET_ADJ4DST; - } - - return (newdata); -} - -/* - * Adjust a given time if that time is in a different timezone than - * some other time. - */ -int -ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc) -{ - struct ptime_data adjtime; - - if (ptime == NULL) - return (-1); - - /* - * Changes are not made to the given time until after all - * of the calculations have been successful. - */ - adjtime = *ptime; - - /* Check to see if this adjustment was already made */ - if ((adjtime.did_adj4dst != TNYET_ADJ4DST) && - (adjtime.did_adj4dst == dstsrc->tm.tm_isdst)) - return (0); /* yes, so don't make it twice */ - - /* See if daylight-saving has changed between the two times. */ - if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) { - if (adjtime.tm.tm_isdst == 1) - adjtime.tsecs -= SECS_PER_HOUR; - else if (adjtime.tm.tm_isdst == 0) - adjtime.tsecs += SECS_PER_HOUR; - adjtime.tm = *(localtime(&adjtime.tsecs)); - /* Remember that this adjustment has been made */ - adjtime.did_adj4dst = dstsrc->tm.tm_isdst; - /* - * XXX - Should probably check to see if changing the - * hour also changed the value of is_dst. What - * should we do in that case? - */ - } - - *ptime = adjtime; - return (0); -} - -int -ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime, - const char *str) -{ - int dpm, pres; - struct tm temp_tm; - - ptime->parseopts = parseopts; - ptime->basesecs = basetime; - ptime->basetm = *(localtime(&ptime->basesecs)); - ptime->tm = ptime->basetm; - ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0; - - /* - * Call a routine which sets ptime.tm and ptime.tspecs based - * on the given string and parsing-options. Note that the - * routine should not call mktime to set ptime.tsecs. - */ - if (parseopts & PTM_PARSE_DWM) - pres = parseDWM(ptime, str); - else - pres = parse8601(ptime, str); - if (pres < 0) { - ptime->tsecs = (time_t)pres; - return (pres); - } - - /* - * Before calling mktime, check to see if we ended up with a - * "day-of-month" that does not exist in the selected month. - * If we did call mktime with that info, then mktime will - * make it look like the user specifically requested a day - * in the following month (eg: Feb 31 turns into Mar 3rd). - */ - dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); - if ((parseopts & PTM_PARSE_MATCHDOM) && - (ptime->tmspec & TSPEC_DAYOFMONTH) && - (ptime->tm.tm_mday> dpm)) { - /* - * ptime_nxtime() will want a ptime->tsecs value, - * but we need to avoid mktime resetting all the - * ptime->tm values. - */ - if (verbose && dbg_at_times > 1) - fprintf(stderr, - "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)", - ptime->tm.tm_year, ptime->tm.tm_mon, - ptime->tm.tm_mday, ptime->tm.tm_hour, - ptime->tm.tm_min, dpm); - temp_tm = ptime->tm; - ptime->tsecs = mktime(&temp_tm); - if (ptime->tsecs > (time_t)-1) - ptimeset_nxtime(ptime); - if (verbose && dbg_at_times > 1) - fprintf(stderr, - " to: %4d/%02d/%02d %02d:%02d\n", - ptime->tm.tm_year, ptime->tm.tm_mon, - ptime->tm.tm_mday, ptime->tm.tm_hour, - ptime->tm.tm_min); - } - - /* - * Convert the ptime.tm into standard time_t seconds. Check - * for invalid times, which includes things like the hour lost - * when switching from "standard time" to "daylight saving". - */ - ptime->tsecs = mktime(&ptime->tm); - if (ptime->tsecs == (time_t)-1) { - ptime->tsecs = (time_t)-2; - return (-2); - } - - return (0); -} - -int -ptime_free(struct ptime_data *ptime) -{ - - if (ptime == NULL) - return (-1); - - free(ptime); - return (0); -} - -/* - * Some trivial routines so ptime_data can remain a completely - * opaque type. - */ -const char * -ptimeget_ctime(const struct ptime_data *ptime) -{ - - if (ptime == NULL) - return ("Null time in ptimeget_ctime()\n"); - - return (ctime(&ptime->tsecs)); -} - -double -ptimeget_diff(const struct ptime_data *minuend, const struct - ptime_data *subtrahend) -{ - - /* Just like difftime(), we have no good error-return */ - if (minuend == NULL || subtrahend == NULL) - return (0.0); - - return (difftime(minuend->tsecs, subtrahend->tsecs)); -} - -time_t -ptimeget_secs(const struct ptime_data *ptime) -{ - - if (ptime == NULL) - return (-1); - - return (ptime->tsecs); -} - -/* - * Generate an approximate timestamp for the next event, based on - * what parts of time were specified by the original parameter to - * ptime_relparse(). The result may be -1 if there is no obvious - * "next time" which will work. - */ -int -ptimeset_nxtime(struct ptime_data *ptime) -{ - int moredays, tdpm, tmon, tyear; - struct ptime_data nextmatch; - - if (ptime == NULL) - return (-1); - - /* - * Changes are not made to the given time until after all - * of the calculations have been successful. - */ - nextmatch = *ptime; - /* - * If the user specified a year and we're already past that - * time, then there will never be another one! - */ - if (ptime->tmspec & TSPEC_YEAR) - return (-1); - - /* - * The caller gave us a time in the past. Calculate how much - * time is needed to go from that valid rotate time to the - * next valid rotate time. We only need to get to the nearest - * hour, because newsyslog is only run once per hour. - */ - moredays = 0; - if (ptime->tmspec & TSPEC_MONTHOFYEAR) { - /* Special case: Feb 29th does not happen every year. */ - if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) { - nextmatch.tm.tm_year += 4; - if (days_pmonth(1, nextmatch.tm.tm_year) < 29) - nextmatch.tm.tm_year += 4; - } else { - nextmatch.tm.tm_year += 1; - } - nextmatch.tm.tm_isdst = -1; - nextmatch.tsecs = mktime(&nextmatch.tm); - - } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) { - /* - * Need to get to the last day of next month. Origtm is - * already at the last day of this month, so just add to - * it number of days in the next month. - */ - if (ptime->tm.tm_mon < 11) - moredays = days_pmonth(ptime->tm.tm_mon + 1, - ptime->tm.tm_year); - else - moredays = days_pmonth(0, ptime->tm.tm_year + 1); - - } else if (ptime->tmspec & TSPEC_DAYOFMONTH) { - /* Jump to the same day in the next month */ - moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); - /* - * In some cases, the next month may not *have* the - * desired day-of-the-month. If that happens, then - * move to the next month that does have enough days. - */ - tmon = ptime->tm.tm_mon; - tyear = ptime->tm.tm_year; - for (;;) { - if (tmon < 11) - tmon += 1; - else { - tmon = 0; - tyear += 1; - } - tdpm = days_pmonth(tmon, tyear); - if (tdpm >= ptime->tm.tm_mday) - break; - moredays += tdpm; - } - - } else if (ptime->tmspec & TSPEC_DAYOFWEEK) { - moredays = 7; - } else if (ptime->tmspec & TSPEC_HOUROFDAY) { - moredays = 1; - } - - if (moredays != 0) { - nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays; - nextmatch.tm = *(localtime(&nextmatch.tsecs)); - } - - /* - * The new time will need to be adjusted if the setting of - * daylight-saving has changed between the two times. - */ - ptime_adjust4dst(&nextmatch, ptime); - - /* Everything worked. Update the given time and return. */ - *ptime = nextmatch; - return (0); -} - -int -ptimeset_time(struct ptime_data *ptime, time_t secs) -{ - - if (ptime == NULL) - return (-1); - - ptime->tsecs = secs; - ptime->tm = *(localtime(&ptime->tsecs)); - ptime->parseopts = 0; - /* ptime->tmspec = ? */ - return (0); -} |