때로는 다른 프로그램과 스크립트를 호출하는 쉘 스크립트를 호출하는 프로그램을 유지 관리해야 할 때도 있습니다. 따라서 기본 셸 스크립트가 종료 코드 126으로 끝나는 경우 해당 종료 코드를 설정한 호출 스크립트와 명령을 찾기가 어렵습니다.
해당 권한을 더 쉽게 확인할 수 있도록 종료 코드의 원인이 된 명령을 확인할 수 있는 방법이 있습니까?
답변1
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
추적 출력이원래stderr, 스크립트 내에서 stderr이 리디렉션되는 경우에도 마찬가지입니다. 그렇지 않으면 이러한 추적 메시지가 유사한 작업을 수행하는 경우 스크립트의 동작에 영향을 미칠 수 있습니다 (....) 2>&1 | ...
. 스크립트 자체가 fd 7을 명시적으로 사용하거나 닫지 않는다고 가정합니다(그럴 가능성은 낮지만 stderr를 리디렉션하는 것보다 훨씬 더 가능성이 높습니다).
답변2
이 옵션을 설정하면 set -x
추적 시 셸에서 실행된 명령이 표시됩니다.
답변3
이것은 약간의 해킹이지만 일부 C 코드를 shim으로 미리 로드하여 호출을 포착 exit(126)
하고 프로세스 그룹에 신호를 보낼 수 SIGSTOP
있습니다. 그러면 프로세스(및 동일한 그룹의 상위 프로세스)가 일시 중지됩니다.
예를 들어 shim에서 종료 코드 2를 캡처한 다음 ls
존재하지 않는 파일에서 실행하는 경우:
LD_PRELOAD=/home/meuh/shim_exit.so bash -c ' sh -c "ls -l xxx; echo"; echo '
메시지가 배경으로 표시됩니다
[1]+ Stopped ...
대기 상태의 프로세스를 볼 수 있습니다 T
.
~ $ 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);
}