OpenDNSSEC-signer 1.2.1

/build/buildd-opendnssec_1.2.1.dfsg-1-ia64-j6OroR/opendnssec-1.2.1.dfsg/signer/src/util/duration.c

Go to the documentation of this file.
00001 /*
00002  * $Id: duration.c 4294 2011-01-13 19:58:29Z jakob $
00003  *
00004  * Copyright (c) 2009 NLNet Labs. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00019  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00020  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00021  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00023  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00025  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  *
00027  */
00028 
00034 #include "util/duration.h"
00035 #include "util/log.h"
00036 #include "util/se_malloc.h"
00037 
00038 #include <stdio.h> /* snprintf() */
00039 #include <stdlib.h> /* atoi(), strtoul(), [arc4]random[_uniform](), getenv() */
00040 #include <string.h> /* strncat(), strchr() */
00041 #include <time.h> /* time(), localtime(), strftime() */
00042 
00043 
00048 duration_type*
00049 duration_create(void)
00050 {
00051     duration_type* duration = (duration_type*)
00052         se_malloc(sizeof(duration_type));
00053     duration->years = 0;
00054     duration->months = 0;
00055     duration->weeks = 0;
00056     duration->days = 0;
00057     duration->hours = 0;
00058     duration->minutes = 0;
00059     duration->seconds = 0;
00060     return duration;
00061 }
00062 
00063 
00068 int
00069 duration_compare(duration_type* d1, duration_type* d2)
00070 {
00071     if (!d1 && !d2) {
00072         return 0;
00073     }
00074     if (!d1 || !d2) {
00075         return d1?-1:1;
00076     }
00077 
00078     if (d1->years != d2->years) {
00079         return d1->years - d2->years;
00080     }
00081     if (d1->months != d2->months) {
00082         return d1->months - d2->months;
00083     }
00084     if (d1->weeks != d2->weeks) {
00085         return d1->weeks - d2->weeks;
00086     }
00087     if (d1->days != d2->days) {
00088         return d1->days - d2->days;
00089     }
00090     if (d1->hours != d2->hours) {
00091         return d1->hours - d2->hours;
00092     }
00093     if (d1->minutes != d2->minutes) {
00094         return d1->minutes - d2->minutes;
00095     }
00096     if (d1->seconds != d2->seconds) {
00097         return d1->seconds - d2->seconds;
00098     }
00099 
00100     return 0;
00101 }
00102 
00107 duration_type*
00108 duration_create_from_string(const char* str)
00109 {
00110     duration_type* duration = duration_create();
00111     char* P, *X, *T, *W;
00112     int not_weeks = 0;
00113 
00114     if (!str) {
00115         return duration;
00116     }
00117 
00118     P = strchr(str, 'P');
00119     if (!P) {
00120         se_log_error("unable to create duration from string %s", str);
00121         duration_cleanup(duration);
00122         return NULL;
00123     }
00124 
00125     T = strchr(str, 'T');
00126     X = strchr(str, 'Y');
00127     if (X) {
00128         duration->years = atoi(str+1);
00129         str = X;
00130         not_weeks = 1;
00131     }
00132     X = strchr(str, 'M');
00133     if (X && (!T || (size_t) (X-P) < (size_t) (T-P))) {
00134         duration->months = atoi(str+1);
00135         str = X;
00136         not_weeks = 1;
00137     }
00138     X = strchr(str, 'D');
00139     if (X) {
00140         duration->days = atoi(str+1);
00141         str = X;
00142         not_weeks = 1;
00143     }
00144     if (T) {
00145         str = T;
00146         not_weeks = 1;
00147     }
00148     X = strchr(str, 'H');
00149     if (X && T) {
00150         duration->hours = atoi(str+1);
00151         str = X;
00152         not_weeks = 1;
00153     }
00154     X = strrchr(str, 'M');
00155     if (X && T && (size_t) (X-P) > (size_t) (T-P)) {
00156         duration->minutes = atoi(str+1);
00157         str = X;
00158         not_weeks = 1;
00159     }
00160     X = strchr(str, 'S');
00161     if (X && T) {
00162         duration->seconds = atoi(str+1);
00163         str = X;
00164         not_weeks = 1;
00165     }
00166 
00167     W = strchr(str, 'W');
00168     if (W) {
00169         if (not_weeks) {
00170             se_log_error("unable to create duration from string %s", P);
00171             duration_cleanup(duration);
00172             return NULL;
00173         } else {
00174             duration->weeks = atoi(str+1);
00175             str = W;
00176         }
00177     }
00178     return duration;
00179 }
00180 
00181 
00186 static size_t
00187 digits_in_number(time_t duration)
00188 {
00189     uint32_t period = (uint32_t) duration;
00190     size_t count = 0;
00191 
00192     while (period > 0) {
00193         count++;
00194         period /= 10;
00195     }
00196     return count;
00197 }
00198 
00199 
00204 char*
00205 duration2string(duration_type* duration)
00206 {
00207     char* str = NULL, *num = NULL;
00208     size_t count = 2;
00209     int T = 0;
00210 
00211     if (!duration) {
00212         str = (char*) se_calloc(5, sizeof(char));
00213         str[0] = '\0';
00214         str = strncat(str, "None", 4);
00215         return str;
00216     }
00217 
00218     if (duration->years > 0) {
00219         count = count + 1 + digits_in_number(duration->years);
00220     }
00221     if (duration->months > 0) {
00222         count = count + 1 + digits_in_number(duration->months);
00223     }
00224     if (duration->weeks > 0) {
00225         count = count + 1 + digits_in_number(duration->weeks);
00226     }
00227     if (duration->days > 0) {
00228         count = count + 1 + digits_in_number(duration->days);
00229     }
00230     if (duration->hours > 0) {
00231         count = count + 1 + digits_in_number(duration->hours);
00232         T = 1;
00233     }
00234     if (duration->minutes > 0) {
00235         count = count + 1 + digits_in_number(duration->minutes);
00236         T = 1;
00237     }
00238     if (duration->seconds > 0) {
00239         count = count + 1 + digits_in_number(duration->seconds);
00240         T = 1;
00241     }
00242     if (T) {
00243         count++;
00244     }
00245 
00246     str = (char*) se_calloc(count, sizeof(char));
00247     str[0] = 'P';
00248     str[1] = '\0';
00249 
00250     if (duration->years > 0) {
00251         count = digits_in_number(duration->years);
00252         num = (char*) se_calloc(count+2, sizeof(char));
00253         snprintf(num, count+2, "%uY", (uint32_t) duration->years);
00254         str = strncat(str, num, count+2);
00255         se_free((void*) num);
00256     }
00257     if (duration->months > 0) {
00258         count = digits_in_number(duration->months);
00259         num = (char*) se_calloc(count+2, sizeof(char));
00260         snprintf(num, count+2, "%uM", (uint32_t) duration->months);
00261         str = strncat(str, num, count+2);
00262         se_free((void*) num);
00263     }
00264     if (duration->weeks > 0) {
00265         count = digits_in_number(duration->weeks);
00266         num = (char*) se_calloc(count+2, sizeof(char));
00267         snprintf(num, count+2, "%uW", (uint32_t) duration->weeks);
00268         str = strncat(str, num, count+2);
00269         se_free((void*) num);
00270     }
00271     if (duration->days > 0) {
00272         count = digits_in_number(duration->days);
00273         num = (char*) se_calloc(count+2, sizeof(char));
00274         snprintf(num, count+2, "%uD", (uint32_t) duration->days);
00275         str = strncat(str, num, count+2);
00276         se_free((void*) num);
00277     }
00278     if (T) {
00279         str = strncat(str, "T", 1);
00280     }
00281     if (duration->hours > 0) {
00282         count = digits_in_number(duration->hours);
00283         num = (char*) se_calloc(count+2, sizeof(char));
00284         snprintf(num, count+2, "%uH", (uint32_t) duration->hours);
00285         str = strncat(str, num, count+2);
00286         se_free((void*) num);
00287     }
00288     if (duration->minutes > 0) {
00289         count = digits_in_number(duration->minutes);
00290         num = (char*) se_calloc(count+2, sizeof(char));
00291         snprintf(num, count+2, "%uM", (uint32_t) duration->minutes);
00292         str = strncat(str, num, count+2);
00293         se_free((void*) num);
00294     }
00295     if (duration->seconds > 0) {
00296         count = digits_in_number(duration->seconds);
00297         num = (char*) se_calloc(count+2, sizeof(char));
00298         snprintf(num, count+2, "%uS", (uint32_t) duration->seconds);
00299         str = strncat(str, num, count+2);
00300         se_free((void*) num);
00301     }
00302     return str;
00303 }
00304 
00305 
00310 time_t
00311 duration2time(duration_type* duration)
00312 {
00313     time_t period = 0;
00314     char* dstr = NULL;
00315 
00316     if (duration) {
00317         period += (duration->seconds);
00318         period += (duration->minutes)*60;
00319         period += (duration->hours)*3600;
00320         period += (duration->days)*86400;
00321         period += (duration->weeks)*86400*7;
00322         period += (duration->months)*86400*31;
00323         period += (duration->years)*86400*365;
00324 
00325         if (duration->months || duration->years) {
00326             /* TODO calculate correct number of days in this month/year */
00327             dstr = duration2string(duration);
00328             se_log_warning("converting duration %s to approximate value",
00329                 dstr?dstr:"(null)");
00330             se_free((void*) dstr);
00331         }
00332     }
00333     return period;
00334 }
00335 
00340 time_t
00341 time_minimum(time_t a, time_t b)
00342 {
00343     return (a < b ? a : b);
00344 }
00345 
00350 time_t
00351 time_maximum(time_t a, time_t b)
00352 {
00353     return (a > b ? a : b);
00354 }
00355 
00356 
00361 time_t
00362 se_rand(time_t mod)
00363 {
00364 #ifdef HAVE_ARC4RANDOM_UNIFORM
00365     return (time_t) (arc4random_uniform((uint32_t) mod+1));
00366 #elif HAVE_ARC4RANDOM
00367     return (time_t) (arc4random() % (unsigned) mod+1);
00368 #else
00369     return (time_t) (random() % (unsigned) mod+1);
00370 #endif
00371 }
00372 
00373 
00374 /* Number of days per month (except for February in leap years). */
00375 static const int mdays[] = {
00376     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00377 };
00378 
00379 
00380 static int
00381 is_leap_year(int year)
00382 {
00383     return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
00384 }
00385 
00386 
00387 static int
00388 leap_days(int y1, int y2)
00389 {
00390     --y1;
00391     --y2;
00392     return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
00393 }
00394 
00395 
00396 /*
00397  * Code taken from NSD 3.2.5, which is
00398  * code adapted from Python 2.4.1 sources (Lib/calendar.py).
00399  */
00400 static time_t
00401 mktime_from_utc(const struct tm *tm)
00402 {
00403     int year = 1900 + tm->tm_year;
00404     time_t days = 365 * (year - 1970) + leap_days(1970, year);
00405     time_t hours;
00406     time_t minutes;
00407     time_t seconds;
00408     int i;
00409 
00410     for (i = 0; i < tm->tm_mon; ++i) {
00411         days += mdays[i];
00412     }
00413     if (tm->tm_mon > 1 && is_leap_year(year)) {
00414         ++days;
00415     }
00416     days += tm->tm_mday - 1;
00417 
00418     hours = days * 24 + tm->tm_hour;
00419     minutes = hours * 60 + tm->tm_min;
00420     seconds = minutes * 60 + tm->tm_sec;
00421 
00422     return seconds;
00423 }
00424 
00425 
00430 time_t
00431 timeshift2time(const char *time)
00432 {
00433         /* convert a string in format YYMMDDHHMMSS to time_t */
00434         struct tm tm;
00435         time_t timeshift = 0;
00436 
00437         /* Try to scan the time... */
00438         if (strptime(time, "%Y%m%d%H%M%S", &tm)) {
00439                 timeshift = mktime_from_utc(&tm);
00440         }
00441         return timeshift;
00442 }
00443 
00444 
00449 time_t
00450 time_now(void)
00451 {
00452 #ifdef ENFORCER_TIMESHIFT
00453     const char* env = getenv("ENFORCER_TIMESHIFT");
00454     if (env) {
00455         return timeshift2time(env);
00456     } else
00457 #endif /* ENFORCER_TIMESHIFT */
00458 
00459     return time(NULL);
00460 }
00461 
00462 
00467 uint32_t
00468 time_datestamp(time_t tt, const char* format, char** str)
00469 {
00470     time_t t;
00471     struct tm *tmp;
00472     uint32_t ut = 0;
00473     char outstr[32];
00474 
00475     if (tt) {
00476         t = tt;
00477     } else {
00478         t = time_now();
00479     }
00480 
00481     tmp = localtime(&t);
00482     if (tmp == NULL) {
00483         se_log_error("time_datestamp: localtime() failed");
00484         return 0;
00485     }
00486 
00487     if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
00488         se_log_error("time_datestamp: strftime() failed");
00489         return 0;
00490     }
00491 
00492     ut = (uint32_t) strtoul(outstr, NULL, 10);
00493     if (str) {
00494         *str = se_strdup(outstr);
00495     }
00496     return ut;
00497 }
00498 
00499 static void
00500 time_itoa_reverse(char* s)
00501 {
00502     int i, j;
00503     char c;
00504 
00505     for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
00506         c = s[i];
00507         s[i] = s[j];
00508         s[j] = c;
00509     }
00510     return;
00511 }
00512 
00513 
00518 void
00519 time_itoa(time_t n, char* s)
00520 {
00521     int i = 0;
00522 
00523     do {       /* generate digits in reverse order */
00524         s[i++] = n % 10 + '0';   /* get next digit */
00525     } while ((n /= 10) > 0);     /* delete it */
00526     s[i] = '\0';
00527     time_itoa_reverse(s);
00528     return;
00529 }
00530 
00531 
00536 void
00537 duration_cleanup(duration_type* duration)
00538 {
00539     if (duration) {
00540         se_free((void*) duration);
00541     } else {
00542         se_log_warning("cleanup empty duration");
00543     }
00544 }