Faire en sorte qu'un processus lise un fichier différent pour le même nom de fichier

9

J'ai une application qui lit un fichier. Appelons-le processname et le fichier ~ / .configuration . Lorsque processname s'exécute, il lit toujours ~ / .configuration et ne peut pas être configuré différemment. Il existe également d'autres applications qui reposent sur "~ / .configuration", avant et après, mais pas pendant que processname est en cours d'exécution.

L'emballage du nom du processus dans un script qui remplace le contenu de ~ / .configuration est une option, mais j'ai récemment eu une panne de courant (alors que le contenu a été échangé), où j'ai perdu le contenu précédent dudit fichier, donc ce n'est pas souhaitable.

Existe-t-il un moyen (peut-être d'utiliser quelque chose de très éloigné LD_DEBUG=files processname?) De tromper un processus en lisant différents contenus lorsqu'il essaie de lire un fichier spécifique? La recherche et le remplacement du nom de fichier dans l'exécutable sont un peu trop invasifs, mais devraient également fonctionner.

Je sais qu'il est possible d'écrire un module de noyau qui prend en charge l' open()appel ( https://news.ycombinator.com/item?id=2972958 ), mais existe-t-il un moyen plus simple ou plus propre?

EDIT: Lors de la recherche de ~ / .configuration dans l' exécutable processname, j'ai découvert qu'il essayait de lire un autre nom de fichier juste avant de lire ~ / .configuration . Problème résolu.

Alexandre
la source
2
Cela pourrait être fait via LD_PRELOADou FUSE, comme avec ce problème quelque peu similaire , mais je ne connais aucune implémentation existante.
Gilles 'SO- arrête d'être méchant'

Réponses:

6

Dans les versions récentes de Linux, vous pouvez annuler le partage de l' espace de noms de montage . Autrement dit, vous pouvez démarrer des processus qui affichent le système de fichiers virtuel différemment (avec des systèmes de fichiers montés différemment).

Cela peut également être fait avec chroot, mais unshareest plus adapté à votre cas.

Comme chroot, vous avez besoin d'un superutilisateur privilégié sur unsharel'espace de noms de montage.

Alors, que vous avez ~/.configurationet ~/.configuration-for-that-cmdfichiers.

Vous pouvez démarrer un processus pour lequel il ~/.configurations'agit en fait d'un montage de liaison de ~/.configuration-for-that-cmddedans, et l'exécuter that-cmdlà-dedans.

comme:

sudo unshare -m sh -c "
   mount --bind '$HOME/.configuration-for-that-cmd' \
                '$HOME/.configuration' &&
     exec that-cmd"

that-cmdet tous ses processus descendants verront un autre ~/.configuration.

that-cmdci-dessus s'exécutera en tant que root, utilisez-le sudo -u another-user that-cmds'il doit s'exécuter en tant qu'autre utilisateur .

Stéphane Chazelas
la source
Je pense que votre solution est probablement la meilleure des deux données jusqu'à présent (et compte tenu de ce que l'OP est après, la redirection en fonction du temps ou les résultats d'un processus de détection me semblent douteux), mais je pense qu'ils veulent un seul fichier pour apparaître différemment. Ils devraient donc probablement le monter ailleurs et utiliser un lien symbolique, en comptant sur les différents points de montage pour agir comme point de redirection réel.
Bratchley
1
@JoelDavis, vous pouvez monter-lier n'importe quel fichier, pas seulement les répertoires.
Stéphane Chazelas
TIL. Y a-t-il cependant des contrôles de sécurité? Je l'ai essayé en utilisant un sous-répertoire où je me trouvais (liaison à partir de / etc / fstab) et il a renvoyé "Pas un répertoire" mais j'ai fait à peu près la même chose sous /testet cela a fonctionné sans problème.
Bratchley
En fait, nm, je peux voir la différence, je l'ai fait dans un répertoire la première fois et dans un fichier la prochaine. Je supposais qu'il ne ferait que rediriger / modifier le VFS comme il convient. Quoi qu'il en soit, merci pour le nouveau jouet.
Bratchley
3

Liens souples.

Créez deux fichiers de configuration et pointez sur l'un d'eux avec un lien logiciel la plupart du temps, mais modifiez le lien logiciel pour qu'il pointe vers l'autre lorsque l'application spéciale est en cours d'exécution.

(Je sais que c'est un horrible hack, mais c'est un peu plus fiable que de changer le contenu d'un fichier).

Ou, manipulez $ HOME.

Dans le script qui démarre le processus ennuyeux, définissez $ HOME comme étant quelque chose dans le répertoire $ HOME normal, et votre application doit ensuite utiliser le fichier de configuration qui s'y trouve (testé et fonctionne pour les commandes de base du shell, ~ se développe en $ HOME).

Selon ce que fait le processus, changer $ HOME peut avoir des conséquences inattendues (c'est-à-dire que les fichiers de sortie peuvent se retrouver au mauvais endroit).

EightBitTony
la source
1

Vous pouvez le faire en utilisant l'astuce LD_PRELOAD . Voici une implémentation qui mappe les chemins commençant par un préfixe spécifique vers un autre emplacement. Le code est également sur github .

Par exemple, vous pouvez simuler l'existence d'un fichier /etc/sans être root. Cela était nécessaire pour le client owncloud qui refuse de travailler lorsque le fichier /etc/ownCloud/sync-exclude.listn'existe pas.

Cela fonctionne en remplaçant les fonctions open()et open64()pour mapper un répertoire à un autre, par exemple tous les open()appels vers /etc/ownCloud/...pourraient être redirigés vers /home/user1/.etc/ownCloud/....

Ajustez simplement le path_map, puis compilez et exécutez votre programme avec la lib préchargée:

gcc -std=c99 -Wall -shared -fPIC path-mapping.c -o path-mapping.so -ldl

LD_PRELOAD=/path/to/my/path-mapping.so someprogram

Code source de path-mapping.c:

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdarg.h>
#include <malloc.h>

// List of path pairs. Paths beginning with the first item will be
// translated by replacing the matching part with the second item.
static const char *path_map[][2] = {
    { "/etc/ownCloud/", "/home/user1/.etc/ownCloud/" },
};

__thread char *buffer = NULL;
__thread int buffer_size = -1;

typedef FILE* (*orig_fopen_func_type)(const char *path, const char *mode);
typedef int (*orig_open_func_type)(const char *pathname, int flags, ...);

static int starts_with(const char *str, const char *prefix) {
    return (strncmp(prefix, str, strlen(prefix)) == 0);
}

static char *get_buffer(int min_size) {
    int step = 63;
    if (min_size < 1) {
        min_size = 1;
    }
    if (min_size > buffer_size) {
        if (buffer != NULL) {
            free(buffer);
            buffer = NULL;
            buffer_size = -1;
        }
        buffer = malloc(min_size + step);
        if (buffer != NULL) {
            buffer_size = min_size + step;
        }
    }
    return buffer;
}

static const char *fix_path(const char *path)
{
    int count = (sizeof path_map) / (sizeof *path_map); // Array length
    for (int i = 0; i < count; i++) {
        const char *prefix = path_map[i][0];
        const char *replace = path_map[i][1];
        if (starts_with(path, prefix)) {
            const char *rest = path + strlen(prefix);
            char *new_path = get_buffer(strlen(path) + strlen(replace) - strlen(prefix));
            strcpy(new_path, replace);
            strcat(new_path, rest);
            printf("Mapped Path: %s  ==>  %s\n", path, new_path);
            return new_path;
        }
    }
    return path;
}


int open(const char *pathname, int flags, ...)
{
    const char *new_path = fix_path(pathname);

    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open");

    // If O_CREAT is used to create a file, the file access mode must be given.
    if (flags & O_CREAT) {
        va_list args;
        va_start(args, flags);
        int mode = va_arg(args, int);
        va_end(args);
        return orig_func(new_path, flags, mode);
    } else {
        return orig_func(new_path, flags);
    }
}

int open64(const char *pathname, int flags, ...)
{
    const char *new_path = fix_path(pathname);

    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open64");

    // If O_CREAT is used to create a file, the file access mode must be given.
    if (flags & O_CREAT) {
        va_list args;
        va_start(args, flags);
        int mode = va_arg(args, int);
        va_end(args);
        return orig_func(new_path, flags, mode);
    } else {
        return orig_func(new_path, flags);
    }
}
Fritz
la source