call_usermodehelper函数分析

 内核中的call_usermodehelper函数可以实现在内核空间调用用户空间的应用程序。

在linux内核中,实现关机的接口:__orderly_poweroff,该接口的主要作用是:在内核空间,调用用户空间的应用程序“/sbin/poweroff”,达到关机的目的。通过调该接口,可以实现在内核中实现“长按关机”操作。


char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";                 
                                                                             
static int __orderly_poweroff(bool force)                                    
{                                                                            
    char **argv;                                                             
    static char *envp[] = {                                                  
        "HOME=/",                                                            
        "PATH=/sbin:/bin:/usr/sbin:/usr/bin",                                
        NULL                                                                 
    };                                                                       
    int ret;                                                                 
                                                                             
    argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL);  //参数分解得到argv[0]为"/sbin/poweroff"                              
    if (argv) {                                                                                             
        ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); //调用关键接口                                     
        argv_free(argv);                                                     
    } else {                                                                 
        printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",     
                     __func__, poweroff_cmd);                                
        ret = -ENOMEM;                                                       
    }                                                                        
                                                                             
    if (ret && force) {                                                      
        //如果call_usermodehelper执行失败,则强制关机                                                                           
        emergency_sync();                                                                                        
        kernel_power_off();                                                                                    
    }                                                                        
                                                                             
    return ret;                                                              
}                                                                            

 

/**                                                                                    
 * call_usermodehelper() - 准备启动一个用户应用程序                   
 * @path: 待执行的用户程序的路径                                                
 * @argv: arg vector for process                                                       
 * @envp: environment for process                                                      
 * @wait: wait for the application to finish and return status.                        
 *        when UMH_NO_WAIT don't wait at all, but you get no useful error back         
 *        when the program couldn't be exec'ed. This makes it safe to call             
 *        from interrupt context.                                                                                                              
 */                                                                                    
int call_usermodehelper(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);                                       
}                                                                                      
EXPORT_SYMBOL(call_usermodehelper);                                                    

call_usermodehelper_setup函数会创建一个工作__call_usermodehelper,并将参数保存到结构体sub_info。

/**                                                                           
 * call_usermodehelper_setup - prepare to call a usermode helper              
 * @path: path to usermode executable                                         
 * @argv: arg vector for process                                              
 * @envp: environment for process                                             
 * @gfp_mask: gfp mask for memory allocation                                  
 * @cleanup: a cleanup function                                               
 * @init: an init function                                                    
 * @data: arbitrary context sensitive data                                    
 *                                                                            
 * Returns either %NULL on allocation failure, or a subprocess_info           
 * structure.  This should be passed to call_usermodehelper_exec to           
 * exec the process and free the structure.                                   
 *                                                                            
 * The init function is used to customize the helper process prior to         
 * exec.  A non-zero return code causes the process to error out, exit,       
 * and return the failure to the calling process                              
 *                                                                            
 * The cleanup function is just before ethe subprocess_info is about to       
 * be freed.  This can be used for freeing the argv and envp.  The            
 * Function must be runnable in either a process context or the               
 * context in which call_usermodehelper_exec is called.                       
 */                                                                           
