出力リダイレクト先が消えるのを防ぎます

出力リダイレクト先が消えるのを防ぎます

出力がシリアルコンソールにリダイレクトされる組み込みLinuxデバイスで実行されるデーモンがあります。

my_daemon > /dev/ttyS0

ただし、ユーザーがexitシリアルインタフェースでシェルを実行すると、シリアルデバイスが再生成され、シリアルデバイスが一時的に消え、デーモンがクラッシュする可能性があります。

このようなことが起こらないようにする(簡単な)方法はありますか?パイプターゲットが消えた後に再接続を試みるラッパー(またはプロセスと組み合わせた名前付きパイプ)が途中にありますか? 「オフライン」時間をバッファリングする必要はありません。

答え1

次のシェル関数を使用します。

relay () (
#!/bin/sh
sink="${1:-/dev/ttyS0}"
exec 4<&0 2>/dev/null
while :; do
   cat 3<"$sink" >/proc/self/fd/3
   <&4 cat >/dev/null & sleep 2; kill -s PIPE "$!" || exit
done
)

このように:

my_daemon | relay

#!/bin/shshebang( ) のようなものが関数内では何の意味もないようです。コードだけがどのシェルをターゲットにしているかを示します。関数の代わりにスクリプトを作成するには、実行可能ファイルに関数本体を保存すると、shebangが機能します。

いくつかのヒントがあります:

  1. リダイレクトまたは同様のリダイレクトを使用するcat >/dev/ttyS0ソリューションには欠陥がある可能性があります。 「シリアルデバイスを再作成する」とは、/dev/ttyS0存在しない時間枠があることを意味すると仮定します。また、コードを実行しているユーザー(おそらくroot)がにアクセスできるとします/dev/>/dev/ttyS0そのファイルが存在しない場合は、通常のファイルが生成されます。これはテストできますが、! [ -e /dev/ttyS0 ]テストの前後にファイルが消えることがあります>/dev/ttyS0

    したがって、私はcat 3<"$sink" >/proc/self/fd/3基本的$sinkに使用します/dev/ttyS0。最初のリダイレクトは読み取り用にファイルを開こうとし、2番目のリダイレクトはstdoutを同じファイルにリダイレクトしようとします。秘密は、2番目のリダイレクトが新しいファイルを生成しないことです。

    /dev/ttyS0別のアプローチは、ユーザーがアクセスできるがファイルを生成できないように配置することです/dev/。この場合、このトリックは必要なく、使用されるソリューションは>/dev/ttyS0安全でなければなりません。何らかの理由でアクセスできない場合は、これが効果的な方法になる可能性があります/proc/self/fd/3。最初のものcatは次のとおりです。

    cat >"$sink"
    
  2. 最初の猫の目的は、受信者にデータを送信することです。リダイレクトが失敗したり、受信者が最終的に消えることがあります。ここで2番目のものがやってくるcat。 2番目の目的は、cat受信者がいないときにデータを削除することです。すぐに再生成する必要がある場合は、/dev/ttyS02番目の項目は必要ありませんcat。しかし、あなたの場合、パス名がすぐに再表示されるかどうかはわかりません。私の考えになければ、代わりに続け/dev/ttyS0たいと思います。my_daemon防ぐ。 2番目はcat続行されます。

    単純なcat >/dev/nullことは良い考えではなく、/dev/ttyS0再作成後も無期限に実行することができます。秘密は非同期で実行し、数秒後に終了することです。その後、コードが繰り返され、最初のコードがcat再びシンクを開こうとします。

    <&4能力が必要なので必要なものです。ジョブ制御が無効になると(デフォルトでは関数付きのサブシェルまたはスクリプト内にある)、コマンドは&標準入力がリダイレクトまたは/dev/null同等のファイルで終了します。前exec 4<&0とこれのおかげで、2番目の<&4ものはcatとにかく関数の標準入力から読み取ることができます。

  3. kill2番目のエントリがcatもう存在しない場合は失敗する可能性がありますsleep 2。出るとmy_daemonこれが起こります。kill … || exitEOF条件を検出する技術です。たとえば、date | relayDueにもかかわらず終了する必要がありますsleep。このトリックがない場合、コードは無限に繰り返されます。

    EOF の場合、2 番目の PID がcat再利用され、kill誤ったプロセスをターゲットにする可能性があります。 LinuxのAFAIK PIDは順次割り当てられます。シーケンスが約2秒で循環して同じPIDを取得する可能性はほとんどありません。トリックは、2番目のエントリがcatEOFのために早く終了したときにkillチェックしてno such process失敗するので、exitそのようなことが起こると仮定します。

デフォルト値はです$sink。最初のコマンドライン引数として指定して、/dev/ttyS0別のパス名(たとえば)を引き続き使用できます。… | relay foo一般ファイルをテストするには、ファイルが開いている間に削除する必要があります。それを破壊しないでください。私のテストでは、set -x何が起こっているのかをよく確認し、Ctrl+d catリクエスト時に簡単に終了(最初)

私のテストプラットフォーム:Linuxカーネル5.15.0、Busybox 1.30.1のsh()。ash

答え2

たぶん、次のようなものがあります。

バッファーペーストプログラム

#!/bin/sh

while true;do
  cat buffer_file > /dev/ttyS0
done

次に、次を実行します。

my_daemon > buffer_file
./bufferpaster.sh

答え3

いいですね。情報がなくても、strace有効なデバイスファイルを確認/待機できます。デーモンが改行(for read)で終わる行を出力すると仮定すると、次のようになります。

tty=/dev/ttyS0
my_daemon | ( 
    # Start with a read of 1 line, to catch when the daemon exits, and then "cat" the rest
    while read line; do 
        # check/wait for device file to exist and be writable
        until [ -w $tty ]; do sleep 1; done
        echo "$line"  >> $tty
        cat >> $tty
    done
)

>>テストではなくファイルを最初にテストしたい場合に備えて添付します。ttyS0

答え4

stdoutをログファイルに書き込み、initsystem inittab / systemd tail -Fログファイルをシリアルポートに送信します。 Init は、tail がシリアルポートで実行されていることを確認し、ユーザーはデーモンの作成に集中できます。ログローテーションを実行する必要がありますが、一部のユーティリティ(Piper、Multilogなど)を使用できます。以下は、stdoutをパイピングしてログファイルを生成する方法の完全な説明です。https://superuser.com/questions/291368/log-rotation-of-stdout)

関連情報