Jak mogę oszukać proces, aby sądzić, że plik nie istnieje?

31

Mam program, w którym są przechowywane jego ustawienia ~/.config/myprogramktórego używam zarówno interaktywnie, jak i z systemem kolejkowania wsadowego. Podczas działania interaktywnego chcę, aby ten program używał moich plików konfiguracyjnych (i robi to). Ale podczas pracy w trybie wsadowym pliki konfiguracyjne nie są konieczne, ponieważ określam opcje wiersza polecenia, które zastępują wszystkie odpowiednie ustawienia. Ponadto dostęp do plików konfiguracyjnych przez sieć wydłuża czas uruchamiania programu o kilka sekund; jeśli pliki nie istnieją, program uruchamia się znacznie szybciej (ponieważ każde zadanie zajmuje tylko około minuty, ma to znaczący wpływ na przepustowość zadań wsadowych). Ponieważ jednak korzystam z programu interaktywnie, nie chcę cały czas przenosić / usuwać plików konfiguracyjnych. W zależności od tego, kiedy moje zadania wsadowe zostaną zaplanowane w klastrze (na podstawie wykorzystania przez innych użytkowników),

(Poza tym: wydajność pliku sieciowego jest tak niska, prawdopodobnie jest to błąd, ale jestem tylko użytkownikiem klastra, więc mogę tylko go obejść, a nie naprawić).

Mógłbym zbudować wersję programu, która nie odczytuje plików konfiguracyjnych (lub ma opcję wiersza poleceń, aby tego nie robić) do użycia wsadowego, ale środowisko kompilacji tego programu jest źle zaprojektowane i trudne do skonfigurowania. Wolałbym używać plików binarnych zainstalowanych za pomocą menedżera pakietów mojego systemu.

Jak mogę oszukać poszczególne instancje tego programu, aby udawać, że moje pliki konfiguracyjne nie istnieją (bez modyfikacji programu)? Mam nadzieję na opakowanie formularza pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., ale jestem otwarty na inne rozwiązania.

Jeffrey Bosboom
źródło
2
Możesz uruchomić go jako użytkownik, który nie ma uprawnień do odczytu pliku.
psimon
1
@psimon Jako „tylko użytkownik” klastra, nie mogę utworzyć nowego użytkownika do uruchomienia zadania wsadowego jako. To jednak sprytny pomysł i jeśli nie ma lepszych sugestii, poproszę administratora klastra, aby zrobił to za mnie.
Jeffrey Bosboom
Lub skonfiguruj skrypt, który najpierw zmienia nazwę pliku konfiguracyjnego, uruchamia program, a następnie ponownie zmienia nazwę pliku konfiguracyjnego.
psimon
@psimon Wydaje mi się, że mógłbym być bardziej przejrzysty: mogę używać programu interaktywnie i jednocześnie w trybie wsadowym, w zależności od tego, kiedy moje zadania wsadowe zostaną zaplanowane w klastrze.
Jeffrey Bosboom
1
Tak, jeśli jest on dynamicznie połączony, możesz użyć LD_PRELOADhaka. To łatwiejsze (możesz to zaimplementować za godzinę lub dwie, jeśli znasz C) niż alternatywa, którą jest ptrace. Prawdopodobnie możesz też użyć do tego fakechroot (jak sądzę, LD_PRELOAD).
derobert

Odpowiedzi:

33

Ten program prawdopodobnie rozpoznaje ścieżkę do tego pliku z $HOME/.config/myprogram. Możesz więc powiedzieć, że twój katalog domowy znajduje się gdzie indziej, na przykład:

HOME=/nowhere your-program

Być może twój program potrzebuje innych zasobów w twoim katalogu domowym. Jeśli wiesz, które to są, możesz przygotować fałszywy dom dla swojego programu z linkami do potrzebnych tam zasobów.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
Stéphane Chazelas
źródło
5
Ta odpowiedź rozwiązuje mój problem, więc zaakceptowałem ją, mimo że nie jest to w pełni ogólna odpowiedź na pytanie zawarte w tytule tego pytania. Hak napięcia wstępnego, jak opisano w innej odpowiedzi, jest rozwiązaniem bardziej ogólnym (ale także wymagającym większego wysiłku).
Jeffrey Bosboom
28

Jeśli wszystko inne zawiedzie, napisz bibliotekę opakowań, którą będziesz wstrzykiwać LD_PRELOAD, aby open("/home/you/my-program/config.interactive")przechwycić wywołanie, ale inne przejdzie. Działa to dla każdego rodzaju programu, nawet skryptów powłoki, ponieważ będzie filtrować wywołania systemowe.

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

Uwaga: Nie testowałem tego kodu i nie jestem w 100% pewien, że ta errnoczęść działa.

Zobacz, jak fakerootto działa w przypadku połączeń takich jak getuid(2)i stat(2).

Zasadniczo linker połączy tę aplikację z biblioteką, która zastępuje opensymbol. Ponieważ nie możesz używać dwóch różnych funkcji nazwanych openwe własnej bibliotece, musisz je rozdzielić na drugą część (np. get_real_open), Która z kolei będzie prowadzić do pierwotnego openwywołania.

Oryginalny: ./Application

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

