summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas De Marchi <lucas.demarchi@intel.com>2021-02-12 01:45:21 -0800
committerLucas De Marchi <lucas.demarchi@intel.com>2021-02-15 11:53:38 -0800
commit01ed9af61e239b40514edf527ac87c79377266ac (patch)
treedd616f3b372100e46fb4eea067ca4ab4d2c5e714
parentfd71604da9d9bab91f270deacceecc0d5c0fcffd (diff)
downloadkmod-01ed9af61e239b40514edf527ac87c79377266ac.tar.gz
kmod-01ed9af61e239b40514edf527ac87c79377266ac.tar.bz2
kmod-01ed9af61e239b40514edf527ac87c79377266ac.zip
libkmod-config: revamp kcmdline parsing into a state machine
The handling of spaces and quotes is becoming hard to maintain. Convert the parser into a state machine so we can check all the states. This should make it easier to fix a corner case we have right now: The kernel also accepts a quote before the module name instead of the value. But this additional is left for later. This is purely an algorithm change with no behavior change. Tested-by: Jessica Yu <jeyu@kernel.org>
-rw-r--r--libkmod/libkmod-config.c86
1 files changed, 52 insertions, 34 deletions
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
index 971f20b..d3cd10d 100644
--- a/libkmod/libkmod-config.c
+++ b/libkmod/libkmod-config.c
@@ -499,7 +499,14 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config)
char buf[KCMD_LINE_SIZE];
int fd, err;
char *p, *modname, *param = NULL, *value = NULL;
- bool is_quoted = false, is_module = true;
+ bool is_quoted = false, iter = true;
+ enum state {
+ STATE_IGNORE,
+ STATE_MODNAME,
+ STATE_PARAM,
+ STATE_VALUE,
+ STATE_COMPLETE,
+ } state;
fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
if (fd < 0) {
@@ -516,54 +523,65 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config)
return err;
}
- for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) {
- if (*p == '"') {
+ state = STATE_MODNAME;
+ for (p = buf, modname = buf; iter; p++) {
+ switch (*p) {
+ case '"':
is_quoted = !is_quoted;
-
- if (is_quoted) {
- /* don't consider a module until closing quotes */
- is_module = false;
- } else if (param != NULL && value != NULL) {
+ break;
+ case '\0':
+ case '\n':
+ /* Stop iterating on new chars */
+ iter = false;
+ /* fall-through */
+ case ' ':
+ if (is_quoted && state == STATE_VALUE) {
+ /* no state change*/;
+ } else if (is_quoted) {
+ /* spaces are only allowed in the value part */
+ state = STATE_IGNORE;
+ } else if (state == STATE_VALUE || state == STATE_PARAM) {
+ *p = '\0';
+ state = STATE_COMPLETE;
+ } else {
/*
- * If we are indeed expecting a value and
- * closing quotes, then this can be considered
- * a valid option for a module
+ * go to next option, ignoring any possible
+ * partial match we have
*/
- is_module = true;
+ modname = p + 1;
+ state = STATE_MODNAME;
}
-
- continue;
- }
- if (is_quoted)
- continue;
-
- switch (*p) {
- case ' ':
- *p = '\0';
- if (is_module)
- kcmdline_parse_result(config, modname, param, value);
- param = value = NULL;
- modname = p + 1;
- is_module = true;
break;
case '.':
- if (param == NULL) {
+ if (state == STATE_MODNAME) {
*p = '\0';
param = p + 1;
+ state = STATE_PARAM;
+ } else if (state == STATE_PARAM) {
+ state = STATE_IGNORE;
}
break;
case '=':
- if (param != NULL)
+ if (state == STATE_PARAM) {
+ /*
+ * Don't set *p to '\0': the value var shadows
+ * param
+ */
value = p + 1;
- else
- is_module = false;
+ state = STATE_VALUE;
+ } else if (state == STATE_MODNAME) {
+ state = STATE_IGNORE;
+ }
break;
}
- }
- *p = '\0';
- if (is_module)
- kcmdline_parse_result(config, modname, param, value);
+ if (state == STATE_COMPLETE) {
+ kcmdline_parse_result(config, modname, param, value);
+ /* start over on next iteration */
+ modname = p + 1;
+ state = STATE_MODNAME;
+ }
+ }
return 0;
}