/* * 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;Explanation * Normal answer: * SimpleResults;OK;Nb_results;Result_1;…;Result_n * Nb_results is the number of results in the answer (number of mobiles). * Result_i follows this format: * Mobile_MAC;X;Y;Z;Area_name * Area_name is the name of the area or room in which the mobile is (may * be empty). * * 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 + 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_simple(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 ; }