Se você definir a opção set -x
, verá os comandos executados no shell em um rastreio.
Às vezes, preciso manter programas que invoquem scripts de shell que invoquem outros programas e scripts. Portanto, quando o shell script principal termina com o código de saída 126, é difícil descobrir quais dos scripts e comandos invocados definem esse código de saída.
Existe uma maneira de ver qual comando foi o motivo do código de saída para facilitar a verificação de suas permissões?
Se você definir a opção set -x
, verá os comandos executados no shell em um rastreio.
Se no Linux, você poderia executar o comando em strace -fe process
para saber qual processo executou exit_group(126)
e qual comando (ou qualquer um de seus pais, se não executou nada) executado antes de fazer isso:
$ strace -fe process sh -c 'env sh -c /; exit'
execve("/bin/sh", ["sh", "-c", "env sh -c /; exit"], [/* 53 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f24713b1700) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24713b19d0) = 26325
strace: Process 26325 attached
[pid 26324] wait4(-1, <unfinished ...>
[pid 26325] execve("/usr/bin/env", ["env", "sh", "-c", "/"], [/* 53 vars */]) = 0
[pid 26325] arch_prctl(ARCH_SET_FS, 0x7fbdb4e2c700) = 0
[pid 26325] execve("/bin/sh", ["sh", "-c", "/"], [/* 53 vars */]) = 0
[pid 26325] arch_prctl(ARCH_SET_FS, 0x7fef90b3b700) = 0
[pid 26325] clone(strace: Process 26326 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fef90b3b9d0) = 26326
[pid 26325] wait4(-1, <unfinished ...>
[pid 26326] execve("/", ["/"], [/* 53 vars */]) = -1 EACCES (Permission denied)
sh: 1: /: Permission denied
[pid 26326] exit_group(126) = ?
[pid 26326] +++ exited with 126 +++
[pid 26325] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 126}], 0, NULL) = 26326
[pid 26325] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26326, si_uid=10031, si_status=126, si_utime=0, si_stime=0} ---
[pid 26325] exit_group(126) = ?
[pid 26325] +++ exited with 126 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 126}], 0, NULL) = 26325
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26325, si_uid=10031, si_status=126, si_utime=0, si_stime=0} ---
exit_group(126) = ?
+++ exited with 126 +++
Acima, esse foi o processo 26326 que saiu pela primeira vez com 126, porque ele tentou executar /
. Foi um filho do processo 26325 que executou pela última vez sh -c /
.
Se esses scripts forem bash
scripts ou se forem sh
scripts e sh
for bash
em seu sistema, você poderá:
$ env SHELLOPTS=xtrace \
BASH_XTRACEFD=7 7>&2 \
PS4='[$?][$BASHPID|${BASH_SOURCE:-$BASH_EXECUTION_STRING}|$LINENO]+ ' \
sh -c 'env sh -c /; exit'
[0][30625|env sh -c /; exit|0]+ env sh -c /
[0][30626|/|0]+ /
sh: /: Is a directory
[126][30625|env sh -c /; exit|0]+ exit
Isso não nos diz exatamente qual processo foi encerrado com 126, mas pode dar pistas suficientes.
Usamos BASH_TRACEFD=7 7>&2
para que os rastreios sejam exibidos no stderr original , mesmo quando o stderr é redirecionado dentro dos scripts. Caso contrário, essas mensagens de rastreamento podem afetar o comportamento dos scripts se fizerem coisas como (....) 2>&1 | ...
. Isso supõe que esses scripts não usam ou fecham explicitamente o fd 7 (isso seria improvável, muito mais improvável do que eles redirecionando o stderr).
É um truque, mas você pode pré-carregar um pouco de código C como um shim para capturar a chamada para exit(126)
e fazer com que emita um sinal SIGSTOP
para o grupo de processos, o que pausará o processo ( e seus pais no mesmo grupo).
Por exemplo, se capturarmos o código de saída 2 em nosso shim, execute ls
em um arquivo não existente:
LD_PRELOAD=/home/meuh/shim_exit.so bash -c ' sh -c "ls -l xxx; echo"; echo '
será o próprio plano de fundo com a mensagem
[1]+ Stopped ...
e você pode ver os processos no status T
wait:
~ $ ps f
PID TTY STAT TIME COMMAND
30528 pts/3 T 0:00 \_ bash -c sh -c "ls -l xxx;echo";echo
30529 pts/3 T 0:00 | \_ sh -c ls -l xxx;echo
30530 pts/3 T 0:00 | \_ ls -l xxx
Nesse estágio, você pode anexar aos processos se eles forem depuráveis, ou simplesmente foreground ou SIGCONT, para que os processos continuem.
Aqui está o código shim_exit.c, veja o comentário C para compilar.
/*
* capture calls to a routine and replace with your code
* http://unix.stackexchange.com/a/308694/119298
* gcc -Wall -O2 -fpic -shared -ldl -o shim_exit.so shim_exit.c
* LD_PRELOAD=/home/meuh/shim_exit.so ./test
*/
#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <dlfcn.h>
/* doesnt work for syscall exit_group() */
void exit(int status){
static void (*real_exit)(int status) = NULL;
if (!real_exit) {
real_exit = dlsym(RTLD_NEXT, "exit");
char *error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
_exit(1);
}
}
if (status==126/* || status==2*/)kill(0,SIGSTOP);
real_exit(status);
}