Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
lk_usermodehelper [2019/02/13 20:33] – [STATIC_USERMODEHELPER] rpjdaylk_usermodehelper [2019/02/14 15:18] (current) – [init/do_mounts_initrd.c] rpjday
Line 41: Line 41:
 </code> </code>
  
 +==== Standard calls ====
 +Common combination:
 +
 +<code>
 +extern struct subprocess_info *
 +call_usermodehelper_setup(const char *path, char **argv, char **envp,
 +                          gfp_t gfp_mask,
 +                          int (*init)(struct subprocess_info *info, struct cred *new),
 +                          void (*cleanup)(struct subprocess_info *), void *data);
 +                          
 +extern int
 +call_usermodehelper_exec(struct subprocess_info *info, int wait);
 +</code>
 +
 +or combination of the above:
 +
 +<code>
 +extern int
 +call_usermodehelper(const char *path, char **argv, char **envp, int wait);
 +</code>
 +
 +===== kernel/umh.c =====
 +
 +==== call_usermodehelper_setup() ====
 +
 +<code>
 +struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 + char **envp, gfp_t gfp_mask,
 + int (*init)(struct subprocess_info *info, struct cred *new),
 + void (*cleanup)(struct subprocess_info *info),
 + void *data)
 +{
 + struct subprocess_info *sub_info;
 + sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask);
 + if (!sub_info)
 + goto out;
 +
 + INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
 +
 +#ifdef CONFIG_STATIC_USERMODEHELPER
 + sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
 +#else
 + sub_info->path = path;
 +#endif
 + sub_info->argv = argv;
 + sub_info->envp = envp;
 +
 + sub_info->cleanup = cleanup;
 + sub_info->init = init;
 + sub_info->data = data;
 +  out:
 + return sub_info;
 +}
 +</code>
 +
 +==== call_usermodehelper_exec() ====
 +
 +<code>
 +int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 +{
 + DECLARE_COMPLETION_ONSTACK(done);
 + int retval = 0;
 +
 + if (!sub_info->path) {
 + call_usermodehelper_freeinfo(sub_info);
 + return -EINVAL;
 + }
 + helper_lock();
 + if (usermodehelper_disabled) {
 + retval = -EBUSY;
 + goto out;
 + }
 +
 + /*
 + * If there is no binary for us to call, then just return and get out of
 + * here.  This allows us to set STATIC_USERMODEHELPER_PATH to "" and
 + * disable all call_usermodehelper() calls.
 + */
 + if (strlen(sub_info->path) == 0)
 + goto out;
 +
 + /*
 + * Set the completion pointer only if there is a waiter.
 + * This makes it possible to use umh_complete to free
 + * the data structure in case of UMH_NO_WAIT.
 + */
 + sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;
 + sub_info->wait = wait;
 +
 + queue_work(system_unbound_wq, &sub_info->work);
 + if (wait == UMH_NO_WAIT) /* task has freed sub_info */
 + goto unlock;
 +
 + if (wait & UMH_KILLABLE) {
 + retval = wait_for_completion_killable(&done);
 + if (!retval)
 + goto wait_done;
 +
 + /* umh_complete() will see NULL and free sub_info */
 + if (xchg(&sub_info->complete, NULL))
 + goto unlock;
 + /* fallthrough, umh_complete() was already called */
 + }
 +
 + wait_for_completion(&done);
 +wait_done:
 + retval = sub_info->retval;
 +out:
 + call_usermodehelper_freeinfo(sub_info);
 +unlock:
 + helper_unlock();
 + return retval;
 +}
 +</code>
 +
 +==== call_usermodehelper() ====
 +
 +Combination of the above two:
 +
 +<code>
 +int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
 +{
 + struct subprocess_info *info;
 + gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
 +
 + info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
 + NULL, NULL, NULL);
 + if (info == NULL)
 + return -ENOMEM;
 +
 + return call_usermodehelper_exec(info, wait);
 +}
 +</code>
 ===== STATIC_USERMODEHELPER ===== ===== STATIC_USERMODEHELPER =====
  
