/* * This file is part of the rtap localisation project. */ #include "owlps-aggregator.h" #define DEBUG cfg_t *cfg ; // Structure contenant la configuration int main(int argc, char **argv) { int ret = 0 ; // Valeur de retour du programme couple_list *couples = NULL ; // Liste des données traitées struct sigaction action ; // Structure de mise en place des gestionnaires de signaux int sockfd ; // Socket d'écoute UDP 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 pthread_t thread ; // Thread pour la fonction de surveillance de la liste d'informations agrégées char *ap_mac_string, *mobile_mac_string ; // Pointeurs pour retour de mac_bytes_to_string() cfg_opt_t opts[] = { // Options reconnues par confuse dans le fichier de config CFG_INT("listening_port", AGGREGATE_DEFAULT_PORT, CFGF_NONE), // Port d'écoute CFG_STR("output_file", "", CFGF_NONE), // Fichier de sortie CFG_INT("aggregate_timeout", DEFAULT_AGGREGATE_TIMEOUT, CFGF_NONE), // Timeout d'agrégation (en millisecondes) CFG_INT("keep_timeout", DEFAULT_KEEP_TIMEOUT, CFGF_NONE), // Temps que l'on conserve les données dans la liste (en millisecondes) CFG_INT("check_interval", DEFAULT_CHECK_INTERVAL, CFGF_NONE), // Temps entre deux vérifications de la liste (en micro-secondes) CFG_END() } ; char *config_file ; // Nom du fichier de configuration int opt ; // Retour de getopt /* L'option -f sert à spécifier un fichier de configuration alternatif, * on regarde en premier si elle est présente */ 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 // Si -f n'est pas présente, on utilise le fichier de config par défaut { config_file = malloc((strlen(DEFAULT_CONFIG_FILE) + 1) * sizeof(char)) ; strcpy(config_file, DEFAULT_CONFIG_FILE) ; } /* Lecture du fichier de configuration */ cfg = cfg_init(opts, CFGF_NONE) ; // Initialisation des options if (cfg_parse(cfg, config_file) == CFG_PARSE_ERROR) { free(config_file) ; return ERR_PARSING_CONFIG_FILE ; } free(config_file) ; /* Lecture des arguments de la ligne de commandes */ optind = 1 ; // On reprend l'analyse des arguments au début while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'f' : // Fichier de configuration // Déjà traité. break ; case 'l' : cfg_setint(cfg, "listening_port", strtol(optarg, NULL, 0)) ; break ; case 'o' : cfg_setstr(cfg, "output_file", optarg) ; break ; case 'a' : cfg_setint(cfg, "aggregate_timeout", strtol(optarg, NULL, 0)) ; break ; case 'k' : cfg_setint(cfg, "keep_timeout", strtol(optarg, NULL, 0)) ; break ; case 'c' : cfg_setint(cfg, "check_interval", strtol(optarg, NULL, 0)) ; break ; default : print_usage(argv[0]) ; return ERR_BAD_USAGE ; } } /* Vérification des arguments */ // output_file // if (cfg_getstr(cfg, "output_file")[0] == '\0') { fprintf(stderr, "Erreur ! Vous devez spécifier un fichier de sortie.\n") ; print_usage(argv[0]) ; return ERR_BAD_USAGE ; } // aggregate_timeout // if (cfg_getint(cfg, "aggregate_timeout") < 0) { #ifdef DEBUG fprintf(stderr, "Attention ! aggregate_timeout n'admet pas de valeur négative : application de la valeur par défaut.\n") ; #endif // DEBUG cfg_setint(cfg, "aggregate_timeout", DEFAULT_AGGREGATE_TIMEOUT) ; } // keep_timeout // if (cfg_getint(cfg, "keep_timeout") < 0) { #ifdef DEBUG fprintf(stderr, "Attention ! keep_timeout n'admet pas de valeur négative : application de la valeur par défaut.\n") ; #endif // DEBUG cfg_setint(cfg, "keep_timeout", DEFAULT_KEEP_TIMEOUT) ; } // check_interval // if (cfg_getint(cfg, "check_interval") < 0) { #ifdef DEBUG fprintf(stderr, "Attention ! check_interval n'admet pas de valeur négative : application de la valeur par défaut.\n") ; #endif // DEBUG cfg_setint(cfg, "check_interval", DEFAULT_CHECK_INTERVAL) ; } #ifdef DEBUG /* Affichage de la configuration */ fprintf(stderr, "Configuration :\n") ; cfg_print(cfg, stderr) ; #endif // DEBUG run = TRUE ; /* 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) ; /* Lecture sur la socket */ 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) ; printf("\n\ *** Message reçu du client ***\n\ \tMAC AP : %s\n\ \tMAC 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, 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) ; got_couple_info(&couples, message) ; } (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 ; } /* 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(). 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 couple_info_ptr = couple_ptr->info ; while (couple_info_ptr != NULL) // On vide la liste des infos { // 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) ; tmp_couple->request_time = message.request_time ; 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 { while (tmp_couple != NULL) // Sinon on cherche si le couple existe déjà dans la liste { if(mac_cmp(message.mobile_mac_addr_bytes, tmp_couple->mobile_mac_addr_bytes) == 1 && message.request_time.tv_usec == tmp_couple->request_time.tv_usec) // Si le couple existe déjà, break ; // 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) ; tmp_couple->request_time = message.request_time ; 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 ; } } } /* 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) ; } /* Affiche le mode d'emploi du programme */ void print_usage(char *prog) { printf("Usage :\n\ \t%s [-f config_file] [-l listening_port] [-a aggregate_timeout] [-k keep_timeout] [-c check_interval] -o output_file\n\ ", prog) ; }