/* * This file is part of the Owl Positioning System (OwlPS) project. * It is subject to the copyright notice and license terms in the * COPYRIGHT.t2t file found in the top-level directory of this * distribution and at * https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t * No part of the OwlPS Project, including this file, may be copied, * modified, propagated, or distributed except according to the terms * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be * distributed along with this file, either separately or by replacing * this notice by the COPYRIGHT.t2t file's contents. * *********************************************************************** * * This is the main source file of libowlps. */ #include "owlps.h" #include #include #include #include #include #include #include #include #include #include #include bool owl_run = true ; /* *** Miscellaneous functions *** */ /** * Converts a MAC address from bytes to character string. * The string is allocated in a static buffer, and will be overwritten * each time this function is called. * **This function is not thread-safe!** Please use the reentrant version * owl_mac_bytes_to_string_r() if concurrent calls are possible. * * @returns A pointer to the static buffer containing the character * string. This pointer must *not* be freed manually by the caller. */ const char* owl_mac_bytes_to_string(const uint8_t *const mac_binary) { static char mac_str[OWL_ETHER_ADDR_STRLEN] ; owl_mac_bytes_to_string_r(mac_binary, mac_str) ; return mac_str ; } /** * Converts a MAC address from bytes to character string. * The `mac_str` buffer must be allocated by the caller and its length * must be of at least `OWL_ETHER_ADDR_STRLEN`. * This function is thread-safe. */ void owl_mac_bytes_to_string_r(const uint8_t *const mac_binary, char mac_str[OWL_ETHER_ADDR_STRLEN]) { snprintf(mac_str, OWL_ETHER_ADDR_STRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", mac_binary[0], mac_binary[1], mac_binary[2], mac_binary[3], mac_binary[4], mac_binary[5]) ; } /** * Compares two MAC addresses. * * @returns `true` if the two addresses are identical, `false` otherwise. */ bool owl_mac_equals(const uint8_t *const mac1, const uint8_t *const mac2) { int i ; for (i = ETHER_ADDR_LEN - 1 ; i >= 0 ; --i) if(mac1[i] != mac2[i]) return false ; return true ; } /** * Converts a IEEE 802.11 frequency (in MHz) into a channel number. * * @returns The channel number corresponding to `frequency`. * @returns 0 if the frequency does not correspond to an official * channel. */ uint_fast8_t owl_frequency_to_channel(const uint_fast16_t frequency) { switch (frequency) { case OWL_80211_MHZ_CHANNEL_1 : return 1 ; case OWL_80211_MHZ_CHANNEL_2 : return 2 ; case OWL_80211_MHZ_CHANNEL_3 : return 3 ; case OWL_80211_MHZ_CHANNEL_4 : return 4 ; case OWL_80211_MHZ_CHANNEL_5 : return 5 ; case OWL_80211_MHZ_CHANNEL_6 : return 6 ; case OWL_80211_MHZ_CHANNEL_7 : return 7 ; case OWL_80211_MHZ_CHANNEL_8 : return 8 ; case OWL_80211_MHZ_CHANNEL_9 : return 9 ; case OWL_80211_MHZ_CHANNEL_10 : return 10 ; case OWL_80211_MHZ_CHANNEL_11 : return 11 ; case OWL_80211_MHZ_CHANNEL_12 : return 12 ; case OWL_80211_MHZ_CHANNEL_13 : return 13 ; case OWL_80211_MHZ_CHANNEL_14 : return 14 ; } return 0 ; } /* *** Time *** */ /** * Sleeps for a given amount of milliseconds. * `time_ms` is an unsigned value, so please be careful: passing a * negative value may not do what you think. * In case of error, a message is displayed on the standard error. * * @returns 0 if everything went well. * @returns A non-zero error code in case of error; if positive, it is * the number of non-slept seconds. */ int owl_msleep(uint32_t time_ms) { int ret ; uint_fast32_t seconds, microseconds ; if (! time_ms) return 0 ; seconds = time_ms / 1000 ; microseconds = time_ms % 1000 * 1000 ; if ((ret = sleep(seconds))) { perror("Cannot sleep()") ; return ret ; } if ((ret = usleep(microseconds))) { perror("Cannot usleep()") ; return ret ; } return 0 ; } /** * Sets the `owl_timestamp` `now` to the current time. * * @returns 0 in case of success, non-zero otherwise. */ int owl_timestamp_now(owl_timestamp *const now) { int ret ; #if _POSIX_TIMERS > 0 struct timespec now_ts ; ret = clock_gettime(CLOCK_REALTIME, &now_ts) ; #else // _POSIX_TIMERS struct timeval now_ts ; ret = gettimeofday(&now_ts, NULL) ; #endif // _POSIX_TIMERS if (ret) { perror("Cannot get the current time") ; return ret ; } #if _POSIX_TIMERS > 0 owl_timespec_to_timestamp(&now_ts, now) ; #else // _POSIX_TIMERS owl_timeval_to_timestamp(&now_ts, now) ; #endif // _POSIX_TIMERS return 0 ; } /** * Converts the `struct timespec` `src` into the `owl_timestamp` `dst`. * `src` and `dst` must not be `NULL`. */ void owl_timespec_to_timestamp(const struct timespec *const src, owl_timestamp *const dst) { assert(src) ; assert(dst) ; dst->tv_sec = src->tv_sec ; dst->tv_nsec = src->tv_nsec ; } /** * Converts the `struct timeval` `src` into the `owl_timestamp` `dst`. * `src` and `dst` must not be `NULL`. */ void owl_timeval_to_timestamp(const struct timeval *const src, owl_timestamp *const dst) { assert(src) ; assert(dst) ; dst->tv_sec = src->tv_sec ; dst->tv_nsec = src->tv_usec * 1000u ; } /** * Compares two `owl_timestamp` and returns `true` if they are equal, * `false` otherwise. * `d1` and `d2` must not be `NULL`. */ bool owl_timestamp_equals(const owl_timestamp *const d1, const owl_timestamp *const d2) { assert(d1) ; assert(d2) ; return d1->tv_sec == d2->tv_sec && d1->tv_nsec == d2->tv_nsec ; } /** * Converts the `owl_timestamp` date value `d` into milliseconds. * `d` must not be `NULL`. * * @returns The millisecond value of `d`. */ uint64_t owl_timestamp_to_ms(const owl_timestamp *const d) { assert(d) ; return (uint64_t) d->tv_sec * 1000u + (uint64_t) d->tv_nsec / 1000000lu ; } /** * Converts the `owl_timestamp` date value `src` into a printable string. * `src` must not be `NULL`. * `dst` must be an allocated array of at least `OWL_TIMESTAMP_STRLEN` * characters. */ void owl_timestamp_to_string(const owl_timestamp *const src, char *const dst) { assert(src) ; assert(dst) ; snprintf(dst, OWL_TIMESTAMP_STRLEN, "%"PRIu32".%09"PRIu32, src->tv_sec, src->tv_nsec) ; } /** * Returns the time (in milliseconds) elapsed between two `owl_timestamp` * `d1` and `d2`. `d1` can either be lower (before) or greater (after) * `d2`, the returned value will always be the delay between the older * and the newer time. * `d1` and `d2` must not be NULL. */ uint_fast32_t owl_time_elapsed_ms(const owl_timestamp *const d1, const owl_timestamp *const d2) { owl_timestamp elapsed ; owl_time_elapsed(d1, d2, &elapsed) ; return owl_timestamp_to_ms(&elapsed) ; } /** * Computes the time difference between two `owl_timestamp` `d1` and * `d2`. The result is stored in the `elapsed` parameter. * Note that it is a delay, not a simple substraction, therefore the * result is always positive. * NULL parameters are not accepted. */ void owl_time_elapsed(const owl_timestamp *const d1, const owl_timestamp *const d2, owl_timestamp *const elapsed) { int_fast32_t sec, nsec ; assert(d1) ; assert(d2) ; assert(elapsed) ; sec = (int_fast64_t) d1->tv_sec - d2->tv_sec ; nsec = (int_fast64_t) d1->tv_nsec - d2->tv_nsec ; if (sec == 0) { elapsed->tv_sec = 0 ; elapsed->tv_nsec = abs(nsec) ; } else if (sec > 0) { if (nsec >= 0) { elapsed->tv_sec = sec ; elapsed->tv_nsec = nsec ; } else // nsec < 0 { elapsed->tv_sec = sec - 1 ; elapsed->tv_nsec = nsec + 1000000000ul ; } } else // sec < 0 { if (nsec > 0) { elapsed->tv_sec = abs(sec) - 1 ; elapsed->tv_nsec = 1000000000ul - nsec ; } else // nsec <= 0 { elapsed->tv_sec = abs(sec) ; elapsed->tv_nsec = abs(nsec) ; } } } /* *** Endianness *** */ /** * Converts an `owl_timestamp` from host endianness to network * endianness. * The conversion is done in-place. `d` must not be `NULL`. */ void owl_hton_timestamp(owl_timestamp *const d) { assert(d) ; d->tv_sec = htonl(d->tv_sec) ; d->tv_nsec = htonl(d->tv_nsec) ; } /** * Converts an `owl_timestamp` from network endianness to host * endianness. * The conversion is done in-place. `d` must not be `NULL`. */ void owl_ntoh_timestamp(owl_timestamp *const d) { assert(d) ; d->tv_sec = ntohl(d->tv_sec) ; d->tv_nsec = ntohl(d->tv_nsec) ; } /** * This function swaps the bytes composing a `float`, i.e. it changes * their order. * You probably want to use the owl_htonf() and owl_ntohf() macros * instead of this function. * * @returns The swapped `float`. */ float owl_swap_float(const float f) { float ret ; char *f_bytes = (char*) &f, *ret_bytes = (char*) &ret ; assert(sizeof(float) == 4) ; ret_bytes[0] = f_bytes[3] ; ret_bytes[1] = f_bytes[2] ; ret_bytes[2] = f_bytes[1] ; ret_bytes[3] = f_bytes[0] ; return ret ; } /* *** Network *** */ /** * Opens a UDP transmission socket and returns its descriptor. * Uppon error, a message is displayed and a negative error code is * returned. * * @param[in] server_address The server's IP address. * @param[in] server_port The listening port on the server. * @param[out] server_description The structure in which the server * description will be saved. * * @returns A file descriptor to the opened socket in case of success, * or a negative error code. */ int owl_create_udp_trx_socket(const char *const server_address, const uint_fast16_t server_port, struct sockaddr *const server_description) { char server_port_str[6] ; struct addrinfo gai_hints, *gai_results = NULL, *gai_res = NULL ; int gai_ret ; // Return value of getaddrinfo() int sockfd = -1 ; // Socket descriptor /* Get the server information */ sprintf(server_port_str, "%"PRIuFAST16, server_port) ; memset(&gai_hints, 0, sizeof(struct addrinfo)) ; gai_hints.ai_family = AF_INET ; // IPv4 only gai_hints.ai_socktype = SOCK_DGRAM ; gai_ret = getaddrinfo(server_address, server_port_str, &gai_hints, &gai_results) ; if (gai_ret) { fprintf(stderr, "UDP socket creation failed: getaddrinfo(): %s\n", gai_strerror(gai_ret)) ; return -OWL_ERR_SOCKET_CREATE ; } /* Create the UDP socket: loop until socket() succeeds */ for (gai_res = gai_results ; gai_res != NULL ; gai_res = gai_res->ai_next) { sockfd = socket(gai_res->ai_family, gai_res->ai_socktype, gai_res->ai_protocol) ; if (sockfd != -1) break ; } if (gai_res == NULL) { fprintf(stderr, "UDP socket creation failed: socket().\n") ; return -OWL_ERR_SOCKET_CREATE ; } /* Copy the server description */ memcpy(server_description, gai_res->ai_addr, gai_res->ai_addrlen) ; freeaddrinfo(gai_results) ; return sockfd ; } /** * Opens a UDP reception socket and returns its descriptor. * `port` is the port on which the socket will listen. * * @returns A file descriptor to the opened socket in case of success, * or a negative error code. */ int owl_create_udp_listening_socket(const uint_fast16_t port) { char port_str[6] ; struct addrinfo gai_hints, *gai_results = NULL, *gai_res = NULL ; int gai_ret ; // Return value of getaddrinfo() int sockfd ; // Socket descriptor /* Get the server information */ sprintf(port_str, "%"PRIuFAST16, port) ; memset(&gai_hints, 0, sizeof(struct addrinfo)) ; gai_hints.ai_family = AF_INET ; // IPv4 only gai_hints.ai_socktype = SOCK_DGRAM ; gai_hints.ai_flags = AI_PASSIVE ; gai_ret = getaddrinfo(NULL, port_str, &gai_hints, &gai_results) ; if (gai_ret) { fprintf(stderr, "UDP socket creation failed: getaddrinfo(): %s\n", gai_strerror(gai_ret)) ; return -OWL_ERR_SOCKET_CREATE ; } /* Create the UDP socket: * loop until both socket() and bind() succeed */ for (gai_res = gai_results ; gai_res != NULL ; gai_res = gai_res->ai_next) { sockfd = socket(gai_res->ai_family, gai_res->ai_socktype, gai_res->ai_protocol) ; if (sockfd == -1) continue ; if (! bind(sockfd, gai_res->ai_addr, gai_res->ai_addrlen)) break ; // Success! close(sockfd) ; } if (gai_res == NULL) { fprintf(stderr, "UDP socket creation failed: socket() or bind().\n") ; return -OWL_ERR_SOCKET_CREATE ; } return sockfd ; } /* *** Signals *** */ /** * Generic signal handler for SIGINT. * * @param num The catched signal. This function will exit with * `OWL_ERR_BAD_SIGNAL` if `num != SIGINT`. */ void owl_sigint_handler(const int num) { if (num != SIGINT) { fprintf(stderr, "Error! The SIGINT handler was called but the" " signal is not SIGINT.\n") ; exit(OWL_ERR_BAD_SIGNAL) ; } owl_run = false ; #ifndef NDEBUG fprintf(stderr, "\nSignal received: end.\n"); #endif // NDEBUG fflush(NULL) ; } /** * Generic signal handler for SIGTERM. * * @param num The catched signal. This function will exit with * `OWL_ERR_BAD_SIGNAL` if `num != SIGTERM`. */ void owl_sigterm_handler(const int num) { if (num != SIGTERM) { fprintf(stderr, "Error! The SIGTERM handler was called but the" " signal is not SIGTERM.\n") ; exit(OWL_ERR_BAD_SIGNAL) ; } owl_sigint_handler(SIGINT) ; } /* *** Thread-related functions *** */ /** * Closes the file descriptor `fd`. * `fd` must be passed as an int pointer (`int*`). If `fd` is `NULL`, * or if the pointed value is negative, nothing will be done. * Uppon error, a message is displayed on the standard error. */ void owl_close_fd(void *const fd) { const int *const file_desc = fd; if (file_desc == NULL || *file_desc < 0) return ; if (close(*file_desc) != 0) perror("Error closing file descriptor") ; #ifndef NDEBUG else fprintf(stderr, "File descriptor %d closed successfully.\n", *file_desc) ; #endif // NDEBUG } /** * Closes the stream `file`. * `file` must be passed as a pointer on a pointer of FILE (FILE**). * If `*file` is either `stdout`, `stderr` or `stdin`, it will not be * closed. */ void owl_close_file(void *const file) { if (file == NULL) return ; FILE **stream = file ; if (*stream == stdout || *stream == stderr || *stream == stdin) return ; if (fclose(*stream) != 0) perror("Error closing stream") ; #ifndef NDEBUG else fprintf(stderr, "Stream closed successfully.\n") ; #endif // NDEBUG }