diff --git a/include/crm/common/iso8601.h b/include/crm/common/iso8601.h index 7c8287dc1e..61c8435832 100644 --- a/include/crm/common/iso8601.h +++ b/include/crm/common/iso8601.h @@ -1,160 +1,128 @@ /* * Copyright (C) 2005 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * http://en.wikipedia.org/wiki/ISO_8601 * */ #ifndef CRM_COMMON_ISO8601 # define CRM_COMMON_ISO8601 # include # include # include -typedef struct ha_has_time_s { - gboolean years; - - gboolean months; - gboolean days; - - gboolean weeks; - gboolean weekdays; - gboolean weekyears; - - gboolean yeardays; - - gboolean hours; - gboolean minutes; - gboolean seconds; -} ha_has_time_t; - -typedef struct ha_time_s { - time_t tm_now; - - int years; - - int months; - int days; - - int weeks; - int weekdays; - int weekyears; - - int yeardays; - - int hours; - int minutes; - int seconds; - - struct ha_time_s *offset; - struct ha_time_s *normalized; - struct ha_has_time_s *has; -} ha_time_t; +typedef struct ha_time_s ha_time_t; enum date_fields { date_month, date_day }; typedef struct ha_time_period_s { ha_time_t *start; ha_time_t *end; ha_time_t *diff; } ha_time_period_t; # define ha_log_date 0x01 # define ha_log_time 0x02 # define ha_log_local 0x04 # define ha_date_ordinal 0x10 # define ha_date_weeks 0x20 +# define ha_date_seconds 0x100 +# define ha_date_epoch 0x200 + int str_lookup(const char *str, enum date_fields); char *date_to_string(ha_time_t * dt, int flags); void log_date(int log_level, const char *prefix, ha_time_t * dt, int flags); void log_time_period(int log_level, ha_time_period_t * dtp, int flags); -ha_time_t *parse_time(char **time_str, ha_time_t * atime, gboolean with_offset); -ha_time_t *parse_time_offset(char **offset_str); ha_time_t *parse_date(char **date_str); ha_time_t *parse_time_duration(char **duration_str); ha_time_period_t *parse_time_period(char **period_str); /* ha_time_interval_t *parse_time_interval(char **interval_str); */ unsigned long long int date_in_seconds(ha_time_t * a_date); unsigned long long int date_in_seconds_since_epoch(ha_time_t * a_date); int compare_date(ha_time_t * lhs, ha_time_t * rhs); gboolean parse_int(char **str, int field_width, int uppper_bound, int *result); gboolean check_for_ordinal(const char *str); void ha_set_time(ha_time_t * lhs, ha_time_t * rhs, gboolean offset); void ha_set_tm_time(ha_time_t * lhs, struct tm *rhs); void ha_set_timet_time(ha_time_t * lhs, time_t * rhs); ha_time_t *add_time(ha_time_t * lhs, ha_time_t * rhs); ha_time_t *subtract_time(ha_time_t * lhs, ha_time_t * rhs); ha_time_t *subtract_duration(ha_time_t * time, ha_time_t * duration); void reset_tm(struct tm *some_tm); void add_seconds(ha_time_t * a_time, int extra); void add_minutes(ha_time_t * a_time, int extra); void add_hours(ha_time_t * a_time, int extra); void add_days(ha_time_t * a_time, int extra); void add_weekdays(ha_time_t * a_time, int extra); void add_yeardays(ha_time_t * a_time, int extra); void add_weeks(ha_time_t * a_time, int extra); void add_months(ha_time_t * a_time, int extra); void add_years(ha_time_t * a_time, int extra); void add_ordinalyears(ha_time_t * a_time, int extra); void add_weekyears(ha_time_t * a_time, int extra); void sub_seconds(ha_time_t * a_time, int extra); void sub_minutes(ha_time_t * a_time, int extra); void sub_hours(ha_time_t * a_time, int extra); void sub_days(ha_time_t * a_time, int extra); void sub_weekdays(ha_time_t * a_time, int extra); void sub_yeardays(ha_time_t * a_time, int extra); void sub_weeks(ha_time_t * a_time, int extra); void sub_months(ha_time_t * a_time, int extra); void sub_years(ha_time_t * a_time, int extra); void sub_ordinalyears(ha_time_t * a_time, int extra); void sub_weekyears(ha_time_t * a_time, int extra); +int crm_get_time(ha_time_t *now, uint32_t *h, uint32_t *m, uint32_t *s); +int crm_get_gregorian_date(ha_time_t *now, uint32_t *y, uint32_t *m, uint32_t *d); +int crm_get_ordinal_date(ha_time_t *now, uint32_t *y, uint32_t *d); +int crm_get_week_date(ha_time_t *now, uint32_t *y, uint32_t *w, uint32_t *d); + /* conversion functions */ int january1(int year); gboolean convert_from_weekdays(ha_time_t * a_date); gboolean convert_from_ordinal(ha_time_t * a_date); gboolean convert_from_gregorian(ha_time_t * a_date); gboolean is_leap_year(int year); int weeks_in_year(int year); int days_per_month(int month, int year); gboolean is_date_sane(ha_time_t * a_date); ha_time_t *new_ha_date(gboolean set_to_now); void free_ha_date(ha_time_t * a_date); void reset_time(ha_time_t * a_time); void log_tm_date(int log_level, struct tm *some_tm); #endif diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c index 7922191dfd..566c605b58 100644 --- a/lib/common/iso8601.c +++ b/lib/common/iso8601.c @@ -1,1380 +1,1120 @@ /* * Copyright (C) 2005 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Primary reference: * http://en.wikipedia.org/wiki/ISO_8601 (as at 2005-08-01) * * Secondary references: * http://hydracen.com/dx/iso8601.htm * http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt * http://www.personal.ecu.edu/mccartyr/isowdcal.html * http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm * */ #include #include #include #include #include -gboolean gregorian_to_ordinal(ha_time_t * a_date); -gboolean ordinal_to_gregorian(ha_time_t * a_date); -gboolean ordinal_to_weekdays(ha_time_t * a_date); -void normalize_time(ha_time_t * a_time); + +struct ha_time_s { + int years; + int months; /* Only for durations */ + int days; + int seconds; + int offset; /* Seconds */ +}; + +char *date_to_string(ha_time_t * date_time, int flags); + +static int year_days(int year) +{ + int d = 365; + if (is_leap_year(year)) { + d++; + } + return d; +} /* * Andrew's code was originally written for OSes whose "struct tm" contains: * long tm_gmtoff; :: Seconds east of UTC * const char *tm_zone; :: Timezone abbreviation * Some OSes lack these, instead having: * time_t (or long) timezone; :: "difference between UTC and local standard time" * char *tzname[2] = { "...", "..." }; * I (David Lee) confess to not understanding the details. So my attempted * generalisations for where their use is necessary may be flawed. * * 1. Does "difference between ..." subtract the same or opposite way? * 2. Should it use "altzone" instead of "timezone"? * 3. Should it use tzname[0] or tzname[1]? Interaction with timezone/altzone? */ #if defined(HAVE_STRUCT_TM_TM_GMTOFF) # define GMTOFF(tm) ((tm)->tm_gmtoff) #else /* Note: extern variable; macro argument not actually used. */ # define GMTOFF(tm) (timezone) #endif +static ha_time_t * +crm_get_utc_time(ha_time_t *dt) +{ + ha_time_t *utc = new_ha_date(FALSE); + ha_set_time(utc, dt, FALSE); + sub_seconds(utc, dt->offset); + crm_trace("utc time"); + return utc; +} + +void +log_date(int log_level, const char *prefix, ha_time_t * date_time, int flags) +{ + char *date_s = date_to_string(date_time, flags); + + if(log_level < LOG_CRIT) { + printf("%s%s%s\n", + prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__"); + } else { + do_crm_log(log_level, "%s%s%s", + prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__"); + } + free(date_s); +} + +void +log_time_period(int log_level, ha_time_period_t * dtp, int flags) +{ + log_date(log_level, "Period start:", dtp->start, flags); + log_date(log_level, "Period end:", dtp->end, flags); +} + +static int crm_get_time_sec(int sec, uint *h, uint *m, uint *s) +{ + uint hours, minutes, seconds; + if(sec < 0) { + seconds = 0 - sec; + } else { + seconds = sec; + } + + hours = seconds/(60*60); + seconds -= 60 * 60 * hours; + + minutes = seconds/(60*60); + seconds -= 60 * minutes; + + crm_trace("%d == %.2d:%.2d:%.2d", sec, hours, minutes, seconds); + + *h = hours; + *m = minutes; + *s = seconds; + + return TRUE; +} + +int crm_get_time(ha_time_t *now, uint *h, uint *m, uint *s) +{ + return crm_get_time_sec(now->seconds, h, m, s); +} + + +int crm_get_gregorian_date(ha_time_t *now, uint *y, uint *m, uint *d) +{ + int months = 1; + int days = now->days; + for(; months <= 12 && days > 0; months++) { + int mdays = days_per_month(months, now->years); + if(mdays >= days) { + break; + } else { + days -= mdays; + } + } + + *y = now->years; + *m = months; + *d = days; + crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", now->years, now->days, now->years, months, days); + return TRUE; +} + +int crm_get_ordinal_date(ha_time_t *now, uint *y, uint *d) +{ + *y = now->years; + *d = now->days; + return TRUE; +} + +int crm_get_week_date(ha_time_t *dt, uint *y, uint *w, uint *d) +{ + /* + * Monday 29 December 2008 is written "2009-W01-1" + * Sunday 3 January 2010 is written "2009-W53-7" + */ + int year_num = 0; + int jan1 = january1(dt->years); + int h = -1; + + CRM_CHECK(dt->days > 0, return FALSE); + +/* 6. Find the Weekday for Y M D */ + h = dt->days + jan1 - 1; + *d = 1 + ((h - 1) % 7); + +/* 7. Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */ + if (dt->days <= (8 - jan1) && jan1 > 4) { + crm_trace("year--, jan1=%d", jan1); + year_num = dt->years - 1; + *w = weeks_in_year(year_num); + + } else { + year_num = dt->years; + } + +/* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */ + if (year_num == dt->years) { + int dmax = year_days(year_num); + int correction = 4 - *d; + if ((dmax - dt->days) < correction) { + crm_trace("year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction); + year_num = dt->years + 1; + *w = 1; + } + } + +/* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */ + if (year_num == dt->years) { + int j = dt->days + (7 - *d) + (jan1 - 1); + + *w = j / 7; + if (jan1 > 4) { + *w -= 1; + } + } + + *y = year_num; + crm_trace("Converted %.4d-%.3d to %.4d-W%.2d-%d", + dt->years, dt->days, *y, *w, *d); + return TRUE; +} + char * date_to_string(ha_time_t * date_time, int flags) { char *date_s = NULL; char *time_s = NULL; char *offset_s = NULL; char *result_s = NULL; ha_time_t *dt = NULL; if (flags & ha_log_local) { crm_trace("Local version"); - dt = date_time; + dt = calloc(1, sizeof(ha_time_t)); + ha_set_time(dt, date_time, FALSE); + } else if(date_time->offset) { + crm_trace("UTC conversion"); + dt = crm_get_utc_time(date_time); } else { - dt = date_time->normalized; + crm_trace("Already UTC"); + dt = calloc(1, sizeof(ha_time_t)); + ha_set_time(dt, date_time, FALSE); } CRM_CHECK(dt != NULL, return NULL); if (flags & ha_log_date) { date_s = calloc(1, 32); if (date_s == NULL) { return NULL; + } else if (flags & ha_date_seconds) { + unsigned long long s = date_in_seconds(date_time); + snprintf(date_s, 31, "%llu", s); + goto done; + + } else if (flags & ha_date_epoch) { + unsigned long long s = date_in_seconds_since_epoch(date_time); + snprintf(date_s, 31, "%llu", s); + goto done; + } else if (flags & ha_date_weeks) { - snprintf(date_s, 31, "%d-W%.2d-%d", dt->weekyears, dt->weeks, dt->weekdays); + /* YYYY-Www-D */ + uint y, w, d; + if(crm_get_week_date(dt, &y, &w, &d)) { + snprintf(date_s, 31, "%d-W%.2d-%d", y, w, d); + } } else if (flags & ha_date_ordinal) { - snprintf(date_s, 31, "%d-%.3d", dt->years, dt->yeardays); + /* YYYY-DDD */ + uint y, d; + if(crm_get_ordinal_date(dt, &y, &d)) { + snprintf(date_s, 31, "%d-%.3d", y, d); + } } else { - snprintf(date_s, 31, "%.4d-%.2d-%.2d", dt->years, dt->months, dt->days); + /* YYYY-MM-DD */ + uint y, m, d; + if(crm_get_gregorian_date(dt, &y, &m, &d)) { + snprintf(date_s, 31, "%.4d-%.2d-%.2d", y, m, d); + } } } + if (flags & ha_log_time) { - int offset = 0; + uint h, m, s; time_s = calloc(1, 32); if (time_s == NULL) { goto cleanup; } - snprintf(time_s, 31, "%.2d:%.2d:%.2d", dt->hours, dt->minutes, dt->seconds); + if(crm_get_time(dt, &h, &m, &s)) { + snprintf(time_s, 31, "%.2d:%.2d:%.2d", h, m, s); + } - if (dt->offset != NULL) { - offset = (dt->offset->hours * 100) + dt->offset->minutes; + if (dt->offset != 0) { + crm_get_time_sec(dt->offset, &h, &m, &s); } offset_s = calloc(1, 32); - if ((flags & ha_log_local) == 0 || offset == 0) { + if ((flags & ha_log_local) == 0 || dt->offset == 0) { + crm_trace("flags %6x %6x", flags, ha_log_local); snprintf(offset_s, 31, "Z"); } else { - int hr = dt->offset->hours; - int mins = dt->offset->minutes; - - if (hr < 0) { - hr = 0 - hr; - } - if (mins < 0) { - mins = 0 - mins; - } - snprintf(offset_s, 31, " %s%.2d:%.2d", offset > 0 ? "+" : "-", hr, mins); + snprintf(offset_s, 31, " %c%.2d:%.2d", dt->offset < 0 ? '-' : '+', h, m); } } + done: result_s = calloc(1, 100); snprintf(result_s, 100, "%s%s%s%s", date_s ? date_s : "", (date_s != NULL && time_s != NULL) ? " " : "", time_s ? time_s : "", offset_s ? offset_s : ""); cleanup: free(date_s); free(time_s); free(offset_s); + free_ha_date(dt); return result_s; } -void -log_date(int log_level, const char *prefix, ha_time_t * date_time, int flags) +static int +parse_time_sec(const char *time_str) { - char *date_s = date_to_string(date_time, flags); + int rc; + uint hour = 0; + uint minute = 0; + uint second = 0; + rc = sscanf(time_str, "%d:%d:%d", &hour, &minute, &second); + if(rc == 1) { + rc = sscanf(time_str, "%2d%2d%2d", &hour, &minute, &second); + } - if(log_level < LOG_CRIT) { - printf("%s%s%s\n", - prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__"); + if(rc > 0 && rc < 4) { + crm_trace("Got valid time: %.2d:%.2d:%.2d", hour, minute, second); + if(hour >= 24) { + crm_err("Invalid hour: %d", hour); + } else if(minute >= 60) { + crm_err("Invalid minute: %d", minute); + } else if(second >= 60) { + crm_err("Invalid second: %d", second); + } else { + second += (minute * 60); + second += (hour * 60 * 60); + } } else { - do_crm_log(log_level, "%s%s%s", - prefix ? prefix : "", prefix ? ": " : "", date_s ? date_s : "__invalid_date__"); + crm_err("Bad time: %s (%d)", time_str, rc); } - free(date_s); + return second; } -void -log_time_period(int log_level, ha_time_period_t * dtp, int flags) +static int +parse_time_offset(char *offset_str) { - log_date(log_level, "Period start:", dtp->start, flags); - log_date(log_level, "Period end:", dtp->end, flags); -} - -ha_time_t * -parse_time_offset(char **offset_str) -{ - ha_time_t *new_time = NULL; - - new_time = calloc(1, sizeof(ha_time_t)); - new_time->has = calloc(1, sizeof(ha_has_time_t)); + int offset = 0; - if ((*offset_str)[0] == 'Z') { - - } else if ((*offset_str)[0] == '+' || (*offset_str)[0] == '-' || isdigit((int)(*offset_str)[0])) { - gboolean negate = FALSE; - - if ((*offset_str)[0] == '-') { - negate = TRUE; - (*offset_str)++; - } - parse_time(offset_str, new_time, FALSE); - if (negate) { - new_time->hours = 0 - new_time->hours; - new_time->minutes = 0 - new_time->minutes; - new_time->seconds = 0 - new_time->seconds; - } - - } else { + tzset(); + if(offset_str == NULL) { #if defined(HAVE_STRUCT_TM_TM_GMTOFF) time_t now = time(NULL); struct tm *now_tm = localtime(&now); #endif int h_offset = GMTOFF(now_tm) / (3600); int m_offset = (GMTOFF(now_tm) - (3600 * h_offset)) / (60); if (h_offset < 0 && m_offset < 0) { m_offset = 0 - m_offset; } - new_time->hours = h_offset; - new_time->minutes = m_offset; - new_time->has->hours = TRUE; - new_time->has->minutes = TRUE; + offset += (60 * 60 * h_offset); + offset += (60 * m_offset); + + } else if (offset_str[0] == 'Z') { + + } else if (offset_str[0] == '+' || offset_str[0] == '-' || isdigit((int)offset_str[0])) { + gboolean negate = FALSE; + + if (offset_str[0] == '-') { + negate = TRUE; + offset_str++; + } + offset = parse_time_sec(offset_str); + if (negate) { + offset = 0 - offset; + } } - return new_time; + return offset; } -ha_time_t * +static ha_time_t * parse_time(char **time_str, ha_time_t * a_time, gboolean with_offset) { + uint h, m, s; + char *offset_s = NULL; ha_time_t *new_time = a_time; tzset(); if (a_time == NULL) { new_time = new_ha_date(FALSE); } - + CRM_CHECK(new_time != NULL, return NULL); - CRM_CHECK(new_time->has != NULL, free_ha_date(new_time); return NULL); + new_time->seconds = parse_time_sec(*time_str); - /* reset the time fields */ - new_time->hours = 0; - new_time->minutes = 0; - new_time->seconds = 0; - - crm_trace("Get hours..."); - new_time->has->hours = FALSE; - if (parse_int(time_str, 2, 24, &new_time->hours)) { - new_time->has->hours = TRUE; - } - - crm_trace("Get minutes..."); - new_time->has->minutes = FALSE; - if (parse_int(time_str, 2, 60, &new_time->minutes)) { - new_time->has->minutes = TRUE; + offset_s = strstr(*time_str, "Z"); + if(offset_s == NULL) { + offset_s = strstr(*time_str, " "); } - - crm_trace("Get seconds..."); - new_time->has->seconds = FALSE; - if (parse_int(time_str, 2, 60, &new_time->seconds)) { - new_time->has->seconds = TRUE; - } - - if (with_offset) { - crm_trace("Get offset..."); - while (isspace((int)(*time_str)[0])) { - (*time_str)++; + if(offset_s) { + while (isspace(offset_s[0])) { + offset_s++; } - - new_time->offset = parse_time_offset(time_str); - normalize_time(new_time); } + new_time->offset = parse_time_offset(offset_s); + crm_get_time_sec(new_time->offset, &h, &m, &s); + crm_trace("Got tz: %c%2.d:%.2d", new_time->offset<0?'-':'+', h, m); return new_time; } -void -normalize_time(ha_time_t * a_time) -{ - CRM_CHECK(a_time != NULL, return); - CRM_CHECK(a_time->has != NULL, return); - - if (a_time->normalized == NULL) { - a_time->normalized = calloc(1, sizeof(ha_time_t)); - } - if (a_time->normalized->has == NULL) { - a_time->normalized->has = calloc(1, sizeof(ha_has_time_t)); - } - - ha_set_time(a_time->normalized, a_time, FALSE); - if (a_time->offset != NULL) { - if (a_time->offset->has->hours) { - sub_hours(a_time->normalized, a_time->offset->hours); - } - if (a_time->offset->has->minutes) { - sub_minutes(a_time->normalized, a_time->offset->minutes); - } - if (a_time->offset->has->seconds) { - sub_seconds(a_time->normalized, a_time->offset->seconds); - } - } - CRM_CHECK(is_date_sane(a_time), return); -} - ha_time_t * parse_date(char **date_str) { - gboolean is_done = FALSE; - gboolean converted = FALSE; + char *time_s; ha_time_t *new_time = NULL; + int year = 0; + int month = 0; + int week = 0; + int day = 0; + int rc = 0; + CRM_CHECK(date_str != NULL, return NULL); CRM_CHECK(strlen(*date_str) > 0, return NULL); if ((*date_str)[0] == 'T' || (*date_str)[2] == ':') { /* Just a time supplied - Infer current date */ new_time = new_ha_date(TRUE); parse_time(date_str, new_time, TRUE); - normalize_time(new_time); - is_done = TRUE; + goto done; } else { new_time = calloc(1, sizeof(ha_time_t)); - new_time->has = calloc(1, sizeof(ha_has_time_t)); } - while (is_done == FALSE) { - char ch = (*date_str)[0]; - - crm_trace("Switching on ch=%c (len=%d)", ch, (int)strlen(*date_str)); - - if (ch == 0) { - /* all done */ - is_done = TRUE; - break; - - } else if (ch == '/') { - /* all done - interval marker */ - is_done = TRUE; - break; - - } else if (ch == 'W') { - CRM_CHECK(new_time->has->weeks == FALSE,;); - (*date_str)++; - if (parse_int(date_str, 2, 53, &new_time->weeks)) { - new_time->has->weeks = TRUE; - new_time->weekyears = new_time->years; - new_time->has->weekyears = new_time->has->years; - } - if ((*date_str)[0] == '-') { - (*date_str)++; - if (parse_int(date_str, 1, 7, &new_time->weekdays)) { - new_time->has->weekdays = TRUE; - } - } - - if (new_time->weekdays == 0 || new_time->has->weekdays == FALSE) { - new_time->weekdays = 1; - new_time->has->weekdays = TRUE; - } - - } else if (ch == '-') { - (*date_str)++; - if (check_for_ordinal(*date_str)) { - if (parse_int(date_str, 3, 366, &new_time->yeardays)) { - new_time->has->yeardays = TRUE; - } - } - - } else if (ch == 'O') { - /* ordinal date */ - (*date_str)++; - if (parse_int(date_str, 3, 366, &new_time->yeardays)) { - new_time->has->yeardays = TRUE; - } - - } else if (ch == 'T' || ch == ' ') { - if (new_time->has->yeardays) { - converted = convert_from_ordinal(new_time); - - } else if (new_time->has->weekdays) { - converted = convert_from_weekdays(new_time); + if(safe_str_eq("epoch", *date_str)) { + new_time->days = 1; + new_time->years = 1970; + log_date(LOG_TRACE, "Unpacked", new_time, ha_log_date | ha_log_time); + return new_time; + } - } else { - converted = convert_from_gregorian(new_time); - } - (*date_str)++; - parse_time(date_str, new_time, TRUE); - is_done = TRUE; - - } else if (isdigit((int)ch)) { - if (new_time->has->years == FALSE && parse_int(date_str, 4, 9999, &new_time->years)) { - new_time->has->years = TRUE; - - } else if (check_for_ordinal(*date_str) && parse_int(date_str, 3, - is_leap_year(new_time->years) ? 366 - : 365, &new_time->yeardays)) { - new_time->has->yeardays = TRUE; - - } else if (new_time->has->months == FALSE - && parse_int(date_str, 2, 12, &new_time->months)) { - new_time->has->months = TRUE; - - } else if (new_time->has->days == FALSE) { - if (parse_int(date_str, 2, - days_per_month(new_time->months, new_time->years), &new_time->days)) { - new_time->has->days = TRUE; - } + /* YYYY-MM-DD */ + rc = sscanf(*date_str, "%d-%d-%d", &year, &month, &day); + if(rc == 1) { + /* YYYYMMDD */ + rc = sscanf(*date_str, "%4d%2d%2d", &year, &month, &day); + } + if(rc == 3) { + if(month > 12) { + crm_err("Invalid month: %d", month); + } else if(day > 31) { + crm_err("Invalid day: %d", day); + } else { + int m; + new_time->days = day; + new_time->years = year; + for(m = 1; m < month; m++) { + new_time->days += days_per_month(year, m); } + crm_trace("Got gergorian date: %.4d-%.3d", year, new_time->days); + } + goto done; + } + /* YYYY-DDD */ + rc = sscanf(*date_str, "%d-%d", &year, &day); + if(rc == 2) { + crm_trace("Got ordinal date"); + if(day > 366) { + crm_err("Invalid day: %d", day); } else { - crm_err("Unexpected characters at: %s", *date_str); - is_done = TRUE; - break; + new_time->days = day; + new_time->years = year; } + goto done; } - if (converted) { - - } else if (new_time->has->yeardays) { - convert_from_ordinal(new_time); + /* YYYY-Www-D */ + rc = sscanf(*date_str, "%d-W%d-%d", &year, &week, &day); + if(rc == 3) { + crm_trace("Got week date"); + if(week > 53) { + crm_err("Invalid week: %d", week); + } else if(day > 7) { + crm_err("Invalid day: %d", day); + } else { + /* + * http://en.wikipedia.org/wiki/ISO_week_date + * This method requires that one know the weekday of 4 January of the year in question. + * Add 3 to the number of this weekday, giving a correction to be used for dates within this year. + * + * Method: Multiply the week number by 7, then add the weekday. + * From this sum subtract the correction for the year. + * + * Example: year 2008, week 39, Saturday (day 6) + * Correction for 2008: 5 + 3 = 8 + * (39 * 7) + 6 = 279 + * 279 - 8 = 271 + * + * http://personal.ecu.edu/mccartyr/ISOwdALG.txt + * + * Monday 29 December 2008 is written "2009-W01-1" + * Sunday 3 January 2010 is written "2009-W53-7" + * + * Saturday 27 September 2008 is written "2008-W37-6" + * + * http://en.wikipedia.org/wiki/ISO_week_date + * If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01. + * If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year. + */ + int jan1 = january1(year); + crm_trace("Jan 1 = %d", jan1); + + new_time->years = year; + add_days(new_time, (week - 1) * 7); + + if(jan1 <= 4) { + sub_days(new_time, jan1 - 1); + } else { + add_days(new_time, 8 - jan1); + } - } else if (new_time->has->weekdays) { - convert_from_weekdays(new_time); + add_days(new_time, day); - } else { - convert_from_gregorian(new_time); + /* Handle any underflow */ + sub_days(new_time, 0); + } + goto done; } - normalize_time(new_time); + crm_err("Couldn't parse %s", *date_str); + done: + + time_s = strstr(*date_str, " "); + if(time_s == NULL) { + time_s = strstr(*date_str, "T"); + } + + if(time_s) { + time_s++; + parse_time(&time_s, new_time, TRUE); + } - log_date(LOG_DEBUG_3, "Unpacked", new_time, ha_log_date | ha_log_time); + log_date(LOG_TRACE, "Unpacked", new_time, ha_log_date | ha_log_time); CRM_CHECK(is_date_sane(new_time), return NULL); return new_time; } ha_time_t * parse_time_duration(char **interval_str) { gboolean is_time = FALSE; ha_time_t *diff = NULL; CRM_CHECK(interval_str != NULL, goto bail); CRM_CHECK(strlen(*interval_str) > 0, goto bail); CRM_CHECK((*interval_str)[0] == 'P', goto bail); (*interval_str)++; diff = calloc(1, sizeof(ha_time_t)); - diff->has = calloc(1, sizeof(ha_has_time_t)); while (isspace((int)(*interval_str)[0]) == FALSE) { int an_int = 0; char ch = 0; if ((*interval_str)[0] == 'T') { is_time = TRUE; (*interval_str)++; } if (parse_int(interval_str, 10, 0, &an_int) == FALSE) { break; } ch = (*interval_str)[0]; (*interval_str)++; crm_trace("%c=%d", ch, an_int); switch (ch) { case 0: return diff; break; case 'Y': diff->years = an_int; - diff->has->years = TRUE; break; case 'M': if (is_time) { - diff->minutes = an_int; - diff->has->minutes = TRUE; + /* Minutes */ + diff->seconds += an_int * 60; } else { diff->months = an_int; - diff->has->months = TRUE; } break; case 'W': - diff->weeks = an_int; - diff->has->weeks = TRUE; + diff->days += an_int * 7; break; case 'D': - diff->days = an_int; - diff->has->days = TRUE; - diff->yeardays = an_int; - diff->has->yeardays = TRUE; + diff->days += an_int; break; case 'H': - diff->hours = an_int; - diff->has->hours = TRUE; + diff->seconds += an_int * 60 * 60; break; case 'S': - diff->seconds = an_int; - diff->has->seconds = TRUE; + diff->seconds += an_int; break; default: goto bail; break; } } return diff; bail: - free(diff->has); free(diff); return NULL; } ha_time_period_t * parse_time_period(char **period_str) { gboolean invalid = FALSE; const char *original = *period_str; ha_time_period_t *period = NULL; CRM_CHECK(period_str != NULL, return NULL); CRM_CHECK(strlen(*period_str) > 0, return NULL); tzset(); period = calloc(1, sizeof(ha_time_period_t)); if ((*period_str)[0] == 'P') { period->diff = parse_time_duration(period_str); } else { period->start = parse_date(period_str); } if ((*period_str)[0] != 0) { CRM_CHECK((*period_str)[0] == '/', invalid = TRUE; goto bail); (*period_str)++; if ((*period_str)[0] == 'P') { period->diff = parse_time_duration(period_str); } else { period->end = parse_date(period_str); } } else if (period->diff != NULL) { /* just aduration starting from now */ time_t now = time(NULL); period->start = calloc(1, sizeof(ha_time_t)); - period->start->has = calloc(1, sizeof(ha_has_time_t)); - period->start->offset = calloc(1, sizeof(ha_time_t)); - period->start->offset->has = calloc(1, sizeof(ha_has_time_t)); ha_set_timet_time(period->start, &now); - normalize_time(period->start); + /* normalize_time(period->start); */ } else { invalid = TRUE; CRM_CHECK((*period_str)[0] == '/', goto bail); goto bail; } /* sanity checks */ if (period->start == NULL && period->end == NULL) { crm_err("Invalid time period: %s", original); invalid = TRUE; } else if (period->start == NULL && period->diff == NULL) { crm_err("Invalid time period: %s", original); invalid = TRUE; } else if (period->end == NULL && period->diff == NULL) { crm_err("Invalid time period: %s", original); invalid = TRUE; } bail: if (invalid) { free(period->start); free(period->end); free(period->diff); free(period); return NULL; } if (period->end == NULL && period->diff == NULL) { } if (period->start == NULL) { period->start = subtract_duration(period->end, period->diff); - normalize_time(period->start); } else if (period->end == NULL) { period->end = add_time(period->start, period->diff); - normalize_time(period->end); } is_date_sane(period->start); is_date_sane(period->end); return period; } -int month2days[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; - -/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt */ +/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt + * + * 5. Find the Jan1Weekday for Y (Monday=1, Sunday=7) + * YY = (Y-1) % 100 + * C = (Y-1) - YY + * G = YY + YY/4 + * Jan1Weekday = 1 + (((((C / 100) % 4) x 5) + G) % 7) + */ int january1(int year) { int YY = (year - 1) % 100; int C = (year - 1) - YY; int G = YY + YY / 4; int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7); crm_trace("YY=%d, C=%d, G=%d", YY, C, G); crm_trace("January 1 %.4d: %d", year, jan1); return jan1; } int weeks_in_year(int year) { int weeks = 52; int jan1 = january1(year); /* if jan1 == thursday */ if (jan1 == 4) { weeks++; } else { jan1 = january1(year + 1); /* if dec31 == thursday aka. jan1 of next year is a friday */ if (jan1 == 5) { weeks++; } } return weeks; } -gboolean -convert_from_gregorian(ha_time_t * a_date) -{ - CRM_CHECK(gregorian_to_ordinal(a_date), return FALSE); - CRM_CHECK(ordinal_to_weekdays(a_date), return FALSE); - return TRUE; -} - -gboolean -gregorian_to_ordinal(ha_time_t * a_date) -{ - CRM_CHECK(a_date->has->years, return FALSE); - CRM_CHECK(a_date->has->months, return FALSE); - CRM_CHECK(a_date->has->days, return FALSE); - - CRM_CHECK(a_date->months > 0, return FALSE); - CRM_CHECK(a_date->days > 0, return FALSE); - - a_date->yeardays = month2days[a_date->months - 1]; - a_date->yeardays += a_date->days; - a_date->has->yeardays = TRUE; - - if (is_leap_year(a_date->years) && a_date->months > 2) { - (a_date->yeardays)++; - } - crm_trace("Converted %.4d-%.2d-%.2d to %.4d-%.3d", - a_date->years, a_date->months, a_date->days, a_date->years, a_date->yeardays); - - return TRUE; -} - -gboolean -convert_from_ordinal(ha_time_t * a_date) -{ - CRM_CHECK(ordinal_to_gregorian(a_date), return FALSE); - CRM_CHECK(ordinal_to_weekdays(a_date), return FALSE); - return TRUE; -} - -gboolean -ordinal_to_gregorian(ha_time_t * a_date) -{ - /* Day of the year this month ends on */ - int m_end = 0; - - CRM_CHECK(a_date->has->years, return FALSE); - CRM_CHECK(a_date->has->yeardays, return FALSE); - - CRM_CHECK(a_date->yeardays > 0, return FALSE); - - if (is_leap_year(a_date->years) && a_date->yeardays > 366) { - crm_err("Year %.4d only has 366 days (supplied %.3d)", a_date->years, a_date->yeardays); - a_date->yeardays = 366; - - } else if (!is_leap_year(a_date->years) && a_date->yeardays > 365) { - crm_err("Year %.4d only has 365 days (supplied %.3d)", a_date->years, a_date->yeardays); - a_date->yeardays = 365; - } - - a_date->days = a_date->yeardays; - a_date->months = 0; - do { - a_date->months++; - m_end += days_per_month(a_date->months, a_date->years); - a_date->days -= days_per_month(a_date->months - 1, a_date->years); - - crm_trace("month %d: %d vs. %d - current day: %d", - a_date->months, a_date->yeardays, m_end, a_date->days); - } while (a_date->months < 12 && m_end < a_date->yeardays); - - CRM_CHECK(a_date->months > 0, return FALSE); - CRM_CHECK(a_date->days <= days_per_month(a_date->months, a_date->years), return FALSE); - - a_date->has->days = TRUE; - a_date->has->months = TRUE; - a_date->has->years = TRUE; - - crm_trace("Converted %.4d-%.3d to %.4d-%.2d-%.2d", - a_date->years, a_date->yeardays, a_date->years, a_date->months, a_date->days); - - return TRUE; -} - -gboolean -ordinal_to_weekdays(ha_time_t * a_date) -{ - int year_num = 0; - int jan1 = january1(a_date->years); - int h = -1; - - CRM_CHECK(a_date->has->years, return FALSE); - CRM_CHECK(a_date->has->yeardays, return FALSE); - CRM_CHECK(a_date->yeardays > 0, return FALSE); - - h = a_date->yeardays + jan1 - 1; - a_date->weekdays = 1 + ((h - 1) % 7); - a_date->has->weekdays = TRUE; - - if (a_date->yeardays <= (8 - jan1) && jan1 > 4) { - year_num = a_date->years - 1; - a_date->weeks = weeks_in_year(year_num); - a_date->has->weeks = TRUE; - - } else { - year_num = a_date->years; - } - - if (year_num == a_date->years) { - int i = 365; - - if (is_leap_year(year_num)) { - i = 366; - } - if ((i - a_date->yeardays) < (4 - a_date->weekdays)) { - year_num = a_date->years + 1; - a_date->weeks = 1; - a_date->has->weeks = TRUE; - } - } - - if (year_num == a_date->years) { - int j = a_date->yeardays + (7 - a_date->weekdays) + (jan1 - 1); - - a_date->weeks = j / 7; - a_date->has->weeks = TRUE; - if (jan1 > 4) { - a_date->weeks -= 1; - } - } - - a_date->weekyears = year_num; - a_date->has->weekyears = TRUE; - crm_trace("Converted %.4d-%.3d to %.4dW%.2d-%d", - a_date->years, a_date->yeardays, a_date->weekyears, a_date->weeks, a_date->weekdays); - return TRUE; -} - -gboolean -convert_from_weekdays(ha_time_t * a_date) -{ - gboolean conversion = FALSE; - int jan1 = january1(a_date->weekyears); - - CRM_CHECK(a_date->has->weekyears, return FALSE); - CRM_CHECK(a_date->has->weeks, return FALSE); - CRM_CHECK(a_date->has->weekdays, return FALSE); - - CRM_CHECK(a_date->weeks > 0, return FALSE); - CRM_CHECK(a_date->weekdays > 0, return FALSE); - CRM_CHECK(a_date->weekdays < 8, return FALSE); - - a_date->has->years = TRUE; - a_date->years = a_date->weekyears; - - a_date->has->yeardays = TRUE; - a_date->yeardays = (7 * (a_date->weeks - 1)); - - /* break up the addition to make sure overflows are correctly handled */ - if (a_date->yeardays == 0) { - a_date->yeardays = a_date->weekdays; - } else { - add_yeardays(a_date, a_date->weekdays); - } - - crm_trace("Pre-conversion: %dW%d-%d to %.4d-%.3d", - a_date->weekyears, a_date->weeks, a_date->weekdays, a_date->years, a_date->yeardays); - - conversion = ordinal_to_gregorian(a_date); - - if (conversion) { - if (jan1 < 4) { - sub_days(a_date, jan1 - 1); - } else if (jan1 > 4) { - add_days(a_date, jan1 - 4); - } - } - return conversion; -} - void ha_set_time(ha_time_t * lhs, ha_time_t * rhs, gboolean offset) { crm_trace("lhs=%p, rhs=%p, offset=%d", lhs, rhs, offset); CRM_CHECK(lhs != NULL && rhs != NULL, return); - CRM_CHECK(lhs->has != NULL && rhs->has != NULL, return); lhs->years = rhs->years; - lhs->has->years = rhs->has->years; - - lhs->weekyears = rhs->weekyears; - lhs->has->weekyears = rhs->has->weekyears; - - lhs->months = rhs->months; - lhs->has->months = rhs->has->months; - - lhs->weeks = rhs->weeks; - lhs->has->weeks = rhs->has->weeks; - lhs->days = rhs->days; - lhs->has->days = rhs->has->days; - - lhs->weekdays = rhs->weekdays; - lhs->has->weekdays = rhs->has->weekdays; - - lhs->yeardays = rhs->yeardays; - lhs->has->yeardays = rhs->has->yeardays; - - lhs->hours = rhs->hours; - lhs->has->hours = rhs->has->hours; - - lhs->minutes = rhs->minutes; - lhs->has->minutes = rhs->has->minutes; - lhs->seconds = rhs->seconds; - lhs->has->seconds = rhs->has->seconds; - - if (lhs->offset) { - reset_time(lhs->offset); - } - if (offset && rhs->offset) { - ha_set_time(lhs->offset, rhs->offset, FALSE); - } - + lhs->offset = rhs->offset; } void ha_set_tm_time(ha_time_t * lhs, struct tm *rhs) { - int wday = rhs->tm_wday; int h_offset = 0; int m_offset = 0; if (rhs->tm_year > 0) { /* years since 1900 */ lhs->years = 1900 + rhs->tm_year; - lhs->has->years = TRUE; } if (rhs->tm_yday >= 0) { /* days since January 1 [0-365] */ - lhs->yeardays = 1 + rhs->tm_yday; - lhs->has->yeardays = TRUE; + lhs->days = 1 + rhs->tm_yday; } if (rhs->tm_hour >= 0) { - lhs->hours = rhs->tm_hour; - lhs->has->hours = TRUE; + lhs->seconds += 60 * 60 * rhs->tm_hour; } if (rhs->tm_min >= 0) { - lhs->minutes = rhs->tm_min; - lhs->has->minutes = TRUE; + lhs->seconds += 60 * rhs->tm_min; } if (rhs->tm_sec >= 0) { - lhs->seconds = rhs->tm_sec; - lhs->has->seconds = TRUE; + lhs->seconds += rhs->tm_sec; } - convert_from_ordinal(lhs); - - /* months since January [0-11] */ - CRM_CHECK(rhs->tm_mon < 0 || lhs->months == (1 + rhs->tm_mon), return); - - /* day of the month [1-31] */ - CRM_CHECK(rhs->tm_mday < 0 || lhs->days == rhs->tm_mday, return); - - /* days since Sunday [0-6] */ - if (wday == 0) { - wday = 7; - } - - CRM_CHECK(rhs->tm_wday < 0 || lhs->weekdays == wday, return); - CRM_CHECK(lhs->offset != NULL, return); - CRM_CHECK(lhs->offset->has != NULL, return); - /* tm_gmtoff == offset from UTC in seconds */ h_offset = GMTOFF(rhs) / (3600); m_offset = (GMTOFF(rhs) - (3600 * h_offset)) / (60); crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(rhs), h_offset, m_offset); - lhs->offset->hours = h_offset; - lhs->offset->has->hours = TRUE; - - lhs->offset->minutes = m_offset; - lhs->offset->has->minutes = TRUE; - - normalize_time(lhs); + lhs->offset = 0; + lhs->offset += 60 * 60 * h_offset; + lhs->offset += 60 * m_offset; } void ha_set_timet_time(ha_time_t * lhs, time_t * rhs) { ha_set_tm_time(lhs, localtime(rhs)); } ha_time_t * -add_time(ha_time_t * lhs, ha_time_t * rhs) +add_time(ha_time_t * dt, ha_time_t * rhs) { + ha_time_t *utc = NULL; ha_time_t *answer = NULL; - CRM_CHECK(lhs != NULL && rhs != NULL, return NULL); + CRM_CHECK(dt != NULL && rhs != NULL, return NULL); answer = new_ha_date(FALSE); - ha_set_time(answer, lhs, TRUE); + ha_set_time(answer, dt, TRUE); - normalize_time(lhs); - normalize_time(answer); + utc = crm_get_utc_time(rhs); - if (rhs->has->years) { - add_years(answer, rhs->years); - } - if (rhs->has->months) { - add_months(answer, rhs->months); - } - if (rhs->has->weeks) { - add_weeks(answer, rhs->weeks); - } - if (rhs->has->days) { - add_days(answer, rhs->days); - } - - add_hours(answer, rhs->hours); - add_minutes(answer, rhs->minutes); - add_seconds(answer, rhs->seconds); - - normalize_time(answer); + add_years(answer, utc->years); + add_months(answer, utc->months); + add_days(answer, utc->days); + add_seconds(answer, utc->seconds); return answer; } ha_time_t * -subtract_time(ha_time_t * lhs, ha_time_t * rhs) +subtract_time(ha_time_t * dt, ha_time_t * rhs) { + ha_time_t *utc = NULL; ha_time_t *answer = NULL; - CRM_CHECK(lhs != NULL && rhs != NULL, return NULL); + CRM_CHECK(dt != NULL && rhs != NULL, return NULL); answer = new_ha_date(FALSE); - ha_set_time(answer, lhs, TRUE); - - normalize_time(lhs); - normalize_time(rhs); - normalize_time(answer); - - sub_seconds(answer, rhs->seconds); - sub_minutes(answer, rhs->minutes); - sub_hours(answer, rhs->hours); - - answer->yeardays -= rhs->yeardays; - while (answer->yeardays < 0) { - answer->yeardays += is_leap_year(answer->years) ? 356 : 355; - answer->years--; - } - - answer->days -= rhs->days; - while (answer->days < 0) { - answer->days += days_per_month(answer->months, answer->years); - answer->months--; - } + ha_set_time(answer, dt, TRUE); - answer->months -= rhs->months; - while (answer->months < 0) { - answer->months += 12; - /* answer->years--; : done in the yeardays section */ - } + utc = crm_get_utc_time(rhs); - answer->years -= rhs->years; + sub_years(answer, utc->years); + sub_months(answer, utc->months); + sub_days(answer, utc->days); + sub_seconds(answer, utc->seconds); return answer; } ha_time_t * -subtract_duration(ha_time_t * lhs, ha_time_t * rhs) +subtract_duration(ha_time_t * dt, ha_time_t * rhs) { + ha_time_t *utc = NULL; ha_time_t *answer = NULL; - CRM_CHECK(lhs != NULL && rhs != NULL, return NULL); + CRM_CHECK(dt != NULL && rhs != NULL, return NULL); answer = new_ha_date(FALSE); - ha_set_time(answer, lhs, TRUE); - - normalize_time(lhs); - normalize_time(rhs); - normalize_time(answer); - - sub_seconds(answer, rhs->seconds); - sub_minutes(answer, rhs->minutes); - sub_hours(answer, rhs->hours); + ha_set_time(answer, dt, TRUE); - sub_days(answer, rhs->days); - sub_weeks(answer, rhs->weeks); - sub_months(answer, rhs->months); - sub_years(answer, rhs->years); + utc = crm_get_utc_time(rhs); - normalize_time(answer); + sub_seconds(answer, utc->seconds); + sub_months(answer, utc->months); + sub_days(answer, utc->days); + sub_years(answer, utc->years); return answer; } -/* ha_time_interval_t* */ -/* parse_time_interval(char **interval_str) */ -/* { */ -/* return NULL; */ -/* } */ - int month_days[14] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 }; int days_per_month(int month, int year) { if (month == 2 && is_leap_year(year)) { month = 13; } return month_days[month]; } gboolean is_leap_year(int year) { gboolean is_leap = FALSE; if (year % 4 == 0) { is_leap = TRUE; } if (year % 100 == 0 && year % 400 != 0) { is_leap = FALSE; } return is_leap; } gboolean parse_int(char **str, int field_width, int uppper_bound, int *result) { int lpc = 0; int intermediate = 0; gboolean fraction = FALSE; gboolean negate = FALSE; CRM_CHECK(str != NULL, return FALSE); CRM_CHECK(*str != NULL, return FALSE); CRM_CHECK(result != NULL, return FALSE); *result = 0; if (strlen(*str) <= 0) { return FALSE; } if ((*str)[0] == 'T') { (*str)++; } if ((*str)[0] == '.' || (*str)[0] == ',') { fraction = TRUE; field_width = -1; (*str)++; } else if ((*str)[0] == '-') { negate = TRUE; (*str)++; } else if ((*str)[0] == '+' || (*str)[0] == ':') { (*str)++; } for (; (fraction || lpc < field_width) && isdigit((int)(*str)[0]); lpc++) { if (fraction) { intermediate = ((*str)[0] - '0') / (10 ^ lpc); } else { *result *= 10; intermediate = (*str)[0] - '0'; } *result += intermediate; (*str)++; } if (fraction) { *result = (int)(*result * uppper_bound); } else if (uppper_bound > 0 && *result > uppper_bound) { *result = uppper_bound; } if (negate) { *result = 0 - *result; } if (lpc > 0) { crm_trace("Found int: %d. Stopped at str[%d]='%c'", *result, lpc, (*str)[lpc]); return TRUE; } return FALSE; } -gboolean -check_for_ordinal(const char *str) -{ - if (isdigit((int)str[2]) == FALSE) { - crm_trace("char 3 == %c", str[2]); - return FALSE; - } - if (isspace((int)str[3])) { - return TRUE; - } else if (str[3] == 0) { - return TRUE; - } else if (str[3] == 'T') { - return TRUE; - } else if (str[3] == '/') { - return TRUE; - } - crm_trace("char 4 == %c", str[3]); - return FALSE; -} - -int -str_lookup(const char *str, enum date_fields field) -{ - return 0; -} - void reset_time(ha_time_t * a_time) { a_time->years = 0; - a_time->has->years = FALSE; - - a_time->weekyears = 0; - a_time->has->weekyears = FALSE; - - a_time->months = 0; - a_time->has->months = FALSE; - - a_time->weeks = 0; - a_time->has->weeks = FALSE; - a_time->days = 0; - a_time->has->days = FALSE; - - a_time->weekdays = 0; - a_time->has->weekdays = FALSE; - - a_time->yeardays = 0; - a_time->has->yeardays = FALSE; - - a_time->hours = 0; - a_time->has->hours = FALSE; - - a_time->minutes = 0; - a_time->has->minutes = FALSE; - a_time->seconds = 0; - a_time->has->seconds = FALSE; } void reset_tm(struct tm *some_tm) { some_tm->tm_sec = -1; /* seconds after the minute [0-60] */ some_tm->tm_min = -1; /* minutes after the hour [0-59] */ some_tm->tm_hour = -1; /* hours since midnight [0-23] */ some_tm->tm_mday = -1; /* day of the month [1-31] */ some_tm->tm_mon = -1; /* months since January [0-11] */ some_tm->tm_year = -1; /* years since 1900 */ some_tm->tm_wday = -1; /* days since Sunday [0-6] */ some_tm->tm_yday = -1; /* days since January 1 [0-365] */ some_tm->tm_isdst = -1; /* Daylight Savings Time flag */ #if defined(HAVE_STRUCT_TM_TM_GMTOFF) some_tm->tm_gmtoff = -1; /* offset from CUT in seconds */ #endif #if defined(HAVE_TM_ZONE) some_tm->tm_zone = NULL; /* timezone abbreviation */ #endif } gboolean -is_date_sane(ha_time_t * a_date) +is_date_sane(ha_time_t * dt) { int ydays = 0; - int mdays = 0; - int weeks = 0; - - CRM_CHECK(a_date != NULL, return FALSE); - ydays = is_leap_year(a_date->years) ? 366 : 365; - mdays = days_per_month(a_date->months, a_date->years); - weeks = weeks_in_year(a_date->weekyears); - crm_trace("max ydays: %d, max mdays: %d, max weeks: %d", ydays, mdays, weeks); + CRM_CHECK(dt != NULL, return FALSE); - CRM_CHECK(a_date->has->years, return FALSE); - CRM_CHECK(a_date->has->weekyears, return FALSE); + ydays = year_days(dt->years); + crm_trace("max ydays: %d", ydays); - CRM_CHECK(a_date->has->months, return FALSE); - CRM_CHECK(a_date->months > 0, return FALSE); - CRM_CHECK(a_date->months <= 12, return FALSE); + CRM_CHECK(dt->days > 0, return FALSE); + CRM_CHECK(dt->days <= ydays, return FALSE); - CRM_CHECK(a_date->has->weeks, return FALSE); - CRM_CHECK(a_date->weeks > 0, return FALSE); - CRM_CHECK(a_date->weeks <= weeks, return FALSE); - - CRM_CHECK(a_date->has->days, return FALSE); - CRM_CHECK(a_date->days > 0, return FALSE); - CRM_CHECK(a_date->days <= mdays, return FALSE); - - CRM_CHECK(a_date->has->weekdays, return FALSE); - CRM_CHECK(a_date->weekdays > 0, return FALSE); - CRM_CHECK(a_date->weekdays <= 7, return FALSE); - - CRM_CHECK(a_date->has->yeardays, return FALSE); - CRM_CHECK(a_date->yeardays > 0, return FALSE); - CRM_CHECK(a_date->yeardays <= ydays, return FALSE); - - CRM_CHECK(a_date->hours >= 0, return FALSE); - CRM_CHECK(a_date->hours < 24, return FALSE); - - CRM_CHECK(a_date->minutes >= 0, return FALSE); - CRM_CHECK(a_date->minutes < 60, return FALSE); - - CRM_CHECK(a_date->seconds >= 0, return FALSE); - CRM_CHECK(a_date->seconds <= 60, return FALSE); + CRM_CHECK(dt->seconds >= 0, return FALSE); + CRM_CHECK(dt->seconds < 24*60*60, return FALSE); return TRUE; } -#define do_cmp_field(lhs, rhs, field) \ - { \ - if(lhs->field > rhs->field) { \ +#define do_cmp_field(l, r, field) \ + if(rc == 0) { \ + if(l->field > r->field) { \ crm_trace("%s: %d > %d", \ - #field, lhs->field, rhs->field); \ - return 1; \ - } else if(lhs->field < rhs->field) { \ + #field, l->field, r->field); \ + rc = 1; \ + } else if(l->field < r->field) { \ crm_trace("%s: %d < %d", \ - #field, lhs->field, rhs->field); \ - return -1; \ + #field, l->field, r->field); \ + rc = -1; \ } \ - } + } int -compare_date(ha_time_t * lhs, ha_time_t * rhs) +compare_date(ha_time_t * a, ha_time_t * b) { - if (lhs == NULL && rhs == NULL) { + int rc = 0; + ha_time_t *t1 = NULL; + ha_time_t *t2 = NULL; + + if (a == NULL && b == NULL) { return 0; - } else if (lhs == NULL) { + } else if (a == NULL) { return -1; - } else if (rhs == NULL) { + } else if (b == NULL) { return 1; } - normalize_time(lhs); - normalize_time(rhs); + t1 = crm_get_utc_time(a); + t2 = crm_get_utc_time(b); - do_cmp_field(lhs->normalized, rhs->normalized, years); - do_cmp_field(lhs->normalized, rhs->normalized, yeardays); - do_cmp_field(lhs->normalized, rhs->normalized, hours); - do_cmp_field(lhs->normalized, rhs->normalized, minutes); - do_cmp_field(lhs->normalized, rhs->normalized, seconds); + do_cmp_field(t1, t2, years); + do_cmp_field(t1, t2, days); + do_cmp_field(t1, t2, seconds); - return 0; + return rc; } ha_time_t * new_ha_date(gboolean set_to_now) { time_t tm_now; ha_time_t *now = NULL; tzset(); now = calloc(1, sizeof(ha_time_t)); - now->has = calloc(1, sizeof(ha_has_time_t)); - now->offset = calloc(1, sizeof(ha_time_t)); - now->offset->has = calloc(1, sizeof(ha_has_time_t)); if (set_to_now) { tm_now = time(NULL); - now->tm_now = tm_now; ha_set_timet_time(now, &tm_now); } return now; } void -free_ha_date(ha_time_t * a_date) +free_ha_date(ha_time_t * dt) { - if (a_date == NULL) { + if (dt == NULL) { return; } - free_ha_date(a_date->normalized); - free_ha_date(a_date->offset); - free(a_date->has); - free(a_date); + free(dt); } void log_tm_date(int log_level, struct tm *some_tm) { const char *tzn; #if defined(HAVE_TM_ZONE) tzn = some_tm->tm_zone; #elif defined(HAVE_TZNAME) tzn = tzname[0]; #else tzn = NULL; #endif do_crm_log(log_level, "%.2d/%.2d/%.4d %.2d:%.2d:%.2d %s" " (wday=%d, yday=%d, dst=%d, offset=%ld)", some_tm->tm_mday, some_tm->tm_mon, 1900 + some_tm->tm_year, some_tm->tm_hour, some_tm->tm_min, some_tm->tm_sec, tzn, some_tm->tm_wday == 0 ? 7 : some_tm->tm_wday, 1 + some_tm->tm_yday, some_tm->tm_isdst, GMTOFF(some_tm)); } -ha_time_t *the_epoch = NULL; - -#define update_seconds(date, field, multiplier) do { \ - before = in_seconds; \ - in_seconds += a_date->field; \ - in_seconds *= multiplier; \ - if(before > in_seconds) { \ - crm_crit("Date wrap detected: %s", #field); \ - return 0; \ - } \ - } while(0) - unsigned long long -date_in_seconds(ha_time_t * a_date) +date_in_seconds(ha_time_t * dt) { - unsigned long long before = 0; + int lpc; + ha_time_t *utc = NULL; unsigned long long in_seconds = 0; - /* normalize_time(a_date); */ - update_seconds(a_date, years, 365); - update_seconds(a_date, yeardays, 24); - update_seconds(a_date, hours, 60); - update_seconds(a_date, minutes, 60); - update_seconds(a_date, seconds, 1); + utc = crm_get_utc_time(dt); + + for(lpc = 1; lpc < utc->years; lpc++) { + int dmax = year_days(lpc); + in_seconds += 60 * 60 * 24 * dmax; + } + + in_seconds += 60 * 60 * 24 * utc->days; + in_seconds += 60 * utc->seconds; + + free_ha_date(utc); return in_seconds; } +#define EPOCH_SECONDS 62135683200 /* Calculated using date_in_seconds() */ unsigned long long -date_in_seconds_since_epoch(ha_time_t * a_date) +date_in_seconds_since_epoch(ha_time_t * dt) { - ha_time_t *since_epoch = NULL; - unsigned long long in_seconds = 0; - - normalize_time(a_date); - - if (the_epoch == NULL) { - char *EPOCH = strdup("1970-01-01"); - - the_epoch = parse_date(&EPOCH); - normalize_time(the_epoch); - free(EPOCH); - } - - since_epoch = subtract_time(a_date, the_epoch); - in_seconds = date_in_seconds(since_epoch); - free_ha_date(since_epoch); - return in_seconds; + return date_in_seconds(dt) - EPOCH_SECONDS; } diff --git a/lib/common/iso8601_fields.c b/lib/common/iso8601_fields.c index d16352424c..c12f6f2c08 100644 --- a/lib/common/iso8601_fields.c +++ b/lib/common/iso8601_fields.c @@ -1,425 +1,246 @@ /* * Copyright (C) 2005 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * http://en.wikipedia.org/wiki/ISO_8601 as at 2005-08-01 * */ #include #include #include #include #include -#define do_add_field(atime, field, extra, limit, overflow) \ - { \ - crm_trace("Adding %d to %d (limit=%d)", \ - extra, atime->field, limit); \ - atime->field += extra; \ - if(limit > 0) { \ - while(limit < atime->field) { \ - crm_trace("Overflowing: %d", atime->field); \ - atime->field -= limit; \ - overflow(atime, 1); \ - } \ - } \ - atime->field = atime->field; \ - crm_trace("Result: %d", atime->field); \ - } - -#define do_add_days_field(atime, field, extra, overflow) \ - { \ - int __limit = days_per_month(atime->months, atime->years); \ - crm_trace("Adding %d to %d (limit=%d)", \ - extra, atime->field, __limit); \ - atime->field += extra; \ - if(__limit > 0) { \ - while(__limit < atime->field) { \ - crm_trace("Overflowing: %d", atime->field); \ - overflow(atime, 1); \ - __limit = days_per_month(atime->months, atime->years); \ - atime->field -= __limit; \ - } \ - } \ - atime->field = atime->field; \ - crm_trace("Result: %d", atime->field); \ - } - -#define do_add_time_field(atime, field, extra, limit, overflow) \ - { \ - crm_trace("Adding %d to %d (limit=%d)", \ - extra, atime->field, limit); \ - atime->field += extra; \ - if(limit > 0) { \ - while(limit <= atime->field) { \ - crm_trace("Overflowing: %d", atime->field); \ - atime->field -= limit; \ - overflow(atime, 1); \ - } \ - } \ - atime->field = atime->field; \ - crm_trace("Result: %d", atime->field); \ - } - -#define do_sub_field(atime, field, extra, limit, overflow) \ - { \ - crm_trace("Subtracting %d from %d (limit=%d)", \ - extra, atime->field, limit); \ - atime->field -= extra; \ - while(atime->field < 1) { \ - crm_trace("Underflowing: %d", atime->field); \ - atime->field += limit; \ - overflow(atime, 1); \ - } \ - crm_trace("Result: %d", atime->field); \ - } - -#define do_sub_days_field(atime, field, extra, overflow) \ - { \ - int __limit = days_per_month(atime->months, atime->years); \ - crm_trace("Subtracting %d from %d (__limit=%d)", \ - extra, atime->field, __limit); \ - atime->field -= extra; \ - while(atime->field < 1) { \ - crm_trace("Underflowing: %d", atime->field); \ - __limit = days_per_month(atime->months, atime->years); \ - atime->field += __limit; \ - overflow(atime, 1); \ - } \ - crm_trace("Result: %d", atime->field); \ - } -#define do_sub_time_field(atime, field, extra, limit, overflow) \ - { \ - crm_trace("Subtracting %d from %d (limit=%d)", \ - extra, atime->field, limit); \ - atime->field -= extra; \ - while(atime->field < 0) { \ - crm_trace("Underflowing: %d", atime->field); \ - atime->field += limit; \ - overflow(atime, 1); \ - } \ - crm_trace("Result: %d", atime->field); \ - } +struct ha_time_s { + int years; + int months; /* Only for durations */ + int days; + int seconds; -void -add_seconds(ha_time_t * a_time, int extra) -{ - if (extra < 0) { - sub_seconds(a_time, -extra); - } else { - do_add_time_field(a_time, seconds, extra, 60, add_minutes); - } -} + struct ha_time_s *offset; +}; -void -add_minutes(ha_time_t * a_time, int extra) +static uint32_t get_ordinal_days(uint32_t y, uint32_t m, uint32_t d) { - if (extra < 0) { - sub_minutes(a_time, -extra); - } else { - do_add_time_field(a_time, minutes, extra, 60, add_hours); + int lpc; + for(lpc = 1; lpc < m; lpc++) { + d += days_per_month(lpc, y); } + return d; } void -add_hours(ha_time_t * a_time, int extra) +add_seconds(ha_time_t * a_time, int extra) { - if (extra < 0) { - sub_hours(a_time, -extra); - } else { - do_add_time_field(a_time, hours, extra, 24, add_days); - } -} + int days = 0; + int seconds = 24 * 60 * 60; -void -add_days(ha_time_t * a_time, int extra) -{ - if (a_time->has->days == FALSE) { - crm_trace("has->days == FALSE"); + crm_trace("Adding %d seconds to %d (max=%d)", extra, a_time->seconds, seconds); + if(extra < 0) { + sub_seconds(a_time, -extra); return; } - if (extra < 0) { - sub_days(a_time, -extra); - } else { - do_add_days_field(a_time, days, extra, add_months); - } - - convert_from_gregorian(a_time); -} -void -add_weekdays(ha_time_t * a_time, int extra) -{ - if (a_time->has->weekdays == FALSE) { - crm_trace("has->weekdays == FALSE"); - return; + a_time->seconds += extra; + while (a_time->seconds >= seconds) { + a_time->seconds -= seconds; + days++; } - if (extra < 0) { - sub_weekdays(a_time, -extra); - } else { - do_add_field(a_time, weekdays, extra, 7, add_weeks); - } - - convert_from_weekdays(a_time); + add_days(a_time, days); } void -add_yeardays(ha_time_t * a_time, int extra) +add_days(ha_time_t * a_time, int extra) { - if (a_time->has->yeardays == FALSE) { - crm_trace("has->yeardays == FALSE"); - return; - } - if (extra < 0) { - sub_yeardays(a_time, -extra); - } else { - /* coverity[result_independent_of_operands] Not interesting */ - do_add_field(a_time, yeardays, extra, - (is_leap_year(a_time->years) ? 366 : 365), add_ordinalyears); - } + int ydays = is_leap_year(a_time->years)?366:365; - convert_from_ordinal(a_time); -} - -void -add_weeks(ha_time_t * a_time, int extra) -{ - if (a_time->has->weeks == FALSE) { - crm_trace("has->weeks == FALSE"); + crm_trace("Adding %d days to %.4d-%.3d", + extra, a_time->years, a_time->days); + if(extra < 0) { + sub_days(a_time, -extra); return; } - if (extra < 0) { - sub_weeks(a_time, -extra); - } else { - do_add_field(a_time, weeks, extra, weeks_in_year(a_time->years), add_weekyears); + + a_time->days += extra; + while (a_time->days > ydays) { + a_time->years++; + a_time->days -= ydays; + ydays = is_leap_year(a_time->years)?366:365; } - - convert_from_weekdays(a_time); } void add_months(ha_time_t * a_time, int extra) { - int max = 0; + int lpc; + uint32_t y, m, d, dmax; - if (a_time->has->months == FALSE) { - crm_trace("has->months == FALSE"); - return; - } - if (extra < 0) { + crm_get_gregorian_date(a_time, &y, &m, &d); + crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d); + if(extra < 0) { sub_months(a_time, -extra); - } else { - do_add_field(a_time, months, extra, 12, add_years); + return; } - max = days_per_month(a_time->months, a_time->years); - if (a_time->days > max) { - a_time->days = max; + for(lpc = extra; lpc > 0; lpc--) { + m++; + if(m == 13) { + m = 1; + y++; + } } - convert_from_gregorian(a_time); -} -void -add_years(ha_time_t * a_time, int extra) -{ - if (a_time->has->years == FALSE) { - crm_trace("has->years == FALSE"); - return; + dmax = days_per_month(m, y); + if(dmax < d) { + /* Preserve day-of-month unless the month doesn't have enough days */ + d = dmax; } - a_time->years += extra; - convert_from_gregorian(a_time); -} -void -add_ordinalyears(ha_time_t * a_time, int extra) -{ - if (a_time->has->years == FALSE) { - crm_trace("has->years == FALSE"); - return; - } - a_time->years += extra; - convert_from_ordinal(a_time); -} + crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d); + + a_time->years = y; + a_time->days = get_ordinal_days(y, m, d); -void -add_weekyears(ha_time_t * a_time, int extra) -{ - if (a_time->has->weekyears == FALSE) { - crm_trace("has->weekyears == FALSE"); - return; - } - a_time->weekyears += extra; - convert_from_weekdays(a_time); + crm_get_gregorian_date(a_time, &y, &m, &d); + crm_trace("Got %.4d-%.2d-%.2d", y, m, d); } void sub_seconds(ha_time_t * a_time, int extra) { - if (extra < 0) { + int days = 0; + + crm_trace("Subtracting %d seconds from %d", extra, a_time->seconds); + if(extra < 0) { add_seconds(a_time, -extra); - } else { - do_sub_time_field(a_time, seconds, extra, 60, sub_minutes); + return; } -} -void -sub_minutes(ha_time_t * a_time, int extra) -{ - if (extra < 0) { - add_minutes(a_time, -extra); - } else { - do_sub_time_field(a_time, minutes, extra, 60, sub_hours); - } -} + a_time->seconds -= extra; + crm_trace("s=%d, d=%d", a_time->seconds, days); -void -sub_hours(ha_time_t * a_time, int extra) -{ - if (extra < 0) { - add_hours(a_time, -extra); - } else { - do_sub_time_field(a_time, hours, extra, 24, sub_days); + while (a_time->seconds < 0) { + crm_trace("s=%d, d=%d", a_time->seconds, days); + a_time->seconds += 24 * 60 * 60; + days++; + crm_trace("s=%d, d=%d", a_time->seconds, days); } + sub_days(a_time, days); } void sub_days(ha_time_t * a_time, int extra) { - if (a_time->has->days == FALSE) { - crm_trace("has->days == FALSE"); + crm_trace("Subtracting %d days from %.4d-%.3d", + extra, a_time->years, a_time->days); + if(extra < 0) { + add_days(a_time, -extra); return; } - crm_trace("Subtracting %d days from %.4d-%.2d-%.2d", - extra, a_time->years, a_time->months, a_time->days); - - if (extra < 0) { - add_days(a_time, -extra); - } else { - do_sub_days_field(a_time, days, extra, sub_months); + a_time->days -= extra; + while (a_time->days <= 0) { + a_time->years--; + a_time->days += is_leap_year(a_time->years)?366:365; } - - convert_from_gregorian(a_time); } void -sub_weekdays(ha_time_t * a_time, int extra) +sub_months(ha_time_t * a_time, int extra) { - if (a_time->has->weekdays == FALSE) { - crm_trace("has->weekdays == FALSE"); + int lpc; + uint32_t y, m, d, dmax; + crm_get_gregorian_date(a_time, &y, &m, &d); + + crm_trace("Subtracting %d months from %.4d-%.2d-%.2d", extra, y, m, d); + if(extra < 0) { + add_months(a_time, -extra); return; - } + } - crm_trace("Subtracting %d days from %.4d-%.2d-%.2d", - extra, a_time->years, a_time->months, a_time->days); + for(lpc = extra; lpc > 0; lpc--) { + m--; + if(m == 0) { + m = 12; + y--; + } + } - if (extra < 0) { - add_weekdays(a_time, -extra); - } else { - do_sub_field(a_time, weekdays, extra, 7, sub_weeks); + dmax = days_per_month(m, y); + if(dmax < d) { + /* Preserve day-of-month unless the month doesn't have enough days */ + d = dmax; } + crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d); - convert_from_weekdays(a_time); + a_time->years = y; + a_time->days = get_ordinal_days(y, m, d); + + crm_get_gregorian_date(a_time, &y, &m, &d); + crm_trace("Got %.4d-%.2d-%.2d", y, m, d); } void -sub_yeardays(ha_time_t * a_time, int extra) +add_minutes(ha_time_t * a_time, int extra) { - if (a_time->has->yeardays == FALSE) { - crm_trace("has->yeardays == FALSE"); - return; - } - - crm_trace("Subtracting %d days from %.4d-%.3d", extra, a_time->years, a_time->yeardays); - - if (extra < 0) { - add_yeardays(a_time, -extra); - } else { - do_sub_field(a_time, yeardays, extra, - is_leap_year(a_time->years) ? 366 : 365, sub_ordinalyears); - } - - convert_from_ordinal(a_time); + add_seconds(a_time, extra * 60); } void -sub_weeks(ha_time_t * a_time, int extra) +add_hours(ha_time_t * a_time, int extra) { - if (a_time->has->weeks == FALSE) { - crm_trace("has->weeks == FALSE"); - return; - } - if (extra < 0) { - add_weeks(a_time, -extra); - } else { - do_sub_field(a_time, weeks, extra, weeks_in_year(a_time->years), sub_weekyears); - } + add_seconds(a_time, extra * 60 * 60); +} - convert_from_weekdays(a_time); +void +add_weeks(ha_time_t * a_time, int extra) +{ + add_days(a_time, extra * 7); } void -sub_months(ha_time_t * a_time, int extra) +add_years(ha_time_t * a_time, int extra) { - if (a_time->has->months == FALSE) { - crm_trace("has->months == FALSE"); - return; - } - if (extra < 0) { - add_months(a_time, -extra); - } else { - do_sub_field(a_time, months, extra, 12, sub_years); - } - convert_from_gregorian(a_time); + a_time->years += extra; } void -sub_years(ha_time_t * a_time, int extra) +sub_minutes(ha_time_t * a_time, int extra) { - if (a_time->has->years == FALSE) { - crm_trace("has->years == FALSE"); - return; - } - a_time->years -= extra; - convert_from_gregorian(a_time); + sub_seconds(a_time, extra * 60); } void -sub_weekyears(ha_time_t * a_time, int extra) +sub_hours(ha_time_t * a_time, int extra) { - if (a_time->has->weekyears == FALSE) { - crm_trace("has->weekyears == FALSE"); - return; - } - a_time->weekyears -= extra; + sub_seconds(a_time, extra * 60 * 60); +} - convert_from_weekdays(a_time); +void +sub_weeks(ha_time_t * a_time, int extra) +{ + sub_days(a_time, 7 * extra); } void -sub_ordinalyears(ha_time_t * a_time, int extra) +sub_years(ha_time_t * a_time, int extra) { - if (a_time->has->years == FALSE) { - crm_trace("has->years == FALSE"); - return; - } a_time->years -= extra; - - convert_from_ordinal(a_time); } + diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c index c254b01f5e..f5ad29a0bb 100644 --- a/lib/pengine/rules.c +++ b/lib/pengine/rules.c @@ -1,718 +1,734 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include CRM_TRACE_INIT_DATA(pe_rules); ha_time_t *parse_xml_duration(ha_time_t * start, xmlNode * duration_spec); gboolean test_date_expression(xmlNode * time_expr, ha_time_t * now); gboolean cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec); gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now); gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now); gboolean test_ruleset(xmlNode * ruleset, GHashTable * node_hash, ha_time_t * now) { gboolean ruleset_default = TRUE; xmlNode *rule = NULL; for (rule = __xml_first_child(ruleset); rule != NULL; rule = __xml_next(rule)) { if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) { ruleset_default = FALSE; if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) { return TRUE; } } } return ruleset_default; } gboolean test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now) { xmlNode *expr = NULL; gboolean test = TRUE; gboolean empty = TRUE; gboolean passed = TRUE; gboolean do_and = TRUE; const char *value = NULL; rule = expand_idref(rule, NULL); value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP); if (safe_str_eq(value, "or")) { do_and = FALSE; passed = FALSE; } crm_trace("Testing rule %s", ID(rule)); for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next(expr)) { test = test_expression(expr, node_hash, role, now); empty = FALSE; if (test && do_and == FALSE) { crm_trace("Expression %s/%s passed", ID(rule), ID(expr)); return TRUE; } else if (test == FALSE && do_and) { crm_trace("Expression %s/%s failed", ID(rule), ID(expr)); return FALSE; } } if (empty) { crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule)); } crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed"); return passed; } gboolean test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now) { gboolean accept = FALSE; const char *uname = NULL; switch (find_expression_type(expr)) { case nested_rule: accept = test_rule(expr, node_hash, role, now); break; case attr_expr: case loc_expr: /* these expressions can never succeed if there is * no node to compare with */ if (node_hash != NULL) { accept = test_attr_expression(expr, node_hash, now); } break; case time_expr: accept = test_date_expression(expr, now); break; case role_expr: accept = test_role_expression(expr, role, now); break; default: CRM_CHECK(FALSE /* bad type */ , return FALSE); accept = FALSE; } if (node_hash) { uname = g_hash_table_lookup(node_hash, "#uname"); } crm_trace("Expression %s %s on %s", ID(expr), accept ? "passed" : "failed", uname ? uname : "all ndoes"); return accept; } enum expression_type find_expression_type(xmlNode * expr) { const char *tag = NULL; const char *attr = NULL; attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); tag = crm_element_name(expr); if (safe_str_eq(tag, "date_expression")) { return time_expr; } else if (safe_str_eq(tag, XML_TAG_RULE)) { return nested_rule; } else if (safe_str_neq(tag, "expression")) { return not_expr; } else if (safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) { return loc_expr; } else if (safe_str_eq(attr, "#role")) { return role_expr; } return attr_expr; } gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now) { gboolean accept = FALSE; const char *op = NULL; const char *value = NULL; if (role == RSC_ROLE_UNKNOWN) { return accept; } value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); if (safe_str_eq(op, "defined")) { if (role > RSC_ROLE_STARTED) { accept = TRUE; } } else if (safe_str_eq(op, "not_defined")) { if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { accept = TRUE; } } else if (safe_str_eq(op, "eq")) { if (text2role(value) == role) { accept = TRUE; } } else if (safe_str_eq(op, "ne")) { /* we will only test "ne" wtih master/slave roles style */ if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { accept = FALSE; } else if (text2role(value) != role) { accept = TRUE; } } return accept; } gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now) { gboolean accept = FALSE; int cmp = 0; const char *h_val = NULL; const char *op = NULL; const char *type = NULL; const char *attr = NULL; const char *value = NULL; attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); if (attr == NULL || op == NULL) { pe_err("Invlaid attribute or operation in expression" " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value)); return FALSE; } if (hash != NULL) { h_val = (const char *)g_hash_table_lookup(hash, attr); } if (value != NULL && h_val != NULL) { if (type == NULL) { if (safe_str_eq(op, "lt") || safe_str_eq(op, "lte") || safe_str_eq(op, "gt") || safe_str_eq(op, "gte")) { type = "number"; } else { type = "string"; } crm_trace("Defaulting to %s based comparison for '%s' op", type, op); } if (safe_str_eq(type, "string")) { cmp = strcasecmp(h_val, value); } else if (safe_str_eq(type, "number")) { int h_val_f = crm_parse_int(h_val, NULL); int value_f = crm_parse_int(value, NULL); if (h_val_f < value_f) { cmp = -1; } else if (h_val_f > value_f) { cmp = 1; } else { cmp = 0; } } else if (safe_str_eq(type, "version")) { cmp = compare_version(h_val, value); } } else if (value == NULL && h_val == NULL) { cmp = 0; } else if (value == NULL) { cmp = 1; } else { cmp = -1; } if (safe_str_eq(op, "defined")) { if (h_val != NULL) { accept = TRUE; } } else if (safe_str_eq(op, "not_defined")) { if (h_val == NULL) { accept = TRUE; } } else if (safe_str_eq(op, "eq")) { if ((h_val == value) || cmp == 0) { accept = TRUE; } } else if (safe_str_eq(op, "ne")) { if ((h_val == NULL && value != NULL) || (h_val != NULL && value == NULL) || cmp != 0) { accept = TRUE; } } else if (value == NULL || h_val == NULL) { /* the comparision is meaningless from this point on */ accept = FALSE; } else if (safe_str_eq(op, "lt")) { if (cmp < 0) { accept = TRUE; } } else if (safe_str_eq(op, "lte")) { if (cmp <= 0) { accept = TRUE; } } else if (safe_str_eq(op, "gt")) { if (cmp > 0) { accept = TRUE; } } else if (safe_str_eq(op, "gte")) { if (cmp >= 0) { accept = TRUE; } } return accept; } /* As per the nethack rules: * * moon period = 29.53058 days ~= 30, year = 365.2422 days * days moon phase advances on first day of year compared to preceding year * = 365.2422 - 12*29.53058 ~= 11 * years in Metonic cycle (time until same phases fall on the same days of * the month) = 18.6 ~= 19 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 * (29 as initial condition) * current phase in days = first day phase + days elapsed in year * 6 moons ~= 177 days * 177 ~= 8 reported phases * 22 * + 11/22 for rounding * * 0-7, with 0: new, 4: full */ static int phase_of_the_moon(ha_time_t * now) { - int epact, diy, goldn; + uint32_t epact, diy, goldn; + uint32_t y; - diy = now->yeardays; - goldn = (now->years % 19) + 1; + crm_get_ordinal_date(now, &y, &diy); + + goldn = (y % 19) + 1; epact = (11 * goldn + 18) % 30; if ((epact == 25 && goldn > 11) || epact == 24) epact++; return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); } static gboolean decodeNVpair(const char *srcstring, char separator, char **name, char **value) { int lpc = 0; int len = 0; const char *temp = NULL; CRM_ASSERT(name != NULL && value != NULL); *name = NULL; *value = NULL; crm_trace("Attempting to decode: [%s]", srcstring); if (srcstring != NULL) { len = strlen(srcstring); while (lpc <= len) { if (srcstring[lpc] == separator) { *name = calloc(1, lpc + 1); if (*name == NULL) { break; /* and return FALSE */ } memcpy(*name, srcstring, lpc); (*name)[lpc] = '\0'; /* this sucks but as the strtok manpage says.. * it *is* a bug */ len = len - lpc; len--; if (len <= 0) { *value = NULL; } else { *value = calloc(1, len + 1); if (*value == NULL) { break; /* and return FALSE */ } temp = srcstring + lpc + 1; memcpy(*value, temp, len); (*value)[len] = '\0'; } return TRUE; } lpc++; } } if (*name != NULL) { free(*name); *name = NULL; } *name = NULL; *value = NULL; return FALSE; } #define cron_check(xml_field, time_field) \ value = crm_element_value(cron_spec, xml_field); \ if(value != NULL) { \ gboolean pass = TRUE; \ decodeNVpair(value, '-', &value_low, &value_high); \ if(value_low == NULL) { \ value_low = strdup(value); \ } \ value_low_i = crm_parse_int(value_low, "0"); \ value_high_i = crm_parse_int(value_high, "-1"); \ if(value_high_i < 0) { \ if(value_low_i != time_field) { \ pass = FALSE; \ } \ } else if(value_low_i > time_field) { \ pass = FALSE; \ } else if(value_high_i < time_field) { \ pass = FALSE; \ } \ free(value_low); \ free(value_high); \ if(pass == FALSE) { \ crm_debug("Condition '%s' in %s: failed", value, xml_field); \ return pass; \ } \ crm_debug("Condition '%s' in %s: passed", value, xml_field); \ } gboolean cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec) { const char *value = NULL; char *value_low = NULL; char *value_high = NULL; int value_low_i = 0; int value_high_i = 0; + uint32_t h, m, s, y, d, w; + CRM_CHECK(now != NULL, return FALSE); - cron_check("seconds", now->seconds); - cron_check("minutes", now->minutes); - cron_check("hours", now->hours); - cron_check("monthdays", now->days); - cron_check("weekdays", now->weekdays); - cron_check("yeardays", now->yeardays); - cron_check("weeks", now->weeks); - cron_check("months", now->months); - cron_check("years", now->years); - cron_check("weekyears", now->weekyears); + crm_get_time(now, &h, &m, &s); + + cron_check("seconds", s); + cron_check("minutes", m); + cron_check("hours", h); + + crm_get_gregorian_date(now, &y, &m, &d); + + cron_check("monthdays", d); + cron_check("months", m); + cron_check("years", y); + + crm_get_ordinal_date(now, &y, &d); + + cron_check("yeardays", d); + + crm_get_week_date(now, &y, &w, &d); + + cron_check("weekyears", y); + cron_check("weeks", w); + cron_check("weekdays", d); + cron_check("moon", phase_of_the_moon(now)); return TRUE; } #define update_field(xml_field, time_fn) \ value = crm_element_value(duration_spec, xml_field); \ if(value != NULL) { \ int value_i = crm_parse_int(value, "0"); \ time_fn(end, value_i); \ } ha_time_t * parse_xml_duration(ha_time_t * start, xmlNode * duration_spec) { ha_time_t *end = NULL; const char *value = NULL; end = new_ha_date(FALSE); ha_set_time(end, start, TRUE); update_field("years", add_years); update_field("months", add_months); update_field("weeks", add_weeks); update_field("days", add_days); update_field("hours", add_hours); update_field("minutes", add_minutes); update_field("seconds", add_seconds); return end; } gboolean test_date_expression(xmlNode * time_expr, ha_time_t * now) { ha_time_t *start = NULL; ha_time_t *end = NULL; const char *value = NULL; char *value_copy = NULL; char *value_copy_start = NULL; const char *op = crm_element_value(time_expr, "operation"); xmlNode *duration_spec = NULL; xmlNode *date_spec = NULL; gboolean passed = FALSE; crm_trace("Testing expression: %s", ID(time_expr)); duration_spec = first_named_child(time_expr, "duration"); date_spec = first_named_child(time_expr, "date_spec"); value = crm_element_value(time_expr, "start"); if (value != NULL) { value_copy = strdup(value); value_copy_start = value_copy; start = parse_date(&value_copy); free(value_copy_start); } value = crm_element_value(time_expr, "end"); if (value != NULL) { value_copy = strdup(value); value_copy_start = value_copy; end = parse_date(&value_copy); free(value_copy_start); } if (start != NULL && end == NULL && duration_spec != NULL) { end = parse_xml_duration(start, duration_spec); } if (op == NULL) { op = "in_range"; } if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) { if (start != NULL && compare_date(start, now) > 0) { passed = FALSE; } else if (end != NULL && compare_date(end, now) < 0) { passed = FALSE; } else if (safe_str_eq(op, "in_range")) { passed = TRUE; } else { passed = cron_range_satisfied(now, date_spec); } } else if (safe_str_eq(op, "gt") && compare_date(start, now) < 0) { passed = TRUE; } else if (safe_str_eq(op, "lt") && compare_date(end, now) > 0) { passed = TRUE; } else if (safe_str_eq(op, "eq") && compare_date(start, now) == 0) { passed = TRUE; } else if (safe_str_eq(op, "neq") && compare_date(start, now) != 0) { passed = TRUE; } free_ha_date(start); free_ha_date(end); return passed; } typedef struct sorted_set_s { int score; const char *name; const char *special_name; xmlNode *attr_set; } sorted_set_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { const sorted_set_t *pair_a = a; const sorted_set_t *pair_b = b; if (a == NULL && b == NULL) { return 0; } else if (a == NULL) { return 1; } else if (b == NULL) { return -1; } if (safe_str_eq(pair_a->name, pair_a->special_name)) { return -1; } else if (safe_str_eq(pair_b->name, pair_a->special_name)) { return 1; } if (pair_a->score < pair_b->score) { return 1; } else if (pair_a->score > pair_b->score) { return -1; } return 0; } static void populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite) { const char *name = NULL; const char *value = NULL; const char *old_value = NULL; xmlNode *list = nvpair_list; xmlNode *an_attr = NULL; name = crm_element_name(list->children); if (safe_str_eq(XML_TAG_ATTRS, name)) { list = list->children; } for (an_attr = __xml_first_child(list); an_attr != NULL; an_attr = __xml_next(an_attr)) { if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) { name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME); crm_trace("Setting attribute: %s", name); value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE); if (name == NULL || value == NULL) { continue; } old_value = g_hash_table_lookup(hash, name); if (safe_str_eq(value, "#default")) { if (old_value) { crm_trace("Removing value for %s (%s)", name, value); g_hash_table_remove(hash, name); } continue; } else if (old_value == NULL) { g_hash_table_insert(hash, strdup(name), strdup(value)); } else if (overwrite) { crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value); g_hash_table_replace(hash, strdup(name), strdup(value)); } } } } struct unpack_data_s { gboolean overwrite; GHashTable *node_hash; GHashTable *hash; ha_time_t *now; }; static void unpack_attr_set(gpointer data, gpointer user_data) { sorted_set_t *pair = data; struct unpack_data_s *unpack_data = user_data; if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) { return; } crm_trace("Adding attributes from %s", pair->name); populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite); } void unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name, GHashTable * node_hash, GHashTable * hash, const char *always_first, gboolean overwrite, ha_time_t * now) { GListPtr sorted = NULL; GListPtr unsorted = NULL; const char *score = NULL; sorted_set_t *pair = NULL; struct unpack_data_s data; xmlNode *attr_set = NULL; if (xml_obj == NULL) { crm_trace("No instance attributes"); return; } crm_trace("Checking for attributes"); for (attr_set = __xml_first_child(xml_obj); attr_set != NULL; attr_set = __xml_next(attr_set)) { /* Uncertain if set_name == NULL check is strictly necessary here */ if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) { pair = NULL; attr_set = expand_idref(attr_set, top); if (attr_set == NULL) { continue; } pair = calloc(1, sizeof(sorted_set_t)); pair->name = ID(attr_set); pair->special_name = always_first; pair->attr_set = attr_set; score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE); pair->score = char2score(score); unsorted = g_list_prepend(unsorted, pair); } } if (pair != NULL) { data.hash = hash; data.node_hash = node_hash; data.now = now; data.overwrite = overwrite; } sorted = g_list_sort(unsorted, sort_pairs); g_list_foreach(sorted, unpack_attr_set, &data); g_list_free_full(sorted, free); } diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index c4cb8c17d4..d5ba973c45 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -1,1513 +1,1513 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include pe_working_set_t *pe_dataset = NULL; extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root); void print_str_str(gpointer key, gpointer value, gpointer user_data); gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); void unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set); static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled); node_t * node_copy(node_t * this_node) { node_t *new_node = NULL; CRM_CHECK(this_node != NULL, return NULL); new_node = calloc(1, sizeof(node_t)); CRM_ASSERT(new_node != NULL); crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node); new_node->weight = this_node->weight; new_node->fixed = this_node->fixed; new_node->details = this_node->details; return new_node; } /* any node in list1 or list2 and not in the other gets a score of -INFINITY */ void node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores) { GHashTable *result = hash; node_t *other_node = NULL; GListPtr gIter = list; GHashTableIter iter; node_t *node = NULL; g_hash_table_iter_init(&iter, hash); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { other_node = pe_find_node_id(list, node->details->id); if (other_node == NULL) { node->weight = -INFINITY; } else if (merge_scores) { node->weight = merge_weights(node->weight, other_node->weight); } } for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; other_node = pe_hash_table_lookup(result, node->details->id); if (other_node == NULL) { node_t *new_node = node_copy(node); new_node->weight = -INFINITY; g_hash_table_insert(result, (gpointer) new_node->details->id, new_node); } } } GHashTable * node_hash_from_list(GListPtr list) { GListPtr gIter = list; GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str); for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; node_t *n = node_copy(node); g_hash_table_insert(result, (gpointer) n->details->id, n); } return result; } GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter) { GListPtr result = NULL; GListPtr gIter = list1; for (; gIter != NULL; gIter = gIter->next) { node_t *new_node = NULL; node_t *this_node = (node_t *) gIter->data; if (filter && this_node->weight < 0) { continue; } new_node = node_copy(this_node); if (reset) { new_node->weight = 0; } if (new_node != NULL) { result = g_list_prepend(result, new_node); } } return result; } static gint sort_node_uname(gconstpointer a, gconstpointer b) { const node_t *node_a = a; const node_t *node_b = b; return strcmp(node_a->details->uname, node_b->details->uname); } void dump_node_scores_worker(int level, const char *file, const char *function, int line, resource_t * rsc, const char *comment, GHashTable * nodes) { GHashTable *hash = nodes; GHashTableIter iter; node_t *node = NULL; if (rsc) { hash = rsc->allowed_nodes; } if (rsc && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't show the allocation scores for orphans */ return; } if (level == 0) { /* For now we want this in sorted order to keep the regression tests happy */ GListPtr gIter = NULL; GListPtr list = g_hash_table_get_values(hash); list = g_list_sort(list, sort_node_uname); gIter = list; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; char *score = score2char(node->weight); if (rsc) { printf("%s: %s allocation score on %s: %s\n", comment, rsc->id, node->details->uname, score); } else { printf("%s: %s = %s\n", comment, node->details->uname, score); } free(score); } g_list_free(list); } else if (hash) { g_hash_table_iter_init(&iter, hash); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { char *score = score2char(node->weight); if (rsc) { do_crm_log_alias(LOG_TRACE, file, function, line, "%s: %s allocation score on %s: %s", comment, rsc->id, node->details->uname, score); } else { do_crm_log_alias(LOG_TRACE, file, function, line + 1, "%s: %s = %s", comment, node->details->uname, score); } free(score); } } if (rsc && rsc->children) { GListPtr gIter = NULL; gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; dump_node_scores_worker(level, file, function, line, child, comment, nodes); } } } static void append_dump_text(gpointer key, gpointer value, gpointer user_data) { char **dump_text = user_data; int len = 0; char *new_text = NULL; len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1; new_text = calloc(1, len); sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value); free(*dump_text); *dump_text = new_text; } void dump_node_capacity(int level, const char *comment, node_t * node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1; dump_text = calloc(1, len); sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname); g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text); if (level == 0) { fprintf(stdout, "%s\n", dump_text); } else { crm_trace("%s", dump_text); } free(dump_text); } void dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ") + strlen(node->details->uname) + strlen(":") + 1; dump_text = calloc(1, len); sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname); g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text); if (level == 0) { fprintf(stdout, "%s\n", dump_text); } else { crm_trace("%s", dump_text); } free(dump_text); } gint sort_rsc_index(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t *)a; const resource_t *resource2 = (const resource_t *)b; if (a == NULL && b == NULL) { return 0; } if (a == NULL) { return 1; } if (b == NULL) { return -1; } if (resource1->sort_index > resource2->sort_index) { return -1; } if (resource1->sort_index < resource2->sort_index) { return 1; } return 0; } gint sort_rsc_priority(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t *)a; const resource_t *resource2 = (const resource_t *)b; if (a == NULL && b == NULL) { return 0; } if (a == NULL) { return 1; } if (b == NULL) { return -1; } if (resource1->priority > resource2->priority) { return -1; } if (resource1->priority < resource2->priority) { return 1; } return 0; } action_t * custom_action(resource_t * rsc, char *key, const char *task, node_t * on_node, gboolean optional, gboolean save_action, pe_working_set_t * data_set) { action_t *action = NULL; GListPtr possible_matches = NULL; CRM_CHECK(key != NULL, return NULL); CRM_CHECK(task != NULL, return NULL); if (save_action && rsc != NULL) { possible_matches = find_actions(rsc->actions, key, on_node); } if (possible_matches != NULL) { if (g_list_length(possible_matches) > 1) { pe_warn("Action %s for %s on %s exists %d times", task, rsc ? rsc->id : "", on_node ? on_node->details->uname : "", g_list_length(possible_matches)); } action = g_list_nth_data(possible_matches, 0); pe_rsc_trace(rsc, "Found existing action (%d) %s for %s on %s", action->id, task, rsc ? rsc->id : "", on_node ? on_node->details->uname : ""); g_list_free(possible_matches); } if (action == NULL) { if (save_action) { pe_rsc_trace(rsc, "Creating%s action %d: %s for %s on %s", optional ? "" : " manditory", data_set->action_id, key, rsc ? rsc->id : "", on_node ? on_node->details->uname : ""); } action = calloc(1, sizeof(action_t)); if (save_action) { action->id = data_set->action_id++; } else { action->id = 0; } action->rsc = rsc; CRM_ASSERT(task != NULL); action->task = strdup(task); if (on_node) { action->node = node_copy(on_node); } action->uuid = strdup(key); pe_set_action_bit(action, pe_action_failure_is_fatal); pe_set_action_bit(action, pe_action_runnable); if (optional) { pe_set_action_bit(action, pe_action_optional); } else { pe_clear_action_bit(action, pe_action_optional); } /* Implied by calloc()... action->actions_before = NULL; action->actions_after = NULL; action->pseudo = FALSE; action->dumped = FALSE; action->processed = FALSE; action->seen_count = 0; */ action->extra = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free); action->meta = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free); if (save_action) { data_set->actions = g_list_prepend(data_set->actions, action); } if (rsc != NULL) { action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE); unpack_operation(action, action->op_entry, data_set); if (save_action) { rsc->actions = g_list_prepend(rsc->actions, action); } } if (save_action) { pe_rsc_trace(rsc, "Action %d created", action->id); } } if (optional == FALSE) { pe_rsc_trace(rsc, "Action %d (%s) marked manditory", action->id, action->uuid); pe_clear_action_bit(action, pe_action_optional); } if (rsc != NULL) { enum action_tasks a_task = text2task(action->task); int warn_level = LOG_TRACE; if (save_action) { warn_level = LOG_WARNING; } if (is_set(action->flags, pe_action_have_node_attrs) == FALSE && action->node != NULL && action->op_entry != NULL) { pe_set_action_bit(action, pe_action_have_node_attrs); unpack_instance_attributes(data_set->input, action->op_entry, XML_TAG_ATTR_SETS, action->node->details->attrs, action->extra, NULL, FALSE, data_set->now); } if (is_set(action->flags, pe_action_pseudo)) { /* leave untouched */ } else if (action->node == NULL) { pe_clear_action_bit(action, pe_action_runnable); } else if (is_not_set(rsc->flags, pe_rsc_managed) && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) { crm_debug("Action %s (unmanaged)", action->uuid); pe_set_action_bit(action, pe_action_optional); /* action->runnable = FALSE; */ } else if (action->node->details->online == FALSE) { pe_clear_action_bit(action, pe_action_runnable); do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)", action->uuid, action->node->details->uname); if (is_set(action->rsc->flags, pe_rsc_managed) && action->node->details->unclean == FALSE && save_action && a_task == stop_rsc) { do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname); action->node->details->unclean = TRUE; } } else if (action->node->details->pending) { pe_clear_action_bit(action, pe_action_runnable); do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)", action->uuid, action->node->details->uname); } else if (action->needs == rsc_req_nothing) { pe_rsc_trace(rsc, "Action %s doesnt require anything", action->uuid); pe_set_action_bit(action, pe_action_runnable); #if 0 /* * No point checking this * - if we dont have quorum we cant stonith anyway */ } else if (action->needs == rsc_req_stonith) { crm_trace("Action %s requires only stonith", action->uuid); action->runnable = TRUE; #endif } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_stop) { pe_clear_action_bit(action, pe_action_runnable); crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid); } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_freeze) { pe_rsc_trace(rsc, "Check resource is already active"); if (rsc->fns->active(rsc, TRUE) == FALSE) { pe_clear_action_bit(action, pe_action_runnable); pe_rsc_debug(rsc, "%s\t%s (cancelled : quorum freeze)", action->node->details->uname, action->uuid); } } else { pe_rsc_trace(rsc, "Action %s is runnable", action->uuid); pe_set_action_bit(action, pe_action_runnable); } if (save_action) { switch (a_task) { case stop_rsc: set_bit(rsc->flags, pe_rsc_stopping); break; case start_rsc: clear_bit(rsc->flags, pe_rsc_starting); if (is_set(action->flags, pe_action_runnable)) { set_bit(rsc->flags, pe_rsc_starting); } break; default: break; } } } free(key); return action; } void unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set) { int value_i = 0; unsigned long long interval = 0; unsigned long long start_delay = 0; char *value_ms = NULL; const char *class = NULL; const char *value = NULL; const char *field = NULL; CRM_CHECK(action->rsc != NULL, return); unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); if (xml_obj) { xmlAttrPtr xIter = NULL; for (xIter = xml_obj->properties; xIter; xIter = xIter->next) { const char *prop_name = (const char *)xIter->name; const char *prop_value = crm_element_value(xml_obj, prop_name); g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value)); } } unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL, action->meta, NULL, FALSE, data_set->now); g_hash_table_remove(action->meta, "id"); class = g_hash_table_lookup(action->rsc->meta, "class"); value = g_hash_table_lookup(action->meta, "requires"); if (safe_str_eq(class, "stonith")) { action->needs = rsc_req_nothing; value = "nothing (fencing op)"; } else if (safe_str_eq(value, "nothing")) { action->needs = rsc_req_nothing; } else if (safe_str_eq(value, "quorum")) { action->needs = rsc_req_quorum; } else if (is_set(data_set->flags, pe_flag_stonith_enabled) && safe_str_eq(value, "fencing")) { action->needs = rsc_req_stonith; } else { if (value) { crm_config_err("Invalid value for %s->requires: %s%s", action->rsc->id, value, is_set(data_set->flags, pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)"); } if (safe_str_eq(action->task, CRMD_ACTION_STATUS) || safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) { action->needs = rsc_req_nothing; value = "nothing (default)"; } else if (data_set->no_quorum_policy == no_quorum_stop && safe_str_neq(action->task, CRMD_ACTION_START)) { action->needs = rsc_req_nothing; value = "nothing (default)"; } else if (is_set(data_set->flags, pe_flag_stonith_enabled)) { action->needs = rsc_req_stonith; value = "fencing (default)"; } else { action->needs = rsc_req_quorum; value = "quorum (default)"; } } pe_rsc_trace(action->rsc, "\tAction %s requires: %s", action->task, value); value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); if (safe_str_eq(action->task, CRMD_ACTION_STOP) && safe_str_eq(value, "standby")) { crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id); value = NULL; } if (value == NULL) { } else if (safe_str_eq(value, "block")) { action->on_fail = action_fail_block; } else if (safe_str_eq(value, "fence")) { action->on_fail = action_fail_fence; value = "node fencing"; if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense"); action->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } } else if (safe_str_eq(value, "standby")) { action->on_fail = action_fail_standby; value = "node standby"; } else if (safe_str_eq(value, "ignore") || safe_str_eq(value, "nothing")) { action->on_fail = action_fail_ignore; value = "ignore"; } else if (safe_str_eq(value, "migrate")) { action->on_fail = action_fail_migrate; value = "force migration"; } else if (safe_str_eq(value, "stop")) { action->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } else if (safe_str_eq(value, "restart")) { action->on_fail = action_fail_recover; value = "restart (and possibly migrate)"; } else { pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value); value = NULL; } /* defaults */ if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { if (is_set(data_set->flags, pe_flag_stonith_enabled)) { action->on_fail = action_fail_fence; value = "resource fence (default)"; } else { action->on_fail = action_fail_block; value = "resource block (default)"; } } else if (value == NULL) { action->on_fail = action_fail_recover; value = "restart (and possibly migrate) (default)"; } pe_rsc_trace(action->rsc, "\t%s failure handling: %s", action->task, value); value = NULL; if (xml_obj != NULL) { value = g_hash_table_lookup(action->meta, "role_after_failure"); } if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) { action->fail_role = text2role(value); } /* defaults */ if (action->fail_role == RSC_ROLE_UNKNOWN) { if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) { action->fail_role = RSC_ROLE_SLAVE; } else { action->fail_role = RSC_ROLE_STARTED; } } pe_rsc_trace(action->rsc, "\t%s failure results in: %s", action->task, role2text(action->fail_role)); field = XML_LRM_ATTR_INTERVAL; value = g_hash_table_lookup(action->meta, field); if (value != NULL) { interval = crm_get_interval(value); if (interval > 0) { value_ms = crm_itoa(interval); g_hash_table_replace(action->meta, strdup(field), value_ms); } else { g_hash_table_remove(action->meta, field); } } field = XML_OP_ATTR_START_DELAY; value = g_hash_table_lookup(action->meta, field); if (value != NULL) { value_i = crm_get_msec(value); if (value_i < 0) { value_i = 0; } start_delay = value_i; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, strdup(field), value_ms); } else if (interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) { char *date_str = NULL; char *date_str_mutable = NULL; ha_time_t *origin = NULL; value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN); date_str = strdup(value); date_str_mutable = date_str; origin = parse_date(&date_str_mutable); free(date_str); if (origin == NULL) { crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s", ID(xml_obj), value); } else { ha_time_t *delay = NULL; int rc = compare_date(origin, data_set->now); unsigned long long delay_s = 0; while (rc < 0) { add_seconds(origin, interval / 1000); rc = compare_date(origin, data_set->now); } delay = subtract_time(origin, data_set->now); delay_s = date_in_seconds(delay); /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */ crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj)); g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000)); start_delay = delay_s * 1000; free_ha_date(origin); free_ha_date(delay); } } field = XML_ATTR_TIMEOUT; value = g_hash_table_lookup(action->meta, field); if (value == NULL) { value = pe_pref(data_set->config_hash, "default-action-timeout"); } value_i = crm_get_msec(value); if (value_i < 0) { value_i = 0; } value_i += start_delay; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, strdup(field), value_ms); } static xmlNode * find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled) { int number = 0; gboolean do_retry = TRUE; char *local_key = NULL; const char *name = NULL; const char *value = NULL; const char *interval = NULL; char *match_key = NULL; xmlNode *op = NULL; xmlNode *operation = NULL; retry: for (operation = __xml_first_child(rsc->ops_xml); operation != NULL; operation = __xml_next(operation)) { if (crm_str_eq((const char *)operation->name, "op", TRUE)) { name = crm_element_value(operation, "name"); interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); value = crm_element_value(operation, "enabled"); if (!include_disabled && value && crm_is_true(value) == FALSE) { continue; } number = crm_get_interval(interval); if (number < 0) { continue; } match_key = generate_op_key(rsc->id, name, number); if (safe_str_eq(key, match_key)) { op = operation; } free(match_key); if (op != NULL) { free(local_key); return op; } } } free(local_key); if (do_retry == FALSE) { return NULL; } do_retry = FALSE; if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) { local_key = generate_op_key(rsc->id, "migrate", 0); key = local_key; goto retry; } else if (strstr(key, "_notify_")) { local_key = generate_op_key(rsc->id, "notify", 0); key = local_key; goto retry; } return NULL; } xmlNode * find_rsc_op_entry(resource_t * rsc, const char *key) { return find_rsc_op_entry_helper( rsc, key, FALSE); } void print_node(const char *pre_text, node_t * node, gboolean details) { if (node == NULL) { crm_trace("%s%s: ", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": "); return; } crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ", node->details == NULL ? "error " : node->details->online ? "" : "Unavailable/Unclean ", node->details->uname, node->weight, node->fixed ? "True" : "False"); if (details && node != NULL && node->details != NULL) { char *pe_mutable = strdup("\t\t"); GListPtr gIter = node->details->running_rsc; crm_trace("\t\t===Node Attributes"); g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable); free(pe_mutable); crm_trace("\t\t=== Resources"); for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE); } } } /* * Used by the HashTable for-loop */ void print_str_str(gpointer key, gpointer value, gpointer user_data) { crm_trace("%s%s %s ==> %s", user_data == NULL ? "" : (char *)user_data, user_data == NULL ? "" : ": ", (char *)key, (char *)value); } void print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details) { long options = pe_print_log; if (rsc == NULL) { do_crm_log(log_level - 1, "%s%s: ", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": "); return; } if (details) { options |= pe_print_details; } rsc->fns->print(rsc, pre_text, options, &log_level); } void pe_free_action(action_t * action) { if (action == NULL) { return; } g_list_free_full(action->actions_before, free); /* action_warpper_t* */ g_list_free_full(action->actions_after, free); /* action_warpper_t* */ if (action->extra) { g_hash_table_destroy(action->extra); } if (action->meta) { g_hash_table_destroy(action->meta); } free(action->task); free(action->uuid); free(action->node); free(action); } GListPtr find_recurring_actions(GListPtr input, node_t * not_on_node) { const char *value = NULL; GListPtr result = NULL; GListPtr gIter = input; CRM_CHECK(input != NULL, return NULL); for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); if (value == NULL) { /* skip */ } else if (safe_str_eq(value, "0")) { /* skip */ } else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) { /* skip */ } else if (not_on_node == NULL) { crm_trace("(null) Found: %s", action->uuid); result = g_list_prepend(result, action); } else if (action->node == NULL) { /* skip */ } else if (action->node->details != not_on_node->details) { crm_trace("Found: %s", action->uuid); result = g_list_prepend(result, action); } } return result; } enum action_tasks get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic) { enum action_tasks task = text2task(name); if (rsc == NULL) { return task; } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) { switch (task) { case stopped_rsc: case started_rsc: case action_demoted: case action_promoted: crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id); return task - 1; break; default: break; } } return task; } action_t * find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node) { GListPtr gIter = NULL; CRM_CHECK(uuid || task, return NULL); for (gIter = input; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (uuid != NULL && safe_str_neq(uuid, action->uuid)) { continue; } else if (task != NULL && safe_str_neq(task, action->task)) { continue; } else if (on_node == NULL) { return action; } else if (action->node == NULL) { continue; } else if (on_node->details == action->node->details) { return action; } } return NULL; } GListPtr find_actions(GListPtr input, const char *key, node_t * on_node) { GListPtr gIter = input; GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; crm_trace("Matching %s against %s", key, action->uuid); if (safe_str_neq(key, action->uuid)) { continue; } else if (on_node == NULL) { result = g_list_prepend(result, action); } else if (action->node == NULL) { /* skip */ crm_trace("While looking for %s action on %s, " "found an unallocated one. Assigning" " it to the requested node...", key, on_node->details->uname); action->node = node_copy(on_node); result = g_list_prepend(result, action); } else if (on_node->details == action->node->details) { result = g_list_prepend(result, action); } } return result; } GListPtr find_actions_exact(GListPtr input, const char *key, node_t * on_node) { GListPtr gIter = input; GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; crm_trace("Matching %s against %s", key, action->uuid); if (safe_str_neq(key, action->uuid)) { crm_trace("Key mismatch: %s vs. %s", key, action->uuid); continue; } else if (on_node == NULL || action->node == NULL) { crm_trace("on_node=%p, action->node=%p", on_node, action->node); continue; } else if (safe_str_eq(on_node->details->id, action->node->details->id)) { result = g_list_prepend(result, action); } crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id); } return result; } static void resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag) { node_t *match = NULL; if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; resource_node_score(child_rsc, node, score, tag); } } pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score); match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (match == NULL) { match = node_copy(node); match->weight = merge_weights(score, node->weight); g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match); } match->weight = merge_weights(match->weight, score); } void resource_location(resource_t * rsc, node_t * node, int score, const char *tag, pe_working_set_t * data_set) { if (node != NULL) { resource_node_score(rsc, node, score, tag); } else if (data_set != NULL) { GListPtr gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; resource_node_score(rsc, node, score, tag); } } else { GHashTableIter iter; node_t *node = NULL; g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { resource_node_score(rsc, node, score, tag); } } if (node == NULL && score == -INFINITY) { if (rsc->allocated_to) { crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname); free(rsc->allocated_to); rsc->allocated_to = NULL; } } } #define sort_return(an_int, why) do { \ free(a_uuid); \ free(b_uuid); \ crm_trace("%s (%d) %c %s (%d) : %s", \ a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \ b_xml_id, b_call_id, why); \ return an_int; \ } while(0) gint sort_op_by_callid(gconstpointer a, gconstpointer b) { int a_call_id = -1; int b_call_id = -1; char *a_uuid = NULL; char *b_uuid = NULL; const xmlNode *xml_a = a; const xmlNode *xml_b = b; const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID); const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID); if (safe_str_eq(a_xml_id, b_xml_id)) { /* We have duplicate lrm_rsc_op entries in the status * section which is unliklely to be a good thing * - we can handle it easily enough, but we need to get * to the bottom of why its happening. */ pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); sort_return(0, "duplicate"); } crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id); crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id); if (a_call_id == -1 && b_call_id == -1) { /* both are pending ops so it doesnt matter since * stops are never pending */ sort_return(0, "pending"); } else if (a_call_id >= 0 && a_call_id < b_call_id) { sort_return(-1, "call id"); } else if (b_call_id >= 0 && a_call_id > b_call_id) { sort_return(1, "call id"); } else if (b_call_id >= 0 && a_call_id == b_call_id) { /* * The op and last_failed_op are the same * Order on last-rc-change */ int last_a = -1; int last_b = -1; crm_element_value_const_int(xml_a, "last-rc-change", &last_a); crm_element_value_const_int(xml_b, "last-rc-change", &last_b); if (last_a >= 0 && last_a < last_b) { sort_return(-1, "rc-change"); } else if (last_b >= 0 && last_a > last_b) { sort_return(1, "rc-change"); } sort_return(0, "rc-change"); } else { /* One of the inputs is a pending operation * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other */ int a_id = -1; int b_id = -1; int dummy = -1; const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC); const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC); CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic")); CRM_CHECK(decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy), sort_return(0, "bad magic a")); CRM_CHECK(decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy), sort_return(0, "bad magic b")); /* try and determin the relative age of the operation... * some pending operations (ie. a start) may have been supuerceeded * by a subsequent stop * * [a|b]_id == -1 means its a shutdown operation and _always_ comes last */ if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { /* * some of the logic in here may be redundant... * * if the UUID from the TE doesnt match then one better * be a pending operation. * pending operations dont survive between elections and joins * because we query the LRM directly */ if (b_call_id == -1) { sort_return(-1, "transition + call"); } else if (a_call_id == -1) { sort_return(1, "transition + call"); } } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) { sort_return(-1, "transition"); } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) { sort_return(1, "transition"); } } /* we should never end up here */ CRM_CHECK(FALSE, sort_return(0, "default")); } time_t get_timet_now(pe_working_set_t * data_set) { time_t now = 0; - if (data_set && data_set->now) { - now = data_set->now->tm_now; - } - + /* if (data_set && data_set->now) { */ + /* now = data_set->now->tm_now; */ + /* } */ + if (now == 0) { /* eventually we should convert data_set->now into time_tm * for now, its only triggered by PE regression tests */ now = time(NULL); crm_crit("Defaulting to 'now'"); - if (data_set && data_set->now) { - data_set->now->tm_now = now; - } + /* if (data_set && data_set->now) { */ + /* data_set->now->tm_now = now; */ + /* } */ } return now; } struct fail_search { resource_t *rsc; int count; long long last; char *key; }; static void get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data) { struct fail_search *search = user_data; const char *key = key_p; const char *match = strstr(key, search->key); if (match) { if (strstr(key, "last-failure-") == key && (key + 13) == match) { search->last = crm_int_helper(value, NULL); } else if (strstr(key, "fail-count-") == key && (key + 11) == match) { search->count += char2score(value); } } } int get_failcount(node_t * node, resource_t * rsc, int *last_failure, pe_working_set_t * data_set) { char *key = NULL; const char *value = NULL; struct fail_search search = { rsc, 0, 0, NULL }; /* Optimize the "normal" case */ key = crm_concat("fail-count", rsc->clone_name?rsc->clone_name:rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.count = char2score(value); free(key); if(value) { key = crm_concat("last-failure", rsc->clone_name?rsc->clone_name:rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.last = crm_int_helper(value, NULL); free(key); /* This block wont be relevant once we omit anonymous instance numbers */ } else if (is_not_set(rsc->flags, pe_rsc_unique)) { int lpc = 0; search.rsc = uber_parent(rsc); search.key = strdup(rsc->id); /* Strip the clone incarnation */ for (lpc = strlen(search.key); lpc > 0; lpc--) { if (search.key[lpc] == ':') { search.key[lpc + 1] = 0; break; } } g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search); free(search.key); } if (search.count != 0 && search.last != 0 && last_failure) { *last_failure = search.last; } if (search.count != 0 && search.last != 0 && rsc->failure_timeout) { if (search.last > 0) { time_t now = get_timet_now(data_set); if (now > (search.last + rsc->failure_timeout)) { crm_debug("Failcount for %s on %s has expired (limit was %ds)", search.rsc->id, node->details->uname, rsc->failure_timeout); search.count = 0; } } } if (search.count != 0) { char *score = score2char(search.count); crm_info("%s has failed %s times on %s", search.rsc->id, score, node->details->uname); free(score); } return search.count; } gboolean get_target_role(resource_t * rsc, enum rsc_role_e * role) { enum rsc_role_e local_role = RSC_ROLE_UNKNOWN; const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); CRM_CHECK(role != NULL, return FALSE); if (value == NULL || safe_str_eq("started", value) || safe_str_eq("default", value)) { return FALSE; } local_role = text2role(value); if (local_role == RSC_ROLE_UNKNOWN) { crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value); return FALSE; } else if (local_role > RSC_ROLE_STARTED) { if (uber_parent(rsc)->variant == pe_master) { if (local_role > RSC_ROLE_SLAVE) { /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */ return FALSE; } } else { crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value); return FALSE; } } *role = local_role; return TRUE; } gboolean order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order) { GListPtr gIter = NULL; action_wrapper_t *wrapper = NULL; GListPtr list = NULL; if (order == pe_order_none) { return FALSE; } if (lh_action == NULL || rh_action == NULL) { return FALSE; } crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid); /* Filter dups, otherwise update_action_states() has too much work to do */ gIter = lh_action->actions_after; for (; gIter != NULL; gIter = gIter->next) { action_wrapper_t *after = (action_wrapper_t *) gIter->data; if (after->action == rh_action && (after->type & order)) { return FALSE; } } wrapper = calloc(1, sizeof(action_wrapper_t)); wrapper->action = rh_action; wrapper->type = order; list = lh_action->actions_after; list = g_list_prepend(list, wrapper); lh_action->actions_after = list; wrapper = NULL; /* order |= pe_order_implies_then; */ /* order ^= pe_order_implies_then; */ wrapper = calloc(1, sizeof(action_wrapper_t)); wrapper->action = lh_action; wrapper->type = order; list = rh_action->actions_before; list = g_list_prepend(list, wrapper); rh_action->actions_before = list; return TRUE; } action_t * get_pseudo_op(const char *name, pe_working_set_t * data_set) { action_t *op = NULL; const char *op_s = name; GListPtr possible_matches = NULL; possible_matches = find_actions(data_set->actions, name, NULL); if (possible_matches != NULL) { if (g_list_length(possible_matches) > 1) { pe_warn("Action %s exists %d times", name, g_list_length(possible_matches)); } op = g_list_nth_data(possible_matches, 0); g_list_free(possible_matches); } else { op = custom_action(NULL, strdup(op_s), op_s, NULL, TRUE, TRUE, data_set); set_bit(op->flags, pe_action_pseudo); set_bit(op->flags, pe_action_runnable); } return op; } void destroy_ticket(gpointer data) { ticket_t *ticket = data; if (ticket->state) { g_hash_table_destroy(ticket->state); } free(ticket->id); free(ticket); } ticket_t * ticket_new(const char *ticket_id, pe_working_set_t * data_set) { ticket_t *ticket = NULL; if (ticket_id == NULL || strlen(ticket_id) == 0) { return NULL; } if (data_set->tickets == NULL) { data_set->tickets = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket); } ticket = g_hash_table_lookup(data_set->tickets, ticket_id); if (ticket == NULL) { ticket = calloc(1, sizeof(ticket_t)); if (ticket == NULL) { crm_err("Cannot allocate ticket '%s'", ticket_id); return NULL; } crm_trace("Creaing ticket entry for %s", ticket_id); ticket->id = strdup(ticket_id); ticket->granted = FALSE; ticket->last_granted = -1; ticket->standby = FALSE; ticket->state = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket); } return ticket; }