Skip to content

Commit b4bbcc1

Browse files
committed
Added policy function classfilterdata()
Ticket: ENT-6193, CFE-3421 Changelog: Title Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>
1 parent b8e4c70 commit b4bbcc1

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

libpromises/evalfunction.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
included file COSL.txt.
2323
*/
2424

25+
#include <limits.h>
2526
#include <platform.h>
2627
#include <evalfunction.h>
2728

@@ -35,6 +36,8 @@
3536
#include <files_names.h>
3637
#include <files_interfaces.h>
3738
#include <hash.h>
39+
#include <stddef.h>
40+
#include <stdint.h>
3841
#include <vars.h>
3942
#include <addr_lib.h>
4043
#include <syntax.h>
@@ -7777,6 +7780,190 @@ static int JsonPrimitiveComparator(JsonElement const *left_obj,
77777780
return StringSafeCompare(left, right);
77787781
}
77797782

7783+
static bool ClassFilterDataArrayOfArrays(
7784+
EvalContext *ctx,
7785+
const char *fn_name,
7786+
JsonElement *json_array,
7787+
const char *class_expr_index,
7788+
bool *remove)
7789+
{
7790+
errno = 0; /* to detect error */
7791+
assert(SIZE_MAX >= ULONG_MAX); /* make sure returned value can fit in size_t */
7792+
char *endptr;
7793+
size_t index = strtoul(class_expr_index, &endptr, 10);
7794+
if (!StringEqual(endptr, "")) /* check that the whole string was consumed */
7795+
{
7796+
Log(LOG_LEVEL_VERBOSE,
7797+
"Function %s(): Bad class expression index '%s': Not a valid integer",
7798+
fn_name, class_expr_index);
7799+
return false;
7800+
}
7801+
if (errno != 0)
7802+
{
7803+
Log(LOG_LEVEL_VERBOSE,
7804+
"Function %s(): Bad class expression index '%s': %s",
7805+
fn_name, class_expr_index, GetErrorStr());
7806+
return false;
7807+
}
7808+
7809+
size_t length = JsonLength(json_array);
7810+
if (index >= length)
7811+
{
7812+
Log(LOG_LEVEL_VERBOSE,
7813+
"Function %s(): Bad class expression index '%s': Index out of bounds (%zu >= %zu)",
7814+
fn_name, class_expr_index, index, length);
7815+
return false;
7816+
}
7817+
7818+
JsonElement *json_child = JsonArrayGet(json_array, index);
7819+
if (JsonGetType(json_child) != JSON_TYPE_STRING)
7820+
{
7821+
Log(LOG_LEVEL_VERBOSE,
7822+
"Function %s(): Bad class expression at index '%zu': Expected type string",
7823+
fn_name, index);
7824+
return false;
7825+
}
7826+
7827+
const char *class_expr = JsonPrimitiveGetAsString(json_child);
7828+
assert(class_expr != NULL);
7829+
7830+
*remove = !IsDefinedClass(ctx, class_expr);
7831+
return true;
7832+
}
7833+
7834+
static bool ClassFilterDataArrayOfObjects(
7835+
EvalContext *ctx,
7836+
const char *fn_name,
7837+
JsonElement *json_object,
7838+
const char *class_expr_key,
7839+
bool *remove)
7840+
{
7841+
JsonElement *json_child = JsonObjectGet(json_object, class_expr_key);
7842+
if (json_child == NULL)
7843+
{
7844+
Log(LOG_LEVEL_VERBOSE,
7845+
"Function %s(): Bad class expression key '%s': Key not found",
7846+
fn_name, class_expr_key);
7847+
return false;
7848+
}
7849+
7850+
if (JsonGetType(json_child) != JSON_TYPE_STRING)
7851+
{
7852+
Log(LOG_LEVEL_VERBOSE,
7853+
"Function %s(): Bad class expression at key '%s': Expected type string",
7854+
fn_name, class_expr_key);
7855+
return false;
7856+
}
7857+
7858+
const char *class_expr = JsonPrimitiveGetAsString(json_child);
7859+
assert(class_expr != NULL);
7860+
7861+
*remove = !IsDefinedClass(ctx, class_expr);
7862+
return true;
7863+
}
7864+
7865+
static bool ClassFilterDataArray(
7866+
EvalContext *ctx,
7867+
const char *fn_name,
7868+
const char *data_structure,
7869+
const char *key_or_index,
7870+
JsonElement *child,
7871+
bool *remove)
7872+
{
7873+
switch (JsonGetType(child))
7874+
{
7875+
case JSON_TYPE_ARRAY:
7876+
if (StringEqual(data_structure, "auto") ||
7877+
StringEqual(data_structure, "array_of_arrays"))
7878+
{
7879+
return ClassFilterDataArrayOfArrays(
7880+
ctx, fn_name, child, key_or_index, remove);
7881+
}
7882+
Log(LOG_LEVEL_VERBOSE,
7883+
"Function %s(): Expected child element to be of container type array",
7884+
fn_name);
7885+
break;
7886+
7887+
case JSON_TYPE_OBJECT:
7888+
if (StringEqual(data_structure, "auto") ||
7889+
StringEqual(data_structure, "array_of_objects"))
7890+
{
7891+
return ClassFilterDataArrayOfObjects(
7892+
ctx, fn_name, child, key_or_index, remove);
7893+
}
7894+
Log(LOG_LEVEL_VERBOSE,
7895+
"Function %s(): Expected child element to be of container type object",
7896+
fn_name);
7897+
break;
7898+
7899+
default:
7900+
Log(LOG_LEVEL_VERBOSE,
7901+
"Function %s(): Expected child element to be of container type",
7902+
fn_name);
7903+
break;
7904+
}
7905+
7906+
return false;
7907+
}
7908+
7909+
static FnCallResult FnCallClassFilterData(
7910+
EvalContext *ctx,
7911+
ARG_UNUSED Policy const *policy,
7912+
FnCall const *fp,
7913+
Rlist const *args)
7914+
{
7915+
assert(ctx != NULL);
7916+
assert(fp != NULL);
7917+
assert(args != NULL);
7918+
assert(args->next != NULL);
7919+
assert(args->next->next != NULL);
7920+
7921+
bool allocated = false;
7922+
JsonElement *parent = VarNameOrInlineToJson(ctx, fp, args, false, &allocated);
7923+
if (parent == NULL)
7924+
{
7925+
Log(LOG_LEVEL_VERBOSE,
7926+
"Function %s(): Expected parent element to be of container type array",
7927+
fp->name);
7928+
return FnFailure();
7929+
}
7930+
7931+
/* Currently only parent type array is supported */
7932+
if (JsonGetType(parent) != JSON_TYPE_ARRAY)
7933+
{
7934+
JsonDestroyMaybe(parent, allocated);
7935+
return FnFailure();
7936+
}
7937+
assert(allocated); /* Non-primitives are always allocated */
7938+
7939+
const char *data_structure = RlistScalarValue(args->next);
7940+
const char *key_or_index = RlistScalarValue(args->next->next);
7941+
7942+
/* Iterate through array backwards so we can avoid having to compute index
7943+
* offsets for each removed element */
7944+
for (size_t i = JsonLength(parent); i > 0; i--)
7945+
{
7946+
size_t index = i - 1;
7947+
JsonElement *child = JsonArrayGet(parent, index);
7948+
assert(child != NULL);
7949+
7950+
bool remove;
7951+
if (!ClassFilterDataArray(ctx, fp->name, data_structure, key_or_index, child, &remove))
7952+
{
7953+
/* Error is already logged */
7954+
JsonDestroy(parent);
7955+
return FnFailure();
7956+
}
7957+
7958+
if (remove)
7959+
{
7960+
JsonArrayRemoveRange(parent, index, index);
7961+
}
7962+
}
7963+
7964+
return FnReturnContainerNoCopy(parent);
7965+
}
7966+
77807967
static FnCallResult FnCallClassFilterCsv(EvalContext *ctx,
77817968
ARG_UNUSED Policy const *policy,
77827969
FnCall const *fp,
@@ -10371,6 +10558,14 @@ static const FnCallArg VALIDJSON_ARGS[] =
1037110558
{NULL, CF_DATA_TYPE_NONE, NULL}
1037210559
};
1037310560

