   #Start Contents English version

         Pisanie programow rezydentnych (tzw. demonow) pod Linuksem

   W Linuksie sa co najmniej dwa sposoby na uruchomienie programu w tle:
    1. dodanie znaczka ampersand (&) do uruchomienia programu, na
       przyklad "program param1 param2 param3 &".
    2. skorzystanie z programu screen, na przyklad "screen -m -d program
       param1 param2".

   Ale nie jestesmy programistami po to, by liczyc, ze nasz program
   zostanie wlasnie tak uruchomiony. Teraz pokaze, jak samemu zadbac o
   dzialanie swojego programu w tle. Najpierw posluzy nam do tego funkcja
   daemon (patrz: man 3 daemon) z biblioteki jezyka C. Dlatego sposob
   pisania programu bedzie troszke inny niz zwykle:
    1. zamiast LD do laczenia programu, skorzystamy z GCC (ktory widzac
       rozszerzenie .o naszego pliku, polaczy go odpowiednio z biblioteka
       jezyka C bez kompilowania)
    2. skoro korzystamy z GCC i biblioteki C (ktora ma juz wlasny symbol
       _start), to nasz kod bedzie sie zaczynal etykieta main (taka sama,
       jak programy w jezyku C)
    3. funkcja daemon musi byc zadeklarowana jako zewnetrzna (extern)

   Aby bylo widac, ze nasz demon (odpowiednik TSR w Linuksie)
   rzeczywiscie dziala, umiescimy w nim petle co jakis czas wyswietlajaca
   jakis napis. W celu odmierzania przerwy skorzystamy z funkcji
   systemowej sys_nanosleep (numer 162).

   Jak widac ze strony podrecznika, funkcja daemon przyjmuje 2 argumenty
   w postaci liczb calkowitych (DWORD):
     * pierwszy argument (ostatni wkladany na stos) mowi o tym, czy nie
       zmieniac katalogu pracy na katalog glowny /. My nie chcemy
       zmieniac, wiec wstawimy wartosc 1.
     * drugi argument (pierwszy wkladany na stos) mowi o tym, czy nie
       zamykac strumieni wejscia i wyjscia. My nie chcemy zamykac, wiec
       wstawimy wartosc 1.

   Po omowieniu tego wszystkiego, przejdzmy wreszcie do przykladowego
   programu. Jego zadaniem jest przejscie w tryb demona i wyswietlanie co
   5 sekund wskazanego tekstu w nieskonczonosc. Dzialanie bedzie wiec
   widoczne na terminalu, z ktorego uruchamiacie program. Sam program
   mozna bedzie oczywiscie zobaczyc na liscie dzialajacych procesow
   (komenda ps -A). Jedynym sposobem na zakonczenie programu jest zabicie
   go poleceniem kill.

   A oto kod dla NASMa:
   (przeskocz program)
; Program przechodzacy w tryb demona systemowego
;
; autor: Bogdan D., bogdandr (at) op.pl
;
; kompilacja:
; nasm -f elf -o demon.o demon.asm
; gcc -o demon demon.o


extern daemon                   ; deklaracja funkcji zewnetrznej

section .text                   ; poczatek sekcji kodu
global main                     ;symbol "main" musi byc globalny dla GCC

main:
        push    dword 1         ; drugi argument
        push    dword 1         ; pierwszy argument
        call    daemon          ; uruchomienie funkcji daemon
        add     esp, 8          ; usuniecie argumentow ze stosu

                        ; przerwa miedzy kolejnymi napisami
                        ; bedzie trwac 5 sekund i 0 nanosekund:
        mov     dword [t1+timespec.tv_nsec], 0
        mov     dword [t1+timespec.tv_sec], 5

