Como obter mais informações sobre a origem de um código de saída?

5

À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?

    
por Cani 08.09.2016 / 11:53

3 respostas

5

Se você definir a opção set -x , verá os comandos executados no shell em um rastreio.

    
por 08.09.2016 / 11:55
5

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

    
por 08.09.2016 / 14:56
4

É 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);
}
    
por 08.09.2016 / 18:11