Sunteți pe pagina 1din 239

SABIN BURAGA, GABRIEL CIOBANU

ATEL IER DE PROGRAMARE


N REELE DE CALCULATOARE

,J

LINU X

'<.

Sabin Buraga este asistent universitar la Facultatea de Informatic, Universitatea Al.I. Cuza".
Dintre domeniile sale de interes se enumer tehnologiile Web, reelele de calculatoare i sistemele
de operare.
Gabriel Ciobanu este
Romn, filiala lai.

cercettor

principal I la Institutul de

Informatic Teoretic,

2001 by Editura POLIROM

http: //www.polirom.ro
Editura POLIROM
Iai, B-dul Copou nr. 4, P.O. BOX 266, 6600
Bucureti, B-dul l.C. Brtianu nr. 6, et. 7

Descrierea CIP a Bibliotecii

Naionale:

BURAGA, SABIN
Atelier de programare n refele de calculatoare I Sabin Buraga, Gabriel Ciobanu
Iai, Polirom, 2001
240 p.; 24 cm

ISBN:

973-6~3-755-6

I. Ciobanu, Gabriel

Printed in ROMANIA

Academia

Sabin Buraga, Gabriel Ciobanu

llllllllllll Illlllll l\11


POLIROM
2001

B.~~~~~~ASI

Dedicat

din

crfile

lui W. Richard Stevens (1951-1999)


cruia se pot nvfa attea lucruri interesante.

Cupri ns
Mulumiri

Prefa

11

Introducere
1.1 UNIX i Linux
1.2 Comenzi uzuale
1.2.1 Exerciii
1.2.2 Rezolvri

13
13
13
13
14

Gestiunea fiierelor
2.1 Prelucrarea fiierelor
2.1.1 Primitiva open()
2.1.2 Primitiva read()
2.1.3 Primitiva wri te O
2.1.4 Primitiva lseek()
2.1.5 Primitiva close O
2.1.6 Exemplu . . . . . .
2.2 Prelucrarea atributelor fiierelor .
2.2.1 Exemplu . . . . . . . . .
2.3 Prelucrarea directoarelor . . . . .
2.4 Prelucrarea fiierelor de sistem
2.4.l Gestiunea conturilor de utilizatori
2A.2 Gestiunea grupurilor de utilizatori
2.4.3 Gestiunea sesiunilor de lucru
2.5 Exerciii .
2.6 Rezolvri

31
31
32
33
33
33
34
34
37
39
41
42
42
43
44
46

Procese
3.1 Noiuni fundamentale
3.2 Comenzi pentru procese

47
54

54
56
5

3.3
3.4
3.5
3.6

Procesele din perspectiva programatorul ui


Exemplu.
Exerciii .
Rezolvri

58
62
64
64

, 4 ) Semnale
4.1 Prezentare general . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Manipularea semnalelor . . . . . . . . . . . . . . . . . . . . . .
4.2.1 Definirea unui anumit comportamen t la apariia unui
semnal .
. .
4.2.2 Trimiterea unui semnal unui proces .
4.2.3 Ateptarea unui semnal
. .
4.2.4 Suspendarea execuiei unui proces
4.3 Exemplu .
4.4 Alarme. . . . .
4.4.1 Exemplu .
4.5 Exerciii . . . .

69
69
71

5 Comunicare a ntre procese. Pipe-uri


5.1 Preliminarii
..
5.2 Primitiva pipe O
5.2.1 Exemplu .
5.3 Primitiva fifo()
5.3.1 Exemplu .
5 .4 Exerciii
5.5 Rezolvri

77

6 Duplicarea descriptorilo r. Redirectri


6.1 Duplicarea descriptorilor
6.1.1 Exemple .
6.2 Exerciii
6.3 Rezolvri

93

Interfaa

7.1
7.2

7.3

socket
Preliminarii
. .
Interfaa socket
. . . .
7.2.1 Primitiva socket O .
7.2.2 Exemplu . . . . .
7.2.3 Primitiva socketpair()
7.2.4 Exemplu .
Exerciii . .
. ...

71
72
72
73
73
74
75
76

77
77
79
80
81
82
83

93

94
96

98
120
120
122
123
125
128
128
130

8 Modelul client/server - TCP


8.1 Modelul client/server ..
8.2 Server TCP iterativ . . . .
8.2.1 Primitiva bind() ..
8.2.2 Primitiva listen () .
8.2.3 Primitiva accept() .
8.2.4 Primitivele send() i recv()
8.2.5 Primitivele close () i shutdown()
8.3 Client TCP . . . .
8.4 Conversia datelor .
8.5 Alte primitive utile
8.6 Exemplu .
8. 7 Exerciii .
8.8 Rezolvri

131
131
131
133
134
134
135
135
136
137
138
139
145
145

9 Modelul client/server - UDP


9.1 Datagrame
9.2 Exemplu .
9.3 Exerciii .
9.4 Rezolvri

150
150
151
156
156

10 Multiplexarea intrrilor /ieirilor


10.1 Primitiva select()
10.2 Exemple . . .
10.3 Asincronism .
10 .4 Exerciii
10.5 Rezolvri . .

160
160
161
168
176
177

11 RPC - Apelul procedurilor la distan


11.1 Sistemul RPC . . . . . . . . . . . . . .
11.2 Cum lucreaz sistemul RPC? . . . . .
11.3 Scrierea programelor server i client RPC
11.4 Exemplu .
11.5 Exerciii . . . . . . . . . . .

186
186
189
190
194
196

12 Utilizarea bibliotecii MySQL


12.1 Formularea problemei . . .
12.1.1 Schi de rezolvare
12.2 Accesarea serverului MySQL .

198

198
198
200

13 Biblioteca ncurses
13.1 Descriere general . . . . . . . . . . . . . .
13.2 Prezentarea funciilor bibliotecii ncurses . _.
13.3 Enunul unei probleme . . . .
13.3.1 Rezolvarea problemei .

214
214
214
217
217

14 Mediul Glade
14.1 Formularea i rezolvarea problemei . . . . . . .
14.2 Conceperea interfeei . . . . . . . . . . . . . . .
14.2.1 Utilizarea mediului de dezvoltare Clade

227
227
227
227

Bibliografie

234

Glosar

235

Mulumiri

!l
4

4
7
7
7

7
7
7

Mulumim

tuturor studenilor notri care au dat dovad de cele mai multe


ori de devotament, pricepere i perseveren. Ei i-au depit adesea profesorii.
Acest lucru ne-a oferit mare satisfacie i a atras admiraia noastr. Am avut de
nvat multe lucruri de la studenii notri, iar unele dintre soluiile prezentate
n cartea de fa le aparin. Acest lucru reprezint o recunoatere a calitilor
lor i vrea s sublinieze c ntr-o instituie academic studenii buni reprezint
de fapt sngele care oxigeneaz ntreg organismul.

Se cuvin menionai Manuel ubredu, Iulian Videanu, Daniel Dumitriu,


Ioana Matei, Oana Captarencu, Olivia Oanea pentru contribuiile la unele
programe prezentate n volum, precum i Sinic Alboaie, Radu Filip, Victor
Tarhon-Gnu pentru observaiile lor asupra coninutului.

Prefa

Lucrarea de fa este inspirat de laboratoarele pe care autorii le-au susinut n


ultimii ani cu studenii Facultii de Informatic de la Universitatea "Alexandru Ioan Cuza" din lai. Aceste laboratoare au fost dedicate programrii n
reele de calculatoare utiliznd interfaa socket BSD {Berkeley System Distribution}, limbajul C i sistemul de operare Linux (UNIX).
Cartea se adreseaz studenilor din anii 2, 3 i 4 din facultile care predau
informatic, elevilor buni i profesorilor din liceele care predau informatic,
tinerilor absolveni care au nevoie de cunotine de programare n reele, dar
care nu au urmat un astfel de curs n timpul studiilor. La multe faculti exist
acum cursuri obligatorii de reele de calculatoare, iar cunotine de programare
n reele de calculatoare se solicit explicit la examenele de licen.
Cartea reprezint doar partea de laborator a cursului de programare n
reele de calculatoare prezentat la Facultatea de Informatic din lai. Cursul
propriu-zis va aprea n curnd ca volum separat i va oferi celor interesai
mai multe amnunte legate de colecia de protocoale TCP /IP i de interfaa
socket BSD. Dei volumele au fost gndite a se susine reciproc, fiecare volum
poate fi citit separat.
Acest volum insist asupra aspectelor legate de dezvoltarea unor programe
de comunicare ntre procese sub sistemul de operare Linux (UNIX). Dezvoltarea unor programe eficiente pentru reelele de calculatoare. presupune
cunoaterea sistemului de operare cu care se lucreaz. Primele capitole ale crii
se refer la acele aspecte din Linux (UNIX) care sunt utile n acest sens. Pentru
comunicarea ntre procese pe acelai calculator am utilizat mecanisme tipice
sistemului de operare UNIX (pipe-uri, semnale, duplicarea descriptorilor), iar
pentru comunicarea ntre procese aflate pe calculatoare diferite conectate n
reea s-a recurs la interfaa socket i modelul client/server, prin intermediul
protocoalelor TCP i UDP, n manier iterativ i concurent. Se prezint multiplexarea intrrilor i ieirilor cu ajutorul primitivei select(). Un capitol este
dedicat sistemului RPC (Remote Procedure Call), iar altul descrie modul de
dezvoltare a aplicaiilor n reea folosind serverul de baze de date MySQL. Ultimele capitole ofer exemple de aplicaii interactive folosind biblioteca ncurses
i mediul de dezvoltare Clade.
Un obiectiv ceva mai dificil de ndeplinit pentru un astfel de volum este de
a oferi aplicaii mai mari i mai complexe care s ghideze cititorul n realizarea
unor proiecte mai ambiioase. Sperm c unele dintre exerciiile rezolvate n
volum i vor ncuraja pe cititori s dezvolte aplicaii care s le furnizeze satisfacia unui lucru deosebit i bine fcut. De fapt volumul este scris cu gndul
la sentimentul special de mulumire pe care-l are un programator atunci cnd

'"

realizeaz c o aplicaie n care a ndrznit lucruri noi funcion


eaz. Este un
sentin1ent pe care sperm s-l ncerce ct mai muli dintre cititorii acestei cri.
Autorii nu pot dect s spere c lucrarea ar putea constitu i o iniiere util
n program area reelelor de calculato are care s contribu ie la formare a unei
culturi profesionale adecvate cerinelor software actuale.
Autorii tiu (relativ la cartea lor) i atenioneaz cititorul (relativ la programele sale) c ntotdea una se poate i mai bine. Pentru a exprima observaii
utile, pentru a fi informa t despre adugiri, corecii, versiuni mbuntite ale
unor program e i alte informaii legate de acest volum, autorii ncurajeaz
cititorul s foloseasc adresa e-mail lrcinf o. uaic. ro, precum i pagina web
http://w ww.inf o.uaic. ro/-lrc.

Autorii
iulie 2001

Iai,

Capitol ul 1

Intr oduc ere


Acest capitol prezint succint principiile de baz ale
sistemului de operare UNIX (Linux) i descrie, prin intermediul unor exerciii rezolvate, cteva dintre comenzile
utile puse la dispoziia utilizatorului.

1.1

UNIX

Linux

Sistemul de operare UNIX este un sistem multi-util izator i multi-task ing. Un


arbitrar de utilizatori poate avea acces la resursele calculator ului pe
care este instalat sistemul n orice moment de timp de la diferite terminale
plasate local sau la distan, putnd rula n manier concurent mai multe
numr

aplicaii.

Una dintre variantele free-software, de succes, ale UNIX-ului este Linux,


sistem pe care l vom utiliza n continuare. Exemplele din aceast carte au fost
testate pe distribuiile Linux RedHat 6.2 i 7.0.
Linux reprezint un sistem multi-task ing i multi-use r compatib il UNIX pe
32/64 de bii, respectn d standardu l POSIX, oferind utilizatori lor acces la cele
mai populare utilitare GNU i posed o interfa grafic bazat pe XWindow
System.
Printre alte faciliti, Linux ofer suport pentru reea, implemen tnd suita
de protocoal e TCP /IP, i un mediu complet de dezvoltare a aplicaiilor n
diverse limbaje de programa re (e.g. C, C++, Perl, Tcl/Tk, Python).

1.2

Comen zi uzuale

Reaminti m o parte dintre cele mai utilizate comenzi UNIX (Linux) prin intermediul ctorva...

1.2.1

Exerciii

1. S se afieze numai intrrile de (sub)direc toare din directorul curent.

2.

Exist
Dac

vreo deosebire ntre efectele comenzilor de mai jos?


da, explicai motivul.

14

Atelier de programar e n

Precizai

3.

care este

diferena

reele

de calculatoa re

ntre:

cat < file


cat file
file cat

4. Ce efect are

urmtoarea

linie de comenzi?

echo' who I cut -c1-9 I sort I uniq >> users

5. Ce anume

realizeaz

linia de comenzi de mai jos?

cut -d: -f1,3 /etc/pass wd I sort -t: +O -1

6.

7.

se

afieze toi

utilizatori i care au conturi terminate n litera "t''.

se scrie, ntr-o singur linie de comenzi, irul de comenzi care


dac exist peste 20 de sesiuni deschise i n caz afirmativ s se
un mesaj prin pota electronic administr atorului de sistem.
S

se gseasc toate fiierele temporar e (al


. bak sau cu caracteru l -) i s se tearg.

8.

9.

se scrie linia de comenzi pentru generarea unui


calea tuturor fiierelor sursa C din sistem.

10.

cror

se compare

execuia

cient?):

celor

dou

nume se

verific
trimit

termin

fiier coninnd

cu

numele

linii de comenzi (care este mai efi-

cat I wc
(cat </dev/tty >/tmp/f

1.2.2

wc </tmp/f

/bin/rm -f /tmp/f) &

Rezolvri

1. Pentru a putea da rspunsul, trebuie s revedem pe scurt cteva aspecte


referitoar e la gestiunea fiierelor n UNIX.
Fiierele

sunt grupate n sisteme de fiiere partajnd spaiul de stocare


(hard-disk -uri, CDROM- uri, floppy-uri, uniti de band etc.). Orice sistem de fiiere ndeplinete funcii precum:

Introducere
stocarea

persistent

numirea

informaiilor

informaiilor,

15

(servicii de directoare),

protecia i folosirea n comun a informaiilor (prin intermediul unui


mecanism de comunicaie inter-proces),

interfaa

interfaa universal

ntre structurile interne de date ale nucleului sistemului


utilizator,

Sistemul de

fiiere

structura

la resursele

reelei.

n Linux este caracterizat prin:

logic arborescent

a directoarelor,

tratarea dispozitivelor (terminale, imprimante, memorie etc.), n


manier unitar i consistent, ca fiiere (n sensul conceptual al
termenului),

protecia

resurselor prin intermediul unui sistem de permisiuni.

n UNJ;X i n particular Linux fiierele pot fi de mai multe tipuri:


ordinare

(obinuite) coninnd

date, programe sau cod

main,

directoare (privite ca fiiere, ele stocheaz alte fiiere sau directoare,


n mod recursiv),
speciale: dispozitive periferice ( e.g. mouse, imprimant, modem),
pipe-uri (FIFO-uri, utilizate n comunicaia ntre procese), socluri
(socket-uri, folosite n cadrul comunicaiei n reea),

legturi simbolice: shortcut-uri ctre un fiier sau director, pentru


a putea fi mai uor de regsit sau accesat; astfel, un fiier poate fi
regsit n cadrul sistemului de fiiere prin mai multe nume, eventual
n directoare diferite. Legturile simbolice, spre deosebire de alte
sisteme de operare, sunt implementate la nivel de sistem de fiiere
i nu n cadrul interfeei cu utilizatorul.

Sistemele de fiiere UNIX sunt destul de multe, n Linux putndu-se folosi


cel puin cincisprezece ( e.g. ext, ext2, minix, msdos, vfat, proc, iso9660,
sysv, hpfs), cel mai popular fiind ext2 (Second Extended File System)
care a aprut n anul 1993.

16

Atelier de program are n

reele

de calculat oare

I-nodu ri

n UNIX, numrul de fiiere care se pot afla la un momen t dat pe un sistem de fiiere este limitat precis, nc de la crearea sistemu lui de fiiere.
Fiecare fiier posed un i-nod ( i-node) care conine aproap e toate informaiile legate de acel fiier, except nd coninutul
i numele. Unele
i-nodur i sunt nefolosite la un momen t dat.
Cele mai multe cmpur i ale unui i-nod pot fi vizualizate cu ajutoru l
comenzii ls (despre ls vom discuta mai detaliat mai trziu n cadrul
acestui capitol) .
Coman da ls are ca argume nte nume de fiiere, nu i-noduri, dar cum
fiecare fiier are un singuri -nod, ls va cunoate de unde s ia informaiile.
Pentru a vedea ce i-nod corespu nde unui
De exemplu:

fiier

se poate da ls -i.

(infoia si):-$ ls -i *.cls


52227 oldthe sis.cls
52228 wrnthe sis.cls

Struct ura de directo are


Orice sistem de tip UNIX are structu ra standar d de directo are
/ directo rul

rdcin

urmtoare:

(directoarele vor fi delimit ate de caracte rul slash);


/bin comenzile externe uzuale ale sistemului;
/ dev dispozitivele sistemului (att cele periferice, ct i cele logice).
Aceste fiiere speciale nu au lungime, ci o succesiune de dou numere
desemn nd numrul major i minor al dispozitivului asociat. De
exemplu, /dev/h da desemneaz primul hard-di sk IDE, /dev/hd a1
desemneaz prima partiie a primulu i hard-di sk IDE,
/dev/s da desemneaz primul hard-di sk SCSI, iar /dev/lp 1 desemn
eaz primul
port paralel i aa mai departe . De asemenea, exist un dispozi tiv
virtual /dev/n ull care joac rolul de "gaur neagr" a sistemului,
orice am scrie ctre acest dispozitiv pierzn du-se definitiv.
/etc utilitar e de admini strare i fiiere de configurare. Se pot meniona:
I etc/pas swd stocheaz informaii despre utilizatori;
I etc/ group stocheaz informaii despre grupuri le de utilizatori;
/etc/se rvices d informaii despre serviciile de reea suporta te;
/etc/p rotoco ls furnizeaz informaii despre protocoalele de reea
suporta te;

17

Introducere

I etc/hosts
care

s8.

conine

activeaz

lista numelor
sistemul.

mainilor

din

/usr anumite fiiere utilizate de membrii sistemului.


subdirectoarele acestui director:

reeaua local

Iat

o parte dintre

1-

le

n
~-

sbin executabile i daemoni suplimentari;


lib biblioteci folosite de diverse programe;
local director coninnd aplicaii cu caracter local;
include fiiere antet (header) necesare dezvoltrii de
man fiierele de date pentru comanda man.
/lib biblioteci
latoare;

module partajabile utilizate de

/boot ncrctorul sistemului (aici se


cleului);

gsete i

aplicaii

aplicaii

C;

sau de compi-

imaginea

binar

a nu-

/tmp datele temporare generate de anumite comenzi sunt stocate aici;


/home directoarele de lucru pentru fiecare utilizator n parte;
/mnt director de montare a unor sisteme de fiiere externe
CD-ROM etc.);
/var

(partiii

FAT,

o serie de fiiere log completate de sistem (/var/log/),


plus cozi de ateptare (n directorul /var I spool/) pentru e-mail
(lvar/spool/mail/), imprimant, comunicaii seriale, gestionarea
timpului etc.;
conine

/proc gzduiete sistemul virtual de fiiere proc i conine cte un subdirector pentru fiecare proces existent, plus date despre conexiunile
de reea. Acest sistem virtual de fiiere reprezint principala modalitate prin care nucleul "afl" informaii despre starea sistemului i
a proceselor.
Comenzile pentru prelucrarea directoarelor sunt:
mkdir path -

creeaz

an director

rmdir path - terge un director gol, n sensul c el nu conine


dect intrrile " . " i " .. " semnificnd directorul nsui i directorul
printe, respectiv;
cd [ path ] pwd -

afieaz

schimb

directorul curent de lucru n cale;

numele directorului curent;

ls [ optiuni ] [ path ] -

listeaz coninutul

unui director.

18

Atelier de programare n
Argume ntul path
slash.
O

reprezint

reele

de calculatoare

o cale de directoa re separate de caracter ul

comand

poate avea un numr variabil de p(3,rametri (argume nte) i de


prefixate de caracter ul "-" (se poate utiliza i varianta explicit
a unei op i uni, prefixat de caractere le " - - ").
opiuni

Cum majorita tea comenzilor pot accepta un numr impresio nant de opcel mai bine este s consultm manualu l oferit de sistem. Acest
lucru se realizeaz prin intermed iul comenzii man. Manualu l sistem este
structur at pe seciuni, acestea fiind:
iuni,

1 pentru comenzi ale sistemului (e.g. cele de mai sus);


2 pentru apeluri sistem ( i. e. open());
3 pentru

funcii

de

bibliotec

( i. e. fprintf ());

4 pentru descrieri ale dispozitivelor I/O;


5 pentru formate de fiiere;
6 pentru jocurile furnizate de sistem;
7 pentru descrieri ale

fiierelor

speciale;

8 pentru procese ale sistemului.


Astfel, pentru a afia pagina de manual corespunztoare comenzii mkdir
vom da man mkdir sau man 1 mkdir, iar pentru a vedea pagina de manual referitoa re la apelul mkdirO vom introduc e man 2 mkdir.
Dac ntmpinai dificulti, putei afia pagina
reeta man man. Alte comenzi nrudite cu

urmnd
whatis.

de ajutor pentru man


man sunt apropos sau

Vom putea utiliza ls pentru a, rezolva primul exerciiu. Dnd man ls,
constatm c pentru a vizualiza doar numele subdirec toarelor
coninute
de directoru l curent putem folosi opiunea "-d".
Alte

opiuni

-a

afieaz

-1

listeaz

-t

listeaz

utile ale comenzii ls sunt:

toate fiierele (chiar i cele al


n mod normal sunt invizibile);

cror

nume ncepe cu " . " care

n format lung (permisiuni, numr de blocuri ocupate, numele propriet arului, numele grupului , lungime a n bytes, data ultimei modificri, numele fiierului);
sortat

dup

data

modificrii;

Introducere

19

-R afieaz recursiv toate (sub)directoarele (aceast opiune va putea fi


folosit

.e

)-

;t

la multe comenzi UNIX) .

Fiecare fiier aparine cuiva. Un identificator (UID) numeric asociat celui


care posed fiierul (de obicei cel care l-a creat) este stocat n cadrul inod ului. Al treilea cmp de pe o linie listat de ls -1 indic utilizatorul.
Utilizatorii, n UNIX, fac parte din unul sau mai multe grupuri. Fiecare
fiier va aparine i el unui anume grup.

e
(infoiasi):-$ ls -1 /home/lrc
total 8
users
drwxr-xr-x
5 lrc
users
lrc
2
drwx-----proprietar

4096 Mar 7 18:35 Desktop


4096 Apr 22 23:00 mail

grup

Drepturi de acces (permisiuni)


Fiecare fiier are asociate anumite drepturi de acces, numite i permisiuni,
pe care le putem vedea n coloanele 2-10 din ceea ce afieaz ls -1.
Drepturile sunt stocate sub forma a trei triplete de bii. Primul triplet
arat ce poate face cu fiierul posesorul lui (un proces al unui utilizator
cu UID egal cu cel al fiierului), al doilea arat ce pot face utilizatorii
din grupul fiierului, iar al treilea triplet arat ce poate face restul lumii
(ali utilizatori care nu aparin grupului fiierului).
Cele trei drepturi asociate celor trei categorii (utilizator, grup,

alii)

sunt:

r citire (Read);
w scriere (Wri te);

execut

(eXecute).

Ele arat dac un fiier poate fi citit, modificat sau respectiv executat
(pentru fiiere de tip director, poate fi cutat) de persoana respectiv.
Ca exemplu concret

s lum

un

fiier

r-x-w---x

\_/\_/\_/
\_ restul lumii (others)
\__ grupul
(group)
\ ___ posesorul
(user)
\

\
\

avnd permisiunile:

20

Atelier de program are n

Acest lucru

de calculat oare

nseamn c:

posesorul
modifica;

fiierului

poate citi

utilizat orii din grupul


pot citi sau executa;

reele

fiierului

executa acest

fiier,

pot modifica acest

ceilali utilizat ori pot doar executa


modifica.

fiierul,

dar nu l poate
fiier, ns

nu l

dar nu l pot citi sau

De asemenea, fiierele mai au trei atribut e speciale (bii). Acestea se


numesc: bitul SUID, bitul SGID i bitul STICK Y (lipicios).
Semnificaia

acestor a este

urmtoarea:

bitul SUID (Set User ID): un fiier cu acest bit setat arat faptul
c execuia acestui fiier d natere unui proces care
are proprie tar
pe posesorul fiierului, i nu pe posesorul procesului care execut
fiierul (vezi i capitolu l 3). Un bit SUID setat este indicat
de comanda ls -1 printr-u n "s" n loc de "x":
(infoia si):-$ ls -1 /usr/bi n/chfn
-rws--x --x 1 root root 13184 Aug 31

2000 /usr/bi n/chfn

Cnd utilizat orul busaco va executa acest fiier, procesul va avea


UID root, i nu busaco , din cauza bitului SUID.
bitul SGID (Set Group ID) are o funcie
grupulu i procesului.

similar, acionnd

asupra

Aceti

doi bii nu au sens dect dac fiierul este executabil. Pentru


care nu sunt executabile, ei sunt folosii pentru a indica faptul
c utilizar ea fiierului se poate face numai dup blocare
a ( lock) lor.
Aceti bii se vd cu litere mari "S".

fiiere

bitul Sticky (lipicios), notat cu "t" are

urmtoarea semantic:

la fiiere executabile iniial acest bit indica nucleului s optimizeze folosirea fiierului: odat executa t (deci citit n memorie) va fi pstrat n memorie, chiar dac procesul se termin,
pentru c probab il va fi reapela t din nou, n curnd.
pentru directo are indic faptul c n directoarele n care oricine
poate scrie ( e.g. /tmp), teoretic oricine poate terge orice fiier.
Un bit lipicios pe un astfel de director va permite ns tergerea
unui fiier doar de ctre proprie tarul lui.

21

Introducere

Un exemplu (opiunea -d a comenzii ls este necesar pentru


a lista drepturile directorului /tmp i nu ale tuturor fiierelor
cu prinse n el) :

(infoiasi):-$ ls -ld /tmp


3 root root 1024 Jul 26 00:10 /tmp/
drwxrwxrwt
bit sticky

Mai trebuie spus c orice operaie de deschidere a unui director pentru


cutare este precedat de verificarea dreptului de a face aceast cutare.
Exist dou tipuri de drepturi pentru directoare:
bitul "r" ( read) la un director arat dreptul de a citi coninutul
directorului (un tablou de intrri), deci dreptul de a afla care i-nod
corespunde la un nume;
bitul "x" (search) indic dreptul de a citi coninutul unui i-nod indicat de o legtur din acest director.
un director este accesibil, are de obicei drepturile r-x. Dac are
numai drepturile r--, atunci se poate afla ce fiiere conine, dar ele nu
pot fi deschise (nici i-nodurile lor nu pot fi accesate). Putem executa
ls sau ls -i, ns nu ls -1 pe un astfel de director. Dac are numai
drepturile --x nu putem vedea ce fiiere conine, dar dac tim unul
dintre nume, l putem deschide.

Dac

Pentru a modifica permisiunile asociate fiierelor vom utiliza comanda


chmod. Drepturile de acces vor putea fi date fie simbolic (spre exemplu
chmod +ux proiect care seteaz dreptul de execuie pentru proprietar),
fie numeric (trei cifre n baza opt). Tabela 1.1 rezum valorile octale care
pot fi folosite pentru alterarea drepturilor de acces.
Tabela 1.1: Valorile octale pentru drepturile de acces folosite de chmod

I Categorie I

User
Group
Others

400
40
4

200
20
2

100
10
1

De exemplu, pentru ca fiierul program. c s poat fi citit i scris de


posesor, s poate fi citit de grup i s poat fi executat de alii, vom da
chmod 641 program.c:

Atelier de programare n

22

reele

de calculatoare

(infoia si):-$ chmod O program .c


(infoia si):-$ ls -1 program .c
1 lrc
lrc
14802 Jun
(infoia si):-$ chmod 641 program .c
(infoias i) :-$ ls -1 program .c
-rw-r-- --x
1 lrc
lrc
14802 Jun

9 16:34 program .c

9 16:34 program .c

2. Putem trece astfel la rezolvarea celui de-al doilea exerciiu. Pentru aceasta,
vom reaminti cele mai populare comenzi de lucru cu fiierele:
cp file1 file2 - copie

fiiere;

mv file1 file2 - mut/redenumete fiiere;


ln file1 file2 - permite unui fiier s aib un nume complemen-

tar (mai multe nume de fiiere pot desemna acelai fiier; legturile
pot fi hard (se creeaz i o copie a coninutului fiierului) sau soft
(legtura simbolic va conine doar numele ctre fiierul surs);

rm file(s) - terge fiiere (atenie! fiierele terse nu pot fi recu-

perate n nici un mod!).

Pentru toate comenzile de mai sus,

exist

o serie de

opiuni

folositoare:

-f foreaz ndeplin irea aciunii, fr confirmare din partea utilizato rului

sau ignornd erorile care pot surveni;


-i mod interacti v, interog nd utilizato rul dac ntr-adevr dorete
realizeze ceea ce s-a specificat;
-v afieaz mai multe informaii la execuia comenzii respective;
-R mod recursiv, executn du-se asupra tuturor subdirectoarelor.

n cadrul numelui unui fiier putem specifica aa-numitele metacar actere


( wildcard s) care pot nlocui o serie de caracter e. Cele mai des folosite
sunt:

substitu ie zero, unul sau mai multe caractere;


? substitu ie un singur caracter , pe poziia pe care apare;
[ ... ] substitu ie un caracter aparinnd unui grup de caractere.
Caracter ul - ( t'ilda) poate fi folosit pentru a substitu i directorul home
(propriu) al utilizatorului.
n acest moment putei da rspunsul la exerciiul propus, fr a executa
efectiv cele patru forme ale comenzii rm.

Introducere

23

3. Pentru a rezolva acest exerciiu trebuie s vedem care sunt comenzile


pentru prelucrarea coninutului fiierelor:
cat file (s) - afieaz coninutul fiierelor specificate. n fapt, co-

manda are drept scop primar concatenarea mai multor

fiiere;

more file (s) - afieaz paginat coninutul fiierelor (se iese cu : q);
less file (s) - similar cu more, dar permite parcurgerea

a,

fiierului

n ambele sensuri (un ecran n sus cu "p", un ecran mai jos cu spaiu,
cutarea unui ir cu "/");
tac file (s) - analog cu comanda cat, dar afieaz de la sfrit
ctre

nceput

wc file(s) -

e
r.i

fiierul;
afieaz numrul

de caractere, cuvinte i linii ale unui

fiier:

(infoiasi):-/tmp$ wc list_users
143
1218
8096 users_list

head file(s) - afieaz primele n linii (implicit 10) dintr-un fiier;


tail file(s) - afieaz ultimele n linii (implicit 10);
file file(s) - afieaz tipul fiierelor (executabil, text, script,
arhiv

etc.):

(infoiasi):-/tmp$ file*
datafiles:
gaend:

"

directory
ELF 32-bit LSB executable,
Intel 80386, version 1
go:
ASCII text
helpfiles:
directory
directory
hintfiles:
lastgaen.zip: Zip archive data, at least v1.0 to extract
mailspool:
directory
motd1:
AS,CII text
sabeav.zip:
Zip archive data, at least v2.0 trr extract
syslog.corn:
English text
syslog.link: ASCII text
userfiles:
directory
users_list:
ASCII text
(infoiasi):/dev$ file tty
tty:
character special

24

Atelier de programare n

stat file (s) -

afieaz

reele

diverse

de calculatoare

informaii

despre

fiiere:

(infoiasi) :/$ stat File: "/home/lrc "


Size: 2048
Filetype: Directory
Mode: (0755/drwx r-xr-x)
Uid: (563/lrc) Gid: (202/lrc)
Device: 8,0
!node: 79576
Links: 13
Access: 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 caracteristic a interpretoru lui de comenzi (shell-ul UNIX) este aceea


de a permite utilizatorul ui s redirecteze intrarea sau ieirea unui program
(comenzi). Implicit, fiecare program citete date de la intrarea standard
(tastatura) i scrie rezultatele prelucrrii la ieirea standard (terminalul ).
Mesajele de eroare sunt trimise unui dispozitiv standard de eroare care
n mod normal este terminalul. Pentru a redirecta intrarea/ieirea vom
folosi operatorii:

< redirecteaz intrarea standard (n loc de


dintr-un

>

fiier

tastatur

vor fi folosite datele

specificat).

redirecteaz ieirea

ntr-un

fiier

standard (n loc de terminal datele vor fi scrise


care dac exist va fi suprascris).

ca mai sus, dar datele vor fi

adugate

la

sfritul fiierului, dac

ist).

ieirea

ex-

unei comenzi va fi intrare pentru alta.

Astfel, putem furniza

soluia

la

exerciiul

Forma cat file va afia coninutul


exist stocat n directorul curent.

propus:
fiierului

cu numele file,

dac

Forma cat <file va avea acelai efect, folosindu-se redirectarea i


proprietate a comenzilor UNIX de a citi de la dispozitivul stand~
de intrare datele n cazul n care nu se specific numele unui fiier
ca argument n linia de comand.
Ultima

form

dac exist

file cat va preciza tipul


stocat n directorul curent.

fiierului

4. Avem de-a face cu o linie de comenzi aparent mai

cu numele cat,

complicat:

echo ' who I cut -c1-9 I sort I uniq ' >> users

25

Introducere
Pentru a putea rezolva acest
comenzi folositoare:

exerciiu, s enumerm nc

o serie de

dintr-un grup de linii anumite


caractere aflate pe diverse poziii sau cmpuri delimitate de anumite
caractere.
Pentru a decupa toate coloanele cuprinse ntre anumite poziii vom
utiliza opiunea "-c", iar pentru a selecta diverse cmpuri vom folosi
"-f" n conjuncie cu opiunea "-d" care precizeaz care va fi caracterul utilizat ca delimitator (implicit este caracterul TAB).
Pentru a determina GID-urile tuturor grupurilor de utilizatori vom
executa:

cut options [file (s)] -

decupeaz

(infoiasi):-$ cut -f3 -d: /etc/group

:l

Pentru a decupa toate coloanele cuprinse ntre


apela comanda:

poziiile

10 vom

(infoiasi):-$ cut -c5-10 .profile


=$PATH
rt PAT
k 0033
n

s lynx

s
s
s
s
s

alta
g='t
dir=
p=pi
a='t

sort [+pos1] [-pos2] [file(s)] [options] - este o

comand

care sorteaz conform unor anumite criterii un fiier (dup poziiile


n cadrul liniei sau dup cmpuri).
Dac pos 1 i pos2 sunt specificate, atunci se va sorta zona cuprins
ntre coloanele pos1 i pos2. Pentru a ordona n fun~ie de anumite
cmpuri se va utiliza opiunea '-t'.
uniq file (s) -

pstreaz

dintr-un

fiier

doar liniile unice, n pre-

alabil ordonate.
acestea, dm cteva comenzi referitoare la utilizatori
UNIX este un sistem multi-utilizator):

Dup
c

(s

nu

uitm

26

Atelier de programar e n reele de calculatoar~-------

who - afieaz. lista sesiunilor deschise ale utilizatori lor conectai

(numele de cont, terminalu l de conectare, data conectrii);


f inger [name] - furnizeaz o list. mai detaliat a sesiunilor deschise (incluznd i numele real al utilizatorilor) sau informaii despre
un anumit utilizator (pe maina local sau pe alta):
(infoias i):-$ finger busaco
Login: busaco
Name: Sabin Buraga
Directory : /home/bus aco
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 celor dou comenzi de mai sus,


i ultima comand executat de utilizatori .

afind

S ncercm s rezolvm

misterul liniei propuse. Dup cum se poate


comanda echo va afia un ir de caractere la terminal (ecran). La
prima vedere, am putea obine irul:
bnui,

who I cut -cl-9 I sort I uniq

Dar nu ar fi prea banal? Shell-ul va executa comenzile nlnuite prin


operatoru l " I" din interiorul accentelor grave, iar rezultatul obinut va fi
returnat ca argument pentru comanda echo. Astfel, se va executa mai nti who care va furniza lista tuturor sesiunilor deschise. Lista va fi trimis
spre prelucrare comenzii cut. Aceasta va decupa din fiecare linie primele
9 caractere (adic tocmai numele de cont ale utilizatorilor conectai),
iar rezultatul va fi redirectat ctre intrarea comenzii sort care va ordona datele primite. La final, uniq va pstra numai liniile unice, adic
va trimite la ieirea standard lista - n ordine alfabetic - a utilizatorilor
prezeni n sistem la momentu l execuiei comenzilor de mai sus. Acest
rezultat va fi dat ca argument pentru echo i va fi scris n fiierul denumit
users. Folosind , scrierea se va face adugnd la vechiul coninut noua

list generat.
Dup

cum se observ, puterea shell-ului rezid n abilitatea de a prelucra, ntr-un mod nlnuit, mai multe comenzi, folosind substituia prin
intermedi ul apostroafelor grave.
5. Pentru a rezolva acest exerciiu s ne reamintim cum sunt
utilizatori i n cadrul unui sistem UNIX.

gestionai

Fiecare utilizator are asociat un identificator unic atribuit de sistem (sau


de ctre administr ator) n momentu l crerii contului. Acest identificator este un numr ntreg, mai mare sau egal cu zero, denumit UID.

Introducere
:1

27

De asemenea, fiecare utilizator poate aparine mcar unui grup de utilizatori. Grupul este identificat de identificatorul de grup GID. UID-ul i
GID-ul se gsesc n fiierul /etc/passw d n care pentru orice utilizator
se memoreaz pe o linie:
numele contului su (login name),
parola (din considerente de securitate, de cele mai multe ori parolele
sunt "umbrite", fiind nregistrate n alt fiier, inaccesibil pentru utilizatorii obinuii, denumit /etc/shado w),
UID-ul (O dac acel utilizator are drepturi depline), corespunznd
utilizatorului special root,
GID-ul (O dac acel utilizator are drepturi depline), corespunznd
grupului special root,
alte informaii (nume real, an de studii, telefon etc.),
directorul personal al utilizatorului (home),
interpretoru l de comenzi (shell-ul) folosit (e.g. /bin/bash) .
Fiecare dintre aceste cmpuri va fi delimitat de caracterul ": ".
Un fragment dintr-un fiier /etc/passw d poate fi (parola nu apare, ea
fiind substituit de un "x"):
busaco:x:5 00:500:Sab in-Corneliu Buraga:/ho me/busaco: /bin/bash
Aadar,

linia de comenzi

cut -d: -f1,3 /etc/passw d I sort -t: +O -1

va afia, sortate dup numele de cont, informaiile coninute n fiierul


I etc/passwd despre numele de cont i UID-ul tuturor utilizatorilor. Decuparea informaiilor se va realiza pe cmpuri, cu ajutorul comenzii cut,
delimit-atorul fiind":", dup care vor fi ordonate de sort.
De notat faptul c utiliznd PAM (Pluggable Authenticat ion Modules)
este posibil s avem i alte baze de date coninnd informaii despre
utilizatori, autentificndu-i jn mod transparent .
6. Pentru a lista toi utilizatorii care au numele de cont terminat n litera
"t" vom reoorge la cut i la grep. Trebuie s decupm /etc/passw d
pentru a pstra doar primul cmp din fiecare linie.
Comanda grep va afia numai liniile care conin un anumit subir (model
sau pattern). Modelul poate fi o expresie regulat (vezi mai jos). Pentru
detalii, consultai manualul.

28

Atelier de programare n
O

prim variant (greit!)

reele

de calculatoare

este:

cut -d: -f1 /etc/p assw d I grep t

Acea sta ar afia toate numele de cont care vor.conine


carac terul "t", nu
doar cele n care apare pe ultim a poziie. Pentr u putea
rezolva exerciiul
va trebu i s recurgem la utiliz area expresiilpr regulate.

O expresie regulat este un model care descrie un set


de iruri de caractere, fiind construit asemntor expresiilor aritm etice,
prin utiliz area
diferiilor opera tori. Cei mai uzuali opera
tori sunt:
[ ... ] delimiteaz o list de carac tere dintre care numa
i unul se va potriv i
expresiei dorite (e.g. [Oi23 45678 9] pentr u a se potriv
i cu o singur cifr
sau [0-9A -Za-z ] pentr u un carac ter alfa-numeric).
- meta- carac ter care indic ncep utul unei linii.
$ meta- carac ter indic nd sfritul unei linii.
? opera tor care
al expresiei.

desemneaz

zero sau cel mult o

apariie

a unui element

* opera tor care desemneaz zero, una sau mai multe apariii.
+ opera tor care

desemneaz

{n} opera tor care

specific

una sau mai multe

desemneaz

exact n

apariii.

apariii.

alternativ.

Varia nta corect care soluioneaz exerciiul propu s este

deci urmtoarea:

cut -d: -f1 /etc/p assw d I grep t$

7. Pentr u rezolvarea acestui exerciiu, ne vom folosi de


facili tatea de execuie condiionat a comenzilor pe care o
pune la dispoziie shell- ul.
Construcia

coma ndai && comanda2 are sema ntica urmtoare: coman


da2
va fi executat numa i n cazul n care coma ndai s-a execu
tat i a retur nat
codul de termi nare O. Ream intim faptu l c orice comand
n caz de succes
va retur na codul O, iar n caz de eec un cod diferit de
O. Valoarea O n
UNIX se consider echivalent cu true (adevrat), iar
o valoarea nenul
este echivalent cu false (fals).
Vom utiliz a coma nda test pentr u a realiza diferite
comparaii i teste
asupr a existenei sau tipulu i fiierelor. Pentr u mai multe
amnunte, consultai man test.

Recurgnd la substituia cu apost rofur i inverse, vom


putea scrie
toare a linie de comenzi care ofer soluia:

urm

test ' who I wc -1' -gt 20 && mail root -s"At


entie " </dev /null

Introducere

29

8. Vom utiliza comanda find pentru a cuta fiiere folosind diverse criterii
i, eventual, a realiza anumite aciuni asupra lor. Cutarea se va efectua
pornind de la un anumit director care va fi explorat conform criteriilor
de cutare alese.
Sintaxa

general

a comenzii este:

find [path] [expression] [action]

unde path reprezint calea de directoare de la care se va ncepe cutarea,


expression semnific o expresie definind criteriul de cutare, iar action
specific aciunea care va fi efectuat la gsirea unui fiier.
Cutarea

se poate realiza

dup:

numele unui fiier - se folosete opiunea -name specificator, n


care specificator reprezint un specificator de fiier (se pot utiliza,
desigur, meta-caracterele de s~tituie);
tipul unui fiier - se folosete -type tip, unde tip poate fi unul
dintre caracterele f (fiier obinuit), d (director), 1 (legtur simbolic), s (socket) etc.;
numele proprietarului - se utilizeaz opiunea -user nume, unde
nume poate fi numele sau UID-ul proprietarului fiierului;
grupul proprietarului - se folosete -group nume, unde nume poate
fi un nume de grup sau un GID.
Exist

o multitudine de alte opiuni care pot compune o expresie de


cutare; pentru amnunte citii paginile de manual dedicate comenzii
find. De asemenea, mai multe criterii de cutare pot fi combinate prin
utilizarea operatorilor logici ! (negaie - NOT), -a (i logic - AND) sau
-o (sau logic - OR) i prin folosirea parantezelor.

Ca

aciune executat

afiarea

execuia

numelui

la gsirea unui

fiier

fiierului gsit

- se

putem avea:

folosete opiunea

-print;

unei comenzi - se utilizeaz opiunea -exec. irul de caractere "{}" va &ubstitui numele fiierului gsit i ~ putea fi dat
ca argument al comenzii care va fi executat. Vom sfri lista argumentelor pasate comenzii cu caracterul punct-virgul.

De exemplu, pentru a gsi toate fiierele surs scrise n limbajul C stocate


de sistemul de fiiere, vom da:
find I -name *.c -print

Atelier de programare n

30

reele

de calculatoare

Astfel, soluia la exerciiul propus poate fi urmtoarea (s-au utilizat


ghilimelele pentru ca shell-ul s nu interpreteze caracterele speciale"{}"
sau";"):
find I -name *.bak -o -name *- -exec rm
Invitm
exerciii

11 { } 11 " ; "

cititorul s descopere de unul singur rezolvarea ultimelor


propuse la nceputul acestui capitol.

dou

Capitolul 2

Gestiunea

fiierelor

Acest capitol prezint modurile de prelucrare a fiierelor


ordinare i de tip director, plus diverse modaliti de accesare a informaiilor coninute de diferite fiiere sistem.

2.1
Dup

Prelucrarea
cum am

vzut

fiierelor

n capitolul precedent, sistemul de operare UNIX

trateaz

manier unitar~ierele.

Programatorul poate astfel gestiona att fiierele propriu-zise, ct i dispozitivele periferice sau alte abstraciuni prin intermediul aceleiai interfee.
Astfel, avem la dispoziie dou moduri de prelucrare a fiierelor:
prin intermediul descriptorilor - fiecrui fiier i se va asocia un ntreg
denumit handler, iar operaiile asupra fiierului se vor realiza apelnd
diferite primitive puse la dispoziie de nucleul sistemului de operare;
prin intermediul structurii FILE definite n stdio. h, operaiile asupra
fiierelor realizndu-se prin apelarea funciilor din biblioteca standard de
intrare/ ieire.
De reinut faptul c folosind prima modalitate recurgem la apelurile (primitivele) sistem (prelucrare low-level ), iar n al doilea caz fiecare operaie
de intrare sau de ieire se realizeaz prin intermediul funciilor de bibliotec,
utilizndu-se un buffer intern. n implementarea lor intim, funciile de bibliotec se vor baza pe primitivele puse la dispoziie de nucleul sistemului.
Tabelul 2.1 sintetizeaz principalele operaii care se pot efectua asupra
fiierelor.

De reinut c pentru a crea un fiier se va putea utiliza apelul creat () sau


apelul open(). Sfritul de fiier va fi desemnat de constanta EOF (pentru varianta folosind descriptori de fiier) sau va fi testat cu ajutorul funciei feof O
(n cazul utilizrii structurii FILE).
Cteva detalii privitoare la primitivele prezentate n tabel urmeaz n continuare.

32

Atelier de programar e n
Tabela 2.1:

Operaie

2.1.1

Operaii

de calculatoa re

asupra

fiierelor

I Primitiv sistem I Funcie stdio. h I

deschidere
citire

open()
read()

scriere

writeO

poziionare

lseek()

nchidere

reele

fopen()
freadO
fgetc()
fgetsO
fscanf()
fwriteO
fputcO
fputsO
fprintf( )
fseek()
ftell()
fclose()

close()

Primitiv a open()

Apelul open() are

urmtorul

prototip:

#include <sys/type s.h>


#include <sys/stat .h>
#include <fcntl.h>
int open (const char *pathnam e, int oflag);
int open (const char *pathnam e, int oflag, mode_t mode);

Primitiva returneaz -1 n caz de eroare, altfel returneaz descriptor ul de


asociat fiierului dorit a fi deschis.
Parametr ul oflag desemneaz opiunile de deschidere a fiierului. Este n
realitate un ir de bii, putnd fi folosite constantele:
fiier

O_RDONLY - deschidere numai pentru citire;


O_WRONLY - deschidere numai pentru scriere;
O_RDWR - deschidere pentru citire
O_APPEND - deschidere pentru
O_CREAT - crearea

adugare

fiierului, dac

scriere;

el nu

la

sfrit;

exist

deja;

fi
a
o

Gestiunea

fiierelor

O_EXCL - utilizar e
fiierul exist

O_ TRUNC -

"exclusiv" a fiierului:
deja, se va returna eroare;

dac fiierul exist, coninutul

33
dac

lui este

s-a folosit O_CREAT

ters;

Parame trul mode se folosete numai dac fiierul este creat i specific drepturile de acces.
Pentru crearea fiierelor poate fi folosit i primiti va creat( ) echivalent
cu specificarea opiunilor O_WRONLY I O_CREAT I O_TRUNC la open() .

2.1.2

Primi tiva re ad O

Citirea datelor dintr-u n

fiier

deschis se

realizeaz

cu apelul re ad():

#includ e <unistd .h>


ssize_ t read (int f<i\ void *buff, size_t nbytes)

Se citete un numr de exact nbytes octei de la poziia curent din fiierul


al crui descrip tor este fd i se memoreaz n zona indicat de pointer ul buff.
Este posibil ca n fiier s fie de citit la un momen t dat mai puin de nbytes
octei (de exempl u dac s-a ajuns spre sfritul fiierului),
astfel nct read O
va pune n buffer doar atia octei ci se pot citi.
Primiti va returneaz numrul de octei citii din fiier. Dac s-a ajuns exact
la sfritul fiierului, se returneaz zero, iar n caz de eroare valoarea -1.

2.1.3

Primi tiva wri te O

Scrierea datelor se

realizeaz

cu wri te():

#includ e <unistd .h>


ssize_ t write (int fd, void *buff, size_t nbytes)

Primiti va va scrie n fiierul indicat de fd primii nbytes octei din bufferu


l
desemn at de buff. Returneaz -1 n caz de eroare sau numrul de octei scrii
efectiv.

2.1.4

Primi tiva lseek( )

Operaiile

de scriere / citire n/ din

fiier

se efectueaz la o anumit poziie n


Fiecare operaie de citire, de exemplu, va
actualiz a indicat orul poziiei curente increm entndu -i valoarea cu numru
l de
octei citii. Indicat orul poziiei curente poate fi setat
i n mod explicit cu
ajutoru l apelulu i lseek ():
fiier, considerat poziie curent.

Atelier de program are n

34

reele

de calculato are

#includ e <sys/typ es.h>


#includ e <unistd. h>
off_t lseek (int fd, off_t offset, int pos)

Se
ztor

indicato rul la deplasam entul offset n


descript orului fd, astfel:
poziioneaz

paramet rul pos ia valoarea SEEK_SET,


la nceputu l fiierului;

dac

dac

dac

fiierul

poziionarea

paramet rul pos ia valoarea SEEK_CUR, atunci


relativ la poziia curent;
paramet rul pos ia valoarea SEEK_END,
relativ la sfritul fiierului.

corespun-

se face relativ

poziionarea

poziionarea

se

se face

realizeaz

Paramet rul offset poate lua i valori negative, reprezen tnd deplasamentul, calculat n octei.
n caz de eroare, se returneaz -1, altfel noua poziie n fiier, relativ la
nceputu l acestuia.

2.1.5

Primit iva close ()

posibilit atea ca descriptorul de fiier s fie din nou disponibil pentru utilizare, nchiznd descriptorul. La terminar ea unui proces, toate
fiierele deschise sunt nchise automat .
Apelul close () are forma urmtoare:
Aceast primitiv d

#includ e <unistd. h>


int close (int fd);

2.1.6

Exemp lu

Pentru a ilustra utilizare a apelurilor de mai sus, ne propune m s scriem un


program care simuleaz comand a tac, afind coninntul unui fiier, linie cu
linie, de la ultima la prima linie.

Gestiunea

Codul

surs

fiierelor

al acestui program este dat mai jos:

/ simuleaza comanda 'tac' linie cu linie/


l-

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define NL
10
#define MAXLINE 4096

:e

int
main (int argc, char argv[])

/* codul sfirsitului de linie */


/* lungimea maxima a unei linii */

:a

:i-

la

char c;
char s = (char) malloc (MAXLINE * sizeof (char));
/ 1 daca am term~at parcurgerea fisierului */
int gata = O;
I pozitia in cadrul fisierului */
int pos = -1;
I fisierul de intrare /
FILE fi;

I verificam numarul de parametri


0-

te

dati in linia de comanda /


if (argc != 2)
{

printf ("Sintaxa: %s <fisier>\n", argv[O]);


return -1;
}

I incercam sa deschidem fisierul */


if ((fi

= fopen

(argv[1], "rt"))

==

NULL)

printf ("Eroare: nu am putut deschide fisierul %s\n", argv[1]);


return -1;
}

un

cu

I ne pozitionam la finalul fisierului */


fseek (fi, pos, SEEK_END);
I cit timp mai putem parcurge fisierul ... I
while ( ! gata)
{

/ citim cite un caracter /


fscanf (fi, "%c", &c);
I este sfirsit de linie? */
if ((int) c == NL)
{

35

Atelier de programare n

36

reele

de calculatoare

f * citim o linie intreaga si o afisam */


fgets (s, MAXLINE, fi);
printf ("%s", s);
if (s[strlen (s) - 1] != '\n')
puts ("");
}

f * cu o pozitie spre inceputul fisierului */


pos--;
/* mai putem sa ne pozitionam? */
gata = (fseek (fi, pos, SEEK_END)

-1);

f * af isam si prima linie din f isier */


fseek (fi, pos + 1, SEEK_END);
fgets (s, MAXLINE, fi);
printf ("%s", s);
f * inchidem fisierul */
fclose (fi);
free (s);
f * terminam programul */
return O;
}

Paii importani

sunt

pe care trebuie

s-i urmm

pentru a executa programul

urmtorii:

editarea codului surs C, folosind unul dintre editoarele de texte puse


la dispoziie de sistemul de operare (de la mai simplele pico sau joe,
pn la complexul emacs). Vom presupune c programul dat mai sus se
va numi mytac. c i deci pentru editarea lui vom putea folosi:
joe mytac.c

compilarea codului surs, utiliznd compilatorul C. Acesta se apeleaz


prin gcc (sau cc pe alte sisteme UNIX) i are ca argument obligatoriu numele programului surs (program C avnd extensia . c, program
C++ avnd extensiile . C, . cc ori . cxx sau program Objective-C cu extensia .m). Dac nu se specific o alt opiune, n urma procesului de
compilare i de editare de legturi va rezulta un program executabil denumit a. out. Pentru a obine un fiier executabil cu alt nume vom folosi
opiunea -o:

gcc mytac.c -o mytac

Il

Gestiun ea

fiierelor

37

Dac procesu l de compila re i de editare de legturi s-a


desfurat fr
erori vom obine codul executa bil al programului mytac. n cazul apariiei
unor erori, va trebuie s trecem din nou la etapa de editare a codului.

Uneori, la compil area unor funcii care vor fi incluse ulterior n cadrul
unei biblioteci va trebui generat numai codul obiect, fr a se iniia i
procesul de editare de legturi. Pentru aceasta, vom utiliza opiunea -c,
rezult nd un fiier obiect cu extensi a . o, i nu un program executabil.
Dac program ele noastre vor necesita funcii de bibliote
c al cror cod
nu este inclus n cadrul bibliotecilor de sistem standar d, atunci va trebui
s specificm numele bibliotecii care va fi folosit n
momen tul editrii
de legturi. Acest lucru se realizeaz apelnd la opiunea -1 urmat de
numele bibliotecii:
gcc gaen.c

-o~aend

-lcryp t

Putem specifica de asemeni calea ctre fiierele de bibliotec prin opiunea -L sau calea spre fiierele antet care vor fi
utilizat e apelnd la
opiunea -I.
Pentru optimiz area codului se va folosi opiunea -0, iar pentru controlul
avertism entelor furniza te de compil ator se va utiliza -W.

ul

Mai multe
se
e,
se

despre compil ator n cadrul manual ului (man gcc).

execuia codului executa bil produs de compila tor se


va realiza apelnd
codul binar rezulta t:

./mytac

Pe cele mai multe sisteme, pentru a putea executa un program aflat n


directo rul curent, va trebui s specificm explicit locul su de stocare ,
deoarece n mod uzual interpre torul de comenzi va cuta fiierele executabil e numai n directoarele coninute de variabila de mediu PATH.

o,m

Dac

au aprut erori de execuie, va trebui s reeditm i apoi s recomprogramul. Pentru depana rea avansat a programelor noastre ne
putem sluji de utilitare le gdb, strace sau ltrace .

pilm

~x-

de
le)Si

amnunte

2.2

Prelu crare a atrib utelo r

fiierelor

n afara posibilitii de a realiza operaii asupra coninutului fiierelor, programatoru lui i se pune la dispoziie o serie de apeluri de sistem pentru a consult
a

Atelier de programare n

38

de calculatoare

reele

~--~~~

~~~~~~

ve
modifica diverse proprieti Sal~ atribut e ale fiierelor. Aceste primiti
iar detaliile
de sistem n mod uzual sunt definite n fiierul antet unistd . h,
pentru
(astfel,
sistem
de
lului
explicative se pot regsi n seciunea 2 a manua
.
a afla amnunte privito are la chmod () vom utiliza man 2 chmod)
Se pun la dispoziie urmtoarele primiti ve pentru :

i/sau

verificarea permis iunilor de accesare: access (),


modifi carea permisiunilor: chmod (),
schimb area propri etarulu i (utiliza tor

grup): chown (),

redenu mirea/ mutare a: rename (),


asocierea unei

legturi

hard: link() ,

asocierea unei

legturi

soft: symlin k(),

unei legturi hard (dac numrul de legturi hard asocia te unui


l su fiind elibera t):
fiier devine nul, atunci i acesta va fi ters, i-nodu
unlink (),
tergerea

de leg
furniza rea informaiilor despre atribu te (tip, permisiuni, numr
i acultime
timpul
a,
mrime
i,
turi hard, UID-ul i GID-u l propri etarulu
cesri, modificri etc.): stat() .
i

Apelul stat O va utiliza o


antetu l sys/s tat .h):

structur

crei definiie

este

urmtoarea

(vezi

struct stat
{

st_dev ;
dev_t
st_ino ;
ino_t
st_mod e;
t
mode_
st_nli nk;
nlink_ t
st_uid ;
uid_t
st_gid ;
gid_t
st_rde v;
dev_t
st_siz e;
off_t
unsign ed long st_blk size;
unsign ed long st_blo cks;
st_atim e;
time_t
st_mti me;
time_t
st_ctim e;
time_t
};

/* device */
/* inod */
I* permis iuni */
/* numar de leg. hard *I
/* UID propr ietar */
/* GID propr ietar */
/* tip device
/* marime (in octeti ) */
I* lung. blocul ui pt. I/O */
/* numaru l de blocu ri alocat e */
/* timpul ultimu lui acces */
I* timpul ultime i modif icari */
/* timpul ultime i schimb ari a starii *I

Gestiune a fiierelor
2

2.2.1

Un exemplu de utilizare a apelului stat() care ne va permite


diferite statistic i despre un anumit fiier:

Exemp lu

#includ e
#includ e
#includ e
#includ e
#includ e

<sys/st at.h>
<sys/typ es.h>
<unistd .h>
<pwd.h>
<stdio.h >

/* afiseaz a statist ici despre un fisier /


int
show_f ile_stat (char filenam e[])
{

I structu ra furniza ta de stat() /


struct stat st_str;
/ structu ra pentru prelucr area fisieru lui /etc/pas swd /
struct passwd pw_str ;
/ structu ra pentru prelucr area fisierul ui /etc/gro up /
struct group gr_str ;
/* 1 daca este un fisier de tip dispozi tiv /
int isdev = O;
/ numele proprie tarului /
char name[30 ];

li
):

c-

:zi
if (stat (filenam e, &st_str )
{

==

-1)

perror ("Eroare la stat");


return 1;
}

printf ("Fisie r: \"%s\"\ t", filenam e);

/* determin am tipul f isierulu i /


if ((st_str .st_mod e & S_IFMT) == S_IFDIR)
printf ("direc tor");
el se
if ((st_str .st_mod e & S_IFMT)
S_IFBLK)
{

printf ("bloc" );
isdev = 1;
}

*I

39

else
if ((st_str .st_mod e & S_IFMT)
{

printf ("carac ter");

==

S_IFCHR)

s furnizm

Atelier de progra mare n

40

isdev

reele

de calculatoare

= 1;

el se

if ((st_ str. st_mode & S_IFMT)

printf ("ordi nar");


el se
if ((st_s tr.st_m ode & S_IFMT)
printf ("FIFO ");
el se
printf ("sock et");
/* daca este dispo zitiv ... *I
i f (isdev )

S_IFREG)

S_IFIFO)

printf ("Devi ce: %d, %d",


(int) (st_st r.st_r dev >> 8) & 0377,
(int) st_str .st,rd ev & 0377);
}

I* dispo zitivu l pe care este stocat fisier ul ... */

printf ("\nSt ocat pe dispo zitivu l: %d, %d\n",


(int) (st_st r.st_d ev >> 8) & 0377,
(int) st_str .st_de v & 0377);
printf ("I-no de: %d; Legatu ri: %d; Marime: %ld\n" ,
(int) st_str .st_in o,
(int) st_str .st_nl ink,
(long int) st_str .st_si ze);

I* aflam propr ietaru l */

if (!(pw_ str = getpw uid(st _str.st _uid)) )


strcpy (name, "<necu noscut >");
el se
strcpy (name, pw_str ->pw_n ame);
printf ("UID propr ietar: %d (%s)\n ",
(int) st_str .st_ui d, name);
/* aflam grupul propr ietaru lui */
if (!(gr_ str = getgrg id(st_ str.st_ gid)))
strcpy (name, "<necu noscut >");
el se
strcpy (name, gr_str- >gr_n ame);
printf ("GID propr ietar: %d (%s)\n ",
(int) st_str .st_gi d, name);

I* afisam permi siunil e specia le */


printf ("Perm isiuni specia le: ");
S_ISUID)
if ((st_s tr.st_m ode & S_ISUID)
printf ("SUID ");
S_ISGID)
if ((st_s tr.st_m ode & S_ISGID)

Gestiunea fiierelor

41

printf ("SGID ");


if ((st_str.st_mode & S_ISVTX) == S_ISVTX)
printf ("Sticky ");
f * afisam permisiunile (in octal) */
printf ("\n>>>Permisiuni: Y.o\n",
st_str.st_mode & 0777);
/* timpii de acces, modificare, schimbare de stare */
printf ("Ultimul acces
: Y.s",
asctime(localtime(&st_str.st_atime)));
printf ("Ultima modificare
: Y.s",
asctime(localtime(&st_str.st_mtime)));
printf ("Ultima schimbare de stare
: Y.s",
asctime(localtime(&st_str.st_ctime)));
return O;
}

2.3

Prelucrarea directoarelor

Pentru prelucrarea directoarelor, vom recurge la funciile puse la dispoziie de


antet dirent . h. Se vor utiliza tipurile DIR (stream director) i dirent
(structur coninnd intrrile dintr-un director). Structura dirent are urm
'"narea definiie:

fiierul

struct dirent
{

long d_ino;
unsigned short d_reclen;
char d_name [NAME_MAX+1];

f * numar i-nodului */
f * lungime d_name */
/* nume intrare (terminat cu '\0') */

Astfel, cel mai simplu program care va


director va fi:

afia

f * afiseaza intrarile dintr-un director */


#include <sys/dir.h>
#include <sys/types.h>
#include <dirent.h>
int
main(int argc, char *argv[])
{

DIR
*dp;
struct dirent *dirp;
if (argc != 2)

toate

intrrile

dintr-un anumit

Atelier de programare n

42

reele

de calculatoare

printf ("Sintaxa: %s <nume_director>\n", argv[O]);

/* incercam sa deschidem directorul */


if ( (dp = opendir (argv[1])) ==NULL)
printf ("Nu se poate deschide %s\n", argv[1]);
/* cit putem citi ceva, afisam intrarea */
while ( (dirp = readdir (dp)) != NULL)
printf ("%s\n", dirp->d_name);
/* inchidem directorul */
closedir (dp);
}

Am folosit apelurile opendir(), readdirO i closedir(). Mai sunt puse


rewinddir (), seekdir (), telldir () i scandir ().
Lsm cititorului plcerea de a vedea care sunt sintaxa i semantica acestora.
Pentru operaiile uzuale cu directoare vom putea folosi:

la

dispoziie i funciile

chdir O

schimb

mkdir ()

creeaz

rmdir O

terge

getcwd()

2.4

directorul curent;
un director;

un director gol;

furnizeaz

Prelucrarea

directorul curent.

fiierelor

de sistem

Vom descrie n continuare o serie de modaliti pentru prelucrarea informaiilor


coninute de unele dintre fiierele de sistem importante.

2.4.1

Gestiunea conturilor de utilizatori

n capitolul 1, /etc/passwd este un fiier vital al sistemului, coninnd informaii despre utilizatorii care au conturi pe acea main.
Pentru a putea prelucra informaiile din acest fiier, vom recurge la funciile
definite n antetul pwd. h:
Dup

cte am

vzut

getpwnamO returneaz informaii despre un utilizator


numele de cont;

dac

cunoatem

getpwuid () ca mai sus, dar vom furniza UID-ul utilizatorului.

Gestiunea

fiierelor

43

Prototipurile acestora sunt:


#include <pwd.h>
#include <sys/types.h>
struct passwd *getpwnam (const char *name);
struct passwd *getpwuid (uid_t uid);
Informaiile

returnate sunt stocate n structura passwd

struct passwd {
char
*pw_name;
char
*pw_passwd;
uid_t
pw_uid;
gid_t
pw_gid;
char
*pw_gecos;
char
*pw_dir;
char
*pw_shell;

definit

astfel:

/* numele utilizatorulu i */
/* parola */
I* UID */
/* GID */
/* numele real */
I* directorul 'home' */
/* shell-ul utilizat */

};
Funcia

getpwnam() se poate implementa cu ajutorul funciilor setpwent (),


getpwent O i endpwent () n modul urmtor:
struct passwd *getpwnarn (const char *name)
{

struct passwd *pw_str;


/* ne pozitionam la inceputul fisierului /etc/passwd */
setpwent O;
/* parcurgem linie cu linie pina cind ... *I
while ((pw_str = getpwent ()) !=NULL)

if (!strcmp (name, pw_str->pw_na rne))


break;
/* ... am gasit */
}

/* inchidem f isierul */
endpwent ();
return (pw_str);

e
}
ll

2.4.2

Gestiunea grupurilor de utilizatori

Similar, pentru a avea acces la informaiile despre grupurile de utilizatori ne


vom sluji de funciile getgrnam () i getgrgid O ale cror prototipuri sunt
definite n grp. h.

Atelier de programare n

44

Structura ncapsulnd
struct group {
*gr_name;
char
*gr_passwd;
char
gr_gid;
gid_t
**gr_mem;
char

informaiile

I*
I*
I*
I*

reele

de calculatoare

despre grupurile de utilizatori este:

numele grupului */
parola grupului */
GID */

membrii grupului */

};

2.4.3

Gestiunea sesiunilor de lucru

Vom descrie n cele ce urmeaz anumite detalii de implementare a comenzilor


last (furnizeaz cele mai recente conectri ale utilizatorilor) i who (listeaz
sesiunile curente ale utilizatorilor conectai).
Aceste informaii sunt stocate n dou fiiere: /var /run/utmp folosit de who
i /var/log/wtmp utilizat de comanda last. Pentru a accesa prin program
aceste informaii, ne vom folosi de structura utmp care este definit n antetul
utmp. h astfel:
struct utmp {
short ut_type;
pid_t ut_pid;
char ut_line[UT_LINES IZE];
char ut_user[UT_NAMESIZE];
char ut_host[UT_HOSTSIZE];
long ut_session;
struct timeval ut_tv;
int32_t ut_addr_v6[4];

/*
/*
/*
/*
/*
/*
/*
/*

tipul de login */
PID-ul procesului login */
numele terminalului fara "/dev/" */
numele utilizatorului */
numele hostului de conectare */
identificatorul sesiunii */
timpul de conectare */
adresa IP a hostului */

};

Vom utiliza aceast structur pentru a parcurge unul dintre cele dou fiiere
de mai sus. Astfel, vom putea implementa funcia de mai jos care simuleaz
comportamentul comenzii who (datele nu vor fi afiate la ieirea standard, ci
vor fi memorate ntr-un fiier):
/* genereaza un fisier care va contine informatii despre
sesiunile utilizatorilor curenti din sistem */
int
who (char *utmpfilename, char *whofilename)
{

FILE *infp, *outfp;


struct utmp wutmp;
struct stat tty_st;
int wusers = O;

Gestiunea

fiierelor

45

int idle;
char *ptr_date, idle_str[6], utty[80];
/* deschidem f isierul utmp */
if ( (infp = fopen (utmpfilename,

r 11 ) )

NULL)

/* cream fisierul de iesire */


if ((outfp = fopen (whofilename, "w 11 ) )

NULL)

11

return -1; /* eroare */


}

fclose (infp);
return -2; /* eroare */

:1
)

/* parcurgem intreg fisierul, citind structura utmp */


while (fread (&wutmp, sizeof (wutmp), 1, infp) !=O)
{

if (wutmp.ut_name != NULL && wutmp.ut_type == USER_PROCESS)


{ /* proces utilizator */
ptr_date = ctime (&wutmp.ut_time); /*luam timpul*/
ptr_date[strlen (ptr_date) - 1] = '\0';
I* determinam timpul de inactivitate
dupa timpul de accesare a terminalului */
sprintf (utty, 11 /dev/%s 11 , wutmp.ut_line);
if (stat (utty, &tty_st))
idle = time(O) - tty_st.st_atime;
el se
{

sprintf (utty, 11 /dev/pts/%s 11 , wutmp.ut_line);


if (stat (utty, &tty_st))
idle
time (O) - tty_st.st_atime;
el se
-1;
id le

;i

/* timpul de inactivitate (in minute) *I


if (idle != -1)
sprintf ( idle_str, 11 %02d' 11 , ( idle % 3600) I 60) ;
el se
strcpy ( idle_str, 11 - 11 ) ;
/* scriem datele in fisier */
fprintf (outfp, 11 %-10s %-12s %-3s %12s %16s\n",
wutmp.ut_name, wutmp.ut_line, idle_str,
ptr_date + 4,
wutmp.ut_host !=NULL? wutmp.ut_host : 1111 ) ;
wusers++;

Atelier de programare n

46

reele

de calculatoare

}
}

fprintf (outfp, "\nTotal %d sesiuni.\n", wusers);


/* inchidem f isierele */
fclose (infp);
fclose (outfp);
return O;
}

Ni se pun la dispoziie, de asemenea, funciile getutent O, getutid O


getutline O pentru a accesa mai uor datele din aceste fiiere.

2. 5
i

Exerciii

la acest final de capitol propunem cteva


s

exerciii

spre rezolvare:
sfritul

unui

fiier coni

1.

se conceap un program care


nutul unui alt fiier.

2.

se scrie un program care

3.

se realizeze un program care

4.

se scrie o variant interactiv a comenzii chmod care va


pentru schimbarea permisiunilor fiierelor.

5.

6.

se realizeze un program care furnizeaz informaii despre un utilizator


care are cont pe maina executnd acest program.

7.

se scrie un program care mparte utilizatorii din /etc/passwd n


fiierele info1, info2, info3, info4, master, colegi, coleg2, coleg3,
biro1, biro2, biro3, profs i others, corespunztoare categoriilor de
utilizatori care au conturi pe maina local (n acest caz serverul studenilor de la Facultatea de Informatic, Universitatea "Alexandru Ioan
Cuza" din Iai).

adauge la

implementeaz
simuleaz

comanda tail.

comanda grep.

se implementeze ct mai multe

opiuni

afia

meniuri

ale comenzii ls.

Fiecare dintre

fiierele

generate va avea formatul

login name [ nume si prenume real ]


uid, gid (nume grup), shell, director home

urmtor:

Gestiunea

2.6

Exerciiul

exerciiile

propuse

rezolvrile

acestora.

1. Concaten area a dou fiiere

Pentru primul exerciiu, o variant de rezolvare este


primitivele open O, read (), wri te O i close O:
#include
#include
#include
#include
#include

i-

47

Rezolvri

Vom furniza pentru cteva dintre

fiierelor

urmtoarea

- am utilizat

<sys/types .h>
<sys/stat.h >
<unistd.h>
<fcntl.h>
<stdio.h>

int
main (int argc, char **argv)
{

int n, in, out;


char buf [1024],;

lri

/* nu s-au dat argumentel e */


if (argc != 3)
{
write (2, "Sintaxa: append <fisier1> <fisier2>\ n", 33);
exit (1);
}

or

/* deschidem primul fisier pentru citire */


if ((in =open (argv[1], O_RDONLY)) <O)
{

perror (argv[1]); I* tratam erorile*/


exit (1);

:3,
:le
um

}
/* deschidem al doilea f isier pentru scriere (cu adaugare) */
if ((out = open (argv[2], O_WRONLY I O_APPEND)) < O)
{

perror (argv[2]);
exit (1);
}

/* copiem datele din primul in al doilea f isier */


while ((n = read (in, buf, sizeof (buf))) >O)
write (out, buf, n);

Atelier de program are n

48

reele

de calculato are

/* inchidem f isierele */
clase (out);
clase (in);
exit (O);
}

De remarca t utilizare a funciei perror () care va afia un mesaj de eroare


e. Mesajul de
corespunztor erorii survenit e la apelul unei anumite primitiv
tratarea
Despre
stderr).
(
eroare
eroare va fi trimis la ieirea standard n caz de
erorilor vom discuta n amnunt n capitolu l 3.
Exerciiul

Urmtorul

5. Implem entarea
program

opiunilor

implementeaz opiunile

comenz ii ls
'-1', '-a'

/* impleme nteaza "ls" cu optiuni le -1, -a, -t */


#includ e <stdio.h >
#includ e <dirent. h>
#includ e <string. h>
#includ e <stdlib. h>
#includ e <sys/sta t.h>
#includ e <unistd. h>
#includ e <pwd.h>
#includ e <grp.h>
#includ e <sys/typ es.h>
#includ e <time.h>
/* numarul maxim de intrari intr-un directo r */
#define MAXFILES 500
/* statist ici despre fisier */
struct stat Infos;
/* lunile anului */
char *Months[] =
{"Jan" , "Feb", "Mar", "Apr", "May", "Jun",
"Ju;J..", "Aug", "Sep", "Oct", "Nov", "Dec"};
/* structu ra pentru sortare a in functie de data */
struct Files
{

time_t f_mtime;
struct dirent *file;
};

'-t' ale comenzii ls:

Gestiunea

fiierelor

/* afiseaza permisiunile f isierelor */


void
Rights ()
{

int R, i;
if (Infos.st_mode & S_IFDIR)
printf ("d");
el se
printf ("-");
for (i = O; i < 3; i++)
{

int P = 1, j;
for (j =O; j < (2 - i); j++) /* calculeaza s-(2-i) */
p

*=

8;

* P;
if (Infos.st_mode & R)
printf ("r");
el se
printf ("- ");
R /= 2;
if (Infos. s.t_mode & R)
printf ("w");
el se
printf ( "-") ;
R /= 2;
if (Infos. st_mode & R)
printf ("x");
el se
printf ("- ");
R = Ox04

}
}

/* afiseaza proprietarul f isierului */


void
OwnerName ()
{

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

49

Atelier de programare n

50

reele

de calculatoare

/* af iseaza grupul proprietarului */


void
OwnerGroup ()
{

struct 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 O
{

struct tm *timp;
timp= localtime (&Infos.st_mtime) ;
printf (" %s %2d", Months[timp->tm_m on], timp->tm_mday);
printf (" %2d:%2d", timp->tm_hour, timp->tm_min);
}

/* programul principal */
int
main (int argc, char *argv[])
{

struct dirent **namelist;


struct Files *Fisiere[MAXFILES];
intn,i,j;
char FileName[256];
O, Time
int CurrentDir = 1, List
char Dir[50] = ".";

O, All

/* vedem ce optiuni au fost date */


if (argc > 1)
{

for (i = 1; i < argc; i++)


{

if (strcmp (argv[i], "--help")


{

O)

O;

i
I

Gestiunea

printf
printf
printf
printf
return

("Usage:
("
("
("

fiierelor

list [-1 -a -t] [dir]\n");


-1 for long list\n");
-a to show all files\n");
-t for time sorting\n");

O;

/* este optiune ... */


if (argv[i] [O] == '-')
{

for (j = 1; j < strlen (argv[i]); j++)


{

switch (argv[i] [j])


{

case 'l': List = 1;


break;
case 'a': All = 1;
break;
case 't': Time= 1;
break;
default: printf ("Unknown option!\n");
return 2;
}

continue;
}
}

if (strncmp (argv[argc - 1],


{

li

li

- '

1)

!= O)

strcpy (Dir, argv[argc - 1]);


CurrentDir = O;
}
}

/* scaneaza intrarile din director */


n = scandir (Dir, &namelist, O, alphasort);
for (i = O; i < n; i++)
{

if ((Fisiere[i] =
(struct Files *) malloc (sizeof (struct Files))} ==
NULL)
{

printf ("Could not allocate memory\n");


return 1;
}

if (CurrentDir == 1)
strcpy (FileName, namelist[i]->d_name);

51

52

Atelier de programare n

reele

de calculatoare

el se
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 < O)
{

printf ("Error reading directory ... ");


return 1;
}

if (Time

==

1)

inti,j;
struct Files *Aux;
for (i =O; 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;
}
}

O; i < n; i++)

for (i
{

==

if (All

O)
'

I* incepe cu'.', este fisier invizibil*/


if (Fisiere [i] ->file->d_name [O]
continue;

== ". ")

if (CurrentDir == 1)
strcpy (FileName, Fisiere[i]->file->d_name);
el se
sprintf (FileName, "%s%s", Dir, Fisiere[i]->file->d_name);
if (List == 1)
{

if (stat (FileName, &Infos)

== -1)

perror ("Error on stat\n");

Gestiunea

fiierelor

return 2;
}

Rights ();
printf (" %3d", Infos.st_nlin k);
OwnerName O ;
OwnerGroup ();
printf (" %7d", Infos.st_size );
Date O;
printf (" %s\n", Fisiere[i]->fi le->d_name) ;
}

printf ("%s\n", Fisiere[i]->fi le->d_name) ;


}

printf ("\n");
return O;
}

53

Capitolul 3

Proc ese
Acest capitol prezint noiunile fundamental e referitoare
la procese i modalitile de gestiune a proceselor.

Noiuni

3.1

fundam entale

Un program (cod binar) devine proces atunci cnd este apelat pentru rulare.
Procesul, aadar, reprezint imaginea dinamic a unui program aflat n execuie. Pentru un program pot exista la un moment dat mai multe procese asociate
pe care le vom numi instane ale acelui program.
n UNIX (i n particular n Linux) fiecare proces va avea asociate mai
multe atribute, cele mai importante fiind:
identificat orul de proces (PID) este un ntreg mai mare dect zero; la iniializare, nucleul sistemului de operare va crea un pseudo-proc es cu PID
nul care va genera procesul init al crui PID va fi 1. Fiecare proces va
avea un printe care l-a creat, iar procesul init va putea fi considerat
strmoul tuturor proceselor existente la un moment dat n sistem. Valoarea PID-ului unui proces va fi stocat de tipul pid_ t definit n antetul
sys/types. h.
identificat orul procesulu i printe (PPID) reprezint identificatorul procesului care a creat procesul curent; dac un proces i-a pierdut printele,
n mod automat l va avea drept printe pe procesul init (deci PPID-ul
su va fi 1).
identificat orul grupului de procese -- fiecare proces poate aparine unui
grup ele procese; dac PID-ul procesului este egal cu identificatorul grupului de procese, atunci acel proces este lider al acelui grup.
terminalu l de control asociat procesului; este primul terminal deschis de liderul grupului ele procese (n cazul proceselor utilizator este terminalul la
care s-a conectat acel utilizator). Accesul la terminalul asociat se va face
prin intermediul fiierului I dev /tty 1 . Dac un proces interacioneaz cu
utilizatorul prin intermediul terminalului, atunci acel proces ruleaz n
1 La

ultimele versiuni de nuclee, n directorul I dev exist i alte informaii suplimentare.

Procese

55

prim-plan (foregroun d). Un proces care nu are asociat un terminal (deci


care nu interacioneaz direct cu utilizatorul) se numete proces de fundal
(background) sau daemon.

UID-ul procesul ui este UID-ul utilizatorului care

execut

acel proces.

GID-ul procesul ui este GID-ul grupului din care face parte utilizator ul care
execut acel proces.

c-

e
u

i)

)-

e,
11
ui
Ll-

ela
ce
~u

n
:e.

UID-ul efectiv (EUID) coincide de cele mai multe ori cu UID-ul, servind
la determina rea dreptului de acces la resursele unui anumit proces. n
unele cazuri anumite procese2 trebuie s ruleze sub auspicii de superutilizator - root - i atunci EUID-ul acelor procese va fi egal cu UIDul utilizator ului root, adic O. Ca exemplu, cazul n care un utilizator
obinuit dorete s-i modifice informaiile de finger prin intermedi ul
comenzii chfn care va opera asupra fiierului sistem /etc/pass wd.
GID-ul efectiv (EGID) ca mai sus, pentru grupul din care face parte utilizatorul care va executa procesul.
starea n care se afl un proces: dup creare, dac s-a reuit alocarea de
memorie pentru proces, procesul este n starea ready (pregtit pentru
execuie) fiind deja introdus n coada de ateptare a planificat orului de
procese. Atunci cnd va fi planificat pentru execuie, n funcie de o
anumit prioritate , va trece n starea run (de rulare) - rularea poate fi la
nivel de nucleu sau la nivel de utilizator. Atunci cnd procesul trebuie s
atepte un evenimen t (e.g. introduce rea datelor de la terminalu l asociat)
va trece n starea wait (de ateptare). La terminare a normal (la apelul
primitivei exi t ()) procesul"va trece n starea finished. Dac un proces nu
a fost complet scos din tabela de alocare a proceselor se numete zombie.
Procesele zombie, dei apar ca existente pe main, nu dispun de nici o
resurs a sistemulu i, suprasatu rnd inutil planificat orul de procese.
valoarea nice este o component a prioritii totale pe care o va avea un
proces, putnd fi ajustat cu ajutorul comenzii nice. Prioritate a unui
proces n Linux poate lua valori de la -20 la 20. Valorile de la -1 la -20
sunt asociate proceselqr sistemului, ele neputnd fi acordate proceselor
unui utilizator obinuit. Cea mai mare prioritate a unui proces utilizator
este O.
mulimea

are la
2

descript orilor de fiier - fiecare proces la momentu l


dispoziie descriptorii:

execuiei

Programele corespunztoare vor avea setat permisiune a Set-UID i, eventual, Set-GID.

Atelier de programar e n

56

reele

de calculatoa re

O descriptorul standard de intrare


n mod obinuit asociat terminalu lui
(corespunztor lui FILE* stdin);
1 descriptorul standard de ieire
n mod obinuit asociat terminalu lui
(corespunztor lui FILE* stdout);
2 descriptor ul standard de eroare
n mod obinuit asociat terminalu lui
(corespunztor lui FILE* stderr).
descripto ri pot fi folosii fr a trebui s fie deschii n prealabil.
De asemenea , se pot redirecta prin intermedi ul operatoril or <, >, sau
I dup cum am vzut n capitolul 1.

Aceti

linia de comand conine argument ele pasate procesului; pot fi folosite prin
intermedi ul primelor dou argument e ale funciei main ():
main (int argc, char *argv[])

mediul reprezent nd lista variabilelor de mediu puse la dispoziie de sistemul


de operare (e.g. variabilele PATH, HOME sau TERM). Comanda set poate fi
utilizat pentru afiarea variabilel or de mediu disponibil e. Din program,
putem avea acces la mediu fie prin al treilea argument al funciei main ():
main(int argc, char *argv[], char *envp[])
fie prin intermedi ul

funciilor

getenv ()

setenv ().

unui program utilizator se poate face n dou moduri: n modul


utilizator i n modul nucleu (sau sistem) asociate modului de funcionare al
procesulu i. n modul utilizator procesele au acces numai la propria zon de
cod, date i stiv utilizator , iar n modul nucleu un proces execut instruciuni
privilegia te i poate avea acces la structuril e de date ale nucleului. Nucleul,
n termeni de proces, nu este un proces separat care se execut n paralel cu
procesul utilizator , ci devine parte integrant a procesului utilizator.
Execuia

3.2

Comen zi pentru proces e

Urmtoarele

comenzi UNIX pot fi folosite pentru manipula rea proceselor:

Comanda ps afieaz informaii despre procese. Pentru a afia toate procesele existente pe un anumit sistem putem da ps aux, iar pentru a vizualiza lista

Procese

57

propriilor noastre procese (inclusiv cele din fundal) vom da ps ux. Comanda
pune la dispoziie foarte multe alte opiuni, pentru detalii consultai manualul.
Iat un exemplu de rulare a comenzii ps:
(infoiasi):/$ ps ux
USER
PID %CPU %MEM
lrc
1524 o.o 0.6
lrc
1545 0.5 1.2
lrc
1546 0.1 0.6
lrc
1570 o.o 0.2
lrc
1571 o.o 0.3
lrc
1572 0.3 0.6
lrc
1583 o.o 0.3

vsz RSS TTY


2268
6356
2268
1436
1440
2336
2556

1284
2456
1292
576
612
1324
756

tty3
tty3
tty5
tty5
tty5
pts/4
pts/4

STAT START
s
15:30
T
15:30
15:30
s
15:31
s
15:31
s
15:31
s
15:32
R

TIME
0:00
0:00
0:00
0:00
0:00
0:00
0:00

COMMAND
-bash
pine
-bash
script
script
bash -i
ps ux

O alt comand util este top care va afia un clasament al proceselor


executate pe sistem la momente periodice.
Un proces poate fi rulat n fundal punnd la sfritul comenzii semnul '&',
dup cum se poate vedea n exemplul de mai jos (intrarea standard este redirectat ctre fiierul cu numele listing, iar ieirea de eroare spre dispozitivul
special /dev/null, deci mesajele de eroare nu vor mai fi afiate):
(infoiasi) :/$find I -name *.c -print >listing 2>/dev/null &

Suspendnd un proces (prin combinaia de taste CTRL+Z), l vom readuce


ruleze n prim-plan cu comanda fg sau n fundal cu bg. Procesele rulnd n fundal se mai numesc i procese job, listarea tuturor acestor procese efectundu-se
cu comanda jobs:

l
e
1

.,
J

(infoiasi) :/$ jobs


[1] - Stopped
[2]+ Stopped
(infoiasi):/$ bg %1
[1]- pine &
[1] +

Stopped

pine
top

pine

De menionat faptul c un proces lansat n fundal se va termina odat cu


terminarea sesiunii de lucru. Pentru ca acest proces s ruleze n fundal i dup
logout, ne vom folosi de comanda nohup (vezi i capitolul 4).

~-

Atelier de programare n

58

reele

de calculatoare

Procesele din perspectiva programatorului

3.3

Programatorului i se pun la

dispoziie

o serie de

pri~itive

importante:

fork() va crea un proces nou; procesul care va apela fork() se va numi


proces printe, iar noul proces creat se va numi proces copil.
#include <unistd.h>
pid_t fork(void);

va returna de do:gl!,_ori:
o valoare pozitiv n procesul printe, desemnnd PID-ul procesUii.iI copil
tocmai creat i o valoare nul n procesul copil. n caz de eec, fork ()
returneaz -1. Procesul printe i procesul copil devin dou procese care
se vor executa n mod concurent, independent unul de cellalt, ncepnd
cu prima instruciune care urmeaz lui fork ().
Dei apelat

singur dat 1 J2ri_mitiy~fork()

Primitiva fork() reprezint singura modalitate n UNIX de a crea procese noi n cadrul unui program.
Un proces va putea crea mai muli copii. Procesul copil va fi o copie a
procesului printe, dar cele dou procese nu vor partaja memoria zonelor
de date (statice sau dinamice) ori stiva.
Un exemplu simplu:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
int
main (void)
{

pid_t pid;
/* creaza un proces nou */
pid = fork O;
printf ("Sunt procesul cu PID-ul %d,\n"
" iar apelul forkO a returnat valoarea %d\n",
getpid O , pid) ;
return O;
}

Procese
Un posibil rezultat

afiat

59

ar putea fi:

Sunt procesul cu PID-ul 347,


iar apelul fork() a returnat valoarea 349
Sunt procesul cu PID-ul 349,
iar apelul fork() a returnat valoarea O

Pentru a vedea cum sunt alocate pentru rulare n manier concurent


procesele, vom scrie programul de mai jos care realizeaz suma primelor
N numere naturale:
#include <stdio.h>
#include <unistd.h>
#define MAXNUM

20000

long sum;
int
main

/* suma numerelor */

int i;

I* iterator */

sum = O;
if (fork () < O)
{

perror ("Eroare la fork() ");


return (1);
}

for (i = O; i <= MAXNUM; i++)


{

printf ("Valoarea lui ieste %d.\n", i);


fflush (stdout);
sum += i;
}

printf ("Suma este: %ld.\n", sum);


return (O);
}

Va fi afiat de dou ori suma primelor 20000 de numere naturale, dar


valorile variabilei de iteraie i vor alterna n funcie de care dintre cele
dou procese este planificat s ruleze.

Atelier de programare n

60

reele

de calculatoare

wai t O va putea fi folosit, n procesul printe, pentru a atepta terminarea


execuiei unui proces fiu. Primitiva wai.t () se va bloca pn la terminarea
unuia dintre copiii procesului. Atunci cnd un1:11 dintre copii se termin,
wai t () va returna PID-ul procesului copil care s.:a terminat, plus starea
de terminare a acestuia. n caz de eroare, se va returna valoarea -1.

Prototipul primitivei wai t () este:


#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int status)
Aa

cum am vzut mai sus, un proces care s-a terminat i pentru care
procesul printe nu a executat wai t () se numete zombie. Aadar, pentru
a nu crea procese zombie, trebuie s ateptm terminarea unui proces
copil prin utilizarea lui wai t O.
wai tpid () este un apel nrudit cu wai t O, dar mai flexibil:
pid_t waitpid(pid_t pid, int status, int options);

Se permite ateptarea unui proces copil sau aparinnd unui grup de procese (pentru amnunte consultai man waitpid). De asemenea, waitpid()
poate fi apelat fr a se bloca.

Apelurile wai t O
wai tpid O pot ajuta la sincronizarea
multor procese concurente.
Pentru a afla o serie de
la primitivele:
- getpid()

informaii

despre un proces vom putea recurge

PID-ul procesului curent;

furnizeaz

PID-ul procesului

printe;

- getpgrp O furnizeaz identificatorului grupului de procese al procesului curent; o variant mai general este getpgid();
- setpgrp O seteaz identificatorul grupului de procese al procesului
curent; o variant mai general este setpgid ();
- getuid O
- getgid()
procesul;

furnizeaz

l
\

furnizeaz

- getppidO

execuiei mai

UID-ul utilizatorului real care a lansat procesul;

returneaz

GID-ul grupului utilizatorului care a lansat

I
I

Procese
geteui d()
procesul;

returneaz

61

UID-ul utilizat orului efectiv care a lansat

getegi d() returneaz GID-ul grupulu i utilizat orului efectiv care a


lansat procesul;
setuid O

seteaz

setgid O seteaz
lui curent.

UID-ul utilizatorului efectiv al procesului curent;


GID-ul grupului utilizat orului efectiv al procesu-

Tabela 3.1: Apelurile execXX()

Primitiv j Argum ente


execl O
list
execv( )
vector
execlp ()
list
execvp ()
vector
execle ()
list
execve ()
vector

Cale
directo r curent
directo r curent

PATH
PATH
directo r curent
directo r curent

I Environment

(mediu)
automa t
automa t
automa t
automa t
preciza t
preciza t

Apelur ile execXX () ajut la invocarea unui program (cod executa bil sau
script) de ctre un proces. Procesu l va fi nlocuit comple t de ctre codul
unui alt program (e.g. o comand UNIX).
Astfel, avem posibil itatea de a executa n cadrul aplicaiilor noastre alte
program e. Desigur, apelurile execXX() vor aprea n cadrul procesului copil.
Aadar, nucleul ncarc n zona de memorie noul program
i procesul
este continu at cu acest program , cruia i sunt transm ise argumentele. n
cadrul procesului nu se schimb dect programul, restul rmne nemodificat. La ncheierea execuiei programului se apeleaz primiti va sistem
exi t () , care cauzeaz termina rea procesului fiu i ieirea din starea de
ateptare a procesu lui printe.

Se pun la dispoziie 6 primitive, prezent ate n tabelul 3.1, -ale cror prototipur i sunt (pentru mai multe amnunte consultai manual ul):
#includ e <unistd .h>

'

int execl (const char *file,


I* numele fisieru lui de execut at */
const char *arg,

62

Atelier de program are n

I* argume ntele
... ) ;

reele

de calculat oare

(se termina cu NULL) */

int execv (const char *file,


I* numele f isierul ui de execut at */
char *const argv[]
/* vectoru l argume ntelor */
);

/* execut ie bazata pe valori le variab ilei PATH */


int execlp (const char *file,
I* numele f isierul ui de execut at */
const char *arg,
/* argume ntele (se termina cu NULL) */

... ) ;

int execvp (const char *file,


/* numele fisieru lui de execut at */
char *const argv[]
/* vector ul argume ntelor */
) ;

/* execut ie bazata pe mediul dat prin program */


int execle (const char *file,
/* numele f isierul ui de execut at */
const char *arg ,
I* argume ntele (se termina cu NULL) */

... '

char *const envp[]


I* variab ilele de mediu VAR=valoare */
);

int execve (const char *file,


I* numele fisieru lui de execut at */
char *const argv[],
I* vector ul argume ntelor */
char *const envp[]
I* variab ilele de mediu VAR=valoare */
);

3.4

Exem plu

Cele descrise mai sus ne vor ajuta


executa comand a ls -a -1:
#includ e <unistd .h>
#includ e <stdio. h>
#includ e <sys/ty pes.h>

s realizm urmtorul

program care va

Procese

63

int
main ()
{

/* PID-ul procesului copil */


pid_t pid;
/* starea de terminare a procesului copil */
int status;
printf ("Vom executa comanda ... \n");
if ((pid

= fork

()) < O)

perror ("fork() ");


exit (1);
}

else if (pid)

/* parinte */

if (wait (&status) < O)


{

perror ("wait()");
}

printf ("Comanda a fost executata.\n");


exit (O);
}

el se
{

/* vom folosi execlp() */


execlp ("ls",
/* comanda de executat
(se va cauta in directoarele
"ls",
/* argv[O]
11 -a",
/* argv[1]
"-1",
I* argv[2]
NULL);
/* daca ajungem aici inseamna ca nu s-a
printf ("Eroare de executie!\n");
exit (1);

din PATH) */
*/

*/
*/
putut executa */

}
}

Putem, n locul lui execlp O, s folosim execl O dnd calea


pentru a putea fi executat comanda ls:
execl ( 11 /bin/ls", "ls", "-a",

11

-1 11 , NULL);

complet

Atelier de programare n

64

3.5
1.

reele

de calculatoare

Exerciii
Explicai

efectul

urmtoarei secvene

de cod:

int i;
for ( i = O; i <= 9; i++)
fork

O;

2. Sunt dai n pointeri a fi() funcii al cror prototip este void fi (void),
cu i = 1, ... , n. S se scrie funcia forkn() care lanseaz n procese,
fiecare proces i executnd funcia fi().
se implementeze apelul execvp() cu ajutorul lui execv().

3.

4.

S se scrie un program va crea 5 procese (inclusiv printele). Fiecare


proces va afia cte 10 linii coninnd tipul procesului (printe, copilul 1,
copilul 2, copilul 3, copilul 4) i PID-ul propriu. Dup aceea, procesele
copil se vor termina, returnnd valori diferite, iar printele va afia valorile returnate de ctre copii.

5. Pornind de la exemplul prezentat mai sus, implementai un interpretor


de comenzi UNIX (shell) care afieaz un prompt, posed un mecanism
de istoric al comenzilor (history) i permite execuia proceselor n fundal.
6.

se scrie un program care verific dac sunt mai mult de 20 de sesiuni deschise pe sistem i n caz afirmativ trimite un e-mail cu subiectul "Atenie" la adresa sysadmfenrir. infoiasi. ro (pentru expedierea
mesajului se va putea utiliza comanda mail).

7.

S se implementeze comanda ps, tiind c informaiile despre procesele


curente ale sistemului sunt stocate n sistemul de fiiere special /proc.

3.6

Rezolvri

Exerciiul

2. Implementarea

funciei

forkn()

O soluie de implementare este urmtoarea (se va utiliza un tablou


pointerii la cele n funcii date):
#include <unistd.h>
#include <sys/types.h>

coninnd

Procese

65

int
forkn(vo id (* tab_func t[])(void ), int n, pid_t *tab_pid [])
{

static pid_t pid;


int i, num_chil dren;
num_chil dren = O; /* initial nici un proces nou */
for (i = O ; i < n
i++)
{

switch (pid = fork()) {


case O : (*tab_fu nct[i])(i ); /*copilu l executa functia */
exit (O);
default: if ((tab_pid [i] = pid) > O)
num_child ren++;
/* retinem PID-ul copilulu i */

.,

>

return (num_chi ldren);

e
l-

}
Funcia returneaz numrul de procese copil care au putut fi create, iar
n
tabloul tab_pid [] sunt stocate PID-urile proceselor lansate.

>r

n
l.

Exerciiul

variant

3. Impleme ntarea primitiv ei execvp()


de implemen tare a primitivei execvp() este cea de mai jos:

#include <sys/type s.h>


#include <stdio.h>
#include <stdlib.h >

#define MAXPATH 50 /* lungimea maxima a variabile i PATH */


#define MAXARGS 20 /* numarul maxim de argumente */
extern char **environ ; /* mediul */
void
my_execvp (char *file, char *arg[])
{

char *colon, *pathseq , path[MAXPATH], *newargv[MAXARGS];


char *getenv (), *strchr ();
int i, len;
if (strchr (file, '/') !=NULL I I
(pathseq = getenv ( 11 PATH 11 ) ) == NULL)
pathseq = 11 : 11 ;
for (; (colon= strchr (pathseq, ':')) !=NULL;

Atelier de programare n

66

reele

de calculatoare

pathseq = colon + 1)
{

I* se parcurg directoarele din PATH (desp,artite de ': ') */


len = colon - pathseq;
strncpy (path, pathseq, len);
path[len] = '\0';
if (len > O)
strcat (path, "/");
strcat (path, file);
I* executam comanda */
execv (path, arg);
I* daca esueaza, inseamna ca nu se gaseste in acel director */
}
}

int
main ()
{

/* testam functia de mai sus executind "ls I -1" */


char *arg[]= { "ls", "/", "-1", NULL};
my_execvp (arg[O], arg);
}
Exerciiul

7. Implementa rea comenzii ps

Pentru a implementa comanda ps va trebui s parcurgem intrrile directorului


/proc (pentru mai multe amnunte consultai man proc).

I* Implementarea comenzii "ps" utilizand /proc/pid/sta t */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void
syserr (char *msg)

/* functie de af isare a erorilor */

extern int errno, sys_nerr;


fprintf (stderr, "ERROR : %s ( %d", msg, errno);
if ((errno > O) && (errno < sys_nerr))
fprintf (stderr, "; %s)\n", sys_errlist[e rrno]);

Procese

67

el se
fprintf (stderr, ")\n");
exit (1);
}

int
main O

I* programul principal */

DIR *dp, *dpp;


FILE *from;
struct dirent *dirp;
char *pid, buf[100];
int fd, i, c, t, k;
pid_t ourretval = -1;
char dr[80];
printf ("PID\tCMD \t\tSTATE\t PPID\n");
/* incercam sa parcurgem /proc */
((dp = opendir ("/proc")) ==NULL)

if

syserr ("proc");
}

chdir ("/proc");
while ((dirp = readdir (dp)) != NULL)
{

ll

if (dirp->d_na me[O] != '.')


{

I
I
I

if ((fd = open (dirp->d_na me, O_RDONLY)) != -1)


{

ourretval = (pid_t) atoi (dirp->d_na me);


/* daca este o intrare numerica,
atunci e un director asociat unui proces */
if (ourretval != O)
{

strcpy (dr, "/proc/") ;


strcat (dr, dirp->d_nam e);
if (chdir (dr) < O)
syserr ("chdir 1");

/* deschidem /proc/PID/ stat */


if ( (from = fopen ("stat" "r"))

NULL)

syserr ("stat");
}

/* citim informatii le despre procesul respectiv */

Atelier de programare n

68

reele

de calculatoare

/* si le afisam */
for (i = 1; i <= 4; i++)
{
t

= 1;

while ((c = getc (from)) != ' ')


pute (c, stdout);
if (i == 2)
printf ("\t");
printf ("\t");
}

printf ("\n");
fflush (stdout) ;
chdir ("/proc");
}
}

} /* while */
closedir (dp);
return O;
}

cum se poate remarca din acest ultim listing, pentru raportarea


erorilor ne folosim de variabilele externe errno (furnizeaz codul de eroare),
sys_nerr (numrul maxim de erori) i sys_errlist [] (tablou care conine
irul explicativ pentru eroarea survenit). De asemenea, se poate utiliza funcia
perror () care scrie la ieirea standard de eroare (stderr) mesajul corespunz
tor erorii aprute. Valorile variabilei errno pot fi testate mai uor recurgnd la
constantele simbolice definite n antetul error. h. Pentru fiecare apel n parte,
manualul sistem descrie i erorile care pot surveni (de exemplu, pentru fork ()
pot aprea erorile EAGAIN sau ENOMEM).
Dup

Capitolul 4

Semnale
Acest capitol prezint noiunile fundamentale referitoare
la semnale i modalitile de tratare a semnalelor. De
asemenea, se prezint mecanismul de ataare de alarme
la procesele utilizatorilor prin intermediul primitivei
alarm().

4.1

Prezentare

general

Un semnal este un scurt mesaj transmis unui proces, la apariia unui anumit
eveniment (excepie). Semnalele pot fi ignorate sau redefinite de un anumit
proces, exceptnd un singur semnal. Un semnal nu furnizeaz informaii suplimentare despre acel eveniment, iar destinatarul unui semnal nu cunoate nici
mcar care a fost expeditorul acestuia.
Cauzele apariiei unui semnal pot fi grupate astfel:
~) '

evenimente generate de hardware ( e.g. execuia unei


de ctre procesor, apariia unei pene de curent etc.);

ie

ia

la

.e, :
()

instruciuni

ilegale

evenimente generate de sistemul de operare ( e.g. inexistena unei primitive sistem, ncercarea de a accesa o zon de memorie nepermis, inexistena resurselor necesare etc.);
evenimente generate de procese utilizator sau de utilizatorul nsui (de
exemplu, terminarea forat a unui proces, activarea unei alarme, ntreruperi etc.).
Unele semnale vor provoca, odat cu terminarea procesului asupra cruia
au acionat, generarea unui fiier denumit core care va conine zona de memorie folosit de acel proces.
Cele mai uzuale semnale sunt urmtoarele:

SIGHUP (Hangup) - deconectarea terminalului (transmis la nchiderea sesiunii, se poate inhiba folosind comanda nohup); semnalul mai poate fi folosit
pentru a transmite daemonilor s-i rencarce fiierele de configuraie 1 ;
1 De exemplu, n loc de a restarta daemonul
httpd (serverul Web) prin comanda
/etc/init.d/httpd restart este suficient s dm killall -HUP httpd.

Atelier de programare n

70

reele

de calculatoare

SIGINT (Interrupt) ntrerup ere de la terminal (trimis la


naiei de taste CTRL+C);

apariia

combi-

SIGQU IT ( Quit) - abandon area unui proces;


SIGILL (Illegal instruction) cesor;

execuia

SIGFP E (Floating point exception) -

unei

instruciuni

excepie

de

ilegale de

ctre

pro-

virgul mobil;

SIGKIL L (Kill) - terminar ea forat a unui proces (trimis de sistemul de


operare tuturor proceselor la moment ul operaiunii de shutdown sau reboot - vezi i comenzile shutdown, halt i reboot) . Este unicul semnal
care nu poate fi capturat sau ignorat;
SIGBU S (Bus error) - eroare de

magistral;

SIGSE GV (Segmentation violation) - violare a memoriei (ncercare de accesare a unei zone de memorie nepermise);
SIGPIP E (Pipe error) - scriere ntr-un pipe cnd nu
din acel p'ipe (vezi capitolu l urmtor);
SIGAL RM (Alarm clock) seciunea 4.4);

apariia

exist

proces de citire

unei alarme (vezi apelul alarm() la

SIGUS Rl (User defined signal 1) - semnal care poate fi definit de utilizator;

:~::::~(~;:;t::~e::::::t:;n~ ::::.:ar::::::::::~~:i~:: ,f
;

proces (generat implicit de comand a kill);

SIGCH LD (Death of child) - semnal primit de procesul printe atunci cnd


unul dintre procesele copil s-a termina t (vezi i capitolul 3);
SIGIO (I/O) -

apariia

i
I

unui eveniment I/O asincron;

SIGUR G ( Urgence) - indic


datelor prin reea).

apariia

unei

urgene

(e.g.

recepia incorect

I
Pentru a vedea lista tuturor semnalelor acceptat e de sistemul de operare,
'
~
ct i corespondena dintre valorile simbolice i numerele asociate semnalelor,
vom utiliza comand a kill -1.

I
I
I

J
i
-

'!

Semnale

Pe un sistem cu nucleu Linux versiunea 2.2 vom

),
t

,_

.e
~-

il

~-

(infoiasi ): -$ kill -1
1) SIGHUP
2) SIGINT
5) SIGTRAP
6) SIGABRT
9) SIGKILL
10) SIGUSR1
13) SIGPIPE
14) SIGALRM
18) SIGCONT
19) SIGSTOP
22) SIGTTOU
23) SIGURG
26) SIGVTALRM
27) SIGPROF
30) SIGPWR
31) SIGSYS
34) SIGRTMIN+2 35) SIGRTMIN+3
38) SIGRTMIN+6
39) SIGRTMIN+7
42) SIGRTMIN+10 43) SIGRTMIN+11
46) SIGRTMIN+14 47) SIGRTMIN+15
50) SIGRTMAX-13 51) SIGRTMAX-12
54) SIGRTMAX-9
55) SIGRTMAX-8
58) SIGRTMAX-5
59) SIGRTMAX-4
62) SIGRTMAX-1
63) SIGRTMAX

71
obine urmtoarele:

3) SIGQUIT
7) SIGBUS
11) SIGSEGV
15) SIGTERM
20) SIGTSTP
24) SIGXCPU
28) SIGWINCH
32) SIGRTMIN
36) SIGRTMIN+4
40) SIGRTMIN+8
44) SIGRTMIN+12
48) SIGRTMAX-15
52) SIGRTMAX-11
56) SIGRTMAX-7
60) SIGRTMAX-3

4)
8)
12)
17)
21)
25)
29)
33)
37)
41)
45)
49)
53)
57)
61)

SIGILL
SIGFPE
SIGUSR2
SIGCHLD
SIGTTIN
SIGXFSZ
SIGIO
SIGRTMIN+1
SIGRTMIN+5
SIGRTMIN+9
SIGRTMIN+13
SIGRTMAX-14
SIGRTMAX-10
SIGRTMAX-6
SIGRTMAX-2

re

4.2
la

4.2.1

ir;
1r;

i
I

ui

1d

Manip ularea semnal elor


Definire a unui anumit compor tament la
semnal

apariia

unm

Definirea unui comporta ment la apariia unui semnal se realizeaz prin intermediul primitive i signal() care are urmtorul prototip specificat n fiierul
antet signal. h:
#include <signal.h >
typedef void Sighandl er (int);
Sighandl er *signal( int signum, Sighandl er *function );
Pentru fiecare semnal desemnat de ntregul signum se asociaz o funcie
de tratare (handler) al lui. Funcia de tratare este o funcie avnd un singur argument desemnn d numrul unui semnal i care nu returneaz nimic.
Parametr ul function este un pointer la aceast funcie sau poate fi una dintre
valorile speciale:

Atelier de programare n

72

reele

de calculatoare

SIG_IGN - ignorare semnal,


SIG_DFL - revenire la comporta mentul predefip.it.

f
I
!

Primitiva signal() poate returna constanta SIG_ERR2 n caz de eroare sau


pointerul la funcia de tratare.
La nivelul shell-ului, putem defini un anumit comporta ment la apariia
unui semnal sau a-l ignora cu ajutorul comenzii trap.

4.2.2

Trimite rea unui semnal unui proces

Pentru a trimite un semnal unui proces vom putea utiliza comenzile kill ori
killall sau apelul de sistem kill () a crui form este urmtoarea:
#include <sys/type s.h>
#include <signal.h >
int kill(pid_ t pid, int sig);

pid este pozitiv, va fi trimis semnalul sig procesului cu PID-ul specificat. Dac pid este zero, atunci semnalul va fi trimis tuturor proceselor din
grupul de procese al procesului curent, iar dac pid are valoarea -1 atunci
semnalul va fi trimis fiecrui proces din tabela de procese, exceptnd primul.
Pentru cazul cnd pid este mai mic dect -1, semnalul va fi trimis tuturor
proceselor din grupul de procese cu -pid.
n caz de eec, kill O va returna -1, iar n caz de succes va returna zero.
Un apel nrudit este rai se() care va trimite un semnal procesului curent.
Astfel, rai se (signum) este echivalent cu apelul kill (getpid () , .signum).
Dac

4.2.3
Pentru a

Ateptarea

unui semnal

atepta apariia

unui semnal vom utiliza primitiva pause ():

#include <unistd.h >

int pause(vo id);

Acest apel este blocant


2 Definiia

i returneaz

ntotdeau na valoarea -1.

lui SIG_ERR este #define SIG_ERR (int O 0)-1.

I
'

r
l

73

Semnale

- i

Suspendarea

4.2.4

execuiei

Pentru a suspenda un anumit timp


sleep ():

unui proces
execuia

unui proces vom putea utiliza

funcia
li

#include <unistd.h>
unsigned int sleep(unsigned int seconds);

De asemenea, pentru a cauza terminarea anormal a unui proces, prin


generarea semnalului SIGABRT, vom putea folosi primitiva abort O.
n

4.3

Exemplu

Vom exemplifica utilizarea semnalelor prin scrierea unui program care captureaz semnalul SIGUSR2, la apariia acestuia afind fiierul /etc/services
prin folosirea comenzii less.
#include
#include
#include
,include

ein

<unistd.h>
<signal.h>
<errno.h>
<sys/types.h>

.ci
il.

void
sighandler (int sig)

or

o.
it.

pid_t pid;

if ((pid

!
II

!'

if ( !pid)

II

/* copil */

I'

execl ("/usr/bin/less", "less", "/etc/services", NULL);


perror ("exec() ");
exit (1);

'
I'
I
I
;

i'

/* executam 'less' ca proces copil */

perror ("fork() ");


exit (1);

= fork ()) < O)

I* functia de tratare a semnalului */

I* parinte */

el se
{

if (wait(NULL) < O)
perror("wait()");
}
}

Atelier de programare n

74

reele

de calculatoare

int
main () /* programul principal */
{

I* atasam functia de tratare la semnalul SIGUSR2 */


if (signal (SIGUSR2, sighandler) == SIG_ERR)
{

perror ("signal()");
return 1;
}

/* asteptam aparitia unui semnal */


pause();
return O;
}
Coninutul fiierului /etc/services va fi afiat la
cnd semnalul SIGUSR2 va fi trimis procesului:

(infoiasi):-$
(infoiasi):-$
(infoiasi):-$
1126 pts/2
1134 pts/3
(infoiasi):-$

ieirea

standard atunci

cc sig.c -o sig
./sig
ps x I grep sig
0:00 ./sig
S
0:00 grep sig
S
kill -USR2 1126

n afar de apelurile descrise mai sus, pentru tratarea unui grup de semnale pot fi utilizate apelurile sigprocmask(), sigactionO, sigpending() sau
sigsuspendO.

4.4

Alarme

Fiecare proces poate avea

setat

alarm

prin folosirea primitivei alarmO:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

Semantica primitivei este urmtoarea: dup scurgerea numrului de secnnde dat ca argument va fi trimis semnalul SIGALRM procesului curent. Dac
seconds este zero, atunci nu mai este planificat nici o alarm. Apelul alarmO
va returna numrul de secunde care au mai rmas pn la activarea alarmei
sau zero dac nu exist nici o alarm planificat.

Semnale

4.4.1

75

Exemplu

Ca exemplu de utilizare prezentm un program care din 3 n 3 secunde scrie


un numr aleatoriu ntr-un fiier:
/* alarm.c
din 3
#include
#include
#include
#include
#include

in 3 secunde scrie un nr. aleator in fisierul "alarm" */


<stdio.h>
<stdlib.h>
<unistd.h>
<signal.h>
<time.h>

#define FILENAME "alarm" /* numele fisierului */


#define TIMER 3
/* temporizare la 3 sec. */

:i
int status;
FILE * f;
static void
scrie O

/* scrie numarul in fisier */

if ((f

= fopen (FILENAME, "a+")) == NULL)

perror ("Nu pot deschide fisierul!\n") ;


exit (1);
}

l-

fprintf (f, "%d\n", rand () % 1000);


fclose (f);

u
}

void
alarma O

/* trateaza SIGALRM */

/* la

aparita lui SIGALRM va fi apelata scrie()


if (signal (SIGALRM, scrie) == SIG_ERR)

*I

perror ("signal() ") ;


exit (1);
}

while (1)

I*

rulam la infinit ...

I*
I*

setam o alta alarma */


asteptam aparitia unui semnal */

{
:

alarm (TIMER);

pause O;

3i

}
}

*I

Atelier de programare n

76

int
main (void)
{

switch (fork ())

reele

de calculatoare

/* program ul princip al */
/* lansam procesu l in fundal */

case -1:
perror ("fork() ");
return -1;
I* copilul preia executi a */
case O:
alarma O;
/* parinte le se termina */
default :
sleep (1);
return O;

}
}

lansare, vom putea vedea cum din trei n trei secunde este scris un
alt numr n fiier utiliznd comand a tail -f alarm.
Dup

4.5
1.

\.

Exerciii

se scrie o funcie sleep20 care


prin intermed iul lui alarm() .
S

implementeaz

primitiv a sleep()

2. S se conceap un program care, atunci cnd apare semnalul SIGSEGV,


genereaz n cascad SIGSEGV (segmentation fault).
3.

se scrie un program care terge din minut n minut toate fiierele


tempora re (avnd numele termina te cu . bak sau-) din directoru l curent.

I
I

I
\1

Capitolul 5

Comunicare a ntre procese


Pipe-uri
Capitolul descrie comunicarea ntre procese care ruleaz
pe aceeai main, folosind pipe-uri anonime i cu nume
(FIFO-uri).

5 .1
11

T'

le

t.

'

Preliminarii

Acest capitolul trateaz comunicarea ntre procese locale, i anume problema


transmiterii de date ntre dou procese.
Soluiile acestei probleme sunt multiple, putnd apela de exemplu la utilizarea fiierelor, prin intermediul semnalelor sau folosind alte mijloace. Rezolvrile implicnd fiiere nu sunt satisfctoare, mai ales privind viteza sau
independena de sistemul de stocare a informaiilor.
Programatorilor UNIX le trebuie altceva, ei avnd la dispoziie abstractizri
precum pipe-urile care vor fi descrise n continuare.

5.2

Primitiva pipe()

Am vzut n capitolul precedent o modalitate primitiv de comunicare ntre


procese via semnale. Pentru ca efectiv s putem trasmite date ntre procese
nrudite (printe-copil, frai etc.) ne vom sluji de conceptul de pipe (conduct)
de transmisii unidirecionale de date efectuate pe aceeai main.
Pentru a crea un pipe vom folosi primitiva pipe() avnd prototipul:
#include <unistd.h>
int pipe (int pfd[2]);

Apelul pipe O va crea un canal de comunicare cu doi descriptori de fiier,


descriptorul pfd [1] fiind utilizat pentru a scrie n pipe, iar pfd [O] pentru
citirea din pipe.
Comportamentul diverselor apeluri sistem n cazul pipe-urilor este cel descris n continuare:

Atelier de programare n

78

reele

de calculatoare

wri te() va scrie datele n ordinea sosirii n pipe (principiul first in,
first out - FIFO). Se blocheaz dac pipe-ul este plin, nu exist scrieri
pariale. De obicei, un pipe are o capacitate fix, destul de limitat. Singura metod de a se transmite un EDF este s .se nchid descriptorul
asociat.
read() citete datele n ordinea sosirii lor. Odat citite, datele nu pot fi
recitite sau puse la loc. Dac pipe-ul este vid, read() se va bloca n mod
uzual. Dac read() returneaz O, atunci nseamn c s-a ajuns la EOF.
el o se () pentru descriptorul de scriere (pfd [1]) nseamn nchidere, plus
trimiterea caracterului special EDF procesului care citete din pipe.

Apelurile open(), creat O sau lseek() nu se

utilizeaz.

de citire/scriere sunt atomice, utiliznd buffere interne.


De reinut c pipe-urile se utilizeaz pentru comunicarea n cadrul a dou
procese nrudite prin fork(), altfel nu au sens, pentru c sunt create n memorie. Comunicaiile bidirecionale simultane (full duplex) prin pipe-uri pot conduce la dead-lock-uri, dar putem realiza comunicaii bidirecionale half duplex
(n ambele sensuri, ns nu simultan).
Schema general de conectare a dou procese printr-un pipe unidirecional
Operaiile

~t:. ~~:::::;:::~~:~iul procfficlor producMor/consumator)


2. se

execut

fork() pentru a se crea procesul copil;

3. procesul copil nchide de obicei descriptorul de scriere al pipe-ului, fiindc


doar va citi din pipe;
4. copilul
5.

citete

printele

6. procesul

prin pipe datele de la

printe i

le

proceseaz;

nchide descriptorul de citire al pipe-ului;


printe

trimite datele prin pipe procesului copil.

Procesul printe este procesul productor, iar procesul copil este procesul consumator. Consumatorul se va bloca pn cnd procesul productor va
trimite datele. Dac s-a umplut pipe-ul, productorul se va bloca pn cnd
procesul consumator va citi din pipe.

lr
1

t'

-I

q
I

Comunicarea ntre procese. Pipe-uri

5.2.1

Exemplu

Vom crea un proces copil care va citi dintr-un pipe un ir de caractere trimis de
procesul printe, convertind orice liter minuscul n liter majuscul. Procesul
printe va citi acest ir de la intrarea standard.

I
J.

!l

/* Converteste caracterele mici in caractere mari */


#include
#include
#include
#include
#include

<stdio.h>
<ctype.h>
<stdlib.h>
<unistd.h>
<sys/wait.h>

int pfd[2]; /*pipe-ul*/


int pid;
/* PID-ul procesului copil */
int c;
/* caracterele citite */

1-

int
main (void) /* programul *I

L-

if (pipe (pfd) < O) /* [1] creare pipe */


{

~1

perror ("pipe()");
exit (1);
}

if ((pid
{

I
'.

II

I
1.

II

fork ()) < O)


I* [2] creare proces copil */
perror ("fork()");
exit (1);

if (pid)

/* parinte */

close (pfd[O]); /* [5] inchidem descriptorul de citire*/


printf ("Proces parinte: introduceti un sir: ");
I* vom citi de la intrarea standard */
while (read (O, &c, 1))
/* [6] scriem datele in pipe *I
if (write (pfd[1], &c, 1) <O)
{

perror ("write() ");


exit (1);

era

.d

79

I* inchidem descriptorul de scriere in pipe *I


close (pfd [1]) ;

Atelier de programare n

80

reele

de calculatoare

/ asteptam terminarea copilului /


if (wait (NULL) < O)
{

perror ("wait()");
exit (1);
}

exit (O);
}

el se
{

/ [3] inchidem descriptorul de scriere/


/* [4] citim datele din pipe /
printf ("Proces fiu: receptam datele ... \n");
while (read (pfd[O], te, 1))
close (pfd[1]);

I este litera mica? /


if (islower (c))
printf ("%c", toupper (c));
el se
printf ("Y.c", c);
}

I inchidem si descriptorul de citire /


close (pfd[O]);
I am terminat /
exit (O);
}
}

5.3

Primitiva fifo()

Primitiva mkfifo() permite crearea pipe-urilor cu nume. Acestea au aceleai


caracteristici ca i pipe-urile anonime, dar ocup loc pe disc, ca fiiere, n cadrul
sistemului de fiiere.
Pentru a crea un pipe cu nume vom recurge la apelul mkfifo():
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo (const char pathname, mode_t mode);

n afara numelui de fiier, i mulimea de permisiuni


de tip fi/o (modul de acces, dat n octal).
Dup crearea cu mkf ifo O, pipe-ul cu nume trebuie deschis cu open(),
putnd fi chiar i ters la terminarea prelucrrii cu primitiva unlink().
Va trebui

precizat,

asociat fiierului

Comunicarea ntre procese. Pipe-uri

81

Procesele trebuie s cunoasc numele fifo-ului, dar pot s nu fie nrudite.


Modul de citire i scriere a datelor n cazul unui fifo este similar cu cel prezentat
la pipe-uri.
Un apel mai general, care va crea un fiier special n cadrul sistemului de
fiiere, este mknod ().

5.3.1

Exemplu

Ne propunem s scriem un mesaj ntr-un fifo


caractere din acel fi!o:

(
I
I

l
i

'
II'
I
I
i

!
.i

ul

'

I
!

I
ni
),

#include
#include
#include
#include
#include
#include
#include
#include

apoi

citim doar primele 5

<stdio.h>
<unistd.h>
<stdlib.h>
<fcntl.h>
<sys/types.h>
<sys/stat.h>
<sys/wait.h>
<string.h>

int
main (int argc, char *argv[]) /*programul*/
{

int pfd;
char mesaj[6];
pid_t pid;

/* descriptorul asociat fifo-ului */


/* mesajul citit */
I* PID-ul copilului */

bzero (mesaj~ 6);


if (argc < 2)
{

fprintf (stderr, "Sintaxa: %s <fifo>\n", argv[O]);


exit (2);
}

/* cream fifo-ul */
if (mkfifo (argv[1], S_IFIFO I 0644) < O)
{

fprintf (stderr, "N-am putut crea %s\n", argv[1]);


exit (1);
}

/* cream procesul fiu */


pid = fork O;
switch (pid)
{

case -1:
/* eroare */
fprintf (stderr, "Eroare la fork(). \n");

Atelier de programare n

82

reele

de calculatoare

exit (3);
/* copil */
case O:
if ((pfd = open (argv[1], O_RDONLY)) < O)
{

fprintf (stderr, "Eroare la deschidere fifo. \n");


exit (1);
}

/*citim din fifo */


read (pfd, mesaj, 5);
fifo: %s\n", mesaj);
din
printf ("Mesajul citit
return O;
/* parinte */
default:
O_WRONLY)) < O)
(argv[1],
if ( (pfd = open
{

fprintf (stderr, "Eroare la deschidere fifo.\n");


exit (1);
}

I* scriem in f ifo */
write (pfd, "Salut, ce mai ziceti?", 22);
I* asteptam terminarea copilului */
if (wait (NULL) < O)
{

fprintf (stderr, "Eroare la wait(). \n");


exit (1);
}

return,:O;
/* switch */

}
}

5.4

Exer~iii
metod

de a afla capacitatea unui pipe.

1.

Imaginai

2.

se scrie un program care trimite unui proces copil linie cu linie coni
nutul unui fiier, procesul copil numr liniile trimise i returneaz prin
telui numrul lor.

3. Un proces trimite unui proces copil un numr aleator ntre Oi 3, folosind


un pipe. Dac procesul copil recepioneaz numrul O, va executa ls,
dac primete 1 va executa who, dac recepioneaz 2 va afia coninutul
fiierului /etc/servi ces, iar dac va primi 3, atunci va executa finger.
4. Un proces transmite coninutul unui fiier, linie cu linie, unui proces
copil. O linie este trimis din 7 n 7 secunde. Procesul receptor va primi
informaiile n maxim 37 de secunde, aceste informaii scriindu-le ntr-un
fiier, apoi trimind printelui un semnal de terminare.

l
II

r
r
\

Comunicarea ntre procese. Pipe-uri

83

5. Fie un proces care trimite din 33 n 33 de secunde cte o cifr unui


proces copil. Fiecare cifr este recepionat de copil, care realizeaz suma
primelor 33 de cifre primite, scrie suma ntr-un fiier, i trimite procesului
printe suma, apoi i expediaz o comand de terminare (e.g. "stop"),
dup care se termin i el.
6. Un proces trimite unui proces copil a 33-a linie a fiierului /etc/passwd.
Procesul copil va extrage numele utilizatorului i UID-ul su, le va afia
i apoi va scrie toate informaiile despre procesele acelui utilizator (dac
este conectat) ntr-un fiier.

7. Un proces trimite unui prnces copil numele unui fiier i una dintre literele
"r'', "l", "w" sau "q", aleatoriu. Pentru "r" fiierul va fi ters, pentru "l"
va fi listat (afiat la ieirea standard), iar pentru "w" i se vor numra
liniile. Dac procesul copil recepioneaz caracterul "q" atunci va cauza
terminarea lui i a printelui.
8.

r
9.
'

lI
i

I
!
!

se simuleze activitatea de detectare a staiilor ntr-o reea de tip magisavnd 5 calculatoare. Fiecare staie trimite cte un mesaj de identificare (de ex. PID-ul propriu) n reea i ateapt mesajele celorlalte
staii. Cnd a gsit toate staiile, va afia PID-urile lor. Dac i citete
propriul mesaj, staia l va repune n reea.
tral

5.5

S se simuleze activitatea de detectare a staiilor ntr-o reea de tip inel


avnd 5 calculatoare. Fiecare staie trimite cte un mesaj ( i. e. PID-ul
propriu) n reea i ateapt mesajele celorlalte staii, afind PID-urile
primite, pn cnd detecteaz toate staiile.

Rezolvri

Exerciiul

Una dintre

1. Capacitatea unui pipe


soluiile

acestei probleme este cea

descris

Cunoatem

faptul c apelul wri te O este blocant, ceea ce va conduce la


programului la momentul cnd pipe-ul va fi umplut complet. Recurgnd la semnalul SIGALRM (setnd o alarm) vom reui s afim utilizatorului capacitatea total a pipe-ului i s terminm programul (i vom trimite
semnalul SIGTERM).

"nghearea"

s
.i
1

mai jos:

I* Determina capacitatea unui pipe anonim */


#include <signal.h>
#include <stdio.h>

Atelier de programare n

84

reele

de calculatoare

#include <unistd.h>

I pipe-ul /
int pfd[2];
/ total va numara cate caractere vom scrie cu write
pana cand se va bloca pipe-ul /
int total = O;
void
gata O
{

printf ("\nCapacitatea pipe-ului este de %d octeti. \n", total);


/* dupa ce este afisata capacitatea pipe-ului,
programul se autodistruge /
raise (SIGTERM);
}

int
main ()
{

/*vom scrie repetat in pipe caracterul'!' /


char c = '!';

I cream pipe-ul /
if (pipe (pfd) < O)
{

perror ("pipe()");
exit (1);
}

I setam alarma /
signal (SIGALRM, gata);
alarm (1);
I la infinit, scriem in pipe ... /
while (1)
{

if (write (pfd[1], &c, 1) != 1)


{

perror ("write()");
exit (1);
}

total++;
}

I
II

I
I!
l

Comunicarea ntre procese. Pipe-uri


Exerciiul

5. 33 de cifre

Vom utiliza dou pipe-uri pentru a realiza


ntre printe i copil.
#include
#include
#include
#include

comunicaii bidirecionale

<signal.h>
<unistd.h>
<stdlib.h>
<stdio.h>

l
r

/* numarul de secunde intre 2 trimiteri consecutive */


#define NRSEC 2
/* numarul de numere generate */
#define NRNR 5
/* numele fisierului in care se depune suma */
#define FILENAME "cifre.txt"
int p1[2], p2[2]; /* cele doua pipe-uri */
void
fuser (int sig)
{

int s;
if (!read (p2[0], &s, sizeof (s)))

perror ("Eroare la citire din pipe");


exit (1);

I
I

85

printf ("Suma primita de la copil este %d\n", s);


close (pi [O]);
close (p2 [1]) ;
exit (O);
}

int
main () /* programul principal */
{

pid_t pid;
FILE *f;
int n = O,

x,
sum = O,
val;

/* numarul de cifre */
/* cifra citita */
I* suma calculata */
I* valoarea generata si transmisa pe pipe */

/* setam o alta valoare pentru generarea nr. aleat


srand (getpid());

(duplex)

Atelier de programare n

86

reele

de calculatoare

/* cream cele 2 pipe-uri */


if (pipe (pi) < O)
{

I
f

perror ("Eroare apel pipe");


exit (1);
}

if (pipe (p2) < O)


{

perror ("Eroare apel pipe");


exit (1);
}

/* cream procesul copil */


pid = fork O;
switch (pid)
{

case -i:
{

perror ("Eroare apel fork");


exit (1);
}

/* copil */
case O:
close (pi [i]) ;
close (p2[0]);
i f ((f = fopen (FILENAME, "a")) == NULL)

r
i

perror ("Eroare la deschidere fisier");


exit (1);
}

while (n < NRNR)


{

if (!read (pi[O], &x, sizeof (x)))


{

perror ("Eroare de citire de la parinte");


exit (i);
}

fprintf (f, "%d\n", x);


printf ("Procesul copil cu PID-ul %da primit %d\n",
getpid O , x) ;
sum += x;
n++;
}

fprintf (f, "%d\n", sum);


I* scriem suma in pipe */
write (p2[i], &sum, sizeof (sum));
fclose (f);
close (pi [O]);

Comunicarea ntre procese. Pipe-uri

87

close (p2 [1]) ;


/* am terminat */

kill (getppid (), SIGUSR1);


exit (O);
default:
/* parinte */
close (pl [O]) ;
close (p2 [1]);
/* atasarea functiei de tratare a semnalului SIGUSR1 */
if (signal (SIGUSR1, fuser) == SIG_ERR)
{

perror ("Eroare apel signal");


exit (1);
}

while (1)

I* la infinit ... */

val = random () % 10; /* cifra trimisa */


write (p1[1], &val, sizeof (val));
printf ("Procesul parinte cu PID-ul %da trimis %d\n",
getpid (),val);
sleep (NRSEC);
}

exit (O);
}
}

Pentru NRSEC egal cu 2 i NRNR egal cu 5, un posibil rezultat ar fi "Suma


de la copil este 24", iar coninutul fiierului cifre. txt ar fi urmtorul
(ultima valoare din fiier este suma transmis printelui):

primit

3
6
7
5
3
24
Exerciiul

t\ft- o
. r~
Sfi i'de

C}

(
'

8. Detectare a

staiilor

dintr-o

reea magistral

Vom prezenta n continuare o soluie bazat pe pipe-uri anonime.


Fiecare staie va fi simulat de ctre un proces, iar canalul de comunicaie
dintre staii (magistrala ) va fi simulat de un pipe partajat de toate procesele.
Staiile din reea deja detectate se vor pstra ntr-un vector.

Atelier de program are n

88
#includ e
#includ e
#includ e
#includ e

reele

de calculat oare

II

<stdio. h>
<unistd .h>
<sys/ty pes.h>
<signa l.h>

/*pipe -ul*/
int pfd[2] ;
I* vector ul statiil or detecta te */
int detecte d[4];
int mypid, pid, idx = O, nr;
/* statia este deja detecta ta? */
int
este (int idx, int elem)
{

int k;
for (k = O; k < idx; k++)
elem)
if (detect ed[k)
return 1;
return O;
}

/* listeaz a statiil e detecta te */


int
afisea za_sta tziile (int eu)
{

int i;
printf ("Stati a cu numaru l %d a detect at statiil e: \n", eu);
fflush (stdou t);
for (i = O; i < 4; i++)
{

printf ("%d ", detect ed[i]);


fflush (stdou t);
}

printf ("\n");
fflush (stdou t);
return O;
}

int
myfork O
{

switch (fork ())

/* functia copilu lui */


/* cream copilu l (statia ) */

case -1:
fprintf (stderr , "Eroare la fork.\n ");

Ii
f

Comunicarea ntre procese. Pipe-uri

return 2;
case O:
mypid = getpid ();
write (pfd[l], &mypid, sizeof (pid_t));
while (idx < 4)
{

read (pfd[O], &nr, sizeof (pid_t));


if (nr != getpid () && !este (idx, nr))
detected[idx++] = nr;
write (pfd[l], &nr, sizeof (pid_t));
}

afiseaza_statziile (getpid ());


close (pfd [1]) ;
close (pfd[O]);
I* am terminat (se sinucide) */
raise (SIGKILL);

i
i

'

}
}

int
main () /* programul principal */
{

int j;
if (pipe (pfd) < O)

/* creeaza pipe-ul */

fprintf (stderr, "Eroare la pipe(). \n");


return 1;
}

/* cream statiile */
for (j = O; j < 5; j ++)
myfork O;
/* parintele */
close (pfd[1]);
close (pfd[O]);
/* asteaptam toti cop111 ... *I
while (wait (NULL) > O)
/* ... si am terminat*/
return (O);
}

89

Atelier de programare n

90

reele

de calculatoare

O posibil rulare a programului de mai sus poate fi


Statia cu
2301 2300
Statia cu
2298 2300
Statia cu
2298 2301
Statia cu
2301 2300
Statia cu
2297 2300

numarul 2299
2297 2298
numarul 2301
2297 2299
numarul 2300
2297 2299
numarul 2297
2299 2298
numarul 2298
2299 2301

Exerciiul

urmtoarea:

a detectat statiile:
a detectat statiile:
a detectat statiile:
a detectat statiile:
a detectat statiile:

9. Detectarea

staiilor

dintr-o

reea

inel

Pentru rezolvarea acestei probleme vom folosi fifo-uri, fiecare canal de comunicaie dintre o staie i cei doi vecini ai ei fiind simulat printr-un fifo.
#include
#include
#include
#include

<stdio.h>
<unistd.h>
<fcntl.h>
<sys/types.h>

#define MAX_STATII 5

I* statiile retelei de tip inel */


pid_t statii[MAX_STATI I];
int n_statii = O;
int
main (int argc, char *argv[])
{

/* vecinii unei statii (descriptori de f ifo) */


int stinga, dreapta;
/* mesajul vehiculat */
pid_t mesaj;
if (argc < 3)
{

fprintf (stderr, "Sintaxa: %s <stinga> <dreapta>\n", argv[O]);


exit (2);
}

I* deschidem canalele de comunicatie */


if ((stinga = open (argv[1], O_RDWR)) < O
I I (dreapta= open (argv[2], O_RDWR)) <O)
{

Comunicarea ntre procese. Pipe-uri

91

perror ("open()");
exit (1);
}

mesaj= getpid ();


I scriem mesajul pe canalul din stinga */
write (stinga, &mesaj, sizeof (mesaj));
/* citim cit putem de pe canalul din dreapta */
while (1)
{

read (dreapta, &mesaj, sizeof (mesaj));


/* am primit identificatorul statiei proprii */
if (mesaj == getpid ())
break; /* cu siguranta celelalte statii l-au primit /
/* retinem mesajul primit ... I
statii[n_statii++] =mesaj;
/ ... si-1 trimitem la vecinul din stinga /
write (stinga, &mesaj, sizeof (mesaj));
}

! am gasit toate statiile /


printf ("Statia: %d a gasit: \n", getpid ());
for (; n_statii > O;)
printf ("%d ", statii[--n_statii]);
printf ("\n");
fflush (stdout);
exit (O);

I
I

Pentru a testa programul, vom crea cinci fiiere de tip fifo cu ajutorul
comenzii mkfifo, apoi vom lansa n fundal cinci instane ale programului (presupunem c n urma compilrii a fost generat executabilul cu numele ring).
Ieirea va fi redirectat n cinci fiiere corespunztoare celor cinci staii al cror
coninut va fi listat la finalul execuiei.
Un posil;>il rezultat este cel de mai jos:
(infoiasi):-$
(infoiasi):-$
(infoiasi):-$
(infoiasi):-$
(infoiasi):-$
(infoiasi):-$
. [1] 2380
(infoiasi):-$
[2] 2381
(infoiasi):-$
[3] 2382

mkfifo
mkfifo
mkfifo
mkfifo
mkfifo
ring 1

1
2
3
4
5
2 >A &

ring 2 3 > B &


ring 3 4 > C &

92

Atelier de programare n

(infoiasi):-$ ring 4 5 > D &


[4] 2383
(infoiasi):-$ ring 5 1 > E &
[5] 2384
(infoiasi):-$ cat [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

reele

de calculatoare

Capitolul 6

r Duplica rea descriptorilor.


I
Redirectri

Capitolul descrie maniera de duplicare a descriptorilor


de fiiere, prezentnd primitivele dup O i dup2 (). Duplicrile descriptorilor vor putea fi folosite la redirectarea
intrrilor i ieirilor standard.

6.1
Am
fiier

Duplicarea descriptorilor
vzut

n capitolul 2 c primitiva open()


asociat fiierului asupra cruia dorim s

returneaz
efectum

un descriptor de
diverse operaii de

intrare/ieire.

Sistemul de operare UNIX ofer posibilitatea ca un descriptor s indice


un alt fiier (fiind asociat unui alt descriptor) dect cel obinuit. Operaia se
numete redirectare i se folosete cel mai des n cazul descriptorilor standard cu
valorile O, 1i2 care reprezint intrarea standard, ieirea standard i, respectiv,
ieirea standard de eroare (vezi i capitolele 2 i 3).
Poate fi utilizat i operaia de duplicare a descriptorilor de fiier care determin existena a mai mult de un descriptor pentru acelai fiier. Redirectarea
poate fi vzut ca un caz particular de duplicare.
,
Duplicarea i redirectarea se re~izeaz, n funcie de cerine, recurgnd la
unul dintre urmtoarele apeluri de sistem:
#include <unistd.h>
int dup (int oldfd);
int dup2 (int oldfd, int newfd);

Primitiva dup() realizeaz duplicarea descriptorului oldfd, returnnd un


nou descriptor. Descriptorul returnat va indica acelai fiier ca i descriptorul oldfd. Att noul, ct i vechiul descriptor vor folosi n comun pointerul
de poziie al fiierului, printre altele. Dac poziia n fiier este modificat
prin intermediul primitivei lseek() folosind unul dintre descriptori, efectul
va fi observat i pentru operaiile fcute utiliznd cellalt de~criptor. Anumii
parametri (de exemplu close-on-exec) nu sunt totui comuni celor doi descriptori (vezi man dup).

Atelier de program are n

94

reele

de calculat oare

De reinut faptul c descriptorul nou alocat de dup () este cel mai mic
descrip tor liber (nchis) disponibil.
Primiti va dup2 () se comport similar cu dup () , _cu deosebirea c poate
(),
fi specificat explicit care va fi noul descriptor duplica t. Dup apelul dup2
,
operaie
de
descrip torul newfd va indica acelai fiier ca i oldfd. Dac, nainte
care
descrip torul newfd era deschis, atunci acesta este mai nti nchis, dup
se realizeaz duplicarea.
Ambele primitiv e returneaz descriptorul nou creat (n cazul lui dup2 O
egal cu newfd) sau valoarea -1 n caz de eroare.

6.1.1

Exem ple
de cod realizeaz redirec tarea
deschis avnd descrip torul fd:

Urmtoarea secven
fiier

ieirii

standar d spre un

fd =open ("fisie r.txt", O_WRONLY);


if ((newfd = dup2 (fd, 1)) < O)
{

perror ("Eroar e la dup2 () ") ;


exit (1);
}

printf ("Salu t");

n urma redirectrii, textul "Salut" tiprit cu printf () nu va fi scris la


are
termina l, ci n fiierul cu numele fisier . txt. Astfel, fragmentul de cod
ie de shell-ul UNIX.
acelai efect ca operato rul de redirec tare > pus la dispozi
i unei primitiv e
execuie
cazul
n
i
Redirectrile de fiiere se pstreaz chiar
vezi
de tip exec () (care suprascrie procesul curent cu program ul luat de pe disc,
rea
capitolul 3). Folosind aceast facilitate, este posibil, de exemplu, conecta
de
citit
bil
executa
program
prin pipe a dou procese, unul dintre ele rulnd un
pe disc.
Ne propun em n continu are s nlnuim execuia a dou comenzi, simuln d
operato rul I al shell-ului.
i who I wc -1:
Urmtorul program va avea acelai efect ca linia de comenz
#includ e
#includ e
#includ e
#includ e

<stdio. h>
<stdlib .h>
<unistd .h>
<sys/w ait.h>

r
I

Duplicarea descriptorilor.

Redirectri

void
who_wc () /* who I wc -1 */
{

int pfd(2];

I* un pipe */

/* cream pipe-ul */
if (pipe (pfd) == -1)
{

fprintf (stderr, "pipe\n");


exit (1);
}

/* cream primul copil */


switch (fork ())
{

case -1:
fprintf (stderr, "fork - 1\n");
exit (1);
/* copilul */
case O:
close (1);
/* duplicam descriptorul de scriere al pipe-ului
la iesirea standard (1) */
if (dup (pfd[1]) != 1)
{

fprintf (stderr, "dup - 1\n");


exit (1);
}

/* putem inchide descriptorii pipe-ului,


din moment ce am realizat duplicarea */
close (pfd[O]);
close (pfd[1]);
/* se executa comanda "who"
datele vor fi trimise descriptorului de scriere
al pipe-ului */
execlp ("who", "who", NULL);
fprintf (stderr, "exec - 1\n");
exit (1);
}

/* cream al doilea copil */


switch (fork O)
{

case -1:
fprintf (stderr, "fork - 2\n");
exit (1);
case O:
/* copilul */
close (O);

95

Atelier de programar e n

96

reele

de calculatoa re

/* duplicam descripto rul de intrare al pipe-ulu i


care va fi asociat intrarii standard */
if (dup (pfd[O]) !=O)

fprintf (stderr, "dup - 2\n");


exit (1);
}

/* descript orii pipe-ulu i pot fi inchisi */


close (pfd [O]) ;
close (pfd [1]) ;
/* executam comanda "wc" care va citi datele
de la intrarea standard, acum redirecta ta
la descripto rul de intrare al pipe-ulu i */
execlp ("wc", "wc", "-1", NULL);
fprintf (stderr, "exec - 2\n");
exit (1);
}

/* parintele */
I* inchidem pipe-ul, nu-l folosim deloc */
close (pfd [O]) ;
close (pfd[l]);
I* asteptam terminare a copiilor */
while (wait (NULL) != -1)
}

int
main () /* programu l principa l */
{

who_wc ();
return O;

6.2
i

la acest
1.

Exerciii

sfrit

de capitol propunem cteva

se scrie un program care


primul proces

creeaz

exerciii

spre rezolvare:

trei procese, astfel:

linie cu linie dintr-un fiier cu numele


fiierului i transmite liniile printr-un

(printe) citete

pn la sfritul
copil;
primului
pipe
primul copil primete caracterel e de la printe i selecteaz doar
literele mici pe care le trimite printr-un alt pipe ctre cel de-al doilea
copil;

date. txt

Duplicarea descriptorilor.

Redirectri

97

al doilea proces copil creeaz un fiier denum it statis tica. txt


n
care va memora, pe cte o linie, fiecare liter distinct din cele primite i numrul de apariii ale acesteia n fluxul de date primit . La
final, va trimite ctre printe, printr- un pipe suplim entar, numru
l
de litere distinc te ntlni te.
Printele

2.

va

afia

rezulta tul primit de la al doilea copil.

se realizeze un progra m de tip

productor-consumator

astfel:

progra mul primete ca param etri n linia de comand dou numer


e:
numrul de proces e productor i numrul de
procese consum ator;
resurs a

comun

printe;

printele

tuturo r proceselor este un pipe creat de procesul

este la rndul lui

productor;

n general, productorii scriu n pipe un numr oareca re de caractere, iar consum atorii citesc din pipe, caract er cu caracte r, ct timp
acest lucru este posibil.
Productorii

vor avea

printele

urmtorul

compo rtamen t:

produc e un

numr

unul dintre

productori

aleator iu de caracte re "p";


ceilali productori sunt procese indepe ndente (existe
nte pe disc ca
progra me de sine stttoare) care genereaz la ieirea standa rd un
numr oareca re de caracte re "c". Pentru realiza
rea scopului propus, ieirea standa rd a acesto r procese va fi conectat la captul de
scriere al pipe-ului.

cel

puin

este coman da finger .

Consu matori i citesc caracterele din pipe i i afieaz identif icatoru


l de
proces (PID-u l) urmat de caracte rul citit la un mome nt dat.
n sistem mai exist un FIFO prin care consumatorii vor raport a
la final
rezulta tele ctre printele tuturo r, scriind linii de forma urmtoare:

le
n

numar _proce s : numar _octet i

unde numar _octe ti este numrul total de octei citii de procesul


respectiv din pipe. Printele va prelua aceste informaii i le va afia la
ieirea
standa rd.

Atelier de progra mare n

98
3.

reele

de calculatoare

se rescrie interpr etorul de comenzi propus ca exerciiu n cadrul capiorilor


talului 3, adugndu-i faciliti precum posibi litatea folosirii operat
de exde redirec tare, posibi litatea modificrii promp t-ului i posibi litatea
n). ~.
panda re a numelui fiierelor pentru directoru! Curent ( tab-completio

k
~~

Rezolvri

6.3

Exerciiul

3. Shell

Vom prezen ta n contin uare rezolvarea


shell-ului.
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de

complet

a problemei

<unist d.h>
<stdio .h>
<strin g.h>
<stdli b.h>
<sys/t ypes.h >
<sys/s tat.h>
<fcntl .h>
<signa l.h>
<termi os.h>
<diren t.h>
"keys. h"

/* Shell- ul poate expand a urmato arele variab ile:


$$ - PID-u l bash-u lui
$0 - numele execu tabilu lui
$! - Parint ele bash-u lui
$? - codul ultime i comenzi execu tate
- nume de fisier e date cu ajutor ul lui *
- are implem entat mecanism de histor y (cu derula re)
- are implem entat mecanism de expand are a param etrilor
(cu folosi rea metac aracte rului "*")
- posib ilitate a de a schimb a promp t-ul
($d - dir. curent , $u - nume utiliz ator)
- tab-co mpleti on (numai pe direct orul curen t);

*I
#defin e TEMP_MASK

11

/tmp/shellXXXXXX"

extern char **env iron; /* mediul */


/* pentru exploa tarea termin alului */
struct termio s save_t erm;

I* variab ile global e */


char
char
char
char
char
char

implementrii

Promp t[200] = "Shell [$u $d]$$ ";


cmdlin e[1000 ];
aux[lOOO];
*TEMP, *TEMP2, *ax;
*HOME, *USERNAME;
*BASH_NAME;

I
l

I
I

Duplicar ea descriptorilor.

char *HISTORY;
wint ifd = -1, ofd = -1, hfd
int lines in hist = O;
-..,. . int curent_ line = -1;
int prompt_ length = O;
.nt SaveOut put;
. _ i~t Saveinp ut;
~.-:(i.'1I1t LastErr or = O;
r

-u

Redirectri

99

-1;

~ f:;~ tratare semnale */

~<"vbid semnal (int signum) ;


"j ::>.~ expande aza numele de fisiere */
~()i~t expanda re (char *exp, char *lista);

. UJ

afisare prompt */
-zy id Wri tePromp t () ;
~. "'"'/ functia de termina re */

Ll

--void Iesire (int cod);

f* permite complet area automat a a numelui de fisiere


la apasare a TAB-ulu i */
int
tab_com pletion ()
{

char *p;
struct dirent **fisie re;
int nr = 1, n, i, index;
int files[10 00], cf =O;
char File [50] ;
char com[50] ;
char wrong = O;

bzero (File, 50);


bzero (corn, 50);
if ((p = strrchr (cmdline , ' '))
NULL)
p = cmdline ;
el se
p++;
strcpy (corn, p);
i f ( (n = scandir ( " lf &fisier e, O, alphaso rt))
. '
{

-1)

printf ( Could not open directo ry\n 11 ) ;


return -1;
11

if ((p == NULL) I I (strlen (p)


{

== O))

for (i = O; i < n; i++)


if, (strncmp (fisiere [i]->d_n ame, 11 11 , 2) ! = O)
i f (strncmp (fisiere[ i]->d_n ame, 11 11 , 1) !=O)
printf ("%s\n", fisiere[i ]->d_na me);
return (n - 2);

for (i = O; i < n; i++)


if (strncmp (fisiere[ i]->d_n ame, p, strlen (p))
{

nr++;
files [cf++]
}

i;

O)

Atelier de programare n

100
cf--;
if (cf < O)

reele

de calculatoare

/* daca nu e nici in fisie r */

retur n -1;
}

etrul */
/* daca avem unul singu r si este egal cu param
&&
O)
==
cf
(
(
f
i
n (com )))
(strl en (fisie re[fi les[O ]]->d _nam e) == strle
{

strcp y (p, com);


retur n 1;

if (cf > O)
{

prin tf ("\n" );
for (i = O; i <= cf; i++)
prin tf ("%s \n", fisie re [file s [i]]-> d_na me);

whil e (1)
{

int len = strle n (com);


wrong = O;
if (strl en (fisie re[fi les[O ]]->d _nam e)
{

len)

strcp y (p, com);


retur n cf + 1 ;

com[ len] = fisie re[fi les[O ]]->d _nam e[len ];


com[ len + 1] = '\0';
for (i =O; i < cf; i++)
if (strn cmp (fisi ere[f iles[ i]]-> d_na me,
com, len + 1) != O)
{

wrong = 1;
break ;
}

if (wrong == 1)
{

com[ len] = '\0';


break ;
}

if (strl en (fisie re[fi les[O ]]->d _nam e)


== (len + 1))
break ;
}
i f (wrong == 1)
{

strcp y (p, com);


retur n (cf + 1);

-- O)
if (strc mp (fisie re[fi les[O ]]->d _nam e, com)
{
}

strcp y (p, com);


retur n (cf + 1);

I
j

Duplicarea descriptorilor.

Redirectri

101

strcpy (p, com);


return (cf + 1);
}

void
o_to_ line (int lineno )
int c = 1; /* liniil e se consid era incepa nd de la 1 */
char buffer ;
lseek (hfd, O, SEEK_SET);
while ((c < lineno ) && (read (hfd, &buffe r, 1)
if (buffe r == '\n')
c++;
}

void
et_li ne (int lineno , char *buffe r)
char *P

= buffer ;

if (linen o < 1)
{
}

buffer = NULL;
return ;

go_to_ line (linen o);


while (read (hfd, p, 1)
{

i f ( *P
{

1)

== ' \n' )

*P = '\0';
break;
}

p++;
}
}

/* scrie in istori cul shell- ului */


void
WriteT oHisto ry (char *buffe r)
{

if (*buff er
return ;

'\n')

lseek (hfd, O, SEEK_END);


write (hfd, buffer , strlen (buffe r));
write (hfd, "\n", 1);
lines_ in_his t++;

I* furniz eaza o anumi ta tasta apasat a */

int
GetKey ()
{

struct termio s term;


int buffer = O;

1))

Atelier de prog rama re n

102

reele

de calc ulato are

tcg etat tr (O, &sa ve_t erm );


cfmakeraw (&te rm);
tcse tatt r (O, TCSANOW, &ter m);
read (O, &bu ffer , 1);
if (bu ffer == 27)
{

read (O, &bu ffer , 2);


tcse tatt r (O, TCSANOW, &sa ve_t erm );
retu rn buf fer;

if (bu ffer == O)
read (O, &bu ffer , 1);
tcse tatt r (O, TCSANOW, &sa ve_t erm );
retu rn buf fer;
}

I* citi m o comanda intr odu sa */

void
_LINE)
read_command (cha r *cm dlin e, int MAX
{

int buf;
int c = -1;

fflu sh (std out) ;


fflu sh (std in);
bzer o (cm dlin e, MAX_LINE);
whi le ((bu f = GetKey ()) != ENTER)
{

swit ch (buf )
{

case BACKSPACE:
if (c >= O)
{

c--;

prin tf ("\b \b") ;


cmd line [c + 1] = '\0' ;
}

brea k;
case BREAK:
Iesi re (O);
case KEY_DOWN:
if (lin es_ in_h ist == O)
brea k;
-1)
if (cur ent_ line
k;
brea
O)
if (cur ent_ line
cure nt_l ine = 1;
if (cur ent_ line < line s_in _his t)
{

int i;
cure nt_l ine+ +;
for (i = O; i < c; i++)
prin tf ("\b \b") ;
get_ line (cur ent_ line , cmd line );
c = strl en (cm dlin e);

Duplicarea descriptorilor.

Redirectri

printf ("%s", cmdlin e);


fflush (stdou t);
break;
}

el se
{

int i
for
= O; i < c; i++)
printf ("\b \b");
curent _line = -1;
bzero (cmdli ne, MAX_LINE);

(i

= O;

fflush (stdou t);


break;
}

case KEY_UP:
if (lines _in_h ist == O)
break;
if (curen t_line == O)
break;
if (curen t_line
-1)
{

int i;
curent _line = lines_ in_his t;
for (i = O; i < c; i++)
printf ("\b \b");
get_li ne (curen t_line , cmdlin e);
c = strlen (cmdli ne);
printf ("%s", cmdlin e);
fflush (stdou t);
break;
}

if (curen t_line > O)


{

int i;
curen t_line --;
for (i = O; i < c; i++)
printf ("\b \b");
get_li ne (curen t_line , cmdlin e);
c = strlen (cmdli ne);
printf ("%s", cmdlin e);
fflush (stdou t);
break;
}

break;
case KEY_LEFT:
case KEY_RIGHT:
break;
case TAB:
{

int i, rez;
rez = tab_co mpleti on ();
i f (rez == 1)
{
int 1 = strlen (cmdli ne);
for (i =O; i < (c + promp t_leng th); i++)

103

Atelier de prog rama re n

104

reele

de calculatoare

prin tf ("\b \b") ;


cmd line[ l] = ' ';
cmd line[ l + 1] = '\0';
}

if (rez >= 1)
{

Wri tePro mpt O ;


prin tf ("'Y.s", cmd line) ;
c = strle n (cmd line) ;
cmd line[ c] = '\0';
brea k;

fflu sh (std out) ;


brea k;
}

brea k;
defa ult:
if (c == -1)
{

cmdl ine[O ]
cmdl ine[1 ]
c = 1;

(cha r) buf;

O;

el se
{

c++;
cmd line[ c - 1] = (cha r) buf;
cmd line[ c] = '\0';
}

prin tf ("'Y.c", (cha r) buf) ;


}

fflu sh (std out) ;


if ((c + 1) == MAX_LINE)
{

cmd line[ c + 1] = '\0';


brea k;

cmd line[ c + 1] = '\0';


tcse tattr (O, TCSANOW, &sav e_ter m);
Writ eToH istor y (cmd line) ;
cure nt_l ine = -1;
retu rn;
}

/* crea za fisie rul de isto ric */


void
crea te_h istor y ()
{

stru ct stat info s;


char buf;
if (sta t (HISTORY, &inf os) != -1)
{
if ((hfd = open (HISTORY, O_RDWR)) == -1)
{

prin tf ("Co uld not open histo ry file\ n");

Duplicarea descriptori lor.


}

Redirectri

Iesire (2);

lines_in_ hist = O;
while (read (hfd, &buf, 1)
if (buf == '\n')
lines_in_ hist++;
lseek (hfd, O, SEEK_SET);
return;

1)

if ((hfd = creat (HISTORY, S_IRUSR I S_IWUSR)) == -1)


{

printf ("Could not create history file\n");


Iesire (1);
}

close (hfd);
if ((hfd =open (HISTORY, O_RDWR)) == -1)
{

printf ("Could not open history file\n");


Iesire (1) ;
}

/* afiseaza prompt-u l */
void
WriteProm pt ()
{

char i;
char buf[400] ;
prompt_l ength = O;
for (i =O; i < strlen (Prompt); i++)
switch (Prompt[ i])
{

I
I

case '$':
i++;
switch (Prompt[ i])
{

case 'u':
printf ("%s", USERNAME);
prompt_l ength += strlen (USERNAME);
break;
case '$':
printf ("$");
prompt_le ngth++;
break;
case 'd':
getcwd (buf, 400);
i f (strcmp (buf, "/") != O)
{

printf ("%s", (strrchr (buf, '/') + 1));


prompt_l ength +=
strlen (strrchr (buf, '/') + 1);

el se
{

printf ("%s", buf);


prompt_l ength += strlen (buf);

105

Atelier de programa re n

106

reele

de calculato_a_r_e____ _____

break;
}

b;reak;
default :
{

printf ("%c", Prompt[ i]);


prompt_ length++ ;

}
}

char *
itoa (int nr, char *buffer )
{

char aux[10] ;
char c = O, i;
char *P = buffer;
if (nr

== O)

*P = 'O';
*(p + 1) = '\0';
return buffer;
}

bzero (aux, 10);


while (nr != O)
{

aux[c++ ] = 48 + (nr% 10);


nr /= 10;

for (i

1; i >= O; i--)

aux[i];
*P
p++;
}

*P = '\O';
return buffer;
}

f * functie de alocare */
char *
alocare (int marime)
{

char *p;
if ((p = (char *) malloc (marime )) ==NULL)
{

printf ("Not enough memory\ n");


exit (2);

return p;
}

Duplicarea descriptorilor.

Redirectri

107

/* functie de expanda re bazata pe "*" */


int
expand (char *cmdlin e)

char Backup[ 1000];


char Return[1 000];
char *p;
bzero (Return , 1000);
if ((p = strtok (cmdline ,

11

11 ) )

NULL)

strcpy (cmdlin e, Backup) ;


return;
}

do
{

if (strchr (p, '*') !=NULL)


{

strcat (Return , 11 11 ) ;
if (expand are (p, Return)
return 1;

1)

el se
{

p
}

strcat (Return , p);


strcat (Return , 11 11 )
strtok (NULL,

11

11 ) ;

while (p !=NULL );
strcpy (cmdlin e, Return) ;
return O;

int
expanda re (char *exp, char *list)
\ {
struct dirent **fisie re;
int n, c = O, i, j, k;
char has_las t = O;
char *param[ 10], *p;
char *index[ 10];
char EXPAND[100];
char *DIRECTORY;
char lista[10 00];

I
I

strcpy (EXPAND, exp);


DIRECTORY =aloca re (200);
getcwd (DIRECTORY, 200);
bzero (lista, 1000);
if ((n = scandir (DIRECTORY, &fisier e, O, alphaso rt))
{

printf ("Error on reading directo ry\n 11 ) ;


list [O] = O;
return 1;

-1)

Atelier de programare n

108

reele

de calculatoare

if ((EXPAND[O] == '*') && (strlen (EXPAND) == 1))


{

param[O] =alocare (3);


strcpy (param[O], "*\O");
param[1] = NULL;

1;

if (strlen (EXPAND) > 1)


{

if (EXPAND[O] == '*')
{

param[c++] =alocare (3);


strcpy (param[c - 1], "*\O");
}

if (EXPAND[strlen (EXPAND) - 1] == '*')


has_last = 1;
((p

if
{

strtok (EXPAND, "*")) ==NULL)

printf ("no expansion\ n 11 ) ;


param[O] = NULL;

el se
param[c++] = p;
while ( (p = strtok (NULL,

11

*")) ! = NULL)

param[c++] =alocare (2);


strcpy (param[c - 1], "*\O");
param[c++] = p;
}

if (has_last == 1)
{

param[c] =alocare (2);


strcpy (param[c], "*\O");
c++;
}

param[c]

~~~

~~~--~~~~~~

= NULL;

for (i = O; i < n; i++)


i f ((strcmp (fisiere[i]- >d_name, ".") ==O) 11 \
(strcmp (fisiere[i]- >d_name, " .. ")==O))
continue;
el se
{

char wrong = O;
/* verifica daca toti sunt '*' *I
for (j = O; j < c; j++)
i f (strcmp (param[j], "*") != O)
wrong = 1;
i f (wrong == O)
{

strcat (lista, fisiere[i]-> d_name);


strcat (lista, 11 " ) ;
continue;

Duplica rea descriptorilor.

Redirectri

for (j = O; j < c; j++)


if (strcmp (param [j], "*") != O)
{

index[ j] = strstr (fisiere [i]->d_ name, param[ j]);


if (index [j] == NULL)
continu e;
li daca sunt ultimu l
if (j == (c - 1))
if (*(inde x[j] + strlen (param [j])) != '\0')
index[ j] = NULL;

I* verific a daca a fost vreo conditi e neinde plinita *I


wrong

f~

strcat (list, lista);


if (strlen (lista) == O)
return 1;
return O;

/* functie de iesire "gratio asa" *I

~~!~re

I{
I

= O;
for (j = O; j < c; i++)
if (strcmp (param [j], "*") != O)
if (index [j] == NULL)
wrong = 1;
if (wrong == 1)
continu e;
wrong = O;
for (j =O; ((j < (c - 1)) && (wrong ==O)); j++)
for (k = (j + 1); ((k < c) && (wrong ==O)); k++)
if ((strcm p (param [j], "*") != O)
&& (strcmp (param [k], "*") !=O))
if (index [j] >= index[k ])
wrong = 1;
if (wrong == 1)
continu e;
strcat (lista, fisiere [i]->d_ name);
strcat (lista, " ") ;

(int cod)

I* sterger ea f isierel or tempor are


unlink (TEMP);
unlink (TEMP2);
I* inchide rea descri ptorilo r *I
if (hfd != -1)
close (hfd);
if (ifd != -1)
close (ifd);
if (ofd != -1)
close (ofd);
I* elibera rea memoriei *I
free (TEMP);
free (TEMP2);
free (ax);
free (HOME) ; .

*I

109

Atelier de program are n

110

reele

de calculato are

free (USERNAME);
free (HISTORY);
exit (cod);
}

/* verific a daca apar variabi le shell */


void
SearchF orVars (char *argv[2 55])
{

int i;
char *p;
for (i = O; i < 255; i++)
if (argv[i] == NULL)
break;
el se
{

if (strcmp (argv[i ], "$?") == O)


{

p =alocar e (8);
argv[i] = itoa (LastEr ror, p);
continu e;
}
if (strcmp (argv[i ], "$$")
{

== O)

p =aloca re (8);
argv[i] = itoa (getpid (), p);
continu e;

}
if (strcmp (argv[i ], "$!") ==O)
{

p =aloca re (8);
argv[i] = itoa (getppid (), p);
continu e;
}
i f (strcmp (argv[i ], "$0") == O)
{

argv[i] = BASH_NAME;
}
}
}

/* functie de executi e propriu -zisa a comenzi lor */


int
MyExec (char *INPUT, char *OUTPUT, char *cmdlin e)

int fd1, fd2;


int b_in, b_out, i;
char *argv[2 55];
unsigne d char c = 1;
char aux[100 0];
char wait_fo r_child = 1;
char *p;
if ((p = strchr (cmdline , '*')) != NULL)
if (expand (cmdline ) != O)
{

Duplica rea descriptorilor.

Redirectri

printf ("No such files ... \n");


LastEr ror = 128;
return ;

strcpy (aux, cmdlin e);


p = strtok (aux, " \t\n");
argv[O] = p;
I* "sparge " argume ntele in asa fel incat
cele date cu " sa ramana intacte */
while .( (p = strtok (NULL, " \ t \n")) ! = NULL)
{

if ( *P ! = '" ')
{

argv[c+ +]
continu e;

p;

char aux [100] ;


bzero (aux, 100);
if (*(p + 1) == '\0')
strcpy (aux, p);
el se
{

strcpy (aux, (p + 1));


strcat (aux, " ");

while ((p = strtok (NULL, " \t\n")) != NULL)


{

I
l

strcat (aux, p);


if ((aux[ strlen (aux) - 1]
' li')
&& (aux[s trlen (aux) - 2] ! = ' \ \'))
break;
strcat (aux, " ");

aux[st rlen (aux) - 1] = '\0';


strcpy (ax, aux);
argv[c+ +] = ax
}
/* sfir~itul bloculu i */

argv[c] = NULL;
SearchF orVars (argv);
if ((c >= 2) && (strcmp (argv[c - 1), "&")
{

O))

wait_f or_chi ld = O;
argv[c - 1] = NULL;

if ((INPU T== NULL) && (OUTPUT== NULL))


{

/* lanseaz a un proces care va executa comanda */


switch (fork O)
{

case -1:
printf ("Fork error\n ");
return 2;
case O:

111

Atelier de program are n

112

reele

de calculat oare

execvp (argv[O ], argv);


printf ("Error on exec\n ");
exit (2);
}

if (wait_ for_ch ild == 1)


wait (&Last Error);
el se
return O;
return O;
}

/* redirec tarea iesirii */


if (INPUT

==

if ((fdl
{

NULL)

= open

(OUTPUT, O_RDWR))

==

-1)

printf ("Could not open output file\n" );


return 3;

ftrunca te (fdl, O);


if ((b_out = dup (1))
{

==

-1)

printf ("Could not save output \n");


close (fdl);
return 4;

dup2 (fdl, 1);


switch (fork ())
{

case -1:
printf
return
case O:
execvp
printf
return

("Fork error\n ");


2;
(argv[O ], argv);
("Error on exec\n ");
3;

if (wait_ for_ch ild == 1)


wait (&Last Error);
close (fdl);
dup2 (b_out, 1);
return O;
}

I* redirec tarea intrar ii */


if (OUTPUT == NULL)
{

if ((fd1 = open (INPUT, O_RDWR)) == -1)


{

printf ("Could not open input file\n" );


return 3;

if ((b_in = dup (O)) == -1)


{

printf ("Could not save input\n ");


close (fd1);
return 4;

dup2 (fdl, O);

Duplicar ea descriptorilor.

Redirectri

113

switch (fork ())


{

case -1:
printf
return
case O:
execvp
printf
return
}

("Fork error\n ");


2;
(argv[O ], argv);
("Error on exec\n" );
3;

if (wait_fo r_child == 1)
wait (&LastE rror);
close (fd1);
dup2 (b_in, O);
return O;

/* redirec tarea ambelor */


if ((fd1 = open (INPUT, O_RDDNLY))
{
}

printf ("Could not open file\n") ;


return 1;

if ((fd2
{

-1)

= open (OUTPUT, O_RDWR)) == -1)

printf ("Could not open output file\n") ;


return 2;
}

ftrunca te (fd2, O);


if (((b_ou t = dup (1))
{

== -1) I I ((b_in = dup (O))

printf ("Error duplica te output\n ");


return 2;
}

dup2 (fd1, O);


dup2 (fd2, 1);
switch (fork ())
{

case -1:
printf
return
case O:
execvp
printf
return
}

("Fork error\n ");


2;
(argv[O ], argv);
("Error on exec\n" );
3;

if (wait_fo r_child == 1)
wait (&LastE rror);

close (fd1);
close (fd2);
dup2 (b_out, 1);
dup2 (b_in, O);
return O;

-1))

Atelier de progra mare n

114

reele

de calcula toare

void
MoveO utputT olnput ()
{

int fd1, fd2;


char Buffer ;
if ((fd1
{

= open (TEMP, O_RDWR)) == -1)

printf ("Coul d not open file\n ");


exit (2);

if ((fd2
{

= open (TEMP2, O_RDWR)) == -1)

printf ("Coul d not open file\n ");


exit (3);

ftrunc ate (fd2, O);


while (read (fd1, &Buff er, 1)
write (fd2, &Buff er, 1);
ftrunc ate (fd1, O);
close (fd1);
close (fd2) ;

lI

1)

/* creeaz a un fisier */
void
Create File (char *filena me)
{

int fd;
char *P

= filenam e;

while (*p

== ' ')

p++;

filenam e = p;
if ((fd = creat (filena me, S_IRUSR I S_IWUSR))
{

printf ("Coul d not create file\n ");


return ;

close (fd);
}

void
Transf erTo (char *dest, char *Sourc e)
{

int sfd, dfd;


char buf;
Create File (dest) ;
if ((sfd = open (sourc e, O_RDDNLY))
{

printf ("Coul d not opens ource file\n ");


return ;

if ((dfd
{

== -1)

= open (dest, O_WRONLY)) == -1)

-1)

Duplicar ea descripto rilor.

Redirectri

printf ( 11 Could not open destina tion file\n 11 ) ;


return;
}

while (read (sfd, &buf, 1) == 1)


write (dfd, &buf, 1);
lseek (sfd, O, SEEK_SET);
ftrunca te (sfd, O);
close (sfd);
close (dfd);
}

/* verific a existen ta operato rilor de redirec tare */


int
CheckFo rPipes ()

char *p;
char SaveLin e[1000];
char Buffer;
if ( (p = str str ( cmdline ,
{

11

I 11 ) ) == NULL)

if ((p = strstr (cmdline ,

'

11

>"))

!=NULL)

int i = O
strcpy (S~veLine, p);
*P = '\O';
SaveLin e[O] =' ';
while (SaveLin e[i++] == ' ');
i--;

MyExec (NULL, TEMP2, cmdline );


Transfe ro (&SaveL ine[i], TEMP2);
return 1;

if ((p = strstr (cmdline , "< 11 ) ) !=NULL)


{

int i = O
strcpy (S~veLine, p);
*P = '\O' ;
SaveLin e[O] =' ';
while (SaveLin e[i++] == ' ');

l
r

i--;

I
I

MyExec (&SaveL ine[i], NULL, cmdline );


return 1;

return O;

strcpy (SaveLin e, p);


*P == '\0';
MyExec (NULL, TEMP2, cmdline );
strcpy (cmdlin e, &SaveL ine[1]);
while ( (p = strstr ( cmdline , 11 I 11 ) ) ! == NULL)
{

strcpy (SaveLin e, p);


*P = '\O';
MyExec (TEMP2, TEMP, cmdline );
MoveOutputToinput ();
strcpy (cmdline , &SaveL ine[1]);

115

Atelier de program are n

116

reele

de calculat oare

MyExec (TEMP2, NULL, cmdlin e);


return 1;
}

/* creaza fisiere tempor are */


void
CreateTemp O
{

TEMP =aloca re (100);


TEMP2 =aloca re (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 = creat (TEMP, S_IRWXU)) == -1)


{

printf ("Could not create tmp file\n" );


exit (2);

close (ifd);
if ((ifd = open (TEMP, O_RDWR)) == -1)
{

printf ("Could not open temp file for readin g\n");


exit (3);

if ((ofd = creat (TEMP2, S_IRWXU)) == -1)


{

printf ("Could not create tmp file\n" );


exit (2);

close (ofd);
if ((ofd = open (TEMP2, O_RDWR)) == -1)
{

printf ("Could not reopen tmp file\n" );


exit (2);

I* seteaza comenz ile */


void
SetCommand (char *cmdlin e)
{

char **Vars = environ ;


int c = O;
if (strlen (cmdlin e) == 3)
{

while (Vars[c ++] !=NULL)


printf ("%s\n" , Vars[c - 1]);

i
II

Dupli carea descriptorilor.


}

retur n;

/* verif ica daca exist a comenzi speci ale:


"exit ", "cd", "set" , "prom pt" */
int
SpecialCommand ()

if

if
{

(strcm p (cmd line, "exit ")


unlin k (TEMP);
unlin k (TEMP2);
exit (O);
(strnc mp (cmd line, "cd" , 3)
if (cmd line[3 ]
{

}
}

if
{

if

{
}

O)

O)

== ,-,)

char aux[3 00];


bzero (aux, 300);
strca t (aux, HOME);
strca t (aux, &cmd line[4 ]);
chdir (aux) ;

chdir (&cm dline [3]);


retur n 1;
(strnc mp (cmd line, "set "

4)

O)

SetCommand (cmd line);


retur n 1;
(strnc mp (cmd line, "prom pt "

7)

O)

strcp y (Prom pt, cmdli ne + 7);


retur n 1;

( }return O;

void
print _info s O

print f ("Sta rting DShe ll. .. \n");


print f (" home direc tory: %s\n ", HOME);
print f (" histo ry file:% s\n", HISTDRY);

/* progr amul princ ipal */


int
main (int argc, char *argv [])
{

char *p;

I* trata re semn ale */


if ((sig nal (SIGTERM, semna l)

SIG_ERR)

Redirectri

117

Atelier de progra mare n

118

reele

de calcula toare

I I (signa l (SIGINT, semnal ) == SIG_ERR))


{

printf ("Coul d not trap signal \n");


return 2;

ax= alocar e (100);


HISTORY =aloc are (300);
bzero (HISTORY, 300);
Create emp () ;
HOME= getenv ("HOME");
USERNAME = getenv ("USER");
BASH_NAME = argv[O ];
strcpy (HISTORY, HOME);
strcat (HISTORY, "/ ") ;
strcat (HISTORY, ".shel l_hist ory");
create _histo ry ();

/* verifi ca linia de comanda */

O))

if ((argc >= 2) && (strcm p (argv[ 1], "-v")


print_ infos O;
/* execut a la infini t ... */
while (1)
{

I* af iseaza prompt */
Wri teProm pt () ;
/* citest e comanda */
read_command (cmdli ne, 1000);
printf ("\n") ;
if (cmdli ne[O] == '\n')
contin ue;
I* verifi ca daca e comanda specia la */
if (SpecialCommand () == 1)
contin ue;
/* verifi ca redire ctari */
if (Check ForPip es () == O)
MyExec (NULL, NULL, cmdlin e);

return O;
}

/* functi e de tratar e a semna lelor */


void
semnal (int signum)
{

if (signum == SIGTERM)
Iesire (SIGTERM);

Progra mul folosete un fiier antet keys. h


utiliza te (depen dente de terminal):

coninnd

#ifnde f __ MYKEYS __
#defin e __ MYKEYS __ 1
#defin e KEY_UP
#defin e KEY_DOWN

16"131
16987

I* sageat a in sus *I
I* sageat a in jos *I

consta ntele tastelor

Duplic area descriptorilor.

#defin e
#defin e
#defin e
#defin e
#defin e
#defin e
#defin e

KEY_LEFT 17499
KEY_RIGHT 17243
BACKSPACE 127
TAB 9
SPACE 32
ENTER 13
BREAK 3

Redirectri

/* sagea ta sting a */
/* sagea ta dreap ta */

#endi f

posibil

rulare a shell-ului este

urmtoarea:

(info iasi): -$ ./she ll


Shell [lrc shell _fina l]$ ls
Make file Make file- keys. h shell shell .c shell
.cShell [lrc shell _fina l]$ cat Make # action are tasta
TAB
Make file
Make fileShell [lrc shell _fina l]$ cat Make file
all:
gcc shell .c -g -o shell
clean :
rm -f *- shell
Shell [lrc shell _fina l]$ cat Make file I wc -1
5

Shell [lrc shell _fina l]$ rm *Shell [lrc shell _fina l]$ ls
Make file keys. h shell shell .c
1(~ Shell
[lrc shell _fina l]$ ls -l *.c
-rwxr -xr-x
1 lrc
lrc
21459 Jun 9 22: 35 shell . c
Shell [lrc shell _fina l]$ promp t Salut $u:>
Salut lrc:> exit

Jr

119

Capit olul 7

Interfaa

soc ket

n cadrul acestui capitol se prezint interfaa socket BSD


rei principa lele concept e referitoa re la comunic area n
i
ea. De asemene a, se descriu primitiv ele socket ()
socket pair ().

Preli mina rii

7 .1

mai multe metode de comunicare ntre procese. Pn n acest moment


De
am discuta t despre comunicarea ntre procese aflate pe aceeai main.
procese
acum ncolo ne vom ocupa de mecanismele de transmi sie de date ntre
aflate pe calcula toare diferite.
pot fi clasificate n:
Dup tipul conexiunii, aceste metode le de comunicare

Exist

orienta te-cone xiune dac cele dou procese (punctele finale


ale transmi siei de date) stabilesc o conexiune (virtual) nainte a efecturii
schimbului de date. Aceste comunicaii au loc n trei pai: stabilir ea conexiunii, transfe rul datelor i nchiderea conexiunii.

comunicaii

comunicaii fr

conexiune n care sunt folosite mesaje (numite i data)


grnme) pentru transm iterea informaiilor. Datagra mele sunt transmise
(
indepen dent i trebuie s conin toate informaiile necesare gsirii ma,
ntr-o
sosi
pot
i
fix
inii de destinaie. n mod normal au o lungime
1
ordine diferit de cea n care au fost trimise sau pot fi pierdut e.

orienta te-cone xiune mai pot fi clasificate


fluxul de date n:

Comunicaiile

se

realizeaz

simplex - datele pot circula ntr-o

dup

modul n care

singur direcie;

half duplex - direcia de transm itere a datelor poate fi schimbat, dar


datele se pot transm ite la un momen t dat ntr-un singur sens;
full duplex
direcii.

(bi-direcional) - datele se pot transmite simultan n ambele ,,

Interfaa

socket

121

Comunicaiile

pot fi blocante sau neblocante. O operaie de transmit ere sau


de date este blocant dac nu returneaz pn cnd operaia nu a fost
terminat (cu succes sau nu). Operaiile neblocan te returneaz
imediat.
Cel mai adesea comunicaiile ntre procese de-a lungul unei reele sunt realizate prin intermed iul unui set de reguli care formeaz o familie de protocoale.
Pentru Internet se utilizeaz familia de protocoale TCP/IP.
Familia de protocoa le TCP /IP const dintr-o stiv de protocoale, structurate pe nivele. Din punctul de vedere al program atorului sunt importa nte
mai ales urmtoarele protocoale (vezi i figura 7.1):
recepie

t
e

e
1:

le
11
)-

TCP (Transm ission Control Protocol} este un protocol orientat- conexiu ne care ofer posibilit atea de a realiza comunicaii full duplex sigure. Se
bazeaz pe protocol ul IP. TCP este de departe cel mai folosit
protocol n
comunic area n reele conectat e la Internet . Protocol ul TCP a fost definit
n mod oficial n docume ntul RFC 1 793.
UDP (User Datagra m Protocol} reprezint protocolul utilizat pentru comunicaii nesigure n mod neconec tat (prin intermed iul datagram
elor).
Detalii despre specificaiile protocul ui UDP pot fi gsite n RFC 768.
ICMP (Interne t Control Message Protocol} este folosit pentru tratarea
erorilor i controlul informaiilor vehiculate n Internet . Folosete protocolul IP pentru realizarea schimbului de date. n mod normal, procesele
utilizato r nu au nevoie s acceseze ICMP, deoarece mesajele acestuia sunt
procesat e de ctre software-ul TCP /IP.
IP (Interne t Protocol} reprezint protocolul de baz pentru UDP, TCP i
ICMP. Procesele utilizato r folosesc doar TCP sau UDP, folosirea direct
a protocol ului IP fiind rar ntlnit.

re

ARP ( Address Resoluti on Protocol) este un protocol utilizat la translatarea adreselor IP n adrese hardware (Etherne t).
RARP (Reverse Address Resoluti on Protocol}
ware n adrese Internet .

translateaz

adresele hard-

ar
~le

1 Documen tele

RFC (Request For Comment s) sunt documentele care descriu, reglemecanismele intime ale Internetului, n special ale protocoalelor.

menteaz i actualizeaz

Atelier de programar e n

122

reele

de calculatoa re

Figura 7.1: Stiva de protocoale TCP /IP

TELNET

SMPT

FTP

7~ I
!

RTP

//
fr,ans.m is.s ion

RTCP

Control Protocol

SNMP

DNS

RlP

DHCP

Use1r Oatag ram


Protocol

OSPF

r1eM~

IGMP
Internet ProtC>CQI

ARP

Ethernet

7.2

Interfaa

Token

Token

Bus

Ring

FGOi

socket

o facilitate general, independent de arhitectur a


hardware , de protocol i de tipul de transmisi une a datelor, pentru facilitarea
introcomunicrii ntre procese aflate pe maini diferite, conectate n reea,
odat
at
dus pentru prima dat n sistemul BSD 4.lc i mai apoi perfecion
cu versiunea BSD 4.2. Socket-urile sunt disponibile n toate versiunile UNIX
actuale.
Interfaa socket suport diferite protocoal e de comunicaie. Protocolu l domeniului UNIX este folosit pentru comunicaii ntre procese de pe aceeai
utimain (rulnd un sistem UNIX), iar protocolu l domeniul ui Internet este
lizat pentru comunicaii ntre procese de pe acelai computer sau de pe computere diferite conectate la reea, folosind TCP /IP. Desigur, sunt acceptate i
Interfaa

socket

reprezint

alte protocoal e de comunicaie.


Aadar, un socket poate avea tipuri diferite i poate fi asociat cu unul sau
mai multe procese, existnd n cadrul unui domeniu de comunicaie. Datele
pot fi schimbate numai ntre socket-uri aparinnd aceluiai domeniu de comunicaie.

>

(,'

Interfaa

/7''

Primitiv

socket( )
bind()
listen( )
accept( )
connect O
send()
receive ()
close ()
shutdow n()

~a

)-

X
)l

i1l

tU

123

Din punctul de vedere al program atorului , un socket arat i se comport


similar unui descript or de fiier. Primitivele precum read O sau wri te O lucreaz cu socket-u rile n aceeai manier n care lucreaz cu
fiiere i pipe-uri.
Diferenele dintre socket-u ri i descriptorii normali de fiiere
apar la crearea
unui socket i la o serie de operaiuni speciale de control al socket-u rilor. Aceste
operaiuni sunt diferite datorit complexitii suplime ntare
la stabilire a conexiunilor n reea compara tiv cu accesul normal la disc.
Cele mai uzuale apeluri care se pot executa asupra unui socket sunt sintetizate n tabelul de mai jos. Aceste primitive vor fi descrise n cadrul acestui
capitol i n capitolele urmtoare.
. -. .

'( Tabela

socket

,,
7.~;/Primitivele

care

acioneaz

asupra socket-urilor

Descriere
Creeaz un nou punct de capt al comunicaiei (socket)
Ataeaz o adres local la un socket
Permite unui socket s accepte conexiuni
Blocheaz. apelantu l pn la sosirea unei cereri de conexiune
(utilizat de serverul TCP)
Tentativ (activ) de a stabili o conexiune
(utilizat de clientul TCP)
Trimite date prin intermed iul unui socket
Recepioneaz date prin intermed iul unui socket
Inchide descriptorul de socket; se elibereaz conexiunea
Inchide direciona:! descriptorul de socket;
se elibereaz conexiunea

7.2.1

Primit iva socket ()

Pentru crearea unui socket vom utiliza primitiv a socket( ):


#includ e <sys/typ es.h>
#includ e <sys/so cket.h>
int socket( int domain, int type, int protoco l)

le
ll-

Cei trei paramet ri ai primitivei stabilesc: domeniul de comunicare, tipul


socket-ului i protocol ul de comunicare utilizat.
Valorile care poate s le ia domain n mod uzual sunt (prescur tarea AF
provine de la Address Family):

Atelier de progra mare n

124

reele

de calcula toare

UNIX);
AF_UNIX stabilete domeniul de comunicare local (domeniul
ntre procese aflate pe aceeai
diferite, folosind stiva de 'protocoale TCP /IP (do-

AF _!NET este utiliza t pentru

sau pe maini
meniul Intern et).

main

comunicaii

or
Argum entul domain permit e deci stabili rea format ului adreselor mainil
va
socket
implicate n transfe rul de date. Pentru domeniul Interne t, fiecare
numr de
avea asociat o adres) format din adresa IP a mainii gazd i un
8).
ul
16 bii, local gazdei respective, denum it port (vezi capitol
a putea folosi i alte
Interfaa socket este suficient de general pentn,i
e precum
domenii de transmisie, recurg nd la protocoale prezente pe sistem
AF se poate
Novell (AF _IPX) sau Apple (AF _APPLETALK). n locul prefixului
, consta nta
(aadar
Family
ol
utiliza i PF, PF fiind prescu rtarea de la Protoc
AF _UNIX este echivalent cu PF _UNIX).
modal itatea de realizare a comunicrii. Cele
,/Tipu l socket-ului se refer la
mai utiliza te dou valori sunt:

ale nregis SOCK_STREAM stabilete un flux (stream) de date fr limite

Livrar ea ntr-un mediu de reea este garantat; dac livrare a


ieste imposibil, expedi torul primete un indica tor de eroare. Comun
carea deci se va realiza full-duplex, sigur, orientat-conexiune.

trrilor.

SOCK_DGRAM va stabili o comunicare

fr

conexiune,

nesigur,

folosind

S.atagrame.

ofer

un acces la protocolul ;l
Se mai poate folosi consta nta SOCK_f\AW care
( e.g. protocolul IP), de nivel inferio ;/Pent ru mai multe detalii,
1

manualul.
t penArgum entul protoc ol specific protocolul particu lar care va fi utiliza
ntldes
tru transm isia datelo r. Valoarea O pentru acest argum ent este foarte
protocol permis cu perechea
nit. Aceast a permit e sistem ului s aleag primul
egal cu
de valori specificate pentru familie i tip. De exemplu, pentru domain
de transp ort
AF _!NET i type egal cu SOCK_STREAM se va considera protocolul
egal cu
TCP, iar pentru cazul n care domain este egal cu AF _!NET i type
UDP.
SOCK_DGRAM se va consid era implicit protocolul de transp ort
de fiier
n caz de succes, primit iva socke t() returneaz un descriptor
errno
ila
eaz -1 i variab
ataat socket-ului, iar n caz de eroare, se return
,
descrie proble ma survenit.

reea

consultai

,,1.

Interfaa

7.2.2

socket

125

Exem plu

Ne propun em n continu are s crem mai multe socket-u ri, fixnd diferite
valori
pentru cele trei argume nte ale primitivei socket O. Vom observa c anumit
e
combinaii sunt invalide, return ndu-se eroare.
#includ e
#includ e
#includ e
#includ e
#includ e

<sys/ty pes.h>
<sys/so cket.h>
<netin et/in.h >
<stdio. h>
<errno. h>

extern int errno;

l
l

ll
ii
l-

l:a

u
rt
:u
;)[

lO

~f

I* domeniul (famili a) 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 };
char *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 socket */
int TYPE[6] =
{ SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SDCK_RAW, SOCK_RDM,
SDCK_PACKET };
char *YPE_names[6] =
{ "SDCK_STREAM", "SDCK_DGRAM", "SOCK_SEQPACKET",
"SOCK_RAW", "SDCK_RDM", "SOCK_PACKET" };
I* protoc oalele de comuni catie */
int PROTOCOL[25] = {
IPPROTD_IP,
/* Dummy
" protoco l for TCP. */
IPPROTD_HOPOPTS,
/* IPv6 Hop-by-Hop option s. ~/
IPPROTD_ICMP,
/* Interne t Contro l Message Protoc ol.
IPPROTD_IGMP,
/* Interne t Group Management Protoc ol.
IPPROTD_IPIP,
I* IPIP tunnels */
IPPROTD_TCP,
/* Transm ission Contro l Protoc ol. */
IPPRDTO_EGP,
I* Exterio r Gateway Protoc ol. */
IPPROTO_PUP,
I* PUP protoc ol. */
IPPROTD_UDP,
/* User Datagram Protoc ol. *I
IPPROTD_IDP,
I* XNS IDP protoc ol. */
IPPROTO_TP,
I* SO Transp ort Protoc ol Class 4. */
IPPRDTO_IPV6,
I* IPv6 header. */
IPPROTD_ROUTING,
I* IPv6 routing header. */
IPPROTO_FRAGMENT,
I* IPv6 fragme ntation header . */
IPPROTD_RSVP,
I* Reserv ation Protoc ol. */
IPPROTD_GRE,
/* Genera l Routing Encaps ulation . */

Atelier de progr amare n

126

IPPROTO_ESP,
IPPROTO_AH,
IPPROTO_ICMPV6,
IPPROTO_NONE,
IPPROTO_DSTOPTS,
IPPROTO_MTP,
IPPROTO_ENCAP,
IPPROTO_PIM,
IPPROTO_RAW,

I*
/*
/*
/*
/*
/*

I*
I*
I*

reele

de calculatoare

encap sulat ing secur ity paylo ad. */


authe ntica tion heade r. */
ICMPv6. */
IPv6 no next heade r: .*/
IPv6 desti natio n optio ns. *I
Mult icast Trans port Proto col. */
Enca psula tion Header. */
Proto col Indep enden t Mult icast . */
Raw IP packe ts. */

};

char *PROTOCOL_names[25] = {
"Dummy proto col for TCP.",
"IPv6 Hop-by-Hop optio ns. ",
"Inte rnet Cont rol Message Proto col." ,
"Inte rnet Group Management Proto col." ,
"IPIP tunne ls (olde r KA9Q tunne ls use 94)." ,
"Tran smiss ion Cont rol Proto col." ,
"Exte rior Gateway Proto col." ,
"PUP proto col." ,
"User Datagram Proto col." ,
"XNS IDP proto col." ,
11
"SO Trans port Proto col Class 4 . '
"IPv6 head er.",
"IPv6 routi ng head er.",
"IPv6 fragm entat ion head er.",
"Rese rvatio n Proto col." ,
"Gen eral Routi ng Enca psula tion." ,
"enca psula ting secu rity paylo ad.",
"auth entic ation head er.",
"ICMPv6",
"IPv6 no next head er.",
"IPv6 desti natio n optio ns.",
"Mul ticas t Trans port Proto col." ,
"Enc apsul ation Head er.",
"Prot ocol Indep enden t Mult icast .",
"Raw IP pack ets."
};

int
main O
{

i de tablo u */
int ii = 10, ij = 6, ik = 26; /* valor i maxime indic
*/
I* itera tori
int i, j, k;
sd;
int

Interfaa

socket

127

printf ("Famil ie:\n");


for (i = O; i < ii; i++)
printf (" %d-%s\n", AF[i], AF_nam es[i]);
printf ("Tipur i de socket: \n");
for (j =O; j < ij; j++)
printf (" %d-%s\n", TYPE[j] , TYPE_names [j]);
printf ("Proto coale:\n ");
for (k = O; k < ik; k++)
printf (" %d-%s\n", PRDTOCOL[k], PROTOCOL_names[k]);
/* incercam toate combin atiile posibile */
for (i = O; i < ii; i++)
for (j =O; j < ij; j++)
for (k = O; k < ik; k++)
{

printf ("Apel socket(% s, %s, %s)\",


AF_nam es[i], TYPE_names[j], PROTOCOL_names[k]);
fflush (stdout );
/* incercam sa cream un socket ... */
if ((sd = socket (AF[i], TYPE[j], PROTOCOL[k]))
{

-1)

perror ("N-am putut crea socket- ul: ");


/* afisam eroarea surveni ta */
switch (errno)
{

case EPRDTDNDSUPPORT:
printf ("Nu este suporta t protoco lul\n");
break;
case ENFILE:
printf
("Nu este suficien ta memorie "
"pentru alocare a unui nou socket\ n");
break;
case EMFILE:
printf ("Depas ire in tabela de alocare a fisierel or\n");
break;
case EACCES:
printf
("Nu avem permisiu nea de a crea un socket "
"de tipul sau protoco lul specifi cat\n") ;
break;
case ENOBUFS:
case ENOMEM:
printf ("Nu este suficien ta memorie \n");
break;
case EINVAL:

Atelier de programare n

128

reele

de calculatoare

prin tf
("Pr otoc ol necu nosc ut sau fami lie de "
"pro toco ale inex isten ta\n ")i
brea k;
}

cont inue ;
}

clos e (sd) ;
}

retu rn O;
}

7.2. 3

Prim itiv a soc ketp airO

este asemntoare apelului sock et O, fiind


crear ea unei perechi de socket-uri conectate.

Aceast primitiv

utilizat

pent ru

#inc lude <sys /type s.h>


#inc lude <sys /soc ket.h >
prot ocol , int sv[2 ]);
int sock etpa ir(in t doma in, int type , int

sock et, iar ultimul este un


Prim ele trei argu men te sunt ca i cele de la
creai n urma execuiei acestei
vector coninnd cei doi descriptori de socket
primitive.
apelul pipe O, socket-urile
Sem antic a primitivei este apropiat celei de la
procese nrudite.
crea te prin sock et pair O put nd fi folosite de

7 .2.4

Exe mpl u

ntre
Un exemplu simplu de trans mite re de mesaje
este cel de mai jos:

dou

procese

etpa ir() */
/* Com unic atie intr e proc ese folo sind sock
#inc lude <sys /type s.h>
#inc lude <sys /soc ket.h >
#inc lude <net inet/ in.h >
#inc lude <std io.h>
#inc lude <err no.h >
#inc lude <uni std.h >
#inc lude <str ing.h >
#inc lude <sys /wai t.h>

printe-copil

Interfaa

socket

int
main ()
{

int sd[2];

/* mesajele vehiculate */
char *P = "Eu sunt parintele";
char *c = "Eu sunt copilul";
char buf[100];
/* se creaza perechea de socket-uri, folosindu-se
domeniul de adrese AF_UNIX */
if (socketpair (AF_UNIX, SOCK_STREAM, O, sd) == -1)
{

l
n
~i

perror ("Eroare la creare socket");


exit (1);
}

/* incercam sa cream un proces copil */


switch (fork O)
{

case -1:
/* eroare*/
perror ("Eroare la fork() ");
exit (1);
break;
case O: /* fiul */
I* citim din socket mesajul *I
if (read (sd[1], buf, 100) <O)
{

perror ("Eroare la read() ");


exit (2);
}

1il

\!

printf ("Procesul cu PID-ul %d (copilul) a primit: '%s'\n",


getpid (), buf);
I* scriem mesajul copilului */
if (write (sd[1], c, 100) < O)
{

perror ("Eroare la write() ");


exit (2);
}

/* trimitem EOF */
clase (sd[1]);
/* am terminat */
exit (O);
default:
/* parintele */
I* trimitem mesajul parintelui */
if (write (sd[O], p, 100) < O)
{

perror ("Eroare la write()");

129

Atelier de programare n

130

reele

de calculatoare

exit (3);
}

/* citim mesaj ul primi t de la copil */


if (read (sd[O ], buf, 100) < O)
{

perro r ("Ero are la read( ) ");


exit (3);
}

t: '%s'\n ",
print f ("Pro cesul cu PID-u l %d (parin tele) a primi
getpi d O , buf) ;
/* astept am termi narea copil ului */
if (wait (NULL) < O)
{

perro r ("Ero are la wait O");


exit (3);
}

/* gata! */
close (sd[O ]);
return (O);

}
}

7.3

Exerciii

) s se conceap
1. Folosi nd perech i de socket -uri gener ate de socke t pair(
ie matem atun progr am care citete de la intrar ea stand ard o expres
proces e copil. Fiecar e proces
ic care va fi transmis spre evalua re unor
-et-impera,
copil va evalua o sub-ex presie matematic, n manier divide
rezult atul
return nd prin socket valoar ea ei. Proce sul printe va primi
vor putea {
final i-l va afia la ieirea standa rd. Expresiile matem atice
I
teze.
paran
i
uzuali
conine opera nzi ntreg i, opera tori aritme tici

2.

reea de tip
se rescrie exerciiul referit or la detect area staiilor dintr- o
-uri.
socket
ns
magistral (propu s n capito lul 5), utiliz ndu-se

<
<

! ,,y.

Capitolul 8

~)') \~'-,;jif. - (' .~'( ~ ,~).'.' '(J


:t-_,.\\': ,(>1, 1
~

1
)[.'

Modelul clien t/serv er - TCP


Acest capitol prezint maniera de scriere a programelor
server i a programelor client utiliznd protocolul de transport TCP ( Transmission Control Protocol).

8.1

p
~t
~es

ra,
tul
;ea

tip

Modelu l client/se rver

n cadrul aplicaiilor n reea paradigma cel mai frecvent folosit este modelul
client/serv er, n care un server ofer anumite servicii anumitor clieni rulnd
pe aceeai main sau la distan (pe alte calculatoare conectate la reea).
Serverul poate satisface cererile provenite de la clieni n mod iterativ (la
un moment dat serverul va deservi cereri provenite de la un singur client,
secvenial pentru toi clienii lui) sau concurent (mai multe cereri, provenite de
la clieni multipli, vor fi procesate simultan). Serverele concurente sunt folosite
mai ales atunci cnd rspunsul nu poate fi transmis napoi imediat sau n
cazul n care trebuie realizate comunicaii suplimenta re cu clientul. De obicei,
serverele concurente folosesc protocoale de comunicaie orientate conexiune.
Astfel, serverul concurent rspunde oricror cereri client independen,t de toi
.

.
.
,J
!
,
I
>"'1
,
, , ,,
ce1lal1 clieni.
t'
, \, p
, "Y'JJ.<l/JJ1!J ,, ' 1 ,_1
1-s._ .
Comunicar ea dintre server i clienii si se va realiza prin intermediul suitei
de protocoale TCP /IP, prin intermediul socket-urilor, utilizndu-se stream-uri
de octei (protocolul TCP) sau datagrame (protocolul UDP). Despre UDP vom
discuta detaliat n capitolul 9.
t

8.2

Server TCP iterativ

Modelul general al unui server TCP iterativ


apeluri de sistem:
socket O

creeaz

respect

ordinea

un socket care va trata conexiunile cu

pregtirea

urmtoarelor

clienii;

structurilor de date (coninute n sockaddr_i n) pentru a


socket-ul la portul folosit de aplicaie;

bind()

ataeaz

socket-ul la port;

ataa

Atelier de programare n

132

reele

de calculatoare

listen() pregtete socket-ul pentru ascultarea portului n vederea stabilirii conexiunii cu clienii;
accept() ateapt realizarea unei conexiuni c:u un anumit client (acest
apel blocheaz programul pn la apariia unei cereri de conectare din
partea unui client);
procesarea cererilor clientului - schimb de mesaje ntre server i client
folosindu-se descriptorul de socket returnat de accept (), prin apelul
primitivelor read() i writeO;

close O nchiderea conexiunii cu clientul la terminarea dialogului cu


acesta;
shutdown () nchiderea
Schema
figura 8.1.

general

direcional

a conexiunii cu clientul.

a programelor server

client TCP poate fi

Figura 8.1: Modelul general al serverului

urmrit

clientului TCP

c
s

cerere
}

rspuns

es

server TCP

client TCP
st

Conexiunea client-server, odat stabilit, rmne valid pn cnd, fie


clientul, fie serverul o ntrerup n mod explicit sau nu. Socket-ul utilizat pentru

Modelul client /serve r - TCP

133

aceast

cone xiune se numete socke t orien tat pe conex


iune. Desig ur, un socke t
poate fi folos it la un mom ent dat pentr u mai
multe conex iuni.
t

8.2.1

11

Prim itiva bind ()

Aa cum am vzut n capit olul prece


dent, apelu l sock et() va retur na un descript or de socket. Pent ru a fi efect iv folosit,
va trebu i ca acest socke t s fie
ataat la portu l mainii la care va
ascul ta serve rul cerer i de cone xiune din
parte a posib ililor clieni. Vom realiz a acest lucru
prin inter medi ul primi tivei
bind () al crei proto tip este urmtorul

#incl ude <sys /type s.h>


#incl ude <sys /sock et.h>
int bind (int sock d, struc t socka ddr *add
r, sock len_t addr len);

Struc tura sock addr este una generic (o putem


privi ca o clas abstract)
menit a stoca informaii de adres
pentr u orica re tip de socke t-uri.
Ea este
definit astfel :
struc t socka ddr {
unsig ned shor t sa_fa mily ;
char

I* fami lia de adres e

(AF_UNIX, AF_INET, ... ) */


sa_d ata[1 4]; /* 14 bytes - adres a folo sita
*/

strncbff partlculnr

'1 J Pent rn Inter net, ne vom folosi de o


(asem eni unei
clase deriv ate din clasa abstract): sock addr _in
care are mem brii:
struc t sock addr_ in {
shor t int

ie
u

sin_f amil y;

unsig ned shor t int

sin_ port;

struc t in_ad dr
unsig ned char

sin_a ddr;
sin_z ero[S ];

/* fami lia de adres e


(AF_INET) */
/* portu l
(0-65 355) */
/* adres a Inter net */
/* bytes neut iliza ti
(zero ) */

nain te de utiliz area acest ei struc turi, trebu ie


s ne asigurm c sin_ zero
este nul, prin folos irea uneia dintr e funciile bzer
o () sau mems et ().
Adre sa Inter net este stocat de struc tura in_a
ddr care are definiia:
struc t in_ad dr {
unsig ned long int s_ad dr;
/* 4 bytes ai adre sei IP */

Atelier de programare n

134

reele

de calculatoare

pent ru a se utiliza adresa


Serverul va pute a folosi cons tanta INADDR_ANY
asigur independena codului
IP a mainii pe care ruleaz procesul (aceasta
uta serverul). Dac n loc de
de adre sa IP a calculatorului pe care se va exec
atunCi bind () va alege un port
un numr de port valid vom utiliza valoarea O,
liber pent ru a-l asocia socket-ului n cauz.
stabilim ca valoare pent ru
Pent ru a oferi servicii nestandard, va trebu i s
la 1 la 1023 sunt rezervate serport un numr mai mare de 1024. Valorile de
/etc /ser vice s pent ru a vedea
viciilor sistem stan dard (putei consulta fiierul
sistem; de exemplu: portu l
perechile port- serv iciu stan dard furnizate de ctre
n prin teln et, port ul 25 este
23 este asignat serviciului de conectare la dista
SMT P (Simple Mail Transfer
folosit de aplicaiile implementnd protocolul
ii i serverele World-Wide
Protocol), iar port ul 80 se utilizeaz de ctre clien
erText Transfer Protocol).
Web, prin intermediul protocolului HTT P (Hyp
leasc o conexiune cu o
n parti cula r, orice proces care dorete s stabi
un fiier, utiliznd protocolul FTP , se poate
main gazd pent ru a trans fera
ru a cont acta serverul ( daeconecta la port ul 21 al mainii destinaie pent
mon ul) FTP .

8.2. 2

Prim itiv a list en ()

conexiuni de
portului, serverul va trebui s atepte viitoare
a apelul
utiliz
ru aceasta vom
la diveri clieni i s le rezolve cererile. Pent
liste n O urm at apoi de acce pt O.
Prot otipu l primitivei liste n() este:

Dup ataarea

#inc lude <sys /soc ket.h >


int liste n(in t sock d, int back log) ;

xiuni permise n coada de


Al doilea para metr u va stabili numrul de cone
ii. Uzual, valoarea sa este 5.
ateptare a conexiunilor cu clien

8.2. 3

Prim itiv a acce pt()

Acceptarea

propriu-zis

a conexiunilor se va realiza cu acce pt():

#inc lude <sys /type s.h>


#inc lude <sys /soc ket.h >
r, sock len_ t *add rlen) ;
int acce pt(in t sock d, stru ct sock addr *add
corespunztor clientului a
Acest apel va retur na un descriptor de socket
stabilindu-se astfel un canal de comunicaie
crui conexiune a fost acceptat,

'.

'

Modelul client/server - TCP

135

duplex ntre server i client. Noul descriptor va putea fi folosit pentru a trimite
i a recepiona date via reea prin mijlocitori precum send O sau wri te() i
recv() sau read(), respectiv.
Argumentul addr va conine informaii despre adresa IP i portul folosite
de clientul conectat la server, iar lungimea acestei structuri va fi stocat de
ultimul argument addrlen.

a
Li
.e
t
u
r-

8.2.4

ul
te
er
ie

Primitivele send()

Primitivele send()

recv()

recv() au prototipurile de mai jos:

#include <sys/types.h>
#include <sys/socket.h>

o
,te

int send(int sockd, char *buf, int len, int flags)


int recv(int sockd, char *buf, int len, int flags)

~e-

n ambele cazuri, sockd reprezint descriptorul de socket.


Pentru send (), argumentul buf indic o zon de memorie care conine
datele ce trebuie trimise, len este lungimea datelor i argumentul flags va fi
de obicei O. Valoarea returnat este numrul de octei trimii n caz de succes.
Dac eueaz, este returnat -1, iar errno descrie eroarea.
Pentru apelul recv(), argumentul buf indic o zon de memorie n care se
vor copia datele recepionate, len semnific mrimea acestor date n octei i
flags este de obicei O sau setat la valoarea MSG_PEEK dac datele recepionate
trebuie reinute i dup ce sunt recepionate. Valoarea returnat este numrul
de octei recepionai n caz de succes. Dac primitiva eueaz, este returnat
valoarea -1.

de
lul

de

8.2.5

Primitivele el o se()

Dup

shutdown()

realizarea dialogului cu clientul, nchiderea conexiunii se va putea face


prin apelul primitivei el ose (). Orice ncercare de a citi sau scrie date folosind
un socket nchis va genera eroare.
Pentru a controla modul de nchidere al socket-ului, vom putea folosi primitiva shutdown O:
#include <sys/socket.h>
int shutdown(int sockd, int how);

ui a
:aie

'.

'

Atelier de programare n

136

reele

de calculatoare

Argum entul how va avea una dintre valorile:


O viitoarele citiri de pe socket nu vor mai fi permise;
1 viitoarele scrieri pe socket nu vor mai fi permise;
2 citirile/scrierile nu vor mai fi permise (similar cu close ()).
Fiecare dintre apelurile prezent ate mai sus va returna valoarea -1 dac a
surveni t o eroare (variabila errno va putea fi folosit pentru a afla mai multe
amnunte despre eroarea respectiv).
Rezum nd, ordinea apelurilor la un server TCP iterativ va fi:
socket ()
bind()
listen ()
accept ()
read() , write( )
shutdo wn()
close( )

8.3

Clien t TCP

n cazul unui client TCP, vom folosi n locul apelului accept () primiti va dual
connec t O care are forma urmtoare:
#includ e <sys/ty pes.h>
#includ e <sys/so cket.h>
);
int connec t(int sockd, struct sockadd r *addr, socklen _t addrlen

Structu ra addr va trebui s conin adresa IP i portul serverului la care


se va conecta clientul.
Apelul listen () nu va mai aprea, iar fiecrui read() din server i va
i
corespunde un apel wri te() la client. La fel, fiecrui apel wri te() n server
va corespunde un re ad O la client.

Modelul client/server - TCP

137

Astfel, ordinea apelurilor n cadrul unui client TCP va fi:


socket()
bind()
connect()
.a
lte

write(), read()
shutdown()
close()

8 .4

Conversia datelor

Comunicarea dintre server i client se realizeaz n cadrul unor reele eterogene,


cu configuraii hardware pe care programatorul nu le poate prevedea. Pentru
a se asigura independena transferului de date, s-a convenit ca octeii pe reea
s respecte o anumit ordine de codificare. Ordinea octeilor dintr-un cuvnt
(word - 2 octei) se poate realiza n dou moduri:
big endian cel mai semnificativ octet este primul (codificare
cesoarele Motorola, de exemplu);

folosit

little endian cel mai semnificativ octet este al doilea (codificare


procesoarele Intel).
'. ">"'-.-'
'

,al

,:_+

'f'1... ,'"
.

.,

j'';

'\"'

"' ..J

-~ ,

:..-'.

'
''

.:'<fi

" t ~ .. ~

i va
er i

'

'I l;

utilizat
.i'
,1
')

_.,,}::r

de

octei)

de la

gazd

la

reea

htonl O conversie a unui ntreg lung (4 octei) de la gazd la reea


host to network long;

ntohs () conversie a unui ntreg scurt (2


network to host short;
ntohl () conversie a unui ntreg lung (4
network to host long.

octei)

octei)

de la
de la

reea

reea

la
la

gazd

gazd

l:

Astfel, pentru ca programele s fie independente de codificarea aleas~ vom


recurge la folosirea unor funcii de conversie de la codificarea de reea la cea a
calculatorului gazd. Aceste funcii (ale cror prototipuri se regsesc n fiierul
antet netinet/in. h) sunt urmtoarele:
htons() conversie a unui ntreg scurt (2
host to network short;

~are

'

'~.~ r'Jt}~/

de pro-

""

"t

Atelier de programare n

138

reele

de calculatoare

De notat c membrii sin_port i sin_addr ai structurii sockaddr _in


trebuie s fie stocai n codificarea de reea.
Pentru a asigura conversia adreselor IP de la formatul intern (patru bytes)
la cel uzual (patru octei desprii de punct) sau invers vom folosi funciile:
inet_addr () realizeaz conversia de la un ir de caractere coninnd o
adres IP la ntreg; dac se furnizeaz o adres IP invalid va fi returnat
constanta INADDR_NONE - aceast funcie se consider a fi demodat,
fiind substituit de funcia inet_aton (), dar poate fi utilizat pentru
compatibili tate cu vechile aplicaii;
inet_aton () realizeaz conversia de la un ir de caractere coninnd
o adres IP la ntreg; n cazul unei adrese IP incorecte, se va returna
valoarea zero;
inet_ntoaO este duala funciei de mai sus, convertind adresa IP
ca ntreg n ir de caractere.

8.5

dat

Alte primitiv e utile

n cadrul aplicaiilor pot fi utilizate i alte primitive utile precum:


getsocknam e O - va returna numele curent al unui socket dat (local)
prototipul de mai jos:

are

#include <sys/socke t.h>


sockfd,
int getsocknam e(int
struct sockaddr *name,
*namelen);
socklen_t

getpeernam e () - furnizeaz informaii despre cellalt


lizate prin intermediul unui socket (la distan).

Acest apel are

urmtoarea form:

#include <sys/socke t.h>


sockfd,
int getpeernam e(int
sockaddr *name,
struct
socklen_t *namelen);

capt

al conexiunii rea-

Modelul client/server - TCP

.n

139

Exemplu

8.6
I

s)
o
t

\ Furnizm un exemplu de server TCP iterativ, nsoit de clientul lui. Serverul


I primete un ir de caractere de la client i l trimite napoi (n ecou). Clientul
citete un ir de caractere de la intrarea standard, l trimite serverului, apoi
ateapt ca serverul s i-l returneze.
Sursa serverului (stocat n fiierul server-tcp. c) este urmtoarea:

ru

server-tcp.c

id

/* Server TCP iterativ (echo)

ria
.t

~re

Asteapta un mesaj de la clienti;


mesajul primit este trimis inapoi

/
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<errno.h>
<unistd.h>
<stdio.h>
<string.h>
<stdlib.h>

/* portul folosit /
#define PORT 8081
/ codul de eroare returnat de anumite apeluri /
extern int errno;

/* programul /

ea-

int
main O
{

/ structurile folosite de server si client /


struct sockaddr_in server;
struct sockaddr_in from;
char buffer[100];
/* mesajul trimis de client /
int sd;
I descriptorul de socket */

I* cream un socket /
if ((sd = socket (AF_INET, SOCK_STREAM, O))
{

perror ("Eroare la socket(). \n");

-1)

Atelier de programare n

140

reele

de calculatoare

return errno;
}

/* pregatim structurile de date */


bzero (&server, sizeof (server));
bzero (&from, sizeof (from));
/* umplem structura folosita de server */
server.sin_fa mily = AF_INET;
/* stabilirea familiei de socket-uri */
server.sin_ad dr.s_addr = htonl (INADDR_ANY);
/* acceptam orice adresa */
server.sin_po rt = htons (PORT);
/* utilizam un port utilizator */
/* atasam socketul */
if (bind (sd, (struct sockaddr *) &server,
sizeof (struct sockaddr)) == -1)

perror ("Eroare la bind() . \n") ;


return errno;
}

/* punem serverul sa asculte


daca vin clienti sa se conecteze */
if (listen (sd, 5) == -1)
{

perror ("Eroare la listen(). \n");


return errno;
}

/* servim in mod iterativ clientii ... *I


while (1)

int client;
int length = sizeof (from);
printf ("Asteptam la portul %d ... \n", PORT);
fflush (stdout);

I* acceptam un client
(ne vom bloca pina la realizarea conexiunii) */
client= accept (sd, &from, &length);

I* eroare la acceptarea conexiunii de la un client */


if (client < O)

*
#
#

Modelul client/server - TCP

141

perror ("Eroare la accept(). \n");


continue;
}

I* am realizat conexiun ea, asteptam mesajul ... */


bzero (buffer, 100);
printf ("Astepta m mesajul. .. \n");
fflush (stdout);
I* citirea mesajulu i */
if (read (client, buffer, 100) <=O)
{

perror ("Eroare la read O de la client. \n") ;


close (client);
/* inchidem conexiune a cu clientul */
continue ;
/*continu am sa ascultam ... *I

printf ("Mesaju l a fost reception at ... \n"


"Trimitem mesaj ul inapoi ... ") ;

I* returnam mesajul clientulu i */


if (write (client, buffer, 100) <= O)
{

perror ("Eroare la write() catre client. \n");


continue ;
/* continuam sa ascultam */

el se
printf (" trasmiter e cu succes.\n ");
I* am terminat cu acest client, inchidem conexiun ea*/
close (client);
}
I* while */
/* main */

Codul

surs

al clientului TCP este cel de mai jos:

client-tc p.c

I*

Client TCP (echo)


Trimite un mesaj unui server;
mesajul trimis este reception at de la server

#include <sys/type s.h>


#include <sys/sock et.h>

Atelier de program are n

142
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de
#inclu de

reele

de calculatoare

<netin et/in.h >


<errno .h>
<unist d.h>
<stdio .h>
<stdli b.h>
<netdb .h>
<strin g.h>

/* codul de eroare return at de anumit e apelur i */


extern int errno;
/* portul de conect are la server */
int port;
/* progra mul */
int
main (int argc, char *argv[ ])
{

I* descri ptorul de socket */


int sd;
I* struct ura folosi ta pentru conect are */
struct sockad dr_in server ;
I* mesaju l trimis */
char buffer [100];

I* exista toate argum entele in linia de comanda? */


if (argc ! = 3)
{

printf ("Sint axa: %s <adres a_serv er> <port> \n", argv[O ]);
return -1;

/* stabil im portul */
port= atei (argv[ 2]);

I* cream socke tul *I


if ((sd = socket (AF_INET, SOCK_STREAM, O))
{

perror ("Eroa re la socke t(). \n");


return errno;
}

/* umplem struct ura folosi ta pentru


realiz area conex iunii cu server ul */
server .sin_f amily = AF_INET;
/* famili a socke t-ului */

-1)

Modelul client/server - TCP

143

server.sin_ad dr.s_addr = htonl (inet_addr(ar gv[1]));


/* adresa IP a serverului */
server.sin_p ort = htons (port);
/* portul de conectare */

/* ne conectam la server */
if (connect (sd, (struct sockaddr *) &server,
sizeof (struct sockaddr)) == -1)
{

perror ("Eroare la connect(). \n");


return errno;
}

I* citirea mesajului si trimiterea catre server */


bzero (buffer, 100);
printf ("Introduceti mesajul: ");
fflush (stdout);
read (O, buffer, 100);
if (write (sd, buffer, 100) <= O)
{

perror ("Eroare la write() spre server. \n");


return errno;
}

I* citirea raspunsului dat de server


(ne blocam pina cind serverul raspunde) */
if (read (sd, buffer, 100) < O)
{

perror ("Eroare la read() de la server. \n");


return errno;
}

/* af isam mesajul primit */


printf ("Mesajul primit este: '%s'. \n", buffer);

I* inchidem conexiunea, am terminat */


close (sd);
}

Clientul va necesita dou argumente date n linia de comand semnificnd


adresa IP a serverului i portul de conectare la server. Dac ambele programe
ruleaz pe aceeai main, atunci vom putea introduce:
(infoiasi):-$ ./server-tcp
(infoiasi):-$ ./client-tcp 127.0.0.1 8081

Atelier de programare n

144

reele

de calculatoare

se dorete a fi folosit un alt port, se va modifica n server numrul


portului (constanta PORT) i se va recompila programul.
Pentru a facilita compilarea programelor, ne put~m sluji de urmtorul fiier
utilizat de comanda make:
Dac

# fisier folosit pentru compilarea


# serverului si clientului TCP iterativ

all:
gcc server-tcp.c -o server-tcp
gcc client-tcp.c -o client-tcp
clean:
rm -f

*-

client-tcp server-tcp

Comanda make este utilizat pentru a automatiza diverse sarcini realizate


la compilarea unui program de mai mari dimensiuni. Dei uzual folosit n
cazul compilrii programelor C (programe surs, antete, fiiere obiect, biblioteci
utilizator etc.), comanda make poate fi util la reactualizarea automat a altor
programe surs (e.g. fiiere Tu\TEX).
Pentru a executa make vom avea nevoie de un fiier Makefile (citit implicit
de make n lipsa altei opiuni) care va descrie relaiile dintre fiierele utilizate la
generarea unei aplicaii i va stabili comenzile care vor fi rulate pentru fiecare
dintre aceste fiiere.
Un fiier Makefile va fi format din macro-definiii precedate de caracterul
TAB sau de":" i din relaii de dependen de forma ref : refi reh refN,
unde ref poate fi un fiier surs sau obiect. De asemenea, pot fi utilizate comentarii precedate de caracterul"#". O macro-definiie este asemntoare operaiei
de atribuire de variabile (de forma identif = valoare), iar valoarea unei variabile definite n acest mod va putea fi accesat prefixnd numele variabilei
cu"$" i ncadrndu-l ntre paranteze, adic prin construcia $ (identif).
Pentru a reactualiza toate fiierele dependente de o referin vom executa
make avnd ca argument numele acelei referine. Pentru exemplul de mai sus,
rulnd make clean vor fi terse fiierele temporare rezultate n urma editrii i
cele executabile. Dac nu se specific nici un nume de dependen, se va ncerca
satisfacerea dependenei cu numele all (n acest caz, compilarea serverului i
clientului).
Comanda make accept o serie de opiuni, dintre care poate fi menionat
opiunea -f care permite procesarea unui fiier de reguli al crui nume este
diferit de Makefile. Pentru mai multe detalii, consultai manualul.

Modelul client/server - TCP

145

Exerciii

8. 7

1.

cum se poate observa, serverul va servi iterativ, client dup client,


toi clienii posibili. Modificai serverul astfel nct clienii s fie procesai
n manier concurent, prin utilizarea apelului fork(). Atenie la apariia
proceselor zombie !

2.

se scrie un server TCP concurent care s poat servi un numr maxim


de N clieni simultan i care s primeasc un ir de la client i s returneze
clientului irul n ordinea invers a caracterelor.

3.

se scrie un server TCP concurent care accept maxim i clieni simultan, ateapt un numr N i returneaz fiecrui client lista numerelor
prime de la 1 pn la numrul N.

4.

Scriei

Dup

;e

n
~i

>r

it
la
:e

1-

ei
aei
ta
B,

:a
i
t

te

un server TCP care ateapt zece cifre de la un client


acelui client maximul dintre cifrele primite.

i returneaz

5. Fie un server TCP concurent care recepioneaz de la fiecare dintre


clienii conectai cte o linie de maxim 80 de caractere. De la un client
vor fi recepionate maximum 33 de linii, dup care serverul va trimite
clientului numrul de litere prezente n liniile receptate i va nchide
conexiunea cu acel client. Serverul poate avea con~ctai maximum trei
clieni simultan.

8.8

Rezolvri

Exerciiul

3. Numere prime

Pentru rezolvarea servirii concurente a mai multor clieni se va folosi fork(),


procesul copil creat deservind un anumit client. De reinut faptul c atunci
cnd un proces fiu i termin execuia, el va emite un semnal SIGCHLD (vezi
i capitolul 4) ctre printe. Dacii printele nu va trata acest semnal, atunci
procesul copil va rmne zombie (sau <defunct>, dup cum se observ cu
ps x). Pentru evitarea acestei situaii, atunci cnd printele primete SIGCHLD
va executa primitiva de ateptare wai t () care are ca efect "ieirea curat" a
copilului, adic se vor elibera toate resursele alocate i procesul se va termina
normal.
Atunci cnd se scrie la client (adic la socket-ul asociat conexiunii) i se ntrerupe sau se nchide conexiunea, procesul server va primi un semnal SIGPIPE
i serverul se va termina n mod forat (aceasta este aciunea implicit la

Atelier de programare n

146
apariia

reele

de calculatoare

semnalului SIGPIPE). Pentru a evita

aceast situaie,

se va ignora

semnalul SIGPIPE.
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types .h>
<sys/socke t.h>
<netinet/in .h>
<unistd.h>
<error.h>
<string.h>
<stdlib.h>
<signal.h>

I*

portul folosit */
const int PORT_SERVER = 9001;
/* numarul maxim de clienti acceptati */
const int CLIENTI_MAXIM = 10;
extern int errno;
int ds;
int de;
O;
int nr

I*
I*

eroarea returnata */
descriptor pentru server */
/* descriptor pentru client */
I* numarul de clienti *I

void
semnal (int nr_semnal) /* functia de tratare a semnalelor */
{

if (nr_semnal

==

SIGCHLD)

wait (NULL);
nr--; /* am pierdut un client */
return;
}

I*

intoarce O daca nu e prim, 1 altfel */


int
e_prim (int i)
{

int k;
for (k = 2; k * k <= i; k++)
if ((i %k) == O)
return O;
return 1;
}

Modelul client/server - TCP

void
client () I* functia de tratare a clientu lui */

char buffer[1 00];


char aux[100 ];
int i, t;
int numar, k;
sprintf (aux, "Esti clientu l numarul : %d\n", nr);
if (write (de, aux, strlen (aux)) != strlen (aux))
{

shutdow n (de, 2); /*eroar e, am iesit */


exit (errno);
}

sprintf (aux, "Dati numarul : ") ;


if (write (de, aux, strlen (aux)) != strlen (aux))
{

shutdow n (de, 2);


exit (errno);
}

bzero (buffer, 100);


/* citeste numarul sub forma de sir de caracte re */
if (read (de, buffer, 100) == O)
{

shutdow n (de, 2);


exit (errno);
}

/* din sir de caracte re in intreg */


numar = atoi (buffer );
for (k = 2; k < numar; k++)
if (e_prim (k))
{

sprintf (aux, "Numar prim: %d\n", k);


if (write (de, aux, strlen (aux)) != strlen (aux))
{

shutdown (de, 2);


exit (errno);
}
}

shutdow n (de, 2);


exit (errno);
}

147

Atelier de progra mare n

148

reele

de calcula toare

int
main () /* progra mul princi pal */
{

struct sockad dr_in server ;

/* tratam semna lele */


if (signa l (SIGCHLD, semnal )

SIG_ERR)

perror ("sign al()") ;


exit (errno );
}

if (signa l (SIGPIPE, SIG_IGN)

SIG_ERR)

perror ("sign al()") ;


exit (errno );
}

I* cream socke t-ul */


if ((ds = socket (AF_INET, SOCK_STREAM, O))

-1)

perror ("sock et()") ;


return errno;
}

I* pregat im struct urile de date */

bzero (&serv er, sizeof (serve r));


server .sin_f amily = AF_INET;
server .sin_p ort = htons (PORT_SERVER);
server .sin_a ddr.s_ addr = htonl (INADDR_ANY);
I* atasam la port */
if (bind (ds, &serv er, sizeof (serve r)) == -1)
{

perror ("bind ()");


return errno;
}

if (liste n (ds, 5)

==

-1)

perror ("liste n() ");


return errno;
}

);
printf ("Aste ptam client i la portul %d ... \n", PORT_SERVER
while (1)
{

/* accept am un client */
de= accept (ds, NULL, NULL);
I* am ajuns la numarul maxim de client i? */
if (nr == CLIENTI_MAXIM)

Modelul client/serv er - TCP


{

shutdown (de, 2);


continue ;
}

/* lansam un proces care trateaza cererile clientulu i */


switch (fork ())
{

case O:
client O;
case -~:
perror ("fork() ");
break;
default:
break;
}

nr++; /* a mai venit un client */


}
}

149

Cap itolu l 9

Modelul client/server-_. UD P
n cadrul acestui capitol se prezint maniera de concepere
a programelor server iterative i a programelor client
utiliznd protocolul de transport UDP (User Datagram
Protocol).

])at agr arn e

9.1

este cea prin intermediul


A doua moda litate de transmisie a datelor n reea
.
UDP
datagramelor, folosindu-se protocolul de trans port
ar celui de la cazul TCP
Scheletul general de server i client UDP este simil
lul general al serverului
(vezi capitolul 8). n figura 9.1 poate fi urmrit mode
i clientului UDP.
l sock et O vom folosi
n locul constantei SOCK_STREAM utilizate la apelu
xiunea dintre server i client,
SOCK_DGRAM. Nefiind n prealabil stabilit cone
mai fi utilizate.
apelurile liste n O, acce pt() i conn ect () nu vor
torului send to O i
Pent ru datag rame , sunt puse la dispoziia programa
u a trimi te i, respectiv, a
recv from (), primitive care pot fi utilizate pentr
t.
recepiona date de la un anum it clien
tiv respect ordinea
Astfel, modelul general al unui server/client UDP itera
urmtoarelor apelu ri de sistem:
iunile cu
sock et () va crea un socket care va trata conex

clienii;

u a ataa
struc turilo r de date (coninute n sock addr _in) pentr
socket-ul la portu l folosit de aplicaie;

pregtirea

bind ()

ataeaz

socket-ul la port;

je ntre server i client,


procesarea cererilor clientului - schimb de mesa
O; se pot utiliza i
prin intermediul primitivelor send toO i recv from
primitivele generale send () i re ev() ;
clos e O nchiderea socket-ului client.

Modelul client/server - UDP

Figura 9.1: Modelul general al serverului

151
clientului UDP

cerere
rspuns

tl
p
li
si

t,
i

ea

server UDP

l
l

Apelul recvfrom() are sintaxa

client UDP

general:

#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockd, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);

Apelul sendto () are

urmtoarea form:

#include <sys/types.h>
#include <sys/socket.h>
oa

int sendto(int sockd, const void *msg, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen);

9.2
nt,
~ i

Exernplu

Prezentm n continuare un exemplu de server UDP iterativ, nsoit de clientul


lui. Serverul primete un ir de caractere de la client i l trimite napoi (n
ecou). Serverul va rula automat n fundal. Clientul citete un ir de caractere
de la intrarea standard, l trimite serverului, apoi ateapt ca serverul s i-l
returneze.

Atelier de programare n

152

Sursa serverului

(stocat

fiierul

reele

de calculatoare

server-udp . c) este urmtoarea:

server-udp .c

/* Server UDP iterativ (echo)


Asteapta un mesaj de la clienti;
mesajul primit este trimis inapoi

#include
#include
#include
#include
#include
#include

<sys/types .h>
<sys/socke t.h>
<stdio.h>
<netinet/in .h>
<errno.h>
<unistd.h>

/* portul folosit */
#define PORT 8081
codul de eroare returnat de anumite apeluri */
extern int errno;

I*

I*

programul */
int
main O
{

structuril e folosite de server si client */


struct sockaddr_i n adresa;
struct sockaddr client;
I* mesajul trimis de client */
char buffer[100 ];
/* descriptor ul de socket */
int sd;

I*

/* lansam serverul in fundal ...


switch (fork ())

*I

I* eroare la fork() */
case -1:
perror ("Fork error\n");
return errno;
/* copilul traieste ... *I
case O:
break;
/* parintele moare ... *I
default:
printf ("Serverul a fost lansat in fundal ... \n");
exit (O);
}

Modelul client/server - UDP

I* cream un socket */
if ((sd =socket (AF_INET, SOCK_DGRAM, O))

153

-1)

perror ("Eroare la socket().\n");


return errno;
}

I* pregatim structura folosita de server */


adresa.sin_family = AF_INET;
I* stabilirea familiei de socket-uri */
adresa.sin_addr.s _addr = htonl (INADDR_ANY);
/* acceptam orice adresa */
adresa.sin_port = htons (PORT);
/* utilizam un port utilizator */
/* atasam socketul */
if (bind (sd, (struct sockaddr *) &adresa,
sizeof (struct sockaddr)) == -1)
{

perror ("Eroare la bind(). \n");


return errno;
}

/* servim in mod iterativ clientii ... */


while (1)
{

int bytes;
int length = sizeof (client);

/* citim mesajul primit de la client */


if ((bytes = recvfrom (sd, buffer, 100, O,
&client, &length)) < O)
{

perror ("Eroare la recvfrom() de la client. \n");


return errno;
}

/* ... dupa care il trimitem inapoi */


if (sendto (sd, buffer, bytes, O, &client, length) < O)
{

perror ("Eroare la sendto () spre client. \n");


return errno;
}
}

/* while */
I* main */

Atelier de programare n

154
Urmeaz

reele

de calculatoare

codul clientului UDP:

client-udp.c

I* Client UDP (echo)


Trimite un mesaj unui server;
mesajul trimis este receptionat de la server

#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/socket.h>
<stdio.h>
<netinet/in.h>
<errno.h>
<netdb.h>
<string.h>

/* codul de eroare returnat de anumite apeluri */


extern int errno;

I* portul de conectare la server*/


int port;

I* programul */
int
main (int argc, char *argv[])
{

I* descriptorul de socket */
int sd;
/* structura folosita pentru conectare */
struct sockaddr_in server;
/* mesajul trimis */
char buffer[100];
int length;
/* exista toate argumentele in linia de comanda? */
i f (argc ! = 3)
{

printf ("Sintaxa: %s <adresa_server> <port>\n", argv[O]);


return -1;
}

/* stabilim portul */
port= atoi (argv[2]);

I
I
I

Modelul client/server - UDP

155

/* cream socketul *I
if ((sd = socket (AF_INET, SOCK_DGRAM, O))

-1)

perror ("Eroare la socket(). \n");


return errno;
}

/* umplem structura folosita pentru


realizarea dialogului cu serverul */
server.sin_ family = AF_INET;
I* familia socket-ulu i */
server.sin_ addr.s_add r = htonl (inet_addr (argv[1])) ;
/* adresa IP a serverului */
server.sin _port = htons (port);
I* portul de conectare */

/* citirea mesajului de la intrarea standard */


bzero (buffer, 100);
printf ('iintroduc eti mesajul: ");
fflush (stdout);
read (O, buffer, 100);
length = sizeof (server);

/* trimiterea mesajului catre server */


if (sendto (sd, buffer, strlen (buffer), O,
&server, length) < O)
{

perror ("Eroare la sendto() spre server. \n");


return errno;
}

/* citirea raspunsulu i dat de server


(ne blocam pina cind serverul raspunde) */
if (recvfrom (sd, buffer, 100, O, &server, &length) < O)
{

perror ("Eroare la recvfrom() de la server. \n");


return errno;
}

printf ("Mesajul primit este: '%s'.\n", buffer);

/* inchidem socketul, am terminat */


close (sd);
return O;
}

Atelier de programare n

156

reele

de calculatoare

Clientul va necesita dou argumente date n linia de comand semnificnd


adresa IP a serverului i portul de conectare la serverul UDP. Dac ambele
programe ruleaz pe aceeai main, atunci vom putea introduce:
(infoiasi):-$ ./server-udp
(infoiasi):-$ ./client-udp 127.0.0.1 8081

Serverul va rula automat n fundal (adoptnd postura de daemon) - vezi


capitolul 3.

9.3
1.

Exerciii

serverul prezentat mai sus astfel nct


fie trimis oglindit napoi.

Modificai
s

irul

primit de la client

2.

se scrie un server UDP care ateapt zece cifre de la un client


returneaz acelui client suma cifrelor primite.

3.

se scrie un client UDP care se conecteaz la portul 13 al unei


pentru a citi timpul curent i a-l afia la ieirea standard.

9.4

maini

Rezolvri

Exerciiul

3. Determinarea timpului curent

Programul care determin timpul curent conectndu-se la portul 13 ( daytime )


al unei maini (server) este urmtorul:
#include
#include
#include
#include
#include
#include
#include
#include
#include

<stdio.h>
<string.h>
<sys/socket.h>
<sys/types.h>
<sys/time.h>
<unistd.h>
<netinet/in.h>
<netdb.h>
"erori.h"

/* adresa IP a masinii la care ne conectam */


#define IP "193. 231. 30 .197"
main ()
{

Modelul client/ser ver - UDP

157

int sd, i;
struct sockadd r_in adr_des t; /* adresa serveru lui 'daytine ' */
char s[70], *st;
struct tm timp;
/* va contine timpul & data */
/* creare socket */
if ((sd = socket (AF_INET, SOCK_DGRAM, O))

-1)

eroare (); /*rapor tare erori* /


exit (1);
}

/* complet are structu ra */


memset (&adr_d est, sizeof (adr_de st), O);
adr_des t.sin_fa mily = AF_INET;
adr_des t.sin_po rt = htons (13);
adr_des t.sin_ad dr.s_ad dr = inet_add r (IP);

:i

/* initiere a dialogu lui cu serveru l */


if (sendto (sd, s, 70, O,
(struct sockadd r *) &adr_de st, sizeof (adr_de st))
< O)

eroare ();
exit (1);
}

/* recepta rea raspuns ului */


if ((i = recvfrom (sd, s, 70, O,
(struct sockadd r *) NULL, (int *) NULL)) < O)

eroare ();
exit (1);
}

/* decupam timpul curent */


st
strpbrk (s, li ") + 1
st = strpbrk (st + 1, li li)' + 1
'
st = strpbrk (st + 1, " li) ;
strtok (st, 11 " ) ;
printf ("Timpu l curent: %s\n 11 , st);
/* am termina t */
close (sd);
exit (O);
}

Atelier de programare n

158

reele

de calculatoare

Antetul erori. h definete funcia eroare() de raportare a erorilor pe baza


valorilor returnate de variabila errno:
#include <errno.h>
void
eroare (void) /* functie de afisare a erorilor survenite */
{

switch (errno)
{

case EPROTONOSUPPORT:
perror ("Tipul protocolul ui folosit nu este suportat." );
break;
case EMFILE:
perror ("Tabela de procese este plina.");
break;
case ENFILE:
perror ("Tabela de descriptor i este plina.");
break;
case EACCES:
perror ("Nu se poate crea un socket "
"cu tipul si/sau protocolul dat.");
break;
case ENOBUFS:
perror ("Nu este suficient spatiu liber in buffer.");
break;
case EBADF:
perror ("Descript orul folosit nu este corect.");
break;
case EINVAL:
perror ("Portul este deja legat la un socket.");
break;
case ENOTSOCK:
perror ("Argument ul dat este nu e un descriptor de socket.");
break;
case EFAULT:
perror ("Pointeru l de adresa specificat este invalid.") ;
break;
case EMSGSIZE:
perror ("Lungimea specificat a este mai mica decat "
"lungimea mesajului care trebuie trimis.");
break;
case EWOULDBLOCK:
perror ("Socketul este creat non-blocan t "
"dar cererea este blocanta." );
break;

Modelul client/server - UDP


case ENOTCONN:
perror ("Socketul ui ii este asociata o conexiune orientata "
"dar nu s-a facut conectarea .");
break;
case EINTR:
perror ("Primirea datelor a fost intrerupta de un semnal"
"inainte ca transmiter ea datelor sa fie terminata. ");
break;
case EISCONN:
perror ("Socketul este deja conectat." );
break;
case ECONNREFUSED:
perror ("Conexiun ea este refuzata de server.");
br~ak;

case ETIMEDOUT:
perror ("Timpul acordat coneex.iuni i a expirat.") ;
break;
case ENETUNREACH:
perror ("Nu ne putem conecta la adresa specificat a.");
break;
case EADDRINUSE:
perror ("Adresa este folosita deja.");
break;
default:
perror ("A survenit o alta eroare");
}
}

159

Capit olul 10

Mu ltip lex are a

intrrilor /ieirilor

Acest capitol trateaz maniera de multiplexare a intrrilor


ei.
i ieirilor, descriind primitiva select( ) i utilizrile

10.l

Prim itiva selec t ()

recurge
Pentru multipl exarea n manier sincron a intrrilor i ieirilor vom
rea:
urmtoa
este
la folosirea primitiv ei select () a crei sintax general
#includ e <sys/ti me.h>
#includ e <sys/ty pes.h>
#includ e <unistd .h>
int

select(

/* valoare a maxima a descri ptorilo r plus 1 */


nfds1,
int
/* multim ea descri ptorilo r de citire */
fd_set *readfd s,
I* multim ea descri ptorilo r de scriere */
fd_set *write fds,
/* multim ea descri ptorilo r de except ie */
fd_set *excep tfds,
/* timpul de astepta re */
struct timeva l *timeo ut

) ;

Pentru manipu larea elemen telor mulimilor de descrip tori (tipul fd_set)
se pun la dispoziie urmtoarele macro-u ri:
/* face multim ea vida */
FD_ZERO (fd_set *set);
/* seteaza un bit ('fd') din multim ea 'set') */
FD_SET (int fd, fd_set *set);
/* sterge un bit ('fd') din multim ea 'set') */
FD_CLR (int fd, fd_set *set);
/* testeaz a aparten enta lui 'fd' la multim ea 'set' */
FD_ISSET (int fd, fd_set *Set);
descrip tor i va corespu nde un bit n repreze ntarea
descrip tori dat de tipul predefi nit fd_set .
Fiecrui

I
mulimii

de

Multiplexa rea

intrrilor /ieirilor

161

Structura timeval (definit n antetul sys/time . h) are doi membri (ntregi


pozitivi) semnific nd secundele (tv_sec) i micro-secundele (tv_usec) . Dac
se furnizeaz valoarea NULL, atunci select() va atepta la nesfrit.
Valoarea returnat de apelul select() va desemna numrul de descriptor i
pregtii pentru o operaiune de citire, scriere sau excepie. O valoare
nul
va nsemna faptul c nici unul dintre descriptori nu este gata, deci timpul de
ateptare s-a scurs. n caz de eroare, select() va returna valoarea -1.
n concluzie, primitiva select() permite unui acelai proces s multiplexeze
operaii blocante de natur diferit peste mulimile de descriptor i precizate.
Ca
efect particular , primitiva poate exprima ateptare la nivel de microsecunde.

10.2

Exemp le

1. Putem utiliza apelul select() pentru citirea temporizat de la intrarea


standard. Se va atepta un nume de la utilizator (furnizat de la intrarea
standard) maxim 5 secunde i 33 de microsecunde.
Sursa acestui program este cea de mai jos:

II
I

t)

Se asteapta un text de la intrarea standard


maxim 5 secunde si 33 de microsecu nde
*I

#include
#include
#include
#include

<sys/type s.h>
<sys/time .h>
<unistd.h >
<stdio.h>

/* programu l */
int
main O
{

I* multimea de descript ori de citire */


fd_set readfds;
/* structura de timp */
struct timeval tv;
I* numele citit de la intrarea standard */
char name[20] ;
/* valoarea returnata de select() */
int retval;

de

I* initial multimea este vida */


FD_ZERO (&readfd s);

Atelier de programare n

162

reele

de calculatoare

I* multimea va contine un singur descriptor */


FD_SET (O, &readfds);
/* setam timpul de asteptare */
tv.tv_sec = 5;
tv.tv_usec = 33;
printf ("Introduc eti un nume (in maxim 5 secunde): ");
fflush (stdout);
if ((retval = select (1, &readfds, NULL, NULL, &tv)) < O)
{

perror ("Eroare la select(). \n");


return 1;
}

if (retval) /* este un singur descriptor gata de citire */


{

I* ar trebui sa fie adevarat */


if (FD_ISSET (O, &readfds))
{

fgets (name, 20, stdin);


/* eliminam caracterul NewLine din sir */
name[strle n (name) - 1] = '\0';
printf ("Salut, %s!\n", name);
}

el se
printf ("Inca nu suntem gata de citire ... \n");
}

else /* select a returnat o valoare nula, timpul a expirat */


printf ("Timpul a expirat ... \n");
/* main */
}

2. Primitiva select() ne poate ajuta s realizm o temporizare mai bun


dect sleep (), att la nivel de secunde, ct i la nivel de micro-secunde.
Pentru aceasta, ne vom folosi numai de ultimul parametru al apelului
select():
#include <sys/types .h>
#include <sys/time.h >
#include <stdio.h>

/* programul */
main (int argc, char *argv[])
{

struct timeval timeout;

I
I
I
I

I
l

Multiplexarea intrrilor /ieirilor

163

/* exista argumentele? */
if (argc != 3)
{

printf ("Sintaxa: %s <#sec> <#microsec>\n", argv[O]);


return 1;
}

/* setam valorile */
timeout.tv_sec =atol (argv[1]);
timeout.tv_usec =atol (argv[2]);
/* apelam select() pentru temporizare */
if (select (O, NULL, NULL, NULL, &timeout) < O)
{

perror ("Eroare la select(). \n");


return 2;
}

return O;
}
Observai

mesajele de eroare raportate la


ducnd valori de timp invalide.

execuia

programului, intro-

3. Apelul select() este folosit preponderent la realizarea de servere concurente, dup cum vom vedea din exemplul de mai jos.
I

Serverul primete un ir de caractere de la client i l trimite napoi (n


ecou), n manier concurent. Clientul primete un ir de caractere de la
tastatur, l trimite serverului, apoi ateapt ca serverul s-l returneze.
Codul

:le.

surs

al serverului TCP concurent este

urmtorul:

server-tcp.c

I* Server TCP concurent (echo)

lui

Primeste si retrimite mesaje de la si la clienti multipli;


multiplexarea intrarilor se realizeaza cu select()
*I

#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/socket.h>
<sys/time.h>
<netinet/in.h>
<unistd.h>
<error.h>
<stdio.h>
<arpa/inet.h>

Atelier de programare n

164

reele

de calculatoare

/* portul folosit */
#define PORT 8081
/* eroarea returnata de unele apeluri */
extern int errno;
/* functie de convertire a adresei IP
a clientului in .sir de caractere */
char *
conv_addr (struct sockaddr_in address)
{

static char str[25];


char port[7];

/* adresa IP a clientului */
strcpy (str, inet_ntoa (address.sin_add r));
I* portul utilizat de client */
bzero (port, 7);
sprintf (port, ":%d", ntohs (address.sin_por t));
strcat (str, port);
return (str);
}

I* programul */
int
main O
{

/* structurile pentru server si clienti */


struct sockaddr_in server;
struct sockaddr_in from;
/* multimea descriptorilor de citire */
fd_set readfds;
/* multimea descriptorilor activi */
fd_set actfds;
/* structura de timp pentru select() */
struct timeval tv;
I* descriptori de socket */
int sd, client;
/* descriptor folosit pentru
parcurgerea listelor de descriptori */
int fd;
/* maximul valorilor descriptorilor */
int nfds;
I* lungimea structurii sockaddr_in */
int len;

Multip lexare a

intrrilor/ ieirilor

/* crear e socke t */
if ((sd = socke t (AF_INET, SOCK_STREAM, O))
{

165

-1)

perro r ("Ero are la socke t(). \n");


return errno ;
}

I* prega tim struc turile de date */

bzero (&ser ver, sizeo f (serv er));


I* umplem struc tura folos ita de serve r */
serve r.sin_ famil y = AF_INET;
serve r.sin_ addr. s_add r = htonl (INADDR_ANY);
serve r.sin_ port = htons (PORT);
/* atasam socke tul */
if (bind (sd, (struc t socka ddr *) &serv er,
sizeo f (struc t socka ddr)) == -1)
{

perro r ("Ero are la bind( ). \n");


retur n errno ;

I
I
l
l

I* punem serve rul sa ascul te


daca vin clien ti sa se conec teze */
if (liste n (sd, 5) == -1)
{

perro r ("Ero are la listen (). \n");


retur n errno ;
}

/* compl etam multim ea de descr iptori de citire */


/* initi al, multim ea este vida */
FD_ZERO (&act fds);
/* includ em in multim e socke tul creat */
FD_SET (sd, &actf ds);
/* se va astep ta un timp nedef init */
tv.tv_ sec = O;
tv.tv_ usec = O;
/* valoa rea maxima a descr iptor ilor folos iti */
nfds = sd;
print f ("Aste ptam la portu l %d ... \n", PORT);
fflush (stdo ut);
I* servim in mod concu rent clien tii ... */
while (1)
{

/* ajusta m multim ea descr iptor ilor activ i


(efec tiv utiliz ati) */
bcopy ((cha r*) &actf ds,
(char *) &read fds, sizeo f (read fds));

Atelier de program are n

166

reele

de calculatoare

/* apelul select() */
if (select (nfds+1, &readfd s, NULL, NULL, &tv) < O)
{

perror ("Eroare la select( ). \n");


return errno;
}

/* vedem daca e pregati t socketu l


pentru a-i accepta pe clienti */
if (FD_ISSET (sd, &readfd s))
{

I* pregati rea structu rii client */


len = sizeof (from);
bzero (&from, sizeof (from));
I* a venit un client, acceptam conexiu nea*/
client= accept (sd, (struct sockadd r *) &from, &len);
/* eroare la accepta rea conexiu nii de la un client */
if (client < O)
{

perror ("Eroare la accept( ). \n");


continu e;
}

I*

ajusteaz a valoare a maximul ui */


if (nfds < client)
nfds = client;
/* includem in lista de descrip tori activi
si acest socket */
FD_SET (client, &actfds );
printf
("S-a conecta t clientu l cu descrip torul %d, "
"de la adresa %s.\n", client, conv_ad dr (from));
fflush (stdout );

I*

vedem daca e pregati t vreun


socket client pentru a face echo */
I* parcurge m multime a de descrip tori */
for (fd = O; fd <= nfds; fd++)
{

/* este un socket de citire pregati t? */


if (fd != sd && FD_ISSET (fd, &readfd s))
{

/* n-a putut fi trimis mesajul */


O)
if (echo(fd )
{

printf ("S-a deconec tat"


"clientu l cu descrip torul %d.\n",

Multiple xarea

intrrilor /ieirilor

167

fd);
fflush (stdout) ;
/* inchidem conexiun ea cu clientu l */
close (fd);
/* scoatem si din multime */
FD_CLR (fd, &actfds );
}
}
}

/* for */
I* while */
/* main */

}
}

I* realizea za primire a si retrimi terea


unui mesaj unui client */
int
echo (int fd)
{

I* mesajul */
char buffer[1 00];
I* numarul de octeti cititi/s crisi */
int bytes;
bytes = read (fd, buffer, sizeof (buffer ));
if (bytes < O)
{

perror ("Eroare la read() de la client. \n");


return O;
}

if (bytes && write (fd, buffer, bytes) < O)


{

perror ("Eroare la write() catre client. \n");


return O;
}

return bytes;
}

Folosim structur a returnat de apelul accept( ) pentru a


despre adresa IP i portul clienilor conectai la server.
Sursa clientului este
meniona aici.

similar

cu cea de la capitolul 8

afia informaii

nu o voru mai

Atelier de program are n

168

10.3

reele

de calculato are

Asinc ronism

Am vzut n capitolele precedente c majorita tea apelurilor sunt blocante,

e.g. accept( ), read() sau recvfro m().


Implicit , nucleul sistemului de operare la crearea unui socket l va seta ca
blocant. Pentru a-l face neblocant, ne vom folosi de una dintre primitivele
fcntl() sau ioctl() .
Dup crearea socket-ului l vom seta ca fiind neblocan t astfel:
#includ e <unistd. h>
#includ e <fcntl.h >

II
I

if ((sockfd = socket (AF_INET, SOCK_STREAM, O)) < O)


{ I* eroare */ }
if (fcntl (sockfd , F_SETFL, O_NONBLOCK) < O)
{ /* eroare */ }

j
J

'

clientul vehiculnd mesaje m ecou,


Ca exemplu, vom rescrie serverul
folosind socket neblocan t n cadrul clientului. Dei serverul nu mai trimite
napoi mesajul recepionat de la clieni, clienii nu se vor bloca (cu toate c va j
fi executa t un apel read () dup trimitere a spre server a irului de caractere
.
citit de la intrarea standard ).
Sursa serverului este urmtoarea (se va putea observa utilizare a apelului select( ) mpreun cu fork() pentru a asigura tratarea concurent a j
I
cererilor):
;li

'..J1.

/* Server TCP concure nt (echo) */


#includ e
#includ e
#includ e
#includ e
#includ e
#includ e
#includ e
#includ e
#includ e
#includ e
#define
#define
#define
#define

<sys/typ es.h>
<sys/so cket.h>
<sys/tim e.h>
<netine t/in.h>
<unistd .h>
<error.h >
<string. h>
<stdlib. h>
<arpa/in et.h>
<signal. h>
MAX_CLIENTS 10 I* numar maxim de clienti *I
FULL "Nu mai pot fi accepta ti alti clienti ... \n"
MESSAGE "Mesaju l:"
MES_LEN strlen(MESSAGE)

Multiplexarea intrrilor/ ieirilor


struct sockaddr_in address;
extern int errno;
int port = 8081;
int sd, cd, l;
char buffer[100];
int nr_cl = O;
fd set citire, scriere;
struct timeval tv;

;e,

ca
;le

char *
ConvAddr O
{

char *p;
char nr[10];
if ((p = (char *) malloc (25)) == NULL)
return inet_ntoa (address.sin_addr);
p = inet_ntoa (address.sin_addr);
bzero (nr, 7);
sprintf (nr, 11 :%d 11 , ntohs (address.sin_port));
strcat (p, nr);
return p;

JU,

ite
va
ere

void
tratare client ()

~lu

i a

char s = 1;
printf ( 11 [PID %d] A sosit un nou client de la %s ... \n 11 ,
getpid(), ConvAddr());
while (1)
{

FD_ZERD (&scriere);
FD_SET (cd, &scriere);
tv.tv_sec = O;
tv.tv_usec = O;
/* select cu multimea descriptorilor de scriere */
if (!select (cd+ 1, NULL, &scriere, NULL, &tv))
break;
/* se poate scrie, scriem efectiv */
i f (s == 1)

if (write (cd, MESSAGE, MES_LEN)


break;
FD_ZERO (&citire);
FD_SET (cd, &citire);

== O)

169

170

Atelier de programare n

reele

de calculatoare

/* asteptam maxim 5 secunde ... *I


tv.tv_sec = 5;
tv.tv_usec =O;
I* select cu multimea descriptorilor de ~itire */
if (!select (cd+ 1, &citire, NULL, NULL, &tv))
{

= O;
continue;

s
}

1;

bzero (buffer, 100);


/* putem citi */
if (read (cd, buffer, 100)
O)
break;
printf ("[PID %d] Clientul de la %s a trimis %s",
getpid (), ConvAddr (),buffer);
if (buffer[strlen (buffer) - 1] != '\n')
printf ("\n");
}

I* inchidem conexiunea cu clientul */


shutdown (cd, 2);
close (cd);
printf ("[PID %d] Clientul de la %s s-a deconectat\n",
getpid (), ConvAddr ());
exit (O);
}

int
main (int argc, char *argv[])
{

struct sockaddr_in server;


int i, max;
if (argc == 2)

port= atoi (argv[1]);


/* tratare semnale */
if (signal (SIGCHLD, SIG_IGN)

SIG_ERR)

perror ("signal()");
return errno;
}

/* creare socket */
if ((sd = socket (AF_INET, SOCK_STREAM, O))
{

perror ("socket()");
return errno;

-1)

Multiplexarea

intrrilor /ieirilor

171

I* completare structura */
server.sin_family = AF_INET;
server.sin_port = htons (port);
server.sin_addr.s_addr = htonl (INADDR_ANY);
I* atasare la port */
if (bind (sd, &server, sizeof (server)) == -1)
{

perror ("bind()");
return errno;
}

/* setare pentru ascultare */


if (listen (sd, 5) == -1)
{

perror ("listen() ");


return errno;
}

printf ("Asteptam clienti la portul %d ... \n", port);


while (1)
{

FD_ZERO (&citire);
FD_SET (sd, &citire);
tv.tv_sec = 5;
tv.tv_usec = O;
if (select (sd + 1, &citire, NULL, NULL, &tv)
continue;
1 = sizeof (address);
bzero (&address, sizeof (address));
/* acceptarea unui nou client */
cd= accept (sd, &address, &l);
/* lansarea unui alt proces care sa trateze
cererile clientilor */
switch (fork ())
{

case -1:
shutdown (cd, 2);
close (cd);
perror ("fork() - 2");
continue;
case O:
tratare_client ();
default:
break;
}

}
}

O)

Atelier de programare n

172

Codul

surs

al clientului este

reele

de calculatoare

urmtorul:

I* Client TCP (echo)


Trimite in maniera neblocanta mesaje serverului
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/time.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<errno.h>
<unistd.h>
<stdio.h>
<netdb.h>
<string.h>
<stdlib.h>
<fcntl.h>

/* timpul de asteptare pentru realizarea conectarii */


#define TIMEOUT 30
extern int errno;
int sd;

/* eroarea returnata */
/* descriptor de socket */

void
Eroare (char *message) /* functie de afisare a erorilor */
{

if (errno == O)
perror (message);
el se
printf (message);
shutdown (sd, 2);
close (sd);
exit (errno);
}

int
main (int argc, char *argv[])
{

int bytes;
struct sockaddr_in server, client;
struct hostent *ip_addr;
char buffer[100];
fd_set citire, scriere;
struct timeval tv;

Multiplexarea

intrrilor /ieirilor

if (argc != 3)
{

printf ("Sintaxa: %s <server> <port>\n", argv[O]);


return 1;
}

/* incercam sa gasim adresa IP */


if ((ip_addr = gethostbyname (argv[1]))
NULL)
Eroare ("Eroare la rezolvarea adresei");
/* completare structuri pentru server si client */
server.sin_family = AF_INET;
server.sin_port = htons (atoi (argv[2]));
/* adresa IP o luam din structura returnata de gethostbyname() */
memcpy (&server.sin_addr .s_addr,
ip_addr->h_addr, sizeof (ip_addr->h_addr) );
client.sin_family = AF_INET;
client.sin_port =O;
client.sin_addr.s _addr = htonl (INADDR_ANY);
/* creare socket */
if ((sd = socket (AF_INET, SOCK_STREAM, O)) == -1)
Eroare ("socket()");
/* setare socket ca fiind neblocant */
if (fcntl (sd, F_SETFL, O_NONBLDCK) != O)
Eroare ("fcntl()");
/* atasare la port */
if (bind (sd, (struct sockaddr *) &client,
sizeof (struct sockaddr)) == -1)
Eroare ("bind()");
/* conectare la server (nu se mai blocheaza) */
if (connect (sd, &server, sizeof (server)) == -1)
switch (errno) /* vedem ce eroare primim ... */
{

case EINPROGRESS:
{
/* conectarea este in progres */
int value, len = sizeof (int);
FD_ZERO (&scriere);
FD_SET (sd, &scriere);
tv.tv_sec =TIMEOUT;
tv.tv_usec = O;
/* setam un timp de asteptare a conectarii */
if (!select (sd + 1, NULL, &scriere, NULL, &tv))
Eroare ("timpul de conectare a expirat!");
/*vedem ce eroare s-a returnat ... */

173

Atelier de programare n

174

reele

de calculatoare

if (getsockopt (sd, SOL_SOCKET, SO_ERROR, &value, &len)


== -1)
Eroare ("get sockopt O") ;
if (value != O)
Eroare ("Eroare la conectare");
break;
}

default:
Eroare ("Eroare la conectare");
}

while (1)
{

FD_ZERD (&citire);
FD_SET (O, &citire);
FD_SET (sd, &citire);
tv.tv_sec = 5;
tv.tv_usec = O;

/* select cu descriptorii de scriere */


if (select (sd + 1, &citire, NULL, NULL, &tv)
continue;

O)

bzero (buffer, 100);


/* este pregatita intrarea standard? */
if (FD_ISSET (O, &citire))
{

fgets (buffer, 100, stdin);


/* incercam sa scriem mesajul catre server */
bytes = write (sd, buffer, strlen (buffer));
i f (bytes == O)
Eroare ("Serverul a inchis conexiunea");
if (bytes == -1)
{

if (errno == EAGAIN) /* nu a reusit sa scrie */


{

FD_ZERO (&scriere);
FD_SET (sd, &scriere);
tv.tv_sec = TIMEOUT;
tv.tv_usec = O;
if (!select (sd + 1, NULL, &scriere, NULL, &tv))
Eroare ("Bufferul de scriere e plin ... ");
continue;
}

Eroare ("write() ");


}

Multiplexarea

intrrilor /ieirilor

175

/* este pregatit socket-ul serverului? */


if (FD_ISSET (sd, &citire))
{

/* citim mesajul de la server */


bytes = read (sd, buffer, 100);
i f (bytes == O)
Eroare ("read() ");
if (bytes == -1) /* posibila eroare? */
{

if (errno == EAGAIN) /* nu a reusit sa citeasca */


continue;
Eroare ("Eroare de citire de la server");
}

/* afisarea mesajului primit */


printf ("%s", buffer);
fflush (stdout);
}
}

/* inchiderea conexiunii si terminarea programului */


close (sd);
return O;
}

Primitiva getsockopt O a fost utilizat pentru a vedea ce eroare a survenit n cazul unui apel neblocant. Cu ajutorul acestei primitive se pot afla
i alte informaii uneori utile pentru programator. Un apel complementar este
setsockopt O care poate fi folosit pentru setarea unor parametri interni asociai socket-urilor.
O alt noutate care poate fi observat n sursa clientului este aceea c
utilizatorul, pentru a se conecta la server, nu va mai trebui s specifice adresa
IP a mainii pe care ruleaz acel server, ci adresa simbolic, n forma standard
dat de DNS (Domain Name Service).
Acest lucru se realizeaz prin intermediul funciei gethostbynam e ():
#include <netdb.h>
struct hostent *gethostbynam e(const char *name);
Funcia

va returna un pointer la tipul hostent avnd

struct hostent {
char
*h_name;
char
**h_aliases;
int
h_addrtype;

definiia:

/* numele oficial al gazdei */


/* alias-uri ale gazdei */
I* tipul de adresa al gazdei (AF_INET) */

Atelier de programare n

176
int
char

h_length;
**h_addr_list;

reele

de calculatoare

/* lungimea adresei */
/* lista de adrese IP */

/* prima adresa IP corespunzatoare gazdei */


#define h_addr h_addr_list[O]

n caz de eroare, se va returna NULL, iar variabila h_errno va conine codul


numeric al erorii (errno nu se poate utiliza). Pentru a vedea descrierea erorii
survenite, se poate folosi funcia herror () definit tot n fiierul antet netdb. h.
O funcie nrudit cu gethostbyname () este funcia gethostbyaddr ().
Exerciii

10.4

Simulai situaiile

de mai jos, implementnd cte un client/server TCP con-

curent:
1. Fie un server TCP concurent la care se pot conecta simultan maxim
3 clieni. Serverul primete mesaje, din 3 n 3 minute, de la mcar doi
dintre clieni. Dac mesajele primite de la cei doi clieni nu coincid,
atunci serverul va trimite celor trei clieni mesajul "stop", dup care deconecteaz toi clienii. Dac mesajele coincid, serverul va atepta urm
toarele mesaje de la ali doi clieni ai si.
2. Dr. Jones era ntr-o expediie n jungla amazonian. Angajase 7 cluze
autohtone, dar nu prea avea ncredere n ele. De la fiecare calauz trebuia s primeasc, din 5 n 5 minute, cte un mesaj. Dac mesajul era
"pericol" nsemna c triburile potrivnice expediiei se pregteau de atac.
n aceast situaie, Dr. Jones trebuia s trimit fiecrei cluze ordinul
de a se ascunde n cocotieri. Dac mesajul era "linite", atunci totul
era (aparent) n ordine. Pentru a se asigura de exactitatea mesajelor
cluzelor sale, Dr. Jones trimitea mesajul de ascundere numai dac m
car 5 dintre cluze i indicau "pericol", altfel le ignora mesajele.
3.

Picard plecase ntr-o misiune de cercetare pe o planet neconducnd o mini-echip de cinci specialiti. Fiecare membru
al echipei avea ordinul ca la fiecare minut s trimit un mesaj ctre nava
Enterprise pentru a raporta datele culese. Echipajul navei avea misiunea de a prelucra aceste mesaje i de a le expedia cpitanului Picard
pentru ca acesta s aib o privire de ansamblu asupra ntregii expediii.
Unele mesaje puteau fi identice i atunci ele erau transmise cpitanului
o singur dat.
Cpitanul

cunoscut,

Multiplexarea

intrrilor /ieirilor

177

4. Un pulsar este un corp ceresc care emite semnale la momente periodice


de timp. Notm s-pulsar un pulsar care emite regulat din s n s secunde.
Un astronom de pe Terra a descoperit cinci pulsari: 1-pulsar, 2-pulsar,
3-pulsar, 3-pulsar, 5-pulsar. La fiecare semnal luminos emis de un anumit
pulsar, astronomul trimite acelui pulsar un semnal radio. Dac primete
semnale simultane de la mai mult de trei pulsari, atunci astronomul va
scrie ntr-un registru apariia acestui eveniment i nu va mai trimite nici
un semnal radio ctre acei pulsari.

10.5

Rezolvri

Exerciiul

3.

Cpitanul

Picard

Pentru rezolvarea problemei cu Enterprise i cpitanul Picard, vom considera


drept server nava, iar drept clieni membrii mini-echipei de cercetare. Jurnalul
pe care se scriu mesajele trimise de membrii echipei va fi un vector de iruri
care vor fi listate la ieirea standard.
Serverul este urmtorul:

/* Server TCP concurent (Picard)

a
1
1
r

ll

a
I-

d
i.
Li

Nava Entreprise (serverul) primeste din 2 in 2 secunde


mesaje de la membrii (clienti) echipajului;
pe cele unice le transmite capitanului Picard.

*/
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/socket.h>
<sys/time.h>
<netinet/in.h>
<unistd.h>
<error.h>
<stdio.h>
<arpa/inet.h>

I* portul folosit */
#define PORT 8111
I* alte constante */
#define DEBUG 1
#define NR_MESAJE_JURNAL 10
#define NR_MEMBRI 5

~:,:~:~:; ~;::7ta de unele apeluri /

Atelier de programare n

178

reele

de calculatoare

/* jurnalul capitanului Picard */


char *picard[NR_MESAJE~JURNAL];
I* mesajele primite */
char *mesaje[NR_MEMBRI];
int count, nr_mesaj;
void
complet_str ()

/* initializeaza sirurile de mesaje */

int i;
for (i

= O; i < NR_MESAJE_JURNAL; i++)

(char*) malloc (30 * sizeof (char));


picard[i]
picard[i][O] = '\0';
}

for (i

= O; i

< NR_MEMBRI; i++)

mesaje [i]

(char*) malloc (30 * sizeof (char));

}
}

int
add (char *msg)

/* functie de memorare a celor 5 mesaje


primite la un moment dat */

char *aux;
int i;
aux = (char*) malloc (30 * sizeof (char));
strcpy (aux, msg);

= O; i < NR_MESAJE_JURNAL; i++)

for (i
{

if ( ! strcmp (picard[i], '"'))


{

strcpy (picard[i], msg);


return i;
}

if (!strcmp (picard[i], aux))


return i;
}

/* daca s-a completat jurnalul, anuntam ca nu mai incap mesaje */


return NR_MESAJE_JURNAL;
}

Multiplexarea

intrrilor /ieirilor

179

/* programul */
int
main ()
{

/* structurile pentru server si clienti */


struct sockaddr_in server;
struct sockaddr_in from;
/* multimea descriptorilor de citire */
fd_set readfds;
/* multimea descriptorilor activi */
fd_set actfds;
/* structura de timp pentru select() */
struct timeval tv;
/* descriptori de socket */
int sd, client, i;
int fd;
char mybuffer[100];
/* numarul maxim de descriptori */
int nfds;
/* lungimea structurii sockaddr in */
int len;
int contor;
/* initializam nr. de mesaje */
count = -1;
I* creare socket */
if ((sd = socket (AF_INET, SOCK_STREAM, O))

-1)

perror ("Eroare la socket() . \n") ;


return errno;
}

/* pregatim structurile de date */


bzero (&server, sizeof (server));
/* umplem structura folosita de server */
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
/* atasam socketul */
if (bind (sd, (struct sockaddr *) &server,
sizeof (struct sockaddr)) == -1)
{

perror ("Eroare la bind(). \n");


return errno;
}

II

I* punem serverul sa asculte daca vin clienti sa se conecteze */


if (listen (sd, 5)
{

== -1)

180

Atelier de programare n

reele

de calculatoare

perror ("Eroare la listen(). \n");


return errno;
}

/* completam multimea de descriptori de citire */


FD_ZERD (&actfds);
FD_SET (sd, &actfds);
tv.tv_sec = O;
tv.tv_usec = O;
I* valoarea maxima a descriptorilor folositi */
nfds = sd;
printf ("Asteptam la portul %d ... \n", PORT);
fflush (stdout);
I* initializam jurnalul picard si mesajele */
complet_str () ;
contor = O;
/* servim in mod concurent clientii ... */
while (1)
{

I* ajustam multimea descriptorilor activi


(efectiv utilizati) */
bcopy ((char*) &actfds, (char*) &readfds, sizeof (readfds));
/* apelul select() */
if (select (nfds + 1, &readfds, NULL, NULL, &tv) < O)
{

perror ("Eroare la select() . \n") ;


return errno;
}

if (FD_ISSET (sd, &readfds))


{

/* pregatirea structurii client */


len = sizeof (from);
bzero (&from, sizeof (from));
/* a venit un client, acceptam conexiunea*/
client= accept (sd, (struct sockaddr *) &from, &len);
/* eroare la acceptarea conexiunii de la un client */
if (client < O)
{

perror ("Eroare la accept O . \n") ;


continue;
}

if (nfds < client) /* ajusteaza valoarea maximului */


nfds = client;
FD_SET (client, &actfds);
printf ("S-a conectat clientul cu descriptorul %d\n",
client);
fflush (stdout);

Multiplexare a intrrilor /ieirilor


}

/* parcurgem multimea de descriptor i */


for (fd = O; fd <= nfds; fd++)
{

I* este un socket de citire pregatit? */


if (fd != sd && FD_ISSET (fd, &readfds))
{

switch (exec_clie nt (fd))


{

case O:
/* eroare de citire */
case 1:
/* terminare transmisiu ne */
close (fd); /* inchidem conexiunea cu clientul *I
FD_CLR (fd, &actfds); /*scoatem si din multime */
printf
("\nS-a deconectat clientul cu descriptor ul %d."
"In acest moment, in jurnal am:\n", fd);
fflush (stdout);
for (contor
O;
contor < NR_MESAJE_JURNAL; contor++)
{

printf ("%s ", picard[con tor]);


fflush (stdout);
}

printf ("\n");
fflush (stdout);
break;
case 2:
printf ("Jurnal Picard e plin. Continut:\ n");
fflush (stdout);
for (contor
O;
contor < NR_MESAJE_JURNAL; contor++)
{

printf ("%s ", picard[con tor]);


fflush (stdout);
}

printf ("\nAstept ati sa-mi iau alt jurnal.. :)\n");


fflush (stdout);
complet_st r () ;
if (count == 4)
count = -1;
sleep (1);
}
/*switch */
}
}
}

I* for *I
/* while */

181

Atelier de programare n

182

reele

de calculatoare

I* main */

/* functie de preluare a mesajelor de la clienti */


int
exec_clien t (int fd)
{

char buffer[lOO ];
int bytes, i, first;
char *token;
int messageEx ist = O;

I* mesajul */
/* numarul de octeti cititi */

bzero (buffer, 100);


bytes = read (fd, buffer, sizeof (buffer));
if (bytes < O)
{

perror ("Eroare la read () de la client. \n") ;


return O;
}
if (DEBUG)
{

printf ("Am citit de la client: %s\n", buffer);


fflush (stdout);
}
i f (!strcmp (buffer, "exit"))

return 1;
count++;
printf ("count: %d\n", count);
fflush (stdout);

I* clientul s-a plictisit .. *I


I* un nou mesaj */

= O; i < count; i++)

for (i
{

if (!strcmp (mesaje[i] , buffer))


{ /* daca mesajul exista,
punem pe pozitia urmatoare '\0' *I
mesaje[i + 1] [O] = '\0';
messageEx ist = 1;
break;
}

/* mesajul nu exista, il punem in mesaje; *I


if (!messageE xist)
strcpy (mesaje[co unt], buffer);
I* avem deja 5 mesaje, le punem pe cele valide in jurnal,
adica in tabloul picard[] */
if (count == NR_MEMBRI - 1)
{

Multiplexarea intrrilor /ieirilor

183

printf ("s-au adunat 5 mesaje; din ele, adaugam:\n");


fflush (stdout);
for (i = O; i <= count; i++)
if (strcmp (mesaje[i], ""))
{ /*daca 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;
}
}

count

-1;

printf ("\n");
fflush (stdout);
return bytes;
}
Urmeaz

n continuare listing-ul clientului TCP care va simula membrii


mini-echipei de cercetare:
/* Client TCP (membru al echipajului)
Trimite un mesaj navei Entreprise;
mesajul trimis este receptionat de server
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<errno.h>
<unistd.h>
<stdio.h>
<stdlib.h>
<netdb.h>
<string.h>

I* lungimea unui mesaj trimis */


#define MESSAGE_LENGTH 2
/* nr. maxim de mesaje trimise, dupa care genereaza "exit" *I
#define MESSAGE_COUNT 30
/* functie de generare de mesaje aleatoare */
void generateMessage (char*);

Atelier de programare n

184

reele

de calculatoare

I* codul de eroare returnat de anumite apeluri */


extern int errno;
I* portul de conectare la server*/
int port;

I* numara cate mesaje s-au trimis*/


int count = O;

I* programul */
int
main (int argc, char *argv[])
{

int sd;
struct sockaddr_in server;
char buffer[100];
/* exista toate argumentele in linia de comanda? */
if (argc ! = 3)
{

fprintf (stderr, "Sintaxa: %s <adresa_server> <port>\n",


argv[O]);
exit (2);
}

/* stabilim portul */
port= atoi (argv[2]);
/* cream socketul */
if ((sd = socket (AF_INET, SDCK_STREAM, O))

-1)

{:

perror ("Eroare la socket(). \n");


exit (1);
}

/* umplem structura folosita pentru realizarea conexiunii */


server.sin_family = AF_INET;
/* familia socket-ului */
server.sin_addr.s _addr = htonl (inet_addr (argv[1]));
/* adresa IP a serverului */
server.sin_port = htons (port);
/* portul de conectare */
/* ne conectam la server */
if (connect (sd, (struct sockaddr *) &server,
sizeof (struct sockaddr)) == -1)
{

Multiplexarea

intrrilor /ieirilor

perror ("Eroare la connect(). \n");


exit (1);
}

/* trimiterea mesajului catre server */


bzero (buffer, 100);
while (strcmp (buffer, "exi t"))
{

generateMessage (buffer);
printf ("Am generat: %s\n", buffer);
fflush (stdout);
if (write (sd, buffer, 100) < O)
{

perror ("Eroare la write() spre server. \n");


exit (1);
}

sleep (2);

/* doarme 2 secunde */

/* inchidem conexiunea, am terminat */


close (sd);
}

/* genereaza un mesaj aleatoriu */


void
generateMessage (char *message)
{

int i;
count++;
if (count

== MESSAGE_COUNT)

strcpy (message, "exit");


return;
}

for (i = O; i < MESSAGE_LENGTH; i++)


/* generam caractere de la 'a' la 'z' */
message[i] = (char) ('a'+ random () %25);
message[MESSAGE_LENGTH - 1] = '\0';
}

185

1
1
Capitol ul 11

RPC - Ape lul procedurilor


la distan
Capitolul de fa prezint sistemul RPC care permite
apelul proceduril or la distan. Este prezentat pe scurt
sistemul RPC i se exemplific utilizarea lui printr-o aplicaie oferind informaii despre mersul trenurilor.

11.1

Sistem ul RPC

Scopul acestui capitol este de a arta cum se utilizeaz sistemul RPC (Remote
Procedure Call) pentru a construi aplicaii distribuite . ncepem prin a prezenta
cum lucreaz sistemul RPC. RPC permite unui client s execute proceduri pe
alte calculatoa re din reea. RPC face modelul client/ser ver mai puternic i
constituie un instrumen t de programa re mai simplu dect interfaa socket.
O aplicaie RPC simpl const dintr-un client i un server, serverul fiind pe
maina care execut procedura . Aplicaia client comunic cu procedura
de pe
calculator ul la distan transmind argument ele i recepionnd rezultatele.
Clientul i serverul se execut ca dou procese separate care pot fi pe calculatoare diferite din reea. Biblioteca RPC realizeaz comunica rea dintre aceste
dou procese. Procesele client i server comunic cu ajutorul a dou interfee
numite stub ("ciot"); vom avea deci un stub pentru client i altul pentru server.
Aceste interfee implementeaz protocolul RPC care specific cum sunt
construite i cum se prelucreaz{t mesajele emise ntre procesele client i server.
n figura de mai jos se poate face o comparaie ntre apelul local de procedur i apelul de procedur la distant. Figura 11.1, ca i alte figuri din
acest
capitol, sunt inspirate de cartea lui John Bloorner: Power Programrning with
RPC (O'Reilly, 1992) pe care o recomandm celor care vor s aprofundeze
mecanismul RPC.
Stub-urile se genereaz de obicei cu ajutorul comenzii rpcgen, dup care
se leag de programele client i server. Stub-urile conin funcii care translateaz apelurile locale de procedur intr-o secven de apeluri de funcii
RPC
de reea. Clientul apeleaz procedurile din stub-ul su prin care utilizeaz biblioteca RPC pentru a gsi procesul la distan i s-i transmit apoi cereri.
Procesul la distan "ascult" reeaua prin intermediul stub-ului su. Stub-uI

RPC - Apelul procedurilor la


Figura 11.1: Apelul local de

procedur vs

187

distan

apelul de

procedur

la

distan

Client
cal ling
procedura

ca lied
1m:ice!lme

Local Promure Call

Client
calling

proceduro

CIlent S.tub
m~twork

transport

Servei
called

prm;edure

Server Stub
network
transport

Remote Procsdure Call


serverului realizeaz invocarea rutinelor dorite cu ajutorul unei interfee de
apel de proceduri locale.
Clientul i serverul trebuie s comunice utiliznd o reprezentare a datelor
independent de tipul calculatorului i de sistemul de operare. RPC utilizeaz
un format propriu pentru reprezentarea datelor cunoscut sub numele de XDR
(Externat Data Representation}. Componenta XDR de reprezentare a datelor
este descris n RFC 1014.
Tipurile standard suportate de XDR sunt cele uzuale din limbajul C (precum int, unsigned int, float, double sau void), plus altele (e.g. string,
fixed array, counted array, symbolic constant etc.).

188

Atelier de programare n

reele

de calculatoare

Stuh-urile client i server sunt responsabile i cu translatarea n i din acest


format. O bibliotec XDR permite translatarea tipurilor predefinite din C,
precum i a unor tipuri mai complexe cum
fi vectorii de lungime variabil.
Pentru conversia datelor din format intern n format XDR se pun la dispoziie funciile de mai jos, definite n antetul rpc/xdr. h:

ar

xdrmen_create O date XDR;

asociaz

unei zone de memorie

obinuite

un flux de

xdr _numetip () - realizeaz conversia datelor, unde numetip se va nlocui


cu unul dintre numele de tipuri definite de XDR.
Astfel, vom putea folosi

funcia

de conversie xdr _int () ca n exemplul

urmtor:

#include <rpc/xdr.h>
#define BUFSIZE 400 I* lungimea zonei de memorie */
/* conversia unui intreg din format intern in format XDR *I
XDR *xdrm;
char buf[BUFSIZE];
int intreg;

/* zona de memorie XDR */

xdr mem_create(xdrm, buf, BUFSIZE, XDR_ENCODE);


intreg = 33;
xdr_int(xdrm, &i);

La cellalt capt al comunicaiei (pe maina aflat la distan) vom nlocui


constanta XDR_ENCDDE cu XDR_DECDDE pentru a realiza conversia n sens invers.
Vezi i man xdr.
O facilitate important oferit de RPC este ascunderea n totalitate a procedurilor de reea n interiorul interfeelor stub. Acest lucru simplific programele
client i server, eliminnd necesitatea de a controla detaliile legate de comunicarea n reea. Ca urmare, RPC uureaz scrierea aplicaiilor distribuite. Din
cauz c sistemul RPC ncearc s ascund detalii legate de reea, el include
de obicei o specificaie legat de schimbul de argumente i rezultate ntre client
i server. Aceast specificaie mrete portabilitatea aplicaiilor.

1I

RPC - Apelul procedurilor la

11.2

Cum

lucreaz

distan

189

sistemul RPC?

Oferirea unui serviciu n reea este diferit la sistemul RPC. Adresele clientului, serverului, numele serviciilor sunt pstrate la nivel simbolic. Un serviciu
este identificat prin portul la care este oferit i unde exist un daemon care
ateapt cererile de conectare. Un port reprezint un canal logic de comunicare. Portmapper-u l este un serviciu de reea care este responsabil cu asocierea
de servicii la diferite porturi; acest serviciu de mapare (asociere) a porturilor
este oferit la portul 111. Utiliznd portmapper-ul , numerele de port pentru
un anumit serviciu nu mai sunt fixe. Figura 11.2 descrie cei trei pai necesari
pentru ca un client s poat apela un server.
Figura 11.2:

Paii

necesari pentru ca un client

s poat

apela un server

Porl:m~pper

~IVllf

Program

Pasul 1 determin adresa la care serverul va oferi serviciul su. La iniia


lizare, programul server stabilete i nregistreaz, prin intermediul portmapper-ului, portul (adresa) la care va oferi serviciul. n figura 11.2 este vorba
despre portul a. Apoi clientul consult portmapper-ul de pe maina programului serverului pentru a identifica portul la care trebuie s trimit cererea RPC
(pasul 2). Clientul i serverul pot comunica acum pentru a realiza execuia
procedurii la distan. Clientul trimite cereri, iar serverul rspunde acestor
solicitri (pasul 3).
Secvena de evenimente iniiat de client printr-un apel de procedur la
distan (pasul 3 de mai sus) este descris de figura 11.3.

Atelier de programare n

190

Figura 11.3: Evenimentele


Clhfnt Mai;iJtne

clwmt
f)(O(Jfilm

iniiate

reele

de calculatoare

de client printr-un apel de

procedur

la

distan

Sem1r Malh/RI .
SIJllliee

daemon

11.'mnfnq

Clientul trimite o cerere n reea cu ajutorul unui apel callrpc O. Programul server ateapt mereu noi cereri, iar cnd o astfel de cerere este recepio
nat se invoc serviciul respectiv. O rutin dispatcher este de obicei folosit
atunci cnd un server furnizeaz mai multe servicii; dispatcher-ul identific
cererile specifice i apeleaz procedura corespunztoare. Se execut procedura
i se returneaz rspunsul care apoi este transmis prin reea la client. Clientul,
care n tot acest timp de dup momentul emiterii cererii a ateptat inactiv,
preia rspunsul i continu execuia.

11.3

Scrierea programelor server

client RPC

un pas util n a atrage cititorul ctre utilizarea RPC-ului este de


a descrie i a arta cum se scriu programele server i client. Vom evita aadar
detaliile legate de RPC i vom trece la a descrie cum se dezvolt aplicaii de
Considerm c

RPC - Apelul procedurilor la

distan

191

reea utiliznd sistemul RPC. n continuare, ne vom referi la implementarea

Sun Microsystems a sistemului RPC, implementare numit Open Network


Computing RPC (ONG RPC) - aceasta este de altfel cea mai rspndit
implementare. Specificaia ei se gsete n RFC 1057.
n implementarea Sun, interfaa RPC se structureaz pe trei nivele:
nivelul superior: complet independent de sistemul de operare, hardware
sau reea;
nivelul intermediar: face apel la
de exemplu:

funciile

definite de biblioteca RPC, ca

- registerrpc O
nregistreaz o procedur spre a putea fi
- callrpc()
apeleaz o

procedur

la

distan

executat

(n prealabil

la

distan,

nregistrat),

- svc_run()
ruleaz un serviciu RPC.
Acest nivel este utilizat de majoritatea

aplicaiilor.

nivelul inferior: d posibilitatea de a controla n detaliu mecanismele


RPC ( e.g. alegerea modului de transport al datelor, sincronizarea apelurilor etc.).

L-

>

a
1,
.;,

le

Procedurile la distan se vor include ntr-un program la distan, Un program la distan reprezint unitatea software care se va executa pe o main
aflat la distan. Fiecare program aflat la distan corespunde unui server,
putnd conine un set de una sau mai multe proceduri la distan sau date
globale. Procedurile pot partaja date comune. De notat faptul c argumentele
pasate procedurilor la distan trebuie s fie incapsulate ntr-o structur (similar cu struct din limbajul C) pentru a reduce numrul de argumente transmise procedurii.
Fiecrui program aflat la distan i se va asigna un identificator unic pe
32 de bii, iar fiecare procedur component (care va fi executat n cadrul
acelui program) este numerotat (indexat) secvenial de la 1 lan, unde n este
numrul maxim de proceduri ale acelui program.
Identificatorii de program n implementarea Sun RPC au fost divizai astfel:
00 00 00 00 - lF FF FF FF pentru

aplicaiile

RPC ale sistemului;

ir

le

20 00 00 00 - 3F FF FF FF

destinai

programelor utilizatorilor;

192

Atelier de programare n
40 00 00 00 - 5F FF FF FF

reele

reprezint

l
I

de calculatoare

identificatori temporari;

60 00 00 00 - FF FF FF FF sunt valori rezervate.


Ca exemple de identificatori

predefinii

se pot enumera:

10001 pentru programul rstatd care ofer informaii despre sistemul


aflat la distan; se pot utiliza procedurile rstat () sau perfmeter ();
10002 pentru programul rusersd furniznd informaii despre utilizatorii
conectai pe maina pe care se va executa procedura la distan;
10003 pentru serverul nfs oferind acces la sistemul de fiiere n
(Network File System).

reea

NFS

Pentru fiecare program la distan, se va include un numr ntreg pozitiv


desemnnd versiunea. Prima versiune a unui program de obicei este 1. Urm
toarele versiuni vor fi identificate de alte numere, n mod unic. Numerele de
versiuni ofer posibilitatea de a schimba detaliile de implementare sau extinderea capabilitilor aplicaiilor fr a asigna un alt identificator unui program.
Un program la distan este, aadar, un 3-uplu format din (identificator de
program, versiune, index procedur).
O component a implementrii Sun pentru RPC este compilatorul (utilitarul) RPCGEN. Acest compilator produce stub-urile client i server, produce
o rutin dispatch capabil s lucreze cu proceduri multiple i ofer flexibilitate
n realizarea programelor server i client. Compilatorul RPCGEN solicit la
intrare un fiier de specificaii RPC. Figura 11.4 prezint cum se obine codul
pentru server i client RPC. Fiierul de specificaii RPC este numit Q. x. Utiliznd compilatorul RPCGEN, nu mai este necesar s realizai comunicarea
RPC n codul serverului i clientului. Funciile respective sunt realizate de
stub-ul server (Q_svc. c) i stub-ul client (Q_clnt. c) generate de compilator
pornind de la fiier de specificaii RPC. Aceste stub-uri utilizeaz apeluri RPC
de nivel sczut; acest lucru nseamn c se complic puin scrierea aplicaiei
client i nu se mai folosete apelul callrpc ().
Compilatorul RPCGEN genereaz i filtrele (funciile) de codificare i decodificare XDR utilizate de clientul i de serverul RPC. Aceste rutine se gsesc
n fiierul Q_xdr. c . Se mai genereaz i un fisier header Q. h care se include
n toate celelalte. trei fiiere generate (Q_svc. c, Q_ clnt. c, Q_xdr. c), dar i
n programele scrise de programator pentru client i server (numite "aplicaie
client" i "aplicaie server" n figur). Compilatorul RPCGEN nu poate realiza
totul. Trebuie scrise programele C pentru server i client pe care noi le vom
numi server.c i client.c.

lI
Il
!

RPC - Apelul procedurilor la


Figura 11.4:

Obinerea

distan

codului pentru serverul

193

clientul RPC

s
V

e
l-

l.

~e

l-

e
a
tl

Pentru a obine serverul va fi necesar compilarea


stub-ului server i a rutinelo r XDR prin comanda:

funciilor

serverului, a

gcc server .c Q_svc.c Q_xdr.c -o server

Pentru a obine clientul va fi necesar compilarea funciilor clientului din


client . c, a stub-ului client i a rutinelo r XDR prin comanda:

L-

a
e
1r
~i

c
e
~i

e
a
n

gcc client .c Q_clnt .c Q_xdr.c -o client

Compil area presupu ne existena bibliotecii rpclib . Pentru Linux, aceasta


este inclus n bibliotecile standar d.
Utiliza rea sistemului RPC afecteaz viteza fa de un apel local; mai exact,
viteza scade considerabil. Utilizar ea RPC trebuie privit ns ca o simplifi
care
a programrii aplicaiilor distribu ite, i nu ca o concuren cu apelurile de
proceduri locale. Exist motive serioase pentru care dorim s distribu im o aplicai
e
n reea. Acest lucru nu este totdeau na evident atunci cnd parcurg em exempl
e
didactic e simple de RPC. Acele aplicaii pot fi scrise mai uor i eficient
folosind apeluri de procedu ri locale. Totui nu trebuie ignorat faptul c exempl
ele
didactic e sunt simple pentru a nu pierde esena mecanismului RPC n detaliile
unei problem e ceva mai dificile.

J
Atelier de programare n

194

11.4

reele

de calculatoare

Exem plu

enRealiza rea aplicaiilor client/s erver cu ajutoru l mecanismului RPC implem


e:
tat de Sun presupu ne scrierea a trei element
1. o

specificaie

RPC ntr-un

2. un program server . c care

fiier

. x;

s conin

3. un program client . c care trebuie

procedurile invocate;

realizeze apelurile

Vom prezent a cele trei compon ente de mai sus pentru o


se solicit informaii despre trenuri.
Fiierul trenur i. x este urmtorul:

corespunztoare.

aplicaie

prin care

/* cererea adresa ta serveru lui */

struct reques t
{

char tren_d[ lOO];


char nr_tren [lO];
int optiun i;

/* descrie rea trenulu i */


/* numaru l trenulu i */
/* FLECARI sau SOSIRI /*

};

/* raspuns primit de client */

struct answer
{

char raspun s[4000 ];


};

program TRENURI
{

version VERSIUNE
{

}
}

/* proced ura apelata la distan ta */


= 1;
st)
(reque
answer TREN
versiun ea 1 (prima) */
/*
= 1;
identif icatoru l unic al program ului */
/*
Ox200000f1;

Se

invoc

compil atorul RPCGE N prin comanda:

rpcgen -K 10 trenuri .X

Se

obin fiierele:

-rw-r- -r--rw-r- -r--rw-r- -r--rw-r- -r--

1
1
1
1

lrc
lrc
lrc
lrc

lrc
lrc
lrc
lrc

1114 Jun 14 09:33


548 Jun 14 09:33
2048 Jun 14 09:33
675 Jun 14 09:33

trenur i.h
trenur i _clnt.c
trenur i _svc.c
trenuri _xdr.c

1
I

RPC - Apelul procedurilor la

Programul server. c pe care trebuie

s-l

distan

scriem este

195
urmtorul:

#include "trenuri.h"
#include "lib/lib.h"
#include "lib/app.h"
answer *
tren_1_svc (request * r, struct svc_req *srq)
{

static answer a; /* raspunsul returnat */

~-

bzero (&a, sizeof (a));


if ((r->optiuni & FLECARI)

:e

FLECARI)

r->optiuni &= (-FLECARI);


plecari (r->tren_d, r->nr_tren, r->optiuni, a.raspuns);
}

el se
{

r->optiuni &= (-SOSIRI);


sosiri (r->tren_d, r->nr_tren, r->optiuni, a.raspuns);
}

return &a;
}

Programul client. c este


#include
#include
#include
#include
#include

urmtorul:

<rpc/rpc.h>
<string.h>
<stdio.h>
"trenuri.h"
"lib/lib.h"

int
main (int argc, char *argv[])
{

request r;
I* cererea trimisa */
answer *a;
I* raspunsul primit */
CLIENT *el;
int opt, only_opt;
char server[300], *number = NULL;
if ((argc == 1) 11 (strcmp (argv[1], "--help") == O) 11

(strcmp (argv[1], "-h") ==O))


{

help (argv[O]);

Atelier de programare n

196

reele

de calculatoare

return O;
}

O;
opt
bzero (server, sizeof (server));
opt = process_ar g (argc, argv, &only_opt, &number, server);
/* creeaza procedura */
el= clnt_creat e (server, TRENURI, VERSIUNE, "tcp");
if (el == NULL)
{

clnt_pcrea teerror (server);


return 1;
}

bzero (&r, sizeof (r));


if (only_opt == O)
strcpy (r.tren_d, argv[argc - 1]);
if (number != NULL)
strcpy (r.nr_tren , number);
r.optiuni = opt;
if ((a= tren_l (&r, el)) == NULL) /* apel la distanta */
{

clnt_perro r (el, "Nu pot executa procedura la distanta\n ");


return 2;
}

printf ("%s", a->raspuns );


clnt_destro y (el);
}

Aceste programe se

compileaz

prin comenzile:

gcc server.c trenuri_sv c.c trenuri_xd r.c -L./ -lme -o server_tre nuri
gcc client.c trenuri_cl nt.c trenuri_xd r.c -L./ -lme -g -o trenuri

Mai multe

11.5

amnunte

despre apelurile RPC se pot afla dnd man rpc.

Exerciii

Multitudin ea de fiiere generate de RPCGEN se poate prelucra mai eficient cu


ajutorul unui fiier Makefile procesat de comanda make.
O propunere pentru un astfel de fiier este prezentat mai jos:
folosit pentru compilarea aplicatiilo r RPC
all:
rm -f *.a
make -C lib
rpcgen -K 10 trenuri.X

li

RPC - Apelul procedurilor la

distan

197

ar -r libme.a lib/*.o
gcc server .c trenuri _svc.c trenuri _xdr.c \
-L./ -lme -o server_ trenuri
gcc client .c trenur i_clnt. c trenuri _xdr.c \
-L./ -lme -g -o trenur i
instal l:
instal l
instal l
instal l
instal l
instal l

-m
-m
-m
-m
-m

0555
0555
0500
0555
0555

plecar i.txt /etc/tr enuri


sosiri .txt /etc/tr enuri
server_ trenuri /usr/sb in
trenur i /usr/bi n
trenuri X /usr/bi n

clean_ all:
make clean -C lib
rm -f *- *.o trenur i server_ trenuri trenuri X libme.a
clean:
make clean -C lib
rm -f *- *.o

Ca

exerciiu, realizai

mai nti propriu l Makefi le. Apoi ncercai s trano aplkaie client/s erver propus n cadrul capitolelor precede nte utiliznd mecani smul RPC.
scriei

cu

.J
.j

~ I

l l

Capitolul 12

Utilizarea bibliotecii MyS QL


Capitolul prezint pe scurt modul de dezvoltare de
aplicaii n reea folosind serverul de baze de date MySQL,
pornind de la rezolvarea unei probleme.

Formula rea problem ei

12.1

Ne propunem s rezolvm urmtoarea problem:


"S se conceap o aplicaie client/serve r pentru managemen tul situaiei
notelor i prezenei studenilor la seminariile /laboratoare le susinute de un
anumit cadru didactic. Editarea datelor se va realiza pe baza autentificrii."

12.1.1

Schi

r-

de rezolvare

Problema va fi rezolvat astfel: serverul TCP folosete serverul MySQL de


managemen t al bazelor de date n vederea gestionrii informaiilor referitoare
la studeni, iar interfaa clientului este implementat n qt.
Pentru a facilita comunicarea dintre server i client, s-a implementa t un
protocol de comunicaie propriu, la nivelul aplicaie. Acest protocol este bazat
pe comenzi pentru managemen tul informaiilor legate de studeni. Furnizm
n continuare aceste comenzi, mpreun cu sintaxa i semantica acestora:

LIST are sintaxa: LIST [<profesor >] [<grupa>] [<student> ]


Descriere parametri:
profesor
grupa
student

[string] numele profesorul ui


[string] grupa
[string] numelestu dentului

LIST simplu va afia toi profesorii din baza de date.


LIST cu parametrul profesor va afia toate grupele profesorului specificat. Dac acesta nu exist n ba~a de date, va fi generat un mesaj de
eroare.
LIST cu parametrii profesor i grupa va afia toi studenii (inclusiv cu
notele i absenele lor) din grupa specificat a profesorului dat.
LIST cu parametrii profesor, grupa i student va afia notele i absenele studentului din grupa profesorului specificat.

l
I

I
~

Utilizarea bibliotecii MySQL

~~~~~~~~~~~~~~~~~~

199

~~~~~~~~~~~~~~~

EDIT cu sintaxa: EDIT <grupa>


Descriere parametri:
grupa : [string] numele grupei

EDIT va crea o nou grup de studeni n baza de date. Dac aceasta


deja, se va returna un mesaj de eroare.
Aceast comand funcioneaz numai pe baza autentificrii.
exist

D are sintaxa: ADD <student> <grupa>


Descriere parametri :

iei
m

de

student
grupa

[string] numele studentului


[string] numele grupei

ADD va aduga un nou student n grupa specificat. Dac studentul


exist deja sau dac grupa nu exist, se va returna un mesaj de eroare.
Comanda funcioneaz numai pe baza autentificrii.

MODIFY cu sintaxa:

,re

MODIFY <student> <grupa> <nota> <absenta> <saptamana>

un

Descriere parametri:

;at
~m

student
grupa
nota
absenta
saptamana

~Cl

de

cu

[string] numele studentului


[string] numele grupei
[int] nota de la seminar
[int] va fi O sau 1 dupa cum studentul este prezent
sau nu la seminar
[int] va fi cuprinsa intre 1 si 14

MODIFY va modifica nota i absena studentului cu numele specificat


din grupa dat n sptmna dat. Dac grupa sau studentul nu exist,
se va genera un mesaj de eroare.
Aceast comand funcioneaz numai pe baza autentificrii.

DELETE cu sintaxa: DELETE <grupa> [<student>]


Descriere parametri:
student
grupa

[string] numele studentului


[string] numele grupei

Atelier de programare n reele de calculatoare

200

DELETE va terge din baza de date fie grupa specificat, fie numai
studentul din grupa specificat. Dac grupa sau studentul nu exist, se
va returna un mesaj de eroare.
Comanda funcioneaz numai pe baza autentificrii.

LOGIN are sintaxa: LOGIN <username> <passwd>


Descriere parametri:
username
passwd

[string] numele utilizator ului


[string] parola utilizator ului

LOGIN va executa autentificar ea utilizatorulu i.


Dac parametrul username este student, acestuia i se va permite numai
comanda LIST, iar modificrile n baza de date i vor fi interzise. Dac
username nu este gsit n baza de date sau dac parola nu corespunde,
se va genera un mesaj de eroare.

12.2

Accesar ea serverul ui MySQL

Pentru a accesa serverul de baze de date MySQL vom recurge la urmtoarele


funcii, scrise n fiierul sql. c (se vor folosi funciile din antetul mysql. h):
/*
Se compileaza cu:
gcc -o sql -I/usr/incl ude/mysql sql.c -lz -lmysqlcli ent
*/
#include <stdio.h>
#include <stdlib.h>
#include <mysql.h>
#include <string.h>
#include "definitii .h"
#include "errorcode s.h"
int cauta(char *grupa, char *student, char *profesor) ;
int cauta_stud ent(char *grupa, char *student);
RESULT selecteaza (char *nume, char *grupa,
char *student, int arg)
{

RESULT out;
MYSQL mysql,*soc k;
MYSQL_RES *res;

l
I

l -

1~~~~~~~~~~-U_t_il_iz_a_re_a~b1_b_li_o_te_c_ii_JJ---'-yS--'-Q_L__~~~~~~~~-20_1

o.ai
se

MYSQL_ROW row;
unsigned int num_fields;
unsigned int i,j;
unsigned long *lengths;
char qbuf[200], *buff;
out.code = OK;
out.lines = O;
/* initializare */
mysql_init(&mysql);
/* conectare la serverul MySQL */
if (!(sock = mysql_real_connect(&mysql,NULL,0,0,"bd",0,NULL,0) ))
{

fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",


mysql_error(&mysql));
perror("");
out.code = ERR_OB_CONNECT;
exit(1);

nai
ac

Lde,
}

/* formulare comenzi SELECT */


switch ( arg
{

rele

case O : sprintf(qbuf,"SELECT nume, materie


FROM profesori OROER BY nume");
break;
case 1
sprintf(qbuf,"SELECT grupa FROM grupe"
"WHERE profesor='%s' "
"OROER BY grupa",nume);
break;
case 2
sprintf (qbuf, "SELECT student, nota, absenta, saptamana "
"FROM note WHERE profesor='%s' ANO "
"grupa='%s' OROER BY student, saptamana ", nume, grupa);
break;
case 3
sprintf(qbuf,"SELECT student, nota, absenta, saptamana"
"FROM note WHERE profesor='%s' ANO "
"grupa='%s' ANO student='%s' OROER BY saptamana",
nume, grupa, student);
}

/* trimitere interogare */
if (mysql_query (sock, qbuf))
{

fprintf(stderr, "Query failed (%s)\n", mysql_error(sock));


out.code = ERR_QUERY;
exit(1);
}

Atelier de programare n

202

reele

de calculatoare

/* receptare raspuns din partea serverului */


if (!(res = mysql_store_result (sock)))
{

fprintf(stderr,"Couldn't get result from.%s\n",


mysql_error(sock));
out.code = ERR_RESULT;
exit(1);
}

out.result_row =(char**) malloc (50 * sizeof(char *));


out.result_row[O] = (char*) malloc (200);

O;

i =

num_fields = mysql_num_fields(res);
while (( row= mysql_fetch_row(res)))
{

out.result_row[i] =(char*) malloc(200);


strcpy(out.result_row[i], "");
lengths = mysql_fetch_lengths (res);
buff =(char*) malloc(50);
for ( j = O; j < num_fields; j++ )
{

strcat(out.result_row[i], "[");
sprintf(buff,"%.*s",
(int) lengths[j], row[j] ? row[j]
strcat(out.result_row[i], buff);
strcat(out.result_row[i], "]");

"NULL");

printf("\n");
i++;

printf("linia %d

%s \n", i,out.result_row[i-1]);

out.lines = i;
mysql_free_result(res);
mysql_close(sock);
return out;
}

/* autentificarea */
RESULT autent (char *login, char *passwd)
{

RESULT out;
MYSQL mysql,*sock;
MYSQL_RES *res;
char qbuf[200];
out.code = OK;
out.lines = O;

Utilizarea bibliotecii MySQL

mysql_init(&mysq l);
if (!(sock = mysql_real_connec t(&mysql,NULL,0, 0,"bd",0,NULL,O)) )
{

fprintf(stderr,"C ouldn't connect to engine!\n%s\n\n",


mysql_error(&mys ql));
perror("");
out.code = ERR_DB_CONNECT;
exit(1);
}

sprintf(qbuf,"SELE CT * FROM profesori


WHERE id='%s' AND passwd='%s'",
login, passwd);
if(mysql_query(so ck,qbuf))
{

fprintf(stderr,"Q uery failed (%s)\n",mysql_er ror(sock));


out.code = ERR_QUERY;
exit(1);
}

if (!(res=mysql_sto re_result(sock)))
{

fprintf(stderr,"C ouldn't get result from %s\n",


mysql_error(sock ));
exit(1);
}

if ( mysql_num_rows(r es)
out.code = ERR_LOGIN;
mysql_free_resu lt(res);
mysql_close(sock );
return out;

O)

I* inserare grupa studenti in baza de date */


RESULT nou_grupa(char *grupa, char *profesor)
{

RESULT out;
MYSQL mysql,*sock;
char qbuf[200];
out.code = OK;
out.lines = O;
mysql_init(&mysq l);
i f (!(sock = mysql_real_connec t(&mysql,NULL,0, 0,"bd",O,NULL,O) ))
{

fprintf(stderr,"C ouldn't connect to engine!\n%s\n\n",


mysql_error(&mys ql));

203

1
j

Atelier de programare n

204

reele

de calculatoare

perror("");
out.code = ERR_DB_CONNECT;
exit(1);
}

sprintf (qbuf, "INSERT INTO grupe VALUES ( '%s', '%s')",


grupa, profesor);
if(mysql_query(sock,qbuf) )
{

fprintf(stderr,"Query failed (%s)\n",mysql_error(sock) );


out.code = ERR_QUERY;
exit (1);
}

mysql_close(sock);
return out;
}

RESULT nou ( char *grupa, char* student, char *profesor )


{

RESULT out;
MYSQL mysql,*sock;
char qbuf[200];
int k;
out.code = OK;
out.lines = O;
mysql_init(&mysql);
if (!(sock = mysql_real_connect(&mysql, NULL,O,O,"bd",0,NULL,O)) )
{

fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",


mysql_error(&mysql));
perror("");
out.code = ERR_DB_CONNECT;
exit(1);
}

if ( cauta(grupa, student, NULL)

==

O)

sprintf(qbuf,"INSERT INTO studenti VALUES ('%s','%s')",


student, grupa);
if(mysql_query(sock,qbuf) )
{

fprintf(stderr,"Query failed (%s)\n",mysql_error(sock) );


out.code = ERR_QUERY;
exit(1);
}
}

Utilizarea bibliotecii MySQL

205

for (k = 1; k <= 3; k++)


{

sprintf(qbuf,"INSE RT INTO note"


"VALUES ('%s','%s', '%s', O, O, %d)",
student, grupa, profesor, k);
if(mysql_query(so ck,qbuf))
{

fprintf (stderr, 11 Query failed ! ! ! (%s) \n 11 ,mysql_error(sock ));


out.code = ERR_QUERY;
exit(1);
}
}

mysql_close(sock );
return out;
}

/* cautare in baza de date */


int cauta( char *grupa, char *student, char *profesor )
{

MYSQL mysql,*sock;
MYSQL_RES *res;
char qbuf[200];
int g=O;
mysql_init(&mysq l);
if (!(sock = mysql_real_connec t(&mysql,NULL,0, 0, 11 bd 11 ,0,NULL,O)))
{

fprintf(stderr, 11 Couldn't connect to engine!\n%s\n\n 11 ,


mysql_error(&mys ql));
perror( 1111 ) ;
exit(1);
}

if ( student == NULL )
sprintf(qbuf,"SELE CT * FROM grupe WHERE grupa='%s'
AND profesor='%s' 11 , grupa, profesor);
if ( (student != NULL) && (profesor == NULL) )
sprintf (qbuf, "SELECT * FROM studenti "
11 WHERE grupa='%s' AND nume='%s' 11
,
grupa, student);
if ( (student !=NULL) && (profesor!=NULL)
sprintf(qbuf,"SELE CT * FROM note WHERE grupa='%s'
11 AND student='%s' AND 11
"profesor='%s'", grupa, student, profesor);
if(mysql_query(so ck,qbuf))
{

fprintf(stderr, 11 Query failed C%s)\n 11 ,mysql_error(sock ));

11

Atelier de programare n

206

reele

de calculatoare

exit(1);
}

if (!(res=mys ql_store_r esult(sock )))


{

fprintf(std err,
"Couldn't get result from %s\n",mys ql_error(so ck));
exit(1);
}

if ( mysql_num _rows(res) > O )


g=l;
mysql_fre e_result(re s);
mysql_clos e(sock);
return g;
}

I* cautare dupa numele studentulu i */


int cauta_stud ent(char *grupa, char *student)
{

MYSQL mysql,*soc k;
MYSQL_RES *res;
char qbuf[200];
int g = O;
mysql_init( &mysql);
if (!(sock = mysql_real_ connect(&m ysql,NULL ,0,0,"bd",0, NULL,0)))
{

fprintf(std err,"Could n't connect to engine!\n% s\n\n",


mysql_erro r(&mysql)) ;
perror("") ;
exit(1);
}

if ( student == NULL )
sprintf(qbu f,"SELECT * FROM note WHERE grupa='%s' ", grupa);
el se
sprintf(qbu f,"SELECT * FROM note WHERE student='% s'
AND grupa='%s '",student, grupa);
if(mysql_q uery(sock,q buf))
{

fprintf(std err,"Query failed (%s)\n",my sql_error(s ock));


exit(1);
}

if (!(res=mys ql_store_r esult(sock )))


{

fprintf(std err,"Could n't get result from %s\n",


mysql_erro r(sock));
exit(1);

Utilizarea bibliotecii MySQL


}

if ( mysql_num_rows(res) > O )
g = 1;
mysql_fre e_result(re s);
mysql_clos e{sock);
return g;
}

/* cauta dupa numele profesorul ui */


int cauta_prof (char *profesor)
{

MYSQL mysql,*soc k;
MYSQL_RES *res;
char qbuf[200];
int g = O;
mysql_init( &mysql);
if (!(sock = mysql_real_ connect(&m ysql,NULL ,0,0,"bd",0, NULL,0)))
{

fprintf(std err,"Could n't connect to engine!\n% s\n\n",


mysql_erro r(&mysql)) ;
perror("") ;
exit(1);
}

sprintf(qbu f,"SELECT * FROM profesori WHERE nume='%s'" ,


profesor);
if(mysql_q uery(sock, qbuf))
{

fprintf(std err,"Query failed (%s)\n",my sql_error(s ock));


exit(1);
}

if (!(res=mys ql_store_r esult(sock )))


{

fprintf(std err,"Could n't get result from %s\n",


mysql_erro r(sock));
exit(1);
}

if ( mysql_num_rows(res) > O )
g = 1;
mysql_fre e_result(re s);
mysql_clos e(sock);
return g;
}

207

Atelier de programare n

208

reele

de calculatoare

/* stergere grupa */
RESULT delete_grupa (char *grupa, char *profesor)
{

RESULT out;
MYSQL mysql,*sock;
char qbuf1[200], qbuf2[200];
out.code = OK;
out.lines = O;
mysql_init(&m ysql);
if (!(sock = mysql_real_co nnect(&mysql, NULL,0,0,"bd ",0,NULL,O)))
{

fprintf (stderr, 11 Couldn't connect to engine!\n%s\n \n 11 ,


mysql_error (&mysql));
perror( 1111 ) ;
out.code = ERR_DB_CONNECT;
exit (1);
}

sprintf(qbuf1," DELETE FROM note WHERE grupa='%s' 11


11 AND profesor='%s ' 11 , grupa, profesor);
sprintf(qbuf2," DELETE FROM grupe WHERE grupa='%s' 11
"AND profesor='%s '", grupa, profesor);
if (mysql_query (sock,qbuf1) I I mysql_query( sock,qbuf2))
{

fprintf(stde rr, "Query failed (%s)\n", mysql_error( sock));


exit(1);
}

if ( cauta_student( grupa,NULL) == O )
{

sprintf(qbuf2," DELETE FROM studenti WHERE grupa='%s'", grupa);


if(mysql_quer y(sock,qbuf1) I I mysql_query( sock,qbuf2))
{

fprintf(stde rr, "Query failed (%s)\n", mysql_error( sock));


exit(1);
}
}

mysql_close( sock);
return out;
}

RESULT delete(char *Student, char *grupa, char *profesor)


{

RESULT out;
MYSQL mysql,*sock;

209

Utilizarea bibliotecii MySQL

char qbuf[200];
out.code = OK;
out.lines = O;
mysql_init(&mysql);
if (!(sock = mysql_real_connect(&mysql,NULL,O,O,"bd 11 ,0,NULL,O)))
{

fprintf(stderr, 11 Couldn't connect to engine!\n%s\n\n 11 ,


mysql_error(&mysql));
perror( 11 " ) ;
out.code = ERR_DB_CONNECT;
exit(1);
}

sprintf (qbuf, "DELETE FROM note WHERE student='%s'


11 AND grupa='%s' AND profesor='%s'",
student, grupa, profesor);
if (mysql_query(sock,qbuf))

11

fprintf(stderr,
11 Query failed (%s)\n 11 , mysql_error(sock));
exit(1);
}

if ( cauta_student(grupa, student) == O )
{

sprintf (qbuf, "DELETE FROM studenti 11


11 WHERE nume='%s' AND grupa='%s' 11 , student, grupa);
if (mysql_query(sock, qbuf))
{

fprintf(stderr,
"Query failed (%s)\n 11 , mysql_error(sock));
exit(1);
}
}

mysql_close(sock);
return out;
}

I modificare informatii /
RESULT modi (char stud, char grupa,
int nota, int abs, int sapt, char prof)
{

RESULT out;
MYSQL mysql,sock;
char qbuf1[200], qbuf2[200];
out.code = OK;

210

Atelier de programare n

reele

de calculatoare

out.lines = O;
mysql_init(&mysql);
i f ( ! (sock = mysql_real~connect (&mysql ,NULL,O ,O, 11 bd'",O ,NULL,O)))
{
'
fprintf(stderr, 11 Couldn't connect to engine!\n%s\n\n 11 ,
mysql_error(&mysql));
perror( 1111 ) ;
out.code = ERR_DB_CONNECT;
exit(1);
}

sprintf(qbuf1, 11 UPDATE note SET nota=%d 11


11 WHERE student='%s' AND grupa='%s' 11
11 AND profesor='%s' AND saptamana=%d 11 ,
nota, stud, grupa, prof, sapt);
sprintf(qbuf2, 11 UPDATE note SET absenta=%d 11
11 WHERE student='%s' AND grupa='%s' 11
11 AND profesor='%s' AND saptamana=%d 11 ,
abs, stud, grupa, prof, sapt);
if(mysql_query(sock,qbuf1) I I mysql_query(sock,qbuf2) )
{

fprintf(stderr,
exit(1);

11

Query failed (%s)\n 11 , mysql_error(sock));

mysql_close(sock);
return out;
}

char * id (char *prof)


{

char *buff;
MYSQL mysql,*sock;
MYSQL_RES *res;
MYSQL_ROW row;
unsigned long *lengths;
char qbuf[200];
mysql_init(&mysql);
if (!(sock = mysql_real_connect(&mysql,NULL,0,0, 11 bd 11 ,0,NULL,O)))
{

fprintf(stderr, 11 Couldn't connect to engine!\n%s\n\n 11 ,


mysql_error(&mysql));
perror( 1111 ) ;
exit(1);
}

sprintf(qbuf, 11 SELECT id FROM profesori WHERE nume='%s' 11 , prof);


if(mysql_query(sock,qbuf))

1
I

Utilizarea bibliotecii MySQL

211

fprintf(stderr,"Query failed (%s)\n",mysql_error(sock));


exit(1);
}

if (!(res=mysql_store_result(sock)))
{

fprintf(stderr,"Couldn't get result from %s\n",


mysql_error(sock));
exit(1);
}

while (( row= mysql_fetch_row(res)))


{

buff = (char*)malloc(30);
lengths = mysql_fetch_lengths(res);
sprintf(buff,"%.*s",
(int) lengths[O], row[O] ? row[O]

"NULL");

mysql_free_result(res);
mysql_close(sock);
return buff;
}
Funciile
fiierele

de mai sus utilizeaz definiii de constante, tipuri i prototipuri din


antet sql. h, errorcodes. h i defini tii. h prezentate n continuare:

sql.h
#include "definitii.h"
#include "errorcodes.h"
/* prototipuri ale functiilor utilizate in dialogul
cu serverul MySQL */
RESULT selecteaza(char *profesor, char *grupa,
char *student, int arg);
RESULT autent( char *login, char *passwd);
RESULT nou_grupa(char *grupa, char *profesor);
RESULT nou(char *grupa, char* stundent, char *profesor);
int cauta(char *grupa, char *student, char *profesor);
int cauta_prof(char *profesor);
RESULT delete_grupa(char *grupa, char *profesor);
RESULT delete(char *student, char *grupa, char *profesor);
RESULT modi(char *student, char *grupa,
int nota, int absenta, int sapt, char *profesor);
char* id(char *prof);

212

Atelier de programare n

reele

de calculatoare

errorcodes. h
/* codurile de eroare returnate de serverul MySQL */
#ifndef errorcodes_h
#define errorcodes_h
#include "definitii.h"
static const ERR_CODE OK
{O, "OK"};
static const ERR_CODE ERR_NSE
{1, "No such entry in table" };
static const ERR_CODE ERR_SYNTAX
{2, "Incorrect syntax"};
static const ERR_CODE ERR_ARG =
{3, "Invalid nurnber of argurnents"};
static const ERR_CODE ERR_FOUND =
{4, "Data already exists"};
static const ERR_CODE ERR_DB_CONNECT
{5, "Error database connection"};
static const ERR_CODE ERR_QUERY =
{6, "Error ~n query co!Illiland"};
static const ERR_CODE ERR_RESULT =
{7, "Error in getting the result"};
static const ERR_CODE ERR_NOTFOUND =
{8, "Data does not exist"};
static const ERR_CODE ERR_COMMAND
{9, "Co!Illiland does not exist"};
static const ERR_CODE ERR_LOGIN =
{10, "Login and password failed"};
static const ERR_CODE ERR_ACCESS =
{11, "Access denied"};
static const ERR_CODE ERR_INCORECT =
{12, "Incorrect data"};
#endif

definitii.h
/* definitii de tipuri utilizate in server */
#ifndef definitii_h
#define _definitii_h
typedef struct st_error_codes {
int value;
char *message;
} ERR_CODE;

Utilizarea bibliotecii MySQL

typedef char **INPUT_LINES;


typedef char **RETURN_LINES;
typedef struct st_input{
int arg;
INPUT_LINES input_row;
ERR_CODE code; /* the error code */
} INPUT;
typedef struct st_result { /* rezultatul returnat de MySQL */
ERR_CODE code;
/* the error code */
RETURN_LINES result_row; /* return data as array of strings */
int lines; /* number of rows in the result */
} RESULT;
typedef struct st_autent { /* structura de autentificare */
char *login; /* login name */
char *passwd; /* password */
int level; /* level of security O-student 1-profs */
} AUTENT;
#endif

213

Capitolul 13

Biblioteca ncurses
Acest capitol prezint pe scurt biblioteca ncurses i cum
se utilizeaz funciile acestei biblioteci prin prezentarea
unei aplicaii client/server.

13.1

Descriere

general

Biblioteca ncurses ofer un set destul de cuprinztor de funcii pentru afiarea


i prelucrarea informaiilor n mod text, independente de terminal. Funciile
ncurses emuleaz pe cele din mai vechea bibliotec curses oferit de System
V Release 4.
Pentru a utiliza rutinele puse la dispoziie de ncurses va trebui ca la editarea de legturi s indicm link-editorului s ncarce i aceast bibliotec prin
opiunea "-1". Aadar, la compilare vom include -!ncurses.
Biblioteca ncurses ofer, printre altele, funcii de manipulare a informaiilor de pe ecran sau din fragmente de ecran, citirea datelor de la intrarea
standard, controlul asupra modului de operare cu terminalul i accesul la rutine de prelucrare a terminalului la nivel cobort.
nainte de a putea fi folosite alte funcii ncurses, programatorul va trebui s
apeleze rutinele de iniializare initscr() sau newterm(). nainte de terminarea
lucrului cu terminalul, va trebui apelat endwin ().
n caz de eroare, funciile ncurses vor returna valoarea special ERR (ntreg)
sau NULL.
Zonele de informaii de pe ecran n mod uzual vor fi organizate sub form
de ferestre, iar ncurses pune la dispoziie tipul predefinit WINDOW. Fiecare
fereastr va fi referit printr-un pointer la acest tip. Pot fi utilizate i aa
numitele pad-uri care sunt ferestre al cror coninut poate s nu fie afiat
complet pe ecran. De reinut faptul c biblioteca ncurses nu ofer suport pentru folosirea ferestrelor suprapuse, acest lucru realizndu-se prin intermediul
altei biblioteci: panel.

13.2

Prezentarea

funciilor

bibliotecii ncurses

Biblioteca ncurses pune la dispoziia programatorului o serie de funcii menite


a construi interfee cu utilizatorul n mod text, oferind diverse faciliti precum

l
i

Biblioteca ncurses

215

definirea de ferestre, utilizarea culorilor i tastelor speciale, posibiliti de


ascundere a cursorului i altele. Urmeaz prezentarea prototipului pentru o
.parte dintre aceste funcii:

WINDOW *initscr (void);


int start_color (void);
int init_pair (short pair, short foregrnd, short backgrnd);
int curs_set (int visibility);
- invisible - visibility = O
- normal - visibility = 1
- very visible - visibility = 2

WINDOW *newwin (int nlines, int ncols,


int begin_y, int begin_x);
WINDOW *subwin (WINDOW *orig, int nlines, int ncols,
int begin_y, int begin_x);
int wcolor_set (WINDOW *win, short color_pair_number,
void* opts);
int color_set (short color_pair_number, void* opts);
int wborder (WINDOW *win, chtype ls, chtype rs, chtype ts,
chtype bs, chtype tl, chtype tr,
chtype bl, chtype br);
'ls' - este caracterul folosit pentru partea
trei (lefi side),
'rs' - right side,
'ts' - top side,
'bs' - bottom side,
'tl' - top left-hand corner,
'tr' - top right-hand corner,
'bl' - bottom left-hand corner,
'br' - bottom right-hand corner.

stng

a chenarului feres-

Atelier de programare n

216

reele

de calculatoare

vreunul dintre aceste argumente este zero, atunci


implicite (definite n curses. h) sunt folosite:

Dac

urmtoarele

ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE,- .


ACS_ULCDRNER, ACS_URCDRNER, ACS_LLCDRNER, ACS_LRCDRNER.
int delwin (WINDOW *Win);
int wclear (WINDOW *Win);
int wrefresh (WINDOW *win);
int refresh (void);
int doupdate (void);
int echo (void);
int noecho (void);
int mvprintw (int y, int x, char *fmt [,arg] ... );
int mvwprintw (WINDOW *win, int y, int x,
char *fmt [,arg] ... );
int getnstr (char *Str, int n);
int wgetch (WINDOW *win);
int chgat (int n, attr_t attr,
short color, const void *opts);
int wstandend (WINDOW *win);
int wstandout (WINDOW *win);
int move (int y, int x);
int keypad (WINDOW *win, bool bf);

valori

217

Biblioteca ncurses

13.3

Enunul

unei probleme

se scrie o aplicaie care


pe care ruleaz."

"S

13.3.1

furnizeaz

lista utilizatorilor

existeni

pe

maina

Rezolvarea problemei

va avea o interfa similar celei a comenzii top, utilizatorul putnd


n funcie de numele proprietarului ori de timpul de conectare
sorta
sau putnd reactualiza informaiile despre sesiunile existente pe main. Interfaa este elaborat cu ajutorul rutinelor puse la dispoziie de biblioteca
Aplicaia

informaiile

ncurses.

Vom prezenta n continuare sursa acestui program:


ftop.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

<stdio.h>
<string.h>
<stdlib.h>
<ctype.h>
<termios.h>
<curses.h>
<utmp.h>
<paths.h>
<time.h>
<unistd.h>
<signal.h>
<netdb.h>
<sys/socket.h>
<sys/stat.h>
<arpa/inet.h>
"ftop.h"

I* Sort
#define
#define
#define
#define

list by ... */
USER_SORT
HDST_SORT
IDLE_TIME_SORT
LDGIN_TIME_SORT

_ /* Sorting order. */
#define SORT_ASCEND
#define SORT_DESCEND

1
2
3
4
O
1

/* This is for the info pad definition. */


#define MAX_USER_COUNT 256

I* Prototypes */

int ftop_cmp (const void*, const void*);


void sort_list (void);
char *format_idle (time_t);
char *format_time (time_t);
void update_data (void);

218

Atelier de programare n

reele

de calculatoare

void update_scr een (void);


void update (int);
void command_l ist (void);
/* Global variables */
struct ftop_data **DATA;
int user_count ;
char host_name[UT_HOSTSIZE], local_time[T IME_LEN];
WINDOW *stat_win, *input_win , *head_win, *info_pad;
unsigned int delay;
int info_pad_p os;
int sort_type, sort_order ;
int
main (int argc, char **argv)
{

int key, i;
char *delay_c =(char*) malloc (COLS * sizeof (char));
struct termios original, raw;
char any;
delay = 5;
alarm (delay);
signal (SIGALRM, update);
tcgetattr (O, &original) ;
cfmakeraw (&raw);
initscr O;
cbreak O;
noecho ();
nonl ();
intrflush (stdscr, FALSE);
stat_win = newwin (2, COLS, O, O);
wattrset (stat_win, A_BOLD);
input_win = newwin (1, COLS, 2, O);
keypad (input_win , TRUE);
head_win = newwin (1, COLS, 3, O);
wattrset (head_win, A_REVERSE);
wprintw (head_win, "%-10s %-7s %-24s %8s %-15s %4s\n",
"USER" "LINE" "FROM" "IDLE" "LOGIN-TIME", "MESG");
wattrset' (head_wln, A_NORMAL); '
wrefresh (head_win) ;
info_pad = newpad (MAX_USER_COUNT, COLS);
DATA = NULL;
user_count = O;
sort_type = USER_SORT;
sort_order = SORT_ASCEND;
info_pad_p os = O;
update (O);
key = wgetch (input_win );
while (key != 'q')
{

switch (key)
{

Biblioteca ncurses

case '?':
case 'h':
alarm (O);
endwin O;
command_list O ;
fflush (stdout);
tcsetattr (O, TCSANOW, &raw);
read (O, &any, 1);
tcsetattr (O, TCSANOW, &original);
doupdate O;
update (O);
break;
case 's':
alarm (O);
wprintw (input_win,
"Delay between updates (O - no updating): ");
echo ();
wgetstr (input_win, delay_c);
wclear (input_win);
noecho O;
wrefresh (input_win);
if (strlen (delay_c) != O)
{

i = O;
while (i < strlen (delay_c) && isdigit (delay_c[i]))
i++

if (i'< strlen (delay_c))


{

beep O;
wattrset (input_win, A_REVERSE);
wprintw (input_win, "Invalid input!");
wrefresh (input_win);
sleep (2);
wattrset (input_win, A_NORMAL);
wclear (input_win);
wrefresh (input_win);
}

el se
delay =atol (delay_c);
}

update (O);
break;
case '> ' :
case 'r':
update (O);
break;

I* Scrolling commands. */
case KEY_UP:
if (info_pad_pos > O)
{

info_pad_pos--;
prefresh (info_pad, info_pad_pos,
O, 4, O, LINES, COLS);
}

el se
beep O;

219

220

Atelier de programare n

reele

de calculatoare

break;
case KEY_DOWN:
if (user_coun t - info_pad_p os > LINES - 4)
{

info_pad_p os++;
prefresh (info_pad, info_pad_p os,
O, 4, O, LINES, COLS);

el se
beep O;
break;
case KEY_PPAGE:
if (info_pad_p os > O)
{

info_pad_p os -= LINES - 4;
if (info_pad_p os < O)
info_pad_p os = O;
prefresh (info_pad, info_pad_p os,
O, 4, O, LINES, COLS);

el se
beep O;
break;
case KEY_NPAGE:
if (user_coun t - info_pad_p os > LINES - 4)
{

info_pad_p os += LINES - 4;
if (user_coun t - info_pad_p os < LINES - 4)
info_pad_p os = user_count - (LINES - 4);
prefresh (info_pad, info_pad_p os,
O, 4, O, LINES, COLS);

el se
beep O;
break;
case KEY_HOME:
if (info_pad_p os > O)
{

info_pad_p os = O;
prefresh (info_pad, info_pad_p os,
O, 4, O, LINES, COLS);

el se
beep O;
break;
case KEY_END:
if (user_coun t - info_pad_p os > LINES - 4)
{

info_pad_p os = user_count - (LINES - 4);


prefresh (info_pad, info_pad_p os,
O, 4, O, LINES, COLS);

el se
beep O;
break;

Biblioteca ncurses

I* Sorting commands. */

case 'u':
sort_type = USER_SORT;
sort_order = SORT_ASCEND;
sort_list () ;
update_screen ();
break;
case 'U':
sort_type = USER_SORT;
sort_order = SORT_DESCEND;
sort_list () ;
update_screen ();
break;
case 'f':
sort_type = HOST_SORT;
sort_order = SORT_ASCEND;
sort_list () ;
update_screen ();
break;
case 'F':
sort_type = HOST_SORT;
sort_order = SORT_DESCEND;
sort_list ();
update_screen ();
break;
case 'i':
sort_type = IDLE_TIME_SORT;
sort_order = SORT_ASCEND;
sort_list () ;
update_screen ();
break;
case 'I':
sort_type = IDLE_TIME_SORT;
sort_order = SORT_DESCEND;
sort_list () ;
update_screen ();
break;
case 'l':
sort_type = LOGIN_TIME_SORT;
sort_order = SORT_ASCEND;
sort_list () ;
update_screen ();
break;
case 'L':
sort_type = LOGIN_TIME_SORT;
sort_order = SORT_DESCEND;
sort_list () ;
update_screen ();
break;
default:
beep ();
wattrset (input_win, A_REVERSE);
wprintw (input_win,
"Unknown command '%c' -- hit 'h' for help", key);
wrefresh (input_win);
sleep (1);
wattrset (input_win, A_NORMAL);

221

Atelier de programare n

222

reele

de calculatoare

wclear (input_win);
wrefresh (input_win);
}

key

= wgetch (input_win);

endwin ();
exit (EXIT_SUCCESS);
}

int
ftop_cmp (const void *a, const void *b)
{

int result;
switch (sort_type)
{

case USER_SORT:
result = strcasecmp
((*((struct ftop_data **) a))->ft_user,
(*((struct ftop_data **) b))->ft_user);
break;
case HOST_SORT:
result = strcasecmp
((*((struct ftop_data **) a))->ft_host,
(*((struct ftop_data **) b))->ft_host);
break;
case IDLE_TIME_SORT:
if ((*((struct ftop_data **) a))->ft_idle_time _t
< (*((struct ftop_data **) b))->ft_idle_time _t)
result = -1;
el se
result = (*((struct ftop_data **) a))->ft_idle_time _t
> (*((struct ftop_data **) b))->ft_idle_time _t;
break;
case LOGIN_TIME_SORT:
if ((*((struct ftop_data **) a))->ft_login_tim e_t
< (*((struct ftop_data **) b))->ft_login_tim e_t)
result = -1;
el se
result = (*((struct ftop_data **) a))->ft_login_tim e_t
> (*((struct ftop_data **) b))->ft_login_tim e_t;
}

return (sort_order

== SORT_ASCEND)

? result : -result;

void
sort_list (void)
{

if (DATA == NULL)
return;
qsort ((void *) DATA, user_count,
sizeof (struct ftop_data *), ftop_cmp);

char *
format_idle (time_t idle)
{

Bibliotec a ncurses

int d, h, m, s;
char sh[3], sm[3], ss[3];
char *res = malloc (8 * sizeof (char));
idle I 3600 I 24;
idle I 3600;
id le % 3600 I 60;
idle % 3600 % 60;
i f (d > O)

d
h
m
s

sprintf (res, "%dd", d);


return res;
}

i f (h > O)
{

sprintf (sh, "%2d", h);


sprintf (sm, "%02d", m);
sprintf (ss, "%02d", s);
sprintf (res, "%s:%s:%s", sh, sm, ss);
return res;
}

strcpy (sh, "");


.if (m > O)
{

sprintf (sm, "%2d", m);


sprintf (ss, "%02d", s);
sprintf (res, "%s:%s", sm, ss);
return res;

strcpy (sm, "");


if (s > O)
{

sprintf (ss, "%2d", s);


sprintf (res" "%s" , ss) ;
return res;
}

strcpy (res, "");


return res;
}

char *
format_ time (time_t time)
{

struct tm *bd_tim e = localtim e (&time);


char *months [12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

char *res = malloc (16 * sizeof (char));


sprintf (res, "%s %d %02d:%02d:%02d",
months[b d_time-> tm_mon] ,
bd_time- >tm_mda y, bd_time ->tm_ho ur,
bd_time- >tm_min , bd_time ->tm_se c);
return res;

223

Atelier de programare n

224

reele

de calculatoare

void
update_data (void)
{

struct utmp *Utmp_entry;


struct stat stat_entry;
struct ftop_data *data;
char terminal[256];
char *idle_time, *login_time;
int i;
/* Free the current list. */
if (DATA != NULL)
{

for (i = O; i < user_count; i++)


free (DATA[i]);
free (DATA);
}

DATA = NULL;
user_count = O;
/* Gather data from utmp file. */
setutent O;
while ((utmp_entry = getutent ()) != NULL)
{

if (utmp_entry->ut_type != USER_PROCESS)
continue;
user_count++;
DATA = (struct ftop_data **)
realloc (DATA, user_count *
sizeof (struct ftop_data *));
DATA[user_count - 1] =
(struct ftop_data *) malloc (sizeof (struct ftop_data));
data= DATA[user_count - 1];
strcpy (data->ft_user, utmp_entry->ut_user);
strcpy (data->ft_tty, utmp_entry->ut_line);
strcpy (data->ft_host, utmp_entry->ut_host);
strcpy (terminal, _PATH_DEV);
strcat (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);
strcpy (data->ft_idle_time_c, idle_time);
free (idle_time);
data->ft_login_time_t = utmp_entry->ut_tv.tv_sec;
login_time = format_time (data->ft_login_time_t);
strcpy (data->ft_login_time_c, login_time);
free (login_time);
data->ft_mesg_stat = (stat_entry.st_mode & S_IWGRP) ? '+'

endutent O;
I* Sort the data according to current sorting order. */
sort_list () ;
}

void
update_screen (void)
{

' - '.
'

Biblioteca ncurses
time_t daytime;
int i;
wclear (stat_win);
wclear (info_pad);
gethostname (host_name, UT_HOSTSIZE);
daytime = time (NULL);
/* ctime ()'s output ends in '\n', which we don't want. */
strcpy (local_time, strtok (ctime (&daytime), "\n"));
mvwprintw (stat_win, O, O, "Host: %s", host_name);
mvwprintw (stat_win, 1, O, "%s, %d user(s)",
local_time, user_count);
wrefresh (stat_win);
for (i = O; i < user_count; i++)
{

mvwprintw (info_pad, i, O,
"%-10.10s %-7s %-24.24s %8s %15s
%c",
DATA[i]->ft_user,
DATA[i]->ft_tty,
DATA[i]->ft_host,
DATA[i]->ft_idle_time_c,
DATA[i]->ft_login_time_c, DATA[i]->ft_mesg_stat);

prefresh (info_pad, info_pad_pos, O, 4, O, LINES, COLS);


wrefresh (input_win);
}

void
update (int signum)
I* Takes an int as an argument to match
the signal-handler prototype. */
{

update_data () ;
if (info_pad_pos > O &&
user_count - info_pad_pos < LINES - 4)
info_pad_pos = user_count - (LINES - 4);
if (info_pad_pos < O)
info_pad_pos = O;
update_screen ();
alarm (delay);
}

void
command_list (void)
{

printf ("Interactive commands are:\n\n\


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 id le time\n\
I
Reverse sort by idle time\n\
1
Sort by login time\n\
L
Reverse sort by login time\n\n\
UpArrow
Backward one line\n\
DownArrow Foreward one line\n\
Backward one page\n\
PgUp
u

225

Atelier de programare n

226

reele

de calculatoare

Foreward one page\n\


Move to the beginning of the list\n\
Move to the end of the list\n\n\
Update list\n\
Print this list\n\
Set the delay in seconds between updates\n\
s
Quit\n\n\
q
Press any key to continue") ;

PgDown
Home
End
space, r
h, ?

Programul de mai sus

folosete urmtorul fiier

antet:

ftop.h
#ifndef _FTOP _H
#define _FTOP_H
#include <utmp.h>
#include <time.h>

/* Number of characters to store the current date,


as returned by ctime (). 48 should be enough. */
48
#define TIME_LEN
struct ftop_data
{

char ft_user[UT_NAMESIZE];
char ft_tty[UT_L INESIZE];
char ft_host[UT_HOSTSIZE];
time_t ft_idle_tim e_t; /* For sorting by idle time. */
char ft_idle_tim e_c[9]; /* 'HH:MM:SS' + '\O' */
time_t ft_login_ti me_t; /* For sorting by login time. *I
char ft_login_ti me_c[16]; /* 'MMM DD HH:MM:SS' + '\0' */
char ft_mesg_st at; /* '+' or '-' */
};

#endif /* _FTOP_H */

Capitolul 14

Mediul Glade
prezint pe scurt maniera de dezvoltare de
n reea folosind mediul Glade, pornind de la
rezolvarea unei probleme. Interfaa grafic se realizeaz
utiliznd biblioteca Gtk.

Capitolul

aplicaii

Formularea

14.1

rezolvarea problemei

n cele ce urmeaz va fi rezolvat aceeai problem prezentat n capitolul 12,


referitoare la managementul situaiei colare a studenilor.
Serverul va oferi urmtoarele posibiliti de gestionare a notelor i prezenelor la laborator pentru studenii unui anumit cadru didactic:

utilizatorul (clientul) este profesor, atunci acesta va putea s adauge


modifice notele i prezenele acestora i s afle informaii
despre un anumit student dintr-o anumit grup;

dac

studeni, s

n cazul n care clientul este un utilizator obinuit (fr drepturi speciale),


el poate doar s afle informaii despre un anumit student.
Pentru autentificarea utilizatorilor s-a utilizat

14.2

Conceperea

crypt().

interfeei

Pentru conceperea clientului s-a realizat o


sind mediul de dezvoltare Glade.

14.2.1

funcia

interfa grafic scris

n Gtk, folo-

Utilizarea mediului de dezvoltare Glade

Mediul Glade genereaz n mod automat urmtoarele fiiere (fien:.re dintre


acestea au asociate fiierele antet corespunztoare conin~rnd detiniiik de tipuri
i prototipurile funciilor utilizate):

callbacks.c
interface.c

Atelier de programare n

228

reele

de calculatoare

main.c
support.c

n fiierul interface. c, Glade genereaz funciile utilizate pentru crearea


casetelor de dialog ale interfeei. n cazul nostru, am avut nevoie de:
o

caset

funcia

de dialog pentru conectarea utilizatorului la server


create_Login (),

un dialog prin care clientul cere


funcia create_informatii O,
o

informaii

despre un student

fereastr folosit

funcia

pentru modificarea notei unui student


create_modifica_notaO,

un dialog pentru modificarea prezenelor unui student


funcia create_Modifica_ore_de_ recuperat (),
o

caset

funcia

de dialog utilizat la
create_adauga().

adugarea

unui student

Fiierul surs callbacks. c va conine funciile de tratare a evenimentelor


survenite asupra unor elemente de interfa (butoane, opiuni de meniu etc.).
De exemplu, s-au implementat funcii precum on_button_ok_afis_click ed()
care trateaz apsarea butonului Ok din dialogul pentru aflarea informaiilor
despre un student sau on_modifica_ore_activate (),funcie executat la selectarea opiunii Modific ore a meniului Date studeni care va crea o caset
de dialog pentru introducerea numelui studentului i a numrului de prezene
ale acestuia.

Fiierul

support. c este un fiier generat de Glade


crearea unor elemente de interfa ( widgets).

coninnd funcii

pentru

Implementarea propriu-zis a clientului se regsete n fiierul main. c.


De asemenea, mediul Galde genereaz fiierul Makefile care va fi utilizat la
compilarea aplicaiei cu ajutorul comenzii make.
Un instantaneu al fazei de proiectare a interfeei cu utilizatorul folosind
mediul Glade poate fi urmrit n figura 14.L

Mediul Glade
Figura 14.1: Proiectarea

Cteva dintre

fiierele

interfeei

cu utilizatorul folosind Glade

generate de mediul Glade sunt prezentate mai jos:

support.h
/* DO NOT EDIT THIS FILE - it is generated by Glade. */
#include <gnome.h>
/* Public Functions. */

/*

This function returns a widget in a component created by Glade.


it with the toplevel widget in the component
(i.e. a window/dialog),

* Call

229

230

Atelier de programare n

reele

de calculatoare

* or alternatively any widget in the component,


and the na.me of the widget you want returned.

*I
GtkWidget*

lookup_widget (GtkWidget
const gchar

*widget,
*widget_name);

I* get_widget() is deprecated. Use lookup_widget instead. */


#define get_widget lookup_widget
I* Private Functions. */
/* This is used to create the pixmaps in the interface. */
*Widget,
GtkWidget* create_pixmap (GtkWidget
const gchar
*filena.me,
gnome_pixmap);
gboolean
Gdkimlibimage* create_image (const gchar

*filena.me);

support.c

I*
* DO NOT EDIT THIS FILE - it is generated by Glade.

*I
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include
#include
#include
#include
#include

<sys/types.h>
<sys/stat.h>
<unistd.h>
<string.h>
<gnome.h>

#include "support.h"
/* This is an internally used function to create pixmaps. */
static GtkWidget* create_dU111I11y_pixmap
*widget,
(GtkWidget
gnome_pixmap);
gboolean
GtkWidget*
lookup_widget (GtkWidget
const gchar

*Widget,
*widget_name)

GtkWidget *parent, *found_widget;

Mediul Glade

for (;;)
{

if (GTK_IS_MENU (widget))
gtk_menu_get_attach_widget (GTK_MENU (widget));
parent
el se
widget->parent;
parent
if (parent == NULL)
break;
widget = parent;
}

found_widget
(GtkWidget*) gtk_object_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 can't be found. */


static char *dummy_pixmap_xpm[] = {
I* columns rows colors chars-per-pixel */
"1 1 1 1"'

" c None",
/* pixels */
1f

li

li

li

};

I* This is an internally used function to create pixmaps. */


static GtkWidget*
*Widget,
create_dummy_pixmap (GtkWidget
gnome_pixmap)
gboolean
{

GdkColormap *colormap;
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
GtkWidget *pixmap;
if (gnome_pixmap)
{

return gnome_pixmap_new_from_xpm_d (du.'lllily_pixmap_xpm);


}

colormap = gtk_widget_get_colormap (widget);


gdkpixmap = gdk_pixmap_colormap_create_from.. xpm_d
(NULL, colormap, &mask,

231

232

Atelier de programare n

reele

de calculatoare

NULL, dummy_pixmap_xpm);
if (gdkpixmap == NULL)
g_error ("Couldn't create replacement pixmap,");
pixmap= gtk_pixmap_new (gdkpixmap, mask);
gdk_pixmap_u nref (gdkpixmap);
gdk_bitmap_u nref (mask);
return pixmap;
}

I* This is an internally used function to create pixmaps. */


GtkWidget*
create_pixmap (GtkWidget
*Widget,
const gchar
*filename,
gboolean
gnome_pixmap)
{

GtkWidget *pixmap;
GdkColormap *colormap;
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
gchar *pathname;
if (!filename I I !filename[O])
return create_dummy_pixmap (widget, gnome_pixmap);
pathname = gnome_pixmap _file (filename);
if ( !pathname)
{

g_warning (_("Couldn't find pixmap file: %s"), filename);


return create_dummy_pixmap (widget, gnome_pixmap);
}

if (gnome_pixmap)
{

pixmap= gnome_pixmap _new_from_fil e (pathname);


g_free (pathname);
return pixmap;
}

colormap = gtk_widget_ge t_colormap (widget);


gdkpixmap = gdk_pixmap_c olormap_create _from_xpm
(NULL, colormap, &mask,
NULL, pathname);
if (gdkpixmap == NULL)
{

g_warning (_("Couldn't create pixmap from file: %s"),


pathname);
g_free (pathname);
return create_dummy_pixmap (widget, gnome_pixmap);
}

Mediul Glade
g_free (pathname);
pixmap= gtk_pixmap_new (gdkpixmap, mask);
gdk_pixmap_unref (gdkpixmap);
gdk_bitmap_unref (mask);
return pixmap;
}

/* This is an internally used function to create imlib images. */


Gdklmliblmage*
create_image (const gchar
*filename)
{

Gdklmliblmage *image;
gchar *pathname;
pathname = gnome_pixmap_file (filename);
i f ( ! pathname)
{

g_warning (_("Couldn't find pixmap file: %s"), filename);


return NULL;
}

image = gdk_imlib_load_image (pathname);


g_free (pathname);
return image;
}

Fereastra

principal

Figura 14.2:

Login

interfeei

Interfaa

client se poate vizualiza n figura 14.2.

clientului conceput n Gtk folosind mediul Glade

Date stude'nti

Student:

Crupa4
PIU

Nota:

Nr prezente:

233

Bibliografie
[1] D. Corner, Internetworking with TCP/IP, Vol. 1: Principles, Protocols,
and Architecture, Second Edition, Prentice Hall, New Jersey, 1991.
[2] D. Corner, D. Stevens, Internetworking with TCP/IP, Vol. 3: ClientServer Programming and Applications, Prentice Hall, New Jersey, 1993.
[3] I. Ignat et al., Sistemul de operare UNIX. Gestionarea fiierelor, Editura
Microinformatica, Cluj-Napoca, 1992.
[4] I. Ignat, A. Kacso, UNIX - Gestiunea proceselor, Editura
Napoca, 1995.

Albastr,

Cluj-

[5] B. Kernighan, D. Ritchie, The C Programming Language, Second Edition,


Prentice Hall, New Jersey, 1998.
[6] M. Rochkind, Advanced UNIX Programming, Prentice Hall, New Jersey,
1985.
[7] D. Rusling, The Linux Kernel Reference, 2001:
http://metalab.unc.edu/pub/Linux/docs/LDP/linu x-kernel/
[8] A. Silberschatz, J. Peterson, P. Galvin, Operating Systems Concepts,
Addison-Wesley, Reading MA, 1992.
[9] R. Stevens, Advanced Programming in the UNIX Environments, AddisonWesley, Reading MA, 1992.
[10] R. Stevens, UNIX Network Programming, Vol. 1: Networking APis, Vol. 2:
Interprocess Communications, Prentice Hall PTR, New Jersey, 1998, 1999.
[11] R. Stevens, G. Wright, TCP/IP Illustrated, Vol. 1: The Protocols, Vol. 2:
The Implementation, Vol. 3: TCP for Transaction, HTTP, NNTP, and the
UNIX Domain Protocols, Addison....:.Wesley Longman, 1994, 1995, 1996.
[12] L. Torvalds, The Story of the Linux Kernel, n Open Sources: Voices from
the Open Source Revolution (C. Dibona, M. Stone i S. Ockman, ed.),
O'Reilly and Associates, 1999:
http://www.oreilly.com/catalog/opensources/boo k/linus.html
[13]

* * *, Paginile web dedicate acestui volum, Universitatea "Al.I. Cuza'',


Facultatea de Informatic, Iai, 2001:
http://www.info.uaic.ro/-lrc/

Glosar
Antete
dirent.h, 41
error.h, 68
grp.h, 43
mysql.h, 200
ncurses.h, 214
netdb.h, 176
netinet/in.h, 137
pwd.h, 42
rpc/xdr.h, 188
signal.h, 71
stdio.h, 31
sys/socket.h, 123
sys/stat.h, 38
sys/time.h, 161
sys/types.h, 54
unistd.h, 38
utmp.h, 44
Asincronism, 121, 168
Biblioteci, 37
/lib, 17
crypt, 227
Gtk, 227
mysqlclient, 200
ncurses, 214
rpclib, 193
Comenzi, 13
bash, 27

bg, 57
cat, 23
cd, 17
chfn, 55
chmod, 21
cp, 22
cut, 25, 27
fg, 57
file, 23
find, 29
finger, 26, 55, 97
gcc, 37, 193, 196
gdb, 37
grep, 27
halt, 70
head, 23
jobs, 57
kill, 70, 72
killall, 72
last, 44
less, 23, 73
ln, 22
ls, 16, 17, 48, 63
ltrace, 37
make, 144, 196, 228
man, 17, 18, 38
mkdir, 17
mkfifo, 91
more, 23

236

Atelier de programare n

mv, 22
nice, 55
nohup, 57, 69
ps, 56, 64, 145
pwd, 17
reboot, 70
rm, 22
rmdir, 17
rpcgen, 186, 194
set, 56
shutdown, 70
sort, 25, 27
stat, 24
strace, 37
tac, 23, 35
tail, 23, 76
top, 57, 217
trap, 72
uniq, 25
w, 26
wc, 23
who, 26, 44
Constante
AF _INET, 124
AF _UNIX, 124
EOF, 31
INADDR_ANY, 134
INADDR_NONE, 138
PF _INET, 124
PF _UNIX, 124
SIG_DFL, 72
SIG_ERR, 72
SIG_IGN, 72
SOCK_DGRAM, 124, 150
SOCK_RAW, 124
SOCK_STREAM , 124, 150

reele

de calculatoare

XDR_DECODE, 188
XDR_ENCODE, 188
Conversia datelor, 137
big endian, 137
gethostbyname(), 175
htonl(), 137
htons(), 137
inet_addr(), 138
inet_aton(), 138
inet _ ntoa(), 138
little endian, 137
ntohl(), 137
ntohs(), 137
Datagrame, 150
Directoare, 16, 17, 41
Exerciii,

13, 46, 64, 76, 82, 96, 130,


145, 156, 176, 196
Expresii regulate, 28
Fiiere,

14, 42
Descriptori, 31
I-nod, 16
Permisiuni, 19, 38
Fiiere sistem, 16, 42
/dev/null, 16, 57
/dev/tty, 54
/etc/group, 16, 43
/etc/hosts, 17
/etc/passwd, 16, 27, 42, 55
/etc/protocols, 16
/etc/services, 16, 73, 134
/etc/shadow, 27
/var/log/wtmp, 44
/var/run/utmp, 44
Modelul client/server, 131, 150, 168,
198, 214, 227

Glosar
Multiplexare, 160, 177
Pipe, 15, 70, 77, 94, 96
Port, 134
Primitive
abort(), 73
accept(), 123, 132, 134, 167, 168
access(), 38
alarm(), 70, 74
bind(), 123, 131, 133
chdir(), 42
chmod(), 38
chown(), 38
close(), 31, 34, 47, 78, 123, 132,
135
connect(), 123, 136
creat(), 31, 33, 78
<lup(), 93
dup2(), 94
exit(), 55, 61
fcntl(), 168
fork(), 58, 68, 78, 145
getegid(), 61
geteuid(), 61
getgid(), 60
getpeername(), 138
getpgrp(), 60
getpid(), 60
getppid(), 60
getsockname(), 138
getsockopt(), 175
getuid(), 60
ioctl(), 168
kill(), 72
link(), 38
listen(), 123, 132, 134
lseek(), 31, 33, 78, 93

237

mkdir(), 42
mkfifo(), 80
mknod(), 81
open(), 31, 32, 47, 78, 80, 93
pause(), 72
pipe(), 77, 128
raise(), 72
read(), 31, 33, 47, 78, 123, 135,
168
receive(), 123
recv(), 135
recvfrom(), 150, 168
rename(), 38
rmdir(), 42
select(), 160, 161
send(), 123, 135
sendto(), 150
setgid(), 61
setpgrp(), 60
setsockopt(), 175
setuid(), 61
shutdown(), 123, 132, 135
signal(), 71
sleep(), 73, 76, 162
socket(), 123", 131, 133, 150
socketpair(), 128
stat(), 38
symlink(), 38
unlink(), 80
wait(), 60, 145
waitpid(), 60
write(), 31, 33, 47, 78, 123, 135
Proces, 34, 54, 72, 96
Daemon, 17, 55, 134, 156
EGID, 55
EUID, 55

238

Atelier de programare n

GID, 27, 55
init, 54
lider, 54
PID, 54, 72
PPID, 54
UID, 26, 55
zombie, 55, 60, 145
Protocol
ARP, 121
HTTP, 134
ICMP, 121
IP, 121
RARP, 121
SMTP, 134
TCP, 121, 124, 131
TCP /IP, 121, 131
UDP, 121, 124, 131, 150
14, 47, 64, 83, 98, 145,
156, 177
RPC, 186
callrpc(), 190-192
dispatcher, 190
portmapper, 189
program la distan, 191
registerrpc(), 191
rpcgen, 192
stub, 186
svc_run(), 191
XDR, 187, 192
xdrmem_create(), 188

Rezolvri,

Semnal, 69, 72
SIGABRT, 73
SIGALRM, 70, 74
SIGBUS, 70
SIGCHLD, 70, 145

reele

de calculatoare

SIGFPE, 70
SIGHUP, 69.
SIGILL', 70
SIGINT, 70
SIGIO, 70
SIGKILL, 70
SIGPIPE, 70, 146
SIGQUIT, 70
SIGSEGV, 70, 76
SIGTERM, 70
SIGURG, 70
SIGUSRl, 70
SIGUSR2, 70, 73
Server
concurent, 145, 163, 168, 177
iterativ, 131, 139, 150, 151
Socket, 15, 122, 123, 128, 131, 168
getpeername(), 138
getsockname(), 138
neblocant, 168
socketpair(), 128
Tipuri de date
DIR, 41
dirent, 41
fd_set, 160
FILE, 31
hostent, 175
in_addr, 133
passwd, 43
pid_t, 54
sockaddr, 133
sockaddr _ in, 133
timeval, 161
utmp, 44
Tratarea erorilor, 48, 68, 176

BIBL. <:.~NTll!. UNIV, \


\ M. EMINESCU''_ ~A._Sl

La Editura POLIROM

au

aprut

Doina Hrinciuc Logoftu - C + + . Probleme rezolvate i algoritmi


Marin Fotache - SQL. Dialecte DB2, Oracle i Visual FoxPro
Sabin Buraga, Gabriel Ciobanu - Atelier de programare n reele de calculatoare

S-ar putea să vă placă și