Final assignment for the course "Real Time and Embedded Systems" of THMMY in AUTH university.
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.
 
 
 
 
 

337 lines
8.6 KiB

#include <unistd.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
#include <time.h>
#include <assert.h>
#include "helpers.h"
// Private Functions
char *get_ip_from_sockaddr(address_t addr) {
// Beware: The inet_ntoa() function returns a string in a statically allocated buffer, which
// subsequent calls will overwrite!
return inet_ntoa(addr.sin_addr);
// TODO: maybe change to this instead:
// #define MAKE_FN_NAME(x) void Callback_ ## x (void)
// #define FUNCTION_NAME(signal) MAKE_FN_NAME(signal)
}
// APIs
node_id_t extract_id_from_ip(const ipv4_t ip) {
assert(ip);
const char separator[2] = ".";
node_id_t id = 0;
char *rest, *token, *ip_cp;
ip_cp = malloc(strlen(ip) * sizeof(char));
strcpy(ip_cp, ip);
rest = ip_cp;
token = strtok_r(rest, separator, &rest);
if (!token || atoi(token) != 10) {
return 0;
}
token = strtok_r(rest, separator, &rest);
if (!token || atoi(token) != 0) {
return 0;
}
token = strtok_r(rest, separator, &rest);
if (!token) {
return 0;
}
id = atoi(token) * 100;
token = strtok_r(rest, separator, &rest);
if (!token) {
return 0;
}
id += atoi(token);
free(ip_cp);
return id;
}
void set_timer_and_handler(void (*handler)(int), long int wakeup_time) {
assert(handler);
struct itimerval timer;
struct sigaction signal_action;
// Installs handler as the signal handler for SIGALRM
// Needs to be set every time!
memset(&signal_action, 0, sizeof(signal_action));
signal_action.sa_handler = handler;
if (sigaction(SIGALRM, &signal_action, NULL)) {
perror("Couldn't install function handler for SIGALRM signals.");
exit(EXIT_FAILURE);
}
// Sets a timer to deliver a SIGALRM signal in wakeup_time seconds
timer.it_interval.tv_usec = 0;
timer.it_interval.tv_sec = 0;
timer.it_value.tv_usec = 0;
timer.it_value.tv_sec = wakeup_time;
if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
perror("Couldn't set timer.");
exit(EXIT_FAILURE);
}
}
uint16_t create_message(char *new_message, uint16_t max_message_length, node_handle_t *neighbors,
uint8_t num_neighbors, node_id_t own_id) {
assert(new_message && neighbors);
node_handle_t random_node = neighbors[rand() % (num_neighbors)];
node_id_t peer_id = node_get_id(random_node);
if (!peer_id) {
perror("Couldn't extract peer's ID.");
exit(EXIT_FAILURE);
}
snprintf(new_message, max_message_length, "%04d_%04d_%ld_%s", own_id, peer_id, time(NULL),
"It's amazing... It's fantastic!");
return strlen(new_message);
}
/*
* Function based on this example:
* https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example
*/
int create_socket_and_listen(port_t port, uint8_t backlog_size) {
int in_sock;
address_t own_name;
// Creates the socket
in_sock = socket(PF_INET, SOCK_STREAM, 0);
if (in_sock < 0) {
perror("Couldn't create the socket.");
exit(EXIT_FAILURE);
}
// Gives the socket a name
own_name.sin_family = AF_INET;
own_name.sin_port = htons(port);
own_name.sin_addr.s_addr = htonl(INADDR_ANY);
// Binds own address structure to socket
if (bind(in_sock, (struct sockaddr *) &own_name, sizeof(own_name)) < 0) {
perror("Couldn't bind the address structure to the socket.");
exit(EXIT_FAILURE);
}
if (listen(in_sock, backlog_size) < 0) {
perror("Couldn't listen for connections on the socket.");
exit(EXIT_FAILURE);
}
return in_sock;
}
void send_message(node_handle_t peer, message_handle_t message) {
assert(peer && message);
int out_sock;
address_t peer_address = node_get_addr(peer);
// Creates the socket
out_sock = socket(PF_INET, SOCK_STREAM, 0);
if (out_sock < 0) {
perror("Couldn't create the socket.");
exit(EXIT_FAILURE);
}
// Connects to the peer
if (connect(out_sock, (struct sockaddr *) &(peer_address), sizeof(node_get_addr(peer)))) {
printf("Couldn't connect to peer : %s.\n", get_ip_from_sockaddr(node_get_addr(peer)));
node_add_timestamp(peer, time(NULL), false);
} else {
// Sends data to the peer
int ignored;
write_to_peer(out_sock, message_get(message, &ignored));
node_add_timestamp(peer, time(NULL), true);
message_add_sent_to(message, node_get_id(peer));
}
close(out_sock);
}
node_id_t accept_connection(int sock, address_t *peer_name, fd_set *active_fd_set,
int *listens_on) {
assert(peer_name && active_fd_set && listens_on);
size_t peer_name_size = sizeof((*peer_name));
int comm_socket = accept(sock, (struct sockaddr *) peer_name, &peer_name_size);
if (comm_socket < 0) {
perror("Couldn't accept the connection.");
// TODO maye be remove this?
exit(EXIT_FAILURE);
}
*(listens_on) = comm_socket;
char *node_addr = get_ip_from_sockaddr(*peer_name);
fprintf(stderr, "Connected to host %s, port %hd.\n",
node_addr, ntohs((*peer_name).sin_port));
FD_SET(comm_socket, active_fd_set);
return extract_id_from_ip(node_addr);
}
uint16_t checkAddNode(node_handle_t **neighbors, uint16_t num_neighbors ,
address_t peer_name, node_id_t node_id) {
assert(neighbors);
bool node_exists = false;
for (int i = 0; i < num_neighbors; ++i) {
if (node_get_id((*neighbors)[i]) == node_id) {
continue;
}
}
if (!node_exists) {
node_handle_t *r_neighbors = realloc((*neighbors),
(num_neighbors + 1) * sizeof(node_handle_t));
if (!r_neighbors) {
free(r_neighbors);
perror("Unable to reallocate memory for neighbor IP.");
exit(EXIT_FAILURE);
}
(*neighbors) = r_neighbors;
(*neighbors)[num_neighbors] = node_init(peer_name, node_id);
return num_neighbors + 1;
}
return num_neighbors;
}
void write_to_peer(int file_desc, const char *message) {
assert(message);
int num_bytes = write(file_desc, message, strlen(message) + 1);
if (num_bytes < 0) {
perror("Couldn't write to peer.");
// TODO maye be remove this?
exit(EXIT_FAILURE);
}
}
int read_from_peer(int file_des, uint16_t max_line, cbuf_handle_t *message_buffer,
node_handle_t **neighbors, uint16_t num_neighbors) {
char buffer[max_line];
int num_bytes;
num_bytes = read(file_des, buffer, sizeof(buffer));
if (num_bytes < 0) {
perror("Couldn't read from peer.");
// TODO maye be remove this?
exit(EXIT_FAILURE);
} else if (num_bytes == 0) {
// End-of-file
return -1;
} else {
fprintf(stderr, "Got message: `%s'\n", buffer);
node_id_t received_from = -1;
for (int i = 0; i < num_neighbors; ++i) {
if (node_get_comm_socket((*neighbors)[i])) {
received_from = node_get_id((*neighbors)[i]);
node_set_comm_socket((*neighbors)[i], -1);
break;
}
}
message_handle_t new_message = message_init(buffer, num_bytes, received_from);
circ_buf_put(*(message_buffer), &new_message);
return 0;
}
}
uint16_t fread_neighbors(char *AEM_file, node_handle_t **neighbors, port_t port) {
assert(AEM_file && neighbors);
uint16_t num_neighbors = 0;
// Reads the ARP file checking for connected devices
FILE *aems_file = fopen(AEM_file, "r");
if (!aems_file) {
perror("Neighbors file: Failed to open file containing the AEMs.");
exit(EXIT_FAILURE);
}
// Extracts IP addresses found in the file
char ip_addr[AEM_BUFFER_LEN];
char **neighbors_ips;
while (1 == fscanf(aems_file, AEM_LINE_FORMAT, ip_addr)) {
++num_neighbors;
char **r_neighbors_ips = realloc(neighbors_ips, num_neighbors * sizeof(char *));
if (!r_neighbors_ips) {
free(r_neighbors_ips);
perror("Unable to reallocate memory for neighbor IP.");
exit(EXIT_FAILURE);
}
neighbors_ips = r_neighbors_ips;
neighbors_ips[num_neighbors - 1] = (char *) malloc(AEM_BUFFER_LEN * sizeof(char));
strcpy(neighbors_ips[num_neighbors - 1], ip_addr);
}
// Allocates memory for the new neighbors structs
(*neighbors) = (node_handle_t *) malloc(num_neighbors * sizeof(node_handle_t));
if (!neighbors_ips) {
perror("Unable to allocate memory for nodes.");
exit(EXIT_FAILURE);
}
// Creates the neighbors structs
for (uint8_t i = 0; i < num_neighbors; ++i) {
address_t peer_name;
init_sockaddr(&peer_name, neighbors_ips[i], port);
(*neighbors)[num_neighbors++] = node_init(peer_name,
extract_id_from_ip(get_ip_from_sockaddr(peer_name)));
}
fclose(aems_file);
return num_neighbors;
}
/*
* Function based on this example:
* https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example
*/
void init_sockaddr(address_t *peer_name, const ipv4_t hostname, port_t port) {
assert(peer_name && hostname);
struct hostent *hostinfo;
peer_name->sin_family = AF_INET;
peer_name->sin_port = htons(port);
hostinfo = gethostbyname(hostname);
if (hostinfo == NULL) {
fprintf(stderr, "Unknown host %s.\n", hostname);
exit(EXIT_FAILURE);
}
peer_name->sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0];
}