s. buraga, g. ciobanu: 'atelier de programare în rețele de calculatoare' (2001)

237

Upload: sabin-buraga

Post on 13-May-2015

492 views

Category:

Technology


2 download

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

Page 1: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 2: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (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.

Page 3: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 4: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 5: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 6: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 7: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 8: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 9: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 10: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (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.

Page 11: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 12: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 13: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 14: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 15: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 16: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 17: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 18: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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. :

Page 19: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 20: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 21: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 22: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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):

Page 23: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 24: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 25: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 26: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 27: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 28: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 29: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 30: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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():

Page 31: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 32: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 33: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 34: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 35: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 36: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 37: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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",

Page 38: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 39: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 40: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 41: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 42: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 43: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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++;

Page 44: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 45: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 46: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

};

Page 47: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 48: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 49: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 50: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

Page 51: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 52: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 53: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 54: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 55: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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).

Page 56: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 57: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 58: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 59: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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,

Page 60: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 61: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 62: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 63: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 = ":";

Page 64: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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))

Page 65: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

}

Page 66: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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).

Page 67: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 68: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 69: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 70: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 71: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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()");

}

}

Page 72: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 .

Page 73: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

}

}

Page 74: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 75: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 76: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 77: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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℄);

Page 78: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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().

Page 79: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

Page 80: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 81: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 82: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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++;

}

}

Page 83: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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());

Page 84: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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℄);

Page 85: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 86: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

Page 87: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

}

Page 88: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 89: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 90: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 91: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 92: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 93: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 94: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 95: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 96: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 97: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 98: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 99: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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++;

}

Page 100: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 101: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

{

Page 102: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 103: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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));

Page 104: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

Page 105: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 106: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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++)

Page 107: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 108: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 109: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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))

{

Page 110: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 111: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 112: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 113: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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℄);

Page 114: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 115: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 116: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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):

Page 117: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 118: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 119: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 120: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 121: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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):

Page 122: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 .

Page 123: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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. */

Page 124: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 125: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 126: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 127: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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()");

Page 128: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 129: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 130: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 131: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

}

Page 132: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 133: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 134: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 135: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 136: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 137: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

Page 138: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 139: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 140: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 141: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 142: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 143: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 144: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 145: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

}

Page 146: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 147: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

}

}

Page 148: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 149: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 150: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

}

Page 151: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 152: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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℄);

Page 153: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 154: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 ()

Page 155: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 156: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 "

Page 157: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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");

}

}

Page 158: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 159: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 160: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 161: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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>

Page 162: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 163: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 164: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 165: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 166: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 167: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 168: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 169: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

}

}

Page 170: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 171: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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... */

Page 172: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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()");

}

Page 173: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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) */

Page 174: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 .

Page 175: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 176: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 177: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 178: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 179: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 180: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 181: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 182: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

Page 183: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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';

}

Page 184: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 185: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 .).

Page 186: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 187: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 188: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 189: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 190: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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. .

Page 191: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 192: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 193: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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℄);

Page 194: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 195: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 196: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 197: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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:

Page 198: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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,

Page 199: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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))

{

Page 200: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 201: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 202: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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));

Page 203: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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' "

Page 204: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

}

Page 205: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 206: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 207: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 208: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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));

Page 209: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 210: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

Utilizarea bibliote ii MySQL 213

RESULT modi( har *student, har *grupa,

int nota, int absenta, int sapt, har *profesor);

har* id( har *prof);

Page 211: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 212: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 213: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 214: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 215: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 216: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 217: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 218: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 ();

Page 219: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 220: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

Page 221: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 222: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

}

Page 223: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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)

{

Page 224: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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\

Page 225: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 */

Page 226: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 227: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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.

Page 228: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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),

Page 229: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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;

Page 230: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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,

Page 231: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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);

}

Page 232: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 233: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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 /

Page 234: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 235: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 236: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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

Page 237: S. Buraga, G. Ciobanu: 'Atelier de programare în rețele de calculatoare' (2001)

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