Owl Positioning System: a Wi-Fi-based, infrastructure-centred indoor positioning system. http://owlps.pu-pm.univ-fcomte.fr/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

owlps-udp-to-http.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /*
  2. * This file is part of the Owl Positioning System (OwlPS) project.
  3. * It is subject to the copyright notice and license terms in the
  4. * COPYRIGHT.t2t file found in the top-level directory of this
  5. * distribution and at
  6. * https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
  7. * No part of the OwlPS Project, including this file, may be copied,
  8. * modified, propagated, or distributed except according to the terms
  9. * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
  10. * distributed along with this file, either separately or by replacing
  11. * this notice by the COPYRIGHT.t2t file's contents.
  12. *
  13. ***********************************************************************
  14. *
  15. * This is the main source file of OwlPS UDP-to-HTTP, a program that
  16. * allows a client to retrieve results from OwlPS Positioner through
  17. * HTTP.
  18. */
  19. #include "owlps-udp-to-http.h"
  20. #include <signal.h>
  21. #include <semaphore.h>
  22. #include <pthread.h>
  23. #include <unistd.h>
  24. #include <stdlib.h>
  25. #include <strings.h>
  26. #include <string.h>
  27. #include <netdb.h>
  28. #include <assert.h>
  29. /* Options */
  30. // TODO: the size of this structure could be reduced by reordering its fields.
  31. struct {
  32. bool verbose ;
  33. // 7 bytes alignment
  34. uint_fast16_t result_port ;
  35. uint_fast16_t http_port ;
  36. int nb_connections ;
  37. // 4 bytes alignment
  38. } options = {
  39. true, // verbose
  40. OWL_DEFAULT_RESULT_PORT, // result_port
  41. DEFAULT_HTTP_PORT, // http_port
  42. DEFAULT_NB_CONNECTIONS // nb_connections
  43. } ;
  44. char *program_name = NULL ;
  45. results_list *results = NULL ;
  46. unsigned int nb_results = 0 ;
  47. sem_t lock_results ;
  48. int udp_sockfd = -1 ;
  49. int tcp_sockfd = -1 ;
  50. char *answer = NULL ; // Answer to send to the client
  51. size_t answer_strlen = 0 ; // Total size of the answer string
  52. size_t answer_buflen = 0 ; // Size of the answer allocated buffer
  53. int main(int argc, char *argv[])
  54. {
  55. struct sigaction action ; // Signal handler structure
  56. int ret = 0 ; // Program return value
  57. pthread_t tcp_server_thread ;
  58. owl_run = true ;
  59. program_name = argv[0] ;
  60. parse_command_line(argc, argv) ;
  61. /* Set up signal handlers */
  62. action.sa_flags = 0 ;
  63. sigemptyset(&action.sa_mask) ;
  64. action.sa_handler = owl_sigint_handler ;
  65. sigaction(SIGINT, &action, NULL) ;
  66. action.sa_handler = owl_sigterm_handler ;
  67. sigaction(SIGTERM, &action, NULL) ;
  68. /* Set up the semaphore */
  69. sem_init(&lock_results, 0, 1) ;
  70. /* Prepare the TCP socket */
  71. ret = init_tcp_socket() ;
  72. if (ret)
  73. goto exit ;
  74. /* Launch the TCP thread */
  75. ret = pthread_create(&tcp_server_thread, NULL,
  76. &tcp_server, NULL) ;
  77. if (ret)
  78. {
  79. perror("Cannot create the TCP server thread") ;
  80. ret = OWL_ERR_THREAD_CREATE ;
  81. goto exit ;
  82. }
  83. /* Main loop */
  84. ret = receive_udp() ;
  85. /* Stop the TCP thread */
  86. // We must cancel the thread because it can be blocked on the
  87. // recv() call:
  88. if (pthread_cancel(tcp_server_thread))
  89. perror("Cannot cancel the TCP server thread") ;
  90. if (pthread_join(tcp_server_thread, NULL))
  91. perror("Cannot join the TCP server thread") ;
  92. exit:
  93. /* Close sockets */
  94. if (tcp_sockfd >= 0)
  95. if (close(tcp_sockfd))
  96. perror("Error closing the TCP socket") ;
  97. if (udp_sockfd >= 0)
  98. if (close(udp_sockfd))
  99. perror("Error closing the UDP socket") ;
  100. /* Last cleaning */
  101. free(answer) ;
  102. free_results_list() ;
  103. sem_destroy(&lock_results) ;
  104. if (options.verbose)
  105. fprintf(stderr, "%s: exiting.\n", program_name) ;
  106. return ret ;
  107. }
  108. void parse_command_line(const int argc, char *const *argv)
  109. {
  110. parse_options(argc, argv) ;
  111. check_configuration() ;
  112. if (options.verbose)
  113. print_configuration() ;
  114. }
  115. void parse_options(const int argc, char *const *argv)
  116. {
  117. int opt ;
  118. while ((opt = getopt(argc, argv, OPTIONS)) != -1)
  119. {
  120. switch (opt)
  121. {
  122. case 'h' :
  123. print_usage() ;
  124. exit(0) ;
  125. case 'l' :
  126. options.result_port = strtoul(optarg, NULL, 10) ;
  127. break ;
  128. case 't' :
  129. options.http_port = strtoul(optarg, NULL, 10) ;
  130. break ;
  131. case 'q' :
  132. options.verbose = false ;
  133. break ;
  134. case 'v' :
  135. options.verbose = true ;
  136. break ;
  137. case 'V' :
  138. print_version() ;
  139. exit(0) ;
  140. default :
  141. print_usage() ;
  142. exit(OWL_ERR_BAD_USAGE) ;
  143. }
  144. }
  145. }
  146. void check_configuration()
  147. {
  148. if (options.result_port > 65535)
  149. {
  150. fprintf(stderr, "Warning! result_port too high: using default"
  151. " value (%d).\n", OWL_DEFAULT_RESULT_PORT) ;
  152. options.result_port = OWL_DEFAULT_RESULT_PORT ;
  153. }
  154. if (options.http_port > 65535)
  155. {
  156. fprintf(stderr, "Warning! http_port too high: using default"
  157. " value (%d).\n", DEFAULT_HTTP_PORT) ;
  158. options.http_port = DEFAULT_HTTP_PORT ;
  159. }
  160. }
  161. void print_configuration()
  162. {
  163. fprintf(stderr, "Options:\n"
  164. "\tVerbose: %s\n"
  165. "\tResult port: %"PRIuFAST16"\n"
  166. "\tHTTP port: %"PRIuFAST16"\n"
  167. ,
  168. OWL_BOOL_TO_STRING(options.verbose),
  169. options.result_port,
  170. options.http_port
  171. ) ;
  172. }
  173. /*
  174. * Opens the UDP socket and reads from it the results sent by OwlPS
  175. * Positioning.
  176. * Returns a non-zero value in case of error.
  177. */
  178. int receive_udp()
  179. {
  180. owl_result *result ;
  181. /* Open the UDP socket */
  182. udp_sockfd = owl_create_udp_listening_socket(options.result_port) ;
  183. if (udp_sockfd < 0)
  184. return OWL_ERR_SOCKET_CREATE ;
  185. /* UDP read loop */
  186. while (owl_run)
  187. {
  188. result = owl_receive_position(udp_sockfd) ;
  189. if (result == NULL)
  190. return OWL_ERR_SOCKET_RECV ;
  191. owl_print_result(result) ;
  192. store_result(result) ;
  193. printf("--------------\n") ;
  194. }
  195. return 0 ;
  196. }
  197. /*
  198. * Adds a new owl_result to the results' list.
  199. */
  200. void store_result(owl_result *const new_result)
  201. {
  202. sem_wait(&lock_results) ;
  203. // The results' list does not exist yet
  204. if (! results)
  205. {
  206. results = malloc(sizeof(results_list)) ;
  207. if (! results)
  208. {
  209. perror("Cannot allocate memory") ;
  210. owl_run = false ;
  211. goto end ;
  212. }
  213. ++nb_results ;
  214. results->result = new_result ;
  215. results->next = NULL ;
  216. }
  217. // The results' list contains at least 1 element
  218. else
  219. {
  220. // Search for an existing result with the same mobile's MAC
  221. results_list *res = results ;
  222. while (res != NULL)
  223. {
  224. char *mac = res->result->mobile_mac_addr ;
  225. if (strncmp(mac, new_result->mobile_mac_addr,
  226. OWL_ETHER_ADDR_STRLEN) == 0)
  227. break ;
  228. res = res->next ;
  229. }
  230. if (res == NULL) // Not found, adding an element
  231. {
  232. res = malloc(sizeof(results_list)) ;
  233. if (! res)
  234. {
  235. perror("Cannot allocate memory") ;
  236. owl_run = false ;
  237. goto end ;
  238. }
  239. ++nb_results ;
  240. res->next = results ;
  241. results = res ;
  242. }
  243. else // Found, clearing it
  244. owl_free_result(res->result) ;
  245. res->result = new_result ;
  246. }
  247. end:
  248. sem_post(&lock_results) ;
  249. }
  250. /*
  251. * Opens the TCP socket.
  252. * Returns a non-zero value in case of error.
  253. */
  254. int init_tcp_socket()
  255. {
  256. char http_port_str[6] ;
  257. struct addrinfo
  258. gai_hints,
  259. *gai_results = NULL,
  260. *gai_res = NULL ;
  261. int gai_ret ; // Return value of getaddrinfo()
  262. /* Get the server information */
  263. sprintf(http_port_str, "%"PRIuFAST16, options.http_port) ;
  264. memset(&gai_hints, 0, sizeof(struct addrinfo)) ;
  265. gai_hints.ai_family = AF_UNSPEC ; // IPv4 or IPv6
  266. gai_hints.ai_socktype = SOCK_STREAM ;
  267. gai_hints.ai_flags = AI_PASSIVE ;
  268. gai_ret = getaddrinfo(NULL, http_port_str, &gai_hints, &gai_results) ;
  269. if (gai_ret)
  270. {
  271. fprintf(stderr, "TCP socket creation failed: getaddrinfo(): %s\n",
  272. gai_strerror(gai_ret)) ;
  273. return OWL_ERR_SOCKET_CREATE ;
  274. }
  275. /* Create the TCP socket:
  276. * loop until both socket() and bind() succeed */
  277. for (gai_res = gai_results ; gai_res != NULL ;
  278. gai_res = gai_res->ai_next)
  279. {
  280. tcp_sockfd = socket(gai_res->ai_family, gai_res->ai_socktype,
  281. gai_res->ai_protocol) ;
  282. if (tcp_sockfd == -1)
  283. continue ;
  284. if (! bind(tcp_sockfd, gai_res->ai_addr, gai_res->ai_addrlen))
  285. break ; // Success!
  286. close(tcp_sockfd) ;
  287. tcp_sockfd = -1 ;
  288. }
  289. if (gai_res == NULL)
  290. {
  291. fprintf(stderr,
  292. "TCP socket creation failed: socket() or bind().\n") ;
  293. return OWL_ERR_SOCKET_CREATE ;
  294. }
  295. return 0 ;
  296. }
  297. /*
  298. * Waits for requests from HTTP clients.
  299. */
  300. void* tcp_server(void *const NULL_value)
  301. {
  302. int newsockfd ;
  303. ssize_t nbytes ; // recv/send return value
  304. char client_message[CLIENT_MESSAGE_STRLEN] ;
  305. char client_request[CLIENT_REQUEST_STRLEN] ;
  306. int request_id ;
  307. listen(tcp_sockfd, options.nb_connections) ;
  308. // Prepare the answer, assuming there is only 1 full result (an error
  309. // message will also fit)
  310. answer_buflen = ANSWER_HDR_STRLEN + OWL_CSV_RESULT_STRLEN ;
  311. answer = malloc(answer_buflen) ;
  312. if (! answer_buflen)
  313. {
  314. perror("Cannot allocate memory") ;
  315. owl_run = false ;
  316. return NULL ;
  317. }
  318. strncpy(answer, ANSWER_HDR, ANSWER_HDR_STRLEN) ;
  319. while (owl_run)
  320. {
  321. newsockfd = accept(tcp_sockfd, NULL, NULL) ;
  322. if (newsockfd < 0)
  323. {
  324. perror("Error accepting a connection on the TCP socket") ;
  325. continue ;
  326. }
  327. nbytes =
  328. recv(newsockfd, client_message, CLIENT_MESSAGE_STRLEN, 0) ;
  329. if (nbytes < 0)
  330. {
  331. perror("Error reading from the TCP socket") ;
  332. close(newsockfd) ;
  333. continue ;
  334. }
  335. client_message[nbytes] = '\0' ;
  336. if (options.verbose)
  337. printf("Got a message from the client:\n"
  338. "\"%s\"\n", client_message) ;
  339. request_id =
  340. extract_request_from_message(client_request, client_message) ;
  341. prepare_answer(request_id) ;
  342. if (options.verbose)
  343. printf("Answer to send:\n\"%s\"\n", answer) ;
  344. /* Send the answer */
  345. nbytes = send(newsockfd, answer, answer_strlen, 0) ;
  346. if (nbytes < 0)
  347. perror("Error sending answer to the TCP socket") ;
  348. close(newsockfd) ;
  349. }
  350. pthread_exit(NULL_value) ;
  351. }
  352. /*
  353. * Reads a message and search for a request in it.
  354. * Returns the identifier of a request if found, or 0 if not found.
  355. * The text of the request is stored in 'client_request'.
  356. */
  357. int
  358. extract_request_from_message(char client_request[CLIENT_REQUEST_STRLEN],
  359. const char *const client_message)
  360. {
  361. char *token ;
  362. token = strchr(client_message, '=') ;
  363. if (! token)
  364. return 0 ;
  365. ++token ;
  366. token = strsep(&token, " ") ;
  367. if (strncmp(SIMPLE_RESULTS_REQUEST, token,
  368. strlen(SIMPLE_RESULTS_REQUEST)) == 0)
  369. {
  370. strncpy(client_request, SIMPLE_RESULTS_REQUEST,
  371. CLIENT_REQUEST_STRLEN) ;
  372. return SIMPLE_RESULTS_ID ;
  373. }
  374. if (strncmp(RESULTS_REQUEST, token,
  375. strlen(RESULTS_REQUEST)) == 0)
  376. {
  377. strncpy(client_request, RESULTS_REQUEST,
  378. CLIENT_REQUEST_STRLEN) ;
  379. return RESULTS_ID ;
  380. }
  381. return 0 ; // No known request found
  382. }
  383. /*
  384. * Prepare the answer string to send to the TCP client.
  385. */
  386. void prepare_answer(const int request_id)
  387. {
  388. // Reset the answer's length:
  389. answer_strlen = ANSWER_HDR_STRLEN ;
  390. switch (request_id)
  391. {
  392. case RESULTS_ID:
  393. strncpy(answer + answer_strlen, RESULTS_ANSWER,
  394. strlen(RESULTS_ANSWER)) ;
  395. answer_strlen += strlen(RESULTS_ANSWER) ;
  396. sem_wait(&lock_results) ;
  397. if (! results)
  398. {
  399. char answer_end[] = ";NOK;NoResults" ;
  400. strncpy(answer + answer_strlen, answer_end,
  401. strlen(answer_end)) ;
  402. answer_strlen += strlen(answer_end) ;
  403. }
  404. else
  405. {
  406. results_list *result ;
  407. char answer_begin[10] ;
  408. size_t answer_begin_len ;
  409. snprintf(answer_begin, 10, ";OK;%u", nb_results) ;
  410. answer_begin_len = strlen(answer_begin) ;
  411. strncpy(answer + answer_strlen, answer_begin,
  412. answer_begin_len) ;
  413. answer_strlen += answer_begin_len ;
  414. realloc_answer(answer_strlen +
  415. nb_results * OWL_CSV_RESULT_STRLEN) ;
  416. result = results ;
  417. while (result != NULL)
  418. {
  419. char result_str[OWL_CSV_RESULT_STRLEN] ;
  420. size_t result_len ;
  421. owl_result_to_csv(result_str, result->result) ;
  422. result_len = strlen(result_str) ;
  423. answer[answer_strlen++] = ';' ;
  424. assert(answer_strlen<answer_buflen) ;
  425. strncpy(answer + answer_strlen, result_str,
  426. result_len) ;
  427. answer_strlen += result_len ;
  428. result = result->next ;
  429. }
  430. }
  431. sem_post(&lock_results) ;
  432. break ;
  433. case SIMPLE_RESULTS_ID:
  434. strncpy(answer + answer_strlen, SIMPLE_RESULTS_ANSWER,
  435. strlen(SIMPLE_RESULTS_ANSWER)) ;
  436. answer_strlen += strlen(SIMPLE_RESULTS_ANSWER) ;
  437. sem_wait(&lock_results) ;
  438. if (! results)
  439. {
  440. char answer_end[] = ";NOK;NoResults" ;
  441. strncpy(answer + answer_strlen, answer_end,
  442. strlen(answer_end)) ;
  443. answer_strlen += strlen(answer_end) ;
  444. }
  445. else
  446. {
  447. results_list *result ;
  448. char answer_begin[10] ;
  449. size_t answer_begin_len ;
  450. snprintf(answer_begin, 10, ";OK;%u", nb_results) ;
  451. answer_begin_len = strlen(answer_begin) ;
  452. strncpy(answer + answer_strlen, answer_begin,
  453. answer_begin_len) ;
  454. answer_strlen += answer_begin_len ;
  455. realloc_answer(answer_strlen +
  456. nb_results * OWL_CSV_RESULT_SIMPLE_STRLEN) ;
  457. result = results ;
  458. while (result != NULL)
  459. {
  460. char result_str[OWL_CSV_RESULT_SIMPLE_STRLEN] ;
  461. size_t result_len ;
  462. owl_result_to_csv_simple(result_str, result->result) ;
  463. result_len = strlen(result_str) ;
  464. answer[answer_strlen++] = ';' ;
  465. strncpy(answer + answer_strlen, result_str,
  466. result_len) ;
  467. answer_strlen += result_len ;
  468. result = result->next ;
  469. }
  470. }
  471. sem_post(&lock_results) ;
  472. break ;
  473. default:
  474. {
  475. char answer_end[] = "UnknownRequest;NOK" ;
  476. strncpy(answer + ANSWER_HDR_STRLEN, answer_end,
  477. strlen(answer_end)) ;
  478. answer_strlen += strlen(answer_end) ;
  479. }
  480. }
  481. answer[answer_strlen] = '\0' ;
  482. }
  483. /*
  484. * Realloc the answer buffer to the if needed: grows it if new_size is
  485. * greater than the current size, shrink it if the current size is
  486. * greater than the double of new_size.
  487. */
  488. void realloc_answer(const size_t new_size)
  489. {
  490. if (new_size > answer_buflen)
  491. answer_buflen = new_size ;
  492. else if (answer_buflen / 2 >= new_size)
  493. answer_buflen /= 2 ;
  494. else
  495. return ;
  496. answer = realloc(answer, answer_buflen) ;
  497. if (! answer)
  498. abort() ;
  499. }
  500. /*
  501. * Frees the memory allocated for the results' list.
  502. */
  503. void free_results_list()
  504. {
  505. results_list *tmp_res ;
  506. while (results != NULL)
  507. {
  508. owl_free_result(results->result) ;
  509. tmp_res = results ;
  510. results = results->next ;
  511. free(tmp_res) ;
  512. }
  513. nb_results = 0 ;
  514. }
  515. /*
  516. * Prints the usage message on the standard output.
  517. *
  518. * /!\ Don't forget to update the documentation when modifying something
  519. * here!
  520. */
  521. void print_usage()
  522. {
  523. printf("Usage:\n"
  524. "\t%s"
  525. " [-v | -q]"
  526. " [-l result_port]"
  527. " [-t http_port]\n"
  528. "Options:\n"
  529. "\t-h\t\tPrint this help.\n"
  530. "\t-V\t\tPrint version information.\n"
  531. "\t-v\t\tTurn on verbose mode (default).\n"
  532. "\t-q\t\tDo not print informational messages.\n"
  533. "\t-l result_port\tPort on which the results are received"
  534. " (default: %d).\n"
  535. "\t-t http_port\tPort on which the HTTP server listens"
  536. " (default: %d).\n"
  537. ,
  538. program_name,
  539. OWL_DEFAULT_RESULT_PORT,
  540. DEFAULT_HTTP_PORT
  541. ) ;
  542. }
  543. void print_version()
  544. {
  545. printf("This is OwlPS UDP-to-HTTP, part of the Owl Positioning System"
  546. " project.\n"
  547. "Version: %s.\n",
  548. #ifdef OWLPS_VERSION
  549. OWLPS_VERSION
  550. #else // OWLPS_VERSION
  551. "unknown version"
  552. #endif // OWLPS_VERSION
  553. ) ;
  554. }