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
95 changes: 91 additions & 4 deletions src/ucs/config/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
/* width of titles in docstring */
#define UCS_CONFIG_PARSER_DOCSTR_WIDTH 10

/* String literal for allow-list */
#define UCS_CONFIG_PARSER_ALL "all"

/* list of prefixes for a configuration variable, used to dump all possible
* aliases.
Expand All @@ -47,6 +49,7 @@ extern char **environ;
UCS_LIST_HEAD(ucs_config_global_list);
static khash_t(ucs_config_env_vars) ucs_config_parser_env_vars = {0};
static pthread_mutex_t ucs_config_parser_env_vars_hash_lock = PTHREAD_MUTEX_INITIALIZER;
static char ucs_config_parser_negate = '^';


const char *ucs_async_mode_names[] = {
Expand Down Expand Up @@ -814,10 +817,14 @@ ucs_status_t ucs_config_clone_array(const void *src, void *dest, const void *arg
ucs_status_t status;
unsigned i;

dest_array->data = ucs_calloc(src_array->count, array->elem_size,
"config array");
if (dest_array->data == NULL) {
return UCS_ERR_NO_MEMORY;
if (src_array->count > 0) {
dest_array->data = ucs_calloc(src_array->count, array->elem_size,
"config array");
if (dest_array->data == NULL) {
return UCS_ERR_NO_MEMORY;
}
} else {
dest_array->data = NULL;
}

dest_array->count = src_array->count;
Expand Down Expand Up @@ -855,6 +862,86 @@ void ucs_config_help_array(char *buf, size_t max, const void *arg)
array->parser.help(buf + strlen(buf), max - strlen(buf), array->parser.arg);
}

int ucs_config_sscanf_allow_list(const char *buf, void *dest, const void *arg)
{
ucs_config_allow_list_t *field = dest;
unsigned offset = 0;

if (buf[0] == ucs_config_parser_negate) {
field->mode = UCS_CONFIG_ALLOW_LIST_NEGATE;
offset++;
} else {
field->mode = UCS_CONFIG_ALLOW_LIST_ALLOW;
}

if (!ucs_config_sscanf_array(&buf[offset], &field->array, arg)) {
return 0;
}

if ((field->array.count >= 1) &&
!strcmp(field->array.names[0], UCS_CONFIG_PARSER_ALL)) {
field->mode = UCS_CONFIG_ALLOW_LIST_ALLOW_ALL;
ucs_config_release_array(&field->array, arg);
if (field->array.count != 1) {
return 0;
}
Comment thread
gleon99 marked this conversation as resolved.
Resolved

field->array.count = 0;
}

return 1;
}

int ucs_config_sprintf_allow_list(char *buf, size_t max, const void *src,
const void *arg)
{
const ucs_config_allow_list_t *allow_list = src;
size_t offset = 0;

if (allow_list->mode == UCS_CONFIG_ALLOW_LIST_ALLOW_ALL) {
snprintf(buf, max, UCS_CONFIG_PARSER_ALL);
return 1;
}

if (allow_list->mode == UCS_CONFIG_ALLOW_LIST_NEGATE) {
buf[offset++] = ucs_config_parser_negate;
max--;
}

return ucs_config_sprintf_array(&buf[offset], max, &allow_list->array, arg);
}

ucs_status_t ucs_config_clone_allow_list(const void *src, void *dest, const void *arg)
{
const ucs_config_allow_list_t *src_list = src;
ucs_config_allow_list_t *dest_list = dest;

dest_list->mode = src_list->mode;
return ucs_config_clone_array(&src_list->array, &dest_list->array, arg);
}

void ucs_config_release_allow_list(void *ptr, const void *arg)
{
ucs_config_allow_list_t *allow_list = ptr;

if (allow_list->mode == UCS_CONFIG_ALLOW_LIST_ALLOW_ALL) {
return;
}

ucs_config_release_array(&allow_list->array, arg);
}

void ucs_config_help_allow_list(char *buf, size_t max, const void *arg)
{
const ucs_config_array_t *array = arg;

snprintf(
buf,
max, "comma-separated list (use \"all\" for including \
all items or \'^\' for negation) of: ");
array->parser.help(buf + strlen(buf), max - strlen(buf), array->parser.arg);
}

int ucs_config_sscanf_table(const char *buf, void *dest, const void *arg)
{
char *tokens;
Expand Down
11 changes: 11 additions & 0 deletions src/ucs/config/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ ucs_status_t ucs_config_clone_array(const void *src, void *dest, const void *arg
void ucs_config_release_array(void *ptr, const void *arg);
void ucs_config_help_array(char *buf, size_t max, const void *arg);

int ucs_config_sscanf_allow_list(const char *buf, void *dest, const void *arg);
int ucs_config_sprintf_allow_list(char *buf, size_t max, const void *src,
const void *arg);
ucs_status_t ucs_config_clone_allow_list(const void *src, void *dest, const void *arg);
void ucs_config_release_allow_list(void *ptr, const void *arg);
void ucs_config_help_allow_list(char *buf, size_t max, const void *arg);

int ucs_config_sscanf_table(const char *buf, void *dest, const void *arg);
ucs_status_t ucs_config_clone_table(const void *src, void *dest, const void *arg);
void ucs_config_release_table(void *ptr, const void *arg);
Expand Down Expand Up @@ -321,6 +328,10 @@ void ucs_config_help_generic(char *buf, size_t max, const void *arg);
ucs_config_clone_array, ucs_config_release_array, \
ucs_config_help_array, &ucs_config_array_##a}

#define UCS_CONFIG_TYPE_ALLOW_LIST {ucs_config_sscanf_allow_list, ucs_config_sprintf_allow_list, \
ucs_config_clone_allow_list, ucs_config_release_allow_list, \
ucs_config_help_allow_list, &ucs_config_array_string}

#define UCS_CONFIG_TYPE_TABLE(t) {ucs_config_sscanf_table, NULL, \
ucs_config_clone_table, ucs_config_release_table, \
ucs_config_help_table, t}
Expand Down
17 changes: 17 additions & 0 deletions src/ucs/config/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ typedef enum {

typedef UCS_CONFIG_STRING_ARRAY_FIELD(names) ucs_config_names_array_t;


/**
* Enum for representing possible modes of an "allow-list"
*/
typedef enum {
UCS_CONFIG_ALLOW_LIST_ALLOW_ALL, /* Allow all possible options */
UCS_CONFIG_ALLOW_LIST_ALLOW, /* Allow only the specified options */
UCS_CONFIG_ALLOW_LIST_NEGATE /* Negate (forbid) the specified options */
} ucs_config_allow_list_mode_t;


