What can make passing init=/path/to/program to the kernel not start program as init?
I’m trying to debug an init script on a Linux system; I’m trying to pass init=/bin/sh
to the kernel to make it start sh
without starting init
so I can run through the init sequence manually.
What I’ve found is that the kernel is starting init
anyway. During bootup, one of the printk messages is the command line, and that is showing that the line is being set properly; in addition, I can affect other things using the kernel command line. I have checked to make sure the path exists; it does.
This is a busybox system, and init is a symlink to busybox; so to make sure busybox doesn’t do strange magic when its PID is 1, I also tried running a non-busybox program as init; that didn’t work, either. It seems that no matter what I do, init is run.
What could be causing this behavior?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
initrd shenanigans
If you are using initrd or initramfs, keep in mind the following:
-
rdinit=
is used instead ofinit=
-
if
rdinit=
is not given, the attempted default paths are:/sbin/init
,/etc/init
,/bin/init
and/bin/sh
but not/init
When not using initrd,
/init
is the first path tried, followed by the others.
v4.15 RTFS: everything is contained inside the https://github.com/torvalds/linux/blob/v4.15/init/main.c file.
First we learn that:
execute_comand
is whatever is passed to:init=
ramdisk_execute_command
is whatever is passed to:rdinit=
as can be seen from:
static int __init init_setup(char *str) { unsigned int i; execute_command = str; /* * In case LILO is going to boot us with default command line, * it prepends "auto" before the whole cmdline which makes * the shell think it should execute a script with such name. * So we ignore all arguments entered _before_ init=... [MJ] */ for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("init=", init_setup); static int __init rdinit_setup(char *str) { unsigned int i; ramdisk_execute_command = str; /* See "auto" comment in init_setup */ for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("rdinit=", rdinit_setup);
where
__setup
is a magic way of handling up command line parameters.start_kernel
, the kernel “entry point”, calls rest_init
, which “calls” kernel_init
on a thread:
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
Then,
kernel_init
does:static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); [...] if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)n", ramdisk_execute_command, ret); } [...] if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/admin-guide/init.rst for guidance."); }
and
kernel_init_freeable
does:static noinline void __init kernel_init_freeable(void) { [...] if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); }
TODO: understand
sys_access
.Also note that there are further differences between ram inits and non-ram inits, e.g. console handling: Difference in execution of init with embedded vs. external initramfs?
Method 2
On
https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
I found:
When debugging a normal root filesystem, it’s nice to be able to boot with
“init=/bin/sh”. The initramfs equivalent is “rdinit=/bin/sh”, and it’s
just as useful.
So probably try ridinit=/bin/sh
Method 3
Looking at Linux kernel source, I see that if the file /init exists, the kernel will always attempt to run it on the assumption that it’s doing a ramdisk boot. Check your system to see if /init exists, if it does, then that’s probably your problem.
Method 4
You can custom your Linux kernel and recompile it. For 4.9 kernel, Edit the function “kernel_init” in init/main.c and try to run the following line first:
try_to_run_init_process("/bin/sh")
In addition, It might be caused by the kernel parameters passed by BootLoader.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0