Pewnego dnia widzę, że aplikacja webowa, którą uruchomiłem kilka dni temu, przestała działać. Zanim doszedłem do tego, co się dzieje, musiałem chwilę pomyśleć, by odtworzyć ciąg przyczynowo-skutkowy.
- Aplikacja korzysta z bibliotek natywnych.
- Biblioteki sam kompilowałem na hostingu.
- Biblioteki są dynamicznie linkowane do bibliotek systemowych.
- Administrator hostingu zrobił update.
Skutek był prosty, linker nie mógł już rozwiązać zależności, bo wersje bibliotek się nie zgadzały. Musiałem ponownie zbudować wszystkie biblioteki. By zabezpieczyć się na przyszłość, postanowiłem wykonać kopię wszystkich potrzebnych zależności dla mojej aplikacji.
Pomysł na rozwiązanie problemu był prosty, przeszukać zależności i wykonać kopię. Niektórzy na Windowsie mogli mieć do czynienia z podobnym problemem, tam wykorzystuje się narzędzie Dependency Walker. Na Unixach wystarczy odpalić ldd, by poznać, jakich bibliotek potrzebuje biblioteka.
ldd /usr/lib/libpng.so linux-vdso.so.1 (0x00007ffca6be7000) libz.so.1 => /usr/lib/libz.so.1 (0x00007fac5a048000) libm.so.6 => /usr/lib/libm.so.6 (0x00007fac59d43000) libc.so.6 => /usr/lib/libc.so.6 (0x00007fac599a2000) /usr/lib64/ld-linux-x86-64.so.2 (0x0000557eefee7000)
Wzorując się na skrypcie stąd, rozszerzyłem jego możliwości. Zamiast podawać każdą bibliotekę z osobna, należy podać katalog z własnymi bibliotekami. Dla nich zostanie stworzona kopia.
#!/bin/bash function useage() { cat << EOU Useage: bash $0 <path to the libraries> <path to copy the dependencies> EOU exit 1 } #Validate the inputs [[ $# < 2 ]] && useage #Check if the paths are valid [[ ! -e $1 ]] && echo "Not a valid input $1" && exit 1 [[ -d $2 ]] || echo "No such directory $2 creating..."&& mkdir -p "$2" exec=`find $1 -type f -exec file {} + | grep ELF | awk -F":" '{print $1}' | xargs readlink -f` #Get the library dependencies echo "Collecting the shared library dependencies for $1..." deps=$exec for el in $exec do deps=$(ldd $el | awk 'BEGIN{ORS=" "}$1\ ~/^\//{print $1}$3~/^\//{print $3}'\ | sed 's/,$/\n/')$deps done #remove duplicates deps=$(readlink -f $deps | sort | uniq) #remove dependencies to itselfs deps=$(echo -e "$deps\n$exec" | sort | uniq -u) echo "Copying the dependencies to $2" #Copy the deps for dep in $deps do echo "Copying $dep to $2" cp "$dep" "$2" done echo "Done!"
W podanym katalogu zostaną znalezione wszystkie biblioteki i binarki (kod wykonywalny w formacie ELF), a następnie rozwiązane zostaną dla nich symlinki do ścieżek bezwzględnych. Pierwotnym założeniem jest to, że katalog z bibliotekami znajduje się w ścieżce linkera (biblioteki muszą działać zanim zostaną skopiowane zależności).
Następnym krokiem jest przeszukanie zależności, wywalenie duplikatów poleceniami sort i uniq, a następnie wywalenie zależności do bibliotek, które już znajdują się w podanym do skryptu katalogu.
Przykładowo
LD_LIBRARY_PATH=/home/globalbus/lib:/usr/local/lib:/usr/local/lib/gcc48/ bash cpld.sh /home/globalbus/lib/ shadow No such directory shadow creating... Collecting the shared library dependencies for lib/... Copying the dependencies to shadow Copying /lib/libc.so.7 to shadow Copying /lib/libcrypt.so.5 to shadow Copying /lib/libcrypto.so.7 to shadow Copying /lib/libcxxrt.so.1 to shadow Copying /lib/libm.so.5 to shadow Copying /lib/libthr.so.3 to shadow Copying /lib/libz.so.6 to shadow Copying /usr/lib/libasn1.so.11 to shadow Copying /usr/lib/libc++.so.1 to shadow Copying /usr/lib/libcom_err.so.5 to shadow Copying /usr/lib/libgssapi.so.10 to shadow Copying /usr/lib/libgssapi_krb5.so.10 to shadow Copying /usr/lib/libheimbase.so.11 to shadow Copying /usr/lib/libheimntlm.so.11 to shadow Copying /usr/lib/libhx509.so.11 to shadow Copying /usr/lib/libkrb5.so.11 to shadow Copying /usr/lib/liblzma.so.5 to shadow Copying /usr/lib/libroken.so.11 to shadow Copying /usr/lib/librt.so.1 to shadow Copying /usr/lib/libssl.so.7 to shadow Copying /usr/lib/libwind.so.11 to shadow Copying /usr/lib/private/libheimipcc.so.11 to shadow Copying /usr/local/lib/gcc48/libgcc_s.so.1 to shadow Copying /usr/local/lib/gcc48/libstdc++.so.6.0.19 to shadow Copying /usr/local/lib/libcurl.so.4.4.0 to shadow Copying /usr/local/lib/libexpat.so.1.6.0 to shadow Copying /usr/local/lib/libgeos-3.5.0.so to shadow Copying /usr/local/lib/libgeos_c.so.1.9.0 to shadow Copying /usr/local/lib/libgeotiff.so.2.1.1 to shadow Copying /usr/local/lib/libgif.so.7.0.0 to shadow Copying /usr/local/lib/libiconv.so.2.5.1 to shadow Copying /usr/local/lib/libintl.so.8.1.4 to shadow Copying /usr/local/lib/libjasper.so.4.0.0 to shadow Copying /usr/local/lib/libjbig.so.2 to shadow Copying /usr/local/lib/libjpeg.so.8.0.2 to shadow Copying /usr/local/lib/libjson-c.so.2.0.1 to shadow Copying /usr/local/lib/libopenjp2.so.2.1.0 to shadow Copying /usr/local/lib/libpcre.so.1.2.5 to shadow Copying /usr/local/lib/libpng16.so.16.21.0 to shadow Copying /usr/local/lib/libpq.so.5 to shadow Copying /usr/local/lib/libproj.so.9.0.0 to shadow Copying /usr/local/lib/libsqlite3.so.0.8.6 to shadow Copying /usr/local/lib/libssh2.so.1.0.1 to shadow Copying /usr/local/lib/libtiff.so.5.2.4 to shadow Copying /usr/local/lib/libxml2.so.2.9.3 to shadow Done!
Teraz można użyć katalogu z kopią bibliotek, dodając go do ścieżki linkera
LD_LIBRARY_PATH=/home/globalbus/lib:/home/globalbus/shadow:/usr/local/lib:/usr/local/lib/gcc48/
Dzięki wykonaniu kopii, zmiana wersji bibliotek systemowych nie skutkuje przerwaniem pracy aplikacji.