Skip to content

Commit a7dac04

Browse files
authored
Merge pull request #5799 from jakub-nt/CFE-2815
CFE-2815: Added `isnewerthantime` function
2 parents b55787e + e497257 commit a7dac04

File tree

5 files changed

+131
-3
lines changed

5 files changed

+131
-3
lines changed

examples/isnewerthan.cf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ bundle agent example
3737
{
3838
classes:
3939

40-
"do_it" and => { isnewerthan("/tmp/later","/tmp/earlier"), "cfengine" };
40+
"do_it" expression => isnewerthan("/tmp/later","/tmp/earlier");
4141

4242
reports:
4343

examples/isnewerthantime.cf

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#+begin_src prep
2+
#@ ```
3+
#@ touch -t '200102031234.56' /tmp/file_a
4+
#@ touch -t '200202031234.56' /tmp/file_b
5+
#@ ```
6+
#+end_src
7+
###############################################################################
8+
#+begin_src cfengine3
9+
body common control
10+
{
11+
bundlesequence => { "example" };
12+
}
13+
14+
bundle agent example
15+
{
16+
classes:
17+
"is_file_a_new" expression => isnewerthantime("/tmp/file_a", 1000000000);
18+
"is_file_b_new" expression => isnewerthantime("/tmp/file_b", 1000000000);
19+
20+
reports:
21+
!is_file_a_new::
22+
"/tmp/file_a is not newer than 2001-09-09 01:46:40";
23+
is_file_b_new::
24+
"/tmp/file_b is newer than 2001-09-09 01:46:40";
25+
}
26+
#+end_src
27+
###############################################################################
28+
#+begin_src example_output
29+
#@ ```
30+
#@ R: /tmp/file_a is not newer than 2001-09-09 01:46:40
31+
#@ R: /tmp/file_b is newer than 2001-09-09 01:46:40
32+
#@ ```
33+
#+end_src

libpromises/evalfunction.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,7 +3316,7 @@ static FnCallResult FnCallIsConnectable(ARG_UNUSED EvalContext *ctx,
33163316
}
33173317

33183318
return FnReturnContext(sd > -1);
3319-
}
3319+
}
33203320

33213321
/*********************************************************************/
33223322

@@ -4744,6 +4744,28 @@ static FnCallResult FnCallIsNewerThan(ARG_UNUSED EvalContext *ctx, ARG_UNUSED co
47444744
return FnReturnContext(frombuf.st_mtime > tobuf.st_mtime);
47454745
}
47464746

4747+
static FnCallResult FnCallIsNewerThanTime(ARG_UNUSED EvalContext *ctx, ARG_UNUSED const Policy *policy, ARG_UNUSED const FnCall *fp, const Rlist *finalargs)
4748+
{
4749+
assert(finalargs != NULL);
4750+
4751+
const char *arg_file = RlistScalarValue(finalargs);
4752+
// a comment in `FnCallStrftime`: "this will be a problem on 32-bit systems..."
4753+
const time_t arg_mtime = IntFromString(RlistScalarValue(finalargs->next));
4754+
4755+
struct stat file_buf;
4756+
int exit_code = stat(arg_file, &file_buf);
4757+
if (exit_code == -1)
4758+
{
4759+
return FnFailure();
4760+
}
4761+
4762+
time_t file_mtime = file_buf.st_mtime;
4763+
4764+
bool result = file_mtime > arg_mtime;
4765+
4766+
return FnReturnContext(result);
4767+
}
4768+
47474769
/*********************************************************************/
47484770

47494771
static FnCallResult FnCallIsAccessedBefore(ARG_UNUSED EvalContext *ctx, ARG_UNUSED const Policy *policy, ARG_UNUSED const FnCall *fp, const Rlist *finalargs)
@@ -7597,7 +7619,7 @@ static FnCallResult ValidateDataGeneric(const char *const fname,
75977619
{
75987620
isvalid = isvalid && JsonGetElementType(json) != JSON_ELEMENT_TYPE_PRIMITIVE;
75997621
}
7600-
7622+
76017623
FnCallResult ret = FnReturnContext(isvalid);
76027624
JsonDestroy(json);
76037625
return ret;
@@ -10102,6 +10124,13 @@ static const FnCallArg ISNEWERTHAN_ARGS[] =
1010210124
{NULL, CF_DATA_TYPE_NONE, NULL}
1010310125
};
1010410126