struct subprocess_info *call_usermodehelper_setup(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);  //创建工作(核心)                     
    sub_info->path = path;                                                    
    sub_info->argv = argv;                                                    
    sub_info->envp = envp;                                                    
                                                                              
    sub_info->cleanup = cleanup;                                              
    sub_info->init = init;                                                    
    sub_info->data = data;                                                    
  out:                                                                        
    return sub_info;                                                          
}                                                                             
EXPORT_SYMBOL(call_usermodehelper_setup);                                     

 __call_usermodehelper完成了调用call_helper完成调用“用户空间程序”的核心工作。

 /* This is run by khelper thread  */                                    
 static void __call_usermodehelper(struct work_struct *work)             
 {                                                                       
     struct subprocess_info *sub_info =                                  
         container_of(work, struct subprocess_info, work);               
     int wait = sub_info->wait & ~UMH_KILLABLE;                          
     pid_t pid;                                                          
                                                                         
     /* CLONE_VFORK: wait until the usermode helper has execve'd         
      * successfully We need the data structures to stay around          
      * until that is done.  */                                          
     if (wait == UMH_WAIT_PROC)                                          
         pid = kernel_thread(wait_for_helper, sub_info,                  
                     CLONE_FS | CLONE_FILES | SIGCHLD);                  
     else {                                                              
         pid = kernel_thread(call_helper, sub_info,    //关键(核心)                   
                     CLONE_VFORK | SIGCHLD);                             
         /* Worker thread stopped blocking khelper thread. */            
         kmod_thread_locker = NULL;                                      
     }                                                                   
                                                                         
     switch (wait) {                                                     
     case UMH_NO_WAIT:                                                   
         call_usermodehelper_freeinfo(sub_info);                         
         break;                                                          
                                                                         
     case UMH_WAIT_PROC:                                                 
         if (pid > 0)                                                    
             break;                                                      
         /* FALLTHROUGH */                                               
     case UMH_WAIT_EXEC:                                                 
         if (pid < 0)                                                    
             sub_info->retval = pid;                                     
         umh_complete(sub_info);                                         
     }                                                                   
 }                                                                       

 call_helper调用____call_usermodehelper

 static int call_helper(void *data)                        
 {                                                         
     /* Worker thread started blocking khelper thread. */  
     kmod_thread_locker = current;                         
     return ____call_usermodehelper(data);  //调用的接口               
 }                                                         

 ____call_usermodehelper函数中通过do_execve运行用户程序  。用户空间可以通过调用execv来运行第一个参数指定的可执行程序。execv对应的系统调用接口是sys_execve。sys_execve同样也会调用do_execve。注意:sys_execve接口需要通过SYSCALL_DEFINE3(execve,...)来展开。

/*                                                                                      
 * This is the task which runs the usermode application                             
 */                                                                                     
static int ____call_usermodehelper(void *data)                                          
{                                                                                       
    struct subprocess_info *sub_info = data;                                            
    struct cred *new;                                                                   
    int retval;                                                                         
                                                                                        
    spin_lock_irq(&current->sighand->siglock);                                          
    flush_signal_handlers(current, 1);                                                  
    spin_unlock_irq(&current->sighand->siglock);                                        
                                                                                        
    /* We can run anywhere, unlike our parent keventd(). */                             
    set_cpus_allowed_ptr(current, cpu_all_mask);                                        
                                                                                        
    /*                                                                                  
     * Our parent is keventd, which runs with elevated scheduling priority.             
     * Avoid propagating that into the userspace child.                                 
     */                                                                                 
    set_user_nice(current, 0);                                                          
                                                                                        
    retval = -ENOMEM;                                                                   
    new = prepare_kernel_cred(current);                                                 
    if (!new)                                                                           
        goto fail;                                                                      
                                                                                        
    spin_lock(&umh_sysctl_lock);                                                        
    new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);                  
    new->cap_inheritable = cap_intersect(usermodehelper_inheritable,                    
                         new->cap_inheritable);                                         
    spin_unlock(&umh_sysctl_lock);                                                      
                                                                                        
    if (sub_info->init) {                                                               
        retval = sub_info->init(sub_info, new);                                         
        if (retval) {                                                                   
            abort_creds(new);                                                           
            goto fail;                                                                  
        }                                                                               
    }                                                                                   
                                                                                        
    commit_creds(new);                                                                  
                                                                                        
    retval = do_execve(sub_info->path,     //关键!                                               
               (const char __user *const __user *)sub_info->argv,                       
               (const char __user *const __user *)sub_info->envp);                      
    if (!retval)                                                                        
        return 0;                                                                       
                                                                                        
    /* Exec failed? */                                                                  
fail:                                                                                   
    sub_info->retval = retval;                                                          
    do_exit(0);                                                                         
}                                                                                       

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值