OpenDNSSEC-signer  1.3.9
duration.c
Go to the documentation of this file.
1 /*
2  * $Id: duration.c 4518 2011-02-24 15:39:09Z 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  snprintf(num, count+2, "%uY", (uint32_t) duration->years);
275  str = strncat(str, num, count+2);
276  free((void*) num);
277  }
278  if (duration->months > 0) {
279  count = digits_in_number(duration->months);
280  num = (char*) calloc(count+2, sizeof(char));
281  snprintf(num, count+2, "%uM", (uint32_t) duration->months);
282  str = strncat(str, num, count+2);
283  free((void*) num);
284  }
285  if (duration->weeks > 0) {
286  count = digits_in_number(duration->weeks);
287  num = (char*) calloc(count+2, sizeof(char));
288  snprintf(num, count+2, "%uW", (uint32_t) duration->weeks);
289  str = strncat(str, num, count+2);
290  free((void*) num);
291  }
292  if (duration->days > 0) {
293  count = digits_in_number(duration->days);
294  num = (char*) calloc(count+2, sizeof(char));
295  snprintf(num, count+2, "%uD", (uint32_t) duration->days);
296  str = strncat(str, num, count+2);
297  free((void*) num);
298  }
299  if (T) {
300  str = strncat(str, "T", 1);
301  }
302  if (duration->hours > 0) {
303  count = digits_in_number(duration->hours);
304  num = (char*) calloc(count+2, sizeof(char));
305  snprintf(num, count+2, "%uH", (uint32_t) duration->hours);
306  str = strncat(str, num, count+2);
307  free((void*) num);
308  }
309  if (duration->minutes > 0) {
310  count = digits_in_number(duration->minutes);
311  num = (char*) calloc(count+2, sizeof(char));
312  snprintf(num, count+2, "%uM", (uint32_t) duration->minutes);
313  str = strncat(str, num, count+2);
314  free((void*) num);
315  }
316  if (duration->seconds > 0) {
317  count = digits_in_number(duration->seconds);
318  num = (char*) calloc(count+2, sizeof(char));
319  snprintf(num, count+2, "%uS", (uint32_t) duration->seconds);
320  str = strncat(str, num, count+2);
321  free((void*) num);
322  }
323  return str;
324 }
325 
326 
331 time_t
333 {
334  time_t period = 0;
335  char* dstr = NULL;
336 
337  if (duration) {
338  period += (duration->seconds);
339  period += (duration->minutes)*60;
340  period += (duration->hours)*3600;
341  period += (duration->days)*86400;
342  period += (duration->weeks)*86400*7;
343  period += (duration->months)*86400*31;
344  period += (duration->years)*86400*365;
345 
346  if (duration->months || duration->years) {
347  /* [TODO] calculate correct number of days in this month/year */
348  dstr = duration2string(duration);
349  ods_log_warning("[%s] converting duration %s to approximate value",
350  duration_str, dstr?dstr:"(null)");
351  free((void*) dstr);
352  }
353  }
354  return period;
355 }
356 
361 time_t
362 time_minimum(time_t a, time_t b)
363 {
364  return (a < b ? a : b);
365 }
366 
371 time_t
372 time_maximum(time_t a, time_t b)
373 {
374  return (a > b ? a : b);
375 }
376 
377 
382 time_t
383 ods_rand(time_t mod)
384 {
385 #ifdef HAVE_ARC4RANDOM_UNIFORM
386  return (time_t) (arc4random_uniform((uint32_t) mod+1));
387 #elif HAVE_ARC4RANDOM
388  return (time_t) (arc4random() % (unsigned) mod+1);
389 #else
390  return (time_t) (random() % (unsigned) mod+1);
391 #endif
392 }
393 
394 
395 /* Number of days per month (except for February in leap years). */
396 static const int mdays[] = {
397  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
398 };
399 
400 
401 static int
402 is_leap_year(int year)
403 {
404  return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
405 }
406 
407 
408 static int
409 leap_days(int y1, int y2)
410 {
411  --y1;
412  --y2;
413  return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
414 }
415 
416 
417 /*
418  * Code taken from NSD 3.2.5, which is
419  * code adapted from Python 2.4.1 sources (Lib/calendar.py).
420  */
421 static time_t
422 mktime_from_utc(const struct tm *tm)
423 {
424  int year = 1900 + tm->tm_year;
425  time_t days = 365 * (year - 1970) + leap_days(1970, year);
426  time_t hours;
427  time_t minutes;
428  time_t seconds;
429  int i;
430 
431  for (i = 0; i < tm->tm_mon; ++i) {
432  days += mdays[i];
433  }
434  if (tm->tm_mon > 1 && is_leap_year(year)) {
435  ++days;
436  }
437  days += tm->tm_mday - 1;
438 
439  hours = days * 24 + tm->tm_hour;
440  minutes = hours * 60 + tm->tm_min;
441  seconds = minutes * 60 + tm->tm_sec;
442 
443  return seconds;
444 }
445 
446 
451 static time_t
452 timeshift2time(const char *time)
453 {
454  /* convert a string in format YYMMDDHHMMSS to time_t */
455  struct tm tm;
456  time_t timeshift = 0;
457 
458  /* Try to scan the time... */
459  if (strptime(time, "%Y%m%d%H%M%S", &tm)) {
460  timeshift = mktime_from_utc(&tm);
461  }
462  return timeshift;
463 }
464 
465 
470 time_t
471 time_now(void)
472 {
473 #ifdef ENFORCER_TIMESHIFT
474  const char* env = getenv("ENFORCER_TIMESHIFT");
475  if (env) {
476  return timeshift2time(env);
477  } else
478 #endif /* ENFORCER_TIMESHIFT */
479 
480  return time(NULL);
481 }
482 
483 
488 uint32_t
489 time_datestamp(time_t tt, const char* format, char** str)
490 {
491  time_t t;
492  struct tm *tmp;
493  uint32_t ut = 0;
494  char outstr[32];
495 
496  if (tt) {
497  t = tt;
498  } else {
499  t = time_now();
500  }
501 
502  tmp = localtime(&t);
503  if (tmp == NULL) {
504  ods_log_error("[%s] time_datestamp: localtime() failed", duration_str);
505  return 0;
506  }
507 
508  if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
509  ods_log_error("[%s] time_datestamp: strftime() failed", duration_str);
510  return 0;
511  }
512 
513  ut = (uint32_t) strtoul(outstr, NULL, 10);
514  if (str) {
515  *str = strdup(outstr);
516  }
517  return ut;
518 }
519 
520 static void
521 time_itoa_reverse(char* s)
522 {
523  int i, j;
524  char c;
525 
526  for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
527  c = s[i];
528  s[i] = s[j];
529  s[j] = c;
530  }
531  return;
532 }
533 
534 
539 void
540 time_itoa(time_t n, char* s)
541 {
542  int i = 0;
543 
544  do { /* generate digits in reverse order */
545  s[i++] = n % 10 + '0'; /* get next digit */
546  } while ((n /= 10) > 0); /* delete it */
547  s[i] = '\0';
548  time_itoa_reverse(s);
549  return;
550 }
551 
552 
557 void
559 {
560  allocator_type* allocator;
561 
562  if (!duration) {
563  return;
564  }
565  allocator = duration->allocator;
566  allocator_deallocate(allocator, (void*) duration);
567  allocator_cleanup(allocator);
568  return;
569 }