m Protegit «Sapomanager» ([edit=autoconfirmed] (indefinit) [move=autoconfirmed] (indefinit)) |
Cap resum de modificació |
||
| Línia 1: | Línia 1: | ||
[[Category:SAPO]] | |||
[[Category:Devel]] | |||
== Introducció == | == Introducció == | ||
Revisió de 12:29, 7 gen 2015
Introducció
Sapomanager es un dimoni de sistema que ens serveix per fer tasques de intercomunicació entre els diversos processos del Carta. A diferencia de versions inicials, no cal que el programa que s'hi vol comunicar l'executi, ni fer servir complicats ( i delicats) sistemes de tipus proxy. El daemon arrenca al iniciar l'ordinador i es manté funcionant fins que l'apaguem.
Estructura
Sapomanager reconeix dos tipus de clients: Les aplicacions i els serveis. Les aplicacions es connecten (registren) al Sapomanager i poden treballar amb ell, llistant les aplicacions en execució, enviant-hi missatges a d'altres aplicacions a través de Sapomanager, o obrint connexió amb serveis.
Tota la comunicació entre Sapomanager i els clients (aplicacions i serveis) es realitza a través de la llibreria libsapomngr. D'aquesta manera no cal preocupar-se de les comunicacions ni de sockets. La aplicació que fa servir sapomanager només realitza crides de funcions a la llibreria libsapomngr.
Funcionament
Registre d'una aplicació
Quan una aplicació vol fer servir el Sapomanager primer s'hi ha de registrar.
SMNGR_client_st client; SMNGR_register (&client, (const char*) appName, (const char*) userName);
userName es opcional. En cas de que sigui NULL, la llibreria intentarà trobar el nom mitjançant les variables d'entorn USER i LOGNAME. En cas d'error retorna 0 (Sapomanager no està executant-se).
No cal des-registrar l'aplicació. Quan mor, Sapomanager ho detecta i l'elimina automàticament.
Llistat d'aplicacions en execució
Per llistar les aplicacions actualment registrades en Sapomanager cal fer:
unsigned int numApps; SMNGR_app_st *list = SMNGR_list_apps(&client, &numApps);
Retorna una llista de numApps estructures SMNGR_app_st. Aquesta llista inclou l'aplicació que crida, per tant mai pot ser buida. En cas d'error retorna NULL. La memoria retornada cal alliberar-la amb free().
Comunicacions entre aplicacions
Es possible enviar missatges a una altra aplicació registrada. Per això farem servir
SMNGR_send_message (&client, (SMNGR_app_st*) &list[i], "HOLA"); /* o bé */ SMNGR_send_messagef (&client, (SMNGR_app_st*) &list[i], "HOLA %s", "MON");
Aquesta funció retorna 0 si falla (aplicació de destí no existeix, o un altre error).
L'enviament de missatges es asíncron. No s'espera una resposta ni es garanteix que l'aplicació que rep el missatge l'estigui esperant ni en faci res concret amb ell. Per rebre missatges, cal configurar una funció de recepció i cridar una de les funcions d'espera de missatges:
SMNGR_set_message_handler (&client, (SMNGR_msg_handler_t) missatge_rebut); SMNGR_dispatch_message (&client); /* crida el handler només si hi ha un missatge esperant per ser processat. */ /* o bé */ SMNGR_dispatch_message_loop (&client,(unsigned long int) 5000); /* s'espera fins a 5000 ms. a veure si rep un missatge */
Les funcions de dispatch retornen 0 si no s'ha rebut cap missatge (no s'ha cridat al handler) o diferent de 0 si han rebut un missatge. Es pot rebre com a màxim un missatge per crida. La funció handler ha de tenir la forma:
void missatge_rebut (SMNGR_client_st *client,
SMNGR_app_st *emissor,
SMNGR_message_st *msg)
{
/* codi d'exemple */
if (libsm_msg_num_arguments(msg)>=1 && !strcmp(libsm_msg_get_argument(msg,0),"HOLA"))
SMNGR_send_messagef (client, emissor, "HOLA %d", emissor->pid);
}
On client es la nostra estructura de registre, emissor es el identificador de la aplicació que ens envia el missatge (o un servei) i msg es el contingut del missatge.
Si necessitem passar mes dades al handler sempre podem fer:
SMNGR_set_private_data (&client, (void*) &dades);
i dintre del handler
void *dades = SMNGR_get_private_data (&client);
Serveis
Els serveis funcionen de forma similar a les aplicacions normals, a diferència de que no es poden registrar per ells mateixos. Quan una aplicació vol contactar amb un servei (p.e. sapocon o saposnapshot), ha de cridar a la funció:
SMNGR_open_service (client, "sapocon");
Si en aquest moment el servei sapocon ja es trobava corrent, l'únic efecte d'aquesta crida es que la aplicació queda configurada per parlar amb aquest servei (les aplicacions només poden parlar amb un servei al mateix temps, ho veurem més endavant).
Si no es trobava corrent, s'inicia el procés de càrrega d'un servei:
- Sapomanager busca un binari amb el nom "<servei>.d" dintre de /opt/sapo/lbin/
- si no el troba la funció SMNGR_open_service() tornarà error (zero).
- Sapomanager executa el binari del servei.
- No cal que aquest binari tingui el bit setuid, doncs Sapomanager s'executa com a de superusuari.
- El servei crida a la funció SMNGR_register_service(&client,"sapocon");
- Te 3 segons per executar-se i fer la crida. Ha de fer servir el mateix identificador. Si no es compleix alguna de les dues restriccions, Sapomanager denegarà el registre. Això es lo que succeirà sempre que executem un servei a mà.
- No cal que el servei faci massa configuracions del seu entorn, ni fork() ni res semblant. Es un fill de Sapomanager.
- Un cop registrat el servei, la funció SMNGR_open_service() retornarà diferent de zero.
A partir d'aquest punt, l'aplicació ja pot comunicar-se amb el servei seleccionat amb les funcions:
SMNGR_service_message (&client, "HOLA"); SMNGR_service_messagef(&client, "HOLA %s", "MON");
La recepció de missatges es idèntica a l'exposat anteriorment. Des de la funció handler de missatges podem saber si l'emissor d'un missatge es un servei mitjançant l'estructura SMNGR_app_st: emissor->name començarà per '@' ('@sapocon' en el nostre exemple) i emissor->user serà "@".
Internament un servei es molt senzill. Es registra a Sapomanager, configura una funció handler de missatges i espera en un bucle a rebre missatges. Pot fer servir altres threads o executar altres processos o fer fork(), però la comunicació amb Sapomanager l'ha de fer sempre des de el procés (i thread) que crida a register_service.
Funcions d'utilitat
Per facilitar el tractament dels missatges rebuts, es treballa amb una estructura de tipus SMNGR_message_st. Sapomanager està dissenyat per fer servir missatges amb arguments separats en espais. La llibreria libsapomngr fa servir aquest tipus de missatges internament (p.e. "REGISTER carta 3214 adria" o "OPENSRV sapocon"). Els serveis i aplicacions haurien de treballar de forma similar en les seves comunicacions.
Així tenim les funcions de missatges:
SMNGR_message_st *msg; /* tenim un missatge "SET Reason I have a problem with my printer" */ ... libsm_msg_num_arguments(msg) /* retorna 9 */ libsm_msg_get_argument(msg,0) /* retorna "SET" */ libsm_msg_get_argument(msg,1) /* retorna "Reason" */ libsm_msg_get_rest (msg,2) /* retorna "I have a problem with my printer" */
Aquestes funcions modifican el missatge original per anar separant els arguments amb '\0' a mesura que els anem demanant. Per això no podem demanar un argument i després demanar la resta des d'aquell mateix argument:
libsm_msg_get_argument(msg,2) /* retorna "I" */ libsm_msg_get_rest (msg,2) /* retorna "I" */ libsm_msg_get_rest (msg,3) /* retorna "have a problem with my printer" */
Comunicacions
Missatges
Els missatges tenen un limit de tamany, MAX_MSG_LEN (a smngr.h). Superior a 1024. Els missatges no han de contenir el caràcter '\n' al final, ni a qualsevol altre posició. En la implementació actual, aquest caràcter viatjarà com a part del missatge. Tampoc poden contenir el caràcter '\0' doncs es fa servir per marcar el final del missatge.
Operacions
Internament libsapomngr i el dimoni sapomanager es comuniquen amb codis d'operació. Aquesta llista es pot ampliar en un futur
| APPREG <pid> <nomapp> <usuari> | Registra l'aplicació que l'envia | ||
| APPLIST | Demana una llista de les aplicacions en curs | ||
| ECHO [text...] | El dimoni respon amb el text que segueix | ||
| MSG <pid> <text...> | Envia el text a la aplicació registrada amb el pid donat | SRVOPEN <nomservei> | Obre el servei demanat |
| SRVINFO <nomservei> | Retorna informació sobre el servei demanat | ||
| SRVREG <pid> <nomservei> | Registra el servei que l'envia | ||
| SRVMSG <text...> | Envia el text al servei al que estiguem associats |
Sockets
Per la comunicació, libsapomngr fa servir packet sockets de la familia AF_LOCAL (també coneguda com AF_UNIX). Aquest tipus de sockets es comporten de forma similar als UDP, de manera que la comunicació es realitza en forma de missatges i no d'stream com a TCP o com els pipes. Això facilita molt el treball per que no cal delimitar els missatges ni mantenir cap tipus de buffer per cada client. A diferencia d'UDP, aquests sockets garanteixen l'enviament i l'ordenació dels missatges.
A la familia AF_UNIX, els sockets s'identifiquem amb un fitxer a disk, d'un tipus especial (socket). Això no ens dona més que problemes: Fitxers que no s'esborren, que no podem sobreescriure per problema de permisos, dos aplicacions que obren el mateix fitxer a l'hora, etc. Des de Linux 2.2 existeix suport per sockets abstractes. En comptes d'identificar-los per un fitxer a disk, es pot fer servir un nom unic en el sistema.
Per treballar d'aquesta manera només cal crear el socket (bind()) indicant el sun_path de la estructura sockaddr_un que comenci amb '\0', seguir de l'identificador del socket. Per mostrar al log l'identificador dels sockets, substituim el '\0' incial per un '#'. Així, a Sapomanager es fan servir els identificadors:
#CADT.SAPOMANAGER.DAEMON -- per el dimoni #CADT.SAPOMANAGER.CLIENT-pid-nnnn -- per la resta de clients, on pid es el pid del client i nnnn es un número aleatori.
D'aquesta manera el Sapomanager pot saber quan un client mor, si el pid ja no existeix (kill (pid,0)==-1) i el seu identificador de socket es pot fer servir (bind() a "\0CADT.SAPOMANAGER.CLIENT-pid-nnnn"). Amb fitxers de socket tradicionals no hi ha manera d'estar segurs.
Si es vol compilar els programes per a un sistema no-Linux o anterior a Linux 2.2, cal eliminar el
#define USE_ABSTRACT_FILE
a smngr.h i relinkar tots els programes que facin servir la llibreria.
Debug
Per veure les comunicacions amb el dimoni Sapomanager, es pot executar amb les opcions -f -x 99. Amb -f no anirà al background. Primer, es clar, cal matar el dimoni Sapomanager que ja hi deu haver corrent. Amb un simple kill $(pidof sapomanager) morirà, junt amb els serveis que hi fossin corrent, en un interval de uns 5 segons.
TODO
Les capacitats d'ampliació son molt grans.