/* * This file is part of the rtap localisation project. */ #include "owlps-aggregator.h" char *program_name = NULL ; cfg_t *cfg = NULL ; // Configuration structure couple_list *couples = NULL ; // Computed data list int main(int argc, char **argv) { int ret = 0 ; // Valeur de retour du programme struct sigaction action ; // Structure de mise en place des gestionnaires de signaux pthread_t thread ; // Thread pour la fonction de surveillance de la liste d'informations agrégées int sockfd ; // Socket d'écoute UDP program_name = argv[0] ; initialise_configuration(argc, argv) ; /* Mise en place des gestionnaires de signaux */ sigemptyset(&action.sa_mask) ; action.sa_handler = sigint_handler ; sigaction(SIGINT, &action, NULL) ; action.sa_handler = sigterm_handler ; sigaction(SIGTERM, &action, NULL) ; /* Création de la socket UDP */ if ((sockfd = create_udp_listening_socket(cfg_getint(cfg, "listening_port"))) < 0) { fprintf(stderr, "Erreur ! Impossible d'écouter sur le port %ld.\n", cfg_getint(cfg, "listening_port")) ; return ERR_CREATING_SOCKET ; } /* Création du thread */ pthread_create(&thread, NULL, (void *) &monitor_couple_list, &couples) ; run = TRUE ; ret = read_loop(sockfd) ; (void) close(sockfd) ; // Fermeture de la socket free_couple_list(&couples) ; // Nettoyage de la liste cfg_free(cfg) ; // Nettoyage de la configuration printf("%s : fin.\n", argv[0]) ; return ret ; } void initialise_configuration(int argc, char **argv) { parse_config_file(argc, argv) ; parse_command_line(argc, argv) ; check_configuration() ; #ifdef DEBUG /* Configuration printing */ fprintf(stderr, "Configuration :\n") ; cfg_print(cfg, stderr) ; #endif // DEBUG } void parse_config_file(int argc, char **argv) { // Config file options for confuse cfg_opt_t opts[] = { CFG_INT("listening_port", AGGREGATE_DEFAULT_PORT, CFGF_NONE), // Port and IP address of the localisation server: CFG_INT("positioner_port", POSITIONER_DEFAULT_PORT, CFGF_NONE), CFG_STR("positioner_ip", POSITIONER_DEFAULT_IP, CFGF_NONE), CFG_STR("output_file", "", CFGF_NONE), // Timeouts (in milliseconds): CFG_INT("aggregate_timeout", DEFAULT_AGGREGATE_TIMEOUT, CFGF_NONE), CFG_INT("keep_timeout", DEFAULT_KEEP_TIMEOUT, CFGF_NONE), // Time between two list checks (in microseconds): CFG_INT("check_interval", DEFAULT_CHECK_INTERVAL, CFGF_NONE), CFG_END() } ; char *config_file ; // Option -f specifies a config file, so we search for it first int opt ; do opt = getopt(argc, argv, OPTIONS) ; while (opt != 'f' && opt != -1) ; if (opt == 'f') { config_file = malloc((strlen(optarg) + 1) * sizeof(char)) ; strcpy(config_file, optarg) ; } else // If -f isn't found, we use the default config file { config_file = malloc((strlen(DEFAULT_CONFIG_FILE) + 1) * sizeof(char)) ; strcpy(config_file, DEFAULT_CONFIG_FILE) ; } /* Parse config file */ cfg = cfg_init(opts, CFGF_NONE) ; // Initialise options if (cfg_parse(cfg, config_file) == CFG_PARSE_ERROR) { free(config_file) ; exit(ERR_PARSING_CONFIG_FILE) ; } free(config_file) ; } void parse_command_line(int argc, char **argv) { int opt ; optind = 1 ; // Rewind argument parsing while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'a' : cfg_setint(cfg, "aggregate_timeout", strtol(optarg, NULL, 0)) ; break ; case 'c' : cfg_setint(cfg, "check_interval", strtol(optarg, NULL, 0)) ; break ; case 'f' : // Config file break ; // (already parsed) case 'i' : cfg_setstr(cfg, "positioner_ip", optarg) ; break ; case 'k' : cfg_setint(cfg, "keep_timeout", strtol(optarg, NULL, 0)) ; break ; case 'l' : cfg_setint(cfg, "listening_port", strtol(optarg, NULL, 0)) ; break ; case 'o' : cfg_setstr(cfg, "output_file", optarg) ; break ; case 'p' : cfg_setint(cfg, "positioner_port", strtol(optarg, NULL, 0)) ; break ; default : print_usage() ; exit(ERR_BAD_USAGE) ; } } } void check_configuration() { // output_file // if (cfg_getstr(cfg, "output_file")[0] == '\0') { fprintf(stderr, "Error! You must specify an output file.\n") ; print_usage() ; exit(ERR_BAD_USAGE) ; } // positioner_ip // if (cfg_getstr(cfg, "positioner_ip")[0] == '\0') { fprintf(stderr, "Error! You must specify the IP address of the" " localisation server.\n") ; print_usage() ; exit(ERR_BAD_USAGE) ; } // aggregate_timeout // if (cfg_getint(cfg, "aggregate_timeout") < 0) { #ifdef DEBUG fprintf(stderr, "Warning! aggregate_timeout cannot be negative:" " failing back to default value.\n") ; #endif // DEBUG cfg_setint(cfg, "aggregate_timeout", DEFAULT_AGGREGATE_TIMEOUT) ; } // keep_timeout // if (cfg_getint(cfg, "keep_timeout") < 0) { #ifdef DEBUG fprintf(stderr, "Warning! keep_timeout cannot be negative:" " failing back to default value.\n") ; #endif // DEBUG cfg_setint(cfg, "keep_timeout", DEFAULT_KEEP_TIMEOUT) ; } // check_interval // if (cfg_getint(cfg, "check_interval") < 0) { #ifdef DEBUG fprintf(stderr, "Warning! check_interval cannot be negative:" " failing back to default value.\n") ; #endif // DEBUG cfg_setint(cfg, "check_interval", DEFAULT_CHECK_INTERVAL) ; } } int read_loop(int sockfd) { int ret = 0 ; // Return value int nread ; // Retour de recvfrom struct sockaddr_in client; // Structure pour le client UDP socklen_t client_len = sizeof(client) ; // Taille du client pour la socket couple_message message ; // Message lu sur la socket char *ap_mac_string, *mobile_mac_string ; // Pointeurs pour retour de mac_bytes_to_string() char *mobile_ip_string ; while (run) { nread = recvfrom(sockfd, &message, sizeof(message), 0, (struct sockaddr *) &client, &client_len) ; if (nread <= 0) { if (run) { fprintf(stderr, "Aucun message reçu du client !\n") ; ret = ERR_NO_MESSAGE_RECEIVED ; } break ; } ap_mac_string = mac_bytes_to_string(message.ap_mac_addr_bytes) ; mobile_mac_string = mac_bytes_to_string(message.mobile_mac_addr_bytes) ; mobile_ip_string = ip_bytes_to_string(message.mobile_ip_addr_bytes) ; printf("\n\ *** Message reçu du client ***\n\ \tMAC AP : %s\n\ \tMAC mobile : %s\n\ \tIP mobile : %s\n\ \tNuméro de séquence (heure de la demande) : %llu\n\ \tHeure d'arrivée de la demande de localisation sur l'AP : %llu\n\ \tSignal : %d dBm\n\ \tPosition X : %f\n\ \tPosition Y : %f\n\ \tPosition Z : %f\n\ \tDirection : %hhd\n\ ", ap_mac_string, mobile_mac_string, mobile_ip_string, timeval_to_ms(message.request_time), timeval_to_ms(message.start_time), message.antenna_signal_dbm - 0x100, message.x_position, message.y_position, message.z_position, message.direction ) ; free(ap_mac_string) ; free(mobile_mac_string) ; free(mobile_ip_string) ; got_couple_info(&couples, message) ; } return ret ; } /* Fonction du thread, qui surveille la liste et envoie les infos au serveur de localisation au bout du timeout */ void* monitor_couple_list(couple_list **couples) { couple_list *couple_ptr, *couple_prev ; couple_info_list *couple_info_ptr ; struct timeval current_time ; FILE *fd = NULL ; char *ap_mac_string ; unsigned long sub ; // Résultat de sub_date(). struct sockaddr_in serv; struct sockaddr_in client ; socklen_t serv_len = sizeof(serv); request demande; couple_info info; int sockfd; sockfd = create_udp_sending_socket(cfg_getstr(cfg, "positioner_ip"), cfg_getint(cfg, "positioner_port"), &serv, &client); fd = fopen(cfg_getstr(cfg, "output_file"), "a") ; // Ouverture du fichier de sortie en ajout if (fd == NULL) // Si ouverture échouée, { perror("Impossible d'ouvrir le fichier de sortie ") ; fprintf(stderr, "Redirection de la sortie sur la sortie standard.") ; fd = stdout ; // on redirige sur stdout } while (run) { couple_ptr = *couples ; couple_prev = NULL ; couple_info_ptr = NULL ; gettimeofday(¤t_time, NULL) ; while (couple_ptr != NULL) // Parcours de la liste { sub = sub_date(couple_ptr->start_time, current_time) ; if (couple_ptr->info != NULL) // Si le couple atteint n'a pas déjà été traité { if (sub > (unsigned long) cfg_getint(cfg, "aggregate_timeout")) // Si le timeout est atteint, { printf("* Timeout dépassé.") ; #ifdef DEBUG printf(" sub=%lu > aggregate_timeout=%ld\n", sub, cfg_getint(cfg, "aggregate_timeout")) ; #else // DEBUG putchar('\n') ; #endif // DEBUG #ifdef TIMESTAMP fprintf(fd, "%llu;", timeval_to_ms(couple_ptr->request_time)) ; // Inscription de la date de la demande (sur le mobile) dans le fichier #endif // TIMESTAMP fprintf(fd, "%0.2f;%0.2f;%0.2f;%hhd", couple_ptr->x_position, couple_ptr->y_position, couple_ptr->z_position, couple_ptr->direction) ; // Inscription des infos du couple dans le fichier memcpy(demande.mobile_mac_addr_bytes, couple_ptr -> mobile_mac_addr_bytes, 6); demande.request_time = couple_ptr->request_time; demande.nb_couples = 0 ; couple_info_ptr = couple_ptr->info ; while (couple_info_ptr != NULL) { demande.nb_couples++; couple_info_ptr = couple_info_ptr->next ; } sendto(sockfd, (void *)&demande, sizeof(request), 0, (struct sockaddr *)&serv, serv_len) ; couple_info_ptr = couple_ptr->info ; while (couple_info_ptr != NULL) // On envoi les couples au serveur et on vide la liste { // Envoi des infos de l'AP vers le serveur de géolocalisation memcpy(info.ap_mac_addr_bytes, couple_info_ptr->ap_mac_addr_bytes, 6); info.antenna_signal_dbm = couple_info_ptr->antenna_signal_dbm - 0x100; sendto(sockfd, (void *)&info, sizeof(couple_info), 0, (struct sockaddr *)&serv, serv_len); // Inscription des infos de l'AP dans le fichier ap_mac_string = mac_bytes_to_string(couple_info_ptr->ap_mac_addr_bytes) ; fprintf(fd, ";%s;%d", ap_mac_string, couple_info_ptr->antenna_signal_dbm - 0x100) ; free(ap_mac_string) ; // Suppression du maillon couple_info_ptr = couple_info_ptr->next ; free(couple_ptr->info) ; couple_ptr->info = couple_info_ptr ; } fprintf(fd, "\n") ; } } else if (sub > (unsigned long) cfg_getint(cfg, "keep_timeout")) // Si le couple a été traité et que le temps de garde est écoulé { couple_list *couple_tmp = couple_ptr ; printf("* Délai de garde dépassé.") ; #ifdef DEBUG printf(" sub=%lu > keep_timeout=%ld\n", sub, cfg_getint(cfg, "keep_timeout")) ; #else // DEBUG putchar('\n') ; #endif // DEBUG couple_ptr = couple_ptr->next ; if (couple_prev == NULL) // Si on est en tête de liste, *couples = couple_ptr ; // on positionne la tête sur le suivant else couple_prev->next = couple_ptr ; // sinon on positionne le suivant du précédent sur le suivant. free(couple_tmp) ; continue ; // On saute les instructions pour aller au suivant puisque c'est déjà fait } // Couple suivant couple_prev = couple_ptr ; couple_ptr = couple_ptr->next ; } fflush(NULL) ; usleep(cfg_getint(cfg, "check_interval")) ; // On attend avant de vérifier à nouveau } /* Fermeture du fichier */ if (fclose(fd) != 0) perror("Erreur lors de la fermeture du fichier de sortie ") ; return NULL ; } /* Fonction appelée lors de la réception d'un paquet */ void got_couple_info(couple_list **couples, couple_message message) { couple_list *tmp_couple = NULL ; couple_info_list *tmp_info = NULL ; struct timeval start_time ; // Heure de réception du paquet par l'agrégateur gettimeofday(&start_time, NULL) ; /* Initialisation d'un nouveau sous-maillon */ tmp_info = malloc(sizeof(couple_info_list)) ; memcpy(tmp_info->ap_mac_addr_bytes, message.ap_mac_addr_bytes, 6) ; tmp_info->antenna_signal_dbm = message.antenna_signal_dbm ; tmp_info->next = NULL ; /* Ajout dans la liste */ tmp_couple = *couples ; if (*couples == NULL) // Si la liste de couples n'existe pas encore, { printf("Création de la liste des couples.\n") ; tmp_couple = malloc(sizeof(couple_list)) ; // on la crée. memcpy(tmp_couple->mobile_mac_addr_bytes, message.mobile_mac_addr_bytes, 6) ; if (timeval_to_ms(message.request_time) != 0) // Paquet explicite tmp_couple->request_time = message.request_time ; // Heure d'émission par le mobile else // Paquet implicite tmp_couple->request_time = message.start_time ; // Heure de réception par l'AP tmp_couple->start_time = start_time ; // Enregistrement de la date locale de l'agrégateur et non de la date de réception de la demande sur l'AP tmp_couple->x_position = message.x_position ; tmp_couple->y_position = message.y_position ; tmp_couple->z_position = message.z_position ; tmp_couple->direction = message.direction ; tmp_couple->next = NULL ; tmp_couple->info = tmp_info ; *couples = tmp_couple ; } else { // Sinon on cherche si le couple existe déjà dans la liste if (timeval_to_ms(message.request_time) != 0) // Paquet explicite { while (tmp_couple != NULL) { // Critère : MAC et heure d'émission identiques. if(mac_cmp(message.mobile_mac_addr_bytes, tmp_couple->mobile_mac_addr_bytes) == 1 && sub_date(message.request_time, tmp_couple->request_time) == 0) break ; // Si le couple existe déjà, on s'arrête dessus. tmp_couple = tmp_couple->next ; } } else // Paquet implicite { while (tmp_couple != NULL) { // Critère : MAC identiques et décalage des heures de réception par les AP d'au maximum 10 ms. if(mac_cmp(message.mobile_mac_addr_bytes, tmp_couple->mobile_mac_addr_bytes) == 1 && sub_date(message.start_time, tmp_couple->request_time) <= 10) // TODO : définir un paramètre pour le décalage maximal break ; // Si le couple existe déjà, on s'arrête dessus. tmp_couple = tmp_couple->next ; } } if (tmp_couple == NULL) // Si couple inexistant dans la liste { printf("Création du couple.\n") ; tmp_couple = malloc(sizeof(couple_list)) ; // on crée un nouveau couple. memcpy(tmp_couple->mobile_mac_addr_bytes, message.mobile_mac_addr_bytes, 6) ; if (timeval_to_ms(message.request_time) != 0) // Paquet explicite tmp_couple->request_time = message.request_time ; // Heure d'émission par le mobile else // Paquet implicite tmp_couple->request_time = message.start_time ; // Heure de réception par l'AP tmp_couple->start_time = start_time ; // Enregistrement de la date locale de l'agrégateur et non de la date de réception de la demande sur l'AP tmp_couple->x_position = message.x_position ; tmp_couple->y_position = message.y_position ; tmp_couple->z_position = message.z_position ; tmp_couple->direction = message.direction ; tmp_couple->next = *couples ; tmp_couple->info = tmp_info ; *couples = tmp_couple ; } else // Si couple trouvé dans la liste { if (tmp_couple->info == NULL) // Si on a déjà envoyé les infos de ce couple au serveur { printf("Demande déjà traitée.\n") ; free(tmp_info) ; } else { printf("Ajout des informations au couple.\n") ; tmp_info->next = tmp_couple->info ; // on ajoute l'info. tmp_couple->info = tmp_info ; } } } } /* Vide la liste chaînée de couples */ void free_couple_list(couple_list **couples) { couple_list *couple_ptr = *couples ; couple_info_list *couple_info_ptr = NULL ; if (*couples != NULL) { while (couple_ptr != NULL) { couple_info_ptr = couple_ptr->info ; while (couple_info_ptr != NULL) { couple_info_ptr = couple_info_ptr->next ; free(couple_ptr->info) ; couple_ptr->info = couple_info_ptr ; } couple_ptr = couple_ptr->next ; free(*couples) ; *couples = couple_ptr ; } } } #ifdef DEBUG /* Affiche la liste des couples */ void print_couple_list(couple_list *couples) { couple_list *couple_ptr = couples ; couple_info_list *info_ptr = NULL ; char *mobile_mac_string ; if (couples == NULL) // Si la liste est vide { printf("Aucun couple.\n") ; // on l'affiche. return ; } while (couple_ptr != NULL) { info_ptr = couple_ptr->info ; // On récupère le pointeur de la sous-liste mobile_mac_string = mac_bytes_to_string(couple_ptr->mobile_mac_addr_bytes) ; printf("MAC du mobile : %s\n\ Numéro de séquence : %llu\n\ Heure de réception de la demande de localisation : %llu\n\ \n", mobile_mac_string, timeval_to_ms(couple_ptr->request_time), timeval_to_ms(couple_ptr->start_time) ) ; free(mobile_mac_string) ; while (info_ptr != NULL) // On parcourt toutes les informations relative au couple courant { print_couple_info(info_ptr) ; putchar('\n') ; info_ptr = info_ptr->next ; } printf("\n\n") ; couple_ptr = couple_ptr->next ; } } /* Affiche un maillon d'une couple_info_list */ void print_couple_info(couple_info_list *info) { char *ap_mac_string ; if (info == NULL) return ; ap_mac_string = mac_bytes_to_string(info->ap_mac_addr_bytes) ; printf("\tMAC de l'AP : %s\n\ \tPuissance du signal : %d dBm\n", ap_mac_string, info->antenna_signal_dbm - 0x100 ) ; free(ap_mac_string) ; } #endif // DEBUG char* ip_bytes_to_string(unsigned char *ip_binary) { int taille = 16, i = 0 ; for (i = 0 ; i < 4 ; ++i) { if (ip_binary[i] < 0x64) taille-- ; if (ip_binary[i] < 0x10) taille-- ; } char *ret = malloc(sizeof(char) * taille) ; sprintf(ret, "%d.%d.%d.%d", ip_binary[0], ip_binary[1], ip_binary[2], ip_binary[3]) ; ret[taille-1] = '\0' ; return ret ; } /* Affiche le mode d'emploi du programme */ void print_usage() { printf("Usage :\n\ \t%s [-f config_file] [-l listening_port] [-i positionner_ip] [-p positioner_port] [-a aggregate_timeout] [-k keep_timeout] [-c check_interval] [-o output_file]\n\ Options générales :\n\ \t-f config_file : spécifie un fichier de configuration à lire à la place du fichier par défaut.\n\ Options serveur :\n\ \t-l listening_port : seules les demandes envoyées sur ce port seront retenues (défaut : %d).\n\ \t-i positionner_ip : adresse IP du serveur de calcul auquel seront transmises les demandes (défaut : %s).\n\ \t-p positioner_port : les demandes agrégées seront transmises sur ce port du serveur de calcul (défaut : %d).\n\ Options d'agrégation :\n\ \t-a aggregate_timeout : temps d'agrégation (défaut : %d millisecondes)\n\ \t-k keep_timeout : temps de garde des demandes (défaut : %d millisecondes)\n\ \t-c check_interval : délai entre deux vérifications des demandes en mémoire, et agrégation (défaut : %d micro-secondes)\n\ ", program_name, AGGREGATE_DEFAULT_PORT, POSITIONER_DEFAULT_IP, POSITIONER_DEFAULT_PORT, DEFAULT_AGGREGATE_TIMEOUT, DEFAULT_KEEP_TIMEOUT, DEFAULT_CHECK_INTERVAL ) ; }