10127+
static const FnCallArg FILE_TIME_ARGS[] =
10128+
{
10129+
{CF_ABSPATHRANGE, CF_DATA_TYPE_STRING, "File name"},
10130+
{CF_VALRANGE, CF_DATA_TYPE_INT, "Time as a Unix epoch offset"},
10131+
{NULL, CF_DATA_TYPE_NONE, NULL}
10132+
};
10133+
1010510134
static const FnCallArg ISVARIABLE_ARGS[] =
1010610135
{
1010710136
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "Variable identifier"},
@@ -10945,6 +10974,8 @@ const FnCallType CF_FNCALL_TYPES[] =
1094510974
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
1094610975
FnCallTypeNew("isnewerthan", CF_DATA_TYPE_CONTEXT, ISNEWERTHAN_ARGS, &FnCallIsNewerThan, "True if arg1 is newer (modified later) than arg2 (mtime)",
1094710976
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
10977+
FnCallTypeNew("isnewerthantime", CF_DATA_TYPE_CONTEXT, FILE_TIME_ARGS, &FnCallIsNewerThanTime, "True if arg1 is newer (modified later) (has larger mtime) than time arg2",
10978+
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
1094810979
FnCallTypeNew("isplain", CF_DATA_TYPE_CONTEXT, FILESTAT_ARGS, &FnCallFileStat, "True if the named object is a plain/regular file",
1094910980
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
1095010981
FnCallTypeNew("isvariable", CF_DATA_TYPE_CONTEXT, ISVARIABLE_ARGS, &FnCallIsVariable, "True if the named variable is defined",
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
body common control
2+
{
3+
inputs => { "../../default.cf.sub" };
4+
bundlesequence => { default("$(this.promise_filename)") };
5+
version => "1.0";
6+
}
7+
#######################################################
8+
9+
bundle agent check
10+
{
11+
vars:
12+
"test_file"
13+
string => "$(this.promise_dirname)/isnewerthantime_file.txt";
14+
"nonexistent_file"
15+
string => "$(this.promise_dirname)/thisfiledoesntexist_1747148781.txt";
16+
"test_file_mtime"
17+
string => filestat("$(test_file)", mtime);
18+
"test_file_time_just_before_mtime"
19+
int => int(eval("$(test_file_mtime) - 1"));
20+
"minus_one"
21+
int => "-1";
22+
classes:
23+
"ok1"
24+
expression => isnewerthantime("$(test_file)", 0);
25+
"ok2"
26+
expression => isnewerthantime("$(nonexistent_file)", 0);
27+
"ok3"
28+
expression => isnewerthantime("$(test_file)", "$(test_file_time_just_before_mtime)");
29+
"ok4"
30+
expression => isnewerthantime("$(test_file)", "$(test_file_mtime)");
31+
"ok5"
32+
expression => isnewerthantime("$(nonexistent_file)", "$(test_file_mtime)");
33+
"ok6"
34+
# as "inf" corresponds to 999999999, this is actually not False
35+
expression => isnewerthantime("$(test_file)", "inf");
36+
"ok7"
37+
expression => isnewerthantime("$(nonexistent_file)", "inf");
38+
"ok8"
39+
expression => isnewerthantime("$(test_file)", "$(minus_one)");
40+
"ok"
41+
expression => and("ok1", "!ok2", "ok3", "!ok4", "!ok5", "ok6", "!ok7", "ok8");
42+
43+
reports:
44+
DEBUG.ok1::
45+
"1. pass";
46+
DEBUG.!ok2::
47+
"2. pass";
48+
DEBUG.ok3::
49+
"3. pass: $(test_file_time_just_before_mtime)";
50+
DEBUG.!ok4::
51+
"4. pass: $(test_file_mtime)";
52+
DEBUG.!ok5::
53+
"5. pass";
54+
DEBUG.ok6::
55+
"6. pass";
56+
DEBUG.!ok7::
57+
"7. pass";
58+
DEBUG.ok8::
59+
"8. pass";
60+
ok::
61+
"$(this.promise_filename) Pass";
62+
!ok::
63+
"$(this.promise_filename) FAIL";
64+
}

tests/acceptance/02_classes/02_functions/isnewerthantime_file.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)