I checked out Kernel APIs, Part 1: Invoking user - space applications from the kernel, and Executing a user-space function from the kernel space - Stack Overflow - and here is a small kernel module, callmodule.c
, demonstrating that:
// http://people.ee.ethz.ch/~arkeller/linux/code/usermodehelper.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
static int __init callmodule_init(void)
{
int ret = 0;
char userprog[] = "/path/to/mytest";
char *argv[] = {userprog, "2", NULL };
char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
printk("callmodule: init %s\n", userprog);
/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/
/* returns 0 if usermode process was started successfully, errorvalue otherwise*/
/* no possiblity to get return value of usermode process*/
ret = call_usermodehelper(userprog, argv, envp, UMH_WAIT_EXEC);
if (ret != 0)
printk("error in call to usermodehelper: %i\n", ret);
else
printk("everything all right\n");
return 0;
}
static void __exit callmodule_exit(void)
{
printk("callmodule: exit\n");
}
module_init(callmodule_init);
module_exit(callmodule_exit);
MODULE_LICENSE("GPL");
... with Makefile
:
obj-m += callmodule.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
When I run this via sudo insmod ./callmodule.ko && sudo rmmod callmodule
, I get in /var/log/syslog
:
Feb 10 00:42:45 mypc kernel: [71455.260355] callmodule: init /path/to/mytest
Feb 10 00:42:45 mypc kernel: [71455.261218] everything all right
Feb 10 00:42:45 mypc kernel: [71455.286131] callmodule: exit
... which apparently means all went fine. (Using Linux 2.6.38-16-generic #67-Ubuntu SMP)
My question is - how can I get the PID of the process instantiated from a kernel module? Is there a similar process other than call_usermodehelper
, that will allow me to instantiate a user-space process in kernel space, and obtain its pid?
Note that it may not be possible to use call_usermodehelper
and get the instantiated process PID:
Re: call_usermodehelper's pid ? — Linux Kernel Newbies
I want to create a user space process from within a kernel module, and be able to kill it, send signals to it, etc...
can I know its pid ?
No, you can't. But since inside the implementation the pid is known, patch that makes it available would not be too hard (note, that errors are always negative in kernel and pids are positive, limited to 2**16). You would have to modify all callers that expect 0 on success though.
I poked around the sources a bit, and it seems ultimately there is a call chain: call_usermodehelper
-> call_usermodehelper_setup
-> __call_usermodehelper
, which looks like:
static void __call_usermodehelper(struct work_struct *work)
{
struct subprocess_info *sub_info =
container_of(work, struct subprocess_info, work);
// ...
if (wait == UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
pid = kernel_thread(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
...
... so a PID of a kernel thread is used, but it is saved nowhere; also, neither work_struct
nor subprocess_info
have a pid
field (task_struct
does, but nothing here seems to use task_struct
). Recording this pid would require changing the kernel sources - and as I'd like to avoid that, this is the reason why I'm also interested in approaches other than call_usermodehelper
...
A tentative answer from my understanding of the implementation in kmod.c.
If you look at the code of
call_usermodehelper
you will see it callscall_usermodehelper_setup
and thencall_usermodehelper_exec
.call_usermodehelper_setup
takes as parameter an init function that will be executed just before thedo_execve
. I believe the value ofcurrent
when the init function gets executed will get you thetask_struct
of the user process.So to get the pid you will need to:
call_usermodehelper
in your code.call_usermodehelper_setup
.task_struct
and in turn the PID.Well, this was tedious... Below is a rather hacky way to achieve this, at least on my platform, as
callmodule.c
(same Makefile as above can be used). As I cannot believe that this is the way this should be done, more proper answers are still welcome (hopefully, also, with a code example I could test). But at least, it does the job as a kernel module only - without the need to patch the kernel itself - for the 2.6.38 version, which was quite important to me.Basically, I copied all functions (renamed with a "B" suffix), until the point where the PID is available. Then I use a copy of
subprocess_info
with an extra field to save it (although that is not strictly necessary: in order not to mess with function signatures in respect to return value, I have to save the pid as a global variable anyway; left it as an exercise). Now, when I runsudo insmod ./callmodule.ko && sudo rmmod callmodule
, in/var/log/syslog
I get:One of the nasty problems here, is that once you start copying functions, at a certain time you come to a point, where kernel functions are used which are not exported, like in this case
wait_for_helper
. What I did was basically look in/proc/kallsyms
(remembersudo
!) to get absolute addresses for e.g.wait_for_helper
, then hardcoded those in the kernel module as function pointers - seems to work. Another problem is that functions in kernel source refer toenum umh_wait
, which cannot be used as argument from the module (those need to simply be converted to useint
instead).So the module starts the user-space process, gets the PID (noting that "What the kernel calls PIDs are actually kernel-level thread ids (often called TIDs) ... What's considered a PID in the POSIX sense of "process", on the other hand, is called a "thread group ID" or "TGID" in the kernel."), gets the corresponding
task_struct
and its parent, and tries to list all children of the parent and of the spawned process itself. So I can see thatkworker/u:1
is typically the parent, and it has no other children thanmytest
- and sincemytest
is very simple (in my case, just a single write to disk file), it spawns no threads of its own, so it has no children either.I encountered a couple of Oopses which required a reboot - I think they are solved now, but just in case, caveat emptor.
Here is the
callmodule.c
code (with some notes/links at end):