¿Cómo puedo engañar a un proceso para que piense que un archivo no existe?

31

Tengo un programa que almacena su configuración en ~/.config/myprogramque uso tanto interactivamente como con un sistema de colas por lotes. Cuando se ejecuta de forma interactiva, quiero que este programa use mis archivos de configuración (y lo hace). Pero cuando se ejecuta en modo por lotes, los archivos de configuración no son necesarios porque especifico las opciones de línea de comandos que sobrescriben todas las configuraciones relevantes. Además, acceder a los archivos de configuración a través de la red aumenta el tiempo de inicio del programa en varios segundos; Si los archivos no existen, el programa se inicia mucho más rápido (ya que cada trabajo solo toma aproximadamente un minuto, esto tiene un impacto significativo en el rendimiento del trabajo por lotes). Pero debido a que también uso el programa de manera interactiva, no quiero mover / eliminar mis archivos de configuración todo el tiempo. Dependiendo de cuándo se programan mis trabajos por lotes en el clúster (según el uso de otros usuarios),

(Aparte: el rendimiento del archivo de red es tan lento que probablemente sea un error, pero solo soy un usuario del clúster, por lo que solo puedo solucionarlo, no solucionarlo).

Podría construir una versión del programa que no lea los archivos de configuración (o que no tenga una opción de línea de comandos) para el uso por lotes, pero el entorno de compilación de este programa está mal diseñado y es difícil de configurar. Preferiría usar los binarios instalados a través del administrador de paquetes de mi sistema.

¿Cómo puedo engañar a instancias particulares de este programa para fingir que mis archivos de configuración no existen (sin modificar el programa)? Espero un contenedor del formulario pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., pero estoy abierto a otras soluciones.

Jeffrey Bosboom
fuente
2
Puede ejecutarlo como un usuario que no tiene permisos para leer el archivo.
psimon
1
@psimon Como "solo un usuario" del clúster, no puedo crear un nuevo usuario para ejecutar mi trabajo por lotes como. Sin embargo, esa es una idea inteligente, y si no hay mejores sugerencias, molestaré al administrador del clúster para que lo haga por mí.
Jeffrey Bosboom
O configure un script que primero cambie el nombre del archivo de configuración, ejecute el programa y luego cambie el nombre del archivo de configuración nuevamente.
psimon
@psimon Supongo que podría haber sido más claro: podría estar usando el programa de forma interactiva y en modo por lotes al mismo tiempo, dependiendo de cuándo se programen mis trabajos por lotes en el clúster.
Jeffrey Bosboom
1
Sí, si está vinculado dinámicamente, puede usar un LD_PRELOADgancho. Eso es más fácil (puede implementar eso en una hora o dos, si conoce C) que la alternativa, que es ptrace. Probablemente también podría usar fakechroot para hacer esto (que es LD_PRELOAD, creo).
derobert

Respuestas:

33

Ese programa probablemente resuelve la ruta a ese archivo desde $HOME/.config/myprogram. Entonces podría decir que su directorio de inicio está en otro lugar, como:

HOME=/nowhere your-program

Ahora, tal vez su programa necesita algún otro recurso en su directorio de inicio. Si sabe cuáles son, puede preparar una casa falsa para su programa con enlaces al recurso que necesita allí.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
Stéphane Chazelas
fuente
55
Esta respuesta resuelve mi problema, así que la he aceptado aunque no sea una respuesta totalmente general a la pregunta en el título de esta pregunta. Un gancho de precarga, como se describe en otra respuesta, es una solución más general (pero también de mayor esfuerzo).
Jeffrey Bosboom
28

Si todo lo demás falla, escriba una biblioteca de contenedor que inyectará LD_PRELOADpara que la llamada a open("/home/you/my-program/config.interactive")sea ​​interceptada pero cualquier otra pase. Esto funciona para cualquier tipo de programa, incluso scripts de shell, ya que filtrará las llamadas al sistema.

extern int errno;

int open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    return get_real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1;
  }
}

Nota: No he probado este código y no estoy 100% seguro de que la errnopieza funcione.

Mira cómo fakerootfunciona para llamadas como getuid(2)y stat(2).

Básicamente, el vinculador vinculará esa aplicación a su biblioteca, que anula el opensímbolo. Como no puede usar dos funciones diferentes nombradas openen su propia biblioteca, debe separarla en una segunda parte (por ejemplo get_real_open) que a su vez se vinculará con la openllamada original .

Original: ./Application

Application -----> libc.so
            open()

Interceptado: LD_PRELOAD=yourlib_wrap.so ./Application

Application -----> yourlib_wrap.so --------------> yourlib_impl.so -----> libc.so
            open()                 get_real_open()                 open()

Editar: Aparentemente hay una ldbandera que puede habilitar ( --wrap <symbol>) que le permite escribir envoltorios sin tener que recurrir a enlaces dobles:

/* yourlib.c */
#include <stdio.h>

