Skip to content
Merged
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
85 changes: 85 additions & 0 deletions libpromises/evalfunction.c
Original file line number Diff line number Diff line change
Expand Up @@ -7597,6 +7597,83 @@ static FnCallResult FnCallStrftime(ARG_UNUSED EvalContext *ctx,

/*********************************************************************/

static int ParseDate(const char *input_string, time_t *out)
{
char date_path[PATH_MAX];
strncpy(date_path, GetBinDir(), sizeof(date_path) - 1);
JoinPaths(date_path, sizeof(date_path), "date");

char buffer[CF_BUFSIZE];
int n = snprintf(buffer, sizeof(buffer), "%s --date '%s' +%%s", date_path, input_string);

if (n < 0 || (size_t) n >= sizeof(buffer)) {
Log(LOG_LEVEL_ERR, "Truncation error: input string '%.10s...' is too long (%d >= %zu)",
input_string, n, sizeof(buffer));
return -1;
}

FILE *fd = cf_popen(buffer, "r", true);
if (fd == NULL)
{
Log(LOG_LEVEL_ERR, "Couldn't run command: '%s'", buffer);
return -1;
}

size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1 , fd);
buffer[bytes_read] = '\0';

if (bytes_read == 0)
{
if (ferror(fd))
{
Log(LOG_LEVEL_ERR, "Error reading output for '%s'", input_string);
}
else if (feof(fd))
{
Log(LOG_LEVEL_DEBUG, "No output read for '%s'", input_string);
}
fclose(fd);
return -1;
}
fclose(fd);

long time_value;
int ret = StringToLong(buffer, &time_value);
if (ret != 0)
{
LogStringToLongError(buffer, "ParseDate", ret);
return -1;
}

*out = (time_t) time_value;
if ((long) *out != time_value)
{
Log(LOG_LEVEL_ERR, "Date value '%ld' does not fit in time_t", time_value);
return -1;
}

return 0;
}