Line 90: Line 223:
         sub_info->path = path;         sub_info->path = path;
 #endif #endif
 +</code>
 +
 +===== Examples =====
 +
 +==== kernel/kmod.c ====
 +
 +<code>
 +static int call_modprobe(char *module_name, int wait)
 +{
 +        struct subprocess_info *info;
 +        static char *envp[] = {
 +                "HOME=/",
 +                "TERM=linux",
 +                "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
 +                NULL
 +        };
 +
 +        char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
 +        if (!argv)
 +                goto out;
 +
 +        module_name = kstrdup(module_name, GFP_KERNEL);
 +        if (!module_name)
 +                goto free_argv;
 +
 +        argv[0] = modprobe_path;
 +        argv[1] = "-q";
 +        argv[2] = "--";
 +        argv[3] = module_name;  /* check free_modprobe_argv() */
 +        argv[4] = NULL;
 +
 +        info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
 +                                         NULL, free_modprobe_argv, NULL);
 +        if (!info)
 +                goto free_module_name;
 +
 +        return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
 +
 +free_module_name:
 +        kfree(module_name);
 +free_argv:
 +        kfree(argv);
 +out:
 +        return -ENOMEM;
 +}
 +</code>
 +
 +==== drivers/video/fbdev/uvesafb.c ====
 +
 +<code>
 +static char v86d_path[PATH_MAX] = "/sbin/v86d";
 +
 +...
 +
 +static int uvesafb_helper_start(void)
 +{
 +        char *envp[] = {
 +                "HOME=/",
 +                "PATH=/sbin:/bin",
 +                NULL,
 +        };
 +
 +        char *argv[] = {
 +                v86d_path,
 +                NULL,
 +        };
 +
 +        return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC);
 +}
 +</code>
 +
 +==== drivers/macintosh/windfarm_core.c ====
 +
 +<code>
 +static int wf_critical_overtemp(void)
 +{
 +        static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
 +        char *argv[] = { (char *)critical_overtemp_path, NULL };
 +        static char *envp[] = { "HOME=/",
 +                                "TERM=linux",
 +                                "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
 +                                NULL };
 +
 +        return call_usermodehelper(critical_overtemp_path,
 +                                   argv, envp, UMH_WAIT_EXEC);
 +}
 +</code>
 +
 +==== init/do_mounts_initrd.c ====
 +
 +<code>
 +static void __init handle_initrd(void)
 +{
 +        struct subprocess_info *info;
 +        static char *argv[] = { "linuxrc", NULL, };
 +        extern char *envp_init[];
 +        int error;
 +
 +        real_root_dev = new_encode_dev(ROOT_DEV);
 +        create_dev("/dev/root.old", Root_RAM0);
 +        /* mount initrd on rootfs' /root */
 +        mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
 +        ksys_mkdir("/old", 0700);
 +        ksys_chdir("/old");
 +
 +        /*
 +         * In case that a resume from disk is carried out by linuxrc or one of
 +         * its children, we need to tell the freezer not to wait for us.
 +         */
 +        current->flags |= PF_FREEZER_SKIP;
 +
 +        info = call_usermodehelper_setup("/linuxrc", argv, envp_init,
 +                                         GFP_KERNEL, init_linuxrc, NULL, NULL);
 +        if (!info)
 +                return;
 +        call_usermodehelper_exec(info, UMH_WAIT_PROC);
 +        
 +        ...
 +</code>
 +
 +==== kernel/reboot.c ====
 +
 +<code>
 +char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
 +static const char reboot_cmd[] = "/sbin/reboot";
 +
 +static int run_cmd(const char *cmd)
 +{
 +        char **argv;
 +        static char *envp[] = {
 +                "HOME=/",
 +                "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
 +                NULL
 +        };
 +        int ret;
 +        argv = argv_split(GFP_KERNEL, cmd, NULL);
 +        if (argv) {
 +                ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
 +                argv_free(argv);
 +        } else {
 +                ret = -ENOMEM;
 +        }
 +
 +        return ret;
 +}
 </code> </code>
  • lk_usermodehelper.1550090004.txt.gz
  • Last modified: 2019/02/13 20:33
  • by rpjday