Как регистрировать вызовы, используя скрипт-обертку, когда есть несколько символических ссылок на исполняемый файл

8

Короче говоря: я хотел бы отследить способ вызова некоторых исполняемых файлов для отслеживания поведения системы. Допустим, у меня есть исполняемый файл:

/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программой регистрации.
Нил Таунсенд
источник

Ответы:

6

Вы часто видите это в случае таких утилит , как 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(), но это не будет оболочкой оболочки.

Рассматривали ли вы использование псевдонима, чтобы поразить регистратор, а затем запустить базовую программу вместо оболочки сценария? Это поймает только ограниченный набор событий, но, возможно, это единственные события, которые вас волнуют

инфиксальный
источник
Пришлось заменить basename на sed, но в остальном работает хорошо.
Нил Таунсенд
1
@NeilTownsend, с POSIX sh, вы можете использовать mybase=${0##*/}вместо basename.
Стефан Шазелас
@ StéphaneChazelas Подобный basenameвызов basename -- foo.barмне незнаком, и в системе Linux, на которой я его тестировал, получается результат, foo.barкоторый может нарушить работу сценария. Вы уверены, что это распространенный метод?
добавлено
basename -- foo.barвозвращается foo.bar, basename -- --foo--/barвозвращается, barкак ожидалось. basename "$foo"работает, только если вы можете гарантировать $foo, не начинается с -. Общий синтаксис для вызова команды с произвольными параметрами является cmd -x -y -- "$argument". cmd "$argument"это неправильно, если вы не имеете в виду cmd "$option_or_arg". Теперь некоторые команды (включая некоторые исторические реализации basename) не поддерживают, --поэтому вам иногда приходится выбирать между переносимостью и надежностью.
Стефан Шазелас
6

Вы можете использовать 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 "$@"

Обратите внимание, что $0это не то, argv[0]что получает команда. Это путь к скрипту, который передается execve()(и который передается в качестве аргумента bash), но этого вполне достаточно для вашей цели.

Как пример, если 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;
}
Стефан Шазелас
источник
+1: Это был бы правильный ответ, за исключением того, что оболочка в системе, над которой я пытаюсь работать, не предоставляет опцию -a для exec ...
Нил Таунсенд
@NeilTownsend Вы можете сделать что-то подобное с perlили, pythonесли доступно. Старые версии zshне поддерживали exec -a, но вы всегда можете использовать ARGV0=the-argv-0 cmd argsвместо этого.
Стефан Шазелас
Это машина на основе busybox, поэтому оболочка выглядит как пепел, у которого нет флага -a. Или, по крайней мере, на этой версии. Поскольку я нахожусь в процессе работы над прошивкой, и у меня нет достаточно четкого контроля над ней, чтобы делать что-то самонадеянное, я стараюсь не добавлять к ней слишком много, просто чтобы понять, как она запускается. Я думаю, что ARGV0 - это только zsh?
Нил Таунсенд
@NeilTownsend, да, это только для zsh. Я имел в виду, что если бы вы имели zsh(хотя теперь вы дали понять, что вы этого не сделали), но он был слишком стар, вы все равно могли бы использовать его ARGV0там.
Стефан Шазелас
Ярмарка достаточно - Если бы я мог галочку вашего ответа за то , что «блестящий , но на самом деле не работать на мою неясную ситуацию» я был бы.
Neil Townsend