// ******************************************************************************************************* // Servicio: ogAdmServer // Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla // Fecha Creación: Marzo-2010 // Fecha Última modificación: Marzo-2010 // Nombre del fichero: ogAdmServer.cpp // Descripción :Este fichero implementa el servicio de administración general del sistema // ******************************************************************************************************* #include "ogAdmServer.h" #include "dbi.h" #include "utils.h" #include "list.h" #include "rest.h" #include "client.h" #include "json.h" #include "schedule.h" #include #include #include #include #include #include #include #include static char usuario[LONPRM]; // Usuario de acceso a la base de datos static char pasguor[LONPRM]; // Password del usuario static char datasource[LONPRM]; // Dirección IP del gestor de base de datos static char catalog[LONPRM]; // Nombre de la base de datos static char interface[LONPRM]; // Interface name char auth_token[LONPRM]; // API token struct og_dbi_config dbi_config = { .user = usuario, .passwd = pasguor, .host = datasource, .database = catalog, }; //________________________________________________________________________________________________________ // Función: tomaConfiguracion // // Descripción: // Lee el fichero de configuración del servicio // Parámetros: // filecfg : Ruta completa al fichero de configuración // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error //________________________________________________________________________________________________________ static bool tomaConfiguracion(const char *filecfg) { char buf[1024], *line; char *key, *value; FILE *fcfg; if (filecfg == NULL || strlen(filecfg) == 0) { syslog(LOG_ERR, "No configuration file has been specified\n"); return false; } fcfg = fopen(filecfg, "rt"); if (fcfg == NULL) { syslog(LOG_ERR, "Cannot open configuration file `%s'\n", filecfg); return false; } servidoradm[0] = '\0'; //inicializar variables globales line = fgets(buf, sizeof(buf), fcfg); while (line != NULL) { const char *delim = "="; line[strlen(line) - 1] = '\0'; key = strtok(line, delim); value = strtok(NULL, delim); if (!strcmp(str_toupper(key), "SERVIDORADM")) snprintf(servidoradm, sizeof(servidoradm), "%s", value); else if (!strcmp(str_toupper(key), "PUERTO")) snprintf(puerto, sizeof(puerto), "%s", value); else if (!strcmp(str_toupper(key), "USUARIO")) snprintf(usuario, sizeof(usuario), "%s", value); else if (!strcmp(str_toupper(key), "PASSWORD")) snprintf(pasguor, sizeof(pasguor), "%s", value); else if (!strcmp(str_toupper(key), "DATASOURCE")) snprintf(datasource, sizeof(datasource), "%s", value); else if (!strcmp(str_toupper(key), "CATALOG")) snprintf(catalog, sizeof(catalog), "%s", value); else if (!strcmp(str_toupper(key), "INTERFACE")) snprintf(interface, sizeof(interface), "%s", value); else if (!strcmp(str_toupper(key), "APITOKEN")) snprintf(auth_token, sizeof(auth_token), "%s", value); line = fgets(buf, sizeof(buf), fcfg); } fclose(fcfg); if (!servidoradm[0]) { syslog(LOG_ERR, "Missing SERVIDORADM in configuration file\n"); return false; } if (!puerto[0]) { syslog(LOG_ERR, "Missing PUERTO in configuration file\n"); return false; } if (!usuario[0]) { syslog(LOG_ERR, "Missing USUARIO in configuration file\n"); return false; } if (!pasguor[0]) { syslog(LOG_ERR, "Missing PASSWORD in configuration file\n"); return false; } if (!datasource[0]) { syslog(LOG_ERR, "Missing DATASOURCE in configuration file\n"); return false; } if (!catalog[0]) { syslog(LOG_ERR, "Missing CATALOG in configuration file\n"); return false; } if (!interface[0]) syslog(LOG_ERR, "Missing INTERFACE in configuration file\n"); return true; } #define OG_CMD_MAXLEN 64 /* Shut down connection if there is no complete message after 10 seconds. */ #define OG_CLIENT_TIMEOUT 10 /* Agent client operation might take longer, shut down after 30 seconds. */ #define OG_AGENT_CLIENT_TIMEOUT 30 // ________________________________________________________________________________________________________ // Función: clienteDisponible // // Descripción: // Comprueba la disponibilidad del cliente para recibir comandos interactivos // Parametros: // - ip : La ip del cliente a buscar // - idx: (Salida) Indice que ocupa el cliente, de estar ya registrado // Devuelve: // true: Si el cliente está disponible // false: En caso contrario // ________________________________________________________________________________________________________ bool clienteDisponible(char *ip, int* idx) { int estado; if (clienteExistente(ip, idx)) { estado = strcmp(tbsockets[*idx].estado, CLIENTE_OCUPADO); // Cliente ocupado if (estado == 0) return false; estado = strcmp(tbsockets[*idx].estado, CLIENTE_APAGADO); // Cliente apagado if (estado == 0) return false; estado = strcmp(tbsockets[*idx].estado, CLIENTE_INICIANDO); // Cliente en proceso de inclusión if (estado == 0) return false; return true; // En caso contrario el cliente está disponible } return false; // Cliente no está registrado en el sistema } // ________________________________________________________________________________________________________ // Función: clienteExistente // // Descripción: // Comprueba si el cliente está registrado en la tabla de socket del sistema // Parametros: // - ip : La ip del cliente a buscar // - idx:(Salida) Indice que ocupa el cliente, de estar ya registrado // Devuelve: // true: Si el cliente está registrado // false: En caso contrario // ________________________________________________________________________________________________________ bool clienteExistente(char *ip, int* idx) { int i; for (i = 0; i < MAXIMOS_CLIENTES; i++) { if (contieneIP(ip, tbsockets[i].ip)) { // Si existe la IP en la cadena *idx = i; return true; } } return false; } // ________________________________________________________________________________________________________ // Función: actualizaConfiguracion // // Descripción: // Esta función actualiza la base de datos con la configuracion de particiones de un cliente // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - cfg: cadena con una Configuración // - ido: Identificador del ordenador cliente // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // Especificaciones: // Los parametros de la configuración son: // par= Número de partición // cpt= Codigo o tipo de partición // sfi= Sistema de ficheros que está implementado en la partición // soi= Nombre del sistema de ficheros instalado en la partición // tam= Tamaño de la partición // ________________________________________________________________________________________________________ bool actualizaConfiguracion(struct og_dbi *dbi, char *cfg, int ido) { int lon, p, c,i, dato, swu, idsoi, idsfi,k; char *ptrPar[MAXPAR], *ptrCfg[7], *ptrDual[2], tbPar[LONSTD]; char *ser, *disk, *par, *cpt, *sfi, *soi, *tam, *uso; // Parametros de configuración. dbi_result result, result_update; const char *msglog; lon = 0; p = splitCadena(ptrPar, cfg, '\n'); for (i = 0; i < p; i++) { c = splitCadena(ptrCfg, ptrPar[i], '\t'); // Si la 1ª línea solo incluye el número de serie del equipo; actualizar BD. if (i == 0 && c == 1) { splitCadena(ptrDual, ptrCfg[0], '='); ser = ptrDual[1]; if (strlen(ser) > 0) { // Solo actualizar si número de serie no existía. result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET numserie='%s'" " WHERE idordenador=%d AND numserie IS NULL", ser, ido); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); } continue; } // Distribución de particionado. disk = par = cpt = sfi = soi = tam = uso = NULL; splitCadena(ptrDual, ptrCfg[0], '='); disk = ptrDual[1]; // Número de disco splitCadena(ptrDual, ptrCfg[1], '='); par = ptrDual[1]; // Número de partición k=splitCadena(ptrDual, ptrCfg[2], '='); if(k==2){ cpt = ptrDual[1]; // Código de partición }else{ cpt = (char*)"0"; } k=splitCadena(ptrDual, ptrCfg[3], '='); if(k==2){ sfi = ptrDual[1]; // Sistema de ficheros /* Comprueba existencia del s0xistema de ficheros instalado */ idsfi = checkDato(dbi, sfi, "sistemasficheros", "descripcion","idsistemafichero"); } else idsfi=0; k=splitCadena(ptrDual, ptrCfg[4], '='); if(k==2){ // Sistema operativo detecdtado soi = ptrDual[1]; // Nombre del S.O. instalado /* Comprueba existencia del sistema operativo instalado */ idsoi = checkDato(dbi, soi, "nombresos", "nombreso", "idnombreso"); } else idsoi=0; splitCadena(ptrDual, ptrCfg[5], '='); tam = ptrDual[1]; // Tamaño de la partición splitCadena(ptrDual, ptrCfg[6], '='); uso = ptrDual[1]; // Porcentaje de uso del S.F. lon += sprintf(tbPar + lon, "(%s, %s),", disk, par); result = dbi_conn_queryf(dbi->conn, "SELECT numdisk, numpar, tamano, uso, idsistemafichero, idnombreso" " FROM ordenadores_particiones" " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", ido, disk, par); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { result_update = dbi_conn_queryf(dbi->conn, "INSERT INTO ordenadores_particiones(idordenador,numdisk,numpar,codpar,tamano,uso,idsistemafichero,idnombreso,idimagen)" " VALUES(%d,%s,%s,0x%s,%s,%s,%d,%d,0)", ido, disk, par, cpt, tam, uso, idsfi, idsoi); if (!result_update) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result_update); } else { // Existe el registro swu = true; // Se supone que algún dato ha cambiado dato = dbi_result_get_uint(result, "tamano"); if (atoi(tam) == dato) {// Parámetro tamaño igual al almacenado dato = dbi_result_get_uint(result, "idsistemafichero"); if (idsfi == dato) {// Parámetro sistema de fichero igual al almacenado dato = dbi_result_get_uint(result, "idnombreso"); if (idsoi == dato) {// Parámetro sistema de fichero distinto al almacenado swu = false; // Todos los parámetros de la partición son iguales, no se actualiza } } } if (swu) { // Hay que actualizar los parámetros de la partición result_update = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores_particiones SET " " codpar=0x%s," " tamano=%s," " uso=%s," " idsistemafichero=%d," " idnombreso=%d," " idimagen=0," " idperfilsoft=0," " fechadespliegue=NULL" " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", cpt, tam, uso, idsfi, idsoi, ido, disk, par); } else { // Actualizar porcentaje de uso. result_update = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores_particiones SET " " codpar=0x%s," " uso=%s" " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", cpt, uso, ido, disk, par); } if (!result_update) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result_update); } dbi_result_free(result); } lon += sprintf(tbPar + lon, "(0,0)"); // Eliminar particiones almacenadas que ya no existen result_update = dbi_conn_queryf(dbi->conn, "DELETE FROM ordenadores_particiones WHERE idordenador=%d AND (numdisk, numpar) NOT IN (%s)", ido, tbPar); if (!result_update) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result_update); return true; } // ________________________________________________________________________________________________________ // Función: checkDato // // Descripción: // Esta función comprueba si existe un dato en una tabla y si no es así lo incluye. devuelve en // cualquier caso el identificador del registro existenet o del insertado // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - dato: Dato // - tabla: Nombre de la tabla // - nomdato: Nombre del dato en la tabla // - nomidentificador: Nombre del identificador en la tabla // Devuelve: // El identificador del registro existente o el del insertado // // Especificaciones: // En caso de producirse algún error se devuelve el valor 0 // ________________________________________________________________________________________________________ int checkDato(struct og_dbi *dbi, char *dato, const char *tabla, const char *nomdato, const char *nomidentificador) { const char *msglog; int identificador; dbi_result result; if (strlen(dato) == 0) return (0); // EL dato no tiene valor result = dbi_conn_queryf(dbi->conn, "SELECT %s FROM %s WHERE %s ='%s'", nomidentificador, tabla, nomdato, dato); // Ejecuta consulta if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return (0); } if (!dbi_result_next_row(result)) { // Software NO existente dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO %s (%s) VALUES('%s')", tabla, nomdato, dato); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return (0); } // Recupera el identificador del software identificador = dbi_conn_sequence_last(dbi->conn, NULL); } else { identificador = dbi_result_get_uint(result, nomidentificador); } dbi_result_free(result); return (identificador); } // ________________________________________________________________________________________________________ // Función: Levanta // // Descripción: // Enciende ordenadores a través de la red cuyas macs se pasan como parámetro // Parámetros: // - iph: Cadena de direcciones ip separadas por ";" // - mac: Cadena de direcciones mac separadas por ";" // - mar: Método de arranque (1=Broadcast, 2=Unicast) // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // ________________________________________________________________________________________________________ bool Levanta(char *ptrIP[], char *ptrMacs[], int lon, char *mar) { unsigned int on = 1; struct sockaddr_in local; int i, res; int s; /* Creación de socket para envío de magig packet */ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s < 0) { syslog(LOG_ERR, "cannot create socket for magic packet\n"); return false; } res = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (unsigned int *) &on, sizeof(on)); if (res < 0) { syslog(LOG_ERR, "cannot set broadcast socket\n"); return false; } memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(PUERTO_WAKEUP); local.sin_addr.s_addr = htonl(INADDR_ANY); for (i = 0; i < lon; i++) { if (!WakeUp(s, ptrIP[i], ptrMacs[i], mar)) { syslog(LOG_ERR, "problem sending magic packet\n"); close(s); return false; } } close(s); return true; } #define OG_WOL_SEQUENCE 6 #define OG_WOL_MACADDR_LEN 6 #define OG_WOL_REPEAT 16 struct wol_msg { char secuencia_FF[OG_WOL_SEQUENCE]; char macbin[OG_WOL_REPEAT][OG_WOL_MACADDR_LEN]; }; static bool wake_up_broadcast(int sd, struct sockaddr_in *client, const struct wol_msg *msg) { struct sockaddr_in *broadcast_addr; struct ifaddrs *ifaddr, *ifa; int ret; if (getifaddrs(&ifaddr) < 0) { syslog(LOG_ERR, "cannot get list of addresses\n"); return false; } client->sin_addr.s_addr = htonl(INADDR_BROADCAST); for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET || strcmp(ifa->ifa_name, interface) != 0) continue; broadcast_addr = (struct sockaddr_in *)ifa->ifa_ifu.ifu_broadaddr; client->sin_addr.s_addr = broadcast_addr->sin_addr.s_addr; break; } freeifaddrs(ifaddr); ret = sendto(sd, msg, sizeof(*msg), 0, (struct sockaddr *)client, sizeof(*client)); if (ret < 0) { syslog(LOG_ERR, "failed to send broadcast wol\n"); return false; } return true; } static bool wake_up_unicast(int sd, struct sockaddr_in *client, const struct wol_msg *msg, const struct in_addr *addr) { int ret; client->sin_addr.s_addr = addr->s_addr; ret = sendto(sd, msg, sizeof(*msg), 0, (struct sockaddr *)client, sizeof(*client)); if (ret < 0) { syslog(LOG_ERR, "failed to send unicast wol\n"); return false; } return true; } enum wol_delivery_type { OG_WOL_BROADCAST = 1, OG_WOL_UNICAST = 2 }; //_____________________________________________________________________________________________________________ // Función: WakeUp // // Descripción: // Enciende el ordenador cuya MAC se pasa como parámetro // Parámetros: // - s : Socket para enviar trama magic packet // - iph : Cadena con la dirección ip // - mac : Cadena con la dirección mac en formato XXXXXXXXXXXX // - mar: Método de arranque (1=Broadcast, 2=Unicast) // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error //_____________________________________________________________________________________________________________ // bool WakeUp(int s, char* iph, char *mac, char *mar) { unsigned int macaddr[OG_WOL_MACADDR_LEN]; char HDaddress_bin[OG_WOL_MACADDR_LEN]; struct sockaddr_in WakeUpCliente; struct wol_msg Trama_WakeUp; struct in_addr addr; bool ret; int i; for (i = 0; i < 6; i++) // Primera secuencia de la trama Wake Up (0xFFFFFFFFFFFF) Trama_WakeUp.secuencia_FF[i] = 0xFF; sscanf(mac, "%02x%02x%02x%02x%02x%02x", &macaddr[0], &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], &macaddr[5]); for (i = 0; i < 6; i++) HDaddress_bin[i] = (uint8_t)macaddr[i]; for (i = 0; i < 16; i++) // Segunda secuencia de la trama Wake Up , repetir 16 veces su la MAC memcpy(&Trama_WakeUp.macbin[i][0], &HDaddress_bin, 6); /* Creación de socket del cliente que recibe la trama magic packet */ WakeUpCliente.sin_family = AF_INET; WakeUpCliente.sin_port = htons((short) PUERTO_WAKEUP); switch (atoi(mar)) { case OG_WOL_BROADCAST: ret = wake_up_broadcast(s, &WakeUpCliente, &Trama_WakeUp); break; case OG_WOL_UNICAST: if (inet_aton(iph, &addr) < 0) { syslog(LOG_ERR, "bad IP address for unicast wol\n"); ret = false; break; } ret = wake_up_unicast(s, &WakeUpCliente, &Trama_WakeUp, &addr); break; default: syslog(LOG_ERR, "unknown wol type\n"); ret = false; break; } return ret; } // ________________________________________________________________________________________________________ // Función: actualizaCreacionImagen // // Descripción: // Esta función actualiza la base de datos con el resultado de la creación de una imagen // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - idi: Identificador de la imagen // - dsk: Disco de donde se creó // - par: Partición de donde se creó // - cpt: Código de partición // - ipr: Ip del repositorio // - ido: Identificador del ordenador modelo // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // ________________________________________________________________________________________________________ bool actualizaCreacionImagen(struct og_dbi *dbi, char *idi, char *dsk, char *par, char *cpt, char *ipr, char *ido) { const char *msglog; dbi_result result; int idr,ifs; /* Toma identificador del repositorio correspondiente al ordenador modelo */ result = dbi_conn_queryf(dbi->conn, "SELECT repositorios.idrepositorio" " FROM repositorios" " LEFT JOIN ordenadores USING (idrepositorio)" " WHERE repositorios.ip='%s' AND ordenadores.idordenador=%s", ipr, ido); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "repository does not exist in database (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); return false; } idr = dbi_result_get_uint(result, "idrepositorio"); dbi_result_free(result); /* Toma identificador del perfilsoftware */ result = dbi_conn_queryf(dbi->conn, "SELECT idperfilsoft" " FROM ordenadores_particiones" " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", ido, dsk, par); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "software profile does not exist in database (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); return false; } ifs = dbi_result_get_uint(result, "idperfilsoft"); dbi_result_free(result); /* Actualizar los datos de la imagen */ result = dbi_conn_queryf(dbi->conn, "UPDATE imagenes" " SET idordenador=%s, numdisk=%s, numpar=%s, codpar=%s," " idperfilsoft=%d, idrepositorio=%d," " fechacreacion=NOW(), revision=revision+1" " WHERE idimagen=%s", ido, dsk, par, cpt, ifs, idr, idi); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); /* Actualizar los datos en el cliente */ result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores_particiones" " SET idimagen=%s, revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," " fechadespliegue=NOW()" " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", idi, idi, ido, dsk, par); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); return true; } // ________________________________________________________________________________________________________ // Función: actualizaRestauracionImagen // // Descripción: // Esta función actualiza la base de datos con el resultado de la restauración de una imagen // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - idi: Identificador de la imagen // - dsk: Disco de donde se restauró // - par: Partición de donde se restauró // - ido: Identificador del cliente donde se restauró // - ifs: Identificador del perfil software contenido en la imagen // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // ________________________________________________________________________________________________________ bool actualizaRestauracionImagen(struct og_dbi *dbi, char *idi, char *dsk, char *par, char *ido, char *ifs) { const char *msglog; dbi_result result; /* Actualizar los datos de la imagen */ result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores_particiones" " SET idimagen=%s, idperfilsoft=%s, fechadespliegue=NOW()," " revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," " idnombreso=IFNULL((SELECT idnombreso FROM perfilessoft WHERE idperfilsoft=%s),0)" " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", idi, ifs, idi, ifs, ido, dsk, par); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); return true; } // ________________________________________________________________________________________________________ // Función: actualizaHardware // // Descripción: // Actualiza la base de datos con la configuracion hardware del cliente // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - hrd: cadena con el inventario hardware // - ido: Identificador del ordenador // - npc: Nombre del ordenador // - idc: Identificador del centro o Unidad organizativa // ________________________________________________________________________________________________________ // bool actualizaHardware(struct og_dbi *dbi, char *hrd, char *ido, char *npc, char *idc) { const char *msglog; int idtipohardware, idperfilhard; int lon, i, j, aux; bool retval; char *whard; int tbidhardware[MAXHARDWARE]; char *tbHardware[MAXHARDWARE],*dualHardware[2], strInt[LONINT], *idhardwares; dbi_result result; /* Toma Centro (Unidad Organizativa) */ result = dbi_conn_queryf(dbi->conn, "SELECT idperfilhard FROM ordenadores WHERE idordenador=%s", ido); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); return false; } idperfilhard = dbi_result_get_uint(result, "idperfilhard"); dbi_result_free(result); whard=escaparCadena(hrd); // Codificar comillas simples if(!whard) return false; /* Recorre componentes hardware*/ lon = splitCadena(tbHardware, whard, '\n'); if (lon > MAXHARDWARE) lon = MAXHARDWARE; // Limita el número de componentes hardware /* for (i=0;iconn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { // Tipo de Hardware NO existente dbi_result_free(result); return false; } else { // Tipo de Hardware Existe idtipohardware = dbi_result_get_uint(result, "idtipohardware"); dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT idhardware FROM hardwares WHERE idtipohardware=%d AND descripcion='%s'", idtipohardware, dualHardware[1]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { // Hardware NO existente dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT hardwares (idtipohardware,descripcion,idcentro,grupoid) " " VALUES(%d,'%s',%s,0)", idtipohardware, dualHardware[1], idc); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } // Recupera el identificador del hardware tbidhardware[i] = dbi_conn_sequence_last(dbi->conn, NULL); } else { tbidhardware[i] = dbi_result_get_uint(result, "idhardware"); } dbi_result_free(result); } } // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones for (i = 0; i < lon - 1; i++) { for (j = i + 1; j < lon; j++) { if (tbidhardware[i] > tbidhardware[j]) { aux = tbidhardware[i]; tbidhardware[i] = tbidhardware[j]; tbidhardware[j] = aux; } } } /* Crea cadena de identificadores de componentes hardware separados por coma */ sprintf(strInt, "%d", tbidhardware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles idhardwares = calloc(1, sizeof(aux) * lon + lon); if (idhardwares == NULL) { syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); return false; } aux = sprintf(idhardwares, "%d", tbidhardware[0]); for (i = 1; i < lon; i++) aux += sprintf(idhardwares + aux, ",%d", tbidhardware[i]); if (!cuestionPerfilHardware(dbi, idc, ido, idperfilhard, idhardwares, npc, tbidhardware, lon)) { syslog(LOG_ERR, "Problem updating client hardware\n"); retval=false; } else { retval=true; } free(whard); free(idhardwares); return (retval); } // ________________________________________________________________________________________________________ // Función: cuestionPerfilHardware // // Descripción: // Comprueba existencia de perfil hardware y actualización de éste para el ordenador // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - idc: Identificador de la Unidad organizativa donde se encuentra el cliente // - ido: Identificador del ordenador // - tbidhardware: Identificador del tipo de hardware // - con: Número de componentes detectados para configurar un el perfil hardware // - npc: Nombre del cliente // ________________________________________________________________________________________________________ bool cuestionPerfilHardware(struct og_dbi *dbi, char *idc, char *ido, int idperfilhardware, char *idhardwares, char *npc, int *tbidhardware, int lon) { const char *msglog; dbi_result result; int i; int nwidperfilhard; // Busca perfil hard del ordenador que contenga todos los componentes hardware encontrados result = dbi_conn_queryf(dbi->conn, "SELECT idperfilhard FROM" " (SELECT perfileshard_hardwares.idperfilhard as idperfilhard," " group_concat(cast(perfileshard_hardwares.idhardware AS char( 11) )" " ORDER BY perfileshard_hardwares.idhardware SEPARATOR ',' ) AS idhardwares" " FROM perfileshard_hardwares" " GROUP BY perfileshard_hardwares.idperfilhard) AS temp" " WHERE idhardwares LIKE '%s'", idhardwares); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { // No existe un perfil hardware con esos componentes de componentes hardware, lo crea dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT perfileshard (descripcion,idcentro,grupoid)" " VALUES('Perfil hardware (%s) ',%s,0)", npc, idc); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); // Recupera el identificador del nuevo perfil hardware nwidperfilhard = dbi_conn_sequence_last(dbi->conn, NULL); // Crea la relación entre perfiles y componenetes hardware for (i = 0; i < lon; i++) { result = dbi_conn_queryf(dbi->conn, "INSERT perfileshard_hardwares (idperfilhard,idhardware)" " VALUES(%d,%d)", nwidperfilhard, tbidhardware[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); } } else { // Existe un perfil con todos esos componentes nwidperfilhard = dbi_result_get_uint(result, "idperfilhard"); dbi_result_free(result); } if (idperfilhardware != nwidperfilhard) { // No coinciden los perfiles // Actualiza el identificador del perfil hardware del ordenador result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET idperfilhard=%d" " WHERE idordenador=%s", nwidperfilhard, ido); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); } /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfileshard_hardwares WHERE idperfilhard IN " " (SELECT idperfilhard FROM perfileshard WHERE idperfilhard NOT IN" " (SELECT DISTINCT idperfilhard from ordenadores))"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); /* Eliminar Perfiles hardware que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfileshard WHERE idperfilhard NOT IN" " (SELECT DISTINCT idperfilhard FROM ordenadores)"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfileshard_hardwares WHERE idperfilhard NOT IN" " (SELECT idperfilhard FROM perfileshard)"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); return true; } // ________________________________________________________________________________________________________ // Función: actualizaSoftware // // Descripción: // Actualiza la base de datos con la configuración software del cliente // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - sft: cadena con el inventario software // - par: Número de la partición // - ido: Identificador del ordenador del cliente en la tabla // - npc: Nombre del ordenador // - idc: Identificador del centro o Unidad organizativa // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // // Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla // ________________________________________________________________________________________________________ bool actualizaSoftware(struct og_dbi *dbi, char *sft, char *par,char *ido, char *npc, char *idc) { int i, j, lon, aux, idperfilsoft, idnombreso; bool retval; char *wsft; int tbidsoftware[MAXSOFTWARE]; char *tbSoftware[MAXSOFTWARE], strInt[LONINT], *idsoftwares; const char *msglog; dbi_result result; /* Toma Centro (Unidad Organizativa) y perfil software */ result = dbi_conn_queryf(dbi->conn, "SELECT idperfilsoft,numpar" " FROM ordenadores_particiones" " WHERE idordenador=%s", ido); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } idperfilsoft = 0; // Por defecto se supone que el ordenador no tiene aún detectado el perfil software while (dbi_result_next_row(result)) { aux = dbi_result_get_uint(result, "numpar"); if (aux == atoi(par)) { // Se encuentra la partición idperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); break; } } dbi_result_free(result); wsft=escaparCadena(sft); // Codificar comillas simples if(!wsft) return false; /* Recorre componentes software*/ lon = splitCadena(tbSoftware, wsft, '\n'); if (lon == 0) return true; // No hay lineas que procesar if (lon > MAXSOFTWARE) lon = MAXSOFTWARE; // Limita el número de componentes software idnombreso = 0; for (i = 0; i < lon; i++) { // Primera línea es el sistema operativo: se obtiene identificador if (i == 0) { idnombreso = checkDato(dbi, rTrim(tbSoftware[i]), "nombresos", "nombreso", "idnombreso"); continue; } result = dbi_conn_queryf(dbi->conn, "SELECT idsoftware FROM softwares WHERE descripcion ='%s'", rTrim(tbSoftware[i])); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO softwares (idtiposoftware,descripcion,idcentro,grupoid)" " VALUES(2,'%s',%s,0)", tbSoftware[i], idc); if (!result) { // Error al insertar dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } // Recupera el identificador del software tbidsoftware[i] = dbi_conn_sequence_last(dbi->conn, NULL); } else { tbidsoftware[i] = dbi_result_get_uint(result, "idsoftware"); } dbi_result_free(result); } // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones for (i = 0; i < lon - 1; i++) { for (j = i + 1; j < lon; j++) { if (tbidsoftware[i] > tbidsoftware[j]) { aux = tbidsoftware[i]; tbidsoftware[i] = tbidsoftware[j]; tbidsoftware[j] = aux; } } } /* Crea cadena de identificadores de componentes software separados por coma */ sprintf(strInt, "%d", tbidsoftware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles idsoftwares = calloc(1, (sizeof(aux)+1) * lon + lon); if (idsoftwares == NULL) { syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); return false; } aux = sprintf(idsoftwares, "%d", tbidsoftware[0]); for (i = 1; i < lon; i++) aux += sprintf(idsoftwares + aux, ",%d", tbidsoftware[i]); // Comprueba existencia de perfil software y actualización de éste para el ordenador if (!cuestionPerfilSoftware(dbi, idc, ido, idperfilsoft, idnombreso, idsoftwares, npc, par, tbidsoftware, lon)) { syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); retval=false; } else { retval=true; } free(wsft); free(idsoftwares); return retval; } // ________________________________________________________________________________________________________ // Función: CuestionPerfilSoftware // // Parámetros: // - db: Objeto base de datos (ya operativo) // - tbl: Objeto tabla // - idcentro: Identificador del centro en la tabla // - ido: Identificador del ordenador del cliente en la tabla // - idnombreso: Identificador del sistema operativo // - idsoftwares: Cadena con los identificadores de componentes software separados por comas // - npc: Nombre del ordenador del cliente // - particion: Número de la partición // - tbidsoftware: Array con los identificadores de componentes software // - lon: Número de componentes // Devuelve: // true: Si el proceso es correcto // false: En caso de ocurrir algún error // // Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla //_________________________________________________________________________________________________________ bool cuestionPerfilSoftware(struct og_dbi *dbi, char *idc, char *ido, int idperfilsoftware, int idnombreso, char *idsoftwares, char *npc, char *par, int *tbidsoftware, int lon) { int i, nwidperfilsoft; const char *msglog; dbi_result result; // Busca perfil soft del ordenador que contenga todos los componentes software encontrados result = dbi_conn_queryf(dbi->conn, "SELECT idperfilsoft FROM" " (SELECT perfilessoft_softwares.idperfilsoft as idperfilsoft," " group_concat(cast(perfilessoft_softwares.idsoftware AS char( 11) )" " ORDER BY perfilessoft_softwares.idsoftware SEPARATOR ',' ) AS idsoftwares" " FROM perfilessoft_softwares" " GROUP BY perfilessoft_softwares.idperfilsoft) AS temp" " WHERE idsoftwares LIKE '%s'", idsoftwares); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } if (!dbi_result_next_row(result)) { // No existe un perfil software con esos componentes de componentes software, lo crea dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT perfilessoft (descripcion, idcentro, grupoid, idnombreso)" " VALUES('Perfil Software (%s, Part:%s) ',%s,0,%i)", npc, par, idc,idnombreso); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); // Recupera el identificador del nuevo perfil software nwidperfilsoft = dbi_conn_sequence_last(dbi->conn, NULL); // Crea la relación entre perfiles y componenetes software for (i = 0; i < lon; i++) { result = dbi_conn_queryf(dbi->conn, "INSERT perfilessoft_softwares (idperfilsoft,idsoftware)" " VALUES(%d,%d)", nwidperfilsoft, tbidsoftware[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); } } else { // Existe un perfil con todos esos componentes nwidperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); dbi_result_free(result); } if (idperfilsoftware != nwidperfilsoft) { // No coinciden los perfiles // Actualiza el identificador del perfil software del ordenador result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores_particiones SET idperfilsoft=%d,idimagen=0" " WHERE idordenador=%s AND numpar=%s", nwidperfilsoft, ido, par); if (!result) { // Error al insertar dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); } /* DEPURACIÓN DE PERFILES SOFTWARE */ /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfilessoft_softwares WHERE idperfilsoft IN "\ " (SELECT idperfilsoft FROM perfilessoft WHERE idperfilsoft NOT IN"\ " (SELECT DISTINCT idperfilsoft from ordenadores_particiones) AND idperfilsoft NOT IN"\ " (SELECT DISTINCT idperfilsoft from imagenes))"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result), /* Eliminar Perfiles software que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfilessoft WHERE idperfilsoft NOT IN" " (SELECT DISTINCT idperfilsoft from ordenadores_particiones)"\ " AND idperfilsoft NOT IN"\ " (SELECT DISTINCT idperfilsoft from imagenes)"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result), /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ result = dbi_conn_queryf(dbi->conn, "DELETE FROM perfilessoft_softwares WHERE idperfilsoft NOT IN" " (SELECT idperfilsoft from perfilessoft)"); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return false; } dbi_result_free(result); return true; } static void og_client_release(struct ev_loop *loop, struct og_client *cli) { if (cli->keepalive_idx >= 0) { syslog(LOG_DEBUG, "closing keepalive connection for %s:%hu in slot %d\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), cli->keepalive_idx); tbsockets[cli->keepalive_idx].cli = NULL; } list_del(&cli->list); ev_io_stop(loop, &cli->io); close(cli->io.fd); free(cli); } static void og_client_keepalive(struct ev_loop *loop, struct og_client *cli) { struct og_client *old_cli; old_cli = tbsockets[cli->keepalive_idx].cli; if (old_cli && old_cli != cli) { syslog(LOG_DEBUG, "closing old keepalive connection for %s:%hu\n", inet_ntoa(old_cli->addr.sin_addr), ntohs(old_cli->addr.sin_port)); og_client_release(loop, old_cli); } tbsockets[cli->keepalive_idx].cli = cli; } static void og_client_reset_state(struct og_client *cli) { cli->state = OG_CLIENT_RECEIVING_HEADER; cli->buf_len = 0; } static int og_client_payload_too_large(struct og_client *cli) { char buf[] = "HTTP/1.1 413 Payload Too Large\r\n" "Content-Length: 0\r\n\r\n"; send(og_client_socket(cli), buf, strlen(buf), 0); return -1; } static int og_client_state_recv_hdr_rest(struct og_client *cli) { char *ptr; ptr = strstr(cli->buf, "\r\n\r\n"); if (!ptr) return 0; cli->msg_len = ptr - cli->buf + 4; ptr = strstr(cli->buf, "Content-Length: "); if (ptr) { sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length); if (cli->content_length < 0) return -1; cli->msg_len += cli->content_length; } ptr = strstr(cli->buf, "Authorization: "); if (ptr) sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token); return 1; } static int og_client_recv(struct og_client *cli, int events) { struct ev_io *io = &cli->io; int ret; if (events & EV_ERROR) { syslog(LOG_ERR, "unexpected error event from client %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); return 0; } ret = recv(io->fd, cli->buf + cli->buf_len, sizeof(cli->buf) - cli->buf_len, 0); if (ret <= 0) { if (ret < 0) { syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), strerror(errno)); } else { syslog(LOG_DEBUG, "closed connection by %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); } return ret; } return ret; } static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events) { struct og_client *cli; int ret; cli = container_of(io, struct og_client, io); ret = og_client_recv(cli, events); if (ret <= 0) goto close; if (cli->keepalive_idx >= 0) return; ev_timer_again(loop, &cli->timer); cli->buf_len += ret; if (cli->buf_len >= sizeof(cli->buf)) { syslog(LOG_ERR, "client request from %s:%hu is too long\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); og_client_payload_too_large(cli); goto close; } switch (cli->state) { case OG_CLIENT_RECEIVING_HEADER: ret = og_client_state_recv_hdr_rest(cli); if (ret < 0) goto close; if (!ret) return; cli->state = OG_CLIENT_RECEIVING_PAYLOAD; /* Fall through. */ case OG_CLIENT_RECEIVING_PAYLOAD: /* Still not enough data to process request. */ if (cli->buf_len < cli->msg_len) return; cli->state = OG_CLIENT_PROCESSING_REQUEST; /* fall through. */ case OG_CLIENT_PROCESSING_REQUEST: ret = og_client_state_process_payload_rest(cli); if (ret < 0) { syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); } if (ret < 0) goto close; if (cli->keepalive_idx < 0) { syslog(LOG_DEBUG, "server closing connection to %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); goto close; } else { syslog(LOG_DEBUG, "leaving client %s:%hu in keepalive mode\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); og_client_keepalive(loop, cli); og_client_reset_state(cli); } break; default: syslog(LOG_ERR, "unknown state, critical internal error\n"); goto close; } return; close: ev_timer_stop(loop, &cli->timer); og_client_release(loop, cli); } enum og_agent_state { OG_AGENT_RECEIVING_HEADER = 0, OG_AGENT_RECEIVING_PAYLOAD, OG_AGENT_PROCESSING_RESPONSE, }; static int og_agent_state_recv_hdr_rest(struct og_client *cli) { char *ptr; ptr = strstr(cli->buf, "\r\n\r\n"); if (!ptr) return 0; cli->msg_len = ptr - cli->buf + 4; ptr = strstr(cli->buf, "Content-Length: "); if (ptr) { sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length); if (cli->content_length < 0) return -1; cli->msg_len += cli->content_length; } return 1; } static void og_agent_reset_state(struct og_client *cli) { cli->state = OG_AGENT_RECEIVING_HEADER; cli->buf_len = 0; cli->content_length = 0; memset(cli->buf, 0, sizeof(cli->buf)); } static void og_agent_deliver_pending_cmd(struct og_client *cli) { const struct og_cmd *cmd; cmd = og_cmd_find(inet_ntoa(cli->addr.sin_addr)); if (!cmd) return; og_send_request(cmd->method, cmd->type, &cmd->params, cmd->json); cli->last_cmd_id = cmd->id; og_cmd_free(cmd); } static void og_agent_read_cb(struct ev_loop *loop, struct ev_io *io, int events) { struct og_client *cli; int ret; cli = container_of(io, struct og_client, io); ret = og_client_recv(cli, events); if (ret <= 0) goto close; ev_timer_again(loop, &cli->timer); cli->buf_len += ret; if (cli->buf_len >= sizeof(cli->buf)) { syslog(LOG_ERR, "client request from %s:%hu is too long\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); goto close; } switch (cli->state) { case OG_AGENT_RECEIVING_HEADER: ret = og_agent_state_recv_hdr_rest(cli); if (ret < 0) goto close; if (!ret) return; cli->state = OG_AGENT_RECEIVING_PAYLOAD; /* Fall through. */ case OG_AGENT_RECEIVING_PAYLOAD: /* Still not enough data to process request. */ if (cli->buf_len < cli->msg_len) return; cli->state = OG_AGENT_PROCESSING_RESPONSE; /* fall through. */ case OG_AGENT_PROCESSING_RESPONSE: ret = og_agent_state_process_response(cli); if (ret < 0) { syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); goto close; } else if (ret == 0) { og_agent_deliver_pending_cmd(cli); } syslog(LOG_DEBUG, "leaving client %s:%hu in keepalive mode\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); og_agent_reset_state(cli); break; default: syslog(LOG_ERR, "unknown state, critical internal error\n"); goto close; } return; close: ev_timer_stop(loop, &cli->timer); og_client_release(loop, cli); } static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events) { struct og_client *cli; cli = container_of(timer, struct og_client, timer); if (cli->keepalive_idx >= 0) { ev_timer_again(loop, &cli->timer); return; } syslog(LOG_ERR, "timeout request for client %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); og_client_release(loop, cli); } static void og_agent_send_refresh(struct og_client *cli) { struct og_msg_params params; int err; params.ips_array[0] = inet_ntoa(cli->addr.sin_addr); params.ips_array_len = 1; err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, ¶ms, NULL); if (err < 0) { syslog(LOG_ERR, "Can't send refresh to: %s\n", params.ips_array[0]); } else { syslog(LOG_INFO, "Sent refresh to: %s\n", params.ips_array[0]); } } static int socket_rest, socket_agent_rest; static void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events) { struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); struct og_client *cli; int client_sd; if (events & EV_ERROR) return; client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen); if (client_sd < 0) { syslog(LOG_ERR, "cannot accept client connection\n"); return; } cli = (struct og_client *)calloc(1, sizeof(struct og_client)); if (!cli) { close(client_sd); return; } memcpy(&cli->addr, &client_addr, sizeof(client_addr)); if (io->fd == socket_agent_rest) cli->keepalive_idx = 0; else cli->keepalive_idx = -1; if (io->fd == socket_rest) cli->rest = true; else if (io->fd == socket_agent_rest) cli->agent = true; syslog(LOG_DEBUG, "connection from client %s:%hu\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); if (io->fd == socket_agent_rest) ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ); else ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ); ev_io_start(loop, &cli->io); if (io->fd == socket_agent_rest) { ev_timer_init(&cli->timer, og_client_timer_cb, OG_AGENT_CLIENT_TIMEOUT, 0.); } else { ev_timer_init(&cli->timer, og_client_timer_cb, OG_CLIENT_TIMEOUT, 0.); } ev_timer_start(loop, &cli->timer); og_client_add(cli); if (io->fd == socket_agent_rest) { og_agent_send_refresh(cli); } } static int og_socket_server_init(const char *port) { struct sockaddr_in local; int sd, on = 1; sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sd < 0) { syslog(LOG_ERR, "cannot create main socket\n"); return -1; } setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(atoi(port)); if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) { close(sd); syslog(LOG_ERR, "cannot bind socket\n"); return -1; } listen(sd, 250); return sd; } struct ev_loop *og_loop; int main(int argc, char *argv[]) { struct ev_io ev_io_server_rest, ev_io_agent_rest; int i; og_loop = ev_default_loop(0); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) exit(EXIT_FAILURE); openlog("ogAdmServer", LOG_PID, LOG_DAEMON); /*-------------------------------------------------------------------------------------------------------- Validación de parámetros de ejecución y lectura del fichero de configuración del servicio ---------------------------------------------------------------------------------------------------------*/ if (!validacionParametros(argc, argv, 1)) // Valida parámetros de ejecución exit(EXIT_FAILURE); if (!tomaConfiguracion(szPathFileCfg)) { // Toma parametros de configuracion exit(EXIT_FAILURE); } /*-------------------------------------------------------------------------------------------------------- // Inicializa array de información de los clientes ---------------------------------------------------------------------------------------------------------*/ for (i = 0; i < MAXIMOS_CLIENTES; i++) { tbsockets[i].ip[0] = '\0'; tbsockets[i].cli = NULL; } /*-------------------------------------------------------------------------------------------------------- Creación y configuración del socket del servicio ---------------------------------------------------------------------------------------------------------*/ socket_rest = og_socket_server_init("8888"); if (socket_rest < 0) exit(EXIT_FAILURE); ev_io_init(&ev_io_server_rest, og_server_accept_cb, socket_rest, EV_READ); ev_io_start(og_loop, &ev_io_server_rest); socket_agent_rest = og_socket_server_init("8889"); if (socket_agent_rest < 0) exit(EXIT_FAILURE); ev_io_init(&ev_io_agent_rest, og_server_accept_cb, socket_agent_rest, EV_READ); ev_io_start(og_loop, &ev_io_agent_rest); if (og_dbi_schedule_get() < 0) exit(EXIT_FAILURE); og_schedule_next(og_loop); syslog(LOG_INFO, "Waiting for connections\n"); while (1) ev_loop(og_loop, 0); exit(EXIT_SUCCESS); }