-
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
ab65cc7
000e151
18da485
a3f881c
486e90d
5e3ad4a
f52beb7
901382b
02a141f
5ddfe9c
5247e71
d56111f
e6c1700
853d999
0a1889d
eadac78
9f0953b
dffb052
5ac8c25
1723718
6d6cfd6
e30d524
20e0bf4
7641bbe
0229b55
b550685
dfc256f
1d583ae
36c8129
a17c005
d20a904
d966425
e01a014
5c590f6
0f71e08
a5bd5c8
3719667
65c7d99
406369c
d57c00d
2ebf66b
ec51073
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) { | ||
|
Contributor
Author
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.
Contributor
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?
Contributor
Author
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. Yes |
||
| 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'; | ||
|
|
||
|
Contributor
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.
Contributor
Author
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. "Port" comes from a time when it was more common for fencing to target a shared power supply or network switch. For example:
It has since been overloaded -- for example,
Previously, the backslash was just getting ignored. Wouldn't hurt to do some testing (or re-testing... not sure what I did, if anything) to be sure. But the reason I felt comfortable making this change in the first place, is that it doesn't appear to change the end result; we simply no longer skip a backslash if present. FWIW Oyvind has no idea why e95198f was done.
Hopefully no one... but I understand your concern. This is a case where I'd be willing to take my chances in order to make the code look sane. But I'm not the one who has to do the z-stream builds. |
||
| 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); | ||
|
|
||
|
Contributor
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.
Contributor
Author
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. It's not even just "before the error occurred," but also after the error occurred. This is a sanity check to catch mappings with an empty name (node) or value (port). Note that this is happening after splitting on semicolons and whitespace. So consider the following (colons are equivalent to equal signs): The mappings for |
||
| 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; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.