Как получить дополнительную информацию о происхождении кода выхода?

Иногда мне нужно поддерживать программы, которые вызывают сценарии оболочки, которые вызывают другие программы и скрипты. Поэтому, когда основной скрипт оболочки заканчивается кодом выхода 126, необходимо выяснить, какой из вызываемых скриптов и команд задал этот код выхода.

Есть ли способ увидеть, какая команда была причиной кода выхода, чтобы было легче проверить его разрешения?

Если вы установите параметр set -x , вы увидите команды, которые выполняются в оболочке в виде трассировки.

Если в Linux вы можете запустить команду в режиме strace -fe process чтобы узнать, какой процесс выполнил exit_group(126) и какую команду он (или любой из ее родительских, если он ничего не выполнил сам), выполнил последний перед этим:

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

Выше, это был процесс 26326, который первым вышел из 126, потому что он попытался выполнить / . Это был ребенок процесса 26325, который последний раз выполнял sh -c / .

Если эти сценарии являются сценариями bash или если они являются sh скриптами, и sh – это bash на вашей системе, вы можете сделать следующее:

 $ 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 

Это не говорит нам, какой именно процесс выходил с 126, но может дать вам достаточно информации.

Мы используем BASH_TRACEFD=7 7>&2 чтобы трассы BASH_TRACEFD=7 7>&2 на исходный stderr, даже когда stderr перенаправляется внутри скриптов. В противном случае эти сообщения трассировки могут повлиять на поведение скриптов, если они выполняют такие вещи, как (....) 2>&1 | ... (....) 2>&1 | ... Это предполагает, что эти сценарии явно не используют или не закрывают сами fd 7 (это было бы маловероятно, намного более маловероятным, чем их перенаправление stderr).

Это немного взломать, но вы можете предварительно загрузить немного кода C в качестве прокладки, чтобы задержать вызов для exit(126) и заставить его выдавать сигнал SIGSTOP группе процессов, что приостанавливает процесс (и его родителей в той же группы).

Например, если мы улавливаем код выхода 2 в нашей прокладке, запустите ls в несуществом файле:

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

он будет опираться на сообщение

 [1]+ Stopped ... 

и вы можете видеть процессы в состоянии 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 

На этом этапе вы можете подключиться к процессам, если они отлаживаются, или просто переднем плане, или SIGCONT для продолжения процессов.

Вот код shim_exit.c, см. Комментарий C для компиляции.

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