Como enganar um processo para que um arquivo não exista?

31

Eu tenho um programa que armazena suas configurações em ~/.config/myprogramque eu uso interativamente e com um sistema de enfileiramento em lote. Ao executar interativamente, desejo que este programa use meus arquivos de configuração (e é o que faz). Mas, quando executados no modo em lote, os arquivos de configuração não são necessários porque eu especifico opções de linha de comando que substituem todas as configurações relevantes. Além disso, o acesso aos arquivos de configuração pela rede aumenta o tempo de inicialização do programa em vários segundos; se os arquivos não existirem, o programa será iniciado muito mais rapidamente (como cada trabalho leva apenas cerca de um minuto, isso tem um impacto significativo no rendimento do trabalho em lotes). Mas como também uso o programa interativamente, não quero mover / excluir meus arquivos de configuração o tempo todo. Dependendo de quando meus trabalhos em lotes são agendados no cluster (com base no uso de outros usuários),

(Além disso: o desempenho do arquivo de rede é tão lento provavelmente é um bug, mas eu sou apenas um usuário do cluster, então só posso contorná-lo, não corrigi-lo.)

Eu poderia criar uma versão do programa que não lê os arquivos de configuração (ou não tem uma opção de linha de comando) para uso em lote, mas o ambiente de compilação desse programa é mal projetado e difícil de configurar. Eu preferiria usar os binários instalados através do gerenciador de pacotes do meu sistema.

Como enganar instâncias específicas deste programa para fingir que meus arquivos de configuração não existem (sem modificar o programa)? Espero um invólucro do formulário pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., mas estou aberto a outras soluções.

Jeffrey Bosboom
fonte
2
Você pode executá-lo como um usuário que não tem permissão para ler o arquivo.
Ps26:
1
@psimon Como "apenas um usuário" do cluster, não consigo criar um novo usuário para executar meu trabalho em lotes como. Essa é uma ideia inteligente e, se não houver sugestões melhores, vou incomodar o administrador do cluster para fazer isso por mim.
precisa
Ou configure um script que renomeie o arquivo de configuração, execute o programa e renomeie o arquivo de configuração novamente.
Psimon
@psimon Acho que poderia ter sido mais claro: posso estar usando o programa de forma interativa e em modo de lote ao mesmo tempo, dependendo de quando meus trabalhos em lotes são agendados no cluster.
Jeffrey Bosboom
1
Sim, se estiver vinculado dinamicamente, você pode usar um LD_PRELOADgancho. Isso é mais fácil (você pode implementar isso em uma hora ou duas, se souber C) do que a alternativa, que é ptrace. Você provavelmente também poderia usar o fakechroot para fazer isso (que é LD_PRELOAD, acredito).
derobert

Respostas:

33

Esse programa provavelmente resolve o caminho para esse arquivo $HOME/.config/myprogram. Então você pode dizer que seu diretório pessoal está em outro lugar, como:

HOME=/nowhere your-program

Agora, talvez o seu programa precise de algum outro recurso no seu diretório pessoal. Se você souber quais são, poderá preparar uma casa falsa para o seu programa, com links para os recursos necessários.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
Stéphane Chazelas
fonte
5
Esta resposta resolve meu problema, por isso a aceitei mesmo que não seja uma resposta totalmente geral à pergunta no título dessa pergunta. Um gancho de pré-carga, conforme descrito em outra resposta, é uma solução mais geral (mas também com maior esforço).
precisa
28

Se tudo mais falhar, escreva uma biblioteca de wrapper que você injetará LD_PRELOADpara que a chamada open("/home/you/my-program/config.interactive")seja interceptada, mas qualquer outra seja transmitida. Isso funciona para qualquer tipo de programa, mesmo shell scripts, pois filtrará as chamadas do 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: Eu não testei esse código e não tenho 100% de certeza de que a errnopeça funcione.

Veja como o fakerootfaz para chamadas como getuid(2)e stat(2).

Basicamente, o vinculador vincula esse aplicativo à sua biblioteca, que substitui o opensímbolo. Como você não pode usar duas funções diferentes nomeadas openem sua própria biblioteca, é necessário separá-la em uma segunda parte (por exemplo get_real_open) que, por sua vez, será vinculada à openchamada 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()

