2015-03-09 59 views
8

W mojej aplikacji C++ moja aplikacja wykonuje execv() w procesie potomnym, aby użyć tego samego pliku wykonywalnego do przetworzenia pracy w nowym procesie potomnym z różnymi argumentami, które komunikują się z rurami z procesem nadrzędnym. Aby uzyskać ścieżkę do siebie, ja wykonać następujący kod na porcie Linux (mam inny kod na komputerze Macintosh):Jak radzić sobie z readlink() z "/ proc/self/exe", gdy plik wykonywalny jest zastępowany podczas wykonywania?

const size_t bufSize = PATH_MAX + 1; 
    char dirNameBuffer[bufSize]; 
    // Read the symbolic link '/proc/self/exe'. 
    const char *linkName = "/proc/self/exe"; 
    const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1)); 

Jednakże, jeśli natomiast wykonywalny jest uruchomiony, wymienić wykonywalny z zaktualizowanej wersji binarna na dysku, wynik readlink() ciąg jest: "/usr/local/bin/myExecutable (deleted)"

rozumiem, że mój plik został zastąpiony przez nowszą zaktualizowaną wersję i oryginał do /proc/self/exe jest teraz zastąpiony jednak, kiedy idę do execv() teraz nie z errno 2 - No such file or directory. ze względu na dodatkowe końcowe " (deleted)" w wyniku.

Chciałbym, aby execv() używał starego pliku wykonywalnego dla siebie lub zaktualizowanego. Mogłem po prostu wykryć ciąg kończący się na " (deleted)" i zmodyfikować go, aby to pominąć i rozwiązać zaktualizowany plik wykonywalny, ale wydaje mi się to niezręczne.

Jak mogę execv() bieżącego pliku wykonywalnego (lub jego wymiany, jeśli jest to łatwiejsze) z nowym zestawem argumentów, gdy oryginalny plik wykonywalny został zastąpiony przez zaktualizowany podczas wykonywania?

+1

Dlaczego po prostu nie używać 'argv [0]', jak przekazano do 'main'? –

+0

@ JonathonReinhart ponieważ argv [0] nie zawsze podaje ścieżkę do pliku wykonywalnego i może jedynie nazwać plik wykonywalny. – WilliamKF

+0

Interesujący problem. Myślę, że przycięcie "(usuniętego)" byłoby tutaj najprostszym rozwiązaniem. –

Odpowiedz

4

Zamiast readlink aby odkryć ścieżkę do własnego pliku wykonywalnego, możesz bezpośrednio wywołać open na /proc/self/exe. Ponieważ jądro ma już otwarte fd do aktualnie wykonywanych procesów, to da ci fd niezależnie od tego, czy ścieżka została zastąpiona nowym plikiem wykonywalnym, czy też nie.

Następnie można użyć fexecve zamiast execv która przyjmuje parametr fd zamiast parametru filename do pliku wykonywalnego.

int fd = open("/proc/self/exe", O_RDONLY); 
fexecve(fd, argv, envp); 

Powyższy kod pomija obsługę błędów dla zwięzłości.

+0

Nie miałem jeszcze okazji potwierdzić, ale lektura brzmi genialnie! – WilliamKF

1

Powód, dla którego część symboliczna otrzymuje część (deleted), polega na tym, że plik został zastąpiony właściwym tekstem binarnym programu innym plikiem, a dowiązanie symboliczne do pliku wykonywalnego nigdy nie jest ważne. Załóżmy, że używasz tego dowiązania symbolicznego, aby uzyskać tabelę symboli tego programu lub załadować niektóre dane osadzone na nim, a zmienisz program ... tabela będzie niepoprawna, a nawet możesz zawiesić swój program. Plik wykonywalny programu, który wykonywałeś, nie jest już dostępny (usunąłeś go), a program, który umieściłeś na swoim miejscu, nie odpowiada binarnie, które wykonujesz.

