스크립트 오류 처리: SSH에서 실행되는 스크립트에서 세그폴트를 보고해야 합니다.

스크립트 오류 처리: SSH에서 실행되는 스크립트에서 세그폴트를 보고해야 합니다.

서버에서 스크립트를 실행 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디버깅이나 추적을 추가해야 합니다 ( 도움이 되지 않으며 기본 로그도 아닙니다).sshdssh -v -v -vsshd

크루거로서 당신은 어떤 일이 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줄) 자체적으로 신호 종료 조건을 발생시킬 가능성은 없습니다. (종료코드는 확인하셨죠?)

관련 정보