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 /*- 52 * Parse a limited subset of ISO 8601. The specific format is as follows: 53 * 54 * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 55 * 56 * We don't accept a timezone specification; missing fields (including timezone) 57 * are defaulted to the current date but time zero. 58 */ 59 time_t 60 parse8601(const char *s, time_t *next_time) 61 { 62 char *t; 63 time_t tsecs; 64 struct tm tm, *tmp; 65 long l; 66 67 tmp = localtime(&timenow); 68 tm = *tmp; 69 if (next_time != NULL) 70 *next_time = (time_t)-1; 71 72 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 73 74 l = strtol(s, &t, 10); 75 if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) 76 return (-1); 77 78 /* 79 * Now t points either to the end of the string (if no time was 80 * provided) or to the letter `T' which separates date and time in 81 * ISO 8601. The pointer arithmetic is the same for either case. 82 */ 83 switch (t - s) { 84 case 8: 85 tm.tm_year = ((l / 1000000) - 19) * 100; 86 l = l % 1000000; 87 case 6: 88 tm.tm_year -= tm.tm_year % 100; 89 tm.tm_year += l / 10000; 90 l = l % 10000; 91 case 4: 92 tm.tm_mon = (l / 100) - 1; 93 l = l % 100; 94 case 2: 95 tm.tm_mday = l; 96 case 0: 97 break; 98 default: 99 return (-1); 100 } 101 102 /* sanity check */ 103 if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 104 || tm.tm_mday < 1 || tm.tm_mday > 31) 105 return (-1); 106 107 if (*t != '\0') { 108 s = ++t; 109 l = strtol(s, &t, 10); 110 if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t))) 111 return (-1); 112 113 switch (t - s) { 114 case 6: 115 tm.tm_sec = l % 100; 116 l /= 100; 117 case 4: 118 tm.tm_min = l % 100; 119 l /= 100; 120 case 2: 121 tm.tm_hour = l; 122 case 0: 123 break; 124 default: 125 return (-1); 126 } 127 128 /* sanity check */ 129 if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 130 || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 131 return (-1); 132 } 133 134 tsecs = mktime(&tm); 135 /* 136 * Check for invalid times, including things like the missing 137 * hour when switching from "standard time" to "daylight saving". 138 */ 139 if (tsecs == (time_t)-1) 140 tsecs = (time_t)-2; 141 return (tsecs); 142 } 143 144 /*- 145 * Parse a cyclic time specification, the format is as follows: 146 * 147 * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 148 * 149 * to rotate a logfile cyclic at 150 * 151 * - every day (D) within a specific hour (hh) (hh = 0...23) 152 * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 153 * - once a month (M) at a specific day (d) (d = 1..31,l|L) 154 * 155 * We don't accept a timezone specification; missing fields 156 * are defaulted to the current date but time zero. 157 */ 158 time_t 159 parseDWM(char *s, time_t *next_time) 160 { 161 char *t; 162 time_t tsecs; 163 struct tm tm, *tmp; 164 long l; 165 int nd; 166 static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 167 int WMseen = 0; 168 int Dseen = 0; 169 170 tmp = localtime(&timenow); 171 tm = *tmp; 172 if (next_time != NULL) 173 *next_time = (time_t)-1; 174 175 /* set no. of days per month */ 176 177 nd = mtab[tm.tm_mon]; 178 179 if (tm.tm_mon == 1) { 180 if (((tm.tm_year + 1900) % 4 == 0) && 181 ((tm.tm_year + 1900) % 100 != 0) && 182 ((tm.tm_year + 1900) % 400 == 0)) { 183 nd++; /* leap year, 29 days in february */ 184 } 185 } 186 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 187 188 for (;;) { 189 switch (*s) { 190 case 'D': 191 if (Dseen) 192 return (-1); 193 Dseen++; 194 s++; 195 l = strtol(s, &t, 10); 196 if (l < 0 || l > 23) 197 return (-1); 198 tm.tm_hour = l; 199 break; 200 201 case 'W': 202 if (WMseen) 203 return (-1); 204 WMseen++; 205 s++; 206 l = strtol(s, &t, 10); 207 if (l < 0 || l > 6) 208 return (-1); 209 if (l != tm.tm_wday) { 210 int save; 211 212 if (l < tm.tm_wday) { 213 save = 6 - tm.tm_wday; 214 save += (l + 1); 215 } else { 216 save = l - tm.tm_wday; 217 } 218 219 tm.tm_mday += save; 220 221 if (tm.tm_mday > nd) { 222 tm.tm_mon++; 223 tm.tm_mday = tm.tm_mday - nd; 224 } 225 } 226 break; 227 228 case 'M': 229 if (WMseen) 230 return (-1); 231 WMseen++; 232 s++; 233 if (tolower(*s) == 'l') { 234 tm.tm_mday = nd; 235 s++; 236 t = s; 237 } else { 238 l = strtol(s, &t, 10); 239 if (l < 1 || l > 31) 240 return (-1); 241 242 if (l > nd) 243 return (-1); 244 tm.tm_mday = l; 245 } 246 break; 247 248 default: 249 return (-1); 250 break; 251 } 252 253 if (*t == '\0' || isspace(*t)) 254 break; 255 else 256 s = t; 257 } 258 259 tsecs = mktime(&tm); 260 /* 261 * Check for invalid times, including things like the missing 262 * hour when switching from "standard time" to "daylight saving". 263 */ 264 if (tsecs == (time_t)-1) 265 tsecs = (time_t)-2; 266 return (tsecs); 267 } 268