Piszemy własny graficzny instalator
18.08.2015 15:05
Dzięki Linuksowi dystrybucja i instalacja oprogramowania wydają się proste, a instalatory zbędne. Nawet Microsoft i Apple, a także Google poszły za ciosem i wprowadziły własne sklepy z aplikacjami. Nie mogę jednak pokryć wszystkich dystrybucji GNU/Linux przez paczki - ba nawet nie mogę wszystkich dystrybucji zainstalować, by przetestować czy paczki działają. W takim wypadku może się przydać instalator. Nie dotyczy to oczywiście bardziej popularnych składników, gdzie społeczność sama przygotowuje paczki.
Właściwy wstęp
Jakiś czas temu napisałem prosty instalator libgreattao. Problem jest jednak taki, że jest to tekstowy instalator. Postanowiłem więc napisać graficzny instalator. Większego problemu nie ma - hierarchia systemu plików GNU/Linux jest dobrze ustandaryzowana(nie dotyczy to dystrybucji Linuksa nie GNU czy dziwnych odchyleń, jak GoboLinux) - nam wystarczy zresztą wiedzieć, gdzie zainstalować biblioteki i katalog z danymi mojego rozwiązania. Jakie będą problemy? Głównym problemem jest to, że większość użytkowników GNU/Linux instaluje system z LiveCD, by nie ściągać zbyt dużo danych. W świecie menadżerów paczek nie jest to problemem, bo brakujące biblioteki zostaną od razu dociągnięte. My jednak chcemy napisać własny instalator. Z tego powodu jestem zmuszony napisać instalator libgreattao w libgreattao.
Trochę technikali
Wybrałem libgreattao, jako iż posiada ono obsługę modułów i automatycznie dobiera odpowiedni moduł do posiadanego środowiska. Jeżeli więc nie mamy biblioteki GTK+, to załaduje się biblioteka Qt. Jeżeli nie mamy środowiska graficznego, to załaduje się interfejs tekstowy. Jest to coś, co jest potrzebne instalatorowi - pokrycie jak największej możliwej ilości konfiguracji. Ponieważ chcemy napisać instalator libgreattao w libgreattao, to musimy zadbać o to, by odpowiednie biblioteki zostały załadowane w trakcie działania programu, a instalator musi wypakować najpierw specjalną wersję libgreattao i zmienić bieżący katalog na katalog z danymi właśnie wypakowanej wersji libgreattao. W przypadku instalacji innych programów/bibliotek, moglibyśmy połączyć instalator statycznie z biblioteką lub (raczej jest to możliwe) załadować biblioteką z pamięci, ale libgreattao obsługuje moduły, które muszą być obecne w systemie plików.
Zaczynamy
Pierwsze, co musimy zrobić, to wykorzystać to, czego się nauczyliśmy pisząc tekstowy instalator. Stworzyliśmy wtedy skrypt powłoki, tworzący instalator. Skorzystamy z niego, dokonując paru przeróbek. Musimy także utworzyć nowy instalator - nowy program w C. Trzeba także zmodyfikować Makefile. Do skryptu tworzącego instalator należy dopisać(tam, gdzie sprzątamy):
rm ./tarbal32-without-absolute-path rm ./tarbal64-without-absolute-path rm -r ./tarbal-files-32-without-absolute-path rm -r ./tarbal-files-64-without-absolute-path touch tarbal32 touch tarbal64 touch tarbal32-without-absolute-path touch tarbal64-without-absolute-path
Następnie należy dopisać(pod koniec bloku warunkowego):
mkdir -p ./tarbal-files-64-without-absolute-path/{share,lib64} make -C ../common clean make -C ../common make -C ../Modules clean make -C ../Modules make -C .. clean make -C .. INSTALLPATH=. make -C .. install INSTALLPATH=./Installer/tarbal-files-64-without-absolute-path cp /usr/lib64/libsell.so* -t ./tarbal-files-64-without-absolute-path/lib64 tar -czf ./tarbal64-without-absolute-path -C tarbal-files-64-without-absolute-path/ .
Ważną odnotowania jest linijka:
make -C .. INSTALLPATH=.
Kropka po installpath oznacza katalog, który będzie bieżący przy wykonaniu konkretnej instrukcji. Oznacza to, że libgreattao będzie szukać danych w bieżącym katalogu. Podobne zmiany należy wprowadzić odpowiednio dla wersji 32‑bitowej. Należy zwrócić uwagę, że w systemie musimy mieć zainstalowany libsell, który jest zależnością libgreattao i zostanie dodany do archiwum. Kolejną zmianą jest dodanie treści do Makefile. Oto one:
installer-graphical: main_gui.c tarbal32-without-absolute-path tarbal64-without-absolute-path gcc main_gui.c -g -ldl -o tao_installer2 -Wl,--format=binary -Wl,LICENSE -Wl,tarbal32 -Wl,tarbal64 -Wl,tarbal32-without-absolute-path -Wl,tarbal64-without-absolute-path -Wl,--format=default
Umożliwi to wygenerowanie instalator graficznego z pliku main_gui.c
Tworzymy właściwy instalator
Na nasze potrzeby, musimy dodać odpowiednie pliki nagłówkowe do naszego programu:
#include <libgreattao/tao.h> #include <libgreattao/log.h> #include <dlfcn.h>
Dodajemy nagłówki libgreattao, by mieć dostęp do predefiniowanych stałych, mimo iż nie łączymy programu z libgreattao na etapie kompilacji. Musimy także dodać następującą treść:
extern char without_absolute_path_start_32[] asm("_binary_tarbal32_without_absolute_path_start"); extern char without_absolute_path_end_32[] asm("_binary_tarbal32_without_absolute_path_end"); extern char without_absolute_path_start_64[] asm("_binary_tarbal64_without_absolute_path_start"); extern char without_absolute_path_end_64[] asm("_binary_tarbal64_without_absolute_path_end"); char *tarbal_start; char *tarbal_end; void (*tao_initialize2)(char *progname, char *help, int *argv, char **argc); void *(*tao_new_window2)(char *path); void (*tao_add_handler2)(void *window, char *path, void *callback, void *data); void (*tao_set_hint2)(void *window, char *path, char type, void *data); void (*tao_handler_events2)(void); void (*tao_release_window2)(void*); void (*tao_close2)(void); void (*tao_set_text2)(void*, char*, char*); void (*tao_set_modal2)(void*, char*, void*, char); void *window;
Dodaliśmy wskaźniki na funkcje importowane z libgreattao. Konieczne jest to, by nazwy tych wskaźników nie pokrywały się z nazwami z naszej biblioteki, gdyż importujemy pliki nagłówkowe naszej biblioteki. Wydzieliliśmy funkcję unpack, gdyż będziemy wypakowywać dwa archiwa - wersję libgreattao przygotowaną dla instalatora, a także wersję libgreattao do instalacji. Funkcja ta dla każdego archiwum tworzy katalog docelowy i wypakowuje pliki z pomocą polecenia tar. To były dopiero przygotowania. Teraz należy się wziąć na poważnie. Instalator tak ważnego składnika, jak biblioteki potrzebuje praw administratora. Ponieważ my możemy korzystać z trybu tekstowego, jak i graficznego, to musimy korzystać zarówno z xdg‑su, jak i z sudo. W pierwszej kolejności wywołujemy xdg‑su, gdyż program może być uruchomiony w środowisku graficznych. Jeżeli uruchomienie xdg‑su się nie powiedzie lub zakończy z błędem, to uruchamiamy sudo. By zapobiec uruchomieniu sudo, to po poprawnym wywołaniu xdg‑su, kończymy aplikację. Dla wywołania xdg‑su konieczne jest stosowanie kodów ucieczki na ścieżce do naszego programu, gdyż xdg‑su przyjmuje jako argument całe polecenie. Funkcji do escapowania stringu nie będę omawiać.
Najważniejszym krokiem jest wypakowanie naszej zmodyfikowanej wersji libgreattao, załadowanie libsell i zmodyfikowanej wersji libgreattao, a następnie utworzenie okna instalatora. Ostatniego kroku nie będę omawiać. Ścieżkę do wypakowanego archiwum libgreattao przechowuje tarbal_path, a jej wartość jest ustawiania przez mkdtemp(tworzy ono tymczasowy katalog). Tę ścieżkę przekazujemy poleceniu unpack. Bardzo istotna jest koleność ładowania bibliotek. Najpierw musimy załadować libsell. Ważne jest też użycie flagi RTLD_GLOBAL, by symbole libsell były dostępne dla libgreattao. Oto kod:
library =(char*) malloc(strlen(tarbal_path)+strlen("/libgreattao.so.2") + 1); strcpy(library,tarbal_path); strcat(library, "/libsell.so"); dlopen(library, RTLD_LAZY | RTLD_GLOBAL); free(library); library =(char*) malloc(strlen(tarbal_path)+strlen("/libgreattao.so.2") + 1); strcpy(library,tarbal_path); strcat(library, "/libgreattao.so.2"); void *lib = dlopen(library, RTLD_LAZY); free(library);
Bibliotekę należy ładować ścieżką bezwzględną, a więc konieczne jest utworzenie bufora przechowującego ścieżkę do katalogu biblioteki, a także nazwę biblioteki. Ścieżką do katalogu z bibliotekami jest ścieżka wskazywana przez tarbal_path.
Ostatnią rzeczą jest załadowanie adresów odpowiednich procedur. Robimy to tak:
tao_initialize2 = dlsym(lib, "tao_initialize");
Wywołanie wskaźnika na procedurę wygląda, jak wywołanie normalnej procedury.
To wszystko, co trzeba zrobić. Jak stworzyć interfejs graficzny w libgreattao nie będę opisywać, ale w libgreattao nie tworzy się interfejsów konkretnego typu.