実行可能ファイルに複数のシンボリックリンクがある場合にラッパースクリプトを使用して呼び出しを記録する方法

実行可能ファイルに複数のシンボリックリンクがある場合にラッパースクリプトを使用して呼び出しを記録する方法

簡単に言えば、いくつかのシステムの動作を追跡するために特定の実行可能ファイルが呼び出される方法を追跡したいと思います。実行可能ファイルがあるとしましょう。

/usr/bin/do_stuff

実際、シンボリックリンクを介してさまざまな名前で呼び出されます。

/usr/bin/make_tea -> /usr/bin/do_stuff
/usr/bin/make_coffee -> /usr/bin/do_stuff

など。明らかにdo_stuff受け取った最初の引数は、取るべき実際のアクションを決定するために使用され、残りの引数はそれに応じて処理されます。

行われた呼び出し/usr/bin/do_stuff(および完全なパラメータのリスト)を記録したいと思います。シンボリックリンクがない場合は、単に移動してdo_stuffスクリプトdo_stuff_realを作成するだけです。

#!/bin/sh
echo "$0 $@" >> logfile
/usr/bin/do_stuff_real "$@"

しかし、私が知っている限り、呼び出された名前を解決するのはうまくいきません。同じ目標を達成しながら、正しいdo_stuff「実行ファイルで使用される名前」を渡すスクリプトをどのように書くことができますか?

記録のために次の質問に答えないようにするには:

  • 私はCで(execveを使用して)それを行うことができることを知っていますが、この場合、シェルスクリプトを使用できる場合ははるかに簡単になります。
  • do_stuff単にロギングプログラムに置き換えることはできません。

答え1

busyboxほとんどの一般的なUNIXユーティリティを単一の実行可能ファイルとして提供するなどのユーティリティでこれをよく見ることができます。このユーティリティの動作は、呼び出されるかbusybox渡されるacpid項目によって異なりますzcat

argv[0]通常、パラメータを見て何をすべきかを決定しますmain()。単純な比較にしてはいけません。なぜなら、argv[0]似たようなことがあるかもしれないしsleep、そうかもしれないし/bin/sleep、同じことをすることに決めなければならないからです。つまり、このパスは状況をより複雑にします。

したがって、ワーカーが正しく実行すると、ロギングラッパーが同じもので実行される可能性があります/bin/realstuff/make_tea。ワーカーがデフォルト名だけを見ると、argv[0]正しい関数を実行する必要があります。

#!/bin/sh -
myexec=/tmp/MYEXEC$$
mybase=`basename -- "$0"`

echo "$0 $@" >> logfile

mkdir "$myexec" || exit
ln -fs /usr/bin/real/do_stuff "$myexec/$mybase" || exit
"$myexec/$mybase" "$@"
ret=$?
rm -rf "$myexec"
exit "$ret"

上記の例では、argv[0]次のように読み取る必要があります/tmp/MYEXEC4321/make_tea(4321が実行されているPIDの場合/bin/sh)。これはbasenamemake_teaアクションをトリガーする必要があります。

argv[0]ラッパーなしで状況を正確に複製したい場合は、より困難な問題に直面するでしょう。絶対ファイルパスが/.で始まるため、新しいパスを作成できません。/bin/sleep (私がいなければchrootそこに行きたくないと思います。)ご存知のように、これを行うためにいくつかの味を使うことができますが、exec()シェルで包まれません。

エイリアスを使用してロガーにアクセスし、スクリプトラッパーの代わりに基本プログラムを起動することを検討しましたか?限られた一連のイベントだけをキャプチャしますが、おそらくそのイベントにのみ興味があるかもしれません。

答え2

exec -abash、、、、にはありますがksh93zshまだPOSIXでは見つかりません)を使用して、実行中のコマンドを指定できます。mkshyashargv[0]

#! /bin/bash -
printf '%s\n' "$0 $*" >> /some/log
exec -a "$0" /usr/bin/do_stuff_real "$@"

$0これはいいえ注文argv[0]が届きました。これはに渡され、execve()引数として渡されるスクリプトへのパスですが、bashこれはあなたの目的には十分です。

たとえば、ifは次make_teaのように呼び出されます。

execv("/usr/bin/make_tea", ["make_tea", "--sugar=2"])

名前でコマンドを呼び出すと(で実行可能ファイルを見つける$PATH)、シェルが通常行うように、ラッパーは次のことを行います。

execv("/usr/bin/do_stuff_real", ["/usr/bin/make_tea", "--sugar=2"])

それではありません:

execv("/usr/bin/do_stuff_real", ["make_tea", "--sugar=2"])

do_stuff_realしかし、それが車のためのものであることを知るだけで十分です。

次のように呼び出される場合do_stuff

execv("/usr/bin/do_stuff", ["/usr/bin/make_tea", "--sugar=2"])

なぜなら、これは次のように翻訳されるからです:

execv("/usr/bin/do_stuff_real", ["/usr/bin/do_stuff", "--sugar=2"])

これは通常の作業中は発生しませんが、ラッパーが同様の操作を実行することに注意してください。

ほとんどのシステムでは、argv[0]インタプリタが実行されると(ここでは)スクリプトに渡された内容が失われます(/bin/bashほとんどのシステムでは、通訳者のルートはargv[0]シェバン行に提供されるルートです。) したがって、シェルスクリプトはこれについて何もできません。

を渡すには、argv[0]実行可能ファイルをコンパイルする必要があります。それは次のとおりです。

#include <stdio.h>
int main(int argc, char *argv[], char *envp[])
{
   /* add logging */
   execve("/usr/bin/do_stuff_real", argv, envp);
   perror("execve");
   return 127;
}

関連情報