Edit: Aparentemente, existe um ldsinalizador que você pode ativar ( --wrap <symbol>) que permite escrever wrappers sem ter que recorrer à vinculação dupla:

/* 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
fonte
2

Mova seu arquivo de configuração para fora do caminho e escreva um wrapper de script de shell para o caso de uso interativo que copia o arquivo para seu destino normal, executa o programa e o exclui na saída.

consertar
fonte
Veja minha edição recente: eu não controlo a programação em lote, portanto, posso estar usando o programa interativamente e como parte de um trabalho em lotes ao mesmo tempo.
precisa
1

Isso deve ser possível com o unionfs / aufs. Você cria um chrootambiente para o processo. Você usa o diretório real como camada somente leitura e coloca um vazio em cima dele. Em seguida, você monta o volume unionfs no diretório respectivo no chrootambiente e exclui o arquivo lá. O processo não o verá, mas todos os outros o fazem.

Hauke ​​Laging
fonte
0

Renomeie o arquivo de configuração para, por exemplo config.interactive. Crie outro arquivo vazio chamado por exemplo config.script.

Agora, crie um link virtual chamado config(ou o que o aplicativo espera como um arquivo de configuração) para a configuração real necessária e execute seu aplicativo.

ln -s config.interactive config

Lembre-se de arrumar seu link posteriormente.

garethTheRed
fonte
Veja minha edição recente: eu não controlo a programação em lote, portanto, posso estar usando o programa interativamente e como parte de um trabalho em lotes ao mesmo tempo. Essa resposta é essencialmente a mesma que mover os arquivos, manualmente ou com um script.
precisa
1
Doh! Eu preciso pensar e digitar mais rápido. É uma aplicação grande? Poderia ser transferido para um chroot e executado a partir dali interativamente? Tudo depende do que o programa interage, suponho. Também pode ser uma tarefa muito entediante colocar tudo o que é necessário em um chroot também. (Eu acho que eu falei me fora dessa opção!)
garethTheRed
0

Se você caracterizou precisamente como seu programa usa o arquivo de configuração, eu o ignorei. Muitos programas (como bashe vi) verificarão um arquivo de configuração imediatamente após o início; se o arquivo existir, leia-o e feche-o. Esses programas nunca acessam esses arquivos de inicialização novamente. Se o seu programa for assim, continue lendo.

Sei que você rejeitou respostas que tornam o arquivo de configuração realmente inexistente (renomeando-o), mas tenho uma ruga que ainda não vi proposta por mais ninguém. Faça isso quando você chamar o programa no modo em lote:

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

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

Isso move o arquivo de configuração para fora do caminho, mas o move um segundo depois, mesmo se myprogramainda estiver em execução. Isso cria uma janela muito breve durante o qual o arquivo não está disponível - qual é a probabilidade de você executar o programa interativamente durante essa janela? (Mesmo que você faça isso, você pode simplesmente sair e reiniciar, e o arquivo de configuração provavelmente estará de volta no lugar.)

Isso cria uma condição de corrida; se o programa demorar muito para abrir o arquivo, ele poderá obter o arquivo real. Se isso acontecer com frequência suficiente, é um problema, apenas aumente o valor de DELAY_TIME.

Scott
fonte
1
Qual é a pior coisa que pode dar errado? Eu literalmente vi isso acontecer. Em produção.
Henk Langeveld
-1

I como a resposta de Stephane, mas isso vai enganar qualquer programa em acreditar qualquer arquivo está vazio - (porque a sua dentry temporariamente aponta para um arquivo que realmente está vazia) :

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

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

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

Você também pode:

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

Se você quisesse.

mikeserv
fonte
Isso é essencialmente equivalente a algumas das respostas anteriores (exceto, acredito, que esta requer que o usuário seja privilegiado). O OP rejeitou essas outras respostas porque ele não deseja enganar nenhum processo - ele deseja enganar a invocação em lote do programa, enquanto permite que a invocação interativa veja o arquivo de configuração normalmente.
Scott
@ Scott - eu discordo - todas as outras respostas anteriores recomendam alguma variação no mvarquivo - que pode ter outras conseqüências além de afetar seu dentry, como na verdade truncar o arquivo ou outros e etc. - enquanto isso funciona apenas com. Ainda assim, devo unshareque mounteu acho ...
mikeserv