Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 39 additions & 22 deletions libenv/time_classes.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <time_classes.h>
#include <eval_context.h>
#include <alloc.h> // xvasprintf

static void RemoveTimeClass(EvalContext *ctx, const char* tags)
{
Expand All @@ -44,17 +45,34 @@ static void RemoveTimeClass(EvalContext *ctx, const char* tags)
RlistDestroy(tags_rlist);
}

StringSet *GetTimeClasses(time_t time) {
static void InsertTimeClassF(StringMap *classes, const char *prefix, const char *type, const char *fmt, ...)
{
char *key;
xasprintf(&key, "time_based_%s%s", prefix, type);

va_list args;
va_start(args, fmt);

char *value;
NDEBUG_UNUSED int ret = xvasprintf(&value, fmt, args);
assert(ret >= 0);

va_end(args);
StringMapInsert(classes, key, value);
}

StringMap *GetTimeClasses(time_t time) {
// The first element is the local timezone
const char* tz_prefix[2] = { "", "GMT_" };
const char* tz_var_prefix[2] = { "", "gmt_" };
const char* tz_function[2] = { "localtime_r", "gmtime_r" };
struct tm tz_parsed_time[2];
const struct tm* tz_tm[2] = {
localtime_r(&time, &(tz_parsed_time[0])),
gmtime_r(&time, &(tz_parsed_time[1]))
};

StringSet *classes = StringSetNew();
StringMap *classes = StringMapNew();

for (int tz = 0; tz < 2; tz++)
{
Expand All @@ -67,16 +85,14 @@ StringSet *GetTimeClasses(time_t time) {
}

/* Lifecycle */

StringSetAddF(classes, "%sLcycle_%d", tz_prefix[tz], ((tz_parsed_time[tz].tm_year + 1900) % 3));
InsertTimeClassF(classes, tz_var_prefix[tz], "lcycle", "%sLcycle_%d", tz_prefix[tz], ((tz_parsed_time[tz].tm_year + 1900) % 3));

/* Year */

StringSetAddF(classes, "%sYr%04d", tz_prefix[tz], tz_parsed_time[tz].tm_year + 1900);
InsertTimeClassF(classes, tz_var_prefix[tz], "yr", "%sYr%04d", tz_prefix[tz], tz_parsed_time[tz].tm_year + 1900);

/* Month */

StringSetAddF(classes, "%s%s", tz_prefix[tz], MONTH_TEXT[tz_parsed_time[tz].tm_mon]);
InsertTimeClassF(classes, tz_var_prefix[tz], "month", "%s%s", tz_prefix[tz], MONTH_TEXT[tz_parsed_time[tz].tm_mon]);

/* Day of week */

Expand All @@ -85,55 +101,56 @@ StringSet *GetTimeClasses(time_t time) {
...
Sunday is 0 in tm_wday, 6 in DAY_TEXT */
day_text_index = (tz_parsed_time[tz].tm_wday + 6) % 7;
StringSetAddF(classes, "%s%s", tz_prefix[tz], DAY_TEXT[day_text_index]);
InsertTimeClassF(classes, tz_var_prefix[tz], "dow", "%s%s", tz_prefix[tz], DAY_TEXT[day_text_index]);

/* Day */

StringSetAddF(classes, "%sDay%d", tz_prefix[tz], tz_parsed_time[tz].tm_mday);
InsertTimeClassF(classes, tz_var_prefix[tz], "dom", "%sDay%d", tz_prefix[tz], tz_parsed_time[tz].tm_mday);

/* Shift */

StringSetAddF(classes, "%s%s", tz_prefix[tz], SHIFT_TEXT[tz_parsed_time[tz].tm_hour / 6]);
InsertTimeClassF(classes, tz_var_prefix[tz], "pod", "%s%s", tz_prefix[tz], SHIFT_TEXT[tz_parsed_time[tz].tm_hour / 6]);

/* Hour */

StringSetAddF(classes, "%sHr%02d", tz_prefix[tz], tz_parsed_time[tz].tm_hour);
StringSetAddF(classes, "%sHr%d", tz_prefix[tz], tz_parsed_time[tz].tm_hour);
InsertTimeClassF(classes, tz_var_prefix[tz], "hr", "%sHr%02d", tz_prefix[tz], tz_parsed_time[tz].tm_hour);
InsertTimeClassF(classes, tz_var_prefix[tz], "hr_2", "%sHr%d", tz_prefix[tz], tz_parsed_time[tz].tm_hour);

/* Quarter */

quarter = tz_parsed_time[tz].tm_min / 15 + 1;

StringSetAddF(classes, "%sQ%d", tz_prefix[tz], quarter);
StringSetAddF(classes, "%sHr%02d_Q%d", tz_prefix[tz], tz_parsed_time[tz].tm_hour, quarter);
InsertTimeClassF(classes, tz_var_prefix[tz], "qoh", "%sQ%d", tz_prefix[tz], quarter);
InsertTimeClassF(classes, tz_var_prefix[tz], "hr_qoh", "%sHr%02d_Q%d", tz_prefix[tz], tz_parsed_time[tz].tm_hour, quarter);

/* Minute */

StringSetAddF(classes, "%sMin%02d", tz_prefix[tz], tz_parsed_time[tz].tm_min);
InsertTimeClassF(classes, tz_var_prefix[tz], "min", "%sMin%02d", tz_prefix[tz], tz_parsed_time[tz].tm_min);

interval_start = (tz_parsed_time[tz].tm_min / 5) * 5;
interval_end = (interval_start + 5) % 60;

StringSetAddF(classes, "%sMin%02d_%02d", tz_prefix[tz], interval_start, interval_end);
InsertTimeClassF(classes, tz_var_prefix[tz], "min_span_5", "%sMin%02d_%02d", tz_prefix[tz], interval_start, interval_end);
}

return classes;
}

static void AddTimeClass(EvalContext *ctx, time_t time, const char* tags)
{
StringSet *time_classes = GetTimeClasses(time);
StringMap *time_classes = GetTimeClasses(time);
if (time_classes == NULL) {
return;
}

StringSetIterator iter = StringSetIteratorInit(time_classes);
const char *time_class = NULL;
while ((time_class = StringSetIteratorNext(&iter)) != NULL) {
EvalContextClassPutHard(ctx, time_class, tags);
StringMapIterator iter = StringMapIteratorInit(time_classes);
MapKeyValue *item;
while ((item = StringMapIteratorNext(&iter)) != NULL) {
EvalContextClassPutHard(ctx, item->value, tags);
EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, item->key, item->value, CF_DATA_TYPE_STRING, "noreport");
}

StringSetDestroy(time_classes);
StringMapDestroy(time_classes);
}

void UpdateTimeClasses(EvalContext *ctx, time_t t)
Expand Down
2 changes: 1 addition & 1 deletion libenv/time_classes.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* 'GMT_Yr2024', 'Hr15', 'Hr15_Q3', 'Lcycle_2', 'May', 'Min30_35',
* 'Min33', 'Q3', 'Wednesday', 'Yr2024' }
*/
StringSet *GetTimeClasses(time_t time);
StringMap *GetTimeClasses(time_t time);

void UpdateTimeClasses(EvalContext *ctx, time_t t);

Expand Down
37 changes: 37 additions & 0 deletions tests/acceptance/01_vars/01_basic/time_based_vars.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
body common control
{
bundlesequence => { "test" };
}


bundle agent test
{
vars:
"time_based_var_names" slist => variablesmatching(".*sys.time_based.*");
"time_based_vars" slist => maplist("$($(this))", "@(time_based_var_names)");

classes:
"year_ok" expression => reglist("@(time_based_vars)", "^Yr\d{4}$");
"month_ok" expression => reglist("@(time_based_vars)", "^(January|February|March|April|May|June|July|August|September|October|November|December)$");
"day_ok" expression => reglist("@(time_based_vars)", "^Day([1-9]|[1-2][0-9]|3[0-1])$");
"weekday_ok" expression => reglist("@(time_based_vars)", "^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)$");
"shift_ok" expression => reglist("@(time_based_vars)", "^(Night|Morning|Afternoon|Evening)$");
"hour_2_ok" expression => reglist("@(time_based_vars)", "^Hr(0[1-9]|1[0-9]|2[0-4])$");
"hour_ok" expression => reglist("@(time_based_vars)", "^Hr([1-9]|1[0-9]|2[0-4])$");
"quarter_ok" expression => reglist("@(time_based_vars)", "^Q[1-4]$");
"minute_ok" expression => reglist("@(time_based_vars)", "^Min([0-5][0-9]|60)$");
"minute_span_5_ok" expression => reglist("@(time_based_vars)", "^Min([0-5][0-9]|60)_(05|[0-5][05]|60)$");
"hour_2_quarter_ok" expression => reglist("@(time_based_vars)", "^Hr(0[1-9]|1[0-9]|2[0-4])_Q[1-4]$");

# Testing for one GMT
"hour_2_gmt_ok" expression => reglist("@(time_based_vars)", "^GMT_Hr(0[1-9]|1[0-9]|2[0-4])$");

"ok" expression => and("year_ok", "month_ok", "day_ok", "weekday_ok", "shift_ok", "hour_2_ok", "hour_ok",
"quarter_ok", "minute_ok", "minute_span_5_ok", "hour_2_quarter_ok", "hour_2_gmt_ok");

reports:
ok::
"$(this.promise_filename) Pass";
!ok::
"$(this.promise_filename) FAIL";
}
14 changes: 9 additions & 5 deletions tests/unit/eval_context_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,14 @@ void test_eval_with_token_from_list(void)
* 'GMT_Yr2024'
*/
const time_t timestamp = 1716989621;
StringSet *time_classes = GetTimeClasses(timestamp);
StringSetIterator iter = StringSetIteratorInit(time_classes);
const char *time_class = NULL;
while((time_class = StringSetIteratorNext(&iter)) != NULL) {
printf("Time class: %s\n", time_class);
StringSet *time_classes = StringSetNew();

StringMap *time_classes_map = GetTimeClasses(timestamp);
StringMapIterator iter = StringMapIteratorInit(time_classes_map);
MapKeyValue *item;
while((item = StringMapIteratorNext(&iter)) != NULL) {
printf("Time class: %s\n", item->value);
StringSetAdd(time_classes, SafeStringDuplicate(item->value));
}

assert_true(EvalWithTokenFromList("GMT_Wednesday", time_classes));
Expand All @@ -146,6 +149,7 @@ void test_eval_with_token_from_list(void)
assert_true(EvalWithTokenFromList("GMT_Monday|GMT_Wednesday", time_classes));
assert_true(EvalWithTokenFromList("!GMT_Monday", time_classes));

StringMapDestroy(time_classes_map);
StringSetDestroy(time_classes);
}

Expand Down
Loading