typedef struct {
ucs_config_names_array_t array;
ucs_config_allow_list_mode_t mode;
} ucs_config_allow_list_t;


/**
* @ingroup UCS_RESOURCE
* BSD socket address specification.
Expand Down
63 changes: 50 additions & 13 deletions test/gtest/ucs/test_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ typedef struct {
ucs_time_t time_value;
ucs_time_t time_auto;
ucs_time_t time_inf;
ucs_config_allow_list_t allow_list;
} car_opts_t;


Expand Down Expand Up @@ -218,6 +219,9 @@ ucs_config_field_t car_opts_table[] = {
{"TIME_INF", "inf", "Time value \"inf\"",
ucs_offsetof(car_opts_t, time_inf), UCS_CONFIG_TYPE_TIME_UNITS},

{"ALLOW_LIST", "all", "Allow-list: \"all\" OR \"val1,val2\" OR \"^val1,val2\"",
ucs_offsetof(car_opts_t, allow_list), UCS_CONFIG_TYPE_ALLOW_LIST},

{NULL}
};

Expand Down Expand Up @@ -414,6 +418,8 @@ UCS_TEST_F(test_config, parse_default) {
EXPECT_EQ(ucs_time_from_sec(1.0), opts->time_value);
EXPECT_EQ(UCS_TIME_AUTO, opts->time_auto);
EXPECT_EQ(UCS_TIME_INFINITY, opts->time_inf);
EXPECT_EQ(UCS_CONFIG_ALLOW_LIST_ALLOW_ALL, opts->allow_list.mode);
EXPECT_EQ(0, opts->allow_list.array.count);
}

UCS_TEST_F(test_config, clone) {
Expand All @@ -436,6 +442,7 @@ UCS_TEST_F(test_config, clone) {
}

EXPECT_EQ(COLOR_WHITE, (*opts_clone_ptr)->color);
EXPECT_EQ(UCS_CONFIG_ALLOW_LIST_ALLOW_ALL, (*opts_clone_ptr)->allow_list.mode);
delete opts_clone_ptr;
}

Expand Down Expand Up @@ -540,27 +547,23 @@ UCS_TEST_F(test_config, unused) {

UCS_TEST_F(test_config, dump) {
/* aliases must not be counted here */
test_config_print_opts(UCS_CONFIG_PRINT_CONFIG, 31u);
test_config_print_opts(UCS_CONFIG_PRINT_CONFIG, 32u);
}

