1 /*-
2 * Copyright (c) 2005 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include "config.h"
28
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31 "@(#)$Id: ipastat_time.c,v 1.2 2011/01/23 18:42:35 simon Exp $";
32 #endif /* !lint */
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <time.h>
41
42 #include "ipa_mod.h"
43
44 #include "queue.h"
45
46 #include "dlapi.h"
47 #include "confcommon.h"
48 #include "memfunc.h"
49
50 #include "ipastat_time.h"
51
52 #include "ipastat_log.h"
53 #include "ipastat_main.h"
54
55 /* List of all opt_tint structures. */
56 struct opt_tint_list opt_tint_list = STAILQ_HEAD_INITIALIZER(opt_tint_list);
57
58 /* Default opt_tint. */
59 static struct opt_tint opt_tint_default;
60
61 /* Regular expressions for checking time interval. */
62 #define PAT_TINT_FORM "^[^-]+(-[^-]+)?$"
63 #define PAT_TINT_PART "^\
64 (\
65 [[:digit:]]{4,}(\\.([[:digit:]]{1,2}|[[:alpha:]]{3}))?(\\.[[:digit:]]{1,2})?|\
66 ([[:digit:]]{1,2}|[[:alpha:]]{3})?(\\.[[:digit:]]{1,2})?\
67 )?\
68 (\
69 /[[:digit:]]{1,2}(:[[:digit:]]{1,2})?(:[[:digit:]]{1,2})?\
70 )?$"
71
72 static regex_t re_tint_form; /* Compiled PAT_TINT_FORM. */
73 static regex_t re_tint_part; /* Compiled PAT_TINT_PART. */
74
75 static ipa_tm curdate; /* Current date. */
76
77 static const char month_name[MONTHES_IN_YEAR][3] = {
78 "jan", "feb", "mar", "apr", "may", "jun",
79 "jul", "aug", "sep", "oct", "nov", "dec"
80 };
81
82 /*
83 * Initialized various local date related variables.
84 */
85 int
init_time_data(void)86 init_time_data(void)
87 {
88 time_t t;
89
90 /* Get current date. */
91 if (time(&t) == (time_t)-1) {
92 logmsg(IPA_LOG_ERR, "init_time_data: time failed");
93 return (-1);
94 }
95 if (localtime_r(&t, (struct tm *)&curdate) == NULL) {
96 logmsg(IPA_LOG_ERR, "init_time_data: localtime_r failed");
97 return (-1);
98 }
99 curdate.tm_year += 1900;
100 curdate.tm_mon++;
101 return (0);
102 }
103
104 /*
105 * Compare two ipa_tm tm1 and tm2 structures:
106 * if (tm1 == tm2)
107 * return (0);
108 * if (tm1 > tm2)
109 * return (1);
110 * if (tm1 < tm2)
111 * return (-1);
112 */
113 static int
cmp_ipa_tm(const ipa_tm * tm1,const ipa_tm * tm2)114 cmp_ipa_tm(const ipa_tm *tm1, const ipa_tm *tm2)
115 {
116 /*
117 * We do not use mktime(3), because there can be problem
118 * with time zone and summer time.
119 */
120 if (tm1->tm_year > tm2->tm_year)
121 return (1);
122 if (tm1->tm_year == tm2->tm_year) {
123 if (tm1->tm_mon > tm2->tm_mon)
124 return (1);
125 if (tm1->tm_mon == tm2->tm_mon) {
126 if (tm1->tm_mday > tm2->tm_mday)
127 return (1);
128 if (tm1->tm_mday == tm2->tm_mday) {
129 if (tm1->tm_hour > tm2->tm_hour)
130 return (1);
131 if (tm1->tm_hour == tm2->tm_hour) {
132 if (tm1->tm_min > tm2->tm_min)
133 return (1);
134 if (tm1->tm_min == tm2->tm_min) {
135 if (tm1->tm_sec > tm2->tm_sec)
136 return (1);
137 if (tm1->tm_sec == tm2->tm_sec)
138 return (0);
139 }
140 }
141 }
142 }
143 }
144 return (-1);
145 }
146
147 /*
148 * Return last month day in the given year/mon.
149 */
150 static int
last_mday(int year,int mon)151 last_mday(int year, int mon)
152 {
153 static int const last_mday_arr[MONTHES_IN_YEAR + 1] =
154 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
155 { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
156
157 if (mon == 2) {
158 /* February. */
159 return ((year % 4 == 0 &&
160 (year % 100 != 0 || year % 400 == 0)) ? 29 : 28);
161 }
162 return (last_mday_arr[mon]);
163 }
164
165 /*
166 * Output error message, that month, day, hours, minutes or seconds are
167 * specified incorrect in some part of time interval.
168 */
169 static void
wrong_tint_part(const char * what,int right)170 wrong_tint_part(const char *what, int right)
171 {
172 logmsgx(IPA_LOG_ERR, "wrong format of time interval's %s part: "
173 "incorrect %s value", right ? "right" : "left", what);
174 }
175
176 static int
parse_tint_date(char * s,ipa_tm * tm,int right)177 parse_tint_date(char *s, ipa_tm *tm, int right)
178 {
179 char *ptr;
180 unsigned int n, val1, val2, val3;
181
182 if (*s == '.') {
183 /* .DD */
184 errno = 0;
185 if (sscanf(++s, "%d", &tm->tm_mday) != 1) {
186 logmsgx(IPA_LOG_ERR, "parse_tint_date: "
187 "sscanf(\"%s\", %%u): failed", s);
188 return (-1);
189 }
190 goto done;
191 }
192
193 /* Check if there is named month. */
194 for (ptr = s; *ptr != '\0'; ++ptr)
195 if (isalpha((unsigned char)*ptr)) {
196 for (n = 0; n < MONTHES_IN_YEAR; ++n)
197 if (strncasecmp(ptr, month_name[n], 3) == 0)
198 break;
199 if (n == MONTHES_IN_YEAR) {
200 logmsgx(IPA_LOG_ERR, "parse_tint_date: wrong "
201 "format of time interval: unknown month "
202 "name");
203 return (-1);
204 }
205 if (sprintf(ptr, "%02u", n + 1) != 2) {
206 logmsgx(IPA_LOG_ERR, "parse_tint_date: "
207 "sprintf failed");
208 return (-1);
209 }
210 ptr += 2;
211 do {
212 *ptr = *(ptr + 1);
213 } while (*++ptr != '\0');
214 break;
215 }
216
217 errno = 0;
218 n = sscanf(s, "%u.%u.%u", &val1, &val2, &val3);
219 if (n < 1) {
220 logmsg(IPA_LOG_ERR, "parse_tint_date: "
221 "sscanf(\"%s\", %%u.%%u.%%u) failed", s);
222 return (-1);
223 }
224 ptr = strchr(s, '.');
225 if (ptr == NULL) {
226 ptr = strchr(s, '/');
227 if (ptr == NULL)
228 ptr = strchr(s, '\0');
229 }
230 switch (n) {
231 case 3:
232 /* YYYY.MM.DD */
233 tm->tm_year = val1;
234 tm->tm_mon = val2;
235 tm->tm_mday = val3;
236 break;
237 case 2:
238 if (ptr - s >= 4) {
239 /* YYYY.MM */
240 tm->tm_year = val1;
241 tm->tm_mon = val2;
242 tm->tm_mday = right ? last_mday(val1, val2) : 1;
243 } else {
244 /* MM.DD */
245 tm->tm_mon = val1;
246 tm->tm_mday = val2;
247 }
248 break;
249 default: /* 1 */
250 if (ptr - s >= 4) {
251 /* YYYY */
252 tm->tm_year = val1;
253 if (right) {
254 tm->tm_mon = 12;
255 tm->tm_mday = last_mday(val1, 12);
256 } else
257 tm->tm_mon = tm->tm_mday = 1;
258 } else {
259 /* MM */
260 tm->tm_mon = val1;
261 tm->tm_mday = right ? last_mday(tm->tm_year, val1) : 1;
262 }
263 }
264
265 done:
266 /* Validate month and month day. */
267 if (tm->tm_mon == 0 || tm->tm_mon > 12) {
268 logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong month %d",
269 tm->tm_mon);
270 return (-1);
271 }
272 if (tm->tm_mday == 0 || tm->tm_mday > 31) {
273 logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong month day %d",
274 tm->tm_mday);
275 return (-1);
276 }
277 return (0);
278 }
279
280 static int
parse_tint_time(const char * s,ipa_tm * tm,int right)281 parse_tint_time(const char *s, ipa_tm *tm, int right)
282 {
283 unsigned int val1, val2, val3;
284 int n;
285
286 errno = 0;
287 n = sscanf(++s, "%u:%u:%u", &val1, &val2, &val3);
288 if (n < 1) {
289 logmsg(IPA_LOG_ERR, "parse_tint_time: "
290 "sscanf(\"%s\", %%u:%%u:%%u) failed", s);
291 return (-1);
292 }
293 if (n >= 1)
294 tm->tm_hour = val1; /* hh */
295 if (n >= 2)
296 tm->tm_min = val2; /* hh:mm */
297 if (n == 3)
298 tm->tm_sec = val3; /* hh:mm:ss */
299
300 /* Validate month, day, hours, minutes and seconds. */
301 if (tm->tm_mon == 0 || tm->tm_mon > 12) {
302 wrong_tint_part("month", right);
303 return (-1);
304 }
305 if (tm->tm_mday == 0 ||
306 tm->tm_mday > last_mday(tm->tm_year, tm->tm_mon)) {
307 wrong_tint_part("day", right);
308 return (-1);
309 }
310 if (tm->tm_hour > 23 && !(tm->tm_hour == HOURS_IN_DAY &&
311 tm->tm_min == 0 && tm->tm_sec == 0)) {
312 wrong_tint_part("hours", right);
313 return (-1);
314 }
315 if (tm->tm_min > 59) {
316 wrong_tint_part("minutes", right);
317 return (-1);
318 }
319 if (tm->tm_sec > 59) {
320 wrong_tint_part("seconds", right);
321 return (-1);
322 }
323 return (n);
324 }
325
326 static int
parse_tint_part(char * s,ipa_tm * tm,int right)327 parse_tint_part(char *s, ipa_tm *tm, int right)
328 {
329 int n;
330
331 /*
332 * Initial value for time in the left part, for the right
333 * part these values are completed at the end of this function.
334 */
335 tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
336
337 if (*s != '/') {
338 /* Date is specified. */
339 if (parse_tint_date(s, tm, right) < 0)
340 return (-1);
341 }
342
343 n = 0;
344 s = strchr(s, '/');
345 if (s != NULL) {
346 /* Time is specified. */
347 n = parse_tint_time(s, tm, right);
348 if (n < 0)
349 return (-1);
350 }
351
352 /* Complete value of time for the right part, if needed. */
353 if (right)
354 switch (n) {
355 case 2:
356 /* hh:mm */
357 if (++tm->tm_min > 59)
358 tm->tm_min = 0;
359 else
360 break;
361 /* FALLTHROUGH */
362 case 1:
363 /* hh */
364 if (++tm->tm_hour <= 23)
365 break;
366 /* FALLTHROUGH */
367 case 0:
368 tm->tm_hour = HOURS_IN_DAY;
369 }
370
371 return (0);
372 }
373
374 /*
375 * Add optional time interval "-q -i|I interval" and parse this
376 * interval.
377 */
378 int
opt_tint_add(char * s,int exact)379 opt_tint_add(char *s, int exact)
380 {
381 static char inited = 0;
382
383 struct tm *tm1, *tm2;
384 struct opt_tint *tint;
385 char *right_part;
386
387 if (!inited) {
388 int error;
389
390 /* Build regular expressions. */
391 error = regcomp(&re_tint_form, PAT_TINT_FORM,
392 REG_EXTENDED|REG_NOSUB);
393 if (error != 0) {
394 logmsgx(IPA_LOG_ERR, "opt_tint_add: regcomp(%s): %s",
395 PAT_TINT_FORM, regerrbuf(error));
396 return (-1);
397 }
398 error = regcomp(&re_tint_part, PAT_TINT_PART,
399 REG_EXTENDED|REG_NOSUB);
400 if (error != 0) {
401 logmsgx(IPA_LOG_ERR, "opt_tint_add: regcomp(%s): %s",
402 PAT_TINT_PART, regerrbuf(error));
403 return (-1);
404 }
405 inited = 1;
406 }
407
408 /* Validate time interval format. */
409 if (regexec_simple(&re_tint_form, s) != 0) {
410 logmsgx(IPA_LOG_ERR, "opt_tint_add: wrong format of time "
411 "interval \"%s\"", s);
412 return (-1);
413 }
414
415 /* Split time interval into parts. */
416 right_part = strchr(s, '-');
417 if (right_part != NULL)
418 /* Split time interval into left and right parts. */
419 *right_part++ = '\0';
420 else
421 /* Left and right parts are the same as whole time interval. */
422 right_part = s;
423
424 if (regexec_simple(&re_tint_part, s) != 0) {
425 logmsgx(IPA_LOG_ERR, "opt_tint_add: cannot recognize format "
426 "of time interval's left part \"%s\"", s);
427 return (-1);
428 }
429 if (right_part != s)
430 if (regexec_simple(&re_tint_part, right_part) != 0) {
431 logmsgx(IPA_LOG_ERR, "opt_tint_add: cannot recognize "
432 "format of time interval's right part \"%s\"",
433 right_part);
434 return (-1);
435 }
436
437 tint = mem_malloc(sizeof(*tint), m_anon);
438 if (tint == NULL) {
439 logmsgx(IPA_LOG_ERR, "opt_tint_add: mem_malloc failed");
440 return (-1);
441 }
442
443 tint->tm1 = tint->tm2 = curdate;
444
445 tm1 = &tint->tm1;
446 tm2 = &tint->tm2;
447 if (parse_tint_part(s, tm1, 0) < 0 ||
448 parse_tint_part(right_part, tm2, 1) < 0)
449 return (-1);
450
451 if (cmp_ipa_tm(&tint->tm1, &tint->tm2) > 0) {
452 logmsgx(IPA_LOG_ERR, "opt_tint_add: first timestamp "
453 "(%d.%02d.%02d/%02d:%02d:%02d) should be less than or "
454 "equal to second timestamp (%d.%02d.%02d/%02d:%02d:%02d)",
455 tm1->tm_year, tm1->tm_mon, tm1->tm_mday,
456 tm1->tm_hour, tm1->tm_min, tm1->tm_sec,
457 tm2->tm_year, tm2->tm_mon, tm2->tm_mday,
458 tm2->tm_hour, tm2->tm_min, tm2->tm_sec);
459 return (-1);
460 }
461
462 tint->exact = exact;
463 STAILQ_INSERT_TAIL(&opt_tint_list, tint, link);
464 return (0);
465 }
466
467 /*
468 * Release memory previously allocated by all opt_tint_add() calls.
469 */
470 void
opt_tint_free(void)471 opt_tint_free(void)
472 {
473 if (STAILQ_EMPTY(&opt_tint_list) ||
474 STAILQ_FIRST(&opt_tint_list) != &opt_tint_default) {
475 struct opt_tint *tint, *tint_next;
476
477 STAILQ_FOREACH_SAFE(tint, &opt_tint_list, link, tint_next)
478 mem_free(tint, m_anon);
479 regfree(&re_tint_form);
480 regfree(&re_tint_part);
481 }
482 }
483
484 /*
485 * If there are not any -i options, than create default one,
486 * which is equal to the current month.
487 */
488 void
opt_tint_init(void)489 opt_tint_init(void)
490 {
491 #define tm1 opt_tint_default.tm1
492 #define tm2 opt_tint_default.tm2
493 tm1.tm_year = tm2.tm_year = curdate.tm_year;
494 tm1.tm_mon = tm2.tm_mon = curdate.tm_mon;
495 tm1.tm_mday = 1;
496 tm2.tm_mday = last_mday(curdate.tm_year, curdate.tm_mon);
497 tm1.tm_hour = tm1.tm_min = tm1.tm_sec = tm2.tm_min = tm2.tm_sec = 0;
498 tm2.tm_hour = HOURS_IN_DAY;
499 STAILQ_INSERT_HEAD(&opt_tint_list, &opt_tint_default, link);
500 #undef tm1
501 #undef tm2
502 }
503