-
Notifications
You must be signed in to change notification settings - Fork 349
Replace uses of strtok() with g_strsplit() and friends #3967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
363e247
21faadc
d6432aa
d4f8bf2
061281c
85e56b3
bf66d15
bca5058
0b892f8
9d9184d
f75b1e0
a108090
1b79d4f
11efc0a
cdf01de
d1fb221
8f81986
487f38f
8984aaa
2f760c7
60a011e
737f136
aa90f0a
1d30a0d
2d2f241
975635f
2c25704
b1b359f
3784aa4
690864b
4f7d083
a8b12f4
32eaebf
8c20741
6686a24
3579d82
0431aae
f9c5d09
4df0aef
0af49a3
8e852db
b936817
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -201,48 +201,110 @@ get_action_delay_max(const fenced_device_t *device, const char *action) | |
return (int) delay_max; | ||
} | ||
|
||
static gchar * | ||
get_value_if_matching(const char *mapping, const char *target) | ||
{ | ||
gchar **nvpair = NULL; | ||
gchar *value = NULL; | ||
|
||
if (pcmk__str_empty(mapping)) { | ||
goto done; | ||
} | ||
|
||
nvpair = g_strsplit(mapping, ":", 2); | ||
|
||
if (pcmk__str_empty(nvpair[0]) || pcmk__str_empty(nvpair[1])) { | ||
crm_err(PCMK_FENCING_DELAY_BASE ": Malformed mapping '%s'", mapping); | ||
goto done; | ||
} | ||
|
||
if (!pcmk__str_eq(target, nvpair[0], pcmk__str_casei)) { | ||
goto done; | ||
} | ||
|
||
// Take ownership so that we don't free nvpair[1] with nvpair | ||
value = nvpair[1]; | ||
nvpair[1] = NULL; | ||
|
||
crm_debug(PCMK_FENCING_DELAY_BASE " mapped to %s for %s", value, target); | ||
|
||
done: | ||
g_strfreev(nvpair); | ||
return value; | ||
} | ||
|
||
static gchar * | ||
get_value_for_target(const char *target, const char *values) | ||
{ | ||
gchar *value = NULL; | ||
gchar **mappings = g_strsplit_set(values, "; \t", 0); | ||
|
||
/* If there are no delimiters after stripping leading and trailing | ||
* whitespace, then we want to parse the string as a single interval, rather | ||
* than as a delimited list of mappings. Short-circuiting here when we split | ||
* into fewer than two mappings avoids a "Malformed mapping" error message | ||
* below. | ||
*/ | ||
if (g_strv_length(mappings) < 2) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong. There could be a single mapping -- though if there are no delimiters, we don't want to throw an error if it's an interval instead of a mapping. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this still need to be addressed? |
||
goto done; | ||
} | ||
|
||
for (gchar **mapping = mappings; *mapping != NULL; mapping++) { | ||
value = get_value_if_matching(*mapping, target); | ||
if (value != NULL) { | ||
break; | ||
} | ||
} | ||
|
||
done: | ||
g_strfreev(mappings); | ||
return value; | ||
} | ||
|
||
/* @TODO Consolidate some of this with build_port_aliases(). But keep in mind | ||
* that build_port_aliases()/pcmk__host_map supports either '=' or ':' as a | ||
* mapping separator, while pcmk_delay_base supports only ':'. | ||
*/ | ||
static int | ||
get_action_delay_base(const fenced_device_t *device, const char *action, | ||
const char *target) | ||
{ | ||
char *hash_value = NULL; | ||
const char *param = NULL; | ||
gchar *stripped = NULL; | ||
gchar *delay_base_s = NULL; | ||
guint delay_base = 0U; | ||
|
||
if (!pcmk__is_fencing_action(action)) { | ||
return 0; | ||
} | ||
|
||
hash_value = g_hash_table_lookup(device->params, PCMK_FENCING_DELAY_BASE); | ||
|
||
if (hash_value) { | ||
char *value = pcmk__str_copy(hash_value); | ||
char *valptr = value; | ||
|
||
if (target != NULL) { | ||
for (char *val = strtok(value, "; \t"); val != NULL; val = strtok(NULL, "; \t")) { | ||
char *mapval = strchr(val, ':'); | ||
param = g_hash_table_lookup(device->params, PCMK_FENCING_DELAY_BASE); | ||
if (param == NULL) { | ||
return 0; | ||
} | ||
|
||
if (mapval == NULL || mapval[1] == 0) { | ||
crm_err("pcmk_delay_base: empty value in mapping", val); | ||
continue; | ||
} | ||
stripped = g_strstrip(g_strdup(param)); | ||
if (target != NULL) { | ||
delay_base_s = get_value_for_target(target, stripped); | ||
} | ||
|
||
if (mapval != val && strncasecmp(target, val, (size_t)(mapval - val)) == 0) { | ||
value = mapval + 1; | ||
crm_debug("pcmk_delay_base mapped to %s for %s", | ||
value, target); | ||
break; | ||
} | ||
} | ||
} | ||
if (delay_base_s == NULL) { | ||
/* Either target is NULL or we didn't find a mapping. Try to parse the | ||
* stripped value itself. Take ownership so that we don't free stripped | ||
* twice. | ||
*/ | ||
delay_base_s = stripped; | ||
stripped = NULL; | ||
} | ||
|
||
if (strchr(value, ':') == 0) { | ||
pcmk_parse_interval_spec(value, &delay_base); | ||
delay_base /= 1000; | ||
} | ||
/* @TODO Should we accept only a simple time+units string, rather than an | ||
* ISO 8601 interval? | ||
*/ | ||
pcmk_parse_interval_spec(delay_base_s, &delay_base); | ||
delay_base /= 1000; | ||
|
||
free(valptr); | ||
} | ||
g_free(stripped); | ||
g_free(delay_base_s); | ||
|
||
return (int) delay_base; | ||
} | ||
|
@@ -839,82 +901,43 @@ fenced_free_device_table(void) | |
} | ||
|
||
static GHashTable * | ||
build_port_aliases(const char *hostmap, GList ** targets) | ||
build_port_aliases(const char *hostmap, GList **targets) | ||
{ | ||
char *name = NULL; | ||
int last = 0, lpc = 0, max = 0, added = 0; | ||
GHashTable *aliases = pcmk__strikey_table(free, free); | ||
gchar *stripped = NULL; | ||
gchar **mappings = NULL; | ||
|
||
if (hostmap == NULL) { | ||
return aliases; | ||
if (pcmk__str_empty(hostmap)) { | ||
goto done; | ||
} | ||
|
||
max = strlen(hostmap); | ||
for (; lpc <= max; lpc++) { | ||
switch (hostmap[lpc]) { | ||
/* Skip escaped chars */ | ||
case '\\': | ||
lpc++; | ||
break; | ||
stripped = g_strstrip(g_strdup(hostmap)); | ||
mappings = g_strsplit_set(stripped, "; \t", 0); | ||
|
||
/* Assignment chars */ | ||
case '=': | ||
case ':': | ||
if (lpc > last) { | ||
free(name); | ||
name = pcmk__assert_alloc(1, 1 + lpc - last); | ||
memcpy(name, hostmap + last, lpc - last); | ||
} | ||
last = lpc + 1; | ||
break; | ||
for (gchar **mapping = mappings; *mapping != NULL; mapping++) { | ||
gchar **nvpair = NULL; | ||
|
||
/* Delimeter chars */ | ||
/* case ',': Potentially used to specify multiple ports */ | ||
case 0: | ||
case ';': | ||
case ' ': | ||
case '\t': | ||
if (name) { | ||
char *value = NULL; | ||
int k = 0; | ||
|
||
value = pcmk__assert_alloc(1, 1 + lpc - last); | ||
memcpy(value, hostmap + last, lpc - last); | ||
|
||
for (int i = 0; value[i] != '\0'; i++) { | ||
if (value[i] != '\\') { | ||
value[k++] = value[i]; | ||
} | ||
} | ||
value[k] = '\0'; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docs (doc/sphinx/Pacemaker_Explained/fencing.rst) say this about
I'm unclear on what a "port" is in this context, and what characters are valid in the definition of a port. I'd like to make sure that if we are expected to support characters such as spaces in port names, that the new parsing code handles that without backslashes. Also, since this has been present since 2.1.2, I'm a little concerned about breaking whoever is using this right now regardless of how little sense it makes or how it is currently mangled in the XML. |
||
crm_debug("Adding alias '%s'='%s'", name, value); | ||
g_hash_table_replace(aliases, name, value); | ||
if (targets) { | ||
*targets = g_list_append(*targets, pcmk__str_copy(value)); | ||
} | ||
value = NULL; | ||
name = NULL; | ||
added++; | ||
if (pcmk__str_empty(*mapping)) { | ||
continue; | ||
} | ||
|
||
} else if (lpc > last) { | ||
crm_debug("Parse error at offset %d near '%s'", lpc - last, hostmap + last); | ||
} | ||
// @COMPAT Drop support for '=' as delimiter | ||
nvpair = g_strsplit_set(*mapping, ":=", 2); | ||
|
||
last = lpc + 1; | ||
break; | ||
} | ||
if (pcmk__str_empty(nvpair[0]) || pcmk__str_empty(nvpair[1])) { | ||
crm_err(PCMK_FENCING_HOST_MAP ": Malformed mapping '%s'", *mapping); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we're changing behavior... I wonder if it makes sense to return an empty mapping in the case where it's malformed. I can see going either way with it. If there's a parse error, who knows what they screwed up so maybe the entire thing is invalid. On the other hand, maybe we should try to use what we can from before the error occurred (what we're doing right now) to implement the user's configuration as much as possible. |
||
if (hostmap[lpc] == 0) { | ||
break; | ||
} else { | ||
crm_debug("Adding alias '%s'='%s'", nvpair[0], nvpair[1]); | ||
pcmk__insert_dup(aliases, nvpair[0], nvpair[1]); | ||
*targets = g_list_append(*targets, pcmk__str_copy(nvpair[1])); | ||
} | ||
g_strfreev(nvpair); | ||
} | ||
|
||
if (added == 0) { | ||
crm_info("No host mappings detected in '%s'", hostmap); | ||
} | ||
|
||
free(name); | ||
done: | ||
g_free(stripped); | ||
g_strfreev(mappings); | ||
return aliases; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little surprised that the argument to
pcmk_host_list
doesn't have to be surrounded by some quotes here (and everywhere else) since it contains spaces.