.petla:
        mov     eax, 4          ; funkcja zapisywania do pliku
        mov     ebx, 1          ; standardowe wyjscie
        mov     ecx, napis      ; co wypisac
        mov     edx, napis_dl   ; dlugosc napisu
        int     80h

        mov     eax, 162        ; funkcja sys_nanosleep
        mov     ebx, t1         ; tyle czekac
        mov     ecx, 0  ; ewentualny adres drugiej struktury timespec
        int     80h             ;  robimy przerwe...

        jmp     .petla          ; i od nowa....

                                ; ponizszy kod nie bedzie wykonany
        mov     eax, 1
        xor     ebx, ebx
        int     80h             ; wyjscie z programu

section .data

napis           db      "Tu Twoj demon mowi.", 10
napis_dl        equ     $ - napis

struc timespec                  ; definicja struktury timespec
                                ; (tylko jako typ danych)
        .tv_sec:        resd 1
        .tv_nsec:       resd 1
endstruc

t1 istruc timespec              ; tworzymy zmienna t1 jako cala
                                ; strukture timespec

   Kompilacja i laczenie odbywa sie tak:
        nasm -f elf -o demon.o demon.asm
        gcc -o demon demon.o

   O jednej rzeczy nalezy wspomniec: sam fakt, ze program jest demonem
   NIE musi oznaczac, ze dziala na prawach administratora (i cale
   zabezpieczenie systemu jest do niczego).
     _________________________________________________________________

Programy rezydentne z wykorzystaniem int 80h

   Funkcja daemon jest co prawda z biblioteki C, ale mozna ja przerobic
   na kod korzystajacy wylacznie z przerwania int 80h. Sam kod funkcji
   jest w pliku misc/daemon.c w zrodlach biblioteki glibc. Nie jest on za
   dlugi i dosc latwo mozna go przerobic na takie oto makro:
   (przeskocz makro)
%macro daemon 2
        ; pierwszy parametr: nochdir - czy nie zmieniac katalogu na glowny?
        ; drugi parametr: noclose - czy nie zamykac stdin i stdout?

        mov     eax, 2
        int     80h             ; sys_fork

        cmp     eax, 0
        jl      %%koniec        ; EAX < 0 oznacza blad

        test    eax, eax
        jz      %%dalej         ; EAX = 0 w procesie potomnym
                                ; EAX > 0 w procesie rodzica

        mov     eax, 1
        xor     ebx, ebx
        int     80h             ; sys_exit - rodzic konczy prace.

 %%glowny:      db      "/", 0
 %%devnull:     db      "/dev/null", 0

 %%dalej:
        mov     eax, 66         ; sys_setsid
        int     80h             ; tworzymy nowa sesje i ustawiamy GID

        cmp     eax, 0
        jl      %%koniec        ; EAX < 0 oznacza blad

        %if %1 = 0

                mov     eax, 12         ; sys_chdir
                mov     ebx, %%glowny
                int     80h             ; zmieniamy katalog na glowny
        %endif

        %if %2 = 0

                ; otwieramy /dev/null:
                mov     eax, 5
                mov     ebx, %%devnull
                mov     ecx, 2
                mov     edx, 0
                int     80h

                cmp     eax, 0
                jl      %%koniec        ; EAX < 0 oznacza blad

                mov     ebx, eax        ; EBX = deskryptor /dev/null

                ;duplikujemy deskryptory standardowego wejscia, wyjscia i
                ;wyjscia bledow do deskryptora otwartego /dev/null, po czym
                ; ten /dev/null zamkamy

                mov     eax, 63
                mov     ecx, 0          ; wejscie
                int     80h

                mov     eax, 63
                mov     ecx, 1          ; wyjscie
                int     80h

                mov     eax, 63
                mov     ecx, 2          ; wyjscie bledow
                int     80h

                mov     eax, 6
                int     80h             ; zamykamy /dev/null

        %endif
 %%koniec:

%endmacro

; uzycie:
        daemon 1, 1     ; bez zadnych PUSH ani ADD ESP

   Teraz mozna juz powrocic do starego schematu programu, gdzie symbolem
   startowym byl _start, a laczenie odbywalo sie za pomoca LD, a nie GCC
   z biblioteka C.

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
