summaryrefslogtreecommitdiffstats
path: root/usr.sbin/newsyslog
diff options
context:
space:
mode:
authorgad <gad@FreeBSD.org>2003-09-23 00:00:26 +0000
committergad <gad@FreeBSD.org>2003-09-23 00:00:26 +0000
commit8dafe430a02604e433903c786d2161b805228379 (patch)
tree5cbce648b655870350dca3b06cc88ab5d10345f8 /usr.sbin/newsyslog
parentf2180e5cf94c12cbf30fcfb34bd27d5818dd5419 (diff)
downloadFreeBSD-src-8dafe430a02604e433903c786d2161b805228379.zip
FreeBSD-src-8dafe430a02604e433903c786d2161b805228379.tar.gz
Restructure the time processing routines, mainly to fix up the
"will trim at" message printed when the user requests '-v'. The previous code would often print the wrong time, such as: On Sept 22, run: newsyslog -nv /var/log/wtmp And see: will trim at Mon Sep 1 05:00:00 2003 correct msg: will trim at Wed Oct 1 05:00:00 2003 MFC after: 20 days
Diffstat (limited to 'usr.sbin/newsyslog')
-rw-r--r--usr.sbin/newsyslog/extern.h30
-rw-r--r--usr.sbin/newsyslog/newsyslog.c137
-rw-r--r--usr.sbin/newsyslog/ptimes.c398
3 files changed, 480 insertions, 85 deletions
diff --git a/usr.sbin/newsyslog/extern.h b/usr.sbin/newsyslog/extern.h
index 05ef92c..c350226 100644
--- a/usr.sbin/newsyslog/extern.h
+++ b/usr.sbin/newsyslog/extern.h
@@ -36,13 +36,33 @@
#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 time_t dbg_timenow;
-extern time_t timenow;
+extern struct ptime_data *dbg_timenow;
__BEGIN_DECLS
-time_t parse8601(const char *_srcstr, time_t *_next_time);
-time_t parseDWM(char *_srcstr, time_t *_next_time);
+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.c b/usr.sbin/newsyslog/newsyslog.c
index a9d1fd2..39efdb8 100644
--- a/usr.sbin/newsyslog/newsyslog.c
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -90,7 +90,7 @@ struct conf_entry {
int numlogs; /* Number of logs to keep */
int size; /* Size cutoff to trigger trimming the log */
int hours; /* Hours between log trimming */
- time_t trim_at; /* Specific time to do trimming */
+ struct ptime_data *trim_at; /* Specific time to do trimming */
int permissions; /* File permissions on the log */
int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */
int sig; /* Signal to send */
@@ -100,6 +100,8 @@ struct conf_entry {
#define DEFAULT_MARKER "<default>"
+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 */
@@ -116,11 +118,13 @@ int rotatereq = 0; /* -R = Always rotate the file(s) as given */
char *requestor; /* The name given on a -R request */
char *archdirname; /* Directory path to old logfiles archive */
const char *conf; /* Configuration file to use */
-time_t dbg_timenow; /* A "timenow" value set via -D option */
-time_t timenow;
+
+struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */
+struct ptime_data *timenow; /* The time to use for checking at-fields */
char hostname[MAXHOSTNAMELEN]; /* hostname */
-char daytime[16]; /* timenow in human readable form */
+char daytime[16]; /* The current time in human readable form,
+ * used for rotation-tracking messages. */
static struct conf_entry *get_worklist(char **files);
static void parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p,
@@ -213,7 +217,9 @@ init_entry(const char *fname, struct conf_entry *src_entry)
tempwork->numlogs = src_entry->numlogs;
tempwork->size = src_entry->size;
tempwork->hours = src_entry->hours;
- tempwork->trim_at = src_entry->trim_at;
+ 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;
@@ -229,7 +235,7 @@ init_entry(const char *fname, struct conf_entry *src_entry)
tempwork->numlogs = 1;
tempwork->size = -1;
tempwork->hours = -1;
- tempwork->trim_at = (time_t)0;
+ tempwork->trim_at = NULL;
tempwork->permissions = 0;
tempwork->flags = 0;
tempwork->sig = SIGHUP;
@@ -264,6 +270,11 @@ free_entry(struct conf_entry *ent)
ent->r_reason = NULL;
}
+ if (ent->trim_at != NULL) {
+ ptime_free(ent->trim_at);
+ ent->trim_at = NULL;
+ }
+
free(ent);
}
@@ -290,6 +301,7 @@ do_entry(struct conf_entry * ent)
{
#define REASON_MAX 80
int size, modtime;
+ double diffsecs;
char temp_reason[REASON_MAX];
if (verbose) {
@@ -330,11 +342,40 @@ do_entry(struct conf_entry * ent)
}
} else {
if (ent->flags & CE_TRIMAT && !force && !rotatereq) {
- if (timenow < ent->trim_at
- || difftime(timenow, ent->trim_at) >= 60 * 60) {
- if (verbose)
+ 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",
- ctime(&ent->trim_at));
+ ptimeget_ctime(ent->trim_at));
+ }
+ return;
+ } 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;
+ } 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;
} else if (verbose && ent->hours <= 0) {
printf("--> time is up\n");
@@ -496,10 +537,10 @@ parse_args(int argc, char **argv)
{
int ch;
char *p;
- char debugtime[32];
- timenow = time(NULL);
- (void)strncpy(daytime, ctime(&timenow) + 4, 15);
+ timenow = ptime_init(NULL);
+ ptimeset_time(timenow, time(NULL));
+ (void)strncpy(daytime, ptimeget_ctime(timenow) + 4, 15);
daytime[15] = '\0';
/* Let's get our hostname */
@@ -578,37 +619,49 @@ parse_args(int argc, char **argv)
* 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;
- strlcpy(debugtime, ctime(&timenow), sizeof(debugtime));
fprintf(stderr, "Debug: Running as if TimeNow is %s",
- debugtime);
+ 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 probably will
- * be off by an hour when crossing a timezone change.
+ * The "TimeNow" debugging option. This might be off
+ * by an hour when crossing a timezone change.
*/
- dbg_timenow = parse8601(doption + sizeof(TN) - 1, NULL);
- if (dbg_timenow == (time_t)-1) {
- warnx("Malformed time given on -D %s", doption);
- return (0); /* failure */
- }
- if (dbg_timenow == (time_t)-2) {
+ 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 */
+ }
+
warnx("Unknown -D (debug) option: %s", doption);
return (0); /* failure */
}
@@ -889,7 +942,7 @@ parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p,
struct conf_entry *lastglob, *lastwork, *working;
struct passwd *pwd;
struct group *grp;
- int eol, special;
+ int eol, ptm_opts, res, special;
/*
* XXX - for now, assume that only one config file will be read,
@@ -1032,24 +1085,26 @@ parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p,
else
working->hours = ul;
- if (*ep != '\0' && *ep != '@' && *ep != '*' &&
- *ep != '$')
+ if (*ep == '\0' || strcmp(ep, "*") == 0)
+ goto no_trimat;
+ if (*ep != '@' && *ep != '$')
errx(1, "malformed interval/at:\n%s", errline);
- if (*ep == '@') {
- working->trim_at = parse8601(ep + 1, NULL);
- working->flags |= CE_TRIMAT;
- } else if (*ep == '$') {
- working->trim_at = parseDWM(ep + 1, NULL);
- working->flags |= CE_TRIMAT;
- }
- if (working->flags & CE_TRIMAT) {
- if (working->trim_at == (time_t)-1)
- errx(1, "malformed at:\n%s", errline);
- if (working->trim_at == (time_t)-2)
- errx(1, "nonexistent time:\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;
@@ -1575,7 +1630,7 @@ age_old_log(char *file)
return (-1);
}
}
- return ((int)(timenow - sb.st_mtime + 1800) / 3600);
+ return ((int)(ptimeget_secs(timenow) - sb.st_mtime + 1800) / 3600);
}
/* Skip Over Blanks */
diff --git a/usr.sbin/newsyslog/ptimes.c b/usr.sbin/newsyslog/ptimes.c
index 46fe1c7..4b63d6b 100644
--- a/usr.sbin/newsyslog/ptimes.c
+++ b/usr.sbin/newsyslog/ptimes.c
@@ -34,6 +34,10 @@
* 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>
@@ -44,11 +48,41 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdint.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.
@@ -92,20 +126,12 @@ days_pmonth(int month, int year)
* We don't accept a timezone specification; missing fields (including timezone)
* are defaulted to the current date but time zero.
*/
-time_t
-parse8601(const char *s, time_t *next_time)
+static int
+parse8601(struct ptime_data *ptime, const char *s)
{
char *t;
- time_t tsecs;
- struct tm tm, *tmp;
long l;
-
- tmp = localtime(&timenow);
- tm = *tmp;
- if (next_time != NULL)
- *next_time = (time_t)-1;
-
- tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ struct tm tm;
l = strtol(s, &t, 10);
if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
@@ -116,18 +142,23 @@ parse8601(const char *s, time_t *next_time)
* 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;
@@ -154,6 +185,7 @@ parse8601(const char *s, time_t *next_time)
tm.tm_min = l % 100;
l /= 100;
case 2:
+ ptime->tmspec |= TSPEC_HOUROFDAY;
tm.tm_hour = l;
case 0:
break;
@@ -167,14 +199,8 @@ parse8601(const char *s, time_t *next_time)
return (-1);
}
- tsecs = mktime(&tm);
- /*
- * Check for invalid times, including things like the missing
- * hour when switching from "standard time" to "daylight saving".
- */
- if (tsecs == (time_t)-1)
- tsecs = (time_t)-2;
- return (tsecs);
+ ptime->tm = tm;
+ return (0);
}
/*-
@@ -191,32 +217,27 @@ parse8601(const char *s, time_t *next_time)
* We don't accept a timezone specification; missing fields
* are defaulted to the current date but time zero.
*/
-time_t
-parseDWM(char *s, time_t *next_time)
+static int
+parseDWM(struct ptime_data *ptime, const char *s)
{
- int daysmon;
+ int daysmon, Dseen, WMseen;
char *t;
- time_t tsecs;
- struct tm tm, *tmp;
long l;
- int WMseen = 0;
- int Dseen = 0;
-
- tmp = localtime(&timenow);
- tm = *tmp;
- if (next_time != NULL)
- *next_time = (time_t)-1;
+ struct tm tm;
/* Save away the number of days in this month */
+ tm = ptime->tm;
daysmon = days_pmonth(tm.tm_mon, tm.tm_year);
- tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ WMseen = Dseen = 0;
+ ptime->tmspec = TSPEC_HOUROFDAY;
for (;;) {
switch (*s) {
case 'D':
if (Dseen)
return (-1);
Dseen++;
+ ptime->tmspec |= TSPEC_HOUROFDAY;
s++;
l = strtol(s, &t, 10);
if (l < 0 || l > 23)
@@ -228,6 +249,7 @@ parseDWM(char *s, time_t *next_time)
if (WMseen)
return (-1);
WMseen++;
+ ptime->tmspec |= TSPEC_DAYOFWEEK;
s++;
l = strtol(s, &t, 10);
if (l < 0 || l > 6)
@@ -255,11 +277,14 @@ parseDWM(char *s, time_t *next_time)
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;
s++;
- t = s;
+ t = __DECONST(char *,s);
} else {
l = strtol(s, &t, 10);
if (l < 1 || l > 31)
@@ -282,12 +307,307 @@ parseDWM(char *s, time_t *next_time)
s = t;
}
- tsecs = mktime(&tm);
+ 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));
+ }
+
/*
- * Check for invalid times, including things like the missing
- * hour when switching from "standard time" to "daylight saving".
+ * The new time will need to be adjusted if the setting of
+ * daylight-saving has changed between the two times.
*/
- if (tsecs == (time_t)-1)
- tsecs = (time_t)-2;
- return (tsecs);
+ 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);
}
OpenPOWER on IntegriCloud