O que pode fazer com que a passagem de init = / path / to / program para o kernel não inicie o programa como init?

13

Estou tentando depurar um script init em um sistema Linux; Estou tentando passar init=/bin/shpara o kernel para iniciá-lo shsem iniciar, initpara que eu possa executar manualmente a sequência init.

O que eu descobri é que o kernel está iniciando de initqualquer maneira. Durante a inicialização, uma das mensagens printk é a linha de comando e isso mostra que a linha está sendo definida corretamente; além disso, posso afetar outras coisas usando a linha de comando do kernel. Eu verifiquei para garantir que o caminho existe; faz.

Este é um sistema de busybox, e o init é um link simbólico para o busybox; portanto, para garantir que o busybox não faça mágica estranha quando seu PID é 1, também tentei executar um programa não-busybox como init; isso também não funcionou. Parece que não importa o que eu faça, o init é executado.

O que poderia estar causando esse comportamento?

Shawn J. Goff
fonte
Qual é a distribuição básica que está usando o busybox init? Eles podem simplesmente estar ignorando a linha de comando ... você pode examinar o initrd e ver o que os scripts estão realmente fazendo.
Aaron D. Marasco 31/01
Não é nenhuma distro - é minha própria construção; é por isso que estou tentando depurar os scripts init.
Shawn J. Goff

Respostas:

3

Observando a fonte do kernel do Linux, vejo que, se o arquivo / init existir, o kernel sempre tentará executá-lo com a suposição de que está executando uma inicialização por ramdisk. Verifique seu sistema para ver se / init existe, se existe, provavelmente esse é seu problema.

Kyle Jones
fonte
Na verdade, ele verifica execute_commandprimeiro, que vem do init=parâmetro de linha de comando do kernel . Se não puder executá-lo, ele imprime um aviso e tenta executar initem vários locais. Isso está na init/main.cfunção init_post(). Examinei as mensagens do kernel printk e encontrei o aviso na saída do meu kernel, então agora tenho que descobrir por que ele não pode iniciar / bin / sh ou qualquer outra coisa que eu tente iniciar.
Shawn J. Goff
O código que eu observei (v3.2.2 eu acho) verificou set ramdisk_execute_command se não estava definido e, em seguida, tentou executá-lo, portanto você não deve ser tão atual. Que pena, porque eu não vi mais nada que explicasse isso.
Kyle Jones
rdinitAparentemente, você deve usar ao inicializar a partir do ramdisk: unix.stackexchange.com/a/430614/32558
Ciro Santilli
8

travessuras initrd

Se você estiver usando initrd ou initramfs, lembre-se do seguinte:

  • rdinit= é usado em vez de init=

  • se rdinit=não é dado, os caminhos padrão tentativas são: /sbin/init, /etc/init, /bin/inite /bin/shnão, mas/init

    Quando não /initestiver usando o initrd, é o primeiro caminho tentado, seguido pelos outros.

v4.15 RTFS: tudo está contido no arquivo https://github.com/torvalds/linux/blob/v4.15/init/main.c .

Primeiro aprendemos que:

  • execute_comand é o que for passado para: init=
  • ramdisk_execute_command é o que for passado para: rdinit=

como pode ser visto em:

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

onde __setupé uma maneira mágica de lidar com os parâmetros da linha de comando.

start_kernel, o kernel "ponto de entrada", chama rest_init, que "chama" kernel_initem um thread:

pid = kernel_thread(kernel_init, NULL, CLONE_FS);

Então, kernel_initfaz:

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

e kernel_init_freeablefaz:

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: entenda sys_access.

Observe também que existem outras diferenças entre inits ram e inits não ram, por exemplo, manipulação de console: Diferença na execução do init com initramfs incorporado e externo?

Ciro Santilli adicionou uma nova foto
fonte
0

Você pode personalizar seu kernel do Linux e recompilá-lo. Para o kernel 4.9, edite a função "kernel_init" em init / main.c e tente executar a seguinte linha primeiro:

try_to_run_init_process("/bin/sh")

Além disso, pode ser causado pelos parâmetros do kernel passados ​​pelo BootLoader.

muchrooms
fonte