/* tab:8 * * pushy.c - server (main thread) code for HTTP push server * * "Copyright (c) 1999 by Steven S. Lumetta." * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without written agreement is * hereby granted, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * IN NO EVENT SHALL THE AUTHOR OR THE UNIVERSITY OF ILLINOIS BE LIABLE TO * ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, * EVEN IF THE AUTHOR AND/OR THE UNIVERSITY OF ILLINOIS HAS BEEN ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * THE AUTHOR AND THE UNIVERSITY OF ILLINOIS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND NEITHER THE AUTHOR NOR * THE UNIVERSITY OF ILLINOIS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS." * * Author: Steve Lumetta * Version: 1 * Creation Date: Wed Feb 17 16:56:46 1999 * Filename: pushy.c * History: * SL 1 Wed Feb 17 16:56:46 1999 * First written. */ #ident "$Id$" /* NOTE: pthread.h must be included before errno.h for correct errno location definition. Alternatively, define _REENTRANT as a compiler flag. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "local_assert.h" #include "exit_codes.h" #include "pushy.h" ASSERT_STRING; /* Static copy of file name for assertions. */ #define SERVER_PORT 5012 /* port for server */ #define BACKLOG 10 /* requested queue size for incoming connections */ /* Creates and prepares a socket at the given TCP port. Returns 0 on success, -1 on failure. */ static int set_up_server_socket (u_short port); /* Closes the server socket. */ static void tear_down_socket (int fd); /* Wait for clients to connect, spawning a thread to handle each one. */ static void wait_for_connections (int fd); int main (int argc, char** argv, char** envp) { int server_fd; /* Ignore broken pipe signals. */ signal (SIGPIPE, SIG_IGN); /* Set up the main socket for listening. */ if ((server_fd = set_up_server_socket (SERVER_PORT)) == -1) return EXIT_STARTUP_FAILED; /* Wait for connections, spawn threads, etc. */ wait_for_connections (server_fd); /* Tear the main socket down. */ tear_down_socket (server_fd); /* Exit gracefully. */ return EXIT_NORMAL; } static int set_up_server_socket (u_short port) { int fd; /* server socket file descriptor */ int yes = 1; /* used for setting socket options */ struct sockaddr_in addr; /* server socket address */ /* Create a TCP socket. */ if ((fd = socket (PF_INET, SOCK_STREAM, 0)) == -1) { perror ("set_up_server_socket/socket"); return -1; } /* Allow port reuse with the bind below. */ if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof (yes)) == -1) { perror ("set_up_server_socket/setsockopt"); return -1; } /* Set up the address. */ bzero (&addr, sizeof (addr)); addr.sin_family = AF_INET; /* Internet address */ addr.sin_addr.s_addr = INADDR_ANY; /* fill in local IP address */ addr.sin_port = htons (port); /* use port specified by caller */ /* Bind the socket to the port. */ if (bind (fd, (struct sockaddr*)&addr, sizeof (addr)) == -1) { perror ("set_up_server_socket/bind"); return -1; } /* Listen for incoming connections on the port (shift the socket into the passive state). */ if (listen (fd, BACKLOG) == -1) { perror ("set_up_server_socket/listen"); return -1; } /* The server socket is now ready. */ return fd; } static void tear_down_socket (int fd) { /* Signal a bug for invalid descriptors. */ ASSERT (fd > 0); /* Simply close the socket. */ if (close (fd) == -1) perror ("tear_down_socket/close"); } static void wait_for_connections (int fd) { pthread_attr_t attr; /* initial thread attributes */ thread_info_t* info; /* thread-specific connection information */ int len; /* value-result argument to accept */ pthread_t thread_id; /* child thread identifier */ /* Signal a bug for invalid descriptors. */ ASSERT (fd > 0); /* Initialize the POSIX threads attribute structure. */ if (pthread_attr_init (&attr) != 0) { fputs ("failed to initialize pthread attributes\n", stderr); return; } /* The main thread never joins with the children. */ if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0) { fputs ("failed to set detached state attribute\n", stderr); return; } /* Use an infinite loop to wait for connections. For each connection, create a structure with the thread-specific data, then spawn a child thread and pass it the data. The child is responsible for deallocating the memory before it terminates. */ while (1) { /* Create a thread information structure and initialize fields that can be filled in before a client contacts the server. */ if ((info = calloc (1, sizeof (*info))) == NULL) { perror ("wait_for_connections/calloc"); return; } info->fname = NULL; info->last_sent = (time_t)0; /* Wait for a client to contact the server. */ len = sizeof (info->addr); if ((info->fd = accept (fd, (struct sockaddr*)&info->addr, &len)) == -1) { perror ("accept"); return; } /* Create a thread to handle the client. */ if (pthread_create (&thread_id, &attr, (void* (*) (void*))client_thread, info) != 0) { fputs ("failed to create thread\n", stderr); /* The child does not exist, so the main thread must clean up. */ close (info->fd); free (info); return; } } }