static FnCallResult FnCallStrToTime(ARG_UNUSED EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
{
assert(fp != NULL);

const char *input_string = RlistScalarValue(finalargs);
time_t result;
int ret = ParseDate(input_string, &result);

if (ret != 0)
{
Log(LOG_LEVEL_ERR, "'%s': Invalid date '%s'", fp->name, input_string);
return FnFailure();
}

return FnReturnF("%ld", result);
}

/*********************************************************************/

static FnCallResult FnCallEval(EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
{
if (finalargs == NULL)
Expand Down Expand Up @@ -11427,6 +11504,12 @@ static const FnCallArg STRFTIME_ARGS[] =
{NULL, CF_DATA_TYPE_NONE, NULL}
};

static const FnCallArg STRTOTIME_ARGS[] =
{
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "String to parse"},
{NULL, CF_DATA_TYPE_NONE, NULL}
};

static const FnCallArg STRING_REPLACE_ARGS[] =
{
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "Source string"},
Expand Down Expand Up @@ -12003,6 +12086,8 @@ const FnCallType CF_FNCALL_TYPES[] =
FNCALL_OPTION_NONE, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, DEFAULT_ARGC),
FnCallTypeNew("strftime", CF_DATA_TYPE_STRING, STRFTIME_ARGS, &FnCallStrftime, "Format a date and time string",
FNCALL_OPTION_NONE, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, DEFAULT_ARGC),
FnCallTypeNew("strtotime", CF_DATA_TYPE_INT, STRTOTIME_ARGS, &FnCallStrToTime, "Parse a timestamp from a string",
FNCALL_OPTION_NONE, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, DEFAULT_ARGC),
FnCallTypeNew("sublist", CF_DATA_TYPE_STRING_LIST, SUBLIST_ARGS, &FnCallSublist, "Returns arg3 element from either the head or the tail (according to arg2) of list or array or data container arg1.",
FNCALL_OPTION_COLLECTING, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL, DEFAULT_ARGC),
FnCallTypeNew("sysctlvalue", CF_DATA_TYPE_STRING, SYSCTLVALUE_ARGS, &FnCallSysctlValue, "Returns a value for sysctl key arg1 pair",
Expand Down
29 changes: 29 additions & 0 deletions tests/acceptance/01_vars/02_functions/strtotime.cf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#######################################################
#
# Test strtotime function
#
#######################################################
body common control
{
inputs => { "../../default.sub.cf" };
bundlesequence => { default("$(this.promise_filename)") };
version => "1.0";
}

#######################################################
bundle agent test
{
vars:
"some_date" int => strtotime("2009-09-21T00:00:00Z");
"epoch" int => int(1253491200);

classes:
"ok" expression => strcmp("$(epoch)", "$(some_date)");

reports:
ok::
"$(this.promise_filename) Pass";

!ok::
"$(this.promise_filename) FAIL";
}
6 changes: 5 additions & 1 deletion tests/acceptance/testall
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ CF_CHECK=${CF_CHECK:-} ; export CF_CHECK
CF_RUNAGENT=${CF_RUNAGENT:-} ; export CF_RUNAGENT
RPMVERCMP=${RPMVERCMP:-} ; export RPMVERCMP
DIFF=${DIFF:-} ; export DIFF
DATE=${DATE:-} ; export DATE
LIBTOOL=${LIBTOOL:-} ; export LIBTOOL
INCLUDE_IN_WORKDIR=${INCLUDE_IN_WORKDIR:-} ; export INCLUDE_IN_WORKDIR

Expand Down Expand Up @@ -443,6 +444,7 @@ runtest() {
$LN_CMD "$RPMVERCMP" "$WORKDIR/bin"
fi
$LN_CMD "$DIFF" "$WORKDIR/bin"
$LN_CMD "$DATE" "$WORKDIR/bin"
fi
for inc in $INCLUDE_IN_WORKDIR
do (
Expand Down Expand Up @@ -957,7 +959,7 @@ else
NEED_RPMVERCMP="no"
fi

if [ -n "$AGENT" -o -n "$CF_PROMISES" -o -n "$CF_SERVERD" -o -n "$CF_EXECD" -o -n "$CF_KEY" -o -n "$CF_SECRET" -o -n "$CF_NET" -o -n "$CF_CHECK" -o -n "$CF_RUNAGENT" -o \( "$NEED_RPMVERCMP" = "yes" -a -n "$RPMVERCMP" \) -o -n "$DIFF" ]
if [ -n "$AGENT" -o -n "$CF_PROMISES" -o -n "$CF_SERVERD" -o -n "$CF_EXECD" -o -n "$CF_KEY" -o -n "$CF_SECRET" -o -n "$CF_NET" -o -n "$CF_CHECK" -o -n "$CF_RUNAGENT" -o \( "$NEED_RPMVERCMP" = "yes" -a -n "$RPMVERCMP" \) -o -n "$DIFF" -o -n "$DATE" ]
then
if [ -n "$BINDIR" ]
then
Expand All @@ -981,6 +983,7 @@ find_default_binary()
[ -x "`pwd`/$2/$2" ] && eval $1=\""`pwd`/$2/$2"\"
[ -n "$BINDIR" -a -x "$BINDIR/$2" ] && eval $1=\""$BINDIR/$2"\"
[ $2 = "diff" ] && eval $1=\""`command -v diff`"\"
[ $2 = "date" -a -z "`eval echo \\$$1`" ] && eval $1=\""`command -v date`"\"
}
find_default_binary DEFAGENT cf-agent
find_default_binary DEFCF_PROMISES cf-promises
Expand All @@ -996,6 +999,7 @@ then
find_default_binary DEFRPMVERCMP rpmvercmp
fi
find_default_binary DIFF diff
find_default_binary DATE date

[ -x "`pwd`/libtool" ] && DEFLIBTOOL="`pwd`/libtool"
[ -x "`pwd`/../../libtool" ] && DEFLIBTOOL="`pwd`/../../libtool"
Expand Down
Loading