int __real_open(const char *pathname, int flags)

int __wrap_open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    /* the undefined reference here will resolve to "open" at linking time */
    return __real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1; 
  }
}
sleblanc
fuente
2

Mueva su archivo de configuración fuera del camino y escriba un contenedor de script de shell para el caso de uso interactivo que copie el archivo en su destino normal, ejecute el programa y lo elimine al salir.

tink
fuente
Vea mi edición reciente: no controlo la programación por lotes, por lo que puedo estar usando el programa de forma interactiva y como parte de un trabajo por lotes al mismo tiempo.
Jeffrey Bosboom
1

Esto debería ser posible con unionfs / aufs. Creas un chrootentorno para el proceso. Utiliza el directorio real como capa de solo lectura y coloca uno vacío encima. Luego, monta el volumen unionfs en el directorio respectivo del chrootentorno y elimina el archivo allí. El proceso no lo verá, pero todos los demás sí.

Hauke ​​Laging
fuente
0

Cambie el nombre del archivo de configuración por ej config.interactive. Cree otro archivo vacío llamado eg config.script.

Ahora, cree un enlace suave llamado config(o lo que la aplicación espere como un archivo de configuración) a la configuración real que necesite y ejecute su aplicación.

ln -s config.interactive config

Recuerde ordenar su enlace después.

garethTheRed
fuente
Vea mi edición reciente: no controlo la programación por lotes, por lo que puedo estar usando el programa de forma interactiva y como parte de un trabajo por lotes al mismo tiempo. Esta respuesta es esencialmente lo mismo que mover los archivos, ya sea manualmente o con un script.
Jeffrey Bosboom
1
Doh! Necesito pensar y escribir más rápido. ¿Es una aplicación grande? ¿Podría transferirse a un chroot y correr desde allí de forma interactiva? Todo depende de con qué interactúa el programa, supongo. También podría ser una tarea muy tediosa incluir todo lo que requiere en un chroot. (¡Creo que he descartado esa opción!)
garethTheRed
0

Si ha caracterizado con precisión cómo su programa utiliza el archivo de configuración, lo he pasado por alto. Muchos programas (como bashy vi) buscarán un archivo de configuración inmediatamente después del inicio; si el archivo existe, léalo y ciérrelo. Estos programas nunca vuelven a acceder a estos archivos de inicialización. Si su programa es así, siga leyendo.

Sé que ha rechazado las respuestas que hacen que el archivo de configuración sea realmente inexistente (al renombrarlo), pero tengo una arruga que no he visto propuesta por nadie más. Haga esto cuando invoque el programa en modo por lotes:

DELAY_TIME=1
REALPATH="~/.config/myprogram"
HOLDPATH="${REALPATH}.hold"

mv "$REALPATH" "$HOLDPATH"
(sleep "$DELAY_TIME"; mv "$HOLDPATH" "$REALPATH")&
myprogram

Esto mueve el archivo de configuración fuera del camino, pero luego lo mueve un segundo más tarde, incluso si myprogramtodavía se está ejecutando. Esto crea una ventana de tiempo muy breve durante la cual el archivo no está disponible. ¿Cuál es la probabilidad de que ejecute el programa de forma interactiva durante esta ventana? (Incluso si lo hace, puede salir y reiniciar, y el archivo de configuración probablemente volverá a su lugar).

Esto crea una condición de carrera; Si el programa tarda demasiado en abrir el archivo, podría obtener el archivo real. Si esto sucede con la frecuencia suficiente como para que sea un problema, solo aumente el valor de DELAY_TIME.

Scott
fuente
1
¿Qué es lo peor que podría salir mal? Literalmente he visto que eso suceda. En producción.
Henk Langeveld
-1

Me gusta la respuesta de Stéphane, pero esto va a engañar a cualquier programa en la creencia de cualquier archivo está vacío - (debido a que su dentry apunta a un archivo temporal que realmente está vacío) :

cat <./test.txt
###OUTPUT###
notempty

mount --bind /dev/null ./test.txt
cat <./test.txt
###NO OUTPUT###

umount ./test.txt
cat <./test.txt
###OUTPUT###
notempty

También podrías:

mount --bind ./someotherconfig.conf ./unwanted.conf

Si querías.

mikeserv
fuente
Esto es esencialmente equivalente a un par de respuestas anteriores (excepto, creo, esta requiere que el usuario tenga privilegios). El OP rechazó esas otras respuestas porque no quiere engañar a ningún proceso: quiere engañar a la invocación por lotes del programa, mientras deja que la invocación interactiva vea el archivo de configuración normalmente.
Scott
@Scott - no estoy de acuerdo - todas las demás respuestas antes de esta recomendaron alguna variación en mv el archivo, lo que podría tener otras consecuencias que afectar su negación, como truncar el archivo u otros, etc., mientras que esto funciona en nada más que. Aún así, supongo unshareque debería mount...
mikeserv