param: hand arguments after -- straight to init
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 28 Apr 2014 02:04:33 +0000 (11:34 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 28 Apr 2014 02:18:34 +0000 (11:48 +0930)
The kernel passes any args it doesn't need through to init, except it
assumes anything containing '.' belongs to the kernel (for a module).
This change means all users can clearly distinguish which arguments
are for init.

For example, the kernel uses debug ("dee-bug") to mean log everything to
the console, where systemd uses the debug from the Scandinavian "day-boog"
meaning "fail to boot".  If a future versions uses argv[] instead of
reading /proc/cmdline, this confusion will be avoided.

eg: test 'FOO="this is --foo"' -- 'systemd.debug="true true true"'

Gives:
argv[0] = '/debug-init'
argv[1] = 'test'
argv[2] = 'systemd.debug=true true true'
envp[0] = 'HOME=/'
envp[1] = 'TERM=linux'
envp[2] = 'FOO=this is --foo'

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
include/linux/moduleparam.h
init/main.c
kernel/module.c
kernel/params.c

index 204a677438049b13570e779f36078e0450298614..b1990c5524e1cea5a0d52f618f0846a1fd442dfe 100644 (file)
@@ -321,7 +321,7 @@ extern bool parameq(const char *name1, const char *name2);
 extern bool parameqn(const char *name1, const char *name2, size_t n);
 
 /* Called on module insert or kernel boot */
-extern int parse_args(const char *name,
+extern char *parse_args(const char *name,
                      char *args,
                      const struct kernel_param *params,
                      unsigned num,
index 9c7fd4c9249f2c72395fcaf2ac953f782a3e2b59..e9d458b5d77bd34449473d6b777da545aec8a81b 100644 (file)
@@ -252,6 +252,27 @@ static int __init repair_env_string(char *param, char *val, const char *unused)
        return 0;
 }
 
+/* Anything after -- gets handed straight to init. */
+static int __init set_init_arg(char *param, char *val, const char *unused)
+{
+       unsigned int i;
+
+       if (panic_later)
+               return 0;
+
+       repair_env_string(param, val, unused);
+
+       for (i = 0; argv_init[i]; i++) {
+               if (i == MAX_INIT_ARGS) {
+                       panic_later = "init";
+                       panic_param = param;
+                       return 0;
+               }
+       }
+       argv_init[i] = param;
+       return 0;
+}
+
 /*
  * Unknown boot options get handed to init, unless they look like
  * unused parameters (modprobe will find them in /proc/cmdline).
@@ -478,7 +499,7 @@ static void __init mm_init(void)
 
 asmlinkage void __init start_kernel(void)
 {
-       char * command_line;
+       char * command_line, *after_dashes;
        extern const struct kernel_param __start___param[], __stop___param[];
 
        /*
@@ -519,9 +540,13 @@ asmlinkage void __init start_kernel(void)
 
        pr_notice("Kernel command line: %s\n", boot_command_line);
        parse_early_param();
-       parse_args("Booting kernel", static_command_line, __start___param,
-                  __stop___param - __start___param,
-                  -1, -1, &unknown_bootoption);
+       after_dashes = parse_args("Booting kernel",
+                                 static_command_line, __start___param,
+                                 __stop___param - __start___param,
+                                 -1, -1, &unknown_bootoption);
+       if (after_dashes)
+               parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
+                          set_init_arg);
 
        jump_label_init();
 
index 11869408f79b86abe33e5194d0f5c705b44e9d81..66e4e0d260a9a287a947d8dcc96d097317d87fed 100644 (file)
@@ -3193,6 +3193,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
 {
        struct module *mod;
        long err;
+       char *after_dashes;
 
        err = module_sig_check(info);
        if (err)
@@ -3277,10 +3278,15 @@ static int load_module(struct load_info *info, const char __user *uargs,
                goto ddebug_cleanup;
 
        /* Module is ready to execute: parsing args may do that. */
-       err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-                        -32768, 32767, unknown_module_param_cb);
-       if (err < 0)
+       after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
+                                 -32768, 32767, unknown_module_param_cb);
+       if (IS_ERR(after_dashes)) {
+               err = PTR_ERR(after_dashes);
                goto bug_cleanup;
+       } else if (after_dashes) {
+               pr_warn("%s: parameters '%s' after `--' ignored\n",
+                      mod->name, after_dashes);
+       }
 
        /* Link in to syfs. */
        err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
index b00142e7f3ba18d52f41e19427f145eb6e95cf2d..1e52ca233fd9a45484418f14820c40b95e89d6ba 100644 (file)
@@ -177,13 +177,13 @@ static char *next_arg(char *args, char **param, char **val)
 }
 
 /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
-int parse_args(const char *doing,
-              char *args,
-              const struct kernel_param *params,
-              unsigned num,
-              s16 min_level,
-              s16 max_level,
-              int (*unknown)(char *param, char *val, const char *doing))
+char *parse_args(const char *doing,
+                char *args,
+                const struct kernel_param *params,
+                unsigned num,
+                s16 min_level,
+                s16 max_level,
+                int (*unknown)(char *param, char *val, const char *doing))
 {
        char *param, *val;
 
@@ -198,6 +198,9 @@ int parse_args(const char *doing,
                int irq_was_disabled;
 
                args = next_arg(args, &param, &val);
+               /* Stop at -- */
+               if (!val && strcmp(param, "--") == 0)
+                       return args;
                irq_was_disabled = irqs_disabled();
                ret = parse_one(param, val, doing, params, num,
                                min_level, max_level, unknown);
@@ -208,22 +211,22 @@ int parse_args(const char *doing,
                switch (ret) {
                case -ENOENT:
                        pr_err("%s: Unknown parameter `%s'\n", doing, param);
-                       return ret;
+                       return ERR_PTR(ret);
                case -ENOSPC:
                        pr_err("%s: `%s' too large for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ret;
+                       return ERR_PTR(ret);
                case 0:
                        break;
                default:
                        pr_err("%s: `%s' invalid for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ret;
+                       return ERR_PTR(ret);
                }
        }
 
        /* All parsed OK. */
-       return 0;
+       return NULL;
 }
 
 /* Lazy bastard, eh? */