Virtlab:Konzolový server
Z VirtlabWiki
Verze z 09:56, 28. 10. 2007 Kuc274 (Diskuse | příspěvky) (→Implementace zakázaných příkazů) ← Předchozí porovnání |
Aktuální verze Kuc274 (Diskuse | příspěvky) (→Zamykání konzoly zařízení pro výlučný přístup) |
||
Řádka 1: | Řádka 1: | ||
+ | == Úkoly konzolového serveru == | ||
+ | Jeho úkolem je zprostředkovat přístup k sériovým nebo telnetovým konzolím síťových zařízení prostřednictvím jednoduchého protokolu nad TCP/IP. Je využíván Java Appletem, který běží ve webovém ovládacím rozhraní, což umožňuje uživateli jednoduchý přístup k síťovým zařízením. Zároveň slouží i jako proxy server, který zprostředkovává přístup k zařízením, která jsou připojena ke vzdáleným konzolovým serverům, kam nemá místní uživatel přímý přístup. Také prostřednictvím speciálního PHP skriptu ověřuje, jsou-li požadavky uživatele oprávněné a tím konzoly prvků zabezpečuje od neautorizovaného přístupu. | ||
- | == Struktura zdrojových souborů pro zařízení a domény == | + | == Popis implementace == |
- | === devices.c (.h) === | + | ''(pozor: nejde o skutecny sekvencni diagram)'' |
- | používané struktury (zdrojový kód po mírné úpravě): | + | |
- | //popis jednoho zarizeni | + | |
- | struct DEVICE_T { | + | |
- | char devicename[DEV_ID_MAX_LENGTH]; | + | |
- | int device_type; | + | |
- | int fd_for_tutor; | + | |
- | int fd_for_client; | + | |
- | union { | + | |
- | char serial_port_path[MAX_SERIAL_PATH]; | + | |
- | TELNET_CONN telnet_data; | + | |
- | }; | + | |
- | }; | + | |
- | //polozka zretezeneho seznamu zarizeni | + | |
- | struct DEVICES_T { | + | |
- | DEVICE_T data; | + | |
- | struct devices_t *next; | + | |
- | }; | + | |
- | Přehled funkcí: | + | Zdrojový soubor '''server.c''' obsahuje hlavní funkci (main), která po úvodní inicializaci volá v nekonečném cyklu funkci '''obsluhuj_klienta()'''. |
- | DEVICES_T *add_device(DEVICE_T device, DEVICES_T **devs); | + | [[Image:Cserver_sekvenceFunkci1.png|center|450px|"Základní funkce Cserveru"]] |
- | int load_devices(const char *devs_file, DEVICES_T **devs); | + | Ve funkci '''obsluhuj_klienta()''' jsou pak načteny 4 řádky parametrů od klientského apletu. Tyto parametry jsou ověřeny pomocí funkce '''check_applet_args()''' a v případě, že klient chce takto přistupovat k prvku, který má korektně na danou dobu rezervován, vrátí funkce čas do konce rezervace (v sekundách). (''V opačném případě vrací nulu a '''obsluhuj_klienta()''' následně skončí.'') |
- | DEVICE_T *find_device_by_name(const char* devname, DEVICES_T *devs); | + | |
- | int split_dev_id_location(char *dev_at_loc, char **dev, char **loc); | + | |
- | Funkce '''add_device''' přidá zařízení do seznamu. Prvnim parametrem je popis pridavaneho zarizeni, druhym pak ukazatel na "prvni zarizeni v aktualnim seznamu". Nove pridane zarizeni se zaradi na zacatek seznamu. Funkce vraci ukazatel na seznam. | + | V případě úspěchu se nastaví '''alarm()''' a hned potom se dostane ke slovu funkce '''open_dev_fd()''', která ověří, jestli zařízení již není používáno jiným apletem. Není-li, zamkne jej pro výlučný přístup a vrátí na něj deskriptor. |
+ | [[Image:Cserver_sekvenceFunkci2.png|center|400px|"Funkce volane z obsluhuj_klienta()"]] | ||
- | Funkce '''load_device''' nacita z textoveho souboru (devs_file) popisy zarizeni. Rozlisi, zda je zarizeni pripojeno seriovou linkou nebo pomoci Ethernetu. Tuto i dalsi informace ulozi do struktury pro popis zarizeni (DEVICE_T), kterou nasledne zakomponuje do seznamu zarizeni (pomoci funkce add_device ). | + | == Zamykání konzoly zařízení pro výlučný přístup == |
- | * ''Soubor oznaceny jako "devs_file" je konfiguracni soubor, ktery se standartne nachazi v '''/etc/virtlab''' a ma nazev '''cons-devices.conf'''.'' | + | |
- | * Tato funkce je volana skoro na zacatku funkce '''main''' v souboru '''server.c'''. | + | |
+ | int lock_device( char *filename ); | ||
+ | void unlock_device( void ); | ||
- | Funkce '''find_device_by_name''' hleda nazev specifikovany parametrem ''devname'' v seznamu zarizeni (specifikovan druhym parametrem). Jestlize zarizeni najde, vraci odkaz na strukturu, ktera jej popisuje. V opacnem pripade vraci odkaz na NULL. | + | Tyto dvě funkce zajistí zamčení resp. odemčení konzoly aktivního prvku pro výlučný přístup. Funkce <code>lock_device</code> nejprve ověří, zda je zařízení "volné". Pokud ano, uzamkne jej tak, aby k němu měl přístup pouze aktuální proces. V opačném případě vrátí informaci, že zařízení je již používáno, na kterou pak proces reaguje tak, že po sobě uklidí, upozorní uživatele a skončí. |
- | Funkce '''split_dev_id_location''' rozdeli vstupni retezec ''dev_at_loc'' na dve casti. Prvni odkazuje na jmeno zarizeni a druha na nazev lokality. (Obe ale realne pracuji se stejnym retezcem, jen ukazuji do jine jeho casti - nic se nekopiruje.) | + | Funkce <code>unlock_device</code> funguje "inverzně" - bylo li zařízení zamčeno aktuálním procesem, odemkne jej, v opačném případě s ním nic neudělá. |
- | === domain.c (.h) === | + | === Jak je to implementováno === |
- | DOMAINS_T *add_domain(DOMAIN_T domain, DOMAINS_T **doms); | + | Celá implementace je založena na použití volání systémové funkce |
- | int load_domains(const char *doms_file, DOMAINS_T **doms); | + | open( lock_file_name, O_RDWR | O_CREAT | O_EXCL ) |
- | DOMAIN_T *find_domain_by_name(const char* domname, DOMAINS_T *doms); | + | Takto zavolaná funkce se pokusí vytvořit soubor s názvem <code>lock_file_name</code> s exkluzivním přístupem. Pokud již soubor existuje, vrátí chybu. Tohoto chování využívá naše funkce '''lock_device''', která si takto vytváří pomocné soubory v adresáři <code>/tmp</code>. Jméno souboru vychází z označení aktivního prvku, ke kterému se vztahuje. Najde-li funkce <code>lock_device</code> již existující soubor, považuje odpovídající zařízení za obsazené. V opačném případě si jej "zamkne" vytvořením tohoto pomocného souboru. |
- | DOMAIN_T *find_domain_by_ip(in_addr_t *ip, DOMAINS_T *doms); | + | |
- | Práce s doménami je velice podobná práci se zařízeními. Jsou k dispozici opět čtyři funkce, odlišný význam má pouze ta poslední (find_domain_by_ip). Má za úkol najít doménu v seznamu, ovšem na základě IP adresy. | + | Zde je ještě důležité upozornit na chování <code>unlock_device</code>, která se před odemčením musí přesvěčit, že aktuální proces je vlastníkem zařízení. To pozná podle hodnoty proměnné <code>locked</code>''.Tato funkce se totiž volá na vždy na konci procesu, který pracuje s konzolou zařízení. Kdyby tento test neproběhl, mohlo by se stát, že proces uvolní zařízení, které "nevlastní".'' |
- | * ''Soubor oznaceny jako "doms_file" je konfiguracni soubor, ktery se standartne nachazi v '''/etc/virtlab''' a ma nazev '''cons-servers.conf'''.'' | + | ==== Kde hledat pomocné soubory ==== |
- | * Tato funkce je volana skoro na zacatku funkce '''main''' v souboru '''server.c'''. | + | V adresáři <code>/tmp</code> |
- | Doména může být typu '''local''' nebo '''remote'''. | + | == Struktura zdrojových souborů == |
+ | Výše uvedené diagramy popisovaly základní funkce obsažené v základním zdrojovém souboru '''server.c''' + pár funkcí z [[Virtlab:Konzolový server/Zařízení a domény|dalších zdrojových souborů]]. | ||
+ | Dále si popíšeme strukturu hlavního souboru - server.c (.h) | ||
+ | |||
+ | === Odchytávání signálů SIGPIPE a SIGALARM === | ||
+ | ''SIG_ALRM je v konzolovém serveru využíván k automatickému ukončení po vypršení rezervace prvku, ke kterému jsem připojen.'' Po ověření práva přístupu uživatele ke konkrétnímu prvku je nastaven timer pomocí funkce '''alarm(seconds)''' na čas, kdy rezervace skončí. Je zde přihlíženo ik takzvané patičce - čas potřebný ke smazání uživatelovy konfigurace a případnému restartu prvku. | ||
+ | static void sig_pipe(int signo); | ||
+ | static void sig_alarm(int signo); | ||
+ | |||
+ | |||
+ | === main === | ||
+ | Hlavni funkce pro cely Konzolovy server. | ||
+ | |||
+ | Postupně provádí tyto akce: | ||
+ | * nahraje soubor s doménami | ||
+ | * nahraje soubor se zařízeními | ||
+ | * vytvoří socket pro příjem spojení od klientů | ||
+ | ** a nastaví některé jeho parametry | ||
+ | ** funkcí bind jej pojmenuje | ||
+ | ** funkcí listen bude očekávat spojení od klientů | ||
+ | * následuje "nekonečná" smyčka, ve které se vlastně pro každého připojeného klienta vytvoří nový proces, jehož úkolem je obsluha tohoto klienta. Tu zajišťujeme funkcí '''obsluhuj_klienta''' | ||
+ | |||
+ | === obsluhuj_klienta === | ||
+ | Tato funkce je spouštěna pro každý nově otevřený aplet v samostatném procesu (předtím je tedy volán fork). | ||
+ | Jejím úkolem je pak řídit veškerou komunikaci od apletu k zařízení a zpět. Musí rovněž zajistit otevření tohoto obousměrného spojení a jeho korektní uzavření. Pro tyto účely využívá další funkce (viz. obrázek výše). | ||
+ | |||
+ | === get_line_from_socket === | ||
+ | Funkce znaky čte ze soketu, který dostane jako první parametr. Tím je právě vytvořený socket ke klientskému apletu. Přečte nejvýše tolik znaků, kolik udává třetí parametr a výsledek uloží do parametru dvě. Čtení končí, narazí-li na znaky CR/LF nebo načte-li maximální počet znaků (třetí parametr). | ||
+ | |||
+ | Tato funkce je tedy volána na začátku obsluhy klienta a její pomocí o něm zjistíme potřebné informace. Ty pak ověřujeme ve funkci check_applet_args(). | ||
+ | |||
+ | === check_applet_args === | ||
+ | Funkce, ktera zkontroluje platnost parametru poslanych appletem oproti webovemu serveru | ||
+ | * V případě, že aplet s těmito parametry má právo přistupovat k požadovanému zařízení, funkce vrátí '''počet sekund do konce rezervace''' (bez "patičky") | ||
+ | * V opačném případě vrátí hodnout '''0''' | ||
+ | Vlastní kontrolu parametrů provádí externí skript '''check-applet-args.sh''', který výsledek zapíše do souboru v '''/tmp/'''. Z něj pak druhý proces hodnotu převezme. (Druhým procesem je myšlen rodičovský proces - po volání funkce fork) | ||
+ | |||
+ | # define applet_checker "./check-applet-args.sh" | ||
+ | # define result_basefile "/tmp/appletchecker" | ||
+ | int check_applet_args(char* sid, char* sip, char* did, char* tm, char *resid) | ||
+ | { | ||
+ | ... | ||
+ | fork(); | ||
+ | //child: | ||
+ | execlp(applet_checker, applet_checker, sid, sip, did, tm, resid, result_file, (char*)(NULL)); | ||
+ | //parent: | ||
+ | //prevezmi hodnotu ze souboru a pak ji predej jako svou navratovou hodnotu pomoci | ||
+ | return result; | ||
+ | ... | ||
+ | } | ||
+ | |||
+ | === open_dev_fd === | ||
+ | funkce, ktera otevre seriovy port/TCP stream pro cteni | ||
+ | |||
+ | int open_dev_fd(const char *_dev_id, int *target_is_cserver, int tutor, int* fd_to_tutor); | ||
+ | |||
+ | * Funkce nejdříve převezme první argument a rozdelí jej na část pro zařízení a část pro lokalitu. Následně ověří, zda lokalita existuje a je dostupná. Stejnětak ověří i existenci zařízení. Podle typu zařízení a hlavně jeho umístění se rozhoduje, zda se s ním bude komunikovat přes sériový port, nebo přes TCP spojení (Jde-li o vzdálené zařízení.) | ||
+ | * V této funkci dochází rovněž k zamykání konzolí laboratorních prvků. V případě, že již je prvek zamknut, celý proces končí a do klientského apletu je poslána informace o tom, že zařízení je již používáno jiným procesem a apletem. | ||
+ | |||
+ | |||
+ | === close_fd_dev === | ||
+ | Zavre socket zarizeni, je-li od tutora ke klientovi, posle jen zpravu o ukonceni | ||
+ | void close_fd_dev(int fd, int is_tutor) | ||
+ | { | ||
+ | if(is_tutor) | ||
+ | { | ||
+ | close_tutor(fd); | ||
+ | } | ||
+ | else | ||
+ | close(fd); | ||
+ | } | ||
+ | |||
+ | === get_timestamp === | ||
+ | vrati casove razitko | ||
+ | char *get_timestamp(void); | ||
+ | |||
+ | === Nastavení FD jako NONBLOCKING === | ||
+ | |||
+ | void set_nonblocking(int fd); | ||
+ | |||
+ | === Nastavení FD jako BLOCKING === | ||
+ | |||
+ | void set_blocking(int fd); | ||
- | == Struktura hlavního souboru - server.c (.h) == | ||
== Implementace zakázaných příkazů == | == Implementace zakázaných příkazů == | ||
Řádka 64: | Řádka 128: | ||
[[Kategorie:Server]] | [[Kategorie:Server]] | ||
[[Kategorie:Konzolový server]] | [[Kategorie:Konzolový server]] | ||
- | [[Kategorie:UNCOMPLETE]] |
Aktuální verze
Obsah |
Úkoly konzolového serveru
Jeho úkolem je zprostředkovat přístup k sériovým nebo telnetovým konzolím síťových zařízení prostřednictvím jednoduchého protokolu nad TCP/IP. Je využíván Java Appletem, který běží ve webovém ovládacím rozhraní, což umožňuje uživateli jednoduchý přístup k síťovým zařízením. Zároveň slouží i jako proxy server, který zprostředkovává přístup k zařízením, která jsou připojena ke vzdáleným konzolovým serverům, kam nemá místní uživatel přímý přístup. Také prostřednictvím speciálního PHP skriptu ověřuje, jsou-li požadavky uživatele oprávněné a tím konzoly prvků zabezpečuje od neautorizovaného přístupu.
Popis implementace
(pozor: nejde o skutecny sekvencni diagram)
Zdrojový soubor server.c obsahuje hlavní funkci (main), která po úvodní inicializaci volá v nekonečném cyklu funkci obsluhuj_klienta().
Ve funkci obsluhuj_klienta() jsou pak načteny 4 řádky parametrů od klientského apletu. Tyto parametry jsou ověřeny pomocí funkce check_applet_args() a v případě, že klient chce takto přistupovat k prvku, který má korektně na danou dobu rezervován, vrátí funkce čas do konce rezervace (v sekundách). (V opačném případě vrací nulu a obsluhuj_klienta() následně skončí.)
V případě úspěchu se nastaví alarm() a hned potom se dostane ke slovu funkce open_dev_fd(), která ověří, jestli zařízení již není používáno jiným apletem. Není-li, zamkne jej pro výlučný přístup a vrátí na něj deskriptor.
Zamykání konzoly zařízení pro výlučný přístup
int lock_device( char *filename ); void unlock_device( void );
Tyto dvě funkce zajistí zamčení resp. odemčení konzoly aktivního prvku pro výlučný přístup. Funkce lock_device
nejprve ověří, zda je zařízení "volné". Pokud ano, uzamkne jej tak, aby k němu měl přístup pouze aktuální proces. V opačném případě vrátí informaci, že zařízení je již používáno, na kterou pak proces reaguje tak, že po sobě uklidí, upozorní uživatele a skončí.
Funkce unlock_device
funguje "inverzně" - bylo li zařízení zamčeno aktuálním procesem, odemkne jej, v opačném případě s ním nic neudělá.
Jak je to implementováno
Celá implementace je založena na použití volání systémové funkce
open( lock_file_name, O_RDWR | O_CREAT | O_EXCL )
Takto zavolaná funkce se pokusí vytvořit soubor s názvem lock_file_name
s exkluzivním přístupem. Pokud již soubor existuje, vrátí chybu. Tohoto chování využívá naše funkce lock_device, která si takto vytváří pomocné soubory v adresáři /tmp
. Jméno souboru vychází z označení aktivního prvku, ke kterému se vztahuje. Najde-li funkce lock_device
již existující soubor, považuje odpovídající zařízení za obsazené. V opačném případě si jej "zamkne" vytvořením tohoto pomocného souboru.
Zde je ještě důležité upozornit na chování unlock_device
, která se před odemčením musí přesvěčit, že aktuální proces je vlastníkem zařízení. To pozná podle hodnoty proměnné locked
.Tato funkce se totiž volá na vždy na konci procesu, který pracuje s konzolou zařízení. Kdyby tento test neproběhl, mohlo by se stát, že proces uvolní zařízení, které "nevlastní".
Kde hledat pomocné soubory
V adresáři /tmp
Struktura zdrojových souborů
Výše uvedené diagramy popisovaly základní funkce obsažené v základním zdrojovém souboru server.c + pár funkcí z dalších zdrojových souborů.
Dále si popíšeme strukturu hlavního souboru - server.c (.h)
Odchytávání signálů SIGPIPE a SIGALARM
SIG_ALRM je v konzolovém serveru využíván k automatickému ukončení po vypršení rezervace prvku, ke kterému jsem připojen. Po ověření práva přístupu uživatele ke konkrétnímu prvku je nastaven timer pomocí funkce alarm(seconds) na čas, kdy rezervace skončí. Je zde přihlíženo ik takzvané patičce - čas potřebný ke smazání uživatelovy konfigurace a případnému restartu prvku.
static void sig_pipe(int signo); static void sig_alarm(int signo);
main
Hlavni funkce pro cely Konzolovy server.
Postupně provádí tyto akce:
- nahraje soubor s doménami
- nahraje soubor se zařízeními
- vytvoří socket pro příjem spojení od klientů
- a nastaví některé jeho parametry
- funkcí bind jej pojmenuje
- funkcí listen bude očekávat spojení od klientů
- následuje "nekonečná" smyčka, ve které se vlastně pro každého připojeného klienta vytvoří nový proces, jehož úkolem je obsluha tohoto klienta. Tu zajišťujeme funkcí obsluhuj_klienta
obsluhuj_klienta
Tato funkce je spouštěna pro každý nově otevřený aplet v samostatném procesu (předtím je tedy volán fork). Jejím úkolem je pak řídit veškerou komunikaci od apletu k zařízení a zpět. Musí rovněž zajistit otevření tohoto obousměrného spojení a jeho korektní uzavření. Pro tyto účely využívá další funkce (viz. obrázek výše).
get_line_from_socket
Funkce znaky čte ze soketu, který dostane jako první parametr. Tím je právě vytvořený socket ke klientskému apletu. Přečte nejvýše tolik znaků, kolik udává třetí parametr a výsledek uloží do parametru dvě. Čtení končí, narazí-li na znaky CR/LF nebo načte-li maximální počet znaků (třetí parametr).
Tato funkce je tedy volána na začátku obsluhy klienta a její pomocí o něm zjistíme potřebné informace. Ty pak ověřujeme ve funkci check_applet_args().
check_applet_args
Funkce, ktera zkontroluje platnost parametru poslanych appletem oproti webovemu serveru
- V případě, že aplet s těmito parametry má právo přistupovat k požadovanému zařízení, funkce vrátí počet sekund do konce rezervace (bez "patičky")
- V opačném případě vrátí hodnout 0
Vlastní kontrolu parametrů provádí externí skript check-applet-args.sh, který výsledek zapíše do souboru v /tmp/. Z něj pak druhý proces hodnotu převezme. (Druhým procesem je myšlen rodičovský proces - po volání funkce fork)
# define applet_checker "./check-applet-args.sh" # define result_basefile "/tmp/appletchecker" int check_applet_args(char* sid, char* sip, char* did, char* tm, char *resid) { ... fork(); //child: execlp(applet_checker, applet_checker, sid, sip, did, tm, resid, result_file, (char*)(NULL)); //parent: //prevezmi hodnotu ze souboru a pak ji predej jako svou navratovou hodnotu pomoci return result; ... }
open_dev_fd
funkce, ktera otevre seriovy port/TCP stream pro cteni
int open_dev_fd(const char *_dev_id, int *target_is_cserver, int tutor, int* fd_to_tutor);
- Funkce nejdříve převezme první argument a rozdelí jej na část pro zařízení a část pro lokalitu. Následně ověří, zda lokalita existuje a je dostupná. Stejnětak ověří i existenci zařízení. Podle typu zařízení a hlavně jeho umístění se rozhoduje, zda se s ním bude komunikovat přes sériový port, nebo přes TCP spojení (Jde-li o vzdálené zařízení.)
- V této funkci dochází rovněž k zamykání konzolí laboratorních prvků. V případě, že již je prvek zamknut, celý proces končí a do klientského apletu je poslána informace o tom, že zařízení je již používáno jiným procesem a apletem.
close_fd_dev
Zavre socket zarizeni, je-li od tutora ke klientovi, posle jen zpravu o ukonceni
void close_fd_dev(int fd, int is_tutor) { if(is_tutor) { close_tutor(fd); } else close(fd); }
get_timestamp
vrati casove razitko
char *get_timestamp(void);
Nastavení FD jako NONBLOCKING
void set_nonblocking(int fd);
Nastavení FD jako BLOCKING
void set_blocking(int fd);
Implementace zakázaných příkazů
Virtuální laboratoř funguje zcela automaticky. Po vypršení času rezervovaného jedním studentem se smaže jím vytvořená konfigurace a síťové prvky se připraví pro práci dalšímu uživateli. Tohle by nebylo možné, kdyby studenti mohli zadávat do zařízení například libovolná hesla, která by pak byla vyžadována i pro smazání konfigurace.
Některé příkazy bylo tedy nutné zakázat.