Comment get plus d'informations sur l'origine d'un code de sortie?

Parfois, j'ai besoin de maintenir des programmes qui invoquent des scripts shell qui invoquent d'autres programmes et scripts. Par conséquent, lorsque le script shell principal se termine par le code de sortie 126, il est difficile de savoir lequel des scripts et commands invoqués a défini ce code de sortie.

Existe-t-il un moyen de voir quelle est la raison du code de sortie pour faciliter la vérification de ses permissions?

Si vous définissez l'option set -x , vous voyez les commands qui s'exécutent dans le shell dans une trace.

Si sur Linux, vous pouvez exécuter la command strace -fe process pour savoir quel process a fait un exit_group(126) et quelle command lui (ou l'un de ses parents s'il n'exécutait rien lui-même) exécuté en dernier avant de le faire:

 $ 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 +++ 

Au-dessus, c'était le process 26326 qui s'est terminé avec 126, c'est parce qu'il a essayé d'exécuter / . C'était un enfant du process 26325 qui a exécuté sh -c / .

Si ces scripts sont des scripts bash ou s'ils sont des scripts sh et que sh arrive à être bash sur votre système, vous pourriez faire:

 $ 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 

Cela ne nous dit pas exactement quel process est sorti avec 126 mais pourrait vous donner assez d'indices.

Nous utilisons BASH_TRACEFD=7 7>&2 pour que les traces soient sorties sur le stderr d' origine , même si stderr est redirigé dans les scripts. Sinon, ces messages de trace pourraient affecter le comportement des scripts s'ils font des choses comme (....) 2>&1 | ... (....) 2>&1 | ... Cela suppose que ces scripts n'utilisent pas explicitement ou ne ferment pas eux-mêmes le fd7 (ce serait improbable, beaucoup plus improbable qu'eux redirigeant stderr).

C'est un peu un hack, mais vous pouvez précharger un peu de code C en tant que shim pour intercepter l'appel à la exit(126) et lui envoyer un signal SIGSTOP au groupe de process, ce qui mettra le process en pause le même groupe).

Par exemple, si nous interceptons le code de sortie 2 dans notre shim, exécutez ls sur un file non existant:

 LD_PRELOAD=/home/meuh/shim_exit.so bash -c ' sh -c "ls -l xxx; echo"; echo ' 

il se fondera avec le message

 [1]+ Stopped ... 

et vous pouvez voir les process dans le statut 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 

À ce stade, vous pouvez attacher aux process s'ils sont débogeables, ou simplement au premier plan ou SIGCONT les process pour continuer.

Voici le code shim_exit.c, voir le commentaire C pour la compilation.

 /* * 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 <ssortingng.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); }