Transcript Document

Alma Mater Studiorum - Universita' di Bologna
Sede di Cesena
Reti di Calcolatori
RPC - Remote Procedure Call
Vedi:
• W.R. Stevens, Unix Network Programming, Prentice Hall, sezz. 18.118-3, pagg. 692-709.
• SunSoft, Network Interfaces Programmer's Guide, parte 2 (solo
alcune parti) e app. C.
• RFC 1057, RPC: Remote procedure Call protocol Specification –
Version 2, 1988.
Copyright © 2006-2014 by Claudio Salati.
Lez. 10
1
Local Procedure Call
• Fino dall’inizio dell’informatica la tecnica principale per la scomposizione di un
programma in piu’ moduli e’ consistita
• nella definizione di sotto-programmi (subroutine, procedure, funzioni, ...)
e
• nella possibilita’ da parte di un sotto-programma (chiamante) di attivare
l’esecuzione di (chiamare) un altro sotto-programma (chiamato)
• Tutti i processori HW supportano direttamente la nozione di trasferimento di
controllo collegato ad una chiamata di procedura mettendo a disposizione
• una istruzione di JumpToSubroutine (o Call, o BranchAndLink, o …)
• l’istruzione duale di Return (o Ret, o …)
• Associata alla nozione di chiamata di procedura c’e’ anche quella di
trasferimento di informazioni, cioe’ del passaggio di argomenti di ingresso e di
risultati di ritorno tra la procedura chiamante e quella chiamata
 I meccanismi per il passaggio dei parametri non sono legati al processore
HW ma al sistema di programmazione
• Il collegamento tra la procedura chiamante e quella chiamata, in un ambiente di
programmazione locale, e’ di norma realizzato dal linker (o dal compilatore) che
costruisce il programma eseguibile complessivo assemblando tutti i moduli 2
che lo compongono
Moduli e procedure
•
Di norma un modulo non e’ costituito da una singola procedura, ma
da un insieme di procedure correlate, ad esempio quelle che
definiscono un particolare tipo di dato astratto (ADT / classe).
•
Nella libreria standard C sono ad esempio presenti i moduli
• stdio, che definisce l’ADT FILE, e che comprende le procedure
fopen(), fclose(), fread(), fwrite(), fgetc(),
fputc(), fprintf(), fscanf(), . . .
• string, che definisce l’ADT stringa di caratteri, e che comprende
le procedure strcpy(), strcat(), strcmp(), strtok(), . . .
•
Il linker non collega tra loro singole procedure ma interi moduli,
quindi, se collega un modulo, collega tutte le procedure definite nel
modulo.
•
Il servizio offerto da un modulo e’ descritto da una interfaccia che,
prendendo come esempio il linguaggio C, e’ descritta in uno header
file (.h) dedicato, uno per ciascun modulo.
3
Local Procedure Call model
•
The caller places arguments to a procedure in some well-specified
location (such as a register window).
•
It then blocks and transfers control to the procedure.
 tramite istruzione Call
•
The callee performs its job, puts its results in some well-specified
location, and transfers control back to the caller procedure.
 tramite istruzione Return
•
The caller regains control (all’istruzione successiva alla Call).
•
At that point, the results of the procedure are extracted from the
well-specified location, and the caller continues execution.
4
Local Procedure Call model
Procedura chiamante
Procedura chiamata
Call ProceduraChiamata();
Return;
Input parameters passing
Result parameters passing
Result parameters extraction
5
Local Procedure Call model
 N.B.: c’e’ una assunzione implicita:
la rappresentazione concreta dell'informazione e’ la stessa per
chiamante e chiamato.
•
•
Il chiamato sa interpretare la rappresentazione concreta
dell’informazione dei parametri d’ingresso (ricevuta dal
chiamante).
•
Il chiamante sa interpretare la rappresentazione concreta
dell’informazione dei parametri di ritorno (ricevuta dal chiamato).
Quando, nella programmazione locale, si devono collegare moduli
scritti in diversi linguaggi, occorre gestire esplicitamente l’adattamento
delle rappresentazioni concrete dei dati, se queste non sono
omogenee.
•
Ad esempio, nel caso della rappresentazione concreta delle
matrici a piu’ dimensioni, che in Fortran e’ per colonne, e in C e in
Pascal e’ per righe.
6
Remote Procedure Call
•
In una chiamata di procedura remota (RPC) un processo su un
sistema invoca una procedura che e’ eseguita su un sistema remoto.
•
La procedura remota potrebbe anche essere eseguita sullo stesso
sistema su cui e’ eseguito il processo chiamante, ma sarebbe
comunque eseguita in un contesto di esecuzione diverso (da un
diverso processo).
•
La ragione per cui una interazione di questo genere e’ denominata
chiamata di procedura e’ che
• sia dal punto di vista della procedura chiamata,
• sia, soprattutto, dal punto di vista della procedura chiamante,
vogliamo che l'interazione sia quanto piu’ simile possibile ad una
chiamata di procedura locale.
•
In realta’ l’essere remota rende l’interazione sostanzialmente diversa
da una chiamata di procedura locale.
1. Sistemi di elaborazione (HW e sistemi operativi) eterogenei
2. Linguaggi e sistemi di programmazione eterogenei
3. Problemi di comunicazione
7
4. Problemi di sicurezza
Remote Procedure Call model
• The RPC model is similar to the local procedure call model.
• One thread of control logically winds through two processes: the caller’s
process, and a server’s process.
• The caller process first sends a call message to the server process and
waits (blocks) for a reply message.
• The call message includes the procedure’s parameters, and the reply
message includes the procedure’s results.
 Ma in realta’ abbiamo visto che la definizione del PDU rpc_msg del
protocollo Sun RPC v2 non prevede, se non in termini di place holders,
la presenza dei parametri di ingresso e di ritorno!
• Once the reply message is received, the results of the procedure are
extracted, and caller’s execution is resumed.
• On the server side, a process is dormant awaiting the arrival of a call
message.
• When a call message arrives, the server process extracts the procedure’s
parameters, computes the results, sends a reply message, and then awaits
the next call message.
• In this model, only one of the two processes is active at any given time.
8
Remote Procedure Call model
Procedura chiamante
Processo chiamato
ReceiveCallMessage(
ProceduraChiamata,
InputParams);
Call ProceduraChiamata();
SendCallMessage(
ProceduraChiamata,
InputParams);
ReceiveReplyMessage(
OutputParams);
Procedura chiamata
call message
Call
ProceduraChiamata();
Return;
reply message
SendReplyMessage(
OutputParams);
Input parameters passing
Result parameters passing
Result parameters extraction
Local Procedure Call
9
Schema dell'interazione RPC
Return
Client routine
Server routine
Call
1
14
8
Client stub
7.2
2
Server stub
13
Client RPC
protocol entity
7.1
3
7
RPC
middleware
9
Server RPC
protocol entity
RPC protocol
12
6
10
5
11
Transport Service
Transport Service
4
10
Schema dell'interazione RPC: legenda
•
Comunicazioni reali:
•
Comunicazioni virtuali:
.1
1. Il cliente (chiamante) chiama una procedura locale, detta client stub.
Dal punto di vista del chiamante essa rappresenta la procedura remota
che lui vuole chiamare.
2. Il client stub encodifica (serializza) i parametri di input della chiamata
(di cui conosce il tipo) e li passa, insieme a un identificatore della
procedura chiamata, all’entita’ di protocollo RPC.
L’operazione di serializzazione dei parametri e’ chiamata marshaling.
3. L’entita’ di protocollo RPC crea il PDU di chiamata e lo comunica al
suo pari remoto utilizzando il Servizio di Trasporto (4, 5).
6. L'entita’ di protocollo RPC remota, che era in attesa di PDU di
chiamata, riceve il PDU e ne estrae l’identificatore della procedura
chiamata e i parametri di ingresso (questi in forma ancora serializzata).
In base al valore dell’identificatore l’entita’ chiama il server stub della
procedura chiamata passandogli i parametri di ingresso, ancora in
forma serializzata.
11
Schema dell'interazione RPC: legenda
.2
7.
Il server stub deserializza (decodifica o unmarshaling) i parametri
di ingresso (di cui conosce il tipo) e chiama la procedura remota
tramite una normale chiamata di procedura locale.
8.
La procedura chiamata inizia la propria esecuzione.
Quando la procedura (remota) termina la propria esecuzione ritorna
il controllo al suo chiamante, il server stub, e gli passa i valori di
ritorno previsti.
9.
Il server stub effettua il marshaling (encodifica, serializzazione) dei
valori di ritorno e ritorna il controllo all’entita’ di protocollo RPC.
10. L’entita’ di protocollo RPC crea il PDU di risposta e lo comunica al
suo pari utilizzando il Servizio di Trasporto (11, 12).
13. L’entita’ di protocollo RPC chiamante, che era in attesa del PDU
RPC di risposta, riceve il PDU, ne estrae i parametri di ritorno
(ancora in forma serializzata), e li passa al client stub.
14. Il client stub deserializza (decodifica o unmarshaling) i parametri di
ritorno (di cui conosce il tipo), e ritorna il controllo alla procedura
12
chiamante passandole i risultati e terminando la chiamata.
Schema dell'interazione RPC: riassumendo
1. Il Layer 7.1 si occupa di realizzare in rete le istruzioni Call e Return,
che nella programmazione concentrata sono implementate
direttamente dal processore HW.
2. Il Layer 7.2 si occupa del passaggio dei parametri, cosi’ come nel
contesto della programmazione concentrata se ne occupa il sistema di
programmazione.
3. Il Layer 7.2 si occupa anche di fornire al programma utente (modulo
chiamante/client routine e modulo chiamato/server routine) una
interfaccia reale (operazioni 1, 14, 7, 8) quanto piu’ simile possibile
all’interfaccia virtuale (RPC Call e Return).
• La procedura chiamante invoca la procedura remota tramite una
normale chiamata di procedura locale, chiamata a una procedura
locale (client stub) che rappresenta e ha la stessa signature di
quella remota.
• La procedura remota chiamata e’ attivata tramite una normale
chiamata di procedura locale.
13
Schema dell'interazione RPC: moduli vs. procedure
Return
Modulo chiamante
Call
1
7.2
RPC
cs1
14
Client stub
...
RPC
cs2
2
Client RPC
protocol entity
7.1
3
RPC
1
RPC
2
8
RPC
csn
13
Modulo chiamato
RPC
ss1
RPC
middleware
7
Server stub
...
RPC
ss2
9
RPC
ssn
6
Server RPC
protocol entity
RPC protocol
12
RPC
n
10
5
11
Transport Service
Transport Service
4
14
Obbiettivo di RPC
•
Il dialogo reale chiamante-chiamato visto dai moduli utente
• chiamata: operazioni 1 e 7
• ritorno: operazioni 8 e 14
deve essere quanto piu’ simile possibile al dialogo virtuale
corrispondente
• chiamata: operazione Call
• ritorno: operazione Return
•
Notare che l’operazione virtuale Call (anche la Return!) e’ magica:
• lato chiamante, la corrispondente operazione reale (1) e’ in
accordo al sistema di elaborazione e di programmazione del
chiamante
• lato chiamato, la corrispondente operazione reale (7) e’ in accordo
al sistema di elaborazione e di programmazione del chiamato
15
Schema di programma RPC: lato client
// client routine. Comprende
handleT handle;
handle = linkTo(RPCServer);
while (notDone) {
. . .
retArgs = remoteProc(args,
// interazioni 1 e 14
. . .
}
exit();
la funzione main()
// link dinamico all’
// interfaccia remota
handle);
// client stub (Layer 7.2)
retParams remoteProc(paramsT params, handleT handle) {
encPar = encode(params);
// sa come si fa
encRes = RPCCall(remoteProcId, encPar, handle);
// interazioni 2 e 13
results = decode(encRes); // sa come si fa
return results;
}
16
Schema di programma RPC: lato client
// RPC protocol entity lato chiamante (Layer 7.1)
handleT linkTo(serviceT RPCServer) {
// link dinamico all’interfaccia remota
// set-up delle risorse di comunicazione
. . .
}
opaque RPCCall(procIdT remoteProc, opaque encPar,
handleT handle) {
rpc_pdu = compileEncode(CALL, remoteProc, encPar);
sendTo(rpc_pdu, handle);
// di CALL
// interazione 3
receiveFrom(&rpc_pdu, handle);
// di REPLY
// interazione 12
encRes = checkDecodeClient(rpc_pdu);
return encRes;
// ancora in forma codificata
}
17
Schema di programma RPC: lato server
// server routine (procedura passiva)
retParamsT remoteProc(paramsT params);
. . .
return retArgs;
}
// server stub (Layer 7.2)
opaque serverStub(procIdT proc, opaque encPars) {
switch (proc) {
. . .
case remoteProcId : {
args = decode(encPars);
// sa come si fa
results = remoteProc(args);
// interazioni 7 e 8
encRes = encode(results); // sa come si fa
return encRes;
// gia’ codificati
}
. . .
}
}
18
Schema di programma RPC: lato server
// RPC protocol entity lato chiamato (Layer 7.1)
handleT RPCSetUp(serviceT RPCServer, … serverStub) {
// registrazione dell’interfaccia remota
// set-up delle risorse di comunicazione
. . .
}
void RPCWaitCall(handleT handle) {
receiveFrom(&rpc_pdu, handle);
// di CALL
// interazione 5
{procId, encArgs} = checkDecodeServer(rpc_pdu);
// encArgs e encRes in forma codificata
encRes = handle.serverStub(procId, encArgs);
// interazioni 6 e 9
rpc_pdu = compileEncode(RETURN, procId, encRes);
sendTo(rpc_pdu, handle);
// di REPLY
// interazione 10
}
19
Schema di programma RPC: lato server
//
//
//
//
//
il server non e’ solo un insieme di procedure
passive: deve aspettare attivamente l’arrivo di
messaggi di chiamata dalla rete, quindi deve essere
implementato come un processo. Quindi:
il server deve avere anche la sua funzione main()
// server process skeleton (Layer 7.2)
int main(. . .) {
handle = RPCHandleSetUp(RPCServer, serverStub);
// potrei voler registrare diverse interfacce
while (1) {
RPCWaitCall(handle);
}
}
20
Schema di programma RPC: note
• Gli stub sono legati al particolare servizio RPC. Per questo:
• Sono in grado di riconoscere e gestire in modo appropriato la particolare
chiamata di RPC (la particolare procedura chiamata).
• Conoscendo la particolare procedura RPC chiamata conoscono i tipi dei
parametri di chiamata e di ritorno e sono quindi in grado di de/codificarli.
• La protocol entity RPC e’ indipendente dal particolare servizio RPC.
• Il lato server, a livello di applicazione utente, e’ passivo: dal punto di vista del
modulo utente esso e’ costituito solo da un insieme di procedure.
• Ma per aspettare messaggi di Call dalla rete bisogna essere attivi!
Ogni processo ha bisogno di una funzione main()!
• Il process skeleton lato server fornisce questa funzione main().
• Lo skeleton lato server, facendo da collante, e’ legato al particolare servizio
RPC e alla particolare interfaccia (API utente) che deve essere esportata per
renderlo disponibile.
• Lo skeleton lato server e’ parte dell’infrastruttura RPC.
• Lato client la funzione main() e’ una normale funzione utente.
21
Schema di programma : lato server
Modulo chiamato
RPC
1
7.2
Process
skeleton
RPC
ss1
RPC
2
Server stub
...
RPC
ss2
RPC
n
RPC
ssn
RPC
middleware
7.1
Server RPC
protocol entity
22
Perche’ RPC
• Nella costruzione di applicazioni distribuite il meccanismo dell’RPC
nasconde la complessita’ dell'interfacciamento alla rete:
• Le procedure utente client e server non si devono preoccupare di
interagire con socket e Layer di Trasporto:
 Ci pensa la protocol entity RPC (Layer 7.1).
