s. buraga, g. ciobanu: 'atelier de programare în rețele de calculatoare' (2001)
DESCRIPTION
Varianta electronică a volumului "Atelier de programare în rețele de calculatoare" (Polirom, 2001) oferind o inițiere utilă în programarea rețelelor de calculatoare utilizând interfața socket BSD (Berkeley System Distribution), în măsură să contribuie la formarea unei culturi profesionale adecvate cerințelor software actuale. Din cuprins: Unix și Linux, gestiunea fișierelor, procese, semnale, comunicarea între procese, interfața socket, modelul client/server TCP și UDP, multiplexarea intrărilor/ieșirilor, RPC (apelul procedurilor la distanță), utilizarea bibliotecii MySQL. Pentru detalii, a se vizita http://profs.info.uaic.ro/~lrc/TRANSCRIPT
Sabin BURAGA Gabriel CIOBANU
Universitatea "Al. I. Cuza" Ia³i
Fa ultatea de Informati
Atelier de Programare
în Reµele de Cal ulatoare
Ia³i, 2001
Cartea este dedi at lui
W. Ri hard Stevens (1951�1999)
din rµile ruia se pot înv µa atâtea lu ruri interesante.
Cuprins
Mulµumiri 9
Prefaµ 11
1 Introdu ere 13
1.1 UNIX ³i Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2 Comenzi uzuale . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.1 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.2 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2 Gestiunea �³ierelor 31
2.1 Prelu rarea �³ierelor . . . . . . . . . . . . . . . . . . . . . . . . 31
2.1.1 Primitiva open() . . . . . . . . . . . . . . . . . . . . . . 32
2.1.2 Primitiva read() . . . . . . . . . . . . . . . . . . . . . . 33
2.1.3 Primitiva write() . . . . . . . . . . . . . . . . . . . . . 33
2.1.4 Primitiva lseek() . . . . . . . . . . . . . . . . . . . . . 33
2.1.5 Primitiva lose() . . . . . . . . . . . . . . . . . . . . . 34
2.1.6 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.2 Prelu rarea atributelor �³ierelor . . . . . . . . . . . . . . . . . . 38
2.2.1 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.3 Prelu rarea dire toarelor . . . . . . . . . . . . . . . . . . . . . . 41
2.4 Prelu rarea �³ierelor de sistem . . . . . . . . . . . . . . . . . . 42
2.4.1 Gestiunea onturilor de utilizatori . . . . . . . . . . . . 42
2.4.2 Gestiunea grupurilor de utilizatori . . . . . . . . . . . . 44
2.4.3 Gestiunea sesiunilor de lu ru . . . . . . . . . . . . . . . 45
2.5 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.6 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3 Pro ese 55
3.1 Noµiuni fundamentale . . . . . . . . . . . . . . . . . . . . . . . 55
3.2 Comenzi pentru pro ese . . . . . . . . . . . . . . . . . . . . . . 57
5
3.3 Pro esele din perspe tiva programatorului . . . . . . . . . . . . 59
3.4 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.5 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.6 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4 Semnale 70
4.1 Prezentare general . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.2 Manipularea semnalelor . . . . . . . . . . . . . . . . . . . . . . 72
4.2.1 De�nirea unui anumit omportament la apariµia unui
semnal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.2.2 Trimiterea unui semnal unui pro es . . . . . . . . . . . . 73
4.2.3 A³teptarea unui semnal . . . . . . . . . . . . . . . . . . 73
4.2.4 Suspendarea exe uµiei unui pro es . . . . . . . . . . . . 74
4.3 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.4 Alarme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.4.1 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.5 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5 Comuni area între pro ese. Pipe-uri 78
5.1 Preliminarii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2 Primitiva pipe() . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2.1 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.3 Primitiva fifo() . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.3.1 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.4 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.5 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6 Dupli area des riptorilor. Redire t ri 94
6.1 Dupli area des riptorilor . . . . . . . . . . . . . . . . . . . . . . 94
6.1.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.2 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.3 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7 Interfaµa so ket 121
7.1 Preliminarii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
7.2 Interfaµa so ket . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
7.2.1 Primitiva so ket() . . . . . . . . . . . . . . . . . . . . . 124
7.2.2 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
7.2.3 Primitiva so ketpair() . . . . . . . . . . . . . . . . . . 129
7.2.4 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.3 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
8 Modelul lient/server � TCP 132
8.1 Modelul lient/server . . . . . . . . . . . . . . . . . . . . . . . . 132
8.2 Server TCP iterativ . . . . . . . . . . . . . . . . . . . . . . . . 132
8.2.1 Primitiva bind() . . . . . . . . . . . . . . . . . . . . . . 134
8.2.2 Primitiva listen() . . . . . . . . . . . . . . . . . . . . . 135
8.2.3 Primitiva a ept() . . . . . . . . . . . . . . . . . . . . . 135
8.2.4 Primitivele send() ³i re v() . . . . . . . . . . . . . . . 136
8.2.5 Primitivele lose() ³i shutdown() . . . . . . . . . . . . 136
8.3 Client TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
8.4 Conversia datelor . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.5 Alte primitive utile . . . . . . . . . . . . . . . . . . . . . . . . . 139
8.6 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
8.7 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
8.8 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
9 Modelul lient/server � UDP 151
9.1 Datagrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
9.2 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
9.3 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.4 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
10 Multiplexarea intr rilor/ie³irilor 161
10.1 Primitiva sele t() . . . . . . . . . . . . . . . . . . . . . . . . . 161
10.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
10.3 Asin ronism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
10.4 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
10.5 Rezolv ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
11 RPC - Apelul pro edurilor la distanµ 187
11.1 Sistemul RPC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
11.2 Cum lu reaz sistemul RPC? . . . . . . . . . . . . . . . . . . . 190
11.3 S rierea programelor server ³i lient RPC . . . . . . . . . . . . 191
11.4 Exemplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
11.5 Exer iµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
12 Utilizarea bibliote ii MySQL 199
12.1 Formularea problemei . . . . . . . . . . . . . . . . . . . . . . . 199
12.1.1 S hiµ de rezolvare . . . . . . . . . . . . . . . . . . . . . 199
12.2 A esarea serverului MySQL . . . . . . . . . . . . . . . . . . . . 201
13 Bibliote a n urses 216
13.1 Des riere general . . . . . . . . . . . . . . . . . . . . . . . . . 216
13.2 Prezentarea fun µiilor bibliote ii n urses . . . . . . . . . . . . . 216
13.3 Enunµul unei probleme . . . . . . . . . . . . . . . . . . . . . . . 219
13.3.1 Rezolvarea problemei . . . . . . . . . . . . . . . . . . . . 219
14 Mediul Glade 229
14.1 Formularea ³i rezolvarea problemei . . . . . . . . . . . . . . . . 229
14.2 Con eperea interfeµei . . . . . . . . . . . . . . . . . . . . . . . . 229
14.2.1 Utilizarea mediului de dezvoltare Glade . . . . . . . . . 229
Bibliogra�e 236
Glosar 237
Mulµumiri
Mulµumim tuturor studenµilor no³tri are au dat dovad de ele mai multe
ori de devotament, pri epere ³i perseverenµ . Ei ³i-au dep ³it adesea profesorii.
A est lu ru ne-a oferit mare satisfa µie ³i a atras admiraµia noastr . Am avut de
înv µat multe lu ruri de la studenµii no³tri, iar unele dintre soluµiile prezentate
în artea de faµ le aparµin. A est lu ru reprezint o re unoa³tere a alit µilor
lor ³i vrea s sublinieze într-o instituµie a ademi studenµii buni reprezint
de fapt sângele are oxigeneaz întreg organismul.
Se uvin menµionaµi Manuel �ubredu, Iulian V ideanu, Daniel Dumitriu,
Ioana Matei, Oana Captaren u, Olivia Oanea pentru ontribuµiile la unele
programe prezentate în volum, pre um ³i Sini Alboaie, Radu Filip, Vi tor
Tarhon-Onu pentru observaµiile lor asupra onµinutului.
Prefaµ
Lu rarea de faµ este inspirat de laboratoarele pe are autorii le-au susµinut în
ultimii ani u studenµii Fa ult µii de Informati de la Universitatea �Alexan-
dru Ioan Cuza� din Ia³i. A este laboratoare au fost dedi ate program rii în
reµele de al ulatoare utilizând interfaµa so ket BSD (Berkeley System Distri-
bution), limbajul C ³i sistemul de operare Linux (UNIX).
Cartea se adreseaz studenµilor din anii 2, 3 ³i 4 din fa ult µile are predau
informati , elevilor buni ³i profesorilor din li eele are predau informati ,
tinerilor absolvenµi are au nevoie de uno³tinµe de programare în reµele, dar
are nu au urmat un astfel de urs în timpul studiilor. La multe fa ult µi exist
a um ursuri obligatorii de reµele de al ulatoare, iar uno³tinµe de programare
în reµele de al ulatoare se soli it expli it la examenele de li enµ .
Cartea reprezint doar partea de laborator a ursului de programare în
reµele de al ulatoare prezentat la Fa ultatea de Informati din Ia³i. Cursul
propriu-zis va ap rea în urând a volum separat ³i va oferi elor interesaµi
mai multe am nunte legate de ole µia de proto oale TCP/IP ³i de interfaµa
so ket BSD. De³i volumele au fost gândite a se susµine re ipro , �e are volum
poate � itit separat.
A est volum insist asupra aspe telor legate de dezvoltarea unor programe
de omuni are între pro ese sub sistemul de operare Linux (UNIX). Dez-
voltarea unor programe e� iente pentru reµelele de al ulatoare presupune
unoa³terea sistemului de operare u are se lu reaz . Primele apitole ale rµii
se refer la a ele aspe te din Linux (UNIX) are sunt utile în a est sens. Pentru
omuni area între pro ese pe a ela³i al ulator am utilizat me anisme tipi e
sistemului de operare UNIX (pipe-uri, semnale, dupli area des riptorilor), iar
pentru omuni area între pro ese a�ate pe al ulatoare diferite one tate în
reµea s-a re urs la interfaµa so ket ³i modelul lient/server, prin intermediul
proto oalelor TCP ³i UDP, în manier iterativ ³i on urent . Se prezint mul-
tiplexarea intr rilor ³i ie³irilor u ajutorul primitivei sele t(). Un apitol este
dedi at sistemului RPC (Remote Pro edure Call), iar altul des rie modul de
dezvoltare a apli aµiilor în reµea folosind serverul de baze de date MySQL. Ul-
timele apitole ofer exemple de apli aµii intera tive folosind bibliote a n urses
³i mediul de dezvoltare Glade.
Un obie tiv eva mai di� il de îndeplinit pentru un astfel de volum este de
a oferi apli aµii mai mari ³i mai omplexe are s ghideze ititorul în realizarea
unor proie te mai ambiµioase. Sper m unele dintre exer iµiile rezolvate în
volum îi vor în uraja pe ititori s dezvolte apli aµii are s le furnizeze satis-
fa µia unui lu ru deosebit ³i bine f ut. De fapt volumul este s ris u gândul
la sentimentul spe ial de mulµumire pe are-l are un programator atun i ând
realizeaz o apli aµie în are a îndr znit lu ruri noi fun µioneaz . Este un
sentiment pe are sper m s -l în er e ât mai mulµi dintre ititorii a estei rµi.
Autorii nu pot de ât s spere lu rarea ar putea onstitui o iniµiere util
în programarea reµelelor de al ulatoare are s ontribuie la formarea unei
ulturi profesionale ade vate erinµelor software a tuale.
Autorii ³tiu (relativ la artea lor) ³i atenµioneaz ititorul (relativ la pro-
gramele sale) întotdeauna se poate ³i mai bine. Pentru a exprima observaµii
utile, pentru a � informat despre ad ugiri, ore µii, versiuni îmbun t µite ale
unor programe ³i alte informaµii legate de a est volum, autorii în urajeaz
ititorul s foloseas adresa e-mail lr �info.uai .ro, pre um ³i pagina web
http://www.info.uai .ro/~lr .
Autorii
Ia³i, iulie 2001
Capitolul 1
Introdu ere
A est apitol prezint su int prin ipiile de baz ale
sistemului de operare UNIX (Linux) ³i des rie, prin in-
termediul unor exer iµii rezolvate, âteva dintre omenzile
utile puse la dispoziµia utilizatorului.
1.1 UNIX ³i Linux
Sistemul de operare UNIX este un sistem multi�utilizator ³i multi�tasking. Un
num r arbitrar de utilizatori poate avea a es la resursele al ulatorului pe
are este instalat sistemul în ori e moment de timp de la diferite terminale
plasate lo al sau la distanµ , putând rula în manier on urent mai multe
apli aµii.
Una dintre variantele free-software, de su es, ale UNIX-ului este Linux,
sistem pe are îl vom utiliza în ontinuare. Exemplele din a east arte au fost
testate pe distribuµiile Linux RedHat 6.2 ³i 7.0.
Linux reprezint un sistem multi-tasking ³i multi-user ompatibil UNIX pe
32/64 de biµi, respe tând standardul POSIX, oferind utilizatorilor a es la ele
mai populare utilitare GNU ³i posed o interfaµ gra� bazat pe XWindow
System.
Printre alte fa ilit µi, Linux ofer suport pentru reµea, implementând suita
de proto oale TCP/IP, ³i un mediu omplet de dezvoltare a apli aµiilor în
diverse limbaje de programare (e.g. C, C++, Perl, T l/Tk, Python).
1.2 Comenzi uzuale
Reamintim o parte dintre ele mai utilizate omenzi UNIX (Linux) prin inter-
mediul âtorva. . .
1.2.1 Exer iµii
1. S se a�³eze numai intr rile de (sub)dire toare din dire torul urent.
2. Exist vreo deosebire între efe tele omenzilor de mai jos?
Da da, expli aµi motivul.
14 Atelier de programare în reµele de al ulatoare
• rm *~
• rm ~*
• rm * ~
• rm ~ *
3. Pre izaµi are este diferenµa între:
• at < file
• at file
• file at
4. Ce efe t are urm toarea linie de omenzi?
e ho � who | ut - 1-9 | sort | uniq � > > users
5. Ce anume realizeaz linia de omenzi de mai jos?
ut -d: -f1,3 /et /passwd | sort -t: +0 -1
6. S se a�³eze toµi utilizatorii are au onturi terminate în litera �t�.
7. S se s rie, într-o singur linie de omenzi, ³irul de omenzi are veri�
da exist peste 20 de sesiuni des hise ³i în az a�rmativ s se trimit
un mesaj prin po³ta ele troni administratorului de sistem.
8. S se g seas toate �³ierele temporare (al ror nume se termin u
.bak sau u ara terul ~) ³i s se ³tearg .
9. S se s rie linia de omenzi pentru generarea unui �³ier onµinând numele
³i alea tuturor �³ierelor sursa C din sistem.
10. S se ompare exe uµia elor dou linii de omenzi ( are este mai e�-
ient ?):
at | w
( at </dev/tty >/tmp/f ; w </tmp/f ; /bin/rm -f /tmp/f) &
1.2.2 Rezolv ri
1. Pentru a putea da r spunsul, trebuie s revedem pe s urt âteva aspe te
referitoare la gestiunea �³ierelor în UNIX.
Fi³ierele sunt grupate în sisteme de �³iere partajând spaµiul de sto are
(hard-disk-uri, CDROM-uri, �oppy-uri, unit µi de band et .). Ori e sis-
tem de �³iere îndepline³te fun µii pre um:
Introdu ere 15
• sto area persistent a informaµiilor,
• numirea informaµiilor (servi ii de dire toare),
• prote µia ³i folosirea în omun a informaµiilor (prin intermediul unui
me anism de omuni aµie inter-pro es),
• interfaµa între stru turile interne de date ale nu leului sistemului ³i
utilizator,
• interfaµa universal la resursele reµelei.
Sistemul de �³iere în Linux este ara terizat prin:
• stru tura logi arbores ent a dire toarelor,
• tratarea dispozitivelor (terminale, imprimante, memorie et .), în
manier unitar ³i onsistent , a �³iere (în sensul on eptual al
termenului),
• prote µia resurselor prin intermediul unui sistem de permisiuni.
În UNIX ³i în parti ular Linux �³ierele pot � de mai multe tipuri:
• ordinare (obi³nuite) onµinând date, programe sau od ma³in ,
• dire toare (privite a �³iere, ele sto heaz alte �³iere sau dire toare,
în mod re ursiv),
• spe iale: dispozitive periferi e (e.g. mouse, imprimant , modem),
pipe-uri (FIFO-uri, utilizate în omuni aµia între pro ese), so luri
(so ket-uri, folosite în adrul omuni aµiei în reµea),
• leg turi simboli e: short ut-uri tre un �³ier sau dire tor, pentru
a putea � mai u³or de reg sit sau a esat; astfel, un �³ier poate �
reg sit în adrul sistemului de �³iere prin mai multe nume, eventual
în dire toare diferite. Leg turile simboli e, spre deosebire de alte
sisteme de operare, sunt implementate la nivel de sistem de �³iere
³i nu în adrul interfeµei u utilizatorul.
Sistemele de �³iere UNIX sunt destul de multe, în Linux putându-se folosi
el puµin in ispreze e (e.g. ext, ext2, minix, msdos, vfat, pro , iso9660,
sysv, hpfs), el mai popular �ind ext2 (Se ond Extended File System)
are a ap rut în anul 1993.
16 Atelier de programare în reµele de al ulatoare
I-noduri
În UNIX, num rul de �³iere are se pot a�a la un moment dat pe un sis-
tem de �³iere este limitat pre is, în de la rearea sistemului de �³iere.
Fie are �³ier posed un i-nod (i-node) are onµine aproape toate in-
formaµiile legate de a el �³ier, ex eptând onµinutul ³i numele. Unele
i-noduri sunt nefolosite la un moment dat.
Cele mai multe âmpuri ale unui i-nod pot � vizualizate u ajutorul
omenzii ls (despre ls vom dis uta mai detaliat mai târziu în adrul
a estui apitol).
Comanda ls are a argumente nume de �³iere, nu i-noduri, dar um
�e are �³ier are un singur i-nod, ls va unoa³te de unde s ia informaµiile.
Pentru a vedea e i-nod orespunde unui �³ier se poate da ls -i.
De exemplu:
(infoiasi):~$ ls -i *. ls
52227 oldthesis. ls 52228 wmthesis. ls
Stru tura de dire toare
Ori e sistem de tip UNIX are stru tura standard de dire toare urm toare:
/ dire torul r d in (dire toarele vor � delimitate de ara terul slash);
/bin omenzile externe uzuale ale sistemului;
/dev dispozitivele sistemului (atât ele periferi e, ât ³i ele logi e).
A este �³iere spe iale nu au lungime, i o su esiune de dou numere
desemnând num rul major ³i minor al dispozitivului aso iat. De
exemplu, /dev/hda desemneaz primul hard-disk IDE, /dev/hda1
desemneaz prima partiµie a primului hard-disk IDE, /dev/sda de-
semneaz primul hard-disk SCSI, iar /dev/lp1 desemneaz primul
port paralel ³i a³a mai departe. De asemenea, exist un dispozitiv
virtual /dev/null are joa rolul de �gaur neagr � a sistemului,
ori e am s rie tre a est dispozitiv pierzându-se de�nitiv.
/et utilitare de administrare ³i �³iere de on�gurare. Se pot menµiona:
/et /passwd sto heaz informaµii despre utilizatori;
/et /group sto heaz informaµii despre grupurile de utilizatori;
/et /servi es d informaµii despre servi iile de reµea suportate;
/et /proto ols furnizeaz informaµii despre proto oalele de reµea
suportate;
Introdu ere 17
/et /hosts onµine lista numelor ma³inilor din reµeaua lo al în
are a tiveaz sistemul.
/usr anumite �³iere utilizate de membrii sistemului. Iat o parte dintre
subdire toarele a estui dire tor:
sbin exe utabile ³i daemoni suplimentari;
lib bibliote i folosite de diverse programe;
lo al dire tor onµinând apli aµii u ara ter lo al;
in lude �³iere antet (header) ne esare dezvolt rii de apli aµii C;
man �³ierele de date pentru omanda man.
/lib bibliote i ³i module partajabile utilizate de apli aµii sau de ompi-
latoare;
/boot în r torul sistemului (ai i se g se³te ³i imaginea binar a nu-
leului);
/tmp datele temporare generate de anumite omenzi sunt sto ate ai i;
/home dire toarele de lu ru pentru �e are utilizator în parte;
/mnt dire tor de montare a unor sisteme de �³iere externe (partiµii FAT,
CD-ROM et .);
/var onµine o serie de �³iere log ompletate de sistem (/var/log/),
plus ozi de a³teptare (în dire torul /var/spool/) pentru e-mail
(/var/spool/mail/), imprimant , omuni aµii seriale, gestionarea
timpului et .;
/pro g zduie³te sistemul virtual de �³iere pro ³i onµine âte un sub-
dire tor pentru �e are pro es existent, plus date despre onexiunile
de reµea. A est sistem virtual de �³iere reprezint prin ipala moda-
litate prin are nu leul �a� � informaµii despre starea sistemului ³i
a pro eselor.
Comenzile pentru prelu rarea dire toarelor sunt:
• mkdir path � reeaz un dire tor;
• rmdir path � ³terge un dire tor gol, în sensul el nu onµine
de ât intr rile �.� ³i �..� semni� ând dire torul însu³i ³i dire torul
p rinte, respe tiv;
• d [ path ℄ � s himb dire torul urent de lu ru în ale;
• pwd � a�³eaz numele dire torului urent;
• ls [ optiuni ℄ [ path ℄ � listeaz onµinutul unui dire tor.
18 Atelier de programare în reµele de al ulatoare
Argumentul path reprezint o ale de dire toare separate de ara terul
slash.
O omand poate avea un num r variabil de parametri (argumente) ³i de
opµiuni pre�xate de ara terul �-� (se poate utiliza ³i varianta expli it
a unei opµiuni, pre�xat de ara terele �--�).
Cum majoritatea omenzilor pot a epta un num r impresionant de op-
µiuni, el mai bine este s onsult m manualul oferit de sistem. A est
lu ru se realizeaz prin intermediul omenzii man. Manualul sistem este
stru turat pe se µiuni, a estea �ind:
1 pentru omenzi ale sistemului (e.g. ele de mai sus);
2 pentru apeluri sistem (i.e. open());
3 pentru fun µii de bibliote (i.e. fprintf());
4 pentru des rieri ale dispozitivelor I/O;
5 pentru formate de �³iere;
6 pentru jo urile furnizate de sistem;
7 pentru des rieri ale �³ierelor spe iale;
8 pentru pro ese ale sistemului.
Astfel, pentru a a�³a pagina de manual orespunz toare omenzii mkdir
vom da man mkdir sau man 1 mkdir, iar pentru a vedea pagina de ma-
nual referitoare la apelul mkdir() vom introdu e man 2 mkdir.
Da întâmpinaµi di� ult µi, puteµi a�³a pagina de ajutor pentru man
urmând reµeta man man. Alte omenzi înrudite u man sunt apropos sau
whatis.
Vom putea utiliza ls pentru a rezolva primul exer iµiu. Dând man ls,
onstat m pentru a vizualiza doar numele subdire toarelor onµinute
de dire torul urent putem folosi opµiunea �-d�.
Alte opµiuni utile ale omenzii ls sunt:
-a a�³eaz toate �³ierele ( hiar ³i ele al ror nume în epe u �.� are
în mod normal sunt invizibile);
-l listeaz în format lung (permisiuni, num r de blo uri o upate, nu-
mele proprietarului, numele grupului, lungimea în bytes, data ul-
timei modi� ri, numele �³ierului);
-t listeaz sortat dup data modi� rii;
Introdu ere 19
-R a�³eaz re ursiv toate (sub)dire toarele (a east opµiune va putea �
folosit la multe omenzi UNIX).
Fie are �³ier aparµine uiva. Un identi� ator (UID) numeri aso iat elui
are posed �³ierul (de obi ei el are l-a reat) este sto at în adrul i-
nodului. Al treilea âmp de pe o linie listat de ls -l indi utilizatorul.
Utilizatorii, în UNIX, fa parte din unul sau mai multe grupuri. Fie are
�³ier va aparµine ³i el unui anume grup.
(infoiasi):~$ ls -l /home/lr
total 8
drwxr-xr-x 5 lr users 4096 Mar 7 18:35 Desktop
drwx------ 2 lr users 4096 Apr 22 23:00 mail
^^^ ^^^^^
proprietar grup
Drepturi de a es (permisiuni)
Fie are �³ier are aso iate anumite drepturi de a es, numite ³i permisiuni,
pe are le putem vedea în oloanele 2�10 din eea e a�³eaz ls -l.
Drepturile sunt sto ate sub forma a trei triplete de biµi. Primul triplet
arat e poate fa e u �³ierul posesorul lui (un pro es al unui utilizator
u UID egal u el al �³ierului), al doilea arat e pot fa e utilizatorii
din grupul �³ierului, iar al treilea triplet arat e poate fa e restul lumii
(alµi utilizatori are nu aparµin grupului �³ierului).
Cele trei drepturi aso iate elor trei ategorii (utilizator, grup, alµii) sunt:
r itire (Read);
w s riere (Write);
x exe ut (eXe ute).
Ele arat da un �³ier poate � itit, modi� at sau respe tiv exe utat
(pentru �³iere de tip dire tor, poate � utat) de persoana respe tiv .
Ca exemplu on ret s lu m un �³ier având permisiunile:
r-x-w---x
\_/\_/\_/
\ \ \_ restul lumii (others)
\ \__ grupul (group)
\___ posesorul (user)
20 Atelier de programare în reµele de al ulatoare
A est lu ru înseamn :
• posesorul �³ierului poate iti ³i exe uta a est �³ier, dar nu îl poate
modi� a;
• utilizatorii din grupul �³ierului pot modi� a a est �³ier, îns nu îl
pot iti sau exe uta;
• eilalµi utilizatori pot doar exe uta �³ierul, dar nu îl pot iti sau
modi� a.
De asemenea, �³ierele mai au trei atribute spe iale (biµi). A estea se
numes : bitul SUID, bitul SGID ³i bitul STICKY (lipi ios).
Semni� aµia a estora este urm toarea:
• bitul SUID (Set User ID): un �³ier u a est bit setat arat faptul
exe uµia a estui �³ier d na³tere unui pro es are are proprietar
pe posesorul �³ierului, ³i nu pe posesorul pro esului are exe ut
�³ierul (vezi ³i apitolul 3). Un bit SUID setat este indi at de o-
manda ls -l printr-un �s� în lo de �x�:
(infoiasi):~$ ls -l /usr/bin/ hfn
-rws--x--x 1 root root 13184 Aug 31 2000 /usr/bin/ hfn
Când utilizatorul busa o va exe uta a est �³ier, pro esul va avea
UID root, ³i nu busa o, din auza bitului SUID.
• bitul SGID (Set Group ID) are o fun µie similar , a µionând asupra
grupului pro esului.
A e³ti doi biµi nu au sens de ât da �³ierul este exe utabil. Pentru
�³iere are nu sunt exe utabile, ei sunt folosiµi pentru a indi a faptul
utilizarea �³ierului se poate fa e numai dup blo area (lo k) lor.
A e³ti biµi se v d u litere mari �S�.
• bitul Sti ky (lipi ios), notat u �t� are urm toarea semanti :
� la �³iere exe utabile iniµial a est bit indi a nu leului s opti-
mizeze folosirea �³ierului: odat exe utat (de i itit în memo-
rie) va � p strat în memorie, hiar da pro esul se termin ,
pentru probabil va � reapelat din nou, în urând.
� pentru dire toare indi faptul în dire toarele în are ori ine
poate s rie (e.g. /tmp), teoreti ori ine poate ³terge ori e �³ier.
Un bit lipi ios pe un astfel de dire tor va permite îns ³tergerea
unui �³ier doar de tre proprietarul lui.
Introdu ere 21
Un exemplu (opµiunea -d a omenzii ls este ne esar pentru
a lista drepturile dire torului /tmp ³i nu ale tuturor �³ierelor
uprinse în el):
(infoiasi):~$ ls -ld /tmp
drwxrwxrwt 3 root root 1024 Jul 26 00:10 /tmp/
^
bit sti ky
Mai trebuie spus ori e operaµie de des hidere a unui dire tor pentru
utare este pre edat de veri� area dreptului de a fa e a east utare.
Exist dou tipuri de drepturi pentru dire toare:
• bitul �r� (read) la un dire tor arat dreptul de a iti onµinutul
dire torului (un tablou de intr ri), de i dreptul de a a�a are i-nod
orespunde la un nume;
• bitul �x� (sear h) indi dreptul de a iti onµinutul unui i-nod in-
di at de o leg tur din a est dire tor.
Da un dire tor este a esibil, are de obi ei drepturile r-x. Da are
numai drepturile r--, atun i se poate a�a e �³iere onµine, dar ele nu
pot � des hise (ni i i-nodurile lor nu pot � a esate). Putem exe uta
ls sau ls -i, îns nu ls -l pe un astfel de dire tor. Da are numai
drepturile --x nu putem vedea e �³iere onµine, dar da ³tim unul
dintre nume, îl putem des hide.
Pentru a modi� a permisiunile aso iate �³ierelor vom utiliza omanda
hmod. Drepturile de a es vor putea � date �e simboli (spre exemplu
hmod +ux proie t are seteaz dreptul de exe uµie pentru proprietar),
�e numeri (trei ifre în baza opt). Tabela 1.1 rezum valorile o tale are
pot � folosite pentru alterarea drepturilor de a es.
Tabela 1.1: Valorile o tale pentru drepturile de a es folosite de hmod
Categorie r w x
User 400 200 100
Group 40 20 10
Others 4 2 1
De exemplu, pentru a �³ierul program. s poat � itit ³i s ris de
posesor, s poate � itit de grup ³i s poat � exe utat de alµii, vom da
hmod 641 program. :
22 Atelier de programare în reµele de al ulatoare
(infoiasi):~$ hmod 0 program.
(infoiasi):~$ ls -l program.
---------- 1 lr lr 14802 Jun 9 16:34 program.
(infoiasi):~$ hmod 641 program.
(infoiasi):~$ ls -l program.
-rw-r----x 1 lr lr 14802 Jun 9 16:34 program.
2. Putem tre e astfel la rezolvarea elui de-al doilea exer iµiu. Pentru a easta,
vom reaminti ele mai populare omenzi de lu ru u �³ierele:
• p file1 file2 � opie �³iere;
• mv file1 file2 � mut /redenume³te �³iere;
• ln file1 file2 � permite unui �³ier s aib un nume omplemen-
tar (mai multe nume de �³iere pot desemna a ela³i �³ier; leg turile
pot � hard (se reeaz ³i o opie a onµinutului �³ierului) sau soft
(leg tura simboli va onµine doar numele tre �³ierul surs );
• rm file(s) � ³terge �³iere (atenµie! �³ierele ³terse nu pot � re u-
perate în ni i un mod!).
Pentru toate omenzile de mai sus, exist o serie de opµiuni folositoare:
-f forµeaz îndeplinirea a µiunii, f r on�rmare din partea utilizatorului
sau ignorând erorile are pot surveni;
-i mod intera tiv, interogând utilizatorul da într-adev r dore³te s
realizeze eea e s-a spe i� at;
-v a�³eaz mai multe informaµii la exe uµia omenzii respe tive;
-R mod re ursiv, exe utându-se asupra tuturor subdire toarelor.
În adrul numelui unui �³ier putem spe i� a a³a-numitele meta ara tere
(wild ards) are pot înlo ui o serie de ara tere. Cele mai des folosite
sunt:
* substituie zero, unul sau mai multe ara tere;
? substituie un singur ara ter, pe poziµia pe are apare;
[...℄ substituie un ara ter aparµinând unui grup de ara tere.
Cara terul ~ (tilda) poate � folosit pentru a substitui dire torul home
(propriu) al utilizatorului.
În a est moment puteµi da r spunsul la exer iµiul propus, f r a exe uta
efe tiv ele patru forme ale omenzii rm.
Introdu ere 23
3. Pentru a rezolva a est exer iµiu trebuie s vedem are sunt omenzile
pentru prelu rarea onµinutului �³ierelor:
• at file(s) � a�³eaz onµinutul �³ierelor spe i� ate. În fapt, o-
manda are drept s op primar on atenarea mai multor �³iere;
• more file(s) � a�³eaz paginat onµinutul �³ierelor (se iese u :q);
• less file(s) � similar u more, dar permite par urgerea �³ierului
în ambele sensuri (un e ran în sus u �p�, un e ran mai jos u spaµiu,
utarea unui ³ir u �/�);
• ta file(s) � analog u omanda at, dar a�³eaz de la sfâr³it
tre în eput �³ierul;
• w file(s) � a�³eaz num rul de ara tere, uvinte ³i linii ale unui
�³ier:
(infoiasi):~/tmp$ w utilizatori
143 1218 8096 utilizatori
• head file(s) � a�³eaz primele n linii (impli it 10) dintr-un �³ier;
• tail file(s) � a�³eaz ultimele n linii (impli it 10);
• file file(s) � a�³eaz tipul �³ierelor (exe utabil, text, s ript,
arhiv et .):
(infoiasi):~/tmp$ file *
datafiles: dire tory
gaend: ELF 32-bit LSB exe utable,
Intel 80386, version 1
go: ASCII text
helpfiles: dire tory
hintfiles: dire tory
lastgaen.zip: Zip ar hive data, at least v1.0 to extra t
mailspool: dire tory
motd1: ASCII text
sabeav.zip: Zip ar hive data, at least v2.0 to extra t
syslog. om: English text
syslog.link: ASCII text
userfiles: dire tory
users_list: ASCII text
(infoiasi):/dev$ file tty
tty: hara ter spe ial
24 Atelier de programare în reµele de al ulatoare
• stat file(s) � a�³eaz diverse informaµii despre �³iere:
(infoiasi):/$ stat ~
File: "/home/lr "
Size: 2048 Filetype: Dire tory
Mode: (0755/drwxr-xr-x)
Uid: (563/lr ) Gid: (202/lr )
Devi e: 8,0 Inode: 79576 Links: 13
A ess: Mon Nov 23 12:43:30 2000(00000.00:02:12)
Modify: Mon Nov 23 12:43:31 2000(00000.00:02:11)
Change: Mon Nov 23 12:43:31 2000(00000.00:02:11)
O ara teristi a interpretorului de omenzi (shell-ul UNIX) este a eea
de a permite utilizatorului s redire teze intrarea sau ie³irea unui program
( omenzi). Impli it, �e are program ite³te date de la intrarea standard
(tastatura) ³i s rie rezultatele prelu r rii la ie³irea standard (terminalul).
Mesajele de eroare sunt trimise unui dispozitiv standard de eroare are
în mod normal este terminalul. Pentru a redire ta intrarea/ie³irea vom
folosi operatorii:
< redire teaz intrarea standard (în lo de tastatur vor � folosite datele
dintr-un �³ier spe i� at).
> redire teaz ie³irea standard (în lo de terminal datele vor � s rise
într-un �³ier are da exist va � supras ris).
>> a mai sus, dar datele vor � ad ugate la sfâr³itul �³ierului, da ex-
ist ).
| ie³irea unei omenzi va � intrare pentru alta.
Astfel, putem furniza soluµia la exer iµiul propus:
• Forma at file va a�³a onµinutul �³ierului u numele file, da
exist sto at în dire torul urent.
• Forma at <file va avea a ela³i efe t, folosindu-se redire tarea ³i
proprietatea omenzilor UNIX de a iti de la dispozitivul standard
de intrare datele în azul în are nu se spe i� numele unui �³ier
a argument în linia de omand .
• Ultima form file at va pre iza tipul �³ierului u numele at,
da exist sto at în dire torul urent.
4. Avem de-a fa e u o linie de omenzi aparent mai ompli at :
e ho � who | ut - 1-9 | sort | uniq � > > users
Introdu ere 25
Pentru a putea rezolva a est exer iµiu, s enumer m în o serie de
omenzi folositoare:
• ut options [file(s)℄ � de upeaz dintr-un grup de linii anumite
ara tere a�ate pe diverse poziµii sau âmpuri delimitate de anumite
ara tere.
Pentru a de upa toate oloanele uprinse între anumite poziµii vom
utiliza opµiunea �- �, iar pentru a sele ta diverse âmpuri vom folosi
�-f� în onjun µie u opµiunea �-d� are pre izeaz are va � ara -
terul utilizat a delimitator (impli it este ara terul TAB).
Pentru a determina GID-urile tuturor grupurilor de utilizatori vom
exe uta:
(infoiasi):~$ ut -f3 -d: /et /group
Pentru a de upa toate oloanele uprinse între poziµiile 5 ³i 10 vom
apela omanda:
(infoiasi):~$ ut - 5-10 .profile
=$PATH
rt PAT
k 0033
n
s lynx
s alta
s g='t
s dir=
s p=pi
s a='t
• sort [+pos1℄ [-pos2℄ [file(s)℄ [options℄ � este o omand
are sorteaz onform unor anumite riterii un �³ier (dup poziµiile
în adrul liniei sau dup âmpuri).
Da pos1 ³i pos2 sunt spe i� ate, atun i se va sorta zona uprins
între oloanele pos1 ³i pos2. Pentru a ordona în fun µie de anumite
âmpuri se va utiliza opµiunea '-t'.
• uniq file(s) � p streaz dintr-un �³ier doar liniile uni e, în pre-
alabil ordonate.
Dup a estea, d m âteva omenzi referitoare la utilizatori (s nu uit m
UNIX este un sistem multi-utilizator):
26 Atelier de programare în reµele de al ulatoare
• who � a�³eaz lista sesiunilor des hise ale utilizatorilor one taµi
(numele de ont, terminalul de one tare, data one t rii);
• finger [name℄ � furnizeaz o list mai detaliat a sesiunilor des-
hise (in luzând ³i numele real al utilizatorilor) sau informaµii despre
un anumit utilizator (pe ma³ina lo al sau pe alta):
(infoiasi):~$ finger busa o
Login: busa o Name: Sabin Buraga
Dire tory: /home/busa o Shell: /bin/bash
Last login Sun Apr 22 22:59 (EEST) on tty2
Mail last read Sun Apr 22 23:00 2001 (EEST)
No Plan.
• w � reprezint o mixtur a elor dou omenzi de mai sus, a�³ând
³i ultima omand exe utat de utilizatori.
S în er m s rezolv m misterul liniei propuse. Dup um se poate
b nui, omanda e ho va a�³a un ³ir de ara tere la terminal (e ran). La
prima vedere, am putea obµine ³irul:
who | ut - 1-9 | sort | uniq
Dar nu ar � prea banal? Shell-ul va exe uta omenzile înl nµuite prin
operatorul �|� din interiorul a entelor grave, iar rezultatul obµinut va �
returnat a argument pentru omanda e ho. Astfel, se va exe uta mai în-
tâi who are va furniza lista tuturor sesiunilor des hise. Lista va � trimis
spre prelu rare omenzii ut. A easta va de upa din �e are linie primele
9 ara tere (adi to mai numele de ont ale utilizatorilor one taµi),
iar rezultatul va � redire tat tre intrarea omenzii sort are va or-
dona datele primite. La �nal, uniq va p stra numai liniile uni e, adi
va trimite la ie³irea standard lista � în ordine alfabeti � a utilizatorilor
prezenµi în sistem la momentul exe uµiei omenzilor de mai sus. A est
rezultat va � dat a argument pentru e ho ³i va � s ris în �³ierul denumit
users. Folosind >>, s rierea se va fa e ad ugând la ve hiul onµinut noua
list generat .
Dup um se observ , puterea shell-ului rezid în abilitatea de a prelu-
ra, într-un mod înl nµuit, mai multe omenzi, folosind substituµia prin
intermediul apostroafelor grave.
5. Pentru a rezolva a est exer iµiu s ne reamintim um sunt gestionaµi
utilizatorii în adrul unui sistem UNIX.
Fie are utilizator are aso iat un identi� ator uni atribuit de sistem (sau
de tre administrator) în momentul re rii ontului. A est identi� a-
tor este un num r întreg, mai mare sau egal u zero, denumit UID.
Introdu ere 27
De asemenea, �e are utilizator poate aparµine m ar unui grup de uti-
lizatori. Grupul este identi� at de identi� atorul de grup GID. UID-ul ³i
GID-ul se g ses în �³ierul /et /passwd în are pentru ori e utilizator
se memoreaz pe o linie:
• numele ontului s u (login name),
• parola (din onsiderente de se uritate, de ele mai multe ori parolele
sunt �umbrite�, �ind înregistrate în alt �³ier, ina esibil pentru uti-
lizatorii obi³nuiµi, denumit /et /shadow),
• UID-ul (0 da a el utilizator are drepturi depline), orespunzând
utilizatorului spe ial root,
• GID-ul (0 da a el utilizator are drepturi depline), orespunzând
grupului spe ial root,
• alte informaµii (nume real, an de studii, telefon et .),
• dire torul personal al utilizatorului (home),
• interpretorul de omenzi (shell-ul) folosit (e.g. /bin/bash).
Fie are dintre a este âmpuri va � delimitat de ara terul �:�.
Un fragment dintr-un �³ier /et /passwd poate � (parola nu apare, ea
�ind substituit de un �x�):
busa o:x:500:500:Sabin-Corneliu Buraga:/home/busa o:/bin/bash
A³adar, linia de omenzi
ut -d: -f1,3 /et /passwd | sort -t: +0 -1
va a�³a, sortate dup numele de ont, informaµiile onµinute în �³ierul
/et /passwd despre numele de ont ³i UID-ul tuturor utilizatorilor. De-
uparea informaµiilor se va realiza pe âmpuri, u ajutorul omenzii ut,
delimitatorul �ind �:�, dup are vor � ordonate de sort.
De notat faptul utilizând PAM (Pluggable Authenti ation Modules)
este posibil s avem ³i alte baze de date onµinând informaµii despre
utilizatori, autenti� ându-i în mod transparent.
6. Pentru a lista toµi utilizatorii are au numele de ont terminat în litera
�t� vom re urge la ut ³i la grep. Trebuie s de up m /et /passwd
pentru a p stra doar primul âmp din �e are linie.
Comanda grep va a�³a numai liniile are onµin un anumit sub³ir (model
sau pattern). Modelul poate � o expresie regulat (vezi mai jos). Pentru
detalii, onsultaµi manualul.
28 Atelier de programare în reµele de al ulatoare
O prim variant (gre³it !) este:
ut -d: -f1 /et /passwd | grep t
A easta ar a�³a toate numele de ont are vor onµine ara terul �t�, nu
doar ele în are apare pe ultima poziµie. Pentru a putea rezolva exer iµiul
va trebui s re urgem la utilizarea expresiilor regulate.
O expresie regulat este un model are des rie un set de ³iruri de ara -
tere, �ind onstruit asem n tor expresiilor aritmeti e, prin utilizarea
diferiµilor operatori. Cei mai uzuali operatori sunt:
[...℄ delimiteaz o list de ara tere dintre are numai unul se va potrivi
expresiei dorite (e.g. [0123456789℄ pentru a se potrivi u o singur ifr
sau [0-9A-Za-z℄ pentru un ara ter alfa-numeri ).
^ meta- ara ter are indi în eputul unei linii.
$ meta- ara ter indi ând sfâr³itul unei linii.
? operator are desemneaz zero sau el mult o apariµie a unui element
al expresiei.
* operator are desemneaz zero, una sau mai multe apariµii.
+ operator are desemneaz una sau mai multe apariµii.
{n} operator are desemneaz exa t n apariµii.
| spe i� o alternativ .
Varianta ore t are soluµioneaz exer iµiul propus este de i urm toarea:
ut -d: -f1 /et /passwd | grep t$
7. Pentru rezolvarea a estui exer iµiu, ne vom folosi de fa ilitatea de exe u-
µie ondiµionat a omenzilor pe are o pune la dispoziµie shell-ul.
Constru µia omanda1 && omanda2 are semanti a urm toare: omanda2
va � exe utat numai în azul în are omanda1 s-a exe utat ³i a returnat
odul de terminare 0. Reamintim faptul ori e omand în az de su es
va returna odul 0, iar în az de e³e un od diferit de 0. Valoarea 0 în
UNIX se onsider e hivalent u true (adev rat), iar o valoarea nenul
este e hivalent u false (fals).
Vom utiliza omanda test pentru a realiza diferite omparaµii ³i teste
asupra existenµei sau tipului �³ierelor. Pentru mai multe am nunte, on-
sultaµi man test.
Re urgând la substituµia u apostrofuri inverse, vom putea s rie urm -
toarea linie de omenzi are ofer soluµia:
test � who | w -l� -gt 20 && mail root -s"Atentie" </dev/null
Introdu ere 29
8. Vom utiliza omanda find pentru a uta �³iere folosind diverse riterii
³i, eventual, a realiza anumite a µiuni asupra lor. C utarea se va efe tua
pornind de la un anumit dire tor are va � explorat onform riteriilor
de utare alese.
Sintaxa general a omenzii este:
find [path℄ [expression℄ [a tion℄
unde path reprezint alea de dire toare de la are se va în epe utarea,
expression semni� o expresie de�nind riteriul de utare, iar a tion
spe i� a µiunea are va � efe tuat la g sirea unui �³ier.
C utarea se poate realiza dup :
• numele unui �³ier � se folose³te opµiunea -name spe ifi ator, în
are spe ifi ator reprezint un spe i� ator de �³ier (se pot utiliza,
desigur, meta- ara terele de substituµie);
• tipul unui �³ier � se folose³te -type tip, unde tip poate � unul
dintre ara terele f (�³ier obi³nuit), d (dire tor), l (leg tur sim-
boli ), s (so ket ) et .;
• numele proprietarului � se utilizeaz opµiunea -user nume, unde
nume poate � numele sau UID-ul proprietarului �³ierului;
• grupul proprietarului � se folose³te -group nume, unde nume poate
� un nume de grup sau un GID.
Exist o multitudine de alte opµiuni are pot ompune o expresie de
utare; pentru am nunte itiµi paginile de manual dedi ate omenzii
find. De asemenea, mai multe riterii de utare pot � ombinate prin
utilizarea operatorilor logi i ! (negaµie � NOT), -a (³i logi � AND) sau
-o (sau logi � OR) ³i prin folosirea parantezelor.
Ca a µiune exe utat la g sirea unui �³ier putem avea:
• a�³area numelui �³ierului g sit � se folose³te opµiunea -print;
• exe uµia unei omenzi � se utilizeaz opµiunea -exe . �irul de a-
ra tere �{}� va substitui numele �³ierului g sit ³i va putea � dat
a argument al omenzii are va � exe utat . Vom sfâr³i lista argu-
mentelor pasate omenzii u ara terul pun t-virgul .
De exemplu, pentru a g si toate �³ierele surs s rise în limbajul C sto ate
de sistemul de �³iere, vom da:
find / -name *. -print
30 Atelier de programare în reµele de al ulatoare
Astfel, soluµia la exer iµiul propus poate � urm toarea (s-au utilizat
ghilimelele pentru a shell-ul s nu interpreteze ara terele spe iale �{}�
sau �;� ):
find / -name *.bak -o -name *~ -exe rm "{}" ";"
Invit m ititorul s des opere de unul singur rezolvarea ultimelor dou
exer iµii propuse la în eputul a estui apitol.
Capitolul 2
Gestiunea �³ierelor
A est apitol prezint modurile de prelu rare a �³ierelor
ordinare ³i de tip dire tor, plus diverse modalit µi de a e-
sare a informaµiilor onµinute de diferite �³iere sistem.
2.1 Prelu rarea �³ierelor
Dup um am v zut în apitolul pre edent, sistemul de operare UNIX trateaz
în manier unitar �³ierele.
Programatorul poate astfel gestiona atât �³ierele propriu-zise, ât ³i dis-
pozitivele periferi e sau alte abstra µiuni prin intermediul a eleia³i interfeµe.
Astfel, avem la dispoziµie dou moduri de prelu rare a �³ierelor:
• prin intermediul des riptorilor � �e rui �³ier i se va aso ia un întreg
denumit handler, iar operaµiile asupra �³ierului se vor realiza apelând
diferite primitive puse la dispoziµie de nu leul sistemului de operare;
• prin intermediul stru turii FILE de�nite în stdio.h, operaµiile asupra
�³ierelor realizându-se prin apelarea fun µiilor din bibliote a standard de
intrare/ie³ire.
De reµinut faptul folosind prima modalitate re urgem la apelurile (pri-
mitivele) sistem (prelu rare low-level ), iar în al doilea az �e are operaµie
de intrare sau de ie³ire se realizeaz prin intermediul fun µiilor de bibliote ,
utilizându-se un bu�er intern. În implementarea lor intim , fun µiile de biblio-
te se vor baza pe primitivele puse la dispoziµie de nu leul sistemului.
Tabelul 2.1 sintetizeaz prin ipalele operaµii are se pot efe tua asupra
�³ierelor.
De reµinut pentru a rea un �³ier se va putea utiliza apelul reat() sau
apelul open(). Sfâr³itul de �³ier va � desemnat de onstanta EOF (pentru va-
rianta folosind des riptori de �³ier) sau va � testat u ajutorul fun µiei feof()
(în azul utiliz rii stru turii FILE).
Câteva detalii privitoare la primitivele prezentate în tabel urmeaz în on-
tinuare.
32 Atelier de programare în reµele de al ulatoare
Tabela 2.1: Operaµii asupra �³ierelor
Operaµie Primitiv sistem Fun µie stdio.h
des hidere open() fopen()
itire read() fread()
fget ()
fgets()
fs anf()
s riere write() fwrite()
fput ()
fputs()
fprintf()
poziµionare lseek() fseek()
ftell()
în hidere lose() f lose()
2.1.1 Primitiva open()
Apelul open() are urm torul prototip:
#in lude <sys/types.h>
#in lude <sys/stat.h>
#in lude <f ntl.h>
int open ( onst har *pathname, int oflag);
int open ( onst har *pathname, int oflag, mode_t mode);
Primitiva returneaz −1 în az de eroare, altfel returneaz des riptorul de
�³ier aso iat �³ierului dorit a � des his.
Parametrul oflag desemneaz opµiunile de des hidere a �³ierului. Este în
realitate un ³ir de biµi, putând � folosite onstantele:
• O_RDONLY � des hidere numai pentru itire;
• O_WRONLY � des hidere numai pentru s riere;
• O_RDWR � des hidere pentru itire ³i s riere;
• O_APPEND � des hidere pentru ad ugare la sfâr³it;
• O_CREAT � rearea �³ierului, da el nu exist deja;
Gestiunea �³ierelor 33
• O_EXCL � utilizare �ex lusiv � a �³ierului: da s-a folosit O_CREAT ³i
�³ierul exist deja, se va returna eroare;
• O_TRUNC � da �³ierul exist , onµinutul lui este ³ters;
Parametrul mode se folose³te numai da �³ierul este reat ³i spe i� drep-
turile de a es.
Pentru rearea �³ierelor poate � folosit ³i primitiva reat() e hivalent
u spe i� area opµiunilor O_WRONLY | O_CREAT | O_TRUNC la open().
2.1.2 Primitiva read()
Citirea datelor dintr-un �³ier des his se realizeaz u apelul read():
#in lude <unistd.h>
ssize_t read (int fd, void *buff, size_t nbytes)
Se ite³te un num r de exa t nbytes o teµi de la poziµia urent din �³ierul
al rui des riptor este fd ³i se memoreaz în zona indi at de pointerul buff.
Este posibil a în �³ier s �e de itit la un moment dat mai puµin de nbytes
o teµi (de exemplu da s-a ajuns spre sfâr³itul �³ierului), astfel în ât read()
va pune în bu�er doar atâµia o teµi âµi se pot iti.
Primitiva returneaz num rul de o teµi itiµi din �³ier. Da s-a ajuns exa t
la sfâr³itul �³ierului, se returneaz zero, iar în az de eroare valoarea −1.
2.1.3 Primitiva write()
S rierea datelor se realizeaz u write():
#in lude <unistd.h>
ssize_t write (int fd, void *buff, size_t nbytes)
Primitiva va s rie în �³ierul indi at de fd primii nbytes o teµi din bu�erul
desemnat de buff. Returneaz −1 în az de eroare sau num rul de o teµi s ri³i
efe tiv.
2.1.4 Primitiva lseek()
Operaµiile de s riere/ itire în/din �³ier se efe tueaz la o anumit poziµie în
�³ier, onsiderat poziµie urent . Fie are operaµie de itire, de exemplu, va
a tualiza indi atorul poziµiei urente in rementându-i valoarea u num rul de
o teµi itiµi. Indi atorul poziµiei urente poate � setat ³i în mod expli it u
ajutorul apelului lseek():
34 Atelier de programare în reµele de al ulatoare
#in lude <sys/types.h>
#in lude <unistd.h>
off_t lseek (int fd, off_t offset, int pos)
Se poziµioneaz indi atorul la deplasamentul offset în �³ierul orespun-
z tor des riptorului fd, astfel:
• da parametrul pos ia valoarea SEEK_SET, poziµionarea se fa e relativ
la în eputul �³ierului;
• da parametrul pos ia valoarea SEEK_CUR, atun i poziµionarea se fa e
relativ la poziµia urent ;
• da parametrul pos ia valoarea SEEK_END, poziµionarea se realizeaz
relativ la sfâr³itul �³ierului.
Parametrul offset poate lua ³i valori negative, reprezentând deplasamen-
tul, al ulat în o teµi.
În az de eroare, se returneaz −1, altfel noua poziµie în �³ier, relativ la
în eputul a estuia.
2.1.5 Primitiva lose()
A east primitiv d posibilitatea a des riptorul de �³ier s �e din nou dispo-
nibil pentru utilizare, în hizând des riptorul. La terminarea unui pro es, toate
�³ierele des hise sunt în hise automat.
Apelul lose() are forma urm toare:
#in lude <unistd.h>
int lose (int fd);
2.1.6 Exemplu
Pentru a ilustra utilizarea apelurilor de mai sus, ne propunem s s riem un
program are simuleaz omanda ta , a�³ând onµinutul unui �³ier, linie u
linie, de la ultima la prima linie.
Gestiunea �³ierelor 35
Codul surs al a estui program este dat mai jos:
/* simuleaza omanda 'ta ' linie u linie*/
#in lude <string.h>
#in lude <stdlib.h>
#in lude <stdio.h>
#define NL 10 /* odul sfirsitului de linie */
#define MAXLINE 4096 /* lungimea maxima a unei linii */
int
main (int arg , har *argv[℄)
{
har ;
har *s = ( har *) mallo (MAXLINE * sizeof ( har));
/* 1 da a am terminat par urgerea fisierului */
int gata = 0;
/* pozitia in adrul fisierului */
int pos = -1;
/* fisierul de intrare */
FILE *fi;
/* verifi am numarul de parametri
dati in linia de omanda */
if (arg != 2)
{
printf ("Sintaxa: %s <fisier>\n", argv[0℄);
return -1;
}
/* in er am sa des hidem fisierul */
if ((fi = fopen (argv[1℄, "rt")) == NULL)
{
printf ("Eroare: nu am putut des hide fisierul %s\n", argv[1℄);
return -1;
}
/* ne pozitionam la finalul fisierului */
fseek (fi, pos, SEEK_END);
/* it timp mai putem par urge fisierul... */
while (!gata)
{
/* itim ite un ara ter */
fs anf (fi, "% ", & );
/* este sfirsit de linie? */
if ((int) == NL)
{
36 Atelier de programare în reµele de al ulatoare
/* itim o linie intreaga si o afisam */
fgets (s, MAXLINE, fi);
printf ("%s", s);
if (s[strlen (s) - 1℄ != '\n')
puts ("");
}
/* u o pozitie spre in eputul fisierului */
pos--;
/* mai putem sa ne pozitionam? */
gata = (fseek (fi, pos, SEEK_END) == -1);
}
/* afisam si prima linie din fisier */
fseek (fi, pos + 1, SEEK_END);
fgets (s, MAXLINE, fi);
printf ("%s", s);
/* in hidem fisierul */
f lose (fi);
free (s);
/* terminam programul */
return 0;
}
Pa³ii importanµi pe are trebuie s -i urm m pentru a exe uta programul
sunt urm torii:
• editarea odului surs C, folosind unul dintre editoarele de texte puse
la dispoziµie de sistemul de operare (de la mai simplele pi o sau joe,
pân la omplexul ema s). Vom presupune programul dat mai sus se
va numi myta . ³i de i pentru editarea lui vom putea folosi:
joe myta .
• ompilarea odului surs , utilizând ompilatorul C. A esta se apeleaz
prin g (sau pe alte sisteme UNIX) ³i are a argument obligato-
riu numele programului surs (program C având extensia . , program
C++ având extensiile .C, . ori . xx sau program Obje tive�C u ex-
tensia .m). Da nu se spe i� o alt opµiune, în urma pro esului de
ompilare ³i de editare de leg turi va rezulta un program exe utabil de-
numit a.out. Pentru a obµine un �³ier exe utabil u alt nume vom folosi
opµiunea -o:
g myta . -o myta
Gestiunea �³ierelor 37
Da pro esul de ompilare ³i de editare de leg turi s-a desf ³urat f r
erori vom obµine odul exe utabil al programului myta . În azul apariµiei
unor erori, va trebuie s tre em din nou la etapa de editare a odului.
Uneori, la ompilarea unor fun µii are vor � in luse ulterior în adrul
unei bibliote i va trebui generat numai odul obie t, f r a se iniµia ³i
pro esul de editare de leg turi. Pentru a easta, vom utiliza opµiunea - ,
rezultând un �³ier obie t u extensia .o, ³i nu un program exe utabil.
Da programele noastre vor ne esita fun µii de bibliote al ror od
nu este in lus în adrul bibliote ilor de sistem standard, atun i va trebui
s spe i� m numele bibliote ii are va � folosit în momentul edit rii
de leg turi. A est lu ru se realizeaz apelând la opµiunea -l urmat de
numele bibliote ii:
g gaen. -o gaend -l rypt
Putem spe i� a de asemeni alea tre �³ierele de bibliote prin op-
µiunea -L sau alea spre �³ierele antet are vor � utilizate apelând la
opµiunea -I.
Pentru optimizarea odului se va folosi opµiunea -O, iar pentru ontrolul
avertismentelor furnizate de ompilator se va utiliza -W.
Mai multe am nunte despre ompilator în adrul manualului (man g ).
• exe uµia odului exe utabil produs de ompilator se va realiza apelând
odul binar rezultat:
./myta
Pe ele mai multe sisteme, pentru a putea exe uta un program a�at în
dire torul urent, va trebui s spe i� m expli it lo ul s u de sto are,
deoare e în mod uzual interpretorul de omenzi va uta �³ierele exe-
utabile numai în dire toarele onµinute de variabila de mediu PATH.
Da au ap rut erori de exe uµie, va trebui s reedit m ³i apoi s re om-
pil m programul. Pentru depanarea avansat a programelor noastre ne
putem sluji de utilitarele gdb, stra e sau ltra e.
38 Atelier de programare în reµele de al ulatoare
2.2 Prelu rarea atributelor �³ierelor
În afara posibilit µii de a realiza operaµii asupra onµinutului �³ierelor, progra-
matorului i se pune la dispoziµie o serie de apeluri de sistem pentru a onsulta
³i/sau modi� a diverse propriet µi sau atribute ale �³ierelor. A este primitive
de sistem în mod uzual sunt de�nite în �³ierul antet unistd.h, iar detaliile
expli ative se pot reg si în se µiunea 2 a manualului de sistem (astfel, pentru
a a�a am nunte privitoare la hmod() vom utiliza man 2 hmod).
Se pun la dispoziµie urm toarele primitive pentru:
• veri� area permisiunilor de a esare: a ess(),
• modi� area permisiunilor: hmod(),
• s himbarea proprietarului (utilizator ³i grup): hown(),
• redenumirea/mutarea: rename(),
• aso ierea unei leg turi hard : link(),
• aso ierea unei leg turi soft : symlink(),
• ³tergerea unei leg turi hard (da num rul de leg turi hard aso iate unui
�³ier devine nul, atun i ³i a esta va � ³ters, i-nodul s u �ind eliberat):
unlink(),
• furnizarea informaµiilor despre atribute (tip, permisiuni, num r de leg -
turi hard, UID-ul ³i GID-ul proprietarului, m rimea, timpul ultimei a -
es ri, modi� ri et .): stat().
Apelul stat() va utiliza o stru tur a rei de�niµie este urm toarea (vezi
³i antetul sys/stat.h):
stru t stat
{
dev_t st_dev; /* devi e */
ino_t st_ino; /* inod */
mode_t st_mode; /* permisiuni */
nlink_t st_nlink; /* numar de leg. hard */
uid_t st_uid; /* UID proprietar */
gid_t st_gid; /* GID proprietar */
dev_t st_rdev; /* tip devi e
off_t st_size; /* marime (in o teti) */
unsigned long st_blksize; /* lung. blo ului pt. I/O */
unsigned long st_blo ks; /* numarul de blo uri alo ate */
Gestiunea �³ierelor 39
time_t st_atime; /* timpul ultimului a es */
time_t st_mtime; /* timpul ultimei modifi ari */
time_t st_ time; /* timpul ultimei s himbari a starii */
};
2.2.1 Exemplu
Un exemplu de utilizare a apelului stat() are ne va permite s furniz m
diferite statisti i despre un anumit �³ier:
#in lude <sys/stat.h>
#in lude <sys/types.h>
#in lude <unistd.h>
#in lude <pwd.h>
#in lude <stdio.h>
/* afiseaza statisti i despre un fisier */
int
show_file_stat ( har filename[℄)
{
/* stru tura furnizata de stat() */
stru t stat st_str;
/* stru tura pentru prelu rarea fisierului /et /passwd */
stru t passwd *pw_str;
/* stru tura pentru prelu rarea fisierului /et /group */
stru t group *gr_str;
/* 1 da a este un fisier de tip dispozitiv */
int isdev = 0;
/* numele proprietarului */
har name[30℄;
if (stat (filename, &st_str) == -1)
{
perror ("Eroare la stat");
return 1;
}
printf ("Fisier: \"%s\"\t", filename);
/* determinam tipul fisierului */
if ((st_str.st_mode & S_IFMT) == S_IFDIR)
printf ("dire tor");
else
if ((st_str.st_mode & S_IFMT) == S_IFBLK)
{
40 Atelier de programare în reµele de al ulatoare
printf ("blo ");
isdev = 1;
}
else
if ((st_str.st_mode & S_IFMT) == S_IFCHR)
{
printf (" ara ter");
isdev = 1;
}
else
if ((st_str.st_mode & S_IFMT) == S_IFREG)
printf ("ordinar");
else
if ((st_str.st_mode & S_IFMT) == S_IFIFO)
printf ("FIFO");
else
printf ("so ket");
/* da a este dispozitiv... */
if (isdev)
{
printf ("Devi e: %d, %d",
(int) (st_str.st_rdev >> 8) & 0377,
(int) st_str.st_rdev & 0377);
}
/* dispozitivul pe are este sto at fisierul... */
printf ("\nSto at pe dispozitivul: %d, %d\n",
(int) (st_str.st_dev >> 8) & 0377,
(int) st_str.st_dev & 0377);
printf ("I-node: %d; Legaturi: %d; Marime: %ld\n",
(int) st_str.st_ino,
(int) st_str.st_nlink,
(long int) st_str.st_size);
/* aflam proprietarul */
if (!(pw_str = getpwuid(st_str.st_uid)))
str py (name, "<ne unos ut>");
else
str py (name, pw_str->pw_name);
printf ("UID proprietar: %d (%s)\n",
(int) st_str.st_uid, name);
/* aflam grupul proprietarului */
if (!(gr_str = getgrgid(st_str.st_gid)))
str py (name, "<ne unos ut>");
else
str py (name, gr_str->gr_name);
printf ("GID proprietar: %d (%s)\n",
Gestiunea �³ierelor 41
(int) st_str.st_gid, name);
/* afisam permisiunile spe iale */
printf ("Permisiuni spe iale: ");
if ((st_str.st_mode & S_ISUID) == S_ISUID)
printf ("SUID ");
if ((st_str.st_mode & S_ISGID) == S_ISGID)
printf ("SGID ");
if ((st_str.st_mode & S_ISVTX) == S_ISVTX)
printf ("Sti ky ");
/* afisam permisiunile (in o tal) */
printf ("\n>>>Permisiuni: %o\n",
st_str.st_mode & 0777);
/* timpii de a es, modifi are, s himbare de stare */
printf ("Ultimul a es : %s",
as time(lo altime(&st_str.st_atime)));
printf ("Ultima modifi are : %s",
as time(lo altime(&st_str.st_mtime)));
printf ("Ultima s himbare de stare : %s",
as time(lo altime(&st_str.st_ time)));
return 0;
}
2.3 Prelu rarea dire toarelor
Pentru prelu rarea dire toarelor, vom re urge la fun µiile puse la dispoziµie de
�³ierul antet dirent.h. Se vor utiliza tipurile DIR (stream dire tor) ³i dirent
(stru tur onµinând intr rile dintr-un dire tor). Stru tura dirent are urm -
toarea de�niµie:
stru t dirent
{
long d_ino; /* numar i-nodului */
unsigned short d_re len; /* lungime d_name */
har d_name [NAME_MAX+1℄; /* nume intrare (terminat u '\0') */
}
Astfel, el mai simplu program are va a�³a toate intr rile dintr-un anumit
dire tor va �:
/* afiseaza intrarile dintr-un dire tor */
#in lude <sys/dir.h>
#in lude <sys/types.h>
#in lude <dirent.h>
42 Atelier de programare în reµele de al ulatoare
int
main(int arg , har *argv[℄)
{
DIR *dp;
stru t dirent *dirp;
if (arg != 2)
printf ("Sintaxa: %s <nume_dire tor>\n", argv[0℄);
/* in er am sa des hidem dire torul */
if ( (dp = opendir (argv[1℄)) == NULL)
printf ("Nu se poate des hide %s\n", argv[1℄);
/* it putem iti eva, afisam intrarea */
while ( (dirp = readdir (dp)) != NULL)
printf ("%s\n", dirp->d_name);
/* in hidem dire torul */
losedir (dp);
}
Am folosit apelurile opendir(), readdir() ³i losedir(). Mai sunt puse
la dispoziµie ³i fun µiile rewinddir(), seekdir(), telldir() ³i s andir().
L s m ititorului pl erea de a vedea are sunt sintaxa ³i semanti a a estora.
Pentru operaµiile uzuale u dire toare vom putea folosi:
• hdir() s himb dire torul urent;
• mkdir() reeaz un dire tor;
• rmdir() ³terge un dire tor gol;
• get wd() furnizeaz dire torul urent.
2.4 Prelu rarea �³ierelor de sistem
Vom des rie în ontinuare o serie de modalit µi pentru prelu rarea informaµiilor
onµinute de unele dintre �³ierele de sistem importante.
2.4.1 Gestiunea onturilor de utilizatori
Dup âte am v zut în apitolul 1, /et /passwd este un �³ier vital al sistemu-
lui, onµinând informaµii despre utilizatorii are au onturi pe a ea ma³in .
Pentru a putea prelu ra informaµiile din a est �³ier, vom re urge la fun µiile
de�nite în antetul pwd.h:
Gestiunea �³ierelor 43
• getpwnam() returneaz informaµii despre un utilizator da îi unoa³tem
numele de ont;
• getpwuid() a mai sus, dar vom furniza UID-ul utilizatorului.
44 Atelier de programare în reµele de al ulatoare
Prototipurile a estora sunt:
#in lude <pwd.h>
#in lude <sys/types.h>
stru t passwd *getpwnam ( onst har *name);
stru t passwd *getpwuid (uid_t uid);
Informaµiile returnate sunt sto ate în stru tura passwd de�nit astfel:
stru t passwd {
har *pw_name; /* numele utilizatorului */
har *pw_passwd; /* parola */
uid_t pw_uid; /* UID */
gid_t pw_gid; /* GID */
har *pw_ge os; /* numele real */
har *pw_dir; /* dire torul 'home' */
har *pw_shell; /* shell-ul utilizat */
};
Fun µia getpwnam() se poate implementa u ajutorul fun µiilor setpwent(),
getpwent() ³i endpwent() în modul urm tor:
stru t passwd *getpwnam ( onst har *name)
{
stru t passwd *pw_str;
/* ne pozitionam la in eputul fisierului /et /passwd */
setpwent ();
/* par urgem linie u linie pina ind... */
while ((pw_str = getpwent ()) != NULL)
{
if (!str mp (name, pw_str->pw_name))
break; /* ... am gasit */
}
/* in hidem fisierul */
endpwent ();
return (pw_str);
}
2.4.2 Gestiunea grupurilor de utilizatori
Similar, pentru a avea a es la informaµiile despre grupurile de utilizatori ne
vom sluji de fun µiile getgrnam() ³i getgrgid() ale ror prototipuri sunt
de�nite în grp.h.
Gestiunea �³ierelor 45
Stru tura în apsulând informaµiile despre grupurile de utilizatori este:
stru t group {
har *gr_name; /* numele grupului */
har *gr_passwd; /* parola grupului */
gid_t gr_gid; /* GID */
har **gr_mem; /* membrii grupului */
};
2.4.3 Gestiunea sesiunilor de lu ru
Vom des rie în ele e urmeaz anumite detalii de implementare a omenzilor
last (furnizeaz ele mai re ente one t ri ale utilizatorilor) ³i who (listeaz
sesiunile urente ale utilizatorilor one taµi).
A este informaµii sunt sto ate în dou �³iere: /var/run/utmp folosit de who
³i /var/log/wtmp utilizat de omanda last. Pentru a a esa prin program
a este informaµii, ne vom folosi de stru tura utmp are este de�nit în antetul
utmp.h astfel:
stru t utmp {
short ut_type; /* tipul de login */
pid_t ut_pid; /* PID-ul pro esului login */
har ut_line[UT_LINESIZE℄; /* numele terminalului fara "/dev/" */
har ut_user[UT_NAMESIZE℄; /* numele utilizatorului */
har ut_host[UT_HOSTSIZE℄; /* numele hostului de one tare */
long ut_session; /* identifi atorul sesiunii */
stru t timeval ut_tv; /* timpul de one tare */
int32_t ut_addr_v6[4℄; /* adresa IP a hostului */
};
Vom utiliza a east stru tur pentru a par urge unul dintre ele dou �³iere
de mai sus. Astfel, vom putea implementa fun µia de mai jos are simuleaz
omportamentul omenzii who (datele nu vor � a�³ate la ie³irea standard, i
vor � memorate într-un �³ier):
/* genereaza un fisier are va ontine informatii despre
sesiunile utilizatorilor urenti din sistem */
int
who ( har *utmpfilename, har *whofilename)
{
FILE *infp, *outfp;
stru t utmp wutmp;
stru t stat tty_st;
int wusers = 0;
46 Atelier de programare în reµele de al ulatoare
int idle;
har *ptr_date, idle_str[6℄, utty[80℄;
/* des hidem fisierul utmp */
if ((infp = fopen (utmpfilename, "r")) == NULL)
{
return -1; /* eroare */
}
/* ream fisierul de iesire */
if ((outfp = fopen (whofilename, "w")) == NULL)
{
f lose (infp);
return -2; /* eroare */
}
/* par urgem intreg fisierul, itind stru tura utmp */
while (fread (&wutmp, sizeof (wutmp), 1, infp) != 0)
{
if (wutmp.ut_name != NULL && wutmp.ut_type == USER_PROCESS)
{ /* pro es utilizator */
ptr_date = time (&wutmp.ut_time); /* luam timpul */
ptr_date[strlen (ptr_date) - 1℄ = '\0';
/* determinam timpul de ina tivitate
dupa timpul de a esare a terminalului */
sprintf (utty, "/dev/%s", wutmp.ut_line);
if (stat (utty, &tty_st))
idle = time(0) - tty_st.st_atime;
else
{
sprintf (utty, "/dev/pts/%s", wutmp.ut_line);
if (stat (utty, &tty_st))
idle = time (0) - tty_st.st_atime;
else
idle = -1;
}
/* timpul de ina tivitate (in minute) */
if (idle != -1)
sprintf (idle_str, "%02d'", (idle % 3600) / 60);
else
str py (idle_str, "-");
/* s riem datele in fisier */
fprintf (outfp, "%-10s %-12s %-3s %12s %16s\n",
wutmp.ut_name, wutmp.ut_line, idle_str,
ptr_date + 4,
wutmp.ut_host != NULL ? wutmp.ut_host : "");
wusers++;
Gestiunea �³ierelor 47
}
}
fprintf (outfp, "\nTotal %d sesiuni.\n", wusers);
/* in hidem fisierele */
f lose (infp);
f lose (outfp);
return 0;
}
Ni se pun la dispoziµie, de asemenea, fun µiile getutent(), getutid() ³i
getutline() pentru a a esa mai u³or datele din a este �³iere.
2.5 Exer iµii
�i la a est �nal de apitol propunem âteva exer iµii spre rezolvare:
1. S se on eap un program are s adauge la sfâr³itul unui �³ier onµi-
nutul unui alt �³ier.
2. S se s rie un program are implementeaz omanda tail.
3. S se realizeze un program are simuleaz omanda grep.
4. S se s rie o variant intera tiv a omenzii hmod are va a�³a meniuri
pentru s himbarea permisiunilor �³ierelor.
5. S se implementeze ât mai multe opµiuni ale omenzii ls.
6. S se realizeze un program are furnizeaz informaµii despre un utilizator
are are ont pe ma³ina exe utând a est program.
7. S se s rie un program are împarte utilizatorii din /et /passwd în
�³ierele info1, info2, info3, info4, master, oleg1, oleg2, oleg3,
biro1, biro2, biro3, profs ³i others, orespunz toare ategoriilor de
utilizatori are au onturi pe ma³ina lo al (în a est az serverul stu-
denµilor de la Fa ultatea de Informati , Universitatea �Alexandru Ioan
Cuza� din Ia³i).
Fie are dintre �³ierele generate va avea formatul urm tor:
login name [ nume si prenume real ℄
uid, gid (nume grup), shell, dire tor home
48 Atelier de programare în reµele de al ulatoare
2.6 Rezolv ri
Vom furniza pentru âteva dintre exer iµiile propuse rezolv rile a estora.
Exer iµiul 1. Con atenarea a dou �³iere
Pentru primul exer iµiu, o variant de rezolvare este urm toarea � am utilizat
primitivele open(), read(), write() ³i lose():
#in lude <sys/types.h>
#in lude <sys/stat.h>
#in lude <unistd.h>
#in lude <f ntl.h>
#in lude <stdio.h>
int
main (int arg , har **argv)
{
int n, in, out;
har buf[1024℄;
/* nu s-au dat argumentele */
if (arg != 3)
{
write (2, "Sintaxa: append <fisier1> <fisier2>\n", 33);
exit (1);
}
/* des hidem primul fisier pentru itire */
if ((in = open (argv[1℄, O_RDONLY)) < 0)
{
perror (argv[1℄); /* tratam erorile */
exit (1);
}
/* des hidem al doilea fisier pentru s riere ( u adaugare) */
if ((out = open (argv[2℄, O_WRONLY | O_APPEND)) < 0)
{
perror (argv[2℄);
exit (1);
}
/* opiem datele din primul in al doilea fisier */
while ((n = read (in, buf, sizeof (buf))) > 0)
write (out, buf, n);
Gestiunea �³ierelor 49
/* in hidem fisierele */
lose (out);
lose (in);
exit (0);
}
De remar at utilizarea fun µiei perror() are va a�³a un mesaj de eroare
orespunz tor erorii survenite la apelul unei anumite primitive. Mesajul de
eroare va � trimis la ie³irea standard în az de eroare (stderr). Despre tratarea
erorilor vom dis uta în am nunt în apitolul 3.
Exer iµiul 5. Implementarea opµiunilor omenzii ls
Urm torul program implementeaz opµiunile `-l', `-a' ³i `-t' ale omenzii ls:
/* implementeaza "ls" u optiunile -l, -a, -t */
#in lude <stdio.h>
#in lude <dirent.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude <sys/stat.h>
#in lude <unistd.h>
#in lude <pwd.h>
#in lude <grp.h>
#in lude <sys/types.h>
#in lude <time.h>
/* numarul maxim de intrari intr-un dire tor */
#define MAXFILES 500
/* statisti i despre fisier */
stru t stat Infos;
/* lunile anului */
har *Months[℄ =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "O t", "Nov", "De " };
/* stru tura pentru sortarea in fun tie de data */
stru t Files
{
time_t f_mtime;
stru t dirent *file;
};
50 Atelier de programare în reµele de al ulatoare
/* afiseaza permisiunile fisierelor */
void
Rights ()
{
int R, i;
if (Infos.st_mode & S_IFDIR)
printf ("d");
else
printf ("-");
for (i = 0; i < 3; i++)
{
int P = 1, j;
for (j = 0; j < (2 - i); j++) /* al uleaza 8^(2-i) */
P *= 8;
R = 0x04 * P;
if (Infos.st_mode & R)
printf ("r");
else
printf ("-");
R /= 2;
if (Infos.st_mode & R)
printf ("w");
else
printf ("-");
R /= 2;
if (Infos.st_mode & R)
printf ("x");
else
printf ("-");
}
}
/* afiseaza proprietarul fisierului */
void
OwnerName ()
{
stru t passwd *nume;
if ((nume = getpwuid (Infos.st_uid)) == NULL)
{
printf ("Could not get user name with uid: %d\n",
Infos.st_uid);
exit (2);
}
printf (" %-8s", nume->pw_name);
Gestiunea �³ierelor 51
}
/* afiseaza grupul proprietarului */
void
OwnerGroup ()
{
stru t group *grup;
if ((grup = getgrgid (Infos.st_gid)) == NULL)
{
printf ("Could not get group name with gid: %d\n",
Infos.st_gid);
exit (3);
}
printf (" %-10s", grup->gr_name);
}
/* afiseaza data fisierului */
void
Date ()
{
stru t tm *timp;
timp = lo altime (&Infos.st_mtime);
printf (" %s %2d", Months[timp->tm_mon℄, timp->tm_mday);
printf (" %2d:%2d", timp->tm_hour, timp->tm_min);
}
/* programul prin ipal */
int
main (int arg , har *argv[℄)
{
stru t dirent **namelist;
stru t Files *Fisiere[MAXFILES℄;
int n, i, j;
har FileName[256℄;
int CurrentDir = 1, List = 0, Time = 0, All = 0;
har Dir[50℄ = ".";
/* vedem e optiuni au fost date */
if (arg > 1)
{
for (i = 1; i < arg ; i++)
{
if (str mp (argv[i℄, "--help") == 0)
{
52 Atelier de programare în reµele de al ulatoare
printf ("Usage: list [-l -a -t℄ [dir℄\n");
printf (" -l for long list\n");
printf (" -a to show all files\n");
printf (" -t for time sorting\n");
return 0;
}
/* este optiune... */
if (argv[i℄[0℄ == '-')
{
for (j = 1; j < strlen (argv[i℄); j++)
{
swit h (argv[i℄[j℄)
{
ase 'l': List = 1;
break;
ase 'a': All = 1;
break;
ase 't': Time = 1;
break;
default: printf ("Unknown option!\n");
return 2;
}
}
ontinue;
}
}
if (strn mp (argv[arg - 1℄, "-", 1) != 0)
{
str py (Dir, argv[arg - 1℄);
CurrentDir = 0;
}
}
/* s aneaza intrarile din dire tor */
n = s andir (Dir, &namelist, 0, alphasort);
for (i = 0; i < n; i++)
{
if ((Fisiere[i℄ =
(stru t Files *) mallo (sizeof (stru t Files))) ==
NULL)
{
printf ("Could not allo ate memory\n");
return 1;
}
if (CurrentDir == 1)
str py (FileName, namelist[i℄->d_name);
Gestiunea �³ierelor 53
else
sprintf (FileName, "%s%s", Dir, namelist[i℄->d_name);
if (stat (FileName, &Infos) == -1)
{
perror ("Error on stat\n");
return 3;
}
Fisiere[i℄->f_mtime = Infos.st_mtime;
Fisiere[i℄->file = namelist[i℄;
}
if (n < 0)
{
printf ("Error reading dire tory...");
return 1;
}
if (Time == 1)
{
int i, j;
stru t Files *Aux;
for (i = 0; i < (n - 1); i++)
for (j = i + 1; j < n; j++)
if (Fisiere[i℄->f_mtime > Fisiere[j℄->f_mtime)
{
Aux = Fisiere[i℄;
Fisiere[i℄ = Fisiere[j℄;
Fisiere[j℄ = Aux;
}
}
for (i = 0; i < n; i++)
{
if (All == 0)
{
/* in epe u '.', este fisier invizibil */
if (Fisiere[i℄->file->d_name[0℄ == ".")
ontinue;
}
if (CurrentDir == 1)
str py (FileName, Fisiere[i℄->file->d_name);
else
sprintf (FileName, "%s%s", Dir, Fisiere[i℄->file->d_name);
if (List == 1)
{
if (stat (FileName, &Infos) == -1)
{
perror ("Error on stat\n");
54 Atelier de programare în reµele de al ulatoare
return 2;
}
Rights ();
printf (" %3d", Infos.st_nlink);
OwnerName ();
OwnerGroup ();
printf (" %7d", Infos.st_size);
Date ();
printf (" %s\n", Fisiere[i℄->file->d_name);
}
printf ("%s\n", Fisiere[i℄->file->d_name);
}
printf ("\n");
return 0;
}
Capitolul 3
Pro ese
A est apitol prezint noµiunile fundamentale referitoare
la pro ese ³i modalit µile de gestiune a pro eselor.
3.1 Noµiuni fundamentale
Un program ( od binar) devine pro es atun i ând este apelat pentru rulare.
Pro esul, a³adar, reprezint imaginea dinami a unui program a�at în exe u-
µie. Pentru un program pot exista la un moment dat mai multe pro ese aso iate
pe are le vom numi instanµe ale a elui program.
În UNIX (³i în parti ular în Linux) �e are pro es va avea aso iate mai
multe atribute, ele mai importante �ind:
identi� atorul de pro es (PID) este un întreg mai mare de ât zero; la ini-
µializare, nu leul sistemului de operare va rea un pseudo-pro es u PID
nul are va genera pro esul init al rui PID va � 1. Fie are pro es va
avea un p rinte are l-a reat, iar pro esul init va putea � onsiderat
str mo³ul tuturor pro eselor existente la un moment dat în sistem. Va-
loarea PID-ului unui pro es va � sto at de tipul pid_t de�nit în antetul
sys/types.h.
identi� atorul pro esului p rinte (PPID) reprezint identi� atorul pro-
esului are a reat pro esul urent; da un pro es ³i-a pierdut p rintele,
în mod automat îl va avea drept p rinte pe pro esul init (de i PPID-ul
s u va � 1).
identi� atorul grupului de pro ese � �e are pro es poate aparµine unui
grup de pro ese; da PID-ul pro esului este egal u identi� atorul grupu-
lui de pro ese, atun i a el pro es este lider al a elui grup.
terminalul de ontrol aso iat pro esului; este primul terminal des his de lide-
rul grupului de pro ese (în azul pro eselor utilizator este terminalul la
are s-a one tat a el utilizator). A esul la terminalul aso iat se va fa e
prin intermediul �³ierului /dev/tty
1
. Da un pro es intera µioneaz u
1
La ultimele versiuni de nu lee, în dire torul /dev exist ³i alte informaµii suplimentare.
56 Atelier de programare în reµele de al ulatoare
utilizatorul prin intermediul terminalului, atun i a el pro es ruleaz în
prim-plan (foreground). Un pro es are nu are aso iat un terminal (de i
are nu intera µioneaz dire t u utilizatorul) se nume³te pro es de fundal
(ba kground) sau daemon.
UID-ul pro esului este UID-ul utilizatorului are exe ut a el pro es.
GID-ul pro esului este GID-ul grupului din are fa e parte utilizatorul are
exe ut a el pro es.
UID-ul efe tiv (EUID) oin ide de ele mai multe ori u UID-ul, servind
la determinarea dreptului de a es la resursele unui anumit pro es. În
unele azuri anumite pro ese
2
trebuie s ruleze sub auspi ii de super-
utilizator � root � ³i atun i EUID-ul a elor pro ese va � egal u UID-
ul utilizatorului root, adi 0. Ca exemplu, azul în are un utilizator
obi³nuit dore³te s -³i modi� e informaµiile de finger prin intermediul
omenzii hfn are va opera asupra �³ierului sistem /et /passwd.
GID-ul efe tiv (EGID) a mai sus, pentru grupul din are fa e parte uti-
lizatorul are va exe uta pro esul.
starea în are se a� un pro es: dup reare, da s-a reu³it alo area de
memorie pentru pro es, pro esul este în starea ready (preg tit pentru
exe uµie) �ind deja introdus în oada de a³teptare a plani� atorului de
pro ese. Atun i ând va � plani� at pentru exe uµie, în fun µie de o
anumit prioritate, va tre e în starea run (de rulare) � rularea poate � la
nivel de nu leu sau la nivel de utilizator. Atun i ând pro esul trebuie s
a³tepte un eveniment (e.g. introdu erea datelor de la terminalul aso iat)
va tre e în starea wait (de a³teptare). La terminarea normal (la apelul
primitivei exit()) pro esul va tre e în starea �nished. Da un pro es nu
a fost omplet s os din tabela de alo are a pro eselor se nume³te zombie.
Pro esele zombie, de³i apar a existente pe ma³in , nu dispun de ni i o
resurs a sistemului, suprasaturând inutil plani� atorul de pro ese.
valoarea ni e este o omponent a priorit µii totale pe are o va avea un
pro es, putând � ajustat u ajutorul omenzii ni e. Prioritatea unui
pro es în Linux poate lua valori de la −20 la 20. Valorile de la −1 la −20sunt aso iate pro eselor sistemului, ele neputând � a ordate pro eselor
unui utilizator obi³nuit. Cea mai mare prioritate a unui pro es utilizator
este 0.
2
Programele orespunz toare vor avea setat permisiunea Set-UID ³i, eventual, Set-GID.
Pro ese 57
mulµimea des riptorilor de �³ier � �e are pro es la momentul exe uµiei
are la dispoziµie des riptorii:
0 des riptorul standard de intrare
în mod obi³nuit aso iat terminalului
( orespunz tor lui FILE* stdin);
1 des riptorul standard de ie³ire
în mod obi³nuit aso iat terminalului
( orespunz tor lui FILE* stdout);
2 des riptorul standard de eroare
în mod obi³nuit aso iat terminalului
( orespunz tor lui FILE* stderr).
A e³ti des riptori pot � folosiµi f r a trebui s �e des hi³i în prealabil.
De asemenea, se pot redire ta prin intermediul operatorilor <, >, >> sau
| dup um am v zut în apitolul 1.
linia de omand onµine argumentele pasate pro esului; pot � folosite prin
intermediul primelor dou argumente ale fun µiei main():
main (int arg , har *argv[℄)
mediul reprezentând lista variabilelor de mediu puse la dispoziµie de sistemul
de operare (e.g. variabilele PATH, HOME sau TERM). Comanda set poate �
utilizat pentru a�³area variabilelor de mediu disponibile. Din program,
putem avea a es la mediu �e prin al treilea argument al fun µiei main():
main(int arg , har *argv[℄, har *envp[℄)
�e prin intermediul fun µiilor getenv() ³i setenv().
Exe uµia unui program utilizator se poate fa e în dou moduri: în modul
utilizator ³i în modul nu leu (sau sistem) aso iate modului de fun µionare al
pro esului. În modul utilizator pro esele au a es numai la propria zon de
od, date ³i stiv utilizator, iar în modul nu leu un pro es exe ut instru µiuni
privilegiate ³i poate avea a es la stru turile de date ale nu leului. Nu leul,
în termeni de pro es, nu este un pro es separat are se exe ut în paralel u
pro esul utilizator, i devine parte integrant a pro esului utilizator.
3.2 Comenzi pentru pro ese
Urm toarele omenzi UNIX pot � folosite pentru manipularea pro eselor:
58 Atelier de programare în reµele de al ulatoare
Comanda ps a�³eaz informaµii despre pro ese. Pentru a a�³a toate pro e-
sele existente pe un anumit sistem putem da ps aux, iar pentru a vizualiza lista
propriilor noastre pro ese (in lusiv ele din fundal) vom da ps ux. Comanda
pune la dispoziµie foarte multe alte opµiuni, pentru detalii onsultaµi manualul.
Iat un exemplu de rulare a omenzii ps:
(infoiasi):/$ ps ux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
lr 1524 0.0 0.6 2268 1284 tty3 S 15:30 0:00 -bash
lr 1545 0.5 1.2 6356 2456 tty3 T 15:30 0:00 pine
lr 1546 0.1 0.6 2268 1292 tty5 S 15:30 0:00 -bash
lr 1570 0.0 0.2 1436 576 tty5 S 15:31 0:00 s ript
lr 1571 0.0 0.3 1440 612 tty5 S 15:31 0:00 s ript
lr 1572 0.3 0.6 2336 1324 pts/4 S 15:31 0:00 bash -i
lr 1583 0.0 0.3 2556 756 pts/4 R 15:32 0:00 ps ux
O alt omand util este top are va a�³a un lasament al pro eselor
exe utate pe sistem la momente periodi e.
Un pro es poate � rulat în fundal punând la sfâr³itul omenzii semnul `&',
dup um se poate vedea în exemplul de mai jos (intrarea standard este redi-
re tat tre �³ierul u numele listing, iar ie³irea de eroare spre dispozitivul
spe ial /dev/null, de i mesajele de eroare nu vor mai � a�³ate):
(infoiasi):/$ find / -name *. -print >listing 2>/dev/null &
Suspendând un pro es (prin ombinaµia de taste CTRL+Z), îl vom readu e s
ruleze în prim-plan u omanda fg sau în fundal u bg. Pro esele rulând în fun-
dal se mai numes ³i pro ese job, listarea tuturor a estor pro ese efe tuându-se
u omanda jobs:
(infoiasi):/$ jobs
[1℄- Stopped pine
[2℄+ Stopped top
(infoiasi):/$ bg %1
[1℄- pine &
[1℄+ Stopped pine
De menµionat faptul un pro es lansat în fundal se va termina odat u
terminarea sesiunii de lu ru. Pentru a a est pro es s ruleze în fundal ³i dup
logout, ne vom folosi de omanda nohup (vezi ³i apitolul 4).
Pro ese 59
3.3 Pro esele din perspe tiva programatorului
Programatorului i se pun la dispoziµie o serie de primitive importante:
• fork() va rea un pro es nou; pro esul are va apela fork() se va numi
pro es p rinte, iar noul pro es reat se va numi pro es opil.
#in lude <unistd.h>
pid_t fork(void);
De³i apelat o singur dat , primitiva fork() va returna de dou ori:
o valoare pozitiv în pro esul p rinte, desemnând PID-ul pro esului opil
to mai reat ³i o valoare nul în pro esul opil. În az de e³e , fork()
returneaz −1. Pro esul p rinte ³i pro esul opil devin dou pro ese are
se vor exe uta în mod on urent, independent unul de el lalt, în epând
u prima instru µiune are urmeaz lui fork().
Primitiva fork() reprezint singura modalitate în UNIX de a rea pro-
ese noi în adrul unui program.
Un pro es va putea rea mai mulµi opii. Pro esul opil va � o opie a
pro esului p rinte, dar ele dou pro ese nu vor partaja memoria zonelor
de date (stati e sau dinami e) ori stiva.
Un exemplu simplu:
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <sys/types.h>
int
main (void)
{
pid_t pid;
/* reeaza un pro es nou */
pid = fork ();
printf ("Sunt pro esul u PID-ul %d,\n"
" iar apelul fork() a returnat valoarea %d\n",
getpid (), pid);
return 0;
}
60 Atelier de programare în reµele de al ulatoare
Un posibil rezultat a�³at ar putea �:
Sunt pro esul u PID-ul 347,
iar apelul fork() a returnat valoarea 349
Sunt pro esul u PID-ul 349,
iar apelul fork() a returnat valoarea 0
Pentru a vedea um sunt alo ate pentru rulare în manier on urent
pro esele, vom s rie programul de mai jos are realizeaz suma primelor
N numere naturale:
#in lude <stdio.h>
#in lude <unistd.h>
#define MAXNUM 20000
long sum; /* suma numerelor */
int
main ()
{
int i; /* iterator */
sum = 0;
if (fork () < 0)
{
perror ("Eroare la fork()");
return (1);
}
for (i = 0; i <= MAXNUM; i++)
{
printf ("Valoarea lui i este %d.\n", i);
fflush (stdout);
sum += i;
}
printf ("Suma este: %ld.\n", sum);
return (0);
}
Va � a�³at de dou ori suma primelor 20000 de numere naturale, dar
valorile variabilei de iteraµie i vor alterna în fun µie de are dintre ele
dou pro ese este plani� at s ruleze.
Pro ese 61
• wait() va putea � folosit, în pro esul p rinte, pentru a a³tepta terminarea
exe uµiei unui pro es �u. Primitiva wait() se va blo a pân la terminarea
unuia dintre opiii pro esului. Atun i ând unul dintre opii se termin ,
wait() va returna PID-ul pro esului opil are s-a terminat, plus starea
de terminare a a estuia. În az de eroare, se va returna valoarea −1.
Prototipul primitivei wait() este:
#in lude <sys/types.h>
#in lude <sys/wait.h>
pid_t wait(int *status)
A³a um am v zut mai sus, un pro es are s-a terminat ³i pentru are
pro esul p rinte nu a exe utat wait() se nume³te zombie. A³adar, pentru
a nu rea pro ese zombie, trebuie s a³tept m terminarea unui pro es
opil prin utilizarea lui wait().
• waitpid() este un apel înrudit u wait(), dar mai �exibil:
pid_t waitpid(pid_t pid, int *status, int options);
Se permite a³teptarea unui pro es opil sau aparµinând unui grup de pro-
ese (pentru am nunte onsultaµi man waitpid). De asemenea, waitpid()
poate � apelat f r a se blo a.
Apelurile wait() ³i waitpid() pot ajuta la sin ronizarea exe uµiei mai
multor pro ese on urente.
• Pentru a a�a o serie de informaµii despre un pro es vom putea re urge
la primitivele:
� getpid() furnizeaz PID-ul pro esului urent;
� getppid() furnizeaz PID-ul pro esului p rinte;
� getpgrp() furnizeaz identi� atorului grupului de pro ese al pro-
esului urent; o variant mai general este getpgid();
� setpgrp() seteaz identi� atorul grupului de pro ese al pro esului
urent; o variant mai general este setpgid();
� getuid() furnizeaz UID-ul utilizatorului real are a lansat pro esul;
� getgid() returneaz GID-ul grupului utilizatorului are a lansat
pro esul;
62 Atelier de programare în reµele de al ulatoare
� geteuid() returneaz UID-ul utilizatorului efe tiv are a lansat
pro esul;
� getegid() returneaz GID-ul grupului utilizatorului efe tiv are a
lansat pro esul;
� setuid() seteaz UID-ul utilizatorului efe tiv al pro esului urent;
� setgid() seteaz GID-ul grupului utilizatorului efe tiv al pro esu-
lui urent.
Tabela 3.1: Apelurile exe XX()
Primitiv Argumente Cale Environment (mediu)
exe l() list dire tor urent automat
exe v() ve tor dire tor urent automat
exe lp() list PATH automat
exe vp() ve tor PATH automat
exe le() list dire tor urent pre izat
exe ve() ve tor dire tor urent pre izat
• Apelurile exe XX() ajut la invo area unui program ( od exe utabil sau
s ript) de tre un pro es. Pro esul va � înlo uit omplet de tre odul
unui alt program (e.g. o omand UNIX).
Astfel, avem posibilitatea de a exe uta în adrul apli aµiilor noastre alte
programe. Desigur, apelurile exe XX() vor ap rea în adrul pro esu-
lui opil.
A³adar, nu leul în ar în zona de memorie noul program ³i pro esul
este ontinuat u a est program, ruia îi sunt transmise argumentele. În
adrul pro esului nu se s himb de ât programul, restul r mâne nemo-
di� at. La în heierea exe uµiei programului se apeleaz primitiva sistem
exit(), are auzeaz terminarea pro esului �u ³i ie³irea din starea de
a³teptare a pro esului p rinte.
Se pun la dispoziµie 6 primitive, prezentate în tabelul 3.1, ale ror pro-
totipuri sunt (pentru mai multe am nunte onsultaµi manualul):
#in lude <unistd.h>
int exe l ( onst har *file,
/* numele fisierului de exe utat */
onst har *arg,
Pro ese 63
/* argumentele (se termina u NULL) */
...);
int exe v ( onst har *file,
/* numele fisierului de exe utat */
har * onst argv[℄
/* ve torul argumentelor */
);
/* exe utie bazata pe valorile variabilei PATH */
int exe lp ( onst har *file,
/* numele fisierului de exe utat */
onst har *arg,
/* argumentele (se termina u NULL) */
...);
int exe vp ( onst har *file,
/* numele fisierului de exe utat */
har * onst argv[℄
/* ve torul argumentelor */
);
/* exe utie bazata pe mediul dat prin program */
int exe le ( onst har *file,
/* numele fisierului de exe utat */
onst har *arg ,
/* argumentele (se termina u NULL) */
...,
har * onst envp[℄
/* variabilele de mediu VAR=valoare */
);
int exe ve ( onst har *file,
/* numele fisierului de exe utat */
har * onst argv[℄,
/* ve torul argumentelor */
har * onst envp[℄
/* variabilele de mediu VAR=valoare */
);
3.4 Exemplu
Cele des rise mai sus ne vor ajuta s realiz m urm torul program are va
exe uta omanda ls -a -l:
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <sys/types.h>
64 Atelier de programare în reµele de al ulatoare
int
main ()
{
/* PID-ul pro esului opil */
pid_t pid;
/* starea de terminare a pro esului opil */
int status;
printf ("Vom exe uta omanda...\n");
if ((pid = fork ()) < 0)
{
perror ("fork()");
exit (1);
}
else if (pid) /* parinte */
{
if (wait (&status) < 0)
{
perror ("wait()");
}
printf ("Comanda a fost exe utata.\n");
exit (0);
}
else /* opil */
{
/* vom folosi exe lp() */
exe lp ("ls",
/* omanda de exe utat
(se va auta in dire toarele din PATH) */
"ls", /* argv[0℄ */
"-a", /* argv[1℄ */
"-l", /* argv[2℄ */
NULL);
/* da a ajungem ai i inseamna a nu s-a putut exe uta */
printf ("Eroare de exe utie!\n");
exit (1);
}
}
Putem, în lo ul lui exe lp(), s folosim exe l() dând alea omplet
pentru a putea � exe utat omanda ls:
exe l ("/bin/ls", "ls", "-a", "-l", NULL);
Pro ese 65
3.5 Exer iµii
1. Expli aµi efe tul urm toarei se venµe de od:
int i;
for ( i = 0; i <= 9; i++)
fork ();
2. Sunt daµi n pointeri a fi() fun µii al ror prototip este void fi (void),
u i = 1, . . . , n. S se s rie fun µia forkn() are lanseaz n pro ese,
�e are pro es i exe utând fun µia fi().
3. S se implementeze apelul exe vp() u ajutorul lui exe v().
4. S se s rie un program va rea 5 pro ese (in lusiv p rintele). Fie are
pro es va a�³a âte 10 linii onµinând tipul pro esului (p rinte, opilul 1,
opilul 2, opilul 3, opilul 4) ³i PID-ul propriu. Dup a eea, pro esele
opil se vor termina, returnând valori diferite, iar p rintele va a�³a val-
orile returnate de tre opii.
5. Pornind de la exemplul prezentat mai sus, implementaµi un interpretor
de omenzi UNIX (shell ) are a�³eaz un prompt, posed un me anism
de istori al omenzilor (history ) ³i permite exe uµia pro eselor în fundal.
6. S se s rie un program are veri� da sunt mai mult de 20 de se-
siuni des hise pe sistem ³i în az a�rmativ trimite un e-mail u subie -
tul �Atenµie� la adresa sysadm�fenrir.infoiasi.ro (pentru expedierea
mesajului se va putea utiliza omanda mail).
7. S se implementeze omanda ps, ³tiind informaµiile despre pro esele
urente ale sistemului sunt sto ate în sistemul de �³iere spe ial /pro .
3.6 Rezolv ri
Exer iµiul 2. Implementarea fun µiei forkn()
O soluµie de implementare este urm toarea (se va utiliza un tablou onµinând
pointerii la ele n fun µii date):
#in lude <unistd.h>
#in lude <sys/types.h>
66 Atelier de programare în reµele de al ulatoare
int
forkn(void (* tab_fun t[℄)(void), int n, pid_t *tab_pid[℄)
{
stati pid_t pid;
int i, num_ hildren;
num_ hildren = 0; /* initial ni i un pro es nou */
for (i = 0 ; i < n ; i++)
{
swit h (pid = fork()) {
ase 0 : (*tab_fun t[i℄)(i); /* opilul exe uta fun tia */
exit (0);
default: if ((tab_pid[i℄ = pid) > 0)
num_ hildren++; /* retinem PID-ul opilului */
}
}
return (num_ hildren);
}
Fun µia returneaz num rul de pro ese opil are au putut � reate, iar în
tabloul tab_pid[℄ sunt sto ate PID-urile pro eselor lansate.
Exer iµiul 3. Implementarea primitivei exe vp()
O variant de implementare a primitivei exe vp() este ea de mai jos:
#in lude <sys/types.h>
#in lude <stdio.h>
#in lude <stdlib.h>
#define MAXPATH 50 /* lungimea maxima a variabilei PATH */
#define MAXARGS 20 /* numarul maxim de argumente */
extern har **environ; /* mediul */
void
my_exe vp ( har *file, har *arg[℄)
{
har * olon, *pathseq, path[MAXPATH℄, *newargv[MAXARGS℄;
har *getenv (), *str hr ();
int i, len;
if (str hr (file, '/') != NULL ||
(pathseq = getenv ("PATH")) == NULL)
pathseq = ":";
Pro ese 67
for (; ( olon = str hr (pathseq, ':')) != NULL;
pathseq = olon + 1)
{
/* se par urg dire toarele din PATH (despartite de ':') */
len = olon - pathseq;
strn py (path, pathseq, len);
path[len℄ = '\0';
if (len > 0)
str at (path, "/");
str at (path, file);
/* exe utam omanda */
exe v (path, arg);
/* da a esueaza, inseamna a nu se gaseste in a el dire tor */
}
}
int
main ()
{
/* testam fun tia de mai sus exe utind "ls / -l" */
har *arg[℄ = { "ls", "/", "-l", NULL };
my_exe vp (arg[0℄, arg);
}
Exer iµiul 7. Implementarea omenzii ps
Pentru a implementa omanda ps va trebui s par urgem intr rile dire torului
/pro (pentru mai multe am nunte onsultaµi man pro ).
/* Implementarea omenzii "ps" utilizand /pro /pid/stat */
#in lude <stdio.h>
#in lude <stdlib.h>
#in lude <string.h>
#in lude <sys/types.h>
#in lude <dirent.h>
#in lude <sys/stat.h>
#in lude <f ntl.h>
#in lude <errno.h>
void
syserr ( har *msg) /* fun tie de afisare a erorilor */
{
extern int errno, sys_nerr;
fprintf (stderr, "ERROR : %s ( %d", msg, errno);
if ((errno > 0) && (errno < sys_nerr))
68 Atelier de programare în reµele de al ulatoare
fprintf (stderr, "; %s)\n", sys_errlist[errno℄);
else
fprintf (stderr, ")\n");
exit (1);
}
int
main () /* programul prin ipal */
{
DIR *dp, *dpp;
FILE *from;
stru t dirent *dirp;
har *pid, buf[100℄;
int fd, i, , t, k;
pid_t ourretval = -1;
har dr[80℄;
printf ("PID\tCMD\t\tSTATE\tPPID\n");
/* in er am sa par urgem /pro */
if ((dp = opendir ("/pro ")) == NULL)
{
syserr ("pro ");
}
hdir ("/pro ");
while ((dirp = readdir (dp)) != NULL)
{
if (dirp->d_name[0℄ != '.')
{
if ((fd = open (dirp->d_name, O_RDONLY)) != -1)
{
ourretval = (pid_t) atoi (dirp->d_name);
/* da a este o intrare numeri a,
atun i e un dire tor aso iat unui pro es */
if (ourretval != 0)
{
str py (dr, "/pro /");
str at (dr, dirp->d_name);
if ( hdir (dr) < 0)
syserr (" hdir 1");
/* des hidem /pro /PID/stat */
if ((from = fopen ("stat", "r")) == NULL)
{
syserr ("stat");
}
Pro ese 69
/* itim informatiile despre pro esul respe tiv */
/* si le afisam */
for (i = 1; i <= 4; i++)
{
t = 1;
while (( = get (from)) != ' ')
put ( , stdout);
if (i == 2)
printf ("\t");
printf ("\t");
}
printf ("\n");
fflush (stdout);
hdir ("/pro ");
}
}
}
} /* while */
losedir (dp);
return 0;
}
Dup um se poate remar a din a est ultim listing, pentru raportarea
erorilor ne folosim de variabilele externe errno (furnizeaz odul de eroare),
sys_nerr (num rul maxim de erori) ³i sys_errlist[℄ (tablou are onµine
³irul expli ativ pentru eroarea survenit ). De asemenea, se poate utiliza fun µia
perror() are s rie la ie³irea standard de eroare (stderr) mesajul orespunz -
tor erorii ap rute. Valorile variabilei errno pot � testate mai u³or re urgând la
onstantele simboli e de�nite în antetul error.h. Pentru �e are apel în parte,
manualul sistem des rie ³i erorile are pot surveni (de exemplu, pentru fork()
pot ap rea erorile EAGAIN sau ENOMEM).
Capitolul 4
Semnale
A est apitol prezint noµiunile fundamentale referitoare
la semnale ³i modalit µile de tratare a semnalelor. De
asemenea, se prezint me anismul de ata³are de alarme
la pro esele utilizatorilor prin intermediul primitivei
alarm().
4.1 Prezentare general
Un semnal este un s urt mesaj transmis unui pro es, la apariµia unui anumit
eveniment (ex epµie). Semnalele pot � ignorate sau rede�nite de un anumit
pro es, ex eptând un singur semnal. Un semnal nu furnizeaz informaµii supli-
mentare despre a el eveniment, iar destinatarul unui semnal nu unoa³te ni i
m ar are a fost expeditorul a estuia.
Cauzele apariµiei unui semnal pot � grupate astfel:
• evenimente generate de hardware (e.g. exe uµia unei instru µiuni ilegale
de tre pro esor, apariµia unei pene de urent et .);
• evenimente generate de sistemul de operare (e.g. inexistenµa unei primi-
tive sistem, în er area de a a esa o zon de memorie nepermis , inexis-
tenµa resurselor ne esare et .);
• evenimente generate de pro ese utilizator sau de utilizatorul însu³i (de
exemplu, terminarea forµat a unui pro es, a tivarea unei alarme, între-
ruperi et .).
Unele semnale vor provo a, odat u terminarea pro esului asupra ruia
au a µionat, generarea unui �³ier denumit ore are va onµine zona de me-
morie folosit de a el pro es.
Cele mai uzuale semnale sunt urm toarele:
SIGHUP (Hangup) � de one tarea terminalului (transmis la în hiderea sesiu-
nii, se poate inhiba folosind omanda nohup); semnalul mai poate � folosit
pentru a transmite daemonilor s -³i reîn ar e �³ierele de on�guraµie
1
;
1
De exemplu, în lo de a restarta daemonul httpd (serverul Web) prin omanda
/et /init.d/httpd restart este su� ient s d m killall -HUP httpd.
Semnale 71
SIGINT (Interrupt) � întrerupere de la terminal (trimis la apariµia ombi-
naµiei de taste CTRL+C);
SIGQUIT (Quit) � abandonarea unui pro es;
SIGILL (Illegal instru tion) � exe uµia unei instru µiuni ilegale de tre pro-
esor;
SIGFPE (Floating point ex eption) � ex epµie de virgul mobil ;
SIGKILL (Kill) � terminarea forµat a unui pro es (trimis de sistemul de
operare tuturor pro eselor la momentul operaµiunii de shutdown sau re-
boot � vezi ³i omenzile shutdown, halt ³i reboot). Este uni ul semnal
are nu poate � apturat sau ignorat;
SIGBUS (Bus error) � eroare de magistral ;
SIGSEGV (Segmentation violation) � violare a memoriei (în er are de a e-
sare a unei zone de memorie nepermise);
SIGPIPE (Pipe error) � s riere într-un pipe ând nu exist pro es de itire
din a el pipe (vezi apitolul urm tor);
SIGALRM (Alarm lo k) � apariµia unei alarme (vezi apelul alarm() la
se µiunea 4.4);
SIGUSR1 (User de�ned signal 1 ) � semnal are poate � de�nit de utilizator;
SIGUSR2 (User de�ned signal 2 ) � semnal are poate � de�nit de utilizator;
SIGTERM (Software termination) � semnal de terminare normal a unui
pro es (generat impli it de omanda kill);
SIGCHLD (Death of hild) � semnal primit de pro esul p rinte atun i ând
unul dintre pro esele opil s-a terminat (vezi ³i apitolul 3);
SIGIO (I/O) � apariµia unui eveniment I/O asin ron;
SIGURG (Urgen e) � indi apariµia unei urgenµe (e.g. re epµia in ore t a
datelor prin reµea).
Pentru a vedea lista tuturor semnalelor a eptate de sistemul de operare,
ât ³i orespondenµa dintre valorile simboli e ³i numerele aso iate semnalelor,
vom utiliza omanda kill -l.
72 Atelier de programare în reµele de al ulatoare
Pe un sistem u nu leu Linux versiunea 2.2 vom obµine urm toarele:
(infoiasi): ~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
30) SIGPWR 31) SIGSYS 32) SIGRTMIN 33) SIGRTMIN+1
34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5
38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9
42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14
50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10
54) SIGRTMAX-9 55) SIGRTMAX-8 56) SIGRTMAX-7 57) SIGRTMAX-6
58) SIGRTMAX-5 59) SIGRTMAX-4 60) SIGRTMAX-3 61) SIGRTMAX-2
62) SIGRTMAX-1 63) SIGRTMAX
4.2 Manipularea semnalelor
4.2.1 De�nirea unui anumit omportament la apariµia unui
semnal
De�nirea unui omportament la apariµia unui semnal se realizeaz prin inter-
mediul primitivei signal() are are urm torul prototip spe i� at în �³ierul
antet signal.h:
#in lude <signal.h>
typedef void Sighandler (int);
Sighandler *signal(int signum, Sighandler *fun tion);
Pentru �e are semnal desemnat de întregul signum se aso iaz o fun µie
de tratare (handler ) al lui. Fun µia de tratare este o fun µie având un sin-
gur argument desemnând num rul unui semnal ³i are nu returneaz nimi .
Parametrul fun tion este un pointer la a east fun µie sau poate � una dintre
valorile spe iale:
Semnale 73
• SIG_IGN � ignorare semnal,
• SIG_DFL � revenire la omportamentul prede�nit.
Primitiva signal() poate returna onstanta SIG_ERR
2
în az de eroare sau
pointerul la fun µia de tratare.
La nivelul shell-ului, putem de�ni un anumit omportament la apariµia
unui semnal sau a-l ignora u ajutorul omenzii trap.
4.2.2 Trimiterea unui semnal unui pro es
Pentru a trimite un semnal unui pro es vom putea utiliza omenzile kill ori
killall sau apelul de sistem kill() a rui form este urm toarea:
#in lude <sys/types.h>
#in lude <signal.h>
int kill(pid_t pid, int sig);
Da pid este pozitiv, va � trimis semnalul sig pro esului u PID-ul spe-
i� at. Da pid este zero, atun i semnalul va � trimis tuturor pro eselor din
grupul de pro ese al pro esului urent, iar da pid are valoarea −1 atun i
semnalul va � trimis �e rui pro es din tabela de pro ese, ex eptând primul.
Pentru azul ând pid este mai mi de ât −1, semnalul va � trimis tuturor
pro eselor din grupul de pro ese u -pid.
În az de e³e , kill() va returna −1, iar în az de su es va returna zero.
Un apel înrudit este raise() are va trimite un semnal pro esului urent.
Astfel, raise(signum) este e hivalent u apelul kill(getpid(), signum).
4.2.3 A³teptarea unui semnal
Pentru a a³tepta apariµia unui semnal vom utiliza primitiva pause():
#in lude <unistd.h>
int pause(void);
A est apel este blo ant ³i returneaz întotdeauna valoarea −1.
2
De�niµia lui SIG_ERR este #define SIG_ERR (int (*)())-1.
74 Atelier de programare în reµele de al ulatoare
4.2.4 Suspendarea exe uµiei unui pro es
Pentru a suspenda un anumit timp exe uµia unui pro es vom putea utiliza
fun µia sleep():
#in lude <unistd.h>
unsigned int sleep(unsigned int se onds);
De asemenea, pentru a auza terminarea anormal a unui pro es, prin
generarea semnalului SIGABRT, vom putea folosi primitiva abort().
4.3 Exemplu
Vom exempli� a utilizarea semnalelor prin s rierea unui program are ap-
tureaz semnalul SIGUSR2, la apariµia a estuia a�³ând �³ierul /et /servi es
prin folosirea omenzii less.
#in lude <unistd.h>
#in lude <signal.h>
#in lude <errno.h>
#in lude <sys/types.h>
void
sighandler (int sig) /* fun tia de tratare a semnalului */
{
pid_t pid;
if ((pid = fork ()) < 0) /* exe utam 'less' a pro es opil */
{
perror ("fork()");
exit (1);
}
if (!pid) /* opil */
{
exe l ("/usr/bin/less", "less", "/et /servi es", NULL);
perror ("exe ()");
exit (1);
}
else /* parinte */
{
if (wait(NULL) < 0)
perror("wait()");
}
}
Semnale 75
int
main () /* programul prin ipal */
{
/* atasam fun tia de tratare la semnalul SIGUSR2 */
if (signal (SIGUSR2, sighandler) == SIG_ERR)
{
perror ("signal()");
return 1;
}
/* asteptam aparitia unui semnal */
pause();
return 0;
}
Conµinutul �³ierului /et /servi es va � a�³at la ie³irea standard atun i
ând semnalul SIGUSR2 va � trimis pro esului:
(infoiasi):~$ sig. -o sig
(infoiasi):~$ ./sig
(infoiasi):~$ ps x | grep sig
1126 pts/2 S 0:00 ./sig
1134 pts/3 S 0:00 grep sig
(infoiasi):~$ kill -USR2 1126
În afar de apelurile des rise mai sus, pentru tratarea unui grup de sem-
nale pot � utilizate apelurile sigpro mask(), siga tion(), sigpending() sau
sigsuspend().
4.4 Alarme
Fie are pro es poate avea setat o alarm prin folosirea primitivei alarm():
#in lude <unistd.h>
unsigned int alarm(unsigned int se onds);
Semanti a primitivei este urm toarea: dup s urgerea num rului de se-
unde dat a argument va � trimis semnalul SIGALRM pro esului urent. Da
se onds este zero, atun i nu mai este plani� at ni i o alarm . Apelul alarm()
va returna num rul de se unde are au mai r mas pân la a tivarea alarmei
sau zero da nu exist ni i o alarm plani� at .
76 Atelier de programare în reµele de al ulatoare
4.4.1 Exemplu
Ca exemplu de utilizare prezent m un program are din 3 în 3 se unde s rie
un num r aleatoriu într-un �³ier:
/* alarm.
din 3 in 3 se unde s rie un nr. aleator in fisierul "alarm" */
#in lude <stdio.h>
#in lude <stdlib.h>
#in lude <unistd.h>
#in lude <signal.h>
#in lude <time.h>
#define FILENAME "alarm" /* numele fisierului */
#define TIMER 3 /* temporizare la 3 se . */
int status;
FILE * f;
stati void
s rie () /* s rie numarul in fisier */
{
if ((f = fopen (FILENAME, "a+")) == NULL)
{
perror ("Nu pot des hide fisierul!\n");
exit (1);
}
fprintf (f, "%d\n", rand () % 1000);
f lose (f);
}
void
alarma () /* trateaza SIGALRM */
{
/* la aparitia lui SIGALRM va fi apelata s rie() */
if (signal (SIGALRM, s rie) == SIG_ERR)
{
perror ("signal()");
exit (1);
}
while (1) /* rulam la infinit... */
{
alarm (TIMER); /* setam o alta alarma */
pause (); /* asteptam aparitia unui semnal */
}
}
Semnale 77
int
main (void) /* programul prin ipal */
{
swit h (fork ()) /* lansam pro esul in fundal */
{
ase -1:
perror ("fork()");
return -1;
ase 0: /* opilul preia exe utia */
alarma ();
default: /* parintele se termina */
sleep (1);
return 0;
}
}
Dup lansare, vom putea vedea um din trei în trei se unde este s ris un
alt num r în �³ier utilizând omanda tail -f alarm.
4.5 Exer iµii
1. S se s rie o fun µie sleep2() are implementeaz primitiva sleep()
prin intermediul lui alarm().
2. S se on eap un program are, atun i ând apare semnalul SIGSEGV,
genereaz în as ad SIGSEGV (segmentation fault).
3. S se s rie un program are ³terge din minut în minut toate �³ierele
temporare (având numele terminate u .bak sau �) din dire torul urent.
Capitolul 5
Comuni area între pro ese.
Pipe-uri
Capitolul des rie omuni area între pro ese are ruleaz
pe a eea³i ma³in , folosind pipe-uri anonime ³i u nume
(FIFO-uri).
5.1 Preliminarii
A est apitolul trateaz omuni area între pro ese lo ale, ³i anume problema
transmiterii de date între dou pro ese.
Soluµiile a estei probleme sunt multiple, putând apela de exemplu la uti-
lizarea �³ierelor, prin intermediul semnalelor sau folosind alte mijloa e. Re-
zolv rile impli ând �³iere nu sunt satisf toare, mai ales privind viteza sau
independenµa de sistemul de sto are a informaµiilor.
Programatorilor UNIX le trebuie alt eva, ei având la dispoziµie abstra tiz ri
pre um pipe-urile are vor � des rise în ontinuare.
5.2 Primitiva pipe()
Am v zut în apitolul pre edent o modalitate primitiv de omuni are între
pro ese via semnale. Pentru a efe tiv s putem trasmite date între pro ese
înrudite (p rinte� opil, fraµi et .) ne vom sluji de on eptul de pipe ( ondu t )
de transmisii unidire µionale de date efe tuate pe a eea³i ma³in .
Pentru a rea un pipe vom folosi primitiva pipe() având prototipul:
#in lude <unistd.h>
int pipe (int pfd[2℄);
Apelul pipe() va rea un anal de omuni are u doi des riptori de �³ier,
des riptorul pfd[1℄ �ind utilizat pentru a s rie în pipe, iar pfd[0℄ pentru
itirea din pipe.
Comportamentul diverselor apeluri sistem în azul pipe-urilor este el de-
s ris în ontinuare:
Comuni area între pro ese. Pipe-uri 79
• write() va s rie datele în ordinea sosirii în pipe (prin ipiul �rst in,
�rst out � FIFO). Se blo heaz da pipe-ul este plin, nu exist s rieri
parµiale. De obi ei, un pipe are o apa itate �x , destul de limitat . Sin-
gura metod de a se transmite un EOF este s se în hid des riptorul
aso iat.
• read() ite³te datele în ordinea sosirii lor. Odat itite, datele nu pot �
re itite sau puse la lo . Da pipe-ul este vid, read() se va blo a în mod
uzual. Da read() returneaz 0, atun i înseamn s-a ajuns la EOF.
• lose() pentru des riptorul de s riere (pfd[1℄) înseamn în hidere, plus
trimiterea ara terului spe ial EOF pro esului are ite³te din pipe.
• Apelurile open(), reat() sau lseek() nu se utilizeaz .
Operaµiile de itire/s riere sunt atomi e, utilizând bu�ere interne.
De reµinut pipe-urile se utilizeaz pentru omuni area în adrul a dou
pro ese înrudite prin fork(), altfel nu au sens, pentru sunt reate în me-
morie. Comuni aµiile bidire µionale simultane (full duplex ) prin pipe-uri pot
ondu e la dead-lo k -uri, dar putem realiza omuni aµii bidire µionale half du-
plex (în ambele sensuri, îns nu simultan).
S hema general de one tare a dou pro ese printr-un pipe unidire µional
este urm toarea (prin ipiul pro eselor produ tor/ onsumator):
1. se reeaz pipe-ul;
2. se exe ut fork() pentru a se rea pro esul opil;
3. pro esul opil în hide de obi ei des riptorul de s riere al pipe-ului, �ind
doar va iti din pipe;
4. opilul ite³te prin pipe datele de la p rinte ³i le pro eseaz ;
5. p rintele în hide des riptorul de itire al pipe-ului;
6. pro esul p rinte trimite datele prin pipe pro esului opil.
Pro esul p rinte este pro esul produ tor, iar pro esul opil este pro e-
sul onsumator. Consumatorul se va blo a pân ând pro esul produ tor va
trimite datele. Da s-a umplut pipe-ul, produ torul se va blo a pân ând
pro esul onsumator va iti din pipe.
80 Atelier de programare în reµele de al ulatoare
5.2.1 Exemplu
Vom rea un pro es opil are va iti dintr-un pipe un ³ir de ara tere trimis de
pro esul p rinte, onvertind ori e liter minus ul în liter majus ul . Pro esul
p rinte va iti a est ³ir de la intrarea standard.
/* Converteste ara terele mi i in ara tere mari */
#in lude <stdio.h>
#in lude < type.h>
#in lude <stdlib.h>
#in lude <unistd.h>
#in lude <sys/wait.h>
int pfd[2℄; /* pipe-ul */
int pid; /* PID-ul pro esului opil */
int ; /* ara terele itite */
int
main (void) /* programul */
{
if (pipe (pfd) < 0) /* [1℄ reare pipe */
{
perror ("pipe()");
exit (1);
}
if ((pid = fork ()) < 0)
{ /* [2℄ reare pro es opil */
perror ("fork()");
exit (1);
}
if (pid) /* parinte */
{
lose (pfd[0℄); /* [5℄ in hidem des riptorul de itire */
printf ("Pro es parinte: introdu eti un sir: ");
/* vom iti de la intrarea standard */
while (read (0, & , 1))
/* [6℄ s riem datele in pipe */
if (write (pfd[1℄, & , 1) < 0)
{
perror ("write()");
exit (1);
}
/* in hidem des riptorul de s riere in pipe */
lose (pfd[1℄);
Comuni area între pro ese. Pipe-uri 81
/* asteptam terminarea opilului */
if (wait (NULL) < 0)
{
perror ("wait()");
exit (1);
}
exit (0);
}
else /* fiu */
{
lose (pfd[1℄); /* [3℄ in hidem des riptorul de s riere */
/* [4℄ itim datele din pipe */
printf ("Pro es fiu: re eptam datele...\n");
while (read (pfd[0℄, & , 1))
{
/* este litera mi a? */
if (islower ( ))
printf ("% ", toupper ( ));
else
printf ("% ", );
}
/* in hidem si des riptorul de itire */
lose (pfd[0℄);
/* am terminat */
exit (0);
}
}
5.3 Primitiva fifo()
Primitiva mkfifo() permite rearea pipe-urilor u nume. A estea au a elea³i
ara teristi i a ³i pipe-urile anonime, dar o up lo pe dis , a �³iere, în adrul
sistemului de �³iere.
Pentru a rea un pipe u nume vom re urge la apelul mkfifo():
#in lude <sys/types.h>
#in lude <sys/stat.h>
int mkfifo ( onst har *pathname, mode_t mode);
Va trebui pre izat , în afara numelui de �³ier, ³i mulµimea de permisiuni
aso iat �³ierului de tip �fo (modul de a es, dat în o tal).
Dup rearea u mkfifo(), pipe-ul u nume trebuie des his u open(),
putând � hiar ³i ³ters la terminarea prelu r rii u primitiva unlink().
82 Atelier de programare în reµele de al ulatoare
Pro esele trebuie s unoas numele �fo-ului, dar pot s nu �e înrudite.
Modul de itire ³i s riere a datelor în azul unui �fo este similar u el prezentat
la pipe-uri.
Un apel mai general, are va rea un �³ier spe ial în adrul sistemului de
�³iere, este mknod().
5.3.1 Exemplu
Ne propunem s s riem un mesaj într-un �fo ³i apoi s itim doar primele 5
ara tere din a el �fo:
#in lude <stdio.h>
#in lude <unistd.h>
#in lude <stdlib.h>
#in lude <f ntl.h>
#in lude <sys/types.h>
#in lude <sys/stat.h>
#in lude <sys/wait.h>
#in lude <string.h>
int
main (int arg , har *argv[℄) /* programul */
{
int pfd; /* des riptorul aso iat fifo-ului */
har mesaj[6℄; /* mesajul itit */
pid_t pid; /* PID-ul opilului */
bzero (mesaj, 6);
if (arg < 2)
{
fprintf (stderr, "Sintaxa: %s <fifo>\n", argv[0℄);
exit (2);
}
/* ream fifo-ul */
if (mkfifo (argv[1℄, S_IFIFO | 0644) < 0)
{
fprintf (stderr, "N-am putut rea %s\n", argv[1℄);
exit (1);
}
/* ream pro esul fiu */
pid = fork ();
swit h (pid)
{
ase -1: /* eroare */
fprintf (stderr, "Eroare la fork().\n");
Comuni area între pro ese. Pipe-uri 83
exit (3);
ase 0: /* opil */
if ((pfd = open (argv[1℄, O_RDONLY)) < 0)
{
fprintf (stderr, "Eroare la des hidere fifo.\n");
exit (1);
}
read (pfd, mesaj, 5); /* itim din fifo */
printf ("Mesajul itit din fifo: %s\n", mesaj);
return 0;
default: /* parinte */
if ((pfd = open (argv[1℄, O_WRONLY)) < 0)
{
fprintf (stderr, "Eroare la des hidere fifo.\n");
exit (1);
}
/* s riem in fifo */
write (pfd, "Salut, e mai zi eti?", 22);
if (wait (NULL) < 0) /* asteptam terminarea opilului */
{
fprintf (stderr, "Eroare la wait().\n");
exit (1);
}
return 0;
} /* swit h */
}
5.4 Exer iµii
1. Imaginaµi o metod de a a�a apa itatea unui pipe.
2. S se s rie un program are trimite unui pro es opil linie u linie onµi-
nutul unui �³ier, pro esul opil num r liniile trimise ³i returneaz p rin-
telui num rul lor.
3. Un pro es trimite unui pro es opil un num r aleator între 0 ³i 3, folosind
un pipe. Da pro esul opil re epµioneaz num rul 0, va exe uta ls,
da prime³te 1 va exe uta who, da re epµioneaz 2 va a�³a onµinutul
�³ierului /et /servi es, iar da va primi 3, atun i va exe uta finger.
4. Un pro es transmite onµinutul unui �³ier, linie u linie, unui pro es
opil. O linie este trimis din 7 în 7 se unde. Pro esul re eptor va primi
informaµiile în maxim 37 de se unde, a este informaµii s riindu-le într-un
�³ier, apoi trimiµând p rintelui un semnal de terminare.
84 Atelier de programare în reµele de al ulatoare
5. Fie un pro es are trimite din 33 în 33 de se unde âte o ifr unui
pro es opil. Fie are ifr este re epµionat de opil, are realizeaz suma
primelor 33 de ifre primite, s rie suma într-un �³ier, îi trimite pro esului
p rinte suma, apoi îi expediaz o omand de terminare (e.g. �stop�),
dup are se termin ³i el.
6. Un pro es trimite unui pro es opil a 33-a linie a �³ierului /et /passwd.
Pro esul opil va extrage numele utilizatorului ³i UID-ul s u, le va a�³a
³i apoi va s rie toate informaµiile despre pro esele a elui utilizator (da
este one tat) într-un �³ier.
7. Un pro es trimite unui pro es opil numele unui �³ier ³i una dintre literele
�r�, �l�, �w� sau �q�, aleatoriu. Pentru �r� �³ierul va � ³ters, pentru �l�
va � listat (a�³at la ie³irea standard), iar pentru �w� i se vor num ra
liniile. Da pro esul opil re epµioneaz ara terul �q� atun i va auza
terminarea lui ³i a p rintelui.
8. S se simuleze a tivitatea de dete tare a staµiilor într-o reµea de tip magis-
tral având 5 al ulatoare. Fie are staµie trimite âte un mesaj de iden-
ti� are (de ex. PID-ul propriu) în reµea ³i a³teapt mesajele elorlalte
staµii. Când a g sit toate staµiile, va a�³a PID-urile lor. Da î³i ite³te
propriul mesaj, staµia îl va repune în reµea.
9. S se simuleze a tivitatea de dete tare a staµiilor într-o reµea de tip inel
având 5 al ulatoare. Fie are staµie trimite âte un mesaj (i.e. PID-ul
propriu) în reµea ³i a³teapt mesajele elorlalte staµii, a�³ând PID-urile
primite, pân ând dete teaz toate staµiile.
5.5 Rezolv ri
Exer iµiul 1. Capa itatea unui pipe
Una dintre soluµiile a estei probleme este ea des ris mai jos:
Cunoa³tem faptul apelul write() este blo ant, eea e va ondu e la
�îngheµarea� programului la momentul ând pipe-ul va � umplut omplet. Re-
urgând la semnalul SIGALRM (setând o alarm ) vom reu³i s a�³ m utiliza-
torului apa itatea total a pipe-ului ³i s termin m programul (îi vom trimite
semnalul SIGTERM).
/* Determina apa itatea unui pipe anonim */
#in lude <signal.h>
Comuni area între pro ese. Pipe-uri 85
#in lude <stdio.h>
#in lude <unistd.h>
/* pipe-ul */
int pfd[2℄;
/* total va numara ate ara tere vom s rie u write
pana and se va blo a pipe-ul */
int total = 0;
void
gata ()
{
printf ("\nCapa itatea pipe-ului este de %d o teti.\n", total);
/* dupa e este afisata apa itatea pipe-ului,
programul se autodistruge */
raise (SIGTERM);
}
int
main ()
{
/* vom s rie repetat in pipe ara terul '!' */
har = '!';
/* ream pipe-ul */
if (pipe (pfd) < 0)
{
perror ("pipe()");
exit (1);
}
/* setam alarma */
signal (SIGALRM, gata);
alarm (1);
/* la infinit, s riem in pipe... */
while (1)
{
if (write (pfd[1℄, & , 1) != 1)
{
perror ("write()");
exit (1);
}
total++;
}
}
86 Atelier de programare în reµele de al ulatoare
Exer iµiul 5. 33 de ifre
Vom utiliza dou pipe-uri pentru a realiza omuni aµii bidire µionale (duplex)
între p rinte ³i opil.
#in lude <signal.h>
#in lude <unistd.h>
#in lude <stdlib.h>
#in lude <stdio.h>
/* numarul de se unde intre 2 trimiteri onse utive */
#define NRSEC 2
/* numarul de numere generate */
#define NRNR 5
/* numele fisierului in are se depune suma */
#define FILENAME " ifre.txt"
int p1[2℄, p2[2℄; /* ele doua pipe-uri */
void
fuser (int sig)
{
int s;
if (!read (p2[0℄, &s, sizeof (s)))
{
perror ("Eroare la itire din pipe");
exit (1);
}
printf ("Suma primita de la opil este %d\n", s);
lose (p1[0℄);
lose (p2[1℄);
exit (0);
}
int
main () /* programul prin ipal */
{
pid_t pid;
FILE *f;
int n = 0, /* numarul de ifre */
x, /* ifra itita */
sum = 0, /* suma al ulata */
val; /* valoarea generata si transmisa pe pipe */
/* setam o alta valoare pentru generarea nr. aleatoare */
srand (getpid());
Comuni area între pro ese. Pipe-uri 87
/* ream ele 2 pipe-uri */
if (pipe (p1) < 0)
{
perror ("Eroare apel pipe");
exit (1);
}
if (pipe (p2) < 0)
{
perror ("Eroare apel pipe");
exit (1);
}
/* ream pro esul opil */
pid = fork ();
swit h (pid)
{
ase -1:
{
perror ("Eroare apel fork");
exit (1);
}
ase 0: /* opil */
lose (p1[1℄);
lose (p2[0℄);
if ((f = fopen (FILENAME, "a")) == NULL)
{
perror ("Eroare la des hidere fisier");
exit (1);
}
while (n < NRNR)
{
if (!read (p1[0℄, &x, sizeof (x)))
{
perror ("Eroare de itire de la parinte");
exit (1);
}
fprintf (f, "%d\n", x);
printf ("Pro esul opil u PID-ul %d a primit %d\n",
getpid (), x);
sum += x;
n++;
}
fprintf (f, "%d\n", sum);
/* s riem suma in pipe */
write (p2[1℄, &sum, sizeof (sum));
f lose (f);
lose (p1[0℄);
88 Atelier de programare în reµele de al ulatoare
lose (p2[1℄);
/* am terminat */
kill (getppid (), SIGUSR1);
exit (0);
default: /* parinte */
lose (p1[0℄);
lose (p2[1℄);
/* atasarea fun tiei de tratare a semnalului SIGUSR1 */
if (signal (SIGUSR1, fuser) == SIG_ERR)
{
perror ("Eroare apel signal");
exit (1);
}
while (1) /* la infinit... */
{
val = random () % 10; /* ifra trimisa */
write (p1[1℄, &val, sizeof (val));
printf ("Pro esul parinte u PID-ul %d a trimis %d\n",
getpid (), val);
sleep (NRSEC);
}
exit (0);
}
}
Pentru NRSEC egal u 2 ³i NRNR egal u 5, un posibil rezultat ar � �Suma
primit de la opil este 24�, iar onµinutul �³ierului ifre.txt ar � urm torul
(ultima valoare din �³ier este suma transmis p rintelui):
3
6
7
5
3
24
Exer iµiul 8. Dete tarea staµiilor dintr-o reµea magistral
Vom prezenta în ontinuare o soluµie bazat pe pipe-uri anonime.
Fie are staµie va � simulat de tre un pro es, iar analul de omuni aµie
dintre staµii (magistrala) va � simulat de un pipe partajat de toate pro esele.
Staµiile din reµea deja dete tate se vor p stra într-un ve tor.
Comuni area între pro ese. Pipe-uri 89
#in lude <stdio.h>
#in lude <unistd.h>
#in lude <sys/types.h>
#in lude <signal.h>
int pfd[2℄; /* pipe-ul */
/* ve torul statiilor dete tate */
int dete ted[4℄;
int mypid, pid, idx = 0, nr;
/* statia este deja dete tata? */
int
este (int idx, int elem)
{
int k;
for (k = 0; k < idx; k++)
if (dete ted[k℄ == elem)
return 1;
return 0;
}
/* listeaza statiile dete tate */
int
afiseaza_statziile (int eu)
{
int i;
printf ("Statia u numarul %d a dete tat statiile:\n", eu);
fflush (stdout);
for (i = 0; i < 4; i++)
{
printf ("%d ", dete ted[i℄);
fflush (stdout);
}
printf ("\n");
fflush (stdout);
return 0;
}
int
myfork () /* fun tia opilului */
{
swit h (fork ()) /* ream opilul (statia) */
{
ase -1:
fprintf (stderr, "Eroare la fork.\n");
90 Atelier de programare în reµele de al ulatoare
return 2;
ase 0:
mypid = getpid ();
write (pfd[1℄, &mypid, sizeof (pid_t));
while (idx < 4)
{
read (pfd[0℄, &nr, sizeof (pid_t));
if (nr != getpid () && !este (idx, nr))
dete ted[idx++℄ = nr;
write (pfd[1℄, &nr, sizeof (pid_t));
}
afiseaza_statziile (getpid ());
lose (pfd[1℄);
lose (pfd[0℄);
/* am terminat (se sinu ide) */
raise (SIGKILL);
}
}
int
main () /* programul prin ipal */
{
int j;
if (pipe (pfd) < 0) /* reeaza pipe-ul */
{
fprintf (stderr, "Eroare la pipe().\n");
return 1;
}
/* ream statiile */
for (j = 0; j < 5; j++)
myfork ();
/* parintele */
lose (pfd[1℄);
lose (pfd[0℄);
/* asteaptam toti opiii... */
while (wait (NULL) > 0)
;
/* ... si am terminat */
return (0);
}
Comuni area între pro ese. Pipe-uri 91
O posibil rulare a programului de mai sus poate � urm toarea:
Statia u numarul 2299 a dete tat statiile:
2301 2300 2297 2298
Statia u numarul 2301 a dete tat statiile:
2298 2300 2297 2299
Statia u numarul 2300 a dete tat statiile:
2298 2301 2297 2299
Statia u numarul 2297 a dete tat statiile:
2301 2300 2299 2298
Statia u numarul 2298 a dete tat statiile:
2297 2300 2299 2301
Exer iµiul 9. Dete tarea staµiilor dintr-o reµea inel
Pentru rezolvarea a estei probleme vom folosi �fo-uri, �e are anal de omu-
ni aµie dintre o staµie ³i ei doi ve ini ai ei �ind simulat printr-un �fo.
#in lude <stdio.h>
#in lude <unistd.h>
#in lude <f ntl.h>
#in lude <sys/types.h>
#define MAX_STATII 5
/* statiile retelei de tip inel */
pid_t statii[MAX_STATII℄;
int n_statii = 0;
int
main (int arg , har *argv[℄)
{
/* ve inii unei statii (des riptori de fifo) */
int stinga, dreapta;
/* mesajul vehi ulat */
pid_t mesaj;
if (arg < 3)
{
fprintf (stderr, "Sintaxa: %s <stinga> <dreapta>\n", argv[0℄);
exit (2);
}
/* des hidem analele de omuni atie */
if ((stinga = open (argv[1℄, O_RDWR)) < 0
|| (dreapta = open (argv[2℄, O_RDWR)) < 0)
{
92 Atelier de programare în reµele de al ulatoare
perror ("open()");
exit (1);
}
mesaj = getpid ();
/* s riem mesajul pe analul din stinga */
write (stinga, &mesaj, sizeof (mesaj));
/* itim it putem de pe analul din dreapta */
while (1)
{
read (dreapta, &mesaj, sizeof (mesaj));
/* am primit identifi atorul statiei proprii */
if (mesaj == getpid ())
break; /* u siguranta elelalte statii l-au primit */
/* retinem mesajul primit... */
statii[n_statii++℄ = mesaj;
/* ...si-l trimitem la ve inul din stinga */
write (stinga, &mesaj, sizeof (mesaj));
}
/* am gasit toate statiile */
printf ("Statia: %d a gasit: \n", getpid ());
for (; n_statii > 0;)
printf ("%d ", statii[--n_statii℄);
printf ("\n");
fflush (stdout);
exit (0);
}
Pentru a testa programul, vom rea in i �³iere de tip �fo u ajutorul
omenzii mkfifo, apoi vom lansa în fundal in i instanµe ale programului (pre-
supunem în urma ompil rii a fost generat exe utabilul u numele ring).
Ie³irea va � redire tat în in i �³iere orespunz toare elor in i staµii al ror
onµinut va � listat la �nalul exe uµiei.
Un posibil rezultat este el de mai jos:
(infoiasi):~$ mkfifo 1
(infoiasi):~$ mkfifo 2
(infoiasi):~$ mkfifo 3
(infoiasi):~$ mkfifo 4
(infoiasi):~$ mkfifo 5
(infoiasi):~$ ring 1 2 > A &
[1℄ 2380
(infoiasi):~$ ring 2 3 > B &
[2℄ 2381
(infoiasi):~$ ring 3 4 > C &
[3℄ 2382
Comuni area între pro ese. Pipe-uri 93
(infoiasi):~$ ring 4 5 > D &
[4℄ 2383
(infoiasi):~$ ring 5 1 > E &
[5℄ 2384
(infoiasi):~$ at [A-E℄
Statia: 2380 a gasit:
2384 2383 2382 2381
Statia: 2381 a gasit:
2380 2384 2383 2382
Statia: 2382 a gasit:
2381 2380 2384 2383
Statia: 2383 a gasit:
2382 2381 2380 2384
Statia: 2384 a gasit:
2383 2382 2381 2380
Capitolul 6
Dupli area des riptorilor.
Redire t ri
Capitolul des rie maniera de dupli are a des riptorilor
de �³iere, prezentând primitivele dup() ³i dup2(). Du-
pli rile des riptorilor vor putea � folosite la redire tarea
intr rilor ³i ie³irilor standard.
6.1 Dupli area des riptorilor
Am v zut în apitolul 2 primitiva open() returneaz un des riptor de
�³ier aso iat �³ierului asupra ruia dorim s efe tu m diverse operaµii de
intrare/ie³ire.
Sistemul de operare UNIX ofer posibilitatea a un des riptor s indi e
un alt �³ier (�ind aso iat unui alt des riptor) de ât el obi³nuit. Operaµia se
nume³te redire tare ³i se folose³te el mai des în azul des riptorilor standard u
valorile 0, 1 ³i 2 are reprezint intrarea standard, ie³irea standard ³i, respe tiv,
ie³irea standard de eroare (vezi ³i apitolele 2 ³i 3).
Poate � utilizat ³i operaµia de dupli are a des riptorilor de �³ier are deter-
min existenµa a mai mult de un des riptor pentru a ela³i �³ier. Redire tarea
poate � v zut a un az parti ular de dupli are.
Dupli area ³i redire tarea se realizeaz , în fun µie de erinµe, re urgând la
unul dintre urm toarele apeluri de sistem:
#in lude <unistd.h>
int dup (int oldfd);
int dup2 (int oldfd, int newfd);
Primitiva dup() realizeaz dupli area des riptorului oldfd, returnând un
nou des riptor. Des riptorul returnat va indi a a ela³i �³ier a ³i des ripto-
rul oldfd. Atât noul, ât ³i ve hiul des riptor vor folosi în omun pointerul
de poziµie al �³ierului, printre altele. Da poziµia în �³ier este modi� at
prin intermediul primitivei lseek() folosind unul dintre des riptori, efe tul
va � observat ³i pentru operaµiile f ute utilizând el lalt des riptor. Anumiµi
Dupli area des riptorilor. Redire t ri 95
parametri (de exemplu lose-on-exe ) nu sunt totu³i omuni elor doi des rip-
tori (vezi man dup).
De reµinut faptul des riptorul nou alo at de dup() este el mai mi
des riptor liber (în his) disponibil.
Primitiva dup2() se omport similar u dup(), u deosebirea poate
� spe i� at expli it are va � noul des riptor dupli at. Dup apelul dup2(),
des riptorul newfd va indi a a ela³i �³ier a ³i oldfd. Da , înainte de operaµie,
des riptorul newfd era des his, atun i a esta este mai întâi în his, dup are
se realizeaz dupli area.
Ambele primitive returneaz des riptorul nou reat (în azul lui dup2()
egal u newfd) sau valoarea −1 în az de eroare.
6.1.1 Exemple
Urm toarea se venµ de od realizeaz redire tarea ie³irii standard spre un
�³ier des his având des riptorul fd:
...
fd = open ("fisier.txt", O_WRONLY);
...
if ((newfd = dup2 (fd, 1)) < 0)
{
perror ("Eroare la dup2()");
exit (1);
}
...
printf ("Salut");
...
În urma redire t rii, textul �Salut� tip rit u printf() nu va � s ris la
terminal, i în �³ierul u numele fisier.txt. Astfel, fragmentul de od are
a ela³i efe t a operatorul de redire tare > pus la dispoziµie de shell-ul UNIX.
Redire t rile de �³iere se p streaz hiar ³i în azul exe uµiei unei primitive
de tip exe () ( are supras rie pro esul urent u programul luat de pe dis , vezi
apitolul 3). Folosind a east fa ilitate, este posibil , de exemplu, one tarea
prin pipe a dou pro ese, unul dintre ele rulând un program exe utabil itit de
pe dis .
Ne propunem în ontinuare s înl nµuim exe uµia a dou omenzi, simulând
operatorul | al shell-ului.
Urm torul program va avea a ela³i efe t a linia de omenzi who | w -l:
#in lude <stdio.h>
#in lude <stdlib.h>
96 Atelier de programare în reµele de al ulatoare
#in lude <unistd.h>
#in lude <sys/wait.h>
void
who_w () /* who | w -l */
{
int pfd[2℄; /* un pipe */
/* ream pipe-ul */
if (pipe (pfd) == -1)
{
fprintf (stderr, "pipe\n");
exit (1);
}
/* ream primul opil */
swit h (fork ())
{
ase -1:
fprintf (stderr, "fork - 1\n");
exit (1);
ase 0: /* opilul */
lose (1);
/* dupli am des riptorul de s riere al pipe-ului
la iesirea standard (1) */
if (dup (pfd[1℄) != 1)
{
fprintf (stderr, "dup - 1\n");
exit (1);
}
/* putem in hide des riptorii pipe-ului,
din moment e am realizat dupli area */
lose (pfd[0℄);
lose (pfd[1℄);
/* se exe uta omanda "who"
datele vor fi trimise des riptorului de s riere
al pipe-ului */
exe lp ("who", "who", NULL);
fprintf (stderr, "exe - 1\n");
exit (1);
}
/* ream al doilea opil */
swit h (fork ())
{
ase -1:
fprintf (stderr, "fork - 2\n");
exit (1);
Dupli area des riptorilor. Redire t ri 97
ase 0: /* opilul */
lose (0);
/* dupli am des riptorul de intrare al pipe-ului
are va fi aso iat intrarii standard */
if (dup (pfd[0℄) != 0)
{
fprintf (stderr, "dup - 2\n");
exit (1);
}
/* des riptorii pipe-ului pot fi in hisi */
lose (pfd[0℄);
lose (pfd[1℄);
/* exe utam omanda "w " are va iti datele
de la intrarea standard, a um redire tata
la des riptorul de intrare al pipe-ului */
exe lp ("w ", "w ", "-l", NULL);
fprintf (stderr, "exe - 2\n");
exit (1);
}
/* parintele */
/* in hidem pipe-ul, nu-l folosim delo */
lose (pfd[0℄);
lose (pfd[1℄);
/* asteptam terminarea opiilor */
while (wait (NULL) != -1)
;
}
int
main () /* programul prin ipal */
{
who_w ();
return 0;
}
6.2 Exer iµii
�i la a est sfâr³it de apitol propunem âteva exer iµii spre rezolvare:
1. S se s rie un program are reeaz trei pro ese, astfel:
• primul pro es (p rinte) ite³te linie u linie dintr-un �³ier u numele
date.txt pân la sfâr³itul �³ierului ³i transmite liniile printr-un
pipe primului opil;
98 Atelier de programare în reµele de al ulatoare
• primul opil prime³te ara terele de la p rinte ³i sele teaz doar
literele mi i pe are le trimite printr-un alt pipe tre el de-al doilea
opil;
• al doilea pro es opil reeaz un �³ier denumit statisti a.txt în
are va memora, pe âte o linie, �e are liter distin t din ele pri-
mite ³i num rul de apariµii ale a esteia în �uxul de date primit. La
�nal, va trimite tre p rinte, printr-un pipe suplimentar, num rul
de litere distin te întâlnite.
P rintele va a�³a rezultatul primit de la al doilea opil.
2. S se realizeze un program de tip produ tor- onsumator astfel:
• programul prime³te a parametri în linia de omand dou numere:
num rul de pro ese produ tor ³i num rul de pro ese onsumator;
• resursa omun tuturor pro eselor este un pipe reat de pro esul
p rinte;
• p rintele este la rândul lui produ tor;
• în general, produ torii s riu în pipe un num r oare are de ara -
tere, iar onsumatorii ites din pipe, ara ter u ara ter, ât timp
a est lu ru este posibil.
Produ torii vor avea urm torul omportament:
• p rintele produ e un num r aleatoriu de ara tere �p�;
• eilalµi produ tori sunt pro ese independente (existente pe dis a
programe de sine st t toare) are genereaz la ie³irea standard un
num r oare are de ara tere � �. Pentru realizarea s opului pro-
pus, ie³irea standard a a estor pro ese va � one tat la ap tul de
s riere al pipe-ului.
• el puµin unul dintre produ tori este omanda finger.
Consumatorii ites ara terele din pipe ³i î³i a�³eaz identi� atorul de
pro es (PID-ul) urmat de ara terul itit la un moment dat.
În sistem mai exist un FIFO prin are onsumatorii vor raporta la �nal
rezultatele tre p rintele tuturor, s riind linii de forma urm toare:
numar_pro es : numar_o teti
Dupli area des riptorilor. Redire t ri 99
unde numar_o teti este num rul total de o teµi itiµi de pro esul respe -
tiv din pipe. P rintele va prelua a este informaµii ³i le va a�³a la ie³irea
standard.
3. S se res rie interpretorul de omenzi propus a exer iµiu în adrul api-
tolului 3, ad ugându-i fa ilit µi pre um posibilitatea folosirii operatorilor
de redire tare, posibilitatea modi� rii prompt-ului ³i posibilitatea de ex-
pandare a numelui �³ierelor pentru dire torul urent (tab- ompletion).
6.3 Rezolv ri
Exer iµiul 3. Shell
Vom prezenta în ontinuare rezolvarea omplet a problemei implement rii
shell-ului.
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude <sys/types.h>
#in lude <sys/stat.h>
#in lude <f ntl.h>
#in lude <signal.h>
#in lude <termios.h>
#in lude <dirent.h>
#in lude "keys.h"
/* Shell-ul poate expanda urmatoarele variabile:
$$ - PID-ul bash-ului
$0 - numele exe utabilului
$! - Parintele bash-ului
$? - odul ultimei omenzi exe utate
- nume de fisiere date u ajutorul lui *
- are implementat me anism de history ( u derulare)
- are implementat me anism de expandare a parametrilor
( u folosirea meta ara terului "*")
- posibilitatea de a s himba prompt-ul
($d - dir. urent, $u - nume utilizator)
- tab- ompletion (numai pe dire torul urent);
*/
#define TEMP_MASK "/tmp/shellXXXXXX"
extern har **environ; /* mediul */
/* pentru exploatarea terminalului */
stru t termios save_term;
100 Atelier de programare în reµele de al ulatoare
/* variabile globale */
har Prompt[200℄ = "Shell [$u $d℄$$ ";
har mdline[1000℄;
har aux[1000℄;
har *TEMP, *TEMP2, *ax;
har *HOME, *USERNAME;
har *BASH_NAME;
har *HISTORY;
int ifd = -1, ofd = -1, hfd = -1;
int lines_in_hist = 0;
int urent_line = -1;
int prompt_length = 0;
int SaveOutput;
int SaveInput;
int LastError = 0;
/* tratare semnale */
void semnal (int signum);
/* expandeaza numele de fisiere */
int expandare ( har *exp, har *lista);
/* afisare prompt */
void WritePrompt ();
/* fun tia de terminare */
void Iesire (int od);
/* permite ompletarea automata a numelui de fisiere
la apasarea TAB-ului */
int
tab_ ompletion ()
{
har *p;
stru t dirent **fisiere;
int nr = 1, n, i, index;
int files[1000℄, f = 0;
har File[50℄;
har om[50℄;
har wrong = 0;
bzero (File, 50);
bzero ( om, 50);
if ((p = strr hr ( mdline, ' ')) == NULL)
p = mdline;
else
p++;
str py ( om, p);
if ((n = s andir (".", &fisiere, 0, alphasort)) == -1)
{
printf ("Could not open dire tory\n");
return -1;
}
if ((p == NULL) || (strlen (p) == 0))
{
for (i = 0; i < n; i++)
if (strn mp (fisiere[i℄->d_name, "..", 2) != 0)
if (strn mp (fisiere[i℄->d_name, ".", 1) != 0)
printf ("%s\n", fisiere[i℄->d_name);
return (n - 2);
Dupli area des riptorilor. Redire t ri 101
}
for (i = 0; i < n; i++)
if (strn mp (fisiere[i℄->d_name, p, strlen (p)) == 0)
{
nr++;
files[ f++℄ = i;
}
f--;
if ( f < 0) /* da a nu e ni i in fisier */
{
return -1;
}
/* da a avem unul singur si este egal u parametrul */
if (( f == 0) &&
(strlen (fisiere[files[0℄℄->d_name) == strlen ( om)))
{
str py (p, om);
return 1;
}
if ( f > 0)
{
printf ("\n");
for (i = 0; i <= f; i++)
printf ("%s\n", fisiere[files[i℄℄->d_name);
}
while (1)
{
int len = strlen ( om);
wrong = 0;
if (strlen (fisiere[files[0℄℄->d_name) == len)
{
str py (p, om);
return f + 1;
}
om[len℄ = fisiere[files[0℄℄->d_name[len℄;
om[len + 1℄ = '\0';
for (i = 0; i < f; i++)
if (strn mp (fisiere[files[i℄℄->d_name,
om, len + 1) != 0)
{
wrong = 1;
break;
}
if (wrong == 1)
{
om[len℄ = '\0';
break;
}
if (strlen (fisiere[files[0℄℄->d_name)
== (len + 1))
break;
}
if (wrong == 1)
{
str py (p, om);
102 Atelier de programare în reµele de al ulatoare
return ( f + 1);
}
if (str mp (fisiere[files[0℄℄->d_name, om) == 0)
{
str py (p, om);
return ( f + 1);
}
str py (p, om);
return ( f + 1);
}
void
go_to_line (int lineno)
{
int = 1; /* liniile se onsidera in epand de la 1 */
har buffer;
lseek (hfd, 0, SEEK_SET);
while (( < lineno) && (read (hfd, &buffer, 1) == 1))
if (buffer == '\n')
++;
}
void
get_line (int lineno, har *buffer)
{
har *p = buffer;
if (lineno < 1)
{
buffer = NULL;
return;
}
go_to_line (lineno);
while (read (hfd, p, 1) == 1)
{
if (*p == '\n')
{
*p = '\0';
break;
}
p++;
}
}
/* s rie in istori ul shell-ului */
void
WriteToHistory ( har *buffer)
{
if (*buffer == '\n')
return;
lseek (hfd, 0, SEEK_END);
write (hfd, buffer, strlen (buffer));
write (hfd, "\n", 1);
lines_in_hist++;
}
Dupli area des riptorilor. Redire t ri 103
/* furnizeaza o anumita tasta apasata */
int
GetKey ()
{
stru t termios term;
int buffer = 0;
t getattr (0, &save_term);
fmakeraw (&term);
t setattr (0, TCSANOW, &term);
read (0, &buffer, 1);
if (buffer == 27)
{
read (0, &buffer, 2);
t setattr (0, TCSANOW, &save_term);
return buffer;
}
if (buffer == 0)
read (0, &buffer, 1);
t setattr (0, TCSANOW, &save_term);
return buffer;
}
/* itim o omanda introdusa */
void
read_ ommand ( har * mdline, int MAX_LINE)
{
int buf;
int = -1;
fflush (stdout);
fflush (stdin);
bzero ( mdline, MAX_LINE);
while ((buf = GetKey ()) != ENTER)
{
swit h (buf)
{
ase BACKSPACE:
if ( >= 0)
{
--;
printf ("\b \b");
mdline[ + 1℄ = '\0';
}
break;
ase BREAK:
Iesire (0);
ase KEY_DOWN:
if (lines_in_hist == 0)
break;
if ( urent_line == -1)
break;
if ( urent_line == 0)
urent_line = 1;
if ( urent_line < lines_in_hist)
104 Atelier de programare în reµele de al ulatoare
{
int i;
urent_line++;
for (i = 0; i < ; i++)
printf ("\b \b");
get_line ( urent_line, mdline);
= strlen ( mdline);
printf ("%s", mdline);
fflush (stdout);
break;
}
else
{
int i;
for (i = 0; i < ; i++)
printf ("\b \b");
urent_line = -1;
bzero ( mdline, MAX_LINE);
= 0;
fflush (stdout);
break;
}
ase KEY_UP:
if (lines_in_hist == 0)
break;
if ( urent_line == 0)
break;
if ( urent_line == -1)
{
int i;
urent_line = lines_in_hist;
for (i = 0; i < ; i++)
printf ("\b \b");
get_line ( urent_line, mdline);
= strlen ( mdline);
printf ("%s", mdline);
fflush (stdout);
break;
}
if ( urent_line > 0)
{
int i;
urent_line--;
for (i = 0; i < ; i++)
printf ("\b \b");
get_line ( urent_line, mdline);
= strlen ( mdline);
printf ("%s", mdline);
fflush (stdout);
break;
}
break;
ase KEY_LEFT:
ase KEY_RIGHT:
break;
ase TAB:
{
Dupli area des riptorilor. Redire t ri 105
int i, rez;
rez = tab_ ompletion ();
if (rez == 1)
{
int l = strlen ( mdline);
for (i = 0; i < ( + prompt_length); i++)
printf ("\b \b");
mdline[l℄ = ' ';
mdline[l + 1℄ = '\0';
}
if (rez >= 1)
{
WritePrompt ();
printf ("%s", mdline);
= strlen ( mdline);
mdline[ ℄ = '\0';
break;
}
fflush (stdout);
break;
}
break;
default:
if ( == -1)
{
mdline[0℄ = ( har) buf;
mdline[1℄ = 0;
= 1;
}
else
{
++;
mdline[ - 1℄ = ( har) buf;
mdline[ ℄ = '\0';
}
printf ("% ", ( har) buf);
}
fflush (stdout);
if (( + 1) == MAX_LINE)
{
mdline[ + 1℄ = '\0';
break;
}
}
mdline[ + 1℄ = '\0';
t setattr (0, TCSANOW, &save_term);
WriteToHistory ( mdline);
urent_line = -1;
return;
}
/* reeaza fisierul de istori */
void
reate_history ()
{
stru t stat infos;
106 Atelier de programare în reµele de al ulatoare
har buf;
if (stat (HISTORY, &infos) != -1)
{
if ((hfd = open (HISTORY, O_RDWR)) == -1)
{
printf ("Could not open history file\n");
Iesire (2);
}
lines_in_hist = 0;
while (read (hfd, &buf, 1) == 1)
if (buf == '\n')
lines_in_hist++;
lseek (hfd, 0, SEEK_SET);
return;
}
if ((hfd = reat (HISTORY, S_IRUSR | S_IWUSR)) == -1)
{
printf ("Could not reate history file\n");
Iesire (1);
}
lose (hfd);
if ((hfd = open (HISTORY, O_RDWR)) == -1)
{
printf ("Could not open history file\n");
Iesire (1);
}
}
/* afiseaza prompt-ul */
void
WritePrompt ()
{
har i;
har buf[400℄;
prompt_length = 0;
for (i = 0; i < strlen (Prompt); i++)
swit h (Prompt[i℄)
{
ase '$':
i++;
swit h (Prompt[i℄)
{
ase 'u':
printf ("%s", USERNAME);
prompt_length += strlen (USERNAME);
break;
ase '$':
printf ("$");
prompt_length++;
break;
ase 'd':
get wd (buf, 400);
if (str mp (buf, "/") != 0)
{
printf ("%s", (strr hr (buf, '/') + 1));
Dupli area des riptorilor. Redire t ri 107
prompt_length +=
strlen (strr hr (buf, '/') + 1);
}
else
{
printf ("%s", buf);
prompt_length += strlen (buf);
}
break;
}
break;
default:
{
printf ("% ", Prompt[i℄);
prompt_length++;
}
}
}
har *
itoa (int nr, har *buffer)
{
har aux[10℄;
har = 0, i;
har *p = buffer;
if (nr == 0)
{
*p = '0';
*(p + 1) = '\0';
return buffer;
}
bzero (aux, 10);
while (nr != 0)
{
aux[ ++℄ = 48 + (nr % 10);
nr /= 10;
}
for (i = - 1; i >= 0; i--)
{
*p = aux[i℄;
p++;
}
*p = '\0';
return buffer;
}
/* fun tie de alo are */
har *
alo are (int marime)
{
har *p;
if ((p = ( har *) mallo (marime)) == NULL)
{
printf ("Not enough memory\n");
108 Atelier de programare în reµele de al ulatoare
exit (2);
}
return p;
}
/* fun tie de expandare bazata pe "*" */
int
expand ( har * mdline)
{
har Ba kup[1000℄;
har Return[1000℄;
har *p;
bzero (Return, 1000);
if ((p = strtok ( mdline, " ")) == NULL)
{
str py ( mdline, Ba kup);
return;
}
do
{
if (str hr (p, '*') != NULL)
{
str at (Return, " ");
if (expandare (p, Return) == 1)
return 1;
}
else
{
str at (Return, p);
str at (Return, " ");
}
p = strtok (NULL, " ");
}
while (p != NULL);
str py ( mdline, Return);
return 0;
}
int
expandare ( har *exp, har *list)
{
stru t dirent **fisiere;
int n, = 0, i, j, k;
har has_last = 0;
har *param[10℄, *p;
har *index[10℄;
har EXPAND[100℄;
har *DIRECTORY;
har lista[1000℄;
str py (EXPAND, exp);
DIRECTORY = alo are (200);
Dupli area des riptorilor. Redire t ri 109
get wd (DIRECTORY, 200);
bzero (lista, 1000);
if ((n = s andir (DIRECTORY, &fisiere, 0, alphasort)) == -1)
{
printf ("Error on reading dire tory\n");
list[0℄ = 0;
return 1;
}
if ((EXPAND[0℄ == '*') && (strlen (EXPAND) == 1))
{
param[0℄ = alo are (3);
str py (param[0℄, "*\0");
param[1℄ = NULL;
= 1;
}
if (strlen (EXPAND) > 1)
{
if (EXPAND[0℄ == '*')
{
param[ ++℄ = alo are (3);
str py (param[ - 1℄, "*\0");
}
if (EXPAND[strlen (EXPAND) - 1℄ == '*')
has_last = 1;
if ((p = strtok (EXPAND, "*")) == NULL)
{
printf ("no expansion\n");
param[0℄ = NULL;
}
else
param[ ++℄ = p;
while ((p = strtok (NULL, "*")) != NULL)
{
param[ ++℄ = alo are (2);
str py (param[ - 1℄, "*\0");
param[ ++℄ = p;
}
if (has_last == 1)
{
param[ ℄ = alo are (2);
str py (param[ ℄, "*\0");
++;
}
param[ ℄ = NULL;
}
for (i = 0; i < n; i++)
if ((str mp (fisiere[i℄->d_name, ".") == 0) || \
(str mp (fisiere[i℄->d_name, "..") == 0))
ontinue;
else
{
har wrong = 0;
/* verifi a da a toti sunt '*' */
for (j = 0; j < ; j++)
110 Atelier de programare în reµele de al ulatoare
if (str mp (param[j℄, "*") != 0)
wrong = 1;
if (wrong == 0)
{
str at (lista, fisiere[i℄->d_name);
str at (lista, " ");
ontinue;
}
for (j = 0; j < ; j++)
if (str mp (param[j℄, "*") != 0)
{
index[j℄ = strstr (fisiere[i℄->d_name, param[j℄);
if (index[j℄ == NULL)
ontinue;
// da a sunt ultimul
if (j == ( - 1))
if (*(index[j℄ + strlen (param[j℄)) != '\0')
index[j℄ = NULL;
}
/* verifi a da a a fost vreo onditie neindeplinita */
wrong = 0;
for (j = 0; j < ; j++)
if (str mp (param[j℄, "*") != 0)
if (index[j℄ == NULL)
wrong = 1;
if (wrong == 1)
ontinue;
wrong = 0;
for (j = 0; ((j < ( - 1)) && (wrong == 0)); j++)
for (k = (j + 1); ((k < ) && (wrong == 0)); k++)
if ((str mp (param[j℄, "*") != 0)
&& (str mp (param[k℄, "*") != 0))
if (index[j℄ >= index[k℄)
wrong = 1;
if (wrong == 1)
ontinue;
str at (lista, fisiere[i℄->d_name);
str at (lista, " ");
}
str at (list, lista);
if (strlen (lista) == 0)
return 1;
return 0;
}
/* fun tie de iesire "gratioasa" */
void
Iesire (int od)
{
/* stergerea fisierelor temporare */
unlink (TEMP);
unlink (TEMP2);
/* in hiderea des riptorilor */
if (hfd != -1)
lose (hfd);
if (ifd != -1)
lose (ifd);
Dupli area des riptorilor. Redire t ri 111
if (ofd != -1)
lose (ofd);
/* eliberarea memoriei */
free (TEMP);
free (TEMP2);
free (ax);
free (HOME);
free (USERNAME);
free (HISTORY);
exit ( od);
}
/* verifi a da a apar variabile shell */
void
Sear hForVars ( har *argv[255℄)
{
int i;
har *p;
for (i = 0; i < 255; i++)
if (argv[i℄ == NULL)
break;
else
{
if (str mp (argv[i℄, "$?") == 0)
{
p = alo are (8);
argv[i℄ = itoa (LastError, p);
ontinue;
}
if (str mp (argv[i℄, "$$") == 0)
{
p = alo are (8);
argv[i℄ = itoa (getpid (), p);
ontinue;
}
if (str mp (argv[i℄, "$!") == 0)
{
p = alo are (8);
argv[i℄ = itoa (getppid (), p);
ontinue;
}
if (str mp (argv[i℄, "$0") == 0)
{
argv[i℄ = BASH_NAME;
}
}
}
/* fun tie de exe utie propriu-zisa a omenzilor */
int
MyExe ( har *INPUT, har *OUTPUT, har * mdline)
{
int fd1, fd2;
int b_in, b_out, i;
har *argv[255℄;
unsigned har = 1;
112 Atelier de programare în reµele de al ulatoare
har aux[1000℄;
har wait_for_ hild = 1;
har *p;
if ((p = str hr ( mdline, '*')) != NULL)
if (expand ( mdline) != 0)
{
printf ("No su h files...\n");
LastError = 128;
return;
}
str py (aux, mdline);
p = strtok (aux, " \t\n");
argv[0℄ = p;
/* "sparge" argumentele in asa fel in at
ele date u " sa ramana inta te */
while ((p = strtok (NULL, " \t\n")) != NULL)
{
if (*p != '"')
{
argv[ ++℄ = p;
ontinue;
}
{
har aux[100℄;
bzero (aux, 100);
if (*(p + 1) == '\0')
str py (aux, p);
else
{
str py (aux, (p + 1));
str at (aux, " ");
}
while ((p = strtok (NULL, " \t\n")) != NULL)
{
str at (aux, p);
if ((aux[strlen (aux) - 1℄ == '"')
&& (aux[strlen (aux) - 2℄ != '\\'))
break;
str at (aux, " ");
}
aux[strlen (aux) - 1℄ = '\0';
str py (ax, aux);
argv[ ++℄ = ax;
} /* sfirsitul blo ului */
}
argv[ ℄ = NULL;
Sear hForVars (argv);
if (( >= 2) && (str mp (argv[ - 1℄, "&") == 0))
{
wait_for_ hild = 0;
argv[ - 1℄ = NULL;
}
if ((INPUT == NULL) && (OUTPUT == NULL))
{
Dupli area des riptorilor. Redire t ri 113
/* lanseaza un pro es are va exe uta omanda */
swit h (fork ())
{
ase -1:
printf ("Fork error\n");
return 2;
ase 0:
exe vp (argv[0℄, argv);
printf ("Error on exe \n");
exit (2);
}
if (wait_for_ hild == 1)
wait (&LastError);
else
return 0;
return 0;
}
/* redire tarea iesirii */
if (INPUT == NULL)
{
if ((fd1 = open (OUTPUT, O_RDWR)) == -1)
{
printf ("Could not open output file\n");
return 3;
}
ftrun ate (fd1, 0);
if ((b_out = dup (1)) == -1)
{
printf ("Could not save output\n");
lose (fd1);
return 4;
}
dup2 (fd1, 1);
swit h (fork ())
{
ase -1:
printf ("Fork error\n");
return 2;
ase 0:
exe vp (argv[0℄, argv);
printf ("Error on exe \n");
return 3;
}
if (wait_for_ hild == 1)
wait (&LastError);
lose (fd1);
dup2 (b_out, 1);
return 0;
}
/* redire tarea intrarii */
if (OUTPUT == NULL)
{
if ((fd1 = open (INPUT, O_RDWR)) == -1)
{
printf ("Could not open input file\n");
return 3;
}
114 Atelier de programare în reµele de al ulatoare
if ((b_in = dup (0)) == -1)
{
printf ("Could not save input\n");
lose (fd1);
return 4;
}
dup2 (fd1, 0);
swit h (fork ())
{
ase -1:
printf ("Fork error\n");
return 2;
ase 0:
exe vp (argv[0℄, argv);
printf ("Error on exe \n");
return 3;
}
if (wait_for_ hild == 1)
wait (&LastError);
lose (fd1);
dup2 (b_in, 0);
return 0;
}
/* redire tarea ambelor */
if ((fd1 = open (INPUT, O_RDONLY)) == -1)
{
printf ("Could not open file\n");
return 1;
}
if ((fd2 = open (OUTPUT, O_RDWR)) == -1)
{
printf ("Could not open output file\n");
return 2;
}
ftrun ate (fd2, 0);
if (((b_out = dup (1)) == -1) || ((b_in = dup (0)) == -1))
{
printf ("Error dupli ate output\n");
return 2;
}
dup2 (fd1, 0);
dup2 (fd2, 1);
swit h (fork ())
{
ase -1:
printf ("Fork error\n");
return 2;
ase 0:
exe vp (argv[0℄, argv);
printf ("Error on exe \n");
return 3;
}
if (wait_for_ hild == 1)
wait (&LastError);
Dupli area des riptorilor. Redire t ri 115
lose (fd1);
lose (fd2);
dup2 (b_out, 1);
dup2 (b_in, 0);
return 0;
}
void
MoveOutputToInput ()
{
int fd1, fd2;
har Buffer;
if ((fd1 = open (TEMP, O_RDWR)) == -1)
{
printf ("Could not open file\n");
exit (2);
}
if ((fd2 = open (TEMP2, O_RDWR)) == -1)
{
printf ("Could not open file\n");
exit (3);
}
ftrun ate (fd2, 0);
while (read (fd1, &Buffer, 1) == 1)
write (fd2, &Buffer, 1);
ftrun ate (fd1, 0);
lose (fd1);
lose (fd2);
}
/* reeaza un fisier */
void
CreateFile ( har *filename)
{
int fd;
har *p = filename;
while (*p == ' ')
p++;
filename = p;
if ((fd = reat (filename, S_IRUSR | S_IWUSR)) == -1)
{
printf ("Could not reate file\n");
return;
}
lose (fd);
}
void
TransferTo ( har *dest, har *sour e)
{
int sfd, dfd;
har buf;
CreateFile (dest);
116 Atelier de programare în reµele de al ulatoare
if ((sfd = open (sour e, O_RDONLY)) == -1)
{
printf ("Could not open sour e file\n");
return;
}
if ((dfd = open (dest, O_WRONLY)) == -1)
{
printf ("Could not open destination file\n");
return;
}
while (read (sfd, &buf, 1) == 1)
write (dfd, &buf, 1);
lseek (sfd, 0, SEEK_SET);
ftrun ate (sfd, 0);
lose (sfd);
lose (dfd);
}
/* verifi a existenta operatorilor de redire tare */
int
Che kForPipes ()
{
har *p;
har SaveLine[1000℄;
har Buffer;
if ((p = strstr ( mdline, "|")) == NULL)
{
if ((p = strstr ( mdline, ">")) != NULL)
{
int i = 0;
str py (SaveLine, p);
*p = '\0';
SaveLine[0℄ = ' ';
while (SaveLine[i++℄ == ' ');
i--;
MyExe (NULL, TEMP2, mdline);
TransferTo (&SaveLine[i℄, TEMP2);
return 1;
}
if ((p = strstr ( mdline, "<")) != NULL)
{
int i = 0;
str py (SaveLine, p);
*p = '\0';
SaveLine[0℄ = ' ';
while (SaveLine[i++℄ == ' ');
i--;
MyExe (&SaveLine[i℄, NULL, mdline);
return 1;
}
return 0;
}
str py (SaveLine, p);
*p = '\0';
MyExe (NULL, TEMP2, mdline);
str py ( mdline, &SaveLine[1℄);
Dupli area des riptorilor. Redire t ri 117
while ((p = strstr ( mdline, "|")) != NULL)
{
str py (SaveLine, p);
*p = '\0';
MyExe (TEMP2, TEMP, mdline);
MoveOutputToInput ();
str py ( mdline, &SaveLine[1℄);
}
MyExe (TEMP2, NULL, mdline);
return 1;
}
/* reeaza fisiere temporare */
void
CreateTemp ()
{
TEMP = alo are (100);
TEMP2 = alo are (100);
if ((TEMP = tempnam ("/tmp/", "shell")) == NULL)
{
printf ("Could not get a tmp file\n");
exit (2);
}
if ((TEMP2 = tempnam ("/tmp", "shell")) == NULL)
{
printf ("Could not get a tmp file\n");
exit (2);
}
if ((ifd = reat (TEMP, S_IRWXU)) == -1)
{
printf ("Could not reate tmp file\n");
exit (2);
}
lose (ifd);
if ((ifd = open (TEMP, O_RDWR)) == -1)
{
printf ("Could not open temp file for reading\n");
exit (3);
}
if ((ofd = reat (TEMP2, S_IRWXU)) == -1)
{
printf ("Could not reate tmp file\n");
exit (2);
}
lose (ofd);
if ((ofd = open (TEMP2, O_RDWR)) == -1)
{
printf ("Could not reopen tmp file\n");
exit (2);
}
}
/* seteaza omenzile */
void
SetCommand ( har * mdline)
{
118 Atelier de programare în reµele de al ulatoare
har **Vars = environ;
int = 0;
if (strlen ( mdline) == 3)
{
while (Vars[ ++℄ != NULL)
printf ("%s\n", Vars[ - 1℄);
return;
}
}
/* verifi a da a exista omenzi spe iale:
"exit", " d", "set", "prompt" */
int
Spe ialCommand ()
{
if (str mp ( mdline, "exit") == 0)
{
unlink (TEMP);
unlink (TEMP2);
exit (0);
}
if (strn mp ( mdline, " d ", 3) == 0)
{
if ( mdline[3℄ == '~')
{
har aux[300℄;
bzero (aux, 300);
str at (aux, HOME);
str at (aux, & mdline[4℄);
hdir (aux);
}
hdir (& mdline[3℄);
return 1;
}
if (strn mp ( mdline, "set ", 4) == 0)
{
SetCommand ( mdline);
return 1;
}
if (strn mp ( mdline, "prompt ", 7) == 0)
{
str py (Prompt, mdline + 7);
return 1;
}
return 0;
}
void
print_infos ()
{
printf ("Starting DShell...\n");
printf (" home dire tory:%s\n", HOME);
printf (" history file:%s\n", HISTORY);
}
/* programul prin ipal */
Dupli area des riptorilor. Redire t ri 119
int
main (int arg , har *argv[℄)
{
har *p;
/* tratare semnale */
if ((signal (SIGTERM, semnal) == SIG_ERR)
|| (signal (SIGINT, semnal) == SIG_ERR))
{
printf ("Could not trap signal\n");
return 2;
}
ax = alo are (100);
HISTORY = alo are (300);
bzero (HISTORY, 300);
CreateTemp ();
HOME = getenv ("HOME");
USERNAME = getenv ("USER");
BASH_NAME = argv[0℄;
str py (HISTORY, HOME);
str at (HISTORY, "/");
str at (HISTORY, ".shell_history");
reate_history ();
/* verifi a linia de omanda */
if ((arg >= 2) && (str mp (argv[1℄, "-v") == 0))
print_infos ();
/* exe uta la infinit... */
while (1)
{
/* afiseaza prompt */
WritePrompt ();
/* iteste omanda */
read_ ommand ( mdline, 1000);
printf ("\n");
if ( mdline[0℄ == '\n')
ontinue;
/* verifi a da a e omanda spe iala */
if (Spe ialCommand () == 1)
ontinue;
/* verifi a redire tari */
if (Che kForPipes () == 0)
MyExe (NULL, NULL, mdline);
}
return 0;
}
/* fun tie de tratare a semnalelor */
void
semnal (int signum)
{
if (signum == SIGTERM)
Iesire (SIGTERM);
}
Programul folose³te un �³ier antet keys.h onµinând onstantele tastelor
utilizate (dependente de terminal):
120 Atelier de programare în reµele de al ulatoare
#ifndef __MYKEYS__
#define __MYKEYS__ 1
#define KEY_UP 16731 /* sageata in sus */
#define KEY_DOWN 16987 /* sageata in jos */
#define KEY_LEFT 17499 /* sageata stinga */
#define KEY_RIGHT 17243 /* sageata dreapta */
#define BACKSPACE 127
#define TAB 9
#define SPACE 32
#define ENTER 13
#define BREAK 3
#endif
O posibil rulare a shell -ului este urm toarea:
(infoiasi):~$ ./shell
Shell [lr shell_final℄$ ls
Makefile Makefile~ keys.h shell shell. shell. ~
Shell [lr shell_final℄$ at Make # a tionare tasta TAB
Makefile
Makefile~
Shell [lr shell_final℄$ at Makefile
all:
g shell. -g -o shell
lean:
rm -f *~ shell
Shell [lr shell_final℄$ at Makefile | w -l
5
Shell [lr shell_final℄$ rm *~
Shell [lr shell_final℄$ ls
Makefile keys.h shell shell.
Shell [lr shell_final℄$ ls -l *.
-rwxr-xr-x 1 lr lr 21459 Jun 9 22:35 shell.
Shell [lr shell_final℄$ prompt Salut $u:>
Salut lr :>exit
Capitolul 7
Interfaµa so ket
În adrul a estui apitol se prezint interfaµa so ket BSD
³i prin ipalele on epte referitoare la omuni area în re-
µea. De asemenea, se des riu primitivele so ket() ³i
so ketpair().
7.1 Preliminarii
Exist mai multe metode de omuni are între pro ese. Pân în a est moment
am dis utat despre omuni area între pro ese a�ate pe a eea³i ma³in . De
a um în olo ne vom o upa de me anismele de transmisie de date între pro ese
a�ate pe al ulatoare diferite.
Dup tipul onexiunii, a este metodele de omuni are pot � lasi� ate în:
• omuni aµii orientate� onexiune da ele dou pro ese (pun tele �nale
ale transmisiei de date) stabiles o onexiune (virtual ) înaintea efe tu rii
s himbului de date. A este omuni aµii au lo în trei pa³i: stabilirea o-
nexiunii, transferul datelor ³i în hiderea onexiunii.
• omuni aµii f r onexiune în are sunt folosite mesaje (numite ³i data-
grame) pentru transmiterea informaµiilor. Datagramele sunt transmise
independent ³i trebuie s onµin toate informaµiile ne esare g sirii ma-
³inii de destinaµie. În mod normal au o lungime �x ³i pot sosi într-o
ordine diferit de ea în are au fost trimise sau pot � pierdute.
Comuni aµiile orientate� onexiune mai pot � lasi� ate dup modul în are
se realizeaz �uxul de date în:
• simplex � datele pot ir ula într-o singur dire µie;
• half duplex � dire µia de transmitere a datelor poate � s himbat , dar
datele se pot transmite la un moment dat într-un singur sens;
• full duplex (bi-dire µional) � datele se pot transmite simultan în ambele
dire µii.
122 Atelier de programare în reµele de al ulatoare
Comuni aµiile pot � blo ante sau neblo ante. O operaµie de transmitere sau
re epµie de date este blo ant da nu returneaz pân ând operaµia nu a fost
terminat ( u su es sau nu). Operaµiile neblo ante returneaz imediat.
Cel mai adesea omuni aµiile între pro ese de-a lungul unei reµele sunt rea-
lizate prin intermediul unui set de reguli are formeaz o familie de proto oale.
Pentru Internet se utilizeaz familia de proto oale TCP/IP .
Familia de proto oale TCP/IP onst dintr-o stiv de proto oale, stru -
turate pe nivele. Din pun tul de vedere al programatorului sunt importante
mai ales urm toarele proto oale (vezi ³i �gura 7.1):
• TCP (Transmission Control Proto ol) este un proto ol orientat� onexiu-
ne are ofer posibilitatea de a realiza omuni aµii full duplex sigure. Se
bazeaz pe proto olul IP. TCP este de departe el mai folosit proto ol în
omuni area în reµele one tate la Internet. Proto olul TCP a fost de�nit
în mod o� ial în do umentul RFC
1
793.
• UDP (User Datagram Proto ol) reprezint proto olul utilizat pentru o-
muni aµii nesigure în mod ne one tat (prin intermediul datagramelor).
Detalii despre spe i� aµiile proto ului UDP pot � g site în RFC 768.
• ICMP (Internet Control Message Proto ol) este folosit pentru tratarea
erorilor ³i ontrolul informaµiilor vehi ulate în Internet. Folose³te proto-
olul IP pentru realizarea s himbului de date. În mod normal, pro esele
utilizator nu au nevoie s a eseze ICMP, deoare e mesajele a estuia sunt
pro esate de tre software-ul TCP/IP.
• IP (Internet Proto ol) reprezint proto olul de baz pentru UDP, TCP ³i
ICMP. Pro esele utilizator foloses doar TCP sau UDP, folosirea dire t
a proto olului IP �ind rar întâlnit .
• ARP (Address Resolution Proto ol) este un proto ol utilizat la trans-
latarea adreselor IP în adrese hardware (Ethernet).
• RARP (Reverse Address Resolution Proto ol) translateaz adresele hard-
ware în adrese Internet.
1
Do umentele RFC (Request For Comments) sunt do umentele are des riu, regle-
menteaz ³i a tualizeaz me anismele intime ale Internetului, în spe ial ale proto oalelor.
Interfaµa so ket 123
Figura 7.1: Stiva de proto oale TCP/IP
7.2 Interfaµa so ket
Interfaµa so ket reprezint o fa ilitate general , independent de arhite tura
hardware, de proto ol ³i de tipul de transmisiune a datelor, pentru fa ilitarea
omuni rii între pro ese a�ate pe ma³ini diferite, one tate în reµea, intro-
dus pentru prima dat în sistemul BSD 4.1 ³i mai apoi perfe µionat odat
u versiunea BSD 4.2. So ket-urile sunt disponibile în toate versiunile UNIX
a tuale.
Interfaµa so ket suport diferite proto oale de omuni aµie. Proto olul do-
meniului UNIX este folosit pentru omuni aµii între pro ese de pe a eea³i
ma³in (rulând un sistem UNIX), iar proto olul domeniului Internet este uti-
lizat pentru omuni aµii între pro ese de pe a ela³i omputer sau de pe om-
putere diferite one tate la reµea, folosind TCP/IP. Desigur, sunt a eptate ³i
alte proto oale de omuni aµie.
A³adar, un so ket poate avea tipuri diferite ³i poate � aso iat u unul sau
mai multe pro ese, existând în adrul unui domeniu de omuni aµie. Datele
pot � s himbate numai între so ket-uri aparµinând a eluia³i domeniu de omu-
ni aµie.
124 Atelier de programare în reµele de al ulatoare
Din pun tul de vedere al programatorului, un so ket arat ³i se omport
similar unui des riptor de �³ier. Primitivele pre um read() sau write() lu-
reaz u so ket-urile în a eea³i manier în are lu reaz u �³iere ³i pipe-uri.
Diferenµele dintre so ket-uri ³i des riptorii normali de �³iere apar la rearea
unui so ket ³i la o serie de operaµiuni spe iale de ontrol al so ket-urilor. A este
operaµiuni sunt diferite datorit omplexit µii suplimentare la stabilirea one-
xiunilor în reµea omparativ u a esul normal la dis .
Cele mai uzuale apeluri are se pot exe uta asupra unui so ket sunt sinte-
tizate în tabelul de mai jos. A este primitive vor � des rise în adrul a estui
apitol ³i în apitolele urm toare.
Tabela 7.1: Primitivele are a µioneaz asupra so ket-urilor
Primitiv Des riere
so ket() Creeaz un nou pun t de ap t al omuni aµiei (so ket)
bind() Ata³eaz o adres lo al la un so ket
listen() Permite unui so ket s a epte onexiuni
a ept() Blo heaz apelantul pân la sosirea unei ereri de onexiune
(utilizat de serverul TCP)
onne t() Tentativ (a tiv ) de a stabili o onexiune
(utilizat de lientul TCP)
send() Trimite date prin intermediul unui so ket
re eive() Re epµioneaz date prin intermediul unui so ket
lose() În hide des riptorul de so ket ; se elibereaz onexiunea
shutdown() În hide dire µional des riptorul de so ket ;
se elibereaz onexiunea
7.2.1 Primitiva so ket()
Pentru rearea unui so ket vom utiliza primitiva so ket():
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int so ket(int domain, int type, int proto ol)
Cei trei parametri ai primitivei stabiles : domeniul de omuni are, tipul
so ket-ului ³i proto olul de omuni are utilizat.
Valorile are poate s le ia domain în mod uzual sunt (pres urtarea AF
provine de la Address Family):
Interfaµa so ket 125
• AF_UNIX stabile³te domeniul de omuni are lo al (domeniul UNIX);
• AF_INET este utilizat pentru omuni aµii între pro ese a�ate pe a eea³i
ma³in sau pe ma³ini diferite, folosind stiva de proto oale TCP/IP (do-
meniul Internet).
Argumentul domain permite de i stabilirea formatului adreselor ma³inilor
impli ate în transferul de date. Pentru domeniul Internet, �e are so ket va
avea aso iat o adres ) format din adresa IP a ma³inii gazd ³i un num r de
16 biµi, lo al gazdei respe tive, denumit port (vezi apitolul 8).
Interfaµa so ket este su� ient de general pentru a putea folosi ³i alte
domenii de transmisie, re urgând la proto oale prezente pe sisteme pre um
Novell (AF_IPX) sau Apple (AF_APPLETALK). În lo ul pre�xului AF se poate
utiliza ³i PF, PF �ind pres urtarea de la Proto ol Family (a³adar, onstanta
AF_UNIX este e hivalent u PF_UNIX).
Tipul so ket-ului se refer la modalitatea de realizare a omuni rii. Cele
mai utilizate dou valori sunt:
• SOCK_STREAM stabile³te un �ux (stream ) de date f r limite ale înregis-
tr rilor. Livrarea într-un mediu de reµea este garantat ; da livrarea
este imposibil , expeditorul prime³te un indi ator de eroare. Comuni-
area de i se va realiza full-duplex, sigur , orientat � onexiune.
• SOCK_DGRAM va stabili o omuni are f r onexiune, nesigur , folosind
datagrame.
Se mai poate folosi ³i onstanta SOCK_RAW are ofer un a es la proto olul
reµea (e.g. proto olul IP), de nivel inferior. Pentru mai multe detalii, onsultaµi
manualul.
Argumentul proto ol spe i� proto olul parti ular are va � utilizat pen-
tru transmisia datelor. Valoarea 0 pentru a est argument este foarte des întâl-
nit . A easta permite sistemului s aleag primul proto ol permis u pere hea
de valori spe i� ate pentru familie ³i tip. De exemplu, pentru domain egal u
AF_INET ³i type egal u SOCK_STREAM se va onsidera proto olul de transport
TCP, iar pentru azul în are domain este egal u AF_INET ³i type egal u
SOCK_DGRAM se va onsidera impli it proto olul de transport UDP.
În az de su es, primitiva so ket() returneaz un des riptor de �³ier
ata³at so ket-ului, iar în az de eroare, se returneaz −1 ³i variabila errno
des rie problema survenit .
126 Atelier de programare în reµele de al ulatoare
7.2.2 Exemplu
Ne propunem în ontinuare s re m mai multe so ket-uri, �xând diferite valori
pentru ele trei argumente ale primitivei so ket(). Vom observa anumite
ombinaµii sunt invalide, returnându-se eroare.
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <stdio.h>
#in lude <errno.h>
extern int errno;
/* domeniul (familia) de adrese */
int AF[10℄ =
{ PF_UNIX, PF_INET, PF_INET6, PF_IPX, PF_NETLINK,
PF_X25, PF_AX25, PF_ATMPVC, PF_APPLETALK, PF_PACKET };
har *AF_names[10℄ =
{ "PF_UNIX", "PF_INET", "PF_INET6", "PF_IPX", "PF_NETLINK",
"PF_X25", "PF_AX25", "PF_ATMPVC", "PF_APPLETALK", "PF_PACKET" };
/* tipul de so ket */
int TYPE[6℄ =
{ SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SOCK_RAW, SOCK_RDM,
SOCK_PACKET };
har *TYPE_names[6℄ =
{ "SOCK_STREAM", "SOCK_DGRAM", "SOCK_SEQPACKET",
"SOCK_RAW", "SOCK_RDM", "SOCK_PACKET" };
/* proto oalele de omuni atie */
int PROTOCOL[25℄ = {
IPPROTO_IP, /* Dummy proto ol for TCP. */
IPPROTO_HOPOPTS, /* IPv6 Hop-by-Hop options. */
IPPROTO_ICMP, /* Internet Control Message Proto ol. */
IPPROTO_IGMP, /* Internet Group Management Proto ol. */
IPPROTO_IPIP, /* IPIP tunnels */
IPPROTO_TCP, /* Transmission Control Proto ol. */
IPPROTO_EGP, /* Exterior Gateway Proto ol. */
IPPROTO_PUP, /* PUP proto ol. */
IPPROTO_UDP, /* User Datagram Proto ol. */
IPPROTO_IDP, /* XNS IDP proto ol. */
IPPROTO_TP, /* SO Transport Proto ol Class 4. */
IPPROTO_IPV6, /* IPv6 header. */
IPPROTO_ROUTING, /* IPv6 routing header. */
IPPROTO_FRAGMENT, /* IPv6 fragmentation header. */
IPPROTO_RSVP, /* Reservation Proto ol. */
IPPROTO_GRE, /* General Routing En apsulation. */
Interfaµa so ket 127
IPPROTO_ESP, /* en apsulating se urity payload. */
IPPROTO_AH, /* authenti ation header. */
IPPROTO_ICMPV6, /* ICMPv6. */
IPPROTO_NONE, /* IPv6 no next header. */
IPPROTO_DSTOPTS, /* IPv6 destination options. */
IPPROTO_MTP, /* Multi ast Transport Proto ol. */
IPPROTO_ENCAP, /* En apsulation Header. */
IPPROTO_PIM, /* Proto ol Independent Multi ast. */
IPPROTO_RAW, /* Raw IP pa kets. */
};
har *PROTOCOL_names[25℄ = {
"Dummy proto ol for TCP.",
"IPv6 Hop-by-Hop options. ",
"Internet Control Message Proto ol.",
"Internet Group Management Proto ol.",
"IPIP tunnels (older KA9Q tunnels use 94).",
"Transmission Control Proto ol.",
"Exterior Gateway Proto ol.",
"PUP proto ol.",
"User Datagram Proto ol.",
"XNS IDP proto ol.",
"SO Transport Proto ol Class 4.",
"IPv6 header.",
"IPv6 routing header.",
"IPv6 fragmentation header.",
"Reservation Proto ol.",
"General Routing En apsulation.",
"en apsulating se urity payload.",
"authenti ation header.",
"ICMPv6",
"IPv6 no next header.",
"IPv6 destination options.",
"Multi ast Transport Proto ol.",
"En apsulation Header.",
"Proto ol Independent Multi ast.",
"Raw IP pa kets."
};
int
main ()
{
int ii = 10, ij = 6, ik = 26; /* valori maxime indi i de tablou */
int i, j, k; /* iteratori */
int sd;
128 Atelier de programare în reµele de al ulatoare
printf ("Familie:\n");
for (i = 0; i < ii; i++)
printf (" %d-%s\n", AF[i℄, AF_names[i℄);
printf ("Tipuri de so ket:\n");
for (j = 0; j < ij; j++)
printf (" %d-%s\n", TYPE[j℄, TYPE_names[j℄);
printf ("Proto oale:\n");
for (k = 0; k < ik; k++)
printf (" %d-%s\n", PROTOCOL[k℄, PROTOCOL_names[k℄);
/* in er am toate ombinatiile posibile */
for (i = 0; i < ii; i++)
for (j = 0; j < ij; j++)
for (k = 0; k < ik; k++)
{
printf ("Apel so ket(%s, %s, %s)\",
AF_names[i℄, TYPE_names[j℄, PROTOCOL_names[k℄);
fflush (stdout);
/* in er am sa ream un so ket... */
if ((sd = so ket (AF[i℄, TYPE[j℄, PROTOCOL[k℄)) == -1)
{
perror ("N-am putut rea so ket-ul: ");
/* afisam eroarea survenita */
swit h (errno)
{
ase EPROTONOSUPPORT:
printf ("Nu este suportat proto olul\n");
break;
ase ENFILE:
printf
("Nu este sufi ienta memorie "
"pentru alo area unui nou so ket\n");
break;
ase EMFILE:
printf ("Depasire in tabela de alo are a fisierelor\n");
break;
ase EACCES:
printf
("Nu avem permisiunea de a rea un so ket "
"de tipul sau proto olul spe ifi at\n");
break;
ase ENOBUFS:
ase ENOMEM:
printf ("Nu este sufi ienta memorie\n");
break;
ase EINVAL:
Interfaµa so ket 129
printf
("Proto ol ne unos ut sau familie de "
"proto oale inexistenta\n");
break;
}
ontinue;
}
lose (sd);
}
return 0;
}
7.2.3 Primitiva so ketpair()
A east primitiv este asem n toare apelului so ket(), �ind utilizat pentru
rearea unei pere hi de so ket-uri one tate.
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int so ketpair(int domain, int type, int proto ol, int sv[2℄);
Primele trei argumente sunt a ³i ele de la so ket, iar ultimul este un
ve tor onµinând ei doi des riptori de so ket reaµi în urma exe uµiei a estei
primitive.
Semanti a primitivei este apropiat elei de la apelul pipe(), so ket-urile
reate prin so ketpair() putând � folosite de pro ese înrudite.
7.2.4 Exemplu
Un exemplu simplu de transmitere de mesaje între dou pro ese p rinte� opil
este el de mai jos:
/* Comuni atie intre pro ese folosind so ketpair() */
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <stdio.h>
#in lude <errno.h>
#in lude <unistd.h>
#in lude <string.h>
#in lude <sys/wait.h>
130 Atelier de programare în reµele de al ulatoare
int
main ()
{
int sd[2℄;
/* mesajele vehi ulate */
har *p = "Eu sunt parintele";
har * = "Eu sunt opilul";
har buf[100℄;
/* se reeaza pere hea de so ket-uri, folosindu-se
domeniul de adrese AF_UNIX */
if (so ketpair (AF_UNIX, SOCK_STREAM, 0, sd) == -1)
{
perror ("Eroare la reare so ket");
exit (1);
}
/* in er am sa ream un pro es opil */
swit h (fork ())
{
ase -1: /* eroare */
perror ("Eroare la fork()");
exit (1);
break;
ase 0: /* fiul */
/* itim din so ket mesajul */
if (read (sd[1℄, buf, 100) < 0)
{
perror ("Eroare la read()");
exit (2);
}
printf ("Pro esul u PID-ul %d ( opilul) a primit: '%s'\n",
getpid (), buf);
/* s riem mesajul opilului */
if (write (sd[1℄, , 100) < 0)
{
perror ("Eroare la write()");
exit (2);
}
/* trimitem EOF */
lose (sd[1℄);
/* am terminat */
exit (0);
default: /* parintele */
/* trimitem mesajul parintelui */
if (write (sd[0℄, p, 100) < 0)
{
perror ("Eroare la write()");
Interfaµa so ket 131
exit (3);
}
/* itim mesajul primit de la opil */
if (read (sd[0℄, buf, 100) < 0)
{
perror ("Eroare la read()");
exit (3);
}
printf ("Pro esul u PID-ul %d (parintele) a primit: '%s'\n",
getpid (), buf);
/* asteptam terminarea opilului */
if (wait (NULL) < 0)
{
perror ("Eroare la wait()");
exit (3);
}
/* gata! */
lose (sd[0℄);
return (0);
}
}
7.3 Exer iµii
1. Folosind pere hi de so ket-uri generate de so ketpair() s se on eap
un program are ite³te de la intrarea standard o expresie matemat-
i are va � transmis spre evaluare unor pro ese opil. Fie are pro es
opil va evalua o sub-expresie matemati , în manier divide-et-impera,
returnând prin so ket valoarea ei. Pro esul p rinte va primi rezultatul
�nal ³i-l va a�³a la ie³irea standard. Expresiile matemati e vor putea
onµine operanzi întregi, operatori aritmeti i uzuali ³i paranteze.
2. S se res rie exer iµiul referitor la dete tarea staµiilor dintr-o reµea de tip
magistral (propus în apitolul 5), utilizându-se îns so ket-uri.
Capitolul 8
Modelul lient/server � TCP
A est apitol prezint maniera de s riere a programelor
server ³i a programelor lient utilizând proto olul de trans-
port TCP (Transmission Control Proto ol).
8.1 Modelul lient/server
În adrul apli aµiilor în reµea paradigma el mai fre vent folosit estemodelul
lient/server, în are un server ofer anumite servi ii anumitor lienµi rulând
pe a eea³i ma³in sau la distanµ (pe alte al ulatoare one tate la reµea).
Serverul poate satisfa e ererile provenite de la lienµi în mod iterativ (la
un moment dat serverul va deservi ereri provenite de la un singur lient,
se venµial pentru toµi lienµii lui) sau on urent (mai multe ereri, provenite de
la lienµi multipli, vor � pro esate simultan). Serverele on urente sunt folosite
mai ales atun i ând r spunsul nu poate � transmis înapoi imediat sau în
azul în are trebuie realizate omuni aµii suplimentare u lientul. De obi ei,
serverele on urente foloses proto oale de omuni aµie orientate onexiune.
Astfel, serverul on urent r spunde ori ror ereri lient independent de toµi
eilalµi lienµi.
Comuni area dintre server ³i lienµii s i se va realiza prin intermediul suitei
de proto oale TCP/IP, prin intermediul so ket-urilor, utilizându-se stream-uri
de o teµi (proto olul TCP) sau datagrame (proto olul UDP). Despre UDP vom
dis uta detaliat în apitolul 9.
8.2 Server TCP iterativ
Modelul general al unui server TCP iterativ respe t ordinea urm toarelor
apeluri de sistem:
• so ket() reeaz un so ket are va trata onexiunile u lienµii;
• preg tirea stru turilor de date ( onµinute în so kaddr_in) pentru a ata³a
so ket-ul la portul folosit de apli aµie;
• bind() ata³eaz so ket-ul la port;
Modelul lient/server � TCP 133
• listen() preg te³te so ket-ul pentru as ultarea portului în vederea sta-
bilirii onexiunii u lienµii;
• a ept() a³teapt realizarea unei onexiuni u un anumit lient (a est
apel blo heaz programul pân la apariµia unei ereri de one tare din
partea unui lient);
• pro esarea ererilor lientului � s himb de mesaje între server ³i lient
folosindu-se des riptorul de so ket returnat de a ept(), prin apelul
primitivelor read() ³i write();
• lose() în hiderea onexiunii u lientul la terminarea dialogului u
a esta;
• shutdown() în hiderea dire µional a onexiunii u lientul.
S hema general a programelor server ³i lient TCP poate � urm rit în
�gura 8.1.
Figura 8.1: Modelul general al serverului ³i lientului TCP
Conexiunea lient-server, odat stabilit , r mâne valid pân ând, �e
lientul, �e serverul o întrerup în mod expli it sau nu. So ket-ul utilizat pentru
134 Atelier de programare în reµele de al ulatoare
a east onexiune se nume³te so ket orientat pe onexiune. Desigur, un so ket
poate � folosit la un moment dat pentru mai multe onexiuni.
8.2.1 Primitiva bind()
A³a um am v zut în apitolul pre edent, apelul so ket() va returna un des-
riptor de so ket. Pentru a � efe tiv folosit, va trebui a a est so ket s �e
ata³at la portul ma³inii la are va as ulta serverul ereri de onexiune din
partea posibililor lienµi. Vom realiza a est lu ru prin intermediul primitivei
bind() al rei prototip este urm torul:
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int bind(int so kd, stru t so kaddr *addr, so klen_t addrlen);
Stru tura so kaddr este una generi (o putem privi a o las abstra t )
menit a sto a informaµii de adres pentru ori are tip de so ket-uri. Ea este
de�nit astfel:
stru t so kaddr {
unsigned short sa_family; /* familia de adrese
(AF_UNIX, AF_INET,...) */
har sa_data[14℄; /* 14 bytes - adresa folosita */
}
Pentru Internet, ne vom folosi de o stru tur parti ular (asemeni unei
lase derivate din lasa abstra t ): so kaddr_in are are membrii:
stru t so kaddr_in {
short int sin_family; /* familia de adrese
(AF_INET) */
unsigned short int sin_port; /* portul
(0-65355) */
stru t in_addr sin_addr; /* adresa Internet */
unsigned har sin_zero[8℄; /* bytes neutilizati
(zero) */
}
Înainte de utilizarea a estei stru turi, trebuie s ne asigur m sin_zero
este nul, prin folosirea uneia dintre fun µiile bzero() sau memset().
Adresa Internet este sto at de stru tura in_addr are are de�niµia:
stru t in_addr {
unsigned long int s_addr; /* 4 bytes ai adresei IP */
}
Modelul lient/server � TCP 135
Serverul va putea folosi onstanta INADDR_ANY pentru a se utiliza adresa
IP a ma³inii pe are ruleaz pro esul (a easta asigur independenµa odului
de adresa IP a al ulatorului pe are se va exe uta serverul). Da în lo de
un num r de port valid vom utiliza valoarea 0, atun i bind() va alege un port
liber pentru a-l aso ia so ket-ului în auz .
Pentru a oferi servi ii nestandard, va trebui s stabilim a valoare pentru
port un num r mai mare de 1024. Valorile de la 1 la 1023 sunt rezervate ser-
vi iilor sistem standard (puteµi onsulta �³ierul /et /servi es pentru a vedea
pere hile port�servi iu standard furnizate de tre sistem; de exemplu: portul
23 este asignat servi iului de one tare la distanµ prin telnet, portul 25 este
folosit de apli aµiile implementând proto olul SMTP (Simple Mail Transfer
Proto ol), iar portul 80 se utilizeaz de tre lienµii ³i serverele World-Wide
Web, prin intermediul proto olului HTTP (HyperText Transfer Proto ol).
În parti ular, ori e pro es are dore³te s stabileas o onexiune u o
ma³in gazd pentru a transfera un �³ier, utilizând proto olul FTP, se poate
one ta la portul 21 al ma³inii destinaµie pentru a onta ta serverul (dae-
monul ) FTP.
8.2.2 Primitiva listen()
Dup ata³area portului, serverul va trebui s a³tepte viitoare onexiuni de
la diver³i lienµi ³i s le rezolve ererile. Pentru a easta vom utiliza apelul
listen() urmat apoi de a ept().
Prototipul primitivei listen() este:
#in lude <sys/so ket.h>
int listen(int so kd, int ba klog);
Al doilea parametru va stabili num rul de onexiuni permise în oada de
a³teptare a onexiunilor u lienµii. Uzual, valoarea sa este 5.
8.2.3 Primitiva a ept()
A eptarea propriu-zis a onexiunilor se va realiza u a ept():
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int a ept(int so kd, stru t so kaddr *addr, so klen_t *addrlen);
A est apel va returna un des riptor de so ket orespunz tor lientului a
rui onexiune a fost a eptat , stabilindu-se astfel un anal de omuni aµie
136 Atelier de programare în reµele de al ulatoare
duplex între server ³i lient. Noul des riptor va putea � folosit pentru a trimite
³i a re epµiona date via reµea prin mijlo itori pre um send() sau write() ³i
re v() sau read(), respe tiv.
Argumentul addr va onµine informaµii despre adresa IP ³i portul folosite
de lientul one tat la server, iar lungimea a estei stru turi va � sto at de
ultimul argument addrlen.
8.2.4 Primitivele send() ³i re v()
Primitivele send() ³i re v() au prototipurile de mai jos:
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int send(int so kd, har *buf, int len, int flags)
int re v(int so kd, har * buf, int len, int flags)
În ambele azuri, so kd reprezint des riptorul de so ket.
Pentru send(), argumentul buf indi o zon de memorie are onµine
datele e trebuie trimise, len este lungimea datelor ³i argumentul flags va �
de obi ei 0. Valoarea returnat este num rul de o teµi trimi³i în az de su es.
Da e³ueaz , este returnat −1, iar errno des rie eroarea.
Pentru apelul re v(), argumentul buf indi o zon de memorie în are se
vor opia datele re epµionate, len semni� m rimea a estor date în o teµi ³i
flags este de obi ei 0 sau setat la valoarea MSG_PEEK da datele re epµionate
trebuie reµinute ³i dup e sunt re epµionate. Valoarea returnat este num rul
de o teµi re epµionaµi în az de su es. Da primitiva e³ueaz , este returnat
valoarea −1.
8.2.5 Primitivele lose() ³i shutdown()
Dup realizarea dialogului u lientul, în hiderea onexiunii se va putea fa e
prin apelul primitivei lose(). Ori e în er are de a iti sau s rie date folosind
un so ket în his va genera eroare.
Pentru a ontrola modul de în hidere al so ket-ului, vom putea folosi pri-
mitiva shutdown():
#in lude <sys/so ket.h>
int shutdown(int so kd, int how);
Modelul lient/server � TCP 137
Argumentul how va avea una dintre valorile:
0 viitoarele itiri de pe so ket nu vor mai � permise;
1 viitoarele s rieri pe so ket nu vor mai � permise;
2 itirile/s rierile nu vor mai � permise (similar u lose()).
Fie are dintre apelurile prezentate mai sus va returna valoarea −1 da a
survenit o eroare (variabila errno va putea � folosit pentru a a�a mai multe
am nunte despre eroarea respe tiv ).
Rezumând, ordinea apelurilor la un server TCP iterativ va �:
• so ket()
• bind()
• listen()
• a ept()
• read(), write() . . .
• shutdown()
• lose()
8.3 Client TCP
În azul unui lient TCP, vom folosi în lo ul apelului a ept() primitiva dual
onne t() are are forma urm toare:
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int onne t(int so kd, stru t so kaddr *addr, so klen_t addrlen);
Stru tura addr va trebui s onµin adresa IP ³i portul serverului la are
se va one ta lientul.
Apelul listen() nu va mai ap rea, iar �e rui read() din server îi va
orespunde un apel write() la lient. La fel, �e rui apel write() în server îi
va orespunde un read() la lient.
138 Atelier de programare în reµele de al ulatoare
Astfel, ordinea apelurilor în adrul unui lient TCP va �:
• so ket()
• bind()
• onne t()
• write(), read() . . .
• shutdown()
• lose()
8.4 Conversia datelor
Comuni area dintre server ³i lient se realizeaz în adrul unor reµele eterogene,
u on�guraµii hardware pe are programatorul nu le poate prevedea. Pentru
a se asigura independenµa transferului de date, s-a onvenit a o teµii pe reµea
s respe te o anumit ordine de odi� are. Ordinea o teµilor dintr-un uvânt
(word � 2 o teµi) se poate realiza în dou moduri:
big endian el mai semni� ativ o tet este primul ( odi� are folosit de pro-
esoarele Motorola, de exemplu);
little endian el mai semni� ativ o tet este al doilea ( odi� are utilizat de
pro esoarele Intel).
Astfel, pentru a programele s �e independente de odi� area aleas , vom
re urge la folosirea unor fun µii de onversie de la odi� area de reµea la ea a
al ulatorului gazd . A este fun µii (ale ror prototipuri se reg ses în �³ierul
antet netinet/in.h) sunt urm toarele:
• htons() onversie a unui întreg s urt (2 o teµi) de la gazd la reµea
host to network short ;
• htonl() onversie a unui întreg lung (4 o teµi) de la gazd la reµea
host to network long ;
• ntohs() onversie a unui întreg s urt (2 o teµi) de la reµea la gazd
network to host short ;
• ntohl() onversie a unui întreg lung (4 o teµi) de la reµea la gazd
network to host long.
Modelul lient/server � TCP 139
De notat membrii sin_port ³i sin_addr ai stru turii so kaddr_in
trebuie s �e sto aµi în odi� area de reµea.
Pentru a asigura onversia adreselor IP de la formatul intern (patru bytes)
la el uzual (patru o teµi desp rµiµi de pun t) sau invers vom folosi fun µiile:
• inet_addr() realizeaz onversia de la un ³ir de ara tere onµinând o
adres IP la întreg; da se furnizeaz o adres IP invalid va � returnat
onstanta INADDR_NONE � a east fun µie se onsider a � demodat ,
�ind substituit de fun µia inet_aton(), dar poate � utilizat pentru
ompatibilitate u ve hile apli aµii;
• inet_aton() realizeaz onversia de la un ³ir de ara tere onµinând
o adres IP la întreg; în azul unei adrese IP in ore te, se va returna
valoarea zero;
• inet_ntoa() este duala fun µiei de mai sus, onvertind adresa IP dat
a întreg în ³ir de ara tere.
8.5 Alte primitive utile
În adrul apli aµiilor pot � utilizate ³i alte primitive utile pre um:
getso kname() � va returna numele urent al unui so ket dat (lo al) ³i are
prototipul de mai jos:
#in lude <sys/so ket.h>
int getso kname(int so kfd,
stru t so kaddr *name,
so klen_t *namelen);
getpeername() � furnizeaz informaµii despre el lalt ap t al onexiunii rea-
lizate prin intermediul unui so ket (la distanµ ).
A est apel are urm toarea form :
#in lude <sys/so ket.h>
int getpeername(int so kfd,
stru t so kaddr *name,
so klen_t *namelen);
140 Atelier de programare în reµele de al ulatoare
8.6 Exemplu
Furniz m un exemplu de server TCP iterativ, însoµit de lientul lui. Serverul
prime³te un ³ir de ara tere de la lient ³i îl trimite înapoi (în e ou). Clientul
ite³te un ³ir de ara tere de la intrarea standard, îl trimite serverului, apoi
a³teapt a serverul s i-l returneze.
Sursa serverului (sto at în �³ierul server-t p. ) este urm toarea:
server-t p.
/* Server TCP iterativ (e ho)
Asteapta un mesaj de la lienti;
mesajul primit este trimis inapoi
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <errno.h>
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <string.h>
#in lude <stdlib.h>
/* portul folosit */
#define PORT 8081
/* odul de eroare returnat de anumite apeluri */
extern int errno;
/* programul */
int
main ()
{
/* stru turile folosite de server si lient */
stru t so kaddr_in server;
stru t so kaddr_in from;
har buffer[100℄; /* mesajul trimis de lient */
int sd; /* des riptorul de so ket */
/* ream un so ket */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
Modelul lient/server � TCP 141
return errno;
}
/* pregatim stru turile de date */
bzero (&server, sizeof (server));
bzero (&from, sizeof (from));
/* umplem stru tura folosita de server */
server.sin_family = AF_INET;
/* stabilirea familiei de so ket-uri */
server.sin_addr.s_addr = htonl (INADDR_ANY);
/* a eptam ori e adresa */
server.sin_port = htons (PORT);
/* utilizam un port utilizator */
/* atasam so ketul */
if (bind (sd, (stru t so kaddr *) &server,
sizeof (stru t so kaddr)) == -1)
{
perror ("Eroare la bind().\n");
return errno;
}
/* punem serverul sa as ulte
da a vin lienti sa se one teze */
if (listen (sd, 5) == -1)
{
perror ("Eroare la listen().\n");
return errno;
}
/* servim in mod iterativ lientii... */
while (1)
{
int lient;
int length = sizeof (from);
printf ("Asteptam la portul %d...\n", PORT);
fflush (stdout);
/* a eptam un lient
(ne vom blo a pina la realizarea onexiunii) */
lient = a ept (sd, (stru t so kaddr *) &from, &length);
/* eroare la a eptarea onexiunii de la un lient */
if ( lient < 0)
142 Atelier de programare în reµele de al ulatoare
{
perror ("Eroare la a ept().\n");
ontinue;
}
/* am realizat onexiunea, asteptam mesajul... */
bzero (buffer, 100);
printf ("Asteptam mesajul...\n");
fflush (stdout);
/* itirea mesajului */
if (read ( lient, buffer, 100) <= 0)
{
perror ("Eroare la read() de la lient.\n");
lose ( lient); /* in hidem onexiunea u lientul */
ontinue; /* ontinuam sa as ultam... */
}
printf ("Mesajul a fost re eptionat...\n"
"Trimitem mesajul inapoi...");
/* returnam mesajul lientului */
if (write ( lient, buffer, 100) <= 0)
{
perror ("Eroare la write() atre lient.\n");
ontinue; /* ontinuam sa as ultam */
}
else
printf (" trasmitere u su es.\n");
/* am terminat u a est lient, in hidem onexiunea */
lose ( lient);
} /* while */
} /* main */
Codul surs al lientului TCP este el de mai jos:
lient-t p.
/* Client TCP (e ho)
Trimite un mesaj unui server;
mesajul trimis este re eptionat de la server
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
Modelul lient/server � TCP 143
#in lude <netinet/in.h>
#in lude <errno.h>
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <stdlib.h>
#in lude <netdb.h>
#in lude <string.h>
/* odul de eroare returnat de anumite apeluri */
extern int errno;
/* portul de one tare la server*/
int port;
/* programul */
int
main (int arg , har *argv[℄)
{
/* des riptorul de so ket */
int sd;
/* stru tura folosita pentru one tare */
stru t so kaddr_in server;
/* mesajul trimis */
har buffer[100℄;
/* exista toate argumentele in linia de omanda? */
if (arg != 3)
{
printf ("Sintaxa: %s <adresa_server> <port>\n", argv[0℄);
return -1;
}
/* stabilim portul */
port = atoi (argv[2℄);
/* ream so ketul */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
return errno;
}
/* umplem stru tura folosita pentru
realizarea onexiunii u serverul */
server.sin_family = AF_INET;
/* familia so ket-ului */
144 Atelier de programare în reµele de al ulatoare
server.sin_addr.s_addr = inet_addr (argv[1℄);
/* adresa IP a serverului, in odifi area retea;
htonl() nu e ne esara, fiind a inet_addr() furnizeaza
rezultatul in odifi area retea */
server.sin_port = htons (port);
/* portul de one tare */
/* ne one tam la server */
if ( onne t (sd, (stru t so kaddr *) &server,
sizeof (stru t so kaddr)) == -1)
{
perror ("Eroare la onne t().\n");
return errno;
}
/* itirea mesajului si trimiterea atre server */
bzero (buffer, 100);
printf ("Introdu eti mesajul: ");
fflush (stdout);
read (0, buffer, 100);
if (write (sd, buffer, 100) <= 0)
{
perror ("Eroare la write() spre server.\n");
return errno;
}
/* itirea raspunsului dat de server
(ne blo am pina ind serverul raspunde) */
if (read (sd, buffer, 100) < 0)
{
perror ("Eroare la read() de la server.\n");
return errno;
}
/* afisam mesajul primit */
printf ("Mesajul primit este: `%s`.\n", buffer);
/* in hidem onexiunea, am terminat */
lose (sd);
}
Clientul va ne esita dou argumente date în linia de omand semni� ând
adresa IP a serverului ³i portul de one tare la server. Da ambele programe
ruleaz pe a eea³i ma³in , atun i vom putea introdu e:
(infoiasi):~$ ./server-t p
(infoiasi):~$ ./ lient-t p 127.0.0.1 8081
Modelul lient/server � TCP 145
Da se dore³te a � folosit un alt port, se va modi� a în server num rul
portului ( onstanta PORT) ³i se va re ompila programul.
Pentru a fa ilita ompilarea programelor, ne putem sluji de urm torul �³ier
utilizat de omanda make:
# fisier folosit pentru ompilarea
# serverului si lientului TCP iterativ
all:
g server-t p. -o server-t p
g lient-t p. -o lient-t p
lean:
rm -f *~ lient-t p server-t p
Comanda make este utilizat pentru a automatiza diverse sar ini realizate
la ompilarea unui program de mai mari dimensiuni. De³i uzual folosit în
azul ompil rii programelor C (programe surs , antete, �³iere obie t, bibliote i
utilizator et .), omanda make poate � util la rea tualizarea automat a altor
programe surs (e.g. �³iere L
A
T
E
X).
Pentru a exe uta make vom avea nevoie de un �³ier Makefile ( itit impli it
de make în lipsa altei opµiuni) are va des rie relaµiile dintre �³ierele utilizate la
generarea unei apli aµii ³i va stabili omenzile are vor � rulate pentru �e are
dintre a este �³iere.
Un �³ier Makefile va � format din ma ro-de�niµii pre edate de ara terul
TAB sau de �:� ³i din relaµii de dependenµ de forma ref : ref1ref2 · · · refN ,
unde ref poate � un �³ier surs sau obie t. De asemenea, pot � utilizate omen-
tarii pre edate de ara terul �#�. O ma ro-de�niµie este asem n toare operaµiei
de atribuire de variabile (de forma identif = valoare), iar valoarea unei va-
riabile de�nite în a est mod va putea � a esat pre�xând numele variabilei
u �$� ³i în adrându-l între paranteze, adi prin onstru µia $(identif).
Pentru a rea tualiza toate �³ierele dependente de o referinµ vom exe uta
make având a argument numele a elei referinµe. Pentru exemplul de mai sus,
rulând make lean vor � ³terse �³ierele temporare rezultate în urma edit rii ³i
ele exe utabile. Da nu se spe i� ni i un nume de dependenµ , se va în er a
satisfa erea dependenµei u numele all (în a est az, ompilarea serverului ³i
lientului).
Comanda make a ept o serie de opµiuni, dintre are poate � menµionat
opµiunea -f are permite pro esarea unui �³ier de reguli al rui nume este
diferit de Makefile. Pentru mai multe detalii, onsultaµi manualul.
146 Atelier de programare în reµele de al ulatoare
8.7 Exer iµii
1. Dup um se poate observa, serverul va servi iterativ, lient dup lient,
toµi lienµii posibili. Modi� aµi serverul astfel în ât lienµii s �e pro esaµi
în manier on urent , prin utilizarea apelului fork(). Atenµie la apariµia
pro eselor zombie!
2. S se s rie un server TCP on urent are s poat servi un num r maxim
de N lienµi simultan ³i are s primeas un ³ir de la lient ³i s returneze
lientului ³irul în ordinea invers a ara terelor.
3. S se s rie un server TCP on urent are a ept maxim i lienµi simul-
tan, a³teapt un num r N ³i returneaz �e rui lient lista numerelor
prime de la 1 pân la num rul N .
4. S rieµi un server TCP are a³teapt ze e ifre de la un lient ³i returneaz
a elui lient maximul dintre ifrele primite.
5. Fie un server TCP on urent are re epµioneaz de la �e are dintre
lienµii one taµi âte o linie de maxim 80 de ara tere. De la un lient
vor � re epµionate maximum 33 de linii, dup are serverul va trimite
lientului num rul de litere prezente în liniile re eptate ³i va în hide
onexiunea u a el lient. Serverul poate avea one taµi maximum trei
lienµi simultan.
8.8 Rezolv ri
Exer iµiul 3. Numere prime
Pentru rezolvarea servirii on urente a mai multor lienµi se va folosi fork(),
pro esul opil reat deservind un anumit lient. De reµinut faptul atun i
ând un pro es �u î³i termin exe uµia, el va emite un semnal SIGCHLD (vezi
³i apitolul 4) tre p rinte. Da p rintele nu va trata a est semnal, atun i
pro esul opil va r mâne zombie (sau <defun t>, dup um se observ u
ps x). Pentru evitarea a estei situaµii, atun i ând p rintele prime³te SIGCHLD
va exe uta primitiva de a³teptare wait() are are a efe t �ie³irea urat � a
opilului, adi se vor elibera toate resursele alo ate ³i pro esul se va termina
normal.
Atun i ând se s rie la lient (adi la so ket-ul aso iat onexiunii) ³i se în-
trerupe sau se în hide onexiunea, pro esul server va primi un semnal SIGPIPE
³i serverul se va termina în mod forµat (a easta este a µiunea impli it la
Modelul lient/server � TCP 147
apariµia semnalului SIGPIPE). Pentru a evita a east situaµie, se va ignora
semnalul SIGPIPE.
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <unistd.h>
#in lude <error.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude <signal.h>
/* portul folosit */
onst int PORT_SERVER = 9001;
/* numarul maxim de lienti a eptati */
onst int CLIENTI_MAXIM = 10;
extern int errno; /* eroarea returnata */
int ds; /* des riptor pentru server */
int d ; /* des riptor pentru lient */
int nr = 0; /* numarul de lienti */
void
semnal (int nr_semnal) /* fun tia de tratare a semnalelor */
{
if (nr_semnal == SIGCHLD)
{
wait (NULL);
nr--; /* am pierdut un lient */
return;
}
}
/* intoar e 0 da a nu e prim, 1 altfel */
int
e_prim (int i)
{
int k;
for (k = 2; k * k <= i; k++)
if ((i % k) == 0)
return 0;
return 1;
}
148 Atelier de programare în reµele de al ulatoare
void
lient () /* fun tia de tratare a lientului */
{
har buffer[100℄;
har aux[100℄;
int i, t;
int numar, k;
sprintf (aux, "Esti lientul numarul: %d\n", nr);
if (write (d , aux, strlen (aux)) != strlen (aux))
{
shutdown (d , 2); /* eroare, am iesit */
exit (errno);
}
sprintf (aux, "Dati numarul: ");
if (write (d , aux, strlen (aux)) != strlen (aux))
{
shutdown (d , 2);
exit (errno);
}
bzero (buffer, 100);
/* iteste numarul sub forma de sir de ara tere */
if (read (d , buffer, 100) == 0)
{
shutdown (d , 2);
exit (errno);
}
/* din sir de ara tere in intreg */
numar = atoi (buffer);
for (k = 2; k < numar; k++)
if (e_prim (k))
{
sprintf (aux, "Numar prim: %d\n", k);
if (write (d , aux, strlen (aux)) != strlen (aux))
{
shutdown (d , 2);
exit (errno);
}
}
shutdown (d , 2);
exit (errno);
}
Modelul lient/server � TCP 149
int
main () /* programul prin ipal */
{
stru t so kaddr_in server;
/* tratam semnalele */
if (signal (SIGCHLD, semnal) == SIG_ERR)
{
perror ("signal()");
exit (errno);
}
if (signal (SIGPIPE, SIG_IGN) == SIG_ERR)
{
perror ("signal()");
exit (errno);
}
/* ream so ket-ul */
if ((ds = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("so ket()");
return errno;
}
/* pregatim stru turile de date */
bzero (&server, sizeof (server));
server.sin_family = AF_INET;
server.sin_port = htons (PORT_SERVER);
server.sin_addr.s_addr = htonl (INADDR_ANY);
/* atasam la port */
if (bind (ds, &server, sizeof (server)) == -1)
{
perror ("bind()");
return errno;
}
if (listen (ds, 5) == -1)
{
perror ("listen()");
return errno;
}
printf ("Asteptam lienti la portul %d...\n", PORT_SERVER);
while (1)
{
/* a eptam un lient */
d = a ept (ds, NULL, NULL);
/* am ajuns la numarul maxim de lienti? */
if (nr == CLIENTI_MAXIM)
150 Atelier de programare în reµele de al ulatoare
{
shutdown (d , 2);
ontinue;
}
/* lansam un pro es are trateaza ererile lientului */
swit h (fork ())
{
ase 0:
lient ();
ase -1:
perror ("fork()");
break;
default:
break;
}
nr++; /* a mai venit un lient */
}
}
Capitolul 9
Modelul lient/server � UDP
În adrul a estui apitol se prezint maniera de on epere
a programelor server iterative ³i a programelor lient
utilizând proto olul de transport UDP (User Datagram
Proto ol).
9.1 Datagrame
A doua modalitate de transmisie a datelor în reµea este ea prin intermediul
datagramelor, folosindu-se proto olul de transport UDP.
S heletul general de server ³i lient UDP este similar elui de la azul TCP
(vezi apitolul 8). În �gura 9.1 poate � urm rit modelul general al serverului
³i lientului UDP.
În lo ul onstantei SOCK_STREAM utilizate la apelul so ket() vom folosi
SOCK_DGRAM. Ne�ind în prealabil stabilit onexiunea dintre server ³i lient,
apelurile listen(), a ept() ³i onne t() nu vor mai � utilizate.
Pentru datagrame, sunt puse la dispoziµia programatorului sendto() ³i
re vfrom(), primitive are pot � utilizate pentru a trimite ³i, respe tiv, a
re epµiona date de la un anumit lient.
Astfel, modelul general al unui server/ lient UDP iterativ respe t ordinea
urm toarelor apeluri de sistem:
• so ket() va rea un so ket are va trata onexiunile u lienµii;
• preg tirea stru turilor de date ( onµinute în so kaddr_in) pentru a ata³a
so ket-ul la portul folosit de apli aµie;
• bind() ata³eaz so ket-ul la port;
• pro esarea ererilor lientului � s himb de mesaje între server ³i lient,
prin intermediul primitivelor sendto() ³i re vfrom() ; se pot utiliza ³i
primitivele generale send() ³i re v();
• lose() în hiderea so ket-ului lient.
152 Atelier de programare în reµele de al ulatoare
Figura 9.1: Modelul general al serverului ³i lientului UDP
Apelul re vfrom() are sintaxa general :
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int re vfrom(int so kd, void *buf, size_t len, int flags,
stru t so kaddr *from, so klen_t *fromlen);
Apelul sendto() are urm toarea form :
#in lude <sys/types.h>
#in lude <sys/so ket.h>
int sendto(int so kd, onst void *msg, size_t len, int flags,
onst stru t so kaddr *to, so klen_t tolen);
9.2 Exemplu
Prezent m în ontinuare un exemplu de server UDP iterativ, însoµit de lientul
lui. Serverul prime³te un ³ir de ara tere de la lient ³i îl trimite înapoi (în
e ou). Serverul va rula automat în fundal. Clientul ite³te un ³ir de ara tere
de la intrarea standard, îl trimite serverului, apoi a³teapt a serverul s i-l
returneze.
Modelul lient/server � UDP 153
Sursa serverului (sto at în �³ierul server-udp. ) este urm toarea:
server-udp.
/* Server UDP iterativ (e ho)
Asteapta un mesaj de la lienti;
mesajul primit este trimis inapoi
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <stdio.h>
#in lude <netinet/in.h>
#in lude <errno.h>
#in lude <unistd.h>
/* portul folosit */
#define PORT 8081
/* odul de eroare returnat de anumite apeluri */
extern int errno;
/* programul */
int
main ()
{
/* stru turile folosite de server si lient */
stru t so kaddr_in adresa;
stru t so kaddr lient;
har buffer[100℄; /* mesajul trimis de lient */
int sd; /* des riptorul de so ket */
/* lansam serverul in fundal... */
swit h (fork ())
{
ase -1: /* eroare la fork() */
perror ("Fork error\n");
return errno;
ase 0: /* opilul traieste... */
break;
default: /* parintele moare... */
printf ("Serverul a fost lansat in fundal...\n");
exit (0);
}
154 Atelier de programare în reµele de al ulatoare
/* ream un so ket */
if ((sd = so ket (AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
return errno;
}
/* pregatim stru tura folosita de server */
adresa.sin_family = AF_INET;
/* stabilirea familiei de so ket-uri */
adresa.sin_addr.s_addr = htonl (INADDR_ANY);
/* a eptam ori e adresa */
adresa.sin_port = htons (PORT);
/* utilizam un port utilizator */
/* atasam so ketul */
if (bind (sd, (stru t so kaddr *) &adresa,
sizeof (stru t so kaddr)) == -1)
{
perror ("Eroare la bind().\n");
return errno;
}
/* servim in mod iterativ lientii... */
while (1)
{
int bytes;
int length = sizeof ( lient);
/* itim mesajul primit de la lient */
if ((bytes = re vfrom (sd, buffer, 100, 0,
& lient, &length)) < 0)
{
perror ("Eroare la re vfrom() de la lient.\n");
return errno;
}
/* ...dupa are il trimitem inapoi */
if (sendto (sd, buffer, bytes, 0, & lient, length) < 0)
{
perror ("Eroare la sendto() spre lient.\n");
return errno;
}
} /* while */
} /* main */
Modelul lient/server � UDP 155
Urmeaz odul lientului UDP:
lient-udp.
/* Client UDP (e ho)
Trimite un mesaj unui server;
mesajul trimis este re eptionat de la server
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <stdio.h>
#in lude <netinet/in.h>
#in lude <errno.h>
#in lude <netdb.h>
#in lude <string.h>
/* odul de eroare returnat de anumite apeluri */
extern int errno;
/* portul de one tare la server*/
int port;
/* programul */
int
main (int arg , har *argv[℄)
{
/* des riptorul de so ket */
int sd;
/* stru tura folosita pentru one tare */
stru t so kaddr_in server;
/* mesajul trimis */
har buffer[100℄;
int length;
/* exista toate argumentele in linia de omanda? */
if (arg != 3)
{
printf ("Sintaxa: %s <adresa_server> <port>\n", argv[0℄);
return -1;
}
/* stabilim portul */
port = atoi (argv[2℄);
156 Atelier de programare în reµele de al ulatoare
/* ream so ketul */
if ((sd = so ket (AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
return errno;
}
/* umplem stru tura folosita pentru
realizarea dialogului u serverul */
server.sin_family = AF_INET;
/* familia so ket-ului */
server.sin_addr.s_addr = inet_addr (argv[1℄);
/* adresa IP a serverului */
server.sin_port = htons (port);
/* portul de one tare */
/* itirea mesajului de la intrarea standard */
bzero (buffer, 100);
printf ("Introdu eti mesajul: ");
fflush (stdout);
read (0, buffer, 100);
length = sizeof (server);
/* trimiterea mesajului atre server */
if (sendto (sd, buffer, strlen (buffer), 0,
&server, length) < 0)
{
perror ("Eroare la sendto() spre server.\n");
return errno;
}
/* itirea raspunsului dat de server
(ne blo am pina ind serverul raspunde) */
if (re vfrom (sd, buffer, 100, 0, &server, &length) < 0)
{
perror ("Eroare la re vfrom() de la server.\n");
return errno;
}
printf ("Mesajul primit este: `%s`.\n", buffer);
/* in hidem so ketul, am terminat */
lose (sd);
return 0;
}
Modelul lient/server � UDP 157
Clientul va ne esita dou argumente date în linia de omand semni� ând
adresa IP a serverului ³i portul de one tare la serverul UDP. Da ambele
programe ruleaz pe a eea³i ma³in , atun i vom putea introdu e:
(infoiasi):~$ ./server-udp
(infoiasi):~$ ./ lient-udp 127.0.0.1 8081
Serverul va rula automat în fundal (adoptând postura de daemon) � vezi
³i apitolul 3.
9.3 Exer iµii
1. Modi� aµi serverul prezentat mai sus astfel în ât ³irul primit de la lient
s �e trimis oglindit înapoi.
2. S se s rie un server UDP are a³teapt ze e ifre de la un lient ³i
returneaz a elui lient suma ifrelor primite.
3. S se s rie un lient UDP are se one teaz la portul 13 al unei ma³ini
pentru a iti timpul urent ³i a-l a�³a la ie³irea standard.
9.4 Rezolv ri
Exer iµiul 3. Determinarea timpului urent
Programul are determin timpul urent one tându-se la portul 13 (daytime )
al unei ma³ini (server) este urm torul:
#in lude <stdio.h>
#in lude <string.h>
#in lude <sys/so ket.h>
#in lude <sys/types.h>
#in lude <sys/time.h>
#in lude <unistd.h>
#in lude <netinet/in.h>
#in lude <netdb.h>
#in lude "erori.h"
/* adresa IP a masinii la are ne one tam */
#define IP "193.231.30.197"
main ()
158 Atelier de programare în reµele de al ulatoare
{
int sd, lung;
stru t so kaddr_in adr_dest; /* adresa serverului 'daytime' */
har s[70℄, *st;
/* reare so ket */
if ((sd = so ket (AF_INET, SOCK_DGRAM, 0)) == -1)
{
eroare (); /* raportare erori */
exit (1);
}
lung = sizeof (adr_dest);
/* ompletare stru tura */
memset (&adr_dest, lung, 0);
adr_dest.sin_family = AF_INET;
adr_dest.sin_port = htons (13);
adr_dest.sin_addr.s_addr = inet_addr (IP);
/* initierea dialogului u serverul */
if (sendto (sd, s, 70, 0,
(stru t so kaddr *) &adr_dest, lung)
< 0)
{
eroare ();
exit (1);
}
/* re eptarea raspunsului */
if ((i = re vfrom (sd, s, 70, 0,
(stru t so kaddr *) &adr_dest, &lung) < 0)
{
eroare ();
exit (1);
}
/* de upam timpul urent */
st = strpbrk (s, " ") + 1;
st = strpbrk (st + 1, " ") + 1;
strtok (st, " ");
printf ("Timpul urent: %s\n", st);
/* am terminat */
lose (sd);
exit (0);
Modelul lient/server � UDP 159
}
Antetul erori.h de�ne³te fun µia eroare() de raportare a erorilor pe baza
valorilor returnate de variabila errno:
#in lude <errno.h>
void
eroare (void) /* fun tie de afisare a erorilor survenite */
{
swit h (errno)
{
ase EPROTONOSUPPORT:
perror ("Tipul proto olului folosit nu este suportat.");
break;
ase EMFILE:
perror ("Tabela de pro ese este plina.");
break;
ase ENFILE:
perror ("Tabela de des riptori este plina.");
break;
ase EACCES:
perror ("Nu se poate rea un so ket "
" u tipul si/sau proto olul dat.");
break;
ase ENOBUFS:
perror ("Nu este sufi ient spatiu liber in buffer.");
break;
ase EBADF:
perror ("Des riptorul folosit nu este ore t.");
break;
ase EINVAL:
perror ("Portul este deja legat la un so ket.");
break;
ase ENOTSOCK:
perror ("Argumentul dat este nu e un des riptor de so ket.");
break;
ase EFAULT:
perror ("Pointerul de adresa spe ifi at este invalid.");
break;
ase EMSGSIZE:
perror ("Lungimea spe ifi ata este mai mi a de at "
"lungimea mesajului are trebuie trimis.");
break;
ase EWOULDBLOCK:
perror ("So ketul este reat non-blo ant "
160 Atelier de programare în reµele de al ulatoare
"dar ererea este blo anta.");
break;
ase ENOTCONN:
perror ("So ketului ii este aso iata o onexiune orientata "
"dar nu s-a fa ut one tarea.");
break;
ase EINTR:
perror ("Primirea datelor a fost intrerupta de un semnal"
"inainte a transmiterea datelor sa fie terminata.");
break;
ase EISCONN:
perror ("So ketul este deja one tat.");
break;
ase ECONNREFUSED:
perror ("Conexiunea este refuzata de server.");
break;
ase ETIMEDOUT:
perror ("Timpul a ordat oneexiunii a expirat.");
break;
ase ENETUNREACH:
perror ("Nu ne putem one ta la adresa spe ifi ata.");
break;
ase EADDRINUSE:
perror ("Adresa este folosita deja.");
break;
default:
perror ("A survenit o alta eroare");
}
}
Capitolul 10
Multiplexarea intr rilor/ie³irilor
A est apitol trateaz maniera de multiplexare a intr rilor
³i ie³irilor, des riind primitiva sele t() ³i utiliz rile ei.
10.1 Primitiva sele t()
Pentru multiplexarea în manier sin ron a intr rilor ³i ie³irilor vom re urge
la folosirea primitivei sele t() a rei sintax general este urm toarea:
#in lude <sys/time.h>
#in lude <sys/types.h>
#in lude <unistd.h>
int sele t( /* valoarea maxima a des riptorilor plus 1 */
int nfds1,
/* multimea des riptorilor de itire */
fd_set *readfds,
/* multimea des riptorilor de s riere */
fd_set *writefds,
/* multimea des riptorilor de ex eptie */
fd_set *ex eptfds,
/* timpul de asteptare */
stru t timeval *timeout
);
Pentru manipularea elementelor mulµimilor de des riptori (tipul fd_set)
se pun la dispoziµie urm toarele ma ro-uri:
/* fa e multimea vida */
FD_ZERO (fd_set *set);
/* seteaza un bit ('fd') din multimea 'set') */
FD_SET (int fd, fd_set *set);
/* sterge un bit ('fd') din multimea 'set') */
FD_CLR (int fd, fd_set *set);
/* testeaza apartenenta lui 'fd' la multimea 'set' */
FD_ISSET (int fd, fd_set *set);
162 Atelier de programare în reµele de al ulatoare
Fie rui des riptor îi va orespunde un bit în reprezentarea mulµimii de
des riptori dat de tipul prede�nit fd_set.
Stru tura timeval (de�nit în antetul sys/time.h) are doi membri (întregi
pozitivi) semni� ând se undele (tv_se ) ³i mi ro-se undele (tv_use ). Da
se furnizeaz valoarea NULL, atun i sele t() va a³tepta la nesfâr³it.
Valoarea returnat de apelul sele t() va desemna num rul de des riptori
preg tiµi pentru o operaµiune de itire, s riere sau ex epµie. O valoare nul
va însemna faptul ni i unul dintre des riptori nu este gata, de i timpul de
a³teptare s-a s urs. În az de eroare, sele t() va returna valoarea −1.
În on luzie, primitiva sele t() permite unui a ela³i pro es s multiplexeze
operaµii blo ante de natur diferit peste mulµimile de des riptori pre izate. Ca
efe t parti ular, primitiva poate exprima a³teptare la nivel de mi rose unde.
10.2 Exemple
1. Putem utiliza apelul sele t() pentru itirea temporizat de la intrarea
standard. Se va a³tepta un nume de la utilizator (furnizat de la intrarea
standard) maxim 5 se unde ³i 33 de mi rose unde.
Sursa a estui program este ea de mai jos:
/*
Se asteapta un text de la intrarea standard
maxim 5 se unde si 33 de mi rose unde
*/
#in lude <sys/types.h>
#in lude <sys/time.h>
#in lude <unistd.h>
#in lude <stdio.h>
/* programul */
int
main ()
{
/* multimea de des riptori de itire */
fd_set readfds;
/* stru tura de timp */
stru t timeval tv;
/* numele itit de la intrarea standard */
har name[20℄;
/* valoarea returnata de sele t() */
int retval;
Multiplexarea intr rilor/ie³irilor 163
/* initial multimea este vida */
FD_ZERO (&readfds);
/* multimea va ontine un singur des riptor */
FD_SET (0, &readfds);
/* setam timpul de asteptare */
tv.tv_se = 5;
tv.tv_use = 33;
printf ("Introdu eti un nume (in maxim 5 se unde): ");
fflush (stdout);
if ((retval = sele t (1, &readfds, NULL, NULL, &tv)) < 0)
{
perror ("Eroare la sele t().\n");
return 1;
}
if (retval) /* este un singur des riptor gata de itire */
{
/* ar trebui sa fie adevarat */
if (FD_ISSET (0, &readfds))
{
fgets (name, 20, stdin);
/* eliminam ara terul NewLine din sir */
name[strlen (name) - 1℄ = '\0';
printf ("Salut, %s!\n", name);
}
else
printf ("In a nu suntem gata de itire...\n");
}
else /* sele t a returnat o valoare nula, timpul a expirat */
printf ("Timpul a expirat...\n");
} /* main */
2. Primitiva sele t() ne poate ajuta s realiz m o temporizare mai bun
de ât sleep(), atât la nivel de se unde, ât ³i la nivel de mi ro-se unde.
Pentru a easta, ne vom folosi numai de ultimul parametru al apelului
sele t():
#in lude <sys/types.h>
#in lude <sys/time.h>
#in lude <stdio.h>
/* programul */
164 Atelier de programare în reµele de al ulatoare
main (int arg , har *argv[℄)
{
stru t timeval timeout;
/* exista argumentele? */
if (arg != 3)
{
printf ("Sintaxa: %s <#se > <#mi rose >\n", argv[0℄);
return 1;
}
/* setam valorile */
timeout.tv_se = atol (argv[1℄);
timeout.tv_use = atol (argv[2℄);
/* apelam sele t() pentru temporizare */
if (sele t (0, NULL, NULL, NULL, &timeout) < 0)
{
perror ("Eroare la sele t().\n");
return 2;
}
return 0;
}
Observaµi mesajele de eroare raportate la exe uµia programului, intro-
du ând valori de timp invalide.
3. Apelul sele t() este folosit preponderent la realizarea de servere on-
urente, dup um vom vedea din exemplul de mai jos.
Serverul prime³te un ³ir de ara tere de la lient ³i îl trimite înapoi (în
e ou), în manier on urent . Clientul prime³te un ³ir de ara tere de la
tastatur , îl trimite serverului, apoi a³teapt a serverul s -l returneze.
Codul surs al serverului TCP on urent este urm torul:
server-t p.
/* Server TCP on urent (e ho)
Primeste si retrimite mesaje de la si la lienti multipli;
multiplexarea intrarilor se realizeaza u sele t()
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <sys/time.h>
#in lude <netinet/in.h>
#in lude <unistd.h>
Multiplexarea intr rilor/ie³irilor 165
#in lude <error.h>
#in lude <stdio.h>
#in lude <arpa/inet.h>
/* portul folosit */
#define PORT 8081
/* eroarea returnata de unele apeluri */
extern int errno;
/* fun tie de onvertire a adresei IP
a lientului in sir de ara tere */
har *
onv_addr (stru t so kaddr_in address)
{
stati har str[25℄;
har port[7℄;
/* adresa IP a lientului */
str py (str, inet_ntoa (address.sin_addr));
/* portul utilizat de lient */
bzero (port, 7);
sprintf (port, ":%d", ntohs (address.sin_port));
str at (str, port);
return (str);
}
/* programul */
int
main ()
{
/* stru turile pentru server si lienti */
stru t so kaddr_in server;
stru t so kaddr_in from;
/* multimea des riptorilor de itire */
fd_set readfds;
/* multimea des riptorilor a tivi */
fd_set a tfds;
/* stru tura de timp pentru sele t() */
stru t timeval tv;
/* des riptori de so ket */
int sd, lient;
/* des riptor folosit pentru
par urgerea listelor de des riptori */
int fd;
/* maximul valorilor des riptorilor */
166 Atelier de programare în reµele de al ulatoare
int nfds;
/* lungimea stru turii so kaddr_in */
int len;
/* reare so ket */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
return errno;
}
/* pregatim stru turile de date */
bzero (&server, sizeof (server));
/* umplem stru tura folosita de server */
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
/* atasam so ketul */
if (bind (sd, (stru t so kaddr *) &server,
sizeof (stru t so kaddr)) == -1)
{
perror ("Eroare la bind().\n");
return errno;
}
/* punem serverul sa as ulte
da a vin lienti sa se one teze */
if (listen (sd, 5) == -1)
{
perror ("Eroare la listen().\n");
return errno;
}
/* ompletam multimea de des riptori de itire */
/* initial, multimea este vida */
FD_ZERO (&a tfds);
/* in ludem in multime so ketul reat */
FD_SET (sd, &a tfds);
/* se va astepta un timp nedefinit */
tv.tv_se = 0;
tv.tv_use = 0;
/* valoarea maxima a des riptorilor folositi */
nfds = sd;
printf ("Asteptam la portul %d...\n", PORT);
fflush (stdout);
/* servim in mod on urent lientii... */
while (1)
{
/* ajustam multimea des riptorilor a tivi
Multiplexarea intr rilor/ie³irilor 167
(efe tiv utilizati) */
b opy (( har *) &a tfds,
( har *) &readfds, sizeof (readfds));
/* apelul sele t() */
if (sele t (nfds+1, &readfds, NULL, NULL, &tv) < 0)
{
perror ("Eroare la sele t().\n");
return errno;
}
/* vedem da a e pregatit so ketul
pentru a-i a epta pe lienti */
if (FD_ISSET (sd, &readfds))
{
/* pregatirea stru turii lient */
len = sizeof (from);
bzero (&from, sizeof (from));
/* a venit un lient, a eptam onexiunea */
lient = a ept (sd, (stru t so kaddr *) &from, &len);
/* eroare la a eptarea onexiunii de la un lient */
if ( lient < 0)
{
perror ("Eroare la a ept().\n");
ontinue;
}
/* ajusteaza valoarea maximului */
if (nfds < lient)
nfds = lient;
/* in ludem in lista de des riptori a tivi
si a est so ket */
FD_SET ( lient, &a tfds);
printf
("S-a one tat lientul u des riptorul %d, "
"de la adresa %s.\n", lient, onv_addr (from));
fflush (stdout);
}
/* vedem da a e pregatit vreun
so ket lient pentru a fa e e ho */
/* par urgem multimea de des riptori */
for (fd = 0; fd <= nfds; fd++)
{
/* este un so ket de itire pregatit? */
if (fd != sd && FD_ISSET (fd, &readfds))
{
/* n-a putut fi trimis mesajul */
if (e ho(fd) == 0)
168 Atelier de programare în reµele de al ulatoare
{
printf ("S-a de one tat"
" lientul u des riptorul %d.\n",
fd);
fflush (stdout);
/* in hidem onexiunea u lientul */
lose (fd);
/* s oatem si din multime */
FD_CLR (fd, &a tfds);
}
}
} /* for */
} /* while */
} /* main */
/* realizeaza primirea si retrimiterea
unui mesaj unui lient */
int
e ho (int fd)
{
/* mesajul */
har buffer[100℄;
/* numarul de o teti ititi/s risi */
int bytes;
bytes = read (fd, buffer, sizeof (buffer));
if (bytes < 0)
{
perror ("Eroare la read() de la lient.\n");
return 0;
}
if (bytes && write (fd, buffer, bytes) < 0)
{
perror ("Eroare la write() atre lient.\n");
return 0;
}
return bytes;
}
Folosim stru tura returnat de apelul a ept() pentru a a�³a informaµii
despre adresa IP ³i portul lienµilor one taµi la server.
Sursa lientului este similar u ea de la apitolul 8 ³i nu o vom mai
menµiona ai i.
Multiplexarea intr rilor/ie³irilor 169
10.3 Asin ronism
Am v zut în apitolele pre edente majoritatea apelurilor sunt blo ante,
e.g. a ept(), read() sau re vfrom().
Impli it, nu leul sistemului de operare la rearea unui so ket îl va seta a
blo ant. Pentru a-l fa e neblo ant, ne vom folosi de una dintre primitivele
f ntl() sau io tl().
Dup rearea so ket-ului îl vom seta a �ind neblo ant astfel:
#in lude <unistd.h>
#in lude <f ntl.h>
...
if ((so kfd = so ket (AF_INET, SOCK_STREAM, 0)) < 0)
{ /* eroare */ }
if (f ntl (so kfd, F_SETFL, O_NONBLOCK) < 0)
{ /* eroare */ }
...
Ca exemplu, vom res rie serverul ³i lientul vehi ulând mesaje în e ou,
folosind so ket neblo ant în adrul lientului. De³i serverul nu mai trimite
înapoi mesajul re epµionat de la lienµi, lienµii nu se vor blo a ( u toate va
� exe utat un apel read() dup trimiterea spre server a ³irului de ara tere
itit de la intrarea standard).
Sursa serverului este urm toarea (se va putea observa utilizarea apelu-
lui sele t() împreun u fork() pentru a asigura tratarea on urent a
ererilor):
/* Server TCP on urent (e ho) */
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <sys/time.h>
#in lude <netinet/in.h>
#in lude <unistd.h>
#in lude <error.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude <arpa/inet.h>
#in lude <signal.h>
#define MAX_CLIENTS 10 /* numar maxim de lienti */
#define FULL "Nu mai pot fi a eptati alti lienti...\n"
#define MESSAGE "Mesajul:"
#define MES_LEN strlen(MESSAGE)
170 Atelier de programare în reµele de al ulatoare
stru t so kaddr_in address;
extern int errno;
int port = 8081;
int sd, d, l;
har buffer[100℄;
int nr_ l = 0;
fd_set itire, s riere;
stru t timeval tv;
har *
ConvAddr ()
{
har *p;
har nr[10℄;
if ((p = ( har *) mallo (25)) == NULL)
return inet_ntoa (address.sin_addr);
p = inet_ntoa (address.sin_addr);
bzero (nr, 7);
sprintf (nr, ":%d", ntohs (address.sin_port));
str at (p, nr);
return p;
}
void
tratare_ lient ()
{
har s = 1;
printf ("[PID %d℄ A sosit un nou lient de la %s...\n",
getpid(), ConvAddr());
while (1)
{
FD_ZERO (&s riere);
FD_SET ( d, &s riere);
tv.tv_se = 0;
tv.tv_use = 0;
/* sele t u multimea des riptorilor de s riere */
if (!sele t ( d + 1, NULL, &s riere, NULL, &tv))
break;
/* se poate s rie, s riem efe tiv */
if (s == 1)
if (write ( d, MESSAGE, MES_LEN) == 0)
break;
FD_ZERO (& itire);
FD_SET ( d, & itire);
Multiplexarea intr rilor/ie³irilor 171
/* asteptam maxim 5 se unde... */
tv.tv_se = 5;
tv.tv_use = 0;
/* sele t u multimea des riptorilor de itire */
if (!sele t ( d + 1, & itire, NULL, NULL, &tv))
{
s = 0;
ontinue;
}
s = 1;
bzero (buffer, 100);
/* putem iti */
if (read ( d, buffer, 100) == 0)
break;
printf ("[PID %d℄ Clientul de la %s a trimis %s",
getpid (), ConvAddr (), buffer);
if (buffer[strlen (buffer) - 1℄ != '\n')
printf ("\n");
}
/* in hidem onexiunea u lientul */
shutdown ( d, 2);
lose ( d);
printf ("[PID %d℄ Clientul de la %s s-a de one tat\n",
getpid (), ConvAddr ());
exit (0);
}
int
main (int arg , har *argv[℄)
{
stru t so kaddr_in server;
int i, max;
if (arg == 2)
port = atoi (argv[1℄);
/* tratare semnale */
if (signal (SIGCHLD, SIG_IGN) == SIG_ERR)
{
perror ("signal()");
return errno;
}
/* reare so ket */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("so ket()");
return errno;
172 Atelier de programare în reµele de al ulatoare
}
/* ompletare stru tura */
server.sin_family = AF_INET;
server.sin_port = htons (port);
server.sin_addr.s_addr = htonl (INADDR_ANY);
/* atasare la port */
if (bind (sd, &server, sizeof (server)) == -1)
{
perror ("bind()");
return errno;
}
/* setare pentru as ultare */
if (listen (sd, 5) == -1)
{
perror ("listen()");
return errno;
}
printf ("Asteptam lienti la portul %d...\n", port);
while (1)
{
FD_ZERO (& itire);
FD_SET (sd, & itire);
tv.tv_se = 5;
tv.tv_use = 0;
if (sele t (sd + 1, & itire, NULL, NULL, &tv) == 0)
ontinue;
l = sizeof (address);
bzero (&address, sizeof (address));
/* a eptarea unui nou lient */
d = a ept (sd, (stru t so kaddr *) &address, &l);
/* lansarea unui alt pro es are sa trateze
ererile lientilor */
swit h (fork ())
{
ase -1:
shutdown ( d, 2);
lose ( d);
perror ("fork() - 2");
ontinue;
ase 0:
tratare_ lient ();
default:
break;
}
}
}
Multiplexarea intr rilor/ie³irilor 173
Codul surs al lientului este urm torul:
/* Client TCP (e ho)
Trimite in maniera neblo anta mesaje serverului
*/
#in lude <sys/types.h>
#in lude <sys/time.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <arpa/inet.h>
#in lude <errno.h>
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <netdb.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude <f ntl.h>
/* timpul de asteptare pentru realizarea one tarii */
#define TIMEOUT 30
extern int errno; /* eroarea returnata */
int sd; /* des riptor de so ket */
void
Eroare ( har *message) /* fun tie de afisare a erorilor */
{
if (errno == 0)
perror (message);
else
printf (message);
shutdown (sd, 2);
lose (sd);
exit (errno);
}
int
main (int arg , har *argv[℄)
{
int bytes;
stru t so kaddr_in server, lient;
stru t hostent *ip_addr;
har buffer[100℄;
fd_set itire, s riere;
stru t timeval tv;
174 Atelier de programare în reµele de al ulatoare
if (arg != 3)
{
printf ("Sintaxa: %s <server> <port>\n", argv[0℄);
return 1;
}
/* in er am sa gasim adresa IP */
if ((ip_addr = gethostbyname (argv[1℄)) == NULL)
Eroare ("Eroare la rezolvarea adresei");
/* ompletare stru turi pentru server si lient */
server.sin_family = AF_INET;
server.sin_port = htons (atoi (argv[2℄));
/* adresa IP o luam din stru tura returnata de gethostbyname() */
mem py (&server.sin_addr.s_addr,
ip_addr->h_addr, sizeof (ip_addr->h_addr));
lient.sin_family = AF_INET;
lient.sin_port = 0;
lient.sin_addr.s_addr = htonl (INADDR_ANY);
/* reare so ket */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
Eroare ("so ket()");
/* setare so ket a fiind neblo ant */
if (f ntl (sd, F_SETFL, O_NONBLOCK) != 0)
Eroare ("f ntl()");
/* atasare la port */
if (bind (sd, (stru t so kaddr *) & lient,
sizeof (stru t so kaddr)) == -1)
Eroare ("bind()");
/* one tare la server (nu se mai blo heaza) */
if ( onne t (sd, (stru t so kaddr *) &server, sizeof (server)) == -1)
swit h (errno) /* vedem e eroare primim... */
{
ase EINPROGRESS:
{ /* one tarea este in progres */
int value, len = sizeof (int);
FD_ZERO (&s riere);
FD_SET (sd, &s riere);
tv.tv_se = TIMEOUT;
tv.tv_use = 0;
/* setam un timp de asteptare a one tarii */
if (!sele t (sd + 1, NULL, &s riere, NULL, &tv))
Eroare ("timpul de one tare a expirat!");
/* vedem e eroare s-a returnat... */
Multiplexarea intr rilor/ie³irilor 175
if (getso kopt (sd, SOL_SOCKET, SO_ERROR, &value, &len)
== -1)
Eroare ("getso kopt()");
if (value != 0)
Eroare ("Eroare la one tare");
break;
}
default:
Eroare ("Eroare la one tare");
}
while (1)
{
FD_ZERO (& itire);
FD_SET (0, & itire);
FD_SET (sd, & itire);
tv.tv_se = 5;
tv.tv_use = 0;
/* sele t u des riptorii de s riere */
if (sele t (sd + 1, & itire, NULL, NULL, &tv) == 0)
ontinue;
bzero (buffer, 100);
/* este pregatita intrarea standard? */
if (FD_ISSET (0, & itire))
{
fgets (buffer, 100, stdin);
/* in er am sa s riem mesajul atre server */
bytes = write (sd, buffer, strlen (buffer));
if (bytes == 0)
Eroare ("Serverul a in his onexiunea");
if (bytes == -1)
{
if (errno == EAGAIN) /* nu a reusit sa s rie */
{
FD_ZERO (&s riere);
FD_SET (sd, &s riere);
tv.tv_se = TIMEOUT;
tv.tv_use = 0;
if (!sele t (sd + 1, NULL, &s riere, NULL, &tv))
Eroare ("Bufferul de s riere e plin...");
ontinue;
}
Eroare ("write()");
}
176 Atelier de programare în reµele de al ulatoare
}
/* este pregatit so ket-ul serverului? */
if (FD_ISSET (sd, & itire))
{
/* itim mesajul de la server */
bytes = read (sd, buffer, 100);
if (bytes == 0)
Eroare ("read()");
if (bytes == -1) /* posibila eroare? */
{
if (errno == EAGAIN) /* nu a reusit sa iteas a */
ontinue;
Eroare ("Eroare de itire de la server");
}
/* afisarea mesajului primit */
printf ("%s", buffer);
fflush (stdout);
}
}
/* in hiderea onexiunii si terminarea programului */
lose (sd);
return 0;
}
Primitiva getso kopt() a fost utilizat pentru a vedea e eroare a sur-
venit în azul unui apel neblo ant. Cu ajutorul a estei primitive se pot a�a
³i alte informaµii uneori utile pentru programator. Un apel omplementar este
setso kopt() are poate � folosit pentru setarea unor parametri interni aso-
iaµi so ket-urilor.
O alt noutate are poate � observat în sursa lientului este a eea
utilizatorul, pentru a se one ta la server, nu va mai trebui s spe i� e adresa
IP a ma³inii pe are ruleaz a el server, i adresa simboli , în forma standard
dat de DNS (Domain Name Servi e).
A est lu ru se realizeaz prin intermediul fun µiei gethostbyname():
#in lude <netdb.h>
stru t hostent *gethostbyname( onst har *name);
Fun µia va returna un pointer la tipul hostent având de�niµia:
stru t hostent {
har *h_name; /* numele ofi ial al gazdei */
har **h_aliases; /* alias-uri ale gazdei */
int h_addrtype; /* tipul de adresa al gazdei (AF_INET) */
Multiplexarea intr rilor/ie³irilor 177
int h_length; /* lungimea adresei */
har **h_addr_list; /* lista de adrese IP */
}
/* prima adresa IP orespunzatoare gazdei */
#define h_addr h_addr_list[0℄
În az de eroare, se va returna NULL, iar variabila h_errno va onµine odul
numeri al erorii (errno nu se poate utiliza). Pentru a vedea des rierea erorii
survenite, se poate folosi fun µia herror() de�nit tot în �³ierul antet netdb.h.
O fun µie înrudit u gethostbyname() este fun µia gethostbyaddr().
10.4 Exer iµii
Simulaµi situaµiile de mai jos, implementând âte un lient/server TCP on-
urent:
1. Fie un server TCP on urent la are se pot one ta simultan maxim
3 lienµi. Serverul prime³te mesaje, din 3 în 3 minute, de la m ar doi
dintre lienµi. Da mesajele primite de la ei doi lienµi nu oin id,
atun i serverul va trimite elor trei lienµi mesajul �stop�, dup are de-
one teaz toµi lienµii. Da mesajele oin id, serverul va a³tepta urm -
toarele mesaje de la alµi doi lienµi ai s i.
2. Dr. Jones era într-o expediµie în jungla amazonian . Angajase 7 l uze
autohtone, dar nu prea avea în redere în ele. De la �e are alauz tre-
buia s primeas , din 5 în 5 minute, âte un mesaj. Da mesajul era
�peri ol� însemna triburile potrivni e expediµiei se preg teau de ata .
În a east situaµie, Dr. Jones trebuia s trimit �e rei l uze ordinul
de a se as unde în o otieri. Da mesajul era �lini³te�, atun i totul
era (aparent) în ordine. Pentru a se asigura de exa titatea mesajelor
l uzelor sale, Dr. Jones trimitea mesajul de as undere numai da m -
ar 5 dintre l uze îi indi au �peri ol�, altfel le ignora mesajele.
3. C pitanul Pi ard ple ase într-o misiune de er etare pe o planet ne-
unos ut , ondu ând o mini-e hip de in i spe iali³ti. Fie are membru
al e hipei avea ordinul a la �e are minut s trimit un mesaj tre nava
Enterprise pentru a raporta datele ulese. E hipajul navei avea misiu-
nea de a prelu ra a este mesaje ³i de a le expedia pitanului Pi ard
pentru a a esta s aib o privire de ansamblu asupra întregii expediµii.
Unele mesaje puteau � identi e ³i atun i ele erau transmise pitanului
o singur dat .
178 Atelier de programare în reµele de al ulatoare
4. Un pulsar este un orp eres are emite semnale la momente periodi e
de timp. Not m s-pulsar un pulsar are emite regulat din s în s se unde.
Un astronom de pe Terra a des operit in i pulsari: 1-pulsar, 2-pulsar,3-pulsar, 3-pulsar, 5-pulsar. La �e are semnal luminos emis de un anumit
pulsar, astronomul trimite a elui pulsar un semnal radio. Da prime³te
semnale simultane de la mai mult de trei pulsari, atun i astronomul va
s rie într-un registru apariµia a estui eveniment ³i nu va mai trimite ni i
un semnal radio tre a ei pulsari.
10.5 Rezolv ri
Exer iµiul 3. C pitanul Pi ard
Pentru rezolvarea problemei u Enterprise ³i pitanul Pi ard, vom onsidera
drept server nava, iar drept lienµi membrii mini-e hipei de er etare. Jurnalul
pe are se s riu mesajele trimise de membrii e hipei va � un ve tor de ³iruri
are vor � listate la ie³irea standard.
Serverul este urm torul:
/* Server TCP on urent (Pi ard)
Nava Entreprise (serverul) primeste din 2 in 2 se unde
mesaje de la membrii ( lienti) e hipajului;
pe ele uni e le transmite apitanului Pi ard.
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <sys/time.h>
#in lude <netinet/in.h>
#in lude <unistd.h>
#in lude <error.h>
#in lude <stdio.h>
#in lude <arpa/inet.h>
/* portul folosit */
#define PORT 8111
/* alte onstante */
#define DEBUG 1
#define NR_MESAJE_JURNAL 10
#define NR_MEMBRI 5
/* eroarea returnata de unele apeluri */
extern int errno;
Multiplexarea intr rilor/ie³irilor 179
/* jurnalul apitanului Pi ard */
har *pi ard[NR_MESAJE_JURNAL℄;
/* mesajele primite */
har *mesaje[NR_MEMBRI℄;
int ount, nr_mesaj;
void
omplet_str () /* initializeaza sirurile de mesaje */
{
int i;
for (i = 0; i < NR_MESAJE_JURNAL; i++)
{
pi ard[i℄ = ( har *) mallo (30 * sizeof ( har));
pi ard[i℄[0℄ = '\0';
}
for (i = 0; i < NR_MEMBRI; i++)
{
mesaje[i℄ = ( har *) mallo (30 * sizeof ( har));
}
}
int
add ( har *msg) /* fun tie de memorare a elor 5 mesaje
primite la un moment dat */
{
har *aux;
int i;
aux = ( har *) mallo (30 * sizeof ( har));
str py (aux, msg);
for (i = 0; i < NR_MESAJE_JURNAL; i++)
{
if (!str mp (pi ard[i℄, ""))
{
str py (pi ard[i℄, msg);
return i;
}
if (!str mp (pi ard[i℄, aux))
return i;
}
/* da a s-a ompletat jurnalul, anuntam a nu mai in ap mesaje */
return NR_MESAJE_JURNAL;
}
180 Atelier de programare în reµele de al ulatoare
/* programul */
int
main ()
{
/* stru turile pentru server si lienti */
stru t so kaddr_in server;
stru t so kaddr_in from;
/* multimea des riptorilor de itire */
fd_set readfds;
/* multimea des riptorilor a tivi */
fd_set a tfds;
/* stru tura de timp pentru sele t() */
stru t timeval tv;
/* des riptori de so ket */
int sd, lient, i;
int fd;
har mybuffer[100℄;
/* numarul maxim de des riptori */
int nfds;
/* lungimea stru turii so kaddr_in */
int len;
int ontor;
/* initializam nr. de mesaje */
ount = -1;
/* reare so ket */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
return errno;
}
/* pregatim stru turile de date */
bzero (&server, sizeof (server));
/* umplem stru tura folosita de server */
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
/* atasam so ketul */
if (bind (sd, (stru t so kaddr *) &server,
sizeof (stru t so kaddr)) == -1)
{
perror ("Eroare la bind().\n");
return errno;
}
/* punem serverul sa as ulte da a vin lienti sa se one teze */
if (listen (sd, 5) == -1)
Multiplexarea intr rilor/ie³irilor 181
{
perror ("Eroare la listen().\n");
return errno;
}
/* ompletam multimea de des riptori de itire */
FD_ZERO (&a tfds);
FD_SET (sd, &a tfds);
tv.tv_se = 0;
tv.tv_use = 0;
/* valoarea maxima a des riptorilor folositi */
nfds = sd;
printf ("Asteptam la portul %d...\n", PORT);
fflush (stdout);
/* initializam jurnalul pi ard si mesajele */
omplet_str ();
ontor = 0;
/* servim in mod on urent lientii... */
while (1)
{
/* ajustam multimea des riptorilor a tivi
(efe tiv utilizati) */
b opy (( har *) &a tfds, ( har *) &readfds, sizeof (readfds));
/* apelul sele t() */
if (sele t (nfds + 1, &readfds, NULL, NULL, &tv) < 0)
{
perror ("Eroare la sele t().\n");
return errno;
}
if (FD_ISSET (sd, &readfds))
{
/* pregatirea stru turii lient */
len = sizeof (from);
bzero (&from, sizeof (from));
/* a venit un lient, a eptam onexiunea */
lient = a ept (sd, (stru t so kaddr *) &from, &len);
/* eroare la a eptarea onexiunii de la un lient */
if ( lient < 0)
{
perror ("Eroare la a ept().\n");
ontinue;
}
if (nfds < lient) /* ajusteaza valoarea maximului */
nfds = lient;
FD_SET ( lient, &a tfds);
printf ("S-a one tat lientul u des riptorul %d\n",
lient);
182 Atelier de programare în reµele de al ulatoare
fflush (stdout);
}
/* par urgem multimea de des riptori */
for (fd = 0; fd <= nfds; fd++)
{
/* este un so ket de itire pregatit? */
if (fd != sd && FD_ISSET (fd, &readfds))
{
swit h (exe _ lient (fd))
{
ase 0: /* eroare de itire */
ase 1: /* terminare transmisiune */
lose (fd); /* in hidem onexiunea u lientul */
FD_CLR (fd, &a tfds); /* s oatem si din multime */
printf
("\nS-a de one tat lientul u des riptorul %d."
"In a est moment, in jurnal am:\n", fd);
fflush (stdout);
for ( ontor = 0;
ontor < NR_MESAJE_JURNAL; ontor++)
{
printf ("%s ", pi ard[ ontor℄);
fflush (stdout);
}
printf ("\n");
fflush (stdout);
break;
ase 2:
printf ("Jurnal Pi ard e plin. Continut:\n");
fflush (stdout);
for ( ontor = 0;
ontor < NR_MESAJE_JURNAL; ontor++)
{
printf ("%s ", pi ard[ ontor℄);
fflush (stdout);
}
printf ("\nAsteptati sa-mi iau alt jurnal..:)\n");
fflush (stdout);
omplet_str ();
if ( ount == 4)
ount = -1;
sleep (1);
} /*swit h */
}
} /* for */
Multiplexarea intr rilor/ie³irilor 183
} /* while */
} /* main */
/* fun tie de preluare a mesajelor de la lienti */
int
exe _ lient (int fd)
{
har buffer[100℄; /* mesajul */
int bytes, i, first; /* numarul de o teti ititi */
har *token;
int messageExist = 0;
bzero (buffer, 100);
bytes = read (fd, buffer, sizeof (buffer));
if (bytes < 0)
{
perror ("Eroare la read() de la lient.\n");
return 0;
}
if (DEBUG)
{
printf ("Am itit de la lient: %s\n", buffer);
fflush (stdout);
}
if (!str mp (buffer, "exit"))
return 1; /* lientul s-a pli tisit.. */
ount++; /* un nou mesaj */
printf (" ount: %d\n", ount);
fflush (stdout);
for (i = 0; i < ount; i++)
{
if (!str mp (mesaje[i℄, buffer))
{ /* da a mesajul exista,
punem pe pozitia urmatoare '\0' */
mesaje[i + 1℄[0℄ = '\0';
messageExist = 1;
break;
}
}
/* mesajul nu exista, il punem in mesaje; */
if (!messageExist)
str py (mesaje[ ount℄, buffer);
/* avem deja 5 mesaje, le punem pe ele valide in jurnal,
adi a in tabloul pi ard[℄ */
if ( ount == NR_MEMBRI - 1)
184 Atelier de programare în reµele de al ulatoare
{
printf ("s-au adunat 5 mesaje; din ele, adaugam:\n");
fflush (stdout);
for (i = 0; i <= ount; i++)
if (str mp (mesaje[i℄, ""))
{ /*da a nu e nul, il adaugam */
printf ("%s ", mesaje[i℄);
fflush (stdout);
if (add (mesaje[i℄) == NR_MESAJE_JURNAL)
{
printf ("\nNu mai primim mesaje...\n");
fflush (stdout);
return 2;
}
}
ount = -1;
}
printf ("\n");
fflush (stdout);
return bytes;
}
Urmeaz în ontinuare listing-ul lientului TCP are va simula membrii
mini-e hipei de er etare:
/* Client TCP (membru al e hipajului)
Trimite un mesaj navei Entreprise;
mesajul trimis este re eptionat de server
*/
#in lude <sys/types.h>
#in lude <sys/so ket.h>
#in lude <netinet/in.h>
#in lude <errno.h>
#in lude <unistd.h>
#in lude <stdio.h>
#in lude <stdlib.h>
#in lude <netdb.h>
#in lude <string.h>
/* lungimea unui mesaj trimis */
#define MESSAGE_LENGTH 2
/* nr. maxim de mesaje trimise, dupa are genereaza "exit" */
#define MESSAGE_COUNT 30
/* fun tie de generare de mesaje aleatoare */
Multiplexarea intr rilor/ie³irilor 185
void generateMessage ( har *);
/* odul de eroare returnat de anumite apeluri */
extern int errno;
/* portul de one tare la server*/
int port;
/* numara ate mesaje s-au trimis*/
int ount = 0;
/* programul */
int
main (int arg , har *argv[℄)
{
int sd;
stru t so kaddr_in server;
har buffer[100℄;
/* exista toate argumentele in linia de omanda? */
if (arg != 3)
{
fprintf (stderr, "Sintaxa: %s <adresa_server> <port>\n",
argv[0℄);
exit (2);
}
/* stabilim portul */
port = atoi (argv[2℄);
/* ream so ketul */
if ((sd = so ket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("Eroare la so ket().\n");
exit (1);
}
/* umplem stru tura folosita pentru realizarea onexiunii */
server.sin_family = AF_INET;
/* familia so ket-ului */
server.sin_addr.s_addr = inet_addr (argv[1℄);
/* adresa IP a serverului */
server.sin_port = htons (port);
/* portul de one tare */
/* ne one tam la server */
if ( onne t (sd, (stru t so kaddr *) &server,
sizeof (stru t so kaddr)) == -1)
186 Atelier de programare în reµele de al ulatoare
{
perror ("Eroare la onne t().\n");
exit (1);
}
/* trimiterea mesajului atre server */
bzero (buffer, 100);
while (str mp (buffer, "exit"))
{
generateMessage (buffer);
printf ("Am generat: %s\n", buffer);
fflush (stdout);
if (write (sd, buffer, 100) < 0)
{
perror ("Eroare la write() spre server.\n");
exit (1);
}
sleep (2); /* doarme 2 se unde */
}
/* in hidem onexiunea, am terminat */
lose (sd);
}
/* genereaza un mesaj aleatoriu */
void
generateMessage ( har *message)
{
int i;
ount++;
if ( ount == MESSAGE_COUNT)
{
str py (message, "exit");
return;
}
for (i = 0; i < MESSAGE_LENGTH; i++)
/* generam ara tere de la 'a' la 'z' */
message[i℄ = ( har) ('a' + random () % 25);
message[MESSAGE_LENGTH - 1℄ = '\0';
}
Capitolul 11
RPC - Apelul pro edurilor
la distanµ
Capitolul de faµ prezint sistemul RPC are permite
apelul pro edurilor la distanµ . Este prezentat pe s urt
sistemul RPC ³i se exempli� utilizarea lui printr-o apli-
aµie oferind informaµii despre mersul trenurilor.
11.1 Sistemul RPC
S opul a estui apitol este de a ar ta um se utilizeaz sistemul RPC (Remote
Pro edure Call) pentru a onstrui apli aµii distribuite. În epem prin a prezenta
um lu reaz sistemul RPC. RPC permite unui lient s exe ute pro eduri pe
alte al ulatoare din reµea. RPC fa e modelul lient/server mai puterni ³i
onstituie un instrument de programare mai simplu de ât interfaµa so ket.
O apli aµie RPC simpl onst dintr-un lient ³i un server, serverul �ind pe
ma³ina are exe ut pro edura. Apli aµia lient omuni u pro edura de pe
al ulatorul la distanµ transmiµând argumentele ³i re epµionând rezultatele.
Clientul ³i serverul se exe ut a dou pro ese separate are pot � pe al ula-
toare diferite din reµea. Bibliote a RPC realizeaz omuni area dintre a este
dou pro ese. Pro esele lient ³i server omuni u ajutorul a dou interfeµe
numite stub (� iot�); vom avea de i un stub pentru lient ³i altul pentru server.
A este interfeµe implementeaz proto olul RPC are spe i� um sunt
onstruite ³i um se prelu reaz mesajele emise între pro esele lient ³i server.
În �gura de mai jos se poate fa e o omparaµie între apelul lo al de pro e-
dur ³i apelul de pro edur la distanµ . Figura 11.1, a ³i alte �guri din a est
apitol, sunt inspirate de artea lui John Bloomer: Power Programming with
RPC (O'Reilly, 1992) pe are o re omand m elor are vor s aprofundeze
me anismul RPC.
Stub-urile se genereaz de obi ei u ajutorul omenzii rp gen, dup are
se leag de programele lient ³i server. Stub-urile onµin fun µii are trans-
lateaz apelurile lo ale de pro edur într-o se venµ de apeluri de fun µii RPC
de reµea. Clientul apeleaz pro edurile din stub-ul s u prin are utilizeaz bi-
bliote a RPC pentru a g si pro esul la distanµ ³i s -i transmit apoi ereri.
188 Atelier de programare în reµele de al ulatoare
Figura 11.1: Apelul lo al de pro edur vs apelul de pro edur la distanµ
Pro esul la distanµ �as ult � reµeaua prin intermediul stub-ului s u. Stub-ul
serverului realizeaz invo area rutinelor dorite u ajutorul unei interfeµe de
apel de pro eduri lo ale.
Clientul ³i serverul trebuie s omuni e utilizând o reprezentare a datelor
independent de tipul al ulatorului ³i de sistemul de operare. RPC utilizeaz
un format propriu pentru reprezentarea datelor unos ut sub numele de XDR
(External Data Representation). Componenta XDR de reprezentare a datelor
este des ris în RFC 1014.
Tipurile standard suportate de XDR sunt ele uzuale din limbajul C (pre-
um int, unsigned int, float, double sau void), plus altele (e.g. string,
fixed array, ounted array, symboli onstant et .).
RPC - Apelul pro edurilor la distanµ 189
Stub-urile lient ³i server sunt responsabile ³i u translatarea în ³i din a est
format. O bibliote XDR permite translatarea tipurilor prede�nite din C,
pre um ³i a unor tipuri mai omplexe um ar � ve torii de lungime variabil .
Pentru onversia datelor din format intern în format XDR se pun la dis-
poziµie fun µiile de mai jos, de�nite în antetul rp /xdr.h:
• xdrmem_ reate() � aso iaz unei zone de memorie obi³nuite un �ux de
date XDR;
• xdr_numetip() � realizeaz onversia datelor, unde numetip se va înlo ui
u unul dintre numele de tipuri de�nite de XDR.
Astfel, vom putea folosi fun µia de onversie xdr_int() a în exemplul
urm tor:
#in lude <rp /xdr.h>
#define BUFSIZE 400 /* lungimea zonei de memorie */
/* onversia unui intreg din format intern in format XDR */
...
XDR *xdrm; /* zona de memorie XDR */
har buf[BUFSIZE℄;
int intreg;
...
xdrmem_ reate (xdrm, buf, BUFSIZE, XDR_ENCODE);
...
intreg = 33;
xdr_int (xdrm, &intreg);
...
La el lalt ap t al omuni aµiei (pe ma³ina a�at la distanµ ) vom înlo ui
onstanta XDR_ENCODE u XDR_DECODE pentru a realiza onversia în sens invers.
Vezi ³i man xdr.
O fa ilitate important oferit de RPC este as underea în totalitate a pro e-
durilor de reµea în interiorul interfeµelor stub. A est lu ru simpli� programele
lient ³i server, eliminând ne esitatea de a ontrola detaliile legate de omuni-
area în reµea. Ca urmare, RPC u³ureaz s rierea apli aµiilor distribuite. Din
auz sistemul RPC în ear s as und detalii legate de reµea, el in lude
de obi ei o spe i� aµie legat de s himbul de argumente ³i rezultate între lient
³i server. A east spe i� aµie m re³te portabilitatea apli aµiilor.
190 Atelier de programare în reµele de al ulatoare
11.2 Cum lu reaz sistemul RPC?
Oferirea unui servi iu în reµea este diferit la sistemul RPC. Adresele lientu-
lui, serverului, numele servi iilor sunt p strate la nivel simboli . Un servi iu
este identi� at prin portul la are este oferit ³i unde exist un daemon are
a³teapt ererile de one tare. Un port reprezint un anal logi de omuni-
are. Portmapper -ul este un servi iu de reµea are este responsabil u aso ierea
de servi ii la diferite porturi; a est servi iu de mapare (aso iere) a porturilor
este oferit la portul 111. Utilizând portmapper -ul, numerele de port pentru
un anumit servi iu nu mai sunt �xe. Figura 11.2 des rie ei trei pa³i ne esari
pentru a un lient s poat apela un server.
Figura 11.2: Pa³ii ne esari pentru a un lient s poat apela un server
Pasul 1 determin adresa la are serverul va oferi servi iul s u. La iniµia-
lizare, programul server stabile³te ³i înregistreaz , prin intermediul portmap-
per -ului, portul (adresa) la are va oferi servi iul. În �gura 11.2 este vorba
despre portul a. Apoi lientul onsult portmapper -ul de pe ma³ina programu-
lui serverului pentru a identi� a portul la are trebuie s trimit ererea RPC
(pasul 2). Clientul ³i serverul pot omuni a a um pentru a realiza exe uµia
pro edurii la distanµ . Clientul trimite ereri, iar serverul r spunde a estor
soli it ri (pasul 3).
Se venµa de evenimente iniµiat de lient printr-un apel de pro edur la
distanµ (pasul 3 de mai sus) este des ris de �gura 11.3.
RPC - Apelul pro edurilor la distanµ 191
Figura 11.3: Evenimentele iniµiate de lient printr-un apel de pro edur la distanµ
Clientul trimite o erere în reµea u ajutorul unui apel allrp (). Progra-
mul server a³teapt mereu noi ereri, iar ând o astfel de erere este re epµio-
nat se invo servi iul respe tiv. O rutin dispat her este de obi ei folosit
atun i ând un server furnizeaz mai multe servi ii; dispat her -ul identi�
ererile spe i� e ³i apeleaz pro edura orespunz toare. Se exe ut pro edura
³i se returneaz r spunsul are apoi este transmis prin reµea la lient. Clientul,
are în tot a est timp de dup momentul emiterii ererii a a³teptat ina tiv,
preia r spunsul ³i ontinu exe uµia.
11.3 S rierea programelor server ³i lient RPC
Consider m un pas util în a atrage ititorul tre utilizarea RPC-ului este de
a des rie ³i a ar ta um se s riu programele server ³i lient. Vom evita a³adar
detaliile legate de RPC ³i vom tre e la a des rie um se dezvolt apli aµii de
192 Atelier de programare în reµele de al ulatoare
reµea utilizând sistemul RPC. În ontinuare, ne vom referi la implementarea
Sun Mi rosystems a sistemului RPC, implementare numit Open Network
Computing RPC (ONC RPC ) � a easta este de altfel ea mai r spândit
implementare. Spe i� aµia ei se g se³te în RFC 1057.
În implementarea Sun, interfaµa RPC se stru tureaz pe trei nivele:
• nivelul superior: omplet independent de sistemul de operare, hardware
sau reµea;
• nivelul intermediar: fa e apel la fun µiile de�nite de bibliote a RPC, a
de exemplu:
� registerrp ()
înregistreaz o pro edur spre a putea � exe utat la distanµ ,
� allrp ()
apeleaz o pro edur la distanµ (în prealabil înregistrat ),
� sv _run()
ruleaz un servi iu RPC.
A est nivel este utilizat de majoritatea apli aµiilor.
• nivelul inferior: d posibilitatea de a ontrola în detaliu me anismele
RPC (e.g. alegerea modului de transport al datelor, sin ronizarea apelu-
rilor et .).
Pro edurile la distanµ se vor in lude într-un program la distanµ . Un pro-
gram la distanµ reprezint unitatea software are se va exe uta pe o ma³in
a�at la distanµ . Fie are program a�at la distanµ orespunde unui server,
putând onµine un set de una sau mai multe pro eduri la distanµ sau date
globale. Pro edurile pot partaja date omune. De notat faptul argumentele
pasate pro edurilor la distanµ trebuie s �e in apsulate într-o stru tur (simi-
lar u stru t din limbajul C) pentru a redu e num rul de argumente trans-
mise pro edurii.
Fie rui program a�at la distanµ i se va asigna un identi� ator uni pe
32 de biµi, iar �e are pro edur omponent ( are va � exe utat în adrul
a elui program) este numerotat (indexat ) se venµial de la 1 la n, unde n este
num rul maxim de pro eduri ale a elui program.
Identi� atorii de program în implementarea Sun RPC au fost divizaµi astfel:
• 00 00 00 00 � 1F FF FF FF pentru apli aµiile RPC ale sistemului;
• 20 00 00 00 � 3F FF FF FF destinaµi programelor utilizatorilor;
RPC - Apelul pro edurilor la distanµ 193
• 40 00 00 00 � 5F FF FF FF reprezint identi� atori temporari;
• 60 00 00 00 � FF FF FF FF sunt valori rezervate.
Ca exemple de identi� atori prede�niµi se pot enumera:
• 10001 pentru programul rstatd are ofer informaµii despre sistemul
a�at la distanµ ; se pot utiliza pro edurile rstat() sau perfmeter();
• 10002 pentru programul rusersd furnizând informaµii despre utilizatorii
one taµi pe ma³ina pe are se va exe uta pro edura la distanµ ;
• 10003 pentru serverul nfs oferind a es la sistemul de �³iere în reµea NFS
(Network File System).
Pentru �e are program la distanµ , se va in lude un num r întreg pozitiv
desemnând versiunea. Prima versiune a unui program de obi ei este 1. Urm -
toarele versiuni vor � identi� ate de alte numere, în mod uni . Numerele de
versiuni ofer posibilitatea de a s himba detaliile de implementare sau extin-
derea apabilit µilor apli aµiilor f r a asigna un alt identi� ator unui program.
Un program la distanµ este, a³adar, un 3-uplu format din (identi� ator de
program, versiune, index pro edur ).
O omponent a implement rii Sun pentru RPC este ompilatorul (utili-
tarul) RPCGEN. A est ompilator produ e stub-urile lient ³i server, produ e
o rutin dispat h apabil s lu reze u pro eduri multiple ³i ofer �exibilitate
în realizarea programelor server ³i lient. Compilatorul RPCGEN soli it la
intrare un �³ier de spe i� aµii RPC. Figura 11.4 prezint um se obµine odul
pentru server ³i lient RPC. Fi³ierul de spe i� aµii RPC este numit Q.x. Uti-
lizând ompilatorul RPCGEN, nu mai este ne esar s realizaµi omuni area
RPC în odul serverului ³i lientului. Fun µiile respe tive sunt realizate de
stub-ul server (Q_sv . ) ³i stub-ul lient (Q_ lnt. ) generate de ompilator
pornind de la �³ier de spe i� aµii RPC. A este stub-uri utilizeaz apeluri RPC
de nivel s zut; a est lu ru înseamn se ompli puµin s rierea apli aµiei
lient ³i nu se mai folose³te apelul allrp ().
Compilatorul RPCGEN genereaz ³i �ltrele (fun µiile) de odi� are ³i de-
odi� are XDR utilizate de lientul ³i de serverul RPC. A este rutine se g ses
în �³ierul Q_xdr. . Se mai genereaz ³i un �sier header Q.h are se in lude
în toate elelalte trei �³iere generate (Q_sv . , Q_ lnt. , Q_xdr. ), dar ³i
în programele s rise de programator pentru lient ³i server (numite �apli aµie
lient� ³i �apli aµie server� în �gur ). Compilatorul RPCGEN nu poate realiza
totul. Trebuie s rise programele C pentru server ³i lient pe are noi le vom
numi server. ³i lient. .
194 Atelier de programare în reµele de al ulatoare
Figura 11.4: Obµinerea odului pentru serverul ³i lientul RPC
Pentru a obµine serverul va � ne esar ompilarea fun µiilor serverului, a
stub-ului server ³i a rutinelor XDR prin omanda:
g server. Q_sv . Q_xdr. -o server
Pentru a obµine lientul va � ne esar ompilarea fun µiilor lientului din
lient. , a stub-ului lient ³i a rutinelor XDR prin omanda:
g lient. Q_ lnt. Q_xdr. -o lient
Compilarea presupune existenµa bibliote ii rp lib. Pentru Linux, a easta
este in lus în bibliote ile standard.
Utilizarea sistemului RPC afe teaz viteza faµ de un apel lo al; mai exa t,
viteza s ade onsiderabil. Utilizarea RPC trebuie privit îns a o simpli� are
a program rii apli aµiilor distribuite, ³i nu a o on urenµ u apelurile de pro-
eduri lo ale. Exist motive serioase pentru are dorim s distribuim o apli aµie
în reµea. A est lu ru nu este totdeauna evident atun i ând par urgem exemple
dida ti e simple de RPC. A ele apli aµii pot � s rise mai u³or ³i e� ient folo-
sind apeluri de pro eduri lo ale. Totu³i nu trebuie ignorat faptul exemplele
dida ti e sunt simple pentru a nu pierde esenµa me anismului RPC în detaliile
unei probleme eva mai di� ile.
RPC - Apelul pro edurilor la distanµ 195
11.4 Exemplu
Realizarea apli aµiilor lient/server u ajutorul me anismului RPC implemen-
tat de Sun presupune s rierea a trei elemente:
1. o spe i� aµie RPC într-un �³ier .x;
2. un program server. are s onµin pro edurile invo ate;
3. un program lient. are trebuie s realizeze apelurile orespunz toare.
Vom prezenta ele trei omponente de mai sus pentru o apli aµie prin are
se soli it informaµii despre trenuri.
Fi³ierul trenuri.x este urm torul:
stru t request /* ererea adresata serverului */
{
har tren_d[100℄; /* des rierea trenului */
har nr_tren[10℄; /* numarul trenului */
int optiuni; /* PLECARI sau SOSIRI /*
};
stru t answer /* raspuns primit de lient */
{
har raspuns[4000℄;
};
program TRENURI
{
version VERSIUNE
{ /* pro edura apelata la distanta */
answer TREN (request) = 1;
} = 1; /* versiunea 1 (prima) */
} = 0x200000f1; /* identifi atorul uni al programului */
Se invo ompilatorul RPCGEN prin omanda:
rp gen -K 10 trenuri.x
Se obµin �³ierele:
-rw-r--r-- 1 lr lr 1114 Jun 14 09:33 trenuri.h
-rw-r--r-- 1 lr lr 548 Jun 14 09:33 trenuri_ lnt.
-rw-r--r-- 1 lr lr 2048 Jun 14 09:33 trenuri_sv .
-rw-r--r-- 1 lr lr 675 Jun 14 09:33 trenuri_xdr.
196 Atelier de programare în reµele de al ulatoare
Programul server. pe are trebuie s -l s riem este urm torul:
#in lude "trenuri.h"
#in lude "lib/lib.h"
#in lude "lib/app.h"
answer *
tren_1_sv (request * r, stru t sv _req *srq)
{
stati answer a; /* raspunsul returnat */
bzero (&a, sizeof (a));
if ((r->optiuni & PLECARI) == PLECARI)
{
r->optiuni &= (~PLECARI);
ple ari (r->tren_d, r->nr_tren, r->optiuni, a.raspuns);
}
else
{
r->optiuni &= (~SOSIRI);
sosiri (r->tren_d, r->nr_tren, r->optiuni, a.raspuns);
}
return &a;
}
Programul lient. este urm torul:
#in lude <rp /rp .h>
#in lude <string.h>
#in lude <stdio.h>
#in lude "trenuri.h"
#in lude "lib/lib.h"
int
main (int arg , har *argv[℄)
{
request r; /* ererea trimisa */
answer *a; /* raspunsul primit */
CLIENT * l;
int opt, only_opt;
har server[300℄, *number = NULL;
if ((arg == 1) || (str mp (argv[1℄, "--help") == 0) ||
(str mp (argv[1℄, "-h") == 0))
{
help (argv[0℄);
RPC - Apelul pro edurilor la distanµ 197
return 0;
}
opt = 0;
bzero (server, sizeof (server));
opt = pro ess_arg (arg , argv, &only_opt, &number, server);
/* reeaza pro edura */
l = lnt_ reate (server, TRENURI, VERSIUNE, "t p");
if ( l == NULL)
{
lnt_p reateerror (server);
return 1;
}
bzero (&r, sizeof (r));
if (only_opt == 0)
str py (r.tren_d, argv[arg - 1℄);
if (number != NULL)
str py (r.nr_tren, number);
r.optiuni = opt;
if ((a = tren_1 (&r, l)) == NULL) /* apel la distanta */
{
lnt_perror ( l, "Nu pot exe uta pro edura la distanta\n");
return 2;
}
printf ("%s", a->raspuns);
lnt_destroy ( l);
}
A este programe se ompileaz prin omenzile:
g server. trenuri_sv . trenuri_xdr. -L./ -lme -o server_trenuri
g lient. trenuri_ lnt. trenuri_xdr. -L./ -lme -g -o trenuri
Mai multe am nunte despre apelurile RPC se pot a�a dând man rp .
11.5 Exer iµii
Multitudinea de �³iere generate de RPCGEN se poate prelu ra mai e� ient u
ajutorul unui �³ier Makefile pro esat de omanda make.
O propunere pentru un astfel de �³ier este prezentat mai jos:
# folosit pentru ompilarea apli atiilor RPC
all:
rm -f *.a
make -C lib
rp gen -K 10 trenuri.x
198 Atelier de programare în reµele de al ulatoare
ar -r libme.a lib/*.o
g server. trenuri_sv . trenuri_xdr. \
-L./ -lme -o server_trenuri
g lient. trenuri_ lnt. trenuri_xdr. \
-L./ -lme -g -o trenuri
install:
install -m 0555 ple ari.txt /et /trenuri
install -m 0555 sosiri.txt /et /trenuri
install -m 0500 server_trenuri /usr/sbin
install -m 0555 trenuri /usr/bin
install -m 0555 trenuriX /usr/bin
lean_all:
make lean -C lib
rm -f *~ *.o trenuri server_trenuri trenuriX libme.a
lean:
make lean -C lib
rm -f *~ *.o
Ca exer iµiu, realizaµi mai întâi propriul Makefile. Apoi în er aµi s tran-
s rieµi o apli aµie lient/server propus în adrul apitolelor pre edente uti-
lizând me anismul RPC.
Capitolul 12
Utilizarea bibliote ii MySQL
Capitolul prezint pe s urt modul de dezvoltare de
apli aµii în reµea folosind serverul de baze de dateMySQL,
pornind de la rezolvarea unei probleme.
12.1 Formularea problemei
Ne propunem s rezolv m urm toarea problem :
�S se on eap o apli aµie lient/server pentru managementul situaµiei
notelor ³i prezenµei studenµilor la seminariile/laboratoarele susµinute de un
anumit adru dida ti . Editarea datelor se va realiza pe baza autenti� rii.�
12.1.1 S hiµ de rezolvare
Problema va � rezolvat astfel: serverul TCP folose³te serverul MySQL de
management al bazelor de date în vederea gestion rii informaµiilor referitoare
la studenµi, iar interfaµa lientului este implementat în qt.
Pentru a fa ilita omuni area dintre server ³i lient, s-a implementat un
proto ol de omuni aµie propriu, la nivelul apli aµie. A est proto ol este bazat
pe omenzi pentru managementul informaµiilor legate de studenµi. Furniz m
în ontinuare a este omenzi, împreun u sintaxa ³i semanti a a estora:
LIST are sintaxa: LIST [<profesor>℄ [<grupa>℄ [<student>℄
Des riere parametri:
profesor : [string℄ numele profesorului
grupa : [string℄ grupa
student : [string℄ numele studentului
LIST simplu va a�³a toµi profesorii din baza de date.
LIST u parametrul profesor va a�³a toate grupele profesorului spe i-
� at. Da a esta nu exist în baza de date, va � generat un mesaj de
eroare.
LIST u parametrii profesor ³i grupa va a�³a toµi studenµii (in lusiv u
notele ³i absenµele lor) din grupa spe i� at a profesorului dat.
200 Atelier de programare în reµele de al ulatoare
LIST u parametrii profesor, grupa ³i student va a�³a notele ³i ab-
senµele studentului din grupa profesorului spe i� at.
EDIT u sintaxa: EDIT <grupa>
Des riere parametri:
grupa : [string℄ numele grupei
EDIT va rea o nou grup de studenµi în baza de date. Da a easta
exist deja, se va returna un mesaj de eroare.
A east omand fun µioneaz numai pe baza autenti� rii.
ADD are sintaxa: ADD <student> <grupa>
Des riere parametri :
student : [string℄ numele studentului
grupa : [string℄ numele grupei
ADD va ad uga un nou student în grupa spe i� at . Da studentul
exist deja sau da grupa nu exist , se va returna un mesaj de eroare.
Comanda fun µioneaz numai pe baza autenti� rii.
MODIFY u sintaxa:
MODIFY <student> <grupa> <nota> <absenta> <saptamana>
Des riere parametri:
student : [string℄ numele studentului
grupa : [string℄ numele grupei
nota : [int℄ nota de la seminar
absenta : [int℄ va fi 0 sau 1 dupa um studentul este prezent
sau nu la seminar
saptamana : [int℄ va fi uprinsa intre 1 si 14
MODIFY va modi� a nota ³i absenµa studentului u numele spe i� at
din grupa dat în s pt mâna dat . Da grupa sau studentul nu exist ,
se va genera un mesaj de eroare.
A east omand fun µioneaz numai pe baza autenti� rii.
DELETE u sintaxa: DELETE <grupa> [<student>℄
Des riere parametri:
Utilizarea bibliote ii MySQL 201
student : [string℄ numele studentului
grupa : [string℄ numele grupei
DELETE va ³terge din baza de date �e grupa spe i� at , �e numai
studentul din grupa spe i� at . Da grupa sau studentul nu exist , se
va returna un mesaj de eroare.
Comanda fun µioneaz numai pe baza autenti� rii.
LOGIN are sintaxa: LOGIN <username> <passwd>
Des riere parametri:
username : [string℄ numele utilizatorului
passwd : [string℄ parola utilizatorului
LOGIN va exe uta autenti� area utilizatorului.
Da parametrul username este student, a estuia i se va permite numai
omanda LIST, iar modi� rile în baza de date îi vor � interzise. Da
username nu este g sit în baza de date sau da parola nu orespunde,
se va genera un mesaj de eroare.
12.2 A esarea serverului MySQL
Pentru a a esa serverul de baze de date MySQL vom re urge la urm toarele
fun µii, s rise în �³ierul sql. (se vor folosi fun µiile din antetul mysql.h):
/*
Se ompileaza u:
g -o sql -I/usr/in lude/mysql sql. -lz -lmysql lient
*/
#in lude <stdio.h>
#in lude <stdlib.h>
#in lude <mysql.h>
#in lude <string.h>
#in lude "definitii.h"
#in lude "error odes.h"
int auta( har *grupa, har *student, har *profesor);
int auta_student( har *grupa, har *student);
RESULT sele teaza( har *nume, har *grupa,
202 Atelier de programare în reµele de al ulatoare
har *student, int arg)
{
RESULT out;
MYSQL mysql,*so k;
MYSQL_RES *res;
MYSQL_ROW row;
unsigned int num_fields;
unsigned int i,j;
unsigned long *lengths;
har qbuf[200℄, *buff;
out. ode = OK;
out.lines = 0;
/* initializare */
mysql_init(&mysql);
/* one tare la serverul MySQL */
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
/* formulare omenzi SELECT */
swit h ( arg )
{
ase 0 : sprintf(qbuf,"SELECT nume, materie
FROM profesori ORDER BY nume");
break;
ase 1 : sprintf(qbuf,"SELECT grupa FROM grupe "
"WHERE profesor='%s' "
"ORDER BY grupa",nume);
break;
ase 2 : sprintf(qbuf,"SELECT student, nota, absenta, saptamana "
"FROM note WHERE profesor='%s' AND "
"grupa='%s' ORDER BY student, saptamana ", nume, grupa);
break;
ase 3 : sprintf(qbuf,"SELECT student, nota, absenta, saptamana "
"FROM note WHERE profesor='%s' AND "
"grupa='%s' AND student='%s' ORDER BY saptamana",
nume, grupa, student);
}
/* trimitere interogare */
if (mysql_query (so k, qbuf))
{
Utilizarea bibliote ii MySQL 203
fprintf(stderr, "Query failed (%s)\n", mysql_error(so k));
out. ode = ERR_QUERY;
exit(1);
}
/* re eptare raspuns din partea serverului */
if (!(res = mysql_store_result (so k)))
{
fprintf(stderr,"Couldn't get result from %s\n",
mysql_error(so k));
out. ode = ERR_RESULT;
exit(1);
}
out.result_row = ( har **) mallo (50 * sizeof( har *));
out.result_row[0℄ = ( har *) mallo (200);
i = 0;
num_fields = mysql_num_fields(res);
while (( row= mysql_fet h_row(res)))
{
out.result_row[i℄ = ( har*) mallo (200);
str py(out.result_row[i℄, "");
lengths = mysql_fet h_lengths (res);
buff = ( har*) mallo (50);
for ( j = 0; j < num_fields; j++ )
{
str at(out.result_row[i℄, "[");
sprintf(buff,"%.*s",
(int) lengths[j℄, row[j℄ ? row[j℄ : "NULL");
str at(out.result_row[i℄, buff);
str at(out.result_row[i℄, "℄");
}
printf("\n");
i++;
printf("linia %d : %s \n", i,out.result_row[i-1℄);
}
out.lines = i;
mysql_free_result(res);
mysql_ lose(so k);
return out;
}
/* autentifi area */
RESULT autent ( har *login, har *passwd)
{
RESULT out;
MYSQL mysql,*so k;
204 Atelier de programare în reµele de al ulatoare
MYSQL_RES *res;
har qbuf[200℄;
out. ode = OK;
out.lines = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
sprintf(qbuf,"SELECT * FROM profesori
WHERE id='%s' AND passwd='%s'",
login, passwd);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
out. ode = ERR_QUERY;
exit(1);
}
if (!(res=mysql_store_result(so k)))
{
fprintf(stderr,"Couldn't get result from %s\n",
mysql_error(so k));
exit(1);
}
if ( mysql_num_rows(res) == 0 )
out. ode = ERR_LOGIN;
mysql_free_result(res);
mysql_ lose(so k);
return out;
}
/* inserare grupa studenti in baza de date */
RESULT nou_grupa( har *grupa, har *profesor)
{
RESULT out;
MYSQL mysql,*so k;
har qbuf[200℄;
out. ode = OK;
out.lines = 0;
Utilizarea bibliote ii MySQL 205
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
sprintf(qbuf,"INSERT INTO grupe VALUES ('%s','%s')",
grupa, profesor);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
out. ode = ERR_QUERY;
exit(1);
}
mysql_ lose(so k);
return out;
}
RESULT nou ( har *grupa, har* student, har *profesor )
{
RESULT out;
MYSQL mysql,*so k;
har qbuf[200℄;
int k;
out. ode = OK;
out.lines = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
if ( auta(grupa, student, NULL) == 0 )
{
sprintf(qbuf,"INSERT INTO studenti VALUES ('%s','%s')",
student, grupa);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
206 Atelier de programare în reµele de al ulatoare
out. ode = ERR_QUERY;
exit(1);
}
}
for (k = 1; k <= 3; k++)
{
sprintf(qbuf,"INSERT INTO note"
"VALUES ('%s','%s', '%s', 0, 0, %d)",
student, grupa, profesor, k);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed !!!(%s)\n",mysql_error(so k));
out. ode = ERR_QUERY;
exit(1);
}
}
mysql_ lose(so k);
return out;
}
/* autare in baza de date */
int auta( har *grupa, har *student, har *profesor )
{
MYSQL mysql,*so k;
MYSQL_RES *res;
har qbuf[200℄;
int g=0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
exit(1);
}
if ( student == NULL )
sprintf(qbuf,"SELECT * FROM grupe WHERE grupa='%s'
AND profesor='%s'", grupa, profesor);
if ( (student != NULL) && (profesor == NULL) )
sprintf(qbuf,"SELECT * FROM studenti "
"WHERE grupa='%s' AND nume='%s'",
grupa, student);
if ( (student !=NULL) && (profesor!=NULL) )
sprintf(qbuf,"SELECT * FROM note WHERE grupa='%s' "
Utilizarea bibliote ii MySQL 207
"AND student='%s' AND "
"profesor='%s'", grupa, student, profesor);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
exit(1);
}
if (!(res=mysql_store_result(so k)))
{
fprintf(stderr,
"Couldn't get result from %s\n",mysql_error(so k));
exit(1);
}
if ( mysql_num_rows(res) > 0 )
g=1;
mysql_free_result(res);
mysql_ lose(so k);
return g;
}
/* autare dupa numele studentului */
int auta_student( har *grupa, har *student)
{
MYSQL mysql,*so k;
MYSQL_RES *res;
har qbuf[200℄;
int g = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
exit(1);
}
if ( student == NULL )
sprintf(qbuf,"SELECT * FROM note WHERE grupa='%s'", grupa);
else
sprintf(qbuf,"SELECT * FROM note WHERE student='%s'
AND grupa='%s'",student, grupa);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
exit(1);
}
208 Atelier de programare în reµele de al ulatoare
if (!(res=mysql_store_result(so k)))
{
fprintf(stderr,"Couldn't get result from %s\n",
mysql_error(so k));
exit(1);
}
if ( mysql_num_rows(res) > 0 )
g = 1;
mysql_free_result(res);
mysql_ lose(so k);
return g;
}
/* auta dupa numele profesorului */
int auta_prof( har *profesor)
{
MYSQL mysql,*so k;
MYSQL_RES *res;
har qbuf[200℄;
int g = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
exit(1);
}
sprintf(qbuf,"SELECT * FROM profesori WHERE nume='%s'",
profesor);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
exit(1);
}
if (!(res=mysql_store_result(so k)))
{
fprintf(stderr,"Couldn't get result from %s\n",
mysql_error(so k));
exit(1);
}
if ( mysql_num_rows(res) > 0 )
g = 1;
mysql_free_result(res);
Utilizarea bibliote ii MySQL 209
mysql_ lose(so k);
return g;
}
/* stergere grupa */
RESULT delete_grupa( har *grupa, har *profesor)
{
RESULT out;
MYSQL mysql,*so k;
har qbuf1[200℄, qbuf2[200℄;
out. ode = OK;
out.lines = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf (stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error (&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
sprintf(qbuf1,"DELETE FROM note WHERE grupa='%s' "
"AND profesor='%s'", grupa, profesor);
sprintf(qbuf2,"DELETE FROM grupe WHERE grupa='%s' "
"AND profesor='%s'", grupa, profesor);
if (mysql_query(so k,qbuf1) || mysql_query(so k,qbuf2))
{
fprintf(stderr, "Query failed (%s)\n", mysql_error(so k));
exit(1);
}
if ( auta_student(grupa,NULL) == 0 )
{
sprintf(qbuf2,"DELETE FROM studenti WHERE grupa='%s'", grupa);
if(mysql_query(so k,qbuf1) || mysql_query(so k,qbuf2))
{
fprintf(stderr, "Query failed (%s)\n", mysql_error(so k));
exit(1);
}
}
mysql_ lose(so k);
return out;
}
210 Atelier de programare în reµele de al ulatoare
RESULT delete( har *student, har *grupa, har *profesor)
{
RESULT out;
MYSQL mysql,*so k;
har qbuf[200℄;
out. ode = OK;
out.lines = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr, "Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
sprintf (qbuf, "DELETE FROM note WHERE student='%s' "
"AND grupa='%s' AND profesor='%s'",
student, grupa, profesor);
if (mysql_query(so k,qbuf))
{
fprintf(stderr,
"Query failed (%s)\n", mysql_error(so k));
exit(1);
}
if ( auta_student(grupa, student) == 0 )
{
sprintf (qbuf, "DELETE FROM studenti "
"WHERE nume='%s' AND grupa='%s'", student, grupa);
if (mysql_query(so k, qbuf))
{
fprintf(stderr,
"Query failed (%s)\n", mysql_error(so k));
exit(1);
}
}
mysql_ lose(so k);
return out;
}
/* modifi are informatii */
RESULT modi ( har *stud, har *grupa,
int nota, int abs, int sapt, har *prof)
{
Utilizarea bibliote ii MySQL 211
RESULT out;
MYSQL mysql,*so k;
har qbuf1[200℄, qbuf2[200℄;
out. ode = OK;
out.lines = 0;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
perror("");
out. ode = ERR_DB_CONNECT;
exit(1);
}
sprintf(qbuf1,"UPDATE note SET nota=%d "
"WHERE student='%s' AND grupa='%s' "
"AND profesor='%s' AND saptamana=%d",
nota, stud, grupa, prof, sapt);
sprintf(qbuf2,"UPDATE note SET absenta=%d "
"WHERE student='%s' AND grupa='%s' "
"AND profesor='%s' AND saptamana=%d",
abs, stud, grupa, prof, sapt);
if(mysql_query(so k,qbuf1) || mysql_query(so k,qbuf2) )
{
fprintf(stderr, "Query failed (%s)\n", mysql_error(so k));
exit(1);
}
mysql_ lose(so k);
return out;
}
har * id ( har *prof)
{
har *buff;
MYSQL mysql,*so k;
MYSQL_RES *res;
MYSQL_ROW row;
unsigned long *lengths;
har qbuf[200℄;
mysql_init(&mysql);
if (!(so k = mysql_real_ onne t(&mysql,NULL,0,0,"bd",0,NULL,0)))
{
fprintf(stderr,"Couldn't onne t to engine!\n%s\n\n",
mysql_error(&mysql));
212 Atelier de programare în reµele de al ulatoare
perror("");
exit(1);
}
sprintf(qbuf,"SELECT id FROM profesori WHERE nume='%s'", prof);
if(mysql_query(so k,qbuf))
{
fprintf(stderr,"Query failed (%s)\n",mysql_error(so k));
exit(1);
}
if (!(res=mysql_store_result(so k)))
{
fprintf(stderr,"Couldn't get result from %s\n",
mysql_error(so k));
exit(1);
}
while (( row= mysql_fet h_row(res)))
{
buff = ( har*)mallo (30);
lengths = mysql_fet h_lengths(res);
sprintf(buff,"%.*s",
(int) lengths[0℄, row[0℄ ? row[0℄ : "NULL");
}
mysql_free_result(res);
mysql_ lose(so k);
return buff;
}
Fun µiile de mai sus utilizeaz de�niµii de onstante, tipuri ³i prototipuri din
�³ierele antet sql.h, error odes.h ³i definitii.h prezentate în ontinuare:
sql.h
#in lude "definitii.h"
#in lude "error odes.h"
/* prototipuri ale fun tiilor utilizate in dialogul
u serverul MySQL */
RESULT sele teaza( har *profesor, har *grupa,
har *student, int arg);
RESULT autent( har *login, har *passwd);
RESULT nou_grupa( har *grupa, har *profesor);
RESULT nou( har *grupa, har * stundent, har *profesor);
int auta( har *grupa, har *student, har *profesor);
int auta_prof( har *profesor);
RESULT delete_grupa( har *grupa, har *profesor);
RESULT delete( har *student, har *grupa, har *profesor);
Utilizarea bibliote ii MySQL 213
RESULT modi( har *student, har *grupa,
int nota, int absenta, int sapt, har *profesor);
har* id( har *prof);
214 Atelier de programare în reµele de al ulatoare
error odes.h
/* odurile de eroare returnate de serverul MySQL */
#ifndef _error odes_h
#define _error odes_h
#in lude "definitii.h"
stati onst ERR_CODE OK =
{0, "OK"};
stati onst ERR_CODE ERR_NSE =
{1, "No su h entry in table" };
stati onst ERR_CODE ERR_SYNTAX =
{2, "In orre t syntax"};
stati onst ERR_CODE ERR_ARG =
{3, "Invalid number of arguments"};
stati onst ERR_CODE ERR_FOUND =
{4, "Data already exists"};
stati onst ERR_CODE ERR_DB_CONNECT =
{5, "Error database onne tion"};
stati onst ERR_CODE ERR_QUERY =
{6, "Error in query ommand"};
stati onst ERR_CODE ERR_RESULT =
{7, "Error in getting the result"};
stati onst ERR_CODE ERR_NOTFOUND =
{8, "Data does not exist"};
stati onst ERR_CODE ERR_COMMAND =
{9, "Command does not exist"};
stati onst ERR_CODE ERR_LOGIN =
{10, "Login and password failed"};
stati onst ERR_CODE ERR_ACCESS =
{11, "A ess denied"};
stati onst ERR_CODE ERR_INCORECT =
{12, "In orre t data"};
#endif
de�nitii.h
/* definitii de tipuri utilizate in server */
#ifndef _definitii_h
#define _definitii_h
typedef stru t st_error_ odes {
int value;
har *message;
} ERR_CODE;
Utilizarea bibliote ii MySQL 215
typedef har **INPUT_LINES;
typedef har **RETURN_LINES;
typedef stru t st_input{
int arg;
INPUT_LINES input_row;
ERR_CODE ode; /* the error ode */
} INPUT;
typedef stru t st_result { /* rezultatul returnat de MySQL */
ERR_CODE ode; /* the error ode */
RETURN_LINES result_row; /* return data as array of strings */
int lines; /* number of rows in the result */
} RESULT;
typedef stru t st_autent { /* stru tura de autentifi are */
har *login; /* login name */
har *passwd; /* password */
int level; /* level of se urity 0-student 1-profs */
} AUTENT;
#endif
Capitolul 13
Bibliote a n urses
A est apitol prezint pe s urt bibliote a n urses ³i um
se utilizeaz fun µiile a estei bibliote i prin prezentarea
unei apli aµii lient/server.
13.1 Des riere general
Bibliote a n urses ofer un set destul de uprinz tor de fun µii pentru a�³area
³i prelu rarea informaµiilor în mod text, independente de terminal. Fun µiile
n urses emuleaz pe ele din mai ve hea bibliote urses oferit de System
V Release 4.
Pentru a utiliza rutinele puse la dispoziµie de n urses va trebui a la edi-
tarea de leg turi s indi m link-editorului s în ar e ³i a east bibliote prin
opµiunea �-l�. A³adar, la ompilare vom in lude -ln urses.
Bibliote a n urses ofer , printre altele, fun µii de manipulare a informa-
µiilor de pe e ran sau din fragmente de e ran, itirea datelor de la intrarea
standard, ontrolul asupra modului de operare u terminalul ³i a esul la ru-
tine de prelu rare a terminalului la nivel oborât.
Înainte de a putea � folosite alte fun µii n urses, programatorul va trebui s
apeleze rutinele de iniµializare inits r() sau newterm(). Înainte de terminarea
lu rului u terminalul, va trebui apelat endwin().
În az de eroare, fun µiile n urses vor returna valoarea spe ial ERR (întreg)
sau NULL.
Zonele de informaµii de pe e ran în mod uzual vor � organizate sub form
de ferestre, iar n urses pune la dispoziµie tipul prede�nit WINDOW. Fie are
fereastr va � referit printr-un pointer la a est tip. Pot � utilizate ³i a³a-
numitele pad-uri are sunt ferestre al ror onµinut poate s nu �e a�³at
omplet pe e ran. De reµinut faptul bibliote a n urses nu ofer suport pen-
tru folosirea ferestrelor suprapuse, a est lu ru realizându-se prin intermediul
altei bibliote i: panel.
13.2 Prezentarea fun µiilor bibliote ii n urses
Bibliote a n urses pune la dispoziµia programatorului o serie de fun µii menite
a onstrui interfeµe u utilizatorul în mod text, oferind diverse fa ilit µi pre um
Bibliote a n urses 217
de�nirea de ferestre, utilizarea ulorilor ³i tastelor spe iale, posibilit µi de
as undere a ursorului ³i altele. Urmeaz prezentarea prototipului pentru o
parte dintre a este fun µii:
• WINDOW *inits r (void);
• int start_ olor (void);
• int init_pair (short pair, short foregrnd, short ba kgrnd);
• int urs_set (int visibility);
� invisible � visibility = 0
� normal � visibility = 1
� very visible � visibility = 2
• WINDOW *newwin (int nlines, int n ols,
int begin_y, int begin_x);
• WINDOW *subwin (WINDOW *orig, int nlines, int n ols,
int begin_y, int begin_x);
• int w olor_set (WINDOW *win, short olor_pair_number,
void* opts);
• int olor_set (short olor_pair_number, void* opts);
• int wborder (WINDOW *win, htype ls, htype rs, htype ts,
htype bs, htype tl, htype tr,
htype bl, htype br);
� 'ls' � este ara terul folosit pentru partea stâng a henarului feres-
trei (left side),
� 'rs' � right side,
� 'ts' � top side,
� 'bs' � bottom side,
� 'tl' � top left�hand orner,
� 'tr' � top right�hand orner,
� 'bl' � bottom left�hand orner,
� 'br' � bottom right�hand orner.
218 Atelier de programare în reµele de al ulatoare
Da vreunul dintre a este argumente este zero, atun i urm toarele valori
impli ite (de�nite în urses.h) sunt folosite:
ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE,
ACS_ULCORNER, ACS_URCORNER, ACS_LLCORNER, ACS_LRCORNER.
• int delwin (WINDOW *win);
• int w lear (WINDOW *win);
• int wrefresh (WINDOW *win);
• int refresh (void);
• int doupdate (void);
• int e ho (void);
• int noe ho (void);
• int mvprintw (int y, int x, har *fmt [, arg℄ ...);
• int mvwprintw (WINDOW *win, int y, int x,
har *fmt [, arg℄ ...);
• int getnstr ( har *str, int n);
• int wget h (WINDOW *win);
• int hgat (int n, attr_t attr,
short olor, onst void *opts);
• int wstandend (WINDOW *win);
• int wstandout (WINDOW *win);
• int move (int y, int x);
• int keypad (WINDOW *win, bool bf);
Bibliote a n urses 219
13.3 Enunµul unei probleme
�S se s rie o apli aµie are furnizeaz lista utilizatorilor existenµi pe ma³ina
pe are ruleaz .�
13.3.1 Rezolvarea problemei
Apli aµia va avea o interfaµ similar elei a omenzii top, utilizatorul putând
sorta informaµiile în fun µie de numele proprietarului ori de timpul de one tare
sau putând rea tualiza informaµiile despre sesiunile existente pe ma³in . In-
terfaµa este elaborat u ajutorul rutinelor puse la dispoziµie de bibliote a
n urses.
Vom prezenta în ontinuare sursa a estui program:
ftop.
#in lude <stdio.h>
#in lude <string.h>
#in lude <stdlib.h>
#in lude < type.h>
#in lude <termios.h>
#in lude < urses.h>
#in lude <utmp.h>
#in lude <paths.h>
#in lude <time.h>
#in lude <unistd.h>
#in lude <signal.h>
#in lude <netdb.h>
#in lude <sys/so ket.h>
#in lude <sys/stat.h>
#in lude <arpa/inet.h>
#in lude "ftop.h"
/* Sort list by... */
#define USER_SORT 1
#define HOST_SORT 2
#define IDLE_TIME_SORT 3
#define LOGIN_TIME_SORT 4
/* Sorting order. */
#define SORT_ASCEND 0
#define SORT_DESCEND 1
/* This is for the info pad definition. */
#define MAX_USER_COUNT 256
/* Prototypes */
int ftop_ mp ( onst void *, onst void *);
void sort_list (void);
har *format_idle (time_t);
har *format_time (time_t);
void update_data (void);
220 Atelier de programare în reµele de al ulatoare
void update_s reen (void);
void update (int);
void ommand_list (void);
/* Global variables */
stru t ftop_data **DATA;
int user_ ount;
har host_name[UT_HOSTSIZE℄, lo al_time[TIME_LEN℄;
WINDOW *stat_win, *input_win, *head_win, *info_pad;
unsigned int delay;
int info_pad_pos;
int sort_type, sort_order;
int
main (int arg , har **argv)
{
int key, i;
har *delay_ = ( har *) mallo (COLS * sizeof ( har));
stru t termios original, raw;
har any;
delay = 5;
alarm (delay);
signal (SIGALRM, update);
t getattr (0, &original);
fmakeraw (&raw);
inits r ();
break ();
noe ho ();
nonl ();
intrflush (stds r, FALSE);
stat_win = newwin (2, COLS, 0, 0);
wattrset (stat_win, A_BOLD);
input_win = newwin (1, COLS, 2, 0);
keypad (input_win, TRUE);
head_win = newwin (1, COLS, 3, 0);
wattrset (head_win, A_REVERSE);
wprintw (head_win, "%-10s %-7s %-24s %8s %-15s %4s\n",
"USER", "LINE", "FROM", "IDLE", "LOGIN-TIME", "MESG");
wattrset (head_win, A_NORMAL);
wrefresh (head_win);
info_pad = newpad (MAX_USER_COUNT, COLS);
DATA = NULL;
user_ ount = 0;
sort_type = USER_SORT;
sort_order = SORT_ASCEND;
info_pad_pos = 0;
update (0);
key = wget h (input_win);
while (key != 'q')
{
swit h (key)
{
Bibliote a n urses 221
ase '?':
ase 'h':
alarm (0);
endwin ();
ommand_list ();
fflush (stdout);
t setattr (0, TCSANOW, &raw);
read (0, &any, 1);
t setattr (0, TCSANOW, &original);
doupdate ();
update (0);
break;
ase 's':
alarm (0);
wprintw (input_win,
"Delay between updates (0 - no updating): ");
e ho ();
wgetstr (input_win, delay_ );
w lear (input_win);
noe ho ();
wrefresh (input_win);
if (strlen (delay_ ) != 0)
{
i = 0;
while (i < strlen (delay_ ) && isdigit (delay_ [i℄))
i++;
if (i < strlen (delay_ ))
{
beep ();
wattrset (input_win, A_REVERSE);
wprintw (input_win, "Invalid input!");
wrefresh (input_win);
sleep (2);
wattrset (input_win, A_NORMAL);
w lear (input_win);
wrefresh (input_win);
}
else
delay = atol (delay_ );
}
update (0);
break;
ase ' ':
ase 'r':
update (0);
break;
/* S rolling ommands. */
ase KEY_UP:
if (info_pad_pos > 0)
{
info_pad_pos--;
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
222 Atelier de programare în reµele de al ulatoare
break;
ase KEY_DOWN:
if (user_ ount - info_pad_pos > LINES - 4)
{
info_pad_pos++;
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
break;
ase KEY_PPAGE:
if (info_pad_pos > 0)
{
info_pad_pos -= LINES - 4;
if (info_pad_pos < 0)
info_pad_pos = 0;
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
break;
ase KEY_NPAGE:
if (user_ ount - info_pad_pos > LINES - 4)
{
info_pad_pos += LINES - 4;
if (user_ ount - info_pad_pos < LINES - 4)
info_pad_pos = user_ ount - (LINES - 4);
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
break;
ase KEY_HOME:
if (info_pad_pos > 0)
{
info_pad_pos = 0;
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
break;
ase KEY_END:
if (user_ ount - info_pad_pos > LINES - 4)
{
info_pad_pos = user_ ount - (LINES - 4);
prefresh (info_pad, info_pad_pos,
0, 4, 0, LINES, COLS);
}
else
beep ();
break;
Bibliote a n urses 223
/* Sorting ommands. */
ase 'u':
sort_type = USER_SORT;
sort_order = SORT_ASCEND;
sort_list ();
update_s reen ();
break;
ase 'U':
sort_type = USER_SORT;
sort_order = SORT_DESCEND;
sort_list ();
update_s reen ();
break;
ase 'f':
sort_type = HOST_SORT;
sort_order = SORT_ASCEND;
sort_list ();
update_s reen ();
break;
ase 'F':
sort_type = HOST_SORT;
sort_order = SORT_DESCEND;
sort_list ();
update_s reen ();
break;
ase 'i':
sort_type = IDLE_TIME_SORT;
sort_order = SORT_ASCEND;
sort_list ();
update_s reen ();
break;
ase 'I':
sort_type = IDLE_TIME_SORT;
sort_order = SORT_DESCEND;
sort_list ();
update_s reen ();
break;
ase 'l':
sort_type = LOGIN_TIME_SORT;
sort_order = SORT_ASCEND;
sort_list ();
update_s reen ();
break;
ase 'L':
sort_type = LOGIN_TIME_SORT;
sort_order = SORT_DESCEND;
sort_list ();
update_s reen ();
break;
default:
beep ();
wattrset (input_win, A_REVERSE);
wprintw (input_win,
"Unknown ommand `% ' -- hit `h' for help", key);
wrefresh (input_win);
sleep (1);
wattrset (input_win, A_NORMAL);
224 Atelier de programare în reµele de al ulatoare
w lear (input_win);
wrefresh (input_win);
}
key = wget h (input_win);
}
endwin ();
exit (EXIT_SUCCESS);
}
int
ftop_ mp ( onst void *a, onst void *b)
{
int result;
swit h (sort_type)
{
ase USER_SORT:
result = str ase mp
((*((stru t ftop_data **) a))->ft_user,
(*((stru t ftop_data **) b))->ft_user);
break;
ase HOST_SORT:
result = str ase mp
((*((stru t ftop_data **) a))->ft_host,
(*((stru t ftop_data **) b))->ft_host);
break;
ase IDLE_TIME_SORT:
if ((*((stru t ftop_data **) a))->ft_idle_time_t
< (*((stru t ftop_data **) b))->ft_idle_time_t)
result = -1;
else
result = (*((stru t ftop_data **) a))->ft_idle_time_t
> (*((stru t ftop_data **) b))->ft_idle_time_t;
break;
ase LOGIN_TIME_SORT:
if ((*((stru t ftop_data **) a))->ft_login_time_t
< (*((stru t ftop_data **) b))->ft_login_time_t)
result = -1;
else
result = (*((stru t ftop_data **) a))->ft_login_time_t
> (*((stru t ftop_data **) b))->ft_login_time_t;
}
return (sort_order == SORT_ASCEND) ? result : -result;
}
void
sort_list (void)
{
if (DATA == NULL)
return;
qsort ((void *) DATA, user_ ount,
sizeof (stru t ftop_data *), ftop_ mp);
}
har *
format_idle (time_t idle)
{
Bibliote a n urses 225
int d, h, m, s;
har sh[3℄, sm[3℄, ss[3℄;
har *res = mallo (8 * sizeof ( har));
d = idle / 3600 / 24;
h = idle / 3600;
m = idle % 3600 / 60;
s = idle % 3600 % 60;
if (d > 0)
{
sprintf (res, "%dd", d);
return res;
}
if (h > 0)
{
sprintf (sh, "%2d", h);
sprintf (sm, "%02d", m);
sprintf (ss, "%02d", s);
sprintf (res, "%s:%s:%s", sh, sm, ss);
return res;
}
str py (sh, "");
if (m > 0)
{
sprintf (sm, "%2d", m);
sprintf (ss, "%02d", s);
sprintf (res, "%s:%s", sm, ss);
return res;
}
str py (sm, "");
if (s > 0)
{
sprintf (ss, "%2d", s);
sprintf (res, "%s", ss);
return res;
}
str py (res, "");
return res;
}
har *
format_time (time_t time)
{
stru t tm *bd_time = lo altime (&time);
har *months[12℄ = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "O t", "Nov", "De "
};
har *res = mallo (16 * sizeof ( har));
sprintf (res, "%s %d %02d:%02d:%02d",
months[bd_time->tm_mon℄,
bd_time->tm_mday, bd_time->tm_hour,
bd_time->tm_min, bd_time->tm_se );
return res;
}
226 Atelier de programare în reµele de al ulatoare
void
update_data (void)
{
stru t utmp *utmp_entry;
stru t stat stat_entry;
stru t ftop_data *data;
har terminal[256℄;
har *idle_time, *login_time;
int i;
/* Free the urrent list. */
if (DATA != NULL)
{
for (i = 0; i < user_ ount; i++)
free (DATA[i℄);
free (DATA);
}
DATA = NULL;
user_ ount = 0;
/* Gather data from utmp file. */
setutent ();
while ((utmp_entry = getutent ()) != NULL)
{
if (utmp_entry->ut_type != USER_PROCESS)
ontinue;
user_ ount++;
DATA = (stru t ftop_data **)
reallo (DATA, user_ ount *
sizeof (stru t ftop_data *));
DATA[user_ ount - 1℄ =
(stru t ftop_data *) mallo (sizeof (stru t ftop_data));
data = DATA[user_ ount - 1℄;
str py (data->ft_user, utmp_entry->ut_user);
str py (data->ft_tty, utmp_entry->ut_line);
str py (data->ft_host, utmp_entry->ut_host);
str py (terminal, _PATH_DEV);
str at (terminal, utmp_entry->ut_line);
stat (terminal, &stat_entry);
data->ft_idle_time_t = time (NULL) - stat_entry.st_atime;
idle_time = format_idle (data->ft_idle_time_t);
str py (data->ft_idle_time_ , idle_time);
free (idle_time);
data->ft_login_time_t = utmp_entry->ut_tv.tv_se ;
login_time = format_time (data->ft_login_time_t);
str py (data->ft_login_time_ , login_time);
free (login_time);
data->ft_mesg_stat = (stat_entry.st_mode & S_IWGRP) ? '+' : '-';
}
endutent ();
/* Sort the data a ording to urrent sorting order. */
sort_list ();
}
void
update_s reen (void)
{
Bibliote a n urses 227
time_t daytime;
int i;
w lear (stat_win);
w lear (info_pad);
gethostname (host_name, UT_HOSTSIZE);
daytime = time (NULL);
/* time ()'s output ends in '\n', whi h we don't want. */
str py (lo al_time, strtok ( time (&daytime), "\n"));
mvwprintw (stat_win, 0, 0, "Host: %s", host_name);
mvwprintw (stat_win, 1, 0, "%s, %d user(s)",
lo al_time, user_ ount);
wrefresh (stat_win);
for (i = 0; i < user_ ount; i++)
{
mvwprintw (info_pad, i, 0,
"%-10.10s %-7s %-24.24s %8s %15s % ",
DATA[i℄->ft_user,
DATA[i℄->ft_tty,
DATA[i℄->ft_host,
DATA[i℄->ft_idle_time_ ,
DATA[i℄->ft_login_time_ , DATA[i℄->ft_mesg_stat);
}
prefresh (info_pad, info_pad_pos, 0, 4, 0, LINES, COLS);
wrefresh (input_win);
}
void
update (int signum)
/* Takes an int as an argument to mat h
the signal-handler prototype. */
{
update_data ();
if (info_pad_pos > 0 &&
user_ ount - info_pad_pos < LINES - 4)
info_pad_pos = user_ ount - (LINES - 4);
if (info_pad_pos < 0)
info_pad_pos = 0;
update_s reen ();
alarm (delay);
}
void
ommand_list (void)
{
printf ("Intera tive ommands are:\n\n\
u Sort by user name\n\
U Reverse sort by user name\n\
f Sort by host name\n\
F Reverse sort by host name\n\
i Sort by idle time\n\
I Reverse sort by idle time\n\
l Sort by login time\n\
L Reverse sort by login time\n\n\
UpArrow Ba kward one line\n\
DownArrow Foreward one line\n\
PgUp Ba kward one page\n\
228 Atelier de programare în reµele de al ulatoare
PgDown Foreward one page\n\
Home Move to the beginning of the list\n\
End Move to the end of the list\n\n\
spa e, r Update list\n\
h, ? Print this list\n\
s Set the delay in se onds between updates\n\
q Quit\n\n\
Press any key to ontinue");
}
Programul de mai sus folose³te urm torul �³ier antet:
ftop.h
#ifndef _FTOP_H
#define _FTOP_H
#in lude <utmp.h>
#in lude <time.h>
/* Number of hara ters to store the urrent date,
as returned by time (). 48 should be enough. */
#define TIME_LEN 48
stru t ftop_data
{
har ft_user[UT_NAMESIZE℄;
har ft_tty[UT_LINESIZE℄;
har ft_host[UT_HOSTSIZE℄;
time_t ft_idle_time_t; /* For sorting by idle time. */
har ft_idle_time_ [9℄; /* `HH:MM:SS' + `\0' */
time_t ft_login_time_t; /* For sorting by login time. */
har ft_login_time_ [16℄; /* `MMM DD HH:MM:SS' + `\0' */
har ft_mesg_stat; /* `+' or `-' */
};
#endif /* _FTOP_H */
Capitolul 14
Mediul Glade
Capitolul prezint pe s urt maniera de dezvoltare de
apli aµii în reµea folosind mediul Glade, pornind de la
rezolvarea unei probleme. Interfaµa gra� se realizeaz
utilizând bibliote a Gtk.
14.1 Formularea ³i rezolvarea problemei
În ele e urmeaz va � rezolvat a eea³i problem prezentat în apitolul 12,
referitoare la managementul situaµiei ³ olare a studenµilor.
Serverul va oferi urm toarele posibilit µi de gestionare a notelor ³i prezen-
µelor la laborator pentru studenµii unui anumit adru dida ti :
• da utilizatorul ( lientul) este profesor, atun i a esta va putea s adauge
studenµi, s modi� e notele ³i prezenµele a estora ³i s a�e informaµii
despre un anumit student dintr-o anumit grup ;
• în azul în are lientul este un utilizator obi³nuit (f r drepturi spe iale),
el poate doar s a�e informaµii despre un anumit student.
Pentru autenti� area utilizatorilor s-a utilizat fun µia rypt().
14.2 Con eperea interfeµei
Pentru on eperea lientului s-a realizat o interfaµ gra� s ris în Gtk, folo-
sind mediul de dezvoltare Glade.
14.2.1 Utilizarea mediului de dezvoltare Glade
Mediul Glade genereaz în mod automat urm toarele �³iere (�e are dintre
a estea au aso iate �³ierele antet orespunz toare onµinând de�niµiile de tipuri
³i prototipurile fun µiilor utilizate):
• allba ks.
• interfa e.
230 Atelier de programare în reµele de al ulatoare
• main.
• support.
În �³ierul interfa e. , Glade genereaz fun µiile utilizate pentru rearea
asetelor de dialog ale interfeµei. În azul nostru, am avut nevoie de:
• o aset de dialog pentru one tarea utilizatorului la server
fun µia reate_Login(),
• un dialog prin are lientul ere informaµii despre un student
fun µia reate_informatii(),
• o fereastr folosit pentru modi� area notei unui student
fun µia reate_modifi a_nota(),
• un dialog pentru modi� area prezenµelor unui student
fun µia reate_Modifi a_ore_de_re uperat(),
• o aset de dialog utilizat la ad ugarea unui student
fun µia reate_adauga().
Fi³ierul surs allba ks. va onµine fun µiile de tratare a evenimentelor
survenite asupra unor elemente de interfaµ (butoane, opµiuni de meniu et .).
De exemplu, s-au implementat fun µii pre um on_button_ok_afis_ li ked()
are trateaz ap sarea butonului Ok din dialogul pentru a�area informaµiilor
despre un student sau on_modifi a_ore_a tivate(), fun µie exe utat la se-
le tarea opµiunii Modi� ore a meniului Date studenµi are va rea o aset
de dialog pentru introdu erea numelui studentului ³i a num rului de prezenµe
ale a estuia.
Fi³ierul support. este un �³ier generat de Glade onµinând fun µii pentru
rearea unor elemente de interfaµ (widgets).
Implementarea propriu-zis a lientului se reg se³te în �³ierul main. .
De asemenea, mediul Galde genereaz �³ierul Makefile are va � utilizat la
ompilarea apli aµiei u ajutorul omenzii make.
Un instantaneu al fazei de proie tare a interfeµei u utilizatorul folosind
mediul Glade poate � urm rit în �gura 14.1.
Mediul Glade 231
Figura 14.1: Proie tarea interfeµei u utilizatorul folosind Glade
Câteva dintre �³ierele generate de mediul Glade sunt prezentate mai jos:
support.h
/* DO NOT EDIT THIS FILE - it is generated by Glade. */
#in lude <gnome.h>
/* Publi Fun tions. */
/*
* This fun tion returns a widget in a omponent reated by Glade.
* Call it with the toplevel widget in the omponent
(i.e. a window/dialog),
232 Atelier de programare în reµele de al ulatoare
* or alternatively any widget in the omponent,
and the name of the widget you want returned.
*/
GtkWidget* lookup_widget (GtkWidget *widget,
onst g har *widget_name);
/* get_widget() is depre ated. Use lookup_widget instead. */
#define get_widget lookup_widget
/* Private Fun tions. */
/* This is used to reate the pixmaps in the interfa e. */
GtkWidget* reate_pixmap (GtkWidget *widget,
onst g har *filename,
gboolean gnome_pixmap);
GdkImlibImage* reate_image ( onst g har *filename);
support.
/*
* DO NOT EDIT THIS FILE - it is generated by Glade.
*/
#ifdef HAVE_CONFIG_H
#in lude < onfig.h>
#endif
#in lude <sys/types.h>
#in lude <sys/stat.h>
#in lude <unistd.h>
#in lude <string.h>
#in lude <gnome.h>
#in lude "support.h"
/* This is an internally used fun tion to reate pixmaps. */
stati GtkWidget* reate_dummy_pixmap
(GtkWidget *widget,
gboolean gnome_pixmap);
GtkWidget*
lookup_widget (GtkWidget *widget,
onst g har *widget_name)
{
GtkWidget *parent, *found_widget;
Mediul Glade 233
for (;;)
{
if (GTK_IS_MENU (widget))
parent = gtk_menu_get_atta h_widget (GTK_MENU (widget));
else
parent = widget->parent;
if (parent == NULL)
break;
widget = parent;
}
found_widget =
(GtkWidget*) gtk_obje t_get_data (GTK_OBJECT (widget),
widget_name);
if (!found_widget)
g_warning ("Widget not found: %s", widget_name);
return found_widget;
}
/* This is a dummy pixmap we use when a pixmap an't be found. */
stati har *dummy_pixmap_xpm[℄ = {
/* olumns rows olors hars-per-pixel */
"1 1 1 1",
" None",
/* pixels */
" ",
" "
};
/* This is an internally used fun tion to reate pixmaps. */
stati GtkWidget*
reate_dummy_pixmap (GtkWidget *widget,
gboolean gnome_pixmap)
{
GdkColormap * olormap;
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
GtkWidget *pixmap;
if (gnome_pixmap)
{
return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm);
}
olormap = gtk_widget_get_ olormap (widget);
gdkpixmap = gdk_pixmap_ olormap_ reate_from_xpm_d
(NULL, olormap, &mask,
234 Atelier de programare în reµele de al ulatoare
NULL, dummy_pixmap_xpm);
if (gdkpixmap == NULL)
g_error ("Couldn't reate repla ement pixmap.");
pixmap = gtk_pixmap_new (gdkpixmap, mask);
gdk_pixmap_unref (gdkpixmap);
gdk_bitmap_unref (mask);
return pixmap;
}
/* This is an internally used fun tion to reate pixmaps. */
GtkWidget*
reate_pixmap (GtkWidget *widget,
onst g har *filename,
gboolean gnome_pixmap)
{
GtkWidget *pixmap;
GdkColormap * olormap;
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
g har *pathname;
if (!filename || !filename[0℄)
return reate_dummy_pixmap (widget, gnome_pixmap);
pathname = gnome_pixmap_file (filename);
if (!pathname)
{
g_warning (_("Couldn't find pixmap file: %s"), filename);
return reate_dummy_pixmap (widget, gnome_pixmap);
}
if (gnome_pixmap)
{
pixmap = gnome_pixmap_new_from_file (pathname);
g_free (pathname);
return pixmap;
}
olormap = gtk_widget_get_ olormap (widget);
gdkpixmap = gdk_pixmap_ olormap_ reate_from_xpm
(NULL, olormap, &mask,
NULL, pathname);
if (gdkpixmap == NULL)
{
g_warning (_("Couldn't reate pixmap from file: %s"),
pathname);
g_free (pathname);
return reate_dummy_pixmap (widget, gnome_pixmap);
}
Mediul Glade 235
g_free (pathname);
pixmap = gtk_pixmap_new (gdkpixmap, mask);
gdk_pixmap_unref (gdkpixmap);
gdk_bitmap_unref (mask);
return pixmap;
}
/* This is an internally used fun tion to reate imlib images. */
GdkImlibImage*
reate_image ( onst g har *filename)
{
GdkImlibImage *image;
g har *pathname;
pathname = gnome_pixmap_file (filename);
if (!pathname)
{
g_warning (_("Couldn't find pixmap file: %s"), filename);
return NULL;
}
image = gdk_imlib_load_image (pathname);
g_free (pathname);
return image;
}
Fereastra prin ipal a interfeµei lient se poate vizualiza în �gura 14.2.
Figura 14.2: Interfaµa lientului on eput în Gtk folosind mediul Glade
Bibliogra�e
[1℄ D. Comer, Internetworking with TCP/IP, Vol. 1: Prin iples, Proto ols,
and Ar hite ture, Se ond Edition, Prenti e Hall, New Jersey, 1991.
[2℄ D. Comer, D. Stevens, Internetworking with TCP/IP, Vol. 3: Client�
Server Programming and Appli ations, Prenti e Hall, New Jersey, 1993.
[3℄ I. Ignat et al., Sistemul de operare UNIX. Gestionarea �³ierelor, Editura
Mi roInformati a, Cluj�Napo a, 1992.
[4℄ I. Ignat, A. Ka so, UNIX � Gestiunea pro eselor, Editura Albastr , Cluj�
Napo a, 1995.
[5℄ B. Kernighan, D. Rit hie, The C Programming Language, Se ond Edition,
Prenti e Hall, New Jersey, 1998.
[6℄ M. Ro hkind, Advan ed UNIX Programming, Prenti e Hall, New Jersey,
1985.
[7℄ D. Rusling, The Linux Kernel Referen e, 2001:
http://metalab.un .edu/pub/Linux/do s/LDP/linux-kernel/
[8℄ A. Silbers hatz, J. Peterson, P. Galvin, Operating Systems Con epts,
Addison�Wesley, Reading MA, 1992.
[9℄ R. Stevens, Advan ed Programming in the UNIX Environments, Addison�
Wesley, Reading MA, 1992.
[10℄ R. Stevens, UNIX Network Programming, Vol. 1: Networking APIs, Vol. 2:
Interpro ess Communi ations, Prenti e Hall PTR, New Jersey, 1998, 1999.
[11℄ R. Stevens, G. Wright, TCP/IP Illustrated, Vol. 1: The Proto ols, Vol. 2:
The Implementation, Vol. 3: TCP for Transa tion, HTTP, NNTP, and the
UNIX Domain Proto ols, Addison�Wesley Longman, 1994, 1995, 1996.
[12℄ L. Torvalds, The Story of the Linux Kernel, în Open Sour es: Voi es from
the Open Sour e Revolution (C. Dibona, M. Stone ³i S. O kman, ed.),
O'Reilly and Asso iates, 1999:
http://www.oreilly. om/ atalog/opensour es/book/linus.html
[13℄ * * *, Paginile web dedi ate a estui volum, Universitatea �Al.I. Cuza�,
Fa ultatea de Informati , Ia³i, 2001:
http://www.info.uai .ro/~lr /
Glosar
Antete
dirent.h, 41
error.h, 69
grp.h, 44
mysql.h, 201
n urses.h, 216
netdb.h, 177
netinet/in.h, 138
pwd.h, 42
rp /xdr.h, 189
signal.h, 72
stdio.h, 31
sys/so ket.h, 124
sys/stat.h, 38
sys/time.h, 162
sys/types.h, 55
unistd.h, 38
utmp.h, 45
Asin ronism, 122, 169
Bibliote i, 37
/lib, 17
rypt, 229
Gtk, 229
mysql lient, 201
n urses, 216
rp lib, 194
Comenzi, 13
bash, 27
bg, 58
at, 23
d, 17
hfn, 56
hmod, 21
p, 22
ut, 25, 27
fg, 58
�le, 23
�nd, 29
�nger, 26, 56, 98
g , 37, 194, 197
gdb, 37
grep, 27
halt, 71
head, 23
jobs, 58
kill, 71, 73
killall, 73
last, 45
less, 23, 74
ln, 22
ls, 16, 17, 49, 64
ltra e, 37
make, 145, 197, 230
man, 17, 18, 38
mkdir, 17
mk�fo, 92
more, 23
237
238 Atelier de programare în reµele de al ulatoare
mv, 22
ni e, 56
nohup, 58, 70
ps, 58, 65, 146
pwd, 17
reboot, 71
rm, 22
rmdir, 17
rp gen, 187, 195
set, 57
shutdown, 71
sort, 25, 27
stat, 24
stra e, 37
ta , 23, 35
tail, 23, 77
top, 58, 219
trap, 73
uniq, 25
w, 26
w , 23
who, 26, 45
Constante
AF_INET, 125
AF_UNIX, 125
EOF, 31
INADDR_ANY, 135
INADDR_NONE, 139
PF_INET, 125
PF_UNIX, 125
SIG_DFL, 73
SIG_ERR, 73
SIG_IGN, 73
SOCK_DGRAM, 125, 151
SOCK_RAW, 125
SOCK_STREAM, 125, 151
XDR_DECODE, 189
XDR_ENCODE, 189
Conversia datelor, 138
big endian, 138
gethostbyname(), 176
htonl(), 138
htons(), 138
inet_addr(), 139
inet_aton(), 139
inet_ntoa(), 139
little endian, 138
ntohl(), 138
ntohs(), 138
Datagrame, 151
Dire toare, 16, 17, 41
Exer iµii, 13, 47, 65, 77, 83, 97, 131,
146, 157, 177, 197
Expresii regulate, 28
Fi³iere, 14, 42
Des riptori, 31
I-nod, 16
Permisiuni, 19, 38
Fi³iere sistem, 16, 42
/dev/null, 16, 58
/dev/tty, 55
/et /group, 16, 44
/et /hosts, 17
/et /passwd, 16, 27, 42, 56
/et /proto ols, 16
/et /servi es, 16, 74, 135
/et /shadow, 27
/var/log/wtmp, 45
/var/run/utmp, 45
Modelul lient/server, 132, 151, 169,
199, 216, 229
Glosar 239
Multiplexare, 161, 178
Pipe, 15, 71, 78, 95, 97
Port, 135
Primitive
abort(), 74
a ept(), 124, 133, 135, 168, 169
a ess(), 38
alarm(), 71, 75
bind(), 124, 132, 134
hdir(), 42
hmod(), 38
hown(), 38
lose(), 31, 34, 48, 79, 124, 133,
136
onne t(), 124, 137
reat(), 31, 33, 79
dup(), 94
dup2(), 95
exit(), 56, 62
f ntl(), 169
fork(), 59, 69, 79, 146
getegid(), 62
geteuid(), 62
getgid(), 61
getpeername(), 139
getpgrp(), 61
getpid(), 61
getppid(), 61
getso kname(), 139
getso kopt(), 176
getuid(), 61
io tl(), 169
kill(), 73
link(), 38
listen(), 124, 133, 135
lseek(), 31, 33, 79, 94
mkdir(), 42
mk�fo(), 81
mknod(), 82
open(), 31, 32, 48, 79, 81, 94
pause(), 73
pipe(), 78, 129
raise(), 73
read(), 31, 33, 48, 79, 124, 136,
169
re eive(), 124
re v(), 136
re vfrom(), 151, 169
rename(), 38
rmdir(), 42
sele t(), 161, 162
send(), 124, 136
sendto(), 151
setgid(), 62
setpgrp(), 61
setso kopt(), 176
setuid(), 62
shutdown(), 124, 133, 136
signal(), 72
sleep(), 74, 77, 163
so ket(), 124, 132, 134, 151
so ketpair(), 129
stat(), 38
symlink(), 38
unlink(), 81
wait(), 61, 146
waitpid(), 61
write(), 31, 33, 48, 79, 124, 136
Pro es, 34, 55, 73, 97
Daemon, 17, 56, 135, 157
EGID, 56
EUID, 56
240 Atelier de programare în reµele de al ulatoare
GID, 27, 56
init, 55
lider, 55
PID, 55, 73
PPID, 55
UID, 26, 56
zombie, 56, 61, 146
Proto ol
ARP, 122
HTTP, 135
ICMP, 122
IP, 122
RARP, 122
SMTP, 135
TCP, 122, 125, 132
TCP/IP, 122, 132
UDP, 122, 125, 132, 151
Rezolv ri, 14, 48, 65, 84, 99, 146,
157, 178
RPC, 187
allrp (), 191�193
dispat her, 191
portmapper, 190
program la distanµ , 192
registerrp (), 192
rp gen, 193
stub, 187
sv _run(), 192
XDR, 188, 193
xdrmem_ reate(), 189
Semnal, 70, 73
SIGABRT, 74
SIGALRM, 71, 75
SIGBUS, 71
SIGCHLD, 71, 146
SIGFPE, 71
SIGHUP, 70
SIGILL, 71
SIGINT, 71
SIGIO, 71
SIGKILL, 71
SIGPIPE, 71, 147
SIGQUIT, 71
SIGSEGV, 71, 77
SIGTERM, 71
SIGURG, 71
SIGUSR1, 71
SIGUSR2, 71, 74
Server
on urent, 146, 164, 169, 178
iterativ, 132, 140, 151, 152
So ket, 15, 123, 124, 129, 132, 169
getpeername(), 139
getso kname(), 139
neblo ant, 169
so ketpair(), 129
Tipuri de date
DIR, 41
dirent, 41
fd_set, 161
FILE, 31
hostent, 176
in_addr, 134
passwd, 44
pid_t, 55
so kaddr, 134
so kaddr_in, 134
timeval, 162
utmp, 45
Tratarea erorilor, 49, 69, 177