10561+
static const FnCallArg CLASSFILTERDATA_ARGS[] =
10562+
{
10563+
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "CFEngine variable identifier or inline JSON"},
10564+
{"array_of_arrays,array_of_objects,auto", CF_DATA_TYPE_OPTION, "Specify type of data structure"},
10565+
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "Key or index of class expressions"},
10566+
{NULL, CF_DATA_TYPE_NONE, NULL}
10567+
};
10568+
1037410569
static const FnCallArg CLASSFILTERCSV_ARGS[] =
1037510570
{
1037610571
{CF_ABSPATHRANGE, CF_DATA_TYPE_STRING, "File name"},
@@ -10907,6 +11102,8 @@ const FnCallType CF_FNCALL_TYPES[] =
1090711102
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_UTILS, SYNTAX_STATUS_NORMAL),
1090811103
FnCallTypeNew("classesmatching", CF_DATA_TYPE_STRING_LIST, CLASSMATCH_ARGS, &FnCallClassesMatching, "List the defined classes matching regex arg1 and tag regexes arg2,arg3,...",
1090911104
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_UTILS, SYNTAX_STATUS_NORMAL),
11105+
FnCallTypeNew("classfilterdata", CF_DATA_TYPE_CONTAINER, CLASSFILTERDATA_ARGS, &FnCallClassFilterData, "Filter data container by defined classes",
11106+
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_IO, SYNTAX_STATUS_NORMAL),
1091011107
FnCallTypeNew("classfiltercsv", CF_DATA_TYPE_CONTAINER, CLASSFILTERCSV_ARGS, &FnCallClassFilterCsv, "Parse a CSV file and create data container filtered by defined classes",
1091111108
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_IO, SYNTAX_STATUS_NORMAL),
1091211109
FnCallTypeNew("countclassesmatching", CF_DATA_TYPE_INT, CLASSMATCH_ARGS, &FnCallClassesMatching, "Count the number of defined classes matching regex arg1",

0 commit comments

Comments
 (0)