スクリプトエラーの処理: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ジョブからメッセージを印刷するときに「メールを送信」を介してエラー報告をサポートします。しかしそれは終了ステータスがゼロ以外のメールを送信しない。したがって、現在のクローン操作では、このエラーに関する電子メールは送信されません。 (そうであれば、「コード255で終了」よりも便利なトラブルシューティング手順があると思います。)

cron「ニュースは良いニュースです」というUnixの規則に従います。しかし、この習慣はここで壊れています。

私はこれを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.csession.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行)、それ自体が信号終了条件を発生する可能性はありません。 (終了コードは確認しましたか?)

おすすめ記事