Comment puis-je faire croire à un processus qu’un fichier n’existe pas?

31

J'ai un programme qui stocke ses paramètres dans ~/.config/myprogramque j'utilise à la fois de manière interactive et avec un système de mise en file d'attente par lots. Lors de l'exécution interactive, je veux que ce programme utilise mes fichiers de configuration (et il le fait). Mais lors de l'exécution en mode batch, les fichiers de configuration ne sont pas nécessaires car je spécifie des options de ligne de commande qui écrasent tous les paramètres pertinents. De plus, l'accès aux fichiers de configuration sur le réseau augmente le temps de démarrage du programme de plusieurs secondes; si les fichiers n'existent pas, le programme se lance beaucoup plus rapidement (car chaque tâche ne prend qu'une minute environ, cela a un impact significatif sur le débit des tâches par lots). Mais parce que j'utilise également le programme de manière interactive, je ne veux pas déplacer / supprimer mes fichiers de configuration tout le temps. Selon le moment où mes travaux par lots sont planifiés sur le cluster (en fonction de l'utilisation des autres utilisateurs),

(En plus: les performances des fichiers réseau sont si lentes, c'est probablement un bogue, mais je ne suis qu'un utilisateur du cluster, donc je ne peux que le contourner, pas le réparer.)

Je pourrais créer une version du programme qui ne lit pas les fichiers de configuration (ou qui n'a pas l'option de ligne de commande pour) pour une utilisation par lots, mais l'environnement de construction de ce programme est mal conçu et difficile à configurer. Je préférerais de loin utiliser les binaires installés via le gestionnaire de packages de mon système.

Comment puis-je tromper des instances particulières de ce programme en prétendant que mes fichiers de configuration n'existent pas (sans modifier le programme)? J'espère un wrapper du formulaire pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., mais je suis ouvert à d'autres solutions.

Jeffrey Bosboom
la source
2
Vous pouvez l'exécuter en tant qu'utilisateur ne disposant pas des autorisations nécessaires pour lire le fichier.
psimon
1
@psimon En tant que «juste un utilisateur» du cluster, je ne peux pas créer un nouvel utilisateur pour exécuter mon travail par lots en tant que. C'est une idée intelligente cependant, et s'il n'y a pas de meilleures suggestions, je bogue l'administrateur du cluster pour le faire pour moi.
Jeffrey Bosboom
Ou configurez un script qui renomme d'abord le fichier de configuration, exécute le programme, puis renomme le fichier de configuration à nouveau.
psimon
@psimon Je suppose que j'aurais pu être plus clair: je pourrais utiliser le programme de manière interactive et en mode batch en même temps, selon le moment où mes jobs batch seront planifiés sur le cluster.
Jeffrey Bosboom
1
Oui, s'il est lié dynamiquement, vous pouvez utiliser un LD_PRELOADcrochet. C'est plus facile (vous pouvez l'implémenter en une heure ou deux, si vous connaissez C) que l'alternative, qui l'est ptrace. Vous pourriez aussi probablement utiliser fakechroot pour ce faire (qui est LD_PRELOAD, je crois).
derobert

Réponses:

33

Ce programme résout probablement le chemin d'accès à ce fichier à partir de $HOME/.config/myprogram. Vous pouvez donc lui dire que votre répertoire personnel est ailleurs, comme:

HOME=/nowhere your-program

Maintenant, peut-être que votre programme a besoin d'une autre ressource dans votre répertoire personnel. Si vous savez de quoi il s'agit, vous pouvez préparer un faux domicile pour votre programme avec des liens vers les ressources dont il a besoin.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
Stéphane Chazelas
la source
5
Cette réponse résout mon problème, donc je l'ai acceptée même si ce n'est pas une réponse entièrement générale à la question dans le titre de cette question. Un crochet de précharge, comme décrit dans une autre réponse, est une solution plus générale (mais aussi à effort plus élevé).
Jeffrey Bosboom
28

Si tout le reste échoue, écrivez une bibliothèque d'encapsuleur que vous injecterez en utilisant de LD_PRELOADsorte que l'appel à open("/home/you/my-program/config.interactive")soit intercepté mais que tout autre passe. Cela fonctionne pour tout type de programme, même les scripts shell, car il filtrera les appels système.

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;
  }
}

Remarque: je n'ai pas testé ce code et je ne suis pas sûr à 100% que la errnopièce fonctionne.

Regardez comment fakerootcela fonctionne pour les appels comme getuid(2)et stat(2).

Fondamentalement, l'éditeur de liens liera cette application à votre bibliothèque, ce qui remplace le opensymbole. Puisque vous ne pouvez pas utiliser deux fonctions différentes nommées opendans votre propre bibliothèque, vous devez la séparer dans une deuxième partie (par exemple get_real_open) qui sera à son tour liée à l' openappel d' origine .

