owlps/infrastructure-centred/owlps-aggregator/owlps-aggregatord.c

480 lines
15 KiB
C

/*
* 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(&current_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) ;
}