OpenDNSSEC-signer  1.4.1
duration.c
Go to the documentation of this file.
1 /*
2  * $Id: duration.c 7039 2013-02-15 08:10:15Z matthijs $
3  *
4  * Copyright (c) 2009 NLNet Labs. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
34 #include "shared/allocator.h"
35 #include "shared/duration.h"
36 #include "shared/log.h"
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 
43 static const char* duration_str = "duration";
44 
45 
52 {
53  duration_type* duration;
54  allocator_type* allocator = allocator_create(malloc, free);
55  if (!allocator) {
56  ods_log_error("[%s] cannot create: no allocator available",
57  duration_str);
58  return NULL;
59  }
60 
61  duration = (duration_type*) allocator_alloc(allocator,
62  sizeof(duration_type));
63  if (!duration) {
64  ods_log_error("[%s] cannot create: allocator failed", duration_str);
65  allocator_cleanup(allocator);
66  return NULL;
67  }
68  duration->allocator = allocator;
69  duration->years = 0;
70  duration->months = 0;
71  duration->weeks = 0;
72  duration->days = 0;
73  duration->hours = 0;
74  duration->minutes = 0;
75  duration->seconds = 0;
76  return duration;
77 }
78 
79 
84 int
86 {
87  if (!d1 && !d2) {
88  return 0;
89  }
90  if (!d1 || !d2) {
91  return d1?-1:1;
92  }
93 
94  if (d1->years != d2->years) {
95  return d1->years - d2->years;
96  }
97  if (d1->months != d2->months) {
98  return d1->months - d2->months;
99  }
100  if (d1->weeks != d2->weeks) {
101  return d1->weeks - d2->weeks;
102  }
103  if (d1->days != d2->days) {
104  return d1->days - d2->days;
105  }
106  if (d1->hours != d2->hours) {
107  return d1->hours - d2->hours;
108  }
109  if (d1->minutes != d2->minutes) {
110  return d1->minutes - d2->minutes;
111  }
112  if (d1->seconds != d2->seconds) {
113  return d1->seconds - d2->seconds;
114  }
115 
116  return 0;
117 }
118 
119 
126 {
127  duration_type* duration = duration_create();
128  char* P, *X, *T, *W;
129  int not_weeks = 0;
130 
131  if (!duration) {
132  ods_log_error("[%s] cannot create from string %s: create failed",
133  duration_str, str);
134  return NULL;
135  }
136  if (!str) {
137  return duration;
138  }
139 
140  P = strchr(str, 'P');
141  if (!P) {
142  ods_log_error("[%s] cannot create from string %s: P not found",
143  duration_str, str);
144  duration_cleanup(duration);
145  return NULL;
146  }
147 
148  T = strchr(str, 'T');
149  X = strchr(str, 'Y');
150  if (X) {
151  duration->years = atoi(str+1);
152  str = X;
153  not_weeks = 1;
154  }
155  X = strchr(str, 'M');
156  if (X && (!T || (size_t) (X-P) < (size_t) (T-P))) {
157  duration->months = atoi(str+1);
158  str = X;
159  not_weeks = 1;
160  }
161  X = strchr(str, 'D');
162  if (X) {
163  duration->days = atoi(str+1);
164  str = X;
165  not_weeks = 1;
166  }
167  if (T) {
168  str = T;
169  not_weeks = 1;
170  }
171  X = strchr(str, 'H');
172  if (X && T) {
173  duration->hours = atoi(str+1);
174  str = X;
175  not_weeks = 1;
176  }
177  X = strrchr(str, 'M');
178  if (X && T && (size_t) (X-P) > (size_t) (T-P)) {
179  duration->minutes = atoi(str+1);
180  str = X;
181  not_weeks = 1;
182  }
183  X = strchr(str, 'S');
184  if (X && T) {
185  duration->seconds = atoi(str+1);
186  str = X;
187  not_weeks = 1;
188  }
189 
190  W = strchr(str, 'W');
191  if (W) {
192  if (not_weeks) {
193  ods_log_error("[%s] cannot create from string: parse error",
194  duration_str, P);
195  duration_cleanup(duration);
196  return NULL;
197  } else {
198  duration->weeks = atoi(str+1);
199  str = W;
200  }
201  }
202  return duration;
203 }
204 
205 
210 static size_t
211 digits_in_number(time_t duration)
212 {
213  uint32_t period = (uint32_t) duration;
214  size_t count = 0;
215 
216  while (period > 0) {
217  count++;
218  period /= 10;
219  }
220  return count;
221 }
222 
223 
228 char*
230 {
231  char* str = NULL, *num = NULL;
232  size_t count = 2;
233  int T = 0;
234 
235  if (!duration) {
236  return NULL;
237  }
238 
239  if (duration->years > 0) {
240  count = count + 1 + digits_in_number(duration->years);
241  }
242  if (duration->months > 0) {
243  count = count + 1 + digits_in_number(duration->months);
244  }
245  if (duration->weeks > 0) {
246  count = count + 1 + digits_in_number(duration->weeks);
247  }
248  if (duration->days > 0) {
249  count = count + 1 + digits_in_number(duration->days);
250  }
251  if (duration->hours > 0) {
252  count = count + 1 + digits_in_number(duration->hours);
253  T = 1;
254  }
255  if (duration->minutes > 0) {
256  count = count + 1 + digits_in_number(duration->minutes);
257  T = 1;
258  }
259  if (duration->seconds > 0) {
260  count = count + 1 + digits_in_number(duration->seconds);
261  T = 1;
262  }
263  if (T) {
264  count++;
265  }
266 
267  str = (char*) calloc(count, sizeof(char));
268  str[0] = 'P';
269  str[1] = '\0';
270 
271  if (duration->years > 0) {
272  count = digits_in_number(duration->years);
273  num = (char*) calloc(count+2, sizeof(char));
274  if (num) {
275  snprintf(num, count+2, "%uY", (uint32_t) duration->years);
276  str = strncat(str, num, count+2);
277  free((void*) num);
278  } else {
279  goto duration2string_num_calloc_failed;
280  }
281  }
282  if (duration->months > 0) {
283  count = digits_in_number(duration->months);
284  num = (char*) calloc(count+2, sizeof(char));
285  if (num) {
286  snprintf(num, count+2, "%uM", (uint32_t) duration->months);
287  str = strncat(str, num, count+2);
288  free((void*) num);
289  } else {
290  goto duration2string_num_calloc_failed;
291  }
292  }
293  if (duration->weeks > 0) {
294  count = digits_in_number(duration->weeks);
295  num = (char*) calloc(count+2, sizeof(char));
296  if (num) {
297  snprintf(num, count+2, "%uW", (uint32_t) duration->weeks);
298  str = strncat(str, num, count+2);
299  free((void*) num);
300  } else {
301  goto duration2string_num_calloc_failed;
302  }
303  }
304  if (duration->days > 0) {
305  count = digits_in_number(duration->days);
306  num = (char*) calloc(count+2, sizeof(char));
307  if (num) {
308  snprintf(num, count+2, "%uD", (uint32_t) duration->days);
309  str = strncat(str, num, count+2);
310  free((void*) num);
311  } else {
312  goto duration2string_num_calloc_failed;
313  }
314  }
315  if (T) {
316  str = strncat(str, "T", 1);
317  }
318  if (duration->hours > 0) {
319  count = digits_in_number(duration->hours);
320  num = (char*) calloc(count+2, sizeof(char));
321  if (num) {
322  snprintf(num, count+2, "%uH", (uint32_t) duration->hours);
323  str = strncat(str, num, count+2);
324  free((void*) num);
325  } else {
326  goto duration2string_num_calloc_failed;
327  }
328  }
329  if (duration->minutes > 0) {
330  count = digits_in_number(duration->minutes);
331  num = (char*) calloc(count+2, sizeof(char));
332  if (num) {
333  snprintf(num, count+2, "%uM", (uint32_t) duration->minutes);
334  str = strncat(str, num, count+2);
335  free((void*) num);
336  } else {
337  goto duration2string_num_calloc_failed;
338  }
339  }
340  if (duration->seconds > 0) {
341  count = digits_in_number(duration->seconds);
342  num = (char*) calloc(count+2, sizeof(char));
343  if (num) {
344  snprintf(num, count+2, "%uS", (uint32_t) duration->seconds);
345  str = strncat(str, num, count+2);
346  free((void*) num);
347  } else {
348  goto duration2string_num_calloc_failed;
349  }
350  }
351  return str;
352 
353 duration2string_num_calloc_failed:
354  ods_log_error("[%s] cannot create string: malloc error", duration_str);
355  free((void*) str);
356  return NULL;
357 }
358 
359 
364 time_t
366 {
367  time_t period = 0;
368  char* dstr = NULL;
369 
370  if (duration) {
371  period += (duration->seconds);
372  period += (duration->minutes)*60;
373  period += (duration->hours)*3600;
374  period += (duration->days)*86400;
375  period += (duration->weeks)*86400*7;
376  period += (duration->months)*86400*31;
377  period += (duration->years)*86400*365;
378 
379  if (duration->months || duration->years) {
380  /* [TODO] calculate correct number of days in this month/year */
381  dstr = duration2string(duration);
382  ods_log_warning("[%s] converting duration %s to approximate value",
383  duration_str, dstr?dstr:"(null)");
384  free((void*) dstr);
385  }
386  }
387  return period;
388 }
389 
394 time_t
395 time_minimum(time_t a, time_t b)
396 {
397  return (a < b ? a : b);
398 }
399 
404 time_t
405 time_maximum(time_t a, time_t b)
406 {
407  return (a > b ? a : b);
408 }
409 
410 
415 time_t
416 ods_rand(time_t mod)
417 {
418 #ifdef HAVE_ARC4RANDOM_UNIFORM
419  return (time_t) (arc4random_uniform((uint32_t) mod+1));
420 #elif HAVE_ARC4RANDOM
421  return (time_t) (arc4random() % (unsigned) mod+1);
422 #else
423  return (time_t) (random() % (unsigned) mod+1);
424 #endif
425 }
426 
427 
428 /* Number of days per month (except for February in leap years). */
429 static const int mdays[] = {
430  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
431 };
432 
433 
434 static int
435 is_leap_year(int year)
436 {
437  return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
438 }
439 
440 
441 static int
442 leap_days(int y1, int y2)
443 {
444  --y1;
445  --y2;
446  return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
447 }
448 
449 
450 #ifdef ENFORCER_TIMESHIFT
451 /*
452  * Code taken from NSD 3.2.5, which is
453  * code adapted from Python 2.4.1 sources (Lib/calendar.py).
454  */
455 static time_t
456 mktime_from_utc(const struct tm *tm)
457 {
458  int year = 1900 + tm->tm_year;
459  time_t days = 365 * ((time_t) (year - 1970)) +
460  ((time_t) leap_days(1970, year));
461  time_t hours;
462  time_t minutes;
463  time_t seconds;
464  int i;
465 
466  for (i = 0; i < tm->tm_mon; ++i) {
467  days += mdays[i];
468  }
469  if (tm->tm_mon > 1 && is_leap_year(year)) {
470  ++days;
471  }
472  days += tm->tm_mday - 1;
473 
474  hours = days * 24 + tm->tm_hour;
475  minutes = hours * 60 + tm->tm_min;
476  seconds = minutes * 60 + tm->tm_sec;
477 
478  return seconds;
479 }
480 
481 
486 static time_t
487 timeshift2time(const char *time)
488 {
489  /* convert a string in format YYMMDDHHMMSS to time_t */
490  struct tm tm;
491  time_t timeshift = 0;
492 
493  /* Try to scan the time... */
494  if (strptime(time, "%Y%m%d%H%M%S", &tm)) {
495  timeshift = mktime_from_utc(&tm);
496  }
497  return timeshift;
498 }
499 #endif
500 
501 
506 time_t
507 time_now(void)
508 {
509 #ifdef ENFORCER_TIMESHIFT
510  const char* env = getenv("ENFORCER_TIMESHIFT");
511  if (env) {
512  return timeshift2time(env);
513  } else
514 #endif /* ENFORCER_TIMESHIFT */
515 
516  return time(NULL);
517 }
518 
519 
524 uint32_t
525 time_datestamp(time_t tt, const char* format, char** str)
526 {
527  time_t t;
528  struct tm *tmp;
529  uint32_t ut = 0;
530  char outstr[32];
531 
532  if (tt) {
533  t = tt;
534  } else {
535  t = time_now();
536  }
537 
538  tmp = localtime(&t);
539  if (tmp == NULL) {
540  ods_log_error("[%s] time_datestamp: localtime() failed", duration_str);
541  return 0;
542  }
543 
544  if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
545  ods_log_error("[%s] time_datestamp: strftime() failed", duration_str);
546  return 0;
547  }
548 
549  ut = (uint32_t) strtoul(outstr, NULL, 10);
550  if (str) {
551  *str = strdup(outstr);
552  }
553  return ut;
554 }
555 
556 static void
557 time_itoa_reverse(char* s)
558 {
559  int i, j;
560  char c;
561 
562  for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
563  c = s[i];
564  s[i] = s[j];
565  s[j] = c;
566  }
567  return;
568 }
569 
570 
575 void
576 time_itoa(time_t n, char* s)
577 {
578  int i = 0;
579 
580  do { /* generate digits in reverse order */
581  s[i++] = n % 10 + '0'; /* get next digit */
582  } while ((n /= 10) > 0); /* delete it */
583  s[i] = '\0';
584  time_itoa_reverse(s);
585  return;
586 }
587 
588 
593 void
595 {
596  allocator_type* allocator;
597 
598  if (!duration) {
599  return;
600  }
601  allocator = duration->allocator;
602  allocator_deallocate(allocator, (void*) duration);
603  allocator_cleanup(allocator);
604  return;
605 }