Shranjevanje podatkov v datotečnem sistemu
Na dinamične spletne strani danes naletimo ob vsakem deskanju po spletu. Razne dveri, iskalniki, forumi so postali stvar vsakdanje rabe. Tudi podjetja so ugotovila, da lahko močno znižajo stroške strojne in programske opreme, če za lastne potrebe uporabljajo spletne programe in se oddaljujejo od rešitev z "debelimi odjemalc"". Na voljo je neskončno odprtokodnih rešitev, ki ponujajo veliko za razvoj takih programov,pa tudi veliko končnih izdelkov. Kot platforma se največkrat omenja kratica - LAMP (Linux, Apache, MySQL in PHP oziroma Perl). V članku se bomo seznanili z malo bolj nenavadnim pristopom k tej problematiki.
Podrobneje razčlenimo kratico LAMP. Za operacijski sistem se uporablja GNU/Linux, v njem teče spletni strežnik Apache, v njem s pomočjo tolmača PHP ali Perl zaganjamo programe ali skripte, ki so pisani v teh jezikih, in podatke shranjujemo v odprtokodni zbirki podatkov MySQL. Kaj pa, če za shranjevanje podatkov ne želimo uporabljati MySQLa, oziroma za podatke sploh ne želimo uporabljati katerekoli zbirke podatkov? Podatke bomo shranjevali in brali kar iz datotečnega sistema. Pa spet se najverjetneje ne razumemo. Ne, podatkov ne bomo zapisovali in brali iz datotek. Za to bomo uporabili dodatne atribute datotek (extended attributes).
Seveda se tu takoj poraja vprašanje o smiselnosti takega početja, ko pa je SQL čisto v redu. V tem članku se ne bomo ukvarjali s smislom tega, tudi ne s hitrostnimi ali drugačnimi prednostmi tega načina. Poskušali bomo ugotoviti predvsem, kako. Lahko pa nakažemo eno prednost v primerjavi s SQL. Si morda želimo tlačiti datoteke, kot so glasba, filmi in podobno, v zbirko SQL? Verjetno ne. Pa moramo res uporabljati v tem primeru zbirko podatkov samo zato, da bomo vanjo shranjevali opisne podatke teh velikih datotek, ko pa jih lahko pripnemo na njih same? Nekateri formati večpredstavnih datotek imajo prostor za opisne podatke v lastnem zaglavju. Če želimo shranjevanje teh podatkov standardizirati po lastnih merilih, je način z dodatnimi atributi enostavnejši.
Dodatni atributi so se pojavili kot rezultat potrebe po ACL (Access Control List) na sistemih *nix. Problem tiči v nezmožnosti uporabe *nixovih pravic (UID, GID in devet bitov) za različne situacije v datotečnih strežnikih, npr. ko moramo več kot dvema skupinama nastaviti različne dostopne pravice do neke datoteke ali imenika. V operacijskem sistemu GNU/Linux je mogoče uporabljati dodatne atribute v datotečnih sistemih XFS, EXT3 in ReiserFS. Kot rečeno, dodelimo jih lahko mapam in datotekam, sestavljeni pa so iz dveh spremenljivk - prva je ime vrednosti, druga pa predstavlja vrednost. Določena datoteka ali mapa jih lahko ima več.
Dodatne atribute pa seveda lahko uporabimo še za kaj drugega kot za kontrolne dostopne liste. V članku jim bomo uporabili kot uporabniške dodatne atribute (Extended User Attributes) za shranjevanje podatkov o uporabnikih namišljenega spletnega sistema.
Nekaj o projektu DoXFS
Projekt DoXFS predstavlja dele kode, ki skupaj predstavljajo spletni dokumentacijski sistem za hrambo datotek v večuporabniškem okolju. Vse datoteke, podatke o njih in drugo (podatki o uporabnikih, skupinah ter pravicah) shranjuje v datotečnem sistemu XFS ob pomoči dodatnih atributov. Strežniški program in odjemalec sta napisana v PHPju, nekaj kode je v Cju, perlu in še kaj bi se našlo. Sistem za iskanje po vsebini dokumentov uporablja iskalno orodje Namazu, ki pregleda vse datoteke v sistemu. Za vse, ki vas navdušujejo orodja kot Google Desktop, je to uporaben dodatek. Pri namestitvi DoXFS je treba namestiti dodatne knjižnice, ki omogočajo delo z dodatnimi atributi in orodjem Namazu, vendar samo za strežniški del sistema. Vse skupaj je izdano pod licenco GNU in obsega zelo velike dokumentacije za tiste, ki želijo razvijati dalje sistem na obstoječi platformi DoXFS s pripadajočimi funkcijami za strežnik in odjemalec. Stvar je mogoče z nekaj sreče tudi preizkusiti. Spletni vmesnik je zelo posrečen in prijazen do uporabnika in ima vgrajene številne možnosti, kot so pravice za več uporabnikov, uporabniških skupin, beležko ipd.
V nadaljevanju se bomo osredotočili na pripravo podobnega sistema, kot je DoXFS, ki podrobneje razjasni delovanje takega načina dostopa do podatkov in ne uporablja DoXFSovih funkcij.
Gremo po svoje!
Za začetek dela bomo potrebovali nameščeno poljubno distribucijo GNU/Linux z jedrom, ki ima podporo XFS (na lastno željo lahko uporabite tudi ext3 ali ReiserFS) in dodatne atribute za izbran datotečni sistem. V sistemu mora biti še prevajalnik gcc in razvojne knjižnice. Potrebujemo še programski paket attr in njegovo razvojno različico. Kje jo boste dobili, je seveda odvisno od vaše distribucije GNU/Linux. Če ne dobite paketov pri vašem priljubljenem ponudniku paketov, se napotite na http://acl.bestbits.at/download.html#Attr ali pa ftp://oss.sgi.com/projects/xfs, kjer boste našli opisana paketa v več oblikah. Če boste uporabljali XFS, je seveda dobro, če namestite še druga potrebna uporabniška orodja za ta datotečni sistem.
Nadaljujemo s pripravo tolmača PHP in programske kode za izbrano nalogo. Sistem bomo po vzoru DoXFS razdelili na strežnik in odjemalec. V sistemu s podatki bo strežniški program in nanj se bo povezal odjemalec, ki se bo zaganjal v spletnem strežniku.
Priprava strežnika
Enostaven strežnik bomo napisali kar v jeziku PHP in ga zaganjali skozi tolmač php v lupini. Za to bomo potrebovali posebno različico tolmača, ki bo podpiral dodatne funkcije. Med te spadajo php_attr za dostop do dodatnih atributov datotečnega sistema, sockets za delo s povezavami tcp/ip, pnctl in posix za boljšo implementacijo strežnika (uporaba funkcij, kot so fork ipd.). Potrebovali bomo svežo izvirno kodo PHPja različice 4.3 (snamemo jo s http://www.php.net ali drugje) in paket php_attr, ki ga je mogoče dobiti na projektni strani DoXFS na http://sourceforge.net (trenutna različica je 0.5). Paketa php-4.3.x.tar.gz in php_attr.tar.gz razpakiramo. Iz nastale mape php_attr skopiramo mapo attr v novo nastalo mapo php-4.3.x/ext/. Pomaknemo se v php-4.3.x/ in zaženemo ./buildconf. Tako dodatek php_attr vključimo v prevajanje. Zaženemo predpripravo in nastavitve za poznejše prevajanje s:
./configure --enable-attr --enable-xml --enable-posix --enable-pcntl \
--enable-sockets --without-apache
Tako, če se vse izteče v redu, lahko začnemo prevajati tolmač php z ukazom make. Ko se vse pravilno prevede, skopiramo sapi/cli/php v poljubno mapo, od koder ga bomo uporabljali za zagon našega strežnika v PHPju. Ponavadi je pri večini distribucij z nameščenim PHPjem ta že v mapi /usr/bin, zato pazimo, da ga ne prepišemo. Priporočeno je, da ga preimenujemo v php-cli ali kaj podobnega in ga kopiramo v mapo, ki je vpisana v sistemsko pot ($PATH). Stvar preizkusimo z zagonom programa php attr.php, najdemo ga v mapi php-4.3.x/ext/attr, ki nam mora vrniti spodaj prikazana sporočila.
$ php-cli attr.php
Functions available in the test extension:<br>
attr_get<br>
attr_set<br>
attr_rm<br>
attr_list<br>
confirm_attr_compiled<br>
<br>
Congratulations! You have successfully modified ext/attr/config.m4. Module attr is now compiled into PHP.
Zdaj smo pripravljeni na pripravo našega strežniškega programa, najprej pa moramo ugotoviti, kaj bo ta počel. Moral bo znati spreminjati, brati atribute, ustvariti in brisati datoteke. V testnem sistemu bomo ponazorili shranjevanje podatkov o uporabnikih v datotečni sistem. V spodaj navedenem programu bomo odčitavali ukaze odjemalca in mu sporočali prave vrednosti. Implementacijo strežnika prepuščam bralcu, saj je možna na več načinov in je ta članek ne zajema.
Zamislimo si naslednje odjemalčeve ukaze:
set:<ime atributa>:<vrednost atributa>:<datoteka>
get:<ime atributa>:<datoteka>
del:<datoteka>
add:<datoteka>
Programska koda za ta del:
...
// Zaključni niz v odgovoru
$zznak = chr(10).chr(13);
// $ch je vzpostavljena povezava tcp/ip
while ($temp = socket_read($ch,1024))
{
// Izločimo iz niza 3 znake,
// ki pri nas predstavljajo ukaz
$cmd=substr($temp,0,3);
switch ($cmd) {
case "get": {
// Ukaz razbijemo na dele ter ga
// izvedemo ter vrnemo preko povezave
$tempa=explode(":",$temp);
$out=attr_get($tempa[1],1,
substr($tempa[2],0,
strpos($tempa[2],chr(10))-1)
);
if (!$out) $out='FAILED';
socket_write($ch,$out.$zznak); }
break;
case "set": {
$tempa=explode(":",$temp);
if (attr_set($tempa[1],$tempa[2],1,
substr($tempa[3],0,
strpos($tempa[3],chr(10))-1)))
socket_write($ch,'OK'.$zznak);
else socket_write($ch,'FAILED'.$zznak);
}
break;
case "add": {
$tempa=explode(":",$temp);
if (touch(substr($tempa[1],0,
strpos($tempa[1],chr(10))-1)))
socket_write($ch,'OK'.$zznak);
else socket_write($ch,'FAILED'.$zznak);
}
break;
case "del": {
$tempa=explode(":",$temp);
if (unlink(substr($tempa[1],0,
strpos($tempa[1],chr(10))-1)))
socket_write($ch,'OK'.$zznak);
else socket_write($ch,'FAILED'.$zznak);
}
}
}
Delovanje lahko preizkusimo z odjemalcem telnet. Naveden je namišljeni scenarij za dodajanje novega uporabnika v našem bodočem sistemu:
telnet localhost 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
add:admin
OK
set:full name:Administrator:admin
OK
set:email:root@localhost:admin
OK
get:email:admin
root@localhost
S tem imamo končan grob strežniški del sistema. Seveda bi lahko še veliko dodali (ustvarjanje map, listanje po mapah, prenos datotek ipd.), vendar to zadošča za demonstracijo delovanja.
Priprava odjemalca
Kot smo že omenili, bo odjemalec prav tako napisan v phpju in bo tekel v spletnem strežniku. Za to ne potrebujemo drugega sistema, saj lahko zaganjamo strežnik in odjemalec skupaj. Potrebovali bomo različico phpja, ki se integrira s spletnim strežnikom Apache in podpira vtičnice (sockets) za povezave tcp/ip. Ponavadi dobimo vse potrebno že z distribucijo. Če še niso nameščeni, namestimo potrebne pakete (apache, php) in preverimo, ali naš php podpira funkcije sockets. To naredimo s preprostim php programčkom:
<?
phpinfo();
?>
V izhodnem sporočilu moramo najti vrstico, ki se glasi:
Sockets Support => enabled
Če te v izhodnem sporočilu ni, si bomo morali priskrbeti drugo različico phpja. Prevesti bomo morali modul PHP za apache po podobnem postopku kakor zgoraj, s to razliko, da nastavitve za prevajanje prilagodimo potrebam (ne potrebuje --enable-attr in namesto --without-apache uporabimo --with-apache=, kjer navedemo mapo z izvirno kodo strežnika apache). Navedli bomo nekaj zgledov kode, s katero lahko dodamo uporabnika, mu določimo opis in geslo, le-to preverimo ipd. Za komunikacijo s strežnikom bomo uporabili lastno funkcijo "poslji", ki pošlje strežniku niz ter vrne odgovor strežnika prav tako kot niz. Implementacijo spet prepuščam bralcem, saj je odvisna od uporabe funkcij iz skupine sockets.
function dodaj($uporabnik) {
if (poslji("add:".$uporabnik))=="FALSE")
return 0;
else
return 1;
}
function brisi($uporabnik) {
if (poslji("del:".$uporabnik))=="FALSE")
return 0;
else
return 1;
}
function preveri($uporabnik,$geslo) {
$pass=crypt($uporabnik,$geslo);
$passs=poslji("get:password:"
$uporabnik.$zznak);
if (!strcmp($pass,$passs))
return 1;
else
return 0;
}
function spremeni($uporabnik,$geslo) {
$pass=crypt($uporabnik,$geslo);
if (poslji("set:password:".$pass.":".$uporabnik)=="FALSE")
return 0;
else
return 1;
}
Programska koda tu govori sama zase. Napisali smo funkcije, ki ustvarijo, brišejo, spremenijo geslo uporabnika (ki je kodirano s phpjevo funkcijo crypt()) in preverijo uporabnikovo geslo. Podobno lahko dodajamo datotekam - uporabnikom še dodatne vsebine, kot so polno ime, pravice, nivo. Z nekaj spretnosti je tako mogoče narediti uporabne spletne programe.
Vsem, ki nimate potrpljenja s pisanjem celotne programske kode za strežnik in odjemalniške funkcije, pa je na voljo razširjena različica na spletnem naslovu http://freeweb.siol.net/golob/test_attr.tgz.