Przechwycone: LD_PRELOAD=yourlib_wrap.so ./Application

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

Edycja: Najwyraźniej istnieje ldflaga, którą można włączyć ( --wrap <symbol>), która pozwala pisać opakowania bez konieczności podwójnego łączenia:

/* 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
źródło
2

Odsuń plik konfiguracyjny i napisz opakowanie powłoki dla interaktywnego przypadku użycia, który kopiuje plik do normalnego miejsca docelowego, uruchamia program i usuwa go przy wyjściu.

zadzwonić
źródło
Zobacz moją ostatnią edycję: nie kontroluję planowania wsadowego, więc mogę używać programu interaktywnie i jako część zadania wsadowego w tym samym czasie.
Jeffrey Bosboom
1

Powinno to być możliwe w przypadku unionfs / aufs. Tworzysz chrootśrodowisko dla procesu. Używasz prawdziwego katalogu jako warstwy tylko do odczytu i umieszczasz na nim pusty. Następnie podłącz wolumin unionfs do odpowiedniego katalogu w chrootśrodowisku i tam usuń plik. Proces tego nie zobaczy, ale wszyscy inni to zrobią.

Hauke ​​Laging
źródło
0

Zmień nazwę pliku konfiguracyjnego na np config.interactive. Utwórz kolejny pusty plik o nazwie np config.script.

Teraz utwórz miękkie łącze o nazwie config(lub cokolwiek aplikacja oczekuje jako plik konfiguracyjny) do dowolnej potrzebnej konfiguracji i uruchom aplikację.

ln -s config.interactive config

Pamiętaj, aby później uporządkować swój link.

garethTheRed
źródło
Zobacz moją ostatnią edycję: nie kontroluję planowania wsadowego, więc mogę używać programu interaktywnie i jako część zadania wsadowego w tym samym czasie. Ta odpowiedź jest zasadniczo taka sama, jak przenoszenie plików, ręcznie lub za pomocą skryptu.
Jeffrey Bosboom
1
Doh! Muszę myśleć i pisać szybciej. Czy to duża aplikacja? Czy można go przenieść do chroota i uruchomić stamtąd interaktywnie? Wszystko zależy od tego, z czym program wchodzi w interakcje. Może to być bardzo żmudne zadanie, aby umieścić wszystko, czego potrzeba, w chroot. (
Wydaje
0

Jeśli dokładnie scharakteryzowałeś sposób, w jaki twój program korzysta z pliku konfiguracyjnego, przeoczyłem go. Wiele programów (takich jak bashi vi) sprawdza plik konfiguracyjny natychmiast po uruchomieniu; jeśli plik istnieje, przeczytaj go i zamknij. Te programy nigdy więcej nie uzyskują dostępu do tych plików inicjujących. Jeśli twój program jest taki, czytaj dalej.

Wiem, że odrzuciłeś odpowiedzi, które sprawiają, że plik konfiguracyjny naprawdę nie istnieje (zmieniając jego nazwę), ale mam zmarszczkę, której nikt inny nie widział. Zrób to, gdy wywołasz program w trybie wsadowym:

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

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

To usuwa plik konfiguracyjny z drogi, ale następnie przesuwa go o sekundę wstecz, nawet jeśli myprogramnadal działa. Tworzy to bardzo krótkie okno czasu, w którym plik jest niedostępny - jakie jest prawdopodobieństwo, że program będzie uruchamiany interaktywnie podczas tego okna? (Nawet jeśli to zrobisz, możesz po prostu wyjść i uruchomić ponownie, a plik konfiguracyjny prawdopodobnie wróci na swoje miejsce).

To tworzy warunki wyścigu; jeśli program zajmuje zbyt dużo czasu, aby przejść do otwarcia pliku, może uzyskać prawdziwy plik. Jeśli zdarza się to wystarczająco często, że jest to problem, po prostu zwiększ wartość DELAY_TIME.

Scott
źródło
1
Co jest najgorsze, co może pójść nie tak? Dosłownie widziałem, że tak się dzieje. W produkcji.
Henk Langeveld
-1

I jak Stephane odpowiedź, ale to będzie oszukać dowolny program do przypuszczeń, każdy plik jest pusty - (ponieważ jego dentry tymczasowo wskazuje na plik, który rzeczywiście jest pusta) :

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

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

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

Możesz także:

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

Jeśli chciałeś.

mikeserv
źródło
Jest to zasadniczo równoważne z kilkoma wcześniejszymi odpowiedziami (z wyjątkiem tego, jak sądzę, ta wymaga uprawnienia użytkownika). OP odrzucił te inne odpowiedzi, ponieważ nie chce oszukać żadnego procesu - chce oszukać wsadowe wywołanie programu, jednocześnie pozwalając interaktywnemu wywołaniu normalnie zobaczyć plik konfiguracyjny.
Scott
@ Scott - nie zgadzam się - każda inna odpowiedź przed tą zalecała zmianę wariacji mvpliku - która może mieć inne konsekwencje niż wpływ na jego uzębienie, takie jak obcięcie pliku lub innych itd. - podczas gdy operacja działa tylko na. Mimo to należy unshare, że mountchyba ...
mikeserv