长话短说:我想跟踪调用某些可执行文件以跟踪某些系统行为的方式。假设我有一个可执行文件:
/usr/bin/do_stuff
Run Code Online (Sandbox Code Playgroud)
它实际上通过符号链接被许多不同的名称调用:
/usr/bin/make_tea -> /usr/bin/do_stuff
/usr/bin/make_coffee -> /usr/bin/do_stuff
Run Code Online (Sandbox Code Playgroud)
等等。显然,do_stuff将使用它收到的第一个参数来确定实际采取的操作,其余参数将根据此进行处理。
我想记录曾经调用过/usr/bin/do_stuff(以及完整的参数列表)。如果没有符号链接,我会干脆转移do_stuff到do_stuff_real写一个脚本
#!/bin/sh
echo "$0 $@" >> logfile
/usr/bin/do_stuff_real "$@"
Run Code Online (Sandbox Code Playgroud)
但是,据我所知,它会检查它被调用的名称,这是行不通的。如何编写一个脚本来实现相同的功能,但仍然传递给do_stuff正确的“可执行使用的名称”?
为了记录,为了避免这些行的答案:
do_stuff用日志程序替换。您可以使用exec -a(在bash, ksh93, zsh, 中找到mksh,yash但尚未在 POSIX 中找到)用于指定argv[0]正在执行的命令:
#! /bin/bash -
printf '%s\n' "$0 $*" >> /some/log
exec -a "$0" /usr/bin/do_stuff_real "$@"
Run Code Online (Sandbox Code Playgroud)
请注意,这$0是不是在argv[0]该命令接收。它是传递给execve()(并且作为参数传递给bash)的脚本路径,但这可能足以满足您的目的。
例如,如果make_tea被调用为:
execv("/usr/bin/make_tea", ["make_tea", "--sugar=2"])
Run Code Online (Sandbox Code Playgroud)
正如 shell 在按名称调用命令(在 中查找可执行文件$PATH)时通常会做的那样,包装器将:
execv("/usr/bin/do_stuff_real", ["/usr/bin/make_tea", "--sugar=2"])
Run Code Online (Sandbox Code Playgroud)
那不是:
execv("/usr/bin/do_stuff_real", ["make_tea", "--sugar=2"])
Run Code Online (Sandbox Code Playgroud)
但这已经足够好了,因为do_stuff_real它是用来泡茶的。
如果do_stuff被调用为:
execv("/usr/bin/do_stuff", ["/usr/bin/make_tea", "--sugar=2"])
Run Code Online (Sandbox Code Playgroud)
因为这将被翻译成:
execv("/usr/bin/do_stuff_real", ["/usr/bin/do_stuff", "--sugar=2"])
Run Code Online (Sandbox Code Playgroud)
在正常操作期间不会发生这种情况,但请注意我们的包装器会执行类似的操作。
在大多数系统中,argv[0]作为传递到脚本一次解释(这里丢失/bin/bash)执行(在argv[0]大多数系统上的解释是对她邦行给出的路径),这样就没有一个shell脚本可以做这件事。
如果你想传递它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;
}
Run Code Online (Sandbox Code Playgroud)
小智 6
你经常会看到这样的类似公用事业的情况下busybox,一个程序,可以提供大多数常见的UNIX实用程序在一个可执行,其行为不同,这取决于它的调用/ busybox可以做一大堆的功能,acpid通过zcat。
它通常通过查看它的argv[0]参数来决定它应该做什么main()。这不应该是一个简单的比较。因为argv[0]可能类似于sleep,或者可能是/bin/sleep并且它应该决定做同样的事情。换句话说,这条路会让事情变得更加复杂。
因此,如果工作程序正确完成了工作,则您的日志记录包装器可以从类似的内容中执行/bin/realstuff/make_tea,如果工作程序argv[0]仅查看basename,则应该执行正确的函数。
#!/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"
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,argv[0]应该读取类似/tmp/MYEXEC4321/make_tea(如果 4321 是/bin/sh那个运行的 PID )这应该触发 basenamemake_tea行为
如果您想argv[0]成为没有包装器的情况的精确副本,那么您将面临更棘手的问题。由于绝对文件路径以/. 你不能做一个新的/bin/sleep (缺席chroot,我认为你不想去那里)。正如您所注意到的,您可以使用exec().
您是否考虑过使用别名来访问记录器,然后启动基本程序而不是脚本包装器?它只会捕获一组有限的事件,但也许这些是您唯一关心的事件