当可执行文件有多个符号链接时,如何使用包装器脚本记录调用

当可执行文件有多个符号链接时,如何使用包装器脚本记录调用

长话短说:我想跟踪调用某些可执行文件来跟踪某些系统行为的方式。假设我有一个可执行文件:

/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_stuffdo_stuff_real编写一个脚本

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

然而,据我所知,它会检查它所调用的名称,这是行不通的。如何编写一个脚本来实现相同的目标,但仍然传递到do_stuff正确的“可执行使用的名称”?

为了记录在案,为了避免回答这些问题:

  • 我知道我可以用 C 语言(使用 execve)来完成它,但如果我可以在这种情况下仅使用 shell 脚本,那就容易多了。
  • 我不能简单地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),这应该触发基本名称make_tea行为

如果您想argv[0]成为没有包装器的情况的精确副本,那么您将遇到更棘手的问题。因为绝对文件路径以/.你不能创造一个新的/bin/sleep (缺席chroot,我认为你不想去那里)。正如您所注意到的,您可以使用 的一些风味来做到这一点exec(),但它不会是外壳包装。

您是否考虑过使用别名来访问记录器,然后启动基本程序而不是脚本包装器?它只会捕获一组有限的事件,但也许这些是您唯一关心的事件

答案2

您可以使用exec -a(如在bashksh93zsh、中找到的mkshyash但尚未在 POSIX 中找到)用于指定argv[0]正在执行的命令:

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

请注意,这$0不是argv[0]命令收到的。它是传递给的脚本的路径execve()(并且作为参数传递给bash),但这可能足以满足您的目的。

例如,ifmake_tea被调用为:

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

正如 shell 通常在按名称调用命令时所做的那样(在 中查找可执行文件$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]解释器(此处),传递给脚本的 as 就会丢失(/bin/bash大多数系统上解释器的路径argv[0]是 she-bang 行上给出的路径)所以 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;
}

相关内容