__ _ ____ __ __ / / (_)___ __ ___ __ / __ \____ ____/ /____/ /_____ __ ____ __ / / / / __ \/ / / / |/_/ / /_/ / __ \/ __ / ___/ __/ __ `/ | /| / / / / / / /___/ / / / / /_/ /> <_ / ____/ /_/ / /_/ (__ ) /_/ /_/ /| |/ |/ / /_/ / /_____/_/_/ /_/\__,_/_/|_(_) /_/ \____/\__,_/____/\__/\__,_/ |__/|__/\__, (_) /____/
☠ morketsmerke ☠
Opisywany tutaj materiał będzie kompatybilny z dystrybucjami pochodnymi od GNU/Linux Debian zarówno tymi opartymi na systemd jak sysvinit oraz tymi z rodziny RHEL/Fedora/CentOS.
GNU/Linux czy raczej sam Linux? Sama nazwa, jest już tematem dość kontrowersyjnym. Ludzie związani z projektem GNU twierdzą, że ta pierwsza liczba jest właściwa ponieważ wskazuje ona na to, że isotne elemnty projektu GNU zostały wykorzystane do stworzenia tego systemu. W mowie potocznej jednak przyjęło się użycie tej drugiej nazwy. Jest to jedno, łatwe do zapamiętania słowo. Jeśli mówimy następujące zdanie wyrażające chęć zainstalowania na jakiejś maszynie omawianego tutaj systemu, mówimy że "zainstalujemy jakiegoś Linuksa". Słowo "jakiegoś" zostało tu użyte w kontekscie wyboru konkretnej dystrybucji. Co to dystrybucja wyjaśnie za chwilę. Bez projektu GNU niebyło by Linuksa. Wydaje mi się, że każdy kto jest nieco bardziej związany z tym środowiskiem o tym wie. Ja również jestem tego świadom dla tego też w tym dokumencie użyje nazwy Linux. Poprostu.
1. Budowa systemu Linux
Nie zagłebiając się w szczegóły, to Linux składa się z jądra oraz przestrzeni użytkownika. Oba kompomenty rezydują w pamięci więc wiele, nie które teksty popularno-naukowe mogą włączać pamięć lub ogólnie sprzęt do składowych systemu operacyjnego Linux, w mojej opinii jest raczej cecha wykorzystywanych przez nas komputerów konwencjonalnych.
Mówiąc o jądrze możemy wskazać konktretny program, konkretny plik. W przypadku przestrzeni użytkownika, w systemie nie istnieje żaden namacalny byt cyfrowy jak w przypadku jądra. Przestrzeń użytkownika jest bowiem warstwą abstrakcji - czyli terminem, bądź założeniem wykorzystywanym w celu określenia czynności, funkcji, zjawiska bez wdawania się w szczegóły. Przestrzeń użytkownika jest miejscem uruchamiania procesów użytkownika. Procesy to nic innego jak wystąpienia programów uruchomionych przez użytkownika. Nie wszystkie procesy są programami użytkownika w dosłownym tych słów znaczeniu. Część tych procesów to programy wspomagające wykorzystanie komputera i jego zasobów. Bez nich systemy operacyjne dalej mogły by spełniać swoją rolę, jednak nie miały by powszechnie znanej nam dzisiaj formy. Przestrzeń użytkownika składa się z wielu ogólno dostępnych kompnentów ich istnienie w danej wersji systemu oraz ich konfiguracja sprawia, iż nie mamy doczynienia z gotowym jednolitym produktem, ale z dystrybucją. Z jedną z wersji, gdzie ktoś wziął jądro, które jest ogolno dostępne i skomponował przestrzeń użytkownika. Obecnie na rynku mamy dostępnych ok. 600 dystrybucji. Wiekszość z nich to pochodne innych, oryginalnych rozwiązań rozwijanych przez setki osób na całym świecie. Kilka takich głównych dystrybucji, znajduje się w tabeli poniżej. Przejrzałem większość z nich, a z częsci osobiście korzystałem.
Logo | Nazwa | Opis |
---|---|---|
![]() |
Linux Mint | Dystrybucja bardzo przyjazna użytkownikowi. Wykorzystywana przez nowych niedoświadczonych użytkowników system Linux. Pod czas instalacji mogą być instalowane nie wolne moduły oraz nie wolne oprogramownie. Jej głównym zadaniem jest sprzyjanie użytkownikowi i umożliwienie mu wykorzystanie Linuksa przy codziennym wykorzystaniu komputera. Mint rozwijany jest przez społeczność zebraną wokół niego. |
![]() |
Ubuntu | Podobnie jak Linux Mint, Ubuntu również jest skierowane dla osób ceniących sobie wygodne i prostę rozwiązania. Jest przyjazna użytkownikowi, ma nieco bardziej konserwatywne podejście do ideii wolnego oprogramowania, jądro może zawierać nie wolne moduły, jednak zamknięte oprogramowanie nie jest domyślnie instalowane. Ubuntu rozwijane jest przez firmę Canonical. Jej technologię są wdrażane do Ubuntu, dzięki czemu może ona uchdzić za system klasy enterprise wśród dystrybucji opartych o GNU/Linux Debian. Poza wersją na komputery biurkowe istnieją również wersja skierowana na serwery oraz inne wersje z preinstalowanymi różnymi środowiskami graficznymi czy wersja skierowana do obróbki multimediów zawierająca pozwalające do tego oprogramowanie. Społeczność zebrana wokół systemu Linux zarzuca jej siłowe próby wdrożenia manedżera oprogramowania Snap, rozwijanego przez tę firmę przez co może ona pretendować do stopniowego zarzucenia klasycznego schematu dystrybucji pakietów rozwijanego wraz z GNU/Linux Debian. |
![]() |
Fedora Linux | Fedora jest dystrybucją skierowaną do różnej maści użytkowników, ponieważ istnieje w kilku głównych wersjach. oraz wiele wersji pobocznych tzw. spins. Fedora ma najprzyjźniejszy instalator chyba ze wszystkich możliwych dystrybucji. Wymaga on głównie wybrania miejsca instalacji i kliknięcia przycisku dalej. Fedora została stworzona i jest rozwiajana przez firme Red Hat Inc. (obecnie IBM) jako upstream (poligon doświadczalny dla zmian), dla glównego produktu tej firmy Red Hat Enterprise Linux - płatnej dystrybucji skierowanej do środowisk produkcyjnych (100$ rocznie). Jest to system o dużej stabilości ze wsparciem dla najnowszego sprzetu. Fedora również charakteryzuje się wprowadzeniej jako pierwsza środowiska GNOME w najnowszej wersji 41 oraz innych nowych technologi wśród otwartego oraz wolnego oprogramowania. |
![]() |
GNU/Linux Debian | Debian jest jedną z pierwszy dostępnych dystrybucji, początek jej istnienia jest datowany na 1993 rok. Dystrybucja konserwatywna, posiadała w pierwszych latach swojego istnienia aprobatę FSF (Free Software Fundation). Jednak została ona wycofana, za zezwolenie na instalację zamkniętego oprogramowania. Kernel przygotowywany przez twórców tej dystrybucji pozbawiony jest tzw. blobów binarnych (nie wolnych prekompilowanych modułów, używanych przy budowaniu jądra.) Bloby najczęściej dotyczą sterowników sprzętu. Dystrybucja charkteryzuje się wysoką stabilnościa porównywalną z RHEL, wsparciem dla starszego sprzętu. Jedną z cech, która może odstraszać potencjalnych użytkowników od niej jest długi cykl wydawniczy (co dwa lata) oraz używanie sprawdzone oprogramowania czy technologii (pozostaje dość mocno w tyle jeśli chodzi o najnowsze wersje oprogramowania). Wydaje mi się, że niema stabilniejszego gotowego rozwiązania niż GNU/Linux Debian. Debian wymaga nieco większego zaawansowania niż dystrybucje podane do tej pory. Stosowany jest częściej w środowiskach produkcyjnych niż np. Ubuntu. Rozwój Debiana opiera się na zaangażowaniu społeczności z całego świata. |
![]() |
Arch Linux | Dystrybucja skierowana do zaawansowanych użytkowników. Charakteryzuje się wysoką konfigurowalnością oraz dostępnością najnowszych wersji oprogramowania. Nie posiada oficjalnego instalatora, choć można pobrać skrypt z sieci. Instalacji dokonuj się ręcznie, wpisującac kolejne polecenia z podręcznika instalacji w środowisku LiveCD, gdzie przygotowuje się dysk, pobiera się pakiety i je konfiguruje. Instalacja i konfiguracji Arch Linux nie jest tak pracochłonna jak innych dystrybucji, można by powiedzieć, meta-dystrybucji. Dość ciekawą cechą jest społeczność zebrana wokół niej, która przechwalająca się swoją wyższością na innymi (ponieważ przebrneli przez proces instalacji) używając frazy "I use Arch BTW.". Dystrybucja rozwijana jest przez społeczność na całym świecie. |
![]() |
Void Linux | Nie zależna dystrybucjna, trochę odmienna od inny dystrybucji głównego nurtu. Systemd zastąpiono programem runit, zamiast OpenSSL, użyto projektu OpenBSD LibreSSL jak jedyna z dystrybucji Linuksa. Kernel Void-a pozbawiony jest blobów, a domyślna instalacja zawiera tylko wolne oprogramowanie, posiada on jednak oficjalne repozytorium z zamkniętym oprogramowaniem. Instalacja pakietów opiera się stworzonym dla Void menedżerze pakietów XBPS. Pakiety są wydawane stylu rolling release, co daje szybkie i stabline aktualizacje. Obok standardowej biblioteki języka C - GNU libc, mamy również bibliotekę musl. Za pomoca programu xbps-src możemy tworzyć z kodu źródłowego własne pakiety XBPS. |
![]() |
Gentoo Linux | Gentoo jest dystybucją na tyle zaawansowaną, że można by się pokusić o nazwanie jej meta-dystrybucją. Jest ona bowiem jedną z najbardziej konfigurowalnych dystrybucji. Jedną z ciekawszych czynności, jakie należy wykonać podczas instalacji, to ręczna kompliacja jądra. Dystrybucja skierowana do jeszcze bardziej zaawansowanych użytkowników niż w przypadku Arch Linux. Instalacja Gentoo na maszynie wirtualnej wraz z poradnikiem, zajeło mi to jakieś dwie godziny. |
![]() |
Linux from scratch | LFS to w zasadzie projekt, a niżejli sama dystrybucja. Umożliwia on stworzenie oraz skonfigurowanie własnej dystrybucji. Na stronie projektu zawarte są wskazówki, co należy zrobić, aby stworzyć rozwiązanie najbardziej elastyczne dla siebie. LFS z pewnością może nosić miano meta dystrybucji. |
W powyższej tabeli przedstawiłem dystrybucje, na które warto zwrócić uwagę. Teraz prawdopodobnie czekać będzie Cię duży dylemat, którą wybrać. W pierwszej kolejności ważny jest sprzęt, na którym będziemy z tego systemu korzystać. Część sprzętu, z którego chcemy korzystać może nie działać out of box, wowczas potrzebne będą sterowniki, które mogą być własnościowe (nie wolne, generalnie być zamkniętym oprogramowaniem), jeśli zależy nam na prywatności, to lepiej upewnić się z jakiego rodzaju sprzętem będzie mieć doczynienia, ponieważ każde zamknięte oprogramowanie można teoretycznie uznać za oprogramowanie szkodliwe. Dobrym wyborem może być zakup Thinkpada z przed 2008 roku. Wówczas będziemy mogli bez obaw wybrać Debiana i zainstalować np. XFCE (to dość lekkie środowisko graficzne, nadające się do codziennej pracy, bez zniechęcania się). Kolejną rzeczą do wyboru dystrybucji jest zapał do pracy. Mimo iż opisując dystrybucje napisałem że ta jest dla początkujących, a ta dla zaawansowanych to żadna z nich nie jest ani dla jednych ani dla drugich. Obsługa czego kolwiek związanego z komputerami wymaga przeczytania dokumentacji ze zrozumieniem i umiejętności radzenie sobie z ewentualnymi problemami. Dlatego dlaczego by nie wybrać Gentoo, zainstalować go z poradnikiem, skonfigurować, a wrazie problemów użyć Googla, lub poprość kogoś ze społeczności o pomoc.
Dość częstym zjawiskiem, wśród społeczności użytkowników Linuksa jest tzw. distro-hopping, czyli przesiadanie się z jednej dystrybucji na drugą. Jest to normalne zjawisko, chciaż można powszechną opinia jego jest raczej negatywna, głównym argumentem oponetów jest stwierdzenie, że przez to nie uczymy się niczego. Moim zdaniem, możemy dojść do wniosku, że tak naprawdę nie ma dystrybucji tylko produkt w ciągłej ewolucji z dostępnym takim a takim oprogramowaniem. Nie mam mendżera pakietów, mam program do instalacji i konfiguracji oprogramowania, nieważne czy jest apt, dnf, yum czy pacman. Mam stronę podręcznika i znajduj sobie potrzebne opcje. Mam dostęp do internetu, i wystarczy wyszukać konkretną potrzebną czynność np.: "Remove packages with all dependencies pacman". I mam gotowy wynik. Wiele miesięcy błądziłem słuchając mendrców jak RMS (Richard Matthew Stallman). Myślcie samodzielnie, przeskakujcie z distro na distro i bawcię się dobrze.
1.1. Sprzęt
Sprzęt sam w sobie nie mozę wchodzić z skład systemu operacyjnego, to jego elementy jak pamieć operacyjna, procesor czy pamięć masowa odgrywają w nim bardzo ważna rolę.
1.1.1. Pamięć operacyjna
W działaniu systemów operacyjnych takich jak Linux, najważniejszym komponentem sprzętowym może być pamięć operacyjna, ponieważ to w niej rezyduje jądro oraz przestrzeń użytkownika. Dane zapisane w pamięci nie są niczym innym jak zbiorem zer i jedynek określanych mianem bitów (najmniejsze przetwarzanej ilości informacji). Procesy oraz jądro są jednymi z takich zbiorów. Takie zbiory określa się mianem obrazu.
1.2. Jądro
Jądro Linux jest to nadrzędy proces w całym systemie, realizuje swoje działania w czterech obszarch funkcjonalności systemu operacyjnego.
- Zarządzanie procesami - jądro jest odpowiedzialne za uruchamianie, wstrzymywanie, ponowne uruchomienie oraz kończenie pracy procesów. Korzystając ze współczesnych systemów operacyjnych możemy mieć wrażenie uruchomione przez nas programy (a co za tym idzie ich procesy) mogą działać jednocześnie. Dzieje się tak dlatego, iż jądro uruchamia kod procesu na ułamek sekundy, po upłynięciu danego przez jądro wycinka czasu stan procesora wykonującego kod danego procesu zapisywany jest w pamięci, a jądro wybiera kolejny proces i ładuje stan procesora po czym wznawia jego wykonanie. Tych czynności jest znacznie więcej zostało tu wymionionych. Te czynności nazywane są przełączaniem kontekstu. Na współczesnych procesorach dzieje się to tak szybko, że możemy mieć złudzenie wielozadaniowości. W przypadku maszyn wielordzeniowych jak i wieloprocesorowych jądro nie musi zwalniać wykorzystywanego procesora (rdzenia), ale robi to aby jak najlepiej wykorzystać zasoby.
- Zarządzanie pamięcią - każdy proces jest obrazem w pamięci, każdy proces również potrzebuje pamięci na swoje obliczenia. Zadaniem jądra jest przydzielanie, zwalnianie jak i ochrona (przed tym aby proces nie uzyskał dostępu do obszaru innego procesu) przekazanych procesom obszarów. Czynności z tym związane są dość złożone, ale jądro może posiłkować się rozszerzenim MMU we współczesnych procesorach. Pozwala ono podczas dzielenia pamięci wykorzystać metodę pamięci wirtualnej, polegającej na zamianie adresów pamięci, przez co proces jest skonfigurowany, że "tak jakby" miał do dyspozycji całą pamięć fizyczną maszyny. Zamiana adresów wiąże się z potrzebą posiadania map (czy też tabel), pozwalających na odzorowanie adresów, co dokłada czynność aktualizacji mapy podczas przełączania kontekstu. Mapy adresów nazywane są tablicami stron.
- Sterowniki urządzeń - zadaniem sterowników jest dostarczenie identycznego interfejsu do komunikacji z poszczególnymi urządzeniami zainstalowanymi w komputerze. Za racji to iż swobodny dostęp do sprzetu jest potencjalnie niebezpieczny, to jaka kolwiek próba komunikacji z urządzeniem odbywać się zawsze będzie za pośrednictwem jądra systemu. Sterowniki w systemie Linux są częścią jądra, nie oznacza to jednak, że nie możemy jakiegoś brakującego do instalować. Sterowniki są przechowywane w postaci modułów, które są ładowane podczas uruchamiania jądra, a nie które znich mogą być ładowane podczas pracy systemu.
- Wywołania systemowe - są to funkcje udostępnione przez jądro procesom użytkownika. Wywołania realizują zadania, które są trudne do zrealizowania przez procesy użytkownika lub w ogóle nie wykonalne. Przykładem wykonywania wywołań systemowych jest obsługa plików (otwieranie, odczyt czy zapis), innymi bardzo często wykorzystywanymi wywołaniami są fork() oraz exec(), wykonywane są za każdym wydanym poleceniem w powłoce.
Inną ciekawą cechą jądra są pseudourządzenia. Procesy widzą takie urządenia jak każde inne, jednak występują on wyłącznie w warstwie programowej, dzięki temu nie muszą być częścią jądra, ale ze względów praktycznych się je tam umieszcza. Inna implementacja urządzenia /dev/random - służacego do generowania liczb pseudolosowych, które jest urządzeniem programowym mogłoby nie być zbyt bezpieczne.
1.3. Przestrzeń użytkownika
Przestrzeń użytkownika formalnie jest obszarem pamięci, w którym spedzimy 99% czasu pracy na Linuksie. Wewnątrz przestrzeni użytkownika znajdują się procesy definiujące dystrybucje wykonujące różne zadania dla użytkownika, teoretycznie są one wobec siebie równe, to jednak przestrzeń użytkownika można podzielić na trzy warstwy, na której warstwie będzie znajdować się proces zależy jak bardzo skomplikowane zadania wykonuje. Przeglądarka sieci WWW, może się taka nie wydawać ale to potężny subsystem więc będzie znajdować na najwyższej warstwie, z kolei proces służący za rejestrowanie logów, tzw. protokół diagnostyczne będzie znajdować się na najniższej warstwie blisko jądra, ponieważ nie jest on zbyt skomplikowany w porównaniu do na przykład przeglądarki, warstwa środkowa zarezerowana jest dla różnej maści serwerów. Najproście rzecz ujmując podstawowe usługi znajdują się na najniższej warstwie, usługi pomocnicze na warstwie środkowej, a aplikacje, które kontroluje już sam użytkownik będą znajdować się na samej górze. Procesy mogą komunikować się z innymi procesami o ile te znajdują się na tym samym lub niższym poziomie. Używanie tego rozdzaju podziału, może być kłopotliwe ponieważ obecne serwery nie są już tak prostym oprogramowaniem więc powinny znajdować się tej samej warstwie co przeglądarka czy klient pocztowy, jednak to te aplikacje mogą wykorzystywać serwery do realizacji zadań użytkownika, więc ich miejsce jest raczej na warstwie centralnej (środkowej).
1.4. Użytkownicy
Użytkownicy w Linksie są odwzorowaniem rzeczywistych obiektów, czyli encją. Użytkownicy mają prawo do uruchamiania procesów oraz posiadnia (bycia właścielem) plików. Jądro nie rozpoznaje użytkowników po ich nazwach, tak jak mają w zwyczaju to ludzie, używa ono identyfikatorów userid w skrócie UID. Identyfikatory są przedstawiane za pomocą liczb.
Użytkownicy istnieją wyłącznie po to aby wyznaczać granice. Każdy proces ma swojego właściela, dlatego też mówi się że proces uruchamia się z uprawnieniami takiego a takiego użytkownika. Użytkownicy mogą uruchamiać i konczyć procesy w własnych granicach (tylko te, których są właścicielami), przez co nie mogą wpływać na procesy innych użytkowników. Poza procesami, użytkownicy mogą tworzyć własne pliki, których automatycznie stają się właścicielami. Mogą oni decydować czy chcą się nimi dzielić, ustalając im odpowiednie uprawnienia.
Poza użytkownikami przypisanymi do konkretnych osób (raczej spotkamy jednego), istnieje kilku dodatkowych specjalnych użytkowników, głównie mają oni na celu ograniczenie uprawnień serwerów. Po za tymi specjalnymi istnieje jeszcze użytkownik root, którego nie tyczą się zapisane powyżej ograniczenia dlatego jest on nazywany superużytkownikiem.
Osoba pracująca na koncie użytkownika root, nazywana jest administratorem systemu. Root może kończyć procesy innych użytkowników, przeglądać cudze pliki czy instalować oprogramowanie z repozytorium. Praca na tym koncie jest dość niebezpieczna z punktu widzenia systemu, ponieważ ten użytkownik jest wstanie wykonać czynności prowadzące do zniszczenia całego systemu. Na Linuksie root ma do tego pełne prawo, dlatego projektancji dystrybucji starają się ograniczyć konieczność pracy z wykorzystaniem tego użytkownika.
Innym tworem podobnym to użytkowników są grupy. Grupy są zbiorem użytkowników, a ich zadaniem jest współdzielenie plików wewnątrz jednej grupy, między jej użytkownikami.
2. Podstawy obsługi Linuksa
W tym rozdziale przedstawione zostaną podstawy obsługi systemu Linux, oczywiście z poziomu powłoki, ponieważ inne sposóby zależą w dużej mierze od programów, które do tego celu będziemy wykorzystywać. Takich programów może być kilka, powłok również dostępnych jest kilka rodzajów, jednak sam program powłoki nie będzie wpływać na prezentowane w tym rozdziale czynności. Ten rozdział zaczniem od tego czy jest powłoka.
2.1. Powłoka
Powłoka jest chyba jednym z najistoniejszych komponentów systemu Linux, pozwala ona na uruchamianie róznych poleceń wydawanych przez użytkownika. Powłoki są również małymi środowiskami programistycznymi. Nie które narzędzia systemowe są skryptami powłoki - plikami tekstowymi zawierającymi zbiór wykonywanych kolejno (jedno po drugim) poleceń powłoki.
Pierwotną powłoką była powłoka Bourna, opracowana jeszcze dla systemu UNIX w laboratoriach Bell Labs. Mimo niezbyt częstego wykorzystywania, powłoka ta jest stałym kompenetem nie tylko systemu Linux, ale i innych systemów uniksopodbnych. Obecnie wykorzystywaną powłoką jest BASH - ulepszona wersja oryginalnej powłoki. Korzystając z róznych dystrybucji, domyślna powłoka może być inna. Ten materiał zakłada wykorzystanie powłoki BASH, szczególnie w rozdziale poświęconym skryptom powłoki.
2.2. Korzystanie z powłoki
Dostęp do powłoki może odbywać się w dwojaki sposób wykorzystać możemy wbudowaną w każdą dystrybucję konsole, nie zależnie od instalacji wybranej przez nas dystrybucji. Jeśli jest to dystrybucja skierowana do komputery biurkowe, to możemy skorzystać z wbudowanego programu terminal. Po uruchomieniu okna powłoki, w prawym górnym rogu pojawi się symbol zachęty. Jest to ciąg znaków wskazujący wiersz, w którym będziemy wprowadzać polecenia. Znak zachęty może przyjmować różną formę:
użytkownik@host:ścieżka$
-użytkownik
- nazwa użytkownika,host
- nazwa komputera,ścieżka
- obecna ścieżka (czym jest ścieżka, będzie za chwilę). Tego typu symbol zachęty stosowany jest w dystrybucjach opartych na GNU/Linux Debian takich Linux Mint (Mint oparty jest na Ubuntu, a Ubuntu na GNU/Linux Debian) czy Ubuntu.[użytkownik@host:katalog]$
-użytkownik
ihost
podobnie jak wyżej,katalog
- katalog w którym się obecnie znajdujemy, z tego typu znakiem zachęty spotkamy się w dystrybucjach RHEL/Fedora/CentOS oraz Arch Linux.bash-wersja$
- Originalny symbol zachęty powłoki BASH,wersja
przedstawia wersję wykorzystywanej powłoki, spotkamy go w ręcznych instalacjach powłoki (kompilacji kodu źródłowego)$
- symbol zachęty wykorzystywany w celu zaoszczędzenia miejsca w wierszu polecenia.
W tych symbolach jeden element jest stały jest to znak dolara ($), oznacza on że polecenia wydawane będą jako zwykły użytkownika, innym symbolem jest znak krzyżyka (#), który mówi nam że polecenia będą uruchamiane przez superużytkownika. Najprostsze polecenie jakie możemy wydać jest użycie polecenia echo, które zwraca na standardowe wyjście podajny mu jako argument ciągu znaków:
$ echo Witaj świecie.
W przykładach w tym materiale, jeśli polecenia ma zostać wydane z uprawnieniami zywkłego użytkownika, przed poleceniem będzie pojawiać się znak dolara ($), a jeśli polecenie ma być uruchomione z wyższymi uprawnieniami, będą one poprzedzone znakiem krzyżyka (#) oznaczający uprawnienia użytkownika root.
2.2.1. Polecnie cat
Polecenie cat wypisuje na standardowe wyjście podane w argumentach pliki jeden po drugim dokonując tym samym połączenia (konkatenacji - stąd nazwa polecenia) na jednym strumieniu zawartości tych wszystkich plików.
$ cat plik1 plik2 plik3 ...
2.2.2. Standardowe wyjście i standardowe wejście
Użyłem powyższego polecenia cat, aby nakreślić kontekst dla omówienia dwóch podstawowcyh strumieni. Linux wykorzystuje strumień wejściowy do odczytu danych, a strumień wyjściowy do ich zapisu. Źródłem strumienia wejściowego może być plik, urządzenie, terminal czy strumień wyjściowy innego procesu.
Strumień wejściowy możemy zaobserować poprzez uruchomienie polecenia cat bez żadnego pliku. Program nie zwróci od razu znaku zachęty, ponieważ oczekuje na dane. Możemy wpisać co kolwiek, a po naciśnięciu klawisza enter polecenie powtórzy ten wpisany tekst. Z racji tego iż nie podaliśmy mu żadnego pliku polecenie zaczęło korzystać ze strumienia standardowego wejścia, przekazanego mu przez jądro, w tym przypadku był to terminal, którym zostało uruchomione to polecenie. Aby zakończyć to polecenie należy wciśnąć kombinacje klawiszy Ctrl+d, która oznacza koniec danych ze standardowego wejścia.
Ze standardowym wyjściem jest podobnie, jądro przezkazuje strumień standardowego wyjścia procesom, do którego mogą one zapisywać swoje dane. Polecenie cat zawsze wypisuje swoje dane na standardowe wyjście, które przez uruchomienie polecenia w terminalu jest do niego podłączone. Dzięki temu mogliśmy zobaczyć wypisywane przez polecenie dane.
Standardowe wyjście oraz standardowe wejście możemy zapisać skrótowo stdout oraz stdin. Takich nazw również należy się spodziewać w wszelakiej dokumentacji.
Prócz wspomanianych strumieni istnieje jeszcze trzeci strumień wejścia-wyjścia - standardowy strumień błędów. Opiszę go nieco później.
Strumienie są dość elastycznym mechanizem, można je zmusić do odczytywania i zapisywania danych z innych miejsc niż terminal. O przekierowaniach strumienii będzie nieco poźniej w tym rozdziale.
2.3. Podstawowe polecenia
Poniżej znajduje się pogrupowane przedstawienie najbardziej podstawowych poleceń niezbędnych do pracy w powłoce systemu Linux.
- polecenie ls - wypisuje zawartość katalogu.
Najważniejsze opcje:
- -a - powoduje wyświetlenie wszystkich elementów, łącznie z tzw. dot-files (plikami ukrytymi, plikami konfiguracyjnymi
- -l - wyświetlenie zwartości katalogu w postaci kilku kolumnowej tabeli zawierającej m.in uprawnienia, czas ostatniej modyfikacji plików, wielkość czy przypisanie pliku, katalogu do użytkownika oraz grupy.
- polecenie cp - kopiujej pliki
Najważniejsze opcje:
- -p - zachowuje atrybuty kopiowanych plików, na przykład takie jak uprawnienia czy przypisanego właściela i grupę
- -r - kopiowanie rekurencyjne, kopiuje całe katalogi wraz z podkatalogami oraz ich zawartością.
- -v - włącza komunikaty diagostyczne, polecenie wypisuje co, gdzie kopiuje. Normalnie program nie zwraca nic poza znakiem zachęty po zakończonym kopiowaniu.
- polecenie mv - w najprostszym przypadku
polecenie służy do zmiany nazwy pliku, jednak gdy drugim
argumentem będzie katalog, plik zostanie przeniesiony do tego
katalogu. Najważniejsze opcje:
- -v - włącza komunikaty diagnostyczne, identycznie jak w przypadku cp.
- polecenie touch - aktualizuje czas modyfikacji pliku, jeśli plik nie istnieje to zostanie utworzony pusty plik o podanej w argumencie nazwie.
- polecenie rm - polecenie służy do kasowania
plików. Kombinacja opcji -rf wykorzystywana jest
kasowania całych katalogów z podkatalogami. Najważniejsze opcje:
- -r - umożliwia, kasowanie rekurencyjne, całych katalogów z podkatalogami.
- -f - przed każdym kasowaniem pliku polecenie pyta czy jesteśmy pewni, że chcemy skasować ten plik. Ta opcja pomija to pytanie wymusząjąc tak jakby kasowanie.
- polecenie echo - polecenie wypisuje ciąg znaków
podany jako argument na standardowe wyjście. Najważniejsze opcje:
- -n - ta opcja wyłącza przechodzenie do nowej linii, po wypisaniu ciągu znaków.
Polecenia działające na katalogach
Uniksy w tym i Linux, korzystają ze standardu hierarchi katalogów, aby utrzymać w porządku dane przestrzeni użytkownika. Za początkowy katalog uznaje się katalog główny oznaczany prawym ukośnikiem lub slashem (/), wewnątrz tego katalogu znajdują się pod katalogi, przechowujące konkretny rodzaj czy typ plików zgodny z ich przeznaczeniem.
Droga do konkretnego katalogu nosi nazwę ścieżki. Jeśli ścieżki zaczynają się od /, czyli od katalogu głównego mamy doczynienia ze ścieżką bezwzględną. Elementy katalogów na ścieżkach katalogi mogą być również wyrażane z pomocą jednej lub dwóch kropek. Dwie kropki (..) oznaczają katalog nadrzędny względem aktualnego katalogu, zaś jedna kropka oznacza (.) aktualny katalog. Ścieżki nie zawierające slasha na początku, czyli nie zaczynające się od katalogu głównego są wówczas określane mianem ścieżki względnej.
- polecenie cd - polecenie służy do zmiany aktualnego katalogu, jako argument przyjmuje katalog, do którego checemy przejść, równie dobrze możemy przenieść się w dowolne miejsce w systemie plików (w katalogu głównym) podając jako argument ścieżkę. Nie podanie argumentu spowowduje przejście do katalogu domowego użytkownika.
- polecenie mkdir - polecenie tworzy nowy katalog.
Jako argument przyjmuje nazwę katalogu lub ścieżkę. Najważniejsze
opcje:
- -p - opcja tworzy katalogi nadrzędne podane w ścieżce o ile te nie istnieją. Za pomocą odpowiednich podstawień powłoki oraz tej opcji można tworzyć całe struktury katalogowe.
- polecenie rmdir - usuwa katalog po warunkiem, że jest on pusty. W przeciwnym razie polecenie zwróci błąd. Chcąc usuwać całe katalogi z danymi oraz podkatalogami należy użyć polecenia rm -rf.
2.4.1. Nazwy wieloznaczne.
Dzięki możliwością powłoki możemy porównywać proste wzorce z nazwami plików w obrębie aktualnego katalogu roboczego (katalogu w którym się znajdujemy) czynność ta nazywana jest rozwijaniem nazw lub globbingiem. Jednym z elementów biorących udział w rozwiązywaniu nazw jest gwiazdka (*) oznaczająca dowolną ilość dowolnych znaków. Dla przykładu poniższe polecenie:
$ echo *
Zwróci nazwy wszystkich plików i katalogów znajdujących się w katalogu. Innym znakiem wykorzystywanym przy nazwach wieloznacznych jest znak zapytania (?) reprezentuje on jeden dowolny znak, dla wzorca b?at pasującymi nazwami mogą być blat oraz brat. Rozwinięcia nazw dokonuje powłoka przed uruchomieniem, więc jeśli chcemy aby, któreś ze znaków wieloznacznych trafiło do polecnie to należy umieść je w pojedyńczych cudzysłowach.
2.5. Polecenia pośredniczące
- polecenie grep - wyszukuje wzorzec
w podanym pliku. Polecenie to korzysta z systemu wzorców nazwanych
wyrażeniami regularnymi. Najważniejszymi opcjami:
- -i - wyłącza rozróżnianie małych i wielkich liter.
- -v - podwoduje odwrócenie wyszukiwania, zwracane są wyniki nie pasujące do wzorca.
- -e - wykorzystuje rozszerzony zestaw instrukcji pozwalajacych na tworzenie wyrażeń regularnych.
- -o - opcja powoduje zwrócenie dokładnie tylko tych znaków pasujących do wzorca. Normalnie polecenie zwraca linię z elementami pasującymi do wzorca, w przypadku wielu plików zwraca również nazwę pliku.
$ man grep
Do najważniejszych wyrażeń, które każdy powinien znać należą:- .* - oznaczające dowolną ilość dowolnych znaków.
- . - oznacza jeden dowolny znak.
- polecenie less - wypisuje dane z pliku, lub ze strumienia wykorzysując podział na strony. Jedna strona to jeden ekran. Następne strony są wyświetlane za naciśnięciem spacji stronę możemy cofnąć klawiszem b, zakończyć przeglądanie danych klawiszem q. Dane możemy przeglądać linia po linii używając strzałek. Możliwe jest również wyszukiwanie fraz w danych za pomocą / (wyszukiwanie w przód) lub za pomocą ? (wyszukiwanie w tył).
- polecenie pwd - wyświetla obecny katalog
roboczy powłoki (świeżkę na której się znajdujemy). Polecenie
niepozorne choć przydatne, ze względu na dowiązania symboliczne
(będzie o nich w dalszej części materiału), które mogą przesłaniać
ścieżkę wyświetlaną w znaku zachęty. Najważnejsza opcja jest
uruchamiana, gdy nie ma żadnej opcji, więc jej opis pominę.
Warto dodać, że obecne systemy posiadają polecenie pwd
wbudowane w powłokę. Dlatego też opcja -P
rozwijająca fizyczne ścieżki nie jest automatycznie uruchamiana
w przypadku poprostu wydania polecenia pwd, prawdziwe
polecenie pwd uruchamiamy:
$ /usr/bin/pwd
- polecenie diff - wszukuje różnice pomiędzy dowoma plikami tekstowymi. Polecenie to posiada wiele różnych opcji formatowania danych wyjściowych, najbardziej czytelnym pozostaje chyba użycie opcji -u. Polecenie wykorzystywane programistów oraz system kontroli wersji git.
- polecenie file - polecenie zwraca format pliku podanego jako argument. W uniksach nie potrzeby stosowania rozszerzeń plików, więc to polecenie może pomóc nam dowiedzieć się co zawiera plik.
- polecenie find i locate - polecenia te służą do wyszukiwania plików w systemie. Polecenie find wymaga podania katalogu po nazwie polecenia, nazwy wyszukiwanego pliki po opcji -name oraz opcji -print, która powoduje wyświetlenie na strumieniu standardowego wyjścia nazw plików pasujących do wzorca podanego w opcji -name. Polecenie locate na podobne zastosowanie jak find działa jednak od niego szybciej ponieważ bazuje na indeksie przygotowywanym co jakiś czas przez system operacyjny. Może być ono bezużyteczne, kiedy szukamy nowych plików, gdyż mogą być one nie ujęte jeszcze w indeksie.
- polecenie head i tail - te polecenia służa do prezentowania wycinka danych czy to ze strumienia lub z pliku. W przypadku polecenia head prezentowane jest n pierwszych linii, domyslnie 10; z kolei polecenie tail prezentuje n koncowych linii. Liczbę linii podajemy bezpośrednio po znaku myślnika (-). Z tych dwóch poleceń polecenie tail ma nieco większe zastosowanie niż polecenie head. Mozemy wywołać to polecenie aby wyświetlić dane od linii, numer linii podajemy po znaku plusa (+), inna właściwością chyba najważniejszą jest wyświetlanie danych na żywo, używając opcji -f, a następnie nazwy pliku lub myślnika gdy dane pochodzą ze strumienia wyjściowego innego polecenia.
- polecenie sort - układa wiersze z pliku tekstowego w porządku alfabetycznym, jeśli na początku wierszy znajdują się liczby to aby je posortować należy użyć opcji -n, aby odwrócić sortowanie możemy użyć opcji -r.
2.6. Zmiana hasła i powłoki
W celu zmiany hasła należy użyć polecenia passwd. Polecenie poprosi o podanie obecnego hasła, po zatwierdzeniu go zostaniemy poproszeni o nowe hasło i jego potwierdzenie (wpisanie ponowne nowego hasła).
Zmiana aktywnej powłoki odbywa się za pomocą polecenia chsh, albo użyć poleceń odpowiadających nazwom innych powłok, kolejno ksh - Korn SHell, tcsh - TENEX C SHell. Użycie tych poleceń w aktywnej powłoce, spowoduje uruchomienie podpowłoki. Zamkniecie jej spowoduje powrót do pierwotnej powłoki.
2.7. Pliki z kropką
Przeglądając pliki nawet w własnym katalogu domowym możemy znaleźć pliki, których nazwa zaczyna się od kropki. Nie które źródła mówią tym o że te pliki są ukryte. Do takich wniosków może dojść, ponieważ te pliki nie są domyślnie wyświetlane przez polecenie ls bez opcji -a lub przez menedżery plików dostępne w desktopowych wersja Linuksa. Jednak te pliki nie różnia się niczym od inny plików, poza właśnie tym przypadkiem opisanym powyżej. Oprócz plików, nazwy katalogów również mogą zaczynać się od kropki. Za pomocą prostego wzorca możemy wyświetlić wszystkie dot-files, jeśli wsród nich trafi się katalog, wówczas zostanie wyświetlona jego nazwa a pod nią jego zawartość.
$ ls .??*
2.8. Zmienne środowiskowe i powłoki
Powłoka może przechowywać zmienne tymczasowe, które mogą przechowywać różne wartości, mogą one kontrolować zachowanie samej powłoki jedną z takich zmiennych jest zmienna PS1 zawierająca znak zachęty. Takie zmienne najczęsćiej wykorzystywane są w skryptach powłoki i nazywane są zmiennymi powłoki. Definicja zmiennych tego składa się z nazwy zmiennej, operatora przypisania (znaku równości =) oraz wartości samej zmiennej.
$ zmienna=12
Odwołać się do wartości zmiennej możemy w dowolnym momencie, podając jej nazwę poprzedzoną znakiem dolara ($).
$ echo $zmienna
Zmienna środowiskowa jest podobna do zmiennej powłoki, ale nie jest ściśle związana z powłoką, bowiem do pamięci zmiennych środowiskowych systemach uniksopodobnych mają wszystkie aplikacje, system operacyjny przezkazuje je do każdego programu uruchomionego w powłoce, programy te nie mają jednak dostępu do zmiennych powłoki. Zmienne środowiskowe definiuje się w ten sam sposób jak zmienne powłoki, jedna aby taka zmienna stała się zmienną środowiskową musi zostać przeniesiona do pamięci tych zmiennych za pomocą polecenia export.
$ zmienna=21 $ export zmienna
Nie które programy mogą wykorzystywać zmienne środowiskowe do własnej konfiguracji. Dla przykładu niektóre programy uruchamiane w powłoce korzystają ze zmiennej środowiskowej EDITOR definiujące domyślny program do edycji plików tekstowych. Wykorzystanie zmiennych środowiskowych zapewne jest opisane w na stronie podręcznika programu.
2.9. Ścieżka poleceń
Istnieje specjalna zmienna środowiskowa PATH, przechowywująca katalogi, w których to powłoka będzie szukać programów odpowiadających wpisanym poleceniom. Jeśli wśród przeszukiwanych katalogów znajduje się kilka programów o tej samej nazwie to powłoka uruchomi pierwszy przez nią znaleziony. Ścieżki katalogów w tej zmiennej odzielone są dwukropkiem (:). Posiadając swoje programy, możemy również umieść katalog z nim wewnątrz zmiennej $PATH. Opcje dodanie katalogu są dwie i mogą mieć wpływ na funkcjonowanie systemu. Możemy dodać nasz katalog na początku zmiennej, wówczas powłoka zacznie od niego poszukiwania, jednak należy pamiętąc przy tym, aby nazwy programów nie pokrywały się istniejącymi dotychczas poleceniami.
$ PATH=kat:${PATH}
Na powyższym przykładzie kat
, to
nasz katalog z oprogramowaniem. Możemy jednak skorzystać
bezpieczeniejszego rozwiązania - dopisać nasz katalog na końcu listy
katalogów zmiennej PATH, wówczas nawet jeśli nasz program
będzie
nazywać się jak jedno z instniejących już poleceń w systemie, nie
będzie miało to wpływu na działanie systemu.
$ PATH=${PATH}:kat
Na powyższych przykładach użyłem znaku dolara wraz z nawiasami klamrowymi. Jest to sposób na separacje nazwy zmiennej od innych znaków, po to aby powłoka nie potraktowała jak w przykładzie powyżej ciągu znaków ":kat" jak części nazwy zmiennej. Przedstawione w przykładach polecenia są nie groźne, jeśli uszkodzimy zawartość zmiennej PATH, to należy zamknąć okno terminala i otworzyć nowe.
2.10. Znaki specjalne
W systemach uniksopodbnych wiele znaków ma szczególne znaczenie. Poniżej znajduje się tabela przedstawiająca wykorzystwane podczas używania systemu znaki specjalne.
Znak | Nazwa | Opis |
---|---|---|
* | gwiazdka | Wyrażenie regularne, znak nazwy wieloznacznej |
. | kropka | Aktualny katalog, ogranicznik nazwy pliku lub hosta |
! | wykrzyknik | Negacja, historia poleceń |
| | potok | Potoki poleceń |
/ | slash | Ogranicznik katalogów, polecenie szukania |
\ | backslash | Literały, makra (nigdy katalogi) |
$ | dolar | Oznaczenie zmiennych, koniec wiersza |
' | pojedynczy cudzysłów | Ciągi znaków literałów |
` | lewy cudzysłów | Podmiana polecenia |
" | podwójny cudzysłów | Ciągi znaków pseudoliterałów |
^ | daszek | Negacja, początek wiersza |
~ | tylda | Negacja, skrót katalogu |
# | krzyżyk | Komentarze, dyrektywy preprocesora, podmiany |
[] | nawiasy kwadratowe | Zakresy |
{} | nawiasy klamrowe | Bloki poleceń, zakresy |
_ | podkreślenie | Prosty zamiennik spacji |
Często możemy napotkać symbol daszka (^) zastępujący klawisz Control, przez co zapis ^C jest równe kombinacji klawiszy Ctrl+C.
2.11. Edycja wiersza poleceń
Znak zachęty wskazuje wiersz polecenia, który możemy edytować przesuwając kursor za pomocą strzałek. Chcąc powtórzyć jakąś czynność nie musimy pisać na nowo tego polecenia, możemy wybrać je z historii poleceń za pomocą strzałek w góre i w dół. Warto jednak obsługę wiersza poleceń za pomocą strzałek odstawić na bok. Wykorzystując skróty z poniższej tabeli, możemy nimi śmiało zastąpić strzałki. Istnieją ku temu dwa powody.
- Nie wszystkie klawiatury posiadają strzałki, lub ich użycie jest strasznie nie konfortowe.
- Wiele programów uniksowych (w tym i linuksowych) obsługuje się za pomocą tzw. biblioteki GNU Readline (skróty klawiszowe w tabeli poniżej), a nie za pomocą strzałek.
Klawisze | Operacja |
---|---|
Ctrl+b | Przesunięcie kursora w lewo |
Ctrl+f | Przesunięcie kursora w prawo |
Ctrl+p | Powrót do poprzedniego polecenia (lub przesunięcie kursora w górę) |
Ctrl+n | Przejście do następnego polecenia (lub przesunięcie klawisza w dół) |
Ctrl+a | Przesunięcie kursora na początek wiersza |
Ctrl+e | Przesunięcie kursora na koniec wiesza |
Ctrl+w | Usunięcie słowa poprzedzjącego kursor |
Ctrl+u | Usunięcie tekstu od kursora do początku wiersza |
Ctrl+k | Usunięcie tekstu od kursora do końca wiersza |
Ctrl+Y | Wyklejanie usuniętego tekstu (na przykłda usuniętego poleceniem Ctrl+u) |
Ctrl+h | Substytut klawisza Backspace |
Ctrl+d | Substytut klawisza delete |
Ctrl+j, Ctrl+m | Substytut klawisza enter |
2.12. Edytory tekstu
Na Linuksie mamy podobną ilość edytorów tekstowych do wyboru jak w przypadku systemów MS Windows czy Apple macOS. Jak nie więcej. Co ciekawe macOS, również jest system uniksopodobnym. Więc to co zostało omówione w tym rozdziale również będzie kompatybilne z tym systemem. Wracając jednak do edytorów tekstu. Tak naprawdę to istnieją dwa, na które warto zwrócić uwagę, oba są standardem jeśli chodzi o edycje tekstu i oba wymagają nauki obsługi. Wybór pozostawiam do roztrzygniecia Tobie.
- GNU Emacs - edytor w którym można zrobić wszystko, od pisania tekstów do wykorzystania go jako menedżer okien. Jego obsługa nie jest zbyt szybka i często by się wydawało proste czynności wymagają użycia kliku poleceń. Wydaje mi się, że nie ma bardziej rozbudowanego uniksowego programu. Pomoc w obsłudze tego edytora, możemy uruchomić naciskając Ctrl+H następnie klawisz t.
- VIm - szybki edytor uruchamiany w terminalu, choć można zainstalować wersję graficzną. Obsługuje się go trochę jak grę. VIm, jest nieco bardziej intuicyjny od Emacsa. Warto dodać, że edytor ten bywa domyślnie doinstalowywany do wielu dystrybucji jak i innych systemów uniksowych, choć tam może występować w podstawowej wersji Vi. Chcąc nauczyć się edytora Vim, możemy skorzystać z z instalowanego wraz z edytorem tutoriala, uruchamianego poleceniem vimtutor.
Jeśli potrzebujemy edytora, który jest wstanie zatąpić nam środowisko graficzne, wybierzmy edytor Emacs. Jeśli jednak chcemy poprostu edytować pliki, w każdym możliwym środowisku wybierzmy edytor Vim. Osobiście jestem przyzwyczajony już do edytora Vim.
2.13. Uzyskiwanie pomocy
Dystrybucje Linuksa są rozporowadzane z dużą ilością różnej dokumentacji. Informacje temat poleceń możemy znaleźć na stronach podręcznika, wydając polecenie man i podając jako argument interesujące nas polecenie. Na przykład:
$ man ls
W ten sposób uruchomimy stronę podręcznika polecenia ls. Większosć stron podręcznika podaje suche informacje na temat polecenia, nie ma co tam szukać jakiś samouczków. Opcje podawana są usystematyzowany sposób (najczęściej alfabetyczny), nie które strony podręcznika mogą zawierać przykłady.
Strony podręcznika możemy przeszukać pod kątem słowa kluczowego, za pomocą opcji -k, polecenia man. Wynikiem tego polecenie jest lista poleceń, oraz krótki opis zawierający podane słowo kluczowe, ciekawa jest liczba podana w nawiasie obok nazwy polecenia, jest to numer rozdziału.
Strony podręcznika są podzielone rozdziały oznaczone numerami, każdy z nich zawiera innego rodzaju strony podręcznika. Rozdziały zostały opisane w tabeli poniżej.
Rozdział | Opis |
---|---|
1 | Polecenia użytkownika |
2 | Niskopoziomowe wywołania systemowe |
3 | Dokunentacja wysokopoziomowych bibliotek Uniksa |
4 | Informacje o interfejsach urządzeń i sterownikach |
5 | Opisy plików (konfiguracji systemu) |
6 | Gry |
7 | Formaty plików, konwencje i kodowaniaa (ASCII, przyrostki itd) |
8 | Polecenia systemowe i serwery |
Jak uzupełnienie tego materiału świetnie sprawdzą się rodziały 1,5,7 i 8. Wywołanie konkretnej strony a danego rozdziału wymaga podania jego numeru jak pierwszego argumentu, wówczas polecenie będzie wszukać informacji na temat podanego słowa w danym rodziale. Świetnym przykładem może być, chęć sprawdzenia na stronach podręcznika pliku /etc/passwd. Należy wydać polecenie:
$ man 5 passwd
Dość często wykorzystywanym sposóbem na uzyskanie informacji o poleceniach mogą być same polecenia, sprawdźmy czy możemy je uruchomić z opcją -h lub --help.
Projekt GNU jakiś czas temu zadecydował, że będzie używać innego formatu plików pomocy niż strony podręcznika, format nazywana jest info lub texinfo. Format ten zawiera więcej informacji choć jest od niego bardziej skomplikowany. Przypomina prostą stronę internetowa, otworzoną w terminalowej przeglądarce. Tego typu pliki pomocy uruchamiane są za pomocą polecenia info, po czym podaje mu się jako argument interesujące nas polecenie.
Nie które z pakietów umieszczają swoją dokumentację w katalogu /usr/share/doc nie zwracając uwagi na format. Warto pamiętać o tych miejscach szukając pomocy, oczywiście pozostaje na do dyspozycji jeszcze internet.
2.14. Wejście i wyjście powłoki
Omawiając po krótce powłokę, wspomniałem o strumieniach standardowego wejścia i wyjścia. Wiele poleceń używa wyjścia aby wypisywać wyniki działa lub komunikaty diagonstyczne. Jeśli nie chcemy ich widzieć lub nie nadąrzymy ich analizować możemy je przekierować na przykład do pliku, za pomocą znaku przekierowania (>).
Używając tego znaku musimy omówić sobie jedną ważną rzecz, znak ten
powoduje nadpisanie wszystkiego co znajduje się w pliku. Tego typu
czynność nazywana jest wymazywaniem
(ang. clobbering). Możemy jednak zablokwać to działanie
za pomocą odpowiednich ustawień powłoki, dla BASH wystarczy użyć
polecenia set -C
. Poza blokowaniem
wymazywania, istnieje jeszcze jeden znak przekierowania, który
powoduje dopisanie przekierowanego wyjścia do pliku:
>>.
Za pomocą przekierowań możemy w prosty sposób połączyć wyjście jednego polecenia z wejściem innego. Służy temu znak potoku (|). Postawienie ponowej kreski, pomiędzy poleceniami w wierszu polecenia połączy wyjście pierwszego z wejściem drugiego.
2.14.1 Standardowy strumień błędów
Korzystając powyższych znaków przekierowania polecenia, dane wyjściowe programów zostaną przekierowane np. do pliku. Ale komunikaty diagnostyczne nadal są wyświetlane. Dzieje się to dlatego iż komunikaty diagnostyczny wykorzystują trzeci dodatkowy strumień standardowy strumień błędów zapisywany skrótowo stderr, który podobnie do stdout jest podłączony do terminala.
Do przekierowania tego strumienia musimy użyć identyfikatorów strumienii jest to liczba, którą posługuje się jądro do rozrózniania strumieni. W fachowej literaturze bądź dokumentacji możemy natknąć się na termin deskryptor pliku. Taki identyfikator dla stderr ma wartość 2, a dla stdout 1. Chcąc przekierować stderr do innego pliku musimy podać jego identyfikator przed znakiem przekierowania. Innym przypadkiem jest przekierowanie stderr do tego samego pliku co stdout, wówczas najprostszym sposób jest podłączenie strumienia błedów do standardowego wyjścia za pomocą znaku >& podając przekierowywany strumień po lewej stronie znaku (przed nim) a strumień docelowy po prawej (za nim).
$ ls /ffffffff >p 2>&1
2.14.2. Przekierowanie standardowego wejścia
Przekierowanie standardowego wejścia występuję dość rzadko ponieważ większość poleceń przyjmuje plik jako argument, jednak może zdarzyć się potrzeba przekierowania wejścia do polecenia. Służy temu znak przekierowania wejścia <.
2.15. Odczytywanie komunikatów o błędach
Prędzej czy później gdzieś podczas naszej pracy z powłoką zdarzy się błąd. Ważne jest aby umieć go odczytać i prawidłowo z interpretować.
Sam komunikat składa się przeważnie z nazwy polecenia oraz komunikatu błędu, w nie których przypadkach w komunikacie znajduje się nazwa pliku, jednak dotyczy specyficznych komunikatów o błędach. Poniżej znajduje się lista, błędów z którym będziemy się spotykać podczas pracy z systemem Linux.
- No such file or directory (nie ma takiego pliku lub katalogu) - występuje gdy plik lub katalog podany jak argument nie istnieje. Innym powodem występowania takiego błędu może być błędnie zapisana nazwa polecenia w skrypcie.
- File exists (plik istnieje) - ten problem występuje w momencie gdy próbujemy stworzyć katalog o nazwie pliku, który już istnieje.
- Not a directory (nie jest katalogiem), Is a directory (jest katalogiem) - błąd występuje w momencie gdy probujemy użycie pliku jak katalogu lub katalogu jak pliku.
- No space left on device (brak miejsca w urządzeniu) - Na pamięci masowej, której próbujemy zapisać dane brakuje wolnego miejsca.
- Permission denied (niewystarczające uprawnienia) - pojawienie się tego błędu może mieć dwa powody, pierwszy to zapis lub odczyt informacji z pliku, do którego nie mamy żadnych uprawnień; drugim jest próba uruchomienia pliku/programu bez ustawionego bitu wykonania. O uprawnieniach będzie w dalszej części tego rodziału.
- Operation not permitted (brak zezwolenia na wykonanie operacji) - błąd występuje w momencie gdy spróbujemy zakończyć proces nie należący do nas.
- Segmentation fault (błąd segmentacji) - błąd programisty. Program, który uruchomiliśmy próbował użyskać dostęp do wycinka pamięci, którego nie miał żadnych uprawnień i jego działanie zostało przerwane przez jądro. Najczęsciej polskie tłumaczenie tego błędu to Naruszenie ochrony pamięci.
- Bus Error (błąd magistrali) błąd podobny do Naruszenia ochrony pamięci, jednak w tym przypadku chodzi o dostęp do pamięci w sposób niewłaściwy, a nie dostęp do innego obszaru pamięci.
Jeśli natrafimy na jeden z dwóch ostatnich błędów, to przyczyną mogą być dane przekazane do programu, których on się nie spodziewał.
2.16. Przeglądanie procesów i manipulowanie nimi
Każdy proces jest programem. Jądro pododobnie jak użytkowników procesy widzi za pomocą liczbowych identyfikator - process ID - PID. Za pomocą polecenia ps możemy wylistować wszystkie procesy uruchomione w powłoce. Domyślnie wynik polecenia podzielony jest na cztery kolumny.
- PID - identyfikator procesu
- TTY - urządzenie terminala, na którym działa dany proces.
- STAT - stan procesu, np. S oznacza uśpieje procesu, zaś R - jego dzialanie. Wszystkie oznaczenia znajdują się na stronie podręcznika polecenia.
- CMD - treść polecenia, warto jednak zaznaczyć, że procesy mogą ją zmieniać.
Polecenie ps, możemy obsługiwać za pomocą opcji zapisanych w trzech stylach, jednak najbardziej powszechnym jest styl BSD, i to zapis opcji w tym stylu przedstawie.
- ps x - wyświetla wszystkie procesy uruchomione przez aktualnego użytkownika.
- ps ax - wyświetla wszystkie procesy działące w systemie.
- ps u - wyświetla bardziej szczegółowe informacje o procesach.
- ps w - podaje pełne nazwy poleceń, nie ograniczając się do pojedyńczego wiersza.
Tak jak w przypadku innych poleceń, w poleceniu ps, również
możemy łączyć opcje, dla przykładu chcąc wyświetlić wszystkie
procesy w systemie wraz ze szczegółami należy wydać polecenie
ps aux
.
Wyświetlenie informacji na temat, konkertnych procesów polega na podaniu po opcjach identyfikatora procesu.
2.16.1. Przerywanie działania procesów
Możemy zakończyć działanie procesu poprzez wysłanie do niego sygnału za pomocą polecenia kill. Wykorzystują to polecenie, jądro systemu proszone jest wysłanie sygnału do wybranego procesu. Domyślnie wysyłanym sygnałem jest TERM, a polecenie do swojego działania potrzebuje tylko identyfikatora PID.
Proces możemy zatrzymać wysyłając do niego sygnał STOP. po nazwie sygnału podajemy PID, tak zatrzymany proces da się wznowić za pomocą sygnału CONT. Nazwy sygnałów podajemy po myślniku (-).
Naciśnięcie kombinacji Ctrl+c podczas działania programu w powłoce, jest równoznaczne z wysłaniem sygnału INT (ang. Interrupt - przerwanie).
Jedną z metod zakończenia procesu jest natychmiastowe zakończenie jego pracy oraz usunięcie go siłą z pamięci, jest to osiągalne poprzez wysłanie do niego sygnału KILL. Jest to ostateczność, gdy dany proces nie odpowiada na inne sygnały. Inne sygnały dają procesom możliwość poprzątania po sobie.
Oczywiście nie należy przerywać pracy dowolnym procesom, kiedy nie wiemy co robią.
2.16.2. Kontrola zadań
Powłoki posiadają mechanizm dzięki, któremu możemy zatrzymywać oraz wznawiać prace procesów za pomocą kombinacji Ctrl+z oraz poleceniami fg, bg. Ten mechanizm nazywa się kontolą zadań. Podczas działania procesu w powłoce, możemy wysłać sygnał TSTP (podobny do sygnału STOP), a następnie kiedy zechcemy do niego wrócić to wystarczy wydać polecenie fg, które wznowi działanie polecenia w terminalu, lub polecenia bg wznawiającego działanie procesu w tle.
Podobne działanie ma program GNU Screen, który jest multiplekserem terminala, a co najlepsze jesteśmy wstanie odłączyć sesję programu od pierwszego planu i pozostanie ona w niezmienionej postaci, tak długo jak włączony jest komputer.
2.16.3. Procesy działające w tle
Uruchamiając polecenie w powłoce dostęp do znaku zachęty, a co za tym idzie wolnego wiersza polecenia otrzymujemy dopiero po zakończeniu działania programu. Jednak możemy odłożyć wykonanie polecenia do tła, poczym od razu uzyskamy dostęp znaku zachęty. Jest to przydatna funkcją, gdy uruchamiamy polecenie, którego wykonanie może zająć trochę czasu. Wykonanie odkładamy do tła dopisując ampersand (&) na końcu polecenia (jako ostatni argument). Działanie takiego programu może trwać nawet po naszym wylogowaniu. Jeśli proces zakończy działanie zostaniemy o tym poinformowani.
Problem związanym z procesami działającymi w tle jest, możliwe pobieranie informacji z standardowego wejścia. Jeśli nie przewidzieliśmy takiego zachowania programu, to wówczas może zostać on zatrzymany lub zakończony. Można go wznowić za pomocą polecenia fg o ile został zatrzymany. Innym problemem jest wypisywanie danych przez proces w tle na standardowe wyjście oraz strumień błędów. Przed uruchomieniem takiego polecenia należy przekierować te strumienie, ponieważ podczas pracy w terminalu z innymi aplikacjami dane ze strumieni mogą zaburzać ich wyświetlanie. Jeśli powłoka będzie dziwnie się zachowywać, wystarczy wydać polecenie reset i wszystko wróci do normy.
2.17. Tryb pliku i uprawnienia
Każdy plik na Uniksie (więc na Linuksie też), posiada komplet
uprawnień określajacy czy można go odczytać,
zapisać do niego lub go
uruchomić. Pierwsza kolumna wyniku polecenia ls -l
zawiera tryb pliku. Dane trybu możemy podzielić na cztery części
Typ, Uprawnienia użytkownika (właściciela),
Uprawnienia grupy, Uprawnienia pozostałych
użytkowników..
Pierwszy znak to typ pliku, jesli występuję w nim myślnik (-), to mamy doczynienia ze zwykłym plikiem, innym typem pliku może być litera d oznaczająca katalog.
Pozostałe znaki, definiują uprawnienia. Dzieli się je na trzy grupy wypisane powyżej, po trzy znaki na każdą z nich. W grupie (zestawie uprawnień, przeznaczonym dla konkretnej grupy lub osoby) mogą wystąpić 4 rodzaje znaków.
- r - oznacza, że plik można czytać.
- w - oznacza, że do pliku można pisać.
- x - oznacza, że plik można uruchomić.
- - - oznacza, brak uprawnienia.
Mówiąc o użytkowniku w pierwszym rodziale, wspomniałem że może być on właścicielem pliku i do niego należy pierwszy zestaw uprawnień. Drugi zestaw określa uprawnienia dla grupy, jaka została przypisana temu plikowi, z tych uprawnień będzie korzystać każdy użytkownik tej grupy, próbujący uzyskać dostęp do pliku. Trzeci grupa, należy do pozostałych użytkowników systemu. Użytkownika root nie obejmują żadne z powyższych grup, choć to może zależć od konfiguracji systemu, mimo to superużytkownik może sobie zmieniać dowolnie uprawnienia.
Nie wymieniony na powyższej liście dodatkowym bitem (o uprawnieniach możemy mówić jak o bitach, np. "potrzebuje bitu odczytu aby odczytać dane z pliku") jest bit s - wybierz identyfikator użytkownika. Pojawia się on zamiast bitu wykonywania x i tyczy się wyłącznie plików wykonywalnych. Programy z ustawionym tym bitem zawsze uruchamiają się z uprawnieniami ich właściciela bez znaczenia, kto uruchamia ten program. Wiele programów korzysta z tego rozwiązania, aby uzyskać uprawnienia superużytkownika i móc zapisywać dane w różnych plikach systemowych.
2.17.1. Modyfikacja uprawnień
Do zamiany uprawnień wykorzystamy polecenie chmod, jako pierwszy podaje się zbiór uprawnień, a następnie bit uprawnienia ze znakiem "+" jeśli chcemy dodać ten bit lub "-" jeśli chcemy ten bit usunąć. Zbiór uprawnień podajemy za pomocą pierwszych liter angielskich nazw u (ang. user)- użytkownika/właściciel, g (ang. group) - grupa, o (ang. others)- pozostali użytkownicy systemu.
$ chmod go+r test.sh
Zbiory uprawnień można łączyć ze sobą, tak jak na powyższym przykładzie lub jeśli chcemy dodać bit do wszystkich zbiorów to możemy je pominąć jak na poniższym przykładzie.
-rw-r--r-- 1 xf0r3m xf0r3m 26 03-08 13:13 test.sh $ chmod +x test.sh -rwxr-xr-x 1 xf0r3m xf0r3m 26 03-08 13:13 test.sh
Przy plikach osobistych, nie warto dawać za dużych oprawnień pozostałym użytkownikom. Chociaż obecnie może mieć to jedynie znaczenie, gdy z serwera korzysta wielu wyspecjalizowanych użytkowników.
Innym sposobem na zmianę uprawnień jest użycie liczb, gdzie każda z trzech liczb określa uprawnienia dla jednego zbioru. Liczby te są sumami bitów, które reprezentowane są przez poszczególne wartości.
- r - 4
- w - 2
- x - 1
- - - 0
$ chmod 644 test.sh
Uprawnienia właściciela mają wartość 6
.
co jest równe 4+2 - u+rw, grupa oraz pozostali mają
4
co jest identyczne z zapisem
go+r. Liczby wykorzysywane tutaj pochodzą z oktalnego
systemu liczbowego.
Zmiana uprawnień nosi nazwę bezwzględnej, ponieważ zmieniane są uprawnienia wszystkich grup.
Odnośnie uprawnień to istnieje bardzo ważna zależność pomiędzy bitami odczytu oraz wykonania. Nadając katalogowi domowemu uprawnienia rwxr--r-- czy 744. Pozostali użytkownicy będą mogli odczytać zawartości katalog, ale nie uzyskają dostępu do pliku podając go w jakimś poleceniu na ścieżce, do tego potrzebny jest jeszcze bit wykonania.
Za pomocą polecenia umask, możemy zdefiniować domyślne uprawnienia dla plików. Polecenie to przyjmuje jako argument te uprawnienia w postaci bezwględnej, które mają zostać usunięte z nowoutworzonych plików i katalogów. Wydanie polecenia umask:
$ umask 022
Spowoduje, że nowoutworzone pliki i katalogi będą mięc uprawnienia w postaci rwxr-xr-x lub 755. Może wydawać się zbyt liberalne, więc możemy ustawić argument polecenia nas 077, wówczas wszystkie utworzone pliki i katalogi będą wyłącznie dla nas. Polecenie umask, czesto występuje w plikach konfiguracyjnych powłoki.
2.17.2. Dowiązania symboliczne
Dowiązanie syboliczne to jest alias będący plikiem wskazującym na inny pliki lub katalog. Można uciec się do jednego słowa, że dowiązanie symboliczne jest poprostu skrótem.
Jeśli dowiązanie wskazuje na katalog, to przejście do dowiązania przeniesie nas w miejsce, na które wskazuje. Cel dowiązania nie musi nawet istnieć, jeśli jednak spróbuje przejść pod takie dowiązanie wówczas uzyskamy typowy błąd, o tym że katalog nie istnieje. Dowiązania uniemożliwają również sprawdzenie charakterystyki wskazywanego elementu, nie będzie wiadomo czy jest to plik, katalog czy inne dowiązanie. Wiele połączonych ze sobą dowiązań symbolicznych nazywane jest łańcuchem dowiązań
Dowiązania symboliczne tworzone są za pomocą polecenia ln z opcją -s (Ważne, aby użyć tej opcji). Argumentami jest na początku cel a poźniej nazwa dowiązania. Zachowanie kolejności argumentów jest ważne, ponieważ możemy utworzyć dowiązanie, które prowadzi do nikąd i wprowadza bałagan (być może w plikach systemowych).
Mimo swoich wad dowiązanią są wygodną metodą na współdzielenie plików oraz dodatkowo rozwiązują kilka drobnych problemów.
2.18. Archiwizowanie i kompresowanie danych
Przesyłając duża ilość małych plików przez sieć czy tez na pamięć masową, możemy odczuć że trwa to wieki, na pewno trwa to dłużej niż przesłanie jednego dużego pliku. Tutaj przedstawię sposoby na stworzenie jednego większego pliku z całego katalogu, przy czym użyjemy jeszcze kilku algorytmów kompresii, przez co zaoszczędzimy na czasie i trochę na zajmowanym miejscu.
2.18.1. Program tar
Pierwsze narzędzie będzie służyć do tworzenia archiwum. Archiwa łączą pliki i katalogi w jeden plik. Tar jest standardowym program do archiwizacji na uniksach. Tworzenie archwium za pomocą tar wymaga kilku opcji. Natomiast składania polecenia jest następująca:
$ tar -cvf archiwum.tar plik1 plik2...
Opcja -c
mówi programowi, że
tworzone będzie nowe archiwum, opcja -v
włącza komunikaty diagnostyczne wyświetlać one będą po kolei pakowane
do archiwum pliki; opcja -f
przekazuje programowi informacje o tym, że archwium będzie plikiem.
Domyślnie tar tworzył archiwa na taśmach. Obecnie pominięcie
tej opcji kończy pracę programu z komunikatem o błędzie. Możemy
natomiast użyć stdout podajac zamiast nazwy archiwum
myślnik (-). Póki co to archiwum nie jest jeszcze
skompresowane.
Rozpakowywanie pliku
Rozpakowawanie różni się tylko jedną opcją - zamiast -c jest -x. Następnie podajemy pozostałe opcje, a na końcu nazwę pliku archiwum.
Wyświetlenie zawartości archiwum
Wypakowanie całego archiwum może nie być do końca porządane, załóżmy że potrzebujemy tylko jednego pliki. Za pomocą polecenia tar z odpowiednim przełącznikiem możemy wyświetlić listę plików w archiwum. Zamiast -x, używamy -t reszta pozostaje taka sama, jeśli archiwum jest duże to możemy podłączyć wyjście tar potokiem do polecenia less. Samego wypakowania dokonujemy podając wypakowywanego pliku za nazwą archiwum.
Ostanią dość istotną opcję jest -p, która powoduje zachowanie oryginalnych atrybutów plików, jakie miały podczas pakowania. Kiedy superużytkownika używa tar, ta opcja jest domyślnie włączona.
2.18.2. Program gzip
Program gzip (GNU zip) jest standardowym narzędziem kompresującym w systemach uniksowych. Pliki skompresowane za jego pomocą otrzymują rozszerzenie .gz. Dekompresuje się je za pomocą polecenia gunzip, jako argument podaje się nazwę pliku. Natomiast kompresji dokonuje za pomocą polecenia gzip, podając plik do skompresowania jako argument.
2.18.3. Skompresowane archiwa tar.gz
Obsługę skompresowanych archwów przy użyciu gzip, rozpoczniemy od rozpakowania takiego archiwum. Nie ma sensu używania do tego dwóch osobnych poleceń, jest to z resztą marnowanie zasobów. Chcąc rozpakować skompresowane gzip archiwum, należy użyć polecenia tar a po opcji -x dodać, opcję -z następnie pozostałe czyli -vf i na końcu podać nazwę archiwum. Tak jak na przykładzie:
$ tar -xzvf archiwum.tar.gz
Przy tego typu archiwach, możemy spodziewać się rozszerzenia .tgz. Są to te same archiwa, jak te mające rozszerzenie tar.gz.
Przy wyświetlaniu zawartości takiego archiwum, zamieniamy opcję -x na -t. A chcąc takie archwiwum utworzyć to opcję -x na opcję -c oraz podać katalog lub listę plików, która ma zostać umieszczona w archiwum po jego nazwie.
2.18.4. Inne metody kompresji
Poza archiwami spakowanymi za pomocą gzip, możemy też spotkać archiwa spakowane za pomocą bzip2 oraz xz. W przypadku bzip2, to programem dekompresującym jest bunzip2, a opcją programu tar jest -j (mała litera j). Jeśli natrafimy na archiwum skompresowane xz, to programem dekompresującym jest unxz, a opcją programu tar jest -J (wielka litera j).
Część dystrybucji wyposażona jest w program unzip pozwalający na rozpakowanie plików .zip przygotowanych pod systemem MS Windows, jak i samo rozpakowywujących się plików .exe.
2.19. Hierarchia katalogów
Struktura katalogów głównego, jest utworzona na podstawie standardu hierarchii systemu plików, określającego jakie podkatalogi ma zawierać katalog główny, oraz co te podkatalogi mają przechowywać. Poniżej opisano na ten czas najważniejsze z nich.
- /bin - przechowuje pliki binarne przygotowane przez kompilator języka C, w nowocześniejszych systemach mogą to być skrypty powłoki. W nim przechowywane są te najprostsze polecenia jak cp.
- /dev - przechowuje pliku urządzeń.
- /etc - katalog zawierający najważniejsze pliki konfiguracji systemu. Znajdują się tutaj pliki haseł, konfiguracji uruchamiania systemu, urządzeń, sieci i innych elementów systemu.
- /home - zbiorczy katalog, katalogów domowych użytkowników. Standard wśród wszystkich nowoczesnych uniksów.
- /lib - katalog przechowywujący biblioteki. Te pliki przechowują kod, który może być wykorzystywany przez pliki wykonywalne. Biblioteki możemy podzielić na statyczne lub współdzielone. Tylko biblioteki współdzielone powinny znajdować się w tym katalogu, pozostałe pliki tego typu znajdują się w katalogu /usr/lib.
- /proc - udostępnia statystyki o systemie w postaci interfejsu plików i katalogów.
- /sys - ten katalog jest podobny do katalogu /proc, z tym, że tworzy interfejs dla urządzeń oraz systemu. Wiecej informacji na ten temat, znajduje się w następnym rozdziale.
- /sbin - w tym katalogu zapisane są systemowe pliki wykonywalne. Programy znajdujące się w katalogach /sbin przeznaczone są do zarządzania systemem, dlatego ten katalog nie występuje na ścieżce zwykłego użytkownika a wiele narzędzi będzie działać tylko z kontem root.
- /tmp - w tym katalogu możemy umieszczać pliki tymczasowe, którymi nikt się nie będzie przejmować. Każdy użytkownik może zapisywać i odczytywać pliki z katalogu w tym katalogu, ale nikt nie ma dostępu do plików zapisanych przez innych użytkowników. Nie które programy wykorzystują, ten katalog jako przestrzeń roboczą. Nie należy zapisywać ważnych danych do tego katalogu, gdyż jego zawartość jest przez wiekszość dystrybucji czyszczona podczas uruchamiania systemu, inne mogą usuwać starsze pliki co jakiś czas.
- /usr - W tym katalogu znajdziemy rozbudowaną, strukturę katalogów, bardzo podobną to katalogu głównego. W tym katalogu zapisana jest większa części systemu Linux.
- /var - podkatalog zawierający "zmienne" dane zapisywane przez programy w czasie swojego działania. Tutaj znajdują się m.in. pliki dzienników systemowych.
2.19.1. Pozostałe katalogi główne
- /boot - przechowuje plik ładujące jądro systemu w czasie uruchamiania komputera. W większości dystrybucji w tym katalogu przechowywane są właściwe pliki jądra oraz początkowego systemu plików w pamięci RAM. Początkowy system plików pamięci RAM zostanie omówiony w dalszej materiału.
- /media - w wielu dystrybucjach jest to główny punkt przyłączania wszystkich mediów wymiennych, takich jak karty pamięci Flash.
- /opt - może przechowywać dodatkowe oprogramowanie firm trzecich. W wielu systemach katalog /opt nie jest wykorzystywany.
2.19.2. Katalog /usr
To właśnie w katalogu /usr znajduje się większość programów i danych przestrzeni użytkownika, a są one rozlokowane po jego podkatalogach. Poniżej znajduje się opis co znajduje się w poszczególnych podkatalogach tego katalogu:
- bin - większość, jak nie wszystkie ogólnodostępne programy w systemie.
- include - przechowuje pliki nagłówkowe wykorzystywane przez kompilator języka C.
- info - zawierają strony dokumentacji GNU info.
- local - miejsce gdzie administratorzy mogą mogą instalować swoje oprogramowanie, katalog ten może zawierać podobną identyczną strukturę, jak katalog /usr lub katalog główny.
- man - przechowuje strony podręcznika systemowego.
- share - kiedyś ten katalog był katalogiem współdzielonym między komputerami, obecnie stracił na znaczeniu. Mimo to dalej przechwouje informacje, przeważnie są to pliki ikon, pliki zawierające znaki towarowe dystrybucji oraz inne dane z których może korzystać wiele programów. Ten katalog może zawierać podkatalogi takie jak man oraz info.
2.19.3. Umiejscowanie jądra w systemie
Wspomniałem już że plik jądra znajduje się w katalogu /boot, plik ten rozpoczyna się od nazwy vmlinuz, po tych znakach mogą wystąpić inne inforamcje oznaczające jego wersje. Po załadowaniu jądra przez program rozruchowy, sam plik przestaje być potrzebny. W trakcie pracy systemu operacyjnego jądro wykorzystuje najróżniejsze ładowane i usuwane dodatkowo modułu. Ładowane moduły jądra umieszczane są w katalogu /lib/modules.
2.20. Uruchamianie poleceń przez superużytkownika
Korzystając z linuksa na naszym osobistym komputerze, przyjdzie taki moment że będziemy musieli skorzystać z konta użytkownika root. Aby to zrobić możemy przelogować się na jego konto wykonać potrzebne czynności a następnie się wylogować. Ta czyność przyniosła by zamierzony efekt ale nie jest bez wad. Dlatego też możemy skorzystać z polecenia sudo, które pozwoli, na uruchomienie polecenia podanego jako arugment z uprawnieniami superużytkownika. Jeśli polecenie nie występuje w systemie, to jest dobry czas aby przelogować się na użytkownika root, i je zainstalować. Polecenie po zainstalowaniu nie zadziała samo w sobie potrzebne jest jeszcze określenie, którzy użytkownicy mogą używać tego polecenia i co za jego pomocą mogą zrobić. Za to odpowiada pliki /etc/sudoers.
2.20.1. Plik /etc/sudoers
Samo polecenie sudo ma bardzo duża ilość opcji, jednak na tym etapie nie skorzystamy z większości z nich. Najprostszym sposobem na konfiguracje pliku /etc/sudoers jest odnalezienie w pliku linii rozpoczynającej się pod słowa root a następnie pod tą linią wpisać linię rozpoczynjącą się nazwy użytkownika oraz dopisaniu kilku opcji, tak jak na poniższym przykładzie.
user ALL=(ALL) ALL
Pierwsze ALL
, oznaczna dowolny
komputer. Drugie
(ALL)
w nawiasach oznacza, że możemy
wydać polecenie jako dowolny użytkownik, być może spotkamy się
z takim zapisem w nawiasie (ALL:ALL), oznacza ono dowolnego
użytkownika i dowolną grupę. Trzecie ALL
oznacza dowolne polecenie.
Jeśli drażnić nas będzie ciągłe wpisywanie haseł, to możemy przed trzecim ALL w konfiguracji umieścić opcję NOPASSWD, pamiętając aby pomiędzy te opcje wstawić dwukropek (:) bo tak naprawdę określamy jakie polecenia mają być uruchamiane bez podawania hasła.
2.21. Podsumowanie
Po przeczytaniu tego rodziały wydaje mi się, że każdy ma solidne podstawy obsługi systemu Linux z poziomu powłoki. Powłoka jest jednym ze stałych komponentów dystrybucji, a wiele z nich dalej obstaje przy BASH-u, jako domyślnej powłoce.
3. Urządzenia
Odkąd powstał system Linux, w sposobach prezentowania urządzeń użytkownikowi zachodziło wiele zmian. Na początku tego rozdziału omówimy sobie tradycjny system sysfs. Aby potem zająć się omówieniem systemu udev, pozwalającego programom przestrzeni użytkownika automatycznie konfigurować oraz używać nowych urządzeń. Podczas normalnego użytkowania systemu rzadko będziemy mieć okazję do zarządzania urządzeniami. Nasza interakcja z urządzeniami będzie ograniczać się do obsługi pamięci masowych i korzystania najstarszego i naprostszego interfejsu jakim jest katalog /dev. Mimo to, warto jednak wiedzieć w systemie co jest od czego.
3.1. Pliki urządzeń
Jądro udostępnia wiele urządzeń pod postacią plików, co daje nam możliwość prostej manipulacji nimi. Te pliki są często nazywane węzłami urządzeń. Korzystać z urządzeń możemy za pomocą zwykłych operacji na plikach. Tego typu rozwiązanie nie jest bez wad dlatego też nie wszystkie urządzenia lub ich funkcje są udostępnianie w ten sposób.
Pliki urządzeń są przechowywane w katalogu /dev. A najprostszym sposobem interakcji z urządziem jest przekierowanie wyniku jakiegoś polecenia do urządzenia /dev/null. Urządzenie to jest miejscem na nie potrzebne nam dane ze strumieni, ponieważ cokolwiek trafi do tego urządzenia, jest przez jądro poprostu ignorowane.
Wyświetlając sobie zawartość katalogu w bardziej szczegółowej liście może zauważyć dziwne oznaczenia w trybie pliku. Litery te określają rodzaje urządzeń a wśród nich możemy wyszczególnić:
- Urządzenia blokowe - b - Procesy mogą odczytywać dane z urządzeń blokowych przy użyciu bloków o stałej wielkości. Pamięci masowe są przykładem urządzeń blokowych. Łatwo dzieli się je na bloki, a ogólna wielkość takiego urządzenia jest stała i można ją zindeskować, co daje możliwość jądru na dostępu do dowolnego bloku danych.
- Urządzenia znakowe - c - Urządzenia znakowe działają w oparciu o strumienie danych. Do takich urządzeń można zapisywać i odczytywać pojedyńcze znaki. Jądro zazwyczaj wykonuje operacje odczytu i zapisu na fizycznym urządzeniu. Drukarki są przykładami urządzeń znakowych. Warto wspomnieć, że jądro nie jest w stanie ponownie odczytać danych ze strumienia po przekazaniu ich dalej do procesu.
- Urządzenia potokowe - p - tzw. nazwane potoki są to urządzenia podobne do urządzeń znakowych z tą jednak różnicą, że na drugim końcu strumienia wejścia-wyjścia nie znajduje się fizyczne urządzenia ale inny proces.
- Urządzenia gniazdkowe - s - tzw. gniazda są interfejsami specjalnego przeznaczenia i służa komunikacji międzyprocesowej. Mogą występować poza katalogiem /dev.
Inną dość rzucającą się w oczy informacją na listingu katalogu, są dwie liczy odzielone przecinkiem zamiast rozmiaru pliku, jest numer główny i numer poboczny. Te numer ułatwiają jądru identyfikacje urządzeń. Dla przykładu partycje tego samego dysku mają ten sam numer główny ale inny numer poboczny.
Nie wszystkie urządzenia mają swoje pliki, takim przykładem są interfejsy sieciowe. Jądro wykorzystuje dla nich inny interfejs wejścia-wyjścia.
3.2. Ścieżka urządzeń sysfs
Ze względu na uproszczoną interakcje z urządzeniami poprzez odwołowywanie się do pliku w katalogu /dev oraz fakt, że jądro systemu nadaje plikom z tego katalogu nazwy na podstawie koleności wykrywania urządzeń podczas uruchamiania systemu, wewnątrz jądra zaimplementowano interfejs sysfs. Sysfs jest ujednoliconym sposobem prezentacji urządzeń bazującym na atrybutach sprzętowych, mający formę struktury katalogów i plików. Główny katalogiem tego systemu jest katalog /sys/devices. Przykładowa ścieżka dla pierwszego dysku SATA mającego swój plik /dev/sda może wyglądać następująco:
/sys/devices/pci0000:00/0000:00:1f:2/host0/target0:0:0/0:0:0:0/block/sda
Warto tutaj zaznaczyć iż, scieżki systemu sysfs nie służą uzyskaniu dostępu do urządzeń, umożliwają przeglądanie informacji oraz zarządzanie urządzeniami. Dane zawarte na plikach na ścieżkach sysfs powinny być odczytywane przez programy nie przez ludzi.
Chcąc sprawdzić scieżkę sysfs dla dowolnego urządzenia z katalogu /dev należało by uzyć programu systemu udev udevadm.
$ udevadm info --query=all --name=/dev/sda
Wykonując to polecenie dowiemy się przy okazji ile danych można uzyskać informacji z systemu udev.
3.3. Polecenie dd
Polecenie dd jest dość prostym, aczkolwiek przydatnym narzędziem jeśli choodzi o prace z urządzeniami znakowymi czy blokowymi. Jedyną rzeczą, którą robi to polecenie jest odczyt danych z pliku wejściowego lub ze strumienia i zapisanie go do wyjściowego pliku lub strumienia, przy okazji dokonując pewnych konwersji. Najczęsciej używane przeze mnie polecenie znajduje się poniżej.
$ sudo dd if=/dev/zero bs=1M of=/dev/sdX count=1
Polecenie wykorzystuje uprawnienia superużytkownika, aby uzyskać dostęp do urządzenia blokowego. Samo polecenie zapisuje jeden blok o wielkości 1M za pomocą zer z pliku /dev/zero (nieskończony strumień zer), co powoduje usunięcie tablicy partycji (o której będzie w następnym rodziale).
Poniżej zostaną opisane najważniejsze opcje programu dd, ich format różni się od innych programów uniksowych. Do przypisania wartości opcją używa się tutaj znaku równości (=).
- if=plik - plik wejściowy. Domyślnie stosowane jest standardowe wejście.
- of=plik - plik wyjściowy. Domyślnie stosowane jest standardowe wyjście.
- bs=rozmiar - rozmiar bloku danych. Polecenie przesyła wiele bloków naraz, więc możemy użyć wielokrotności takich jak: bajt - B, kilobajt - K, megabajt - M, gigabajt - G i tak dalej.
- ibs=rozmiar, obs=rozmiar - rozmiar blok wejściowego oraz bloku wyjściowego, jeśli nie możliwe jest stosowanie dla plików wejściowych oraz wyjściowych tego samego rozmiaru bloku.
- count=liczba - liczba kopiowanych bloków.
- skip=liczba - powoduje pominięcie pierwszych liczba bloków danych. Nie są one kopiowane do pliku wyjściowego.
Przy korzystaniu z dd, należy uważać gdyż literówka w poleceniu wystarczy, aby uszkodzić system lub spowodować utratę ważnych dla nas danych.
3.4. Podsumowanie nazewnictwa urządzeń
Do pracy z urządzeniami potrzebujemy jego nazwy. W systemie istnieje kilka metod pozwalających na ustalenia nazwy urządzenia.
- Odpytanie systemu udev, za pomocą polecenia udevadm.
- Przeszukanie katalogu /sys.
- Wyświetlenie plików dziennika jądra za pomcą polecenia dmesg, w plikach dziennika znajdują się jego ostatnie komunikaty w tym informacje o znalezionych urządzenia i nadanych im nazwach.
- Jeśli to dysk i jest on widoczny w systemie to możemy skorzystać z polecenia mount, chyba że nasz system nie montuje samodzielnie partycji, to w tym przypadku możemy skorzystać z polecenia fdisk z opcją -l.
- Ostatni sposób działa tylko dla urządzeń blokowych oraz znakowych. W katalogu /proc/devices znajduje się zestawienie urządzeń reprezentowanych przez numer główny oraz przez przypisany im sterownik. Wystarczy użyć numeru głównego jako wyrażenia regulanego i zastosować go na listingu katalogu /dev.
Dzisiaj praca z urządzeniami na Linuksie sprowadza się głównie do partycjonowania dysku, więc aby znależć właściwe urządzenie wystarczy użyć polecenia fdisk -l.
Na poniższej liście znajdują się najczęściej wykorzystywane na Linkusie konwencje nazwenicze.
- Dyski twarde SATA - /dev/sd* - Nazewnictwo dysków pochodzi od protokołu SCSI, mimo iż same urządzenia SCSI wyszły z użycia. To protokół mający świetne zdolności adaptacyjne cały czas działa we współczesnych systemach. Nazwa np. /dev/sda odnosi się do całego dysku. Partycje oznaczne są dodatkowo liczbą np. /dev/sda1. Pamięci masowe ze złączem USB również wykorzysują podsystem SCSI do komunikacji z komputerem dla tego też dyski tego typu również mogą występować jak /dev/sd*.
- Napędy CD i DVD - /dev/sr* - Napędy optyczne tak samo jak dyski korzystają z SCSI. Jednak te urządzenia są tylko do odczytu. Jeśli chcielibyśmy skasować zawartość płyty, lub na niej coś nagrać, to należało by się odwołać do ogólnego urządzenia którego nazwa może byc na przykład taka: /dev/sg0. Raczej jednak do nagrywania płyt będziemy wykorzystywać specjalne oprogramowanie.
- Dyski twarde PATA - /dev/hd* - w starszych wersjach jądra, dyski były przedstawione za pomocą innych nazw. Obecnie dyski PATA również wykorzystują SCSI, więc ich nazwy nie będą się różnić od dysków SATA.
- Terminale - /dev/tty1, /dev/pts/*, /dev/tty - Terminale są urządzeniami przeznaczonymi do przesyłania znaków pomiędzy system a urządzeniem wejscia-wyjścia, co najczęściej ma celu wyświetlanie tekstu na ekranie terminala. Pseudoterminale posiadają wyszystkie funkcję fizycznych terminali, jednak jądro nie komunikuje się z fizycznym urządzniem, a z programowym interfejsem wejscia-wyjścia takim jak okno powłoki. Urządzenie /dev/tty1 to pierwsza wirtualna konsola, a /dev/pts/0 to pierwsze okno powłoki. Linux do działania nie potrzebuje klasycznego interfejsu użytkownika, jaki znamy z innych systemów operacyjnych. Nie potrzebuje okien, paska zadań, menu "Start" i innych elementów graficznych. Równie dobrze może działać jak system 'DOS' w tzw. trybie tekstowym. Praca w tym trybie pozwala na wykorzystanie komputera znacznym stopniu. Jedyne czego nie będziemy w stanie zrobić to skorzystać z czego kolwiek co wymaga wyświetlania graficznego wymagającego pokazania na ekranie czegoś więcej niż teskt i 8 podstawowych kolorów. Tryb graficzny zawiera wszystkie te rzeczy znane z innych systemów operacyjnych a wymienione zostały one powyżej, choć to wszystko zależy od uruchomionego menadżera okien. Pierwsza wirtualna konsola jest pierwszym ekranem powłoki w trybie tesktowym, okna powłoki mogą wystąpić w środowisku graficznym po uruchomieniu odpowiedniego programu Terminal lub w przypadku trybu tesktowego multipleksera terminala. Multipleksery są powielaczami, pozwalającymi podzielić konsolę na kilka mniejszych "okien", w których możemy uruchomić kolejną powłokę. Zaś urządzenie /dev/tty, jest odniesienie procesu do właściwego terminala, jeżeli program odczytuje i zapisuje dane do terminala.
- Porty szeregowe - /dev/ttyS* - są specjalnymi urządzeniami terminalowymi pozwalającymi na komunikację z różnej maści sprzętem wykorzystując do tego oprogramowanie, które pozwoli skonfigurowanie komunikacji. Porty szeregowe otrzymują w systemie nazwy kolejno: /dev/ttyS0 itd. w zależności od tego ile mamy portów zainstalowanych w komputerze. Do połączenia za pomocą portu szeregowego można wykorzystać adaptery USB, te urządzenia mogą posiadać następujące nazwy: /dev/ttyUSB* lub /dev/ttyAMC*. Modemy sieci komórkowych mogą występować w systemie pod postacią adapterów USB portów szeregowych.
- Porty równoległe - /dev/lp0, /dev/lp1 - służyły do podłączenia drukarek wykorzystujących port LPT. Obecenie zostały zastąpione przez porty USB. Drukarki mogą wymagać dodatkowych znaków sterujących, dlatego też do drukowania lepiej wykorzystać serwer druku CUPS, niż pisanie bezpośrednio do tego portu.
- Urządzenia audio - /dev/dsp, /dev/audio, /dev/snd/* - Na Linuksie dostępne są dwa zestawy urządzeń odpowiadająych za dźwiek. Starszy, rzadziej spotykany system OSS korzystający z urządzeń /dev/dsp oraz /dev/audio i nowszysz spotykany w większości dystrybucji ALSA używający urządzeń w katalogu /dev/snd. System dzwięku na Linuksie może być dwuwartstwowy, ponieważ do dyspozycji mamy serwer pośredniczący PulseAudio, główną jego zaletą jest łatwa możliwość przełącznia wyjść dzwięku oraz proste zarządzanie całym podsystem dźwięku z poziomu jednego panelu. Wykorzystanie samego systemu ALSA jest wystarczające, jednak nie zbyt wygodne, ale oczywiście co kto lubi.
3.4.1. Tworzenie plików urządzeń
We współczesnych systemach nie ma potrzeby samodzielnego tworzenia urządzeń, jednak czasami w specyficznych konfiguracjach może dość do potrzeby utworzenia urządzenia. Osobiście spotkałem się z takim przypadkiem konfigurując VPN na dystrybucji Alpine Linux, należało osobiście utworzyć urządzęnie znakowe TUN.
Urządzenie tworzymy za pomocą polecenia mknod, podając nazwę urządzenia, jego rodzaj i w zależności od rodzaju numer główny oraz numer poboczny (w przypadku nazwanych potoków, nie trzeba podawać numery głównego i numeru pobocznego.
3.5 System udev
Zarządzanie plikami urządzeń jest jedną z cech jądra, która mogła działać w przestrzeni użytkownika. Jądro tylko gdyby wykryło nowe urządzenie wysłało by powiadomienie do procesu udevd. Proces ten zbadał by charakterystykę urządzenia, utworzył dla niego odpowiedni plik, a na koniec przewprowadził jego inicjację. Niestety to tylko teoria.
Rozwiązanie tego typu nie uwzględnia kilku problemów. Pliki urządzeń są potrzebne już na wszczesnych etapach uruchamiania, zatem proces udev musiał by zostać uruchomiony bardzo wcześnie, nie może mieć żadnych zależności wobec plików urządzeń i uruchomić się błyskawicznie, aby nie spowalniać procedury rozruchu systemu.
3.5.1. System plików devtmpfs
System plików (będzie o tym w dalszej części materiału) devtmpfs, został opracowany w celu rozwiąznia problemów z dostęp do urządzeń w czasie uruchamiania systemu. W razie gdy jądro będzie potrzebować pliku urządzenia to utworzy je oraz powiadamia o tym fakcie system udev, który zamiast zajmować się tworzeniem pliku przystępuje do inicjacji urządzenia i informuje o tym pozostałe procesy. Po za tym proces udev tworzy kilka dowiązań symbolicznych w katalogu /dev, które bardziej szczegółowo identyfikują urządzenie, wyniki tego działania możemy obejrzeć w katalogu /dev/disk/by-id.
System udev, tworzy nazwy dowiązań bazdując na typie interfejsu, nazwie producenta, informacji o modelu, numerze seryjnym oraz partycji. Proces pobiera te informacji na podstawie reguł systemu udev, jednak nie będziemy się tym tutaj zajmować.
4. Dyski i systemy plików
Dyski w systemach Linux przedstawiane są jako urządzenia blokowe z nazwami pochodzącymi od podsystemu SCSI - /dev/sdX. Z punktu widzenia systemu na dysku znajduje się wiele warstw oraz komponentów. Wybrane częsci dysku możemu zaalokować na partycje, które system prezentuje w taki sam sposób jak dyski, dodają liczbę na końcu nazwy dysku. Wystąpienia partycji na dysku przechowywane są w tablicy partycji.
Jądro systemu umożliwia dostęp do całego urządzenia (dysku) oraz do partycji dzięki osobnym plikom urządzeń.
Każdy dysk musi posiadać chociażby jedną partycję, aby był użyteczny w systemie, z kolei taka partycja musi zostać sformatowana pod wybrany system plików, aby mogła przez chowywać jakie kolwiek dane. System plików możemy określić mianem bazy danych przechowywującej informacje na temat plików i katalogów.
4.1. Partycjonowanie dysków
Partycjonowanie dysku, odbywa się w oparciu o schematy. Schematy partycjonowania okreslają ilość możliwych do utworzenia na dysku partycji oraz ewentualne dodatkowe informacje przechowywane w tablicy partycji. Wśród obecnie stosowanych możemy wyróżnić takie schematy jak MBR oraz GPT.
Na Linuksie dostępnych jest wiele narzędzi partycjonujących dysk, jedne są obsługiwane jak z poziomu środowiska graficznego inne zaś z poziomu terminala. Osobiście używam programu fdisk i to na nim skupię się jeśli chodzi o partycjonowanie. Program ten ma dwie istotne zalety. Po pierwsze nic nie zostanie zapisane do momentu gdy nie wydamy polecenia (Tak, w fdisk wydaje się polecenia, ale są one ograniczone do wpisania jednej litery i naciśnięcia klawisza enter); po drugie w pakiecie fdisk zawarty jest również program sfdisk (co prawda z nieco dziwną składnią), ktory umożliwia manipulowanie dyski z poziomu pojedynczych polecenie (w przypadku fdisk wykorzystywany jest tryb interaktywny), przez co możemy użyć sfdisk w skrypcie.
4.1.1. Przeglądanie tablicy partycji
Przy użyciu polecenia:
$ sudo fdisk -l
możemy wyświetlić zawartość tablicy partycji wszystkich dysków w
systemie, Jeśli zaś interesuje nas wybrane urządzenie możemy wpisać
jego nazwę po opcji -l
. Informacja
zwracana przez polecenie zawiera informacje o schemacie
partycjonowania w polu Disklabel type: oraz tabele
przedstawiającą nazwę urządzenia, informacje o ustwionej fladze
rozruchu, początku, końcu i rozmiarze podanym w sektorach (informacja
ile wynosi sektor znajduje się w linii Sector size) oraz
identyfikatorze i nazwie typu partycji. Identyfikatory można wypisać
podczas nadawania typu.
Schemat partycjonowania MBR nazwany jest fdisk dos. Identyfikator dysk jest krótszy. W przypadku tablicy partycji GPT identyfikator dysku zawiera ciągu znaków odzielone spacjami, nie występują w tabeli identyfikatory typów partycji ponieważ są tak duże jak identyfikator dysku (podczas ustalania wybierane są z wyświetlonej listy) oraz może wystąpić dodatkowa kolumna przechowywująca etykietę partycji. Jednak główną różnicą wśród tych schematów jest zarządzanie miejscem na dysku. Dyski z tablicami MBR mogą mieć maksymalną pojemność do 2TB, jeśli użyjemy wiekszego, to stosując tego typu partycję pozbędziemy się pozostałej części dysku, kolejny minus dla tego rodzaju to możlwość tworzenie maksymalnie czterech partycji podstawowych (zwykłych partycji na dane), jeśli chcielibyśmy więcej owszem możemy jednak, musimy użyć jedno miejsce na partycję rozszerzoną bedącą kontenerem dla dysków logicznych. W przypadku GPT raczej maksymalna wielkość dysku nie jest póki co osiąglna (9,4 mld TB) a partycji podstawowych możemy utworzyć, aż 128. To są tak naprawę za i przeciw, które decydują o użytym schemacie.
4.1.2. Modyfikowanie tablicy partycji
Modyfikowanie tablicy partycji należy rozpocząć, od zastanowienia się na temat przydatności danych, które znajduja się obecnie na dysku. Ponieważ modyfikowanie tablicy partycji, często będzie wiązać się z potrzebą reformatowania modyfikowanej partycji. W stopniu podstawowym skupimy się tworzeniu oraz usuwaniu partycji. Modyfikowanie tablicy za pomocą fdisk, jest dość łatwe, polega na interaktywnym wydawaniu poleceń oraz odpowiadniu na pytania programu, dlatego też dla urozmaicenia użyjemy sfdisk zamiast wymienionego wcześniej programu. Dla przykładu stworzymy dysk do klasycznej instalacji systemu Linux z tablicą partycji typu MBR. Za dysk testowy może posłużyć nam pendrive, karta pamięci lub plik na dysku. Ja skorzystam z pliku.
Plik przygotowuje za pomocą polecenia dd poznanego w poprzednim rozdziale.
$ sudo dd if=/dev/zero bs=1M of=vhd.img count=8192
Zapisanie 8GB zer może chwilę potrwać. Po utworzeniu pliku przechodzę do jego inicjalizacji.
$ echo "label:dos" | sudo sfdisk vhd.img
Polecenia sfdisk muszą pochodzić ze strumienia lub z wcześniej przygotowanego skryptu. Polecenie tworzy na dysku tablice partycji typu MBR. Kolejne polcenie będą już poleceniami właściwymi tworzącymi partycje na naszym dysku.
Polecenia sfdisk składają się z czterech pól:
- początkowy sektor - w pierwszym polu wskazujemy od którego sektora na dysku ma zaczynia się partycja. Pierwsza partycja zawsze zaczyna się od drugiego megabajtu dysku. Wartość ta jest najczęściej pomijana i pozostawiana do decyzji programowi.
- wielkość partycji - wielkość partycji podajemy zapisując na początku znak plusa (+) następnie podając jej wielkość w wygodnej dla nas wielkrotności bajtu, pamiętając że zapisując jednostkę, używamy tylko pierwszej litery (dla GB nie jest GB tylko G).
- typ partycji - typ partycji określany jest za pomocą pojedyńczej litery dla zywkłej partycji na dane Linuksa jest to L, dla przestrzeni wymiany (będzie o niej za chwilę) jest S, a dla partycji rozszerzonej jest to E.
- flaga rozruchowa - flaga rozruchowa była stosowana dawniej, obecnie straciła na znaczeniu. I jej obecność może służyć zaznaczeniu partycji przechowywującej podkatalog /boot katalogu głównego.
Po utworzeniu pierwszej partycji, każda kolejna będzie wymagać podania podania opcji -a, która spowoduje wykorzystanie wcześniej utworzonej tablicy partycji oraz jej numeru po opcji -N przed wskazaniem poleceniu urządzenia. Zatem pierwszą partycję tworzymy za pomocą poniższego polecenia:
$ echo ",+7G,L,*" | sudo sfdisk vhd.img
Pierwszej partycji przydzielono większość miejsca na dysku, będzie ona przechowywać katalog główny. Resztę miejsca wykorzystamy na partycję rozszerzoną, a wewnątrz niej utworzymy dysk logiczny będący partycją wymiany.
$ echo ",,E," | sudo sfdisk -a -N 2 vhd.img $ echo ",,S," | sudo sfdisk -a -N 5 vhd.img
Teraz możemy wyświetlić sobie tablice dysku, który partycjonowaliśmy. Zwróćmy uwagę na to, że wystarczy odpowiedni numer partycji aby utworzyć dysk logiczny. Pominięcie rozmiaru spowoduje zaalokowanie pozostałego wolnego miejsca. Tak przygotowane partycje są gotowe do sformatowania pod wybrany system plików.
Usuwanie partycji
Eksperymentując poraz pierwszy z sfdisk może nam coś nie wyjść dlatego też zamiast rozpoczynać partycjonowanie od nowa możemy źle przygotowaną partycję usunąć. Wydając polecenie sfdisk opcję --delete następnie nazwę urządzenia oraz numer partycji, którą chcemy usunąć.
Korzystając z sfdisk pozbawiamy się bufora, ponieważ program ten zmienia tablicę partycji z każdą modyfikacją. Jeśli chcemy tylko sprawdzić jak będą wyglądać pewne zmiany to lepiej użyć polecenia fdisk. Pomoc uruchamiana jest za pomocą polecenia m po uruchomieniu programu.
4.2. Systemy plików
Systemy plików umożliwiają zamianę prostego urządzenia blokowego w sktrukturę plików i katalogów zarozumiałą dla końcowego użytkownika. Dawniej służyły głównie przchowywaniu plików jednak obecne ich funkcje umożliwiają wykorzystanie ich jako interfejsów systemowych w takich katalogach jak /proc czy /sys.
Normalnie systemy plików są implementowane w jądrze systemu, jednak rozwiązana zastosowane w następcach Uniksa, takich jak Plan 9. umożliwiły stworzenie systemów plików działających w przestrzeni użytkownika, tzw. FUSE. Dzięki tej funkcji możemy zapisywać dane na nośnikach z takim system plików jak NTFS.
Istotną funkcję jeśli chodzi o sposób działania systemów plików, jest wykorzystanie VFS, który standaryzuje dostęp do plików i katalogów dla aplikacji użytkownika, dlatego też Linux obsługuje tak wiele systemów plików.
4.2.1. Typy systemów plików
Mimo iż Linux, może obsługiwać chyba wszystkie możliwe systemy plików, to większość z nich wymaga dodatkowego oprogramowania. Natywnie obsługiwane systemy plików Linuksa znajduje się na liście poniżej.
- EXT4 (Czwarty rozszerzony system plików) - domyślny i najpopularniejszy system plików dla linuksa, wyposarzony w pliki dziennika, znane z ext3 oraz zwiększone limity związane z wielkością plików oraz ilością podkatalogów w katalogu względem wersji trzeciej. W przyszłości może zostać zastąpiony przez btrfs lub xfs.
- iso9660 - standardowy system plików stosowany na płytach CD-ROM.
- FAT - rodzina systemów plików znan z wczesnych wersji systemu MS Windows, obecnie wykorzystywana przez pamięci flash, jak pendrive oraz karty pamięci.
- HFS+ - system plików stosowany w starszych wersjach systemu Apple macOS. Zastąpiony prze APFS. Wykorzystanie partycji system HFS+, jest jedyną możliwością przenaszalności plików pomiędzy współczesnymi Macami a Linuksem.
4.2.2. Tworzenie systemu plików
Tworząc partycje w poprzednim punkcie pozostało jeszcze sformatować pod konkretny system plików, aby można było przechowywać na nich informacje. System plików tworzony jest za pomocą polecenia mkfs. Polecenie ma inną nieco inna składnię, ponieważ żądany system plików podaje się po kropce w nazwie polecenia np. mkfs.ext4. Jako argument podajemy nazwę urządzenia partycji.
$ sudo mkfs.ext4 /dev/sda1
Podczas formatowania partycji program wyświetla komunikaty diagnostyczne. Wśród nich znajdują się liczby oddzielone do siebie przecinkami. Te liczby to kopie zapasowe superbloku. Superblok to najwyższy poziom bazy danych systemu plików, jest on na tyle ważny że program tworzy kilka jego kopii. Numery bloków zawierających kopie superbloku należy zachować ponieważ może być ona potrzebna do ewentualnego odzyskiwania danych.
Przyglądając się samemu programowi mkfs dojedziemy do
wniosku, że jest to swojego rodzaju interfejs do całego zbioru
programów tworzących systemy plików. Nie które wystąpienia tego
interfejsu są dowiązaniami sybolicznymi do innych programów.
np mkfs.ext4
wskazuje na
program mke2fs, będący głównym programem do służącym
do tworzenia systemów plików z rodziny EXT, warto o tym pamiętać
ponieważ możemy natknąć się na systemy bez polecenia mkfs.
4.2.3. Montowanie systemów plików
Proces dołączania systemu plików w uniksach nazywany jest montowaniem. Aby zamontować w systemie jakiś system plików należy użyć polecenia mount, użyć tego polecnia bez żadnej opcji spowoduje wyświetlenie podmonotowanych systemów plików. Montowanie jak i późniejsze odmontowywanie wymagają uprawnień superużytkownika.
Każdy wpis to jedno montowanie systemu plików, wpisy zwierają kolejno nazwę urządzenia, docelowe miejsce montowania, typ systemu oraz opcje specyficzne dla systemu.
Montowanie systemu plików odbywa się za pomocą tego samego polecenia, jednak wymaga podania kilku argumentów, kolejno:
- nazwy urządzenia
- typ systemu plików
- punktu montowania - katalogu docelowego dla montowanego systemu plików.
- opcje specyficzne dla systemu plików - opcje są podawane jak wartość opcji -o polecenia. Nie zawsze trzeba podawać opcje systemu plików.
Montując systemy takie jak EXT, czy któryś z FAT możemy pominąć rodzaj podczas montowania, program sam to ustali. Jednak montowanie udziałów sieciowych CIFS, wymaga podania typu aby program mount mógł rozpoznać wartości zapisane w argumentach.
Po skończeniu prac z system plików, możemy go odmontować za pomocą polecenia umount. Polecenie wymaga podania albo urządzenia albo punktu montowania jako argumentu.
4.2.4. Identyfikator UUID systemu plików
Montowanie systemu plików wymaga podania nazyw urządzenia. Pliki konfiguracyjne odpowiedzialne za automatyczne montowanie systemów plików w systemie podczas jego startu, nie mogą polegać na tych samych nazwach urządzeń co użytkownicy, ponieważ są one ustalane pod czas startu systemu, i ich nazwy zależą od kolejności wykrycia ich przez jądro. W takich plikach używa się identyfikatorów UUID swoistych numerów seryjnych systemów plików nadawanych podczas formatowania. Listę urządzeń wraz z UUID-ami, możemy wywołać za pomocą polecenia:
$ sudo blkid
Identyfikatorów możemy używać nie tylko z plikami konfiguracyjnymi jak /etc/fstab, ale równie przy polecniu mount zamiast klasycznej nazwy urządzenia. Jednak posługiwanie się tak długim i skomplikowanym ciągiem znaków nie jest za wygodne.
Polecenie blkid może zwracać w polu UUID numery identyfikacyjne innych systemów plików takich jak na przykład FAT, gdzie UUID-em jest numer seryjny woluminu FAT. Oczywiście takie identyfikatory możemy używać podczas konfiguracji pliku /etc/fstab.
UUID musi być unikatowy, dlatego też jeśli zasła potrzeba skopiowania całego systemu plików, to należy zmienić ten identyfikator aby odróżnić kopię od oryginału.
4.2.5. Buforowanie dysku i systemu plików
Uniksy w tym i Linux nie zapisują wszysktkich zmian w systemie plików po otrzymaniu takiego żądania. Zmiany przechowywane są w pamięci RAM od momentu kiedy jądro będzie mogło swobodnie zapisać je na dysku.
W momencie odmontowywania systemu plików jądro automatycznie synchronizuje zawartość dysku. Jeśli z jakiego powodu nie będziemy mogli odmontować systemu plików to wówczas możemy wydać polecenie sync, które wymusza zapisanie na dysku wszyskich zmian w systemie plików. Oczywiście w wiekszej liczbie przypadku problemów z odmonotowaniem systemu plików jest proces używający któregoś z plików na dysku.
Jądro dysponują całą serią mechanizmów wykorzystujących pamięć RAM do buforowania danych odczytywanych z dysków, przez co jeśli proces wielokrotnie będzie odczytywać dane z tego samego pliku, jądro nie będzie musiało odwoływać się do danych na dysku przez każde dane do procesu z bufora, oszczędzając tym samym czas i zasoby.
4.2.6. Opcje montowania
Polecenie mount posiada dużą ilość opcji. Jest ona tak duża że wprowadzenie opcji długich wynikało z obowiązku aniżeli wygody, ponieważ nazwyczajniej zaczynało tych liter w alfabecie brakować. Z posród opcji krótkich - jedno literowych możemy wyróżnić najważniejsze:
- -r - powoduje zamontowanie systemu plików w trybie tylko do odczytu.
- -n - powoduje nie modyfikowanie pliku /etc/mtab (plik zawiera zamontowane obecnie systemy plików, wydanie polecenia mount bez żadnych opcji powoduje wyświetlenie zawartości tego pliku). Opcja ta pozwala na zamontowanie systemu plików w momencie gdy system plików zawierjący katalog główny, a co za tym idzie plik /etc/mtab jest zamknotowany w trybie tylko do odczytu, bowiem nie zapisanie zmian w tym pliku spowoduje niepowodzenie montowania.
- -t - umożliwia podanie systemu plików.
Opcje długie podawane wraz z opcjami specyficznymi dla systemu plików po opcji -o. Z opcji długich możemy wyróżnić takie jak:
- exec, noexec - włącza i wyłącza możliwość uruchamiania programów w danym systemie plików.
- suid, nosuid - włącza i wyłącza możliwość korzystania z bitu suid przez programy.
- ro - montuje system plików w trybie tylko do odczytu.
- rw - mountuje system plików w trybie pełnego dostępu.
4.2.7. Pownowne montowanie systemu plików
W trakcie odzyskiwania danych może zajść potrzeba ponownego zamontowania systemu plików w celu zmiany opcji montowania. Najczęściej chodzi o przełączenie systemu plików zawierającego katalog główny z trybu tylko do odczytu w tryb pełnego dostępu. Ponownemu montowaniu służy opcja remount.
4.2.8. Tablica systemów plików /etc/fstab
Plik /etc/fstab przechowuje informacje o systemach plików oraz ich punktach montowania, dzięki czemu montuje te systemy podczas uruchamiania systemu. Każdy wiersz tego pliku przechowuje informacje o jednym systemie plików i jest podzielony na sześć pól.
- Nazwa urządzenia lub UUID - dla dysków stosowane są identyfikatory UUID, jednak napęd optyczny w tym pliku, zapisywany jest nazwą urządzenia. Urządzeniem tylko do odczytu napędu optycznego zazwyczja będzie /dev/sr0.
- Punkt monotowania - katalog docelowy dla montowanego systemu plików.
- Typ systemu plików
- Opcje - list długich opcji rodzielonych przecinkami.
- Informacje o kopiach bezpieczeństwa dla programu dump - w tym polu zawsze należy podawać 0.
- Kolejność sprawdzania spójność systemów plików - systemowi plików zawierającemu katalogów zawsze podajemy wartość 1. Oznacza to sprawdzenie tego systemu w pierwszej kolejności. Pozostałym możemy podać wartość 2 lub 0, gdzie 2 oznacza sprawdzenie tych systemów po systemie oznaczonym wartością 1. Każdy kolejny system przychowywujący jakieś dane oznacza się 2. Po sprawdzeniu systemu z jedynką, program będzie sprawdzać po kolei systemy z dwójką. Natomiast 0 oznacza pominięcie sprawdzania systemu przez program fsck.
Posiadając odpowiednie wpisy w plik /etc/fstab, możemy montować system plików podając poleceniu mount tylko punkty montowania co może być wygodne podczas montowania systemów plików dużą ilością opcji.
Istnieje kilka opcji które mają zastosowanie tylko w omawianym przez nas pliku.
- defaults - włącza domyślne ustawienie polecenia mount dając tym samym największe uprawenienia.
- errors - ta opcja ma zastosowanie tylko dla systemów z rodziny EXT, pozwala na ustalenia zachowania systemu w momencie problemów z montowaniem systemu plików. Do wyboru mamy takie możliwości jak: continue - wygeneruj kod błędu i kontynuuj pracę; remount-ro - zamontuj ponownie w trybie tylko do odczytu; panic - zatrzymaj system.
- noauto - opcja nakazuje pominąć wpis podczas automatycznego montowania systemów plików.
- user - pozwala na podmontowanie tego systemu za pomocą polecenia mount bez potrzeby uprawnień superużytkownika - podmontować ten system plików może każdy użytkownik.
4.2.9 Pojemność systemu plików
Sprawdzenia zajętości systemu plików możemy dokonać za pomocą polecenia df. Polecenie to domyślnie zwraca wszelkie wartość w postaci kilobajtów, które nie są zbyt czytelne dla człowieka. Aby przeskalować jednostki możemy posłużyć się opcją -h. Polecenie wyświetla wynik swojego działania w postaci pięciu kolumn przedstawiających kolejno system plików, jego rozmiar, użyte miejsce, dostępne miejsce, stopień użycia w procentach oraz punkt montowania. W przypadku użycia programu bez podanej opcji rozmiar systemu nosi nazwę 1K-bl jest to wielkość systemu plikach w jednokilobajtowych blokach.
$ df -h
Jeśli przjrzymy się na chwilę wynikom działania tego polecenia, możemy dość do wniosku, że albo mamy doczynienia z błędem albo tolerancja błędu przybliżenia jest bardzo. Otóż nie. Kilku gigabajtów brakuje ze względu na to, że zostały zarezerwowane i są do dyspozycji superużytkownika w momencie wyczerpania się miejsca na danym systemie plików, aby zapewnić systemówi dalsze funkcjonowanie oraz umożliwić administratorowi odzyskanie chociaż części miejsca na dysku.
4.2.10. Sprawdzanie i naprawnia systemu plików
Jądro do pracy systemu musi mieć pewność, że zamontowane systemy plików są pozbawione błędów. Błędy systemów plików mogą powodować utratę danych lub załamanie systemu. Najczęstszym powodem występowania błędów w systemie plików, są zaniki zasialania komputera spowodowane ludzką niewiedzą lub czynnikami środowiskowymi. Najnowszej generacji systemy plików wykorzystują pliki dziennika, dzięki, przerwanie działania systemu w wyniku różnych czynników nie doprowadza do katastrofy to są przypadki gdzie i one zawodzą.
Narzędzie przeznaczone do sprawdzania oraz naprawy systemu plików nazywa się fsck. Fsck podobobnie do mkfs uruchamia odpowiedni dla użytego na partycji systemu plików. Program w trybie interaktywnym uruchamiamy wydając polecenie fsck następnie podając nazwę urządzenia.
$ fsck /dev/sdb1
Nie wolno uruchamiać programu na zamontowanym systemie plików, gdyż grozi to utratą danych oraz załamaniem systemu. Inaczej sprawa ma się gdy system plików jest w trybie tylko do odczytu.
W trybie interaktywnym program będzie zwracać raport z kolejnych etapów, jeśli napotka jakiś problem program zapytanie o usunięcie błędu. W wyniku błedów w systemie plików może zdarzyć się, że pewne pliki zostaną pozbawione nazwy (nazwy plików są w uniksach elementami systemu plików). Program kiedy napotka na taki to zostanie on przeniesiony do katalogu lost+found z nazwą odpowiadającą numerowi identyfikacyjnemu z systemu plików (węzła i-node). Rzeczywistą nazwę musimy ustalić samodzielnie na podstawie analizy jego zawartości.
Program e2fsck - właściwy program fsck dla rodziny systemów plików EXT, posiada opcję -p zajmującą się naprawą drobnych błędów. Program zatrzyma się wówczas tylko wtedy gdy napotka poważny błąd. Jeśli mamy podejrzenie, że coś się dzieje z system plików, to możemy sprawdzić system plików bez dokonywania w nim żadnych modyfikacji, korzystając z opcji -n. Co w przypadku uszkodzenia superbloku? Podstawową bazę danych możemy odbudować za pomocą opcji -b po opcji należy podać lokalizację kopii superbloku (numer sektora podawany przez mkfs podczas tworzenia systemu plików). W przypadku gdy zapomnieliśmy spisać te numery, możemy spróbować je odzyskać wydając polecenie mke2fs wraz z opcją -n dla urządzenia. Należy upewnić się, że na pewno użyliśmy tej opcji jej pominięcie sformatuje partycję.
Istnieją przypadki uszkodzeń, które wykraczają po za sferę programową program fsck, nie jednokrotnie pokazał mi, że by się wydawało katastrofę, naprawiał pojedyńczym domyślnym uruchomieniem. Jeśli nasze systemy dyskowe przechowują ważne informacje, to najlepszą ochroną jest kopia zapasowa warto je robić. Lepiej jest wymieć dysk, zainicjalizować dysk i przegrać dane niż liczyć na to, że może coś się uda odzyskać. Może się uda, jakieś szanse istnieją. Jeśli posiadamy kopię, to szanse przekraczają 90% reszta to ich aktualność.
4.2.11. Systemy plików o specjalnym znaczeniu
Nie wszyskie systemy plików służą zapisywaniu informacji na fizycznych nośnikach, nie które z nich mogą służyć jako intefejsy systemowe lub przezentować informacje systemowe. Takimi systemami są:
- proc - system montowany w katalogu /proc, Zawiera katalogi odpowiadające każdemu z procesów w systemie ich nazwy pochodzą od PID-u procesu. Pliki opisują różne aspekty procesów. Na Linuksach katalog ten przechowuje wiele dodatkowych informacji o jądrze systemu i sprzecie.
- sysfs - system montowany w katalogu /sys. Omawiany był w poprzednim rozdziale.
- tmpfs - montowany w katalogu /run oraz innych miejsach. Za pomocą tego systemu możemy przekształcić pamięć systemową w coś w rodzaju przestrzeni dyskowej, za pomocą dwóch paramerów (size oraz nr_block) możemy określić jego wielkość podczas montowania. Nie należy przesadzać z zapisem danych do tego systemu, możemy doprowadzić do braku pamięci i załamania systemu.
4.3. Przestrzeń wymiany
Za pomocą przestrzeni na dysku jesteśmy wstanie powiększyć ilość użytkowej pamięci operacyjnej. System będzie automatycznie przenosić strony pamięci (obszary) na dysk i z dysku, wykorzystująca tzw. pamięć wirtualną. Operacja przenoszenia stron pamięci na dysk i z powrotem nosi nazwę wymiany (ang. swapping), ponieważ polega na wymianie nieaktywnych stron w pamięci z aktywowany zanajdującymi się aktualnie na dysku. Przestrzeń, w której zapisywane są strony pamięci nazywa się przestrzenią wymiany.
4.3.1. Wykorzystanie partycji jako przestrzeni wymiany.
Wykorzystanie partycji jako przestrzeni wymiany jest standardową procedurą wykonywaną podczas instalacji systemu. Wiele dystrybucji zwraca uwagę na to, gdy brakuje partycji wymiany. Partycja wymiany może nie być nigdy wykorzystana, jednak chroni system przed załamianiem gdy zaczyna brakować pamięci RAM.
Podczas modyfikowania tablicy partycji, utworzyliśmy dysk logiczny, który posłuży jako przestrzeń wymiany. Przestrzeń wymiany należy sformatować, tak jak każdą partycje, jednak już nie za pomocą polecenia mkfs, ale mkswap podając nazwę urządzenia jako parametr.
$ sudo mkswap /dev/sda5
Partycję wymiany możemy montować automatycznie w systemie za pomocą wpisu w pliku /etc/fstab. Poniżej znajduje się wpis, który można wykorzystać w instalacji. Zgaduje jednak, ża zainstalowana przez nas dystrybucja Linuksa do Mint, Ubuntu lub Kali więc raczej taki wpis znajduje się już tym pliku.
UUID="..." none swap sw 0 0
Przestrzeń wymiany możemy włączać oraz wyłączać na żądanie za pomocą poleceń swapon oraz swapoff podając urządzenie lub plik jako argument.
4.3.2. Wykorzystanie pliku jako przestrzeni wymiany
Jeśli nie mamy dostępnego wolnego miejsca na dysku, możemy wówczas stworzyć plik, który będzie służyć nam za przestrzeń wymiany. Aktywujemy go, wówczas gdy ilość wolnej pamięci RAM, będzie niebezpieczenie niska. Aby utworzyć taki plik, musi on posiadać żądaną przez nas wielkość. Najprościej zapisać do niego określoną liczbę zer z strumienia.
$ sudo dd if=/dev/zero bs=1M of=swap.img count=2048
Polecenie to zapisze dwa gigabajty zer do pliku
swap.img
. Taki plik będziemy mogli
potraktować jak dysk lub partycję. Chciałbym tutaj również zaznaczyć,
że w tym momencie spotykamy się w praktyce z jedną z fundamentalnych
zasad Uniksów otóż "Wszystko jest plikiem".
Taki plik pozostaje jeszcze sformatować jako partycję wymiany.
$ sudo mkswap swap.img
Przygotowaną w ten sposób przestrzeń możemy uruchomić za pomocą polecenia swapon.
4.3.3. Jak dużej przestrzeni wymiany potrzeuje?
Odpowiedź na to pytanie nie jest trudna do sformułowania. Otóż powszechnie przyjeło się, że przestrzeń wymiany powinna mieć wielkość dwukrotności zainstalowanej pamięci RAM. Biorąc pod uwagę to, że obecnie jesteśmy w posiadaniu dysków o bardzo dużej pojemności to jest to nawet śmieszna wartość, jednakże to przekonanie poważnie podważyło upowszechnienie się dysków SSD, które już dużych wartości nie muszą mieć. Weźmy sprzęt wbudowany (tzw. embedded) lub sprzęt mobilny, nie mówię tu o telefonach, ale np. chrombookach czy rozwiązaniach typu HP Stream. To tym przypadku przestrzeni dyskowej może być za mało jak na 8GB swapu. Weźmy również pod uwagę fakt iż prawdopodbnie nie skorzystamy z przestrzeni wymiany używając Linuksa na współczesnym sprzęcie. Więc wydaje mi się, że najbardziej optymalną wielkości swapu będzie 1GB. Na poparcie dodam że wiele instalatorów właśnie tyle alokuje podczas automatycznego partycjonowania.
4.4. Przyszłość systemów plików
Jedną z zauważalnych zmian w komputerach jest odejście powoli od dysków talerzowych na rzecz dysków SSD, więc możemy spodziewać się systemów domyślnie zoptymalizowanych pod kątem ich pełnego, właściwego wykorzystania. Zauważymy lub już jesteśmy świadkami odchodzenia od rodziny systemu plików EXT, na rzecz takich systemów jak brtfs, który obecnie jest domyślnym systemem dla takich dystrybucji jak Fedora. Alternatywą dla tego systemu będzie system plików xfs. Ich porównanie możemy znaleźć w internecie.
5. Uruchamianie jądra Linux
W tym rodziale rozpoczeniemy przyglądanie się procedurze uruchamiania systemu operacyjnego, z czego ten fragment materiału poświęcimy na procedurę uruchamiania jądra, programy rozruchowe oraz praktyczną konfigurację najpopularniejszego programu rozruchowego jakim nie wątpliwie jest GRUB.
Procedura uruchamiania systemu wygląda w następujący sposób:
- System BIOS lub firmware (w przypadku UEFI) ładuje program rozruchowy z dysku i uruchamia go.
- Program rozruchowy szuka na dysku obrazu jądra, następnie ładuje go do pamięci i uruchamia.
- Jądro inicjuje wszystkie urządzenia wraz ze sterownikami
- Jądro montuje partycję z katalogiem głównym.
- Jądro uruchamia program o nazwie init, proces tego programu zawsze ma PID o wartości 1. Od momentu startu procesu typu init, rozpoczyna się uruchamianie przestrzeni użytkownika.
- Za pomocą programu init uruchamiane są pozostałe elementy systemu (różnego rodzaju usługi).
- Na koniec uruchamiany jest proces pozwalający się zalogować do systemu.
W tym rozdziale skupimy się na punktach tej listy od 2 do 4. Pozostałe z nich, po za pierwszym (pierwszy wykracza po za ramy merytoryczne, tego materiału) omówimy w następnym rozdziale.
Niestety możliwość identyfikowania poszczególnych etapów uruchamiania systemu jest osiągalna w zależności od dystrybucji. Te przeznaczone na desktopy, ukrywają wiele informacji na temat pierwszych etapów rozruchu systemu pod graficznymi ekranami, zawierającymi logo dystrybucji oraz pasek postępu. Jeśli spotkamy się z takim ekranem, to wówczas możemy naciśnać klawisz ESC, aby wyświetlić komunikaty wypisywane podczas uruchamiania systemu. Umiejętość identyfikacji poszczególnych etapów może pomoć w ewentualnych problemach podczas rozruchu.
5.1. Komunikaty rozruchowe
Większość systemów uniksopodobnych generuje wiele komunikatów diagnostycznych. Część z nich pochodzi o samego jądra, pożniej pojawiają się komunikaty z poźniejszych etapów uruchamiania. Komunikaty te nie są zbyt przyjazne zwykłemu użytkownikowi, a dystrybuje dążą do tego aby być jak najbardzie przyjazne dla użytkownikowi nietechnicznemu, dlatego też ukrywają je za wyżej wspomnianymi ekranami, drugą ważną rzeczą jest ciągły rozwój jądra oraz sprzętu przez to systemy uruchamiają się tak szybko, że nawet mając te komunikaty przed oczami zdołalibyśmy za nimi nadąrzyć.
Na szczęście są one zapisywane w plikach dziennika w katalogu /var/log oraz nie tylko, poniżej znajduje się lista miejsc, w których możemy szukać komunikatów rozruchowych.
- Pliki dziennika systemu, takie jak: /var/log/kern.log, oraz /var/log/messages rownież zawierają komunikaty diagnostyczne jądra oraz nie których usług systemowych.
- Polecenie dmesg przyczym należy pamiętać, że trzeba przepuścić jego wyście przez polecenie less. Danych na pewno będzie więcej niż może pomieścić jeden ekran. Polecenie ty wykorzystuje bufor cykliczny jądra mający ograniczoną wielkość, jednak w nowoczesnych jądrach jest on na tyle duży, że może przechowywać komunikaty rozruchowe przez dłuższy czas.
Nie wszystkie etapy są uwzględnione w wyżej wymienionych miejscach, część z nich może być wypisywana jedynie na konsole przez co przepada bezpowrotnie. Programy typu init nowszej generacji mogą przechwytywać te komunikaty a następnie zapisywać je za pomocą swoich rozwiązań protokołowania (prowadzenia plików dziennika).
5.2. Inicjowanie jądra i opcje rozruchu
Podczas rozruchu systemu jądro uruchamiane jest następującej kolejności.
- Sprawdzenie procesora
- Sprawdzenie pamięci
- Rozpoznawanie magistrali urządzeń
- Rozpoznawanie urządzeń
- Konfigurowanie uzupełniających podsystemów jądra (sieci itp.)
- Montowanie partycji z katalogiem głównym
- Uruchomienie przestrzeni użytkownika
Jednym z etapów inicjacji jądra jest montowanie katalogu głównego. Generalnie to nic w tym nazwyczajnego oczywiście gdy potrzebne do tego komponenty są wbudowane w jądro. Jeśli jednak te modułu są w postaci odrębnych modułów, może wówczas zajść potrzeba załadowania ich przed zamontowaniem głównego katalogu. Tym właśnie zajmuje się początkowych system plików w pamięci RAM - initramfs.
Zakończenie inicjacji i przekazanie uruchamiania procesu init możemy zaobserować w komunikatach diagnstycznych szukając poniższej linii.
Freeing unused kernel memory: ... freed
W tym momencie jądro zwalnia zaalokowaną nie używaną pamięć.
5.3. Parametry jądra
Parametry jądra pozwajają na określenie jego zachowania, na przykład ilość komunikatów diagnostycznych lub podają opcje właściwe dla sterowników urządzeń. Parametry jądra użyte przy jego uruchamianiu dostępne są pliku /proc/cmdline.
BOOT_IMAGE=/vmlinuz-4.19.0-19-amd64 root=UUID=59382884-accb-4106-9d25-44d1ba914530 ro quiet
Na parametry mogą składać się pojedyńcze słowa jak np.
ro
czy quiet
lub opcje w formacje klucz=wartość. Opcja root
jest najważniejszą opcją, ponieważ bez niej jądro nie będzie mogło
odnaleźć plików programu typu init i uruchomić go. W
większości dystrybucji będzie UUID systemu plików zawierającego
katalog główny.
Warto zwrócić uwagę również na opcję ro
,
która nakazuje jądru zamontować system plików w trybie tylko do
odczytu. Ta czynność umożliwi bezpieczene sprawdzenie systemu plików
przez program fsck.
Jeśli jądro nie rozumie jakiegoś parametru to zostanie on przezkazany do programu init. Przykładem jest wartość -s, który nakaże uruchomić przestrzeń użytkownika w trybie pojedyńczego użytkownika.
5.4. Programy rozruchowe
Zadaniem programu rozruchowego jest załadowanie jądra do pamięci i uruchomienie go z odpowiednimi parametrami. Dla linuksa dostępnych jest wiele bootloaderów, poniżej znajduje się przedstawiająca je lista.
- GRUB - standardowy program rozruchowy większości dystrybucji Linuksa.
- LILO - jeden z pierwszych programów, obecnie został zastąpiony GRUB-em, jednak dalej są dystrybucje takie jak Slackware (trzecia najstarsza, funkcjonująca do tej pory dystrybucja), które go używają.
- SYSLINUX - można go skonfigurować, tak aby współpracował z wieloma różnymi systemami. Można go spotkać w systemach wbudowanych, a jego pochodna ISOLINUX występuje na obrazach płyt.
- coreboot - zamiennik systemu BIOS komputera, coreboot charkteryzuje się wysoką wydajnością. Stosowany jest w odblokowanych chromebookach.
- Linux Kernel EFISTUB - moduł jądra pozwalający na załadowanie jądra bezpośrednio z partycji systemowej EFI/UEFI.
Z racji tego iż, duża liczba dystrybucji, korzysta z GRUB. To jego omówimy sobie pod względem praktycznym. Coreboot stosuje się jako zamiennik BIOS-u. A SYSLINUX raczej ma zastosowanie specjalistyczne wykraczające po za ramy tego materiału.
Aby program rozruchowy mógł załadować do pamięci jądro systemu, musi uzyskać dostęp do dysku. Nie posiadając żadnych sterowników program rozruchowy uzyskuje dostęp do dysku na poziomie BIOS-u, za pomocą adresowania Linear Block Addressing mimo bardzo niskiej wydajności zapewnia ono uniwersalny dostęp do dysku.
5.4.1. Linux i Secure Boot
Na nowych komputerach korzystających z UEFI, mogliśmy spotkać się przypadkiem, gdy komputer odmówił uruchomienia z płyty czy też z pamięci flash. Wiele z nich wyświetlało monit o tym, że włączona jest opcja bezpiecznego rozruchu. Opcja ta wymaga podpisu przez zaufaną organizację programu rozruchowego, aby mógł on być uruchomiony. Microsoft w ramach dystrybucji systemu Windows 8 wymógł na producentach sprzętu domyślne włącznie tej opcji. Przyczyny nie są mi znane, chciaż podejrzewam chęć utrudnienia instalacji innego systemu nawet Windows 7. Oczywiście tę opcję możemy wyłączyć odszukując opcję w panelu sterowania fimwarem (w BIOSie). Jeśli obawiamy się o nasze bezpieczeństwo, to wyłączenie tej opcji potrzebne jest wyłącznie na czas instalacji, ponieważ najnowsze wersje programów rozruchowych są już podpisane, więc będą zostaną zaaprobowane przez tę opcję.
5.5. Praktyczne użycie programu rozruchowego GRUB
GRUB jest obecnie najszerzej wykorzystywanym bootloaderem, dlatego też warto poznać podstawy jego obsługi, bez owijania w bawełnę, bez wertowania kolejnych kart książek z teorią na temat tego programu rozruchowego.
5.5.1. Pierwszy kontakt z GRUBm
Po przejściu procedur testowych firmware naszego komputera bez znaczenia czy jest to BIOS czy UEFI, pokaże nam się tabela z której będziemy mogli wybrać jedną z opcji. Podobna do tej na poniższym rysunku.
Przedstawiona na rysunku tabela może różnić się wyglądem ale
funkcjonalność pozostaje taka sama. Na samym dole jest napisane
Wyróżniony wpis zostanie wykonany
automatycznie za 5s.
. Wyróżniony wpis to ten zaznaczony
na biało, a po pięciu sekundach zostanie on uruchomiony, ładując
system przy standardowych ustawieniach. Za pomocą strzałek możemy
poruszać się pod tabeli dokonując wyboru interesującego nas wpisu.
Wpis zawierający napis
Opcje zaawansowane dla systemu...
jest podmenu zwierającym (najczęściej) wpisy ładujące system z
poprzednimi wersjami jądra. Wpis w tabeli możemy edytować
wybierając go strzałką następnie naciskając klawisz e. GRUB
daje nam możliwość załadowani systemu z własnego wiersz poleceń. Ta
opcja przeznaczona jest bardziej zaawansowanych czynności, takich
jak na przykład diagnostyka konfiguracji GRUB-a. W tym materiale
nie będziemy się jednak zajmować wierszem polecenia. Jeśli ruszymy
się chociaż w menu
to odliczanie zostanie przerwane, więc aby załadować system trzeba
wybrać wpis. Naciśnięcie jakiego kolwiek klawisza w menu spowoduje
przerwanie odliczania.
5.5.2. Instalacja GRUB w trybie BIOS
Instalacja GRUB w tryb BIOS, jest banalnie prosta. Wystarczy użyć dwóch poleceń. Zazwyczaj korzystając z mainstreamowych dystrybucji przeznaczonych dla użytkowników desktopowowych, nigdy nie będziemy musieli tego robić, ponieważ zrobi to za nas instalator. Natomiast są dwa scenariusze, kiedy niezbędna będzie ponowna instalacja programu GRUB.
- Instalacja bardziej zaawansowanych dystrybucji takich jak Gentoo czy Arch Linux
- Przenoszenie systemu z jednego komputera na drugi za pomocą programów do obrazowania, jak Norton Ghost czy Paragon Backup.
Pierwsze polecenie jest takie same dla każdej dystrybucji. Jako argument podajemy dysk (urządzenie główne - np. sda, sdb itp.). Polecenie musi zapisać dane bezpośrednio na urządzeniu, więc niezbędne będą uprawnienia administratora.
# grub-install /dev/sdX
Drugie polecenie zależy już od dystrybucji. Możemy wówczas spotkać takie polecenie jak:
Debian / Ubuntu i pochodne: # update-grub Arch Linux i pochodne: # grub-mkconfig -o /boot/grub/grub.cfg
Drugie polecenie jest odpowiedzialne za wygenerowanie pliku konfiguracyjnego. W przypadku GRUB jest to normalne, ponieważ ze względu na jego skomplikowanie przygotowanie plików składowych (będzie o tym później) spoczywa w rękach twórców samego GRUB-a lub twórców dystrybucji. Użytkownik końcowy otrzymuje gotowe polecenie, które stworzy taki plik konfiguracyjny za niego. Polecenie z Arch Linux, jest dłuższe ale pochodzi bezpośrednio z pakietu. Natomiast w przypadku Debiana i pochodnych mamy dostępne narzędzie przygotowane przez dystrybucję, jest ono bez obsługowe i uruchamia program do poszukiwania innych systemów operacyjnych na innych dyskach podłączonych do komputera. Na Debianie i pochodnych na pewno dostępne jest również to drugie polecenie, jednak lepiej skorzystać z polecenia dedykowanego dla naszej dystrybucji. Ponieważ to tak naprawdę jej twórcy zajmują się przygotowanie GRUB-a do użycia co nie jest takie proste.
5.5.3. Instalacja GRUB w trybie UEFI
Instalacja w trybie UEFI również składa się dwóch poleceń jednak samo polecenie instalacyjne jest cieco dłuższe od tego z trybu BIOS. Wymaga ona również więcej zachodu niż w trybie BIOS oraz może zakończyć się niepowodzeniem. Tak się zdarza to, przy nie których specyficznych sprzętach.
Polecenie instalacyjne wygląda w następująco:
# grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=debian
Instalacja GRUB w trybie uefi wymaga, aby podać docelowy system,
w tym przypadku jest x86_64-efi
,
w trybie BIOS, nie trzeba było podawać docelowego systemu gdyż
w przypadku BIOS cel i386-pc jest celem domyślnym, kolejny
argumentem jest podanie punktu montowania partycji efi.
Taka partycja jest zazwyczaj montowana albo bezpośrednio w głównym
katalogu - /efi lub wewnątrz katalogu /boot -
/boot/efi. Ostatnim argumentem jest
--bootload-id
i tutaj panuje pewnego
rodzaju niewiadoma. Bo jeśli korzystamy instalujemy program
rozruchowy
pod Arch Linuxem, to bootloader-id to GRUB.
z kolei pod Debianem musi być to debian. Te nazwy
wynikają z faktu potrzeby podpisania programu rozruchowego aby
mógł on być uruchamiany z włączoną opcją bezpiecznego rozruchu. Warto
dodać, że to co podamy w tej opcji będzie wyświetlane w
boot menu komputera. Jeśli chcemy użyć jakiejś
niestandardowej nazwy i nie za bardzo przejmujemy się
secure bootem,
to wówczas możemy użyć opcji --no-uefi-secure-boot.
Do w pełni zainstalowanego GRUB-a potrzebujemy tylko pliku konfiguracyjnego. Do tego celu możemy wykorzystać polecenia podane w punkcie odnośnie instalacji GRUB w trybie BIOS.
5.5.4. Zmiana koleności w menu GRUB
Aby zamknąć już temat GRUB-a, przjedziemy do ostatniego zagadnienia. Rozważmy taki przypadek. Mamy jeden komputer w domu, z którego nie korzystamy tylko my. Mamy pozwolenie na wydzielenie pozostałej wolnej części dysku (w duży uproszczemiu) i obok Windows 10 chcemy zainstalować jakąś dystrybucje Linuksa. Załóżmy, że instalacja się powiodła. Niestety mamy problem, gdyż współużytkownicy komputera narzekają, że jak uruchamia się komputer to uruchamia się Linux a nie Windows. Uruchamiając komputer widzisz, że początkowym i domyślnym wyborem w menu GRUB-a jest Linux, a Windows z kolei znajduje się na samym końcu listy.
Pierwsza myśl jest przychodzi nam do głowy to ręczna edycja pliku GRUB-a. Na pewno jest jakieś rozwiązanie. Zatem wyedytowaliśmy plik zapisaliśmy zmiany i póki co jest spokój. Wszyscy zadowoleni, korzystają z komputera. Któregoś dnia siedząc przy komputerze i korzystając z naszej dystrybucji, zostajemy poinformowani o tym, że są nowe aktualizacje. Instalujemy je zatem. Aktualizacje się zainstalowały, zauważyliśmy że była również aktualizacja jądra. Dobra, wyłączamy komputer. Następnego dnia gdy nasz współużytkownik komputera chce skorzystać z niego, znów mu się uruchamia Linux a nie Windows. Jaki z tego morał? Instalacja nowego jądra wymaga z aktualizowania pliku konfiguracjnego GRUB-a, aby można było korzystać ze świerzo zainstalowanego jądra automatycznie po ponownym uruchomieniu komputera, więc nie należy ręcznie zmieniać ręcznie pliku konfiguracjnego GRUB-a ponieważ aktualizacja czy to jądra czy samego programu rozruchowego nadpisze te zmiany.
Sposobów wykonania tej czynności jest kilka, ja znam jedną. Jak wiemy plik konfiguracjny jest tworzony z plików składowych. Pliki te znajdują się w katalogu /etc/grub.d, oto listing tych plików z mojego komputera:
00_header 05_debian_theme 10_linux 20_linux_xen 30_os-prober 30_uefi-firmware 40_custom 41_custom README
Jeśli obserwowaliśmy generowanie pliku konfiguracjnego GRUB-a, to już
powinniśmy znaleźć potrzebny nam plik. A jeśli nie to przyjrzyjmy się
nazwom tych plików. Mamy
header, debian_theme, linux, linux-xen
i os-prober
. OS to akronim
od Operating System, z kolei prober oznacza to
samo co der Untersucher po niemiecku, czyli kontroler/badacz.
Czyli chodzi o kontrolera systemów operacyjnych. Jeśli wpiszemy
os-p w wierszu polecenia i naciśniemy tab, dowiemy się, że
istnieje nawet takie polecenie, wyszukaniu strony podręcznika dla
wiem że jest narzędzie odpowiedzialne za poszukiwanie innych systemów
operacyjnych na wszystkich dyskach komputera. Więc znamy już część
konfiguracji GRUB-a odpowiedzialną za Windows. Teraz jak zmienić
kolejność? Otóż przed każdą z tych nazw stoi liczba, wystarczy
zmienić nazwę, tak aby ten plik znajdował się przed plikiem
10_linux
.
$ sudo mv /etc/grub.d/30_os-prober /etc/grub.d/09_os-prober
Teraz trzeba już tylko wygenerować nowy plik GRUB-a. Rozwiązanie powinno być odporne aktualizacje. Nie jest to może najpopularniejsze rozwiązanie, ale działa.
5.6. Wykorzystanie rEFInd jako menedżera rozruchu
Instalacja GRUB-a w trybie UEFI może się nie powieść, gdyż jednym z jego etapów jest zapisanie w pamięci firmware-u informacji o nowym programie rozruchowym o nazwie podanej podanej w parametrze bootloader-id. Z błedem jak ja się spotkałem by:
Could not prepare boot variable: No space left on device
Nie które firmware mają ograniczoną ilość pamięci przechowywującej wskazania programów rozruchowych. Mimo usunięcia jednej ze zmiennych problem dalej występował. Usunięcie wszystkich spowodowało tak jakby uziemienie komputera, ponieważ nie był on wstanie uruchomić się z żadnego podpiętego dysku. Przełączenie trybu na BIOS, na tym sprzęcie nie było możliwe. Na sprzęcie tej samej klasy oraz tego samego producenta, ale nowszym spotkałem się z problemem z takim problemem, iż system zainstalował się poprawnie nawet GRUB w trybi UEFI, ale system nie był wstanie wystartować z wbudowanej pamięci, ani z system zainstalowanym w trybie UEFI ani w trybie BIOS.
Rozwiązanie może i jest proste, niestety może nie za bardzo estetyczne oraz wymaga użycia dodatkowego pendrive-a. Pamięć USB nie musi być duża, wystarczy 1GB. Polega ono na użyciu odrębnego mendżera rozruchu jakim jest rEFInd. Program ten przeszukuje dyski w poszukiwaniu systemów operacyjnych i uruchamia je.
Sama dystrybucja przy wykorzystaniu programu rEFInd może być zainstalowana trybie BIOS. W środowisku LiveCD instalujemy pakiet refind. Podczas instalacji zostanie nam wyświetlony monit z pytaniem czy zainstalować rEFInd na partycji ESP. Wybieramy opcję No.
# apt update # apt install refind
Po zainstalowaniu, tworzymy na dysku USB 1 jedną partycję pod system plików FAT32, następnie należy ją sformatować.
# dd if=/dev/zero bs=1M of=/dev/sdX count=1 # echo ',,b,' | sfdisk /dev/sdX # mkfs.vfat -F32 /dev/sdXY
W powyższym przykładzie X
to litera
porządkowa dysku, z kolei Y
to
liczba partycji. Partycji nie musimy montować. Poniższe polecenie
zainstaluje na przygotowanym dysku program rEFInd.
# refind-install --usedefault /dev/sdXY --alldrivers
Po zainstalowaniu programu, możemy zrestartować komputer a następnie w ustawienia UEFI, ustawić kolejkę rozruchu tak, aby komputer startował z pendrive-a, na którym zainstalowaliśmy rEFInd. W menu powinien pojawić się wpis z Linuksem zainstalowanym naszym dysku.
6. Uruchamianie przestrzeni użytkownika
Istotnym momentem podczas startu systemu operacjnego jest uruchomienie przez jądro procesu init oznacza to, że pamięć oraz procesor są gotowe normalnej pracy. Po za tym uruchomie tego programu może przynieść również korzyść dydaktyczną, pozwalając obserwować montowanie różnych komponentów w gotowy do pracy system. Budowa programów typu init jest bardzie modułowa, więc jeśli chcielibyśmy zmienić coś w tym procesie (uruchamiania przestrzeni użytkownika) nie potrzebujemy umiejętności niskopoziomowego programowania oraz trzymania się ściśle określonej ścieżki, jak ma to miejsce w przypadku jądra. Uruchamianie przestrzeni użytkownika zostało przedstawione na poniższej liście kroków:
- Jądro uruchamia program typu init.
- Uruchamiane są usługi niskiego poziomu takie jak udevd czy syslogd.
- Sieć zostaje skonfigurowana.
- Uruchamiane zostają pozostałe usługi jak (cron, cups, itp.)
- Startują aplikacje wyskokiego poziomu, ekrany logowania oraz środowiska graficzne.
6.1. Proces init
Głównym zadaniem procesu typu init jest uruchamianie, zatrzymywanie i ponowne uruchamianie procesów istotnych dla pracy całego systemu, chciaż jego najnowsze implementacje mają o wiele więcej zadań, to jest to program jak każdy inny. Możemy go znaleźć w katalogu /sbin. W dystrybucja Linuksa ma zastosowanie kilka różnych implementacji procesu init. Na poniższej liście znajdują się te wciąż używane.
- System V init - tradycyjna wersja tego procesu obecnie została wyparta z użycia w dystrybucjach głównego nurtu, co przez nie których uznane za zły zabieg. Dalej rozwiajane są dystrybucje, które są forkami tych głównych, ale opierają się, nie tylko o sysvinit ale dają możliwość wyboru innego procesu typu init niż systemd.
- systemd - najnowsza implementacja tego procesu, wykorzystywana w głównych dystrybucjach.
- runit - alternatywa dla systemd, ma zastoswanie nie tylko dla dystrybucji Linuksa ale innych systemów zgodnych ze standardem POSIX. Jego działanie opiera się o 3 etapy. Wykorzystywany jest w dystrybucjach, takich jak AntiX oraz Void Linux.
- OpenRC - alternatywa dla systemd, stosowana przez takie dystrybucje jak Gentoo oraz Alpine Linux. Ma on podobne możliwości do systemd.
6.2. Poziomy uruchomienia
Poziomem uruchomienia nazywamy stan maszyny, w którym na dany moment uruchomione są wybrane komponenty. Są one oznaczne liczbą od 0 do 6. Podczas pracy systemu, większość czasu spędzamy na tylko na jednym poziomie uruchomienia, z momencie zamykania systemu lub uruchamiania go ponownie przełączamy się na inny poziom odpowiedzialny za zatrzymanie pracy jądra oraz poprawne zakończenie pracy usług. Na poziomach uruchomienia bazuje tradycjny sysvinit.
Za pomocą poniższego polecenia możemy, sprawdzić na jakim poziomie uruchomienia się znajdujemy:
$ who -r
Polecenie zwraca również moment załączenia wyświetlanego poziomu.
Poziomy uruchomienia służą okreslaniu stanu systemu operacyjnego. Może on być stanie uruchamiania, zamykania, trybie konsoli (serwery, instalacje bez środowiska graficznego), trybie awaryjny (tryb pojedyńczego użytkownika, będzie o tym pod koniec rozdziału). 5 poziom uruchomienia oznacza najczęściej w pełnii uruchomiony system wraz z trybem graficznym.
Dzisiaj poziomy uruchomienia są domeną programu sysvinit. Dystrybucje z systemd wykorzystują je, aby zapewnić obsługę z usług, które nadal korzystają ze skryptów sysvinit.
6.3. Rozpoznawanie programu typu init.
Identyfikacja programu typu init polega, na przeczytaniu oprogramowania oferowanego przez dystrybucje. Jeśli program typu init nie jest wyszczególniony, możemy przyjąć ze jest to systemd. Wiele dystrybucji posiada w swoich cechach wymienioną informacje o tym że używa innego programu typu init niż systemd.
6.4. Wprowadzenie do wybranych programów typu init
Ze względu na fakt, jak ograniczonych ram tego materiału. Nie będę szczegółowo zagłebiał się w tematykę omawianych tutaj programów typu init. Opisze ich zalety, na czym opiera się ich działanie, jakich plików używają oraz co najważniejsze dla nas na tym etapie w jaki sposób możemy zarządzać usługami przy użyciu tych programów. Pierwszym jaki procesem typu init jaki poruszę z racji popularności jest systemd.
6.4.1. Systemd
Systemd wykonując swoje zadania osiąga cel. Cel jest defiowany przez nas wraz z wszystkimi zależnościami (wymaganiami) oraz z góry ustalonym momentem realizacji tego celu. Następnie system rozwiązuje wszystkie zależności oraz wykonuje postawione przed nim zadanie. Wynika z tego jedna z jego cech, możemy opoźnić np. start usługi do momentu gdy będzie ona niezbędna.
Poza obsługą usług, której wszyscy oczekują od programu init, systemd stara się z integrować ze sobą wiele klasycznych usług takich jak cron czy inetd.
Podczas uruchamiania usług systemd nie kieruje się żadną kolejnością, co więcej większość jego konfiguracji stara unikać jakiej kolwiek sekwencyjności, nawet pod czas spełniania zależności. Takie działanie pozwala na zachowanie dużej dozy elastyczności w procesie uruchamiania systemu.
W zanadrzu swoich możliwości systemd może: montować systemy plików, monitorować gniazda sieciowe czy uruchamiać zegary. Takie rodzaju funkcje w tej impementacji programu typu init nazywane są jednostkami (ang. units). A operacja uruchomienia takiej jednostki nazywana jest aktywowanie. Typ jednostek systemd dostępne są na stronie podręcznika: systemd(1) (1 w nawiasie to numer rozdziału).
Każda jednostka może mieć zależności wobec innej jednostki. Może wymagać lub chcieć jej działania na potrzeby zadania, które sama realizuje. Zależności są definiowane w plikach jednostek dostępnych w dwóch miejscach, w konfiguracji globalnej skierowanej do całego systemu - /usr/lib/systemd/system oraz w definicjach lokalnych - /etc/systemd/system. Jeśli będziemy musieli wprowadzić jakieś zmiany w konfiguracji to zalecanym miejscem jest katalog definicji lokalnych. Rozwiązywanie zależności najczęściej polega na uruchomieniu samej jednostki, wobec której ważna dla nas jednostka jest zależna. Liczy się czy natomiast czy daną jednostkę udało się aktywować lub czy jednostka jest już uruchomiona w trakcie uruchamiania naszej jednostki, tego typu uwarunkowania definiowane przez typy zależności. Poza zależnościami wobec innych jednostek, mogą występować również zależności bazujące na stanie (istnieje, nie istnieje, jest niepusty) elementów takich jak ścieżka, katalog czy plik.
Uruchomienie jednostki nazywane jest aktywowaniem. Dlaczego nie włączeniem? Otóż, aby uruchomić należy ją aktywować poniższym poleceniem:
$ sudo systemctl start unit.service
To czy każdą jednostkę możemy tak sobie uruchomić zależy od jej zależności. Zależności można definiować na odwrót, użwając sekcji [Install], a wewnątrz niej typów zależności: RequiredBy oraz WantedBy, najczęściej wykorzystywana jest ta druga opcja. Za pomocą tej sekcji oraz wymienionych zależności, jednostki instalowane są w celach. Większość usług wykorzystywanych w linuksach, posiadają zależność WantedBy ustawioną na cel multi-user.target. Jest to spowodowane tym, że samo istnienie pliku jednostki może powodować jej aktywacje, co w przypadku usług sieciowych nie jest porządane. Zatem włączenie jednostki, jest w przypadku systemd równoznaczne z włączeniem jej do zależności jednostki zapisanej w zależności odwrotnej (cele w systemd, też są jednostkami ale ich rola raczej ogranicza się do grupowanie różnego rodzaju innych jednostek). Wydaje mi się, że wszystko rozjaśni się gdy zobaczymy polecenie służące do włączania.
$ sudo systemctl enable unit.service
Dezaktywacja oraz wyłaczenie jednostki to kolejno:
#Dezaktywacja: $ sudo systemctl stop unit.service #Wyłączenie: $ sudo systemctl disable unit.service
Do obsługi systemd używamy pojedyńczego polecenia
systemctl
, ma ono bardzo wiele opcji
jednak na poziomie podstawowym wystarczym to co napisałem powyżej
oraz sprawdzenie stanu jednostki poleceniem:
$ sudo systemctl status unit.service
Dawniej w czasch powszechnego panowania sysvinit oraz obecnie w dystrybucja wykorzystujących ten rodzaj programu typu init, gdy usługa definiowała zasób, z którego korzystały inne usługi, to musiały one zostać opóźnione do momentu uruchomienia usługi macierzytej oraz udostępnienia przez nią zasobu. Za pomocą systemd możemy na podstawie dokumentacji usługi utworzyć jednostkę zasobu. Taka jednostka zostanie uruchomiona w momencie aktywowania jednostki macierzystej usługi. Dzięki temu inne usługi korzystające z tego zasobu będą mogły zostać aktywowane w tym samym czasie co ta usługa. Usługi nie zrócą żadnego błędu, ponieważ zasób jest dostępny. W gdy coś spróbuje uzyskać dostęp do tego zasobu, to zostanie zablokowane przez systemd do momentu pełnego uruchomienia usługi i przekazania jej kontroli nad zasobem. Jeśli podczas próby dostępu do zasobu napłyną jakieś dane to zostaną one zbuforowane i przekazne do usługi, gdy przejmie ona pieczę na zasobem. Cała ta procedura pozwala na znaczne przyspieszenie uruchamiania systemu.
Systemd zapewnia pewien stopień zgodności z tradycyjnym sysvinit, dla usług nie wspierających plików jednostek. Ta funkcjonalność jest na tyle rozwinięta, że pozwala na podobne zarządzanie taką usługą, jakoby miała ona plik jednostki.
Implementacja procesu typu init jaką jest systemd jest na prawdę dość sporym tematem. Więc zostanie on poruszony ponownie na tej stronie.
6.4.2. Proces typu init w stylu System V
Klasyczny proces typu init jakim jest sysvinit, opiera się o poziomy uruchomienia oraz wykonywane w nich polecenia. Polecenia te opierają się na sekwencyjny wykonywaniu skryptów umieszczanych w odpowiednich katalogach. Najważniejszym plikem jest w tym przypadku plik /etc/inittab, w jego zawrtości znajdują się wspomniane już polecenia, rozpisane dla poszczególnych poziomów uruchomienia. Polecenie dla 5 poziomu uruchomienia może wyglądać w następujący sposób:
l5:5:wait:/etc/rc.d/rc 5
To polecenie przy wejściu na 5 poziom uruchomi wszystkie skrypty w
katologu /etc/rc5.d, o ile będą miały odpowiednią nazwę.
Słowo wait
spowoduje, że proces
init nie przejdzie na kolejny poziom uruchomieniowy do
momentu zakończenia pracy tego polecenia. Inne akcje
niż wait
mogą
uruchamiać ponownie polecenie po jego zakończeniu czy definiować
co należy zrobić po naciśnięciu kombinacji klawiszy
Ctrl+Alt+Delete, z kolei akcja initdefault
określa domyślny poziom uruchomieniowy dla przestrzeniu użytkownika.
Skrypt zawarte w katalogu rc5.d, który jest akronimem od polecenia run command. Posiadają dość specyficzne nazwy, zaczynają się one od wielkiej litery S lub K. Następny jest numer, na końcu zaś znajduje się nazwa własna. Jeśli wylistujemy ten katalog bardziej szczegółowo, to zauważymy, że te skrypty to tak naprawdę dowiązania symboliczne. Wiele dowiązań w jednym katalogu nazywane jest farmą dowiązań. Te dowiązania zawarte w katalogach rc wskazują na właściwe skrypty znajdujące się w katalogu /etc/init.d.
Wielkie litery na początku nazw dowiązań oznaczają operację podejmowaną na usłudze czy ma ona zostać uruchomiony - S (ang. start), czy jej działanie ma zostać zakończone - K (ang. kill). Te czynności wykonywane są poprzez uruchomienie skryptu z argumentem start lub stop. Numery w nazwach określają miejsce tej czynności w sekwencji uruchomieniowej sysvinit. Usługi niskiego poziomu jak np. syslogd uruchamiane są bardzo wcześnie (mają niskie numery), demony świadczące użytkownikom jakieś usługi zazwyczaj mają numery powyżej 90. Nazwa wskazuje na uruchamianego daemona.
Zatem jeśli nie chcemy, aby jakaś usługa startowała należy wówczas zmienić nazwę dowiązania. Warto jednak pozostawić sobie późniejszą możliwość jej włączenia. Powiedzmy że chcemy wyłączyć daemona httpd jego nazwa to S99httpd, więc najlepiej postawić na początku nazwy znak podkreślenia:
$ mv /etc/rc5.d/S99httpd /etc/rc5.d/_S99httpd
W ten sposób daemon zostanie wyłączony z sekwencji uruchomieniowej. Jeśli chcemy uruchomić/zatrzymać usługę na żądanie, należy uruchomić skrypt z katalogu /etc/init.d z argumentem start lub stop.
#Uruchomienie: $ sudo /etc/init.d/httpd start #Zatrzymanie: $ sudo /etc/init.d/httpd
Wraz z sysvinit i nie tylko, rozprowadzane jest narzędzie, które działa w systemach nie używających już sysvinit. Run-parts, jest to bardzo proste na rzędzie, które uruchamia wszystkie pliki wykonywalne w danym katalogu według ściśle określonego porządku. Implementacja tego narzędzia zależy od dystrybucji. Te bardziej złożone pozwalają na użycie wyrażenia regularnego do określenia porządku ich uruchamiania. Na potrzeby tego materiału wystarczy wiedzieć, że takie narzędzie w ogóle istnieje.
Wspomnieniem o run-parts kończymy wprowadzenie do programów typu init. Omówiłem tylko te dwa, gdyż są w najpowszechniejszym użyciu. Poznając podstawy Linuksa, nie ma co zniechęcać się obszernymi szczegółami. Najważniejsze dla nas na ten moment jest uruchamienia/zatrzymywanie usług oraz włączanie i wyłączanie ich z sekwencji uruchomieniowej. W ramach ćwiczeń możemy wydedukować jak należy włączyć usługę, do sekwencji uruchomieniowej przy procesie init w stylu System V, chociaż nie powinno to zająć więcej niż 30 sekund.
6.5. Wyłączanie systemu
Jedynym prawidłowym sposobem na wyłącznie systemu, jest użycie polecenia shutdown. Wyłączyć system możemy na dwa różne sposoby. Pierwszym z nich jest jego programowe zamknięcie systemu oraz wyłącznie zasilania osiągane przez poniższe polecenie.
$ sudo shutdown -h now
Do wyżej wymienionego celu możemy, użyć polecenia, które może być nieco bardziej powszechne:
$ sudo poweroff
Ma ono działanie identyczne działanie jak polecenie z przykładu powyżej.
Przy poleceniu shutdown
należy
wybrać czy system ma zostać zamknięty, zatrzymany, lub uruchomiony
ponownie. Kolejnym wymaganym argumentem jest czas. Najczęściej
używany jest argument now
co
przy przyjmowanym zapisze czasonym jest +0 minut.
Zapis czasowy możemy określić w minutach jak podałem powyżej lub
lub przy zapisie hh:mm , który pozwala określić konkretną
godzinę zamknięcia systemu.
Innym sposobem na zamknięcie systemu jest ponowne uruchomienie komputera. System będzie musiał zostać poprawnie zamknięty, aby uruchomić komputer ponownie. W tym celu możemy użyć polecenia,
$ sudo shutdown -r now #lub $ sudo reboot
Wyłączenie systemu dla systemd oznacza aktywację jednostek zatrzymywania (systemd, posiada wiele typów jednostek), dla sysvinit przejście z poziomu 5 na 6 lub 0.
6.6. Początkowy system plików w pamięci RAM
initramfs czy initrd, z tych nazw korzysta się zamiennie mimo iż oznaczają coś innego, to odnoszą się do tego samego komponentu, czyli początkowego systemu plików w pamięci RAM. Jest to bardzo proste archiwum przechowujące mini przestrzeń użytkownika z mini katalogiem głównym. Zadaniem initramfs jest stworzenie optymalnego środowiska dla narzędzi pozwalających na załadowanie do jądra zewnętrznych modułów, wśród których może znajdować się sterownik dysku, co pozwoli zamontować już właściwyw katalog główny z dysku i przejść na kolejne etapy uruchamiania systemu.
Wykorzystując nasz system w stopniu podstawowym raczej nie będziemy mieć styczności z initramfs, chyba że używamy bardziej zaawansowanej dystrybucji, która może wymagać załadowania do niego dodatkowych modułów. Takie przypadki będą zazwyczaj opisane w na stronach dokumentacji dystrybucji. Przykładem takiego działania może być szyfrowana partycja z katalogiem głównym, w takich dystrybucjach jak Arch Linux. Do obsługi początkowego systemu w pamięci RAM służa takie polecenia jak mkinitramfs lub update-initramfs.
System plików pamięci RAM może zostać pominięty w momencie gdy, w jądrze znajdują się wszystkie potrzebne mu sterowniki, jednak obecnie nie jest to praktykowane.
Istnieją dystrybucje, których działanie opiera się na initramfs, pliki dystrybucji znajdują się w tym archiwum, a oprogramowanie jest znajduje się wówczas w archiwach squashfs montowanych podczas w odpowiednich miejscach w systemie podczas jego ładowania. Taką dystrybucją jest na przykład TinyCore, którego obraz płyty waży 21 MB.
6.7. Tryb jednego użytkownika
Tryb pojedyńczego użytkownika, jest swojego rodzaju trybem awaryjnym na Linuksie, a jedynym dostępnym użytkownikiem będzie superużytkownik. W tym trybie zostanie załadowane jądro, za montowany katalog główny, a proces typu init zapewni dostęp tylko do niezbędnych dla działania systemu usług, wyeliminowywując tym samym potencjalnie wadliwe komponenty. To środowisko ma za zadanie umożliwić nam naprawę systemu.
Ze względu na to, iż to środowisko może nie zapewnić potrzebnych do takiej naprawy narzędzi, najlepiej jest skorzystać jednak z obrazu LiveCD, który pozwala na korzystanie z systemu bez konieczności jego instalacji. Jednak na podstawie wiedzy zawartej w tym materiale nie sądze, aby można byłoby naprawić system. Dlatego też jeśli zdarzy się awaria systemu, to najlepszym rozwiązaniem na teraz jest zabezpieczenie osobistych danych i przeinstalowanie systemu, chodziaż nie chce nikogo zniechęcić do grzebania w systemie, w ten sposób możemy się wiele nauczyć. Z drugiej strony, ciężko jest aby system przestał działać tak sam z siebie. Dlatego jesli tak się stało, więcej niż raz to warto zgłość problem społeczności za pomocą jednego z kanałów udostepnionych na stronie dystrybucji i zmienić dystrybucje na jakiś czas. Linux to głównie narzędzie do pracy na naszym komputerze, na tym etapie - podstawowym.
7. Konfiguracje systemowe oraz użytkownicy
W tym rozdziale zajmiemy się drobnymi konfigracjami, nie których komponentów systemowych takich jak syslog czy cron zajmiemy się również tematem czasu systemowego na Linuksie. Rozdział zakończymy dodatkową wiedzą, (choć nadal w stopniu podstawowym) na temat użytkowników.
Wszystkie te powyższe zagadnienia łączy jedna rzecz, ich pliki konfiguracyjne znajdują się w katalog /etc i od omówienia tego katalogu zaczniemy.
7.1. Katalog /etc
Jak wiemy z opisu hierarchii systemu plików (katalogu głównego), w katalogu /etc przechowywane są różnego rodzaju konfiguracje, i to nie zależnie do wielkości czy istotności programu w systemie. Kiedyś każdy z programów przechowywał luzem swoją konfigurację tym katalogu. Obecnie jak możemy się przekonać większość zawartości /etc stanowią podkatalogi. Oczywiście wiele plików nadal się w nim znajduje, najczęsciej są to takie pliki jak /etc/fstab czy /etc/passwd lub /etc/shadow służące do przechowywania informacji o użytkownikach. Katalogi w /etc mają nazwy przeważnie odpowiadające nazwom programów, które konfigurują. Wyjątkiem są katalogi z końcówką .d. Pliki konfiguracyjne zostały umieszczone w tych katalogach, aby nie zostały one nadpisane przez aktualizacje pakietów. Obecnie nie ma to już miejsca, a mimo to konfiguracje wielu pakietów są umieszczane w tych katalogach.
Pliki konfiguracyjne, nie których pakietów mogą występować w dwóch różnych wersjach. Pierwsza to jest, ta którą wszyscy znamy czyli katalog /etc taka konfiguracja nazywana jest konfiguracją z możliwością dostosowania. Druga wersją jest konfiguracja bez możliwości dostosowania znajdująca się w katalogu /usr/lib. Oczywiście to jest tylko koncepcja, aby administratorzy zajęli się konfiguracją w /etc a konfiguracje w /usr/lib zostawili opiekunom pakietów (osobom przygotowywującym pakiet oprogramowania dla danej dystrybucji, z wybranym programem) oraz twórcom samej dystrybucji. Możemy zmieniać oczywiście konfiguracje w tym katalogu, jednak trzeba mieć na uwadze dwie rzeczy:
- Trzeba wiedzieć co się robi
- Zmiany w konfiguracji znajdującej w /usr/lib mogą zostać nadpisane przez aktualizacje pakietu.
System Linuks nie jest miejscem, gdzie się cokolwiek komu kolwiek zabrania. Zasady i jakieś regułu wprowadza się po to aby zapewnić względne bezpieczeństwo oraz stabilność dystrybucji czy też ogolnie systemu.
7.2. syslog
Znaczna część komunikatów dignostycznych z różnych komponentów systemowych spływa do protokołowania czy rejestrowania lub prowadzenia plików dziennika. W języku polskim istnieje kilka określeń na to co konkretnie robi usługa syslog, której zadaniem jest nasłuchiwanie na komunikaty diagnostyczne i przekazywanie ich do pliku, na ekran poszczególnych użytkowników lub całkowite zignorowanie. Wszystko zależy od konfiguracji.
Obecnie wykorzystywana jest nowsza wersja syslog - rsyslog. Funkcje tej wersji nie ograniczają się tylko do zapisywania komunikatów diagnostycznych do pliku, program może na przykład przesyłać je do bazy danych. Na tym etapie nie będziemy się jednak tym zajmować, póki co będziemy musieli się zadowolić zwykłymi plikami tekstowymi przechowywanymi w katalogu /var/log. Warto mieć jednak na uwadze fakt, iż nie wszystkie pliki przechowywane w tym katalogu są zarządzane, przez tę usługę. Nie które daemony mogą posiadać swoje sposoby na utrzymanie i prezentowanie użytkownikowi własnych komunikaty diagnostycznych. Więcej informacji na temat jakie logi są przechwytywane przez rsyslog znajduje się w pliku konfiguracyjnym - /etc/rsyslog.conf.
Na konfiguracje składają się tradycyjne reguły oraz dyrektywy dostępne w rozszerzonej wersji rsyslog. Dyrektywy możemy poznać po tym, że rozpoczynają się od symbolu dolara ($). Natomiast reguły konfiguracyne klasycznej wersji protokołu są nieco bardziej złożone.
Reguły protokołu syslog określają sposób przychwytywania komunikatów diagostnycznych oraz docelowe miejsce ich zapisu. Zasady składają się z selektora i akcji, o to kilka z nich.
*.info;mail.none;authpriv.none;cron.none /var/log/messages authpriv.* /var/log/secure,root mail.* -/var/log/maillog *.emerg :omusrmsg:*
Reguły syslog możemy podzielić na dwie części prawą i lewą. Po lewej stronie znajduje się selektor, określający przechwywane dane. Natomiast po prawej znajduje się akcja zazwyczaj jest ścieżka do pliku docelowego dla przychwconych komunikatów. Przy jednej ze ścieżek znajduje się myślnik, który powoduje nie synchronizowanie tego pliku jeśli włączono by synchronizacje (jest ona domyślnie wyłączona). Synchronizacja powoduje znaczny spadek wydajność i może doprowadzić do gubienia komunikatów.
Selektor zaś składa się z kolejnych dwóch części: funkcji oraz priorytetu. Funkcja określa źródło komunikatów i są one na stałe zaimplementowane w rsyslog a priorytety ich rodzaj wśród, których możemy wymienić (ułożenie według od najniższego do najwyższego):
debug, info, notice, warning, error, crit, alert, emerg
Tworząc selektor oddziela się funkcję od priorytetu za pomocą kropki.
Priorytet służy do ograniczania wielkość przechwytywanych komunikatów,
albowiem rsyslog rozpoczyna przechwytywanie komunikatów od
tego priorytetu w górę. Jeśli przypatrzmy się pierwszej linii
konfiguracji, zauważymy, że do pliku
/var/log/messages
będzie
spływać masa informacji, ponieważ selektor uwzględnia komunikaty
ze wszystkich funkcji z minimalnym priorytetem
debug
. Istnieją jednak pewne
wykluczenia. Na selektor może składać się więcej niż jedna para
funkcja.priorytet, co również widać w pierwszej linii
przykładu. Kolejne pary rozdzielone są średnikami. W omawianym
przykładzie, pary mają ten sam priorytet, który nie został
uwzględniony na powyższej liście none powoduje
wyłączenie przechwytywania z użytych w raz z nim funkcji. Tak więc
w pierwszej linii przechwytywane będą komunikaty ze wszystkich
funkcji z minmalnym priorytetem info
,
poza takimi funkcjami jak mail
,
authpriv
oraz
cron
. Komunikaty będą zapisywane
zgodnie z akcją w pliku /var/log/messages
.
Innymi ciekawymi przypadkami w pokazanymi na przykładzie jest
podanie w akcji w drugiej linii dwóch miejsc docelowych. Pliku
/var/log/secure
oraz nazwy
superużytkownika. Podanie jakiej kolwiek nazwy użytkownika w akcji
spowoduje przesłanie mu (za pomocą polecenia write)
komunikatu diagnostycznego, o ile użytkownik zezwala na wyświetlanie
tego typu komunikatów (polecenie mesg). Chociaż
komunikaty wysłane przez superużytkownika są wyświetlane mimo tych
ustawień. Kolejnym przypadkiem związanym z wysyłaniem jest użycie
specjalnego modułu wyjściowego reprezentującego
konkretną akcje, w tym przypadku jest wysyłanie wiadomości do
zobrazowane w ostatniej linii przykładu. Jak mogliśmy zauważyć w
liniach reguł możemy używać symbolu wieloznacznego gwiazdki
(*).
Dyrektywy nowszej wersji daemona rejestrującego są dość proste do zrozumienia i nie wymgają dodatkowego opisu.
$FileOwner syslog $FileGroup adm $FileCreateMode 0640 $DirCreateMode 0775 $Umask 0022
Jeśli chodzi o syslog, to w przypadku usługi rejestrowania może stać się nie wiele złego, jedynym problemem jaki możemy napotkać jest brak przechwytywania komunikatów z powodu nieuwzględnienia jakiejś funkcji lub priorytetu w selektorze. Niemniej jednak rejestrator systemowy możemy przetestować za pomocą polecenia logger podając mu parę funkcja.priorytet po opcji -p (na stronie podręcznika opcja określona jest jako priorytet) oraz komunikat do zapisania. Jeśli priorytet został pominięty, zostanie użyty domyślny user.notice. W zależności od konfiguracji rsyslog oraz użytej funkcji nasz komunikat powinien pojawić jednym z plików wyszczególnionych w konfiguracji. W przypadku użycia narzędzia logger z domyślnymi wartościami komunikat zostanie zapisany do /var/log/messages.
Większość rejestratorów istnieje nie tylko w postaci odrębnego programu jakim jest rsyslog, ale także w postaci funkcji, nie których daemonów jak np. serwer WWW Apache2 one również zapisuje swoje komunikaty diagnostyczne do /var/log. Bardzo duża ilość danych spływająca do jednego katalogu może powodować szybkie zapełnienie przestrzeni dyskowej. Jednak się to nie dzieje, dzięki programowi logrotate, którego zadaniem jest (w zależności od konfiguracji) usuwanie lub kompresja starych plików dzienników i utworzenie miejsca na nowe komunikaty.
Z racji tego, iż demony działają w trakcje czynności wykonywanych przez logrotate, skrypty obsługujące konkretne pliki dziennika, tworzą puste pliki o takiej samej nazwie jak te utworzone przez demon.
7.3. Konfiguracja użytkowników
Komputery jak i systemy operacyjne mają za zadanie służyć użytkownikom. Jak wiemy użytkownicy w systemach istnieją aby wyznaczać granicę. Mówiąc kolokwialnie każdy z nas ma swoją piaskownice i swoje zabawki. W Linuksach użytkownicy są opisywani za pomocą kilku plików w katalogu /etc.
7.3.1. Plik /etc/passwd
Plik /etc/passwd jest podstawowym źródłem informacji o użytkownikach w systemie. W tym pliku każdy wiersz to jeden wpis definiujący użytkownika. Każdy wiersz podzielony jest na 7 pól. Poniżej znajduje się kilka przykładowych wpisów:
pulse:x:109:114:PulseAudio daemon,,,:/run/pulse:/usr/sbin/nologin saned:x:110:117::/var/lib/saned:/usr/sbin/nologin colord:x:111:118:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin lightdm:x:112:119:Light Display Manager:/var/lib/lightdm:/bin/false libvirt-qemu:x:64055:105:Libvirt Qemu,,,:/var/lib/libvirt:/usr/sbin/nologin geoclue:x:113:124::/var/lib/geoclue:/usr/sbin/nologin user:x:1000:1000::/home/user:/bin/bash xf0r3m:x:1001:1001::/home/xf0r3m:/bin/bash
Pola te zawierają kolejno:
- Nazwa użytkownika - jednoznaczny identyfikator użytkownika, służący do rozpoznawania użytkowników miedzy innymi użytkownikami.
- Zaszyfrowane hasło - w dystrybucjach Linuksa
hasło nigdy nie było przechowywany w jawnej postaci, nawet w tak
zamieszchłych czasas kiedy znajdowało się ono w pliku
/etc/passwd. Obecnie po haśle został tylko
x
, co oznacza, że hasło jest przechowywany w pliku /etc/shadow, który również zostanie tu omówiony. - UID (identyfikator użytkownika) - numer identyfikujący użytkownika, służy głównie odwołaniu się jądra do konkretnego użytkownika.
- GID (identyfikator grupy) - numer identyfikujący podstawową grupę użytkownika.
- Pole GECOS - pole przechowujące rzeczywiste informacje o użytkowniku takie jak imie, nazwisko, numer telefonu, adres e-mail, numer pokoju. Pole może zostać pominięte, co widzimy na kilku wpisach na przykładzie.
- Katalog domowy - ścieżka wskazująca na katalog domowy użytkownika.
- Program uruchamiany po zalogowaniu (powłoka) - ścieżka wskazująca na program jaki ma zostać uruchomiony po zalogowaniu, najczęściej jest to interaktywna powłoka. .
Drugie pole wpisu w /etc/passwd może przyjmować dwie dodatkowe wartości. Pole może zawierać gwiazdkę (*), która uniemożliwia logowanie lub pole może być puste co pozwala na zalogowanie się bez podawania hasła.
Jeśli przefiltrujemy za pomocą polecenia grep plik /etc/passwd pod kątem ustawionych programów (powłok), to na powiedzmy ok. 50 (zależności od dystrybucji) tylko dwóch, trzech użytkowników ma ustawioną powłokę. Pozostali użytkownicy zazwyczaj mają ustawiony program, który uniemożliwia im korzystanie z systemu nawet gdy się zalogują. Tacy użytkownicy nazwani są pseudoużytkownikami. Tego rodzaju użytkownicy istnieją w jednym celu, aby uruchamiać z ich uprawnieniami różne programy, głównie demony sieciowe. Co w razie włamania spowoduje, że atakujący zostanie uwięziony na koncie, na którym nic nie może zrobić.
Pseudoużytkownicy zaliczają się do użytkowników specjalnych obok nich istnieje jeszcze jeden użytkownik - root, który ma niczym nie ograniczone uprawnienia. Dlatego też nosi nazwę superużytkownika. Posiada on UID i GID równy 0 oraz jego katalog domowy znajduje się bezpośrednio w głównym katalogu. Innym ciekawym użytkownikiem specjalnym jest użytkownik nobody, który nie ma możliwości zapisu niczego w systemie.
Kombinacja wpisu w pliku /etc/passwd oraz katalogu domowego może być określana jako konto.
7.3.2. Plik /etc/shadow
Plik /etc/shadow jest podobnym plikiem /etc/passwd, jednak zamiast przechowywać informacje o użytkownika plik ten przechowuje informacje o hasłach. Plik /etc/shadow posiada jedno wspólne pole wraz z plikiem /etc/passwd a jest nim nazwa użytkownika. Hasło podobnie do pliku /etc/passwd znajduje się na drugim polu we wpisie. Pozostałe pola są odpowiedzialne za ważność hasła. Wpisy w pliku /etc/shadow biorą również czynny udział w blokowaniu dostępu użytkownikom. Zawartośc pliku wygląda w następujący sposób:
root:$y$j9T$h19rJ2ObBXMdBdXOHB0wF.$.Lqb5iG3.HpsO0FcghqSkXbcA6D5rIp9woC/Ovj40Q7:19251:0:99999:7::: ... user:$y$j9T$bAf/P4bLm00VJQyS3Lf8I1$dzie3XL5lORpP7jmo4CeanOqhuWMpPzdQArAlQ9AfG0:19327:0:99999:7:::
Na tym etapie nauki dalsza analiza tych wpisów nie ma sensu, jeśli jednak ktoś jest ciekawy pozostałych pól to może zajrzeć do mojego materiału przygotowywującego do RHCSA: https://morketsmerke.github.io/articles/terminallog/RedHat_-_RHCSA.html#6.1.passwordaging
Plik /etc/shadow jest dość mocno zabezpieczonym plikiem, zwykli użytkownicy nie posiadają do niego żadnych praw. Jedyną uprawnioną osobą do manipulacji nim jest superużytkownik.
7.3.3. Manipulowanie użytkownikami i hasłami
Omawiane dotychczas pliki są zwykłymi plikami tekstowymi, których zawartością możemy manipulować za pomocą ulubionego edytora tekstowego. Nie jest to jednak zalecane działanie. Ze względu na ścisłą budowę (nie możliwe jest aby umieszczać w tych plikach komentarze lub puste wiersze) oraz restrykcyjną składnie wierszy. Możemy użyć specjalnego narzędzia jakim jest vipw. Polecenie to tworzy kopię zmienianych plików i następnie otwiera ten plik w terminalowym edytorze. Konfiguruje ono również ten edytor aby zaznaczał składnię wpisów w plików, po zapisaniu zmian polecenie sprawdzi składnie zmienionych lub dodanych wpisów. Jeśli wszystko będzie dobrze, to polecenie zapisze zmiany jeśli coś będzie nie tak to polecenie zaproponuje poprawienie błędu. Polecenie wymaga oczywiście uprawnień administratora. Wydając polecenie bez żadnej opcji otworzy ono plik /etc/passwd do edycji a jesli dodamy opcje -s to wówczas zostanie otworzony plik /etc/shadow.
Inną metodą jest wykorzystanie np. opcji polecnia passwd jego domyślną rolą jest zmiana hasła, bądź ustawianie ważności hasła, jednak dodając opcję -s mozemy zmienić domyślny program startowy (powłokę) lub za pomocą opcji -f możemy zmienić nazwę użytkownika. Po więcej informacji zapraszam do strony podręcznika polecenia passwd. Jeśli na naszym przypadku polecenie passwd może nie być zadowalające. Można wówczas skorzystać z polecenia typu usermod.
7.3.4. Grupy
Rolą grup w systemach uniksopodobnych jest współdzielenie plików. Dzięki nim możemy zwiększyć uprawnienia do pliku, nie którym użytkownikom, chroniąc je przed innymi. Wynika to z możliwosci nadania uprawnień grupie, natomiast można je zabrać wszystkim pozostałym. Obecnie stacje uniksowe stały się bardziej spersonalizowanie to współdzielenie plików przy użyciu grup straciło na znaczeniu. Jednak grupy tak jak użytkowników możemy wykorzystać do uzyskiwania dodatkowych uprawnień lub ich ograniczania.
Wraz z każdym nowym użytkownikiem tworzona jest nowa grupa o tej samej nazwie co użytkownik. Grupa ta jest również ustawiana jako grupa podstawowa tworzonego użytkownika. Definicje grup znajdują się w pliku /etc/group. Poniżej znajduje się fragment:
libvirt:x:123:user,xf0r3m libvirt-qemu:x:64055:libvirt-qemu,user,xf0r3m geoclue:x:124: user:x:1000: xf0r3m:x:1001:
Jak możemy zauważyć wpisy dzielą się na cztery pola, które kolejno oznaczają:
- Nazwa grupy - nazwa jednoznacznie identyfikująca grupę.
- Hasło grupy - hasło grupy. Obecnie aby podłączyć użytkownika do grupy potrzebna jest interwencja administratora lub kogoś z jego uprawnieniami. Jeśli jednak grupa posiada hasło to użytkownik może podłączyć się do grupy za pomocą polecenia newgrp (dalej jest taka możliwość). Obecnie jednak nie stosuje się haseł dla grup. Hasła grup są przechowywane w pliku /etc/gshadow.
- GID (identyfikator grupy) - numeryczny identyfikator grupy, służy do odniesienia jądra do konkretnej grupy.
- List członków grupy - lista uzytkowników należących do grupy.
Grupy odpowiadające istniejącym użytkownikom nie posiadają żadnych członków w tym pliku, jednak odpowiadający tym grupom użytkownicy są ich domyślnymi członkami.
Jądro Linux, nie posługuje się nazwami użytkowników czy grup odwołując się do jednego lub drugiego bytu. W tym celu używa wartości UID oraz GID, aby ich używanie miało sens muszą być one unikatowe w skali całego systemu. Oczywiście może zdażyć się przypadek, że dwóch użytkowników będzie miało ten sam UID, ale są to bardzo rzadkie i specyficzne przypadki.
7.4. Getty oraz login
Te dwa programy nie są zbyt skomplikowane w swoim działaniu. Działanie programu getty polega na podłaczeniu się pod terminal (nie ważne czy wirtualny czy fizyczny podłączony przez port COM) i wyświetlenie znaku zachęty logowania (napisu "login: ") i oczekiwania na podanie nazwy użytkownika. Kiedy nazwa zostanie podana polecenie to zastępuje same siebie wywołując program login. Ten program odpowiedzialny jest za wyświetlenie znaku zachęty hasła (napisu "hasło:") po podaniu przez użytkownika hasła program spróbuje go uwierzytelnić. Jeśli to się powiedzie wówczas program login zastąpi sam siebie po przez wywołanie systemowe exec() programem startowym ustawionym w ostatnim polu wpisu w pliku /etc/passwd.
Prawdopodbnie nigdy nie będzie potrzeby konfiguracji tych programów, nie mniej jednak jest taka możliwość.
7.5. Ustawienia czasu.
Działanie wielu operacji w systemach uniksopodobnych opiera się o czas. Za zegar w tych systemach odpowiedzialne jest jądro. Jednak nie jest ono wstanie wziąć precyzyjnych ustawień czasowych samo z siebie. Większość komputerów klasy PC posiada potrzmywany bateryjnie zegar czasu rzeczywistego, jądro synchronizuje swój zegar z tym zegarem jednak z racji tego, iż maszyny uniksowe nie są uruchamiane ponownie przez miesiące a nawet lata. W czasie systemowym pojawia się odchylenie.
Zegar systemowy jądra również jest korygowany na podstawie ustawień strefy czasowej w jakiej się znajdujemy, dlatego też RTC (ang. Real Time Clock) w BIOS lub UEFI powinien być ustawiony zgodnie ze strefą czasową UTC. Za pomocą poniższego polecenia możemy ustawić czas UTC jądra względem RTC.
$ sudo hwclock --hctosys --utc
7.5.1. Strefy czasowe i reprezentacja czasu
Jądro przechowuje swój czas w formacie uniksowym, czyli ilości sekund od północy 1 stycznia 1970 roku. Na podstawie tych informacji takie polecenia jak date dokonuja wszelkich konwersji i wyświetlają nam zrozumiałą dla nas datę i czas. Czas uniksowy możemy wyświetlić sobie za pomocą polecenia date z opcją +%s (niestety polecenie date ma nieco inny format opcji).
xf0r3m@immudex:~$ date śro, 28 gru 2022, 13:11:53 CET xf0r3m@immudex:~$ date +%s 1672229517
Domyślnie polecenie date
wyświetla
datę i czas skorygowany o ustawioną w naszym systemie, na podstawie
przybliżonej lokalizacji strefę czasową. Ustawiona strefa czasowa
znajduje się katalogu /etc jako dowiązanie symboliczne do
jednego z plików stref dostępnych w systemie w katalogu
/usr/share/zoneinfo.
xf0r3m@immudex:~$ ls -al /etc/localtime lrwxrwxrwx 1 root root 33 11-04 17:46 /etc/localtime -> /usr/share/zoneinfo/Europe/Warsaw
Strefę czasową możemy zmienić za pomocą polecenia interaktywnego polecenia tzselect. Polecenie to spróbuje określić twoją przybliżoną lokalizacje na podstawie stolicy kraju w jakim się znajdujesz. Następnie utworzy nowe dowiązanie symboliczne /etc/localtime do odpowiedniego pliku strefy.
Jeśli z jakiś przyczyn potrzebujemy sprawdzić jaki bedzie czas w wybranej przez nas strefie czasowej, możemy zmienić zmienną środowiskową TZ, która przechowuje informacje o ustawionej strefie czasowej na czas wykonania polecenia. Tę czynność możemy wykonać na czas wykonania pojedyńczego polecenia, ustawiając wartość zmiennej w tej samej linii co polecenie date.
xf0r3m@immudex:~$ TZ=Asia/Tokyo date śro, 28 gru 2022, 21:29:01 JST
7.5.2. Czas sieciowy
Jeśli nasza maszyna sieciowa jest na przykład serwerem ze stałym dostępem do sieci. To aby zniwelować to odchylenie czasowe, którym wspomniałem na początku tego podrozdziału możemy uruchomić na nim specjalną usługę sieciową, która pobierze czas z serwerów podłączonych do bardzo precyzyjnych zegarów atomowych.
Pierwszą rzeczą jak należy zrobić to pobrać pakiet odpowiedzialny za demon NTP w naszym systemie. Następnie skonfigurować go zgodnie jego dokumentacją. Najprostsza konfigurację możemy zapisać w poniższych krokach.
- Znalezienie najbliższego dla siebie serwera NTP, najlepiej na poziomie STRATUM nie większym niż 3.
- Zapisanie adresów serwera (serwerów, może być pula) w pliku konfiguracyjnym demona NTP, najczęściej jest to /etc/ntp.conf.
- Uruchomienie polecenia ntpdate.
- Włącznienie demona NTP do uruchomienia w trakcie autostartu.
Jeśli nasz komputery nie posiada stałego łącza internetowego możemy wówczas uruchomić takiego demona jak chronyd. Ostatecznie mozemy zsynchonizowany czas systemowy załadować do zegara RTC za pomocą poniższego polecenia.
$ sudo hwclock --systohc --utc
W tym podrozdziale umieściłem polecenie, którego nie wyjaśniłem. Jest nim poziom STRATUM. Wyjasnienie tego pojecia wymaga zagłebienia się w protokół NTP, co zrobiłem w materiale przygotowawczym do RHCSA znajdującym się tutaj: https://morketsmerke.github.io/articles/terminallog/RedHat_-_RHCSA.html#18.1.ntp.
7.6. Tworzenie powtarzalnych zadań za pomocą cron
Wróćmy na chwile do drugiego podrozdziału. Tam pod koniec omówiliśmy sobie gdzie znajdują się pliki dziennika i w jaki sposób system radzi sobie z problem szybko przyrastających plików zawierających komunikaty diagnostyczne z przeróżnych demonów. Wspomnialiśmy tam o logrotate. Ten program aby zapobiec zużyciu całego miejsca na dysku musi uruchamiać się co jakiś czas.
W uniksach aby wykonywać jakieś czynności co określoną ilość czasu, musimy skorzystać z programu o nazwie cron. Ten program to swojego rodzaju harmonogram zadań, w którym to może zdefiniować co uruchomić, kiedy lub co ile czasu i przez kogo. Definicje zadań znajdując się w specjalnych plikach zwanych crontab. Każdy użytkownik ma swój plik. Ponieważ pliki te znajdując się w miejscu, do którego zwykli użytkownicy nie mają dostępu muszą używać dostarczonym wraz z cron narzędziem.
Aby móc zdefiniować zadania należy wydać następujące polecenie:
$ crontab -e
Wówczas otworzy nam się wybrany przez nas wcześniej edytor terminalowy. Wewnątrz pliku znajdziemy obszerny komentarz, w którym to rozpisano dokładnie całą składnie definicji zadania cron.
Otóż zadania cron to jednowierszowe wpisy definiujące na początku czas (kiedy, co ile czasu) w którym należy uruchomić zadanie Następnie polecenie, które należy uruchomić.
21 0 * * * /usr/local/bin/backup.sh > /var/log/backup_sh.log 2>&1
To zadanie zostanie uruchomione codziennie 21 minut po północy. Skąd to wiem, otóż już śpieszę z wyjaśnieniami. Najpierw objaśnie poszczególne pola.
21
- pole minut, podanie tutaj takie zwykłej wartości jak na przykładzie, spowoduje uruchomienie zadania o podanej minucie godziny. Zakres wartości od 0 - 59.0
- pole godziny, podanie wartości w tym polu spowoduje, że zadanie zostanie uruchomione o tej samej pełnej godzinie każdego dnia, chyba że podano konkretną minutę. Zakres wartości od 0 - 23.- * - pole dnia miesiąca, wartość w tym polu spowoduje, że zadanie zostanie uruchomione o północy podanego dnia miesiąca, chyba że zdefiniowano wcześniejsze pola. Zakres wartości od 1 - 31.
- * - pole miesiąca, wartość w tym polu spowoduje uruchomienie zadania o północy 1 dnia miesiąca, chyba że zdefiniowano wcześniejsze pola. Zakres wartości od 1 - 12.
- * - pole dnia tygodnia, wartość w tym polu spowoduje uruchomienie zadania co tydzeń o północy podanego dnia tygodnia. Chyba że zdefiniowano wcześniejsze pola. Zakres wartości od 0 - 7, przy czym 0 i 7 to niedziela.
Ustawienia czasu wykonania zadania jest w miarę proste o ile nie wymagamy bardzo złożonego warunku czasowego lub nie próbujemy go zrozumieć (zadając sobie pytanie: kiedy to dokładnie się wykona?). Załóżmy że chcemy aby zadanie wykonało się w każdy weekend wakacji w Polsce. Warunek czasowy może przyjmować: wartości krokowe (/2) co dwie minuty, godziny, dni, miesiące; zakresy (5-10) od 5-10 minuty, godziny, dnia itd lub listy (11,12) zadanie wykoania się np. 11 i 12 każdego miesiąca. Zakresy oraz listy można ze sobą łączyć, jak i dodać pojedyńczą wartość do zakresu, np. 5-10,12. Przy tworzeniu warunku czasowego możemy posłużyć się stronami takimi jak:
7.6.1. Instalacja tablicy cron.
Po zapisaniu zmian tablica z zadaniami crona (crontab)
zostanie automatycznie zainstalowana w katalogu
/var/spool/cron/crontab. Polecenie
crontab
daje możliwość instalacji
tablicy zadań z zwykłego pliku tekstowego, dokonujemy tego za pomocą
polecenia crontab
wraz z opcją
-l. Jednak
korzystanie z opcji -e
jest bardziej
zalecane. Wówczas podczas zapisywania zmian polecenie sprawdzi
zadania pod kątem poprawności składni i przypadku błedu, zapyta czy
nie chcemy go poprawić.
Tablice z zdaniami cron możemy usunąć za pomocą opcji
-r polecania crontab
.
7.6.2. Systemowa tablica zadań crontab.
Prócz tablic użytkowników, istnieje jescze systemowa tablica z zadaniami cron. Ją edytujemy już pomocą edytora tekstowego ponieważ jej wpisy mają nieco inną składnie niż tablice użytkowników. W jej wpisach znajduje się dodatkowe pole między dniem tygodnia a poleceniem. W nim definiuje się użytkownika, z którego uprawnienami realizowane jest zapisane zadanie. Dlatego też polecenie crontab z opcją -e nie ma tutaj zastosowania. Jako systemowej tablicy cron, nie wykorzystuje się również tablicy superużytkownika. Za tablicę systemową odpowiedzialny jest plik /etc/crontab. Plik ten edytujemy za pomocą zwykłego edytora, kiedy zakończymy edycję, należy uruchomić demon cron ponownie.
xf0r3m@immudex:~$ sudo vim /etc/crontab xf0r3m@immudex:~$ sudo systemctl restart cron
Inną metodą na powtarzalne wykonywanie czynności jest skorzystania z jednego katalogów: /etc/cron.daily, /etc/cron.hourly, /etc/cron.monthly lub /etc/cron.weekly. Wykorzystanie tych katalogów polega na umieszczeniu skryptu (powłoki, nawet jednej linii z konkretnym poleceniem) w jednym z nich. W systemowej tablicy cron jest napisane o której wykonywane są zadania zapisane w tych katalogach. Skrypty uruchamiane są przez znane nam z poprzedniego rozdziału narzędzie jakim jest run-parts. W nowszych dystrybucjach obsługą tych katalogów może zajmować się demon anacron.
7.6.3. Przysłość narzędzia cron.
Narzędzie cron jest starsze od samego Linuksa. Jeśli coś jest, aż tak stare to kwalifikuje się do wymiany. Obecnie są czynione postępy w temacie zastąpienia cron-a jakimś innym rozwiązaniem. Kandydatem mogą być elementy licznika czasu systemd. Jednak utworzenie ulepszonej funkcjonalności to jedno, a zapewnienie wstecznej zgodność z systemami oraz narzedziami dalej wykorzystującymi cron to drugie, bez zapewnienia wstecznej zgodności zmian nie będzie, więc póki co cron jeszcze z nami pozostanie.
7.7. Planowanie jednorazowych zadań.
Jedno narzędzie do planowania zadań w przyszłości już poznaliśmy. Narzędzie at na pewno nie ma tak szerokiego użycia jak cron, to pomaga wykonać pewne zadania po za czasem czynnego użytkowania komputera. Jeśli musimy poczekać do powiedzmy północy, aby móc wykonać jakąś czynność, możemy ją zaplanować za pomocą polecenia at, pozostawić komputer włączony i iść spać.
Za pomocą polecenia at, możemy nie tylko zaplanować wykonanie zadania na kilka godzin do przodu ale i nawet na kilka miesięcy. Aby jednak to zrealizować należy poznać polecenie at, które może nie być domyślnie dostępne w każdej dystrybucji i trzeba je będzie zainstalować.
Aby rozpocząć planowanie zadania należy wydać polecenie at i jako argument podać czas ewentualnie datę jeśli zadanie ma zostać wykonane w dalszej przyszłości. Po wydaniu tego polecenia zostanie nam wyświetlony prompt at>. Po wyświetleniu prompta podajemy polecenie do wykonania w zadaniu. Polecenia możemy podawać do momentu kiedy naciśniemy kombinację klawiszy Ctrl+d, przed zatwiedzeniem polecenia należy upewnić się, że zostało poprawnie zapisane ponieważ nie będzie możliwości jego edycji. Po naciśnięciu wspomnianiej kombinacji zadanie zostanie zatwierdzone Na poniższym przykładzie utworzyłem jedno zadanie z jednym poleceniem:
xf0r3m@immudex:~$ at 00:00 01.01.2023 warning: commands will be executed using /bin/sh at> echo "Happy New Year" > /dev/pts/1 at> <EOT> job 1 at Sun Jan 1 00:00:00 2023
Za pomocą opcji -l polecenia at lub polecenia atq możemy wyświetlić znajdujące się w pamieci demona atd zadania.
xf0r3m@immudex:~$ at -l 1 Sun Jan 1 00:00:00 2023 a xf0r3m xf0r3m@immudex:~$ atq 1 Sun Jan 1 00:00:00 2023 a xf0r3m
Natomiast za pomocą opcji -r polecenia at lub za pomocą polecenia atrm, możemy usunąć zdanie, przyczym należy podać numer zadnia widniejący w pierwszej kolumnie danych wyjściowych zwracanych przez polecenie at w raz z opcją -l lub polecenie atq.
xf0r3m@immudex:~$ atq 1 Sun Jan 1 00:00:00 2023 a xf0r3m xf0r3m@immudex:~$ at -r 1 xf0r3m@immudex:~$ atq
7.8. Identyfikatory użytkowników oraz ich przełączanie.
Jak pamiętamy, lub może nie bit setuid powodował fakt iż program uruchomiony z tym bitem dział (jego proces) z uprawnieniami właściciela pliku. Tutaj poznamy inne możliwości przełączania użytkowników wraz regułami oraz jaki udział ma w tym wszystkim jądro.
Na Linuksie istnieją dwie metody na przełączanie użytkowników. Pierwszym z nich jest ustawienie wspomnianego wcześniej bitu dla pliku wykonywalnego. A drugim sposobem jest wykorzystanie wywołania systemowego setuid(), a żeby sprawę jeszcze bardziej skomplikować to wersji tego wywołania systemowego jest kilka, w zależności o tego na jaki identyfikator użytkownika chcemy się przełączyć. Zanim poznamy możliwe identyfikator (a przynajmniej ich część) warto poznać reguły dotyczące możliwości procesów w zakresie przełączania identyfikatorów. Otóż:
- Procesy działające z uprawnieniami superużytkownika mogą używać wywołania systemowego setuid() do uzyskania uprawnień dowolnego innego użytkownika.
- Proces, który nie działa z uprawnieniami superużytkownika ma poważne ograniczenia odnośnie korzystania z wywołania systemowego setuid(). Przeważnie takie procesy nie mogą z niego korzystać.
- Dowolny proces może wykonać program z ustawionym bitem setuid, o ile dysponuje odpowiednimi uprawnieniami do tego pliku (każdy proces działa z uprawnieniami jakiegoś jakiegoś użytkownika i dziedziczy po nim uprawnienia).
Dla ścisłości w tym przełączaniu użytkowników nie chodzi o przełączanie między kontami. A raczej o to, że na podstawie dostępnych mechanizmów w systemie proces może zmieniać uprawnienia wrazie potrzeby oraz możliwości z jakim został uruchomiony.
7.8.1. Właściciel procesu oraz identyfikatory użytkownikow
Dotych czas można było sądzić, że procesy, które uruchamiamy w Linuksie posiadły jeden identyfikator użytkownika. Użytkownika, który ten proces zainicjował. Rzeczywistość jest niestety zgoła inna. Każdy proces posiada co najmniej dwa identyfikatory użytkowników. Pierwszym z nich jest euid (ang. Effective User IDentifier) - efektywy identyfikator użytkownika. Ten identyfikator wskazuje na użytkownika, z ktorego uprawnienia działa ten proces. Drugim jest ruid (ang. Real User IDentifier) - rzeczywisty identyfikator użytkownika. Ten wskazuje natomiast użytkownika, który zainicjował ten proces.
Ze względu na niejasność związane z rozgraniczeniem miedzy euid a ruid. Rzeczywistemu identyfikatorowi użytkownika przypisje się rolę właściciela procesu. Może on prowadzić interakcje z procesem wysyłać do niego sygnały w tym kończyć jego działanie. W przypadku większości procesów działających w systemie ruid oraz euid będą równe. Jednak w przypadku gdy uruchomimy proces z ustawionym bitem setuid, efektywny identyfikator zostanie ustawiony na właściciela pliku, zaś rzeczywisty identyfikator będzie przechowywać nasz UID. Najprostszym przykładem uruchomienia programu z bitem setuid a za razem doświadczenia przełączenia identyfikatorów jest użycie polecenia sudo.
Żeby całość jeszcze bardziej skomplikować, do tych dwóch identyfikatorów należy dodać jescze większego poziomu skomplikowania to istnieje trzeci identyfikator suid (ang. Saved User IDentifier) - zapisany identyfikator użytkownika. Podczas działania, proces może przełączać się między zapisanym a rzeczywistym identyfikatorem.
Jeśli uruchomimy jakiś proces przy użyciu polecenia sudo i ten proces będzie trwać, i spróbujemy go zabić to wówczas dostaniemy informacje o braku dostępu. Polecenie sudo z ustawionym bitem setuid (jak i inne) zamienia jawnie identyfikatory użytkowników za pomocą wywołania systemowego setuid(). Wynika to z kilku problemów jakie wynikają z braku zgodności między identyfikatorami, przez co nie musimy się zbytnio przejmować nimi oraz ich przełączaniem.
Ze względu na duże uprawnienia (czynny udział jądra w przełączaniu użytkowników oraz obsłudzie uprawnień dostępu do pliku) programów z ustawionym bitem setuid i działającym wraz z nim wywowałań systemowych. Należy uważać jakim programom nadaje się ten bit. Pozostawienie kopii powłoki z ustawionym takim bitem, daje możliwość zwykłym użytkownikom przejęcia kontroli nad całym systemem. Wydając jedno polecenie.
7.9. Identyfikacja i uwierzytelnianie
Każdy wieloużytkowy system musi zapewnić mechanizmy czuwające nad kontrolą użytkowników. Użytkownicy powinni przedstawić się systemowi przekazać tajną informację (hasło), wówczas mechanizmy w systemie będą wiedzieć, że ten użytkownik jest tym za kogo się podaje. Jeśli użytkownik został uwierzytelniony ma on dostęp do swojego konta, które jest autoryzowane (posiada pewne prawa w systemie) i w ten sposób systemy identyfikują użytkowników. To tak wygląda tylko w teorii. Z racji tego, iż jądro zna tylko UID, ustalenie nazwy użytkownika wymaga kilku czynności. Można przedstawić tutaj krokowy algorytm, który byłby w stanie ustalić taką nazwę, jednak stosuje sie funkcję biblioteki standardowej, które pomagają w ustaleniu takich informacji. Problem stanowią natomiast hasła, ponieważ funkcje biblioteki standardowej posiadają w sobie pewne założenia, które niestety nie sprawdzają się przy obecnym sposobie przechowywania haseł w systemie. Więc zatem w jaki sposób obecne dystrybucje dokonuja uwierzytelniania? Wykorzystują on do tego podsystem PAM.
7.10. System PAM
System PAM (ang. Pluggable Authentication Modules) jest to zbiór bibliotek współdzielonych, których zadaniem jest udostępnienie uwierzytelniania użytkownika, w taki sposób aby aplikacja nie musiała się tym zajmować. System PAM może również kontrolować autoryzację użytkownika np. blokując mu dostęp do pewnych usług. Jak sama nazwa wskazuje system ten wykorzystuje dynamicznie ładowane moduły, które realizują poszczególne zadania w zależności od użytej funkcji (o tym za chwilę). Jeśli system ma sprawdzić hasło użytkownika użyje modułu pam_unix.so. Ze względu te informacje dość łatwym zdaniem jest stworzenie dodatkowych modułów obsługujących uwierzytelniania dwuskładnikowe lub klucze fizyczne, mimo to system PAM dalej pozostaje spuścizną Uniksa przez co, niektóre zagadnienia bywają skomplikowane a intefejs programowania nie jest zbyt prosty. To z tego systemu korzysta większość aplikacji wymagających uwierzytelniania na dystrybucjach Linuksa, ponieważ połączenie aplikacji z PAM wymaga niewiele pracy lub wcale.
7.10.1. Konfiguracja PAM
Ze względu na to, iż konfiguracja różni się w zależności od dystrybucji, cieżko jest się odnaleźć i wyjaśnić to w ogólny sposób. pliki konfiguracyjne systemu powinny znajdować się w katalogu /etc/pam.d/. Ich nazwy często zawierają nazwy komponentów systemu, które wykorzystują system PAM. Najprostszym z nich jest plik /etc/pam.c/chsh. Polecenie chsh służy do zmiany powłowki. Pierwszy wiersz pliku wygląda następująco:
auth requisite pam_shell.so
Nakłada on wymóg, aby podana podczas zmiany powłoka była wymieniona w pliku /etc/shells, w przeciwym wypadku uwierzytelnienie użytkownika kończy się nie powodzeniem.
Każda linia konfiguracji PAM składa się z co najmniej trzech części. Między innymi z:
- Typu funkcji, które aplikacja żąda od systemu PAM
w tym przypadku jest to
auth
powodujący uwierzytelnienie użytkownika. - Argumentu kontrolnego, decydującego o tym jak system zareaguje na powodzenie lub niepowodzenie działania wykonywanego przez typ funkcji.
- Modułu, określającego jaka czynność zostanie tak naprawdę wykonana. W tym przypadku jest sprawdzenie czy funkcja występuje w pliku /etc/shells.
Typy funkcji
System PAM oferuje aplikacji wykonanie czynności określonej jedną z z poniższych funkcji:
- auth - uwierzytelnienie użytkownika.
- account - sprawdzenie status (czy użytkownik jest autoryzowany do wykonania pewnej czynności).
- session - wykonanie jakieś czynność na bierzącej sesji użytkownika.
- password - zmiana danych uwierzytelniania.
Warto wspomnieć, że moduły mogą zachować się inaczej (wykonywać inną czynność, gdy zestawimy je z innymi funkcjami. Dla przykładu moduł pan_unix.so dla modułu auth sprawdzi poprawność podanego hasła, natomiast dla funkcji password ustawi je użytkownikowi. Dlatego też funkcję i moduł podczas konfiguracji PAM należy traktować jako parę.
Argumenty kontrolne
Inną częścią, wartą omówienie są argumenty kontrolne. Są one stosowane ze względu na to, iż wiersz zapisane w konfiguracji są zestawiane, co oznacza, że nie zawsze status (powodzenie, niepowodzenie) jednego wiersza może oznaczać zwrócenie do aplikacji informacji o powodzeniu lub niepowodzeniu funkcji przez nią żądanej. Inną kwestią jest rozszerzona składnia argumentów kontolnych dopuszająca inne wartości niż tylko prawda czy fałsz. Więcej na ten temat znajduje się na stronie podręcznika pliku pam.conf. Poniżej znajdują się arguementy kontrolne o prostej składni.
- sufficient. Jeśli reguła opatrzona tym argumentem powiedzie się, zbędne staje się sprawdzanie pozostałych reguł, system PAM nie musi tego robić. Jeśli się niepowiedzie to system przechodzi do kolejnej reguły.
- requisite. Jeśli reguła z tym argumentem niepowiedzie się to wówczas całe uwierzytelnianie kończy się niepowodzeniem. Jeśli reguła powiedzie się, to system przechodzi do sprawdzenia pozostałych reguł, oczywiście jeśli zostały zdefiniowane.
- required. Jeśli reguła powiedzie się to system przechodzi do kolejnych reguł. Jeśli nie to system i tak sprawdzi pozostałe dostępne reguły jednak jedyną zwracaną infomacją bedzie niepowodzienie bez znaczenia czy następujące po required reguły sie powiodły lub nie.
Znając składnię reguł systemu PAM, możemy spóbować samodzielnie przeanalizować drugą linię znajdującą się w pliku chsh w katalogu /etc/pam.d.
auth sufficient pam_rootok.so
Pamiętając o tym, że typ funkcji oraz moduł należy taktować jako parę. Skupimy się na początku na argumencie kontrolonym. Otóż jeśli ta reguła zakończy się powodzeniem system PAM nie będzie dalej sprawdzać reguł. Natomiast funkcja (wraz z modułem) mają za zadanie ustalenie czy to superużytkownik próbuje się uwierzytelnić.
Zwrócićmy uwagę na to, iż mimo tego co poznaliśmy konfigrację PAM oraz przeanalizowaliśmy skonstruowany plik konfiguracyjny dla chsh, to root nadal może ustawić dowolną powłokę. Wynika to z konstrukcji samego narzędzia. W tym przypadku PAM ma działać nad uwierzytelnianiem zwykłego użytkownika i jego działań.
Moduły w regułach PAM mogą posiadać argumenty i są one umieszczane po nazwie modułu, na przykład:
auth sufficient pam_unix.so nullok
Argument ten pozwala na stosowanie pustego hasła podczas uwierzytelnienia.
7.10.2. Uwagi dotyczące PAM
Ze względu na to, iż wiekszość mechanizmów opisanych w tym materiale zostało przedstawionych pobierznie, przecież to podstawy. To poniżej znajduje się kilka wskazówek odnośnie systemu PAM.
- Lista dostępnych w systemie modułów możemy wyświetlić za pomocą
polecenia
$ man -k pam_
Jeśli potrzebujemy znać lokalizację modułów PAM, wówczas możemy spróbowac użyć polecenia locate podając nazwę modułu jako argument polecenia. - Strony podręcznika zawierają opis funkcji i argumentów dla każdego modułu.
- W wielu dystrybucjach pliki konfiguracji systemu PAM są generowane automatycznie, wiec ich modyfikacja może nie być najlepszym pomysłem. Przed wprowadzeniem zmian warto zapoznać się z umieszczonymi w tych plikach komentarzami.
- Plik /etc/pam.d/other definiuje konfigurację dla aplikacji, które nie posiadają własnego pliku. Zazwyczaj wszystko tam jest blokowane.
- Istnieje możliwość dołączenia dodatkowych plików konfiguracjnych. Za pomocą dyrektywy @include możemy załadować cały dodatkowy plik, ale za pomocą specjalnego argumentu kontrolnego możemy załadować plik konfiguracjny dla określonej funkcji. Te sposoby są określne przez dystrybucje.
- Definicja reguł systemu PAM nie kończy się na argumentach modułów. Moduły mogą uzyskiwać dostęp do plików w katalogu /etc/security, które głownie mają na celu ograniczanie uprawnień użytkownikom.
7.10.3. System PAM i hasła
System PAM możemy wykorzystać do uzyskiwania informacji na temat haseł w systemie. Wykorzystanie modułu pam_unix.so wraz z funkcją auth powoduje sprawdzenie hasła. Natomiast jeśli użyjemy tego modułu wraz z funkcją password moduł ustawi podane przez uzytkownika hasło. Wyszukując odpowiednią regułę możemy dowiedzieć się na przykład jakiego algorytmu użyto do tworzenia skrótu hasła. Do odnalezienia tej linii posłużymy się poniższym poleceniem.
xf0r3m@immudex:/ic0$ grep password.*unix /etc/pam.d/* /etc/pam.d/common-password:# used to change user passwords. The default is pam_unix. /etc/pam.d/common-password:password [success=1 default=ignore] pam_unix.so obscure yescrypt
W drugiej linii znajduje się poszukiwana przez nas
reguła. Skupmy się tylko na argumentach modułu. Otóż argument
obscure
, najprościej rzecz ujmując
powoduje on sprawdzenie czy podane hasło jest wystarczająco
"przesłonięte" (nie jest zbliżone do obecnie używanego). Kolejny
argument to algorytm szyfrowania w tym przypadku jest to
nowy algorytm yescrypt
, do Debiana
został on wdrożony wraz z wypuszeniem wersji 11 "Bullseye".
No dobrze, w przypadku ustawiania hasła mamy jawnie podany algorytm. A co w przypadku gdy moduł PAM musi sprawdzić czy podane hasło jest poprawne. Niestety w tym przypadku PAM próbuje odgadnąć algorytm wykorzystując do tego bibliotekę libcrypt, ktora wypróbowuje wszystkie dostępne możliwości do momentu aż coś zadziała lub nie pozostanie nic sprawdzenia.
8. Procesy oraz monitorowania zasobów
Jeśli pamiętamy definicję procesu, to wiemy, że proces to nic innego jak wystąpienie uruchomionego programu. Każdy proces aby mógł wykonać swoje zadanie potrzebuje zasobów sprzetowych naszych komputerów oferowanych przez system operacyjny. Jądro odpowiada za sprawiedliwy przydział zasobów systemowych. Samo jądro również może być zasobem - programowym wykorzystywanym przez procesy do uzyskiwania dostępu do plików czy do urządzeń wejścia-wyjścia.
W tym rozdziale objaśnimy sobie nieco bardziej procesy oraz zajmiemy się monitorowaniem zasobów. Jednak nie po to aby optymalizować system, ponieważ ten na domyślnych ustawieniach dystrybucji działa całkiem dobrze i nie potrzeba nic zmieniać. Zajmiemy się natomiast monitorowaniem zasobów by lepiej zrozumieć co jest dokładnie mierzone, dzięki czemu przybliżymy sobie, niektóre działania jądra.
8.1. Śledzenie procesów
Procesy możemy śledzić za pomocą polecenia ps i w zależności od użytych przełączników możemy uzyskać różne rezultaty działania tego polecenia. Osobiście polecam kombinację trzech przełączników -aux. Poza tym to polecenie posiada trzy rózne możliwości wprowadzania do niego opcji. Ja skupie się na jednym myślniku i łączeniu razem opcji. Inną godną polecenia kombinacją opcji jest -elf te opcje zwracają najważniesze dla nas informacje, na przykład jak wartość priorytetu oraz wartość nice, które biorą udział w szeregowaniu procesów do wykonania. Dlaczego polecam tę pierwszą kombinacją, ponieważ najczęściej do zarządzania jakimś procesami będzie nam potrzebny PID, właściciel procesu, z jakiego polecenia proces pochodzi lub procentowe wartości zużycia pamięci czy procesora.
Innym przydatnym narzędziem dla śledzenia procesów jest polecenie top. Ponieważ informacje wyświetlane przez to polecenie są odświeżane co sekundę dając aktualny obraz tego co się aktualnie dzieje w systemie. Po uruchomieniu tego programu na samej górze listy procesów znajdują się najbardziej aktywne z nich. Podczas działania programu, można przekazywać do niego opcje za pomocą naciśniecia odpowiedniego klawisza. Poniżej znajduje się kilka opisanych klawiszy.
- Spacja - natychmiastowe odświerzanie ekranu.
- Shift + m - sortuje procesy pod względem zajętości pamięci.
- Shift + t - sortuje procesy pod względem całkowitego zużycia czasu procesora.
- Shift + p - sortuje procesy pod względem aktualnego zużycia czasu procesora. Użycie tego polecenia przywraca domyślne ustawienia.
- u - wyświetla tylko dane procesów użytkownika.
- f - umożliwia wybranie różnych statystyk do wyświetlenia.
- ? - wyświetla informacje o opcjach programu top.
W dystrybucjach Linuksa dostępne są różne odmiany polecenia top, takie jak htop lub atop. Polecenie htop jest znacznie bardziej rozbudowane, a jego interaktywna konfiguracja pozwala nie tylko na zmianę wyświetlanych danych, ale również zmianę tematu wyświetlania (kolorów). Za pomocą htop możemy monitorować stan baterii. Po za tym polecenie to posiada, niektóre możliwości innego przydatnego polecenia jakim jest lsof.
8.2. Wyszukiwanie otwartych plików z pomocą polecenia lsof
Polecenie lsof może być bardzo przydatne ponieważ pozwala wyświetlić listę plików otwartych przez różnego rodzaju procesy. Co może okazać się przydatne przypadku gdy chcemy odmontować jakiś system plików, ale otrzymujemy informacje o tym, że target is busy. Ten komunikat może być spowowany tym, że w systemie istnieją jeszcze procesy działające na plikach znajdujących się na odmontowywanym systemie plików. Poza tym polecenie to generuje masę danych ze względu na to, że w systemach uniksopodobnych wszystko jest plikiem, a więc nie otrzymamy informacji tylko i wyłącznie o konwencjonalnych plikach, ale również o gniazdkach czy nazwanych potokach. Poniżej znajduje się linia z otwartym plikiem podczas pisania tego tekstu.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME vim.gtk3 3645 xf0r3m 7u REG 254,0 20480 8391246 /media/xf0r3m/immudex_crypt0/ Repos/morketsmerke-dev/articles/terminallog/.Linux.Podstawy.html.swp
Linia przedstawia otwarty plik z materiałem w trakcie redagowania. Zwróćmy uwagę na nazwę pliku. Wygląda na to, że edytor Vim ładuję zawartość pożądanego przez nas pliku do pliku bufora (rozszenienie .swp). W momencie zapisu otwiera właściwy plik, zapisuje dane poczym go zamyka. Edytor ten jest znany z tej metody obsługi plików. Dzięki temu w przypadku nagłego wyłącznia komputera, dane wciąż pozostają w pliku bufora. Nawet w przypadku, kiedy będziemy otwierać ten plik to edytor zauważy pozostawiony plik bufora, który o opuszczeniu programu powinien zostać usunięty. Jeśli dla otwieranego przez ten edytor istnieje już plik wymiany, wówczas edytor zapyta co zrobić z jego zawartością.
Wracając, linie zwracane przez to polecenie podzielone są na 9 kolumn Każda z nich zawiera:
- COMMAND - polecenie / nazwa procesu.
- PID - identyfikator procesu.
- USER - użytkownik, który zainicjował proces.
- FD - deskryptor pliku lub jego przeznaczenie. Deskryptor otwartego pliku jest wykorzystywany przez proces przy użyciu bibliotek wspódzielonych oraz jądra do identyfikowania i manipulowania plikiem. Na przedstawionej linii mamy doczynienia z deskryptorem.
- TYPE - rodzaj otwartego pliku (zwykły plik, katalog).
- DEVICE - główny i poboczny numer urządzenia przechowywującego plik.
- SIZE/OFF - rozmiar pliku.
- NODE - numer węzła inode.
- NAME - nazwa pliku / ścieżka do pliku.
Ze względu na przytłaczającą ilość danych zwracanych przez to polecenie, możemy uruchomić je na dwa sposóby. Pierwszym z nich jest przepuszcznie danych zwracanych przez to polecenie przez jakiś filtr, najprostszym jest chyba polecenie less jednak lepiej wyszukać informacji z użyciem wyrażeń regularnych (polecenia grep). Drugą metodą jest zawężenie informacji zwracanych poprzez wykorzystanie dostępnych opcji narzędzia. Najłatwiejszym w użyciu jest podanie jako argumentu po prostu ścieżki do katalogu, z którego chcemy widzieć otwarte pliki. Na przykład:
xf0r3m@immudex:~$ lsof /home/xf0r3m COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME pipewire 978 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m dbus-daem 983 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m pipewire- 988 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m xfce4-ses 989 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m at-spi-bu 1042 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m dbus-daem 1047 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m xfconfd 1051 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m ... mpv 2923 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m atrild 3227 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m
A jeśli chcelibyśmy się dowiedzieć jakie otawrte pliki posiada proces mpv, to możemy na przykład skorzystać z opcji -p a jako jej argument podać PID procesu mpv tak jak przedstawiłem to na przykładzie.
xf0r3m@immudex:~$ lsof -p 2923 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mpv 2923 xf0r3m cwd DIR 0,27 400 638 /home/xf0r3m mpv 2923 xf0r3m rtd DIR 0,27 200 2 / mpv 2923 xf0r3m txt REG 0,29 2158056 4302 /usr/bin/mpv mpv 2923 xf0r3m mem REG 7,0 4302 /usr/bin/mpv (path dev=0,29) ... mpv 2923 xf0r3m 5u IPv4 26738 0t0 TCP 192.168.168.29:34108-> prg03s12-in-f14.1e100.net:https (CLOSE_WAIT) mpv 2923 xf0r3m 6u IPv4 26743 0t0 TCP 192.168.168.29:34122-> prg03s12-in-f14.1e100.net:https (CLOSE_WAIT) mpv 2923 xf0r3m 7u IPv4 88390 0t0 TCP 192.168.168.29:38584-> prg03s13-in-f14.1e100.net:https (ESTABLISHED) mpv 2923 xf0r3m 8u IPv4 86801 0t0 TCP 192.168.168.29:48618-> 85.162.162.204:https (ESTABLISHED) mpv 2923 xf0r3m 9u unix 0x000000006b6ad31e 0t0 26745 type=STREAM mpv 2923 xf0r3m 10r FIFO 0,12 0t0 26746 pipe mpv 2923 xf0r3m 11w FIFO 0,12 0t0 26746 pipe mpv 2923 xf0r3m 12u CHR 226,0 0t0 237 /dev/dri/card0 mpv 2923 xf0r3m 13u CHR 226,0 0t0 237 /dev/dri/card0 mpv 2923 xf0r3m 14u CHR 226,0 0t0 237 /dev/dri/card0 mpv 2923 xf0r3m 15u CHR 226,0 0t0 237 /dev/dri/card0 mpv 2923 xf0r3m 16r FIFO 0,12 0t0 26079 pipe mpv 2923 xf0r3m 17w FIFO 0,12 0t0 26079 pipe mpv 2923 xf0r3m 18u a_inode 0,13 0 7971 [eventfd] mpv 2923 xf0r3m 19u unix 0x0000000075d44df6 0t0 26081 type=STREAM mpv 2923 xf0r3m 20u a_inode 0,13 0 7971 [eventfd] mpv 2923 xf0r3m 21r FIFO 0,12 0t0 26082 pipe mpv 2923 xf0r3m 22w FIFO 0,12 0t0 26082 pipe
Zastanawiające może być, dlaczego mpv korzysta z połączeń sieciowych, otóż za pomocą mpv oraz pakietu youtube-dl, można korzystać z serwisu YouTube bez nadmiernego obciążenia komputera przez nieoptymalną aplikację internetową.
Jeśli często aktualizujemy jądro, poza aktualizacją całej dystrybucji to należy pamiętać o aktualizacji programu lsof. Po aktualizacji jądra oraz programu lsof, może ono nie nie działać prawidłowo, dopóki nie uruchomimy nowego jądra.
8.3. Śledzenie wykonania programów oraz wywołań systemowych
Zazwyczaj program jeśli się uruchamia i napotka podczas wykonywania swoich czynności błąd to zwróci jakąś informację o tym co się stało (większość programów uruchomianych na uniksach). Możemy jednak napotkać taki przypadek, że uruchomimy program i on odrazu się zamknie. Wówczas pojawia się problem w jaki sposób mamy dowiedzieć się co jest nie tak z programem czy naszym środowiskiem (czy wszyskie wymagane pakiety zostały zainstalowane, na przykład). Rozwiązania, które przedstawię w tym podrozdziale na pewno nie są idealne i nie sprawdzą się w przypadku każdego "migającego" programu. Nie mniej jednak warto uruchomić dla niego chodziaż jedno z zaprezentowanych tutaj poleceń.
8.3.1. Polecenie strace
Polecenie strace pozwala na uruchomienie programu wraz ze śledzeniem wywołań systemowych (interfejsu jądra, pozwalającego na wykonanie wielu czynności systemowych, na przykład otwarcia pliku zapisanego gdzieś w systemie plików). Najprostszą poleceniem jakie możemy wykonać dla przykładu jest wyświetlenie zawartości jakiegoś pliku. Polecenie strace może nie występować we wszystkich dystrybucjach, więc będzie trzeba je zainstalować. Z racji tego, iż informacji zwracanych przez to polecenie jest na prawdę dużo to poniżej przedstawiłem wywołanie prostego wyświetlenia zawartości pliku przy użyciu polecenia cat:
xf0r3m@immudex:~$ strace cat yt-links execve("/usr/bin/cat", ["cat", "yt-links"], 0x7ffc8ee949b8 /* 39 vars */) = 0 brk(NULL) = 0x564490232000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Nie ma takiego pliku ani katalogu) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=105669, ...}) = 0 mmap(NULL, 105669, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f770de000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@>\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1905632, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f770dc000 mmap(NULL, 1918592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f76f07000 mmap(0x7f2f76f29000, 1417216, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f76f29000 mmap(0x7f2f77083000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17c000) = 0x7f2f77083000 mmap(0x7f2f770d2000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ca000) = 0x7f2f770d2000 mmap(0x7f2f770d8000, 13952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f770d8000 close(3) = 0 arch_prctl(ARCH_SET_FS, 0x7f2f770dd580) = 0 mprotect(0x7f2f770d2000, 16384, PROT_READ) = 0 mprotect(0x56448ed5a000, 4096, PROT_READ) = 0 mprotect(0x7f2f77122000, 4096, PROT_READ) = 0 munmap(0x7f2f770de000, 105669) = 0 brk(NULL) = 0x564490232000 brk(0x564490253000) = 0x564490253000 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=3041312, ...}) = 0 mmap(NULL, 3041312, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f76c20000 close(3) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0 openat(AT_FDCWD, "yt-links", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=512, ...}) = 0 fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0 mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f76bfe000 read(3, "lofi girl stream: www.youtube.co"..., 131072) = 512 write(1, "lofi girl stream: www.youtube.co"..., 512lofi girl stream: www.youtube.com/watch?v=jfKfPfyJRdk Stalker czyste niebo: www.youtube.com/playlist?list=PLMnTK-S7An4KqvvXn6AMkE00s_aDNZ29o Stalker zew prypeci: www.youtube.com/playlist?list=PLMnTK-S7An4K8BMXzFCVA0Y8KLSSHRfwV MysteryTV CP S03: www.youtube.com/playlist?list=PLjkTsi__dtwWJm4gUqaFLBva0k1TljXcn MysteryTC CP S04: www.youtube.com/playlist?list=PLjkTsi__dtwWR247M06TZ_2FxccG91f1u Tu skończyłem: https://www.youtube.com/watch?v=wnY3cTAujMc Iceberg o stalkerze: www.youtube.com/watch?v=uMHvO7LXVz8 ) = 512 read(3, "", 131072) = 0 munmap(0x7f2f76bfe000, 139264) = 0 close(3) = 0 close(1) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++
Polecenie strace
wykorzystuje
wyołanie systemowe fork() aby utworzyć kopię swojego procesu
następnie ta kopia jest zastępowana przez wywołanie systemowe
execve()
, które uruchamia
podany przez nas proces, w tym przypadku jest wyświetlenie
zawartości pliku yt-links
, po
zinicjowaniu pamięci oraz odpytaniu się bibliotek
proces cat chcę otworzyć
plik za pomocą wywołania systemowego
openat
, jeśli otwarcie pliku
się powiedzie, otwartemu plikowi zostanie nadany pierwszy wolny
deskryptor, najczęsiej będzie to 3
,
co zostało przedstawione na przykładzie. Jeśli samodzielnie będziemy
uruchamiać podobne polecenie (nie każdy w swoim systemie posiada,
pliki yt-links), to zauważym że ten deskrytor jest
przypisywany i zwalniany kilkukrotnie podczas wykonywania czynności
Przed pobraniem zawartości pliku, pobierane są jego atrybuty za
pomocą wywołania fstat()
oraz
alokowana jest pamięci operacyjna
(mmap
) i wówczas następuje wywołanie
systemowe read()
, które pobiera
zawartości pliku. Następne wywołanie systemowe odpowiedzialne jest
za wypisanie zawartości, do deskryptora o numerze
1
. Jak pamiętamy polecenie
cat wyświetla zawartość pliku podanego jako ścieżka
albo wyświetla podane dane z standardowego wejścia na standardowe
wyjście. A
więc numer standardowego wyjścia, którym jest 1 jest
deskryptorem otwartego pliku. Dlatego też wywołanie systemowe
write()
odwołuje się do
deskryptora o numerze 1
. Następnie
pamięć oraz wykorzystywane deskrytory zostają zwolnione i w ten
sposób kończy się wykonanie procesu polecenia cat.
Czytając ten obszerny opis możemy przyjrzeć się jak działają programy na uniksach. Nie mniej jednak polecenie strace może pomóc nam przy, nie których problemach z programami. Często sytuacją, w której program może nam "mignąć" jest brak jakiegoś pliku lub problem z jego dostępnością. Kiedy analizujemy wykonanie programu za pomocą polecenia strace to należy szczególną uwagę zwrócić na wywołania systemowe openat(). Poniżej znajdują się dwa przykładowe komunikaty:
openat(AT_FDCWD, "test.txt", O_RDONLY) = -1 ENOENT (Nie ma takiego pliku ani katalogu) openat(AT_FDCWD, "/etc/shadow", O_RDONLY) = -1 EACCES (Brak dostępu)
W przypadku błędu, zazwyczaj otrzymujemy deskryptor o numerze
-1, jednak zwróćmy uwagę na komunikaty błedów różnia się od
siebie (ENOENT
a
EACCES
).
8.3.2. Polecenie ltrace
Podobnym do strace narzędziem jest ltrace. Jednak zamiast wywołań systemowych śledzi on wywołania bibliotek wspóldzielonych, dane wyjściowe tego programu są zbliżone do strace. Program ltrace nie śledzi niczego na poziomie jądra, to jednak warto mieć na uwadzę fakt, iż programy o wiele częściej korzystają z bibliotek wspódzielonych niż z wywołań systemowych. Polecenie ltrace nie zadziała w przypadku bibliotek dołączonych statycznie. A jego dane wyjściowe możemy odfiltrować za pomocą opcji polecenia, których opis dostępny jest na stronie podręcznika.
8.4. Wątki
Procesy mogą dzielić się na podobne byty zwane wątkami. Wątki w pewnym sensie są podobne do procesów, również zawierają identyfikator zwany TID (ang. Thread IDentifier). Podobnie jak program może mieć kilka procesów, odpowiadających wykonywanym przez niego czynnością, to wątki mogą być efektem podziału czynności procesu na jeszcze mniejsze części. W przypadku komputerów metoda rozwiązywania problemów "dziel i rządź", będzie miała zastosowanie jeszcze nie jednokrotnie.
8.4.1. Procesy jedno oraz wielowątkowe
Część procesów uruchamianych w systemie zawiera tylko jeden wątek. Wówczas taki proces jest procesem jednowątkowym. Na początku, każdy proces posiada jeden wątek, zwany wątkiem głównym. Wątek główny może tworzyć kolejne wątki, zmieniając ten proces tym samym w proces wielowątkowy.
Podstawową zaletą procesów wielowątkowych jest fakt wykonania zaplanowanych w procesie czynności o wiele szybciej, gdyż każdy wątek może być wykonywany przez jeden procesor (wątek procesora, tak rdzenie procesorów też mogą zawierać w sobie wątki, przeważnie na jeden rdzeń przypadają dwa wątki). Innym cechą wątków jest to iż wykorzystują one wspólne obszary pamięci, (nie tak jak w przypadku procesów, gdzie procesy nie mają dostępu do obszaru pamięci innych procesów) usprawniając tym samy komunikację miedzy wątkami. Wątki najczęsciej wykorzystywane są do obsługi operacji wejscia-wyjścia. Użycie w tym przypadku wątków zamiast procesów pozwala nam zaoszczędzić trochę czasu procesora.
8.4.2. Wyświetlanie wątków.
Pozanane do tej pory narzędzia służące do obserowania procesów również sprawdzą się gdy będziemy chcieli wyświetlić informacje na temat wątków. W przypadku polecenia ps wystarczy dodać do polecenia opcję m, warto jednak zaznaczyć by nie mieszać opcji m wraz z opcją u. Na poniższym przykładzie wyświetliłem moje procesy uruchomione w terminalach.
xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ ps -am -opid,tid,time,cmd PID TID TIME CMD 1799 - 00:00:02 /usr/bin/python3 -O /usr/bin/ranger - 1799 00:00:02 - 7507 - 00:00:00 /bin/sh -c set -- '/ic0/Repos/morketsmerke-dev/articles/terminallog/Linux.P - 7507 00:00:00 - 7508 - 00:00:00 /bin/sh /usr/bin/sensible-editor -- /ic0/Repos/morketsmerke-dev/articles/te - 7508 00:00:00 - 7512 - 00:00:05 /usr/bin/vim.gtk3 -- /ic0/Repos/morketsmerke-dev/articles/terminallog/Linux - 7512 00:00:05 - 12070 - 00:00:00 ps -am -opid,tid,time,cmd - 12070 00:00:00 -
Zwróćmy uwagę na to numery PID oraz TID są takie same. W przypadku poleceń jednowątkowych, wątek główny posiada taki sam TID jak proces macierzysty. Jeśli pojawiłby się procesy, wkorzystujące wątki, to wówczas TIDy wynośiły by kolejne numery rozpoczynając od PID-u lub TID-u wątku głównego.
Jeśli preferujemy narzędzie top to wówczas użycie kombinacji klawiszy Śhift + h pozwoli nam na wyświetlenie wątków. Możemy je rozpoznać po tym, że wyświetlone tam "procesy" są uruchomione z tego samego polecenia i posiadają następujące po sobie identyfikatory.
Wątki w tym materiale zostały przedstawione, aby zaprezetować ich istnienie i na tym temat się kończy.
8.5. Monitorowanie zasobów
Monitorowanie zasobów komputerów przeprowadza się głównie, aby dowiedzieć się, które z komponentów systemów lub komputerów należy z optymalizować, aby nasza praca była jescze bardziej wydajna, abyśmy mogli zrobić coś lepiej, szybciej oraz niższym nakładem pracy. Jak już wspomniałem jądro Linuksa jest bardzo wydajne przy domyślnych ustawieniach i nie trzeba się tym przejmować. Dlatego też wykorzystamy monitorowanie zasobów czasu procesora, pamięci operacyjnej oraz operacji wejścia-wyjścia do sprawdzenia w jaki sposób dzieli je między procesami.
8.6. Pomiar czasu procesora
Do monitorowania w czasie rzeczywistym pojedynczych procesów możemy wykorzystać polecenie top wraz z opcją -p. Jako argumenty opcji podajemy listę identyfikatorów procesów.
$ top -p 3329,1230
Wówczas polecenie top pokaże na swojej liście tylko te dwa procesy.
Aby dowiedzieć się ile czasu procesora w trakcie swojego działania wykorzystało konkretne polecenie, to należy je uruchomić za pomocą polecenia time, jednak tu musimy się na chwilę zatrzymać gdyż przeważnie w większości dystrybucji istnieją dwa polecenia time. Jedno jest polecenie wbudowanym w powłokę i nie ma z niego za bardzo pożytku. Dla przykładu poniżej umieszczam pomiar czasu procesora wbudowanym w powłokę polecenie time:
xf0r3m@immudex:~$ time ls ... real 0m0,007s user 0m0,001s sys 0m0,007s
Z kolei dostęp do właściwego polecenie uzyskamy uruchamiając konkretny plik: /usr/bin/time, jednak twórcy wiodących dystrybucji uznają, że polecenie wbudowane w powłokę wystarczy, dlatego też prawdopodbne jest, że omawiany przez nas program nie będzie domyślnie zainstalowany w naszym systemie. Poniżej znajduje się to samo polecenie wykonane za pomocą właściwego programu.
xf0r3m@immudex:~$ /usr/bin/time ls ... 0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 2516maxresident)k 0inputs+0outputs (0major+123minor)pagefaults 0swaps
To polecenie zwraca znacznie więcej informacji na temat wykonanego
polecenia. Na tym etapie będą interesować trzy pierwsze wartości:
0.00user 0.00system 0:00.00elapsed
.
Wskazują one kolejno:
- Czas użytkownika
(
0.00user
) - liczba sekund poświęcona przez procesor na wykonanie właściwego kodu programu. Przy obecnej mocy obliczeniowej komputerów czas wykonania prostej czynności jest natyle krótki, że program zaokrągla go do zera. - Czas systemowy
(
0.00system
) - czas poświęcony przez jądro na obsługę procesu (na przykład, odczyt zawartości plików lub katalogów). - Czas trwania
(
0.00elapsed
) - całkowity czas działania procesu od początku do końca jego życia, wraz ze wszystkimi dodatkowymi czynnościami. Czas ten nie jest szczególnie brany pod uwagę podczas pomiarów wydajności, ale odjęcie sumy czasu użytkownika oraz czasu systemowego od tej wartości przedstawi czas oczekiwania na zasoby.
Pozostałe wartości zwracane przez to polecenie dotyczą pamięci, operacji wejścia-wyjścia oraz stronicowania (do stronicowania jeszcze wrócimy w tym rozdziale).
8.7. Priorytetyzacja procesów
W pierwszym rodziale poruszyliśmy temat zarządzania procesami przez jądro, dowiedzielśmy się, że każdy proces otrzymuje dostęp do procesora na ułamek sekundy. Modułem odpowiedzialnym za to, który z procesów uzyska w chwili obecnej dostęp do procesora jest program szeregujący, który na podstawie priorytetu procesu może przydzielć mu więcej lub mniej czasu. W dystrybucja Linuksa priorytety funkcji przedstawiane są dwojako. Polecenie top przestawia domyślny priorytet wartością 20 (kolumna PR), jeśli jednak wywołamy polecenie ps z opcjami -elf (ta sama kolumna), to domyślnym priorytetem będzie 80. My na tym etapie będziemy trzymać się raczej wartosci przedstawianych przez polecenie top, ponieważ łatwiej będzie nam je zrozumieć. Więc domyślnym priorytem jest 20, poniżej przedstawiam kilka procesów wyświetlonych przez narzędzie top:
xf0r3m@immudex:~$ top top - 09:32:04 up 1:55, 1 user, load average: 0,73, 0,65, 0,61 ... PID UŻYTK. PR NI WIRT REZ WSP S %CPU %PAM CZAS+ KOMENDA 2364 xf0r3m 20 0 2504764 158992 90048 S 0,3 2,0 0:37.66 Isolated Web Co 2418 xf0r3m 20 0 474048 94468 66272 S 0,3 1,2 1:39.81 xfce4-terminal 4395 root 20 0 0 0 0 I 0,3 0,0 0:01.40 kworker/3:0-events 33705 xf0r3m 20 0 10404 4212 3452 R 0,3 0,1 0:00.36 top
Najwyższym priortem w tym przypadku jest działanie w czasie rzeczywistym, ale na tym etapie nie będziemy się tym zajmować. Tak więc na chwilę obecną najwyższym priortetem jest 1, a najniższym 39.
Do manipulacji priorytetami wartość nice (kolumna NI), przechowuje ona wartość wpływającą na priorytet zwiększając go lub zmieniajszając. Do ustawienia wartości nice służy polecenie renice. To polecenie możemy wykonać bez uprawnień administrator o ile zmniejszamy priorytet (podając wysoką wartość nice, która dodawana jest do domyślnej wartości priortetu), zmniejszenie priorytetu (podanie ujemnej wartości nice) wymaga już uprawnień superużytkownika. Polecenie to poza nową wartością nice wymaga podania PID-u procesu.
xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ pidof top 37426 xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ renice 20 37426 37426 (process ID) old priority 0, new priority 19 #lub krócej: xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ renice 20 $(pidof top) 37426 (process ID) old priority 0, new priority 19
Polecenie pidof
pozwala uzyskać
PID procesu na podstawie polecenia, jeśli uruchomionych jest więcej
niż jedna instancja danego programu polecenie zwróci listę PID-ów.
Podczas zmiany priorytetu mimo, iż podaliśmy wartość 20 to
największą wartością nice jest 19 tak samo jest w drugą
stronę. Najmniejszą wartością nice (a zatem proces będzie
mieć największy priortet) jest -19.
Priortety i manipulacja nimi miała dużo większe znaczenie w czasch gdy z jednego systemu korzystało wielużytkowników. Obecnie maja one mniejsze znaczenie. Warto też dodać, aby nie wymuszać wysokich priortetów, gdyż mogą one zablokować istotne dla funkcjonowania systemu procesy i go zdestabilizować.
8.8. Średnie obciążenia
Średnie obciążenia jest to o szaczowana liczba procesów do uruchomienia. Ten parametr określa ilość procesów gotowych w każdej chwili użyć procesora. Jak pamiętamy nie wszystkie procesy są gotowe do działania, część z nich czeka na dane. Średnie obciążenia są wyświetlane przez polecenie uptime.
xf0r3m@immudex:~$ uptime 10:40:15 up 3:03, 1 user, load average: 1,14, 0,99, 1,17
Wartości przedstawione obok etykiety
load average
przedstawiaja średnie
obciążenia z minuty, 5 i 15 minut. Na powszszym przykładzie widzimy
że ciągu ostatniej minuty z użyto na wykonanie procesów użytkownika
114% procent procesora. Jeśli obiążenia tak jak na przykładzie stale
utrzymują się powyżej 1, to oznacza to, że jeden proces cały czas
wykorzystuje jeden rdzeń procesora. Pełne obiążenie komputera w
przypadku tego wskaźnika będzie oscylować w granicach ilości
rdzeni/wątków procesora zamontowanego w naszym komputerze.
Wysokie średnie obciążenia mogą wynikać nie tylko z działania w systemie procesów ale również ze względu na pozostałą nie wielką ilość pamięci dostępnej w systemie. Wówczas jądro może zarządzić proces przeładowania (ang. trashing), powoduje to szybkie przenoszenie stron pamięci na dysk oraz z dysku. Gdy ma to miejsce ilość procesów gotowych do uruchomienia zwiększa sią powodując znacznie zwiększenie średniej obiążenia. Ze względu na małą ilość wolnej pamięci system może działać znacznie wolniej i niż zwykle.
8.9. Pamięc
Pamięć operacyjna jest bardzo ważnym komponentem komputera, jeśli chodzi o maszyny uniksowe. W pamięci rezydują obszary, w których procesy przechowują swoje dane, a jej ilość jest ograniczona. Do monitorowania pamięci możemy posłużyć się takimi narzędziami jak polecenie free (w przypadku tego polecenia, warto przeskalować sobie wartości zwracane za pomocą opcji -h) lub skorzystać z interfejsu systemowego wyświetlając zawartość pliku /proc/meminfo (tutaj jednak jestśmy skazani na wartość wyrażone w kilobajtach). Jeśli pamięć podręczna/bufora nie zajmuje dużego obszaru pamięci fizycznej, a mimo to nie mamy w zanadrzu wiekszej ilości wolnej pamięci to niezbędne może być dołożenie pamięci do naszego komputera, aby poprawić jego wydajność.
8.9.1. Zarządzenie pamięcią
Jądro w tym zadaniu opera się na jednosce MMU (ang. Memory Management Unit), której zadaniem jest zamiana adresów pamięci wirtualnej na adresu pamięci fizycznej. Pamięć wirtualna jest wykorzystywana przez procesy. Jądro współpracuje z MMU dzieląc obszary procesów na mniejsze strony, trzymając tym samym dane służące MMU do odzworowania adresów w strukturze danych zwanej tabelą stron. W momencie uzyskania przez proces dostępu do pamięci jednostka MMU dzięki tej strukturze może dokonywać translacji adresów.
Procesy zazwyczaj nie wymagają dostępu do pełnego obszaru w pamięci od razu. Jądro ładuje wówczas tylko te strony, których proces wymaga. Taki rodzaj przydzielania pamięci nazywany jest stronicowaniem na żądanie.
Przydzielenie pamięci nowemu procesowi, możemy zapisać w czterech krokach.
- Jądro ładuje do stron pamięci początek kodu programu
- Jeśli zajdzie taka potrzeba jądro może przypisać procesowi kilka stron pamięci.
- W trakcie działania procesu, może zajść potrzeba załadowania większej ilości kodu, ponieważ następna instrukcja do wykonania nie znajduje się na załadowanych początkowo stronach. W takiej sytuacji jądro przejmuje kontrolę, ładuje wymagane strony i pozwala programowi wznowic działanie.
- Jeśli zajdzie potrzeba przydzielenia więcej pamięci niż zakładano na początku, jądro znajduje nieużywane strony, zwalnia je i przydziela procesowi.
8.9.2. Błędy stron
Nie zawsze wyżej wymienione czynności da się spiąć w czasie. Jeśli żądana przez proces strona w pamięci nie jest jeszcze gotowa to generują on błąd strony. Gdy taki błąd zostanie wygenerowany, to kontrolę nad procesorem przejmuje jądro aby załadować żądaną stronę. Błędy dzielą się na podstawowe oraz drugorzędne.
Drugorzędne błedy stron
Błędy tego typu nię są poważnym błedami, i zdarzają się dość często. Ich najczęstszym powodem występowania jest fakt iż MMU nie zna położenia strony z instrukcjami programu. Samo MMU może nie mieć odpowiednio dużo miejsca aby przechować adresy wszystkich obszarów. W przypadku występowania takiego błędu jądro przekazuje do MMU informacje o położeniu strony i pozwala na wznowienie działania procesu.
Podstawowe błędy stron
Podstawowe błędy występują wówczas gdy strony nie ma w ogóle w pamięci. Jądro musi ją załadować z jakiegoś nośnika najczęsciej jest to dysk. Duża ilość tego typu błędów może przeciążyć system ponieważ procesor jest zajęty przez jądro ładujące kod z niekoniecznie szybkich nośników, blokując go tym samym dla innych procesów. Niestety pewna ilość tego typu błedów jest nie unikniona, a mają one miejsce kiedy po raz pierwszy uruchamiamy jakiś program, wówczas należy załadować kod z dysku.
Obserowowanie błędów stron
Do obserwowania błędów stron przydatne staje się polecenie
time, o którym wspomniałem przy okazji pomiaru
czasu procesora. Jednak w tym przypadku będzie interesować nas
wartość w nawiasie obok napisu
pagefaults
.
xf0r3m@immudex:~$ /usr/bin/time timedatectl Local time: nie 2023-01-08 17:59:08 CET Universal time: nie 2023-01-08 16:59:08 UTC RTC time: nie 2023-01-08 16:59:08 Time zone: Europe/Warsaw (CET, +0100) System clock synchronized: no NTP service: n/a RTC in local TZ: no 0.00user 0.01system 0:00.10elapsed 24%CPU (0avgtext+0avgdata 7416maxresident)k 522inputs+0outputs (8major+359minor)pagefaults 0swaps
W przypadku uruchomienia w moim systemie polecenia
timedatectl
wystąpiły 8 błędów
podstawowych (8major
) oraz 359
błedów drugorzędnych.
Inne narzędzia takie top oraz ps również mogą wyświetlać informacje o błędach stron pamięć, w przypadku polecenia top należy włączyć wyświetlanie nMaj oraz nMin, aby przejść do konfiguracji należy nacisnąć literę f, a następnie postępować zgodnie z instrukcją. Program ps pozwala na wyświetlenie błędów strony poprzez podanie niestandardowego wyświetlania kolumn (opcja -o) kolumny noszą kolejno nazwy maj_ftl i min_ftl.
8.10. Monitorowanie wydajności za pomocą polecenia vmstat
Polecenie vmstat pozwala na monitorowanie wielu aspektów wydajności systemu, a jest jednym z najstarszych narzędzi tego typu. Dane wyświetlane również pozostawiają wiele do życzenia i osoba nie mająca styczności z tym narzędziem, może uznać je za mało czytelne.
xf0r3m@immudex:~$ vmstat 2 proc. -----------pamięć---------- ---swap-- ---we/wy--- -system-- ------cpu----- dz bl swap wolna bufor cache si so bi bo in cs uż sy be io sk 0 0 0 861264 13196 5331448 0 0 56 3 189 210 13 4 83 0 0 0 0 0 861112 13196 5330624 0 0 0 0 2071 4073 8 4 88 0 0 0 0 0 861712 13196 5330664 0 0 0 0 1944 3661 7 4 88 0 0 0 0 0 862012 13196 5330608 0 0 0 0 2007 3866 7 4 89 0 0
Polecenie to przyjmuje jako argument interwał czasowym co ile sekund
ma wyświetlać nowe statystki. Każda linia to w tym przypadku
statystyki pobrane co dwie sekundy. Wyjście polecenia zawiera
tematyczne kolumny. Pierwszą z nich są procesy, ta kolumna zawiera
jeszcze dwie inne kolumny wskazujące procesy gotowe do uruchomienia
(dz
) oraz te
zablokowane (bl
). W następnej
kolumnie znajdują się informacje na temat
pamięci, a w niej informacje o wykorzystaniu przestrzeni wymiany
(swap
), ilości wolnej
pamięci, pamięci przeznaczonej na bufor oraz pamięci przeznaczonej
na pamięć podręczną. W trzeciej kolumnie znajdują się informacje o
przestrzeni wymiany, ile stron zostało przeniesionych na dysk
(si
) oraz ile stron zostało
załadowanych z dysku do pamięci
(so
). Czwarta kolumna zawiera
informacje o użyciu urządzeń wejścia-wyjścia, dane odczytane z dysku
(bi
) oraz dane zapisane na dysku
(bo
). Piąta kolumna zwiera
informacje systemowe, w niej znajdują się liczniki wywołań
systemowych (in
) oraz przełączeń
kontekstu (cs
). Ostatnia kolumna
zawiera procentowe zużycie czasu procesora dla kolejno: aplikacji
użytkownika (uż
), jądra oraz obsługi
procesów (sy
), stanu bezczynności
(be
), czasu przeznaczonego na
obsługę operacji wejścia-wyjścia
(io
), czasu skradziony wirtualnej
maszynie (sk
).
Polecenie to zawiera wiele przydanych opcji, które są zawarte na stronie podręcznika programu. Jak na przykład opcję -d, która pozwala na monitorowanie dysków.
8.11. Monitorowanie operacji wejścia-wyjścia
Na dystrybucje Linuksa dostępnych jest kilka narzędzi służących do monitorowania operacji wejścia-wyjścia, które w dużej mierze są operacjami dyskowymi.
8.11.1. Polecenie iostat
Jedno znich przypomina omawiany w wcześniejszym podrozdziale program vmstat, a jest nim polecenie iostat, to polecenie może nie być domyślnie zainstalowane i jeśli chcemy z niego skorzystać to należy je zainstalować. Pakiet zawierający ten program zajduje się w repozytoriach Debiana po nazwą sysstat. Poniżej znajduje się przykład:
xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ iostat
Linux 5.10.0-20-amd64 (immudex) 10.01.2023 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
17,27 0,00 4,22 0,01 0,00 78,50
Device tps kB_read/s kB_wrtn/s kB_dscd/s kB_read kB_wrtn kB_dscd
dm-0 0,35 3,52 0,61 0,00 25273 4396 0
loop0 6,27 73,04 0,00 0,00 524237 0 0
sda 1,55 66,41 0,56 0,00 476690 4048 0
Oryginalny wydruk jest kolorowy. Ciemno niebieskie pola, mogą być
trochę nieczytelne, ale oznaczają one wartość
0,00
. Pierwsza linia zawiera nazwę
jądra, nazwę hosta w nawiasie, aktualną datę, architekturę procesora
oraz liczbę logicznych procesorów (rdzenie/wątków). Druga oraz
trzecia linia zawierają średnie zużycie czasu procesora oraz
objaśnienia tych wartości. Poniżej znajduje się tabelka
przedstawiająca urządzenia (Device
),
liczba transferów na sekundę (liczba operacji wejścia-wyjścia na
sekundę wystosowana wobec urządzenia,
tps
); liczba danych odczytanych na
sekundę (kB_read/s
); liczba danych
zapisanych na sekundę (kB_write/s
);
liczba danych odrzuconych na sekundę dla urządznia
(kB_dscd
), ostatnie trzy kolmny
wrażają podobne wartości tylko zamiast prędkości jest przedstawiona
tam łączna ilość.
Domyślnie dane wyrażane są w kilobajtach, jak prawie wszystko w systemie co dotyczy pamięci masowych, jednostki możemy przeskalować do megabajtów za pomocą opcji -m, natomiast za pomocą opcji -p wraz z argumentem ALL możemy wyświetlić statystki dla wszystkich urządzeń blokowych dostępnych w systemie. Podobnie do vmstat podanie gołej liczby jako argumentu spowoduje włączenie interwału czasowego o podanej wartości. Więcej opcji oraz bardziej szczegółowe wyjaśnienia znajdują się na stronie podręcznika programu.
8.11.2. Polecenie iotop
Innym poleceniem służącym do monitorowania operacji wejścia-wyjścia jest program iotop, zasada działania tego programu jest podobna do znanego nam już narzędzia top tylko tym razem zamiast skupiać się na procesach, położono nacisk na operacje wejścia wyjścia.
xf0r3m@immudex:~$ sudo iotop Total DISK READ: 0.00 B/s | Total DISK WRITE: 0.00 B/s Current DISK READ: 0.00 B/s | Current DISK WRITE: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init 2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd] 3 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_gp] 4 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_par_gp] 6 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kworker/0:0H-events_highpri] 8 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [mm_percpu_wq] 9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_tasks_rude_] 10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_tasks_trace] 11 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0] 12 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_sched] 13 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0] 15 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [cpuhp/0] 16 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [cpuhp/1] 17 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1] 18 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/1]
Jak możemy zauważyć to polecenie wyświetla nam identyfikatory wątków.
Jest to jedno z nielicznych narzędzi wyświetlających wyświetlających
wątki, ponieważ często procesy dzielą się na nie aby własnie
zająć się obsługą wejścia-wyjścia. Inna kolumną wartą opisania jest
PRIO
, podobnie jak procesy jądro
stara się szergować operacje wejścia-wyjścia. Przyczym priorytet
tutaj dzieli się na dwie wartości, klasę oraz poziom samego
priorytetu. Wątki o priorytecie
be/0
otrzymają więcej czasu
procesora na realizacje operacji niż wątki z priorytetem
be/4
. Samych klas priorytetów
mogą wystąpić trzy rodzaje takie jak:
- be(best-effort) - w przypadku tego priorytetu jądro stara się jak najbardziej sprawiedliwie uszeregować operacje wejścia-wyjścia. Tę klasę posiada większość operacji.
- rt(real-time) - Jądro bezwarunkowo szereguje operacje opatrzone tą klasą ponad każdą inną.
- idle, klasa bezczynności. Jądro będzie wykonywać operacje z tą klasą, tylko wtedy gdy nie jest wykonywana żadna inna operacja. Ta klasa nie posiada żadnych poziomów, będąc zarazem najniższym priorytetem.
Do manipulacji priortetami operacji wejścia-wyjścia służy polecenie ionice, więcej na jego temat znajdziemy na stronie podręcznika.
8.12. Monitorowanie procesów za pomocą narzędzia pidstat
Za pomocą polecenia top możemy śledzić wykorzystanie zasobów przez wybrany proces. Problem w przypadku tego rozwiązania jest brak poprzednich wartości, ponieważ dane odświerzane są co interwał czasowy (w przypadku top jest to jedna sekunda). Rozwiązaniem tej niedogodności może być zastosowanie narzędzia pidstat, ponieważ zachowuje on się tak jak vmstat, ale dla procesów. Jeśli nie podamy konkretnego PID-u, polecenie wyświetli wszystkie procesy, które zostały uruchomione w systemie. Polecenie to nie jest domyślnie dostępne w dystrybucjach opartych na Debianie, jest ono częścią pakietu sysstat, ten pakiet zawiera również polecenie iostat.
xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ pidstat Linux 5.10.0-20-amd64 (immudex) 11.01.2023 _x86_64_ (4 CPU) 17:02:56 UID PID %usr %system %guest %wait %CPU CPU Command ... 17:02:56 1001 2820 4,84 0,99 0,00 0,08 5,83 1 mpv 17:02:56 1001 2886 0,04 0,01 0,00 0,00 0,05 1 ranger 17:02:56 1001 3366 0,18 0,04 0,00 0,00 0,21 1 atril 17:02:56 1001 3398 0,00 0,00 0,00 0,00 0,00 0 WebKitNetworkPr 17:02:56 1001 3805 0,03 0,01 0,00 0,00 0,04 1 vim.gtk3 17:02:56 1001 3829 0,00 0,00 0,00 0,00 0,00 2 bash 17:02:56 0 4176 0,00 0,01 0,00 0,00 0,01 0 kworker/u8:2-kcryptd/254:0 17:02:56 0 5013 0,00 0,00 0,00 0,00 0,00 0 kworker/u8:3-i915 17:02:56 1001 5610 0,00 0,00 0,00 0,00 0,00 0 Web Content 17:02:56 1001 28551 0,00 0,00 0,00 0,00 0,00 2 pidstat
Polecenie to w pierwszej linii zwraca nam podsumowanie odnośnie
systemu (wersję jądra, nazwę hosta, architekturę procesora oraz
ilość rdzeni/wątków) i datę. Następnie wyświetlana jest tabela z
procesami, w kolumnach kolejno od lewej znajduje się czas wywołania
polecenia; UID użytkownika, który ten proces zainicjował; PID
procesu. Następne pięć kolumn pokazuje informacje o zużyciu czasu
procesora. Kolumna %wait
jest
procentowym żużyciem czasu procesora poświęconym na operacje
wejścia-wyjścia. Ciekawą wartością, nie zwracaną przez inne polecenia
jest kolumna %guest
, która
przedstawia zużycie procesora na potrzeby zadań wykonywanych przez
maszynę wirtualną. Kolejnymi kolumnami są sumaryczne procentowe
zużycie czasu procesora; wskazanie, z którego z rdzeni/wątków
procesora korzysta ten proces oraz nazwa polecenia.
Uruchomienie tego polecenia dla pojedyńczego procesu wymaga podania opcji -p wraz z wartością, którą jest PID procesu oraz najlepiej podanie interwału czasowego w postaci suchej liczby po PID-zie.
xf0r3m@immudex:/media/xf0r3m/immudex_crypt0$ pidstat -p 2820 2 Linux 5.10.0-20-amd64 (immudex) 11.01.2023 _x86_64_ (4 CPU) 17:32:37 UID PID %usr %system %guest %wait %CPU CPU Command 17:32:39 1001 2820 13,50 5,00 0,00 0,50 18,50 1 mpv 17:32:41 1001 2820 13,00 3,00 0,00 0,00 16,00 0 mpv 17:32:43 1001 2820 14,00 4,50 0,00 0,50 18,50 0 mpv 17:32:45 1001 2820 12,50 4,00 0,00 0,00 16,50 1 mpv 17:32:47 1001 2820 14,00 4,00 0,00 0,00 18,00 1 mpv 17:32:49 1001 2820 15,00 2,50 0,00 0,00 17,50 3 mpv 17:32:51 1001 2820 14,00 2,50 0,00 0,50 16,50 2 mpv 17:32:53 1001 2820 15,00 3,50 0,00 0,00 18,50 1 mpv 17:32:55 1001 2820 11,50 5,50 0,00 0,00 17,00 3 mpv ^C Średnia: 1001 2820 13,61 3,83 0,00 0,17 17,44 - mpv
Przerywając działanie polecenia przed jego zakończeniem wyświetli nam o ono jedną dodatkową linię zawierającą średnią poszczególnych wartości uzyskanych podczas działania polecenia.
Więcej przydanych opcji dla tego polecenie znajduje się na stronie podręcznika.
8.13. Informacje dodatkowe
Powodem istnienia tak szerokiej gamy różnych narzedzi do monitowania wydajności jest możliwość użycia, nie których komponentów na różne sposoby. Najprostszym przykładem jaki przychodzi mi teraz na myśl jest wykorzystanie pamięci operacyjnej jako dysku. Inną rzeczą jest, iż te zasobo są ograniczone, a takie systemy jak serwery sieciowe wymagają stałego i intesywnego monitoringu. Dlatego jeśli chcemy lub musimy monitorować systemy, którymi zarządzamy lub będziemy zarządzać warto zapoznać się z poniższymi zagadnieniami.
- sar (raportowanie dotyczące aktywności systemu). Pakiet sar oferuje możliwości monitorowania systemu podobne do programu vmstat, ale również rejestruje on wykorzystanie zasobów w czasie. Przez co możemy sprawdzić jak wyglądało zużycie zasobów podczas wykonywania wcześniejszych zadań.
- acct (ewidencja procesów). Ten pakie z kolei zajmuje się ewidencjonowanie procesów, a nie monitorowaniem wydajności, nie mniej jednak funkcja sprawdzenia jakie procesy działały powiedzmy kilka dni temu może być przydatna.
- Przydziały przy użyciu przydziałów możemy nałożyć limity wykorzystania podstawowych zasobów (czas procesora, pamięć operacyjna, czy przestrzeń dyskowa). Niestety jest to funkcja systemu PAM więc podlegją im procesy uruchomione w obrębie czegoś co z niego korzysta, na przykład powłoka logowania.
9. Sieć
Komputery służą do przetwarzania informacji. Informacje wykorzystywane do obliczeń mogą pochodzić z różnych źródeł a jednym z nich może być inny system komputerowy. Jednak, aby to miało miejsce komputery muszą się w jakiś sposób komunikować ze sobą. W celach w miarę swobodnej komunikacji między systemami, komputery łączy się w sieci. Połączenie ze sobą dwóch komputerów za pomocą jednego kabla również możemy nazwać się siecią, ponieważ mechanizmy oraz komponenty związane z komunikacją pozozstają takie same jak dla większej ilości komputerów. Zagadnienia łączności sieciowej zostały w taki sposób zdefiniowane, aby były niezależne od systemu operacyjnego, ale to systemy definiują narzędzia do ich konfiguracji. W tym rodziale zapoznamy się z konfiguracją sieci w Linuksie.
9.1. Podstawy sieci
Dla łatwiejszego zrozumienia sieci, zaczniemy od zapoznania się z nazewnictwem, niektórych jej elementów. Nasz komputer podłączony do sieci nazywany jest hostem. Hosty często są podłączone do sieci lokalnej określanej jako LAN. Sieci tego typu są wszechobecne. Tego typu sieci posiadamy w naszych domach, nie przekraczają one swoim obszarem działania jednego budynku lub grupy pomiesczeń. Składają się one z routera łączącego sieć internet z siecią LAN oraz (zazwyczaj) z podłączonych do niego hostów. Definicja LAN-u nie ogranicza się tylko do hostów podłączonych za pomocą przewodów, łączność może zostać zapewniona tutaj bezprzewodowo nie ma na tym polu żadnych ograniczeń.
Poza możliwością podłączenia sieci LAN do internetu routery klasy SOHO (Small Office/Home Office) posiadają wiele usług przez co konfiguracja i obsługa hosta w sieci staje się bezobsługowa. Kilka z tych usług omówimy sobie w kontekscie Linuksa w tym rozdziale.
9.1.1. Pakiety
Jeśli żądana strona internetowa ma zostać przekazana do chcącego ją obejrzeć użytkownika, musi zostać specjalnie przygotowana do transmisji sieciowej. Takim przygotowaniem zajmują się poszczególne warstwy stosu TCP/IP. Dane strony (powiedzmy kod) przechodzi z jednej warstwy do drugiej będąc zamienianym w pakiety zrozumiałe dla tych samych warstw po drugiej stronie transmisji. Dane są przetwarzane przez poszczególne warstwy stosu aż staną się możliwe do przesłania przez fizyczny nośnik w postaci kabla miedzianego, światłowodu lub fal radiowych. Pakiety składają się najczęsciej z danych z poprzednej warstwy (zwanych ładunkiem) oraz dedykowanego tej warstwie nagłówka. Przetwarzanie danych przez stos TCP/IP nazwywane jest enkapsulacją Dwie warstwy stosu zostaną opisane w tym rodziale. Warstwą najwyższą zajmiemy się następnym rodziale. Natomiast warstwę najniższą omówimy sobie w dużym uogólnieniu.
9.1.2. Stos TCP/IP
W stosie TCP/IP możemy wyróżnić cztery różne warstwy:
- Warstwa aplikacji - określa sposób komunikacji między aplikacjami. Wewnątrz tej warstwy rezydują protokoły aplikacji wykorzystywane przez użytkowników takiej np. HTTP.
- Warstwa transportowa - określa sposób transmisji danych z warstwy aplikacji. W tej warstwie znajdują się protokoły takie jak TCP oraz UDP. Tutaj także rezyduje pojęcie portu. Warstwa transportowa będzie jeszcze omawiana w tym materiale.
- Warstwa internetowa - określa sposób dostarczania pakietów z hostów źródłowych do docelowych i odwrotnie. W tej warstwie rezyduje protokół IP oraz protokoły odpowiedzialne za trasowanie (znalezienie jak najlepszej drogi z jednego do drugiego hosta). W tej warstwie znajduje się również protokół ICMP odpowiedzialny z diagnozowanie problemów z protokołem IP.
- Warstwa łącza - ta warstwa definuje topologię logiczną sieci. Okresla ona metody transmisji danych przez użyty w sieci nośnik fizyczny. W tej warstwie znajdują się takie protokoły jak Ethernet oraz standard 802.11, definiujący transmisję bezprzewodową.
Warto wspomnieć o tym, że w dystrybucjach Linuksa trzy z czterch warstw znajdują się w jądrze systemu. Mogą jednak pojawić się wyjątki, wówczas pakiet może trafic do przetworzenia w przestrzeni użytkownika.
9.2. Warstwa sieciowa
Jak wiemy warstwa sieciowa definiuje sposoby dostarczenia pakietów z jednego do drugiego hosta. Ze względu na to, iż będziemy zajmować się sieciami internetowymi, skupimy się na jednym protokole tej warstwy na - protokole IP. Ten protokół posiada kilka funkcji, jednak dla nas na tym etapie najważniejsza będzie jedna z nich - adresacja. Każdy host aby mógł komunikować się w sieci musi mieć przypisany taki adres. Adres ten składa się (a przynajmniej w wersji 4 protokołu IP) z czterech grup liczb z zakresu od 0 do 255 rozdzielonych kropkami. Na przykład adres IP mojego komputera to:
192.168.8.101
Uzyskałem te informacje za pomocą poniższych poleceń. Te polecenia są tożsame:
xf0r3m@immudex:~$ ip address show #lub xf0r3m@immudex:~$ ip a
Drugi zapis jest skrótem pierwszego polecenia. Analizując wynik działania tego polecenia musimy zwrócić uwagę na dwie rzeczy. Pierwszą z nich są "dziwne" nazwy intefejsów sieciowych. Jeśli nikt nie korzystał z dystrybucji Linuksa przed 2016 rokiem to wówczas nie mógł spotkać się z klasycznymi nazwami interfejsów sieciowych. To jest pierwszy interfejs przewodowy = eth0 i tak dalej do ethX. Miało to jedną podstawową wadę znaną już z dysków. Otóż po którymś uruchomieniu ponownym system mógł wykryć karty sieciowe w innej kolejności niż poprzednio co powodowało problemy ze statyczną (zapisana na stałe w systemie) konfiguracją sieciową, ponieważ adresy niezgadzały się z adresacją podpiętej sieci. Obecnie ich nazwy są zaczerpnięte z modułów jądra, które są wykorzystywane do ich obsługi. Nie wszystkie dystrybucje stosują nową nomenklaturę dla interfejsów sieciowych. W dużej mierze są to systemy korzystające z innych programów typu init niż systemd, choć co prawda nie jest to regułą.
9.2.1. Adresacja IP
Inną rzeczą jest sposób prezentowania adresów IP. W linii
rozpoczynającej się od słowa inet
znajduje się adres IP przedstawioy w ten sposób
192.168.8.101/24
. Jest to zapis w
notacji CIDR. Na zapis ten składa się adres IP
oraz maska podsieci w postacji ilości bitów.
Maska podsieci jest rodzajem adresu IP, który określa gdzie i jakie liczby z zakresu możemy wpisać do adresów IP hostów. Wyzanczając tym samym pierwszy oraz ostatni adres w sieci (początek i koniec sieci), na podstawie maski możemy określić ilość hostów w sieci. W jaki sposób się to dzieje? Otóż adres IP jak i maskę możemy przedstawić w postaci binarnej. W na poprzedni przykładzie maska wynosiła 24 bity. Co to oznacza? Aby sobie to wyjaśnić przedstawimy adres IP w postaci binarnej.
192.168.8.101 = 11000000.10101000.00001000.01100101
Z adresem poszło w miarę łatwo, ponieważ znaliśmy konkretne liczby. Jeśli policzymy wszystkie cyferki w postaci binarnej otrzymam wynik 32 cyfr, z racji tego że są to wartości binarne bardziej mówimy o bitach - podstawowej jednostce informacji, klasycznemu 0 oraz 1. Adresy IP w wersji 4 (można zapisać to skrótowo IPv4) mają długość 32-bitów. Powyżej omawiając maskę podsieci zostało wspomniane, że maska wyznacza początek oraz koniec sieci. Adres IP możemy przyrównać do adresów pocztowych, kolejne jego części mogą wskazywać na miasto, ulicę, budynek oraz na mieszkanie. Adresy pocztowe zawierają zazwyczaj stałe elementy jak nazwy miast, ulic czy numerację budynków. Tak samo jest w przypadku adresów IP. Te stałe elementy wydzielane są maskę podsieci. Mając maskę w postaci ilości bitów z notacji CIDR, możemy zapisać ją w ten sposób.
/24 = 11111111.11111111.11111111.00000000 /24 = 255.255.255.0
Więc zapis maski w notacji CIDR, to nic innego jak ilość bitów o
wartości jeden (1) zapisanych od lewej w
reprezentacji binarnej adresu
IP. No dobrze, ale 32 - 24 = 8. Co zrobić z tymi 8 bitami? Otóż te
bity pozostają do dyspozycji administratora i stanowią przestrzeń
adresową. Maskę podsieci można podzieli na dwie częsci. Jedną częścią
jest część sieciowa, która wyznacza stałą część adresu IP w adresacji
hostów w sieci i ta część adresu IP jest niezmienna podczas adresacji.
Drugą częścią jest część hosta, wskazująca, która część adresu IP
będzie zawierać konkretną wartość wskazującą na hosta w sieci. Teraz
złożymy ze sobą adres IP oraz maskę zapisaną w postaci dziesiętnej
(255.255.255.0
).
Adres IP: 192.168.8.101 Maska: 255.255.255.0
Poszczególne części adresu IP odzielone kropkami noszą nazwe oktetów, ponieważ w reprezentacji binarnej każda z częsci posiada 8 bitów. Zatem przyglądając się powyższemu przykładowi możemy dojść do wniosku, że czwarty ostatni oktet będzie przeznaczony na częśc hostów, i tylko on będzie się zmieniać podczas adresacji kolejnych hostów w tej sieci.
Warto zaznaczyć że przy prostych sieciach, maski mogą pozostać typowe tj. 24, 12 oraz 8 bit. Gdzie niegdzie spotykałem się również z maską 16 bitową. Dającą dwa oktety na część przeznaczną dla hostów. W przypadku 12-bitowej maski, nie ma już takiego eleganckiego podziału. Część bitów z drugiego oktetu pozostanie w części sieciowej, a druga część przejdzie do części hosta. Rozpiszmy sobie ten przykład. Załóżmy że nasz komputer jest podłączony do sieci 172.16.X.Y/12 i chcielibyśmy dowiedzieć się jak duża jest ta podsieć. Za stałą możemy przyjąć, że jeśli na konkretnym oktecie maski występuje 0, to ten sam oktet adresu IP może przyjąć wartości od 0 do 255.
172.16.0.0/12 10101100.00010000.00000000.00000000 = 172.16.0.0 11111111.11110000.00000000.00000000 = /12 = 255.240.0.0 #Część bazowa adresu IP po usunięciu wszystkich bitów z zermi na masce: 10101100.0001 10101100.00010000.00000000.00000000 = 172.16.0.0 1. 0000 = 172.16 2. 0001 = 172.17 3. 0010 = 172.18 4. 0011 = 172.19 5. 0100 = 172.20 6. 0101 = 172.21 7. 0110 = 172.22 8. 0111 = 172.23 9. 1000 = 172.24 10. 1001 = 172.25 11. 1010 = 172.26 12. 1011 = 172.27 13. 1100 = 172.28 14. 1101 = 172.29 15. 1110 = 172.30 16. 1111 = 172.31 10101100.00011111.00000000.00000000 = 172.31.0.0 (16 x 256 ^ 2) - 2 = 1048574
Myślę że powyższy przykład jasno przedstawia jak wygląda podział klasowych sieci. A co jeśli będziemy chcieli rozszerzyć sieci 192.168.8.0/24? Jeśli założym, że potrzebowalibyśmy kolejne 255 adresów to wówczas wystarczy zabrać z maski 1 bit. Da to maskę 23-bitową a my swojej sieci będziemy mogli zaadresować hosty od 192.168.8.1 - 192.168.9.254.
Każdy komputer posiadający skonfigurowaną w ten sposób warstwę internetową/sieciową może komunikować się z hostami w sieci. Konfigurowanie warsty sieciowej, jest chyba jedyną czynnością, w w której użytkownik będzie mieć styczność z stosem TCP/IP. Jednak aby móc skomunikować się za pośrednictwem routera z internetem potrzebujemy jeszcze kilku informacji.
9.2.2. Routowanie oraz tabela routingu
Każdy funkcjonujący w sieci system musi posiadać źródło informacji na temat gdzie należy przesłać pakiety lub jakiego interfejsu do tego celu należy użyć. Czynność ustalenia optymalnej trasy dla pakietu nazywa się routowaniem, natomiast informacje na temat jakie sieci są osiągalne i przez jakie interfejsy znajduje się w tabeli routingu. W systemie, który nie jest jakmiś routerem, tabela routingu będzie zawierać prawdopodobnie tylko dwa wpisy. Na poniższym przykładzie widnieje zrzut tabeli routingu z mojego komputera.
default via 192.168.8.1 dev enp0s31f6 onlink 192.168.8.0/24 dev enp0s31f6 proto kernel scope link src 192.168.8.154
Pierwsza linia zawiera definicję bramy domyślnej,
którą zajmiemy się za chwilę. Natomiast druga linia wskazuje jaka
sieć jest osiągalna przez jaki interfejs w tym przypadku sieć
192.168.8.0/24
jest dostępną przez
interfejs enp0s31f6
.
W systemach, które łączą ze sobą sieci (routerach) wpisów w tabeli routingu takich jak druga linia może być znacznie więcej. Możemym sobie wówczas zadać pytanie na jakiej podstawie jądro wybiera właściwy port. Otóż jądro przy wyborze będzie bazować na dwóch czynnikach. Pierwszy z nich jest oczywisty i chodzi tu adres częsci sieciowej adresu IP, musi on pasować do adresu sieci inaczej ma się rzecz jeśli jedna z sieci jest na tyle duża, że obejmuje swoim zakresem także adresacje innej sieci. Tutaj wówczas pojawia się drugi czynnik, którym jest długość maski/części sieciowe. Czasami nazywanej także prefiksem. Jeśli przy którymś z wpisów adresy sieci zachodzą na siebie, to wówczas dłuższy prefiks (a co za tym idzie, mniejsza sieć) jest wybierana jako trasa dla pakietu.
9.2.3. Brama domyślna
Omawiając tablę routingu wspomnieliśmy o bramie domyślnej.
Brama domyślna jest rodzajem wpisu we wspomnianej tabeli a jej
zadaniem jest przechowywanie domyślniej trasy, która wybierana jest
jeśli żadna inna nie jest odpowiednia. Wynika to z faktu jak jądro
wybiera właściwe dla pakietów trasy oraz adresu jaki kryje się za
słowem default
a jest nim
0.0.0.0/0. Oznacza on wszystkie hosty w adresacji
IPv4. Co daje najmniejszy możliwy prefiks, dlatego też jeśli komputer
nie posiada właściwej dla pakietu trasy to jest ona przekzywana
do adresu przez który taka sieć jest osiągalna.
Tym adresem jest przeważnie adres routera, który łączy sieć LAN z inną siecią np. siecią usługodawcy internetowego. Zwyczajowo takie urządzenia zwykło się nazywać routerami, mimo ich możliwość to najczęściej wykorzystywaną funkcjonalnością jest automatyczna konfiguracja hostów (protokół DHCP, będzie o nim w tym rozdziale), pamięć podręczna i przekazywanie zapytań systemu DNS (o systemie DNS, też będzie tutaj) oraz translacja adresów (o adresach prywatnych oraz translacji adresów też sobie wspomnimy) oraz przekazywanie pakietów dalej do jednego z routerów usługodawcy, więc równie dobrze urządzenia tego typu można nazwać bramkami.
9.3. Adres IPv6
Adres IPv4 posiada długość 32 bitów, co daje nam możliwość zaadresowania 4,3 miliarda hostów w Internecie. Co jest o wiele mniejszym wynikem niż ilość ludzi na świecie, biorąc pod uwagę rozwój przedsiębiorstw oraz samego internetu wymaga od organizacji czuwających nad jego standaryzacją rozwiązań, które są w stanie sprostać wymaganiom czasów obecnych oraz przyszłości. Jedym z takich rozwiązań są prace na rozwojem oraz wdrożeniem nowego standardu jakim protokół IP w wersji 6.
Adres IP w wersji 6, ma długość 128-bitów, co czyni go 4 razy dłuższym od adresu IPv4. Adres ten nie przypomina adresu z poprzedniej wersji. Zamiast cyfr dziesiętnych użytko cyfr systemu heksadecymalnego, a kropki zastąponio dwukropkami. Poniżej znajduje się adres IPv6 komputera na którym pisze ten tekst.
fe80::921b:eff:fe6a:717d/64
Cyfry heksadecymalne są reprezentowane przez znaki: 0-9 oraz a-f. Każda grupa cyfr jest odzielona dwukropkiem, a każda z nich posiada po 4 znaki. Jeśli zero jest napoczątku grupy, to można je pominąć, jeszcze inną zależność jak możemy zauważyć jest to, jeśli grupa składa się z samych zero to można ją pominąć, wówczas w zapisie adresu widnieją dwa dwukropki obok siebie. Nie tyczy się to tylko jednej grupy, ale jeśli grupy zer następują po sobie to rownież można je pomniąć. Zapis natomiast nie ulegnie zmianie, jesli będzie więcej niż jedna grupa zer do pominięcia.
Jak możemy zauważyć adresy IPv6 również możemy zapisać w notacji CIDR. Tutaj maska wynosi 64-bit, co oznacza ze połowa adresu zajmuje prefiks. Natomiast druga połowa jest już częścią hosta.
Adresy IPv6 możemym podzielić na globalne, która mogą być osiągalne z internetu lub lokalne, które są przypisywane automatycznie przez systemy z obsługą IPv6. Każdy z tych typów posiada swój prefix i dla adresów globalnych (znanych także jako globalny adres hosta) prefix wynosi 2000::/3, przez co globalny adres hosta zaczyna się od 2 lub od 3 (ze zwględu na prefix, 3 pierwsze bity pierwszej cyfry są stałe (001), a zatem do dyspozycji mamym tylko jeden bit, którego zmiana nie dam nam innych wartości jak 2 lub 3). Pozostałe zera służa jako dopełnienie do zapisu. Z kolei prefiksem dla adresów lokalnych jest fe80::/10. W przypadku adresu łącza lokalnego resztę częsci sieciowej adresu wypełniają 54-bity zer. Na podstawie przedstawionych tutaj prefiksów możemy ustalić z jakiego rodzaju adresem mamy doczynienia.
9.3.1. Wyświetletnie adresów IPv6
Do wyświetlenia adresów IPv6 może posłużyć nam to samo polecenie,
z którego korzystaliśmy do wyświetlenia adres IP w wersji 4.
Mianowicie polecenie ip
, po za
samymi podpoleceniami tego polecenia dla adresów IPv6 podaje się
także opcję -6.
$ ip -6 addr show
Podobnie rzecz ma się adresami IP tras, tutaj wykorzystuje się tę samą opcją natomiast inne podpolecenia.
$ ip -6 route show
9.4. Podstawowe narzędzia ICMP oraz DNS
W tym podrozdziale omówimy sobie podstawowe narzędzia wykorzystywane przy połączeniach sieciowych. Służą one do ustalania adresów IP oraz sprawdzania dostępności hosta.
9.4.1. Narzędzie ping
Protokół ICMP jest protokołem diagostycznym dla protokołu IP. Do jego podstawowych zadań należy sprawdzenie dostępności hosta w sieci. Do tego celu wykorzystujemy narzędzie ping. To niepozorne narzędzie wysyła specjalny pakiet z żądaniem odesłania pakietu z odpowiedzią. W przypadku tego narzędzia istotna jest sama odpowiedź ale poza nią zwracanych jest kilka statystyk, które pozwolają na przykład na określenie jakość połączenia oraz czasem stanu samej sieci (gdy odpowiedzi z bramy domyślnej przychodzą po ok. 1 sekundzie).
xf0r3m@immudex:~$ ping wp.pl PING wp.pl (212.77.98.9) 56(84) bytes of data. 64 bytes from www.wp.pl (212.77.98.9): icmp_seq=1 ttl=54 time=11.2 ms 64 bytes from www.wp.pl (212.77.98.9): icmp_seq=2 ttl=54 time=11.0 ms 64 bytes from www.wp.pl (212.77.98.9): icmp_seq=3 ttl=54 time=14.1 ms ^C --- wp.pl ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms rtt min/avg/max/mdev = 11.049/12.096/14.062/1.391 ms
Na 56-bajtowe żądanie echa wysłane z naszego komputera
host docelowy odpowiada 64-bajtowym pakietem. Jego zawartość nie
jest istotna. Dla nas ważne są pola
icmp_seq
oraz
time
, gdyż swiadczą on one jakości
połączenia pomiędzy naszym komputerem, a hostem docelowym.
icmp_seq
wskazuje kolejności
otrzymywanych odpowiedzi. Jeśli jakiegoś pakietu brakuje, oznacza to
że został on zgubiony podczas transmisji. Zagubione pakiety
zdarzają się gdy korzystamy z połączenia bezprzewodowego przy słabym
zasięgu. Drugim ważnym polem jest czas wyrażony w kolumnie
time
. Czas poniżej 30 ms oznacza
że mamy bardzo dobre połącznie z hostem. Czas w sieci lokalnej
oscyluje poniżej 1 ms. Natomiast jeśli jest powyżej jednej sekundy
(1000 ms) lub w jej granicach oznacza, że gdzieś w sieci może
znajdować się problem. Polecenie ping w dystrybucjach
Linuksa ale i ogólnie w implementacji Uniksowej działa do momentu,
aż nie przerwiemy mu działania. Na sam koniec zostanie wyświetlona
statystyka zawierająca informacje o tym ile pakietów zostało
wysłanych ile zostało odebranych, jaki procent pakietów został
utracony. W drugiej linii podsumowania znajduje się podstawowe
statystki związane z odpowiedzia na żądania.
Ze względów bezpieczeństwa nie wszystkie hosty (z właszcza w internecie są w stanie odpowiedzieć na żądania narzędzia ping. Pakiety mogą zostać do nich wysłane, ale jest wielce prawdopodobne, że zostaną zablokowane po drodze. Takie zachowanie jest przecidziałaniem przeciwko atakowi odmowy usługi, który opiera się na przesłaniu wielu żądań (echo request, typ pakietu nazywa się właśnie w ten sposób), tak aby host nie był w stanie obsłużyć żadnego innego połączenia sieciowego.
Istnieje możliwość wykorzystania pakietów echo request zarówno z protokołu IPv4 oraz IPv6. Wówczas należy wskazać to za pomocą opcji -4 lub -6 w poleceniu ping.
9.4.2. Narzędzie host
Narzędzie host jest najprostszym sposobem na skorzystanie z systemu DNS.
System DNS pozwala on zapamiętać ludziom typowy adres strony, bez potrzeby znajomości jej adresu IP, aby zrealizować to połączenie. Jeśli wpiszemy adres strony do przeglądarki, zapyta ona system DNS o jej adres IP i ten udzieli jej takiej odpowiedzi. Oczywiście w skrócie rzecz ujmując. Czasami możemy znaleźć się w odwrotnej sytuacji, kiedy będziemy znać adres IP, a chcielibyśmy się dowiedzieć do jakiej organizacji jest on przypisany. Jednak ta funkcjonalność nie jest kluczowa i rzadko bywa dobrze skonfigurowana, nie mniej jednak warto spróbować.
Aby skorzystać z narzędzia host, podajemy nazwę lub adres IP jako argument polecenia.
xf0r3m@immudex:~$ host morketsmerke.org morketsmerke.org has address 213.186.33.5 morketsmerke.org mail is handled by 10 mx3.mail.ovh.net. morketsmerke.org mail is handled by 1 mx4.mail.ovh.net. xf0r3m@immudex:~$ host 93.184.216.34 Host 34.216.184.93.in-addr.arpa. not found: 3(NXDOMAIN) xf0r3m@immudex:~$ host 213.186.33.5 5.33.186.213.in-addr.arpa domain name pointer redirect.ovh.net.
Na powyższym przykładzie widzimy trzy wywołania polecenia
host
jedno z nich klasyczne
pytające o adres IP i dwa pytające o nazwę domenową, zwróćmy uwagę
na to w jaki sposób zostały zapisane adresy IP. Nie będę tutaj
przytaczać więcej informacji o sposobie działania systemu DNS, gdyż
wykraczają one poza ramy tego materiału. Do systemu DNS wrócimy
podczas konfiguracji interfejsu hosta.
9.5. Warstwa fizyczna
Wiemy, że sieć internetowa oparta jest o oprogramowanie. Nie miej jednak gdzieś zagadnienia sieciowe muszą stykać się ze swiatem realnym, z tym że komputery koniec, końców są ze sobą połączone za pomocą specjalnych przewodów oraz z lub bez pośrednictwa dodatkowych urządzeń. Tymi elementami zajmuje się warstwa fizyczna. Standardów implementacji warstwy fizycznej (tego w jaki sposób informacje przesyłane są przez media (przewody lub fale radiowe)) jest wiele, jednak wykorzystywaną po dzień dzisiejszy oraz najpowszechniejszą z nich jest Ethernet. Sprzęt sieciowy, mimo tej samej implementacji będzie różnić się od siebie ze względu na użyte medium transmisji, ale mają one wiele cech wspólnych. Oto kilka z nich:
- Element identyfikującym w sieciach Ethernet jest adres sprzetowy znany także jako adres MAC. Adresy MAC są niezależne od adresów progrmowych i aby transmisja w sieci Ethernet doszła do skutku to musi on być unikatowy w obrębie sieci lokalnej (i zazwyczaj tak jest, w sieci lokalnej, jednak w sieciach rozległych, już może tak nie być. Obecnie losowy MAC adres interfejsu sieciowego może być stosowany aby podnieść poziom prywatności w sieciah publicznych (ogólnodostępne sieci bezprzewodowe)). Adres sprzętowy składa się z 6 grup po 2 cyfry systemu heksadecymalnego oddzielonego dwukropkami. Całość ma łącznie 48-bitów.
- W sieciach Ethernet dane wysłane są w ramkach. Ramki są opatrzone adresami MAC hostów biorących udział w wymianie danych
Host mając nawet dwa interfejsy sieciowe podłączone do różnych sieci Ethernet nie może przekazać pakietu z jednej do drugiej sieci, chyba skonfigurowano na nim most. Więc jeśli dane mają zostać wysłane do internetu, potrzebny jest udział warstw wyższych, albowiem każda sieć fizyczna jest podsiecią sieci warstwy wyższej, zatem router może odebrać ramkę, wyodrębnić jej dane a następnie przepakować do nowej ramki i wysłać dalej w świat.
9.6. Sieciowe interfejsy jądra
Jądro samodzielnie odróżnia warstwę fizyczną od sieciowej, przez co musi zapewniać im standard w komunikacji. Czymś takim właśnie jest interfejs sieciowy. W trakcie jego konfiguracji przypisane parametry warstwy internetowej są łączone z parametrami urządzenia. Nazwy interfejsów opierają się o rodzaj sprzętu na którym bazują. Np. enp0s4, oznacza zazwyczaj kartę sieciową podłączoną do magistrali PCI. Nazwy tego typu są określane nazwami przewidywalnymi i nie zmieniają się one ponownym uruchomieniu systemu. Jest to jednak prowadzone głównie program typu init - systemd. Aby wyświetlić interfejsy wydajemy polecenie:
xf0r3m@immudex:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 90:1b:0e:6a:71:7d brd ff:ff:ff:ff:ff:ff inet 172.16.1.2/16 brd 172.16.255.255 scope global enp4s0 valid_lft forever preferred_lft forever inet6 fe80::921b:eff:fe6a:717d/64 scope link valid_lft forever preferred_lft forever 3: enp0s25: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 90:1b:0e:87:3c:43 brd ff:ff:ff:ff:ff:ff
Polecenie ip a
, jest poleceniem
skrótowym dla polecenia ip address show.
Interfejsy sieciowe są numerowane kolejno do numeru 2, numer 1
zarezerowany jest dla specjalnego interfejsu systemowego - zwanego
pętlą zwrotną. Każdy działający interfejs w linii z numerem oraz
nazwą posiada flagę UP
. Poza
informacjami warstwy sieciowej możmy również zauważyć informacje z
warstwy fizycznej takie jak adres MAC. Polecenie
ip a
, głównie skupia się na warstwie
sieciowej. Jeśli chcielibyś poznać więcej szczegółów związanych z
warstwą fizyczną, wówczas należało by użyć polecenia takiego jak
ethtool.
9.7. Konfiguracja interfejsu sieciowego
Do tej pory omawialiśmy kwestie sieciowe wyłącznie teoretycznie, a to przeanalizowaliśmy sobie wynik jednego czy drugiego polecenia, ale teraz zajmiemy konfiguracją interfejsu sieciowego. Aby użytkownik dowolnej dystrybucji mógł połączyć się z internetem musimy wykonać cztery poniższe czynności:
- Zapewnienie obecności sterownika karty sieciowej w jądrze. Jeśli sterownika będzie brakować, to nawet nieskonfigurowany interfejs nie zostanie wyświetlony po wydaniu polecenia ip a.
- Dodatkowa konfiguracja warstwy fizycznej, na przykład poprzez podanie nazwy sieci oraz hasła (głównie sieci bezprzewodowe).
- Ustawienie dla interfejsu adresu IP oraz maski podsieci (dzięki temu warstwy będą mogły się ze sobą komunikować).
- Ustawienie wszystkich wymaganych tras w tym trasy domyślnej.
Wiedząc teraz co musimy zrobić. Przejdziemy do wykonania tych czynności w systemie.
9.7.1. Ręczna konfiguracja interfejsu
Reczna konfiguracja interfejsów sieciowych rzadko jest wymagana, chyba eksperymentujemy z systemem lub próbujemy skonfigurować urządzenie, które samo nam interfejsu nie skonfiguruje. Nie mniej jednak, a własnego doświadczenia wiem, że warto posiadać taką wiedzę. Więc tak załóżmy, że posiadamy sterowniki do karty sieciowej (interfejsu), którą chcemy skonfigurować oraz łączymy się z siecią Ethernet za pomocą kabla miedzianego, dlatego też punkt nr. 2 też możemy pominąć. Jeśli tak rzeczywiście jest to wówczas konfiguracja interfejsu sprowadza się do dwóch poleceń.
Aby skonfigurować ręcznie interfejs musimy musimy użyć polecenia ip wraz z podpoleceniem address lub addr, następnie podać komendę add po niej adres IP wraz z maską w notacji CIDR do tego opcję dev po której należy wskazać nazwę interfejsu jaki chcemy skonfigurować.
xf0r3m@immudex:~$ sudo ip address add 172.16.1.1/16 dev enp4s0
Wykorzystując powyższe polecenie mamy pierwszą część za sobą. Pamiętajmy jednak aby zmienić dane.
Drugą czynnością jest dodanie tras. Do połączenia z internetem potrzebna będzie na pewno brama domyślna. Jeśli będziemy potrzebować dodatkowych tras wystarczy zmodyfikować poniższe polecenie:
xf0r3m@immudex:~$ sudo ip route add default via 172.16.0.1 dev enp4s0
Za trasy odpowiada podpolecenie route
,
komenda add
odpowiada za dodawanie
tras. Następnie podawana jest sieć jaka ma być osiągalna. Po opcji
via
podawny jest adres IP na jaki
mają być kierowane pakiety do docelowej sieci, tym adresem może być
adres routera sieci lokalnej lub nawet jeden z interfejsów
zainstalowanych na tym samym komputerze, na koniec po opcji
dev
podajemy interfejs, przez który
adres IP podany po opcji via
jest
osiągalny.
Jeśli trasa jest nam nie potrzebna lub pomyliliśmy się podczas jej dodawnia, to taką trasę bez obaw można usunać, przy użyciu komendy del podpolecenia route. Komenda przyjmuje adres sieci jako argument.
xf0r3m@immudex:~$ sudo ip route del 192.168.60.0/24
9.8. Konfiguracja interfejsów sieciowych podczas rozruchu
Próba konfiguracji interfejsu podczas rozruchu mogła by wyglądać w taki sposób, że gdzieś podczas sekwencji rozruchowej program typu init (lub jeden z jego skryptów) mógłby wydać polecenia ip (takie jak te, które wkorzystywaliśmy do ręcznej konfiguracji) lub innego narzędzia. Pytanie tylko jakiego? A odpowiedzi tyle co wiodących dystrybucji.
Do tej pory próbowano ustandaryzować ten proces za pomocą poleceń ifup oraz ifdown. Z niezbyt zadawalającym skutkiem ze względu na różne implementacje tych narzędzi przez osoby zajmujące się rozwojem dystrybucji, czego następstwem są różnice w plikach konfiguracyjnych. Kolejną problematyczną rzeczą jest fakt iż wiele elementów sieciowych wykorzystywanych do konfiguracji znajduje się różnych miescach w systemie i odpowiadają za nie inne komponenty. W Linuksie panuje również zasada aby nie rozpowszechniać plików konfiguracyjnych w przypadku wielu osobnych narzędzi czy bibliotek, ponieważ zmiany wprowadzone w jednym narzędziu mogą spowodować, że inne przestaną działać.
Więc tak chcąc skonfigurować interfejsy sieciowe podczas rozruchu musimy skorzystać z jednego z dostępnych managerów lub dostępnego w systemie kompnentu. W Debianie, interfejsy sieciowe są zarządzane przez dodatek ifupdown, którego składową jest plik /etc/network/interfaces. Na Ubuntu system Netplan, od najnowszysch wersji dystrybucji opartych o Red Hat, korzysta się głównie z managerów. Natomiast managerem godnym uwagi jest NetworkManager i to na nim się skupimy podczas ich omawiania. Aby ten podrozdział wniósł coś pożytecznego poza kilkoma teoretycznymi dywagacjami omówimy sobie konfigurację interfejsów na przykładzie dystrybucji Debian.
source /etc/network/interfaces.d/* # The loopback network interface auto lo iface lo inet loopback # The primary network interface auto enp4s0 iface enp4s0 inet static address 172.16.1.2 netmask 255.255.0.0 gateway 172.16.0.1 dns-nameservers 172.16.0.1
Na powyższym przykładzie mamy dwie deklaracje interfejsów sieciowych.
Pierwszym z nich jest interfejs pętli zwrotnej. Jego rola w systemie
jeszcze zostanie omówiona. Nie miej jednak jego deklaracja zawiera
słowo auto
, która powoduje załączenie
interfejsu podczas startu systemu. Po linii ze słowem auto następuje
definicja interfejsu (linia ze słowem
iface
) w niej podawana jest nazwa
interfejsu (enp4s0
), stos protokółów
(inet
, co oznacza IPv4. Mogą być też
inne). Następnie podawana jest metoda konfiguracji interfejsu.
Najczęściej wykorzystywanymi są static
oraz dhcp, w przypadku pętli zwrotnej w tym miejscu
używane jest słowo loopback
. W
przypadku drugiego (właściwego) interfejsu adres przypisywany będzie
statycznie. To tak samo jakbyśmy przypisywali go ręcznie, jest to
czynność automatyczna wykonywana podczas rozruchu. Po uruchomieniu
ponownym komputera interfejs powinien zostać automatycznie podniesiony
(załączony) i skonfigurowany przy użyciu podanych w pliku parametrów.
Obecnie systemy służą użytkownikom końcowym, którzy zapewne mają uprawnienia administratora, ale nie posiadają odpowiedniej wiedzy aby móc konfigurować odpowiednie warstwy. Proste rozwiązania, takie jak to opisane powyżej daje oczywiście taką mozliwośc, jednak nie jest ono zbyt przystępne dla użytkownika, dlatego też w dystrybucjach takich Red Hat, stosowane są managery, których zadaniem jest ułatwienie konfiguracji.
9.9. Menedżery konfiguracji sieciowe
Dla dystrybucji Linuksa dostępnych jest wiele programów będących menedżerami konfiguracji sieciowe. Część z nich przeznaczona jest na systemy wbudowane. Dlatego godnymi uwagi pozostają tylko dwa. Jeden będący częścią programu typu init systemd-networkd nadaje się on wykonania podstawowej konfiguracji sieciowej. Jest wystarczający dla serwerów, ale nie dla użytkonika końcowego. Wówczas pozostaje jeden - NetworkManager i to na nim się skupimy.
NetworkManager jest demonem takim samym jak pozostałe więc jego zadaniem jest nasłuchiwanie zdarzeń generowanych zarówno przez system jak i przez samych użytkowników, dodatkowo NetworkManager zajmuje się konfiguracją sieciową zgodnie z założonymi regułami. Program ten operuje na informacjach o dostępnych urządzenia uzyskanch od jądra oraz na połączeniach, które są wskazaniem konkretnych urządzeń wraz z parametrami warstw sieciowych. Konfigurując sieć w danej dystrybucji, NetworkManager korzysta z określonego dodatku aby dostosować konfigurację do standardu używanego przez dystrybucję, nie narzucacjąc przy tym swoich rozwiązań.
W momencie uruchomienia NetworkManager, zbiera informacje o dostępnych urządzeniach sieciowych, sprawdza listę połączeń i podejmuje decyzję o próbie połączenia. Proces decyzyjny na podstawie, którego menedżer decyduje, z którą z sieci chce się połączyć można rozpisać w trzech krokach.
- Jeśli dostępne jest połaczenie kablowe. To ono jest preferowane jako pierwsze.
- Jeśli połączenie kablowe nie jest dostępne, wówczas jeśli w systemie dostępny jest interfejs bezprzewodowy to skanowane jest otoczenie podzwględem występowania sieci bezprzewodowych. Jesli dostępne są sieci, z którymi system już kiedyś nawiązał połączenia. To jest ono nawiązywane ponownie. Jeśli nie to dostępna jest lista sieci bezprzewodowych osiągalnych na danym terenie.
- Jeśli dostępne są więcej niż dwie sieci, z którymi już wcześniej nawiązywano połączenie to wówczas wybierana jest, z którą łączono się jako z ostatnią.
Połączenie sieciowe zwłaszacza wśród sieci bezprzewodowych utrzymywane jest do momentu uzyskanie połączenia o lepszych parametrach. Na przykład, po przez wpięcie kabla.
9.9.1. Interakcja z Network Manager
Kontrolować NetworkManager możemy za pomocą apletu, przygotowanego dla środowiska graficznego. Jednak w tym materiale skupiamy się wyłącznie na rozwiązaniach opartych o wiersz polecenia. Więc przejdziemy od razu do tego typu interakcji z tym narzędziem. Do kontrolowania NetworkManager z poziomu powłoki, służy narzędzie nmcli. Za pomocą podpoleceń tego narzędzia możemy kontrolować połączenia sieciowe zarządzane przez omawiany menedżer.
Innym narzędziem związanym z NetworkManager-em, jest polecenie nm-online, które zwraca jedynie infomacje w zależności od tego czy posiadamy aktywne połączenie czy też nie.
Poniżej znajduje się przykładowe uruchomienie, nmcli bez żadnych podpoleceń.
Polecenie to agreguje informacje zwracane przez wiele poznanych do tej pory poleceń, takich jak ip addr lub ip route show. Dodatkowo wyświetlane są informacje o konfiguracji systemu DNS.
Dla przykładu możemy utworzyć ręczną konfigurację dla interfejsu tym razem wykorzystując do tego NetworkManager.
xf0r3m@immudex:~$ sudo nmcli con add con-name my-connection ifname enp4s0 \ type ethernet ipv4 192.168.8.150/24 gw4 192.168.8.1
Efekt działania tego polecenia możemy sprawdzić za pomocą polecenia ip a lub za pomocą nmcli
xf0r3m@immudex:~$ ip a xf0r3m@immudex:~$ nmcli -p con show my-connection
Opcja -p
formatuje wynik polecenia,
aby był on bardziej czytelny dla człowieka. Więcej przykładowych
poleceń wraz z opisami znajduje się na stronie podręcznika:
man 7 nmcli-examples. NetworkManager to naprawdę
rozbudowane narzędzie więc warto mieć z tyłu głowy to polecenie,
chociażby dlatego, że znajduje się tam opis sposobu łączność z siecią
bezprzewodową z poziomu wiersza poleceń. Sposób dużo bardziej
przystępny niż samodzielne skanowanie i obcowanie z pakietem
wpa_supplicant, gdzie często demon tego narzędzia może być
już użwany przez jeden z komponentów systemu.
9.9.2. Konfiguracja NetworkManager
Konfiguracja NetworkManagera wymaga edycji pliku /etc/NetworkManager/NetworkManager.conf. Plik jest formatu XDG lub wśród osób zaznajomionych z systemami od Microsoftu formatu .ini. Rzadko będzie potrzeba zmiany czego kolwiek w tym pliku. Nie mniej jednak jest jedna funkcją warta omówienia związana z konfiguracją tego menedżera - wyłączenie zarządania interfejsu przez NetworkManager. Interfejs pętli zwrotnej jest izolowany od tego narzędzia i jeśli uznamy, że inny/inne interfejsy w systemie też powinny to możemy zadeklarować w tym pliku. W sekcji [keyfile] (jeśli nie ma takowej, to należy dopisać). Plik ma format XDG, więc opcje zapisywane są w postaci klucz=wartość. Tak więc do klucza unmanaged-devices przypisujemy adres MAC interfejsów, które mają być wyłączone z spod zarządzania przez NetworkManager w taki sposób jak przedstawio to poniżej.
... [keyfile] unmanaged-devices=mac:08:00:27:3d:1b:9c
Inną kwestią związaną z konfiguracją omawianego w tym rodziale menedżera jest propagacja informacji o zmianie status interfejsu sieciowego. Niektóre demony do swojego poprawnego działania muszą otrzymać takową informację o tego typu sytuacji, aby zakończyć lub rozpocząć nasłuchiwanie na danym interfejsie. NetworkManager realizuje to za pomoca sieciowych skrytów sterujących znajdujących się w katalogu /etc/NetworkManager/dispatcher.d. Jeśli status interfejsu sieciowego ulegnie zmianie menedżer uruchomi wszystko co zostało umieszczone w powyższym katalogu z odpowiednim dla stanu interfejsu (up lub down). Na przykład w dystrybucjach opartych na Debianie (w tym i Ubuntu) istnieje plik 01-ifupdown, który na podstawie otrzymanego komunikatu zdrzenia (jeśli przyjrzeć się plikowi, to dowiemy się że to nie tylko up oraz down) uruchomi wszystko znajduje się w odpowiednich podkatalogach w katalogu /etc/network.
Dla nas zarówno konfiguracja NetworkManager może być mało istotna, podobnie jest ze skryptami rozsyłającymi. Nie mniej jednak może się kiedyś zdarzyć, że będzię potrzeba zmiany czegoś w skrypcie tego typu lub wyłączenie interfejsu z menedżera.
9.10. Rozwiazywanie nazw hostów.
Jeśli chcemy nawiązać jakie kolwiek połączenie sieciowe musimy podać adres komputera, z którym chcemy to połączenie nawiązać. Ludzie rzadko posługują się adresami IP, chyba że są to specjaliści lub entuzajści nauk komputerowych i pracują oni w lokalnych środowiskach. Takie adresy trudno zapamiętąć, więc niezbędne jest zapewnienie systemu/usługi, która zmieni adresu domenowe (np. morketsmerke.org) na adres IP. Z systemem DNS mieliśmy już styczność, w momencie gdy omawialiśmy protokół ICMP. Konfiguracja adresów systemu DNS jest tak naprawdę czwartą ostatnią wartością konfigurowaną na interfejsie sieciowym, aby mógł on nawiązać połączenie z internetem. Jest to o tyle ciekawe, że system DNS należy do warstwy aplikacji. Większość aplikacji sieciowych w Linuksie ma możliwość odpytania systemu DNS o adres IP, dlatego możemy zarówno użyć adresów IP lub nazw domenowych (chyba, że wyszczególniono inaczej). Uzyskiwania takiego adresu od DNS zwykle przebiega w następujący sposób:
- Przy uzyciu systemowej biblioteki współużytkowanej aplikacja wywołuje funkcję wyszukiwania adresu IP.
- Określa się kolejność odpytywania źródeł na podstawie danych zapisanych w pliku /etc/nsswitch.conf. Jedną z takowych reguł może być wymuszenie kolejności odpytania na początku pliku /etc/hosts jeszcze przed odpytaniem systemu DNS.
- Jeśli funkcja jednak zdecyduje o skorzystaniu z systemu DNS, musi użyć zawartości pliku konfiguracyjnego, w którym zawarte są adres serwerów systemu DNS.
- Funkcja wysyła zapytanie z adresem domenowym serwera, prosząc o jego adres IP.
- Serwer DNS odsyła do funkcji adres IP serwera, który zwracany jest do aplikacji.
Często występującym scenariuszem jest to, że serwer systemu DNS nie zna adresu IP serwera, o który prosimy, wówczas serwer, do którego skierowaliśmy to zapytanie musi zapytać się innych serwerów. DNS ma budowę hierarchiczną, a cała hierarchia jest zapisana w adresie domenowym. Szerszy opis systemu DNS wykracza poza ramy meryteoryczne tego materiału.
9.10.1. Pliki biorące udział w działaniu systemu DNS
Jak możemy wywnioskować z wyżej wymienionych czynności system DNS nawet w roli klienta potrzebuje korzysta z dużej ilości plików. Pierwszym z nich jest /etc/nssswitch.conf. Ten plik nie jest związany stricte z systemem DNS, ale ze wszystkim co jest związane z nazwami w systemie. Jest to klasyczny interfejs określający pierwszeństwo w źródłach rozwiązywania nazw. Jeśli przeanalizujemy sobie zawartość tego pliku, możemy zauważyć skąd i na jakiej podstawie, niektóre z narzędzi czerpią wiedzę taką jak informacje o użytkownikach, usługach czy innych rodzajach sieci niż IP. Jednak na tym etapie interesuje nas wyłącznie linia rozpoczynająca się od hosts. Wskazuje ona pierwszeństwo pliku /etc/hosts nad system DNS, przez co korzystając z dystrybucji możemy dowolnie manipulować rozwiązywaniem nazw. Jest to szczególnie przydane podczas administracji i testowania usług, które później będziemy chcieli użyć.
Kolejnym plikiem jest wspomaniany już wcześniej /etc/hosts. Plik ten zawiera zestawienie adresów IP wraz z nazwami domenowymi. Kiedyś kiedy hosty w internecie można było policzyć na palcach obu rąk służył za centralną bazę danych odnosnie odwzrowania nazw. Był on swojego czasu protoplastą systemu DNS. Jednak ze względu na bum rozwoju internetu, zaprzestano korzystania z tej praktyki. Jednak obecnie wciąż można spotkać go w roli lokalnej bazy rekordów DNS w sieciach, gdzie centralnym urządzeniem (router/bramka core'owy) jest urządzenie oparte na jakimś uniksie.
Ostatnim plikiem jest /etc/resolv.conf. Zawartość tego pliku, ma wpływ na to z jakiego źródła systemu DNS korzysta nasz system. Otóż w tym pliku przechowywane są zazwyczaj dwie wartości pierwszą z nich są adresy serwerów systemu DNS, a drugim sufiks nazwy domenowej wykorzystywany wtedy gdy podamy samą nazwę hosta bez nazwy domeny, to znaczy jeśli na przykład podamy samo www zamiast www.morketsmerke.org, wówczas funkcja na podstawie sufiksu z tego pliku sama go doda i przekaże żądanie dalej na jeden z adresów widniejących po opcji nameserver.
9.10.2. Buforowanie oraz bezkonfiguracyjne systemy DNS
Buforowanie to czyność, którą głównie się kocha a czasem nienawidzi. Buforowanie odpowiedzi systemu DNS jest odpowiedzią na fakt, że komputery odpytujące system DNS nie przechowywały nigdzie uzyskanych od serwera odpowiedzi, zbyt często powtarazna tego typu czynność mogła przytkać sieć. Obecnie bardzo często adres serwera DNS jest również adres bramy. Ma to głównie na celu właśnie buforowanie, na naszym domowym routerze za pewne uruchomiony jest demon buforująco-przekazujący. Niestety w przypadku wielu urządzeń nie będzie my się wstanie tego dowiedzieć. Ale jeśli nasz sprzęt jest zmodyfikowany, lub naszym routerem jest jest oprogramowanie zainstalowane na komputerze z wieloma kartami sieciowymi, to raczej na 100% tak będzie. Innym sposóbem na wykrycie systemu buforującego, jest sprawdzenie zawartości pliku /etc/resolv.conf. Jeśli widnieje tam tylko jeden wpis nameserver to za pewne z adresem 127.0.0.53. Jest to adres z klasy pętli zwrotnej (o której będzie za chwilę), czyli wiemy, że to usługa uruchomiona lokalnie na tej maszynie. Jest to jeden z demonów programu typu init (systemd) - systemd-resolved. Często w tym wypadku sam plik jest dowiązaniem symboliczym. Użycie tego rodzaju systemu rozwiązuje inny problem związany z brakiem elastyczności statycznych konfiguracji, wówczas nie ma potrzeby wprowdzania zmian w komponentach systemu.
Wspomniany wcześniej demon posiada jedną ważną cechę. Potrafi łączyć wiele sposobów wyszukiwania nazw w sieci, co przekłada się na możliwość użycia bezkonfiguracyjnych systemów DNS. W przypadku takiej konfiguracji jeśli podłączymy nowe urządzenie do sieci będziemy mogli wywołać je za pomocą nazwy. Jeśli host istnieje udzieli odpowiedzi ze swoim adresem IP. Rozwiązaniami tego typu są mDNS (multicastDNS) oraz LLMNP (Link-Local Multicast Name Protocol). Rozwiązania te poza zwykłym odwzorowaniem nazw, mogą przedstawiać dostępność usług. Ich dostępność w systemie możemy sprawdzić za pomocą polecenia resolvectl status, warto jednak pamiętać, że jego działanie jest uzależnione od działania wcześniej wspomnianego demona.
9.11. Host lokalny
W wielu systemach operacyjnych, poznając ich możliwości sieciowe możemy natknąć się na dośc specyficzny interfejs, który w uniksach określany jako lo. Jest to jedyny interfejs, który będzie posiadać w plikach konfiguracyjnych statyczną konfigurację. Interfejs pętli zwrotnej bo tak jest określany przez swój sposób działnia służy głównie interakcji z programami sieciowymi bez potrzeby angażowania innych interfejsów. Na przykład jeśli aplikacja jest w fazie testów i póki co nikt po za naszym komputerem nie powinien się z nią łączyć. To wtedy taką aplikację ustawia się tak aby oczekiwała na połączenia na interfejsie pętli zwrotnej. Wówczas pakiety będą przesyłane przez ten interfejs w obu kierunkach i opuszczając tym samym jednej fizycznej maszyny. Można powiedzieć również że adres pętli zwrotnej to połączenie z samym sobą.
Jeśli wyświetlimy sobie konfigurację interfejsu pętli zwrotnej to zwrócimy uwagę na to, jaki adres został mu przypisany:
xf0r3m@immudex:~$ ip addr show lo 1: lo: <LOOPBACK,UP> mtu 1500 group default qlen 1 link/loopback 00:00:00:00:00:00 inet 127.0.0.1/8 brd 127.255.255.255 scope global dynamic valid_lft forever preferred_lft forever inet6 ::1/128 scope host dynamic valid_lft forever preferred_lft forever
Jest to bardzo duża klasa adresowa. Jeśli przypisany do usługi adres będzie rozpoczynął się od 127 i posiadał 8 bitową maskę, będzie wówczas przypisany do pętli zwrotnej.
9.12. Warstwa transportowa oraz usługi
Przedostatnią warstwą modelu TCP/IP jest warstwa transportowa. Jak sobie wcześniej omówilismy definiuje ona sposób transmisji w sieciach IP. Wewnątrz niej możemy znajdują się takie protokoły jak TCP oraz UDP, ona także definiuje pojęcie portu.
9.12.1. TCP - Transmission Control Protocol
TCP jest protokołem przepływu danych w sieciach IP. Jest to protokół, który przed właściwą transmisją danych zestawia coś w rodzaju połączenia. Za pomocą specjalnych pakietów kontrolnych strona odległa informowana jest o nadchodzącej transmisji, i jeśli faktycznie jest ona w stanie odebrać dane (port na który, strona lokalna chce wysłać informacje jest otwarty) odsyła informacje zwrotną o tym, że może wysyłać. Wówczas po wymianie pakietów kontrolnych, dochodzi do wysłania danych z komputera lokalnego do komputera zdalnego. Protokół TCP czuwa nad przebiegiem transmisji, każdy pakiet ma swój numer i strona zdalna po jego otrzymaniu wysyła pakiet kontrolny z potwierdzeniem jeśli odpowiedź nie przychodzi w określonym czasie wówczas dochodzi do retransmisji zagubionych pakietów. TCP jest protokołem wolniejszym od innego protokołu z tej samej warstwy, zapewnia jednak niezawdność transmisji, przez co strona odległa może mieć pewność, że uzyska poprawne dane. Ten protokół wykorzystywany jest w wiekszości usług w internecie, w których przenoszone przez jego łącza dane mają znaczenie użytkowników końcowych. Dane są po prostu istotne.
9.12.2. UDP - User Datagram Protocol
UDP jest trochę innym protokołem niż TCP, ale jego główna rola jest bardzo podobna, z tą różnicą, że nie zawiera on żadnych mechanizmów kontroli. Ma to swoje plusy i minusy. Protokół jest na tyle szybki, że wykorzystywany jest przez różnego rodzaju aplikacji służące do przesyłania strumieniowego. W tym wypadku możemy osobiście doświadczyć błędów w transmisji objawiających się spadkiem jakość streamowanych materiałów. Inną ważnym wykorzystaniem tego, protokołu, właśnie ze względu na zalety protokołu TCP jest użycie UDP do budowania sieci prywatnych VPN. Faktem, dla którego nie korzysta się protokołu TCP jest piekło retransmisji. Załóżmy, że dochodzi do błedów w zewnętrznej transmisji, czyli między serwerami VPN i jeśli byłaby to transmisja TCP doszło by do retransmisji, system VPN będzie musiał zatrzymać się wówczas host docelowy transmisji wewnętrznej zauważy zagubienie pakietu i rozpocznie swoją retransmisję co doprowadzi do zapchania łącza i wstrzymania trasnsmisji w ogóle. Dlatego też stosuje się protokół UDP dla transmisji zewnętrznej, wówczas nad poprawnością danych przenoszonych przez tunel VPN czuwać będą hosty transmisji wewnętrznej. Jak możemy zauważyć wszystko co może być zaletą w konkretnym przypadku może okazać się wadą w innym.
9.12.3. Porty
Z wyżej wymienionych metod transmisji przy uzyciu portów korzystają różne aplikacje. Port to nic innego jak numer wystąpienia aplikacji w obszarze warstwy transportowej. Na ten numer kierowane są pakiety i w ten sposób aplikacja otrzymuje dane z sieci. Aplikacje sieciowe posidaja przypisane te numery przez swoich twórców, ich lista wstępuje w każdym uniksie, który ma możliwośc podłaczenia do sieci. Lista znajduje się w pliku, /etc/services. Większość zebranych tam aplikacja powstawa wraz z internetem, więc lista może być długa i już nie zbyt aktualna, ale jednak jest pewnym zbiorem informacji. Poniżej znajduje się kilka protokołów wraz z informacją z której pochodzi źródło.
# Updated from https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml . http 80/tcp www # WorldWideWeb HTTP https 443/tcp # http protocol over TLS/SSL https 443/udp # HTTP/3 http-alt 8080/tcp webcache # WWW caching service
Obok portu wyświetlony jest również rodzaj transmisji. Ciekawostką jest fakt, że trzecia generacja protokołu HTTPS będzie oparta na protokole UDP, a nie TCP.
9.12.4. Wyświetlanie połączeń sieciowych w systemie
Omawiając warstwę transportową to nie sposób jest nie wspomnieć o wyświetlaniu połączeń. Obecnie w dystrybucjach Linuksa używa się nowego polecenia jakim jest ss, aby wyświetlić połączenia TCP w systemie należy wydać poniższe polecenie. Polecenie to dla przykładu zostało wydane na jednym z moich serwerów VPS
root@searx:~# ss -tr State Recv-Q Send-Q Local Address:Port Peer Address:Port Process CLOSE-WAIT 32 0 ip109.ip-51-178-2.eu:57026 146.75.74.208:https CLOSE-WAIT 64 0 ip109.ip-51-178-2.eu:32808 text-lb.drmrs.wikimedia.org:https CLOSE-WAIT 64 0 ip109.ip-51-178-2.eu:43164 text-lb.drmrs.wikimedia.org:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:57466 52.84.150.51:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:35092 server-143-204-68-27.lhr61.r.cloudfront.net:https CLOSE-WAIT 25 0 ip109.ip-51-178-2.eu:45492 upload-lb.drmrs.wikimedia.org:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:41928 server-143-204-68-21.lhr61.r.cloudfront.net:https CLOSE-WAIT 32 0 ip109.ip-51-178-2.eu:36336 qwantbot-115-168-187-194.qwant.com:https ESTAB 0 52 ip109.ip-51-178-2.eu:2022 213.25.29.68:63950 CLOSE-WAIT 130 0 ip109.ip-51-178-2.eu:60580 waw02s07-in-f4.1e100.net:https CLOSE-WAIT 47 0 ip109.ip-51-178-2.eu:33310 104.16.55.16:https CLOSE-WAIT 130 0 ip109.ip-51-178-2.eu:43572 waw02s07-in-f4.1e100.net:https CLOSE-WAIT 32 0 ip109.ip-51-178-2.eu:36666 140.177.50.13:https CLOSE-WAIT 1 0 ip109.ip-51-178-2.eu:33408 par21s17-in-f14.1e100.net:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:53446 172.67.22.17:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:57454 52.84.150.51:https ESTAB 0 0 ip109.ip-51-178-2.eu:2022 213.25.29.68:61628 CLOSE-WAIT 32 0 ip109.ip-51-178-2.eu:53654 www.wolframalpha.com:https CLOSE-WAIT 32 0 ip109.ip-51-178-2.eu:58752 qwantbot-109-168-187-194.qwant.com:https CLOSE-WAIT 130 0 ip109.ip-51-178-2.eu:58880 waw02s07-in-f4.1e100.net:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:37804 qwantbot-106-168-187-194.qwant.com:https CLOSE-WAIT 25 0 ip109.ip-51-178-2.eu:50924 server-18-244-117-84.lhr50.r.cloudfront.net:https CLOSE-WAIT 64 0 ip109.ip-51-178-2.eu:43148 text-lb.drmrs.wikimedia.org:https CLOSE-WAIT 90 0 ip109.ip-51-178-2.eu:41176 151.101.65.181:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:58504 server-18-244-153-127.lhr50.r.cloudfront.net:https CLOSE-WAIT 40 0 ip109.ip-51-178-2.eu:58216 server-13-224-132-41.lhr3.r.cloudfront.net:https
W pierwszej kolumnie znajduje się stan połaczenia, jak już wcześniej
wspominałem protokół TCP ustanawia połączenia i ta kolumna
przedstawia ich stan. Estab
skrót od
ang. established oznacza że połączenie zostało nawiązane i
dalej trwa. Dalej przesyłane są jakieś pakiety. Reszta połączeń
pozostaje w stanie CLOSE-WAIT
jest
to stan oczekiwania na zamknięcie połączenia przez użytkownika.
Przeważnie nie musimy się przejmować tego rodzaju połączeniami, one
zostaną zakończone z czasem. Druga i trzecia kolumna przechowują
informacje odnośnie pamięci podręcznej wysyłania i odbioru. Najlepiej
aby te wartości były jak najbliżej zera. Wysokie wartości w kolumnie
Recv-Q
mogą świadczyć o tym, że
pakiety osiągneły komputer docelowy, ale nie ma kto ich odebrać, na
natomiast w wysokie wartości w kolumnie
Send-Q
mogą najczęściej oznaczać, że
pakiety zostały wysłane ale do hosta, który na nie oczekuje i nie
potwierdza ich odebrania lub pakiety nie zostały wysłane w ogóle.
Czwarta oraz piąta kolumna zawierają gniazda lokalne
oraz zadalne. Za pomocą gniazd określamy połączenie
adresu IP lub nazwy domenowej (tak, jak w tym przypadku) z portem.
O gniazadach w kontekście Uniksów więcej informacji przedstawię w
następnym rozdziale.
Wydając powyższe polecenie użyłem opcji -t
określającej rodzaj transmisji
(-t
- TCP) oraz opcji
-r
, która zamienia adres IP oraz
numery portów na nazwy domenowe i nazwy usług. Warto zapoznać się
zawartością strony podręcznika narzędzia ss. Można odnależć
opcje pozwalającą na zamykanie dowolnych połączeń. Połączenia
(może, raczej rejestr transmisji) dla protokołu UDP możemy
wyświetlić za pomocą opcji -u.
9.13. Protokół dynamicznej konfiguracji hosta - DHCP
Ciężko sobie wyobrazić współczesną sieć bez tak ważnego składnika jakim jest protokół DHCP. Jest on odpowiedzialny za konfigurację interfejsów sieciowych oraz za utrzymywanie informacji o przekazanych parameterach, aby nie dochodziło do absurdalnych zdarzeń, np. żeby dwa komputery w sieci nie dostały tego samego adresu. Poza adresami IP serwer DHCP, konfigurje interfejsy w taki sposób, aby komputer był podłączony do internetu. W sieciach korporacyjnych mogą być konfigurowane dodatkowe paramety lub domyślna pula może nie istnieć, a nowe urządzenia trzeba dodawać do listy rezerwacji, w tym wypadku komputery uzyskują statyczny adres IP ale nie jest on przypisywany przez administratora, tylko przez DHCP właśnie.
Metoda uzyskiwania adresu jest dość niezwykła. Otóż jak komputer, który nie posiada adresu IP ma skomunikować z innym komputerem i jakimi. Otóż z każdym. Klient DHCP rozsyła na pakiet z poszukiwaniem serwera DHCP, na adres rozgłoszeniowy sieci (255.255.255.255) w tym pakiecie może być zawarty adres, jaki klient kiedyś posiadał. W odpowiedzi od serwera, klient dostanie ofertę, z którą może się zgodzić lub żądać innych, bądź dodatkowych parametrów, wówczas zostaje wysłane żądanie. Po uznaniu żądania, serwer wysyła potwierdzenie. Kiedy klient otrzyma potwierdzenie rozpoczyna konfigurację interfejsu sieciowego i na tym etapie rola DHCP kończy się. Jednak należy pamiętać, że adresy są dzierżawione od serwera, czyli czas konfiguracji jest określony i od czasu do czasu trzeba się z tym serwerem skomunikować, aby utrzymać obecną kofigurację lub prosić o nową. Jednak to pozostaje już w gestii klienta.
Obsługa protokołu DHCP na uniksach występuje w oryginalnej implementacji. Do dyspozycji mamy pakiet klienta dhclient, wśród serwerów możemy wyróżnić co najmniej dwie godne polecenie implementacje. Jedną z nich wykorzystamy tworząc z komputera z dystrybucją Linuksa domową bramkę. Aby pobrać konfigurację z serwera DHCP (nie które dystrybucje, nie posiadają domyślnie żadnej konfiguracji sieciowej) należy wydać poniższe polecenie.
xf0r3m@immudex:~$ dhclient enp4s0
Podanie interfejsu jako argument spowoduje skonfigurowanie tylko tego konkretnego interfejsu, jeśli go pominiemy skonfigurowane zostaną wszystkie aktywe (w których warstwy poniżej internetowej już działają).
Jeśli chcelibyśmy poznać np. czas dzierżawy to informacje uzyskane od serwera znajdują sie w pliku dzierżawy /var/lib/dhcp/dhclient.leases
Do tej pory omawiając DHCP, skupialiśmy się głównie na protokole IPv4. W przypadku protokołu IPv6 również istnieje możliwość zastosowania DHCP, nawet został przygotowany ku temu standard protokołu DHCPv6 to proponowane jest innego rodzaju rozwiązanie. Ze względu na długość adresów IPv6, organizacja zarządzająca wykorzystała częśc adresów na potrzeby nowej metody konfiguracji. Konfiguracja bezstanowa nie wymaga punktów centralnych w postaci serwerów do konfiguracji interfejsów dla IPv6.
Metoda działania tej konfiguracji opiera się o adresy sieci lokalnej łącza, ze względu długość adresu host może wygenerować sobie adres, który może z mały prawdopodobnieństwem powtórzyć się w sieci. Z resztą mając ustalony prefiks (fe80::/64) można wysłać zapytania do innych hostów w sieci czy adres, który host wygenerował jest wykorzystywany przez inne hosty w sieci. Jest to mało prawdopodobne gdyż do wygenerowania tego adresu bierze udział adres MAC. Po uzyskaniu łącza lokalnego, host może określić adres globalny z pomocą komunikatów RA (Router Advertisement) wysłanego co jakiś czas przez router w naszej sieci lokalnej. Komunikat zawiera prefiks sieci globalnej, adres routera oraz adresy serwerów DNS. Po otrzymaniu tych informacji może podjąć próbę wygenerowania części adresów odpowiadającej za identyfikator interfejsu. W ten sposób interfejs otrzymuje dostęp do internetu za pośrednictwem protokołu IP w wersji 6.
9.14. Konfiguracja systemu Linux jako router
Ten podrozdział będzie dość obszerny, ponieważ postanowiłem zebrać w nim część zagadnień sieciowych dotyczących konfiguracji dystrybucji Linuksa w celu wykorzystania jako router.
Chcąc wykorzystać komputer z dystrybucją Linuksa jako bramę czy router (w zależności ile sieci będzie łączyć) musimy zaopatrzyć nasz komputer w odpowiednią ilość interfejsów. Jak możemy się domyślić interfejsy to nic innego jak karty sieciowe. Jeśli jest to zwykły PC to dołożenie kart nie powino być problemem (zakładając, że nasza sieć jest oparta na miedzianej skrętce) a najzwyklejsze karty (gigabitowe) mogą kosztować w okolicach 50 zł jeśli nie mamy możliwości rozbudowania komputera o więcej niż jedną kartę to możemy użyć kart serwerowych, one zazwyczaj posiadają od 2 do 4 portów a zajmują jeden port PCI-E x16 (rzadko, kto będzie mieć w komputerze przeznaczonym na router, złącza PCI-E x8 lub x4, bo takie złącze będą mieć karty tego typu).
9.14.1. Oprogramownie główne
Wiele urządzeń dostępnych na rynku jako router posiada przyjazny, z którym możemy połączyć się za pośrednictwem przeglądarki internetowej, a obecnie cześć producentów posiada również aplikacje mobline do ich kontrolowania. Tego typu rozwiązania zapewniają nam łatwość konfiguracji, która często sprowadza sie to wpisania kilku rzeczy i klikania Dalej, dalej, dalej... i na koniec zrestartowania urządzenia. Na tym zadanie się, kończy za pośrednictwem tego urządzenia komputery w naszej sieci lokalnej będą posiadać połączenie z internetem. Mimo to na tych sprzętach w dużej mierze przeważa Linux. Ten system w warunkach wbudowanych jest wstanie uruchomić się z każdego urządzenia. Czytając te słowa, zabrneliśmy tak daleko w głąb tego materiału, że możemy z pewnością stwierdzić, że domyślnie na Linuksie próżno szukać takich udogodnień.
Każda dystrybucja posiada wiele cech specyficznych, z racji tego iż nie narzucałem żadnej konkretej (chociaż, Debian tutaj przoduje). To warto wybrać tę której używaliśmy do tej pory jeśli jest to wiodąca dystrybucja to możemy zainstalować takie oprogramowanie jako cockpit, jest to środowisko do zarządania serwerem z poziomu WWW, przez przeglądarkę. Dostępne jest ono w wiekszości dystrybucji. Niestety nie daje ono zbyt rozbudowanych możliwości. Cechą dominującą przy wyborze powinna być stabliność dystrybucji, dlatego takie dystrybucje jak Debian lub dystrybucje klasy Enterprise (pomijając te płatne) jak Rocky czy Alma Linux sprawdzą się tutaj znakomicie. Problemem z tymi dystrybucjami jest trochę inna konfiguracją zapory sieciowej, ale ona opisana jest w innym moim materiale odnośnie RHCSA. Zatem wybierając taką dystrybucję mamy nieco więcej pracy, ale możemy mieć pewność, że nie spotkamy się z różnymi, nie zawsze przemyślanymi rozwiązaniami dostawców różnych nakładek czy dystrybucji skierowanych na routery. Sam osobiście wole takie rozwiązanie.
Innym, dużo prostszym podejściem do tego typu zadania, może być użycie dystrybucji przeznaczonej do zamieniania PC-tów z kilkoma kartami sieciowymi w router, takimi jak np. dystrybucja IPFire. Takich dystrybucji nie tylko opartych o Linux jest wiele więcej. Część z nich jest już niewspierana lub wątpliwej jakość (zeroshell, może dlatego upadła). Cała lista dostępna jest pod tym adresem.
Możemy również zmienić całkowicie podejście do tematu. Jeśli nie mamy wolnego komputera, którego możemy użyć w roli routera. To możemy użyć domowego routera, jako mikroserwer z Linux. Za pomocą dystrybucji takiej jak DD-WRT, a szczególnie OpenWRT możemy uzyskać dostęp dystrybucji Linuksowej na tym nie pozornym urządzeniu. Lista kompatybilnego sprzętu, na którym możemy zainstalować OpenWRT jest dość obszerna. Więc może znajdziemy system pasujący do naszego sprzętu. Dodam również, że obsługa tego typu dystrybucji może może opierać na konkretnym narzędziu i żadne z przedstawionych poniżej rozwiązań, skierowanych raczej do dystrybucji ogólnych, takich jak Debian, może nie działać. Wówczas zachęcam do zapoznania się z dokumentacją.
Poniższe rozwiązania bedą dotyczyć głównie wiodących dystrybucji. Więc jeśli zdecydujemy się inne rozwiązania wykonanie zadania możemy oprzeć o dostępną dokumnetację.
9.14.2. Oprogramowanie dodatkowe
Poza system operacyjnym i jego wewnętrznymi komponentami sieciowymi do zbudowania pełno prawnego routera lub bramy potrzebujemy kilku pakietów:
- Pakietu zapory sieciowej - iptables
- Pakietu DHCP wraz z DNS - dnsmasq
- Pakietu rozgłaszania sieci bezprzewodowej (opcjonalnie) - hostapd
Jeśli jesteśmy w posiadaniu odpowiedniego sprzętu nasz router może rozgłaszać sieć bezprzewodwą. Jeśli chcielibyśmy coś takiego zrealizować to warto zapoznać się z tematem tworzenia mostów. Jeśli taki most miedzy LAN-em a WLAN-em zostanie zestawiony to oba interfejsy zostaną połączone jednym wirtualnym, który będzie obsługiwał połączenia z zupełnie różnych dwóch warstw fizycznych.
Dostępność oraz sposób instalacji tych pakietów dla wybranych przez nas dystrybucji (ze względu na ich róznorodność) należy ustalić we własnym zakresie. Poniżej znajduje się polecenie dla dystrybucji Debian oraz pokrewnych.
xf0r3m@immudex:~$ sudo apt install dnsmasq
To polecenie zainstaluje tylko jeden pakiet. Debian 11 jest już rozprowadzany zainstalowanym pakietem zapory.
9.14.3. Konfiguracja dystrybucji jako router
Po złożeniu sprzętu oraz zainstalowaniu oprogramowania. Przyszedł czas na jego konfigrację. Najlepiej rozpocząć ją (a wręcz trzeba) od konfiguracji interfejsów. Musimy ustalić wykorzystywany przez nas sposób łączenia z Internetem, czy posiadamy statyczny adres IP lub interfejs jest konfigurowany dynamicznie przez naszego usługodawcę. Te informacje musimy zapisać w charkterystyczny dla naszej dystrybucji sposób konfiguracji interfejsów. Jeśli chcemy możemy wykorzystać do tego celu NetworkManager. Pamietając, że w przypadku Debiana interfejs wykorzystywany podczas instalacji będzie kontrolowany przez system ifupdown, trzeba go stamtąd usunąć. Po skonfigurowaniu interfejsu łączącego z internetem musimy skonfigurować interfejsy sieci LAN. Tutaj wystarczy sam adres oraz maska podsieci. Adres ten musi być jednym z adresów sieci prywatnych IPv4.
Sieci prywatne IPv4
Sieci prywatne to nic innego jak wycięte klasy adresowe sieci IP w wersji 4. Te adresy mają zastosowanie jedynie w sieciach lokalnych, ale i usługodawcy również mogą się nimi posługiwać, ponieważ to co wpinamy w port WAN routera jest umowne i tak naprawdę naszym WAN-em, może być sieć osiedlowa, która jest nieco wiekszym LAN-em lub będąc bardziej szczegółowym siecią kampusową. Adresy sieci prywatnych powstały ze względu na wyczerpywanie się adresów publicznych. Można by się zastanowić nad tym w jaki sposób zabranie prawie 18 milionów adresów, przyczniło się do tego, że sieci IPv4 jest na wyczerpaniu to nadal są przydzielane konkretne adresy. Otóż adresy klas prywatnych nie mogą funkcjonowac w internecie, są tylko do użytku wewnetrznego. Największa klasa A (posidająca grubo ponad 16 miliownów adresów) może być widoczna w internecie jako jeden adres. Dzięki technologii translacji adresów (bardziej szczegółów informacje na ten temat znajdują się poniżej). Stąd taka optymalizacja. Dostępne mamy trzy klasy adresowe:
- Klasa A - o masce 255.0.0.0, dającą 256^3 - 2 adresów w zakresie od 10.0.0.0 do 10.255.255.255.
- Klasa B - o masce 255.240.0.0, dająca 16*256^2 -2 adresów w zakresie od 172.16.0.0 do 172.31.255.255.
- Klasa C - o masce 255.255.0.0, dająca 256^2 - 2 adresów w zakresie od 192.168.0.0 do 192.168.255.255.
Gdzieś wśród młodych adeptów informatyki, wzięło się przeswiadczenie, że klasy B oraz C są dużo mniejsze. Klasie B przypisywało się wówczas maskę 16-bitową, odcinając wszystkie adresy powyżej 172.16.255.255. A klasie C przypisywano maskę 24-bitową, przez co zmniejszano ją do 254 adresów. Ten sposób adresacji wziął się z nie wiedzy osób konfigurjących sprzęt oraz z narzucenia przez producentów domyślnie takich wartości. Nie jest to błędem, ponieważ możemy alokować te adresy odpowiednio do swoich potrzeb. Dlatego jeśli nie potrzebujemy tak dużej sieci, to możemy ją zmniejszyć dzieląc te sieci na podsieci i wykorzystując tylko jedną z nich.
Po konfiguracji interfejsów możemy przjeść do konfiguracji serwera DHCP, który jest również serwerem buforująco-przekazujący systemu DNS. Przyczym konfiguracja DNS-u nie wymaga od nas żadnej uwagi dnsmasq zaraz po uruchomieniu zaczyna nasłuchiwać na porcie 53/UDP. Naszym zadaniem będzie jedynie ograniczenie tego nasłuchiwania do portów LAN. Dla przykładu żałożymy, że naszym interfejsem LAN-u jest klasyczny port eth0.
interface=eth0 dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,12h dhcp-option=3,192.168.4.1 dhcp-option=6,192.168.4.1
Pierwsza opcja interface=eth0
ogranicza działanie demona tylko i wyłącznie do interfejsu
eth0
. Dzięki tej opcji nasłuchiwanie
na porcie 53/udp (DNS) zostanie ograniczone do wskazane interfejsu.
Pozostałe opcje konfigurją nam już serwer DHCP. Pierwsza opcja
dhcp-range
definiuje nam zakres
przydzielanych adresów, maskę podsieci oraz czas dzierżawy. Następnie
zaczynają się typowe opcje protokołu DHCP, są one opisane w
standardzie, ale dla zachowania kontekstu przytocze je również tutaj
opcja 3
odpowiada za przypisanie
adresu domyślnej bramy, natomiast opcja
6
odpowiada za przypisanie adresów
serwerów DNS. Adres bramy oraz adres serwera DNS wskazują na adres
portu LAN. Te cztery linie wystarczą aby skonfigurować serwer DNS i
DHCP dla naszej sieci. Teraz po podłączeniu komputera do routera
powinniśmy uzyskać adres IP wraz z pozostałymi paramerami
pozwalającymi na połączenie z internet za pośrednictwem naszego
systemu.
Aby zrealizować połączenie z internetem hostów podłączanych do sieci LAN musimy wykonać jescze dwie czynności. Umożliwić w systemie przsyłanie pakietów między interfejsami, domyślnie jest ono wyłączone oraz włączyć funckję NAT-u.
NAT - Translacja adresów sieciowych
Translacja adresów potocznie zwana NAT-em, jest funkcja zapór sieciowych, której zadaniem jest najczęściej zamiana adresów sieci LAN, na adres sieci publicznej. Tak aby komputery mogły wysyłać oraz odbierać dane z sieci rozległej nie będąc do niej bezpośrednio podłączone. Istnieje kilka rodzajów translacji adresów nas będą interesować tylko dwa. Przeznaczone dla ruchu wychodzącego (danych generowanych przez komputery z sieci LAN). Do wybory wówczas mamy SNAT - ang. Source Network Address Translation lub MASQUERADE. Użycie SNAT-u będzie wymagać podania adresu IP portu WAN, teoretycznie nic w tym złego, ale jeśli nasz usługodawca automatycznie przypisuje swoim klientom adresy IP, za każdą zmianą adresu na tym interfejsie połączenie z internetem zostało by zerwane. Zatem SNAT jest szybszą metodą NAT-u, ale wymaga stałego adresu IP. Jeśli takim nie władamy, pozostało nam tylko użycie MASQUERADE, który pozwala na podanie interfejsu. Działanie NAT-u opiera się na podstawieniu w miejscu adresu źródłowego pakietu albo adresu podanego w regule (SNAT) albo adres interfejsu (MASQUERADE).
Za przekazywanie pakietów między interfejsami odpowiada jądro. Aby je włączyć musimy skorzystać z interfejsu ukrytego za poleceniem sysctl. Do konkretnych wartości prowadzi ścieżka, której elementy są odzielone od siebie kropkami. Poniżej znajduje sie polecenie, które odpowiada za przekazywanie pakietów miedzy interfejsami.
xf0r3m@immudex:~$ sudo sysctl net.ipv4.ip_forward=1
Teraz pakiety będą swobodnie przekazywane między interfejsami. Tak jak wspomniałem NAT jest funkcją zapory sieciowej. Tak więc konfiguracja dystrybucji Linuksa jako router będzie wymagać jej konfiguracji. Co prawda będzie to jedno polecenie, ale żeby nie pozostawiać użytkownika w niewiedzy trzeba trochę przybliżyć temat działania zapory jaką jest iptables.
Zapora sieciowa w systemie Linux
Zapora sieciowa w dystrybucjach Linux realizowana jest przez pakiet iptables, jego zasada działania opiera się o łańcuchy zasad pod kątem których sprawdzany może być pakiet przychodzący, wychodzący a nawet dodatkowo przekazywany dalej. Łańcuchy łączą się w tablice, które mogą modyfikować moment konfrontacji pakiety z łańcuchem, ale ogólna zasada pozostaje bez zmian. Każdy łańcuch posiada domyślną politykę, która jest stosowana dla pakietów niepasujących do żadnej z reguł. Domyślnie jest to polityka przepuszczaj, oznacza to swobodną transmisję. Wśród najważniejszych tablic oraz łańcuchów możemy wyróżnić:
- filter - tablica zwierająca łańcuchy
sprawdzające pakiety, które są związane jakoś z docelowym
komputerem, Ta tablica jest również domyślna i nie ma
potrzeby definiowania jest nazwy wśród niej możemy wyróżnić takie
łańcuchy jak:
- INPUT - zawiera reguły filtrujące pakiety przeznaczone dla lokalne hosta (tego komputera).
- OUTPUT - zawiera reguły filtrujące pakiety wysyłane (jego połączenia) z tego komputera.
- FORWARD - zawiera reguły filtrujące pakiety przesyłane przez ten komputer.
- nat - tablica zawierająca łańcuchy, których
zadaniem jest modyfikacja przechodzących przez nią pakietów.
Np. łańcucha PREROUTING korzysta się do
port-forwardingu, czyli udostępniania połączeń
z internetu dla wybranych usług. Kluczowe łańcuchy tej tablicy
przetwarzają pakiety zaraz po pojawieniu się na interfejsie (tj.
bardzo wcześnie) lub zaraz przed jego opuszczeniem (tj. bardzo
późno). Będą przy tym pierwszymi oraz ostatnimi zestawiami reguł
przetwarzającymi pakiet. Wśród tej tablicy możemy wyróżnić takie
łańcuchy jak:
- PREROUTING - zawiera reguły filtrujące, które najwcześniej przetwarzają pakiety. Ten łańcuchy wykorzystywany jest w port-forwarding.
- OUTPUT - zawiera reguły filtrujące pakiety dla wygenerowanych lokalnie pakietów, które poźniej będą wymagały trasowania.
- POSTROUTING - zawiera reguły filtrujące, które najpóźniej przetwarzają pakiety. Tutaj stosowany jest SNAT oraz maskarada.
Iptables konfrontując pakiet z łańcuchem zastosuję akcję dla pierwszej reguły do której będzie pasować przetwarzany pakiet. Natomiast możemy wyróżnić trzy rodzaje akcji podstawowych takich jak:
- ACCEPT - przepuść pakiet do dalszego przetwarzania
- DROP - odrzuć pakiet
- REJECT - odrzuć pakiet, ale poinformuj nadawcę o takiej sytuacji
Istnieją także dodatkowe akcje specyficzne dla tablicy. Przeważnie po podaniu akcji w tablicy filter reguła się kończy. W przypadku akcji SNAT oraz DNAT tablicy nat wymagane jest podanie docelowego adresu IP. Akcją bez argumentów dla tej tablicy jest MASQUERADE uruchamiające maskaradę.
Z powyższego wywodu możemy wywnioskować, że aby uruchomić NAT na naszym routerze musimy zapisać regułę tablicy nat w łańcuchu POSTROUTING. Więc tak, tablicę wskazujemy z pomoca opcji -t, teraz w zależności od tego czy chcemy wstawić w konkretną linią naszą regułę czy ją dopisać musimy użyć innych opcji. Opcja -I odpowiedzialną za wstawianie (INSERT) wymaga po podaniu nazwy łańcucha numeru linii, w którą ma wstawić daną regułem, natomiast opcja -A odpowiedzialna za dopisanie (APPEND). Ze względu na to, że jest to łańcuch POSTROUTING, to raczej będzie on pusty, więc nie będzie to miało znaczenia czy użyjemy dopisania czy wstawienia. Drugą rzeczą jest wskazanie interfejsu, na którym interfejsy będą wymagały NAT-u, aby transmisja miała sens. W przypadku wyjściowych łańcuchów takich jak POSTROUTING czy OUTPUT interfejs podwany jest po opcji -o (nazywane jest kierunkien od in/out, jednak łańcuchy mają już zdefiniowane możliwe kierunki transmisji) następnie po opcji -j podawana akcja, zawsze wieliki literami. Więc nasza reguła uruchamiająca NAT prezentuje się następująco. W przypadku tego oraz poniższych przykładów dotyczących zapory sieciowej interfejsem przeznaczonym do połączenia z siecią WAN będzie eth1.
xf0r3m@immudex:~$ sudo iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
Teraz o ile komputery podłaczone do interfejsu LAN posiadają prawidłowe adresy IP, powinny mieć już połączenie z Internetem. Mimo, że wszystko funkcjonuje prawidłowo to pozostawienie tak podłączonego routera do Internetu pozostawia wiele do życzenia. Pierwszym rażącym zaniedbanie jest fakt iż router będzie przyjmował połączenia z internetu, z każdym dowolnym portem, w ramach lepszego poznania pakietu firewall-a na Linuksie możemy nieco bardziej zabezpieczyć naszą transmisję.
Jak zapewne pamiętamy, każdy łańcuch posiada swoją politykę, która jest stosowana dla każdego pakietu, które nie zostanie przypasowana żadna reguła. Moglibyśmy ustawić ją na odrzucanie i dopuścić tylko potrzebny ruch. To dobry pomysł szczególnie jeśli myślimy o interfejsie WAN, ale zły jeśli pomyślimy interfejsie LAN. Ponieważ możemy zdefiniować polityki per interfejs, ten pomysł odpada, ponieważ dla interfejsu LAN musiała by powstać długa lista reguł dopuszczająca ruch do wszystkie bardziej oraz mniej znanych usług. Dlatego też pozostawimy politykę bez zmian i dopuścimy tylko niezbędny ruch. Na pewno z internetu musimy przyjąć dane od serwera DNS, aby rozwiązywać dotąd nieznane nazwy oraz w zależności konfiguracji interfejsu WAN dane z serwera DHCP usługodawcy. Łańcuch INPUT będziemy konfigurować dodają na początku reguły zezwalające na ruch, żeby następnie zablokować pozostały nieokreślony regułami.
xf0r3m@immudex:~$ sudo iptables -A INPUT -i eth1 -p udp --sport 53 -j ACCEPT #Opcjonalnie: xf0r3m@immudex:~$ sudo iptables -A INPUT -i eth1 -p udp --sport 67 -j ACCEPT #Zablokowanie pozostałego ruchu xf0r3m@immudex:~$ sudo iptables -A INPUT -i eth1 -p udp -j DROP; xf0r3m@immudex:~$ sudo iptables -A INPUT -i eth1 -p tcp --syn -j DROP;
Dwie pierwsze reguły dopusczają ruch z sieci WAN (interfejs eth1) na
porcie 53/udp (serwer systemu DNS) oraz 67/udp (serwer DHCP).
Pozostały ruch protokołu UDP został zablokowany. Podobnie jest z
całym ruchem na protokole TCP, tutaj dodano także modyfikator
--syn
. Jak wspominałem omawiając
protokół TCP, musi on zestawić połączenie, aby rozpocząć transmisje
co wymaga wysłania kilku pakietów sterujących. Pakiet wysyłany aby
zainicjować zestawienie połączenia TCP posiada własnie ustawioną
flagę SYN (pakiety TCP posiadają specjalne miejsce na flagi).
I tego typu pakiety są właśnie blokowane. To rozwiązanie skutecznie
zablokuje próbę połączenia, ale prawdopodbnie nie uchroni przed
metodą rozpoznawczą jaką jest skanowanie portów. Należało by tutaj
zamiast flagi, stanów połączenia.
Ze względu na to, iż zapora na Linuksie to temat rzeka. Małe ćwiczenie. Usprawnienie tej ostatniej reguły, takby aby opierała się na stanach TCP. (Podpowiedź: Zablokowanie ma służyć nawiązywaniu nowych połączeń z naszym routerem. Transmisja przez hosty w sieci, ktore łączy nasz routera powina przebiegać bez zarzutu).
Iptables nie jest bez wad jedną z nich jest utrzymanie reguł w łańuchach do momentu wyłączenia systemu. Dlatego systemy, w których instalowany jest ten pakiet dostępny jest też pakiet odpowiadający za zapisanie reguł w pliku i przywrócenie ich w momencie startu systemu. Pakiet nazywa się iptables-persistent dostępny jest w repozytoriach Debiana.
xf0r3m@immudex:~$ sudo apt install iptables-persistent
Zapisanie reguł odbywać się będzie za wydaniem poniższego polecenia.
xf0r3m@immudex:~$ sudo iptables-persistent save
Teraz router jest skonfigurowany i gotowy do działania. Routery programowe mają to do siebie, że ich funkcjonalność można rozszerzyć np. o serwer plików. Konfiguracje takiego serwera plików będziemy jeszcze omawiać w tym materiale. Także będziemy jeszcze wracać do naszego routera.
Kolejną kwestią jest komunikacja między sieciami. Jeśli nasz router łączy ze sobą więcej niż jedną sieć lokalną i ich komputery mają się ze sobą komunikowac to należy utworzyć statyczne trasy, wskazujące przez, który interfejs należy komunikować się z daną siecią.
xf0r3m@immudex:~$ sudo ip route add 192.168.1.0/24 via 192.168.1.1 dev eth2 xf0r3m@immudex:~$ sudo ip route add 192.168.4.0/24 via 192.168.4.1 dev eth0
Trasy te mogą zostać utworzone automatycznie jeśli np. interfejs sieci WAN zostanie podniesiony podczas automatycznej konfiguracji. Jeśli takie zachowanie nie jest przez nas porządane to w najlepszym wypadku jest zablokować transmisję między sieciami na firewall-u. Ninejszym temat konfigruacji routera uważam za zakończony. Nie mniej jednak to nie koniec rodziału. Zostały jeszcze dwa tematy do omówienia.
9.15. Sieci IP oparte na Ethernet. Protokół ARP i NDP
Mając na uwadzę to, że chcąc wysłać jakieś dane musimy podać adres hosta docelowego. Zazwyczaj takim adresem jest albo adres IP albo nazwa domenowa. Te piewsze wykorzystywane są głównie w sieciach lokalnych przez specjalistów. Zatem podając nazwę domenową aplikacja musi rozwiązać ją na adres IP i dokonuje tego za pomoca systemu DNS. To dzieje się w warstwie aplikacji. Warstwa transportowa definiuje port oraz rodzaj transmisji dla wysłanych danych. Najczęściej są to znane protokoły jak HTTPS, protokoły przesyłania strumieniowego oraz protokoły poczty. Warstwa sieciowa posiada już adres IP hosta docelowego i bez względu na to czy host docelowy znajduje się w tej samej sieci czy nie, pakiet przekazywany jest dalej i tutaj pozostaje nam pewna nie wiadoma, skąd host źródłowy wie jakim adresem MAC ma zaadresować ramkę ethernet?
Tutaj pojawia się protokoły ARP oraz RARP. Ten drugi jest może mniej wykorzystywany. Ich zadaniem jest zamiana adresów IP na MAC i odwrotnie. Głownie chodzi tu o protokół ARP i to nanim się się skupimy. Informacje na temat adresów MAC hostów w sieci przechowywane są w tablicy lub w buforze ARP. Jednak co jeśli tablica jest pusta? Wówczas protokół ARP dla docelowego adresu IP tworzy ramkę z żądaniem ARP. Taka ramka rozsyłana jest w całej sieci. Jeśli jeden ze znajdujących się w niej hostów posiada adres MAC dla adresu IP z żądania wówczas odsyłana jest odpowiedź. Nasz host na podstawie odpowiedzi uzpełnia swoją tablicę o ten wpis wówczas może on rozpocząć transmisję. Dostęp do tablicy w dystrybucjach Linuksa odbywa się z pomocą:
xf0r3m@immudex:~$ sudo ip -4 neigh 172.16.2.20 dev enp4s0 lladdr 74:27:ea:e9:cc:14 STALE 172.16.7.6 dev enp4s0 lladdr 70:85:c2:a4:bf:b9 STALE 172.16.8.2 dev enp4s0 lladdr 6c:4b:90:be:91:74 STALE 172.16.6.1 dev enp4s0 lladdr 78:45:c4:12:6a:5d STALE 172.16.2.100 dev enp4s0 lladdr 3c:84:6a:44:d7:a4 STALE 172.16.2.46 dev enp4s0 lladdr 74:27:ea:ea:98:8a STALE 172.16.17.15 dev enp4s0 lladdr 78:45:c4:09:23:4c STALE 172.16.2.146 dev enp4s0 lladdr 1c:69:7a:cd:f4:cb STALE 172.16.2.119 dev enp4s0 lladdr 74:27:ea:ea:63:56 STALE 172.16.8.13 dev enp4s0 lladdr 6c:4b:90:be:88:16 STALE 172.16.0.1 dev enp4s0 lladdr 90:e2:ba:45:4f:8c REACHABLE ...
Wpisy w tablicy same ulegją usunięciu jeśli nie nastąpiła aktywność przez pewien czas. Nie mniej jednak istnieje możliwość ręcznego usunięcia wpisów za pomocą polecenia:
xf0r3m@immudex:~$ sudo ip neigh del 172.16.8.13 dev enp4s0
Efektem działania tego polecenia będzie usunięcie wpisu dotyczączego
host 172.16.8.13
.
Protokołem o działaniu udwrotnym jest RARP, jego zadaniem była zamiana adresów MAC na adresy IP, przez co wykrzystywany był do automatycznej konfiguracji hosta, przed upowszechnieniem się protokołu DHCP. Obecnie jest już rzadko spotykany.
Za pomocą podpolecenia neigh polecenia ip mogliśmy sprawdzić tablicę ARP w systemie. Kiedyś używano do tego polecenia arp. Obecnie doszło do unifikacji przez narzędzie ip obsługi protokołu ARP dla IPv4 oraz protokołu NDP dla IPv6. NDP jest podobny rozwiązaniem dla IPv6. Jego działanie opiera na dwóch rodzajach komunikatów:
- Poszukiwanie adresu - pakiet tego rodzaju służy do uzyskania informacji o sieci lokalnej hosta wraz z adresem MAC.
- Ogłaszenie adresu - pakiet z odpowiedzią wraz z żądanymi informacjami.
9.16. Sieć bezprzewodowa
Logiczna konstrukcja sieci bezprzewodowej, nieco rózni się od połączeń kablowych kilkoma charakterystycznymi elementami w warstwie fizycznej. Sieci bezprzewodowe definiowane są przy użyciu standardu 802.11 i jego poszczególnych odmian (np. 802.11a lub 802.11n) Poniżej opiszemy sobie cechy charakterystyczne, z którymi będziemy mieć styczność podczas konfiguracji sieci bezprzewodowej.
- Identyfikator/Nazwa sieci/ESSID jest ciąg znaków jednoznacznie określający sieć. Każda sieć bezprzewodowa go posiada. Jest on niezbedny do połączenia.
- Dane autoryzacyjne sieci bezprzewodowe, mogą zostać zabezpieczone za pomocą funkcji kryptograficznych (zaszyfrowane), aby osoby nie będące jej częścią nie mogły się podłączyć lub przechwycić przesyłanych przez nie danych. Najczęsciej jest hasło, chociaż, czasami też stoswane są PIN-y (w przypadku funkcji WPS) lub dane logowania (w przypadku sieci korporacyjnych).
- Częstotliwość - fragment pasma częstotliwości radiowej, na której możliwa jest wymiana informacji w obrębie danej sieci bezprzewodowej. Bezprzewodowe sieci komputerowe operują najczęściej na częstotliwości 2,4GHz lub 5GHz. Każda sieć ma do dyspozycji tylko fragment pasma powyższych częstotliwości.
- Kanał - kanał jest powiązany z częstotliwością Kanał określa konkretną częstotliwość dla pasma np. 2,4GHz. Jeśli nasza sieć ma nadawać na pierwszym kanale (Kanał/CH 1, oznaczenia są różne) to znaczy że będzie nadawać na częstotliwości 2412MHz.
Oczywiście cech charakterystycznych jest jescze więcej. Nie opisywałem tutaj siły sygnału. To chyba każdy rozumie. My tutaj skupimy się głownie na podłączaniu się do sieci i to najlepiej za pomocą terminala. Opcji jest kilka.
- NetworkManager. Do przyłączenia się do sieci
bezprzewodowej możemy wykorzystać dobrze znany nam już menedżer.
Za pomocą komend polecenia nmcli możemy wyświetlić sobie
listę dostępnych sieci i następnie się do niej podłączyć. Poniżej
znajdują się polecenia, dzięki którym możemym wykorzystać ten
menedżer do podłączenia do sieci.
xf0r3m@immudex:~$ nmcli device wifi list * SSID MODE CHAN RATE SIGNAL BARS SECURITY netdatacomm_local Infra 6 54 Mbit/s 37 ▂▄__ WEP * F1 Infra 11 54 Mbit/s 98 ▂▄▆█ WPA1 LoremCorp Infra 1 54 Mbit/s 62 ▂▄▆_ WPA2 802.1X Internet Infra 6 54 Mbit/s 29 ▂___ WPA1 xf0r3m@immudex:~$ nmcli device wifi connect "F1" password "p4ssw0rd" #lub xf0r3m@immudex:~$ nmcli --ask device wifi connect "F1" xf0r3m@immudex:~$ nmcli -p -f general,wifi-properties device show wlan0
Można użyć modyfikatora--ask
zamiast wpisywać hasło w poleceniu. Ostatnie polecenie zwraca podstawowe informacje na temat interejsu oraz samej sieci. - iwd, jest menedżer sieci bezprzewodywych stworzony
przez firmę Intel. Jego obsługa jest interaktywna i opiera sie na
wydaniu kilku poleceń. W przeciwieństwie do NetworkManagera
(który może być już zainstalowany w wielu dystrybucjach) trzeba go
zainstalować. Pomoc dostępna jest wewnąrz narzędzia po jego
uruchomieniu. Uruchomienie odbywa się poprzez wydanie polecenia:
xf0r3m@immudex:~$ iwctl [iwd]# help [iwd]# device list [iwd]# station wlp1s0 scan [iwd]# station wlp1s0 get-networks [iwd]# station wlp1s0 connect SSID
Ten program wymaga nieco więcej poleceń. Ostanie poleceń, jeśli połączenie tego będzie wymagać zapyta o hasło. (Więcej informacji na https://wiki.archlinux.org/title/iwd) - wireless-tools - Pakiet. Konfiguracja ręczna.
Ten sposób jest najdłuższy, ale zadziała na każdej dystrybucji.
Składa się on z klasycznego interfejsu iwconfig oraz
programu wpa_supplicant służącego do łączenia się
sieciami wykorzystującymi mechanizm bezpieczeństwa WPA. Chcąc
znaleźć sieć, do której chcemy się podłaczyć musimy skorzystać z
polecenia iwlist, który wyświetli nam raport ze skanowania
z znalezionymi sieciami oraz ich parametrami. Te informacje są
trochę nieczytelne, ale nam potrzebny jest jedynie ESSID
(nazwa sieci) oraz sam fakt jej obecnosci. Po uzyskaniu tych
informacji musimy utworzyć plik zawierający profil sieci
posiłkując się poleceniem wpa_passphrase. Czasami może
być wymagane dodanie kilku opcji, ale przedstawie to na liście
poleceń. Następnie należy uruchomić polecenie
wpa_supplicant z odpowiednimi przełącznikami oraz opcjami
a na sam koniec należy pobrać adresy IP. Jeśli zestawienie
połączenie niedochodzi do skuktu to może oznaczać, że uruchomiony
jest już demon, którego to my mieliśmy uruchamiać. Można wówczas
wyłączyć jednostkę i zabić wszystkie pozostałe procesy związane
z wpa_supplicant, a następnie spróbować ponownie. Jednak
tutaj mała wskazówka. W raz z obecnością działającego demona
wpa_supplicant w systemie dostępne jest polecenie
wpa_cli, które umożliwia nam podłączenie za
pomocą interaktywnego interfejsu. Nie mniej jednak próba reczenego
podłączenia się do sieci bezprzewodowej wygląda następująco.
xf0r3m@immudex:~$ sudo iwlist wlp1s0 scan xf0r3m@immudex:~$ wpa_passphrase "F1" 'p4ssw0rd' >> f1_wlan.conf #lub xf0r3m@immudex:~$ echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev" | tee f1_wlan.conf xf0r3m@immudex:~$ echo "update_config=1" | tee -a f1_wlan.conf xf0r3m@immudex:~$ echo "country=PL" | tee -a f1_wlan.conf xf0r3m@immudex:~$ wpa_passphrase "F1" 'p4ssw0rd' | tee -a f1_wlan.conf xf0r3m@immudex:~$ sudo systemctl stop wpa_supplicant.service xf0r3m@immudex:~$ sudo kill -9 $(ps -aux | grep '^root.*wpa_supplicant' | awk '{printf %2" "}') xf0r3m@immudex:~$ sudo wpa_supplicant -B -Dwext -iwlp1s0 -cf1_wlan.conf xf0r3m@immudex:~$ sudo dhclient
Jak możemy zauważyć proces jest nieco bardziej złożony, ale jest to ręczna metoda. Z racji tego iż opisałem główny proces powyżej tutaj skupię się na kilku szczegółach. Programwpa_passphrase
tworzy tak zwany blok sieci w nim zawarta jest nazwa sieci oraz klucz PSK (hasło do sieci). Mimo tego my podajemy ten plik jako plik konfiguracyjny wpa_supplicant i w niektórych przypadkach nie jest wystarczająca konfiguracja. Dlatego też podaje się takie informacje jak lokalizacja interfejsu sterowania wpa_supplicant (ctrl_interface=DIR=/var/run/wpa_supplicant
), grupa użytkowników, którzy mogą sterować wpa_supplicant (GROUP=netdev
), zezwolenie na możliwość modyfikacji konfiguracji (update_config=1
) oraz kod ISO kraju w którym działa odbiornik (country=PL
). Kod kraju jest o o tyle istotny, że każdy kraj posiada swoje regulacje prawne odnośnie wykorzystania częstotliwości radiowych, które są wykorzystywane przez sieci bezprzewodowe. Na przykład w Polsce dostępnych jest 13 kanałów a w USA tylko 11. A to są tylko kanały większość krajów reguluje również moc nadawczą. Więc manipulacja kodem kraju może wpłynąć na siłę sygnału i poprawić jakość połączenia. Kolejną rzeczą jest działanie już wpa_supplicant w systemie. Ten program może działać jako usługa. Dzięki czemu możemy zapisać bloki sieci w ogólnym pliku konfiguracjnym. Wówczas powoduje to, że jeśli komputer z włączony demon znajdzie się w zasięgu sieci, zostanie z nią połączony. W przypadku kiedy łaczność ma być wykonywana na żadanie użytkownika działanie demona nie jest pożądane. Dlatego demona należy wyłączyć, ale mimo jego wyłączenia nadal może pozostawać proces, który bedzie blokował próbę podłączenia się. Dlatego też ważne jest aby pozbyć się tego procesu i za to odpowiada polecenie (kill
) w podstawieniu polecenia uzyskujemy PID tego procesu. Uruchomienie polecenia wpa_supplicant wymaga kilku opcji. Opcja-B
uruchamia narzędzie w tle, opcja-D
wraz z wartościąwext
jest to wskazanie sterownika odpowiedzialnego za obsługę sieci bezprzewodowej, dostępnych jest ich kilka,wext
jest rozwiązaniem przestarzałym niemniej jednak cechuje się obsługa przez bardzo wiele układów kart bezprzewodowych na rynku. Następnie po opcji-i
podaje się interfejs sieciowy, ostatnią opcją jest-c
, która wskazuje na plik konfiguracyjny. Zwróćmy uwagę na to, iż opcję wraz z wartościami zapisujemy łącznie. Inną opcją skorzystania z tego pakietu jest wykorzytanie narzędzia wpa_cli. Poniżej znajduje się lista polecenie, jakich należy użyć. Użycie wpa_cli wymaga działania demona wpa_supplicant.xf0r3m@immudex:~$ sudo wpa_cli > scan > scan_results #tu_wyswietli_sie_lista_znalezionych_sieci > add_network #tu_wyswietli_sie_id_sieci_wazne_bo_sluzy_do_odlowania_sie_do_sieci > set_network 0 ssid "nazwa_sieci/ESSID" > set_network 0 psk "haslo/PSK" > enable_network 0 #tu_wyswietli_sie_informacja_o_podlaczaeniu_do_sieci > save_config > quit
Jeśli nie chcemy aby połączenie zostało zapisane dołączenia do łączenia automatycznego to należy pominąć przedostatnią opcję.
Na konfiguracji narzędzia wpa_suplicant zakończymy temat konfiguracji sieci w Linuks. Jednak to nie koniec tematów sieciowych ponieważ następne dwa rozdziały będą na niej bazować.
10. Warstwa aplikacji - usługi sieciowe
W poprzednim rozdziale dowiedzieliśmy się czym są sieci komputerowe i jak wygląda ich konfiguracja w dystrybucjach Linuksa. Sieci służą do przesyłania danych, a te musimy dostarczyć za pomocą uzupełnienia formularza na stronie internetowej lub w postaci zwykłego pliku tekstowego. Za obie te formy odpowiadają różne ustandaryzowane zbiory programów składające się na jeden konkrent protokół. Protokołów nie należy stawiać obok programów, które możemy kojarzyć z daną formą sposóbu przesyłania informacji przez sieć. Te programy najczęściej zajmują się ich obsługą, zawierają tym samym ich implementacje oraz dostosowanie aby korzystanie z protokołu było najcześciej niezauważalne a końcowy użytkownik utrzymał tylko i wyłącznie żądaną stronę internetową lub dostęp do zasobu dyskowego udostępnionego gdzieś na serwerze.
Aby pomiędzy dwoma stronami mogła zajść komunikacja potrzebny jest nadawca i odbiorca. Z punktu widzenia sieci komputerowej, każdy host jest jednocześnie jednym i drugim. Jednak patrząc na to ze strony obecnie omiawianego tematu, różnica między stronami jest już dużo bardziej podkreślona. Mamy sztywny podział między aplikacji klienckimi danego protokołu, które żądają dostępu do konkretnego zasobu a serwera, które te dane udostępniają. Wiele serwerów określa się mianem usług sieciowych. Często mówiąc o jakiś serwerze do słowa serwer dodaje się protokół, który obsługuje. Jak możemy pamiętać np. z rozdziału 7, niektóre serwery domyślnie nie są usługami sieciowymi (chociaż mogą, wystarczy odpowienio je skonfigurować).
Usługi sieciowe mają za zadanie udostępniać dane poprzez wybrane protokoły. Na przykład coś co nazywamy przeglądarką internetową opiera swoje działanie na protokole HTTP bądź jego bezpieczeniejszej wersji w postacji protokołu HTTPS. Programy pocztowe wykorzystują protokoły SMTPS oraz POP3S lub IMAPS. Wiele różnych aplikacji sieciowych może wykorzystywać różne protokoły lub samodzielnie implementować format danych przesyłanych przez sieć. Nie mniej jednak wszystkie te protokoły rezydują w warstwie aplikacji, czwartej warstwie modelu TCP/IP.
W tym rozdziale nadal pozostaniemy w sieciach. Przeanalizujemy działanie protokołu HTTP. Omówimy sobie usługę sieciową na podstawie SSH. Dowiemy się jak uruchamiane były, niektóre specyficzne usługi i jak są uruchamiane teraz. Sprawdzimy sobie narzędzia diagnostyczne, które pozwolą nam na sprawdzenie dostępności usługi ale również monitorowanie przesłanych przez nia i do niej danych. Na koniec zajmiemy się zdalnym wywołaniem procedur (RPC) oraz omówimy sobie podstawowe zagadnienia związane z bezpieczeństwem. Tematem zaawansowamym będą gniazda.
10.1. Analiza HTTP
Na wstępie omówilśmy sobie co tak naprawdę znajduje się w warstwie aplikacji. Aby była możliwość zapewnienia bezbłędnego działania wielu znanych protokołów, które są podstawą komunikacji w internecie, zespoły odpowiedzialne za implementacje tych standardów często umieszczają w swoich programach klienckich możliwość analizy przesyłanych danych. W tym podrozdziale skupimy się głównie na jednym protokole i jednym narzędziu umożliwiającym śledzenie danych przesyłanych za pomocą protokołu. Jednak w podrozdziale poświęconym narzędziom dianostycznym dowiemy się, że możemy sprawdzić większość usług, za pomocą kilku narzędzi.
W większości dystrybucji dostępne jest narzędzie curl jest ono nagminnie wykorzystywane do pobierania plików za pomocą protokołu HTTP w terminalu. Program ten jako, że wykorzystuje protokół HTTP/S posiada również możliwość prześledzenia transmisji. Jako przykład wykorzystamy serwis pogodowy, który przesyła obecną sytuację pogodową (lub dwu-dniową prognozę) za ASCII. Dla przykładu:
xf0r3m@immudex:~$ curl wttr.in/Warszawa?0\&lang=pl Pogoda w: Warszawa \ / Słonecznie .-. 21 °C ― ( ) ― → 9 km/h `-’ 10 km / \ 0.0 mm
Na podstawie wyścia tego polecenia, ciężko jest na prierwszy rzzut oka dostrzec, że jest to strona internetowa, ale tak właśnie jest. Chcąc przeanalizować jak wygląda komunikacja za pomocą protokołu HTTP należy wydać następujące polecenie:
xf0r3m@immudex:~$ curl --trace-ascii curl.log wttr.in/Warszawa?0\&lang=pl
Opcja --trace-ascii
mówi programowi
curl aby zapisywał on komunikaty diagnostyczne. Dodatkowo
ta opcja wymaga podania pliku przeznaczonego na dane wyjściowe. W
moim przypadku jest curl.log. Poniżej znajduje się jego
zawartość, pominąłem na poniższym przykładzie dane zwracane przez
polecenie (są one normalnie dostępne w terminalu po wydaniu
polecenia).
== Info: Trying 5.9.243.187:80... == Info: Connected to wttr.in (5.9.243.187) port 80 (#0) => Send header, 89 bytes (0x59) 0000: GET /Warszawa?0&lang=pl HTTP/1.1 0022: Host: wttr.in 0031: User-Agent: curl/7.74.0 004a: Accept: */* 0057: == Info: Mark bundle as not supporting multiuse <= Recv header, 17 bytes (0x11) 0000: HTTP/1.1 200 OK <= Recv header, 32 bytes (0x20) 0000: Access-Control-Allow-Origin: * <= Recv header, 21 bytes (0x15) 0000: Content-Length: 314 <= Recv header, 41 bytes (0x29) 0000: Content-Type: text/plain; charset=utf-8 <= Recv header, 37 bytes (0x25) 0000: Date: Wed, 23 Aug 2023 08:23:51 GMT <= Recv header, 2 bytes (0x2) 0000: <= Recv data, 314 bytes (0x13a) 0080: . .[38;5;226m ... ( ) ... .[0m .[1m....[0m .[38;5;154m9.[0 00c0: m km/h.[0m . .[38;5;226m `-... .[0m 10 km.[0m 0100: . .[38;5;226m / \ .[0m 0.0 mm.[0m . == Info: Connection #0 to host wttr.in left intact
Pierwsze dwie linie, są to linie informujące nas o uzyskanym adresie
IP oraz o tym czy połącznie powiodło się. Jeśli tak jest, to wówczas
wysyłany jest nagłówek - w nagłówkach HTTP
przeważnie znajdują się informacje kontrolne dla programów, które
będą zajmować się interpretowaniem treści przesłanej przez serwer.
No właśnie. Logiczne było by to, że przeglądarka powinna
pobrać żądaną stronę WWW. W rzeczywistości to klient przesyła
jedynie nagłówek z żądaniem. Przyglądając się pierwszej linii
nagłówka
rozpoczynajcego się liczbą: 0000
(Ta liczba to bajt rozpoczęcią tych danych w ładunku/bloku danych
przesyłanych w pakietach sieciowych). Zawiera ona słowo kluczowe
GET
Następnie występuje wartość
żądania (czego sobie życzysz), na podstawie tych informacji skrypt
jest wstanie zwrócić odpowiedni stan pogodowy, w przypadku kiedy
poprostu odwiedzamy jakąś witrynę to najczęściej po słowie
GET występuje ukośnik (/). Serwery wówczas
interpretują to, że żądanie dotyczy głównie pliku index.html
zapisanego w katalogu przypisanym na serwerze do danej witryny.
Uwaga! Jeden serwer WWW, może utrzymać kilka jak nie kilkanaście lub kilkadziesiąt różnych stron internetowych. Wszystko zależy od jego ustawień oraz konfiguracji sprzętowej, która go utrzymuje. Dlatego też wiele stron może kierowanych pod jeden adres IP. Taka funkcjonalność nazywa się hostingiem.
W naszym przypadku również żądamy głównego pliku tego hostingu. W
nagłówku natomiast przekazujemy dane, które pomogą w uzyskaniu
żądanych przez nas danych. Jest to jedna z cech protokołu HTTP. Dane
(głównie do aplikacji, ponieważ statyczne strony rzadko interpretują
jakieś dane) mogą być przekazywane jako rozwinięcie adresów jest to
tak zwana metoda GET lub w postaci odrębnego pakietu danych
poza widocznością dla zwykłego użytkownika - metoda POST.
Przy użyciu metody POST najczęściej przesyłane są dane
uzupełnionych formularzy na stronach WWW. Po danych przekazywanych
aplikacji (w tym przypadku) wstepują już informacje kontrolne dla
samego protokołu HTTP w linii 0022 (będę używał takich
oznaczeń) występuje pole nagłówka
Host:
zawiera ono nazwę hosta do,
którego kierujemy nasze żądanie. Następne pole nagłówka to
User Agent:
, to pole zawiera
informacje jakiego programu klienckiego WWW używamy. Ta informacja
jest zapisywana w plikach dziennika serwera wraz z adresem IP.
Narzędzia takie jak curl lub podobny mu i bardziej
powszechny wget pozwalają na zmianę tej wartości, za pomocą
jednej opcji możemy podać się za np. program indeksujący jednej z
wyszukiwarek. Trzecim polem jest rodzaje danych jakie może
zinterpretować, polecenie curl to służy głównie pobieraniu
treści więc
rzadko kiedy je interpretuje. Wyjątkiem może być omawiany przez nas
przykład. Ostatnim pole jest znak nowego wiersza, który kończy
nagłówek żądania. Teraz następuje jego przesłanie i oczekiwnaie na
odpowiedź serwera.
W przypadku odpowiedzi jak możemy zauważyć przesyłanych jest wiele nagłówków przedstawiających pojedyncze pola. Nie będe ich tutaj wszystkich opisywał, jednak na uwagę zasługuję jedna informacja, która została wcześniej pominięta. Otóż, w polu metody żądania lub w pierwszym naglówku odpowiedzi znajduje się wykorzystywana w transmisji wersja protokołu HTTP. Wersja 1.1 jest obecnie standardem, jednak obecnie w stanie proponowanych standardów są wersje 2 i 3. Seria nagłówków odpowiedzi kończy się w taki sam sposób jak w przypadku nagłówka żądania, znakiem nowego wiersza. Następnie odebrany zostaje pakiet danych a dane w nim zawarte trafiają do użytkownika.
Tak moglibyś to wywnioskować na podstawie danych diagnostycznych zwróconych przez program curl. Wygląda to nieco inaczej, ale różnice występują tylko w przedstawieniu danych. Otóż otrzymując pakiet protokołu HTTP, nie ma w nim podziału na sekcje nagłówka i danych. Nie ma także dwóch odrębnych pakietów. Wszystko spakowane jest w jednej paczce, a curl na podstawie znaku nowego wiersza jest jedynie oddzielić te dane.
Innym sposobem na wejście interakcje np. z protokołem HTTP jest wykorzystanie zapomnianego już programu Telnet. Telnet kiedyś służył do tego do czego służy teraz protokół SSH. Do zdalnego połączenia się z powłoką. Telnet sam w sobie również jest protokołem, obecnie rzadko jest spotykany w swojej pierwotnej formie, chyba że w postaci lokalnej konsoli w systemach wbudowanych.
xf0r3m@immudex:~$ telnet example.org 80 > telnet.log Trying 93.184.216.34... Connected to example.org. Escape character is '^]' GET / HTTP/1.1 Host: example.org HTTP/1.1 200 OK Age: 126394 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Wed, 23 Aug 2023 11:28:50 GMT Etag: "3147526947+ident" Expires: Wed, 30 Aug 2023 11:28:50 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (dcb/7F7F) Vary: Accept-Encoding X-Cache: HIT Content-Length: 1256 ...
Po wielokropku występuje już tylko kod HTML strony. Polecenie
telnet
wymaga portu, pod który
należy się podłączyć. Aby otrzymać odpowiedź musimy metodę
(GET
), ewentualne dane/żądany plik
(/
), wersje protokołu
(HTTP/1.1
) w drugiej lini nagłówka
należy podać nazwę hosta
(Host: example.org
), ze względu na
to aby serwer WWW wiedział z jakiej witryny przesłać odpowiedź.
Protokół jest może trochę mało interaktwny przy takim połączeniu, ale np. usługi pocztowe są już bardziej skore do rozmowy.
10.2. Serwery sieciowe
W dystrybucjach Linuksa możemy napodkać wiele serwerów, niektóre z nich działają w oparciu o sieć i oczekują zdarzeń pochodzących od zdalnych użytkowników. Serwery wewnątrz systemów to dość niszowa grupa, do których zalicza się tylko kilka demonów np. cron. Dlatego w mowie potocznej przyjęło się, że mówiąc o serwerach mamy na myśli serwery sieciowe. Poniżej znajduje się lista rodzajów serwerów wraz kilkoma przykładmi oprogramowania.
- Serwery WWW: - Apache, Nginx
- Serwery pocztowe: - SMTP: Postfix, Exim; POP3/IMAP: dovecot
- Serwery plików: - CIFS (udziały Windows): Samba; FTP: vsftpd; NFS: nfs-kernel-server
- Serwery zdalnego dostępu: - SSH: openssh; RDP: xrdp.
- Serwery sieciowe: - DHCP: isc-dhcp-server, dnsmasq; DNS: bind9, dnsmasq
- Usługa zdalnego wywołania procedury (RPC)
Aby bardziej zaznajomić się z serwerami sieciowymi, omówimy sobie po krótce jeden z nich. Z powyższej listy najciekawszym z nich jest znana implementacja protokołu SSH - openssh. Implementacji protokołu SSH jest kilka, jednak ta jest w najpowszechniejszym użytku obecna jest nawet w najnowszych wersjach MS Windows 10.
10.3. Serwer bezpiecznej powłoki - implementacja openssh
Demony sieciowe zawyczaj skupiają się na czynnościach wokół protokołu, który obsługują. Implementacja openssh ma za zadanie zapewnienie solidnego poziomu bezpieczeństwa zarówno dla zdalnej powłoki, transferu plików jak i innych połączeń za pomocą proxy czy tunelowania.
10.3.1. Aplikacja klienta - polecenie ssh
Za pomocą polecenia ssh mamy dostęp do większości cudów jakie oferuje ta implementacja. Najprostszym jej wywołaniem jest podanie nazwy użytkownika następnie adresu zdalnego hosta, z którym chcemy się połączyć. Obie te wartości połączone są ze sobą za pomocą małpy (@). Po poprawnym znalezieniu hosta i połączeniu z jego demonem SSH (będzie o nim za chwile), zostaniemy poproszeni o hasło. Jeśli łączymy się z serwerem po raz pierwszy, Zostanie nam wyświetlona informacja na temat, że nie można zweryfikować autentyczności hosta.
xf0r3m@immudex:~$ ssh -p 2022 searx.morketsmerke.org The authenticity of host '[searx.morketsmerke.org]:2022 ([82.117.231.222]:2022)' can't be established. ECDSA key fingerprint is SHA256:ghhvjaz6T/qcsX9TiWN4UEV4fuiv6oqHxsD1bGB+40c. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[searx.morketsmerke.org]:2022,[82.117.231.222]:2022' (ECDSA) to the list of known hosts.
W przypadku protokołu SSH, serwer musi się przedstawić za każdym razem gdy nawiązujemy z nim połączenie. Jeśli jest pierwsze połączenie z tym serwerem, to na komputerze klienta nie ma jeszcze danych autentykacji serwera (bez wdawania sie w zawiłe, kryptograficzne szczegóły) wówczas musimy się upewnić we własnym zakresie czy jest rzeczywiście ten serwer, z którym chcemy się połączyć, a nie np. próba wyłudzenia hasła. Po wyświetleniu się tego komunikatu zostaniemy zapytaniu czy chcemy kontynuować, jeśli odpowiedź jest twierdząca to dane autentykacyjne serwera zostaną zapisane w plikach na naszym komputerze i przy kolejnej próbie logowania poproszeni zostaniem wyłącznie o hasło.
xf0r3m@immudex:~$ ssh -p 2022 searx.morketsmerke.org xf0r3m@searx.morketsmerke.org's password:
Na powyższym przykładzie podałem opcję
-p
, która pozwala na wkazanie
nie standardowego portu (standardowy port to 22/TCP).
Na tym przykładzie pominąłem, użytkownika. Jeśli go pominiemy
ssh jako użytkownika przyjmie tego, który wydał polecenie.
Domyślnie podczas logowania używa się haseł, ale jeśli mamy pod sobą kilka maszyn i wpisywanie do każdej hasła może być męczące. SSH daje nam możliwość uwierzytelnienia za pomocą kluczy kryptograficznych. Nie będę zagłębiał się charkterystykę kluczy kryptograficznych, ale istnieje możliwość wygenerowania pary plików (klucza publicznego oraz prywatnego), następnie załadowanie jednego z nich do maszyn, którymi administrujemy. Podczas generowanie pary kluczy, będzie można podać hasło chroniące klucz prywatny. Najlepiej, aby dodać to hasło. Wówczas będziemy mieć jedno hasło do wielu maszyn (niezależne, bo nawet jeśli główny administrator serwera zmieni nam hasło, to jeśli klucz nadal będzie w systemie, to nadal będziemy mieli możliwość zalogowania się do systemu.).
Aby wygenerować klucze wydajemy następujące polecenie. Dodam tylko, że obecnie będziemy generować klucze algorytmu kryptograficznego RSA, są one domyślne dla SSH. Choć, nie które z systemów mogą ich nie przyjmować i wymagać innych rozdzajów. Więcej na temat generowanie kluczy znajduje się na stronie podręcznika polecenia ssh-keygen.
xf0r3m@immudex:~$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/xf0r3m/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/xf0r3m/.ssh/id_rsa Your public key has been saved in /home/xf0r3m/.ssh/id_rsa.pub The key fingerprint is: SHA256:7N+WSF5OuOXiGh0hWr3hjMClfssGrn7Ofh8igPYpqcw xf0r3m@immudex The key's randomart image is: +---[RSA 3072]----+ | . | | . o . | | + o + | | . . = = + | | o . + S =. | | . o + = oo.+ | | o o o BooO . | |o. . o.o.+=o= | |.E .o++..++o. | +----[SHA256]-----+
Polecenie wydajemy bez żadnych opcji chcąc wygenerować parę kluczy RSA. Ścieżkę zapisu klucza równięż pozostawiamy bez zmian, oczywiście jeśli mamy w planach ustawienie hasła klucz prywatnego. Jeśli nie to może warto było by wskażać jakie bezpieczne miejsce np. szyfrowaną partycję lub dysk. Posiadając parę kluczy możemy je załadować do docelowych systemów.
xf0r3m@immudex:~$ ssh-copy-id xf0r3m@172.26.38.226 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/xf0r3m/.ssh/id_rsa.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys xf0r3m@172.26.38.226's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'xf0r3m@172.26.38.226'" and check to make sure that only the key(s) you wanted were added
Klucze możemy załadować do systemu za pomocą polecenia
ssh-copy-id
. Działanie tego polecenia
widzimy na powyższym przykładzie. To polecenie przyjmuje również
te same opcje jak w przypadku polecenia ssh. Jeśli podaliśmy
inną ścieżkę podczas generowania klucza to za pomoca opcji
-i możemy skazać plik klucza publicznego
(plik z rozszerzeniem .pub). Natomiast jeśli chcemy się uwierzytelnić
za pomocą klucza to przy opcji -i polecenia ssh
podajemy plik klucza prywatnego (bez rozszerzenia).
10.3.2. Demon serwera - sshd
Jak możemy zdawać sobie z tego sprawę z demona nie korzysta się jak z aplikacji klienckiej. Nie wydajemy poleceń, żeby go uruchomić. Oczywiście możemy to zrobić, ale normalnie tego się nie robi. Demony uruchamiane są za pomocą plików jednostek (systemd). Dlatego, że obsługa demona różni się od obsługi klienta, w tym podrozdziale skupimy się głównie zmianie konfiguracji demona openssh, jednak nie będziemy się skupiać na wszystkich możliwych opcjach poniżej przedstawię tylko te, które mogą nam się przydać na obecnym poziomie wtajemniczenia. Opisy tych opcji przybliżą nam również nie co bardziej działanie samego SSH.
Konfiguracja demona openssh znajduje się w pliku: /etc/ssh/sshd_config. W tym pliku wiele opcji jest wyłaczonych (ujęte w komentarz), oznacza to, że przechowują one domyślne wartości. Jeśli więc chcemy coś zmienić w konfiguracji daemona SSH, musimy usunąć początkowy symbol komentarz i zmienić wartość opcji.
- Port - Czasami, aby nieco ukryć przed wścibstwem zmienia się domyślny port, aby nie każdy wiedział, że można się do tego systemu za pomocą SSH podłączyć. Obcnie nie przynosi to rezultatów. Bezpieczeniej jest wyłączyć logowanie za pomocą haseł.
- LoginGraceTime - oczekiwanie na wpisanie hasła. Podczas podłączania się do serwera SSH oczekuje na wpisanie hasła i ten czas ma swój limit. Domyślnie są to 2 minuty. Po tym czasie mimo prawidłowo podanego hasła połączenie zostanie zerwane i trzeba się będzie logować ponownie.
- PermitRootLogin - Zezwolenie na logowanie się jako superużytkownik. Jeśli dostęp do tego konta nie jest jakos bardzo potrzebny to możemy ustawić wartość tego pola na no. Ze względu na to, że znana jest jego potoczna nazwa często jego konto pada ofiarą ataków siłowych, skupionych na próbach odgadnięcia hasła (Przeciwdziałanie tego typu atakom, zostanie przedstawione w tym rozdziale).
- PasswordAuthentication - zmiana wartości tej opcji na no zabroni używania haseł do logowania się. W użyciu pozostaną wówczas same klucze. Lepiej nie zmieniać tej opcji, do póki nie załadujemy swojego klucza.
- X11Forwarding - dość ciekawa opcja pozwala uruchomić program okienkowy na serwerze, kontrolując go z poziomu klienta. Proces programu uruchomiany jest na serwerze, okno (np. przeglądarki, uruchamiane jest na komputerze klienta). Pozwala to na konfiguracje, aplikacji wymagającej połączenia z tej samej sieci co serwer lub z poziomu pętli zwrotnej, co nie zawsze jest fizycznie możliwe (serwery VPS). Uruchomienie takiego programu wymaga podania użycia przez klient opcji -Y lub -X.
Oczywiście opcje z omawianego przez nas pliku są opisane na stronie
podręcznika man 5 sshd_config
. Po
zapisaniu zmian w pliku konfiguracyjnym demona SSH, należy uruchomić
go ponownie aby nasze zmiany zaczęły działać.
xf0r3m@immudex:~$ sudo systemctl restart ssh.service
10.3.3. Zabezpieczenie fail2ban
Wystawiając otwarty port 22/TCP czyli SSH, możemy zauwać w plikach dzienników systemowych (plik /var/log/btmp - polecenie lastb - nieudane próby logowania) wiele prób logowania się, odgadywania nazwy użytkownika oraz hasła. Możemy wpaść na pomysł wyłączenia wyłaczenia logowania za pomocą hasła. Jest to pewne rozwiązanie, jednak:
- Logowanie wyłącznie za pomocą kluczy spowoduje, że połączenie z serwerem będzie możliwe z tylko i wyłącznie z komputerów, na których znajduje się klucz. Zdaje sobie z tego sprawę, że może być to dobre rozwiązanie. Oczywiście bezpieczeństo to niewygoda. Poza tym klucze można kopiować i przenościć, o ile zostały podjęte jakieś działania mające na celu zabezpieczenie klucza prywatnego.
- Ta metoda nie rozwiązuje pojawiających się w logach prób logowania. Mimo iż serwer jeśli nie podamy mu odpowiedniego klucza zwróci informacje o braku dostępu. To sowiste pukanie do drzwi, gdzie nasz serwer odpowiada: "Nieczynne! Remanent." ;)
Co jeśli serwer ma być podpięty do domeny, i umożliwiać logowania dużej ilości użytkowników? Wtenczas administrator będzie musiał spędzić trochę kariery na dodaniu tych wszystkich kluczy. A to nie koniec, ponieważ rotacje pracowników zdarzają się dość często. W tym wypadku nie możemy zablokować logowania za pomocą hasła.
Istnieje kompromis między logowaniem za pomocą hasła a ograniczeniem odgadywania dostępu przez różnego rodzaju automaty. Jest nim fail2ban. Jest to usługa monitorująca próby uwierzytelnienia się i po przekroczeniu nie udanych prób odcinająca dostęp do usługi na jakiś czas. Usługa ta opiera swoje działanie na monitorowaniu komunikatów diagnostycznych oraz dynamicznym tworzeniu i usuwaniu reguł zapory sieciowej.
Uwaga! Na Debianie fail2ban nie posiada w swoich zależnościach pakietu zapory, dlatego też należy pamiętać, żeby dopisać go podczas instalacji programu. Pakiet fail2ban znajduje się w repozytoriach.
Aby banowanie miało skutek konfiguracja narzędzia musi znaleźć się w pliku /etc/fail2ban/jail.conf. Domyślnie ten plik zawiera bardzo dużo różnych opcji, sekcji oraz wartości. Poniżej umieściłem minimalną działającą konfigurację narzędzia dla SSH. Fail2ban może rownież działać z innymi usługami.
[INCLUDES] before = paths-debian.conf [DEFAULT] protocol = tcp chain = <known/chain> port = 0:65535 banaction = iptables-multiport action = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] [sshd] enabled = true port = 2022 filter = sshd maxretry = 3 findtime = 5m bantime = 30m backend = systemd
W piewszej sekcji ([INCLUDES]
)
znajduje się jedna opcja wskazująca plik z konfiguracją scieżek
przeznaczoną
dla wybranej dystrybucji. Każda dystrybucja może zawierać w różnych
miejscach komponenty potrzebne mu do działania stąd ten plik. Sama
sekcja zawiera może zawierać dodatkowe konfiguracje, które mogą być
interpretowane przez program przed interpretacją sekcji przeznaczonej
dla wybranej usługi (opcja before
).
Sekcja [DEFAULT]
rozpoczyna się
od określenia domyślnej akcji podejmowanej podczas nadużycia oraz
jej konfiguracji. Jak możemy się domyśleć po wartości opcji
banaction
, wstrzymanie możliwości
logowania będzie opierać się reguły iptables. Sama wartość
tej opcji jest wskazaniem pliku w katalogu
/etc/fail2ban/action.d. W tym pliku możemy zobaczyć jak na
podstawie wykonywanych przez fail2ban czynności układane są
reguły iptables. Sekcja
[DEFAULT]
zawiera podsekcje
dedykowane dla konkretnych usług. W naszym przypadku jest to demon
SSH. W sekcji usługi znajdują się opcje definujące kryteria dotyczące
blokowania dostępu podczas nadużycia. Zatem kolejno:
- enable - załączenie kontrolowania oraz blokowanie w przypadku nadużyć
- port - port pod którym uruchomiona jest monitorowana usługa na naszym komputerze.
- filter - jest wskazanie zbioru wzorców w postaci wyrażeń regularnych. Fail2ban wykorzystuje usługi dzienników systemowych do wykrywania prób uwierzytelnienia. Plik odpowiadający wartości tej opcji znajduje się w katalogu /etc/fail2ban/filter.d.
- maxretry - ilość możliwych prób, przed zablokowaniem.
- findtime - ilość czasu jaką należy odczekać od między próbami, aby uzyskać z powrotem wszystkie próby zdeklarowane w opcji maxretry. Wygląda to w ten sposób, że możemy po nieudanej próbie uwierzytelnienia zrezygnować z dalszych prób, ale jeśli spróbujemy ponownie przed upłynięciem tego czasu, to dalej liczba prób pozostanie nie zmieniona (pozostaną 2 próby).
- bantime - ilość czasu blokady dostępu do usługi.
- backend - źródło komunikatów logowania. Bez tej wartości sama usługa fail2ban nie wystartuje. Systemd może być dobry źródłem takich informacji.
Tyle informacji wystarczy demonowi fail2ban do monitorowania i także przeciw działania próbą uzyskania dostępu do powłoki. Jeśli zmieniamy coś w plikach konfiguracyjnych to wówczas należy pamiętać o restartcie usługi. Po zastosowaniu tego rozwiązania będzie zauważymy znaczny spadek nieudanych prób logowania. Każdy ma 3 próby jeśli wszystkie się nie powiodą adres IP, z którego nasz świadek jehowy puka zostanie zbanowany na 30 minut. Pozostaje również możliwość logowania się za pomocą hasła.
Nie ma metody, która jednoznacznie rozprawiwła by się z próbami uzyskania dostępu do SSH. Można za pomocą informacji w internecie ustawić czas blokady na stałe lub zwiększać go jeśli trafi się naprawdę natrętny osobnik, albo odciąć osiągalność demona SSH z internetu i wymagać do tego połączeń VPN.
10.3.4. Pozostałe metody użycia SSH
Najpopularniejsza implementacja protokołu bezpiecznej powłoki, to nie tylko jakby sama nazwa wskazywała dostęp do powłoki systemu w bezpieczny sposób, ale także bezpieczne przesyłanie plików za pomocą SSH.
Pierwszym z nich jest SCP, który tak jakby zwykłym poleceniem cp, ale przesyłającym plik za pomocą szyfrowanago kanału i rozszerzającym scieżki o dowolne Uniksy w internecie. Istotnym czynnikiem korzystania z SCP jest fakt, że musimy niestety znać zdalną ściezke dostępu do pliku.
xf0r3m@immudex:~$ scp test.txt user@server:/usr/share/doc/test/test.txt
Wraz z SCP dostępne jest bardziej interaktywne polecenie, które jest
też protokołem - SFTP. W informatyce jest wiele jest
rozwiązań tego skrótu, nas będzie interesować wyłącznie
SSH File Transfer Protocol. Połaczenie SFTP realizowane jest
na takiej same zasadzie jak SSH. Podajemy nazwę polecenia użytkownika
oraz host, po jego zgłoszeniu się podajemy hasło. Wówczas zostaniem
na zwrócony prompt sftp>
.
xf0r3m@vm-3eb3a0e:~$ sftp -P 2022 xf0r3m@192.168.122.76 xf0r3m@192.168.122.76's password: Connected to 192.168.122.76 sftp>
Na powyższym przykładzie użyłem opcji
-P
, która w przypadku SCP (to
polecenie formalnie korzysta SFTP) oraz SFTP służą do określania
portu. Opcja -p, służy do zachowania praw własności
przesyłanych plików. Przy czym -p przy polecenie
ssh służy określania portu zdalnego demona SSH.
Bywa to czasami bardzo irytujące.
Po zwróceniu znaku zachęty do dyspozycji mamym dostęp do części podstawowych poleceń takich jak: ls, cd, mkdir. Dodatkowo te wymienione i kilka dodatkowych mają swój lokalny odpowiednik poprzedzony literą l za pomocą tych poleceń możemy poruszać się po lokalnym systemie plików, bez przerywania połączenia na potrzeby na przykład, zmiany katalogu.
Wymiana danych w przypadku polecenia SFTP, odbywa się poprzez
polecenia takie jak: get (pobierz) lub
put
(wyślij), każde z tych poleceń zawiera opcję -r lub
-R, co oznacza rekurencję. Niektóre implementacje tego
protokołu wymagają, aby katalog istniał przed rekurencyjnym
przesłaniem danych. Inne dostępne polecenia są opisane na stronie
podręcznika: man sftp
.
Poza bezpieczym przesyłaniem plików pozostało jeszcze umieszczenie innego połączenia sieciowego wewnątrz połączenia SSH. Takie działanie nazywane jest tunelowaniem. Połączenie pierwotne pozwala na przenoszenie wewnątrz swoich pakietów danych wewnątrz innego połączenia. Mozliwości tuneli tworzonych za pomocą SSH w implementacji openssh znajdują się w innym moim materiale: Laboratorium sieci VPN. Dodam tylko, że połaczenie SSH jest dość wrażliwe (jak każde zapewniające jakiś standard bezpieczeństwa) na jakość połączenia jeśli połączenie jest niewykorzystywane może zostać łatwo zerwane. Należy mieć na uwadze, że tunele SSH otwierają sesje powłoki, więc musimy posiadać dostęp do użytkownika, który może uruchomić dowolną powłokę, może być to równiez program, który będzie działać po zalogowaniu do momentu zakończenia połączenia, jeśli jest to zwykła powłoka np. BASH, to wówczas trzeba pomyśleć o prostym skrypcie, którego zadaniem będzie nieskończone przesyłanie danych wypisywanie czegoś w połowce (Pisanie skryptów powłoki, nie będzie obowiąkowym rodziałem tego materiału, dlatego umieszczę go na samym końcu. Możemy przejść do niego nawet teraz po skończeniu tego podrozdziału) lub użycie programu tmux, który jest multiplekserem terminala dodatkowo pozwala na podtrzymania połączenia SSH.
10.4. Demony internetowe - inetd, xinetd
Niektóre małe usługi sieciowe takie jak np. telnetd - demon usługi telnet czy klasyczna usługa FTP oraz wiele innych posiadają te same lub podobne wymagania dotyczące obsługi sieci, zatem żeby nie implementować tego samego kodu. Twórcy tych usług stworzyli superdemona on nazwie inetd i najprościej rzecz ujmując jego zadaniem jest obsługa połączeń: otwieranie portów oraz przekierowywanie połączeń już do właściwych usług.
Na przestrzenii lat działania tego rozwiązania powstała nowsza implementacja xinetd posiadająca lepsze mechanizmy obsługi oraz kontroli połączeń.
Obecnie rzadko spotyka się działający demon inet lub xinetd, został on wyparty przez jednego z demonów systemd, a demony które obsługiwał obecnie posiadają własne implementacje obsługi połączeń, z biegiem lat rozszerzały swoje możliwości i finalnie stały się bardziej samodzielne. Wiele życzenia pozostawiaja również kwestie bezpieczeństwa, chociaż wiele rzeczy zostało poprawionych między oryginalną implementacją a xinetd.
Inną równie archaiczną rzeczą jest wrapper TCP w postaci demona tcpd. Jego działanie przypomina nieco działanie filtra sieciowego. Był on wykorzystywany jeszcze zanim takie rozwiązania jak iptables zostały na stałe wdrożone jako swoisty standard i podstawowy poziom ochrony. Zanim ruch z inetd trafił do właściwego demona przechodził przez tą usługę. Tcpd rejestrował połączenie następnie konfrontował je z listami dostępu /etc/hosts.allow lub /etc/hosts.deny, na podstawie informacji ustalonych przez wrapper ruch był dopuszczany do właściwego procesu lub nie.
Plikowe listy kontroli dostępu mogą być wykorzystywane w dystrybucjach klasy enterprise, do ustalenia dostępu do wybranych komponentów systemu. Takim przykładem może być dostęp do cron (harmonogramu zadań). Więcej informacji znajduje się pod tym adresem: https://morketsmerke.github.io/articles/terminallog/RedHat_-_RHCSA.html#8.2.1.scheduleaccesscontrol
10.5. Narzędzia diagnostyczne
Podczas analizownia działania protokołu HTTP, na samym końcu wspomniałem o innym sposobie połączenia się z serwerem WWW przy użyciu programu telnet. Program ten może być programem diagnostycznym w podczas sprawdzania uruchomienia i dostępności usługi. W większości dystrybucji program telnet trzeba będzie zainstalować z repozytorium. Jednak możemy w systemie naleźć co najmniej jedno narzędzie, które jest nam wstanie zwrócić jakieś informacje na temat usług sieciowych takim program jest już omawiany ss.
Wynik działania programu ss, był już omawiany podczas omawiania transmisji TCP w warstwie transportowej. Ale poniżej znajdują się najważniejsze (dla celów diagnostycznych) opcje.
- -t - informacje o połączeniach TCP
- -u - informacje o połączeniach UDP
- -l - wyświetla informacje o portach oczekujących na połączenie (nasłuchujących).
- -a - wyświetla wszystkie dostępne połączenia w systemie
- -n - wyłącza rozwiązywanie nazw (przyspiesza to wynik działania programu jeśli serwery DNS nie odpowiadają).
- -4, -6 - wyświetlenie informacji dotyczących protokołu IPv4 lub IPv6.
10.5.1. Polecenie lsof
Jesli pamiętamy z rozdziału 8, polecenie lsof służy do wyświetlania otwartych plików w systemie. Z jego pomocą możemy również wyświetlić połączenia realizowane w systemie. Rola tego polecenia może przypominać ss, jednak polecenie lsof do działania w obrębie całego systemu wymagają uprawnień administratora, w przeciwnym razie polecenie wyświetli tylko i wyłącznie połączenia utworzne przez użytkownika, który wydał to polecenie. Aby móc wyświetlić połączenia sieciowe przy użyciu polecenia lsof należy użyć opcji -i, tak jak przedstawiono to na poniższym poleceniu.
xf0r3m@vm-3d6b184:/media/xf0r3m/immudex_crypt0$ sudo lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 35u IPv4 11469 0t0 TCP *:sunrpc (LISTEN) systemd 1 root 36u IPv4 11472 0t0 UDP *:sunrpc systemd 1 root 37u IPv6 11475 0t0 TCP *:sunrpc (LISTEN) systemd 1 root 38u IPv6 11478 0t0 UDP *:sunrpc rpcbind 427 _rpc 4u IPv4 11469 0t0 TCP *:sunrpc (LISTEN) rpcbind 427 _rpc 5u IPv4 11472 0t0 UDP *:sunrpc rpcbind 427 _rpc 6u IPv6 11475 0t0 TCP *:sunrpc (LISTEN) rpcbind 427 _rpc 7u IPv6 11478 0t0 UDP *:sunrpc avahi-dae 476 avahi 12u IPv4 13560 0t0 UDP *:mdns avahi-dae 476 avahi 13u IPv6 13561 0t0 UDP *:mdns avahi-dae 476 avahi 14u IPv4 13562 0t0 UDP *:33600 avahi-dae 476 avahi 15u IPv6 13563 0t0 UDP *:34773 NetworkMa 675 root 26u IPv4 14371 0t0 UDP vm-3d6b184.morketsmerke.org:bootpc-> DESKTOP-TMP2137.mshome.net:bootps cupsd 790 root 6u IPv6 14463 0t0 TCP localhost:ipp (LISTEN) cupsd 790 root 7u IPv4 14464 0t0 TCP localhost:ipp (LISTEN) cups-brow 860 root 7u IPv4 15147 0t0 UDP *:631 chronyd 883 _chrony 5u IPv4 14538 0t0 UDP localhost:323 chronyd 883 _chrony 6u IPv6 14539 0t0 UDP localhost:323 ssh 60722 xf0r3m 3u IPv4 187934 0t0 TCP vm-3d6b184.morketsmerke.org:36440-> ip82-117-231-222.static.awhost.cloud:2022 (ESTABLISHED)
Na powyższym przykładzie możemy zauważyć, że wyjście tego polecenia jest trochę bardziej podrasowanym wyjściem polecenia ss. Poza tym lsof daje nam możliwość przefiltrowania listy dostępnych połączeń. Ogólny format filtra wygląda następująco:
$ sudo lsof -i[wersja_IP][protokół]@[komputer]:[port]
Oczywiście istnieje pełna dowolność w stosowaniu tych elementów, aby to pokazać przedstawię kilka użytecznych filtrów:
xf0r3m@vm-3d6b184:/media/xf0r3m/immudex_crypt0$ sudo lsof -i4 -s TCP:LISTEN COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 35u IPv4 11469 0t0 TCP *:sunrpc (LISTEN) rpcbind 427 _rpc 4u IPv4 11469 0t0 TCP *:sunrpc (LISTEN) cupsd 790 root 7u IPv4 14464 0t0 TCP localhost:ipp (LISTEN)
Powyższy przykład przestawia wyłącznie otwarte porty dla transmisji
TCP. W tym przypadku uzyskanie stanu połączenia
(LISTEN
) wymaga podania dodatkowej
opcji (-s
) a ona ma swoją składnię
dla wartości: PROTOKOŁ:STAN.
xf0r3m@vm-3d6b184:/media/xf0r3m/immudex_crypt0$ sudo lsof -iTCP@searx.morketsmerke.org COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ssh 60722 xf0r3m 3u IPv4 187934 0t0 TCP vm-3d6b184.mshome.net:36440-> ip82-117-231-222.static.awhost.cloud:2022 (ESTABLISHED)
Ciekawy przypadek mamy tutaj. Ponieważ wśród połączeń TCP wyszukujemy
hosta o konkretnej nazwie. Nie dostajemy informacji zwrotnej, że coś
jest nie tak, wręcz przeciwnie uzyskujemy odpowiedź, na której
nie widnieje nasza nazwa. Ta odpowiedź jest jak najbardziej
prawidłowa.
Adres searx.morketsmerke.org
jest
odwzrowywany do 82.117.231.222, ale jeśli skorzystamy z zamiany
adresu IP na nazwę domenową wówczas otrzymamy taki oto wynik:
ip82-117-231-222.static.awhost.cloud
Więc jeśli adres searx.morketsmerke.org
jest tożsamy z
ip82-117-231-222.static.awhost.cloud
,
co oznacza, że filtr jest jak najbardziej poprawny. Poniżej znajduje
się ostatni już przykład:
xf0r3m@vm-3d6b184:/media/xf0r3m/immudex_crypt0$ sudo lsof -i6UDP:323 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME chronyd 883 _chrony 6u IPv6 14539 0t0 UDP localhost:323
10.5.2. tcpdump
Innym narzędziem, dzięki któremu możemy śledzić ruch sieciowy jest tcpdump. Program ten działa na takiej zasadzie, że rejestruje każdy pakiet, który będzie pojawiać się na kartach sieciowych naszego komputera. Zakres działania urządzenia jest bardzo szeroki gdyż zbiera on zarówno pakiety aplikacji takie jak DNS czy inne jak komunikaty protokołu ARP.
xf0r3m@vm-f48088c:/media/xf0r3m/immudex_crypt0$ sudo tcpdump tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 07:27:01.136396 IP DESKTOP-TMP2137.mshome.net.60948 > 239.255.255.250.1900: UDP, length 175 07:27:01.238437 IP vm-f48088c.mshome.net.46759 > DESKTOP-TMP2137.mshome.net.domain: 9527+ PTR? 250.255.255.239.in-addr.arpa. (46) 07:27:01.244853 IP DESKTOP-TMP2137.mshome.net.mdns > 224.0.0.251.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52) 07:27:01.247220 IP DESKTOP-TMP2137.mshome.net.mdns > 224.0.0.251.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52) 07:27:01.250774 IP6 DESKTOP-TMP2137.morketsmerke.org.mdns > ff02::fb.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52) 07:27:01.252918 IP6 DESKTOP-TMP2137.morketsmerke.org.mdns > ff02::fb.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52) 07:27:02.142559 IP DESKTOP-TMP2137.mshome.net.60948 > 239.255.255.250.1900: UDP, length 175 07:27:02.241843 IP DESKTOP-TMP2137.mshome.net.mdns > 224.0.0.251.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52) 07:27:02.242557 IP6 DESKTOP-TMP2137.morketsmerke.org.mdns > ff02::fb.mdns: 0 PTR (QM)? 250.255.255.239.in-addr.arpa.local. (52)
W moim przypadku zostało tylko złapanych kilka zapytań w przód (PTR) do usługi Multicast-DNS do serwera na którym hostuje maszynę wirtualną. Uruchamiając ten program od ręki, nie dokonując żadnych zmian w systemie wyłapiemy tylko pakiety adresowane do nas lub wysyłane przez nas. Teoretycznie powinniśmy przełączyć nasz interfejs w tryb nasłuchiwania (promiscous mode) możemy to zrobić za pomocą poniższego polecenia:
xf0r3m@immudex:~$ sudo ip link set <interfejs> promisc on
Niestety w obecnych konfiguracjach sieci to narzędzie może sprawdzić się głównie w sieciach bezprzewodowych do przechwycenia komunikatów warstwy łącza danych, chyba że sieć jest w pełni otwarta. Sieci kablowe są obecnie zbudowane na przełącznikach, a te powodują bezpośrednią komunikację w sieci, więc teoretycznie nie ma możliwości aby pakiety niezaadresowane do naszej karty do niej trafiły. Oczywście, tylko teoretycznie, bowiem inteligentne przełączniki (większość przełączników zarządzalnych) pozwalają na tzw. port mirror. Ustawienie portu w ten tryb oraz wybranie portu docelowego spowoduje, że to coś się pojawi na porcie docelowym trafi również to portu z ustawioną tą funkcją. Wówczas możemy monitorować ruch z jednego komputera, chyba że będzie kopiować ruch z portu, który łączy nasz przełącznik z pozostałą częścią sieci. Jednak wówczas będziemy monitorować jedynie ruch wychodzący i przychodzący do tego segmentu. Natomiast komunikacja między komputerami tego samego segmentu może pozostać dalej nie osiągalna.
Nie mniej jednak jeśli już przyjdzie nam pracować z tym programem to musimy wiedzieć jego działania odbywa się na zasadzie filtrów. Filtry tcpdump składaja się (oczywiscię te proste) z elementów pierwotnych (z ang. primitives) oraz z operatorów takich jak and, or lub wykrzyknik (!) powodujący negacje całego filtru. Poniżej znajduje się lista elementów pierwotnych:
tcp
- transmisja TCPudp
- transmisja UDPip4
- pakiety IPv4ip6
- pakiety IPv6port nr_portu
- porthost ip_komputera
- hostnet adres_podsieci
- sieć
Tworząc filtr oparty na sieci jako wartość elementu
net
podajemy adres podsieci np.
192.168.8.0. Poniższe polecenie wyświetli nam pakiety UDP,
połączenia HTTPS oraz połączenia SSH.
xf0r3m@immudex:~$ sudo tcpdump udp or port 443 or port 22
To co zostało przedstawione powyżej to tylko mała część możliwosci
filtrowania ruchu zbieranego przez tcpdump. Więcej
informacji na temat filtrów możemy znaleźć na stronie podręcznika:
man pcap-filter
(pcap to
biblioteka odpowidzialna zbieranie pakietów).
tcpdump jest tekstowym odpowiednikiem podobnego narzędzia działającego w oknie - programu Wireshark.
10.5.3. Program netcat
Jest to program sieciowy o ogromnych możliwościach. Zwany często szwajcarskim scyzorykiem. Za jego pomoca możemy sprawdzić dostępność usługi, czy otworzyć port a następnie przekierować dane z polecenia na standardowe wyjście. Jesli ktoś się podłączy do tego i przekieruje standardowe wejście, na przykład za pomoca netcata. Wówczas klient będzie mógł wysyłać do serwera komunikaty tekstowe, które będą się wyświetlać w terminalu serwera. Serwer również będzie mógł wysyłać komunikaty wpisując bezpośrednio w to samo okno terminala, w którym działa proces. Na nasze szczęście program nc sam dokonuje takich przekierowań i wystarczy, że po jednej stronie uruchomimy serwer a po drugiej stronie podłączymy się do niego, wygląda to mniej więcej tak:
xf0r3m@server:~$ nc -l 9595 test test2 Server: To jest wiadomość testowa. Proszę o odpowiedź. Klient: Odpowiedź na wiadomość testową. xf0r3m@client:~$ nc -N 172.24.171.253 9595 test test2 Server: To jest wiadomość testowa. Proszę o odpowiedź. Klient: Odpowiedź na wiadomość testową.
Na powyższym przykładzie zmieniłem nazwy hostów w znakach zachęty,
aby było wiadomo kto jest kim w tej komunikacji. Do uruchomienia
serwera na prawdę nie potrzeba wiele wystarczy podanie opcji
-l
, która powoduje nasłuchiwanie
następnie ja podałem port, z tego co można wyczytać nas stronie
podręcznika tego narzędzia, to że możemy pominąć opcję odpowiedzialną
za wskazanie portu i podać sam numer portu oraz dodać do opcji adres
IP interfejsu, na którym serwer ma nasłuchiwać w tym przypadku opcję
opowiedzialną za ten funkcję też możemy pominąć.
Opcja -N
podana przez klienta
powoduje, że jeśli wyślemy znak EOF (End of File, Ctrl+d),
to połaczenie zostanie zakończone i serwerów również zakończy swoją
pracę.
Domyślnie otwieranymi portami są porty TCP, jeśli jednak chcemy użyć transmisji UDP należy podać -u.
W przypadku programu netcat naprawdę warto zapoznać się z stroną podręcznika, po opisie wszystkich obsługiwanych opcji znajdują się przykłady zastosowania tego narzędzia.
10.5.4. Program nmap - skanowanie portów
Program nmap jest dość ważnym narzędzie w rękach większości administratorów, ma on masę możliwości jednak nas na tym etapię będzie interesować możliwość odpytania hosta na różnych portach i określenie czy możliwe jest połączenie z tymi portami. Ta czynność nosi nazwę skanowania portów i w niektórych sieciach bywa nielegalna (Więcej o tym w podrozdziale dotyczącym bezpieczeństwa). Istnieje wiele metod skanowania. A samo skanowanie może zostać rozszerzone np. o próbę zmuszenia usługi do powiedzenia czegoś więcej o sobie przez co można poznać jaki system operacyjny jest zainstalowany na hoście, którego porty skanujemy.
Rodzajów skanowania jest kilka. Jednym z prostszych i szybszych jest SYN. Polega ono na wysłaniu pakietu inicjalizacji połączenia i kiedy uzyskamy odpowiedź na ten pakiet, możemy uznać (oczywiście tylko wedle tej metody), że port jest otwarty.
xf0r3m@searx:~$ sudo nmap -sS ftp.morketsmerke.org Starting Nmap 7.80 ( https://nmap.org ) at 2023-08-31 08:39 CEST Nmap scan report for ftp.morketsmerke.org (195.164.150.92) Host is up (0.040s latency). Not shown: 993 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident 443/tcp open https 2000/tcp open cisco-sccp 2022/tcp open down 5060/tcp open sip 9090/tcp open zeus-admin Nmap done: 1 IP address (1 host up) scanned in 4.43 seconds
Na powyższym przykładzie przeskanowałem za pomocą metody
SYN jeden ze swoich serwerów. Program podczas skanowania może działać
znacznie dłużej niż tym przypadku w zależności od tego ile hostów
ma do przeskanowania lub jaki ma zakres portów. Zatem po uruchomieniu
skanowania zostanie nam przedstawiona tylko pierwsza linia. Po
zakończeniu skanowania zostanie nam zwrócony raport. Pierwsza linia
raportu zawienie nazwę hosta oraz jego adres IP.
(Nmap scan report for ftp.morketsmerke.org (195.164.150.92)
)
Następnie informacje o tym, że host jest dostępny opóźnieniach jakie
mogą wystąpić podczas komunikacji.
(Host is up (0.040s latency).
)
Trzecia linia zawiera informacje o niepokazywanych portach/usługach,
ze względu na to, że nie udało się uzyskać od nich żadnej odpowiedzi.
Nmap rozpoznaje trzy stany portu:
- open - Port jest otwarty, komunikacja jest możliwa.
- closed - Port jest zamknięty, komunikacja nie jest możliwa. Ten stan jest ustawiany kiedy strona zdalna odpowiedziała, że taki port nie jest czynny na tym hoście
- filtered - Port jest filtrowany lub jego stan jest nieznany. Ten stan jest ustawiany kiedy minie okres ważności wysyłanych pakietów a strona skanująca nie dostanie żadnej odpowiedzi. Stan ten jest ustawiany także dla niedostępnych portów protokołu UDP ze zględu na charakterystykę tej transmisji.
Za pomocą skanowania portów możemy zauważyć różnicę miedzy akcjami drop oraz reject, pakietu zapory iptables. W przypadku akcji drop, stan portu będzie określony jako filtered, a w przypadku reject closed.
Polecenie nmap poza wykrywanie dostępnych na hostach usług, może zostać wykorzystane do odkrywania hostów w sieci, za pomocą opcji -sP jako argument podając adres sieci w notacji CIDR.
Opcji skanowania jest masa, funkcjonalność programu Nmap można rozszerzyć za pomoca skryptów Lua. Więc jeśli nie mielśmy styczności z tym programem warto się zapoznać stroną podręcznika.
10.6. Zdalne wywołanie procedury - RPC
RPC (ang. Remote Procedure Call), czyli zdalne wywołanie procedury to system, ktory ma umożliwić aplikacjom sieciowym uruchomienie funkcji znajdujących się na serwerze. Każdemu z tych programów przypisywany jest numer, dzięki któremu będzie można go wywołać. System ten znajduje się w niższych partiach warsty aplikacji.
Aplikacje korzystające z RPC używają klasycznych portów transmisji warstwy transportowej takich jak: TCP czy UDP. Wymagają one natomiast jeszcze jednej usługi pośredniczącej mianowicie rpcbind. Jej zadaniem jest mapowanie portów TCP/UDP na numery programów RPC. Usługa rpcbind może być widoczna podczas skanowania. Za pomocą poniższego polecenie możemy sprawdzić czy jakiś program możemy uruchomić i za pomocą jakiej usługi.
xf0r3m@vm-76925c3:~$ rpcinfo -p localhost program vers proto port service 100000 4 tcp 111 portmapper 100000 3 tcp 111 portmapper 100000 2 tcp 111 portmapper 100000 4 udp 111 portmapper 100000 3 udp 111 portmapper 100000 2 udp 111 portmapper
Podobny wynik polecenia możemy uzyskać posiadając obsługę NFS w swoim systemie. Nie będę tutaj rozpisywał się na temat wyników tego polecenia. RPC to póki co nie nasza liga, nie mniej jednak warto wiedzieć że istnieje coś takiego. Poza NFS z RPC korzysta także usługa NIS (Network Information Service) oraz program monitorujący FAM (File Access Monitor) środowiska GNOME.
10.7. Zabezpieczenie sieci
Zabezpieczanie sieci to temat rzeka. Polega on na takim skonfigurowaniu elementów/warstw bezpieczeństwa hostów w sieci, aby zniechęcić potencjalnych atakujących do brania za cel naszych komputerów. Dystrybucje Linuksa sprawdzaja się głównie w środowiskach serwerowych i to one stają się najczęściej ofiarami ataków. Obecnie procesy przetwarzania informacji opieraja się w mniejszym bądź wiekszym stopniu na bazach danych, a te przechowują różne informacje. Dane wrażliwe firmy współdzelone na są na serwerach udostępniających pliki czy strony internetowe wszystkie te usługi opierają swoje działanie na dystrybucjach Linuksa. Udział innych systemów jest tutaj niszowy, chociaż to powoli zaczyna ulegać zmianie. Oczywiście nie ma zabezpieczeń idealnych i jedynym bezpiczecznym sposobem przechowywania danych jest przechowywanie ich poza siecią. To jednak w mniejszym lub większym stopniu stanie się uciążliwe. Dlatego też poniżej znajduje się kilka zasasd, które pozwolą na podniesienie poziomu bezpieczeństwa naszych systemów.
- Utrzymać w systemie minimalną liczbę usług sieciowych - jeśli nie wiemy do czego usługa służy najlepiej ją wyłączyć, aby wśród połaczeń sieciowych widniały tylko te usługi, które znamy. Ewentualnie jeśli stwierdzimy, że coś musi działać to spróbujmy ograniczyć nasłuchiwanie tej usługi wyłącznie do tego hosta (pętli zwrotnej).
- Filtrować tyle ruchu ile się tylko da - filtrowanie ruchu jest podstawowym elementem zabepieczającym system. Zapory sieciowe kształtują ruch i pozwalają kontrolować dostęp do naszych systemów. Najprostszą metodą jest zablokowanie całego ruchu przychodzącego i dopuszczenie tylko tego pasującego do naszych usług.
- Śledzenie informacji o zagrożeniach - warto śledzić informacje na temat wykorzystywanych przez usług, czy nie ma jakiejś nowo odkrytej podatności. Tutaj warto przypomnieć o aktualizacji pakietów oprgramowania dystrybucji, samej dystrybucji oraz aplikacji z których korzystamy na tych serwerach.
- Używanie dystrybucji z długoterminowym wsparciem - Na serwerach używa się głównie stablinych dystrybucji takich jak systemy klasy Enterprise: Rocky czy Alma Linux. Innymi dystrybucjami są GNU/Linux Debian lub Ubuntu LTS. Systemy klasy enterprise mogą pochwalić się aż 10 letnim wsparciem podobnie Ubuntu. Debian posiada 5 letni okres wsparcia.
- Ograniczenie ilość kont do minimum - tutaj sprawa jest prosta jeśli ktoś niepotrzebuje konta to się mu go nie zakłada. Wiele usług może potrzebować osobnego użytkownika do ograniczenia dostępu, to przy zakładniu takiego konta należy pamiętać aby ustawić mu odpowiednią powłokę.
- Unikanie instalowania niepewnych pakietów - Chodzi głównie o pakiety binarne, jednak można by to rozszerzyć o różne repozytoria stron trzecich.
Systemy uniksopodobne w tym i dystrybucje Linuksa mają to do siebie, że ich poziom bezpieczeństwa zależy od tego jaki jest nasz własny, nas jako ludzi. Jak bardzo mamy rozbudowaną wiedzę i umiejętności w tym temacie. Tak jest w przypadku wielu dystrybucji, jest to podyktowane wolnością użytkowników i nie chęcią do narzucania komukolwiek czegokolwiek. Trochę inaczej sprawa ma się w przypadku systemów klasy enterprise, jak sama nazwa wskazuje często są one wyposażone w dodatkowe mechanizmy, aby mogły działać jak podstawa systemów produkcyjnych w przedsiębiorstwach, a kierowanie się powyższymi zasadami powinno ograniczyć ryzyko uzyskania pełnego dostępu do systemu.
10.7.1. Typowe zagrożenia
Wśród ataków jakie można przeprowadzić na serwery z dystrybucją Linuksa możemy wyróżnić między innymi:
- Zdalny dostęp - jest to uzyskanie dostępu do maszyny. Pod tym określeniem możemy znależć pełen wachlarz różnych technik. Kiedyś było to przepełnienie bufora, obecnie możemy głównie wśród serwerów WWW oraz bazodanowych jedną z technik dostania się do systemu jest sql injection. Po zastosowaniu tych technik, atakujący może uzyskać dostęp do powłoki z poziomu użytkownika, który uruchamiana daną usługę. Jeśli takim użytkownikiem jest superuser wówczas atakujący przejmuje całkowicie kontrolę nad maszyną. Dlatego tak ważne jest aby jak najbardziej ograniczyć uprawnienia usług względem całego systemu. Skompromitowany system może być wykorzystywany do ataku na inne cele wewnątrz sieci lub w intenecie stając się przy tym cześcią sieci włamywaczy (botnetu) do prowadzenia dalszych działań takich jak ataki DDoS czy rozsyłanie spamu. Przy dużej liczbie usług stosuje się tzw. DMZ, jest najczęściej wydzielona podsieć, w której umieszcza się serwery mające świadczyć usługi w internecie. Ta podsieć najczęściej nie ma możliwości komunikacji z resztą sieci.
- Ataki DoS - ataki odmowy usługi lub rozproszone ataki odmowy usługi. Atak tego typu jest bardzo prosty do zrealizowania. Każdy może go przeprowadzić. Polega on na zwiększonym korzystaniu z usługi do tego stopnia, że serwer nie będzie wstanie więcej świadczyć tej usługi. Zabezpieczeniem przed tego typu działaniami mogą być usługi pośredniczące, które mogą blokować szkodliwy ruch. Czasami uruchamiając jakąś stronę możemy spotkać się na początku ze stroną przedstawiającą logo dostawcy oraz usługę w stylu Anti-(D)Dos. Jeśli już dojdzie do takiego ataku, powstrzymanie go jest banalne. Wystarczy zablokować ruch na zaporze granicznej i zrestartować serwer blokowanej usługi
- Złośliwe oprogramowanie - to zagrożenie może mniej dotyczyć naszych serwerów, ale nie powinnismy odrzucać takiej możliwości. Narażone na to mogą być serwery plików oraz serwery pocztowe. Generalnie systemy uniksopodobne są chronione przed różnym rodzajemy złośliwego opramowania samym faktem, że są mniej popularne, niż inne systemy. Nie mniej jednak malware dla dystrybucji Linuksa istnieje. Nawet jeśli te systemy nie są celem ataków wirusowych to mogą być ich nośnikiem. Dlatego też uruchamiając serwer pocztowy lub serwer do współdzielenia plików należało by go zaopatrzyć w jakiś program antywirusowy, którym może działać na naszej dystrybucji. Dostępnych jest wiele programów antywirusowych otwartoźródłowych z myślą o Uniksach.
Omawiając zabezpieczanie sieci, moglismy się nie spodziewać informacji tego typu tutaj. W podrozdziale 10.5 zostały przedstawione dwa narzędzia netcat oraz nmap. Ich wykorzystywanie, w niektórych sieciach, może podnieść alarm o potencjalnej próbie włamania. Dlatego lepiej nie sprawdzać sieci którymi nie zarządzamy, ponieważ konsekwencje mogą być spore. Wydanie nawet pojedynczego polecenia ze skanowaniem, może zakończyć się oskarżeniem o włamanie.
Jakby nie patrzeć najwieksze podatności systemów informatycznych siędzą przed ekranami urządzeń, które się z nimi łączą. W firmach mimo wysokiego poziomu zabezpieczeń kluczowych systemów, to za pomocą inżynierii społecznej (sztuki manipulowania ludźmi) można uzyskać dostęp systemów w nawet tak dobrze zabezpieczonej organizacji i to z minimalną wiedzą techniczną. Dlatego też mimo zabezpieczeń technicznych trzeba mieć się na baczności, np. kiedy czytamy wiadomość e-mail z prośbą otwarcia załącznika, albo że nasz komputer jest zarażony złośliwym oprogramowanie i możesz połączyć się z konsulatantem, który je usunie za darmo.
10.8. Gniazda sieciowe
Omawiając warstwę aplikacji trzeba również wspomnieć o tym w jaki sposób procesy są wstanie brać udział w komunikacji sieciowej. W zależności od tego czy połączenie jest już zestawione to sprowadza się to skorzystania z dwóch wywołań systemowych recv oraz send. Proces chcąc skorzystać z tych wywołań musi odwołać się do połączenia za pomocą takie tworu jak gniazdo. Gniazda są wykorzystywane do uzyskania dostępu do sieci przy użyciu jądra. Proces wykorzystuje do określenia tego jak i kiedy komunikuje się on z siecią. Gniazda również biorą udział w komunikacji międzyprocesowej (IPC).
W zależności od tego w jaki sposób proces chce uzyskac dostęp do sieci wówczas musi skorzystać z określonego rodzaju gniazda. Dla protokołu TCP używane jest gniazdo strumienia (SOCK_STREAM) a dla UDP wykorzystywane jest gniazdo datagramu (SOCK_DGRAM). Konfigracja gniazda wymaga ustalenia rodzaju gniazda, adresów IP, portów i protokołu warstwy transportowej. Gniazda zazwyczaj są konfigurowane przez procesów serwerów. Po ustaleniu tych informacji proces wykorzystuje standardowe metody obsługi sieci.
Typowy serwer podczas swojego działania wykorzystuje dwa gniazda a nie jedno. Jeśli tak by było to wówczas serwery mogły świadczyć usługi tylko punktowo. Jedne klient się podłączył to drugi musi czekać na zakończenie połączenia. Na szczęście jedno z gniazd służy do nasłuchiwania, jeśli proces nadrzędny wykryje na nim ruch to za pomocą wywołania systemowego accept akceptuje to połączenie i tworzy dla tego połączenia dedykowane gniazdo zapisu i odczytu. Po tej czynności za pomocą wywołania systemowego fork tworzy proces podrzędny do obsługi tego połączenia. Gniazdo nasłuchujące dalej będzie prowadzić nasłuch dla procesu nadrzędnego.
Po skonfigurowaniu gniazda proces może prowadzić z nim interakcje zgodnie z rodzajem użytego gniazda.
10.9. Gniazda uniksowe
Gniazda uniksowe rodzaje gniazd szczególnie wykorzystywanym w komunikacji międzyprocesowej (IPC), wiele aplikacji bowiem jest projektowanych w architekturze klient-serwer (nawet te, które nie są sieciowe), a tego typu gniazda mogą się zachowywać jak gniazda sieciowe, mimo że nimi nie są i nie jest powiązana z nim żadana sieć. Możliwe jest również takie skonfigurowanie gniazd zachowywały się podobnie do protokołów TCP/UDP. Przykładem wykorzystania tego typu gniazd jest komunikacja z systemem D-Bus (odpowiedzialnym za monitorowanie i reagowania na zdarzenia w systemie).
Powodem popularności gniazd uniksowych jest możliwość utworzenia specjalnego pliku gniazda, które jak każdy plik w systemie będzie podlegać kontroli na podstawie klasycznych uniksowych uprawnień przez co inne procesy nie będą mieć dostępu do niego. Przykładem takiego pliku może być na przykład:
xf0r3m@vm-eba25a2:~$ ls -al /run/rpcbind.sock srw-rw-rw- 1 root root 0 09-01 07:04 /run/rpcbind.sock
Inną sprawą jest wydajność, korzystając z gniazd uniksowych jądro nie musi używać podsystemu sieciowego. Sam proces programowania nie różni się zbytnio od gniazd sieciowych, dlatego też wiele aplikacji sieciowych również umożliwia połączenia za pomocą gniazd uniksowych. Przykładem takiej aplikacji może być przedstawiony na przykładzie rpcbind.
Za pomocą polecenia lsof z opcją -U możemy wyświetlić wszystkie uniksowe gniazda dostępne w systemie.
xf0r3m@vm-eba25a2:/media/xf0r3m/immudex_crypt0$ sudo lsof -U COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 10u unix 0x00000000c0eae923 0t0 15927 /run/systemd/journal/stdout type=STREAM (CONNECTED) systemd 1 root 17u unix 0x00000000a1f6a748 0t0 11408 /run/systemd/notify type=DGRAM (CONNECTED) systemd 1 root 18u unix 0x0000000062a0c5b9 0t0 11409 type=DGRAM (CONNECTED) systemd 1 root 19u unix 0x00000000962057e3 0t0 11410 type=DGRAM (CONNECTED) systemd 1 root 20u unix 0x000000001183727b 0t0 11411 /run/systemd/private type=STREAM (LISTEN) systemd 1 root 21u unix 0x000000007042f6df 0t0 11413 /run/systemd/userdb/io.systemd.DynamicUser type=STREAM (LISTEN) systemd 1 root 22u unix 0x000000008bad806c 0t0 11414 /run/systemd/io.system.ManagedOOM type=STREAM (LISTEN) systemd 1 root 23u unix 0x0000000096526742 0t0 20935 /run/systemd/journal/stdout type=STREAM (CONNECTED) systemd 1 root 24u unix 0x00000000f0a1522b 0t0 182291 /run/systemd/journal/stdout type=STREAM (CONNECTED)
11. Udostępnianie plików w sieci
Do tej pory zajmowaliśmy się siecią jako komponentem systemu pora skorzystać z niej jako z środka komunikacji. W wiekszości przypadków, ludzie wykorzystują sieć do dzielenia się informacjami a te moga przybrać formę pliku dowolnego formatu, a my poznamy metody w jaki sposób możemy przenieść pliki z jednego komputera na drugi za pośrednictwem sieci. Pominiemy jednak takie narzędzia jak SCP oraz SFTP, ponieważ były już one omawiane w poprzednim rodziale.
11.1. Proste udostępnienie z pośrednictwem WWW
Rozważmy taki scenariusz, że musimy udostępnić plik wielu osobom w tym samym laboratorium. Plik jest za duży, żeby przesłać go mailem, ale z użyciem poczty elektronicznej możemy przesłać link. Wówczas przeglądarki połaczą się z naszym komputerem i pobiorą udostępniony plik. Dla maksimum bezpieczeństwa możemy utworzyć specjalny folder dla tego pliku, przenieść/skopiować go to tam i będą wewnątrz go wydać następujące polecenie:
xf0r3m@vm-d67b064:~$ python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... 127.0.0.1 - - [08/Sep/2023 12:35:06] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [08/Sep/2023 12:35:06] code 404, message File not found 127.0.0.1 - - [08/Sep/2023 12:35:06] "GET /favicon.ico HTTP/1.1" 404 - 127.0.0.1 - - [08/Sep/2023 12:35:25] "GET /newsfeed HTTP/1.1" 200 -
Za pomocą wydanego przez nas polecenia z użyciem modułu języka
programowania Python, uruchomiliśmy prosty serwer
WWW. Serwer działa na wysokim porcie ze względu na uprawnienia.
Zwykli użytkownicy nie mogą otwierać portów poniżej numeru 1023.
Domyślnie serwer startuje na porcie 8000, jeśli z jakiś przyczyn
nie możemy użyć tego portu, to wówczas możemy podać mu inny wysoki
port na końcu polecenia. Adres 0.0.0.0
oznacza każdy adres przypisany do tego komputera, oznacza to że
mogę połączyć z tym serwerem przez pętlę zwrotną co własśnie
uczyniłem, koncowo pobrałem plik
newsfeed
. Działanie serwera
kończymy prostym Ctrl+c.
11.2. Synchronizacja katalogu zdalnego - program rsync
Kolejny przykład udostępniania plików, wyłamuje się poza konwencję tego rozdziału, nie mniej jednak warto o nim wspomnieć. Może zostać po traktowany jako narzędzie do przesłania plików na serwer służący do ich udostępniania. Mowa tutaj o programie rsync. Jest on powszechnie wykorzystywany przez administratorów do przesyłania danych między Uniksami. Program wymaga istnienia na obu stronach komunikacji oraz dostępu do powłoki (rsync domyślnie wykorzystuje SSH). Program jeśli ma działać na zdalnym systemie zachowuje się podobnie do SCP, program może również działać na lokalnym systemie zastępując klasyczne polecenie cp i tak też to polecenie działa w prosty niemodyfikowany opcjami sposób. Opcje programu są w stanie dość mocno wpłynąc na jego zachowanie, że te dwa powyższe polecenie mogą stać się zbędne. Polecenie rsync może być również wykorzystywane to kopiowania/przenoszenia w obrębie jednego komputera.
Najprostsza składnia polecenia rsync wygląd następująco:
xf0r3m@immudex:~$ rsync -r katalog_lokalny user@host:/sciezka/do/katalogu/zdalnego
Polecenie to prześle cały katalog
katalog_lokalny
do katalogu zdalnego
na serwerze. Na powyższym poleceniu użyłem opcji
-r
(rekurencja), w przeciwnym razie
nic nie zostanie przesłane.
Podczas przekazywania ścieżek do polecenia (głównie ścieżki lokalnej) istnieje pewna zależność. Jeśli definiując scieżkę lokalną podamy na jej końcu ukośnik, to wówczas do katalogu zdalnego zostanie przekopiowana sama zawartość bez tworzenia początkowego katalogu.
#Bez kończącego ukośnika: xf0r3m@vm-331503c:/ic0$ rsync -r Repos2 /ic0/Dokumenty # Katalog docelowy xf0r3m@vm-331503c:~/Dokumenty$ ls -al razem 12 drwxr-xr-x 3 xf0r3m xf0r3m 4096 09-18 09:52 . drwxr-xr-x 11 xf0r3m xf0r3m 4096 09-18 09:12 .. drwxr-xr-x 4 xf0r3m xf0r3m 4096 09-18 09:52 Repos2 xf0r3m@vm-331503c:~/Dokumenty$ ls -al Repos2/ razem 16 drwxr-xr-x 4 xf0r3m xf0r3m 4096 09-18 09:52 . drwxr-xr-x 3 xf0r3m xf0r3m 4096 09-18 09:52 .. drwxr-xr-x 5 xf0r3m xf0r3m 4096 09-18 09:52 Repos drwxr-xr-x 2 xf0r3m xf0r3m 4096 09-18 09:52 .test #Z kończącym ukośnikiem. xf0r3m@vm-331503c:/ic0$ rsync -r Repos2/ /ic0/Dokumenty xf0r3m@vm-331503c:~/Dokumenty$ ls -al razem 16 drwxr-xr-x 4 xf0r3m xf0r3m 4096 09-18 09:58 . drwxr-xr-x 11 xf0r3m xf0r3m 4096 09-18 09:12 .. drwxr-xr-x 5 xf0r3m xf0r3m 4096 09-18 09:58 Repos drwxr-xr-x 2 xf0r3m xf0r3m 4096 09-18 09:58 .test
Na tę zależność należy uważać podczas kopiowania danych, ponieważ sam utworzony przez polecenie katalog może być istotny. Na powyższych przykładach użyłem polecenia rsync wewnątrz tego samego systemu.
Jak wcześniej wspominiałem dużą rolę w polecenie rsync odgrywają jego opcje. Poniżej znajduja się najważniejsze z nich.
- --archive, -a - Tryb archiwalny. zachowanie większości atrybutów plików, poza ACL-kami, atrybutami specjalnymi takim jak setuid, czasu utworzenia oraz czasu ostatniego dostępu. W przypadku zastosowania tej opcji nie są kopiowane również dowiązania twarde. Wydanie opcji -a jest równoznaczne z wydaniem opcji serii opcji: -rlptgoD.
- --update, -u - Aktualizacja. Powoduje, że rsync pomija istniejące już pliki na miejscu docelowym jeśli ich czas modyfikacji jest póżniejszy niż pliku źródłówego, jeśli czas modyfikacji jest równy wówczas program opiera swoje porównania na rozmiarze pliku.
- --checksum -c - Sumy kontrolne. Zmienia sposób porównywania plików. Zamiast sprawdzać czas modyfikacji czy rozmiar liczona jest suma kontrolna. Te metoda jest o wiele dokładniejsza, jednak wydłuża czas kopiowania oraz zużywa znacznie więcej zasobów.
- --delete, --del - Oczyszanie katalogu. Usuwa z katalogu docelowego wszystkie pozostałe pliki, które nie były częścią tego transferu.
- --dry-run, -n - Testy. Pozwala na sprawdzenie czy nasze opcje i ścieżki zostały właściwie dobrane przed dokonaniem faktycznych zmian. Polecenie to wykonuje czynności na sucho, nie dokunując żadnych zmian w systemie.
- --verbose, -v - zwiększenie ilości komunikatów diagnostycznych.
- --compress, -z - Kompresja. Włącza kompresję podczas przesyłania danych.
W tym materiale przedstawiono tylko, kilka opcji powszechnie wykorzysywanych podczas korzystania z polecenia rsync. Obszerną wiedzę na ten temat zwiera strona podręcznika polecenia, zawierająca opisy wszystkich opcji oraz inne zagadnienia związane z programem.
Ostatnią rzeczą związana z poleceniem jest przekazywanie do rsync poleceń związanych z SSH, w końcu ten program domyślnie korzysta z tego protokołu. Przekazanie opcji SSH wymaga użycia zmiennej powłoki.
export RSYNC_RSH='-p 2022 -i id_rsa';
W powyższym poleceniu użyto polecenia wbudowane
export
w celu przeniesienia zmiennej
do obszaru pamięci odpowiedzialnego za zmienne środowiskowe (będzie
o tym w ostatnim rodziale materiału). Zmiennej nadano wartość
składającą się z dwóch opcji polecenia SSH. Rsync będzie
korzystać z tych informacji podczas zestawiania połączenia ze stroną
zdalną.
11.3. Wprowadzenie do udostępniania plików
Operując w sieci lokalnej maszynami z dystrybucjami Linuksa, przyjdzie kiedyś pora, na to aby wykorzystać je do współdzielenia plików między użytkownikami, nie zależnie od tego jakiego systemu używają. Tutaj warto się zastanowić na tym między jakimi systemami będą one wymieniane oraz w jaki sposób użytkownicy łączą się z naszymi serwerami. Czy łączą się bezpośrednio z siedziby firmy gdzie też znajdują się serwery czy też pracują zdalnie.
Dlaczego o tym mówię, otóż istnieją metody lepiej i gorzej przystosowane do współdzielenia plików między różnymi systemami. Jedne są prostsze w obsłudze, ale np. nie posiadają żadnych metod uwierzytelniania, albo zostosowanie ich wymaga dodatkowych środków i nakładu pracy. Inne posiadają już pewne zabezpieczenia przed nieuprawnionym dostępem, ale nie są aż tak wydajne. Trzecią opcją są metody bardziej skupiające się na bezpieczeństwie, ale ich wydajność dość mocno kuleje. Oczywiście przy większym zangażowaniu można użyć tych metod w pozostałych środowiskach, a nie tylko w tych wymienionych, jednak warto mieć powyższe na uwadze oraz przyjąć do wiadomości fakt, że jeśli coś jest bardziej skomplikowane, to więcej składników tego czegoś może zawieść. Tym optymistycznym akcentem przjdziemy do pierwszej metody.
11.4. Współdzielenie plików między wszystkimi platformami
Jedną z metod współdzielenia plików między większością dostępnych platform na świecie jest pakiet Samba. Pakiet ten zawiera wszelkie niezbędne oprogramowanie do obsługi protokołu SMB (ang. Server Message Block) firmy Microsoft. Protokół ten w systemach MS Windows służy do udostępniania folderów w sieci. Samba jest natomiast implementacją protokołu SMB dla Uniksów (bo nie tylko dla samych dystrybucji Linuksa). Opis zagadnienia będzie składać sie głównie z opisu konfiguracji serwera. W niej zajmiemy się ogólną konfiguracją serwera, konfiguracją uwierzytelniania i użytkowników, udostępnianiem udziałów (katalogów) oraz udostępnianie drukarek. Zatem nie przedłużając:
11.4.1. Ogólna konfiguracja serwera Samba
Samba to potężny demon, posiadający wiele zastosowań, na przykład po za konfiguracją udostępniania folderów oraz drukarek Samba może świadczyć usługi katalogowe (domenowe) dla systemów w sieci jeśli chcelibyć zarządzać wszystkimi komputerami w sieci. O konfiguracji Samby jako serwera domenowego Active Directory (usługi katalogowe w implementacji Microsoft-u) zrobiłem odrębny materiał i znajduje się pod tym linkiem: https://morketsmerke.github.io/articles/linux/samba_AD_DC_-_Instalacja.html Pakiet Samby składa się głównie z dwóch programów. Głównego demona protokołu SMB smbd oraz demona protokołu NetBIOS, który jest protokołem pomocniczym dla SMB i zapewnia interfejs pozwalający na łączenie się aplikacji w innym komputerami w sieci lokalnej. Istotną cechą jaką odgrywa NetBIOS jest używanie przyjaznej nazwy zamiast adresu. A odpowiadającym za niego demonem jest nmbd. Te informacje mogą być nam potrzebne podczas zarządzania Sambą, bowiem czasami trzeba zrestartować oba demony, aby pewne zmiany mogły wejść w życie.
Plik konfiguracyjny na Debianie znajduje się w katalogu
/etc/samba. Plik jest formatu XDG, czyli jest podzielny na
sekcję (np. [global]) w sekcjach zawarte są opcje w formacie
klucz-wartość (np. workgroup = WORKGROUP). Sama konfiguracja
jest dość rozwleczona przez duża ilość opisów, pomocne rozwiazanie
kiedy nad lub pod opcją mamy wyjaśnienie co dokładnie ona robi. Sama
sekcja [global]
podzielona jest
za pomocą komentarzy na podsekcje, w których znajdują się opcje np.
dotyczące sieci czy uwierzytelniania. Zawartość tego pliku jest
zależna od dystrybucji. Dlatego też na Debianie mamy w podsekcji
Browsing/Identification
mamy tylko
opcję definiującą grupę roboczą.
[global] ## Browsing/Identification ### workgroup = WORKGROUP
Jest to wskazanie grupy roboczej, której częścią ma być serwer
Samby, najczęściej jest to
WORKGROUP
, który jest wartością
domyślną. Po tej opcji następuje Podsekcja sieciowa. W niej występują
następujące opcje:
#### Networking #### ; interfaces = 127.0.0.0/8 eth0 ; bind interfaces only = yes
Tutaj znajdują się tylko dwie opcje służą one kształtowaniu ruchu
jaki może docierać do Samby. Za pomocą opcji
interfaces
podajemy adresy sieci
w notacji CIDR wraz z nazwami interfejsów, które będą mogły korzystać
z serwera. Druga opcja
bind interfaces only
powoduje, że
ruch do Samby będzie dopuszczony tylko ze zdefiniowanych za
pomocą opcji interfaces sieci.
#### Debugging/Accounting #### log file = /var/log/samba/log.%m max log size = 1000 logging = file panic action = /usr/share/samba/panic-action %d
W podsekcji Debugging/Accounting
znajdują się opcje odwiedzialne za konfiguracje rejestrowania
komunikatów diagnostycznych i tak kolejno:
- log file - ta opcja wskazuje nam konkretny plik z komunikatami diagnostycznymi połączeń. Nadanie tej opcji takiej wartości jak na przykładzie spowoduje, że dla każdego podłączonego do serwera komputera będzie tworzony osobny plik na komunikaty diagnostyczne.
- max log size - ustawienie maksymalnego rozmiaru dla pojedynczego pliku z komunikatami diagnostycznymi wyrażone w KiB.
- logging - ustawienie systemu logowania,
domyślnie będą to pliki (
file
): /var/log/samba/log.smbd oraz /var/log/samba/log.nmbd - panic action - czynność wykonywana w momencie, kiedy w wyniku błedu dojdzie do zatrzymania demona. Domyślnie wysłany jest mail (wewnątrz systemu, do superużytkownika, ze rzutem procesu wygenerowanym przez debuger)
W ten sposób omówilismy sobie podstawowe opcje konfiguracji serwera Samba.
11.4.2. Konfiguracja uwierzytelniania użytkowników na serwerach Samba
Poniżej podsekcji dotyczącej komunikatów diagnostycznych znajduje się
fragment pliku konfiguracyjneg dotyczący ustawień uwierzytelniania -
Authentication
, ta podsekcja zawiera
ustawienia dostępu użytkowników do serwera. Samba może
mieć odrębne hasła, niż te wykorzystywane przez użytkowników do
dostępu do powłoki. Poniżej znajduje domyślna podsekcja
uwierzytelniania dołączona pakietu Samby dostępnego na
Debianie.
####### Authentication ####### server role = standalone server obey pam restrictions = yes unix password sync = yes passwd program = /usr/bin/passwd %u passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* . pam password change = yes map to guest = bad user
Pierwsza z opcji jest rola serwera. Jeśli nasza Samba ma
być serwerem plików to zostaje wartość wskazująca na samodzielny
serwer (standalone server
), kolejna
opcja decyduje czy dostęp musi być zgodny z wymogami systemu PAM
(obey pam restrictions
). Trzecia
opcja jest dość istotna
(unix password sync
). Dodając
użytkownika do Samby, należy przypisać mu hasło. Te hasła
mogą być w odrębnej bazie, ale mogą być również synchonizowane z
hasłami systemowymi. Ta opcja właśnie o tym decyduje. Czy hasła
powinny być synchronizowane z systemowymi czy też nie. Nie ma
jednoznacznej odpowiedzi czy tak czy nie. Wszystko
zależy od potrzeb, tak jak wspomniałem o tym podczas wprowadzenia
do współdzielenie plików. Warto natomiast znać konsekwencje takiego
działania, włączenie synchonizacji haseł, zmieni hasło dostępu do
systemu i najbezpieczniejszym rozwiązaniem będzie zablokowanie
dostępu do powłoki dla użytkowników wyłącznie Samby,
ponieważ hasła zapisane aby mieć łatwiejszy dostęp do udziałów
Samby z platform nie Linuksowych można odzyskać.
Dziś w dystrybucjach takich jak Debian, mimo iż ta opcja jest
domyślnie włączona to i tak nie działa, ponieważ element
podsystemu PAM, który kontrolował i obsługiwał cały ten proces
został usunięty w z Samby w okolicach 2015 roku.
Opcje poprzedzone frazą passwd
,
służą wskazaniu programu służącemu ustawianiu hasła
(passwd program
) oraz
zdefiniowaniu komunikatów wyświelanych użytkownikowi podczas
procesu zmiany hasła (passwd chat
).
Obecnie te opcje są puste (przynajmniej na Debianie), nie
mają żadnego wpływu na działanie pakietu, ponieważ na dzień dziejszy
synchronizacja haseł nie działa i nie zapowiada się aby kiedy
kolwiek przywórcono tę funkcjonalność. W wspomniano o tych opcjach
dla spójności opisu konfiguracji.
Przedostatnia opcja
(pam password change
)
służy kontroli wykorzystania PAM podczas zmiany hasła za pomocą
klienta SMB niż w przypadku użycia programu zdefiniowanego w opcji
passwd program
. Ostatnia opcja
definiuje kiedy należy zrzucić uprawnienia do połaczenia anonimowego.
Domyślnie jest to stosowane, kiedy nie uda się pomyślnie
uwierzytelnić użytkownika.
Opcje uwierzytelniania otrzymały osobny podrozdział ze względu na to, że pełnią dość istotną rolę we współdzieleniu plików i zapewniają jakiś minimalny stopień bezpieczeństwa. Warto zwrócić uwagę na to, iż przedstawione tutaj są domyślymi ustawieniami zapożyczonymi bezpośrednio z pakietu Samby rozprowadzanego wraz z Debianem.
11.4.3. Współdzielenie katalogów - udziały Samby
Eksport katalogów domowych użytkowników
Jedną z najważniejszych cech, dla których ludzie korzystają z pakietu Samba jest współdzielenie katalogów, w których użytkownicy mogą dzielić się plikami z innymi w sieci. Innym zagadnieniem może być centralizacja katalogów domowych między użytkownikami wielu systemów uniksowych.
Pozostawiamy resztę opcji i przechodzimy do podsekcji
Share Definitions
. Tutaj
kończy się sekcja [global]
.
Domyślnie w pliku konfiguracyjnym nie znajdują się żadne
predefiniowane
udziały więc na początek zajmiemy się exportem katalogów domowych.
Za udostępnienie katalogów domowych odpowiada sekcja
[homes]
. Poniżej znajduje się
sekcja eksportu katalogów domowych zapożyczona z domyślnego pliku
konfiguracyjnego Debiana:
[homes] comment = Home Directories browseable = no read only = yes create mask = 0700 directory mask = 0700 valid users = %S
Sekcję rozpoczyna opcja comment
,
najzwyczajniej w swiecie jest opis. W nim możemy opisać zawartość
czy wskazać właściciela, nie które programy (lub składniki programów)
zajmujące się obsługą protokołu SMB w systemie mogą wykorzystać to
pole do tworzenia opisów znalezionych udziałów. A odnośnie
programów obsługujących protokół SMB to za widoczność udziału w
nim odpowiedzialna jest opcja
browseable
. Tutaj (domyślnie)
ustawiona jest na no
, przez co
udziału nie będzie widać w składnikach typu "Otoczenie sieciowe"
(obecnie "Sieć") w eksploratorach plików. Wpływ następnej opcji na
udział możemy się domyślić po samej nazwie
(read only
)
oraz ustawionej jej wartości (yes
).
Opcje zawierające frazę mask
zawierają domyślne uprawnienia dla tworzonych plików
(create mask
) oraz katalogów
(directory mask
) i jak możemy
zauważyć tylko właściciel będzie mieć jakie kolwiek uprawnienia.
Ostatnią opcją jest (valid users
),
która określa jacy użytkownicy mają mieć dostęp do danego udziału.
W przypadku eksportu katalogów domowych zamiast listy użytkowników
używa się wartości %S
, co powoduje,
że nie musimy podawać listy wszystkich użytkowników, a wymusza to
na użytkowniku stosowanie
\\SERVER\username
w momecie
odwołania się do serwera. W przypadku Uniksów stosuje odwrotne
ukośniki: //SERVER/username
. W ten
sposób wygląda eksportowanie katalogów domowych użytkowników. Dostęp
katalogu domowego użytkownika wymaga podmontowania udziału lub
skorzystania z klienta Samby. O czym będzie jeszcze w tym
podrozdziale.
Tworzenie udziałów Samba
W pliku konfiguracyjnym Samby każdy udział jest odrębną sekcją. Nazwa udziału jest zarazem nazwą sekcji. Poniżej znajduje sie przykładowa sekcja definiująca udział.
[nazwa_udziału] path = sciezka/do/katalogu comment = opis guest ok = no writeable = yes printable = no
Jak możemy zauważyć udostępnienie katalogu w sieci za pomocą
pakietu Samba nie wymaga za dużo pracy. Na przykładzie
pierwszym parametrem jest path
,
określa on ścieżkę do katalogu, który mamy zamiar udostępnić. Na
temat opcji comment
wspominałem
w podczas opisu eksportu katalogów domowych. Opcja
guest ok
umożliwia udzielenie
dostępu anonimowego do udziału. W tym przypadku dostęp w ten sposób
jest zablokowany. Rzadko się zdarza generalnie aby dostęp anonimowy
był wymagany, jeśli tak jest to najczęściej udział jest tylko
odczytu. Chciaż spotkałem się z taką prośbą aby skonfigurować udział
tak, aby każdy mógł w nim zapisać w nim swój plik.
Opcja writeable
konfiguruje czy na
udziale będzie można zapisywać jakie kolwiek dane. Ostatnia opcja
wprawdzie ma jedno głowne zadanie, wskazać Sambie, że ta
sekcja to współdzielony katalog, a nie udostępiona drukarka dlatego,
też ustawiono drukowalne jako nie. Nie ma potrzeby
ustawiania opcji browsable, ponieważ domyślnie jest ona
włączona. Chcąc zmienić ten stan, należy zapisać jej wystąpienie
definicji udziału i ustawić wartość
no
. Oczywiście jeśli jest taka
potrzeba możemy dodawać do definicji naszego udziału wiele innych
poznanych tutaj opcji takich jak domyślne uprawnienia, zezwolenie
na dostęp do tego udziału tylko dla wybranych użytkowników. Strona
podręcznika jest pełna opcji, jedną z ciekawszych może być
veto files
, gdzie na podstawie
wzorca możem zablokować udostępnianie konkretnych rodzajów plików.
Na stronie podręcznika: man 5 smb.conf
znajdziemy więcej informacji na temat tej opcji oraz wielu innych.
11.4.4. Współdzielenie drukarek przy użyciu pakietu Samba
Istnieje możliwość, aby za pomocą pakietu Samba można dać możliwość drukowania użytkownikom innych platform na drukarkach podłączonych do systemów z dystrybucjami Linuksa. Samo drukowanie na dystrybucjach odbywa się za pomocą system CUPS, ale to nie temat na teraz. Zajmiemy się nim w poźniejszych rodziałach. Teraz zakładamy, że drukarki są podpiętę do naszego komputera, na którym hostowana jest Samba i z tego komputera możemy drukować.
Dlaczego przyjąłem takie założenia, otóż drukowanie to jeden z cięższych tematów, ponieważ większość konsumenckich drukarek nie ma dobrego wsparcia dla dystrybucji Linuksa i korzystanie z nich to swojego rodzju loteria. Dlatego może warto zajrzeć na stronę https://www.openprinting.org/printers przed zakupem drukarki i jeśli ma działać pod którąś z dystrybucji to można rozważyć zakup jedenego z dostępnych tam modeli. Chociaż może to niebyć w cale takie prostę, poniważ wiekszość tych urządzeń lata świetności ma już za sobą. Szkoda, bo za pomocą komputerów jednopłytkowych (takich jak Raspberry Pi) można by drukarki, które nie są sieciowe podłączyć do sieci.
Nie mniej jednak. W domyślnej konfiguracji Debiana jest zdefiniowane współdzielenie drukarek. Obecne wersje Samby może nie zajmują się udostępnianiem drukarek, ale ich eksportem podobnie jak w przypadku katalogów domowych, przez co użytkownicy będą mieć dostęp do wszystkich drukarek podłączonych do naszego serwera Samby. Poniżej znajduje się wycinek konfiguracji dotyczącej eksportu drukarek w Debianie:
[printers] comment = All Printers browseable = no path = /var/tmp printable = yes guest ok = no read only = yes create mask = 0700
Wszystkie występujące tutaj opcje, dobrze już znamy. Zastanawiająca
może być wartość opcji path
, która
wskazuje na ścieżkę /var/tmp
.
Samba wymaga katalogu z możliwością zapisu przez pozostałych
użytkowników wraz z ustawionym bitem sticky, a to jedyny
obok, drugiego /tmp taki katalog. Problem z ogólnym
katalogiem /tmp jest taki, że znajduje się na ramdysku.
Posiada raczej małą objetość, a spływające od wielu użytkowników
pliki do drukarek mogą wyczerpać szybko to miejsce.
11.4.5. Dodawanie użytkowników Samby
Samba prowadzi odrębną bazę użytkowników wykorzystując do tego inne rodzaj szyfrowania. Dlatego też podczas "synchronizacji" (obecnie niedziałającej), wymaga wpisania hasła do /etc/shadow. Aby uzyskać dostęp do udziałów użytkownicy innych platform muszą mieć założone odrębne konta. Nazwy użytkowników kont Samby muszą odpowiadać tym Uniksowym, więc nie możemy utworzyć użytkownika mającego mieć dostęp tylko do udziałów, bez konta w systemie gdzie oprograwowanie do współdzielenia zasobów w systemie. Takie konto oczywiście możemy zabezpieczyć zmieniając domyślny program odpowiedzialny za uruchomienie zaraz po zalogowaniu. Użytkowników do Samby dodajemy za pomocą dostarczanego przez ten pakiet polecenia, musimy użyć opcji -a wskazującej na dodanie. Pominięcie tej opcji spowoduje, że będziemy zmieniać jedynie hasło. Do obsługi użytkowników Samby wymagane są uprawnienia administratora.
xf0r3m@debian:/var$ sudo smbpasswd -a debian [sudo] hasło użytkownika xf0r3m: New SMB password: Retype new SMB password: Added user debian.
Tak naprawdę to tyle, konto użytkownika jest gotowe. Polecenie
smbpasswd
posiada o wiele wiecej
funkcji niż tylko samo dodanie poniżej znajduje się lista opcji,
które mogą pomóc w administrowaniu użytkownikami Samby.
- -x - usunięcie użytkownika
- -d - zablokowanie użytkownika
- -e - odblokowanie użytkownika
Warto pamietać o tym, że użytkownicy służą jedynie uwierzytelnianiu, natomiast autoryzacji do zapisu na jednym z udziałów należy dokonać z użyciem użytkowników uniksowych. Nie warto szukać przyczyn problemów z dostępem do elementów udziału w użytkownikach Samby. Na początku warto w ogóle sprawdzić czy udział jest dobrze ustawiony (read only = no oraz writeable = yes) oraz czy uprawnienia do udziału umożliwiają na zapis.
11.4.6. Użycie protokołu samby
Kiedy nasz serwer SMB jest już skonfigurowany możemy przejść do konfiguracji klienta, a w zasadzie klientów. Ponieważ istnieje kilka metod na skorzystania z serwera Samby.
Pierwszym z nich jest skorzyststanie z przeglądarki sieciowej wbudowanej w menedżer plików, jeśli korzystamy z jednego z popularniejszych środowisk graficznych. Przycisk uruchamiający te funkcję znajduje się zazwycznaj po lewej stronie, najczęsciej nosi nazwę Przeglądanie sieci lub Sieć. Tutaj musimy przypomnieć sobie opcję browseable, która powodowała, że udziały były widoczne w tego typu komponentach. Domyślnie wyeksportowane katalogi domowe nie są w nich widoczne.
Takie ukryte dla przeglądarek udziały są osiągalne na dwa sposoby. Pierwszym z nich jest zamontowanie udziału jak dysku wymienego, czy partycji. Montowanie udziałów SMB wygląda podobnie, ale wymaga kilku dodatkowych opcji. Pierwszą z nich dość istotną jest wymagana w systemie obsługa protokołu SMB jako systemu plików. Taki system plików nosi nazwę CIFS i pakiety go obsługujący jest gotowy do zainstalowania w repozytorium dystrybucji (przynajmniej na GNU/Linux Debian - cifs-utils) Po zainstalowaniu pakietu, możemy wykorzystać polecenie mount do zamontowania udziału w systemie. Jak pamietamy montowanie systemów plików może wymagać uprawnień administratora.
xf0r3m@vm-9585173:~$ sudo mount -t cifs //DEBIAN/xf0r3m homes_on_debian -o user=xf0r3m,uid=1001,gid=1001 Password for xf0r3m@//DEBIAN/xf0r3m: xf0r3m@vm-9585173:~$ sudo mount | grep '//DEBIAN/xf0r3m' //DEBIAN/xf0r3m on /media/xf0r3m/immudex-crypt0/homes_on_debian type cifs (rw,relatime,vers=3.1.1,cache=strict,username=xf0r3m,uid=1001,noforceuid, gid=1001,noforcegid,addr=172.18.227.14,file_mode=0755,dir_mode=0755,soft, nounix,serverino,mapposix,rsize=4194304,wsize=4194304,bsize=1048576, echo_interval=60,actimeo=1,closetimeo=1)
Chcąc wykorzystać polecnie mount
.
Należy wskazać za pomocą opcji -t
montowany system plików, następnie występuje adres udziału w
przypadku dystrybucji Linuksa, ukosniki mają zwrot zgodny z tymi
wykorzystywanymi w systemie
(//DEBIAN/xf0r3m
). Następnie
występuje punkt montowania
(homes_on_debian
). Na samym końcu
występuje specyficzne dla systemu
CIFS
opcje montowania, więc kolejno:
user=xf0r3m
- nazwa użytkownika wskazywana do uwierzytelnienia się przed serwerem Sambyuid=1001
- mapowanie identyfikatora UID. Stosowane jest to gdy, użytkownicy posiadają inne UID-y na serwerze.gid=1001
- mapowanie identyfikatora grupy. Podobnie do opcjiuid
stosowane aby zapewnić odpowiednie prawa dostępu.
Po wydaniu takiego polecenia, zostaniemy zapytani o hasło. Hasło
również możemy w komponować w powyższe polecenie, za pomocą opcji
pass
. Jest to przydane gdy chcemy
montować udziały samby podczas uruchamiania systemu
(dodać wpis to pliku /etc/fstab). Jeśli po podaniu hasła zostanie
nam zwrócony znak zachęty, wówczas oznacza to, że udział został
poprawnie zamontowany. W przypadku problemów zostaniemy o tym
poinformowani stosownym komunikatem. Dowód zamontowania możemy
również otrzymać kiedy przefiltrujemy wyjście polecenia
mount
pod kątem występowania
adresu udziału. Co uczyniono w drugim polecenie.
Odłączenia udziału od systemu dokonujemy w ten sam sposób, co z każdym innym system plików, za pomocą polecenia umount.
xf0r3m@vm-9585173:~$ sudo umount homes_on_debian
Po wydaniu tego polecenia udział zostanie odłączony od systemu.
Innym sposobem jest dostęp do udziału SMB poza jego montowanie jest skorzystanie z dedykowanego klienta - smbclient. Korzystanie z niego przypomina trochę SFTP. Prawdopodobnie nie znajdziemy go domyślnie w systemie i trzeba będzie go zainstalować. Za pewne znajduje się on w repozytorium (na GNU/Linux Debian: smbclient). Po instalacji wydajemu poniższe polecenie.
xf0r3m@vm-9585173:~$ smbclient -U xf0r3m //DEBIAN/xf0r3m Password for [WORKGROUP\xf0r3m]:
Jesli jesteśmy się w stanie połączyć z serwerem Samby to to zostanie nam zwrócony prompt z prośbą o hasło. Jeśli hasło jest poprawne otrzymamy o taki znak zachęty.
Try "help" to get a list of possible commands. smb: \>
Lista dostępnych poleceń uzyskamy po przez wpisania polecenia
help
. Ta opcja może być przydatna,
kiedy musimy skorzytać udziału Samby a nie możemy montować
(np. wewnątrz kontenerów) lub nie mamy wystarczających uprawnień do
podmontowania udziału.
Oczywiście przedstawione poniżej dwa sposoby tyczą się głównie Uniksów, chciaż metoda z użyciem menedżera plików, również moze znaleźć zastosowanie oczywiście jesli posiadamy opowiednie komponenty w swoim systemie. Jednak inne platformy jak głównie MS Windows będą wykorzystywać wyłącznie ją. Rzadko używa się Samby do wymiany danych między dwoma Uniksami są do tego... No właśnie, nie można jednoznacznie powiedzieć, że lepsze metody. Zatem będzie metoda bezpieczeniejsza ale mało wydajna i metoda mniej bezpieczna (można by powiedzieć, że domyślnie nie posiadająca żadnych mechanizmów bezpiczeństwa), ale za to bardziej wydajna.
11.5. Bezpieczenie współdzielenie plików - SSHFS
Na podstawie informacji z poprzedniego rozdziału wiemy już, że przy użyciu protokołu SSH możemy przesyłac pliki służy do tego SFTP. Ale to nie wszystkie możliwości związane z obsługą plików przez ten protokół. Otóż możemy w lokalnym systemie zamontować katalog zdalny z wykorzystaniem SSH. Służy do tego SSHFS. Ta metody współdzielenia plików jest bezpieczna i łatwa w konfiguracji, SSHFS wymaga jedynie dostępu do protokołu SFTP. Ten system plików wykorzystuje ciekawą mechnikę o nazwię FUSE, która pozwala na zamontowanie udziału przez zwykłego użytkownika i nie wymaga żadnych dodatkowych uprawnień. Oczywiście jeśli coś ma tyle zalet, to albo musimieć równie dużo wad lub jedną dużą wadę, która nieco przekreśla popularność tej metody udostępniania plików. Jest nią wydajność, która jest wręcz mierna. Nie mniej jednak jest to chyba jeden z najbezpieczniejszych metod montowania katalogów zdalnych. Obsługa wymaga aby na komputerze klienta znajdowało się polecenie sshfs oraz fusermount są one dostarczane za pomoca pakietów (przynajmniej na Debianie): fuse3 oraz sshfs. Montowanie katalogu odbywa się za pomocą poniższego polecenia.
xf0r3m@vm-c1b9654:~$ sshfs xf0r3m@debian:/home/xf0r3m homes_on_debian -o reconnect xf0r3m@debian's password: xf0r3m@vm-c1b9654:~$ mount | grep 'xf0r3m@debian' xf0r3m@debian:/home/xf0r3m on /media/xf0r3m/immudex-crypt0/homes_on_debian type fuse.sshfs (rw,nosuid,nodev,relatime,user_id=1001,group_id=1001)
Tak zamontowany udział jest normalnie widocznym katalogiem w systemie.
Jak byśmy montowali np. udział Samby. Jeśli katalog
zdalny znajduje się w tej samej sieci lokalnej co klient to
nie powinniśmy dostrzec żadnych problemów z wydajnością. Problem
może okazać się gdy klient korzysta z niepewnego łącza jak jest np.
transmisja danych w sieci komórkowej. Mimo wolnego transferu, możemy
doświadczyć także zerwania połącznia co spowoduje, że nasz katalog
stanie się nieosiągalny, zaradzić temu może opcja
reconnect
dołączona na końcu
polecenia montowania.
Po zakończeniu prac katalog należy odmontować z systemu. Jednak nie robi się tego za pomocą znanego nam polecenie umount. To polecenie jest zarezerwowane do obsługi klasycznego montowania z udziałem jądra. Tutaj montowaliśmy ograniczając się tylko do przestrzeni użytkownika. Do tego celu wykorzystamy drugie polecenie jakim jest fusermount.
xf0r3m@vm-c1b9654:~$ fusermount -u /media/xf0r3m/immudex-crypt0/homes_on_debian xf0r3m@vm-c1b9654:~$ mount | grep 'xf0r3m@debian'
Po wyniku działania drugiego polecenia, możemy żauważyć że montowany udział nie istnieje już w systemie.
11.6. Współdzielenie plików między dwoma Uniksami
Ostatnią metodą udostępniania plików, jest współdzielenie zasobów dyskowych między dwoma systemami uniksopodobnym. W systemach Microsoftu, również istnieje możliwość wykorzystania tego protokołu, jednak ze względów bezpieczeństwa jest on wykorzystywany głównie między systemami godnymi zaufania. A wykorzystywanym do tego protokołem jest NFS (Network File System). Wymiana danych między hostami za pomocą NFS nie jest szyfrowana więc zaleca się wykorzystanie VPN jeśli jest konieczność użycia tej metody między dwoma odległymi serwerami. Ten protokół również nieposiada, żadnych wbudowanych metod uwierzytelniania, poza jednym przypisaniem dostępu do udziału konkretnemu adresowi IP, ale również taki udział może być udostępniony całej sieci. Zatem najlepszym środowiskiem jest dla NFS była sieć SAN-owska (Storage Area Network) odseparowana fizycznie i logicznie od sieci LAN.
W tym materiale ze względu na to, że maja to być wyłącznie podstawy obsługi dystrybucji linuksowych skupimy się tylko i wyłącznie na montowaniu udziału NFS w systemie. Jeśli chcialibyśmy skonfigurować serwer NFS, to możemy skorzystać z innego materiału, gdzie zostało to opisane pod tym adresem: https://morketsmerke.github.io/articles/terminallog/RedHat_-_RHCSA.html#17.nfs
Wykorzystanie NFS w naszym systemie wymaga oprogramowania, które trzeba zainstalować. Na Debianie pakiet nosi nazwę nfs-common. Po zainstalowaniu pakietu możemy przejść do montowania naszego udziału w systemie. W tym przypadku nie musimy podawać żadnych dodatkowych informacji poleceniu mount, polecenie samo na podstawie wprowadzonego adresu serwera spróbuje połączyć się z wykorzystaniem protokołu NFS.
xf0r3m@vm-c1b9654:~$ sudo mount debian:/home/xf0r3m homes_on_debian/ xf0r3m@vm-c1b9654:~$ mount | grep "debian:/home/xf0r3m" debian:/home/xf0r3m on /media/xf0r3m/immudex-crypt0/homes_on_debian type nfs4 (rw,relatime,vers=4.2,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp, timeo=600,retrans=2,sec=sys,clientaddr=172.19.87.103,local_lock=none, addr=172.19.82.231)
Zwróćmy uwagę na prostę z jaką dokonuje się podłączenia udziału NFS
na Uniksach. Dowód znajduje się w informacji zwróconej przez
polecenie mount
. Podobnie wygląda
odmontowywanie.
xf0r3m@vm-c1b9654:~$ sudo umount homes_on_debian xf0r3m@vm-c1b9654:~$ mount | grep "debian:/home/xf0r3m"
Sieciowy system plików został odłączony od naszego systemu.
11.7. Jak wygląda współdzielenie plików dzisiaj?
Przedstawione tutaj metody nie są archaiczne. Są one wykorzystywane głównie w sieciach lokalnych. Są one wykorzystywane głównie wewnątrz organizacji co może mieć mało wspólnego ze zwykłym konsumenckim współdzieleniem plików. W dzisiejszych czas wszystko zależy od dostępności zasobów na żądanie. Chcąc mieć te same pliki na laptopie z dystrybucją Linuksa oraz np. na telefonie z system Android to wystarczy że skorzystamy z usług przechowywania danych jakie serwuje na Google w postaci Google Drive. Jednak ilość miejsca jest ograniczona do 15GB dla zwykłych użytkowników, oczywiście można poszukać innego dostawcy tego typu usług, bo i tak nalepszym narzędziem do korzystania z tego typu rozwiązań jest przeglądarka internetowa. Ceny są do siebie zbliżone. Jedyną taka usługą ze strony trzeciej godną uwagi wydaję się Internxt oferując np. plan dożywotni. Nie mniej jednak na uwagę zasługuje przchowywanie danych, które opiera się na szyfrowaniu oraz na polityce zero-knowledge, co oznacza, że nawet sami pracownicy nie są wstanie dowiedzieć się co tak naprawdę tam jest przechowywane. Inną metodą może być samodzielne hostowanie takich rozwiązań jak Google drive chociażby w postaci Nextcloud. W takim bądź przypadku spada na nas rola zorganizowania oraz utrzymania niezbędnej infrastruktury. Na koniec warto dodać, że najlepszą metodę współdzielenia plików określa poziom zaawansowania technicznego udostępniących pliki. Jedni potrzebują specjalnych rozwiązań i specjalnych aplikacji innych zadowoli SFTP oraz SSHFS.
12. Konfiguracja środowiska użytkownika
Chcąc przedstawić użytkownikom dostępnę środowiska często przedstawia się podział, na środowisko tekstowe (TUI/CLI) lub środowisko graficzne. Dla uproszczenia też zastosowuje tutaj ten podział. Ze względu na to, że istnieje bardzo duża liczba dostępnych środowisk graficznych oraz fakt, że jego obecność do normalnego działania systemu jest zbędna, w tym rodziale skupimy się głównie na środowisku tesktowym, a tym jest głównie powłoka.
12.1. Rodzaje powłok
W Obrębie konfiguracji środowiska tekstowego użytkownika, pojęcie rodzaje powłok, nie tyczy się programów powłoki takich jak BASH czy CSH. Tutaj zakładamy, że BASH jest naszą domyślną powłoką oraz że to jej używamy. Powłoka może interaktywna (korzystamy z niej domyślnie np. logując się do systemu.) lub nieinteraktywna (taki rodzaj powłoki uruchamiają skrypty [będzie o nich w ostatnim rozdziale]). Innym rodzajem podziału działania powłoki może być powłoka logowania oraz powłoka bez logowania
Powłoka logowania uruchamiana jest za każdym razem kiedy, (najprościej rzecz ujmując) wymagane jest uprzednio uwierzytelnienie się aby uruchomić sesję powłoki. Logując się jedeną z konsol na serwerze uruchamiamy interaktywną połokę logowania podobnie jest w przypadku połączeń zdalnych z wykorzystaniem protokołu SSH.
Drugim rodzajem jest powłoka bez logowania, tyczy się ona głównie środowisk graficznych, bo tego rodzaju działanie powłoki jest zapewnianie programy typu emulator terminala oraz multiplekser terminala jak GNU Screen lub tmux.
Określenie tego czy powłoką, którą używany jest powłoką logowania
czy też bez logowania następuje poprzez analizę wartości zwróconej
przez polecenie echo $0
.
xf0r3m@debian:~$ echo $0 -bash xf0r3m@vm-6bee73e:~$ echo $0 bash
Pierwsza powłoka została zinicjowana przez połączenie SSH na
maszynie wirtualnej. W przypadku wartości zwróconej przez powyższe
polecenie na tym hoście, nazwę powłoki poprzedza myślnik
(-
), co oznacza, że jest to powłoka
logowania, wartość zmiennej bez myślnika, oznacza powłokę bez
logowania. Druga powłoka została uruchomiona na drugiej karcie tego
samego emulatora terminala, na którym pisze ten tekst.
Istnieje mozliwość uruchomienia wielu kombinacji rodzajów powłoki, nawet możliwość uruchomienia nieinteraktywnej powłoki logowania.
12.2. Pliki środowiska użytkownika
Dlaczego o tym wspominam, otóż różne rodzaje powłok mogą korzystać z różnych plików uruchomieniowych, z ktorych składa się środowisko użytkownika. Tych plików jest kilka, głównego podziału możemy doknac tylko i wyłącznie na podstawie logowania zatem:
- ~/.profile - powłoki logowania.
- ~/.bashrc - powłoki bez logowania
Jak możemy wnioskować po obecności tyldy (~), te pliki są plikami użytkownika, chociaż w systemie może znaleźć się także wersja globalną. Tak jest w przypadku dysrybucji Debian, pliki znajdują się w katalogu /etc: /etc/profile lub /etc/bash.bashrc. W każdym systemie znajdują się te pliki. Pliki globalne posiadają kilka różnic od tych użytkowników. Ale my w ramach tego materiału skupiamy sie głównie środowisku użytkownika więc i na plikach użytkownika poprzestaniemy. Tego typu pliki posiada każdy w swoim systemie więc nie mam sensu ich tutaj przytaczać w przykładach.
Jeśli przyjrzymy się plikowi ~/.profile to zauważymy, że wewnątrz ładowany jest plik ~/.bashrc. Tak więc powłoka logowania zostanie zainicjowana również zgodnie z plikiem ~/.bashrc. Na koniec do zmiennej zawierającej ścieżkę wyszukiwania poleceń dodane są lokalne katalogi, w których to użytkownik powinien przechowywać swoje pliki binarne. I to cały plik konfigurjący powłokę logowania. Znacznie bogatszym plikiem jest plik konfigurujący powłokę bez logowania. Wewnątrz niego znajdują się miedzy innymi:
- Konstrukcja dodająca polecenia do konfiguracji w przypadku powłoki nieinteraktywnej jak i interkatywnej.
- Domyślne ustawienia historii BASH.
- Ustawienie formatu oraz kolorów dla znaku zachęty na podstawie terminala na którym uruchomiona jest powłoka.
- Ustawienie aliasów polecenia ls włączających domyślne kolorowanie elementów.
Generalnie plik ~/.bashrc jest plikiem, który przechowuje informacje na temat:
- Aliasów.
- Zmiennej zawierającej ścieżkę wyszukiwania poleceń,
- Definicji wyglądu znaku zachęty,
- Domyślnego edytora (obecnie wykorzystywany jest do tego inny program, ale jesli nie jest obecny w systemie możemy skorzystać z dwóch zmiennych takich EDITOR oraz VISUAL),
- Programu stronicującego oraz jego domyślnego zachowania,
- Domyślnych uprawnień nadawanych nowotworzonym elementom (maska uprawnień).
Jeśli potrzebujemy zmienić coś z powyższych zaganień to należy umieścić je w tym pliku.
Generalnie jeśli chodzi o zmiany dokonywane w tych plikach, to raczej możemy pominąć plik ~/.profile, ponieważ on sam sobie zawiera załączenie (polecenie source, będzie o nim w ostatnim rozdziale) pliku ~/.bashrc. Więc wszystkie zmiany możemy zawrzeć w tym pliku. Nie należy tworzyć pliku na nowo został on przygotowany przez twórców dystrybucji i jest najlepiej dostosowany do tego co się w niej znajduje. Dodając jakie kolwiek zmiany do tych plików, musimy mieć na uwadzę wykonanie czynności, które chcemy umieści. Dłuższe wykonywanie poleceń w pliku ~/.bashrc to dłuższe oczekiwanie na znak zachęty po zalogowaniu się lub uruchomieniu terminala. Dlatego też, za nim wdrożymy zmiany w plikach osobistych, warto przetestować je z użyciem dodatkowego użytkownika. Jeśli uznamy, że działanie zmian jest przez nas akceptowalne, to możemy je dodać do naszych plików.
12.3. Rozszerzenie domyślnego środowiska użytkownika
Korzystanie z włącznie konsol wirtualnych, może niebyć za bardzo ekscytującym doświadczeniem pod kątem wizualnym. Poza takim rzeczami jak przeglądanie zdjęć czy oglądanie filmów jesteśmy wstanie normalnie korzystać z takiego systemu. Ma to swoje wady i zalety. Jedną z nich możebyć wykorzystanie naprawdę archaicznego sprzętu, np. laptopów z początku wieku. Zrezygnowanie z środowiska graficznego może pomóc w wyrobieniu sobie przyzwyczajeń odnośnie pracy w terminalu, nauke jednego z edytorów takich jak Vim, emacs lub inne. W teminalu możemy przeglądać strony internetowe pozbawione wszelkich pstrokatych dodatków, sam tekst, sama treść. Możemy przeglądać pocztę czy słuchać muzyki. Także wielu użytkownikom uniksów korzystanie z terminala wystarczy. Sam należę do takich osób, u których terminal to podstawowe narzędzie pracy i śmiało mogę obejść się bez środowiska graficznego. Jeśli już zdecydujemy się na to aby porzucić środowisko, to warto zaznajomić się z jednym z takich narzędzi jak tmux lub GNU Screen. Osobiście wolę tmux, jednak potrafi on sprawiać problemy czasami. W starszych wersjach zmiana katalogu głównego w sesji powłoki uruchomionej przez tmux, może powodować jej dziwne zachowanie, np. brak efektu działania klawisza backspace, mimo tego że znaki teoretycznie zostały usunięte. Jak wiemy mamy do dyspozycji kilka konsol. Jeśli zaś uruchomimy tmux to może zdefiniować sobie bardzo dużą ilość okien (tak jakby odrębna konsola), a je można jeszcze podzielić na mniejsze części. Tmux powzwala na kopiowanie i wklejanie przy użyciu innej metody dostępnej bez środowiska graficznego, co czyni go dość użytecznym programem.
13. Interfejs użytkownika i drukowanie
Jeśli kiedyś przyjdzie nam korzystać z dystrybucji Linuksa poza, serwera, systemami wbudowanymi oraz innymi rozwiązaniami, w których do interakcji z systemem wykorzystujemy wyłącznie powłokę, spotkamy się z interfejsem graficznym użytkownika. Najprościej rzecz ujmując jest tak jakby klasyczny pulpit z wyświetlającymi się w okienkach programami. Tak jakby ponieważ na interfejs graficzny składa się bardzo duża ilość różnych elementów, które mogą się między sobą diametralnie różnić.
Krótko scharakteryzujemy elementy, z których składa sie interfejs użytkownika. Ten rodział będzie dość mocno teoretyczny, więc jeśli nie interesuje nas czym jest Wayland czy X Window System lub magistrala D-Bus można przejść kolejnego rodziału.
13.1. Elementy składowe interfejsu graficznego
Jak już wcześniej wspomniałem na interfejs graficzny użytkownika składa się duża ilość elementów. Te elementy moga być zostać pogrupowane na podstawie czym tak na prawdę są. Jesli powiem, że środowisko graficzne uzupełniają widget-y, to tymi widget-ami są po prostu aplikacje dostarczane jako zależności zadania instalacji wybranego środowiska. Środowiska oczywiście mogą się bez nich obejść jednak przyjemnosć korzystania z takiego pulpitu powoduje mieszane uczucia.
13.1.1. Bufory ramki
Na samym dole intefejsu użytkownika znajduje się zazwyczaj bufor ramki jest to obszar pamięci odczytywany przez układy graficzne i następnie przekazywany do wyświetlenia na ekranie. Kilka bajtów reprezentuje jeden piksel. Podczas wyświetlania okien procesy na bierząco aktualizują zawartość bufora lub buforów (może ich być kilka, o czym się zaraz przekonamy) pamiętając przy tym aby nie nadpisać elementów innych okien (procesy programów wyświetlających się w oknie).
13.1.2. Mechnizm wyświetlania
Ciężko jest sklasyfikować przedstawione poniżej elementy. Niby służą temu samemu to jednak posiadają wiele zasadniczych różnic, że trzeba się na chwilę zatrzymać i zastanowić na tym jak je sklasyfikować.
X Window System
System X Window bazuje na architekturze klient-serwer, serwer wyświetlania, nazywany również serwerem X jest tak jakby jądrem całego interfejsu użytkownika. Serwer X zarządza wszystkimi elementami związanymi z pulpitem przy czym nie narzucając jak coś powinno działać lub wyglądać. Klientem w tej releacji pozostaja wszelkie aplikacje, które chcą wyświetlić okna w systemie X. Po nawiązaniu połączenia z nim, aplikacja żąda wyświetlenia okna. W odpowiedzi uzyskamy informacje o docelowym położeniu okna oraz zostanie mu wskazany obszar pamięci przeznaczony na bufor ramki. Czasmi serwer X sam zajmuje się renderowaniem elementów graficznycznych.
Ze względu na to, że serwer X bierze udział w tak wielu czynnościach, może stać się wąskim gardłem w pewnym momencie, chociaż X Window System jest aktywnie wykorzystywany od lat 80-tych, okazał się dość elastyczny aby móc obsługiwać elementy współczesnych interfejsów.
Wayland
Protokół Wayland w przeciwieństwie do X Window jest nastawiony na decentralizację. Żadna centralny serwer nie bierze udziału w renderowaniu elementów graficznych. Każdy klient otrzymuje swój bufor ramki oraz składnik kompozycji łączący bufory ramki klienta do postacji akceptowanej przez bufor ramki ekranu, przy czym to zadanie jest wspierane sprzętowo co może podnieść wydajność.
Różnice między Wayland oraz X Window System
Generalnie to nie ma zbyt wielkich różnic między protokołem Wayland a system X Window. Aplikacje w obecnych środowiskach wykorzystując X Window nie oczekują na wspracie od serwera i same renderują bitmapę (zapisuja elementy graficzne w buforze ramki w postaci bajtów) i przesyłają ja serwerowi X. Serwer łaczy je ze sobą z wykorzystaniem rozszerzenia kompozycji, które jest dostępne w nim od kilku lat. Jedną z faktycznych różnic jest wymaganie biblioteki libinput służącej do kierowania danych wejściowych do klientów. Protokół nie wymaga tej biblioteki. Jednak jest ona w każdym dostęp środowisku. O mówimy ją sobie przy okzaji głębszego zapoznania się z system X Window. Kolejną rzeczą jest rola menedżera okien w wyświetlaniu interfejsu użytkownika.
13.1.3. Mendżery okien
Menedżer okna w interfejsie użytkownika zajmuje się renederowanie elementów dekoracyjnych, obsługuje kierowane do tych elementów zdarzenia wejściowe oraz informuje serwer o położeniu okien. Te zadania są wykonywane kiedy korzystamy z systemu X Window. Inaczej jest w przypadku protokołu Wayland tutaj menedżer okien pełni rolę serwera, składa bufory ramek, aby były zgodne z buforem przeznaczonym do wyświetlania oraz obsługuje przekazywanie urządzeń wejścia na kanale zdarzeń. Jest to jedna z różnic miedzy opisanymi wcześniej mechanizmami wyświetlania. Każdy z dostępnych interfejsów może mieć swój własny menedżer okien. Niektóre menedżery okien są traktowane jako pełne interfejsy użytkownika. Oczywiście jest błędne założenie, ponieważ takim gołym menedżerom okien, brakuje kilku elementów. Zazwyczaj jest nazwa potoczna dla zestawu programów składających się na środowiska użytkownika, lecz nie jest to pełno prawne środowisko graficzne jak GNOME czy KDE.
13.1.4. Biblioteki interfejsu graficznego
Elementy interfejsów graficznych są często oparte o jedną z bibliotek, która wspiera tworznie różnego rodzaju przycisków, paneli, elementów dekoracyjnych czy projektowania okien. Wśród środowisk interfejsu użytkownika prym wiodą biblioteki standardu GTK+ oraz standardu Qt. Przyczym GTK+ jest wykorzystywany przez większą ilość środowisk.
13.1.5. Graficzny interfejs użytkownika
GUI obecnie są domyślnie określane jako całe środowisko użytkownika. Fakt, spinają one wiele cześci razem, np. są oparte o standard GTK+ lub Qt, często posiadają swój menedżer okien oraz menedżer wyświetlania (będzie o nim w dalszej części tego rozdziału) GUI zazwyczaj decydują o tym w jaki sposób wyświetlić elementy, decydują o wyglądzie wielu programów, dostaczaja swoje narzędzia np. takie jak emulator terminal, będąc jednym produktem składającym się z wielu składników. Warto poruszyć kwestię tego, że wielu użytkowników nie korzytsta z typowych GUI takich jak GNOME, KDE czy XFCE. Często wybierają oni menedżer okien, np. Fluxbox czy IceWM następnie dodają do tego menedżer wyświetlania, biblioteki oraz potrzebny im zestaw aplikacji. Tego typu rozwiązania powodują, mniejsze zużycie zasobów niż w przypadku użycia gotowych GUI, a to z kolei przekłada się to na konfort naszej pracy, niektóre czynności, które w przypadku gotowych GUI są automatyczne to w przypadku innych rozwiązań mogą wymagać dodatkowej konfiguracji. Oczywiście rozwiązania tego typu są pracochłonne, ale mogą przyspieszyć działanie systemu oraz umożliwić uruchomienie tak przygotowanego środowiska na słabszym sprzęcie, dzieki czemu możemy zaoszczędzic pieniądze. GUI mimo iż może być opartę o którąś z powyższych bibliotek, to potrafi być niezależne w domyśle od mechanizmu wyświetlania, wszystko zależy od twórców dystrybucji jak został skonfigurowany pakiet instalujący oraz konfigurujący domyślnie GUI.
13.1.6. Aplikacje
Do tej pory korzystaliśmy z programów, które były uruchamien z poziomu konsoli/terminala. Posiadając środowisko graficzne mamy możliwość skorzystania z przeglądarek WWW, programów pocztowych i wielu innych aplikacji. Oczywiście istnieją programy, dzięki którym możemy przeglądać strony internetowe (co prawda w postaci, czystego tekstu) lub czytać pocztę w terminalu. To jednak większa część tych programów jest projektowana z myślą o użytkownikach GUI upraszczając interakcje z nimi. Warto pamiętać jednak o tym, jednej z złotych zasad korzystania z jakiego kolwiek Uniksa. Nie zawsze to GUI może być dostępne, a powłoka będzie zawsze w mniej lub bardziej rozbudowanej formie
13.2. Określenie mechanizmu wyświetlania
Wiele wiodących dystrybucji może automatycznie instalować środowisko
użytkownika, bazując na zawartości obrazu płyty, z którego
uruchomiliśmy nasz komputer. Wykorzystując ten system do testowania
przykładów z tej książki, i czytając wyżej wymieniony podrozdział
możemy zacząć się zastanawiać, z jakiego mechanizmu wyświetlania
korzystamy w naszym w systemie. Do ustalenia informacji na temat
wykorzystywanego przez nas serwera wyświetlania (tak wiem
Wayland nie jest serwerem, ale tak będzie prościej do
zrozumienia i tak pozostanie do końca tego materiału), wykorzystamy
zmienną $WAYLAND_DISPLAY
. Jeśli
wyświetlenie jej zawartości zwróci jakieś informacje to oznacza, że
możemy być nie mal pewni, że korzystamy z protokołu Wayland.
Jeśli wyświetlenie jej zawrtości nie zwróci nic, to najpewniej nie
jest ona w ogóle zainicjowana, co oznacza że korzystamy z serwera
X Window.
[xf0r3m@fedora39-sway ~]$ echo $WAYLAND_DISPLAY wayland-1
13.3. Protokół Wayland
Wayland jest protokołem pośredniczącym między menedżerem kompozycji a klientem graficznym. Ten system nie posiada jednego dużego pakietu, ale bibliotekę protokołu, z której korzystają klienci. Poza tym w skład protokołu wchodzi referencyjny menedżer kompozycji Weston, kilka klientów oraz programów narzędziowych.
Referencyjność Weston polega na tym, że nie powinien być on wykorzystywany samodzielnie, natomiast posiada on wszelkie składniki składniki kompozycji i jego zadaniem jest zapewnienie podstawowego interfejsu, który projektancji własnych menedżerów kompozycji mogą analizować i na podstawie tego poprawnie implementować kluczowe funkcje.
13.3.1. Menedżer kompozycji
Korzystając z Wayland, możemy nie dokońca być świadomi tego z jakiego menedżera kompozycji korzystamy, każde wieksze środowisko wykorzystuje swoje rozwiązanie. Jednak zdobycie tych informacji z systemu jest dośc proste do realizacji. Menedżer kompozycji do komunikacji z klietami wykorzystuje gniazdo uniksowe o nazwie wayland-X. Tę nazwę może kojarzyć z wartości z zwracanej przez zmienną WAYLAND_DISPLAY, bo tym tak dokładnie jest - nazwą ekranu. Przy użyciu polecenia ss wraz z opcjami -xlp wyświetlającymi gniazda uniksowe na których prowadzony jest nasłuch wraz procesami, odpowiedzialnymi za ten nasłuch.
[xf0r3m@fedora39-sway ~]$ ss -xlp | grep 'wayland' u_str LISTEN 0 1 /tmp/.X11-unix/X0 22765 * 0 users:(("Xwayland",pid=1153,fd=23)) u_str LISTEN 0 128 /run/user/1000/wayland-1 22759 * 0 u_str LISTEN 0 1 @/tmp/.X11-unix/X0 22764 * 0 users:(("Xwayland",pid=1153,fd=21))
13.3.2. Biblioteka libinput
Biblioteka libinput jest odpowiedzialna z uzyskanie od jądra danych wejściowych z urządzeń, np. znaków wprowadzanych na klawiaturze oraz ustandaryzowanie ich do danych protokołu. Mimo, iż te informacje mogą wydawać się nie ciekawe do omówienia to przy użyciu tej biblioteki możemy śledzić zdarzenia wejściowe:
Zanim przjedziemy do śledzenia zdarzeń możemy za pomoca polecenia libinput, które jest powiązane z biblioteka libinput wyświetlić listę dostępnych w systemie urządzeń wejściowych.
[xf0r3m@fedora39-sway ~]$ sudo libinput list-devices ... Device: AT Translated Set 2 keyboard Kernel: /dev/input/event1 Group: 3 Seat: seat0, default Capabilities: keyboard Tap-to-click: n/a Tap-and-drag: n/a Tap drag lock: n/a Left-handed: n/a Nat.scrolling: n/a Middle emulation: n/a Calibration: n/a Scroll methods: none Click methods: none Disable-w-typing: n/a Disable-w-trackpointing: n/a Accel profiles: n/a Rotation: 0.0 ...
Teraz kiedy orientujemy się jakie urządzenia są podłączone oraz w jaki sposób są widziane przez protokoł Wayland możemy przjeść do śledzenia zdarzeń.
[xf0r3m@fedora39-sway ~]$ sudo libinput debug-events --show-keycodes ... -event1 KEYBOARD_KEY +8.267s KEY_SPACE (57) pressed event1 KEYBOARD_KEY +8.387s KEY_SPACE (57) released event1 KEYBOARD_KEY +12.993s KEY_K (37) pressed k event1 KEYBOARD_KEY +13.113s KEY_K (37) released event1 KEYBOARD_KEY +15.175s KEY_M (50) pressed m event1 KEYBOARD_KEY +15.283s KEY_M (50) released -event5 POINTER_MOTION_ABSOLUTE +18.768s 18.95/ 39.58 event5 POINTER_MOTION_ABSOLUTE +18.770s 18.95/ 39.45 event5 POINTER_MOTION_ABSOLUTE +18.782s 18.95/ 39.32 ... -event1 KEYBOARD_KEY +20.305s KEY_LEFTCTRL (29) pressed event1 KEYBOARD_KEY +20.308s KEY_C (46) pressed
Po uruchomieniu mozemy poprzemieszczać kurs myszy lub poklikać kilka klawiszy na klawiaturze, w na wyjściu polecenia zobaczymy takie informacje jak te przedstawione na przykładzie.
13.3.3. Zgodność Wayland z system X Window
Protokół Wayland jest w miarę świerzym (jak na postrzeganie czasu przez uniksy) podejściem jesli chodzi o wyświetlanie. Do tej pory większość okienkowych, mających uruchamiać się na dystrybucjach Linuksa, ale i nie tylko - system X Window jest również używany na innych uniksach, było projektowanych z myślą o system Xorg. Zgodność natomiast między tymi dowoma systemami polega na dwóch podejściach.
Podejście pierwsze polega na dostosowaniu aplikacji natywnie przygotowanej dla X. Aplikacje przygotowane z myślą o dystrybujach Linuksa często wykorzystują zestawy narzędzi dużych środowisk graficznych takich jak GNOME lub KDE. Te zestawy są przygotowane do działania z protokołem Wayland. Wiele podstawowych aplikacji zostało już dostosowanych w ten sposób. Różnice tutaj mogą polegąć na obsłudze elementów dekoracyjnych czy konfiguracji urządzeń wejściowych. Pozostaja jescze zależności wobec bibliotek związanych z systemem X.
Innym rozwiązaniem jest uruchomienie aplikacji przeznaczonych dla X po przez warstwę zgodności, polegającą na uruchomieniu jako klienta Wayland całego serwera X Window. Nazwywane jest to serwerem Xwayland i jest domyślne działanie podczas sekwencji ładowania menedżera kompozycji. Wykorzystanie takiej warstwy wymaga przetłumaczenia zdarzeń wejściowych oraz utrzymania osbono buforów ramek okien. Metoda ta jest równiez nieco wolniejsza, ale często nie jest brane w ogóle pod uwagę.
Oczywście odwrotnie to nie zadziała nie można uruchmaiać aplikacji dla Wayland pod system X, choć teoretycznie jest to możliwe. Możemy uruchomić menedżer kompozycji w oknie system X, jednak nie jest zalecane ze wzgledu na efekty uboczne, takie jak uruchomienie aplikacji w sesji X Window, a jej rzeczywiste pojawienie się w uruchomionej w oknie sesji Wayland.
13.4. System X Window
W dość odgłegłych czasach jedną z cech systemu X window był dość duży gabaryt, bowiem pakiet ten zawierał poza serwerem wyświetlania biblioteki oraz kilka klientów. Te czasy na szczęście minęły dzięki upowszechnieniu się niezależnych środowisk graficznych takich jak GNOME czy KDE, wówczas można było skupić się na samym serwerze. Uproszczeniu uległa również biblioteka klientów.
Działanie serwera X Window jest proste do namierzenia, wystarczy spojrzeć na listę procesów:
xf0r3m@vm-65cd1fb:~$ ps -aux | grep 'Xorg' root 1001 1.1 6.1 406772 119620 tty7 Ssl+ 10:59 0:32 /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
Istotną wartością w linii polecenia uruchamiającego jest
:0
. Jest to oznaczenie
wyświetlacza X. Wyświetlaczem X możemy
nazwać całość obrazu jaki jest generowany przez serwer wyświetlania.
Na to składają się wyświetlacze podłączone do karty graficznej lub
do komputera. Najczęściej jednak wyświetlacz X odpowiada
podłączonemu monitorowi. Jeśli już chcielibyśmy użyć podobnych metod
do ustalanie mechanizmu wyświetlania jak w przypadku Wayland,
to możemy skorzystać ze zmiennej DISPLAY. Wyświetli ona zapewne
oznaczenie wyświetlacza oraz oznaczenie ekranu. Jeśli mamy
podłączony do systemu tylko jeden fizyczny ekran, to zapewne
wartość, tej zmiennej będzie wyglądać następująco.
xf0r3m@vm-65cd1fb:~$ echo $DISPLAY :0.0
13.4.1. Menedżery wyświetlaczy
Korzystając z dystrybucji przeznacznonych na komputery biurkowe, raczej nię będziemy sami uruchamiać serwera wyświetlania w jednej z wirtualnych konsoli. Po załadowaniu się systemu naszym oczom ukaże się okienko służące do logowania z jakimś obrazkiem w tle oraz umiesczonym w jednym z rogów przycisku wyboru sesji lub ustawień ułatwień dostępu. Menedżery wyświetlaczy mogą być częścią środowisk graficznych i to nie koniecznie tych dużych. Ponieważ środowisko LXDE, uważane za jedno z leżejszych posiada swój menedżer wyświeltacza. Jednak posiadanie dedykowanego menedżera jest raczej domeną, największych środowisk takich jak GNOME czy KDE. Istnieją również menedżery niezależne pozwalające uruchomić najróżniejsze środowiska czy menedżery okien. Jednym z nich jest lightdm, który nawet jest uruchomiony na komputerze na którym piszę ten materiał. Możemy to wywnioskować z widniejącej w poleceniu uruchomienia serwera X nazwy menedżera.
Głównym zadaniem menedżerów wyświetlaczy jest uruchomienie serwera wyświetlania. Po uwierzytelnieniu, bądź od razu w przypadku ustawionego automatycznego logowania. Jednak samo uruchomienie serwera X Window nie jest jedynym zadaniem tego programu. Jeśli by tak było, to po zalogowaniu/włączeniu systemu dostalibyśmy czarny ekran z x zamiast kursora na środku ekranu. Jednym z zadań jest zainicjowanie sesji użytkownika, poprzez np. odpowiednie skonfigurowanie składników tego środowiska (np. włączenie wygaszacza ekranu, ochrony wzroku czy ustawienie tapety [nie które mendżery okien, funkcjonują w ten sposób]) i uruchomienie odpowiedniego menedżera okien będącego jednym z najważniejszych programów sesji użytkownika. Menedżery wyświetlania odpowiadają za szereg ciekawych funkcji, których możemy doświadczyć i dostosowac je do swoich potrzeb.
13.4.2. Właściwości sieciowe serwera X Window
Serwer X Window oferuje zdalny dostęp poprzez sieć. Rozwiązanie to jest podobne do, możliwe że znanych nam usług zdalnego pulpitu. Jednak ta usługa w obecnych czasach jest wyłaczona ze względów bezpieczeństwa - nie zapewnia poza uwierzytlenieniem żadnych zabepieczeń. Warto rozejrzeć się za jakimś innym dostępem, większość konfiguracji na Uniksach wykonamy z poziomu powłoki więc dostęp przez secure shell powinien wystarczyć. Mimo to jeśli na przykład potrzebujemy:
- Skonfigurować aplikację internetową, domyślnie otwierającą
wysoki port na pętli zwrotnej. Możliwość wywołania tej aplikacji
z poziomu naszego komputera odpada. Możemy zastosować opcję
-X polecenia
ssh
. Ta opcja powoduje przekierowywanie klientów X Window uruchomionych na serwerze do komputera klienta SSH (powoduje wyświetlenie okna z aplikacją, często wykorzystywane do wyświetlenia okna przeglądarki, która uruchomiona jest faktycznie na serwerze i tam ma dostęp do sieci). Uruchomiona w ten sposób przeglądarka da dostęp do aplikacji. - Uruchomić okno z programem na uruchomionej już sesji użytkownika.
Posiadając dostęp do SSH oraz do użytkownika z uprawnieniami,
którego uruchamiamy sesję środowiska graficznego możemy,
uruchamiać na pulpicie okna X Window. Wymagane do tego
jest posiadanie polecenia xhost oraz skonfigurowanie
go poprzez pliki uruchomieniowe sesji środowiska graficznego
użytkownika, najczęściej to w nich zapisywane są takie czynności
jak ustawienia tapety dla nie których WM (windows manager).
Tym plikami są: ~/.xinitrc, ~/.xsession lub
~/.xprofile (dla, niektórych DM [displays manager]).
Wewnątrz tych plików, musimy za pomocą polecenia xhost
musimy nadać dostęp do swojej sesji użytkownika.
xhost +SI:localuser:<nazwa_użytkownika>
Taki użytkownik przenosząc zmienną DISPLAY z oznaczeniem wyświetlacza X, uzyska dostęp do sesji użytkownika. Jeśli uruchomi program, który domyślnie otwiera okno ze swoją zawartością np. przeglądarkę, czytnik plików PDF to, to okno zostanie otwarte w sesji użytkownika, do którego użytkownik inicjujący program ma dostęp.
Ze pomocą wyżej wymienionych sztuczek, możemych wchodzić w interakcje z serwerem wyświetlania X window na odległość. A jeśli naprawdę potrzebujemy zdalnego pulpitu, to warto użyć do tego np. takich protokół jak RDP lub VNC.
Przegląd klientów X Window
Mogliśmy spodziewać się tego, że omawianie środowiska graficznego użytkownika może nie mieć nic wspólnego z wierszem polecenia, ale system X Window wychodzi nam na przekór, udostępniając kilka narzędzi, które pozwalają wyświetlenie informacji czy też diagnostykę. Jednym z takich narzędzi jest polecenie xwininfo. Polecenie to zwraca informacje na temat wybranego przez nas okna. Po uruchomieniu poprosi nas o wybranie okna za pomocą myszy o po kliknięciu na nie w terminalu zostaną nam zwrócone informacje o tym oknie.
xf0r3m@vm-cffa62e:~$ xwininfo xwininfo: Please select the window about which you would like information by clicking the mouse in that window. xwininfo: Window id: 0x3400003 "Terminal - xf0r3m@vm-cffa62e: ~" Absolute upper-left X: 5 Absolute upper-left Y: 158 Relative upper-left X: 5 Relative upper-left Y: 29 Width: 817 Height: 578 Depth: 32 Visual: 0x50c Visual Class: TrueColor Border width: 0 Class: InputOutput Colormap: 0x3400002 (not installed) Bit Gravity State: NorthWestGravity Window Gravity State: NorthWestGravity Backing Store State: NotUseful Save Under State: no Map State: IsViewable Override Redirect State: no Corners: +5+158 -202+158 -202-32 +5-32 -geometry 100x30+0-27
Innym ciekawym narzędziem może być polecenie xlclients wraz z opcją -l. Jego zadaniem jest wyświetlenie wszystkich klientów serwera X Window.
13.4.4. Zdarzenia serwera X
Okna programów otrzymują dane wejściowe oraz informacje o stanie serwera X za pośrednictwem zdarzeń. Serwer X odbiera zdarzenia ze źródła następnie kieruje do dowolnego zainteresowanego klienta. System X Window daje nam możliwość eksperymentowania z zdarzeniami przy użyciu polecenia xev. Uruchomienie tego polecenia spowoduje otworzenie okna, po którym możemy jeździć kursorem i na nim klikać, natomiast w terminalu polecenie zajmie się obróbką tych zdarzeń i ich przedstawieniem użytkownikowi.
xf0r3m@vm-cffa62e:~$ xev ButtonPress event, serial 38, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 21524866, (41,103), root:(464,396), state 0x0, button 1, same_screen YES ... MotionNotify event, serial 38, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 21526915, (49,104), root:(472,397), state 0x100, is_hint 0, same_screen YES MotionNotify event, serial 38, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 21526998, (51,104), root:(474,397), state 0x100, is_hint 0, same_screen YES MotionNotify event, serial 38, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 21527185, (52,104), root:(475,397), state 0x100, is_hint 0, same_screen YES ButtonRelease event, serial 38, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 21527854, (52,104), root:(475,397), state 0x100, button 1, same_screen YES MotionNotify event, serial 37, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 19437248, (99,87), root:(522,380), state 0x0, is_hint 0, same_screen YES ... KeyPress event, serial 37, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 19448798, (194,226), root:(617,519), state 0x0, keycode 39 (keysym 0x73, s), same_screen YES, XLookupString gives 1 bytes: (73) "s" XmbLookupString gives 1 bytes: (73) "s" XFilterEvent returns: False KeyRelease event, serial 37, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 19448918, (194,226), root:(617,519), state 0x0, keycode 39 (keysym 0x73, s), same_screen YES, XLookupString gives 1 bytes: (73) "s" XFilterEvent returns: False KeyPress event, serial 37, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 19449093, (194,226), root:(617,519), state 0x0, keycode 40 (keysym 0x64, d), same_screen YES, XLookupString gives 1 bytes: (64) "d" XmbLookupString gives 1 bytes: (64) "d" XFilterEvent returns: False KeyRelease event, serial 37, synthetic NO, window 0x3800001, root 0x529, subw 0x0, time 19449206, (194,226), root:(617,519), state 0x0, keycode 40 (keysym 0x64, d), same_screen YES, XLookupString gives 1 bytes: (64) "d" XFilterEvent returns: False
Na przykładzie pokazano w przyciśniecie klawisza myszy, przesunięcie
kursora z przytrzymanym klawiszem, następnie zwolnienie klawisza
i przesunięcie kursora. Następnie naciśnięto dwa klawisze na
klawiaturze. W polu root otrzymamy współrzędne kursora myszy
na ekranie. Natomiast przed tym polem znajdują się współrzędne
względem okna otwartego przez polecenie xev. W przypadku
naciśniętych na klawiszy istotną informacją zwracaną przez to
polecenie jest keycode
, który
pozwoli nam na zmianę mapowania klawiatury, co może być przydatne
przy komputerach firmy Apple, zamieniając na ich klawiaturach
klawisze Command z Option. W tych klawiaturach
klawisz Option pełni rolę klawisza Alt, zatem
po zmianie mapowania, klawiatura funkcjonalnie będzie przypinać
każdą inną.
Ciekawą opcją polecenia xev jest
-id identyfikator
. Pozwala ona na
podanie identyfikatora okna (możną go uzyskać, za pomocą polecenia
xwininfo
), z którego polecenie
xev
będzie odczytywać zdarzenia.
13.4.5. Ustawianie preferencji i dane wejściowe serwera X
System wyświetlania Xorg oraz jego serwer zapewniają kilka
możliwych metod na zmianę preferencji, a niektóre z nich mogą nawet
nie zadziałać. Jedną ze zmian jakie możemym chceć przeprowadzić jest
wspomniana wcześniej zmiana mapowania, które można przeprowadzić na
dwa sposoby, albo zmiany są małe i wprowadzane na obecnym mapowaniu
przy użyciu polecenia
xmodmap
albo utworzenie nowego
mapowania, skompilowania go za pomocą polecenia
xkbcomp
oraz załadowania i
aktywacji przy użyciu polecenia
setxkbmap
. Za pomocą tych poleceń
możemy zmieniać mapowania większości klawiszy, jak i mamy możliwość
definicji mapować dla każdej podłączonej klawiatury.
Innym ustawieniem, które możemy chceć zmienić jest kolejność klawiszy
przy myszy. W przypadku osób leworęcznych podstawowy przypisk (LPM,
dla osób praworęcznych mieści skrajnie po lewej stronie) powinien
znajdować sie po drugiej stronie urządzenia (skrajnie po prawej).
Takiej zamiany możemy dokonać z pomocą
xinput
. Na poniższym przykładzie
pokazano w jaki sposób dostosować myszkę dla osób leworęcznych.
xf0r3m@vm-cffa62e:~$ xinput --set-button-map dev 3 2 1
Gdzie dev
jest identyfikatorem
urządzenia. Po podaniu informacji o urządzeniu wskazujemy kolejność
klawiszy. Jednak skąd można wziąć taki identyfikator?
Aby poznać urządzenia wejsciowe podłączone do komputera z dystrybucją
linuksa, na której wykorzystywany jest serwer wyświetlania X,
należy użyć wyżej wymienionego polecenia
xinput
.
xf0r3m@vm-cffa62e:~$ xinput ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ JUCHEN USB Keyboard id=11 [slave pointer (2)] ⎜ ↳ JUCHEN USB Keyboard id=12 [slave pointer (2)] ⎜ ↳ Logitech G502 HERO Gaming Mouse id=13 [slave pointer (2)] ⎜ ↳ Logitech G502 HERO Gaming Mouse Keyboard id=14 [slave pointer (2)] ⎜ ↳ AlpsPS/2 ALPS GlidePoint id=17 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Video Bus id=7 [slave keyboard (3)] ↳ Power Button id=8 [slave keyboard (3)] ↳ Sleep Button id=9 [slave keyboard (3)] ↳ JUCHEN USB Keyboard id=10 [slave keyboard (3)] ↳ Dell WMI hotkeys id=15 [slave keyboard (3)] ↳ AT Translated Set 2 keyboard id=16 [slave keyboard (3)] ↳ DELL Wireless hotkeys id=18 [slave keyboard (3)] ↳ JUCHEN USB Keyboard id=19 [slave keyboard (3)] ↳ Logitech G502 HERO Gaming Mouse Keyboard id=20 [slave keyboard (3)]
W przypadku mojego komputera, klawiatury posiadają identyfikatory 10 lub 19 oraz 16, w przypadku myszy (wskaźnika) jest to 13 oraz 17.
Nie ma takiej potrzeby aby klienci mieli by nasłuchiwać zdarzeń na (w tym przypadku) na trzech lub większej ilości urządzeń. Wykorzystują oni natomiast pojedyncze urządzenia rdzenia wirtualnego, dzięki temu możemy podłączyć i korzystać z więcej niż jednej klawiatury i więcej niż jednej myszy. Nie mniej jednak, klienci mogą korzystać z rozszerzenia X Input Extension w celu skupienia nasłuchiwania na jednym z nich, najczęsciej wiekszość z nich nie posiada w ogóle informacji o tym rozszerzeniu.
Pozostałymi preferencjami to ustawienia może być tło ekranu
nazywane potocznie tłem pulpitu, w tym przypadku możemy skorzystać
z polecenia xsetroot
, jednak
nie działa ono we wszystkich przypadkach. Lepszym rozwiązaniem może
być użycie zewnętrznego programu feh, które de facto jest
przeglądarką plików graficznych; czy ustawienia wygaszacza ekranu
lub funkcji DPMS (ang. Display Power Management Signaling)
za pomocą polecenia xset
.
13.5. Usługa D-Bus
Usługa D-Bus jest mechanizm przekazywania komunikatów, umożliwia ona wymianę informacji między aplikacjmi klienta, wykorzystując do tego komunikację między procesową. Jednak aplikację wykorzystują ją do powiadamiania o zdarzeniach systemowych takich jaki np. podłącznie pendrive-a do komputera. Proces chcąc wykorzystać ją do nasłuchiwania zdarzeń muszą połączyć się na początku z centralnym koncentratorem - demonem dbus-daemon i zarejestrować w celu odbierania konkretych rodzajów komunikatów. Dla przykładku inny monitor udisks-daemon sprawdza stan systemu plików udev pod kątem zdarzeń dyskowych i następnie wysyła je do dbus-daemona, a on przesyła je dalej do aplikacji zaintersowanych zdarzeniami tego typu.
13.5.1. Instancja systemowa oraz instancja sessji
System D-Bus obecnie jest jedną z ważniejszych części dystrybucji Linuksa - kanałami komunikacji D-Bus dysponuje systemd, a ze względu na to, że jest on ściśle powiązany ze środowiskiem graficznym powoduje to konflikt natury projektowej. Rozwiązaniem okazało się podzielenie centralnego koncentratora na dwa osobne procesy - instancję systemową, która uruchamiana jest podczas rozruchu przez program typu init. Proces ten jest uruchamiany wówczas w raz z opcją --system i działa z uprawnieniami użytkowanika usługi D-Bus, a procesy mogą łączyć się z nią za pomocą uniksowego gniazda.
Nie zależnie od instacji systemowej występuje instancja sesji która jest uruchamiana wyłącznie w momencie startu środowiska graficznego i to z nią łączą się aplikacje.
13.5.2. Monitorowanie komunikatów usługi D-Bus
W przypadku instancji systemowej, może dziać się nie wiele w
zależności od sprzętu na jakim pracujemy. Nie mniej jednak do
obserwacji komunikatów usługi D-Bus możemy wykorzystać
dostępne polecenie takie jak
dbus-monitor
Wybóru instancji dokonujemy za pomocą odpowiedniej opcji:
instancja systemowa - --system oraz instancja sesji -
--session.
Osobiście na swoim sprzęcie za pomocą monitorowania komunikatów D-Bus zauważyłem informacje odnośnie baterii oraz skanowania sieci bezprzewodowych przez mój komputer.
xf0r3m@laptop-0b3697e:~$ dbus-monitor --system ... signal time=1712415292.734476 sender=:1.39 -> destination=(null destination) serial=3626 path=/org/freedesktop/UPower/devices/battery_BAT0; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged string "org.freedesktop.UPower.Device" array [ dict entry( string "UpdateTime" variant uint64 1712415292 ) dict entry( string "Voltage" variant double 8.388 ) ] array [ ] signal time=1712415307.335665 sender=:1.8 -> destination=(null destination) serial=14104 path=/org/freedesktop/NetworkManager/Devices/3; interface=org.freedesktop.NetworkManager.Device.Wireless; member=AccessPointAdded object path "/org/freedesktop/NetworkManager/AccessPoint/794" signal time=1712415307.335727 sender=:1.8 -> destination=(null destination) serial=14105 path=/org/freedesktop/NetworkManager/Devices/3; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged string "org.freedesktop.NetworkManager.Device.Wireless" array [ dict entry( string "AccessPoints" variant array [ object path "/org/freedesktop/NetworkManager/AccessPoint/348" object path "/org/freedesktop/NetworkManager/AccessPoint/349" object path "/org/freedesktop/NetworkManager/AccessPoint/766" object path "/org/freedesktop/NetworkManager/AccessPoint/787" object path "/org/freedesktop/NetworkManager/AccessPoint/788" object path "/org/freedesktop/NetworkManager/AccessPoint/792" object path "/org/freedesktop/NetworkManager/AccessPoint/793" object path "/org/freedesktop/NetworkManager/AccessPoint/794" ] ) ] array [ ]
W przypadku instancji sesji informacji jest znacznie więcej, wystarczy że zmienimy aktywne okno lub nacisniemy klawisz kontrol przy aktywnym terminalu.
xf0r3m@laptop-0b3697e:~$ dbus-monitor --session ... error time=1712415718.701034 sender=:1.119 -> destination=:1.106 error_name=org.xfce.Xfconf.Error.PropertyNotFound reply_serial=25086 string "Właściwość „/shortcuts-no-menukey” nie istnieje na kanale „xfce4-terminal”" method call time=1712415718.925428 sender=:1.106 -> destination=:1.119 serial=25087 path=/org/xfce/Xfconf; interface=org.xfce.Xfconf; member=GetProperty string "xfce4-terminal" string "/shortcuts-no-menukey" error time=1712415718.926183 sender=:1.119 -> destination=:1.106 error_name=org.xfce.Xfconf.Error.PropertyNotFound reply_serial=25087 string "Właściwość „/shortcuts-no-menukey” nie istnieje na kanale „xfce4-terminal”"
13.6. Drukowanie
Drukowanie dokumentów w dystrybucjach linuksa, to wieloetapowy proces:
Program odpowiedzialny za drukowanie może przekszatałcić drukowany dokument na format PostScript. Ten krok jest opcjonalny. W przypadku braku konwersji program przesyła dokument do serwera wydruku, dokument wówczas zostaje umieszczony w kolejce wydruku. W momencie rozpoczęcia procesu drukowania serwer wydruku przesła dokument do filtra wydruku. W przypadku gdy dokument nie ma formatu PostScript filtr wydruku może dokonać konwersji. Jeśli drukarka nie obsługuje formatu PostScript to przy użyciu sterownika dokument zostaje skonwertowany do postaci odpowiedniej dla urządzenia drukującego. Ze sterownika drukarki zostają pobrane takie informacje jak źródło papieru czy ewentualny dupleks - zostają one dodane do dokumentu. Na koniec serwer wydruku wykorzystuje postprocesor aby wysłać dokument do drukarki.
Sam proces jest dość skomplikowany, ponieważ wykorzystuje się on w bardzo dużym stopniu na języku programowania PostScript. Język ten pełni rolę standardu drukowania w dystrybucjach Linuksa.
13.6.1. CUPS
System wydruku w dystrybucjach jest CUPS, produkt firmy Apple, więc
jest on także stosowany systemach macOS. Demonem tego system jest
cupsd. W raz system drukowania mamy dostępne polecenie
lpr
, które może pełnić rolę prostego
klienta pozwalającego na wysłanie plików do demona.
Istotną funkcją systemu CUPS jest implementacja protokołu IPP (ang. Internet Print Protocol), wykorzystuje on port TCP/631 i jego działanie opiera się o transakcje podobne do protokołu HTTP. System CUPS można konfigurować za pomocą przeglądarki łącząc się z adresem http://localhost:631. Użytkownikiem administracyjnym jest root i hasło jest takie same jak do zalogowania się na tego użytkownika w systemie. Większość serwerów wydruku obsługuje protokół IPP, w tym również system MS Windows. Co umożliwia nam tworznie drukarek sieciowych, które nie są domyślnie sieciowe. To zadanie jednak wymaga nieco ingerencji, ponieważ domyślna konfiguracja nie jest zbyt bezpieczna.
Konfiguracja drukarek w dystrybucjach Linuksa, jeśli mówimy tu o komputerze desktopowym zazwyczaj sprowadza się dodania drukarki przy użyciu specjalnego apletu w sekcji ustawień dla danego środowiska graficznego. Oczywiście, jak w przypadku większości zwykłego sprzętu dostępnego dla ogółu problemem mogą być sterowniki lub brak pliku PPD (wyjaśnienie czym jest plik PPD znajduje się w podrozdziale 13.6.2). Z własnego doświadczenia wiem, że z dystrybucjami Linuksa (przynajmniej z Debianem) dobrz działają drukarki firmy Kyocera (dawniej Kyocera-Mita) jeśli nie działają plug and play to zapewne dostępny jest sterownik własnościowy - w przypadku tych urządzeń problemem może być koszt tonera (są to urządzenia laserowe). Urządzeniem domowym, z które rzadko korzystam jest Canon Pixma TR4550 działa z Debianem plug and play dużym plusem jest tego urządzenia jest to, że te tusze nie zasychają (przynajmniej oryginalne). Z racji tego że to urządzenie wielofunkcyjne różnież jest możliwość skanowania przez pakiet Sane.
13.6.2. Konwersja formatów i filtry wydruku
Większość tanich drukarek nie rozpoznaje formatów PostScript ani PDF, aby móc znich korzystać na dystrybucjach Linuksa niezbędna jest konwersja formatu do tego określanego przez drukarkę.
System CUPS wysyła dokument do procesora RIP (ang. Raster Image
Processor), aby wygenerować bitmapę. Procesor RIP zawsze stosuje
program Ghostscript (gs
) do
realizowanie większości zadań. Czynność jest dość skomplikowana, gdyż
bitmapa musi być dostowana do formatu drukarki. Sterowniki drukarki
używane przez CUPS sprawdzają plik PPD
(ang. PostScript Printer Definition) konkretnego urządzenia,
aby określić takie ustawienia jak rozmiar papieru czy rozdzielczość.
14. Narzędzia programistyczne
Dystrybucje Linuksa, nie wymagają do podstawowej obsługi posiadania jakich kolwiek umiejętności programistycznych. Nie mniej jednak ten rodzaj systemów operacyjnych jest niezwykle często wybierany przez programistów, ze względu na swoją przejrzystość oraz obszerne udokumnetowanie.
Odwiedzając tę stronę czy też czytając ten materiał, za pewne oczekujemy od naszych komputerów czegoś więcej niż wyświetlenie żądanej strony internetowej czy odtworzenie wybranego pliku wideo. Może nadejść taka sytuacja, w której będziemy mieli styczność z kodem źródłowym i to nie tylko języków interpretowanych (zostaną one omówione również) ale także języków takich jak C, C++ czy Java. Warto zapoznać się z narzędziami programistycznymi dostępnymi w dystrybucjach, aby czytając pliki README dostarczone do paczek z kodem czy innej jego dokumentacji wiedzieć co się robi i być może dostosować proces tworzenia plików wykonywalnych do własnych wymagań.
14.1. Kompilator języka C
Za pomocą języka C, stworzono cały ten tematyczny obszar wokół, które oscyluje ten materiał. Wszystkie obecnie wykorzystywane Uniksy korzystają z języka C. Dlatego też warto wiedzieć na tym etapie, w jaki sposób możemy uruchomić program, który dostaniem w postaci zrozumiałej dla człowieka - w postaci kodu źródłowego (zwykłego tekstu).
W uproszczniu zamiana kodu źródłowego na postać wykonywalną przez komputer nosi nazwę kompilacji. Jednak jak się za chwilę okaże jest tylko połowa sukcesu (w większości przypadków). Obecnie w dystrybucjach Linuksa dostępne są dwa kompilatory GNU C Compiler (gcc) oraz Clang/LLVM. W tym materiale skupimy się na klasycznym kompilatorze gcc.
Kompilator oraz inne narzędzia programistyczne mogą domyślnie nie występować w dystrybucjach. W przypadku Debiana czy Ubuntu wystarczy zainstalować za pomocą domyślnego menedżera pakietów pakiet build-essential, w przypadku rodziny dystrybucji opartych o Red Hat należy użyć grupy instalacyjnej Development Tools.
W celu zobrazowania będę umieszczać w przykładach bardzo proste proste programy zapisane w języku C. Tak jak wspominałem, nie jest wymagana umiejętność programowania w żadnym z języków.
#include <stdio.h> int main() { printf("Hello, World!\n"); }
Zapisany powyżej bardzo prosty program, zapisałem w pliku hello.c. Pliki kodu źródłowego języka C powinny mieć rozszerzenie .c. Chociaż wiemy, że w przypadku Uniksów nie to znaczenia, to dla utrzymania porządku w plikach, warto je pokrótce opisać za pomocą zwykłego rozszerzenia plików.
Aby zamienić kod źródłowy na postać niskopoziomową wykonywalną przez nasz komputer musimy skompilować kod przy użyciu kompilatora. Program ten może być dostępny za pomocą poleceń cc lub gcc
xf0r3m@vm-cac72df:~/C$ cc hello.c
Polecenie to powinno zakończyć się, bez oznajmiania jakiego kolwiek działania na standardowym wyjściu. Wynikiem jego pracy jest pojawienie się tuż obok pliku a.out, który ma odpowiednii rodzaj uprawnień, aby móc go odrazu uruchomić - jest to plik wykonywalny, efekt działania kompilatora na powierzonym mu kodzie. Przekonać się możemy o tym wydając poniższe polecenie:
xf0r3m@vm-cac72df:~/C$ ./a.out Hello, World!
Wcześnie uruchamiając kompilator nie podaliśmy mu poza kodem żadnych innych informacji. Jeśli chcemy, aby nasz plik wykonywalny miał bardziej odpowienią nazwę możemy ją podać jako wartość opcji -o.
xf0r3m@vm-cac72df:~/C$ cc -o hello hello.c xf0r3m@vm-cac72df:~/C$ ls a.out hello hello.c xf0r3m@vm-cac72df:~/C$ ./hello Hello, World!
14.1.1. Kompilowanie wielu plików źródłowych
W przypadku pojednyczych programów takie działanie może wystarczyć, jednak rzadko się zdarza, aby programy były pojedynczymi plikami kodu źródłowego, zazwyczaj zawierają go znacznie, znacznie więcej. Sama praca z pojedynczym plikiem, może być niepożądana przez programistów jak i kompilatory mogą mieć problem z ich przetworzeniem.
Główne składowe programów są najczęściej grupowane a w pojedynczych plikach umieszcza się ich poszczególne elementy. Kompilacja również wygląda nieco inaczej. Wymagane jest użycie opcji -c w celu utworzenia dla każdego ze składowych plików obiektowych - zawierających kod obiektowy, który finalnie przyjmie formę pliku wykonywalnego. Załóżmy, że mamy tylko dwa pliki.
main.c:
void hello_call(); int main() { hello_call(); }
aux.c:
#include <stdio.h> void hello_call() { printf("Hello, World!\n"); }
Teraz dla każdego z plików źródłowych, musimy wygenerować pliki obiektowe. Pliki obiektowe są teoretycznie plikami wykonywalnymi ale brakuje w nich informacji pozwalających na dokończenie kompilacji. Sam system nie wie jak ma uruchomić pliki obiektowe, zazwyczaj na jeden program składa się wiele plików obiektowych.
xf0r3m@vm-cac72df:~/C$ cc -c main.c xf0r3m@vm-cac72df:~/C$ cc -c aux.c xf0r3m@vm-cac72df:~/C$ ls aux.c aux.o main.c main.o
W celu połaczenia plików obiektowych w plik wykonywalny użyjemy innego narzędzia, jakim jest konsolidator. Na systemach uniksowych nosi on nazwę ld i rzadko jest uruchamiany przez programistów samodzielnie. Kompilator wie w jaki sposób należy go uruchomić, dlatego też skorzystamy z jego pomocy. Mimo tego, że będziemy używać innego narzędzia to polecenie pozostaje to samo. Konsolidator może również występować pod nazwą linker.
xf0r3m@vm-cac72df:~/C$ cc -o myprog main.o aux.o xf0r3m@vm-cac72df:~/C$ ls aux.c aux.o main.c main.o myprog xf0r3m@vm-cac72df:~/C$ ./myprog Hello, World!
Dla celów dydaktycznych stworzyliśmy program składający z dwóch plików kodu źródłowego, zwykle jednak jest tego o wiele wiele więcej. Tutaj poszczególne czynności wykonaliśmy ręcznie. Jeśli w przypadku normalnego programu mielibyś to robić w ten sposób, zajęło by to masę czasu. Na szczęście nie trzeba robić tego ręcznie. Wykorzystamy do tego narzędzie GNU make, które omówimy sobie za chwilę.
14.1.2. Konsolidacja z bibliotekami
Obecne systemy do utworzenia pełnoprawnego programu z plików obiektowych wymagają zbiorów wcześniej skompilowanych komponentów. Pliki obiektowe zawierające te zestawy nie są niczym innym jak bibliotekami, a w ich skład poza wspomnianymi już plikami wchodzą również pliki nagłówkowego, których używaliśmy już w kodzie programów przy użyciu dyrektywy include. Biblioteki dodawane są na etapie konsolidacji, ich użycie w trakcie tej czynności nazywane jest konsolidacją na bazie biblioteki. W przypadku kiedy podczas konsolidacji zapomnimy wspomnieć o bibliotece konsolidatorowi to zostaniem nam zwrócony błąd. Chociaż błedy związane z niewłaściwym odwołaniem się do biblioteki, może nie wynikać z naszych ustawień konsolidatora. Może to również być wynikiem braku zainstalownych bibliotek w systemie.
Do wskazania bibliotek służy opcja -l kompilatora, oczywiście jeśli znajdują się one w domyślnej lokalizacji tj. /lib lub /usr/lib (chociaż w przypadku większości dystrybucji jest to ten sam katalog - kiedy program typu init to systemd), wystarczy podać jej nazwę. W przeciwnym razie musimy na początku wskazać ścieżkę, na której można znaleźć tą bibliotekę - za pomocą opcji -L, a następnie podać jej nazwę za pomocą wspomanianej już wcześniej opcji -l.
Przekazując do konsolidatora (kompilatora) informacje o bibliotekach wartości opcji wraz z opcjami piszemy łącznie.
$ cc -o badobject badobject.o -lcurses -L/usr/junk/lib -lcrud
Podczas wyszukiwania bibliotek, przydatne może okazać się polecenie locate, niestety może nie być domyślne zainstalowane w systemie. Dodatkowo wywmaga ono aktualizacji bazy przy użyciu polecenia updatedb.
Nasze proste programiki z przykładów, nie są pozbawione bibliotek. Tak jak już wcześnie wspominałem kompilator nie jest wstanie utworzyć pełnoprawnego pliku wykonywalnego. Konsolidator zawsze dołącza do utworzonego pliku obiektowego standardową bibliotekę języka C - libc.a - zawiera ona podstawowe komponenty języka C, jest ona zawsze dołączana chyba, że świadomie ją wykluczymy.
14.1.3. Biblioteki współużytkowane
Jak wiemy konsolidator zawsze dodaje do programów standardową bibliotekę języka C - libc.a. Ten plik jest biblioteką statyczną. Oznacza to, że konsolidator podczas tworzenia właściwego pliku wykonywalnego będzie kopiować do naszego pliku fragmenty kodu maszynowego z biblioteki, wówczas plik biblioteki nie będzie nam potrzeby do uruchomienia.
Tego typu rozwiązanie ma dwie dość znaczące wady. Pierwszą z nich są rozmiary bibliotek statycznych, a co za tym idzie rozmiary naszych programów oraz zajętosć pamięci operacyjnej podczas ich pracy. Drugą wadą, ale i nie kiedy zaletą, jest fakt przechowywanie kopii kodu biblioteki (oczywiście, określonych fragmentów), wewnątrz gotowego pliku wykonywalnego. Może być to wadą w momencie gdy, niektóre używane przez nas funkcje mogą okazać się niebezpieczenie i potrzebne będzie rekompilacja, a zaletą gdy twórcy przy okazji kolejnej wersji biblioteki zmieną coś, co nie dokońca będzie nam pasować lub zmiany będą zawierać błędy. Wówczas funkcje używane w naszym programie dalej pozostaną takie jakie powinny być według naszego projektu.
Rozwiązaniem wyżej wymienionych są obecnie stoswane biblioteki współdzielone. Podczas konsolidacji z wykorzystaniem bibliotek współdzielonych, nie są kopiowane całe fragmenty kodu, a tylko odwołania do nazw w bibliotece. W tym przypadku biblioteka jest ładowana do pamięci w momencie gdy jest potrzebna. Dodatkową zaletą tego rozwiązania jest fakt, że procesy mogą współdzielić ze sobą obszar pamięci, w którym znajdują się biblioteki, aby nie ładować ich za każdym razem, gdy uruchamiamy jakiś program.
Czasami po aktualizacji pakietów naszej dystrybucji, nie które otwarte programy mogą zachowywać się dziwnie lub przestać odpowiadać, może dziać się to, ze względu na to iż manadżer pakietów zaktualizował wraz z oprogramowaniem pakiety bibliotek współdzielonych. Wówczas wystarczy ponowne uruchomienie systemu (niektóre menedżery pakietów mogą same to zasugerować) i wszystko powinno wrócić do normy. Ta sama reguła tyczy się również jądra. Tutaj warto wspomnieć o różnicy między Uniksami a innymi systemami. W przypadku Uniksów, aktualizacja jest świadomym działaniem administracyjnym, wymagającym rozwagi oraz przemyślenia idących za nimi konsekwencji.
Rozwiązanie bibliotek współdzielonych nie jest rozwiązaniem bez wad. Biblioteki współużytkowane mają nieco bardziej złożony proces obsługi, a i sam proces kompilacji staje się nieco bardziej skomplikowanym zadaniem. Chcąc wykorzystywać biblioteki w swoich programach musimy odpowiedzieć sobie na kilka pytań:
- Jakich bibliotek współużytkowanych potrzebuje nasz program i jak to sprawdzić?
- W jaki sposób wyszukiwane są tego typu biblioteki?
- W jaki sposób należy dokonać konsolidacji z bibliotekami współdzielonymi?
- Jakie mogą wyniknąć problemy podczas używania tych bibliotek i jak ich uniknąć?
Wypisywanie zależności od bibliotek współużytkowanych
Omawiajac kwestię bibliotek wspominalśmy miejsce ich położenia w systemie. We współczesnych dystrybucjach katalog /lib jest dowiązaniem symbolicznym do /usr/lib. Nie uświadczymy również zbyt wielu bibliotek statycznych. Większość znich to do biblioteki współdzielone, których pliki mają rozszerzenie .so. Tego typu plików nawet w małym systemie, może być klikadziesiąt. Aby ustalić z jakich bibliotek korzysta konkretny program możemy użyć polecnia ldd.
xf0r3m@vm-2339495:~/C$ ls a.out aux.c aux.o hello hello.c main.c main.o myprog xf0r3m@vm-2339495:~/C$ ldd myprog linux-vdso.so.1 (0x00007fffc13dd000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1a32d99000) /lib64/ld-linux-x86-64.so.2 (0x00007f1a32f94000)
Polecenie ldd
wymaga argumentu w
postaci wskazania pliku wykonywalnego. Ten program zwróci nam
wszystkie zależność od bibliotek podanego programu. Jak możemy
zauważyć na powyższym przykładzie mimo tak prostego programu,
linker dołacza standardową bibliotekę języka C, konsolidator
dynamiczny
(ld-linux-x86-64.so.2
,
opisany poniżej) oraz
bibliotekę ułatwiającą wykonywanie wywołań systemowych
(linux-vdso.so.1
).
Podczas konsolidacji nie podaje się pełnych ścieżek do bibliotek, aby
zachować elastyczność. Zazwyczaj dostępne będą jedynie nazwy. Za
odnalezienie bibliotek w systemie odpowiada właśnie
konsolidator dynamiczny - ld.so
(/lib64/ld-linux-x86-64.so.2
).
Przyjrzmy się linii standardowej biblioteki języka C. To co znajduje
się po lewej stronie operatora (=>
)
zostało odczytane z programu, natomiast to co znajduje się po
po prawej stronie operatora, jest wynikiem pracy właśnie
konsolidatora dynamicznego.
Odnajdywanie przez ld.so bibliotek współużytkowanych
Program ld.so posiada wewnątrz wstępnie skonfigurowaną ścieżkę wyszukiwania bibliotek. Do utworzenia tej wykorzystywana jest tzw. składnica systemowa - w pliku /etc/ld.so.cache. Zawiera ona informacje o bibliotekach współdzielonych i ich lokalizacji. Przyczym aby uwzględnić taką lokalizację należy podać ścieżkę do katalogu z bibliotekami w pliku konfiguracjnym składnicy - /etc/ld.so.conf lub w plikach wewnątrz katalogu /etc/ld.so.conf.d. Zawartość tego pliku zazwyczaj składa się ze ścieżek wskazujących na katalogi jakie należy dodać do składnicy. Poniżej znajduje się jeden z plików w katalogu /etc/ld.so.conf.d, zawierający listę ścieżek
xf0r3m@vm-2339495:~/C$ cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf # Multiarch support /usr/local/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
Standardowe lokalizacje takie /usr/lib czy /lib, są domyślne i nie ma potrzeby ich dodawania do składnicy. Każda zmiana w pliku konfiguracyjnym wymaga przebudowania pliku samej składnicy, a dokonać tego możemy za pomocą poniższego polecenia:
xf0r3m@vm-2339495:~/C$ sudo ldconfig -v [sudo] hasło użytkownika xf0r3m: ldconfig: Nie można wykonać stat na /usr/local/lib/x86_64-linux-gnu: Nie ma takiego pliku ani katalogu ldconfig: Ścieżka `/usr/lib/x86_64-linux-gnu' podana więcej niż raz (od /etc/ld.so.conf.d/x86_64-linux-gnu.conf:4 i /etc/ld.so.conf.d/x86_64-linux-gnu.conf:3) ldconfig: Ścieżka `/lib/x86_64-linux-gnu' podana więcej niż raz (od:0 i /etc/ld.so.conf.d/x86_64-linux-gnu.conf:3) ldconfig: Ścieżka `/usr/lib/x86_64-linux-gnu' podana więcej niż raz (od :0 i /etc/ld.so.conf.d/x86_64-linux-gnu.conf:3) ldconfig: Ścieżka `/usr/lib' podana więcej niż raz (od :0 i :0) /usr/lib/x86_64-linux-gnu/libfakeroot: (od /etc/ld.so.conf.d/fakeroot-x86_64-linux-gnu.conf:1) libfakeroot-0.so -> libfakeroot-tcp.so /usr/local/lib: (od /etc/ld.so.conf.d/libc.conf:2) /lib/x86_64-linux-gnu: (od /etc/ld.so.conf.d/x86_64-linux-gnu.conf:3) libzxcvbn.so.0 -> libzxcvbn.so.0.0.0 libzvbi.so.0 -> libzvbi.so.0.13.2 libzvbi-chains.so.0 -> libzvbi-chains.so.0.0.0 libzstd.so.1 -> libzstd.so.1.5.5 libzmq.so.5 -> libzmq.so.5.2.5 libzmf-0.0.so.0 -> libzmf-0.0.so.0.0.2 libzix-0.so.0 -> libzix-0.so.0.4.2 libzimg.so.2 -> libzimg.so.2.0.0 libzbar.so.0 -> libzbar.so.0.3.0 libz3.so.4 -> libz3.so.4
Opcja -v
powoduje wyświetlenie
bardziej szczegółowych komunikatów.
Program ld.so może wykorzystać jeszcze jedno miejsce w celu znalezienia informacji na temat gdzie mogą znajdować się biblioteki - zmienną LD_LIBRARY_PATH. Nie we wszystkich systemach jest ona zdefiniowana i może zostać wykorzystana do wskazania niestandardowych bibliotek tuż przed uruchomieniem programu.
Nie ma co za bardzo skupiać się na dodawaniu ścieżek do składnicy. Wprowadzone przez nas zmiany mogą doprowadzić do chaosu w systemie i powodować konflikty. Jeśli już musimy uzyć jakiejś niestandardowej biblioteki to należy już podczas kompilacji zadeklarować ją oraz jej lokalizację (w podrozdziale poniżej, pokazano jak to zrobić).
Konsolidacja programów z bibliotekami współużytkowanymi
Jeśli w kompilowanym programie używaliśmy jakiś niestandardowych bibliotek współużytkowanych, których próżno szukać w zasobach systemowych musimy podczas konsolidacji wskazać scieżkę oraz nazwę biblioteki podczas uruchamiania kompilatora (tak jak robiliśmy to wcześniej - w przypadku bibliotek statycznych). Tutaj niezbędne będzie również wskazanie katalogu w celu dodania go do ścieżki wyszukiwania bibliotek. W tego typu przypadkach nie należy posługiwać się ustawieniami konsolidatora dynamicznego.
Opcja, o której mowa to:-Wl,-rpath
.
Przykład poniżej może to zobrazować:
xf0r3m@laptop-5cfe659:~$ cc -o myprog myprog.o -Wl,rpath=/opt/pt/lib -L/opt/pt/lib -lnetstats
Domyślny standardem dla plików wykonywalnych oraz bibliotek w dystrybucjach Linuksa jest ELF - Executable and Linkable Format, dzięki czemu możemy zmienić ścieżkę wyszukiwania bibliotek dla środowiska wykonawczego. Możemy tego dokonać za pomocą polecenia patchelf.
Problemy związane z bibliotekami współdzielonymi
Najczęściej występującym problemem związanym z bibliotekami współdzielonymi jest ich potencjalny brak w naszym systemie. Wówczas musimy szukać pakietów z końcówką -dev w nazwie i ześmiecać system gigabajtami niepotrzebnych plików.
Kolejną rzeczą może być, za długa wartość zmiennej
LD_LIBRARY_PATH
. Zawartość tej
zmiennej jest odczytywana przez konsolidator, który następnie
przeszukuje podane ścieżki pod kątem występowania bibliotek. Ta
zmiana ma pierwszeństwo przez pozostałymi lokalizacjami, w których
mogą wystąpić biblioteki w systemie. Dlatego też ustawienie jej
globalnie gdzieś w systemie (np. w plikach konfiguracyjnych powłoki)
może spowodować znaczy spadek wydajności systemu. Jeśli już musimy
wskazać inną lokalizację bibliotek dla programów a nie chcemy
ponownie kompilować (lub nie mamy takiej możliwości, brak kodu
źródłowego), możemy utworzyć tzw. skrypt opakowywujący -
manipulujący zawartością zmiennej
LD_LIBRARY_PATH
oraz uruchamiający
żądany program. Przy czym zmienna zostanie zmieniona wyłącznie
w obrębie procesu podpowłoki uruchomionego na potrzeby skryptu
(Więcej o skryptach znajduje się w dodatku A).
Nie mniej jednak najlepszą ucieczką przez problemami związanymi z bibliotekami jest ich wskazanie w procesie konsolidacji lub użycie bibliotek statycznych.
14.1.4. Pliki nagłówkowe
Aby programiści mogi skorzystać z bibliotek w swoich programach muszą
się do nich w jakiś sposób odwołać. W języku C mogą wykorzystać do
tego pliki nagłówkowe zawierający deklaracje typów
oraz funkcji bibliotecznych. Jednym z takich plików, był stdio.h
przedstawiany we wcześniejszych przykładach - ten plik nagłówkowy
udostępniał nam funkcję printf()
.
Do wskazywania plików nagłówkowych służy dyrektywa
#include
. Czasami zdarza się, że
programiści dodają omyłkowo jakiś plik nagłówkowy, który nie
istnieje. Tego typu błędy zostają wyłapane już na poziomie kompilacji
gdzie kod jest wstępnie analizowany.
Oczywiście, może tak być, że wykorzystywane przez programistów pliki nagłówkowe nie znajdują się w standardowej lokalizacji dla tego rozdzaju plików w systemie (/usr/include). Jeśli tak jest to, możemy skorzystać z opcji kompliatora pozwalającą dodać podany katalog do ścieżki wyszukiwania plików nagłówkowych.
xf0r3m@laptop-5cfe659:~$ cc -c -I/opt/pt/include netstats.c
Istnieją dwie metody na dołączenie plików nagłówkowych do kodu programu. Różnice te są dość subtelne jednak mają dość duże znaczenie.
#include <stdio.h>
Klasyczne dołączenie pliku nagłówkowego. Mówi on nam, że kompilator będzie przeszukiwać zasoby systemowego, aby użyć tego pliku na potrzeby kompilacji.
#include "ip.h"
Użycie podwójnych apostrofofów zamiast ostych nawiasów, spowoduje że kompilator przeszuka katalog z podanym kodem źródłowym w poszukiwaniu tak załączonego pliku nagłówkowego. Tak załączone pliki nagłówkowe muszą występować w tym samym folderze do kompilowany kod źródłowy. Jest to przydatne w przypadku, gdy korzystamy z własnych plików tego rodzaju.
Preprocesor języka C
Wcześnie mówiliśmy o kompilatorze w kontekście odnajdywania plików nagłówkowych, jednak nie jest do dokońca prawdą. Preprocesor w przypadku języka C, zajmuje się dostoswaniem kodu pisanego przez człowiek dla kompilatora. Wykonuje on swoje działania na podstawie dyrektyw zapisywanych przez ludzi w kodzie dodając do niego kilka uproszczeń oraz skrótów.
Dyrektywy to nic innego jak instrukcje. Jednak, aby odróżniały się
do instrukcji właściwego języka w C, poprzedza się znakiem
krzyżyka (#). Taką dyrektywę poznaliśmy już, a jest
nią dyrektywa #include
. W języku C
dostępne są trzy rodzaje dyrektyw:
- Dołaczenia plików nagłówkowych - #include - nakazuja preprocesorowi dołaczyć do kodu źródłowego cały plik nagłówkowy.
- Makra - #define - makra nakazują
zmianę w kodzie nazwy makra np. PI na jej wartość np.
3.14. Konwencja mówi, że nazwy makr zapisujemy wielkimi
literami i nie należy przesadzać z ich stosowaniem. Nie są one
również tożsame ze stałymi, które możemy znać innych języków
programowania. Przykładem makra może być np.:
#define PI 3.14
- Kompilacja warunkowa - #ifdef, #if, #endif - zwane rownież dyrektywami warunkowymi. Pozwalaja one kontrolować kompilację kodu źródłowego na podstawie występowania makr w preprocesorze (dyrektywa #ifdef) lub niezerowego warunku (dyrektywa #if). W przypadku gdy warunek nie jest spełniony, kod źródłowy między dyrektywami #ifdef oraz #if a #endif nie zostanie w ogóle skompilowany.
Istotnym czynnikiem w wykorzystaniu makr a co za tym idzie kompilacji warunkowej jest możliwość przekazania makra do preprocesora za pomocą opcji -D kompilatora. Taka możliwość interakcji, na pewno bardziej wyjaśnia sens istnienia dyrektyw oraz samego preprocesora.
Odnośnie samego preprocesora, to nie zna on żadnych elementów języka C. Jest on skupiony wyłącznie na makrach oraz pozostałych dyrektywach.
14.2. Narzędzie make
Do tej pory kompilacja składała się maksymalnie z dwóch plików i nie obejmowała żadnych bibliotek. Przy takich projektach kompilacją była szybka i przyjmna. Jednak w 99% przypadków z jakim możemy spotkać się raczej tak nie będzie folder będą zawierać podkatalogi a w nich sterty plików ręczna kompilacja, przy nie których mogła by zająć tygodnie jak nie lata. Dlatego też powstało narzędzie typu make. Narzędzie make ma za zadanie zarządzać procesem kompilacji. Mimo, że jest to potężny program to jest on dość prosty w działaniu. Jeśli gdzieś w paczkach z kodem znajdziemy plik makefile lub Makefile oznacza to możemy użyć programu make do kompilacji projektu.
Działanie make polega na celu jaki chcemy osiągnąć, gdy je uruchamiamy. Takim celem najczęściej jest pliki wykonywalny, ale mogą to być również inne byty programistczne takie jaki pliki obiektowe. Same cele mogą się rozgałęziać być uzależnione od wyników działania innych celów. Cele tego rodzaju nazwyamy zależnościami.
W czasie wykonywania celów, narzędzie postępuje zgodnie z regułą (ang. rule), która może np. określać sposób w jaki kod źródłowy ma zostać zmieniony na plik obiektowy. Sam make posiada już zdefiniowane reguły, ale możemy je dostosowywać do własnych potrzeb jak i również tworzyć własne.
14.2.1. Przykładowy plik Makefile
Na podstawie dwóch plików z poprzedniego podrozdziału utworzyłem przykładowy plik Makefile. Plik ten tworzy znany nam rownież plik myprog, który nie robi nic więcej poza wyświetleniem napisu Hello, World!.
xf0r3m@laptop-7bf2993:~/prog/C$ cat Makefile OBJS=aux.o main.o all: myprog myprog: $(OBJS) $(CC) -o myprog $(OBJS)
W pierwszej linii znajduje się definicja makra, która w tym
przypadku wskazuje na dwie nazwy plików obiektowych
(OBJS=aux.o main.o
). W następnej
linii znajduje się bowiem reguła
(all:
) reguły jak już wcześniej
wspomniano określają sposób w jaki ma zostać zbudowany cel. W tym
przypadku reguła all:
wskazuje
mogłoby się wydawać, że na nasz plik końcowy. Jest to poczęści
prawda, ale zwróćmy uwagę na to, że poza
myprog
nie znajduje się nic innego.
W jaki sposób narzędzie make ma wiedzieć jak zbudować ten
ten program. Odpowiedź kryje się w linii niżej, bowiem
myprog
jest celem,
a co za tym idzie zależnością dla
all:
. W przypadku celu
myprog:
widzimy już jakieś konkrety:
pierwszym jest odwołanie się do makra, jest ono rozwiązywane do
nazw zapisanych na początku pliku Makefile, w pliku nie
znajduje się żadne inne elementy systemu Make o tej nazwie
tak więc ten cel jest od tych plików uzależniony. Warto dodać, że
Make zakłada, że plki źródłowe znajdują się w tym samym
katalogu co pliki Makefile. Uruchomienie polecenia
make w tym katalogu prezentuje się następująco.
xf0r3m@laptop-7bf2993:~/prog/C$ make cc -c -o aux.o aux.c cc -c -o main.o main.c cc -o myprog aux.o main.o
Poza tym co sami zapisaliśmy w pliku Makefile to nie wszystkie czynności jakie są wykonywane przez system Make. W Makefile użyliśmy plików obiektowych, zatem skąd narzędzie ma wiedzieć o tym, że ma wykorzystać pliki kodu źródłowego (.c), aby utworzyć pliki obiektowe (.o). Za tego typu czynności odpowiadają reguły wbudowane. Przez co osoby zajmujące się utworzeniem plików Makefile mogą od razu operować na właściwych plikach.
Ostatnia linia pliku Makefile jest odpowiedzialna za
zbudowanie właściwego pliku wykonywalnego naszego programu.
Po wcześniejszym przygotowaniu plików obiektowych z plików
źródłowych odwołanie się do makra
$(OBJS)
staje się zwykłym
podstawieniem argumentów do polecenia. Ta linia jest zwykłym
poleceniem utworzonym na podstawie makr pliku Makefile.
Każde polecenie w pliku Makefile musi zostać
wprowadzone w nowej linii i poprzedzone
znakiem tabulacji równym czterem znakom spacji.
Makro $(CC)
przechowywuje nazwę programu kompilatora C, w wiekszości przypadku
jej wartość będzie ustawiona na cc.
Jednym z najczęstszych błędów, jakie możemy doświadczyć na początku tworzenia plików Makefile jest:
xf0r3m@laptop-7bf2993:~/prog/C$ make Makefile:5: *** brakujący separator. Stop.
Wynika to najczęściej ze złego ustawienia szerokości tabulacji w naszym edytorze. Ja korzystam z 2 spacji na 1 tab, ze względu na wytyczne Makefile musiałem zmienić swoje ustawienia.
14.2.2. Aktualizacjia zależności
Program make został zaprojektowany w taki sposób aby do realizacji określonego celu była wymagana jak najmniejsza ilość kroków. Tak więc nie uświadczymy ponownego budowania programu w momencie ponownego wydania polecenia make. Aczkolwiek jedną z zasad systemu Make jest to, że cele zawsze powinny być budowane wraz ze swoimi zależnościami. Taki komunikat otrzymamy gdy wydamy polecenie make jeszcze raz.
xf0r3m@laptop-7bf2993:~/prog/C$ make make: Nie ma nic do zrobienia w 'all'.
Natomiast jeśli chociażby zmienimy czas modyfikacji, któregoś z plików kodu źródłowego za pomocą prostego polecenia touch. Wówczas plik obiektowy będzie starszy niż plik źródłowy i Make dokona budowy naszego programu ponownie.
xf0r3m@laptop-7bf2993:~/prog/C$ touch aux.c xf0r3m@laptop-7bf2993:~/prog/C$ make cc -c -o aux.o aux.c cc -o myprog aux.o main.o
Z tą róźnicą, że zaktualizowany zostanie wyłącznie ten plik, który został zmieniony oraz ponownie zostanie zbudowany końcowy plik wykonywalny.
14.2.3. Argumenty i opcje wiersza poleceń programu make
Uzupełniając wywołanie programu make o różne opcje oraz argumenty możemy zmusić go do wykonani kilku przydatnych czynności. Najprostszym przykładem może być zmiana wartości makra CC, wskazując inny niż domyślny kompilator.
$ make CC=clang
Tak utworzone makra mogą być przydatne w trakcie testów. Szczególnie takie jak CFLAGS oraz LDFLAGS. Innym przypadkiem może być użycie programu make dla najprostrzych programów wówczas podajemy nazwe pliku bez rozszerzenia jako argument, a program zbuduje nam końcowy plik wynikowy o podanej nazwe. Zwróćmy uwagę, że nie ma potrzeby przygotowania pliku Makefile.
$ make foo cc foo.o -o foo
Tego typu uruchmianie narzędzia make bedzie sprawdzać się w przypadku programów zapisanych w jednym pliku lub programów, w których jezykiem nie jest C. Jest to dobre rozwiązanie gdy dopiero rozpoczynamy pracę z danym językiem i nie wiem jeszcze jak działa kompilator czy inne narzędzia z nim związane. Jeśli nawet zbudowanie celu się niepowiedzie to i tak otrzymamy informację zwrotną co jest nie tak.
Po za zmianami makr, możemy wykorzystać kilka przydatnych opcji takich jak:
- -n - wypisuje polecenia wymagane do zbudowania programu, ale nie pozwala na ich wykonanie (taki dry-run).
- -f plik - nakazuje narzędziu make odczytać dane z podane pliku zamiast z pliku Makefile/makefile.
14.2.4. Standardowe makra i zmienne
System make posiada wiele predefiniowanych zmiennych oraz makr. Sama różnica między makrami i zmiennymi jest trudna do określenia. Dlatego też przyjęło się, że makra są niezmienne przez cały okres trwania jakiegoś procesu. W tym przypadku nazwiemy tak wszystko co nie zmieni się od momemntu rozpoczęcia procesu budowania celów zawartych w pliku Makefile.
Najczęsciej wykorzystywane makra to między innymi:
- CFLAGS - opcje kompilatora C. Make przekazuje je kompilatorowi w czasie tworzenia kodu obiektowego z plików .c.
- LDFLAGS - opcje przekazywane do konsolidatora w czasie tworzenia pliku wykonywalnego z plików obiektowych.
- LDLIBS - w przypadku gdy niechcemy łączyć opcji nazw bibliotek ze ścieżką wyszukiwania zapisaną w makrze LDFLAGS, to można umieszcząć je w tym makrze.
- CC - nazwa kompilatora języka C. Domyślnie jest to cc.
- CPPFLAGS - opcje preprocesora języka C. Jeżeli w którymś momencie system make będzie wywoływał preprocesor, to przekaże mu podane w tym makrze opcje.
- CXXFLAGS - opcje kompilatora języka C++.
Poza makrami w narzędziu make możemy korzystać ze zmiennych. Zmienne mogą zmienić się podczas budowania zdefiowanych celów w Makefile. Najczęściej jednak będziemy się spotykać ze zmiennymi zdefiniowanymi automatycznie w obrębie reguł celów. Oto trzy z nich:
- $@ - wewnątrz reguły, zmienna opisuje nazwę jej celu.
- $< - wewnątrz reguły, zmienna opisuje pierwszą zależność celu.
- $* - zmienna rozwijana jest w nazwę podstawową aktualnego celu. Wczasie tworzenia celu foo.o, ta zmienna przechowuje nazwę foo.
14.2.5. Typowe cele kompilacji
W plikach makefile odpowiedzialnych za pokierowanie systemu make faktycznych programów użytkowych możemy spotkać wiele innych celów realizujących zadania niekoniecznie związane z kompilacją. Są one predefiniowane przez make i oto kilka z nich:
- clean - ten cel usuwa wszystkie pliki obiektowe oraz pliki wykonywalne, dzięki temu będziemy mogli rozpocząć proces kompilacji od nowa.
- distclean - cel podobny do clean, ale usuwa on wszystko co nie jest częścią oryginalnej dystrybucji w tym i plik makefile.
- install - ten cel powoduje skopiowanie wszystkich plików, które w Makefile zostały oznaczone jako nięzbędne do zainstalowania w systemie. Operacja jest uznawana za dość niebezpieczną warto w tym przypadku zastosować opcję -n, która pozowoli nam zobaczyć co dokładnie będzie kopiowane do naszego systemu.
- test lub check - cel pozwala sprawdzić czy po skompilowaniu wszystko działa jak należy.
- depend - cel ten tworzy zależności wywołując kompilator ze specjalną opcją -M nakazującą mu sprawdzenie kodu. Cel ten nie jest już powszechnie używany, aczkolwiek mogą zdarzyć się projekty, które będą wymagać wywołanie tego celu.
- all - najczęściej pierwszy cel w pliku Makefile. W wielu projektach będziemy spotykać się z potrzebą wywołanie tego celu, a nie samego pliku wykonywalnego. Wiele projektów składa się z wielu plików wykonywalnych i często nie ma tego jednego głównego. Natomiast użycie celu all pozwala na zbudowanie ich wszystkich w trakcie jednej czynności.
14.2.6. Organizowanie pliku Makefile
Istnieje wiele róznych stylów tworzenia plików Makefile, mimo tego stosowanych jest kilka ogólnych zasad. W pierwszej częsci pliku, najcześciej spotykane są (jako definiecje makr) opcje bibliotek oraz plików nagłówkowych po grupowanych wg. określonych pakietów.
MYPACKAGE_INCLUDES=-I/usr/local/include/mypackage MYPACKAGE_LIB=-L/usr/local/lib/mypackage -lmypackage PNG_INCLUDES=-I/usr/local/include PNG_LIB=-L/usr/local/lib -lpng
Następnie zapisywane są makra z opcjami kompilatora oraz opcje konsolidatora każde z opcji zapisywane jest w osobnym makrze.
CFLAGS=$(CFLAGS) $(X_INCLUDES) $(PNG_INCLUDES) LDFLAGS=$(LDFLAGS) $(X_LIB) $(PNG_LIB)
Po tych opcjach zapisywana jest hierarchia makr opisujących pliki obiektowe. Hierarchia ze względu na zależności.
UTIL_OBJS=util.o BORING_OBJS=$(UTIL_OBJS) boring.o TRITE_OBJS=$(UTIL_OBJS) trite.o PROGS=boring trite
W pozostałej częsci plików występują już cele, które składają główne pliki wykonywalne. Jeśli trzeba przygować regułę dla pliku obiektowego należy ją umieścić zaraz nad regułą programu pliku wykonywalnego. Jeśli z tego pliku obiektowego korzysta więcej plików wykonywalnych niż ten jednen to trzeba je przenieść nad nie wszystkie.
14.3. Lex i Yacc
Programy odczytujące pliki konfiguracyjne lub polecenia korzystają z takich narzędzi jak Lex oraz Yacc. Także możemy spotkać się z nimi podczas budowania niektórych projektów.
- Program Lex jest analizatorem leksykalnym, jego zadaniem jest przekształcenie tekstu w specjalne znaczniki opisane etykietami. Dostępną w dystrybucjach Linuksa implementacja programu Lex nosi nazwę flex. Korzystając tego programu będziemy musieli użyć opcji konsolidatora takich jak: -ll lub -lfl.
- Program Yacc jest to analizator składniowy. Jego zadaniem jest odczyt składni zgodnie z wcześniej ustaloną gramatyką. W dystrybucjach Linuksa dostępna jest bison, który może być kompatybilny z programem yacc w momencie gdy użyjemy opcji -y, natomiast dla konsolidatora będziemy musieli użyć opcji -ly.
14.4. Języki skryptowe
Kiedyś poza obsługą powłoki oraz programu awk użytkownik nie musiał znać żadanych innych skryptowych języków programowania, zmieniło się to wraz z rozpowszechnieniem się takich języków jak Perl a w poźniejszych latach Python. Niektóre z narzędzi systemowych zostały przepisane z klasycznego C do np. Pythona - na przykład narzędziem whois. Języki skryptowe obecnie przechylają szalę na swoją korzyść względem języków kompilowanych, mimo to zalety takich języków jak C oraz wpisanie się tego języka w rdzeń nauk komputerowych nie sposowoduje zaprzestania jego używania nawet w ciągu kilku następnych dziesięcioleci. Język ten jest językiem niskopoziomowym, wymagającym abstrakcyjnego myślenia i dość trudnym w jego bardziej zaawansowanym zastosowaniu, dlatego też większość młodszych programistów skupia się na językach skryptowych, w których można osiągnać podobne rezultaty mniejszym kosztem.
W większości przypadków skrypty rozpoczynają się od wskazania interpretera poniższego skryptu. Takie wskaznie rozpoczyna się od od znaków #! (każdy plik tekstowy rozpoczynający się tych znaków uznawany jest za skrypt), po których bez przerwy występuje ścieżka wskazująca na właściwy program interpretera danego języka. Dla języka Perl, wskazanie interpretera prezentuje się w następujący sposób:
#!/usr/bin/perl -w
Natomiast dla języka Python wygląda to następująco:
#!/usr/bin/env python
W systemie może być dostępnych kilka wersji Pythona. Za pomocą tego rozdzaju wskazania wybieramy pierwszą wersję, która znajduje się na ścieżce wyszukiwania poleceń. Inną metodą na wskazanie interpretera Pythona jest: #!/usr/bin/python3, wskazująca konkretnie na jedną z 3 wersji Pythona, obecnie 3.12.
Opiszemy sobie teraz kilka ważniejszych języków skryptowych.
- Python - Python zapewnia nam bogaty zestaw funkcji pozwalających rozwiązać każdy problem związany np. z przetwarzeniem tesktu, obsługą baz danych czy sieci. Dostarcza on interaktywną powłokę, w które możemy linia po lini testować nasze rozwiązania. Język ten posiada on dość dobry model obiektowy. Jego zakres zastosowań jest obecnie bardzo szeroki, podobny do języka C, może poza programowaniem układowym oraz architekturą jąder systemowych czy sterowników urządzeń. Jest on prosty do nauki o ile nie posiada się naleciałości z innych języków takich jak np. C.
- Perl - język skrytowy stworzony dla systemów Uniksowych. Język ten ma ogromne możliwości porównywalne z Pythonem, jednak nie jest on taki prosty w użyciu. Jego głównym zadaniem jest obróbka tekstu. Obecnie wiele narzędzi wykorzystuje jego możliwości np. znany wszystkim system kontroli wersji - Git.
- PHP - jest językiem służacym do dynamicznego tworzenie treści na stronach internetowych. Można wykorzystać go do generowania stron hipertekstowych oraz do modyfikacji baz danych SQL.
Innym wartym wspomnienia językiem jest m4 odpowiedzialny z przetwarzanie makr. Dostępny jest on w systemie GNU autotools. Bierze udział w automatycznym generowaniu pliku Makefile (Wiecej w 15 rozdziale).
14.5. Język Java
Java podobnie do języka C jest językiem kompilowanym. Zapewnia on prostszą składnie oraz duże możliwości progrowamowania obiektowego. Dawniej służył on jako środowisko dla aplikacji internetowych, jednak obecnie zmieniło się na rzecz interpretowanego przez przeglądarki JavaScriptu. Nie mniej jednak możemy spotkać się z aplikacją Javy na na dystrybucjach Linuksa lub z potrzebą uruchomienia takowej. Dlatego też warto się zapoznać, chociaż w podstawowym stopniu z zapoznać się z obsługą projektów w tworzonych w tym języku.
Java może być kompilowana w dwojaki sposób. Kompilator może przekształcić kod źródłowy do kodu maszynowego zgodnego z naszym systemem (tak samo, jak w przypadku języka C) lub może przetworzyć kod źródłowy na tzw. kod bajtowy interpretowany poźniej przez wirtualną maszynę Javy (interpreter). W Uniksach najczęściej będziemy się spotykać z kodem bajtowym - pliki z rozszerzeniem .class. Wszystkie niezbędne uruchomienia narzędzia znajdują się w pakiecie zwanym środowiskiem uruchomieniowym Javy (JRE). Jeśli mamy go zainstalowanego w systemie to do uruchomienia wystarczy wydać poniższe polecenie:
$ java file.class
Kod bajtowy może również występować w postaci skompresowanej, wówczas będziemy mieć doczynienia z plikami z rozszerzeniem .jar. Je również możemy uruchomić w podobny sposób jak zwykły kod bajtowy wystarczy użyć opcji -jar, tak jak pokazano to na poniższym przykładzie.
$ java -jar file.jar
Czasami do wykorzystania programów Java będzie potrzebne zdefiniowanie dwóch zmiennych JAVA_HOME zawierającej ścieżkę do katalogu z instalacją JRE. Drugą zmienną jest CLASSPATH, zmienna ta zawiera nazwy katalogów przechowywujących pliki kodu bajtowego, których dany program może wymagać.
Jesli zajdzie taka potrzeba, aby skompilować kod Javy to w zestaw narzędzi programistycznych JDK wchodzi w skład kompilator javac. Uruchmiamy go podobnie do interpretera. W skład JDK wchodzi również polecenie jar, które może kompresować kod bajtowy Javy.
$ javac file.java
15. Wprowadzenie do kompilacji oprogramowania kodu źródłowego C
Jedną z wielu zalet dystrybucji Linuksa jako systemu operacyjnego jest fakt, że większość dostępnego na niego oprogramowania jest nie tyle darmowa co rozprowadzana poprzez archiwa zawierające kod źródłowy danego programu. Każdy może sobie go pobrać, użyć, dostosować, a nawet redystrybuować z uznaniem pierwotnego autora, jednak ten aspekt jest już kwestią licencji na jakiej dostarczany jest on do użytkowników. W ten sposób prezentuje się idea open source - otwartych źródeł.
W zamierzchłych czasach gdy komputery znajdowały się w zasięgu uczelni wyższych, nawet oprogramowanie własnościowe było rozprowadzane przy użyciu kodu źródłowego. Dawniej chcać skorzystać z programu trzeba było wyposażyć się w zestaw narzędzi programistycznych aby móc w ogóle go uruchomić. Czy te czasy minęły? I tak i nie. Obecnie w wielu dystrybucjach częściej stosuje się paczki przygotowywane osoby związane z dystrybucją aniżeli archiwa kodu niezależnie rozprowadzane przez autora programu. Ma to oczywiście swoje wady oraz zalety. Tym rozdziale spróbujemy sobie przybliżyć w jaki sposób możemy zainstalować aplikację dostępną w postaci kodu źródłowego w naszym systemie.
15.1. Systemy do budowania oprogramowania
Jak pamietamy z poprzedniego rozdziału do automatycznego stworzenia pliku wykonywalnego dla naszej przykładowej aplikacji wykorzystaliśmy narzędzie make, narzędzie to wymaga pliku Makefile, w którym to zapisywane były cele, zależności czy różnego rodzaju makra. Jednym z systemów budowania oprogramowania dostępnym na Uniksy jest GNU autoconf. Zadaniem skryptów tego narzędzia jest przygotowanie plików Makefile na podstawie analizy docelowego systemu. System ten korzysta z istniejących już narzędzi takich jak make. Natomiast jeśli mieli byśmy opisać proces instalacji programów w C w Uniksach to wyglądałby on następująco:
- Rozpakowanie archiwum z kodem źródłowym.
- Konfigurowanie pakietu.
- Uruchomienie polecenie make lub innego polecenia służącego do kompilacji programów.
- Uruchomienie polecenie make install i instalacja programów.
15.2. Rozpakowywanie kodu źródłowego.
Oczywiście zdaje sobie sprawę, że chyba nie trzeba opisywać procesu rozpakowywania archiwów w Uniksach. Ten temat był już wałkowany na samym początku. Nie mniej jednak w przypadku kodu źródłowego, warto zachować pewną dozę niepewności. Otóż zadajmy sobie proste pytanie. Co odróżnia program przydatny od szkodliwego oprogramowania? Odpowiedź jest prosta. Czynności jakie on wykonuje. Na pewnej płaszczyźnie jedno i drugie to programy, które mogą być rozporowadzane w postaci kodu źrodłowego, który zostanie być może zostanie przez nas pobrany, skompilowany i zainstalowany w systemie. Najczęstszym tego typu oprogramowaniem są konie trojańskie. Dlatego też przed rozpakowaniem takiej paczki z kodem należy upewnić się co jest w środku. Opcja -t polecenia tar, pozwoli nam wyświetlić zawartość archiwum bez jego wcześniejszego rozpakowywania. Na co powinno się zwrócić uwagę? Otóż na taki drobiazg jak scieżka bezwzględna, przez co wypakowanie może nadpisać ważne systemowe pliki.
W samych archiwach możemy natknąć się takie pliki jak README czy INSTALL. Te pliki mogą zawierać ważne informacje dotyczące ustawień narzędzi programistycznych, a nawet cała instrukcję krok po kroku jak skompilować i zainstalować ten program w naszym systemie.
15.3. GNU autoconf
Jeśli kiedyś uczyliśmy się jezyka C, to wiemy że jest on przenośny między systemami, jednak między systemami operacyjnymi czy platformami sprzętowymi instnieje tak wiele różnic, że najczęściej użycie pojedynczego pliku Makefile nie wykonana poprawnie budowania programu. Rozważano opcję utworzenia dla każdej platformy osobny plik Makefile jednak. To wspomniany wcześniej GNU autoconf stał się rozwiązaniem tego problemu.
Projekty programistyczne przygotowane do współpracy z GNU autoconf zazwyczaj są dostarczane z plikami configure, Makefile.in oraz config.h.in. Pliki z rozszeniami .in są szablonami.
Natomiast sama koncepcja jest dość prosta. Polega ona na uruchomieniu pliku configure, który sprawdzi system i dokona odpowiednich podstawień w pliku Makefile.in, tworząc odpowiedni plik Makefile dla uruchomionego systemu oraz platformy sprzętowej. Uruchomienie skryptu następuje w bardzo prosty sposób.
$ ./configure
W czasie działania skryptu zostanie nam wyświetlona duża ilość komunikatów diagonstycznych. Jeśli działanie skryptu zakończy się powodzeniem, to wówczas zostanie wygenerowany co najmniej jeden pliki Makefile, config.h oraz config.cache. Dzięki czemu nie ma potrzeby przeprowadzania wszystkich testów ponownie w przypadku kolejnego uruchomienia skryptu.
15.3.1. Przykładowe użycie systemu GNU autoconf
Dla przykładu uruchomimy sobie testowe budowanie jakieś małego projektu, aby nabrać nieco praktyki w systemie GNU autoconf zanim przejdziemy do jego ustawień. Dla przykładu wybrałem pakiet GNU coreutils, zawierający narzędzia poznane w drugim rozdziale. Paczkę z kodem źródłowym możemy pobrać z adresu: http://ftp.gnu.org/gnu/coreutils/ Kod należy pobrać, rozpakować a następnie skonfigurować. Żeby nie robić bałaganu w używanym systemie ustawimy jako katalog docelowy jeden z podkatalogów w naszym katalogu domowym.
xf0r3m@laptop-d71f06f/ coreutils-9.5/ ./configure --prefix=$HOME/mycoreutils checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a race-free mkdir -p... /usr/bin/mkdir -p checking for gawk... no checking for mawk... mawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking whether make supports nested variables... (cached) yes checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out ... config.status: executing po-directories commands config.status: creating po/POTFILES config.status: creating po/Makefile
Po zakończeniu działania skryptu configure, zostaje wygenerowany na podstawie pliku Makefile.in właściwy dla naszego systemu plik Makefile. W tym wypadku nie pozostaje nam nic innego jak wydanie polecenia: make
xf0r3m@laptop-d71f06f/ coreutils-9.5/ make GEN lib/alloca.h GEN lib/arpa/inet.h GEN lib/configmake.h GEN lib/ctype.h GEN lib/dirent.h GEN lib/error.h GEN lib/fcntl.h ... CC lib/libcoreutils_a-copy-acl.o CC lib/libcoreutils_a-set-acl.o CC lib/libcoreutils_a-acl-errno-valid.o CC lib/libcoreutils_a-acl-internal.o CC lib/libcoreutils_a-get-permissions.o CC lib/libcoreutils_a-set-permissions.o ... CCLD src/chroot CC src/hostid.o CCLD src/hostid CC src/timeout.o CC src/operand2sig.o CCLD src/timeout CC src/nice.o CCLD src/nice CC src/who.o CCLD src/who CC src/users.o CCLD src/users CC src/pinky.o CCLD src/pinky ... GEN man/chroot.1 GEN man/hostid.1 GEN man/timeout.1 GEN man/nice.1 GEN man/who.1 GEN man/users.1 GEN man/pinky.1 GEN man/stty.1 ... make[2]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5' Making all in gnulib-tests make[2]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' ## ---------------------------------------------------- ## ## ------------------- Gnulib tests ------------------- ## ## You can ignore compiler warnings in this directory. ## ## ---------------------------------------------------- ## make all-recursive make[3]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' Making all in . make[4]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' CC bench_md5-bench-md5.o CC c-strcasestr.o CC c32tob.o CC ioctl.o CC localename.o ... make[4]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[3]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[2]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[1]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5'
Powyższy przykład może być nieco bardziej obszerny, ponieważ zamieściłem w nim fragmenty kolejnych etapów jakie wykonuje narzędzie make. Teoretycznie możemy już wydadwać polecenie: make install, ale na początku zobaczymy sobie jakie pliki będą chciały się zainstalować (uruchamiając tym samym polecenie make w trybie dry-run).
xf0r3m@laptop-d71f06f/ coreutils-9.5/ make -n install make install-recursive make[1]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5' ... dot_seen=no; \ target=`echo install-recursive | sed s/-recursive//`; \ case "install-recursive" in \ distclean-* | maintainer-clean-*) list='po . gnulib-tests' ;; \ *) list='po . gnulib-tests' ;; \ esac; \ for subdir in $list; do \ echo "Making $target in $subdir"; \ if test "$subdir" = "."; then \ dot_seen=yes; \ local_target="$target-am"; \ else \ local_target="$target"; \ fi; \ (CDPATH="${ZSH_VERSION+.}:" && cd $subdir && make $local_target) \ || eval $failcom; \ done; \ if test "$dot_seen" = "no"; then \ make "$target-am" || exit 1; \ fi; test -z "$fail" Making install in po make[2]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/po' ...
Dodanie opcji -n
do polecenia
make spowoduje wyświetlenie poleceń wykonywanych przez
narzędzie make do zrealizowania celu
install
. Po sprawdzeniu poleceń,
możemy wydać polecenie make install
.
xf0r3m@laptop-d71f06f/ coreutils-9.5/ make install make install-recursive make[1]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5' Making install in po make[2]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/po' installing af.gmo as /home/xf0r3m/mycoreutils/share/locale/af/LC_MESSAGES/coreutils.mo installing af.gmo link as /home/xf0r3m/mycoreutils/share/locale/af/LC_TIME/coreutils.mo installing be.gmo as /home/xf0r3m/mycoreutils/share/locale/be/LC_MESSAGES/coreutils.mo installing be.gmo link as /home/xf0r3m/mycoreutils/share/locale/be/LC_TIME/coreutils.mo installing bg.gmo as /home/xf0r3m/mycoreutils/share/locale/bg/LC_MESSAGES/coreutils.mo ... make[2]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/po' Making install in . make[2]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5' make[3]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5' /usr/bin/mkdir -p '/home/xf0r3m/mycoreutils/bin' src/ginstall -c src/ginstall '/home/xf0r3m/mycoreutils/bin/./install' src/ginstall -c src/chroot src/hostid src/timeout src/nice src/who src/users src/pinky src/stty ... make[4]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[5]: Entering directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[5]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[4]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[3]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[2]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5/gnulib-tests' make[1]: Leaving directory '/home/xf0r3m/Pobrane/coreutils-9.5'
Po podaniu za pomocą opcji --prefix miejsca docelowego dla programów, narzędzie make zainstalowało zbudowane pliki wykonywalne we wskazanym w opcji katalogu. A tak prezentuje się zawartość tego katalogu:
xf0r3m@laptop-d71f06f/ coreutils-9.5/ ls -l ~/mycoreutils/ razem 0 drwxr-xr-x 2 xf0r3m xf0r3m 2160 04-30 18:49 bin drwxr-xr-x 3 xf0r3m xf0r3m 60 04-30 18:49 libexec drwxr-xr-x 5 xf0r3m xf0r3m 100 04-30 18:49 share
15.3.2. Instalacja za pomocą narzędzia do tworzenia pakietów
Istnieje możliwość instalacji kompilowanego oprogramowania w taki sposób, aby można było je kontrolować za pomocą mendżera paketów dystrybucji. Dystrybucjami, w których najprościej jest wykonać to zadanie są te oparte na Debianie, ze względu na prostotę tworzenia pakietów .deb. W tym celu posłużymy się narzędziem checkinstall. Domyślnie może nie występować w naszym systemie, ale możliwe jest ono do zainstalowania z repozytorium. Na chwilę obecną to program zawiera błąd i aby móc zbudować pakiet należy podać opcję: --fstrans=0. Jeśli nie chcemy aby pakiet automatycznie instalował się w naszym systemie podajemy opcje: --install=no. Podczas tworzenia pakietu wymagane będzie podanie opisu naszęgo pakietu oraz sprawdzenie czy skrypt dobrze odczytał takie informacje jak nazwa pakietu, jego wersja czy docelowa architektura. Te informacje można poprawić. Po zatwierdzeniu tych informacji pakiet zostaje zbudowany. Po zakończeniu budowania będzie go można zainstalować za pomocą polecenia dpkg.
xf0r3m@vm-83a53d4/ coreutils-9.5/ checkinstall --install=no --fstrans=0
15.3.3. Opcje skyptu configure
Po za zaprezentowaną opcją --prefix, która zmienia katalog docelowy dla instalacji oprogramowania w systemie, skrypt configure posiada wiele innych przydatnych opcji. Sam skrypt posiada opcję --help, jednak informacji jest na tyle dużo, że cięzko znaleźć tę jedną potrzebną, dlatego poniżej zamieszczam kilka najważniejszych z nich:
- --bindir=katalog - pliki wykonywalne będą instalowane w podanym katalogu.
- --sbindir=katalog - programy systemowe będą instalowane w podanym katalogu.
- --libdir=katalog - biblioteki wykonywalne będą instalowane w podanym katalogu.
- --disable-shared - opcja ta zakazuje tworzenia bibliotek współdzielonych. Ta opcja może pomóc w zapobiegnięciu problemów z bibliotekami współdzielonymi.
- --with-pakiet=katalog - ta opcja służy do wskazania bibliotek zainstalowanych w niestandardowej lokalizacji. Jednak nie wszystkie skrypty rozpoznają te opcję.
15.3.4. Zmienne środowiskowe
Za pomocą skryptu configure możemy przekazać do narzędzia make makra dla kompilatora, konsolidatora czy preprocesora języka C. Skonfigurowanie zmiennych środowiskowych przed uruchomieniem skryptu daje nam możliwość dostosowywania kompilacji do własnych potrzeb.
Zmienne środowiskowe możemy podawać w tym samy wierszu polecenia przed uruchomieniem skryptu:
$ CPPFLAGS=-DDEBUG ./configure
Lub jako jego opcje:
$ ./configure CPPFLAGS=-DDEBUG
Na powyższych przykładach przedstawiłem zmienną środowiskową preprocesora, ale również dostępna jest LDFLAGS (zmienna środowiskowa konsolidatora) oraz CFLAGS (zmienna środowiskowa kompilatora). Ich wartości są identyczne jak opcje, które przekazywaliśmy do wyżej wymienionych programu - o czym warto pamiętać.
15.3.5. Cele tworzone przez GNU autoconf
Po za standardowymi celami takimi jak all czy install, skrypt configure w pliku Makefile tworzy kilka innych celów. Oto, niektóre z nich, część z nim została już opisana w poprzednim rozdziale.
- make clean
- make distclean
- make check
- make install-strip - ten cel działa podobnie standardowego make install, jednak usuwa tablice symboli oraz informacje dla debugera z utworzonych bibliotek oraz plików wykonywalnych. Zainstalowany w ten sposób pakiet oprogramowania zabiera mniej miejsca na dysku.
15.3.6. Plik dziennika systemu GNU autoconf
Jeśli podczas pracy skryptu configure zdarzy się coś nieprzewidzianego jakiś błąd lub problem, to wówczas możemy posłużyć się plikiem dziennika tworzonym podczas pracy skryptu. Plik zowie się config.log i zewzględu na to, że zawierać bedzie on wiele danych to najlepiej jest go przeglądać od końca (po otwarciu pliku za pomocą narzędzia less naciskamy G (Shift+g), przeniesie nas to na koniec pliku.).
15.3.7. Narzędzie pkg-config
Narzędzie pkg-config zostało zaprojektowane w celach rozprowadzania informacji o lokalizacji bibliotek, plików nagłówkowych, a także opcji wymaganych do poprawnego skompilowania i skonsolidowania programu. Składnia tego polecenie prezentuje się w następujący sposób:
$ pkg-config opcje pakiet1 pakiet2 ... pakietN
Przykładowo jeśli do konsolidacji naszego programu potrzebujemy dodać bibliotekę kompresującącą możemy wydać poniższe zapytanie. W odpowiedzi otrzymamy opcję, którą należy dodać do argumentów konsolidatora.
xf0r3m@laptop-69924a2/ ~/ pkg-config --libs zlib -lz
Za pomocą opcji --list-all możemy wyświetlić informacje na temat wszystkich bibliotek, o których informacje mogą być dostępne przez pkg-config, co już może sugerować nam, że nie wszystkie biblioteki są dostępne w jego zasobach.
xf0r3m@laptop-69924a2/ ~/ pkg-config --list-all blkid blkid - Block device id library dbus-1 dbus - Free desktop message bus egl EGL - EGL library and headers expat expat - expat XML parser form form - ncurses 6.4 add-on library formw formw - ncurses 6.4 add-on library geoclue-2.0 Geoclue - The Geoinformation Service ...
Działanie programu pkg-config opiera sie o pliki .pc, które mogą być rozprowadzane w pakietach naszej dystrybucji w momencie instalacji bibilioteki, oczywiście zarządca pakietu musi utworzyć taki plik. Polecenie przeszukuje katalog lib/pkg-config swojego przedrostka instalacji (prefiksu), jeśli np. jego prefiksem jest /usr, to będzie on przeszukiwać katalog /usr/lib/pkg-config.
Jeśli chcemy dodać do zasobów pkg-config bibliotekę o niestandardowym miejscu instalacji, to możemy wykorzystać do tego dwie metody. Pierwsza z nich jest zwykłą kopią pliku .pc do domyślnego katalogu narzędzia lub utworzenie dowiązania symbolicznego. Drugą opcją jest uzupełnienie zmiennej PKG_CONFIG_PATH, o dodatkowy katalog. Jednak to rozwiązanie niesprawdza się w obrębie całego systemu.
15.4. Zalety i wady ręcznej instalacji
Na poziomie podstawowym obsługi systemów Linuksowych, nikt nie pownien od was wymagać umiejętności ręcznej instalacji oprogramowania w systemie. Raczej będziemy polegać na tym co oferują nam repozytoria dystrybucji. Obecnie dostawcy różnych programów często przygotowują pakiety systemu zarządzania oprogramowaniem dla wielu wiodących dystrybucji, dlatego też ręczna instalacja spada do rangi pewnego rodzaju niszy. Budowanie może zająć naprawdę wiele czasu i nie zawsze mozemy osiągnąć żądany skutek. Chcemy skompilować jakiś program, okazuje się że brakuje nam bibliotek w takiej wersji, więc musimy przeprowadzić kompilację bibliotek w takiej wersji w ten sposób nasza praca związana tym zagadnieniem się nawarstwia. Weźmy pod uwagę też sprzęt. Nie każdy dysponuje potężną maszyną, która będzie wstanie kompilować duże programy w ciągu kilku minut. Większość użytkowników dystrybucji Linuksa, do których adresowany jest ten materiał, raczej korzysta z budżetowego laptopa z marketu. Ucieczka do MS Windows, w celu polepszenia swoich doświadczeń z komputerem, bez reklam czy najważniejszej rzeczy w tym systemie czyli aktualizacji. Tak więc kompilacja na takim sprzęcie może zająć dłuższą chwilę, co sprawia, że budowanie od podstaw oprogramowania traktowane jest raczej jako ostateczność.
Nie mniej jednak taka instalacja oprogramowania w systemie nie jest bez zalet. Najważniejszymi z nich jest dostarczenie do naszych systemów najnowszych wersji programów, dostosowanie ich możliwości oraz funkcjonalności przy użyciu opcjonalnych bibliotek czy samych opcji kompilacji do naszych potrzeb. Nawet lepiej, istnieją całe dystrybucje, które opierają się tylko i wyłącznie o ręczną instalację lub zawierają takie programy do zarządzania oprogramowaniem, które i tak koniec końców sprowadzania się do ręcznej instalacjacji przeprowadzonej w nieco bardziej bezobsługowy sposób.
Oczywiście zachęcam do ręcznej instalacji, tylu programów ilu się da. Dzięki temu uczymy się, poznajemy te programy. Domyślnym miejscem przeznaczonym na ręczną instalację jest katalog /usr/local.
15.5. Stosowanie poprawek
Poprawki są nieodłącznym elementem życia oprogramowania. Są one dystrybuowane za pomocą takich narzędzi jak system kontroli wersji Git. Mogą one wprowadzać szereg zmian do kodu źródłowego, poprawiać błedy lub uzupełaniać oprogramowanie o nowa funkcjonalność. Poprawki tworzone są zapomocą polecenia diff. Poniżej znajduje się nagłówek pliku poprawki wprowadzający zamiany, które obecnie czytasz do tego materiału.
diff --git a/articles/terminallog/Linux.Podstawy.html b/articles/terminallog/Linux.Podstawy.html index 464a32d..09c06a7 100644 --- a/articles/terminallog/Linux.Podstawy.html +++ b/articles/terminallog/Linux.Podstawy.html @@ -12597,6 +12597,212 @@
Wszysktie linie, które są dodatkowo wprowadzena oznaczne symbolem plusa (+) najczęściej koloru zielonego. Jeśli nasz terminal obsługuje kolory. Natomiast usunięcia oznaczane są za pomocą symbolu minusa (-) najczęściej koloru czerwonego. Linie z minusem (linie na czerwono) zawierają również treść, która występowała w pliku przed wprowadzeniem zmian. Warto mieć to na uwadze.
Pliki poprawek są tworzone za pomocą polecenia diff, natomiast instalowane są za pomocą polecenia patch. Przyczym podczas instalacji poprawek istotne jest dobre ustawie katalogu roboczego w folderze z kodem źródłowym. Załóżmy, że mamy plik poprawki, który obejmuje plik src/plik.c. Wówczas taki plik poprawki należy nałożyć w katalogu nadrzędnym katalogu src, wydając poniższe polecenie:
$ patch -p0 < plik_poprawki
Opcja -p0
usunie dokładnie 0
poprzedzających ukośników ze ścieżki odczytanej w poprawce aby
zlokalizować plik. Jest to tyle istotne, ponieważ mogą występować
różnice w lokalizacji instalacji na komputerze osoby tworzącej
taką poprawkę a jej stosującej. Rozważmy taki przykład, że pliku
poprawki widnieje taka ścieżka jak: patches-3.42/src/plik.c.
W naszej instalacji nie ma tego katalogu patches-3.42, to
aby zastosować poprawkę musimy przejść do katalogu zawierającego
katalog src i wydać następujące polecenie:
$ patch -p1 < plik_poprawki;
16. Wirtualizacja
Same pojęcie tego, że coś jest wirtualne odnosi się do translacji warstwy bazowej do postaci uproszczonego interfejsu, z którego może korzystać wielu odbiorców. Z pojęciem wirtualizacji zapoznaliśmy się pośrednio przy poznawaniu zagadnienia pamięci wirtualnej, oferującej procesom duży odizolowany obszar pamięci operacyjnej. W tym przpadku skupimy się nieco szerszym aspekcie możliwości wirtualizacyjnych pozwalających na tworzenie izolowanych środowisk umożliwiających uruchomienie wielu systemów operacyjnych bez powstawania konfliktu miedzy nimi.
16.1. Maszyny wirtualne
Maszyny wirtualne obejmują całość sprzętu komputerowego za pomocą oprogramowania. Wydzielane są zasoby fizycznego hosta oraz tworzone nowe urządzenia aby powstała zupełnie nowa maszyna. Z terminu maszyna wirtualna, korzysta się od wielu lat, dlatego też w celach rozróżnienia (przecież spotkaliśmy się z maszyną wirtualną Javy) dla maszyn tworzących izolowane środowiska zdolne do uruchamiania systemów operacyjnych, które podwzględem programowym nie różnią się niczym od fizycznych komputerów używa się sformuowania systemowa maszyna wirtualna.
Żeby tego było mało, to możemy wróżnić jeszcze maszyny oparte całkowicie na oprogramowaniu, czyli emulatory. Przy użyciu tego rozwiązania możemy uruchamiać np. programy nieprzeznaczone domyślnie na wykorzystywaną przez nas architekturę. Dla przykładu mamy obecnie dostępne emulatory konsol do gier czy smartfonów.
16.1.1 Hipernadzorcy
Programy pozwalające nam na kontrolowanie maszyn wirtualnych i wchodzenie w interakcje z nimi (chociażby po to, aby zainstalować system operacyjny) nazywane jest hipernadzorcą. Istnieją dwa typy oprogramowania hipernadzorcy. Z typem 2, każdy mógł mieć styczność są to takie środowiska virtualizacji jak Oracle VirtualBox czy VMware Player lub Workstation. Hipernadzorcami drugiego typu jest najczęściej oprogramowanie instalowane zawierające pełen pakiet wirtualizacji. Inaczej jest w przypadku typu 1, gdzie instalujemy na fizycznym komputerze specjalny system operacyjny pełniący rolę hipernadzorcy. Pozostają również rozwiązania zawieszone pomiędzy pierwszym a drugim typem, np. rozwiązanie KVM z dystrybucji Linuksa. Pozwala ono zamienić normalną dystrybucję w hipernadzorcę typu 1.
Po omówieniu roli hipernadzorcy, możemy wprowadzić odpowiednią nomenklaturę dla zagadnienia maszyn wirtualnych. Mianowicie komputer, który udostępnia maszyny wirtualne lub ma możliwość ich tworzenia nazywany jest hostem, natomiast maszyna wirtualna gościem.
16.1.2. Sprzęt maszyny wirtualnej
Istnieje wiel różnic między sprzętem fizycznym a tym z którego korzystają maszyny - wirtualny. Wynikają one najczęściej z umożliwienia gościom bardziej bezpośredniego dostępu do zasobów hosta. Wykorzystywany jest mechnizm parawirtualizacji, który pozwala na pomięcie sprzetu wirtualnego między składnikami wirtualizacji (hostem a gościem). Tego typu rozwiązanie najczęściej stosowane jest w przypadku urządzeń blokowych (pamięci masowych) oraz interfejsów sieciowych.
Niezależnie od wykorzystywanych mechanizmów, celem wirtualizacji zawsze będzie takie dostosowanie zasobów, a żeby gośćie mogli trakotować te urządzenia jak swóje własne. Zapewni to stabilność działania uruchmianego na maszynach oprogramowanie, to dzięki tego typu rozwiązaniom możemy np. normalnie alokować przestrzeń pamięci masowych, czy formatować partycje.
Innym ważnym zagadnieniem związanym z maszynami wirtualnymi jest dostęp do procesora. Jak pamiętamy w systemie operacyjnym mamy dwa trybu dostępu do procesora. Tryb jądra - pozwalający na wykonanie dowolnych czynności oraz tryb użytkownika - najprościej rzecz ujmując - ograniczony. Jeśli mamy uruchmiać na maszynach systemy operacyjne, to hipernadzorcy powinni być uruchamiani w trybie jądra. I tu jest mały problem, tryb jądra jest zarezerwowany tylko dla jądra. Hipernadzorca działa w trybie użytkownika (jak każdy inny proces, po za procesami jądra). Jedną z koncepcji, która działa przez jakiś czas było wychwytywanie przez hipernadzorcę wszystkich ograniczonych instrukcji i emulowanie ich działania. To rozwiązanie zostało wyparte przez rozszerzenia procesorów VT-x (Intel) oraz AMD-V (AMD), które udostępniają odpowiednie zestawy instrukcji, które zapewniają gościom bardziej swobodny dostęp do procesora.
16.1.3. Użycie maszyn wirtualnych
Jak wielkrotnie było wspomniane w tym rozdziale maszyny wirtualne tworzą odizolowanie środowisko, zatem ich zastosowaniami mogą być wszelkiego rodzaju testy. Np. jeśli korzystamy z dystrybucji Linuksa na naszych komputerach codziennego użytku, to mogliśmy wykorzystać maszyny wirtualne to testowania przykładów z tego materiału, lub np. dostosowania konfiguracji systemowych do własnych potrzeb. Wiele systemów wirtualizacji posiada mechnizm migawek, które umożliwiają zapisanie stanu maszyny i poźniej ewentualnego cofnięcia się do tego zapisu w razie ewentualnych problemów.
Drugim zastosowaniem maszyn może byc sztywne wydzielenie zasobów dla konkretnych środowisk. Przydzielenie takiej a takiej ilośći pamięci operacyjnej, tyle i tyle rdzeni procesora oraz takiej wielkości dysk twardy znajdujący się np. na macierzy.
16.2. Kontenery
Kontenery w przeciwieństwie do maszyn wirtualnych nie są tak odizolowane od systemu hosta. Często są wykorzystywane jako prostsza alternatywa. Kontenery korzystają z rozwiązań dostępnych już w systemie, aby zapewnić swój rodzaj wirtualizacji. Najczęściej korzystają z możliwości zmiany katalogu głównego (chroot) oraz określenia limitów jądra (rlimit). Kontener można określić jako ograniczone środowisko uruchomieniowe przeznaczone dla zbioru procesów. Oznacza to, że mogą one nie mieć dostępu do niczego poza obrębem swojego środowiska. Tego typu wirtualizację określa się mianem wirtualizacji na poziomie systemu operacyjnego.
Kontenery uruchomione w systemie hosta, będą wspołdzielić ze sobą jądro jego bazowego systemu, bez znaczenia jaka dystrybucja jest uruchomiona w kontenerze. Procesy przestrzeni użytkownika mogą pochodzić z wielu różnych dystrybucji w przypadku kontenerów.
Kontenery są ograniczne za pomocą kilku opcji jądra, przez co one:
- Mają własne grupy cgroup
- Mają własne urządzenia i systemy plików
- Nie mogą uzyskać dostępu do jakichkolwiek innych procesów w w systemie ani prowadzić z nimi interakcji.
- Mają własne interfejsy sieciowe
Mimo iż jest to możliwe, rzadko kto zadaje sobie tyle trudu aby poustawiać te wszystkie aspekty ręcznie. Dlatego też korzysta się z uproszczonych systemów konteneryzacji takich jak Docker. Nie mniej jednak za pomocą takie systemu LXC mamy możliwość własnoręcznego skonfigurowania tych zagadanień. W tym rozdziale nacisk położono na Docker, ale system LXC również po krótce omowiono.
16.2.1. Docker i Podman
Docker jest jednym z podstawowych narzędzi kontenerowych, jest on bardzo łatwy do instalacji z strony projektu. Wymaga do korzystania z kontenerów procesu serwera oraz przywilejów superużytkownika aby uzyskać dostęp do opcji jądra.
Alternatywą dla Dockera jest Podman. Nie wymaga on do swojego działania procesu serwera. Podman może zostać uruchomiony z uprawnieniami zwykłego użytkownika (ang. rootless), wówczas stosowane są inne mechanizmy do uzyskania izolacji. Jeśli uruchomimy Podman z uprawnieniami administratora to użyje on rozwiązań stosowanych przez Docker. Podman jest kompatybilny pod względem wiersza polecenia z Dockerem. Oczywiście występują różnice w implementacji, szczególnie gdy Podman działa z uprawnieniami zwykłego użytkownika. Jeśli gdzieś zajdzie taka potrzeba rożnice między tymi systemami zostaną one wyraźnie zaznaczone.
W tym materiale skupie się na Docker-ze. Podman-a z resztą już omawiałem w tym rozdziale materiału o RHCSA: https://morketsmerke.github.io/articles/terminallog/RedHat_-_RHCSA.html#23.containers
16.2.2. Użycie Dockera
Zamieszczony w tym podrozdziale przykład użycia Docker-a przestawia podstawowe elementy ukazujące działanie kontenerów. Na początku niezbędne może być utworzenie obrazu składającego się z systemu plików oraz innych elementów definiujących kontener. Obraz ten będzie bazować na obrazie pobranym z repozytorium. Jest to standardowe zachowanie. Przez sam obraz można rozumieć jako system plików kontenera. Obraz naszego kontenera przygotujemy do uruchomienia za pomocą pliku Dockerfile. Oczywiście nie zawsze takie działanie jest wymagane, ponieważ moglibyśmy utworzyć kontener ręcznie, zalogować się do jego powłoki i zainstalować powłokę BASH. Za pomocą pliku Dockerfile utworzymy obraz, z którego będziemy mogli uruchamiać wiele kontenerów.
xf0r3m@debian:~/lp_test$ cat Dockerfile FROM alpine:latest RUN apk add bash CMD ["/bin/bash"]
Po zapisaniu zmian w pliku Docker-a, wydajemy poniższe
polecenie.
Opcja -t lp_test
wskazuje na
identyfikator obrazu. Później łatwiej będzie go zlokalizować,
ewentualnie wykorzystać do utworzenia nowego kontenera.
xf0r3m@debian:~/lp_test$ docker build -t lp_test . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM alpine:latest latest: Pulling from library/alpine 4abcf2066143: Pull complete Digest: sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b Status: Downloaded newer image for alpine:latest ---> 05455a08881e Step 2/3 : RUN apk add bash ---> Running in ad8a95239176 fetch https://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.19/community/x86_64/APKINDEX.tar.gz (1/4) Installing ncurses-terminfo-base (6.4_p20231125-r0) (2/4) Installing libncursesw (6.4_p20231125-r0) (3/4) Installing readline (8.2.1-r2) (4/4) Installing bash (5.2.21-r0) Executing bash-5.2.21-r0.post-install Executing busybox-1.36.1-r15.trigger OK: 10 MiB in 19 packages Removing intermediate container ad8a95239176 ---> 72784ac891aa Step 3/3 : CMD ["/bin/bash"] ---> Running in 5571f05ede12 Removing intermediate container 5571f05ede12 ---> 9abc76076917 Successfully built 9abc76076917 Successfully tagged lp_test:latest
Zwróćmy uwagę na informacje zwracane przez Docker podczas
przetwarzania
pliku Dockerfile. Na początku pobierany jest oficjalny
obraz kontenera dystrybucji Alpine Linux
(id: 05455a08881e
) na jego podstawie
tworzony jest kontener
(id: ad8a95239176
) w którym to
uruchamiany jest proces menedżera pakietów, które zdaniem jest
zainstalowanie powłoki BASH
(RUN apk add bash
), jest to druga
linia pliku Dockerfile. Na podstawie tego kontenera
tworzony jest obraz (id: 72784ac891aa
).
Następnie z tego obrazu tworzony jest kontener, który uruchamia proces
powłoki (id: 5571f05ede12
). Ten
kontener finalnie posłuży nam za wzorzec dla naszego obrazu - wyniku
pliku Dockerfile. Po między kolejnymi czynnościamy odczytanymi
z pliku, pośrednie kontenery są usuwane. Wynik działania polecenia
możemy również zobaczyć na liście dostępnych obrazów. Dostęp do niej
możemy uzyskać za pomocą polecenia:
docker images
.
xf0r3m@debian:~/lp_test$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE lp_test latest 9abc76076917 5 minutes ago 11.6MB alpine latest 05455a08881e 3 months ago 7.38MB
Na uruchomienie naszego kontenera mamy dwa sposoby: nieinteraktywny, który dobry byłby gdybyśmy mieli uruchomić jakiś skrypt powłoki. Próba uruchomienia naszego kontenera w tym trybie wygląda w następujący sposób:
xf0r3m@debian:~/lp_test$ docker run lp_test xf0r3m@debian:~/lp_test$
Ze względu na to, że nie było żadnego polecenia dla powłoki, proces
zamknął się natychmiastowo. Na potwierdzenie możemy wyświetlić listę
wszystkich procesów (kontenerów), nawet wykonanych za pomocą
polecenia docker ps -a
, polecenie
bez opcji -a
spowoduje wyświetlenie
działających kontenerów.
xf0r3m@debian:~/lp_test$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4e8739ee569 lp_test "/bin/bash" 2 seconds ago Exited (0) 2 seconds ago modest_ardinghelli 6f5067c97eb9 lp_test "/bin/bash" About a minute ago Exited (0) About a minute ago great_tharp
Jak możemy wywnioskować po pierwszym wpisie, to coś się działo.
Jednak nie wiele z tego użyliśmy. Jeśli chcelibyśmy uruchomić
kontener w drugim trybie interaktywnym to wówczas
musimy do podpolecenia run
dodać
opcje
-it, czyli -i - praca interaktywna oraz -t
- podłączenien terminala.
xf0r3m@debian:~/lp_test$ docker run -it lp_test 1d87aa6e43e7:/#
Dopóki pozostawimy tę sesje powłoki uruchomioną, doputy kontener pozostanie uruchiomy. A w ten sposób prezentuje się lista procesów, które są uruchomione na kontenerze:
1d87aa6e43e7:/# ps PID USER TIME COMMAND 1 root 0:00 /bin/bash 203 root 0:00 ps
Proces uruchomionej powłoki (a co za tym idzie, kontenera) możemy rownież zobaczyć wyświetlając listę procesów hosta:
root 2369 0.0 0.1 2612 2360 pts/0 Ss+ 14:42 0:00 /bin/bash
Za tego typu zachowanie odpowiedzialna jest jedna z opcji jądra wykorzystywana na potrzeby kontenerów - przestrzenie nazw Dzięki tej funkcji proces może utworzy zupełnie nowy zestaw identyfikatorów dla siebie oraz swoich procesów potomnych zaczynając do PID-u 1. Procesy te będą wówczas miały dostęp tylko do tego identyfikatora.
Jeśli mamy na to ochotę to po skończonej zabawie możemy usunąć
nieaktywne już kontenery za pomocą polecenia
docker rm identyfikator
:
xf0r3m@debian:~/lp_test$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1d87aa6e43e7 lp_test "/bin/bash" 2 hours ago Exited (0) 19 minutes ago mystifying_gauss 31808ed62e2a hello-world "/hello" 3 hours ago Exited (0) 3 hours ago elegant_davinci xf0r3m@debian:~/lp_test$ docker rm 1d87aa6e43e7 1d87aa6e43e7 xf0r3m@debian:~/lp_test$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 31808ed62e2a hello-world "/hello" 3 hours ago Exited (0) 3 hours ago elegant_davinci
Nakładkowy system plików
Inną funkcją jądra wykorzystywaną przez kontenery jest nakładkowy system plików. Opcja ta pozwala na utworzenie systemu plików po przez połącznie istniejących katalogów jako warstw, wówczas zmiany przechowywane są w jednym miejscu. Ten mechanizm składa się z trzech warstw:
- Dolnego katalogu (lowerdir) - zawierającego bazowy system plików.
- Górnego katalogu (upperdir) - zawierającego wszelkie zmiany dokonane na bazowym systemie plików w trakcie działania systemu.
- Katalogu roboczego (workdir) - katalog przejściowy, w którym sterownik systemu plików realizuje swoje działania przed zapisaniem zmian w górnym katalogu.
W przypadku Podmana bez uprawnień, wykorzystywany nakładkowy system plików w wersji FUSE.
Obsługa sieci
W przypadku Docker-a istnieje kilka metod na zapewnienie łączności kontenerom. Najczęściej stosowanym rozwiązaniem jest sieć z mostem wykorzystującym siecią przestrzeń nazw (netns). Serwer Docker-a tworzy w systemie hosta interfejs sieciowy zazwyczaj o nazwie docker0 przypisanym pierwszym adresie z podsieci prywatnej 172.17.0.0/16 - 172.17.0.1/16. Ten interfejs wykorzystywany do komunikacji miedzy hostem a kontenerami. Docker wykorzystuje dodatkowe interfejsy w sieciowej przestrzeni nazw oraz mechnizm NAT aby zapewnić kontenerom dostęp do sieci zewnętrznych (w tym sieci lokalnej) hosta, w tym do Internetu.
Podman bez uprawnień wykorzystuje natomiast interfejsy TAP oraz mechanizmy przekazujące w postaci daemona slirp4netns, aby uzyskać dostęp do sieci zewnętrznych. To rozwiązanie ma jeden minus, kontenery niestety nie mogą łączyć się ze sobą.
16.2.3. LXC
LXC jest to jeden z najstarszych systemów konteneryzacji dostępnych na dystrybucje Linuksa. Pierwsze wersje Docker-a operały się o ten pakiet. Niekiedy termin ten jest używany do odniesienia się do mechanizmów jądra pozwalających na obsługę kontenerów, jak i biblioteki oraz pakietu narzędzi wykorzystywanych do ich tworzenia oraz modyfikowania.
Pakiet LXC nie nadaje się dla osób, które nie czują się biegłe w administracji kontenerami oraz nie posiadają odpowiedniej wiedzy jak działają poszczególe mechanizmy systemów konteneryzacji. LXC wymaga dość sporego zakresu ręcznej konfiguracji. Wymagane jest utworzonie np. dla kontenerów interfejsów sieciowych czy zapewnienie mapowania identyfikatorów użytkowników. Wartym wspomnienia faktem odnośnie kontenerów LXC, jest fakt że zawierają one najwięcej elementów z dystrybucji, np. program typu init.
Kontenery LXC są bardziej elastyczne pod kątem dostosowania do różnych wymagań. Jeśli ktoś będzie mieć ochotę pobawić się LXC, To zapraszam na stronę wiki projektu Debian, gdzie jest opisane w jaki sposób zainstalować LXC. Obecnie jednak nie musimy się tym przejmować ponieważ w wersji stablinej Debiana możliwy do zainstalowania jest pakiet LXD, który podczas inicjalizacji wykona większość tej konfiguracji pakietu LXC za nas Trzeba tylko odpowiedzieć na kilka pytań instalatora. Pakiet LXD został odłączony od projektu linuxcontainers.org i stał się produktem firmy Canonical Inc. w repozytorium backports wersji stabilnej znajduje się pakiet Incus, który jest forkiem LXD dalej rozwijanym przez społeczność.
16.3. Wirtualizacja oparta na środowisku uruchomieniowym
Inny rodzaj wirtualizacji może bazować na typie środowiska aplikacji. W tym przypadku nie wykorzystuje się innych mechanik niż izolacja i to tylko określonej aplikacji. Celem takie działania jest zapewnie spójności między środowiskiem języka programowania wykorzystywanym przez ważne kompenenty systemu operacjnego, a zapewnieniem możliwości uruchomienia żądanej przez nas aplikacji. Głównie ma to zastosowanie przy Pythonie, i to jego wykorzystamy do przedstawienia tego rodzaju wirtualizacji.
Przed uruchomieniem takiego środowiska, należy upewnić się, że mamy zainstalowanym pakiet (w Debianie): python3-venv jeśli tak to możemy przjeść do stworzenia środwiska wirtualnego.
xf0r3m@vm-f99031d:~$ python3 -m venv test-env
Polecenie zakończy swoje działanie bez żadnego komunikatu, jeśli
faktycznie utworzył to środwisko wirtualne, to powiniśmy zobaczyć
katalog o nazwie - w moim przypadku -
test-env
, a wewnątrz niego
powinniśmy znaleźc kilka katalogów systemowych:
xf0r3m@vm-f99031d:~/test-env$ ls bin include lib lib64 pyvenv.cfg
Aby środowisko mogło zadziałać należy je aktywować za pomocą poniższego polecenia:
xf0r3m@vm-f99031d:~$ . test-env/bin/activate (test-env) xf0r3m@vm-f99031d:~$
O fakcie, że znajdujemyu się wewnątrz środowiska może świadczyć
zmieniony znak zachęty. W ten sposób uzyskaliśmy czyste środowsko
uruchomieniowe dla aplikacji w Pythonie. Możemy je smiało również
wykorzystać do programowania, czy testowania modułów z repozytorium
narzędzia pip. W celu opuszczenia do dyspozycji mamy
polecenie będące funkcją powłoki -
deactivate
Polecenie to przywrócić zmienne środowiskowe oraz ustawienie powłoki
do stanu sprzed uruchomienia skryptu
deactivate
. Na podobnej zasadzie
działa polecenie activate
.
(test-env) xf0r3m@vm-f99031d:~$ deactivate xf0r3m@vm-f99031d:~$
Oczywiście nie wszystkie języki programowania, będą miały możliwość utworzenia takie środowiska wirtualnego. W przypadku Pythona jest najprostsze do osiągniecia.
Podsumowanie
Rozdziałem o wirtualizacji zakończyliśmy ten materiał. Nosi on tytuł Linux. Podstawy., jednak czytelnik po jego lekturze może dojść do wniosku, że wiele informacji zostało opisanych nadwyraz szczegołowo, za brakło przykładów faktycznie przezentujących podstawy. Jednak ciężko jest sklasyfikować podstawy. Gdzie kończą się podstawy a zaczyna się wiedza bardziej zaawansowana? W tym materiale, takie faktyczne podstawy znajdują się w pierwszych dwóch rozdziałach, a te tematy zaawansowane zostały omówione w dość powierzchownie, aby wyłącznie nakreślić użytkownikowi jak pewne mechanizmy funkcjonują w dystrybucjach Linuksa i wrazie potrzeby pogłebienia informacji na dany temat, móc napisać odpowiednie zapytanie do wyszukiwarki lub wiedzieć o czym się pisze tworząc wątek na jakimś tematycznym forum z prośbą o pomoc. Tematy te również mogą być traktowe, jako wyłącznie przedstawie danego zagadnienia, aby czytelnik mógł stwierdzić czy ten akurat temat go interesuje. Ze względu na to, że materiał był tworzony na przestrzeni kilku lat. A tak dokładnie to 3. I w trakcie materiał źródłowy, z którego korzystałem został zaktualizowany, można zauważyć rozbierzności w sposobie oraz ilości przedstawianych treści między różnymi rozdziałami.
17.1. Co dalej?
Dobry rozszerzeniem tego materiału, ale już od bardziej usystematyzowanej, bardziej praktycznej formy jest materiał o RHCSA. Mimo iż tutaj używałem wyłącznie GNU/Linux Debian, nie mniej jednak mechnizmy zawarte w Red Hat, mają swoje odpowiedniki w takich systemach jak właśnie Debian. Poniżej pozostał jeszcze dodatek zawierający podstawy programowania w powłoce BASH. O nieco bardziej zaawansowanym wykorzystaniu tej powłoki powstawał osobny materiał
18. Dodatek A. Podstawy programowania skryptów w powłoce BASH
Jak już wspomniałem w 14 rozdziale do zrozumienia tego materiału nie są potrzebne żadne umiejętności programistyczne. Dlatego też przeniosłem rozdział o tworzeniu skrytów powłoki na sam jego koniec do roli dodatku.
Wśród Uniksów mamy dostępne kilka rodzajów powłok. Najczęściej będą to powłoki BASH oraz KSH (wykorzystywana w środowiskach BSD). Obecnie w wielu dystrybucjach możemy spotkać się z ewolucją kwestii domyślnej powłoki a przynajmniej dla naszego domyślnego użytkownika. Co raz częściej możemy spotkać się z takimi powłokami jak ZSH oraz FISH są one następcami powłoki BASH, zawierającymi wiele udogodnień pozwalających na łatwiejsze tworzenie poleceń. Obecnie na popularności zyskuje projekt o nazwie nu-shell jest to nowy rodzaj połoki zmieniający dotychczasowe doświadczenia z nią związane. Nie mniej jednak informacje przedstawione w tym rozdziale będą działać w powłoce BASH oraz z pewnymi ograniczeniami i potrzebą dostosowania w powłoce KSH. Dostosowanie tych informacji do innych programów powłoki pozostawiam wam.
18.1. Podstawy tworzenia skryptów
Podczas tworzenia skryptu powłoki, warto miec uwadze to że ma się do dyspozycji powłokę, na drugiej karcie lub drugim oknie. Przed utworzeniem skryptu można przetestować, niektóre pomysły zanim zostaną one zapisane w skrypcie. Może być to szczególnie przydatne, gdy działanie skryptu może byc destrukcyjne - np. skrypt coś usuwa. Sprawdzenie tego czy nasze pomysły maja w ogóle sens, pozwala zaoszczędzić czas podczas debugowania skryptu. Może nam się wydawać, że pewne konstrukcje powłoki są cieżkie do zapisania w postaci polecenia. Tak może faktycznie być, ale weźmy pod uwagę to, że te konstrukcje mają szczególnie zaznaczony początek i koniec. Jeśli powłoka nie napotka odpowiedniego słowa kluczowego w wprowadzonym wierszu polecenie wówczas przejdzie w tryb wielolinii, w którym to będzie można kontynuować wprowadzanie polecenia.
18.1.1. Początek skryptu
Jak może pamiętamy z podrozdziału o językach skryptowych na początku każdego skryptu umieszczamy linię wskazującą jednoznacznie na program interpretujący zawartość skryptu. W przypadku skryptów powłoki ta linia wygląda następująco:
#!/bin/bash
W dobrym tonie jest pozostawienie jednej linii odstępu do wskazania interpretera a treści skryptu. Nie raz możemy spotkać się takim zapisem jak:
#!/bin/sh
W tym przypadku możemy to dwojako interpretować, albo interpreterem skryptu jest klasyczna powłoka Bourne-a, albo autor zakłada, że domyślnym programem dostarczającym powłokę jest BASH, a plik /bin/sh jest tylko dowiązaniem symboliczym do /bin/bash. W przypadku Debiana, /bin/sh jest dowiązaniem do powłoki DASH bedąca tworem podobnym do KSH. Takie skrypty zazwyczaj wykorzysują proste polecenie oraz instrukcje bez specyficznych dla BASH mechanizmów.
18.1.2. Prosty skrypt.
Na potrzeby tego podrozdziału stworzymy pierwszy skrypt składający się z jednego polecenia echo. Polecenie to służy do wyświetlania ciągów znaków podanych jako argumenty. Polecenie to posiada kilka przydatnych opcji, których opisy znajdują się na stronie podręcznika.
#!/bin/bash echo "Hello, World!";
Te dwie linie nalezy zapisać w pliku. Możemy dopisać rozszerzenie .sh, wówczas nasz edytor będzie kolorować składnię skryptu. Obecnie edytor mogą uruchamiać odpowiednie kolorowanie składni na podstawie zawartości pliku. Vim działa na takiej zasadzie. Plik należy zapisać i zamknąć następnie otworzyć go ponownie.
18.1.3. Uruchomienie skryptu
Uruchomienie skryptu powłoki BASH może odbyć się na dwa sposóby: bezpośrednio z wiersza polecenia oraz pośrednio podając ścieżkę do skryptu poleceniu bash. Uruchomienie bezpośrednie wymaga nadania plikowi uprawnień do wykonywania. Wówczas takie uruchomienie wygląda następująco:
$ ls -al test.sh -rw-r--r-- 1 xf0r3m xf0r3m 35 05-10 12:08 test.sh $ ./test.sh -bash: ./test.sh: Brak dostępu $ $ chmod +x test.sh $ ./test.sh Hello, World!
Teraz jeśli odbiorę uprawnienia wykonywania dla tego pliku to i tak będę wstanie go uruchomić, poprzez uruchomienie pośrednie.
$ chmod -x test.sh $ ls -al test.sh -rw-r--r-- 1 xf0r3m xf0r3m 35 05-10 12:08 test.sh $ bash test.sh Hello, World!
Oczywiście te uruchomienia poza poleceniami się niczym innym nie różnią. W momencie uruchomienia skryptu, dochodzi to startu procesu podpowłoki, który wykonuje kolejno jedno po drugim polecenie zapisane w pliku skryptu. Efekty uruchomienia tych sposóbów są identyczne.
18.1.4. Debugowanie skryptów.
W przypadku skryptów użycie słowa debugowanie jest określeniem nadwyrost, nie mniej jednak możliwe jest śledzenie tego co robi skrypt, poprzez wywołanie procesu powłoki wraz z opcją -x, mozna to zarówno zrealizować przez uruchomienie pośrednie jak i bezpośrednie. Jednak raczej stosuje się tylko uruchomienie pośrednie. Nikt nie chce aby jego standardowe wyjście zalała fala informacji z np. każdą wartością zmiennej zadeklarowanej w skrypcie.
#!/bin/bash -x ... $ bash -x test.sh + echo 'Hello, World!' Hello, World!
Każdy znak plusa (+), oznacza że dane polecenie zostało uruchomione w podpowłoce. Więcej niż jeden taki symbol może mówić nam, że mamy doczynienia z wyrażeniem okrągłych nawiasów lub z podstawieniem polecenia. Obie te konstrukcje zostaną omówione w tym rozdziale.
Teraz kiedy wyjaśniliśmy sobie w jaki sposób utworzyć, uruchomić oraz w wrazie problemów wyświetlić więcej szczegółów w celu namierzenia błedu. Możemy poznać podstawowe instrukcje.
18.2. Podstawowe instrukcje powłoki.
Głównym celem jaki przyświeca tworzeniu skryptów powłoki jest automatyzacja powtarzalnych zadań wykonywanych w powłoce dystrybucji Linuksa. Zatem jej najprostszą oraz najbardziej złożoną instrukcją będzie najzwyklejszy uruchamiany przez nią program. Możemy dla przykładu umieścić w skrypcie z poprzedniego podrozdziału polecenie ls.
$ cat test.sh #!/bin/bash -x echo "Hello, World!"; ls
Same polecenie echo
jest instrukcją
powłoki, co za tym idzie poleceniem skryptu.
Jak możemy zauwazyć na powyższym przykładzie linia z instrukcją
echo
, kończy się średnikiem
(;)a linia z ls
nie. Generalnie średnik nie jest wymagany na końcu
linii. W ten sposób BASH interpretując skrypt wie gdzie się kończy
polecenie. Jeśli zaś linia musi zawierać dwa polecenia, to należy
umieszczać między poleceniami średnik. Jest jeszcze kilka przypadków
gdy należy stosować średniki, ale przypomnę o tym w sposobnym
momencie. Jeśli poza BASH-em programujemy w C lub w PHP, gdzie te
średniki są wymagane to nie ma przeszkód aby je stosować.
18.2.1. Zmienne
Instrukcje programowania dostarczane w wraz powłoka BASH pozwalające na pisanie skrytów, są pełnoprawnym językiem programowania. Jak każdy inny język potrzebuje on konstrukcji, które pozwolą mu na pracę z danymi. Tak najprostszą z konstrukcji znaną także w innych językach są zmienne - kontenery na dane, ułatwiające zapisanie toku myślenia w postaci algorytmu. W programowaniu ma to swoje odzwierciedlenie na pamięci, pozwalajac przypisać do niewielkich obszarów pamięci, identyfikatory aby móc odwołać się, w kodzie źródłowym do ich zawartości.
W BASH-u (będę używać tego określenia, na programowanie skryptów powłoki BASH. Myślę że upowszechniło się na tyle, że śmiało możemy powiedzieć o programowaniu w BASH-u.) zmienne są definiowane w prosty sposób. Podajemy nazwę zmiennej (identyfikator), następnie znak równości (=) po tym znaku podajemy wartość. Ważne jest aby nie robić przerw między tymi elementami, wówczas nazwa zmiennej zostanie potraktowana jako polecenie (nazwa programu do uruchomienia).
var=1;
Do tak zdefiniowanej zmiennej możemy się odwołać poprzedzając nazwę zmiennej znakiem dolara ($).
echo "$var";
Polecenie echo
wyświetli zawartość
zmiennej var
na standardowym
wyjściu.
18.2.2. Komentarze
Ciągi znaków oznaczone jako komentarze po przez umieszczenie na ich początku krzyżyka (#) są całkowicie ignorowane przez powłokę w trakcie wykonywania skryptu.
# To jest komentarz #Tresc komenentarza może przylegać do krzyzyka # lub może być miedzy nimi odstep
Komentarze mają służyć wyłącznie programiście. Ich zadaniem jest możliwość dodawania notatek do kodu, w celu np. wyjaśnienia zawiłego bloku kodu.
18.2.3. Potoki
Potoki czy inaczej polecenia potokowe są to ciągi poleceń połączonych ze sobą za pomocą mechnizmów wejścia-wyjścia.
cat /etc/passwd | grep 'xf0r3m' | sed 's,/bin/bash,/bin/sh,'
W powyższym poleceniu, dane wejściowe programu
cat
pochodzą z pliku, ale dane
wejściowe polecenia grep
pochodzą
już ze standardowego wyjścia polecenia
cat
. Zawartość pliku zostaje
przekazana na standardowej wejscie drugiego polecenia, które
wyszukuje odpowiedniego wzorca. Ta sama sytuacja tyczy się ostatniego
polecenia (sed
). Potoki konstrułuje
się przy użyciu znaku potoku lub znaku kreski pionowej
(|).
Potoki są przydatne gdy musimy przetworzyć w jakiś sposób dane, wówczas sprowadzamy je do postaci strumienia. Każde z poleceń na potoku wykonuje swoją działkę i przekazuje je dalej, aż trafiają one do zmiennej lub na standardowej wyjście powłoki. Z prostej analogii wygląda to trochę jak praca na linii produkcyjnej.
Powyższy przykład ma duży potencjał optymalizacyjny swoją drogą,
ponieważ pierwsze polecenie można pominąć i podać pliki od razu
poleceniu grep
.
18.2.4. Podstawienie polecenia
Mając omówione zmienne oraz potoki, to warto może połączyć ze sobą te zagadnienia i zadać pytanie: w jaki sposób można zapisać w zmiennej dane przetwarzane przez potok?
Chcąc zapisać wynik działania potoku do zmiennej, należy wykorzystać mechanizm zwany podstawieniem polecenia. Działa on na takiej zasadzie, że w momencie napotkania tego mechnizmu przez podpowłokę uruchamiana jest kolejna podpowłoka wykonująca polecenia zapisane w tym przypadku w potoku w podstawieniu polecenia. Po zakończeniu wartość zwracana jest przekazywana do zmiennej. Podstawienie polecenia może mieć dwie postacie, można wykorzystać lewe cudzysłowy (``) lub parę okrągłych nawiasów poprzezdzonych znakiem dolara ($()). Obie te formy są poprawne. Do niedawna istniała jedynie wersja z cudzysłowami.
var=$(grep 'xf0r3m' | sed 's,/bin/bash,/bin/sh,'); # lub: var=`grep 'xf0r3m' | sed 's,/bin/bash,/bin/sh,'`;
18.2.5. Wyrażenie nawiasów okrągłych
Wyrażenie nawiasów okrągłych lub podstawienie podpowłoki, umożliwia nam oddzielenie wykonania pewnych poleceń od głównego procesu podpowłoki wykonującego skrypt. Użycie tego mechinizmu sprawdza się do zapisania, poleceń w zwykłym nawiasie okrągłym.
(cd ytfzf && make install doc)
Jest to szczególnie przydane, gdy musimy trzymać kurczowo jakiś lokalizacji w systemie plików, a niektóre czynności tak jak instalacja projektu pokazana na powyższym przykładzie wymaga zmiany tej ścieżki. Ścieżka może zostać zmieniona w podpowłoce i wykonanie czynności również. Nie będzie to miało wpływu na główny nurt działania skryptu.
18.2.6. Znaki cytowania
Jak było prawdopodobnie wspomniane o znakach cytowania w drugim rozdziale. W BASH-u do dyspozycji mamy 3 rodzaje cudzysłowów:
- Lewy cudzysłów - (`) - jeden z operatorów podstawienie polecenia.
- Pojedynczy cudzysłów - (') - cytowanie dosłowne, wszystko zapisane pomiędzy tymi znakami zostanie potraktowane jako ciąg znaków. BASH nie będzie próbować interpretować, żadnych treści w tym i instrukcji.
- Podwójny cudzysłów - (") - cytowanie niedosłowne, Występujące wewnątrz odwłania do zmiennych czy podstawienia polecenia oraz inne mechnizmy i instrukcje zostaną przez BASH zinterpretowane i wykonane, wówczas wyniki działania BASH-a uzupełnią zapisany ciąg znaków.
Należy o tym pamiętać! Że cudzysłowy w powłoce są rozróżniane i mają różne funkcje. Szczególnie może mieć to znaczenie przy takich poleceniach jak sed czy grep.
18.2.7. Parametry pozycyjne
Przekazanie do skryptu informacji jest jak najbardziej możlwe, oczywiście możemy zrobić to jak przypadku innych języków - podczas działania programu poprosić o podanie danych wejściowych. Możemy również użyć mechanizmy parametrów pozycyjnych, które pozwolą nam na przekazanie danych wejściowych podczas uruchamiania skryptu.
$ ./test.sh foo bar
Dostęp do tak przekazanych danych można uzyskać poprzez odwłanie się do numeru parametru:
#!/bin/bash echo "1: $1"; echo "2: $2"; # $ ./test.sh foo bar 1: foo 2: bar
Parametry pozycyjne są czesto wykorzysywane do implementowania opcji skryptów. Paramentrów jest maksymalnie 10 (chociaż jest mozliwe jest uzyskanie dostępu do parametrów powyżej 10 za pomocą mechanizmu wyrażenia parametru.
Dostęp do całej listy parametrów pozycyjnych możemy uzyskać na dwa sposoby. Oba są warte poznania. Pierwszym z nich jest lista parametrów, w której każdy jest osobnym elementem. Dostęp do niej uzyskujemy poprzez - $@ - tego typu odwołanie się. Innym sposobem dostęp do parametrów jako pojedynczego ciagu znaków, wówczas wszysktie paramety są jednym elementem. Dostęp do parametrów w ten sposób osiągamy poprzez - $*.
18.2.8. Wyrażenie parametru
Wyrażenie parametru posiada w BASH-u kilka funkcji. Jednak chyba najważniejszą oraz tą, która wymaga zapamiętania jest separacja nazwy zmiennej od pozostałych elementów, jest to szczególnie przydatne gdy scieżki w instrukcjach zawierają zmienne. Wyrażenie parametru jest przedstawiane za pomocą pary nawiasów klamrowych poprzezdzonych znakiem dolara (${}) pomiędzy nawiasami zapisuje się nazwę zmiennej, już bez znaku dolara.
wget https://ftp.server.com/example/${ARCH}/${VERSION}/file.tgz
Oczywiście poza funkcją separacyjną wyrażenie parametru kilka innych zastosowań, jedno już zostało tutaj wymienione, jest to dostęp do parametrów pozycyjnych powyżej 10-tej pozycji. Pozostałe są dostępne na stronie podręcznika powłoki BASH.
18.2.7. Exitcode - wartość zwracana przez program
Niektóre narzędzie wykorzystywane przez nas do pracy w dystrybucjach Linuksa, zostają przez nas uruchomione i zaraz po tym zostaje nam zwrócony znak zachęty. I tak naprawdę to nie wiemy czy program wykonał swoje zadanie pomyślnie czy też nie. Nie mniej jednak większość programów działających na Uniksach zwraca takzwany kod wyjścia.
- 0 - program poprawnie wykonał swoje zadanie.
- !0 - (wartość inna niż 0), podczas działania program nie mógł poprawnie wykonać swoich zadań z różnych powodów.
W przypadku wartości nie zerowych programiści mają cały wachlarz możliwości na opisanie co poszło nie tak, zwracając kod wyjścia inny niż 0. Do dyspozycji mają od 1 - 255 możliwości. Kod wyjścia 1 jest najczęściej wykorzystywany jako błąd ogólny i ta wartość jest najczęsciej zwracana przez programy. Programy raczej sporadycznie korzystają z pozostałych wartości, oczywiście wszystko pozostaje w rękach programistów.
18.3. Wyrażenia warunkowe
Rozpoczynając omawianie wyrażeń warunkowych, warto sobie wyjaśnić, że w BASH-u nie operujemy na wartościach logicznych. Na wartościach logicznych operują polecenia wykonujące sprawdzenie danego wyrażenia warunkowego. Na podstawie wyniku tego wyrażnia narzędzia te generują kod wyjścia: 0 - dla prawdy i 1 - dla fałszu. Warto mieć to na uwadzę, aby podczas debugowania nie zastanawiać się dlaczego nasz warunek zwraca 0, a wykonywany jest blok kodu dla prawdy.
18.3.1. Instrukcje warunkowe
Instrukcjami warunkowymi możemy nazwać polecenia, które zwrócą nam odpowiedni kod wyjścia na podstawie testu logicznego podanego jako argument. Zazwyczaj zapisanie takie testu nie różni się niczym od zapisania warunku w innych językach.
W BASH-u do dyspozycji mamy takie instrukcje jak [, test, [[. Pierwsze dwie instrukcje są identyczne i najczęściej instrukcja [ jest przedstawiana jako alias instrukcji test, która występuje nie tylko BASH-u, ale w większości powłok Uniksowych. Inną instrukcją jest [[ jest klasyczny test jednak jego funkcjonalność została rozszerzona, jednym z takich rozszerzeń jest sprawdzenie czy np. podany ciąg znaków pasuje do nazwy wieloznacznej. Instrukcji [[ nie będziemy tutaj omawiać.
Instrukcje warunkowe do wykonania warunku logicznego potrzebujemy dwóch operandów oraz operatora lub jednego operandu oraz operatora, ponieważ wśród operacji warunkowych istnieją operacje jednoargumentowe. Zazwyczaj przeprowadzane są one ścieżkach wskazujących plik lub katalog. Takich jak na przykład:
[ -f $HOME/testfile.txt ]
Ten warunek sprawdzi czy plik
$HOME/testfile.txt
jest zwykłym
plikiem.
Warunki, które wymagają dwóch operatorów, to zazwyczaj takie jakie znamy z lekcji matematyki: a > b, d <= e itd. Przyczym tutaj warto przypomnień, że zapis instrukcji warunkowych (warunków), jest jednocześnie zapisem polecenia, tak więc nie można użyć znaków mniejszości (<) czy większości (>), zamiast nich musimy używać specjalnie przygotowanych na tę okazję zamienników:
- -lt - mniejsze niż,
- -gt - większe niż,
- -eq - równe,
- -ne - nierówne.
Nie będę tutaj wszysktich wypisywał, wszystkie operatory wraz tymi jednoargumentowymi znajdziemy na stronie podręcznika polecenia test lub na stronie podręcznika powłoki BASH w sekcji CONDITIONAL EXPRESSIONS. Mimo to warto zaznaczyć, że te wyżej wymienione operatory wymagają całkowitych (liczb całkowitych) operandów, a przyrównanie ciągów znaków wymaga pojednyczego znaku równości (=), a nie dwóch jak w przypadku innych języków. Warto mieć to uwadze.
Instukcje warunkowe na podstawie przekazanych przez programistę warunków, zwracaja kod wyjscia, który najczęściej interpretowany jest przez konstrukcje warunkowe.
18.3.2. Konstrukcje warunkowa - if
Konstrukcje warunkowe są elementem kontrolnym w programowaniu. Ich zadaniem jest wykonanie określonych instrukcji determinowanych na podstawie przekazanej instrukcji warunkowej. I najprostszym tego typu konstrukcją jest if-then-else.
Konstrukcja if-then-else, jest podstawową konstrukcją warunkową opartą o wynik jednej instrukcji warunkowej oraz zawierającą dwa bloki kodu, dla prawdy oraz fałszu.
if warunek; then #Blok kodu dla prawdy else #Blok kodu dla fałszu fi
Konstrukcja rozpoczyna się od słowa kluczowego
if
, oznacza on początek konstrukcji,
następnie obok znajduje się warunek
,
warunek może być instrukcją warunkową, ale może być rownież
poleceniem lub jego podstawieniem - czymś co jest nam wstanie
zwrócić kod wyjścia. Po warunku obowiązkowo występuje średnik,
następnie słowo kluczowe then
otwierające blok kodu dla sytuacji, w której warunek zwrócił kod
wyjścia równy 0 (potocznie nazywany prawdą). Po
tym bloku występuje słowo kluczowe
else
otwierając tym samym blok
kodu dla każdej innej wartości kodu wyjścia, która nie jest zerem.
Na samym końcu znajduje się słowo kluczowe
fi
, zamykające blok else
oraz całą konstrukcję.
Konstrukcję if-then-else można rozszerzyć o dodatkowy blok warunkowy elif. W momencie interpretacji takiej konstrukcji BASH sprawdzi na początek warunek przy if następnie przy elif i jeśli oba te warunki zwrócą niezerowy kod wyjścia, wtedy zostaną wykonane instrukcje zapisane w bloku else. Warto dodać, że nie ma limitu w ilości dodatkowych warunków (elif) tworząc przy tym całą kaskadę. Ponizej znajdue się przykład poglądowy przezentujący konstrukcję if-then-elif-else:
if warunek; then #Blok dla prawdy warunku; elif warunek2; then #Blok dla prawdy warunku2; else #Blok dla fałszu obu warunków; fi
18.3.3. Konstrukcja warunkowa - case
Inny rodzajem konstrukcji warunkowej, jest konstrukcja case, działa ona trochę na innej zasadzie niż konstrukcja if-then-else. Nie mam tutaj klasycznego warunku, a jedynie zmienna, która jest porównywana z zapisanymi przez programistę wartościami (przypadkami). Przypadki definiują bloki kodu, w momencie gdy wartość zmiennej jest równa wartości danego przypadku wówczas BASH wykona instrukcje zapisane w bloku tego przypadku.
case $alphabet in 'a') echo "A";; 'b') echo "B";; esac
Zmienna alphabet
będzie kolejno
porównywana z przypadkami 'a'
'b'
. Jeśli zostanie odnaleziony
odpowiedni przypadek, wówczas zostanie uruchomiony odpowiedni blok
kodu. Zwróćmy uwagę na to, że bloki kodu poszczególnych przypadków
zakończone są podwójnym średnikiem, jest to wymagane, a jego
pominięcie spowoduje błąd powłoki.
Konstrukcja case posiada przypadek uniwersalny wykorzystywany gdy zmienna nie pasuje, do żadnego z przypadków. Wartość takiego przypadku zapisuje się symbolem gwiazdki (*).
case $alphabet in 'a') echo "A";; 'b') echo "B";; *) echo "To nie jest litera alfabetu";; esac
W przypadku konstrukcji case, bloki kodu mogą nie być aż tak widoczne. Niemniej jednak blok rozpoczyna się od wskazania wartości przypadku i obejmuje wszystkie instrukcje, aż do napotkania podwójnego średnika. Samo wskazanie wartości przypadku nie musi się ograniczać do pojednczej wartości, ale może zawierać warianty.
case $alphabet in 'a'|'A') echo "A";; 'b') echo "B";; *) echo "To nie jest litera alfabetu";; esac
Konstrukcja case, stosowana jest głównie po to aby nie tworzyć kaskady konstrukcji if-then-elif-else. Każda konstrukcja case, kończy się słowem kluczowym esac.
18.4. Pętle
Pętle w programowaniu służa wykonywaniu powtarzalnych czynności pod pewnym warunkiem. W bloku pętli element warunku (najprawdopodobniej) ulega zmianie, tak aby pętla zakończyła się i interpretacja programu wróciła na właściwy tor. Jeśli tak się nie dzieje, to wówczas mamy doczynienia z nieskończoną pętlą, tego typu konstrukcje nie zawsze muszą oznaczać błąd programisty, mogą być one pożądne, w zależności co znajduje się w ich bloku kodu. W BASH-u dość często wykorzystywane są dwa rodzaje pętli.
18.4.1. Pętla while
Pętla while jest podstawowm rodzajem pętli, który potrzebuje do działania spełnionego warunku, wówczas uruchamiana jest sekwencja poleceń znajdujące sie w bloku tek pętli. W przypadku tego rodzaju pętli istotne jest utworzenie licznika - zmiennej, która będzie kontrolować ilość powtórzeń pętli. Licznik zmieniany jest zazwyczaj, gdzieś na samym końcu pętli lub jego zmiana jest użależniona od określonego warunku. Ze względu na to, iż warunek pętli musi być prawdą, aby mogła ona w ogóle rozpocząć działanie - licznik musi zostać zdefiniowany przed pętlą. Poniżej znajduje się przykładowa konstrukcja pętli while wyświetlająca kwadraty pierwszych 10 dodatnich liczb.
count=1 while [ $count -le 10 ]; do pow=$(expr $count \* $count); echo "${count}: $pow"; count=$(expr $count + 1); done
Pętla while, rozpoczyna się od słowa kluczowego
while
, po którym następuję
warunek. Warunek (jak sama nazwa pętli może wskazywać) musi być
spełniony, aby pętla się uruchomiła, bez tego pętla nie wykona się
ani razu. Warunek operuje na klasycznych instrukcjach warunkowych.
Co pozwala stwierdzić, że pętla while działa na takiej
samej zasadzie jak konstrukcja if. Po warunku obowiązkowo
występuje średnik, blok kodu zawierającego instrukcje do wykonania w
pętli otwiera słowo kluczowe do
.
Po tym słowie umiesczana jest sekwencja poleceń, która będzie
wykonywana dopóki warunek będzię spełniony (będzie zwracać prawdę).
Blok kodu pętli, kończy się słowem kluczowym
done
.
18.4.2. Pętla for
Pętla for działa na odmiennej zasadzie niż pętla while. W tym przypadku zamiast warunku, mamy listę oraz jej element (zmienną). Pętla wykonuje swoje działanie do momentu, aż skończą się elementy na liście. Elementy te są przypisywane do zmiennej i następnie uruchamiany jest blok kodu pętli. Za pomocą zmiennej możemy odwłać się do elementu ustawionego dla tego przebiegu pętli. Poniżej znajduje się przykładowa pętla for realizująca to samo zadanie co pętla while.
for i in 1 2 3 4 5 6 7 8 9 10; do echo "${i}: $(expr $i \* $i)"; done
W tym przypadku lista została zapisana z ręki (hardcoded). Jednak tutaj panuje duża elastyczność, listę możemy przekazać zmienną lub w postaci podstawienia polecenia. Wykonanie zadania zostało skompresowane do pojedynczej linii.
18.4.3. Inne pętle oraz instrukcje sterujące
Poza omówionymi wyżej pętlami, BASH oferuje jeszczę pętlę do-while, której cechą charakterystyczną jest fakt, iż wykona się co najmniej raz, ponieważ sprawdzanie warunku występuje poniżej bloku kodu pętli, kiedy zostaną wykonane jej instrukcje wówczas będzie można określić czy warunek zwraca kod wyjścia 0 czy też inny.
Kolejną z pętli jest select, która na podstawie listy wyświetla menu, oddając wykonanie programu pod interaktywną decyzję użytkownika. Użytkownik wybierając odpowiedni numer ustawia wartość przedstawioną w tym przypadku do zmiennej, do której można się odwołać w bloku pętli. Powtarzalność tej konstrukcji, polega na tym, że będzie ona ciągle pytać użytkownika o wybór do momentu, aż nie przerwiemy wykonania całego skryptu lub w kodzie pętli zaimplementujemy warunek, że jeśli została wybrana powiedzmy litera 'q', to zakończ pętle. Poniższy przykład wyświetla nazwy kolorów dostępnych do wyświetlenia w powłoce, po wybraniu koloru, zostanie wyświetlona jego próbka.
colors="black red green yellow blue purple cyan white"; select i in $colors; do case $i in 'black') cc=0;; 'red') cc=1;; 'green') cc=2;; 'yellow') cc=3;; 'blue') cc=4;; 'purple') cc=5;; 'cyan') cc=6;; 'white') cc=7;; esac echo -e "${i}: \e[1;4${cc}m \e[0m"; done
Instrukcjami wpływającymi na działanie pętli w BASH-u są instrukcje: break oraz continue. Instrukcja break służy przerwaniu wykonania pętli i przejściu do instrukcji znajdującej się tuż za pętlą, inaczej jest w przypadku instrukcji continue. Instrukcja ta powoduje zresetowanie przebiegu pętli - wykonanie wraca do sprawdzenia warunku i rozpoczęcia wykonania bloku kodu od nowa.
18.5. Funkcje
Funkcje w BASH-u służą temu samemu celowi co w innych językach programowania - udostępnieniu fragmentów kodu realizującego powtarzalne zadania, aby nie trzeba było ich przepisywać. Dopasowuje się algorytm tak, aby mógł działać z podanymi z zewnątrz danymi, ponieważ funkcję uruchamia się w taki sam sposób jak polecenie.
18.5.1. Definicja oraz wywołanie funkcji
Definicja funkcji wymaga użycia specjalnej konstrukcji function. Przykładową definicję zamieszczono poniżej:
function square () { echo "$(expr $1 \* $1)"; }
Definicja funkcji wymaga słowa kluczowego
function
następnie nazwy funkcji
oraz pary okrągłych nawiasów (()). Sekcję bloku
kodu funkcji wskazują nawiasy klamrowe ({}).
Dane z zewnątrz zostały zaimplementowane pod postacią parametrów
pozycjnych. Oczywiście, może nam się wydawać, że definiowanie
funkcji dla pojednczego polecenia może nie końca mieć sens,
nie mniej jednak takie działanie może posłużyć jako ustawienie
aliasu, dla czynności, które mozna zapisać w
krótki i prosty sposób, nie tłumaczący jednak dokońca jaka operacja
jest wykonywana.
Funkcje tak jak już wspominałem, są wywoływane jak polecenia. Tak więc chcąc wywołać wcześniej zdefiniowaną funkcję należy zapisać pod jej definicją następujące polecenie:
square 4;
18.5.3. Dołączanie plików skryptów
Korzystając z możliwości tworzenia funkcji może się okazać, że jest ich całkiem spora ilość i dużo lepszym sposobem na zarządzanie nimi jest przeniesienie do innego pliku. Wówczas aby móc dalej korzystać z tych funkcji muszą być one dostępne dla skryptu, w którym będą wywoływane. W BASH-u nie mamy takich poleceń jak include czy import. Tutaj musimy posłużyć się wywołaniem skryptu w tym samym proces powłoki, dzięki temu obszar pamięci będzie taki sam, więc dla intepretera wykonującego główny skrypt wczytane z innego pliku funkcję będą dostępne, przez co główny skrypt będzie wstanie z nich skorzystać.
Dołaczenie dodatkowego skryptu można zrealizować na dwa sposoby. Wykonują one tę samą operację i różnią się tylko zapisem. Do wyboru mamy polecenie source oraz zwykłą kropkę (.).
source functions.sh #lub: . functions.sh
Drugi sposób nie jest tożsamy z takim zapisem jak:
./functions.sh
. Tym przypadku
kropka została użyta jako element ścieżki. My będziemy
wykorzystywać ją jako polecenie.
18.6. Inne przydatne narzędzia
Tworząc skryptu w BASH-u, za pewne będziemy pracować z tekstami. Takim zadaniem może być wyciągnięcie z pliku feed-u RSS adresu odnośnika do najnowszej wersji oprogramowania, którego używamy. Jest to jedno z kilku przykładów, do których będziemy potrzebowali narzędzi, które mogą być wykorzystywane w wielu skryptach.
18.6.1 Polecenie cut
Polecenie cut może być wykorzystywane do ekstrakcji podciągu znaków z innego ciągu. W tym przypadku jego działanie będzie opierać się na wskazaniu ogranicznika za pomocą opcji -d, przez co ciag znaków - najczęsciej przekazany na standardowej wejście (polecenie cut często jest umieszczane w potoku) - podzielony zostaje na pola, które możemy wybrać podając numer tego pola liczony od początku ciągu. Tutaj warto zazanaczyć, że część ciągu do wystąpienia pierwszego ogranicznika, będzie pierwszym polem. Dostęp do pól uzyskujemy za pomocą opcji -f
...| cut -d " " -f 2 |...
18.6.2. AWK
Samo narzędzie AWK jest języka programowania operującym na tekstach. Nie mniej jednak, nie będziemy skupiać się na szczegółach tego narzędzia. Wykorzystamy jedną z jego funkcjonalności. Wspomniane wcześniej rozwiązanie związane z poleceniem cut ma jedną zasadniczą wadę, możemy miec problem z określeniem żądanego pola, w momencie gdy znak ogranicznika występuje po sobie kilka razy. Najczęściej jest tak z białymi znakami (spacją oraz znakami tabulacji). Wówczas możemy skorzystać z pól prezentowanych przez narzędzie AWK, pola tego narzędzia są tak naprawdę kolumnami, więc należy zadbać o odseparowanie konkretnej linii. Tutaj podział jest prosty, każda kolumna rozpoczyna się od każdego innego znaku nie będącego tabulatorem lub spacją. Numer kolumny podajemy po znaku dolara, tak jak na poniższym przykładzie.
... | awk '{printf $1}' | ...
Narzędzie zbierze wszystkie ciągi występujące w tej kolumnie w jeden ciąg bez żadnych przerw. Dodając trzy znaki, możemy zamienić ten nieczytelny ciąg znaków, chociażby w listę dla pętli for.
... | awk '{printf $1" "}' | ...
18.6.3. sed
Sed, czyli edytor strumienia (ang. stream editor), jest nieinteraktywnym edytorem tekstu opierającym się na wydawaniu pojedynczych poleceń. Za jego pomocą możemy wstawiać do plików poszczególne linie lub je zmieniać stosując znaną metodę find and replace. Sed działa zarówno na plikach jak i strumieniu danych wejściowych.
W plikach może zajść taka potrzeba, aby zmienić pewien zapis tylko w jednej linii, to wskazanie tej wybranej linii może odbyć się na dwa sposoby: za pomocą wyrażenia regularnego lub podając konkretny numer linii. Wyrażenie regularne sprawdzi się przy kilku liniach, w przypadku jednej konkretnej, wystarczy podać jej numer.
Przy początkach naszej pracy ze skryptami, najczęściej będziemy wykorzystywać raczej opcje find and replace, nie mniej jednak specyficzna składania polecenia sed wymaga, aby chociażby pokazać jak wyglądają polecenia insert, append czy print.
Polecenie sed - insert
Polecenie insert wstawia podaną wartość w konkretną linię podaną przez wyrażenie regularne lub numer linii.
$ sed '21i Lorem ipsum dolor sit amet, consectetur adipiscing elit' plik.txt
Na powyższym przykładzie i
oznacza
właśnie polecenie insert. Przed poleceniem znajduje się
numer linii, który w przypadku narzędzia sed pisany jest
łącznie wraz ze wskazniem konkretnego polecenia. Po poleceniu
występuje wartość, która ma zostać zapisana w pliku.
Polecenie to domyślnie zwrócić na standardowe wyjście całą zawartość pliku z podmienioną linią, ponieważ jest to edytor strumienia to musi on otworzyć plik i działać na nim w pamięci. Aby nasze zamiany faktycznie miały jakiś skutek musimy dodać opcję -i.
Podobnie jest poleceniem append - a, również wymaga ono opcji -i. Składania tego polecenia jest identyczna z insert, zmianie ulega tylko literka wskazująca polecenie, nie mniej jednak zasada działania jest trochę inna. Otóż to polecenie dopisze pod wskazaną linią, podaną wartość. W przypadku naszego przykładu, podana wartość znalazła by się w 22 linii.
Polecenie sed - print
Za polecenie print odpowiedzialna jest litera p nie ma w tym nic dziwnego, warto jednak zaznaczyć, że polecenie to przyjmuje zakres linii do wyświetlania np. jesli chcemy wyświetlić linie od 1 do 12, to wówczas możemy skorzystać z poniższego polecenia:
$ sed '1,12p' test.txt
Tutaj istotną rolę pełni cytowanie. Jeśli checielibyśmy wyświetlić
linie od 100 do końca pliku, to polecenie sed mogło by
wyglądać w ten sposób: '100,$p'
.
No dobrze, a jeśli początek zakresu jest zapisany w zmiennej to
polecenie powinno wyglądać mniej więcej tak
"$a,$p"
. Problem w tym, że dolna
granica zakresu, rownież zostanie zinterpretowana jako zmienna. W tej
sytuacji należy zacytować sam znak dolara.
$ sed "$a,\$p" test.txt
Natomiast jeśli w zmiennej znajduje się dolna granica ciągu, to aby oddzielić odwołanie się do zmienne od polecenia print należy użyć funkcji powłoki BASH, jaką jest wyrażenie parametru.
$ sed "1,${a}p" test.txt
Polecenie sed - find and replace
Do funkcji programu sed, find and replace przypisana jest litera s. Domyślna składnia jest następująca:
$ sed "s/ala/ola/" test.txt
Po poleceniu s
występuje separator,
następnie wartość poszukiwana, znów separator oraz nowa wartość,
która zastąpi wartość poszukiwania, na końcu polecenia znów występuje
separator. Polecenie zamieni pierwsze wystąpienie ciągu
ala
na ciąg
ola
.
W przypadku tego polecenia sed jest bardzo elastyczny. Ponieważ równie dobrze jako separator możemy użyć innych znaków. Znak ukośnika (/) wykorzystywany jest w ścieżkach, a one są jednymi z częściej zmienianych elementów konfiguracyjnych w Uniksach. Jako separatora możemy użyć zarówno: małpy (@) jak i przecinka (,).
Ostatni separator nie musi kończyć polecenia. Ponim zaś występują modyfikatory. Jednym z takich modyfikatorów jest g. Ten modyfikator powoduje, że wszystkie znalezione występienia szukanej frazy zostaną zastąpione.
Podobnie polecenie s
nie musi
rozpoczynać polecenia. Polecenie s zarówno jak
i czy a może zostać sprecyzowane do konkretnej
linii lub grupy elementów pasujących do wyrażenia regularnego.
18.6.4. wc
Polecenie wc (ang. word count), jest poleceniem, które zadaniem jest liczenie linii, znaków czy też słów, podanych za pomocą pliku czy też ze strumienia standardowego wejścia. Wykorzystanie polecenia wc, może być całkiem przydatne w momencie gdy musimy ustalić np. granicę dla pętli while. Najczęsciej stoswane jest liczenie linii (za pomoca opcji -l), czasami może się przydać liczenie znaków (za pomocą opcji -c) oraz słów (za pomocą opcji -w).
18.7. Podsumowanie
W tym rozdziale poznaliśmy podstawy tworzenia skryptów powłoki, które pomogą nam automatyzować zadania wykonywana w powłoce. Nie piszę tutaj, konkretnych nazw. Ponieważ jeśli zagłebimy się w temat, to BASH zebrał najlepsze funkcje oraz rozwiązania z innych powłok i zaimplementował u siebie. Oczywiście sam BASH nie jest nowym rozwiązaniem, ponieważ powstał już 1987 roku. Innymi stosowanymi rozwiązaniami są takie powłoki jak zsh czy fish. Inny ciekawy rozwiązaniem jest projekt nu shell, przestawiający troche inne podejście. Zachęcam do zapoznania się z projektem. Jeśli jednak twardo będziemy obstawać, przy BASH-u (ma to swoje zalety, większość dystrybucji wykorzystuje jednak BASH, więc lepiej mieć to samo środowisko w domu jak i na serwerze w pracy). To można zapoznać się z dodatkowymi materiałami: