More extraction of functionality

Extracting server and tty functions into separate headers.
Thread functions for new connections, tty data and client data
are also extracted and used as separate functional blocks.
This commit is contained in:
Igor Socec 2016-11-19 00:12:08 +01:00
parent 2c26ef201a
commit c17e696566
11 changed files with 534 additions and 393 deletions

View file

@ -11,8 +11,6 @@
#define NAME "moxerver"
#define SERVER_WAIT_TIMEOUT 2 /* seconds for select() timeout in server loop */
/* Prints help message. */
static void usage() {
//TODO maybe some styling should be done
@ -44,12 +42,6 @@ void quit_handler(int signum) {
cleanup(0);
}
/* Converts from time in seconds from Epoch to conveniently formatted string. */
int time2string(time_t time, char* timestamp) {
strftime(timestamp, TIMESTAMP_LEN, TIMESTAMP_FORMAT, localtime(&time));
return 0;
}
/* Parse handler function, used to configure serial port */
int parse_handler(void *user, const char *section, const char *name, const char *value) {
@ -71,18 +63,241 @@ int parse_handler(void *user, const char *section, const char *name, const char
return 1;
}
void time2string(time_t time, char* timestamp)
{
strftime(timestamp, TIMESTAMP_LEN, TIMESTAMP_FORMAT, localtime(&time));
}
void* thread_new_client_connection(void *args)
{
char msg[DATABUF_LEN];
char timestamp[TIMESTAMP_LEN];
client_t temp_client;
/* get resources from args */
resources_t r = *((resources_t*) args);
/* accept new connection request */
if (server_accept(r.server, &temp_client) != 0)
{
return (void *) -1;
}
/* if no client is connected then make the new client available for connection */
if (r.client->socket == -1)
{
memcpy(r.new_client, &temp_client, sizeof(client_t));
return (void *) 0;
}
/* if there is already a new client being handled then reject this client request */
if (r.new_client->socket != -1)
{
sprintf(msg, "\nToo many connection requests, please try later.\n");
send(temp_client.socket, msg, strlen(msg), 0);
client_close(&temp_client);
time2string(time(NULL), timestamp);
fprintf(stderr, "[%s] rejected new client request %s @ %s\n", __func__,
temp_client.ip_string, timestamp);
return (void *) 0;
}
/* make the new client available for connection */
memcpy(r.new_client, &temp_client, sizeof(client_t));
/* inform the new client that the port is already in use */
time2string(r.client->last_active, timestamp);
sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n",
r.server->port, r.client->username, timestamp);
send(r.new_client->socket, msg, strlen(msg), 0);
/* ask the new client if the currently connected client should be dropped */
sprintf(msg, "\nDo you want to drop the current user?\n"
"If yes then please type YES DROP (in uppercase):\n");
send(r.new_client->socket, msg, strlen(msg), 0);
/* wait for client input */
client_wait_line(r.new_client);
/* check client confirmation */
if (strncmp(r.new_client->data, "YES DROP", 8) == 0)
{
/* drop the connected client */
client_close(r.client);
fprintf(stderr, "[%s] dropped client %s @ %s\n", __func__,
r.client->ip_string, timestamp);
}
else
{
/* reject this client request */
client_close(r.new_client);
time2string(time(NULL), timestamp);
fprintf(stderr, "[%s] rejected new client request %s @ %s\n", __func__,
temp_client.ip_string, timestamp);
return (void *) 1;
}
return (void *) 0;
}
void* thread_tty_data(void *args)
{
struct timeval tv;
ssize_t br = 0;
int ret;
fd_set read_fds;
/* get resources from args */
resources_t r = *((resources_t*) args);
fprintf(stderr, "[%s] tty thread started with passed argument: %s\n",
NAME, r.tty_dev->path);
while (1)
{
/* setup parameters for select() */
tv.tv_sec = TTY_WAIT_TIMEOUT;
tv.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(r.tty_dev->fd, &read_fds);
/* wait with select() */
ret = select(r.tty_dev->fd + 1, &read_fds, NULL, NULL, &tv);
if (ret > 0 && FD_ISSET(r.tty_dev->fd, &read_fds))
{
br = tty_read(r.tty_dev);
client_write(r.client, r.tty_dev->data, br);
}
else
{
}
if (debug_messages)
{
fprintf(stderr, "[%s] tty thread alive\n", NAME);
}
}
fprintf(stderr, "[%s] tty thread stoped\n", NAME);
return (void*) 0;
}
void* thread_client_data(void *args)
{
int ret;
fd_set read_fds;
int fdmax;
struct timeval tv;
pthread_t new_client_thread;
/* loop with timeouts waiting for client connection requests and data */
while (1) {
/* check if new client is available for connection */
if ( (client.socket == -1) && (new_client.socket != -1) ) {
/* copy new client information */
memcpy(&client, &new_client, sizeof(client_t));
new_client.socket = -1;
fprintf(stderr, "[%s] client %s connected\n", NAME, client.ip_string);
/* ask client to provide a username before going to "character" mode */
if (client_ask_username(&client) != 0) {
/* close client if not able to provide a username */
client_close(&client);
continue;
}
/* put client in "character" mode */
char msg[TELNET_MSG_SIZE_CHARMODE];
telnet_message_set_character_mode(msg);
client_write(&client, msg, TELNET_MSG_SIZE_CHARMODE);
}
/* setup parameters for select() */
tv.tv_sec = SERVER_WAIT_TIMEOUT;
tv.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(server.socket, &read_fds);
if (client.socket != -1) {
FD_SET(client.socket, &read_fds); /* wait for client if connected */
}
fdmax = (server.socket > client.socket) ? server.socket : client.socket;
/* wait with select() */
ret = select(fdmax+1, &read_fds, NULL, NULL, &tv);
if (ret == -1) {
//TODO do we really break here and stop server when select returns an error?
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
break;
}
if (ret > 0) {
/* check server status */
if (FD_ISSET(server.socket, &read_fds)) {
fprintf(stderr, "[%s] received client connection request\n", NAME);
/* handle new client connection request in a separate thread */
resources_t r = {&server, &client, &new_client, &tty_dev};
if (pthread_create(&new_client_thread, NULL, thread_new_client_connection, &r) != 0) {
/* print error but continue waiting for connection request */
fprintf(stderr, "[%s] problem with handling client connection request\n", NAME);
continue;
}
}
/* check client status if connected */
if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) {
/* read client data */
ret = client_read(&client);
/* check if client disconnected or other errors occurred */
if (ret == -ENODATA) {
fprintf(stderr, "[%s] client %s disconnected\n", NAME, client.ip_string);
/* close client connection and continue waiting for new clients */
client_close(&client);
continue;
}
if ( ret < 0) {
/* print error but continue waiting for new data */
fprintf(stderr, "[%s] problem reading client\n", NAME);
continue;
}
/* pass received data to tty device */
if (tty_dev.fd != -1) {
tty_write(&tty_dev, client.data, ret);
}
}
}
if (ret == 0) {
/* do something with inactive client */
if (client.socket != -1) {
//TODO we could drop client if inactive for some time
time_t current_time = time(NULL);
if (debug_messages) {
fprintf(stderr, "[%s] client last active %u seconds ago\n", NAME,
(unsigned int) (current_time - client.last_active));
}
}
/* do something while listening for client connections */
else {
if (debug_messages) {
fprintf(stderr, "[%s] listening for client connection\n", NAME);
}
}
}
} /* END while() loop */
return (void*) 0;
}
/* MoxaNix main program loop. */
int main(int argc, char *argv[]) {
int ret;
unsigned int tcp_port = -1;
int def_conf = 0; // is default config used or from .cfg file
fd_set read_fds;
int fdmax;
struct timeval tv;
pthread_t tty_thread, new_client_thread;
pthread_t tty_thread;
/* zero init tty_dev */
if (cfsetispeed(&(tty_dev.ttyset), B0) < 0 ||
@ -170,103 +385,15 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "[%s] TCP port: %d, TTY device path: %s\n", NAME, tcp_port, tty_dev.path);
/* start thread that handles tty device */
ret = pthread_create(&tty_thread, NULL, tty_thread_func, &tty_dev);
resources_t r = {&server, &client, &new_client, &tty_dev};
ret = pthread_create(&tty_thread, NULL, thread_tty_data, &r);
if (ret) {
fprintf(stderr, "[%s] error starting serial monitor thread"
", pthread_create returned %d\n", NAME, ret);
return -1;
}
/* loop with timeouts waiting for client connection requests and data */
while (1) {
/* check if new client is availabe for connection */
if ( (client.socket == -1) && (new_client.socket != -1) ) {
/* copy new client information */
memcpy(&client, &new_client, sizeof(client_t));
new_client.socket = -1;
fprintf(stderr, "[%s] client %s connected\n", NAME, client.ip_string);
/* ask client to provide a username before going to "character" mode */
if (client_ask_username(&client) != 0) {
/* close client if not able to provide a username */
client_close(&client);
continue;
}
/* put client in "character" mode */
char msg[TELNET_MSG_SIZE_CHARMODE];
telnet_message_set_character_mode(msg);
client_write(&client, msg, TELNET_MSG_SIZE_CHARMODE);
}
/* setup parameters for select() */
tv.tv_sec = SERVER_WAIT_TIMEOUT;
tv.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(server.socket, &read_fds);
if (client.socket != -1) {
FD_SET(client.socket, &read_fds); /* wait for client if connected */
}
fdmax = (server.socket > client.socket) ? server.socket : client.socket;
/* wait with select() */
ret = select(fdmax+1, &read_fds, NULL, NULL, &tv);
if (ret == -1) {
//TODO do we really break here and stop server when select returns an error?
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
break;
}
if (ret > 0) {
/* check server status */
if (FD_ISSET(server.socket, &read_fds)) {
fprintf(stderr, "[%s] received client connection request\n", NAME);
/* handle new client connection request in a separate thread */
if (pthread_create(&new_client_thread, NULL, server_new_client_thread, NULL) != 0) {
/* print error but continue waiting for connection request */
fprintf(stderr, "[%s] problem with handling client connection request\n", NAME);
continue;
}
}
/* check client status if connected */
if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) {
/* read client data */
ret = client_read(&client);
/* check if client disconnected or other errors occurred */
if (ret == -ENODATA) {
fprintf(stderr, "[%s] client %s disconnected\n", NAME, client.ip_string);
/* close client connection and continue waiting for new clients */
client_close(&client);
continue;
}
if ( ret < 0) {
/* print error but continue waiting for new data */
fprintf(stderr, "[%s] problem reading client\n", NAME);
continue;
}
/* pass received data to tty device */
if (tty_dev.fd != -1) {
tty_write(&tty_dev, client.data, ret);
}
}
}
if (ret == 0) {
/* do something with inactive client */
if (client.socket != -1) {
//TODO we could drop client if inactive for some time
time_t current_time = time(NULL);
if (debug_messages) {
fprintf(stderr, "[%s] client last active %u seconds ago\n", NAME,
(unsigned int) (current_time - client.last_active));
}
}
/* do something while listening for client connections */
else {
if (debug_messages) {
fprintf(stderr, "[%s] listening for client connection\n", NAME);
}
}
}
} /* END while() loop */
thread_client_data(NULL);
/* unexpected break from while() loop */
fprintf(stderr, "[%s] unexpected condition\n", NAME);