Virtlab:Konzolový server

Z VirtlabWiki

(Rozdíly mezi verzemi)
Přejít na: navigace, hledání
Verze z 18:20, 25. 10. 2007
Kuc274 (Diskuse | příspěvky)
(devices.c (.h))
← 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:
-[[Private:Konzolový server/Zakázané příkazy | Zakázané příkazy]]+== Ú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()'''.
 +[[Image:Cserver_sekvenceFunkci1.png|center|450px|"Základní funkce Cserveru"]]
 +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.
 +[[Image:Cserver_sekvenceFunkci2.png|center|400px|"Funkce volane z obsluhuj_klienta()"]]
 + 
 +== 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 <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 <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á.
 + 
 +=== 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 <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.
 + 
 +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í".''
 + 
 +==== Kde hledat pomocné soubory ====
 +V adresáři <code>/tmp</code>
== Struktura zdrojových souborů == == Struktura zdrojových souborů ==
-=== devices.c (.h) ===+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ů]].
-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í:+Dále si popíšeme strukturu hlavního souboru - server.c (.h)
- DEVICES_T *add_device(DEVICE_T device, DEVICES_T **devs);+
- int load_devices(const char *devs_file, DEVICES_T **devs);+
- 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.+=== 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);
-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 ). 
-* ''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'''. 
-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.+=== main ===
 +Hlavni funkce pro cely Konzolovy server.
-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.)+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'''
-=== domain.c ===+=== obsluhuj_klienta ===
- DOMAINS_T *add_domain(DOMAIN_T domain, DOMAINS_T **doms);+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).
- int load_domains(const char *doms_file, DOMAINS_T **doms);+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).
- DOMAIN_T *find_domain_by_name(const char* domname, DOMAINS_T *doms);+
- 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.+=== 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).
-Doména může být typu '''local''' nebo '''remote'''.+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.
 + 
 +[[Private:Konzolový server/Zakázané příkazy | Zakázané příkazy]]
[[Kategorie:Komponenty virtlabu]] [[Kategorie:Komponenty virtlabu]]
[[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().

"Základní funkce Cserveru"

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.

"Funkce volane z obsluhuj_klienta()"

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.

Zakázané příkazy

Osobní nástroje