owlps/libowlps/libowlps.c

597 lines
14 KiB
C

/*
* This file is part of the Owl Positioning System (OwlPS).
* OwlPS is a project of the University of Franche-Comte
* (Université de Franche-Comté), France.
*
* Copyright © Université de Franche-Comté 2007-2012.
*
* Corresponding author: Matteo Cypriani <mcy@lm7.fr>
*
***********************************************************************
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL:
* http://www.cecill.info
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided
* only with a limited warranty and the software's authors, the holder
* of the economic rights, and the successive licensors have only
* limited liability.
*
* In this respect, the user's attention is drawn to the risks
* associated with loading, using, modifying and/or developing or
* reproducing the software by the user in light of its specific status
* of free software, that may mean that it is complicated to manipulate,
* and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users
* are therefore encouraged to load and test the software's suitability
* as regards their requirements in conditions enabling the security of
* their systems and/or data to be ensured and, more generally, to use
* and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*
***********************************************************************
*
* This is the main source file of libowlps.
*/
#include "owlps.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <arpa/inet.h>
#include <assert.h>
owl_bool owl_run = owl_true ;
/* *** Miscellaneous functions *** */
/*
* Converts a MAC address from bytes to 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!
*/
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 string.
* 'mac_str' must be allocated by the caller.
* 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 owl_true if they are identical, owl_false otherwise.
*/
owl_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 owl_false ;
return owl_true ;
}
/*
* Converts a IEEE 802.11 frequency into a channel number.
* Returns 0 if the frequency does not correspond to an official channel.
*/
uint_fast8_t owl_frequency_to_channel(const uint_fast16_t channel)
{
uint_fast8_t c = 0 ; // Result
switch (channel)
{
case OWL_80211_MHZ_CHANNEL_1 :
c = 1 ;
break ;
case OWL_80211_MHZ_CHANNEL_2 :
c = 2 ;
break ;
case OWL_80211_MHZ_CHANNEL_3 :
c = 3 ;
break ;
case OWL_80211_MHZ_CHANNEL_4 :
c = 4 ;
break ;
case OWL_80211_MHZ_CHANNEL_5 :
c = 5 ;
break ;
case OWL_80211_MHZ_CHANNEL_6 :
c = 6 ;
break ;
case OWL_80211_MHZ_CHANNEL_7 :
c = 7 ;
break ;
case OWL_80211_MHZ_CHANNEL_8 :
c = 8 ;
break ;
case OWL_80211_MHZ_CHANNEL_9 :
c = 9 ;
break ;
case OWL_80211_MHZ_CHANNEL_10 :
c = 10 ;
break ;
case OWL_80211_MHZ_CHANNEL_11 :
c = 11 ;
break ;
case OWL_80211_MHZ_CHANNEL_12 :
c = 12 ;
break ;
case OWL_80211_MHZ_CHANNEL_13 :
c = 13 ;
break ;
case OWL_80211_MHZ_CHANNEL_14 :
c = 14 ;
break ;
}
return c ;
}
/* *** 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 and a non-zero error code
* is returned (if positive, it is the number of non-sleeped 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' at 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'.
*/
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'.
*/
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 ;
}
owl_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 ;
}
owl_bool owl_timestamp_is_null(const owl_timestamp *const d)
{
assert(d) ;
return d->tv_sec == 0 && d->tv_nsec == 0 ;
}
/*
* Converts a owl_timestamp date value into milliseconds.
*/
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 a owl_timestamp date value into a printable string.
* '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) between two dates.
*/
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'.
* Note that it is a delay, not a simple substraction, therefore the
* result is always positive. The result is stored in the 'elapsed'
* argument.
*/
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) ;
}
}
}
/* *** Endianess *** */
/*
* Converts a owl_timestamp from host endianess to network endianess.
*/
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 a owl_timestamp from network endianess to host endianess.
*/
void owl_ntoh_timestamp(owl_timestamp *const d)
{
assert(d) ;
d->tv_sec = ntohl(d->tv_sec) ;
d->tv_nsec = ntohl(d->tv_nsec) ;
}
/*
* Swap bytes composing a float.
* You probably want to use the macros owl_htonf() and owl_ntohf()
* instead of this function.
*/
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 *** */
/*
* Creates a UDP transmission socket and returns its descriptor.
* Parameters:
* - server_address: the server IP address.
* - server_port: the listening port on the server.
* - server_description (in/out): the structure in which the server
* description will be saved.
* - client_description (in/out): the structure in which the client
* description will be saved.
* Returns a negative error code in case of error.
*/
int owl_create_udp_trx_socket(const char *const server_address,
const uint_fast16_t server_port,
struct sockaddr_in *const server_description,
struct sockaddr_in *const client_description)
{
int sockfd ; // Socket descriptor
/* Create the UDP socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0) ;
if (sockfd < 0)
{
perror("UDP socket creation failed") ;
return -OWL_ERR_SOCKET_CREATE ;
}
/* Initialise the client structure */
memset(client_description, 0, sizeof(*client_description)) ;
client_description->sin_family = AF_INET ; // INET socket
client_description->sin_addr.s_addr = htonl(INADDR_ANY) ;
/* Initialise the server structure */
memset(server_description, 0, sizeof(*server_description)) ;
server_description->sin_family = AF_INET ; // INET socket
// Server IP address:
server_description->sin_addr.s_addr = inet_addr(server_address) ;
// Listening port on the server:
server_description->sin_port = htons(server_port) ;
return sockfd ;
}
/*
* Creates a UDP reception socket and returns its descriptor.
* Parameters:
* - port: port on which the socket listens.
* Returns a negative error code in case of error.
*/
int owl_create_udp_listening_socket(const uint_fast16_t port)
{
int sockfd ; // Socket descriptor
struct sockaddr_in server_description ; // Server structure
int ret = 0 ; // Return value
/* Create the UDP socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0) ;
if (sockfd < 0)
{
perror("UDP socket creation failed") ;
return -OWL_ERR_SOCKET_CREATE ;
}
/* Initialise the server structure */
memset(&server_description, 0, sizeof(server_description)) ;
server_description.sin_family = AF_INET ; // INET socket
// All the connections are accepted:
server_description.sin_addr.s_addr = htonl(INADDR_ANY) ;
server_description.sin_port = htons(port) ; // Listening port
/* Port reservation */
ret = bind(sockfd, (struct sockaddr*) &server_description,
sizeof(server_description)) ;
if (ret < 0)
{
perror("Cannot bind the UDP socket") ;
close(sockfd) ;
return -OWL_ERR_SOCKET_CREATE ;
}
return sockfd ;
}
/* *** Signals *** */
/*
* Generic signal handler for 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 = owl_false ;
#ifndef NDEBUG
fprintf(stderr, "\nSignal received: end.\n");
#endif // NDEBUG
fflush(NULL) ;
}
/*
* Generic signal handler for 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*).
*/
void owl_close_fd(void *const fd)
{
if (fd == NULL)
return ;
const int *const file_desc = fd ;
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 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
}