diff --git a/include/crm/common/iso8601_internal.h b/include/crm/common/iso8601_internal.h index 7226e8b1662..54f89f008ea 100644 --- a/include/crm/common/iso8601_internal.h +++ b/include/crm/common/iso8601_internal.h @@ -20,25 +20,31 @@ extern "C" { #endif -typedef struct pcmk__time_us pcmk__time_hr_t; - -pcmk__time_hr_t *pcmk__time_hr_now(time_t *epoch); -pcmk__time_hr_t *pcmk__time_hr_new(const char *date_time); -void pcmk__time_hr_free(pcmk__time_hr_t *hr_dt); -char *pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt); +char *pcmk__time_format_hr(const char *format, const crm_time_t *dt, int usec); char *pcmk__epoch2str(const time_t *source, uint32_t flags); char *pcmk__timespec2str(const struct timespec *ts, uint32_t flags); const char *pcmk__readable_interval(guint interval_ms); crm_time_t *pcmk__copy_timet(time_t source); -struct pcmk__time_us { +// A date/time or duration +struct crm_time_s { + // Calendar year (date/time) or number of years (duration) int years; - int months; /* Only for durations */ + + // Number of months (duration only) + int months; + + // Ordinal day of year (date/time) or number of days (duration) int days; + + // Seconds of day (date/time) or number of seconds (duration) int seconds; - int offset; /* Seconds */ + + // Seconds offset from UTC (date/time only) + int offset; + + // True if duration bool duration; - int useconds; }; #ifdef __cplusplus diff --git a/lib/common/fuzzers/iso8601_fuzzer.c b/lib/common/fuzzers/iso8601_fuzzer.c index 0e151c6dbd8..65a2d430cea 100644 --- a/lib/common/fuzzers/iso8601_fuzzer.c +++ b/lib/common/fuzzers/iso8601_fuzzer.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 the Pacemaker project contributors + * Copyright 2024-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,6 +10,9 @@ #include #include #include +#include // struct timespec + +#include // qb_util_timespec_from_epoch_get() #include #include @@ -18,11 +21,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { char *ns = NULL; - char *result = NULL; - time_t epoch = 0; - pcmk__time_hr_t *now = NULL; crm_time_period_t *period = NULL; + struct timespec tv = { 0, }; + crm_time_t now = { 0, }; + char *result = NULL; + // Ensure we have enough data. if (size < 10) { return -1; // Do not add input to testing corpus @@ -33,12 +37,10 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) period = crm_time_parse_period(ns); crm_time_free_period(period); - now = pcmk__time_hr_new(ns); - pcmk__time_hr_free(now); - - now = pcmk__time_hr_now(&epoch); - result = pcmk__time_format_hr(ns, now); - pcmk__time_hr_free(now); + qb_util_timespec_from_epoch_get(&tv); + crm_time_set_timet(&now, &(tv.tv_sec)); + result = pcmk__time_format_hr(ns, &now, + (int) (tv.tv_nsec / QB_TIME_NS_IN_USEC)); free(result); free(ns); diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c index 0b419e69c86..48964e599dd 100644 --- a/lib/common/iso8601.c +++ b/lib/common/iso8601.c @@ -69,16 +69,6 @@ ((QB_ABS(usec) < QB_TIME_US_IN_SEC) \ && (((sec) == 0) || ((usec) == 0) || (((sec) < 0) == ((usec) < 0)))) -// A date/time or duration -struct crm_time_s { - int years; // Calendar year (date/time) or number of years (duration) - int months; // Number of months (duration only) - int days; // Ordinal day of year (date/time) or number of days (duration) - int seconds; // Seconds of day (date/time) or number of seconds (duration) - int offset; // Seconds offset from UTC (date/time only) - bool duration; // True if duration -}; - static crm_time_t *parse_date(const char *date_str); static crm_time_t * @@ -161,12 +151,7 @@ crm_time_free(crm_time_t * dt) static int year_days(int year) { - int d = 365; - - if (crm_time_leapyear(year)) { - d++; - } - return d; + return crm_time_leapyear(year)? 366 : 365; } /* From http://myweb.ecu.edu/mccartyr/ISOwdALG.txt : @@ -1798,8 +1783,6 @@ crm_time_add_seconds(crm_time_t *a_time, int extra) crm_time_add_days(a_time, days); } -#define ydays(t) (crm_time_leapyear((t)->years)? 366 : 365) - /*! * \brief Add days to a date/time * @@ -1814,12 +1797,13 @@ crm_time_add_days(crm_time_t *a_time, int extra) crm_trace("Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days); if (extra > 0) { - while ((a_time->days + (long long) extra) > ydays(a_time)) { + while ((a_time->days + (long long) extra) > year_days(a_time->years)) { if ((a_time->years + 1LL) > INT_MAX) { - a_time->days = ydays(a_time); // Clip to latest we can handle + // Clip to latest we can handle + a_time->days = year_days(a_time->years); return; } - extra -= ydays(a_time); + extra -= year_days(a_time->years); a_time->years++; } } else if (extra < 0) { @@ -1831,7 +1815,7 @@ crm_time_add_days(crm_time_t *a_time, int extra) return; } a_time->years--; - extra += ydays(a_time); + extra += year_days(a_time->years); } } a_time->days += extra; @@ -1912,80 +1896,8 @@ crm_time_add_years(crm_time_t * a_time, int extra) } } -/* The high-resolution variant of time object was added to meet an immediate - * need, and is kept internal API. - * - * @TODO The long-term goal is to come up with a clean, unified design for a - * time type (or types) that meets all the various needs, to replace - * crm_time_t, pcmk__time_hr_t, and struct timespec (in lrmd_cmd_t). - */ - -static pcmk__time_hr_t * -time_to_hr(const crm_time_t *dt) -{ - pcmk__time_hr_t *hr_dt = NULL; - - pcmk__assert(dt != NULL); - - hr_dt = pcmk__assert_alloc(1, sizeof(pcmk__time_hr_t)); - hr_dt->years = dt->years; - hr_dt->months = dt->months; - hr_dt->days = dt->days; - hr_dt->seconds = dt->seconds; - hr_dt->offset = dt->offset; - hr_dt->duration = dt->duration; - return hr_dt; -} - -/*! - * \internal - * \brief Return the current time as a high-resolution time - * - * \param[out] epoch If not NULL, this will be set to seconds since epoch - * - * \return Newly allocated high-resolution time set to the current time - */ -pcmk__time_hr_t * -pcmk__time_hr_now(time_t *epoch) -{ - struct timespec tv; - crm_time_t dt; - pcmk__time_hr_t *hr; - - qb_util_timespec_from_epoch_get(&tv); - if (epoch != NULL) { - *epoch = tv.tv_sec; - } - crm_time_set_timet(&dt, &(tv.tv_sec)); - hr = time_to_hr(&dt); - hr->useconds = tv.tv_nsec / QB_TIME_NS_IN_USEC; - return hr; -} - -pcmk__time_hr_t * -pcmk__time_hr_new(const char *date_time) -{ - pcmk__time_hr_t *hr_dt = NULL; - - if (date_time == NULL) { - hr_dt = pcmk__time_hr_now(NULL); - } else { - crm_time_t *dt = parse_date(date_time); - - hr_dt = time_to_hr(dt); - crm_time_free(dt); - } - return hr_dt; -} - -void -pcmk__time_hr_free(pcmk__time_hr_t * hr_dt) -{ - free(hr_dt); -} - static void -ha_get_tm_time(struct tm *target, const pcmk__time_hr_t *source) +ha_get_tm_time(struct tm *target, const crm_time_t *source) { *target = (struct tm) { .tm_year = source->years - 1900, @@ -2074,7 +1986,8 @@ get_g_date_time(const struct tm *tm, int offset) * \param[in] format Date/time format string compatible with * \c g_date_time_format(), with additional support for * \c "%N" for fractional seconds - * \param[in] hr_dt Time value to format + * \param[in] dt Time value to format (at seconds resolution) + * \param[in] usec Microseconds to add to \p dt when formatting * * \return Newly allocated string with formatted string, or \c NULL on error * @@ -2083,7 +1996,7 @@ get_g_date_time(const struct tm *tm, int offset) * in a future release. */ char * -pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) +pcmk__time_format_hr(const char *format, const crm_time_t *dt, int usec) { int scanned_pos = 0; // How many characters of format have been parsed int printed_pos = 0; // How many characters of format have been processed @@ -2099,8 +2012,8 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) buf = g_string_sized_new(128); - ha_get_tm_time(&tm, hr_dt); - gdt = get_g_date_time(&tm, hr_dt->offset); + ha_get_tm_time(&tm, dt); + gdt = get_g_date_time(&tm, dt->offset); if (gdt == NULL) { goto done; } @@ -2225,7 +2138,7 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt) * our microseconds value by 10^0 == 1, which is powers[6 - 1]. */ g_string_append_printf(buf, "%0*d", frac_digits, - hr_dt->useconds / powers[frac_digits - 1]); + usec / powers[frac_digits - 1]); } } diff --git a/lib/common/tests/iso8601/pcmk__time_format_hr_test.c b/lib/common/tests/iso8601/pcmk__time_format_hr_test.c index 3b9ad79d362..8c27a868119 100644 --- a/lib/common/tests/iso8601/pcmk__time_format_hr_test.c +++ b/lib/common/tests/iso8601/pcmk__time_format_hr_test.c @@ -14,16 +14,17 @@ #include #include -#define DATE_S "2024-06-02" +#define YEAR_S "2024" +#define MONTH_S "06" +#define DAY_S "02" +#define DATE_S YEAR_S "-" MONTH_S "-" DAY_S #define HOUR_S "03" #define MINUTE_S "04" #define SECOND_S "05" #define TIME_S HOUR_S ":" MINUTE_S ":" SECOND_S -#define OFFSET_S "+00:00" - -#define TEST_TIME pcmk__time_hr_new(DATE_S " " TIME_S " " OFFSET_S) +#define DATE_TIME_S DATE_S " " TIME_S /*! * \internal @@ -42,12 +43,12 @@ static void assert_hr_format(const char *format, const char *expected, const char *alternate, int usec) { - pcmk__time_hr_t *hr = TEST_TIME; + crm_time_t *dt = crm_time_new(DATE_TIME_S); char *result = NULL; - hr->useconds = usec; - result = pcmk__time_format_hr(format, hr); - pcmk__time_hr_free(hr); + assert_non_null(dt); + + result = pcmk__time_format_hr(format, dt, usec); if (expected == NULL) { assert_null(result); @@ -69,8 +70,10 @@ assert_hr_format(const char *format, const char *expected, static void null_format(void **state) { - assert_null(pcmk__time_format_hr(NULL, NULL)); - assert_hr_format(NULL, NULL, NULL, 0); // for pcmk__time_format_hr(NULL, hr) + assert_null(pcmk__time_format_hr(NULL, NULL, 0)); + + // For pcmk__time_format_hr(NULL, dt, 0) + assert_hr_format(NULL, NULL, NULL, 0); } static void diff --git a/lib/lrmd/lrmd_alerts.c b/lib/lrmd/lrmd_alerts.c index 79798425445..392e099189f 100644 --- a/lib/lrmd/lrmd_alerts.c +++ b/lib/lrmd/lrmd_alerts.c @@ -123,21 +123,27 @@ exec_alert_list(lrmd_t *lrmd, const GList *alert_list, enum pcmk__alert_flags kind, const char *attr_name, lrmd_key_value_t *params) { - bool any_success = FALSE, any_failure = FALSE; + bool any_success = false; + bool any_failure = false; const char *kind_s = pcmk__alert_flag2text(kind); - pcmk__time_hr_t *now = NULL; - time_t epoch = 0; + + struct timespec now_tv = { 0, }; + crm_time_t now_dt = { 0, }; + int now_usec = 0; + + qb_util_timespec_from_epoch_get(&now_tv); + crm_time_set_timet(&now_dt, &(now_tv.tv_sec)); + now_usec = now_tv.tv_nsec / QB_TIME_NS_IN_USEC; params = alert_key2param(params, PCMK__alert_key_kind, kind_s); params = alert_key2param(params, PCMK__alert_key_version, PACEMAKER_VERSION); - for (const GList *iter = alert_list; - iter != NULL; iter = g_list_next(iter)) { - const pcmk__alert_t *entry = (pcmk__alert_t *) (iter->data); + for (const GList *iter = alert_list; iter != NULL; iter = iter->next) { + const pcmk__alert_t *entry = iter->data; lrmd_key_value_t *copy_params = NULL; - lrmd_key_value_t *head = NULL; - int rc; + char *str = NULL; + int rc = pcmk_ok; if (!pcmk__is_set(entry->flags, kind)) { crm_trace("Filtering unwanted %s alert to %s via %s", @@ -153,43 +159,37 @@ exec_alert_list(lrmd_t *lrmd, const GList *alert_list, continue; } - if (now == NULL) { - now = pcmk__time_hr_now(&epoch); - } crm_info("Sending %s alert via %s to %s", kind_s, entry->id, entry->recipient); /* Make a copy of the parameters, because each alert will be unique */ - for (head = params; head != NULL; head = head->next) { - copy_params = lrmd_key_value_add(copy_params, head->key, head->value); + for (const lrmd_key_value_t *param = params; param != NULL; + param = param->next) { + + copy_params = lrmd_key_value_add(copy_params, param->key, + param->value); } copy_params = alert_key2param(copy_params, PCMK__alert_key_recipient, entry->recipient); - if (now) { - char *timestamp = pcmk__time_format_hr(entry->tstamp_format, now); - char *timestamp_epoch = pcmk__assert_asprintf("%lld", - (long long) epoch); - char *timestamp_usec = pcmk__assert_asprintf("%06d", now->useconds); - - if (timestamp) { - copy_params = alert_key2param(copy_params, - PCMK__alert_key_timestamp, - timestamp); - free(timestamp); - } - + str = pcmk__time_format_hr(entry->tstamp_format, &now_dt, now_usec); + if (str != NULL) { copy_params = alert_key2param(copy_params, - PCMK__alert_key_timestamp_epoch, - timestamp_epoch); - copy_params = alert_key2param(copy_params, - PCMK__alert_key_timestamp_usec, - timestamp_usec); - free(timestamp_epoch); - free(timestamp_usec); + PCMK__alert_key_timestamp, str); + free(str); } + str = pcmk__assert_asprintf("%lld", (long long) now_tv.tv_sec); + copy_params = alert_key2param(copy_params, + PCMK__alert_key_timestamp_epoch, str); + free(str); + + str = pcmk__assert_asprintf("%06d", now_usec); + copy_params = alert_key2param(copy_params, + PCMK__alert_key_timestamp_usec, str); + free(str); + copy_params = alert_envvar2params(copy_params, entry); rc = lrmd->cmds->exec_alert(lrmd, entry->id, entry->path, @@ -197,16 +197,12 @@ exec_alert_list(lrmd_t *lrmd, const GList *alert_list, if (rc < 0) { crm_err("Could not execute alert %s: %s " QB_XS " rc=%d", entry->id, pcmk_strerror(rc), rc); - any_failure = TRUE; + any_failure = true; } else { - any_success = TRUE; + any_success = true; } } - if (now) { - free(now); - } - if (any_failure) { return (any_success? -1 : -2); }