서버에서 스크립트를 실행 cron
하는 작업을 작성했습니다 . ssh
방금 스크립트를 실행해 보았지만 지금은 만족스럽지 않습니다.
client# ssh server.local /usr/local/bin/script
client#
server# /usr/local/bin/script
Segmentation fault (core dumped)
server#
client# ssh server.local /usr/local/bin/script
client# echo $?
255
/bin/sh
스크립트 인터프리터 (심볼릭 링크가 를 가리킴 ) 에서 충돌이 발생하는 것을 확인할 수 있습니다 /bin/dash
. 예를 들어, script &
서버에서 실행할 때 쉘은 백그라운드 작업의 PID가 30860이라고 알려줍니다 coredumpctl
. 즉, 충돌을 해결해야 하는데 이 질문은 그러한 충돌을 감지하는 방법에 관한 것입니다.
cron
작업에서 메시지를 인쇄할 때 "메일 보내기"를 통해 오류 보고를 지원합니다. 하지만 그것은종료 상태가 0이 아닌 메일을 보내지 않음. 따라서 현재 크론 작업에서는 이 오류에 대한 이메일을 보내지 않습니다. (그렇다면 "코드 255로 종료"보다 더 유용한 문제 해결 지침이 있으면 좋겠습니다.)
cron
"무소식은 좋은 소식이다"라는 유닉스 규칙을 따릅니다. 그러나 이 관례는 여기서 깨졌습니다.
나는 이것을 SSH의 한계로 해석합니다. 원격 명령에서 분할 오류를 항상 확인하려면 이 SSH 제한 사항을 해결하기 위해 어떤 규칙을 따라야 합니까?
(나도 이 제한에 '합당한 이유'가 있는지에도 관심이 있다. 어느 정도 알 것 같다.어떻게구현 수준에서 발생할 수 있습니다.)
답변1
% cat segfault.c
#include <stdio.h>
int main()
{
char *s = "hello world";
*s = 'H';
printf("%s\n", s);
}
% CFLAGS=-g make segfault
gcc -g segfault.c -o segfault
오류는 waitpid
일반적으로 쉘과 같은 호출을 수행하는 데서 발생합니다.
% ./segfault
zsh: bus error ./segfault
왜냐하면 여기에서 zsh
우리는 a를 종료 waitpid
한 다음 관련 코드 경로로 방황 했기 때문입니다 WIFSIGNALED
. (macOS는 segfault(다른 이름의 장미)가 아닌 버스 오류를 발생시키며 정확한 문자열 오류는 셸에 따라 다릅니다.)
OpenSSH Portable(커밋 ed7bd5d93fe14c7bd90febd29b858ea985d14d45 기준)은 WIFSIGNALED(status)
특히 misc.c
, session.c
및 에서 다양한 호출을 수행합니다 sshd.c
. 이들 중 일부는 return -1
관찰된 종료 상태로 쉽게 전환될 수 있지만 이것이 어떻게 발생하는지 정확히 이해하려면 255
디버깅이나 추적을 추가해야 합니다 ( 도움이 되지 않으며 기본 로그도 아닙니다).sshd
ssh -v -v -v
sshd
크루거로서 당신은 어떤 일이 waitpid
일어나도록 강요할 수 있습니다. 이를 위해서는 쉘이 exec
자체적으로 간단한 교체를 수행하지 않도록 속이는 것이 필요하며, 이는 가능한 경우 최적화 역할을 합니다.
% ssh localhost 'sh -c ./segfault'
% ssh localhost ':; ./segfault'
% ssh localhost 'sh -c ":; ./segfault"'
sh: line 1: 9068 Bus error: 10 ./segfault
%
:; ...
sh
그렇지 않을 fork
정도로 복잡 exec
하지만 결국에는 waitpid
이에 segfault
대해 보고합니다. 버그 보고서는 를 기반으로 작성됩니다 sh
.
% ssh localhost '/usr/local/bin/sh -c ":; ./segfault"'
Bus Error
이제 sh
그 자체로 인해 segfault(또는 예상치 못한 신호)가 발생하면 SSH의 종료 코드를 확인해야 합니다. 또 다른 옵션은 waitpid
쉘리스 트릭을 수행하기 위해 작은 래퍼를 호출하는 것입니다 .
#include <sys/wait.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int status;
pid_t pid;
if (argc < 2) {
fprintf(stderr, "Usage: waiter command [args ..]\n");
exit(1);
}
pid = fork();
if (pid < 0) {
err(1, "could not fork");
} else if (pid == 0) { /* child */
argv++;
execvp(*argv, argv);
err(1, "could not exec");
} else { /* parent */
if (waitpid(pid, &status, 0) < 0)
err(1, "could not waitpid");
if (WIFEXITED(status)) {
exit(WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
warnx("child exited with signal %d", WTERMSIG(status));
exit(128 + WTERMSIG(status));
} else {
err(1, "unknown waitpid condition?? status=%d", status);
}
}
exit(1);
}
...이 래퍼에도 불구하고반품특히 서버에 하드웨어 문제, 메모리 오류 등이 있는 경우 세그폴트(또는 모든 신호)가 발생할 수 있습니다.
% ssh localhost ./waiter ./segfault
waiter: child exited with signal 10
그러나 래퍼에는 일반적인 것보다 훨씬 적은 코드가 있으므로 sh
(가보 본 쉘의 코드는 약 50줄인 반면 가보 본 쉘의 코드는 약 10,000줄) 자체적으로 신호 종료 조건을 발생시킬 가능성은 없습니다. (종료코드는 확인하셨죠?)