From e77ad8fb0f8f1d4a4a2b610b8fc0b6977b3db7f4 Mon Sep 17 00:00:00 2001 From: Matteo Cypriani Date: Sat, 20 Aug 2011 19:56:17 +0200 Subject: [PATCH] Add owlps-udp-to-http This new module allows to get the results sent by the positioning server with HTTP queries. See the heading comment in owlps-udp-to-http.c. --- Makefile | 16 +- owlps-udp-to-http/Makefile | 110 ++++++++ owlps-udp-to-http/owlps-udp-to-http.c | 385 ++++++++++++++++++++++++++ 3 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 owlps-udp-to-http/Makefile create mode 100644 owlps-udp-to-http/owlps-udp-to-http.c diff --git a/Makefile b/Makefile index 51d916b..cb3fca4 100644 --- a/Makefile +++ b/Makefile @@ -6,16 +6,19 @@ owlps-client \ owlps-listener \ owlps-aggregator \ + owlps-udp-to-http \ owlps-positioning \ install \ install-owlps-client \ install-owlps-listener \ install-owlps-aggregator \ + install-owlps-udp-to-http \ install-owlps-positioning \ uninstall \ uninstall-owlps-client \ uninstall-owlps-listener \ uninstall-owlps-aggregator \ + uninstall-owlps-udp-to-http \ uninstall-owlps-positioning ## Compilation ## @@ -30,7 +33,8 @@ c: \ libowlps-resultreader-udp \ owlps-client \ owlps-listener \ - owlps-aggregator + owlps-aggregator \ + owlps-udp-to-http libowlps: @make -C $@ @@ -44,6 +48,8 @@ owlps-listener: libowlps libowlps-client @make -C $@ owlps-aggregator: libowlps @make -C $@ +owlps-udp-to-http: libowlps libowlps-resultreader-udp + @make -C $@ owlps-positioning: libowlps @make -C $@ @@ -55,6 +61,7 @@ install : \ install-owlps-client \ install-owlps-listener \ install-owlps-aggregator \ + install-owlps-udp-to-http \ install-owlps-positioning install-libowlps: @@ -65,6 +72,8 @@ install-owlps-listener: install-libowlps @make -C $(subst install-,,$@) install install-owlps-aggregator: install-libowlps @make -C $(subst install-,,$@) install +install-owlps-udp-to-http: install-libowlps + @make -C $(subst install-,,$@) install install-owlps-positioning: install-libowlps @make -C $(subst install-,,$@) install @@ -76,6 +85,7 @@ uninstall : \ uninstall-owlps-client \ uninstall-owlps-listener \ uninstall-owlps-aggregator \ + uninstall-owlps-udp-to-http \ uninstall-owlps-positioning uninstall-libowlps: @@ -86,6 +96,8 @@ uninstall-owlps-listener: @make -C $(subst uninstall-,,$@) uninstall uninstall-owlps-aggregator: @make -C $(subst uninstall-,,$@) uninstall +uninstall-owlps-udp-to-http: + @make -C $(subst uninstall-,,$@) uninstall uninstall-owlps-positioning: @make -C $(subst uninstall-,,$@) uninstall @@ -99,6 +111,7 @@ clean : @make -C owlps-client clean @make -C owlps-listener clean @make -C owlps-aggregator clean + @make -C owlps-udp-to-http clean @make -C owlps-positioning clean purge : @@ -108,6 +121,7 @@ purge : @make -C owlps-client purge @make -C owlps-listener purge @make -C owlps-aggregator purge + @make -C owlps-udp-to-http purge @make -C owlps-positioning purge diff --git a/owlps-udp-to-http/Makefile b/owlps-udp-to-http/Makefile new file mode 100644 index 0000000..817f7ae --- /dev/null +++ b/owlps-udp-to-http/Makefile @@ -0,0 +1,110 @@ +# Source version +ifndef OWLPS_VERSION + OWLPS_VERSION := $(shell git describe 2>/dev/null || echo 'UNKNOWN_VERSION') +endif + +# Répertoire d'installation +PREFIX=/usr/local +INSTALL_DIR= $(PREFIX)/bin +INSTALL_LIB= $(PREFIX)/lib +INSTALL_INC= $(PREFIX)/include +INSTALL_MAN= $(PREFIX)/share/man + +# Compilateur +COLORGCC := $(shell which colorgcc >/dev/null 2>&1 ; echo $$?) +ifeq ($(COLORGCC), 0) + CC = colorgcc +endif + +# Commandes d'installation et de désinstallation +RM = rm -f +CP = cp + +# Cible +TARGET = owlps-udp-to-http +HEADER = + +# Flags +LIBOWLPS_DIR = ../libowlps +LIBOWLPSRESULTREADERUDP_DIR = ../libowlps-resultreader-udp +CFLAGS = -O2 -Wall -Wextra -Wstrict-prototypes \ + -I$(LIBOWLPS_DIR) -I$(LIBOWLPSRESULTREADERUDP_DIR) +#CFLAGS += -g -O0 +DEPFLAGS = -MMD +XCFLAGS = $(CFLAGS) $(DEPFLAGS) $(WARN) $(HEADERS) +PICFLAG = -fPIC +OWLPSFLAGS = -D OWLPS_VERSION=\"$(OWLPS_VERSION)\" +OWLPSFLAGS += -D DEBUG +LIBS = -L$(LIBOWLPS_DIR) -lowlps \ + -L$(LIBOWLPSRESULTREADERUDP_DIR) -lowlps-resultreader-udp + +OS := $(shell uname) +ifeq ("$(OS)", "Linux") + LIBS += -lrt +endif + +STATIC_LIBS = + + +## Cibles de compilation standard ## + +.PHONY : all dynamic static install uninstall clean purge help + +dynamic : $(TARGET) +static : $(TARGET).static +all : dynamic static + +# Cancel implicit make rule +%: %.c + +%: %.o + $(CC) $(LDFLAGS) $(STRIPFLAGS) $(XCFLAGS) -o $@ $^ $(LIBS) +%.static: %.o + $(CC) $(STRIPFLAGS) $(XCFLAGS) -o $@ $^ \ + $(LDFLAGS) $(LIBS) $(STATIC_LIBS) -static +%.o: %.c $(HEADER) + $(CC) $(XCFLAGS) $(OWLPSFLAGS) -c $< + + +## Installation / désinstallation ## + +install : $(TARGET) + @$(CP) $(TARGET) $(INSTALL_DIR) + @cd $(INSTALL_DIR) ; chown root:root $(TARGET) ; chmod 755 $(TARGET) + +install-static : $(TARGET).static + @$(CP) $(TARGET).static $(INSTALL_DIR) + @cd $(INSTALL_DIR) ; chown root:root $(TARGET).static ; chmod 755 $(TARGET).static + +uninstall : + @$(RM) $(INSTALL_DIR)/{$(TARGET),$(TARGET).static} + + +## Nettoyage ## + +clean : + @$(RM) *~ *.o *.d + +purge : clean + @$(RM) $(TARGET) $(TARGET).static + + +## Aide ## + +help : + @echo "Bibliothèques nécessaires à la compilation :" + @echo " libowlps1.0 (fournie)" + @echo " libowlps-resultreader-udp1.0 (fournie)" + @echo + @echo "Cibles possibles :" + @echo " $(TARGET) (cible par défaut) : Compile le programme \ +$(TARGET)." + @echo " $(TARGET).static : Compile le programme $(TARGET).static \ +(version sans lien dynamique)." + @echo " install : Installe le programme $(TARGET)." + @echo " uninstall : Désinstalle le programme $(TARGET)." + @echo " clean : Supprime les fichiers temporaires." + @echo " purge : Supprime le résultat de la compilation." + @echo + @echo "Note : l'installation se fait dans l'arborescence $(PREFIX). \ +Modifiez la variable PREFIX du Makefile pour changer ce comportement." diff --git a/owlps-udp-to-http/owlps-udp-to-http.c b/owlps-udp-to-http/owlps-udp-to-http.c new file mode 100644 index 0000000..98cae3a --- /dev/null +++ b/owlps-udp-to-http/owlps-udp-to-http.c @@ -0,0 +1,385 @@ +/* + * This program listens for results sent by OwlPS Positioning on a UDP + * socket, and listens for a client on a TCP socket. The client is + * expected to send an HTTP GET request, with the request string as the + * first variable value (the variable name does not matter). + * For example, a correct request is detected if you load + * http://localhost:8080/?request=ReadSimpleResults + * (assuming the host running this program is localhost). + * + * The UDP listening port is the port on which OwlPS Positioning sends + * result by default. + * The TCP listening port is 8080. + * + * Only the last result read from the positioning server, for each + * mobile, is memorised and provided to the HTTP client. + * + * The only HTTP request currently implemented is "ReadSimpleResults". + * Answer in case of error: + * SimpleResults;NOK + * Normal answer: + * SimpleResults;OK;Nb_results;Result_1;…;Result_n + * Nb_results is the number of results in the answer. + * Result_i follows this format: + * Mobile_MAC;Algorithm_name;X;Y;Z;Area_name + * + * If a unknown request is received, the answer format is: + * UnknownRequest;NOK + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define TCP_PORT 8080 +#define NB_CONNECTIONS 1 +#define CLIENT_MESSAGE_STRLEN 2500 + +#define SIMPLE_RESULTS_ID 1 +#define SIMPLE_RESULTS_REQUEST "ReadSimpleResults" +#define SIMPLE_RESULTS_ANSWER "SimpleResults" +#define CLIENT_REQUEST_STRLEN 21 + +#ifndef OWLPS_VERSION +# define OWLPS_VERSION "unknown version" +#endif // OWLPS_VERSION + +#define ANSWER_HEADER \ + "HTTP/1.1 200 OK\n" \ + "Date: Sat, 01 Jan 0001 00:00:01 GMT\n" \ + "Server: OwlPS UDP-to-HTTP ["OWLPS_VERSION"]\n" \ + "Access-Control-Allow-Origin: *\n" \ + "Connection: close\n" \ + "Content-Type: text/html\n" \ + "\n" + + +typedef struct _results_list +{ + owl_result *result ; + struct _results_list *next ; +} results_list ; + + +void store_result(owl_result *result) ; +void* tcp_server(void *NULL_value) ; +int +extract_request_from_message(char client_request[CLIENT_REQUEST_STRLEN], + char *client_message) ; +void free_results_list(void) ; + + +char *program_name = NULL ; + +results_list *results = NULL ; +unsigned int nb_results = 0 ; +sem_t lock_results ; + + +int main(int argc, char *argv[]) +{ + struct sigaction action ; // Signal handler structure + int ret = 0 ; // Program return value + pthread_t tcp_server_thread ; + owl_result *result ; + int udp_sockfd ; + + program_name = argv[0] ; + owl_run = owl_true ; + + /* Set up signal handlers */ + action.sa_flags = 0 ; + sigemptyset(&action.sa_mask) ; + action.sa_handler = owl_sigint_handler ; + sigaction(SIGINT, &action, NULL) ; + action.sa_handler = owl_sigterm_handler ; + sigaction(SIGTERM, &action, NULL) ; + + /* Set up the semaphore */ + sem_init(&lock_results, 0, 1) ; + + /* Open the UDP socket */ + udp_sockfd = owl_create_udp_listening_socket(OWL_DEFAULT_RESULT_PORT) ; + if (udp_sockfd < 0) + { + ret = 1 ; + goto exit ; + } + + /* Launch the TCP thread */ + ret = pthread_create(&tcp_server_thread, NULL, + &tcp_server, NULL) ; + if (ret) + { + perror("Cannot create the TCP server thread") ; + ret = 1 ; + goto exit ; + } + + /* UDP read loop */ + while (owl_run) + { + result = owl_receive_position(udp_sockfd) ; + if (result == NULL) + { + ret = 1 ; + goto exit ; + } + owl_print_result(result) ; + store_result(result) ; + printf("--------------\n") ; + } + + /* Stop the TCP thread */ + if (pthread_join(tcp_server_thread, NULL)) + perror("Cannot join the TCP server thread") ; + + /* Cleaning */ + exit: + free_results_list() ; + sem_destroy(&lock_results) ; + close(udp_sockfd) ; + + printf("%s: end.\n", program_name) ; + return ret ; +} + + +void store_result(owl_result *new_result) +{ + sem_wait(&lock_results) ; + + // The results' list does not exist yet + if (! results) + { + results = malloc(sizeof(results_list)) ; + ++nb_results ; + results->result = new_result ; + results->next = NULL ; + } + + // The results' list contains at least 1 element + else + { + // Search for an existing result with the same mobile's MAC + results_list *res = results ; + while (res != NULL) + { + char *mac = res->result->mobile_mac_addr ; + if (strncmp(mac, new_result->mobile_mac_addr, + OWL_ETHER_ADDR_STRLEN) == 0) + break ; + res = res->next ; + } + + if (res == NULL) // Not found, adding an element + { + res = malloc(sizeof(results_list)) ; + ++nb_results ; + res->next = results ; + results = res ; + } + else // Found, clearing it + owl_free_result(res->result) ; + + res->result = new_result ; + } + + sem_post(&lock_results) ; +} + + +void* tcp_server(void *NULL_value) +{ + int tcp_sockfd, newsockfd ; + socklen_t client_len ; + struct sockaddr_in server_addr, client_addr ; + ssize_t nbytes ; // recv/send return value + char client_message[CLIENT_MESSAGE_STRLEN] ; + char client_request[CLIENT_REQUEST_STRLEN] ; + int request_id ; + char *answer ; // Answer to send to the client + size_t answer_hdr_len = strlen(ANSWER_HEADER) ; + size_t answer_len ; // Total size of the answer + + tcp_sockfd = socket(AF_INET, SOCK_STREAM, 0) ; + if (tcp_sockfd < 0) + { + perror("Error opening the TCP socket") ; + exit(1) ; + } + pthread_cleanup_push(&owl_close_fd, &tcp_sockfd) ; + + bzero((char *) &server_addr, sizeof(server_addr)) ; + server_addr.sin_family = AF_INET ; + server_addr.sin_addr.s_addr = INADDR_ANY ; + server_addr.sin_port = htons(TCP_PORT) ; + + if (bind(tcp_sockfd, (struct sockaddr *) &server_addr, + sizeof(server_addr)) < 0) + { + perror("Error binding the TCP socket") ; + exit(1) ; + } + + listen(tcp_sockfd, NB_CONNECTIONS) ; + client_len = sizeof(client_addr) ; + + // Prepare the answer, assuming there is only 1 result (an error + // message will also fit) + answer = malloc(answer_hdr_len + OWL_CSV_RESULT_STRLEN) ; + strncpy(answer, ANSWER_HEADER, answer_hdr_len) ; + + while (owl_run) + { + newsockfd = accept(tcp_sockfd, + (struct sockaddr*) &client_addr, &client_len) ; + if (newsockfd < 0) + { + perror("Error accepting a connection on the TCP socket") ; + continue ; + } + + nbytes = + recv(newsockfd, client_message, CLIENT_MESSAGE_STRLEN, 0) ; + if (nbytes < 0) + { + perror("Error reading from the TCP socket") ; + close(newsockfd) ; + continue ; + } + + client_message[nbytes] = '\0' ; + +#ifdef DEBUG + printf("Got a message from the client:\n" + "\"%s\"\n", client_message) ; +#endif // DEBUG + + answer_len = answer_hdr_len ; + request_id = + extract_request_from_message(client_request, client_message) ; + switch (request_id) + { + case SIMPLE_RESULTS_ID: + strncpy(answer + answer_len, SIMPLE_RESULTS_ANSWER, + strlen(SIMPLE_RESULTS_ANSWER)) ; + answer_len += strlen(SIMPLE_RESULTS_ANSWER) ; + + sem_wait(&lock_results) ; + + if (! results) + { + char answer_end[] = ";NOK;NoResult" ; + strncpy(answer + answer_len, answer_end, + strlen(answer_end)) ; + answer_len += strlen(answer_end) ; + } + + else + { + results_list *result ; + char answer_begin[10] ; + size_t answer_begin_len, answer_buf_len ; + + snprintf(answer_begin, 10, ";OK;%u", nb_results) ; + answer_begin_len = strlen(answer_begin) ; + strncpy(answer + answer_len, answer_begin, + answer_begin_len) ; + answer_len += answer_begin_len ; + + answer_buf_len = + answer_len + answer_begin_len + + nb_results * OWL_CSV_RESULT_STRLEN ; + answer = realloc(answer, answer_buf_len) ; + + result = results ; + while (result != NULL) + { + char result_str[OWL_CSV_RESULT_STRLEN] ; + size_t result_len ; + owl_result_to_csv(result_str, result->result) ; + result_len = strlen(result_str) ; + answer[answer_len++] = ';' ; + strncpy(answer + answer_len, result_str, + result_len) ; + answer_len += result_len ; + result = result->next ; + } + } + + sem_post(&lock_results) ; + + break ; + + default: + { + char answer_end[] = "UnknownRequest;NOK" ; + strncpy(answer + answer_hdr_len, answer_end, + strlen(answer_end)) ; + answer_len += strlen(answer_end) ; + } + } + + answer[answer_len] = '\0' ; + printf("Answer to send:\n\"%s\"\n", answer) ; + + nbytes = send(newsockfd, answer, answer_len, 0) ; + if (nbytes < 0) + perror("Error sending answer to the TCP socket") ; + + close(newsockfd) ; + } + + /* Cleaning */ + // Close tcp_sockfd + pthread_cleanup_pop(1) ; + free(answer) ; + + pthread_exit(NULL_value) ; +} + + +int +extract_request_from_message(char client_request[CLIENT_REQUEST_STRLEN], + char *client_message) +{ + char *token ; + + token = strchr(client_message, '=') ; + if (! token) + return 0 ; + ++token ; + token = strsep(&token, " ") ; + + if (strncmp(SIMPLE_RESULTS_REQUEST, token, + strlen(SIMPLE_RESULTS_REQUEST)) == 0) + { + strncpy(client_request, SIMPLE_RESULTS_REQUEST, + CLIENT_REQUEST_STRLEN) ; + return SIMPLE_RESULTS_ID ; + } + + return 0 ; // No known request found +} + + +void free_results_list() +{ + results_list *ptr = results ; + while (ptr != NULL) + { + owl_free_result(ptr->result) ; + ptr = ptr->next ; + } + nb_results = 0 ; +}