• Le procedure utente client e server non si devono preoccupare delle
diverse modalita’ di rappresentazione locale dell’informazione e
dell’un/marshaling (dell’interazione con il Layer di Presentazione):
 Ci pensano gli stub (Layer 7.2).
• Scrivere un’applicazione distribuita dovrebbe risultare altrettanto facile
che scrivere un’applicazione concentrata.
• I principi per la scomposizione in moduli di un’applicazione distribuita
sono identici a quelli utilizzati nella programmazione concentrata (in
linea di principio: quanto costa una chiamata RPC?).
• I meccanismi di interazione tra moduli che si utilizzano nella
programmazione distribuita sono gli stessi utilizzati nella
programmazione concentrata (ma con un costo ben diverso!).
23
Il servizio RPC nel modello di riferimento OSI
• Secondo il modello OSI, RPC e’ un servizio dell’Application Layer.
• RPC utilizza i servizi del Presentation Layer per due scopi:
1. Per descrivere e comunicare i PDU del protocollo RPC stesso.
 Protocollo del Layer 7.1 (e.g. protocollo Sun RPC v2).
2. Per descrivere e comunicare i parametri di ingresso e di uscita
delle chiamate.
 Protocollo del Layer 7.2.
• I sistemi di programmazione RPC sono composti di:
•
Un compilatore per generare gli stub cliente e servitore (e il server
skeleton: Layer 7.2) a partire dai prototipi delle procedure remote.
•
Le entita’ di protocollo RPC (client/chiamante e server/chiamato:
Layer 7.1. Costituiscono la parte run-time del sistema di
programmazione RPC).
• I sistemi di programmazione RPC si basano sull’utilizzo dei sistemi di
programmazione del Layer di Presentazione (ASN.1 o XDR).
24
Problemi
• Passaggio dei parametri e dei risultati e
rappresentazione dei dati.
• Binding.
 N.B.: sfortunatamente il significato della parola binding e’ ambiguo. Nel
contesto RPC il suo significato e’ lo stesso che le si attribuisce nel
contesto dei linguaggi di programmazione: collegamento tra un
riferimento e una definizione di una funzione.
 Riportato in terminologia di reti di calcolatori il significato del termine
binding di RPC e’ quindi piu’ simile a quello di parole come
collegamento o connessione.
• Semantica dell’RPC e
modalita’ d’esecuzione.
• Servizio di trasporto utilizzato.
• Exception handling.
• Security.
25
Passaggio dei parametri e rappresentazione dei dati
• Tutti i sistemi di programmazione RPC si basano su tre principi:
• La definizione di una sintassi di trasferimento canonica.
• La definizione di un linguaggio per definire la sintassi astratta.
• La selezione di un insieme di linguaggi di programmazione target per
i quali supportare il mapping tra la sintassi di trasferimento e la
sintassi locale di rappresentazione dell’informazione.
• La sintassi locale di rappresentazione dell’informazione e’ normalmente
definita non dal programmatore ma dal sistema di programmazione
RPC.
• Il problema del passaggio dei parametri tra chiamante e chiamato e’
risolto in linea di principio
• attraverso l’utilizzo dei servizi del Layer di Presentazione e
• attraverso l’utilizzo di un type checking dinamico.
• Questo risolve il problema costituito dal fatto che diversi sistemi di
programmazione per diversi linguaggi, su diverse macchine,
rappresentano lo stesso tipo di informazione in modi diversi.
26
Passaggio dei parametri e rappresentazione dei dati
• Notare che attraverso meccanismi del Layer di Presentazione quali i
dati opzionali di XDR e’ possibile passare tra chiamante e chiamato
anche strutture dati complesse (e basate sull’uso di puntatori!) come
liste e alberi.
 Quello che si passa e’ pero’ una copia della lista o dell'albero
(passaggio per valore), non un riferimento ad esso.
• Rimangono differenze significative rispetto alla chiamata di procedura
locale:
•
Non e’ possibile passare parametri per riferimento.
•
Non e’ possibile condividere variabili globali.
• Questi problemi sono stati poi risolti in un contesto di programmazione
Object-Oriented in cui:
•
E’ (deve essere) possibile passare come parametro il riferimento
ad un oggetto: ovviamente, non per modificarlo da parte del
chiamato ma solo per poterne invocare i metodi.
•
L’utilizzo di variabili globali e’ comunque deprecato.
27
Binding
• Nei sistemi di programmazione locale il collegamento corretto tra una
invocazione di una particolare procedura e la procedura stessa e’ basato
sul match dei nomi e sul type checking, ed e’ responsabilita’ del linker
(quando chiamante e chiamato non appartengono ad uno stesso
modulo, altrimenti esso e’ realizzato direttamente dal compilatore).
• Nel caso di un programma distribuito chiamante e chiamato non
possono essere collegati insieme staticamente.
• Come puo’ il chiamante localizzare correttamente la procedura chiamata
(dato il suo nome)?
• Localizzare la macchina su cui essa e’ allocata.
• Localizzare la porta di protocollo di trasporto tramite la quale sono
raggiungibili i servizi offerti sulla rete dal modulo remoto.
• Identificare la particolare interfaccia / procedura remota su quella
macchina / porta.
• N.B.: ricordare sempre: una procedura fa parte di un modulo (e’ una
delle n procedure del modulo) e, in prima battuta, e’ il modulo nel suo
complesso che viene linkato.
28
Binding
• Tre soluzioni possibili:
• Un name server globale, con un indirizzo ben noto
(vedi ORB, Object Request Broker, di CORBA)
• Un name server locale sulla macchina remota, su una porta ben
nota
Il cliente deve conoscere l’indirizzo di rete della macchina (come?),
ma il name server risolve il nome della RPC
• Un indirizzo di rete e una porta ben noti per la procedura
(interfaccia) remota
• Vantaggi di un name server globale:
• Consentirebbe la riallocazione dinamica del server su macchine
diverse in modo trasparente al cliente.
• Svantaggi di un name server globale: complessita’
29
Semantica dell'RPC
•
Stevens:
• "When we call a local procedure, there is never any question as
how many times the procedure executed.
• If it returns to the caller we know that it executed exactly once.
• With a remote procedure, however, if we don't get a response
after a certain interval, we don't know how many times the remote
procedure was executed."
•
Perche' non c'e' stata risposta?
• La richiesta del chiamante non e' mai arrivata a destinazione.
• La richiesta del chiamante e' arrivata a destinazione, ma la
procedura remota non e' stata eseguita a causa di un crash.
• La procedura remota e' stata eseguita, ma la risposta non e' stata
inviata a causa di un crash.
• La procedura remota e' stata eseguita e la risposta e' stata
inviata, ma non e' arrivata a destinazione.
• E se a subire il crash fosse il chiamante, dopo avere inviato la
richiesta? Cosa dovrebbe succedere al chiamato rimasto orfano?
• E se il chiamante, se non riceve risposta, ripete la richiesta? 30
Semantica dell'RPC
•
Sono definite canonicamente tre diverse, possibili semantiche per
l'RPC:
1. At least once.
2. Exactly once.
3. At most once,
o all-or-nothing,
o transazionale
o atomica
o ACID:
• Atomic
• Consistent
• Isolated
• Durable
31
Semantica dell'RPC: at least once
•
Quando il chiamante riceve la risposta il chiamato e' stato eseguito
almeno una volta.
•
Gli schemi di chiamante e chiamato sono i seguenti:
chiamante
do {
chiamato
while (TRUE) {
inviaRichiesta(. . .);
riceviRichiesta(. . .);
attendiRisposta(. . .);
processa(. . .);
} while (non riceve
risposta);
inviaRisposta(. . .);
}
•
Questa semantica e' utilizzabile quando la procedura remota e'
idempotente (puo' essere ripetuta fornendo sempre il medesimo
risultato): e.g. leggi/scrivi un blocco di disco, leggi l'estratto conto.
•
Esempi di procedure non idempotenti: appendi un blocco ad un file,
preleva 1.000$ dal conto corrente.
32
Semantica dell'RPC: exactly once
•
Se il chiamante riceve la risposta il chiamato e' stato eseguito
esattamente una volta, se no puo' essere stato eseguito 0 o 1 volta.
•
Gli schemi di chiamante e chiamato sono i seguenti:
chiamante
inviaRichiesta(. . .);
chiamato
while (TRUE) {
attendiRisposta(. . .);
riceviRichiesta(. . .);
if (nessuna risposta) {
processa(. . .);
riallinea(. . .);
}
inviaRisposta(. . .);
}
•
Questa semantica affida al programma chiamante il trattamento delle
situazioni anomale.
•
Se non riceve risposta il chiamante, prima di fare qualunque cosa,
deve interrogare il chiamato per verificarne lo stato.
33
Semantica dell'RPC: at most once
•
Se il chiamante riceve la risposta il chiamato e' stato eseguito
esattamente una volta, se no il chiamato non e' stato eseguito
(nemmeno parzialmente!) e il suo stato e lo stato generale del
sistema non sono stati modificati.
•
E' la semantica delle transazioni distribuite, quando occorre
coordinare l'attivita' di diversi server, garantendo che, o tutti
eseguono il loro lavoro fino in fondo, o nessuno fa niente.
•
Esempi:
• ritiro 100$ al bancomat CityBank,
• e CityBank addebita a WellsFargo Bank 100$ piu' le spese,
• e WellsFargo Bank addebita sul mio conto 100$ piu' le
spese.
• Voglio prenotare l'aereo per Parigi e un albergo:
• se l'uno o l'altro non e' disponibile, non voglio prenotare
niente.
34
RPC: modello d'esecuzione
•
Server concorrente vs.
Server sequenziale.
•
RPC confermate vs.
RPC non confermate
 e in particolare, RPC non confermate broadcast
