Ambiente separado com diferentes / bin / sh

1

Eu tenho um monte de scripts de shell que assumem incorretamente /bin/sh para ser equivalente a /bin/bash . Por exemplo, eles têm o #!/bin/sh shebang, mas usam o comando source em vez de . (ponto).

Eu rodei o Ubuntu 16, onde /bin/sh links para dash e, portanto, bash-isms não são suportados.

Eu preciso executar os scripts periodicamente. Além disso, de vez em quando eu precisarei atualizá-los do autor original, que não está em consertar esse bug em particular. Eu gostaria de evitar consertar todos esses arquivos (há um monte deles, eles não são meus, e eu vou perder todas as alterações após a atualização). Além disso, gostaria de evitar fazer alterações globais no sistema, já que ele poderia potencialmente quebrar outros scripts.

Existe uma maneira de criar um ambiente (temporário ou não) com /bin/sh apontando para o bash, para ser usado para esses scripts, sem tocar no sistema global /bin/sh ?

    
por Alex Che 11.09.2018 / 16:07

5 respostas

1

Eu consegui chegar perto do que eu queria inicialmente usando monte namespaces . (Minha solução original usou unionfs também, mas como acabou não sendo necessário). Ele é usado para vincular a montagem /bin/bash a /bin/sh para um conjunto limitado de processos. O procedimento curto para configurar um novo shell, onde sh é bash , é descrito abaixo.

Primeiro, iniciamos um novo shell com um namespace de montagem isolado:

sudo unshare -m sudo -u user /bin/bash

E, no novo shell, vinculamos-mount /bin/bash a /bin/sh :

sudo mount --bind /bin/bash /bin/sh

É isso aí!

Vamos ver o que temos neste shell:

user@ubuntu:~$ /bin/sh --version
GNU bash, version ...
user@ubuntu:~$ diff -s /bin/sh /bin/bash
Files /bin/sh and /bin/bash are identical

Mas se estiver executando em outro shell:

user@ubuntu:~$ /bin/sh --version
/bin/sh: 0: Illegal option --
user@ubuntu:~$ diff -s /bin/sh /bin/bash
Binary files /bin/sh and /bin/bash differ
user@ubuntu:~$ diff -s /bin/sh /bin/dash
Files /bin/sh and /bin/dash are identical
    
por 12.09.2018 / 12:14
4

Você pode corrigi-los facilmente, não quebre seu sistema!

find . -name '*.sh' -type f -exec sed -i '1s|^#! */bin/sh|#!/bin/bash|' {} +
    
por 11.09.2018 / 16:14
3

Suponho que os namespaces de montagem ou semelhantes possam ser usados para que processos / usuários diferentes tenham uma ideia diferente do que é /bin/sh .

Mas isso soa hack e também pode contar como "fazer mudanças permanentes no sistema". Provavelmente seria mais fácil apenas fazer essa correção de uma linha. Faça essa parte do seu processo de atualização e publique um relatório de bug e um patch sobre o upstream do hashbang errado.

Com o GNU sed , algo assim deve ser feito para corrigi-los:

sed -i -e '1s,^#! */bin/sh,#!/bin/bash,' /all/the/scripts/*
    
por 11.09.2018 / 18:04
3

Se /bin/sh - > /bin/dash é um executável vinculado dinamicamente no seu sistema como no meu (você pode verificar isso com o arquivo (1)), você pode usar um LD_PRELOAD hack para isso.

Funciona assim: Uma pequena biblioteca dinâmica carregada com LD_PRELOAD substitui o __libc_start_main do glibc (a função que chama a função main() do executável) e, se argv[0] == /bin/sh , executa /bin/bash do execut mesmos argumentos (exceto para argv[0] ); caso contrário, chama o original __libc_start_main como se nada tivesse acontecido.

$ cat sh_is_bash.c
#define _GNU_SOURCE     /* for RTLD_NEXT */
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <err.h>
int __libc_start_main(
        int (*main)(int,char**,char**), int ac, char **av,
        int (*init)(int,char**,char**), void (*fini)(void),
        void (*rtld_fini)(void), void *stack_end)
{
        typeof(__libc_start_main) *real_lsm;
        if(ac > 0 && !strcmp(av[0], "/bin/sh")){
                av[0] = "/bin/bash";
                execv(av[0], av);
                err(1, "execv %s", av[0]);
        }else if(real_lsm = dlsym(RTLD_NEXT, "__libc_start_main"))
                return real_lsm(main, ac, av, init, fini, rtld_fini, stack_end);
        else
                errx(1, "BUG: dlsym: %s", dlerror());
}
$ cc -fPIC -shared -Wall -W -Wno-parentheses sh_is_bash.c -o sh_is_bash.so -ldl
$ LD_PRELOAD='pwd'/sh_is_bash.so program ...

Qualquer script com #! /bin/sh shebang será executado com /bin/bash em vez de /bin/sh quando a variável de ambiente LD_PRELOAD contiver o caminho absoluto de sh_is_bash.so .

Isso é feio, mas não requer alterações significativas no seu sistema ou nos scripts, é fácil implantar & gerenciar, e não precisa de privilégios especiais para isso.

    
por 12.09.2018 / 14:19
0

Os scripts que contêm extensões não POSIX devem ter o cabeçalho #! correto relacionado, no seu caso:

#!/bin/bash

então não vejo como editar todos os scripts incorretos.

BTW: se você tem certeza, pode criar um link temporário para o bash e renomeá-lo:

cd /bin
ln -s bash nsh
mv nsh sh

Como mv funciona atomicamente, isso garante que sempre haja um /bin/sh

em funcionamento

Como resultado, os scripts e outros shells atualmente em execução enquanto você faz a renomeação continuariam a funcionar e após a renomeação, isso chamaria bash ao invés de traço.

Se o seu sistema, no entanto, for executado de uma forma que permita editar os scripts, prefiro apenas editar os scripts.

Se você substituir /bin/sh por um link para bash , não se esqueça de corrigir isso para tornar /bin/sh um link para dash novamente depois que você terminar.

Se os scripts em questão fizerem parte de um pacote binário, não esqueça de fazer um relatório de bug contra esse problema no seu upstream.

    
por 11.09.2018 / 16:14