UCS_TEST_F(test_config, dump_hidden) {
/* aliases must be counted here */
test_config_print_opts((UCS_CONFIG_PRINT_CONFIG |
UCS_CONFIG_PRINT_HIDDEN),
38u);
test_config_print_opts(UCS_CONFIG_PRINT_CONFIG | UCS_CONFIG_PRINT_HIDDEN, 39u);
}

UCS_TEST_F(test_config, dump_hidden_check_alias_name) {
/* aliases must be counted here */
test_config_print_opts((UCS_CONFIG_PRINT_CONFIG |
UCS_CONFIG_PRINT_HIDDEN |
UCS_CONFIG_PRINT_DOC),
38u);

test_config_print_opts((UCS_CONFIG_PRINT_CONFIG |
UCS_CONFIG_PRINT_HIDDEN |
UCS_CONFIG_PRINT_DOC),
38u, "TEST_");
test_config_print_opts(
UCS_CONFIG_PRINT_CONFIG | UCS_CONFIG_PRINT_HIDDEN | UCS_CONFIG_PRINT_DOC,
39u);

test_config_print_opts(
UCS_CONFIG_PRINT_CONFIG | UCS_CONFIG_PRINT_HIDDEN | UCS_CONFIG_PRINT_DOC,
39u, "TEST_");
}

UCS_TEST_F(test_config, deprecated) {
Expand Down Expand Up @@ -594,3 +597,37 @@ UCS_TEST_F(test_config, deprecated) {
/* reset to not warn about unused env vars */
ucs_global_opts.warn_unused_env_vars = 0;
}

UCS_TEST_F(test_config, test_allow_list) {
const std::string allow_list = "UCX_ALLOW_LIST";

{
/* coverity[tainted_string_argument] */
ucs::scoped_setenv env(allow_list.c_str(), "first,second");

car_opts opts(UCS_DEFAULT_ENV_PREFIX, NULL);
EXPECT_EQ(UCS_CONFIG_ALLOW_LIST_ALLOW, opts->allow_list.mode);
EXPECT_EQ(2, opts->allow_list.array.count);
EXPECT_EQ(std::string("first"), opts->allow_list.array.names[0]);
EXPECT_EQ(std::string("second"), opts->allow_list.array.names[1]);
}

{
/* coverity[tainted_string_argument] */
ucs::scoped_setenv env(allow_list.c_str(), "^first,second");

car_opts opts(UCS_DEFAULT_ENV_PREFIX, NULL);
EXPECT_EQ(UCS_CONFIG_ALLOW_LIST_NEGATE, opts->allow_list.mode);
EXPECT_EQ(2, opts->allow_list.array.count);
EXPECT_EQ(std::string("first"), opts->allow_list.array.names[0]);
EXPECT_EQ(std::string("second"), opts->allow_list.array.names[1]);
}
}

UCS_TEST_F(test_config, test_allow_list_negative)
{
ucs_config_allow_list_t field;

EXPECT_EQ(ucs_config_sscanf_allow_list("all,all", &field,
&ucs_config_array_string), 0);
}