•
RPC confermata sincrona (bloccante) vs.
RPC confermata asincrona (non bloccante).
•
Possibilita' di effettuare piu' chiamate RPC senza attendere la
risposta vs.
Possibilita' di effettuare una sola chiamata RPC per volta.
•
N.B.: "non/confermato" non ha niente a che fare con l'affidabilita'
della comunicazione.
La nozione e' legata al modello di interazione: c'e' o non c'e' una
risposta esplicita dal chiamato al chiamante.
35
Unix: modello d'esecuzione confermata sincrona
chiamante / padre
chiamato / figlio
fork();
wait();
exec();
exit();
36
Unix: modello d'esecuzione non confermata
chiamante / padre
chiamato / figlio
fork();
exec();
chiamato / figlio
fork();
exec();
exit();
chiamato / figlio
fork();
exit();
exec();
exit();
37
Unix: modello d'esecuzione confermata asincrona
chiamante / padre
chiamato / figlio
fork();
exec();
chiamato / figlio
fork();
exec();
exit();
wait();
// quale figlio
// e’ terminato?
exit();
38
Servizio di trasporto
•
I sistemi di programmazione RPC tendono a proclamarsi
indipendenti dal Servizio di Trasporto utilizzato.
• Puo’ essere persino ammesso che un server offra il proprio servizio
contemporaneamente su diversi Servizi di Trasporto.
•
Naturalmente la semantica dell’RPC puo’ essere influenzata dalle
caratteristiche del Servizio di Trasporto.
• La semantica exactly-once, quella piu’ normale e diffusa, e’ facile da
implementare su COTS, piu’ difficile (in forma affidabile) su CLTS.
• Anche l’interfaccia offerta da una procedura remota puo’ essere vincolata
dal Servizio di Trasporto utilizzato, ad esempio perche’ i PDU RPC di
chiamata e di ritorno possono dover essere contenuti in un singolo TSDU (come nel caso caso dell’RPC Sun su UDP).
•
A parita’ di semantica RPC (e livello di affidabilita’) il supporto RPC
deve operare diversamente a seconda che si utilizzi CLTS o COTS.
• Nel caso operi su CLTS puo’ volersi occupare di error detection e
recovery (e.g. di ritrasmissione di una chiamata in caso non sia ricevuto
un PDU di risposta, e del corrispondente scarto lato server di chiamate
duplicate).
39
Exception handling
•
La possibilita’ del verificarsi di situazioni abnormi aumenta
moltissimo quando si va ad operare in ambiente distribuito:
• Problemi di comunicazione.
• Restart/crash di sistemi.
• Abort di thread remote (lato chiamante o chiamato).
•
In caso di errore occorre poter distinguere tra:
• Errore rivelato dal provider del servizio RPC (e.g. binding della
RPC fallito, problemi di type checking, problemi di
comunicazione): eccezione di sistema.
• Errore durante l’esecuzione della RPC (e.g. il file cercato non
esiste): eccezione utente.
•
Possibilita’ per il client di richiedere il kill del server.
•
Gestione di server orfani (il cui chiamante e’ abortito).
40
Security
• Al di la' di eventuali problemi di riservatezza, i problemi fondamentali
dell'RPC sono:
• autenticazione dell'identita' del chiamante, e
• conseguente verifica dell'autorizzazione del chiamante a
richiedere l'operazione chiamata.
• Sono gli stessi problemi che si incontrano in applicazioni di terminale
remoto (rlogin, TELNET) o di esecuzione remota di comandi (rexec,
rcmd).
• "Allowing a remote program to call a procedure on your system is
similar to someone executing a command on your system." (Stevens)
• Il problema fondamentale e' quindi l'autenticazione del chiamante
(client) da parte del chiamato (server).
• Ovviamente esiste anche il problema di autenticare il chiamato
(server) rispetto al chiamante (client).
41
Sun RPC: Open Network Computing Remote Procedure Call
• http://en.wikipedia.org/wiki/Open_Network_Computing_Remote_Procedure_Call
• Open Network Computing (ONC) Remote Procedure Call (RPC) is a widely
deployed remote procedure call system. ONC was originally developed by Sun
Microsystems as part of their Network File System project, and is sometimes
referred to as Sun ONC or Sun RPC. ONC is considered "lean and mean”.
• ONC is based on calling conventions used in Unix and the C programming
language. It serializes data using the XDR.
• ONC then delivers the XDR payload using either UDP or TCP.
• Access to RPC services on a machine are provided via a port mapper that
listens for queries on a well-known port (number 111) over UDP and TCP.
• Implementations of ONC RPC exist in most Unix-like systems.
• Microsoft supplies an implementation for Windows in their Microsoft Windows
Services for UNIX product.
• In addition, a number of third-party implementation of ONC RPC for Windows
exist, including versions for C/C++, Java, and .NET.
• ONC RPC was described in RFC 1831, published in 1995. RFC 5531, published
in 2009, is the current version.
42
Sun RPC (RFC 1057)
•
A network service is a collection of one or more remote programs.
•
For example, a network file service may be composed of two programs.
• One program may deal with high-level applications such as file
system access control and locking.
• The other may deal with low-level file input and output and have
procedures like "read" and "write".
•
A remote program implements one or more remote procedures.
• un remote program rappresenta un modulo del programma
distribuito e il servizio da esso fornito implementa l’interfaccia del
modulo.
•
The procedures, their parameters and results are documented in the
specific program’s protocol specification
(specifica dell’interfaccia, in linguaggio RPC, una estensione di XDR).
•
A server may support more than one version of a remote program in
order to be compatible with changing protocols.
•
N.B. un server puo’ implementare anche piu’ di una interfaccia!
43
Sun RPC (RFC 1057): rappresentazione dei dati
• Viene utilizzato XDR, che fornisce sia una sintassi di trasferimento che
un linguaggio per la definizione di sintassi astratte.
• Il linguaggio di programmazione target per il sistema di
programmazione XDR/RPC e' il C (N.B.: l’unico che consideriamo qui).
• Non e' un vincolo di architettura, e' solo che e' stato scelto il C come
primo linguaggio target, poi non ci sono piu' state tante altre
implementazioni (e' un limite del supporto XDR/RPC).
• In realta' esiste un supporto XDR/RPC anche per Java e .NET.
• La XDR library e il compilatore XDR/RPC (rpcgen) definiscono la
sintassi concreta di rappresentazione locale dei dati.
• Diverse versioni del supporto RPC hanno supportato diversi template
(formati del prototipo) per definire procedure remote.
• All'inizio era ammesso un unico parametro di ingresso, che doveva
essere passato per riferimento.
• Versioni piu' recenti ammettono anche piu' parametri che possono
essere passati anche per valore, come si fa normalmente in C. 44
Sun RPC (RFC 1057): rappresentazione dei dati
•
XDR, in quanto sintassi di trasferimento, si basa sulla tipizzazione
implicita dell'informazione:
• Nessuna informazione di tipo viene trasportata insieme al valore.
•
Che conseguenze ha cio' per RPC?
• In base alla conoscenza di quale procedura remota e' chiamata o
sta ritornando il risultato, e al prototipo della procedura stessa, i tipi
del parametro di ingresso e di quello di ritorno sono noti, non c'e'
bisogno di trasportarli esplicitamente nel PDU di call o return.
• Dal punto di vista di chiamante e chiamato, la presenza di una
informazione di tipo esplicita consentirebbe solo (solo?!) un type
check migliore.
•
La presenza di informazioni esplicite di tipo renderebbe invece
possibile la realizzazione di protocol analyzer, che cosi' sono invece
impossibili da realizzare in assenza di informazioni sulla sintassi
astratta della RPC.
45
Sun RPC (RFC 1057): rappresentazione dei dati
 E’ evidente che anche se un argomento e’ passato per riferimento
la procedura chiamata non puo’ modificarne (direttamente) il valore!
• Quello che la procedura chiamata ha in mano e’ il riferimento ad
una copia locale dell’argomento attuale remoto.
 Logicamente il passaggio di parametri, in input e in output, e’
sempre per valore.
•
Esercizio: date le regole di de/codifica di XDR (fare riferimento
all’uso di XDR stream in memoria) come puo’ avvenire la
generalizzazione da un solo parametro per riferimento a N
parametri, per valore o per riferimento?
•
Esercizio: in che cosa consiste il type checking effettuato dal
chiamato? Come, nel contesto di una operazione di decodifica
XDR, ci si puo’ accorgere di una incongruenza tra il valore ricevuto
e il suo tipo atteso?
46
Sun RPC (RFC 1057): semantica
•
Because of transport independence, the RPC protocol does not attach
specific semantics to the remote procedures or their execution
requirements.
• Semantics can be inferred from (but should be explicitly specified
by) the underlying transport protocol.
•
Sun RPC protocol does not try to implement any kind of reliability.
 In ogni caso, in base alla classificazione che abbiamo dato, la
semantica della RPC Sun e’ exactly once, in quanto non e’
prevista, da parte del chiamante, la ritrasmissione del PDU di
CALL in caso di mancata ricezione del PDU di REPLY.
•
The application may need to be aware of the type of transport protocol
underneath RPC.
• If it is running on top of an unreliable transport such as UDP (e se
vuole comunque avere un livello elevato di affidabilita’ nelle
comunicazioni), it must implement its own time-out,
retransmission, and duplicate detection policies as the RPC layer
does not provide these services.
47
RPC: semantica (non di Sun RPC!)
• Consider RPC running on top of an unreliable transport such as UDP.
• If an application retransmits RPC call messages after time-outs, and does not
receive a reply, it cannot infer anything about the number of times the procedure
was executed.
• If it does receive a reply, then it can infer that the procedure was executed at
least once.
• A server may wish to remember previously granted requests from a client and
not regrant them in order to insure some degree of exactly-once semantics.
• A server can do this by taking advantage of the transaction ID (campo xid) that
is packaged with every RPC message (PDU rpc_msg).
• The main use of this transaction ID is by the client RPC layer in matching
replies to calls.
• However, a client application may choose to reuse its previous transaction
ID when retransmitting a call.
• The server may choose to remember this ID after executing a call and not
execute calls with the same ID.
• Il server deve ricordare anche il valore di ritorno di ogni chiamata eseguita,
in modo da poterlo inviare al cliente nel PDU di REPLY in caso riceva di
48
nuovo un PDU di CALL con lo stesso ID.
Sun RPC (RFC 1057): modello di esecuzione
• The intended use of the RPC protocol is for calling remote procedures.
• Normally, each call message is matched with a reply message.
• However, the RPC protocol itself is a message-passing protocol with
which other (non-procedure call) protocols can be implemented.
 N.B.: qui per protocollo intende solo i PDU scambiati tra i pari.
• Sun currently uses, or perhaps abuses, the RPC message protocol for
•
the batching (or pipelining) of procedure calls.
• In the case of batching, the client never waits for a reply from
the server and the server does not send replies to batch calls.
• A sequence of batch calls is usually terminated by a legitimate
RPC operation in order to flush the pipeline and get a positive
acknowledgement.
•
broadcast remote procedure calls.
• In broadcast protocols, the client sends a broadcast call to the
network and waits for numerous replies.
• This requires the use of packet-based protocols (like UDP) as
49
its transport protocol.
Broadcast RPC
•
Poiche’ TCP e’ basato su connessioni punto-punto non supporta l’idea
di messaggi broadcast.
•
IP supporta indirizzi broadcast e multicast, sia a livello di sottorete che
di internetwork.
•
E’ quindi possibile indirizzare un datagram UDP contemporaneamente
a molteplici destinatari
• allocati su macchine diverse,
• ma che si affacciano alla rete con lo stesso numero di porta.
•
Dal punto di vista dell’RPC Sun, in cui i server RPC offrono il loro
servizio su porte effimere, e’ praticamente impossibile che lo stesso
servizio sia offerto su macchine diverse tramite la stessa porta!
•
Come e’ possibile allora effettuare chiamate broadcast di RPC?
 Un supporto ad hoc e’ fornito dal Port Mapper (vedi seguito).
50
Sun RPC (RFC 1057): modello di concorrenza
• The Sun RPC protocol makes no restrictions on the concurrency
model implemented and, beside the synchronous local-like model,
others are possible.
• For example, an implementation may choose to have RPC calls be
asynchronous, so that the client may do useful work while waiting for
the reply from the server.
• Another possibility is to have the server create a separate task/thread
to process an incoming call, so that the original server can be free to
receive other requests.
• Server concorrente vs. Server sequenziale.
• Entrambi i modelli sono supportati dal sistema di programmazione
Sun RPC.
51
Sun RPC (RFC 1057): binding
Secondo lo schema normale di costruzione di programmi distribuiti basati sul
supporto Sun RPC:
• Un server RPC offre il proprio servizio tramite una porta di trasporto effimera.
• Il cliente deve sapere a priori su quale macchina il servizio e’ offerto.
• Il cliente deve sapere a priori se un servizio e’ offerto tramite TCP o UDP (o
entrambi).
• Quello che il cliente non puo’ comunque sapere a priori e’ su quale porta il
servizio e’ offerto: lo stesso server puo’ saperlo solo dopo che la porta effimera
e’ stata effettivamente allocata (bind-ata)!
• La porta utilizzata dal servizio deve quindi essere acquisita dal cliente
dinamicamente!
• Perche’ questo sia possibile occorre che ci sia un servizio di pagine gialle
(name service) che su ogni macchina effettui il mapping
servizio basato su RPC  porta su cui il servizio e’ offerto.
• Notare che questo name service, nell’architettura Sun RPC, e’ locale: fornisce
informazioni solo relativamente a servizi basati su RPC che sono allocati sulla
sua stessa macchina.
• Ogni RPC server, durante la sua fase di inizializzazione, deve registrare sul
52
name service la porta effimera su cui offre il servizio.
Sun RPC (RFC 1057): binding
• The act of binding (collegare) a particular client to a particular service
and transport parameters is not part of the Sun RPC protocol
specification.
• Binding is left up to some higher-level software.
• Implementors could think of the RPC protocol as the Jump-ToSubroutine instruction (JSR o Call) of a network.
• La funzione di binding di un programma remoto (interfaccia remota) e'
supportata da servizi applicativi come il Port Mapper (vedi seguito).
• Il Port Mapper a sua volta si basa sull'utilizzo di RPC, sull'uso di un
numero di porta well-known, e sull'offerta dei propri servizi sia su
UDP che su TCP.
• Per una descrizione del Port Mapper vedi il seguito della lezione.
• Versioni piu' recenti del supporto RPC (su Sun Solaris) hanno
sostituito il Port Mapper con un servizio analogo chiamato rpcbind.
• Il binding di singole procedure remote all'interno di un programma
53
(interfaccia) e' demandato al supporto RPC del server stub.
Sun RPC (RFC 1057): servizio di trasporto
• The RPC protocol can be implemented on several different transport
protocols.
• The RPC protocol does not care how a message is passed from one
process to another, but only with specification and interpretation of
messages.
• On the other hand, the application may wish to obtain information about
(and perhaps control over) the transport layer.
• The transport protocol may impose a restriction on the maximum size
of RPC messages, like UDP, or it may be stream-oriented like TCP
with no size limit.
• If RPC is running on top of an unreliable transport such as UDP, the
service must devise its own retransmission and time-out policy.
Sun RPC does not provide this service.
• Client and server must agree on their transport protocol choices, e.g.
through a mechanism such as the one supported by the Port Mapper
(vedi).
• Un programma remoto puo' offrire i suoi servizi simultaneamente su TCP
54
e su UDP.
Sun RPC (RFC 1057): Exception handling
.1
• Sono previste diverse condizioni di eccezione di sistema:
• Se non riceve una risposta entro lo scadere di un timer
(configurabile) il client stub genera una eccezione.
• Eccezioni vengono generate anche se si rilevano condizioni
anomale nel protocollo RPC, nel binding o nell’un/marshaling.
• Nel caso il Servizio di Trasporto utilizzato sia COTS (TCP) una
eccezione viene sollevata anche in caso di caduta della
connessione.
• La condizione di eccezione viene segnalata esplicitamente dal client
stub al modulo chiamante.
• Il chiamante, utilizzando funzioni del middleware RPC,
• Puo’ interrogare il supporto RPC per avere informazioni di
dettaglio sulla condizione di errore (clnt_geterr()), e/o
• Puo’ tracciare l’errore su standard error (clnt_perror()).
55
Sun RPC (RFC 1057): Exception handling
•
.2
Non e' invece prevista la possibilita' di definire condizioni di
eccezione utente sull'interfaccia.
 Sono i valori dei normali parametri di ritorno che devono essere
in grado di descrivere anche condizioni di eccezione utente.
•
Il chiamante non ha alcuna possibilita' di interrompere l'esecuzione
della procedura remota.
•
Non c'e' alcun supporto che consenta la gestione della condizione di
orfano.
56
Sun RPC: Security
•
Il sistema Sun RPC supporta diverse modalita' di autenticazione del
client da parte del server (ed e' aperto ad ulteriori estensioni).
• Nessuna autenticazione (AUTH_NULL, modalita' di default).
• Autenticazione à la Unix (AUTH_SYS, prima detta AUTH_UNIX).
(vedi seguito)
• Autenticazione basata sulla tecnica di encrittazione DES
(AUTH_DES).
• Autenticazione secondo la modalita' Kerberos (anch'essa basata
su encrittazione DES) (AUTH_KERB).
•
Un campo del PDU RPC indica la modalita' di autenticazione
utilizzata:
(struct call_body).cred.flavor
•
Tutti gli esempi delle dispense non implementano alcuna forma di
autenticazione: questa e' la modalita' di default.
(struct call_body).cred.flavor == AUTH_NULL
57
rpcgen
•
The rpcgen tool generates remote program interface modules (client
e server stub + server skeleton).
It compiles source code written in the XDR/RPC language
•
rpcgen produces one or more C language source modules, which
are then compiled by a C compiler
•
The default output of rpcgen is:
• A header file of definitions (e dichiarazioni C) common to the
server and the client (l’interfaccia implementativa client-server)
• A set of XDR routines that translate (de/codifica: le funzioni filtro
XDR) each data type defined in the header file
• A stub program for the server (compreso il process skeleton)
• A stub program for the client
•
rpcgen can optionally generate:
• A time-out for servers
• Server stubs that are not main programs
• An RPC dispatch table that checks authorizations
58
rpcgen: esempio 1
•
.1
Stralcio di un modulo che realizza un servizio locale di message log:
# include “messagelog.h”
/* Prints a message to the the log file.
* Returns a boolean: success (true)/ failure(false)
*/
int printmessage(msg m) {
FILE *f;
if ((f = fopen("my.log", "a")) == NULL) {
return (0);
}
if (fprintf(f, "%s\n", m) != strlen(m)+1) {
fclose(f);
return (0);
}
fclose(f);
return(1);
}
// continua alla pagina seguente
59
rpcgen: esempio 1
.1’
// continua dalla pagina precedente
/* Reset the log file. Return a boolean
* indicating whether the operation succeded.
*/
int resetlog(void) {
FILE *f;
if ((f = fopen("my.log", "w")) == NULL) {
return (0);
}
fclose(f);
return(1);
}
60
rpcgen: esempio 1
.2
•
Quando si definisce un servizio, per prima cosa bisogna definire
l’API tramite cui esso e’ accessibile ai clienti.
•
Header file C che descrive l’API di accesso al servizio di message
log, cioe’ il protocollo chiamante-chiamato:
// messagelog.h – message log service API
#ifndef _MESSAGELOG
#define _MESSAGELOG
typedef char *msg;
int printmessage(msg);
int resetlog(void);
#endif
 Vorremmo trasformare il servizio di message log in un servizio di
rete.
61
rpcgen: esempio 1
.3
• Prima cosa: definire l’interfaccia del servizio in linguaggio XDR/RPC.
/* messagelog.x: Remote message log protocol */
const MAXLINE = 256;
typedef string msg<MAXLINE>;
program MESSAGELOG {
version MESSAGELOGVERS {
int PRINTMESSAGE(msg) = 1;
int RESETLOG(void) = 2;
} = 1;
} = 0x20000001;
• Remote procedures are always declared as part of remote programs
(interfacce remote versionate).
62
Identificazione di una procedura remota
• Procedure PRINTMESSAGE is declared to be
• procedure 1
• in version 1
• of the remote program MESSAGEPROG, with the program number
0x20000001.
• Version numbers are incremented when functionality is changed in the
remote program.
 More than one version of a remote program can be defined.
• Una procedura remota e’ identificata da una tripla di numeri:
1. il numero del programma cui appartiene;
2. il numero della versione di programma cui e’ relativa;
3. il numero assegnato alla funzione all’interno della versione.
• In realta’ ci sono altre 3 informazioni necessarie per indirizzare una
istanza del servizio:
• La macchina su cui viene offerto il servizio (viene implementata
l’interfaccia): indirizzo IP.
• La porta e il protocollo di trasporto su cui viene offerto il servizio
63
(viene implementata l’interfaccia).
Identificazione di una interfaccia remota
• Il numero di versione e quello della funzione sono assegnati dal
programmatore, e sono significativi solo all’interno del programma.
• I numeri dei programmi sono invece soggetti a regolamentazione:
• Da 0x00000000 a 0x1fffffff sono assegnati dalla Sun.
• Da 0x20000000 a 0x3fffffff sono assegnati liberamente per
uso locale.
• Da 0x40000000 a 0x5fffffff sono per applicazioni che
utilizzano program numbers generati dinamicamente.
• Da 0x60000000 a 0xffffffff sono riservati.
• Solo i numeri di programma assegnati da Sun hanno garanzia di
univocita’ universale.
• Numeri assegnati localmente sono utilizzabili solo localmente (per
evitare conflitti d’uso).
64
Identificazione di una interfaccia versionata remota
• Il numero di una funzione e’ significativo solo nel contesto di una
interfaccia versionata.
• Una interfaccia versionata identifica un servizio di rete RPC-based.
• Una interfaccia versionata e’ identificata da una coppia di numeri:
• Numero (identificatore) del programma (cioe’ dell’interfaccia).
• Versione del programma (cioe’ dell’interfaccia).
• Il numero di versione e’ significativo solo nel contesto di una
interfaccia (programma).
• Il Port Mapper si occupa quindi solo del binding di interfacce
versionate (locali), cioe’ del mappaggio
interfaccia versionata  porta su cui viene offerto il servizio
• in questo momento,
• su questa macchina.
65
rpcgen: esempio 1
.4
• Header file C prodotto dalla compilazione dell'interfaccia remota con rpcgen:
#ifndef _MESSAGELOG_H_RPCGEN
#define _MESSAGELOG_H_RPCGEN
#include <rpc/rpc.h>
#define MAXLINE 256
typedef char *msg;
#define MESSAGELOG 0x20000001
#define MESSAGELOGVERS 1
#define PRINTMESSAGE 1
int *printmessage_1(msg *, CLIENT *);
int *printmessage_1_svc(msg *, struct svc_req *);
#define RESETLOG 2
int *resetlog_1(void *, CLIENT *);
int *resetlog_1_svc(void *, struct svc_req *);
int messagelog_1_freeresult(SVCXPRT *,
xdrproc_t, caddr_t);
bool_t xdr_msg(XDR *, msg *);
66
#endif
rpcgen : esempio 1
.4’
program MESSAGELOG {
version MESSAGELOGVERS {
int PRINTMESSAGE(msg) = 1;
int RESETLOG(void) = 2;
} = 1;
} = 0x20000001;
messagelog.x
Client routine
. . .
printmessage_1(…);
. . .
Server routine
int *printmessage_1_svc(…) {
. . .
}
messagelog.h
Client stub:
int *printmessage_1(…) {
7.2
. . .
}
Server stub:
. . .
printmessage_1_svc(…);
. . .
67
rpcgen: esempio 1
.5
• Server RPC:
/* msg_proc.c: implementation of the remote
* module "message log" */
#include <stdio.h>
#include "messagelog.h" /* generated by rpcgen */
int *printmessage_1_svc(msg *m,
struct svc_req *req) {
static int result; /* must be static! Perche’? */
FILE *f;
if ((f = fopen("my.log", "a")) == NULL) {
result = 0; return (&result);
}
if (fprintf(f, "%s\n", *m) != strlen(*m)+1) {
fclose(f); result = 0; return (&result);
}
fclose(f);
result = 1; return(&result);
}
68
rpcgen: esempio 1
.6
• Server RPC (continua):
int *resetlog_1_svc(void *dummy,
struct svc_req *req) {
// N.B.: nota che una procedura senza parametri
// funzionali e’ stata trasformata in una procedura
// con un parametro funzionale di tipo (void*), che
// poi viene ignorato!
static int result; /* must be static! */
FILE *f;
if ((f = fopen("my.log", "w")) == NULL) {
result = 0;
return (&result);
}
fclose(f);
result = 1;
return (&result);
}
69
rpcgen: esempio 1
.7
• Server RPC (continua). Notare che:
• Il nome delle procedure e’ correlato al nome utilizzato nella
definizione dell’interfaccia (conversione caratteri maiuscoli in
minuscoli, e suffisso legato alla versione).
• I parametri funzionali di ingresso e anche il risultato sono passati
(implementativamente) per riferimento e non per valore.
• Sono ammessi un solo parametro funzionale di ingresso e uno di
uscita (e questi ci sono sempre!).
 Nel caso sia necessario scambiarsi piu’ informazioni, queste
devono (e possono sempre!) essere impaccate in una singola
struttura dati.
 N.B.: in realta’ il sistema di programmazione RPC adesso
supporta anche interfacce con piu’ parametri di ingresso e il
passaggio per valore dei parametri.
 L’esempio e’ un po’ un ibrido, perche’ si basa sulla vecchia
disciplina ma e’ in C ANSI: l’uso del C ANSI anziche’ del C
tradizionale come linguaggio target e’ un’opzione diventata
70
possibile in rilasci successivi di rpcgen.
rpcgen: esempio 1
.8
• Server RPC (continua). Notare che:
• I parametri di ingresso e il risultato sono rappresentati localmente
in accordo alla rappresentazione concreta locale prevista dal
sistema di programmazione XDR.
• Poiche’ il risultato (valore di ritorno della funzione chiamata) e’
passato per riferimento, esso deve essere contenuto in una
variabile statica (perche’?).
• Nelle procedure e’ presente un parametro addizionale (struct
svc_req *req). Esso contiene informazioni aggiuntive sul
contesto di chiamata, ed in particolare:
• informazioni correlate ai parametri di security
(cred e verf del PDU RPC di call);
• il numero della procedura remota che e’ stata chiamata.
(questo numero e’ utilizzato da un dispacciatore che e’ parte
del server stub per selezionare quale delle procedure che
fanno parte del programma deve essere chiamata)
71
rpcgen: esempio 1
.9
• Client RPC:
#include <stdio.h>
#include "messagelog.h" /* generated by rpcgen */
main(int argc, char *argv[]) {
CLIENT *clnt;
int *result;
char *server;
char sendLine[MAXLINE+1]; msg message = sendLine;
if (argc != 2) {
fprintf(stderr,
"usage: %s host missing\n", argv[0]);
exit(1);
}
server = argv[1];
/* Create client handle used for calling MESSAGELOG
* on the server designated on the command line. */
clnt = clnt_create(server, MESSAGELOG,
MESSAGELOGVERS, "udp");
72
rpcgen: esempio 1
•
.10
Client RPC (continua):
if (clnt == NULL) {
/* Couldn’t establish connection with server. */
clnt_pcreateerror(server);
exit(1);
}
result = resetlog_1(NULL, clnt);
if (result == NULL) { // eccezione di sistema
/* An error occurred while calling the server. */
clnt_perror(clnt, server);
exit(1);
}
/* we successfully called the remote procedure. */
if (*result == 0) { // eccezione utente
/* Server was unable to reset log file. */
fprintf(stderr, "%s: could not reset log.\n",
argv[0]);
exit(1);
73
}
rpcgen: esempio 1
•
.11
Client RPC (continua):
while (fgets(sendLine, MAXLINE, stdin) != NULL) {
/* Call remote procedure printmessage. */
result = printmessage_1(&message, clnt);
if (result == NULL) { // eccezione di sistema
clnt_perror(clnt, server);
exit(1);
}
/* we successfully called the remote procedure. */
if (*result == 0) { // eccezione utente
fprintf(stderr, "%s: couldn't print message\n",
argv[0]);
exit(1);
}
printf("Message delivered to %s\n", server);
}
clnt_destroy(clnt);
exit(0);
}
74
rpcgen: esempio 1
.12
• Client RPC (continua). Notare che:
• Il nome delle procedure (chiamate, e quindi delle procedure-stub
lato chiamante) e' correlato al nome utilizzato nella definizione
dell'interfaccia.
• I parametri funzionali di ingresso e anche il risultato sono passati
(implementativamente) per riferimento e non per valore.
•
Si applicano le stesse considerazioni gia' fatte per il lato
server.
• Il fatto che il valore di ritorno sia passato per riferimento assume
particolare rilevanza lato client dato che viene adottata la
seguente convenzione:
 Una eccezione di sistema relativa alla realizzazione della
chiamata remota viene segnalata al modulo chiamante
ritornando, anziche' un puntatore al valore di ritorno, un
puntatore NULL.
75
Binding del server e client-handle
CLIENT *clnt_create(const
const
const
const
.1
char *host,
rpcprog_t prognum,
rpcvers_t versnum,
char *nettype);
• Client creation routine for program prognum and version versnum.
 prognum e versnum identificano l’interfaccia remota ai cui servizi il
client vuole accedere.
• host identifies the name of the remote host where the server is located.
 Ma quale e’ il suo indirizzo IP? Vedi DNS!
 E quale e’ la porta? Vedi Port Mapper!
 Nota che il client deve sapere a priori su quale macchina il servizio
remoto e’ allocato.
• nettype indicates the class of transport protocol to use.
 Il protocollo di trasporto utilizzato dal servizio remoto per comunicare
76
sulla rete (che deve essere noto a priori al client).
Binding del server e client-handle
.2
• Una client handle (inizializzata) consente al client di invocare sulla
macchina server indicata tutte le procedure dichiarate nella interfaccia di
cui ha effettuato il binding (collegamento, “connessione”) eseguendo la
funzione clnt_create().
• Per completare il binding la funzione clnt_create() deve:
• Risolvere il nome della macchina server host nel relativo indirizzo IP
(vedi DNS).
• Identificare su quale porta del protocollo di trasporto nettype e’
implementata l’interfaccia del servizio definito dall’interfaccia
versionata [prognum, versnum](vedi Port Mapper).
• Allocare le risorse di comunicazione necessarie per supportare il
dialogo con il server (e.g. socket e risorse necessarie per il Layer di
Presentazione).
• Effettuare la connessione di trasporto al server remoto che
implementa l’interfaccia
(puramente logica se il server offre il servizio tramite UDP, effettiva se
il server offre il servizio tramite TCP).
77
Binding del server e client-handle
void clnt_destroy(CLIENT *clnt);
•
Destroys the client’s RPC handle.
•
Destruction usually involves deallocation of private data structures,
including clnt itself.
•
Use of clnt is undefined after calling clnt_destroy().
•
La funzione clnt_destroy() termina l’accesso del client che la
invoca ai servizi offerti dall’interfaccia remota acceduta tramite la
handle clnt.
•
Disconnette il server (nel caso di un server TCP chiude la
connessione di trasporto).
•
Disalloca le risorse locali di comunicazione (socket, …).
78
Server skeleton
• “. . . On the server side, a process is dormant awaiting the arrival of a
call message. When one arrives . . .”
• Mentre per il lato client rpcgen produce effettivamente solo il client
stub (procedurale/passivo), per il lato server esso produce (puo’
produrre) l’intero process skeleton (attivo) del programma server.
 Lo skeleton contiene la funzione main().
 Lo skeleton garantisce l’esecuzione in background del processo
server.
• Per prima cosa il processo server si registra con il Port Mapper
(utilizzando una funzione offerta dalla protocol entity RPC).
• Poi apre le comunicazioni sulla rete e si mette in attesa di richieste.
• E’ possibile specificare a rpcgen su quale/i protocolli di trasporto
(UDP, TCP) il processo server deve offrire i suoi servizi.
• L’I/O dalla rete puo’ essere gestito in modo tale da consentire una
attivazione del processo server anche da un port monitor come inetd.
79
rpcgen
• Input files:
• Protocol.x
Definizione della sintassi astratta del dialogo RPC-based.
• Output files:
• Protocol.h
Prototipi C per l’interazione con gli stub chiamante e chiamato e
tipi di dato per la rappresentazione concreta locale dei parametri.
E’ la definizione locale (sia lato client che lato server), language
specific, dell’API definita dalla sintassi astratta.
• Protocol_xdr.c
Funzioni filtro XDR per la de/serializzazione dei parametri di
ingresso/uscita delle procedure remote.
• Protocol_clnt.c
Implementazione degli stub lato chiamante.
• Protocol_svc.c
Implementazione degli stub (e dello skeleton) lato chiamato.
80
rpcgen e run time system RPC (lato chiamante)
Protocol.x
(sintassi astratta RPC / XDR)
rpcgen
produce
Protocol_xdr.c
(funzioni di
de/codifica per i tipi
definiti nella sintassi
astratta)
chiama
Protocol.h
(rappresentazione
concreta locale di valori
della sintassi astratta e
prototipi di RPC)
Protocol_clnt.c
(RPC stub lato
chiamante)
chiama
chiama
chiama
chiama
chiama
include
RPC
Protocol Entity
chiama (clnt_create()
e clnt_destroy())
XDR Library
chiama (xdr_free())
User program
(C)
81
RPC Protocol Entity
RPC_protocol.x
(sintassi astratta XDR del
protocollo Sun RPC v2)
rpcgen
produce
RPC_protocol.h
(rappresentazione
concreta locale di
PDU del protocollo
Sun RPC v2)
include
RPC_protocol_xdr.c
(funzioni di
de/codifica per
rpc_msg e tipi riferiti)
chiama
Sun RPC v2
Protocol Entity
(C)
chiama
chiama
XDR Library
82
rpcgen e run time system RPC (lato chiamato)
Protocol.x
(sintassi astratta RPC / XDR)
rpcgen
produce
Protocol_xdr.c
(funzioni di
de/codifica per i tipi
definiti nella sintassi
astratta)
chiama
Protocol.h
(rappresentazione
concreta locale di valori
della sintassi astratta e
prototipi di RPC)
Protocol_svc.c
(RPC stub e
skeleton lato
chiamato)
chiama
chiama
chiama
chiama
chiama
include
RPC
Protocol Entity
XDR Library
Chiama (xdr_free())
Implementazione delle
procedure remote
esportate
(C)
83
rpcgen
Interfaccia.x
Client routine
Server routine
Interfaccia.h
7.2
Client stub:
Server stub:
• Interfaccia_clnt.c
• Interfaccia_svc.c
• Interfaccia_xdr.c (pres. layer)
• Interfaccia_xdr.c (pres. layer)
RPC
middleware
7.1
Client RPC
protocol entity
Transport Service
rpc_msg
Server RPC
protocol entity
Transport Service
84
rpcgen options
• -a
Generate all the files including sample code for client and server
side.
• -I
Generate a server side that can be started from inetd.
• -M
Use the newstyle of rpcgen (piu’ di un parametro di ingresso,
parametri per valore).
• -s nettype
Compile into server side stubs for all the transports belonging to
the class nettype (tra le classi che ci interessano, “tcp” e “udp”).
Lo stesso risultato si puo’ ottenere con lo switch –n netid dove, per
i valori che ci interessano, netid ha gli stessi valori di nettype.
N.B.: queste opzioni si possono utilizzare ripetutamente per far si’
che il server utilizzi contemporaneamente piu’ servizi di trasporto.
85
Server skeleton / stub
• Quando dalla rete arriva una richiesta, il supporto RPC attiva una
procedura di dispacciamento che e’ parte del server stub. Questa:
• Riconosce quale procedura server e’ stata chiamata;
• Ne decodifica il parametro di ingresso;
• Chiama la procedura server corrispondente definita dall’utente;
• Al termine dell’esecuzione della procedura server:
• Codifica il parametro di ritorno;
• Genera la risposta verso il chiamante utilizzando i servizi
della protocol entity RPC;
• Libera la memoria allocata per contenere la rappresentazione
locale del parametro di ingresso.
• Esiste la possibilita’ di farsi produrre da rpcgen:
• Un server skeleton sequenziale o un server skeleton concorrente
(basato sull’uso del supporto multi-thread).
• Un server stub passivo, senza l’intero process skeleton, per
consentire ad un unico processo server di supportare piu’
86
interfacce remote.
Gestione della memoria
•
Per ogni chiamata alla funzione printmessage() il server stub
(tramite la procedura di decodifica) alloca memoria dinamica per
contenere il valore del parametro di ingresso (una stringa).
•
Per ogni chiamata a qualunque delle due procedure remote il client
stub potrebbe dover allocare memoria dinamica per contenere il
valore del risultato (nel caso particolare, essendo il risultato un
intero, non viene allocata memoria dinamica).
•
Chi e' responsabile di liberare questa memoria?
•
Il client stub:
• Non e' responsabile di liberare la memoria dinamica utilizzata ne'
dai parametri di ingresso ne' dai valori di ritorno di una chiamata
remota. Queste responsabilita' sono del programma cliente.
•
Il server stub:
• E' responsabile di liberare la memoria utilizzata per la
rappresentazione locale degli argomenti di input della chiamata
(quando questa e' terminata, e gli viene restituito il controllo).
• Non e' responsabile di liberare la memoria utilizzata per la
87
rappresentazione del valore di ritorno della funzione.
Gestione della memoria lato chiamante
•
.1
Per tenere conto della disciplina di de/allocazione definita dal
supporto RPC il client potrebbe dover essere modificato nel modo
seguente (ad esempio, per la chiamata a printmessage(): nel
caso particolare questo non e' effettivamente necessario):
result = printmessage_1(&message, clnt);
if (result == NULL) {
clnt_perror(clnt, server);
exit(1);
}
if (*result == 0) {
fprintf(stderr, "%s: couldn't print message\n",
argv[0]);
exit(1);
}
/* libera la memoria che contiene il valore di
* ritorno (N.B.: in questo caso, niente): */
xdr_free(xdr_int, (char*)result);
88
Gestione della memoria lato chiamante: esercizio
• In realta’ ci sarebbe anche il problema di liberare la memoria relativa
alla rappresentazione locale del parametro di ingresso.
• Perche’ abbiamo aggiunto
xdr_free(xdr_int, (char*)result);
ma non
xdr_free(xdr_msg, (char*)&message);
?
• Come avremmo dovuto modificare il programma chiamante perche’,
dopo la chiamata della funzione remota printmessage_1(), fosse
corretta la liberazione sia della memoria del parametro di ingresso
che di quella del valore di ritorno, secondo lo schema seguente?
result = printmessage_1(&message, clnt);
...
xdr_free(xdr_int, (char*)result);
xdr_free(xdr_msg, (char*)&message);
89
Gestione della memoria lato chiamato
.1
• Per tenere conto della disciplina di de/allocazione definita dal
supporto RPC la procedura remota (server, chiamata) dovrebbe farsi
carico di liberare la memoria contenente il suo valore di ritorno.
•
N.B.: nel nostro caso questo non e’ necessario perche’ il valore di
ritorno e' contenuto in una semplice variabile statica.
Esso potrebbe pero’ essere un valore piu’ complesso, che
richiede per la sua rappresentazione l’utilizzo di memoria
dinamica.
• Come e’ possibile che la procedura remota faccia qualcosa (si occupi
di liberare la memoria) dopo essere terminata?
• E’ evidentemente impossibile che durante una attivazione la funzione
liberi la memoria contenente il valore di ritorno di quella attivazione.
• Durante una attivazione pero’ una procedura remota puo’ liberare la
memoria che conteneva il risultato dell'attivazione precedente!
 Questa e’ la disciplina di programmazione prevista dal supporto
Sun RPC.
90
Gestione della memoria lato chiamato
.2
• L’alternativa sarebbe stata di attribuire al server stub la responsabilita’
della liberazione della memoria allocata per la rappresentazione
concreta locale del valore di ritorno.
• Ma questo avrebbe implicato assumere che questa memoria, che e’
allocata dalla procedura chiamata (dall’utente), fosse stata allocata
dinamicamente.
• Chi lo puo’ garantire?
• E se lo si ponesse come vincolo al codice utente, sarebbe un
vincolo appropriato?
91
Esempio 2: remote directory
•
.1
Servizio di remote directory list (dir.x):
const MAXNAMELENGTH = 255; /* max length of dir entry
*/
typedef string nametype<MAXNAMELENGTH>; /* entry */
/* A node in the directory listing */
struct namenode {
nametype
name;
struct namenode *next;
/* name of dir entry */
/* next entry */
};
typedef namenode *namelist; /* link in the listing */
/* continua alla pagina seguente */
92
Esempio 2: remote directory
•
.2
Servizio di remote directory list (dir.x, continua):
/* The result of a READDIR operation. Error codes
* rely upon passing UNIX errno’s back. */
union readdir_res switch (int errno) {
case 0: namelist list;
/* no error: return directory listing */
default: void;
/* error occurred: nothing to return */
};
program DIRPROG {
version DIRVERS {
readdir_res READDIR(nametype) = 1;
} = 1;
} = 0x20000076;
93
Esempio 2: remote directory
•
.3
Header file C prodotto dalla compilazione di dir.x con rpcgen (dir.h):
#ifndef _DIR_H_RPCGEN
#define _DIR_H_RPCGEN
#include <rpc/rpc.h>
#define MAXNAMELENGTH 255
typedef char *nametype;
struct namenode {
nametype
name;
struct namenode *next;
};
typedef struct namenode namenode;
typedef namenode *namelist;
struct readdir_res {
int errno;
union {
namelist list;
}
readdir_res_u;
};
typedef struct readdir_res readdir_res;
94
Esempio 2: remote directory
•
.4
Header file C dir.h, continua:
#define DIRPROG 0x20000076
#define DIRVERS 1
#define READDIR 1
readdir_res *readdir_1(nametype *, CLIENT *);
readdir_res *readdir_1_svc(nametype *,
struct svc_req *);
int dirprog_1_freeresult(SVCXPRT *, xdrproc_t,
caddr_t);
/* the
bool_t
bool_t
bool_t
bool_t
xdr functions */
xdr_nametype(XDR *,
xdr_namenode(XDR *,
xdr_namelist(XDR *,
xdr_readdir_res(XDR
nametype*);
namenode*);
namelist*);
*, readdir_res*);
#endif /* !_DIR_H_RPCGEN */
95
Esempio 2: remote directory
•
.5
Funzioni filtro XDR prodotte dalla compilazione di dir.x con rpcgen (dir_xdr.c):
#include "dir.h"
bool_t xdr_nametype(XDR *xdrs, nametype *objp) {
if (!xdr_string(xdrs, objp, MAXNAMELENGTH)) return (FALSE);
return (TRUE);
}
bool_t xdr_namenode(XDR *xdrs, namenode *objp) {
if (!xdr_nametype(xdrs, &objp->name)) return (FALSE);
if (!xdr_pointer(xdrs, (char **)&objp->next,
sizeof(namenode), (xdrproc_t)xdr_namenode))
return (FALSE);
return (TRUE);
}
96
Esempio 2: remote directory
•
.6
Funzioni filtro XDR (dir_xdr.c), continua:
bool_t xdr_namelist(XDR *xdrs, namelist *objp) {
if (!xdr_pointer(xdrs, (char **)objp, sizeof(namenode),
(xdrproc_t)xdr_namenode)) return (FALSE);
return (TRUE);
}
bool_t xdr_readdir_res(XDR *xdrs, readdir_res *objp) {
if (!xdr_int(xdrs, &objp->errno)) return (FALSE);
switch (objp->errno) {
case 0: if (!xdr_namelist(xdrs,
&objp->readdir_res_u.list))
return (FALSE);
break;
}
return (TRUE);
}
97
Esempio 2: remote directory
•
.7
Client stub prodotto dalla compilazione di dir.x con rpcgen (dir_clnt.c):
#include <memory.h> /* for memset */
#include "dir.h"
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };
readdir_res *readdir_1(nametype *argp, CLIENT *clnt) {
static readdir_res clnt_res;
memset((char *)&clnt_res, 0, sizeof (clnt_res));
if (clnt_call(clnt, READDIR,
(xdrproc_t)xdr_nametype, (caddr_t)argp,
(xdrproc_t)xdr_readdir_res,
(caddr_t)&clnt_res, TIMEOUT)
/* RPC: utilizza la API RPC di livello simple */
!= RPC_SUCCESS) {
return (NULL);
}
return (&clnt_res);
}
 N.B.: azzera clnt_res per utilizzarlo nella decodifica del valore di ritorno!98
Esempio 2: remote directory
•
.8
Procedura remota server:
/* dir_proc.c: remote readdir implementation */
#include
#include
#include
#include
#include
<dirent.h>
<stdlib.h>
<errno.h>
<string.h>
"dir.h" /* Created by rpcgen */
readdir_res * readdir_1_svc(nametype *dirname,
struct svc_req *req) {
DIR *dirp;
struct dirent *d;
namelist nl;
namelist *nlp;
static readdir_res res = {0, {NULL}}; /* must be static! */
/* Free previous result */
xdr_free(xdr_readdir_res, (char*)&res);
99
Inizializzazione della rappresentazione locale
•
•
Quando si esegue la xdr_free() di una variabile locale e’ necessario
che:
•
Tutte le strutture dati secondarie linkate ad essa siano state
allocate dinamicamente.
•
La variabile abbia un valore corretto (legittimo).
Una variabile i cui byte siano tutti nulli non ha necessariamente un
valore legittimo.
 Basta pensare al caso di un campo tag di una union che non
preveda il valore 0 tra i suoi valori possibili.
•
Per questo, o e’ possibile inizializzare staticamente il valore del
parametro di ritorno in modo corretto, oppure bisogna evitare che la
prima attivazione di una procedura remota effettui la xdr_free()
della rappresentazione locale di questo parametro.
100
Esempio 2: remote directory
•
}
.9
Procedura remota server (continua):
/* Open directory */
if ((dirp = opendir(*dirname)) == NULL) {
res.errno = errno; return (&res);
}
/* Collect directory entries. Memory allocated here is freed
* by xdr_free the next time readdir_1 is called */
nlp = &res.readdir_res_u.list;
while (d = readdir(dirp)) {
nl = *nlp = (namenode *) malloc(sizeof(namenode));
if (nl == NULL) {
closedir(dirp);
res.errno = EAGAIN; return(&res);
}
nl->name = strdup(d->d_name);
nlp = &nl->next;
}
*nlp = NULL;
/* Return the result */
closedir(dirp);
res.errno = 0; return (&res);
101
Esempio 2: remote directory
•
.10
Client:
#include <stdio.h>
#include <errno.h>
#include "dir.h" /* generated by rpcgen */
main(int argc, char *argv[]) {
CLIENT *clnt;
char *server;
char *dir;
readdir_res *result;
namelist nl;
if (argc != 3) {
fprintf(stderr, "usage: %s host directory\n",argv[0]);
exit(1);
}
server = argv[1];
dir = argv[2];
// server domain name
// path to dir we want to read
102
Esempio 2: remote directory
•
.11
Client (continua):
/* Create client "handle" used for calling DIRPROG
* on the server designated on the command line.
*/
clnt = clnt_create(server, DIRPROG, DIRVERS, "tcp");
if (clnt == (CLIENT *)NULL) {
clnt_pcreateerror(server); exit(1);
}
/* call remote server procedure */
result = readdir_1(&dir, clnt);
if (result == (readdir_res *)NULL) {
clnt_perror(clnt, server); exit(1);
}
/* Okay, we successfully called the remote procedure. */
if (result->errno != 0) {
/* Remote system error. Print error message and die. */
errno = result->errno; perror(dir); exit(1);
}
103
Esempio 2: remote directory
•
.12
Client (continua):
/* Successfully got a directory listing. Print it. */
for (nl = result->readdir_res_u.list; nl != NULL;
nl = nl->next) {
printf("%s\n", nl->name);
}
/* free dynamic memory of result value */
xdr_free(xdr_readdir_res, (char*)result);
clnt_destroy(clnt);
exit(0);
}
 N.B. In questo caso particolare non e’ necessario deallocare il
parametro di ingresso.
104
RPC API: call remota
•
.1
La funzione clnt_call() e' chiamata dal client stub generato da
rpcgen.
enum clnt_stat clnt_call(CLIENT *clnt,
const rpcproc_t procnum,
const xdrproc_t inproc,
const caddr_t in,
const xdrproc_t outproc,
caddr_t out,
const struct timeval tout);
•
Calls the remote procedure procnum associated with the client handle
clnt, which is obtained with an RPC client creation routine such as
clnt_create().
• inproc is the XDR function used to encode the procedure’s
parameters.
• outproc is the XDR function used to decode the procedure’s results.
Continua ->
105
RPC API: call remota
.2
clnt_call() (continua):
• in is the address of the procedure’s argument(s).
• out is the address of where to place the result(s).
• tout is the time allowed for results to be returned, which is overridden
by a time-out set explicitly through clnt_control().
•
If the remote call succeeds, the status returned is RPC_SUCCESS.
Otherwise, an appropriate status is returned.
 N.B.:
•
Il lavoro di encodifica del parametro di ingresso e di decodifica del
parametro di ritorno e’ fatto concretamente dalla funzione
clnt_call() ma le funzioni filtro che devono essere utilizzate
sono indicate dal client stub.
•
Quindi il vero responsabile di queste attivita’ non e’ la funzione
clnt_call() (cioe’ la protocol entity RPC) ma il client stub.
106
Procedure senza parametri di ingresso / ritorno
•
A fronte della dichiarazione di una procedura remota senza parametri
di ingresso, e.g.
int RESETLOG(void) = 2;
rpcgen genera un corrispondente client stub che prevede comunque
un parametro di ingresso di tipo *void:
int *resetlog_1(void *, CLIENT *);
e il modulo chiamante deve comunque passare un valore (dummy)
NULL in corrispondenza a questo pseudo-parametro:
result = resetlog_1(NULL, clnt);
•
Cosa succede a fronte di una dichiarazione di procedura remota
senza parametri di ritorno?
• La questione e' rilevante perche' si e' visto che per convenzione il
client stub ritorna NULL per indicare una eccezione di sistema.
• Come puo' sfruttare questa convenzione in caso di mancanza di un
parametro di ritorno?
107
Procedure senza parametri di ritorno
• A fronte della dichiarazione di una procedura remota senza parametri
di ritorno, e.g.
void PING(void) = 2;
rpcgen genera un corrispondente client stub che prevede comunque
un parametro di ritorno di tipo void*:
void *ping_1(void *, CLIENT *);
• Il valore ritornato dal client stub e'
• NULL per indicare una eccezione di sistema.
• != NULL per indicare una esecuzione con successo della
procedura remota.
 Ovviamente, in questo caso, la cosa riferita dal puntatore
ritornato e' non significativa
(non ha senso, ed e' impossibile, dereferenziare il puntatore in
quanto e' un (void*)).
108
Procedure senza parametri di ritorno
•
Stub cliente della procedura remota: void PING(void) = 2;
static struct timeval TIMEOUT = { 25, 0 };
void *ping_1(void *argp, CLIENT *clnt) {
static char clnt_res;
memset((char *)&clnt_res, 0, sizeof (clnt_res));
if (clnt_call(clnt, PING,
(xdrproc_t)xdr_void,
(caddr_t)argp,
(xdrproc_t)xdr_void,
(caddr_t)&clnt_res,
TIMEOUT)
!= RPC_SUCCESS) {
return (NULL);
}
return ((*void)&clnt_res);
}
109
RPC API
•
Sia il lato client che il lato server utilizzano la libreria di supporto
RPC (che implementa la protocol entity RPC).
• clnt_create()
• clnt_call()
• clnt_destroy()
• svc_create()
• svc_run()
• svc_getargs()
• svc_sendreply()
• svc_freeargs()
Protocol entity RPC lato chiamante
Protocol entity RPC lato chiamato
•
Alcune di queste funzioni sono chiamate direttamente dal
programma utente (clnt_create(), clnt_destroy()), ma la
maggior parte sono chiamate indirettamente tramite gli stub generati
dal compilatore rpcgen (tutte le altre, e.g. clnt_call()).
•
Volendo, si potrebbe fare anche a meno di rpcgen e codificare
direttamente le funzioni fornite dagli stub.
110
RPC: stub e protocol entity
Interfaccia.x
Client routine
Server routine
Interfaccia.h
7.2
7.1
Client stub:
Server stub:
• Interfaccia_clnt.c
• Interfaccia_svc.c
• Interfaccia_xdr.c
• Interfaccia_xdr.c
Client RPC protocol ent.:
• clnt_create()
• clnt_call()
• clnt_destroy()
Transport Service
RPC
middleware
rpc_msg
Server RPC protocol ent.:
• svc_create()
• svc_run()
• svc_getargs()
• svc_sendreply()
• svc_freeargs()
Transport Service
111
RPC API
• Il sistema RPC offre una API che consente un accesso a 5 diversi
livelli (tramite diversi insiemi di funzioni).
• Il livello di accesso utilizzato dal compilatore rpcgen e' quello
chiamato simple:
• Richiede/consente al cliente di creare (e, eventualmente,
distruggere) esplicitamente la handle RPC lato client.
• Lato client supporta solo chiamate confermate sincrone
(bloccanti).
• Diversi livelli di accesso
• presentano diversi livelli di complessita' d'uso;
• consentono un controllo piu' o meno forte sulle operazioni
(e.g. gestione degli errori e delle ritrasmissioni);
• consentono o meno l'accesso ad alcune funzionalita'.
• Ad esempio, solo i livelli di accesso avanzati consentono di invocare
procedure remote in modo non confermato e in modo broadcast. 112
RPC API: svc_create()
int svc_create
(void (*dispatch)(const struct svc_req *,
const SVCXPRT *),
rpcprog_t prognum, rpcvers_t versnum,
char *nettype);
• svc_create() creates server handles for all the transports
belonging to the class nettype.
• nettype defines a class of transports which can be used for a
particular application.
• svc_create() registers itself with the rpcbind (Port Mapper)
service.
• dispatch() is called when there is a remote procedure call for the
given prognum and versnum; this requires calling svc_run().
• If svc_create() succeeds, it returns the number of server handles
it created, otherwise it returns 0 and an error message is logged.
113
svc_create()
• svc_create() crea tutte le risorse necessarie per supportare
l’interazione sulla rete con i clienti, e.g.:
• Socket, associato (per quanto detto) ad una porta effimera.
• Risorse per il Layer di Presentazione (e.g. stream XDR).
• Conoscendo l’indirizzo di trasporto su cui sara’ offerto il servizio RPC
prognum/versnum, puo’ registrare questa informazione sul Port
Mapper.
• dispatch() e’ il server stub associato all’interfaccia RPC
prognum/versnum.
• Il server stub dispatch() supporta tutte le RPC definite come parte
dell’interfaccia RPC prognum/versnum.
• Il server skeleton chiamera’ tante volte svc_create(), una per
ciascuna interfaccia RPC (coppia prognum/versnum) supportata
dal server.
114
RPC API: svc_run()
void svc_run(void);
• This routine never returns.
• (server RPC sequenziale) In single threaded mode, it waits for RPC
requests to arrive, and calls the appropriate service procedure
(per il tramite della funzione di dispacciamento dispatch registrata
per un certo programma/versione tramite chiamata a
svc_create()).
• (server RPC concorrente) Applications executing in the Automatic or
User MT (MT = Multi Thread) modes should invoke this function
exactly once.
• In the Automatic MT mode, it will create threads to service client
requests.
• In the User MT mode, it will provide a framework for service
developers to create and manage their own threads for servicing
client requests.
115
svc_run()
• Attende l’arrivo di PDU rpc_msg di CALL su tutte le server handle
definite dal server skeleton tramite svc_create().
• Attende cioe’ l’arrivo di PDU rpc_msg di CALL su tutti i socket
registrati ognuno in una delle server handle del processo server.
• Quando arriva un PDU rpc_msg di CALL su uno di questi socket (lo
decodifica e) cerca a quale handle questo socket e’ associato e
passa la chiamata al server stub associato a questa server handle.
• Al momento dell’attivazione del server stub nella server handle e’
rimasto registrato il parametro di ingresso della call (in forma
codificata).
 Infatti un PDU RPC di call e’ effettivamente composto di due parti:
• un valore (PDU) di tipo rpc_msg di tipo CALL (l’istruzione di
call su rete vera e propria)
• e, di seguito, cioe’ in fondo al PDU rpc_msg, come indicato
nel testo XDR della sua definizione, l’encodifica del parametro
di ingresso.
116
RPC API: svc_getargs()
bool_t svc_getargs(const SVCXPRT *xprt,
const xdrproc_t inproc,
caddr_t in);
• svc_getargs() decodes the arguments of an RPC request
associated with the RPC service transport handle xprt.
• The parameter in is the address where the arguments will be placed.
• The parameter inproc is the XDR routine used to decode the
arguments.
• The parameter xprt is the request’s associated transport handle.
• This routine returns TRUE if decoding succeeds, and FALSE
otherwise.
117
RPC API: svc_freeargs()
bool_t svc_freeargs(const SVCXPRT *xprt,
const xdrproc_t inproc,
caddr_t in);
• svc_freeargs() frees any data allocated by the RPC/XDR system
when it decoded the arguments to a service procedure using
svc_getargs().
• The parameter xprt is the request’s associated transport handle.
• This routine returns TRUE if it succeeds, and FALSE otherwise.
118
RPC API: svc_sendreply()
bool_t svc_sendreply(const SVCXPRT *xprt,
const xdrproc_t outproc,
const caddr_t out);
• Called by an RPC service’s dispatch routine (server stub) to send the
results of a remote procedure call.
• The parameter xprt is the request’s associated transport handle.
• The parameter outproc is the XDR routine which is used to encode
the results.
• The parameter out is the address of the results.
• This routine returns TRUE if it succeeds, FALSE otherwise.
119
RPC API server-side e rpcgen
• rpcgen produce la procedura di dispacciamento del programma
remoto/versione (del modulo RPC).
• Questa procedura implementa il server stub per tutte le procedure
remote definite nel programma (modulo RPC).
• La procedura di dispacciamento:
• riconosce in base al contenuto della struct svc_rec in input
quale e’ la procedura remota chiamata;
• (di conseguenza) ne deserializza il parametro di input (if any)
chiamando svc_getargs();
• quindi attiva la procedura stessa;
• al termine dell’esecuzione della procedura (cioe’ quando questa
esegue l’istruzione C return), e tenendo conto dell’eventuale
parametro di ritorno, genera la risposta remota invocando la
system call svc_sendreply().
• Deve anche liberare la memoria allocata per contenere la
rappresentazione locale del parametro di ingresso (tramite
svc_freeargs()).
120
RPC API server-side e rpcgen
• rpcgen genera anche il process skeleton (funzione main()) del
programma remoto (modulo server RPC).
• Per prima cosa il process skeleton, chiamando la funzione
svc_create(), crea la RPC server handle (per poter offrire il
servizio in rete), vi registra la procedura di dispacciamento (relativa,
quella generata dal compilatore rpcgen per l’interfaccia versionata),
collegandola al numero del programma/versione, e registra sul Port
Mapper le coordinate del servizio.
• Quindi, supponendo uno skeleton sequenziale, questo entra in un
loop eterno chiamando la funzione svc_run()
(il loop infinito e’ contenuto all’interno della funzione svc_run()).
• La funzione svc_run(), a fronte della ricezione di un PDU RPC di
call (che, come detto, contiene sia un valore di tipo rpc_msg che, di
seguito, l’encodifica del parametro di ingresso), chiama la funzione di
dispacciamento relativa al programma remoto/versione riferito nel
PDU passandole (nel parametro struct svc_rec) numero della
procedura remota chiamata e parametro di input (if any) ancora
121
serializzato.
RPC API server-side e rpcgen: esempio
.1
• Consideriamo il programma di esempio
program ESPROG {
version ESVERS {
...
outT ESPROC(inT) = ...;
...
} = 1;
} = ...;
• Quale sarebbe la struttura dello scheletro e dello stub server prodotti
da rpcgen?
• Assumiamo che il protocollo di trasporto utilizzato sia UDP.
• Trascuriamo i casi di errore.
122
RPC API server-side e rpcgen: esempio
.2
• Server skeleton
int main(int argc, char *argv[]) {
svc_create(serverStub, ESPROG, ESVERS, "udp");
// rpcgen chiamato con lo switch –s udp
svc_run();
// waits for call PDUs from sockfd
// and dispatches them to serverstub.
// forever (never returns)
// in realta’ svc_run() opera su tutte le server
// handle definite dal server skeleton tramite
// chiamate a svc_create()
}
123
RPC API server-side e rpcgen: esempio
•
.3a
Esempio semplificato di svc_create()
struct SVCXPRT {
int prog;
int vers;
void (*dispatch)(const struct svc_req *,
const SVCXPRT *);
int sock;
XDR decStream;
XDR encStream;
XDR freeStream;
. . . };
struct SVCPRT serverHandle;
•
Un server RPC puo’ supportare diverse interfacce remote.
All’arrivo di un PDU rpc_msg di call le scandisce tutte per
identificare quale di queste e’ quella riferita
(se nessuna, allora e’ un errore di binding!).
124
RPC API server-side e rpcgen: esempio
•
.3b
Esempio semplificato di svc_create()
int svc_create
(void (*dispatch)(const struct svc_req *,
const SVCXPRT *),
rpcprog_t prognum, rpcvers_t versnum,
char *nettype) {
// assume nettype == "udp"
int
struct sockaddr_in
sockfd;
serv_addr;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family
= AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port
= htons(0);
// int svc_create: continua
125
RPC API server-side e rpcgen: esempio
•
.3c
Esempio semplificato di svc_create()
// int svc_create: continua
tmp = bind(sockfd, (struct sockaddr*) &serv_addr,
sizeof(serv_addr));
getsockname(sockfd, &serv_addr, &tmp);
mapping m = {prognum, versnum, IPPROTO_UDP,
ntohs(serv_addr.sin_port)}
portMapper.PMAPPROC_SET(m);
// portMapper locale
serverHandle.prog = prognum;
serverHandle.vers = versnum;
serverHandle.sock = sockfd;
serverHandle.stub = dispatch;
...
}
126
RPC API server-side e rpcgen: esempio
•
.4
Esempio semplificato di svc_run()
int svc_run () {
// assume nettype == "udp" e una sola server handle.
// per esercizio.
while (1) {
• Si mette in attesa di un PDU di tipo rpc_msg sul (sui) socket
registrato nella (nelle) server handle.
• Quando questo arriva lo decodifica e verifica che esso riferisca
programma/versione registrati nella (in una delle) server handle.
• Verifica anche che l’operazione sia autorizzata.
• Compila in base al PDU di chiamata il parametro struct
svc_req.
• Chiama la procedura di dispacciamento (server stub) registrata
nelle server handle.
}
}
127
RPC API server-side e rpcgen: esempio
•
.5
Server stub
int serverStub(const struct svc_req *req,
const SVCXPRT *svc) {
inT
outT
in;
*out;
// N.B.: solo un puntatore!
switch (req->procnum) {
...
case ESPROC :
{
// qui c’e’ un errore (di omissione)!
svc_getargs(svc, xdr_inT, (caddr_t) &in);
out = esproc_1_svc(&in, req);
svc_sendreply(svc, xdr_outT,
(caddr_t) out);
svc_freeargs(svc, xdr_inT, (caddr_t) &in);
}
...
}
}
128
Sun RPC: Security, modalita' Unix
• Il chiamante trasmette al chiamato, con ogni richiesta:
• un time stamp;
• il nome del proprio host;
• i propri userId e groupId effettivi, e la lista dei propri altri groupId.
• Il chiamato, in base a queste informazioni, decide se soddisfare o
meno la richiesta.
• The bytes of the credential's opaque body encode the following
structure:
struct auth_unix {
unsigned int stamp;
string machinename<255>;
unsigned int uid;
unsigned int gid;
unsigned int gids<16>; };
• The verifier accompanying the credentials should be AUTH_NULL
(cioe', non c'e' verifier e quindi possibilita' di autenticazione).
• Note that these credentials are only unique within a particular domain
129
of machine names, uids, and gids.
Sun RPC: Security, modalita' Unix
• Per utilizzare questa modalita’ il chiamante, dopo avere creato la RPC
client handle clnt, deve settare su di questa la modalita’ Unix.
• Per farlo deve eseguire l’assegnamento:
clnt->cl_auth = authsys_create_default();
• In questo modo inizializza nella handle anche le informazioni di
machine name, uid e gids (dell’utente che esegue il programma
chiamante) che saranno poi utilizzate in tutte le successive chiamate.
• In realta’ il chiamante puo’ modificare queste informazioni nella handle
a suo piacimento, per cui puo’ simulare una identita’ diversa da quella
reale senza possibilita’ di effettiva autenticazione da parte del
chiamato.
• In modalita’ Unix non e’ prevista alcuna autenticazione del chiamato
nei riguardi del chiamante.
 L’idea e’ che l’autenticazione dell’utente sia avvenuta sulla
macchina client, e che il server si fidi di questa autenticazione.
• La verifica delle credenziali del chiamante non e’ operata dal supporto
RPC, che al riguardo e’ trasparente, ma e’ lasciata alla procedura 130
chiamata.
Security, modalita' Unix (system): esempio .1
•
Client:
#include <stdio.h>
#include <errno.h>
#include "dir.h" /* generated by rpcgen */
main(int argc, char *argv[]) {
CLIENT *clnt;
char *server;
char *dir;
readdir_res *result;
namelist nl;
if (argc != 3) {
fprintf(stderr, "usage: %s host directory\n",argv[0]);
exit(1);
}
server = argv[1];
dir = argv[2];
131
Security, modalita' Unix (system): esempio .2
•
Client: (continua):
/* Create client "handle" used for calling DIRPROG
* on the server designated on the command line.
*/
clnt = clnt_create(server, DIRPROG, DIRVERS, "tcp");
if (clnt == (CLIENT *)NULL) {
clnt_pcreateerror(server); exit(1);
}
/* set unix security with my userid and groupid */
clnt->cl_auth = authsys_create_default();
/* call remote server procedure */
result = readdir_1(&dir, clnt);
if (result == (readdir_res *)NULL) {
clnt_perror(clnt, server); exit(1);
}
132
Security, modalita' Unix (system): esempio .3
•
Client: (continua):
/* Okay, we successfully called the remote procedure. */
if (result->errno != 0) {
/* Remote system error. Print error message and die. */
errno = result->errno;
perror(dir);
exit(1);
}
/* Successfully got a directory listing. Print it. */
for (nl = result->readdir_res_u.list; nl != NULL;
nl = nl->next) {
printf("%s\n", nl->name);
}
/* free dynamic memory of result value */
xdr_free(xdr_readdir_res, (char*)result);
clnt_destroy(clnt);
exit(0);
}
133
Security, modalita' Unix (system): esempio .4
•
Remote server procedures:
#include
#include
#include
#include
#include
#include
#include
#include
#include
<dirent.h>
<stdlib.h>
<errno.h>
<string.h>
<sys/types.h>
<sys/stat.h>
<rpc/rpc.h>
<rpc/auth_sys.h>
"dir.h" /* Created by rpcgen */
readdir_res * readdir_1_svc(nametype *dirname,
struct svc_req *req) {
DIR *dirp;
struct dirent *d;
namelist nl;
namelist *nlp;
static readdir_res res = {0, {NULL}}; /* must be static! */
/* Free previous result */
xdr_free(xdr_readdir_res, (char*)&res);
134
Security, modalita' Unix (system): esempio .5
•
Remote server procedures (continua):
/* verifica che il cliente utilizzi security Unix */
switch(req->rq_cred.oa_flavor) {
case AUTH_SYS: {
struct authsys_parms *sys_cred;
struct stat buf;
mode_t st_mode;
if (stat(*dirname, &buf)) {
res.errno = errno; return (&res);
}
st_mode = buf.st_mode;
sys_cred = (struct authsys_parms *) req->rq_clntcred;
/* verifica l'autorizzazione del cliente in base ai
* suoi valori di userId e groupId, relativamente ai
* valori di userId e groupId dell'owner del file, e
* rispetto ai diritti d'accesso previsti per il file
*/
135
Security, modalita' Unix (system): esempio .6
•
Remote server procedures (continua):
if (sys_cred->aup_uid == buf.st_uid) {
if (!(st_mode & S_IREAD)) {
res.errno = EACCES; return (&res);
}
} else if (sys_cred->aup_gid == buf.st_gid) {
if (!(st_mode & S_IRGRP)) {
res.errno = EACCES; return (&res);
}
} else {/*other including superuser*/
if (!(st_mode & S_IROTH)) {
res.errno = EACCES; return (&res);
}
}
break;
}
default: /* il cliente non utilizza security Unix */
res.errno = EACCES; return (&res);
}
136
Security, modalita' Unix (system): esempio .7
•
Remote server procedures (continua):
if ((dirp = opendir(*dirname)) == NULL) {
res.errno = errno; return (&res);
}
nlp = &res.readdir_res_u.list;
while (d = readdir(dirp)) {
nl = *nlp = (namenode *) malloc(sizeof(namenode));
if (nl == NULL) {
closedir(dirp);
res.errno = EAGAIN; return(&res);
}
nl->name = strdup(d->d_name);
nlp = &nl->next;
}
*nlp = NULL;
closedir(dirp);
res.errno = 0; return (&res);
}
137
Port Mapper
• The Port Mapper program maps RPC program and version numbers
to transport-specific port numbers.
•
Il mappaggio e’ locale all’host su cui il Port Mapper e’ in
esecuzione.
•
Il Port Mapper e’ un name service locale!
• This program makes dynamic binding of remote programs possible.
• By running only the Port Mapper on a reserved (well known) port, the
port numbers of other remote programs can be ascertained by
querying the Port Mapper.
 Il Port Mapper offre il suo servizio sulla porta well known 111, sia
su TCP che su UDP.
• Il servizio di Port Mapper e’ implementato tramite RPC, ed e’ quindi
descritto da una interfaccia RPC.
 Ma noi non lo possiamo accedere via RPC perche’ non sappiamo
come effettuare il binding a un server RPC che opera su porta
138
well known (anziche’ su porta effimera)!
RPC binding
• Il cliente conosce il nome DNS (o l’indirizzo IP) della macchina su cui
e’ offerto il servizio RPC che gli interessa.
 Rivolgendosi al resolver DNS gethostbyname()il cliente
ottiene la traduzione del nome DNS della macchina nel relativo
indirizzo IP.
• Il cliente (a) conosce i numeri di programma e di versione
dell’interfaccia remota (l’identificatore dell’interfaccia versionata) del
servizio RPC che gli interessa, e (b) conosce anche il servizio di
Trasporto su cui il servizio e’ offerto.
 Rivolgendosi al Port Mapper residente all’indirizzo IP identificato
al passo precedente e indirizzabile sulla porta well known 111 (sia
su TCP che su UDP), il cliente ottiene la porta sui cui e’ offerto
• in questo momento
• sulla macchina su cui e’ in esecuzione il Port Mapper a cui si
e’ rivolto
il servizio RPC che gli interessa.
139
gethostbyname()
.1
• Poiche' la funzione normale del name service DNS e' quella di
tradurre il nome di una macchina nel suo indirizzo IP, esiste una
funzione (un resolver DNS) dedicata a questo scopo.
• Fornendo alla funzione un nome di una macchina essa ne ritorna la
descrizione completa secondo la struttura hostent.
#include <netdb.h>
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
/*
/*
/*
/*
/*
canonical name of
alias list */
host address type
length of address
list of addresses
host */
*/
*/
*/
struct hostent *gethostbyname(const char *name);
140
gethostbyname()
.2
• Nel nostro caso h_addrtype==AF_INET e la lista di indirizzi e' una
lista di struct in_addr.
• Se la funzione non trova alcun host con il nome (domain name)
indicato ritorna NULL.
• Entrambi i campi che rappresentano liste sono implementati tramite un
puntatore ad un vettore che contiene i puntatori agli elementi della
lista. I vettori sono terminati da un puntatore di valore NULL.
• DNS opera solo su domain name completi.
Di norma e' pero' sufficiente fornire a gethostbyname() solo il nome
locale, senza il suffisso di dominio, perche' e' la funzione stessa che
concatena il nome indicato con una lista configurabile di suffissi.
• E' ovvio che se il nodo che cerchiamo non appartiene ad uno dei
domini configurati e' necessario invocare gethostbyname()
passando il domain name completo.
• N.B.: questa funzione e' considerata obsoleta e di uso pericoloso
perche' ritorna il suo risultato su memoria allocata staticamente.
141
Port Mapper: PMAPPROC_CALLIT() e multicast call
• The Port Mapper also aids in broadcast RPC.
• A given RPC program will usually have different port number
bindings on different machines, so there is no way to directly
broadcast to all of these programs.
• The Port Mapper, however, does have a fixed port number.
• So, to broadcast to a given program, the client actually sends its
message to the Port Mappers located at the broadcast address.
• Each Port Mapper that picks up the broadcast then calls the local
service specified by the client.
• When the Port Mapper gets the reply from the local service, it sends
the reply on back to the client.
• Si riferisce ad un possibile utilizzo della operazione del Port Mapper
PMAPPROC_CALLIT(), che consente al chiamante di chiamare una
operazione remota di un server RPC senza doversi prima collegare a
questo, effettuando la chiamata indirettamente con la mediazione del
Port Mapper.
142
Port Mapper: service definition
const PMAP_PORT = 111;
.1
/* well known
* portmapper port number */
/* A mapping of (program, version, protocol) to port
* number: */
struct mapping {
unsigned int
unsigned int
unsigned int
unsigned int
};
prog;
vers;
prot;
port;
/* Supported values for the "prot" field: */
const IPPROTO_TCP = 6;
const IPPROTO_UDP = 17;
/* prot number for TCP */
/* prot number for UDP */
143
Port Mapper: service definition
.2
/* A list of mappings: */
struct *pmaplist {
mapping map;
pmaplist next;
};
/* Arguments to callit: */
struct call_args {
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque args<>;
};
/* Results of callit: */
struct call_result {
unsigned int port;
opaque res<>;
};
144
Port Mapper: service definition
.3
program PMAP_PROG {
version PMAP_VERS {
void PMAPPROC_NULL(void)
= 0;
bool PMAPPROC_SET(mapping)
= 1;
bool PMAPPROC_UNSET(mapping)
= 2;
unsigned int PMAPPROC_GETPORT(mapping)
= 3;
pmaplist PMAPPROC_DUMP(void)
= 4;
call_result PMAPPROC_CALLIT(call_args)
= 5;
} = 2;
} = 100000;
145
Port Mapper: operazioni
.1
• The Port Mapper program currently supports two transport protocols
(UDP and TCP). The Port Mapper is contacted by talking to it on
assigned port number 111 (Sun RPC) on either of these protocols.
• PMAPPROC_NULL: this procedure does no work. By convention,
procedure zero of any protocol takes no parameters and returns no
results. Serve per fare il ping di un programma remoto!
• PMAPPROC_SET: when a program first becomes available on a machine,
it registers itself with the Port Mapper program on the same machine.
• The program (server RPC) passes its program number prog,
version number vers, transport protocol number prot, and the port
port on which it awaits service request.
• The procedure returns a boolean reply whose value is TRUE if the
procedure successfully set up the mapping and FALSE otherwise.
• The procedure refuses to establish a mapping if one already exists
for the tuple (prog, vers, prot).
• Notare che un server RPC puo' registrarsi piu' volte: per diverse
versioni o perche' supporta diversi protocolli o diverse interfacce.
146
Port Mapper: operazioni
.2
• PMAPPROC_UNSET: when a program becomes unavailable, it should
unregister itself with the Port Mapper program on the same machine.
The parameters and results have meanings identical to those of
PMAPPROC_SET.
• The protocol and port number fields of the argument are ignored.
• Quindi la deregistrazione per una coppia (prog, vers) e’ globale, da
tutti i mapping, tramite una singola invocazione dell’operazione
PMAPPROC_UNSET.
• PMAPPROC_GETPORT: given a program number prog, version number
vers, and transport protocol number prot, this procedure returns the
port number on which the program is awaiting call requests.
• A port value of zero as the return parameter means the program has
not been registered.
• The port field of the input argument is ignored.
• PMAPPROC_DUMP: this procedure enumerates all entries in the Port
Mapper's database.
147
Port Mapper: operazioni
.3
• PMAPPROC_CALLIT: this procedure allows a client to call another
remote procedure on the same machine (del Port Mapper) without
knowing the remote procedure's port number.
It is intended for supporting broadcasts to arbitrary remote programs via
the well-known port mapper's port.
Parameters prog, vers, proc, and the bytes of args are the program,
version and procedure numbers, and parameters of the remote
procedure that is to be called.
• Esercizio: implementare le operazioni PMAPPROC_GETPORT e
PMAPPROC_SET, e PMAPPROC_CALLIT.
Nell’operazione PMAPPROC_CALLIT la chiamata effettiva
dell’operazione target deve essere effettuata utilizzando direttamente i
servizi di Trasporto e di Presentazione.
148
Esercizio 1
• Definire una API per il sistema RPC composta di una singola
operazione che sfruttando la procedura PMAPPROC_CALLIT() del
Port Mapper, consenta di invocare qualunque operazione remota di
qualunque server RPC presente sulla stessa macchina del Port
Mapper stesso.
• Dare il prototipo di questa procedura di interfaccia al sistema RPC e
poi implementare la procedura utilizzando direttamente i servizi di
Trasporto e di Presentazione per interagire con il Port Mapper.
 Domanda: il servizio di Trasporto utilizzato per
l’implementazione di questa operazione dipende da quello sul
quale e’ offerto il servizio RPC che si vuole accedere?
149
Esercizio 2
A. Implementare le seguenti funzioni della protocol entity RPC lato
server:
• svc_run()
• svc_getargs()
• svc_sendreply()
Si assuma che il server RPC implementi un’unica interfaccia remota
e che il protocollo di trasporto utilizzato sia UDP.
Utilizzare direttamente i servizi di Trasporto e di Presentazione per
gestire il dialogo con il chiamante.
B. Implementare le funzioni clnt_create() e clnt_call() della
protocol entity RPC lato client utilizzando direttamente i servizi di
Trasporto e di Presentazione per interloquire rispettivamente con il
Port Mapper e con il server RPC.
 In entrambi gli esercizi, inventare in modo ragionevole e
giustificabile il contenuto e la gestione delle strutture dati rilevanti
(e.g. la client handle CLIENT).
150