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

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

#define TIME_ERROR ((time_t) -1)

static const char *date_paths[] = {
"/usr/bin/date",
"/bin/date",
NULL
};

static time_t ParseDate(const char *input_string)
{
const char *path = NULL;
for (size_t i = 0; date_paths[i] != NULL; i++)
{
const char *tmp = date_paths[i];

if (IsExecutable(tmp))
{
path = tmp;
break;
}
}

if (path == NULL)
{
Log(LOG_LEVEL_ERR, "Unable to find 'date' binary");
return TIME_ERROR;
}

char buffer[MAX_INPUT];
int n = snprintf(buffer, sizeof(buffer), "--date=%s", input_string);

if (n < 0 || (size_t) n >= sizeof(buffer)) {
Log(LOG_LEVEL_DEBUG, "Truncated input string '%s'", input_string);
return TIME_ERROR;
}

const char *argv[] = {path, buffer, "+%s", NULL};
// cf_popen escapes the date, which makes it fail to parse
FILE *fd = cf_popen_exact_args(argv, "r", true);

if (fd == NULL)
{
Log(LOG_LEVEL_DEBUG, "Couldn't run date \"--date='%s' +%%s\"", input_string);
return TIME_ERROR;
}

memset(buffer, 0, sizeof(buffer));

size_t bytes_read = fread(buffer, 1, sizeof(buffer), fd);
fclose(fd);

if (bytes_read == 0)
{
Log(LOG_LEVEL_DEBUG, "No output read for '%s'", buffer);
return TIME_ERROR;
}

long out;
if (StringToLong(buffer, &out) != 0)
{
Log(LOG_LEVEL_DEBUG, "Couldn't parse date: '%s'", buffer);
return TIME_ERROR;
}

return (time_t) out;
}

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

const char *string = RlistScalarValue(finalargs);
time_t result = ParseDate(string);

if (result == TIME_ERROR)
{
Log(LOG_LEVEL_ERR, "'%s': Invalid date '%s'", fp->name, string);
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
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 +11512,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 +12094,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
2 changes: 2 additions & 0 deletions libpromises/pipes.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ FILE *cf_popensetuid(const char *command, const Seq *arglist, const char *type,
FILE *cf_popen_sh(const char *command, const char *type);
FILE *cf_popen_sh_select(const char *command, const char *type, OutputSelect output_select);
FILE *cf_popen_shsetuid(const char *command, const char *type, uid_t uid, gid_t gid, char *chdirv, char *chrootv, int background);
FILE *cf_popen_exact_args_select(const char **argv, const char *type, OutputSelect output_select);
FILE *cf_popen_exact_args(const char **argv, const char *type, bool capture_stderr);
int cf_pclose(FILE *pp);
void cf_pclose_nowait(FILE *pp);
bool PipeToPid(pid_t *pid, FILE *pp);
Expand Down
25 changes: 18 additions & 7 deletions libpromises/pipes_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,18 +371,15 @@ IOData cf_popen_full_duplex(const char *command, bool capture_stderr, bool requi
}
}

FILE *cf_popen_select(const char *command, const char *type, OutputSelect output_select)
FILE *cf_popen_exact_args_select(const char **argv, const char *type, OutputSelect output_select)
{
int pd[2];
pid_t pid;
FILE *pp = NULL;

char **argv = ArgSplitCommand(command, NULL);

pid = CreatePipeAndFork(type, pd);
if (pid == (pid_t) -1)
{
ArgFree(argv);
return NULL;
}

Expand Down Expand Up @@ -427,7 +424,6 @@ FILE *cf_popen_select(const char *command, const char *type, OutputSelect output
if ((pp = fdopen(pd[0], type)) == NULL)
{
cf_pwait(pid);
ArgFree(argv);
return NULL;
}
break;
Expand All @@ -439,20 +435,35 @@ FILE *cf_popen_select(const char *command, const char *type, OutputSelect output
if ((pp = fdopen(pd[1], type)) == NULL)
{
cf_pwait(pid);
ArgFree(argv);
return NULL;
}
}

ChildrenFDSet(fileno(pp), pid);
ArgFree(argv);
return pp;
}

ProgrammingError("Unreachable code");
return NULL;
}

FILE *cf_popen_exact_args(const char **argv, const char *type, bool capture_stderr)
{
return cf_popen_exact_args_select(
argv,
type,
capture_stderr ? OUTPUT_SELECT_BOTH : OUTPUT_SELECT_STDOUT);
}

FILE *cf_popen_select(const char *command, const char *type, OutputSelect output_select)
{
char **argv = ArgSplitCommand(command, NULL);
FILE *ret = cf_popen_exact_args_select(argv, type, output_select);
ArgFree(argv);

return ret;
}

FILE *cf_popen(const char *command, const char *type, bool capture_stderr)
{
return cf_popen_select(
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";
}
Loading