Fare in modo che un processo legga un file diverso per lo stesso nome file


9

Ho un'applicazione che legge un file. Chiamiamolo nome processo e il file ~ / .configuration . Quando processname viene eseguito, legge sempre ~ / .configuration e non può essere configurato in modo diverso. Esistono anche altre applicazioni che si basano su "~ / .configuration", prima e dopo, ma non mentre il nome processo è in esecuzione.

Il wrapping del nome processo in uno script che sostituisce il contenuto di ~ / .configuration è un'opzione, ma di recente ho avuto un'interruzione di corrente (mentre i contenuti sono stati scambiati), dove ho perso i contenuti precedenti di detto file, quindi questo non è desiderabile.

C'è un modo (forse usando qualcosa a cui è legato in modo distante LD_DEBUG=files processname?) Di ingannare un processo nella lettura di contenuti diversi quando tenta di leggere un file specifico? Cercare e sostituire il nome file nell'eseguibile è un po 'troppo invasivo, ma dovrebbe funzionare anche.

So che è possibile scrivere un modulo del kernel che prende in carico la open()chiamata ( https://news.ycombinator.com/item?id=2972958 ), ma esiste un modo più semplice o più pulito?

EDIT: Durante la ricerca di ~ / .configuration nell'eseguibile nome processo ho scoperto che ha provato a leggere un altro nome file prima di leggere ~ / .configuration . Problema risolto.


2
Questo potrebbe essere fatto tramite LD_PRELOADo FUSE, come con questo problema in qualche modo simile , ma non conosco alcuna implementazione esistente.
Gilles 'SO- smetti di essere malvagio' il

Risposte:


6

Nelle versioni recenti di Linux, è possibile annullare la condivisione dello spazio dei nomi mount . Cioè, è possibile avviare processi che visualizzano il file system virtuale in modo diverso (con i file system montati in modo diverso).

Questo può essere fatto anche con chroot, ma unshareè più adattato al tuo caso.

Ad esempio chroot, hai bisogno di un superutente privilegiato unsharenello spazio dei nomi di mount.

Quindi, supponiamo di avere ~/.configuratione ~/.configuration-for-that-cmdfile.

È possibile avviare un processo per il quale in ~/.configurationrealtà è un bind-mount di ~/.configuration-for-that-cmdin, ed eseguire that-cmdlì.

piace:

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

that-cmde tutti i suoi processi discendenti vedranno un diverso ~/.configuration.

that-cmdsopra verrà eseguito come root, utilizzare sudo -u another-user that-cmdse deve essere eseguito come un altro utente .


Penso che la tua soluzione sia probabilmente la migliore delle due date finora (e dato ciò che l'OP sta cercando, il reindirizzamento basato sul tempo o sui risultati di un processo di rilevamento mi sembra incerto), ma penso che vogliano un singolo file mostrarsi diversamente. Quindi probabilmente avrebbero dovuto montarlo altrove e utilizzare un collegamento simbolico, contando sui diversi punti di montaggio per agire come il vero punto di reindirizzamento.
Bratchley,

1
@JoelDavis, puoi associare qualsiasi file, non solo quelli di directory.
Stéphane Chazelas,

TIL. Ci sono controlli di sicurezza con questo, però? L'ho provato usando una sottodirectory in cui mi trovavo (vincolante da / etc / fstab) e ha restituito "Not a directory" ma ho fatto praticamente la stessa cosa sotto /teste ha funzionato senza problemi.
Bratchley,

In realtà, nm riesco a vedere la differenza, l'ho fatto in una directory la prima volta e in un file il prossimo. Supponevo che avrebbe semplicemente reindirizzato / modificato il VFS come appropriato. Comunque, grazie per il nuovo giocattolo.
Bratchley,

3

Collegamenti morbidi.

Crea due file di configurazione e punta a uno di essi con un soft link per la maggior parte del tempo, ma modifica il soft link in modo che punti all'altro quando l'app speciale è in esecuzione.

(So ​​che questo è un trucco orribile, ma è leggermente più affidabile rispetto alla modifica del contenuto del file).

Oppure, manipola $ HOME.

Nello script che avvia il fastidioso processo, imposta $ HOME come qualcosa nella normale directory $ HOME e la tua app dovrebbe quindi utilizzare il file di configurazione che si trova lì (testato e funziona per i comandi di shell di base, ~ si espande in $ HOME).

A seconda di cos'altro fa il processo, la modifica di $ HOME può avere conseguenze non intenzionali (ovvero i file di output potrebbero finire nel posto sbagliato).


1

Puoi farlo usando il trucco LD_PRELOAD . Ecco un'implementazione che mappa i percorsi che iniziano con un prefisso specifico in un'altra posizione. Il codice è anche su github .

Ad esempio, potresti falsificare l'esistenza di un file /etc/senza essere root. Ciò era necessario per il client owncloud che si rifiuta di funzionare quando il file /etc/ownCloud/sync-exclude.listnon esiste.

Funziona sovrascrivendo le funzioni open()e open64()per mappare una directory su un'altra, ad esempio tutte le open()chiamate /etc/ownCloud/...potrebbero essere reindirizzate a /home/user1/.etc/ownCloud/....

Basta regolare path_map, quindi compilare ed eseguire il programma con la lib precaricata:

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

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

Codice sorgente di 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);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.