Temos dois sistemas RHEL 6 que executam versões idênticas do kernel e da glibc (glibc – 2.12–1.90.el6_3.6). De acordo com o padrão POSIX e as man pages do Linux, se o kernel determinar que um arquivo executável não está em um formato executável como o ELF e não possui uma linha shebang ( #!
), as funções execl
, execlp
, execle
, execv
, execvp
e execvpe
(mas não execve
) tentarão executar esse arquivo usando um shell POSIX, que no Linux é /bin/sh
.
No entanto, em um dos sistemas, executar um script de shell cuja primeira linha é :
usando a função execvp
falha, enquanto na outra máquina o script é executado usando /bin/sh
, como esperado. Especificamente, estamos usando esses programas de teste; /tmp/test_script
é feito como executável:
x.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
extern char **environ;
int main () {
char *argv[] = { "/tmp/test_script", NULL };
char *progname = argv[0];
if (execvp(progname, argv) == -1) {
perror("execvp");
return -1;
}
}
/ tmp / test_script:
:
echo "Test script called"
exit 0
Pesquisamos o RPM de origem para a versão instalada do glibc e ele implementa definitivamente o comportamento desejado (o execvp é um wrapper trivial em torno do execvpe):
posix / execvpe.c:
if (strchr(file, '/') != NULL)
{
/* Don't search when it contains a slash. */
__execve(file, argv, envp);
if (errno == ENOEXEC)
{
/* Count the arguments. */
int argc = 0;
while (argv[argc++])
;
size_t len = (argc + 1) * sizeof(char *);
char **script_argv;
void *ptr = NULL;
if (__libc_use_alloca(len))
script_argv = alloca(len);
else
script_argv = ptr = malloc(len);
if (script_argv != NULL)
{
scripts_argv(file, argv, argc, script_argv);
__execve(script_argv[0], script_argv, envp);
free(ptr);
}
}
}
else
︙
Aqui, scripts_argv
é uma função simples que pré-acrescenta /bin/sh
à lista de argumentos, e __execve
é idêntico ao execve
que é exposto ao espaço do usuário via glibc.
Alguém mais encontrou esse problema no Linux? O comportamento está correto em todos os outros sistemas em que eu experimentei.