Co może sprawić, że przekazanie init = / path / to / program do jądra nie uruchomi programu jako init?

13

Próbuję debugować skrypt inicjujący w systemie Linux; Próbuję przejść init=/bin/shdo jądra, aby uruchomić go shbez uruchamiania, initaby ręcznie uruchomić sekwencję inicjującą.

Odkryłem, że i tak jądro się uruchamia init. Podczas uruchamiania jeden z komunikatów printk jest wierszem poleceń, który pokazuje, że wiersz jest ustawiony poprawnie; ponadto mogę wpływać na inne rzeczy za pomocą wiersza polecenia jądra. Sprawdziłem, czy ścieżka istnieje; to robi.

To jest system busy, a init jest dowiązaniem symbolicznym do busybox; więc aby upewnić się, że busybox nie robi dziwnej magii, gdy jego PID wynosi 1, próbowałem również uruchomić program, który nie jest zajęty jako init; to też nie działało. Wydaje się, że bez względu na to, co robię, init jest uruchamiany.

Co może być przyczyną takiego zachowania?

Shawn J. Goff
źródło
Do jakiej podstawowej dystrybucji używa busybox init? Mogą po prostu ignorować wiersz poleceń ... możesz sprawdzić initrd i zobaczyć, co faktycznie robią skrypty.
Aaron D. Marasco
To nie jest żadna dystrybucja - to moja własna wersja; dlatego próbuję debugować skrypty inicjujące.
Shawn J. Goff,

Odpowiedzi:

3

Patrząc na źródło jądra Linuksa, widzę, że jeśli plik / init istnieje, jądro zawsze będzie próbowało uruchomić go przy założeniu, że wykonuje rozruch z ramdysku. Sprawdź swój system, aby sprawdzić, czy / init istnieje, jeśli tak, to prawdopodobnie jest to twój problem.

Kyle Jones
źródło
W rzeczywistości sprawdza execute_commandnajpierw, który pochodzi z init=parametru wiersza poleceń jądra . Jeśli nie może go wykonać, drukuje ostrzeżenie i próbuje uruchomić się initw różnych lokalizacjach. To jest w init/main.cfunkcji init_post(). Przejrzałem komunikaty printk jądra i znalazłem ostrzeżenie w wynikach mojego jądra, więc teraz muszę dowiedzieć się, dlaczego nie można go uruchomić / bin / sh lub cokolwiek innego, co próbuję uruchomić.
Shawn J. Goff,
Kod, na który patrzyłem (wydaje mi się, że v3.2.2) sprawdził zestaw ramdisk_execute_command, jeśli był wyłączony, a następnie próbował go uruchomić, więc nie możesz być tak aktualny. Szkoda, bo nie widziałem nic innego, co by to wyjaśniało.
Kyle Jones
Musisz użyć rdinitpodczas uruchamiania z ramdysku najwyraźniej: unix.stackexchange.com/a/430614/32558
Ciro Santilli 新疆 中心 中心 法轮功 六四 事件
8

initrd shenanigans

Jeśli używasz initrd lub initramfs, pamiętaj o następujących kwestiach:

  • rdinit= jest używany zamiast init=

  • jeśli rdinit=nie zostanie podany, próba ścieżki domyślne są: /sbin/init, /etc/init, /bin/inita /bin/shjednak nie/init

    Gdy nie używasz initrd, /initjest wypróbowywana pierwsza ścieżka, a następnie pozostałe.

v4.15 RTFS: wszystko jest zawarte w pliku https://github.com/torvalds/linux/blob/v4.15/init/main.c .

Najpierw dowiadujemy się, że:

  • execute_comand to cokolwiek jest przekazywane do: init=
  • ramdisk_execute_command to cokolwiek jest przekazywane do: rdinit=

jak widać z:

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);

gdzie __setupjest magiczny sposób obsługi parametrów wiersza poleceń.

start_kernel, „punkt wejścia” jądra, wywołania rest_init, które „wywołują” kernel_initwątek:

pid = kernel_thread(kernel_init, NULL, CLONE_FS);

Następnie kernel_init:

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.");
}

i kernel_init_freeablerobi:

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();
    }

DO ZROBIENIA: zrozum sys_access.

Zauważ też, że istnieją dalsze różnice między jednostkami RAM i nie-RAM, np. Obsługa konsoli: Różnica w wykonaniu init z osadzonymi vs. zewnętrznymi initramfs?

Ciro Santilli
źródło
0

Możesz dostosować jądro Linuksa i skompilować je ponownie. W przypadku jądra 4.9 edytuj funkcję „kernel_init” w pliku init / main.c i spróbuj najpierw uruchomić następujący wiersz:

try_to_run_init_process("/bin/sh")

Ponadto może to być spowodowane parametrami jądra przekazywanymi przez program BootLoader.

sale sypialne
źródło