1 /*- 2 * ------+---------+---------+---------+---------+---------+---------+---------* 3 * Initial version of parse8601 was originally added to newsyslog.c in 4 * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>. 5 * Initial version of parseDWM was originally added to newsyslog.c in 6 * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>. 7 * 8 * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * The views and conclusions contained in the software and documentation 33 * are those of the authors and should not be interpreted as representing 34 * official policies, either expressed or implied, of the FreeBSD Project. 35 * 36 * ------+---------+---------+---------+---------+---------+---------+---------* 37 */ 38 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 42 #include <ctype.h> 43 #include <limits.h> 44 #include <stdio.h> 45 #include <stdint.h> 46 #include <stdlib.h> 47 #include <time.h> 48 49 #include "extern.h" 50 51 static int days_pmonth(int month, int year); 52 53 /* 54 * Simple routine to calculate the number of days in a given month. 55 */ 56 static int 57 days_pmonth(int month, int year) 58 { 59 static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 60 30, 31, 30, 31}; 61 int ndays; 62 63 ndays = mtab[month]; 64 65 if (month == 1) { 66 /* 67 * We are usually called with a 'tm-year' value 68 * (ie, the value = the number of years past 1900). 69 */ 70 if (year < 1900) 71 year += 1900; 72 if (year % 4 == 0) { 73 /* 74 * This is a leap year, as long as it is not a 75 * multiple of 100, or if it is a multiple of 76 * both 100 and 400. 77 */ 78 if (year % 100 != 0) 79 ndays++; /* not multiple of 100 */ 80 else if (year % 400 == 0) 81 ndays++; /* is multiple of 100 and 400 */ 82 } 83 } 84 return (ndays); 85 } 86 87 /*- 88 * Parse a limited subset of ISO 8601. The specific format is as follows: 89 * 90 * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 91 * 92 * We don't accept a timezone specification; missing fields (including timezone) 93 * are defaulted to the current date but time zero. 94 */ 95 time_t 96 parse8601(const char *s, time_t *next_time) 97 { 98 char *t; 99 time_t tsecs; 100 struct tm tm, *tmp; 101 long l; 102 103 tmp = localtime(&timenow); 104 tm = *tmp; 105 if (next_time != NULL) 106 *next_time = (time_t)-1; 107 108 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 109 110 l = strtol(s, &t, 10); 111 if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) 112 return (-1); 113 114 /* 115 * Now t points either to the end of the string (if no time was 116 * provided) or to the letter `T' which separates date and time in 117 * ISO 8601. The pointer arithmetic is the same for either case. 118 */ 119 switch (t - s) { 120 case 8: 121 tm.tm_year = ((l / 1000000) - 19) * 100; 122 l = l % 1000000; 123 case 6: 124 tm.tm_year -= tm.tm_year % 100; 125 tm.tm_year += l / 10000; 126 l = l % 10000; 127 case 4: 128 tm.tm_mon = (l / 100) - 1; 129 l = l % 100; 130 case 2: 131 tm.tm_mday = l; 132 case 0: 133 break; 134 default: 135 return (-1); 136 } 137 138 /* sanity check */ 139 if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 140 || tm.tm_mday < 1 || tm.tm_mday > 31) 141 return (-1); 142 143 if (*t != '\0') { 144 s = ++t; 145 l = strtol(s, &t, 10); 146 if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t))) 147 return (-1); 148 149 switch (t - s) { 150 case 6: 151 tm.tm_sec = l % 100; 152 l /= 100; 153 case 4: 154 tm.tm_min = l % 100; 155 l /= 100; 156 case 2: 157 tm.tm_hour = l; 158 case 0: 159 break; 160 default: 161 return (-1); 162 } 163 164 /* sanity check */ 165 if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 166 || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 167 return (-1); 168 } 169 170 tsecs = mktime(&tm); 171 /* 172 * Check for invalid times, including things like the missing 173 * hour when switching from "standard time" to "daylight saving". 174 */ 175 if (tsecs == (time_t)-1) 176 tsecs = (time_t)-2; 177 return (tsecs); 178 } 179 180 /*- 181 * Parse a cyclic time specification, the format is as follows: 182 * 183 * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 184 * 185 * to rotate a logfile cyclic at 186 * 187 * - every day (D) within a specific hour (hh) (hh = 0...23) 188 * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 189 * - once a month (M) at a specific day (d) (d = 1..31,l|L) 190 * 191 * We don't accept a timezone specification; missing fields 192 * are defaulted to the current date but time zero. 193 */ 194 time_t 195 parseDWM(char *s, time_t *next_time) 196 { 197 int daysmon; 198 char *t; 199 time_t tsecs; 200 struct tm tm, *tmp; 201 long l; 202 int WMseen = 0; 203 int Dseen = 0; 204 205 tmp = localtime(&timenow); 206 tm = *tmp; 207 if (next_time != NULL) 208 *next_time = (time_t)-1; 209 210 /* Save away the number of days in this month */ 211 daysmon = days_pmonth(tm.tm_mon, tm.tm_year); 212 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 213 214 for (;;) { 215 switch (*s) { 216 case 'D': 217 if (Dseen) 218 return (-1); 219 Dseen++; 220 s++; 221 l = strtol(s, &t, 10); 222 if (l < 0 || l > 23) 223 return (-1); 224 tm.tm_hour = l; 225 break; 226 227 case 'W': 228 if (WMseen) 229 return (-1); 230 WMseen++; 231 s++; 232 l = strtol(s, &t, 10); 233 if (l < 0 || l > 6) 234 return (-1); 235 if (l != tm.tm_wday) { 236 int save; 237 238 if (l < tm.tm_wday) { 239 save = 6 - tm.tm_wday; 240 save += (l + 1); 241 } else { 242 save = l - tm.tm_wday; 243 } 244 245 tm.tm_mday += save; 246 247 if (tm.tm_mday > daysmon) { 248 tm.tm_mon++; 249 tm.tm_mday = tm.tm_mday - daysmon; 250 } 251 } 252 break; 253 254 case 'M': 255 if (WMseen) 256 return (-1); 257 WMseen++; 258 s++; 259 if (tolower(*s) == 'l') { 260 tm.tm_mday = daysmon; 261 s++; 262 t = s; 263 } else { 264 l = strtol(s, &t, 10); 265 if (l < 1 || l > 31) 266 return (-1); 267 268 if (l > daysmon) 269 return (-1); 270 tm.tm_mday = l; 271 } 272 break; 273 274 default: 275 return (-1); 276 break; 277 } 278 279 if (*t == '\0' || isspace(*t)) 280 break; 281 else 282 s = t; 283 } 284 285 tsecs = mktime(&tm); 286 /* 287 * Check for invalid times, including things like the missing 288 * hour when switching from "standard time" to "daylight saving". 289 */ 290 if (tsecs == (time_t)-1) 291 tsecs = (time_t)-2; 292 return (tsecs); 293 } 294