diff --git a/client.c b/client.c index 66495ce..c96bf43 100644 --- a/client.c +++ b/client.c @@ -86,32 +86,31 @@ int client_write(struct client_t *client, char *databuf, int datalen) { /* Waits for client input in "line mode". Blocks until input arrives. */ int client_wait_line(struct client_t *client) { - int ret; fd_set read_fds; + struct timeval tv; client->data[0] = '\0'; /* loop waiting for client input */ while (client->data[0] == '\0') { /* setup select() parameters */ + tv.tv_sec = 15; /* 15 second timeout */ + tv.tv_usec = 0; FD_ZERO(&read_fds); FD_SET(client->socket, &read_fds); /* send prompt character to client */ client_write(client, "> ", 2); /* block until input arrives */ - if (select((client->socket)+1, &read_fds, NULL, NULL, NULL) == -1) return -1; + if (select((client->socket)+1, &read_fds, NULL, NULL, &tv) <= 0) return -1; if (FD_ISSET(client->socket, &read_fds)) { /* read client input */ - ret = client_read(client); - if (ret == -1) return -1; + if (client_read(client) == -1) return -1; /* we don't want empty data so stop on \r or \n */ if ( (client->data[0] == '\r') || (client->data[0] == '\n') ) { - fprintf(stderr, "client data is empty\n"); client->data[0] = '\0'; } } } - fprintf(stderr, "client data: %s\n", client->data); return 0; } diff --git a/moxerver.c b/moxerver.c index fd66249..13dcdee 100644 --- a/moxerver.c +++ b/moxerver.c @@ -11,7 +11,7 @@ #define NAME "moxerver" -#define SERVER_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */ +#define SERVER_WAIT_TIMEOUT 2 /* seconds for select() timeout in server loop */ /* Prints help message. */ static void usage() { @@ -82,15 +82,14 @@ int main(int argc, char *argv[]) { int fdmax; struct timeval tv; - pthread_t tty_thread; + pthread_t tty_thread, new_client_thread; /* zero init tty_dev */ if (cfsetispeed(&(tty_dev.ttyset), B0) < 0 || cfsetospeed(&(tty_dev.ttyset), B0) < 0) { fprintf(stderr, "[%s] error configuring tty device speed\n", NAME); return -1; - } - + } /* enable catching and handling some quit signals, SIGKILL can't be caught */ signal(SIGTERM, quit_handler); @@ -141,6 +140,7 @@ int main(int argc, char *argv[]) { /* initialize */ if (server_setup(&server, tcp_port) < 0) return -1; client.socket = -1; + new_client.socket = -1; tty_dev.fd = -1; /* parse config file if any */ @@ -150,7 +150,7 @@ int main(int argc, char *argv[]) { return -1; } else if (!def_conf && ret) { - fprintf(stderr, "[%s] error parsing congig file %s on line %d\n", NAME, CONFILE, ret); + fprintf(stderr, "[%s] error parsing config file %s on line %d\n", NAME, CONFILE, ret); return -1; } @@ -177,9 +177,25 @@ int main(int argc, char *argv[]) { return -1; } - /* loop with timeouts waiting for client connection and data */ + /* 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(struct 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 */ + telnet_set_character_mode(&client); + } + /* setup parameters for select() */ tv.tv_sec = SERVER_WAIT_TIMEOUT; tv.tv_usec = 0; @@ -188,7 +204,7 @@ int main(int argc, char *argv[]) { if (client.socket != -1) { FD_SET(client.socket, &read_fds); /* wait for client if connected */ } - fdmax = (server.socket > client.socket) ? server.socket : client.socket; + fdmax = (server.socket > client.socket) ? server.socket : client.socket; /* wait with select() */ ret = select(fdmax+1, &read_fds, NULL, NULL, &tv); @@ -201,36 +217,11 @@ int main(int argc, char *argv[]) { /* check server status */ if (FD_ISSET(server.socket, &read_fds)) { fprintf(stderr, "[%s] received client connection request\n", NAME); - /* accept connection request if no client is connected */ - if (client.socket == -1) { - ret = server_accept(&server, &client); - if ( ret != 0) { - /* print error but continue waiting for connection request */ - //TODO maybe we should break here to avoid endless loop, what are possible causes of this failure? - fprintf(stderr, "[%s] problem accepting client\n", NAME); - continue; - } - /* ask client to provide a username before going to "character" mode */ - client_ask_username(&client); - /* put client in "character" mode */ - telnet_set_character_mode(&client); - } - /* if a client is already connected then new clients need to be handled */ - else { - /* decide if current client needs to be dropped or new client needs to be rejected */ - ret = server_drop(&server, &client); - if (ret < 0) { - /* error in handling new clients, print but continue waiting for new clients */ - fprintf(stderr, "[%s] problem handling new client request\n", NAME); - continue; - } - /* if current client was dropped we need to set up new client */ - if (ret > 0) { - /* ask client to provide a username before going to "character" mode */ - client_ask_username(&client); - /* put client in "character" mode */ - telnet_set_character_mode(&client); - } + /* 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 */ diff --git a/moxerver.h b/moxerver.h index 797d123..427f9bd 100644 --- a/moxerver.h +++ b/moxerver.h @@ -21,34 +21,35 @@ /* Structures used for communication parameters. */ struct server_t { - int socket; /* server socket */ - struct sockaddr_in address; /* server address information */ + int socket; /* server socket */ + struct sockaddr_in address; /* server address information */ unsigned int port; /* server port in host byte order, practical reference */ }; struct client_t { - int socket; /* client socket */ - struct sockaddr_in address; /* client address information */ - char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */ - time_t last_active; /* time of client's last activity in seconds from Epoch */ - char username[USERNAME_LEN]; /* username for human identification */ - char data[DATABUF_LEN]; /* buffer for data received from client */ + int socket; /* client socket */ + struct sockaddr_in address; /* client address information */ + char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */ + time_t last_active; /* time of client's last activity in seconds from Epoch */ + char username[USERNAME_LEN]; /* username for human identification */ + char data[DATABUF_LEN]; /* buffer for data received from client */ }; struct tty_t { - int fd; /* tty file descriptor */ + int fd; /* tty file descriptor */ struct termios ttysetdef; /* default tty termios settings */ struct termios ttyset; /* tty termios settings */ - char path[DEV_PATH]; /* tty device path */ - char data[DATABUF_LEN]; /* buffer for data received from tty */ + char path[DEV_PATH]; /* tty device path */ + char data[DATABUF_LEN]; /* buffer for data received from tty */ }; /* Global variables used throughout the application. */ -int debug_messages; /* if > 0 debug messages will be printed */ -struct server_t server; /* main server structure */ -struct client_t client; /* connected client structure */ //TODO working with only 1 client, this can be expanded into a list -struct tty_t tty_dev; /* connected tty device */ +int debug_messages; /* if > 0 debug messages will be printed */ +struct server_t server; /* main server structure */ +struct client_t client; /* connected client structure */ //TODO working with only 1 client, this can be expanded into a list +struct client_t new_client; /* client structure for new client request */ +struct tty_t tty_dev; /* connected tty device */ /* Global functions used throughout the application. */ @@ -91,15 +92,17 @@ int server_close(struct server_t *server); int server_accept(struct server_t *server, struct client_t *accepted_client); /** - * Asks new incoming client connection if current client needs to be dropped. - * If current client doesn't need to be dropped then new client is rejected. + * Thread function handling new client connections. + * If there is no connected client then first client request is accepted. + * If there is a connected client then new client is asked if connected client should be dropped. * * Returns: - * - positive value if current client was dropped - * - 0 if new client was rejected - * - negative value if error occurred + * Return value from this thread function is not used. + * Function handles global client variables: + * - client structure is reset if new client is available to connect to + * - new_client structure stores information about the client to connect to */ -int server_drop(struct server_t *server, struct client_t *client); +void* server_new_client_thread(void *args); /* Functions handling communication with clients. */ diff --git a/server.c b/server.c index a068d61..b426b8d 100644 --- a/server.c +++ b/server.c @@ -47,8 +47,8 @@ int server_setup(struct server_t *server, unsigned int port) { server->port = port; fprintf(stderr,"[%s] assigned port %u\n", __func__, server->port); //ntohs(server->address.sin_port) - /* listen for a client connection, allow 2 connections in queue */ - if (listen(server->socket, 2) == -1) { + /* listen for a client connection, allow some connections in queue */ + if (listen(server->socket, 1) == -1) { fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); return -errno; } @@ -98,58 +98,69 @@ int server_accept(struct server_t *server, struct client_t *accepted_client) { /* print client information */ time2string(accepted_client->last_active, timestamp); - fprintf(stderr, "[%s] accepted client %s on port %u @ %s\n", __func__, - accepted_client->ip_string, server->port, timestamp); + fprintf(stderr, "[%s] accepted client %s @ %s\n", + __func__, accepted_client->ip_string, timestamp); return 0; } -/* Drops current client or rejects new client. */ -int server_drop(struct server_t *server, struct client_t *current_client) { +/* Thread function handling new client connections. Handles global client variables. */ +void* server_new_client_thread(void *args) { - struct client_t new_client; char msg[DATABUF_LEN]; char timestamp[TIMESTAMP_LEN]; + struct client_t temp_client; /* accept new connection request */ - if (server_accept(server, &new_client) != 0) return -1; + if (server_accept(&server, &temp_client) != 0) return (void *) -1; + + /* if no client is connected then make new client available for connection */ + if (client.socket == -1) { + memcpy(&new_client, &temp_client, sizeof(struct client_t)); + return (void *) 0; + } + + /* if there is already a new client being handled then reject this client request */ + if (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 new client available for connection */ + memcpy(&new_client, &temp_client, sizeof(struct client_t)); /* inform new client that port is already in use */ - time2string(current_client->last_active, timestamp); + time2string(client.last_active, timestamp); sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n", - server->port, current_client->username, timestamp); + server.port, client.username, timestamp); send(new_client.socket, msg, strlen(msg), 0); /* ask new client if current client should be dropped */ - sprintf(msg, "\nDo you want to drop the current user?\nIf yes then please type YES DROP (in uppercase):"); + sprintf(msg, "\nDo you want to drop the current user?\nIf yes then please type YES DROP (in uppercase):\n"); send(new_client.socket, msg, strlen(msg), 0); /* wait for client input */ - if (client_wait_line(&new_client) != 0) return -1; + client_wait_line(&new_client); /* check client confirmation */ if (strncmp(new_client.data, "YES DROP", 8) == 0) { - /* current client should be dropped, drop it */ - client_close(current_client); - time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] dropped current client %s on port %u @ %s\n", __func__, - current_client->ip_string, server->port, timestamp); - /* make new client the current client */ - memcpy(current_client, &new_client, sizeof(struct client_t)); - time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] accepted new client request %s on port %u @ %s\n", __func__, - current_client->ip_string, server->port, timestamp); - /* return 1 because current client was dropped */ - return 1; + /* drop connected client */ + client_close(&client); + fprintf(stderr, "[%s] dropped client %s @ %s\n", + __func__, client.ip_string, timestamp); } else { - /* new connection should be rejected, close connection */ - close(new_client.socket); + /* reject this client request */ + client_close(&new_client); time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] rejected new client request %s on port %u @ %s\n", __func__, - new_client.ip_string, server->port, timestamp); - /* return 0 because new client was rejected */ - return 0; + fprintf(stderr, "[%s] rejected new client request %s @ %s\n", + __func__, temp_client.ip_string, timestamp); + return (void *) 1; } - + return (void *) 0; }