Kiedy unlink (2) program, który jest wykonywany, znaczniki jądra że dowiązania symbolicznego w /proc, więc program może

  • wykryć, że binarny został usunięty i nie jest już dostępny.
  • pozwalają jeszcze zebrać trochę informacji o nazwisku to miało (zamiast usuwania dowiązania z drzewa /proc)

Nie można zapisać do pliku, który jest aktualnie wykonywany przez jądro, ale nikt nie zapobiega aby usunąć ten plik. Plik będzie nadal obecny w systemie plików tak długo, jak go uruchomisz, ale żadna nazwa nie wskaże go (jego przestrzeń zostanie zwolniona po zakończeniu procesu exit (2)). Jądro nie kasuje jego zawartości dopóki nie zostanie wszczęty liczyć w pamięci jądra dostaje się do zera, co dzieje się, gdy wszystkie zastosowania (odniesienia) do tego pliku są należne.

2

Jednym z rozwiązań jest uruchomienie uruchamialne (np.blisko początku main()), aby odczytać wartość link /proc/self/exe raz i przechowywać go statycznie do wykorzystania w przyszłości:

static string savedBinary; 
    static bool initialized = false; 

    // To deal with issue of long running executable having its binary replaced 
    // with a newer one on disk, we compute the resolved binary once at startup. 
    if (!initialized) { 
    const size_t bufSize = PATH_MAX + 1; 
    char dirNameBuffer[bufSize]; 
    // Read the symbolic link '/proc/self/exe'. 
    const char *linkName = "/proc/self/exe"; 
    const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1)); 

    savedBinary = dirNameBuffer; 

    // On at least Linux, if the executable is replaced, readlink() of 
    // "/proc/self/exe" gives "/usr/local/bin/flume (deleted)". 
    // Therefore, we just compute the binary location statically once at 
    // startup, before it can possibly be replaced, but we leave this code 
    // here as an extra precaution. 
    const string deleted(" (deleted)"); 
    const size_t deletedSize = deleted.size(); 
    const size_t pathSize = savedBinary.size(); 

    if (pathSize > deletedSize) { 
     const size_t matchPos = pathSize - deletedSize; 

     if (0 == savedBinary.compare(matchPos, deletedSize, deleted)) { 
     // Deleted original binary, Issue warning, throw an exception, or exit. 
     // Or cludge the original path with: savedBinary.erase(matchPos); 
     } 
    } 
    initialized = true; 
    } 

    // Use savedBinary value. 

W ten sposób, to jest bardzo prawdopodobne, że oryginalny plik wykonywalny zostanie zastąpiony w ciągu mikrosekund głównego() buforowanie ścieżki do pliku binarnego. W ten sposób długotrwała aplikacja (na przykład godziny lub dni) może zostać zastąpiona na dysku, ale na oryginalne pytanie może zostać zaktualizowana wersja binarna, która prawdopodobnie ma poprawkę. Ma to dodatkową zaletę, że działa na różnych platformach, dlatego też differing Macintosh code, aby odczytać ścieżkę binarną, można również chronić przed binarną wymianą po uruchomieniu.

UWAGA redaktorzy uwaga: readlink nie zerowa zakończyć ciąg, więc powyższy program może lub nie może pracować przypadkowo jeśli bufor nie została wypełniona zerami przed wywołaniem readlink

+0

Mało prawdopodobne nie oznacza niemożliwego. Nadal bym sprawdzał błędy w tej sprawie. Ale to jest fajne – inetknght

+0

Wierzę, że mam idealne rozwiązanie - zobacz odpowiedź, którą właśnie dodałem. – andrewrk

+0

@andrewrk Dzięki, kocham Twoje rozwiązanie. Czy potwierdziłeś, że działa w pliku wykonywalnym? Obecnie nie jestem w stanie, ale czytając, moje oczy mówią, że to działa! – WilliamKF