3

基本的に特定のインターフェイスとポートで tcpdump を実行し、キャプチャされた最初の 10 個のパケットで特定の文字列を探す小さな Nagios 監視スクリプトを開発しました。システムを監視していますが、システムがハングし、特定のメッセージでサーバーがフラッディングされる可能性があります。

私はプロの Perl プログラマーではありませんが、できる限りの期待に応えてきたと思います。

このスクリプトをローカルで実行すると問題なく終了し、コンソールが返されます。ただし、Nagios サーバー経由で ssh (ssh user@host -i private_key '/path/script.pl') 経由で実行しようとすると、スクリプトは正常に実行され、終了メッセージが表示されますが、ssh は実行されません。出口。bash を返すには、Ctrl+C を押すか、リターンを数回押す必要があります。これを check_by_ssh で実行すると、明らかな理由でプラグインのタイムアウト エラーが発生します。

私が使用している fork() と関係があると確信していますが、何が問題なのかわかりません。

#!/usr/bin/perl -w
use strict;
use warnings;
use Getopt::Long;

my $RC_OK = 0;
my $RC_WARNING = 1;
my $RC_CRITICAL = 2;
my $RC_UNKNOWN = 3;

my $GREP_RC = undef;

my $PORT = undef;
my $INT = undef;
my $STRING = undef;

my $PID = undef;

# Handler principal de alarme de timeout
$SIG{ALRM} = sub {
    print "UNKNOWN: Main script timed out!\n";
    exit $RC_UNKNOWN;
};

# Inicio contagem global
alarm(8);

# Coleta parametros
GetOptions ("port=s" => \$PORT,
            "interface=s" => \$INT,
            "string=s" => \$STRING);

# Sanity check de parametros
if((not defined $PORT) || (not defined $STRING)) {
    print "Usage: ./check_stratus.pl -p=PORT -i=INTERFACE -s=STRING\n";
    exit $RC_UNKNOWN;
}

# Capturando pelo tcpdump
defined($PID = fork()) or die "Problema ao criar o fork: $!\n";
if ($PID == 0) {

    # Handler secundario de alarme de timeout
    $SIG{ALRM} = sub {
        exit 1;
    };

    # Captura no maximo por 5 segundos, ou 10 pacotes
    alarm(5);

    `sudo /usr/sbin/tcpdump -nX -s 2048 -c 10 -i $INT port $PORT > /tmp/capture.txt 2>&1`;

    # Checando se o tcpdump rodou com sucesso
    if ($? != 0) {
        print "Erro ao executar \"/usr/sbin/tcpdump -nX -s 2048 -c 1 -i $INT port $PORT > /tmp/capture.txt\", verifique o arquivo de saida para mais detalhes.\n";
        exit $RC_UNKNOWN;
    }
    exit $RC_OK;
}

# Espera o filho encerar...
waitpid($PID, 0);

# Verificando se o arquivo capturado esta ok
`/bin/ls /tmp/capture.txt`;

if ($? != 0) {
    print "Erro ao encontrar o arquivo /tmp/capture.txt\n";
    exit $RC_UNKNOWN;
}

# Executando grep da string em cima da captura
`/bin/grep $STRING /tmp/capture.txt`;

# Verificando resultado do grep
if ($? == 0) {
    print "Foi encontrada a string \"$STRING\" na captura do tcpdump escutando na interface $INT e na porta $PORT!\n";
    exit $RC_CRITICAL;
}

if ($? == 256) {
    print "Nao foi encontrada a string \"$STRING\" na captura do tcpdump escutando na interface $INT e na porta $PORT.\n";
    exit $RC_OK;
} else {
    print "Erro desconhecido! Codigo do grep foi $?\n";
    exit $RC_UNKNOWN;
}

どんな助けでも大歓迎です。

ありがとうございました!

4

1 に答える 1

2

ここを見て:

#!/usr/bin/perl
use strict;
my $PID;
defined($PID = fork()) or die "no fork works";
if ($PID == 0) {

    # Handler secundario de alarme de timeout
    $SIG{ALRM} = sub {
        exit 1;
    };

    # Captura no maximo por 5 segundos, ou 10 pacotes
    alarm(1);
    `sleep 100`;
}
waitpid($PID, 0);

/tmp$ ps xawww |grep sleep
 1705 pts/2    S+     0:00 grep sleep
host:/tmp$ time /tmp/test.pl

real    0m1.008s
user    0m0.000s
sys     0m0.004s
host:/tmp$ ps xawww |grep sleep
 1708 pts/2    S      0:00 sleep 100
 1710 pts/2    S+     0:00 grep sleep

システムが新しいプロセスをフォークし、そのプロセスが親プロセスからシグナルを取得しないために、問題が発生します。

解決策は、新しいプロセスをフォークしない代わりにexec()使用するだけです。``system()exec()

    alarm(1);
    exec("sleep 100");
于 2012-11-22T21:02:23.073 に答える