diff --git a/server/main.c b/server/main.c index 00fe29a..8cac86d 100644 --- a/server/main.c +++ b/server/main.c @@ -10,6 +10,7 @@ * 2001, Rene Wagner * 2002, Mike Patnode * 2002, Guillaume Filion + * 2003, Benjamin Tse (Win32 support) * * * Contains main(), plus signal callback functions and a help screen. @@ -31,20 +32,25 @@ #include #include #include +#ifndef WIN32 #include +#include +#else +#include "getopt.h" +#endif #include #include #include #include #include #include -#include #ifdef HAVE_SYS_TIME_H # include #endif /* TODO: fill in what to include otherwise */ +/* REVISIT: externs should be provided by a header */ extern char *optarg; extern int optind, optopt, opterr; @@ -79,6 +85,10 @@ extern int optind, optopt, opterr; #define DEFAULT_SCREEN_DURATION 32 #define DEFAULT_HEARTBEAT HEARTBEAT_ON +/* All variables are set to 'unset' values*/ +#define UNSET_INT -1 +#define UNSET_STR "\01" + /* Socket to bind to... Using loopback is much more secure; it means that this port is @@ -221,7 +231,7 @@ main (int argc, char **argv) /* Only catch SIGHUP if not in foreground mode */ /* Startup the subparts of the server */ - CHAIN( e, sock_init() ); + CHAIN( e, sock_init(bind_addr, bind_port) ); CHAIN( e, screenlist_init() ); CHAIN( e, init_drivers() ); CHAIN( e, clients_init() ); @@ -514,6 +524,7 @@ set_default_settings() void install_signal_handlers (int allow_reload) { +#ifndef WIN32 /* Installs signal handlers so that the program does clean exit and * can also receive a reload signal. * sigaction() is favoured over signal() */ @@ -537,6 +548,14 @@ install_signal_handlers (int allow_reload) /* Treat this signal just like INT and TERM */ } sigaction (SIGHUP, &sa, NULL); +#else + /* Win32 does not support POSIX signals i.e. sigaction(). However, it does + * support ANSI signals in mingw. */ + signal (SIGINT, exit_program); /* Ctrl-C will cause a clean exit...*/ + signal (SIGTERM, exit_program); /* and "kill"...*/ + + /* REVISIT: implement SIGHUP on windows */ +#endif } @@ -554,6 +573,13 @@ child_ok_func (int signal) { pid_t daemonize() { +#ifdef WIN32 + /* WIN32 does not support fork() - CreateProcess() does not even have + * similar functionality. Instead don't daemonize on WIN32. + */ + pid_t parent; + parent = getpid(); +#else pid_t child; pid_t parent; int child_status; @@ -605,6 +631,7 @@ daemonize() setsid(); /* Create a new session because otherwise we'll * catch a SIGHUP when the shell is closed. */ +#endif return parent; } @@ -613,9 +640,11 @@ daemonize() int wave_to_parent (pid_t parent_pid) { +#ifndef WIN32 debug( RPT_DEBUG, "%s( parent_pid=%d )", __FUNCTION__, parent_pid ); kill( parent_pid, SIGUSR1 ); +#endif return 0; } @@ -664,6 +693,7 @@ init_drivers() int drop_privs(char *user) { +#ifndef WIN32 struct passwd *pwent; debug( RPT_DEBUG, "%s( user=\"%.40s\" )", __FUNCTION__, user ); @@ -679,6 +709,10 @@ int drop_privs(char *user) } } } +#else + /* Don't alter privileges in WIN32 */ +#endif + return 0; } @@ -740,8 +774,14 @@ void do_mainloop () { Screen * s; +#ifndef WIN32 struct timeval t; struct timeval last_t; +#else + LARGE_INTEGER t; + LARGE_INTEGER last_t; + LARGE_INTEGER perf_freq; /* frequency of high perf counter (cycles per usec) */ +#endif int sleeptime; long int process_lag = 0; long int render_lag = 0; @@ -749,15 +789,32 @@ do_mainloop () debug( RPT_DEBUG, "%s()", __FUNCTION__ ); +#ifndef WIN32 gettimeofday (&t, NULL); /* Get initial time */ +#else + QueryPerformanceFrequency(&perf_freq); + /* scale perf_freq to number of cycles per micro-sec */ + /* REVISIT: this causes rounding errors below but is more efficient */ + perf_freq.QuadPart /= 1e6; + QueryPerformanceCounter(&t); +#endif while (1) { /* Get current time */ last_t = t; +#ifndef WIN32 gettimeofday (&t, NULL); t_diff = ((t.tv_sec - last_t.tv_sec) * 1e6 + (t.tv_usec - last_t.tv_usec)); - - process_lag += t_diff; +#else + QueryPerformanceCounter(&t); + /*t_diff.HighPart = t.HighPart - last_t.HighPart; + t_diff.LowPart = t.LowPart - last_t.LowPart; + */ + t_diff = t.QuadPart - last_t.QuadPart; + t_diff /= perf_freq.QuadPart; /* scale to microseconds */ + /* REVISIT: assumes fits into long */ +#endif + process_lag += t_diff; if (process_lag > 0) { /* Time for a processing stroke */ sock_poll_clients (); /* poll clients for input*/ @@ -796,7 +853,12 @@ do_mainloop () /* Sleep just as long as needed */ sleeptime = min (0-process_lag, 0-render_lag); if( sleeptime > 0 ) { +#ifndef WIN32 usleep (sleeptime); +#else + /* Sleep in Windows takes milliseconds argument */ + Sleep(sleeptime / 1000); +#endif } /* Check if a SIGHUP has been caught */ @@ -848,6 +910,7 @@ exit_program (int val) menuscreens_shutdown (); screenlist_shutdown (); /* shutdown screens (must come after client_shutdown) */ input_shutdown (); /* shutdown key input part */ + sock_shutdown(); /* shutdown the sockets server */ report( RPT_INFO, "Exiting." ); _exit (0); diff --git a/server/sock.c b/server/sock.c index 769ba07..3c73879 100644 --- a/server/sock.c +++ b/server/sock.c @@ -6,33 +6,36 @@ * COPYING file distributed with this package. * * Copyright (c) 1999, William Ferrell, Scott Scriven - * + * 2003, Benjamin Tse (blt@ieee.org) - Winsock port * * LCDproc sockets code... * - * This is messy, and needs to be finished. - * */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include #include #include #include +#ifdef WINSOCK2 +#include +#else #include #include -#include -#include #include #include -#include #include +#endif /* WINSOCK */ +#include +#include #include #include -#include "shared/sockets.h" -#include "main.h" #include "sock.h" #include "client.h" #include "clients.h" @@ -41,26 +44,97 @@ #include "screenlist.h" -fd_set active_fd_set, read_fd_set; -int listening_fd; +/****************************************************************************/ +static fd_set active_fd_set, read_fd_set; +static int listening_fd; + +/* For efficiency we maintain a list of open sockets. Nodes in this list + * are obtained from a pre-allocated pool - this removes heap operations + * from the polling loop. A list of open sockets is also required under WINSOCK + * as sockets can be arbitrary values instead of low value integers. */ +static LinkedList* openSocketList = NULL; +static LinkedList* freeClientSocketList = NULL; + +struct ClientSocketMap +{ + int socket; + Client* client; +}; + +/* The memory referenced from clientSocketPoolList is obtained from the + * clientSocketPool array. */ +struct ClientSocketMap* freeClientSocketPool; /* Length of longest transmission allowed at once...*/ #define MAXMSG 8192 -int sock_read_from_client (int filedes); +/**** Internal function declarations ****************************************/ +int sock_read_from_client(struct ClientSocketMap* clientSocketMap); +/****************************************************************************/ int -sock_init () +sock_init(char* bind_addr, int bind_port) { + int i; + +#ifdef WINSOCK2 + /* Initialize the Winsock dll */ + WSADATA wsaData; + int startup = WSAStartup( MAKEWORD(2, 2), &wsaData ); + if (startup != 0) + { + report(RPT_ERR, "%s: Could not start Winsock library - %s", + __FUNCTION__, sock_geterror()); + } + /* REVISIT: call WSACleanup(); */ +#endif + debug (RPT_DEBUG, "%s( bind_addr=\"%s\", port=%d )", __FUNCTION__, bind_addr, bind_port); /* Create the socket and set it up to accept connections. */ listening_fd = sock_create_inet_socket (bind_addr, bind_port); if (listening_fd < 0) { - report (RPT_ERR, "%s: Error creating socket", __FUNCTION__); + report (RPT_ERR, "%s: Error creating socket - %s", + __FUNCTION__, sock_geterror()); return -1; } + + /* Create the socket -> Client mapping pool */ + /* How large can FD_SETSIZE be? Even if it is ~2000 this only uses a + few kilobytes of memory. Let's trade size for speed! */ + freeClientSocketPool = (struct ClientSocketMap*) + malloc(sizeof(struct ClientSocketMap) * FD_SETSIZE); + if (!freeClientSocketPool) + { + report(RPT_ERR, "%s: Error allocating memory for client sockets.", + __FUNCTION__); + return -1; + } + + freeClientSocketList = LL_new(); + for (i = 0; i < FD_SETSIZE; ++i) + { + LL_AddNode(freeClientSocketList, (void*) &freeClientSocketPool[i]); + } + + /* Create and initialize the open socket list with the server socket */ + openSocketList = LL_new(); + if (!openSocketList) + { + report(RPT_ERR, "%s: Error allocating memory for the open socket " + "list.", __FUNCTION__); + return -1; + } else { + struct ClientSocketMap* entry; + + /* LL_DeleteNode removes an entry from the list and returns it */ + entry = (struct ClientSocketMap*) LL_Pop(freeClientSocketList); + entry->socket = listening_fd; + entry->client = NULL; + LL_AddNode(openSocketList, (void*) entry); + } + return 0; } @@ -84,12 +158,46 @@ This code gets the send and receive buffer sizes. int sock_shutdown () { + int retVal = 0; + debug( RPT_DEBUG, "%s()", __FUNCTION__ ); + /*struct ClientSocketMap* clientIt;*/ + + /* delete all clients */ + /* This should be done by calling clients_shutdown */ + /* + LL_Rewind(openSocketList); + for (clientIt = (struct ClientSocketMap*) LL_Get(openSocketList); + clientIt; + clientIt = LL_GetNext(openSocketList)) + { + if (clientIt->client) + { + /* destroying a client also closes its socket */ + /* client_destroy(clientIt->client); + } + } + LL_Destroy(openSocketList); + */ close( listening_fd ); + LL_Destroy(freeClientSocketList); + free(freeClientSocketPool); + +#ifdef WINSOCK2 + if (WSACleanup() != 0) + { + report(RPT_ERR, "%s: Error closing Winsock library - %s", + __FUNCTION__, sock_geterror()); + retVal = -1; + } +#endif + + return retVal; } +/****************************************************************************/ /* Creates a socket in internet space */ int sock_create_inet_socket (char * addr, unsigned int port) @@ -101,13 +209,20 @@ sock_create_inet_socket (char * addr, unsigned int port) /* Create the socket. */ sock = socket (PF_INET, SOCK_STREAM, 0); - if (sock < 0) { - report(RPT_ERR, "%s: Could not create socket", __FUNCTION__); +#ifdef WINSOCK2 + if (sock == INVALID_SOCKET) +#else + if (sock < 0) +#endif + { + report(RPT_ERR, "%s: Could not create socket - %s", + __FUNCTION__, sock_geterror()); return -1; } /* Set the socket so we can re-use it*/ - if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&sockopt,sizeof(sockopt)) < 0) { - report(RPT_ERR, "%s: Error setting socket option SO_REUSEADDR", __FUNCTION__); + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) < 0) { + report(RPT_ERR, "%s: Error setting socket option SO_REUSEADDR - %s", + __FUNCTION__, sock_geterror()); return -1; } @@ -115,17 +230,26 @@ sock_create_inet_socket (char * addr, unsigned int port) memset (&name, 0, sizeof (name)); name.sin_family = AF_INET; name.sin_port = htons (port); +#ifndef WINSOCK2 + /* REVISIT: can probably use the same code as under winsock */ inet_aton(addr, &name.sin_addr); +#else + name.sin_addr.S_un.S_addr = inet_addr(addr); +#endif - if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { - report(RPT_ERR, "Could not bind to port %d", port); + if (bind(sock, (struct sockaddr *) &name, sizeof (name)) < 0) + { + report(RPT_ERR, "%s: Could not bind to port %d at address %s - %s", + __FUNCTION__, port, addr, sock_geterror()); return -1; } else { report(RPT_NOTICE, "Listening for queries on %s:%d", addr, port); } if (listen (sock, 1) < 0) { - report(RPT_ERR, "%s: error in attempting to listen to port", __FUNCTION__); + report(RPT_ERR, "%s: error in attempting to listen to port " + "%d at %s - %s", + __FUNCTION__, port, addr, sock_geterror()); return -1; } @@ -141,12 +265,10 @@ sock_create_inet_socket (char * addr, unsigned int port) int sock_poll_clients () { - int i; int err; struct sockaddr_in clientname; - size_t size; struct timeval t; - Client * c; + struct ClientSocketMap* clientSocket; debug (RPT_DEBUG, "%s()", __FUNCTION__); @@ -157,35 +279,73 @@ sock_poll_clients () read_fd_set = active_fd_set; if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, &t) < 0) { - report (RPT_ERR, "%s: Select error", __FUNCTION__); + report(RPT_ERR, "%s: Select error - %s", + __FUNCTION__, sock_geterror()); return -1; } /* Service all the sockets with input pending. */ - for (i = 0; i < FD_SETSIZE; ++i) { - if (FD_ISSET (i, &read_fd_set)) { - if (i == listening_fd) { + LL_Rewind(openSocketList); + for (clientSocket = (struct ClientSocketMap*) LL_Get(openSocketList); + clientSocket; + clientSocket = LL_GetNext(openSocketList)) + { + if (FD_ISSET(clientSocket->socket, &read_fd_set)) + { + if (clientSocket->socket == listening_fd) + { /* Connection request on original socket. */ + Client* c; int new_sock; - size = sizeof (clientname); + size_t size = sizeof(clientname); new_sock = accept (listening_fd, (struct sockaddr *) &clientname, &size); - if (new_sock < 0) { - report (RPT_ERR, "%s: Accept error", __FUNCTION__); +#ifdef WINSOCK2 + if (new_sock == INVALID_SOCKET) +#else + if (new_sock < 0) +#endif + { + report(RPT_ERR, "%s: Accept error - %s", + __FUNCTION__, sock_geterror()); return -1; } report (RPT_NOTICE, "Connect from host %s:%hd on socket %i", inet_ntoa (clientname.sin_addr), ntohs (clientname.sin_port), new_sock); FD_SET (new_sock, &active_fd_set); - fcntl (new_sock, F_SETFL, O_NONBLOCK); +#ifdef WINSOCK2 + { + unsigned long tmp; + ioctlsocket(new_sock, FIONBIO, &tmp); + } +#else + fcntl(new_sock, F_SETFL, O_NONBLOCK); +#endif /* Create new client */ if ((c = client_create (new_sock)) == NULL) { - report( RPT_ERR, "%s: Error creating client on socket %i", __FUNCTION__, i); + report( RPT_ERR, "%s: Error creating client on socket %i - %s", + __FUNCTION__, clientSocket->socket, sock_geterror()); return -1; - } + } else { + /* add new_sock */ + struct ClientSocketMap* newClientSocket; + newClientSocket = (struct ClientSocketMap*) LL_Pop(freeClientSocketList); + if (newClientSocket) + { + newClientSocket->socket = new_sock; + newClientSocket->client = c; + LL_InsertNode(openSocketList, (void*) newClientSocket); + /* advance past the new node - check it on the next pass */ + LL_Next(openSocketList); + } else { + report(RPT_ERR, "%s: Error - free client socket list exhausted - %d clients.", + __FUNCTION__, FD_SETSIZE); + return -1; + } + } if (clients_add_client (c) != 0) { - report( RPT_ERR, "%s: Could not add client on socket %i", __FUNCTION__, i); + report( RPT_ERR, "%s: Could not add client on socket %i", __FUNCTION__, clientSocket->socket); return -1; } } else { @@ -193,21 +353,30 @@ sock_poll_clients () err = 0; do { debug (RPT_DEBUG, "%s: reading...", __FUNCTION__); - err = sock_read_from_client (i); + err = sock_read_from_client(clientSocket); debug (RPT_DEBUG, "%s: ...done", __FUNCTION__); - if (err < 0) { - /* Client disconnected, destroy client data */ - c = clients_find_client_by_sock (i); - if (c) { - /*sock_send_string(i, "bye\n");*/ - report (RPT_NOTICE, "Client on socket %i disconnected", i); - client_destroy (c); - clients_remove_client (c); - close (i); - FD_CLR (i, &active_fd_set); - } else { - report (RPT_ERR, "%s: Can't find client of socket %i", __FUNCTION__, i); - } + if (err < 0) + { + /* Client disconnected, destroy client data */ + /* c = clients_find_client_by_sock(); - Deprecated by clientsocketmap*/ + if (clientSocket->client) + { + struct ClientSocketMap* entry; + + /*sock_send_string(i, "bye\n");*/ + report (RPT_NOTICE, "Client on socket %i disconnected", + clientSocket->socket); + client_destroy(clientSocket->client); + clients_remove_client(clientSocket->client); + FD_CLR(clientSocket->socket, &active_fd_set); + close(clientSocket->socket); + + entry = (struct ClientSocketMap*) LL_DeleteNode(openSocketList); + LL_Push(freeClientSocketList, (void*) entry); + } else { + report (RPT_ERR, "%s: Can't find client of socket %i", + __FUNCTION__, clientSocket->socket); + } } } while (err > 0); } @@ -217,25 +386,29 @@ sock_poll_clients () } int -sock_read_from_client (int filedes) +sock_read_from_client(struct ClientSocketMap* clientSocketMap) { char buffer[MAXMSG]; int nbytes, i; - Client * c; debug (RPT_DEBUG, "%s()", __FUNCTION__); errno = 0; - if ((nbytes = sock_recv (filedes, buffer, MAXMSG)) < 0) { + nbytes = sock_recv(clientSocketMap->socket, buffer, MAXMSG); + if (nbytes < 0) + { if (errno != EAGAIN) - report (RPT_DEBUG, "%s: Error on socket %d: %s", __FUNCTION__, filedes, strerror(errno)); + report(RPT_DEBUG, "%s: Error on socket %d - %s", + __FUNCTION__, clientSocketMap->socket, sock_geterror()); return 0; } else if (nbytes == 0) /* EOF*/ + { return -1; - else if (nbytes > (MAXMSG - (MAXMSG / 8))) /* Very noisy client...*/ + } else if (nbytes > (MAXMSG - (MAXMSG / 8))) /* Very noisy client...*/ { - sock_send_string (filedes, "huh? Too much data received... quiet down!\n"); - report (RPT_WARNING, "%s: Too much data received on socket %d", __FUNCTION__, filedes); + sock_send_string(clientSocketMap->socket, "huh? Too much data received... quiet down!\n"); + report(RPT_WARNING, "%s: Too much data received on socket %d", + __FUNCTION__, clientSocketMap->socket); return -1; } else /* Data Read*/ { @@ -245,13 +418,16 @@ sock_read_from_client (int filedes) if (buffer[i] == 0) buffer[i] = '\n'; /* Enqueue a "client message" here...*/ - c = clients_find_client_by_sock (filedes); - if (c) { - client_add_message (c, buffer); - } else - report (RPT_DEBUG, "%s: Can't find client %d", __FUNCTION__, filedes); + /* c = clients_find_client_by_sock (filedes); - Deprecated by clientsocketmap*/ + if (clientSocketMap->client) { + client_add_message(clientSocketMap->client, buffer); + } else { + report(RPT_DEBUG, "%s: Can't find client %d", + __FUNCTION__, clientSocketMap->socket); + } - report (RPT_DEBUG, "%s: got message from client %d: \"%s\"", __FUNCTION__, filedes, buffer); + report(RPT_DEBUG, "%s: got message from client %d: \"%s\"", + __FUNCTION__, clientSocketMap->socket, buffer); return nbytes; } return nbytes; diff --git a/server/sock.h b/server/sock.h index 9710f92..a697675 100644 --- a/server/sock.h +++ b/server/sock.h @@ -17,9 +17,9 @@ typedef struct sockaddr_in sockaddr_in; /* Server functions...*/ -int sock_init (); -int sock_shutdown (); -int sock_create_inet_socket (char * addr, unsigned int port); -int sock_poll_clients (); +int sock_init(char* bind_addr, int bind_port); +int sock_shutdown(); +int sock_create_inet_socket(char* bind_addr, unsigned int port); +int sock_poll_clients(); #endif