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


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.