Original: ./Application

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

Intercepté: LD_PRELOAD=yourlib_wrap.so ./Application

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

Edit: Apparemment, il existe un ldindicateur que vous pouvez activer ( --wrap <symbol>) qui vous permet d'écrire des wrappers sans avoir à recourir à une double liaison:

/* 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
la source
2

Déplacez votre fichier de configuration à l'écart et écrivez un wrapper de script shell pour le cas d'utilisation interactif qui copie le fichier dans sa destination normale, exécute le programme et le supprime à la sortie.

tink
la source
Voir ma modification récente: je ne contrôle pas la planification par lots, il se peut donc que j'utilise le programme de manière interactive et dans le cadre d'un travail par lots en même temps.
Jeffrey Bosboom
1

Cela devrait être possible avec unionfs / aufs. Vous créez un chrootenvironnement pour le processus. Vous utilisez le vrai répertoire en tant que couche en lecture seule et en placez un vide par-dessus. Vous montez ensuite le volume unionfs dans le répertoire respectif de l' chrootenvironnement et vous y supprimez le fichier. Le processus ne le verra pas, mais tous les autres le voient.

Hauke ​​Laging
la source
0

Renommez le fichier de configuration par exemple config.interactive. Créez un autre fichier vide appelé par exemple config.script.

Maintenant, créez un lien configlogiciel appelé (ou tout ce que l'application attend en tant que fichier de configuration) vers la configuration réelle dont vous avez besoin et exécutez votre application.

ln -s config.interactive config

N'oubliez pas de ranger votre lien par la suite.

garethTheRed
la source
Voir ma modification récente: je ne contrôle pas la planification par lots, il se peut donc que j'utilise le programme de manière interactive et dans le cadre d'un travail par lots en même temps. Cette réponse est essentiellement la même que pour déplacer les fichiers, manuellement ou avec un script.
Jeffrey Bosboom
1
Ah! J'ai besoin de réfléchir et de taper plus vite. Est-ce une grande application? Pourrait-il être transféré vers un chroot et exécuté à partir de là de manière interactive? Tout dépend de ce avec quoi le programme interagit, je suppose. Cela pourrait également être une tâche très fastidieuse d'obtenir tout ce dont il a besoin dans un chroot. (Je pense que je me suis exclu de cette option!)
garethTheRed
0

Si vous avez précisément caractérisé la façon dont votre programme utilise le fichier de configuration, je l'ai ignoré. De nombreux programmes (tels que bashet vi) recherchent un fichier de configuration immédiatement au démarrage; si le fichier existe, lisez-le et fermez-le. Ces programmes n'accèdent plus jamais à ces fichiers d'initialisation. Si votre programme est comme ça, lisez la suite.

Je sais que vous avez rejeté les réponses qui rendent le fichier de configuration vraiment inexistant (en le renommant), mais j'ai une ride que je n'ai pas vue proposée par quelqu'un d'autre. Faites cela lorsque vous appelez le programme en mode batch:

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

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

Cela éloigne le fichier de configuration, mais le ramène une seconde plus tard, même s'il myprogramest toujours en cours d'exécution. Cela crée une fenêtre de temps très brève pendant laquelle le fichier n'est pas disponible - quelle est la probabilité que vous exécutiez le programme de manière interactive pendant cette fenêtre? (Même si vous le faites, vous pouvez simplement quitter et redémarrer, et le fichier de configuration sera probablement de nouveau en place.)

Cela crée une condition de concurrence; si le programme prend trop de temps pour ouvrir le fichier, il peut obtenir le vrai fichier. Si cela se produit assez souvent pour que ce soit un problème, augmentez simplement la valeur de DELAY_TIME.

Scott
la source
1
Quelle est la pire chose qui pourrait mal tourner? J'ai littéralement vu cela se produire. En production.
Henk Langeveld
-1

J'aime la réponse de Stéphane, mais cela incitera n'importe quel programme à croire qu'un fichier est vide - (car sa dentisterie pointe temporairement vers un fichier qui est réellement vide) :

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

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

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

Vous pouvez également:

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

Si tu voulais.

Mikeserv
la source
Ceci est essentiellement équivalent à quelques-unes des réponses précédentes (sauf, je crois, celle-ci nécessite que l'utilisateur soit privilégié). L'OP a rejeté ces autres réponses parce qu'il ne veut pas tromper aucun processus - il veut tromper l'appel par lots du programme, tout en laissant l'invocation interactive voir le fichier de configuration normalement.
Scott
@Scott - je ne suis pas d'accord - toutes les autres réponses avant celle-ci recommandaient des variations sur mvle fichier - qui pourraient avoir d'autres conséquences que d'affecter sa dentisterie telles que la troncature du fichier ou d'autres et etc. - alors que cela ne fonctionne que sur. Pourtant, je devrais unshareque mountje suppose ...
mikeserv