aboutsummaryrefslogtreecommitdiff
path: root/libkmod/libkmod-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'libkmod/libkmod-config.c')
-rw-r--r--libkmod/libkmod-config.c127
1 files changed, 92 insertions, 35 deletions
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
index 971f20b..e83621b 100644
--- a/libkmod/libkmod-config.c
+++ b/libkmod/libkmod-config.c
@@ -498,8 +498,15 @@ 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;
+ char *p, *p_quote_start, *modname, *param = NULL, *value = NULL;
+ 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,102 @@ 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;
+ p_quote_start = NULL;
+ 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) {
- /*
- * If we are indeed expecting a value and
- * closing quotes, then this can be considered
- * a valid option for a module
- */
- is_module = true;
+ /*
+ * only allow starting quote as first char when looking
+ * for a modname: anything else is considered ill-formed
+ */
+ if (is_quoted && state == STATE_MODNAME && p == modname) {
+ p_quote_start = p;
+ modname = p + 1;
+ } else if (state != STATE_VALUE) {
+ state = STATE_IGNORE;
}
- continue;
- }
- if (is_quoted)
- continue;
-
- switch (*p) {
+ break;
+ case '\0':
+ iter = false;
+ /* fall-through */
case ' ':
- *p = '\0';
- if (is_module)
- kcmdline_parse_result(config, modname, param, value);
- param = value = NULL;
- modname = p + 1;
- is_module = true;
+ case '\n':
+ case '\t':
+ case '\v':
+ case '\f':
+ case '\r':
+ 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 {
+ /*
+ * go to next option, ignoring any possible
+ * partial match we have
+ */
+ modname = p + 1;
+ state = STATE_MODNAME;
+ p_quote_start = NULL;
+ }
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) {
+ /*
+ * We may need to re-quote to unmangle what the
+ * bootloader passed. Example: grub passes the option as
+ * "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf"
+ * instead of
+ * parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf"
+ */
+ if (p_quote_start && p_quote_start < modname) {
+ /*
+ * p_quote_start
+ * |
+ * |modname param value
+ * || | |
+ * vv v v
+ * "parport\0dyndbg=file drivers/parport/ieee1284_ops.c +mpf" */
+ memmove(p_quote_start, modname, value - modname);
+ value--; modname--; param--;
+ *value = '"';
+ }
+ kcmdline_parse_result(config, modname, param, value);
+ /* start over on next iteration */
+ modname = p + 1;
+ state = STATE_MODNAME;
+ p_quote_start = NULL;
+ }
+ }
return 0;
}
@@ -854,8 +909,10 @@ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
memcpy(cf->path, path, pathlen);
tmp = kmod_list_append(path_list, cf);
- if (tmp == NULL)
+ if (tmp == NULL) {
+ free(cf);
goto oom;
+ }
path_list = tmp;
}