Fazendo um processo ler um arquivo diferente para o mesmo nome de arquivo

9

Eu tenho um aplicativo que lê um arquivo. Vamos chamá-lo de processname e do arquivo ~ / .configuration . Quando o processname é executado, ele sempre lê ~ / .configuration e não pode ser configurado de forma diferente. Existem também outras aplicações que dependem de "~ / .configuration", antes e depois, mas não enquanto processname está em execução.

Quebra automática processname em um script que substitui o conteúdo de ~ / .configuration é uma opção, mas recentemente tive uma queda de energia (enquanto o conteúdo foi trocado) , onde eu perdi o conteúdo anterior do arquivo, então isso não é desejável.

Existe uma maneira (talvez usando algo distantemente relacionado a LD_DEBUG=files processname ?) para enganar um processo para ler conteúdos diferentes quando ele tenta ler um arquivo específico? Pesquisar e substituir o nome do arquivo no executável é um pouco invasivo, mas deve funcionar também.

Eu sei que é possível escrever um módulo do kernel que assuma a chamada open() ( link ), mas existe uma maneira mais simples ou mais limpa?

EDIT: Ao pesquisar ~ / .configuration no executável processname , descobri que ele tentou ler outro nome de arquivo antes de ler ~ / .configuration . Problema resolvido.

    
por Alexander 28.06.2013 / 12:56

3 respostas

6

Em versões recentes do Linux, você pode não compartilhar o espaço de nomes de montagem . Ou seja, você pode iniciar processos que visualizam o sistema de arquivos virtual de maneira diferente (com sistemas de arquivos montados de forma diferente).

Isso também pode ser feito com chroot , mas unshare é mais adaptado ao seu caso.

Como chroot , você precisa de privilégios de superusuário para unshare do namespace de montagem.

Então, digamos que você tenha arquivos ~/.configuration e ~/.configuration-for-that-cmd .

Você pode iniciar um processo para o qual ~/.configuration é realmente uma montagem de ligação de ~/.configuration-for-that-cmd , e execute that-cmd lá.

como:

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

that-cmd e todos os seus processos descendentes verão um% diferente~/.configuration.

that-cmd acima será executado como root , use sudo -u another-user that-cmd se precisar ser executado como outro usuário .

    
por 28.06.2013 / 16:54
3

Soft links.

Crie dois arquivos de configuração e aponte para um deles com um link temporário na maior parte do tempo, mas mude o link para apontá-lo para o outro quando o aplicativo especial estiver em execução.

(Eu sei que este é um hack horrível, mas é um pouco mais confiável do que alterar o conteúdo do arquivo).

Ou, manipule $ HOME.

No script que inicia o processo irritante, defina $ HOME para ser algo sob o diretório $ HOME normal, e seu aplicativo deve usar o arquivo de configuração localizado lá (testado e funciona para comandos básicos do shell, ~ expande para $ HOME).

Dependendo do que mais o processo faz, alterar $ HOME pode ter consequências inesperadas (ou seja, os arquivos de saída podem acabar no lugar errado).

    
por 28.06.2013 / 13:10
1

Você pode fazer isso usando o truque LD_PRELOAD . Aqui está uma implementação que mapeia caminhos começando com um prefixo específico para outro local. O código é também no github .

Por exemplo, você pode falsificar a existência de um arquivo em /etc/ sem ser root. Isso foi necessário para o cliente owncloud, que se recusa a trabalhar quando o arquivo /etc/ownCloud/sync-exclude.list não existe.

Funciona sobrescrevendo as funções open() e open64() para mapear um diretório para outro, por exemplo, todas as chamadas open() para /etc/ownCloud/... podem ser redirecionadas para /home/user1/.etc/ownCloud/... .

Basta ajustar o path_map , depois compilar e executar seu programa com o lib pré-carregado:

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

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

Código-fonte 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);
    }
}
    
por 21.10.2016 / 10:39