Cómo registrar llamadas usando un script de envoltura cuando hay múltiples enlaces simbólicos al ejecutable

8

En pocas palabras: me gustaría rastrear la forma en que se llaman algunos ejecutables para rastrear el comportamiento del sistema. Digamos que tengo un ejecutable:

/usr/bin/do_stuff

Y en realidad se llama por varios nombres diferentes a través del enlace simbólico:

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

y así. Claramente, do_stuffva a usar el primer argumento que recibe para determinar qué acción se lleva a cabo realmente, y el resto de los argumentos se manejarán a la luz de eso.

Me gustaría grabar alguna llamada a /usr/bin/do_stuff(y la lista completa de argumentos). Si no existieran los enlaces simbólicos, me basta con mover do_stuffa do_stuff_realy escribo un guión

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

Sin embargo, como sé que examinará el nombre por el que se llama, esto no funcionará. ¿Cómo se escribe un script para lograr lo mismo, pero aún se pasa al do_stuff'nombre usado ejecutable' correcto?

Para el registro, para evitar respuestas en estas líneas:

  • Sé que puedo hacerlo en C (usando execve), pero sería mucho más fácil si pudiera, en este caso, simplemente usar un script de shell.
  • No puedo simplemente reemplazar do_stuffcon un programa de registro.
Neil Townsend
fuente

Respuestas:

6

A menudo se ve esto en caso de utilidades como busybox, un programa que puede proporcionar la mayor parte de las utilidades de Unix comunes en un solo ejecutable, que se comporta diferentes dependiendo de su invocación / busyboxpueden hacer una gran cantidad de funciones, acpida través zcat.

Y comúnmente decide lo que se supone que debe hacer mirando su argv[0]parámetro main(). Y eso no debería ser una simple comparación. Porque argv[0]podría ser algo así sleep, o podría ser /bin/sleepy debería decidir hacer lo mismo. En otras palabras, el camino va a hacer las cosas más complejas.

Entonces, si el programa de trabajo hizo bien las cosas, su contenedor de registro podría ejecutarse desde algo así /bin/realstuff/make_teay si el trabajador solo mira el argv[0]nombre base, entonces la función correcta debería ejecutarse.

#!/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"

En el ejemplo anterior, argv[0]debería leer algo como /tmp/MYEXEC4321/make_tea(si 4321 fue el PID para el /bin/shque se ejecutó) que debería desencadenar el make_teacomportamiento de nombre base

Si desea argv[0]ser una copia exacta de lo que sería sin el contenedor, tiene un problema más difícil. Debido a las rutas de archivos absolutas que comienzan con /. No puedes hacer un nuevo /bin/sleep (ausente chrooty no creo que quieras ir allí). Como notará, podría hacerlo con un poco de sabor exec(), pero no sería una envoltura de concha.

¿Ha considerado usar un alias para presionar el registrador y luego iniciar el programa base en lugar de un contenedor de script? Solo captaría un conjunto limitado de eventos, pero tal vez esos son los únicos eventos que te interesan

infijo
fuente
Tuve que reemplazar basename con sed, pero por lo demás funciona bien.
Neil Townsend
1
@NeilTownsend, con un POSIX sh, puede usar en mybase=${0##*/}lugar de basename.
Stéphane Chazelas
@ StéphaneChazelas Una basenameinvocación como basename -- foo.barno me resulta familiar, y en el sistema Linux que lo probé produce el resultado foo.barque rompería el script. ¿Estás seguro de que es un método común?
infijo
basename -- foo.barvuelve foo.bar, basename -- --foo--/barvuelve barcomo se esperaba. basename "$foo"solo funciona si puedes garantizar $fooque no comienza con a -. La sintaxis general para invocar un comando con argumentos arbitrarios es cmd -x -y -- "$argument". cmd "$argument"está mal a menos que quieras decir cmd "$option_or_arg". Ahora, algunos comandos (incluidas algunas implementaciones históricas de basename) no son compatibles, --por lo que a veces puede que tenga que elegir entre portabilidad y confiabilidad.
Stéphane Chazelas
6

Se puede utilizar exec -a(como se encuentra en bash, ksh93, zsh, mksh, yashpero no POSIX aún) que se utiliza para especificar una argv[0]a un comando que se está ejecutando:

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

Tenga en cuenta que ese no$0 es el que recibe el comando. Es la ruta del script que se pasa a (y que se pasa como argumento a ), pero es probable que sea suficiente para su propósito.argv[0]execve()bash

Como ejemplo, if make_teafue invocado como:

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

Como lo haría normalmente un shell al invocar el comando por su nombre (buscando el ejecutable en $PATH), el contenedor debería:

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

Eso no es:

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

pero eso es lo suficientemente bueno como se do_stuff_realsabe que está destinado a hacer té.

Donde eso sería un problema es si do_stuffse invocara como:

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

como eso se traduciría a:

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

Eso no sucedería durante las operaciones normales, pero tenga en cuenta que nuestro contenedor hace algo así.

En la mayoría de los sistemas, el argv[0]que pasó a la secuencia de comandos se pierde una vez que el intérprete (en este caso /bin/bashse ejecuta) ( la argv[0]del intérprete en la mayoría de los sistemas es la ruta dada en la línea de ella-bang ) así que no hay nada que un script puede hacer al respecto.

Si quisieras pasar el argv[0]tiempo, necesitarías compilar un ejecutable. Algo como:

#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;
}
Stéphane Chazelas
fuente
+1: Esta sería la respuesta correcta, excepto que el shell en el sistema en el que estoy tratando de trabajar no proporciona la opción -a para el ejecutivo ...
Neil Townsend
@NeilTownsend Puede hacer algo similar con perlo pythonsi está disponible. Las versiones anteriores de zshno eran compatibles exec -a, pero siempre puedes usarlas ARGV0=the-argv-0 cmd args.
Stéphane Chazelas
Es una máquina basada en busybox, por lo que el shell parece ser ceniza, que no parece tener la bandera -a. O, al menos, en esta versión. Como estoy en el proceso de trabajar en el firmware, y no tengo un control lo suficientemente claro como para hacer algo presuntuoso, estoy tratando de evitar agregarle demasiado solo para entender cómo se inicia. Creo que ARGV0 es una única cosa zsh?
Neil Townsend
@NeilTownsend, sí, eso es solo para zsh. Quise decir que si lo hubiera hecho zsh(aunque ahora ha dejado en claro que no) pero era demasiado viejo, aún podría usarlo ARGV0allí.
Stéphane Chazelas
Bastante justo: si pudiera marcar su respuesta por ser 'brillante, pero en realidad no funcionó para mi oscura situación ", lo haría.
Neil Townsend