⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 server.c

📁 快速开发
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met:  * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer.  * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the name of Silicon Graphics, Inc. nor the names of its *    contributors may be used to endorse or promote products derived from *    this software without specific prior written permission.  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <fcntl.h>#include <signal.h>#include <pwd.h>#include "st.h"/****************************************************************** * Server configuration parameters *//* Log files */#define PID_FILE    "pid"#define ERRORS_FILE "errors"#define ACCESS_FILE "access"/* Default server port */#define SERV_PORT_DEFAULT 8000/* Socket listen queue size */#define LISTENQ_SIZE_DEFAULT 256/* Max number of listening sockets ("hardware virtual servers") */#define MAX_BIND_ADDRS 16/* Max number of "spare" threads per process per socket */#define MAX_WAIT_THREADS_DEFAULT 8/* Number of file descriptors needed to handle one client session */#define FD_PER_THREAD 2/* Access log buffer flushing interval (in seconds) */#define ACCLOG_FLUSH_INTERVAL 30/* Request read timeout (in seconds) */#define REQUEST_TIMEOUT 30/****************************************************************** * Global data */struct socket_info {  st_netfd_t nfd;               /* Listening socket                     */  char *addr;                   /* Bind address                         */  int port;                     /* Port                                 */  int wait_threads;             /* Number of threads waiting to accept  */  int busy_threads;             /* Number of threads processing request */  int rqst_count;               /* Total number of processed requests   */} srv_socket[MAX_BIND_ADDRS];   /* Array of listening sockets           */static int sk_count = 0;        /* Number of listening sockets          */static int vp_count = 0;        /* Number of server processes (VPs)     */static pid_t *vp_pids;          /* Array of VP pids                     */static int my_index = -1;       /* Current process index */static pid_t my_pid = -1;       /* Current process pid   */static st_netfd_t sig_pipe[2];  /* Signal pipe           *//* * Configuration flags/parameters */static int interactive_mode = 0;static int serialize_accept = 0;static int log_access       = 0;static char *logdir     = NULL;static char *username   = NULL;static int listenq_size = LISTENQ_SIZE_DEFAULT;static int errfd        = STDERR_FILENO;/* * Thread throttling parameters (all numbers are per listening socket). * Zero values mean use default. */static int max_threads = 0;       /* Max number of threads         */static int max_wait_threads = 0;  /* Max number of "spare" threads */static int min_wait_threads = 2;  /* Min number of "spare" threads *//****************************************************************** * Useful macros */#ifndef INADDR_NONE#define INADDR_NONE 0xffffffff#endif#define SEC2USEC(s) ((s)*1000000LL)#define WAIT_THREADS(i)  (srv_socket[i].wait_threads)#define BUSY_THREADS(i)  (srv_socket[i].busy_threads)#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i))#define RQST_COUNT(i)    (srv_socket[i].rqst_count)/****************************************************************** * Forward declarations */static void usage(const char *progname);static void parse_arguments(int argc, char *argv[]);static void start_daemon(void);static void set_thread_throttling(void);static void create_listeners(void);static void change_user(void);static void open_log_files(void);static void start_processes(void);static void wdog_sighandler(int signo);static void child_sighandler(int signo);static void install_sighandlers(void);static void start_threads(void);static void *process_signals(void *arg);static void *flush_acclog_buffer(void *arg);static void *handle_connections(void *arg);static void dump_server_info(void);static void Signal(int sig, void (*handler)(int));static int cpu_count(void);extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd);extern void load_configs(void);extern void logbuf_open(void);extern void logbuf_flush(void);extern void logbuf_close(void);/* Error reporting functions defined in the error.c file */extern void err_sys_report(int fd, const char *fmt, ...);extern void err_sys_quit(int fd, const char *fmt, ...);extern void err_sys_dump(int fd, const char *fmt, ...);extern void err_report(int fd, const char *fmt, ...);extern void err_quit(int fd, const char *fmt, ...);/* * General server example: accept a client connection and do something. * This program just outputs a short HTML page, but can be easily adapted * to do other things. * * This server creates a constant number of processes ("virtual processors" * or VPs) and replaces them when they die. Each virtual processor manages * its own independent set of state threads (STs), the number of which varies * with load against the server. Each state thread listens to exactly one * listening socket. The initial process becomes the watchdog, waiting for * children (VPs) to die or for a signal requesting termination or restart. * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen * log files and reload configuration. All currently active connections remain * active. It is assumed that new configuration affects only request * processing and not the general server parameters such as number of VPs, * thread limits, bind addresses, etc. Those are specified as command line * arguments, so the server has to be stopped and then started again in order * to change them. * * Each state thread loops processing connections from a single listening * socket. Only one ST runs on a VP at a time, and VPs do not share memory, * so no mutual exclusion locking is necessary on any data, and the entire * server is free to use all the static variables and non-reentrant library * functions it wants, greatly simplifying programming and debugging and * increasing performance (for example, it is safe to ++ and -- all global * counters or call inet_ntoa(3) without any mutexes). The current thread on * each VP maintains equilibrium on that VP, starting a new thread or * terminating itself if the number of spare threads exceeds the lower or * upper limit. * * All I/O operations on sockets must use the State Thread library's I/O * functions because only those functions prevent blocking of the entire VP * process and perform state thread scheduling. */int main(int argc, char *argv[]){  /* Parse command-line options */  parse_arguments(argc, argv);  /* Allocate array of server pids */  if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL)    err_sys_quit(errfd, "ERROR: calloc failed");  /* Start the daemon */  if (!interactive_mode)    start_daemon();  /* Initialize the ST library */  if (st_init() < 0)    err_sys_quit(errfd, "ERROR: initialization failed: st_init");  /* Set thread throttling parameters */  set_thread_throttling();  /* Create listening sockets */  create_listeners();  /* Change the user */  if (username)    change_user();  /* Open log files */  open_log_files();  /* Start server processes (VPs) */  start_processes();  /* Turn time caching on */  st_timecache_set(1);  /* Install signal handlers */  install_sighandlers();  /* Load configuration from config files */  load_configs();  /* Start all threads */  start_threads();  /* Become a signal processing thread */  process_signals(NULL);  /* NOTREACHED */  return 1;}/******************************************************************/static void usage(const char *progname){  fprintf(stderr, "Usage: %s -l <log_directory> [<options>]\n\n"	  "Possible options:\n\n"	  "\t-b <host>:<port>        Bind to specified address. Multiple"	  " addresses\n"	  "\t                        are permitted.\n"	  "\t-p <num_processes>      Create specified number of processes.\n"	  "\t-t <min_thr>:<max_thr>  Specify thread limits per listening"	  " socket\n"	  "\t                        across all processes.\n"	  "\t-u <user>               Change server's user id to specified"	  " value.\n"	  "\t-q <backlog>            Set max length of pending connections"	  " queue.\n"	  "\t-a                      Enable access logging.\n"	  "\t-i                      Run in interactive mode.\n"	  "\t-S                      Serialize all accept() calls.\n"	  "\t-h                      Print this message.\n",	  progname);  exit(1);}/******************************************************************/static void parse_arguments(int argc, char *argv[]){  extern char *optarg;  int opt;  char *c;  while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) {    switch (opt) {    case 'b':      if (sk_count >= MAX_BIND_ADDRS)	err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded",		 MAX_BIND_ADDRS);      if ((c = strdup(optarg)) == NULL)	err_sys_quit(errfd, "ERROR: strdup");      srv_socket[sk_count++].addr = c;      break;    case 'p':      vp_count = atoi(optarg);      if (vp_count < 1)	err_quit(errfd, "ERROR: invalid number of processes: %s", optarg);      break;    case 'l':      logdir = optarg;      break;    case 't':      max_wait_threads = (int) strtol(optarg, &c, 10);      if (*c++ == ':')	max_threads = atoi(c);      if (max_wait_threads < 0 || max_threads < 0)	err_quit(errfd, "ERROR: invalid number of threads: %s", optarg);      break;    case 'u':      username = optarg;      break;    case 'q':      listenq_size = atoi(optarg);      if (listenq_size < 1)	err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg);      break;    case 'a':      log_access = 1;      break;    case 'i':      interactive_mode = 1;      break;    case 'S':      /*       * Serialization decision is tricky on some platforms. For example,       * Solaris 2.6 and above has kernel sockets implementation, so supposedly       * there is no need for serialization. The ST library may be compiled       * on one OS version, but used on another, so the need for serialization       * should be determined at run time by the application. Since it's just       * an example, the serialization decision is left up to user.       * Only on platforms where the serialization is never needed on any OS       * version st_netfd_serialize_accept() is a no-op.       */      serialize_accept = 1;      break;    case 'h':    case '?':      usage(argv[0]);    }  }  if (logdir == NULL && !interactive_mode) {    err_report(errfd, "ERROR: logging directory is required\n");    usage(argv[0]);  }  if (getuid() == 0 && username == NULL)    err_report(errfd, "WARNING: running as super-user!");  if (vp_count == 0 && (vp_count = cpu_count()) < 1)    vp_count = 1;  if (sk_count == 0) {    sk_count = 1;    srv_socket[0].addr = "0.0.0.0";  }}/******************************************************************/static void start_daemon(void){  pid_t pid;  /* Start forking */  if ((pid = fork()) < 0)    err_sys_quit(errfd, "ERROR: fork");  if (pid > 0)    exit(0);                  /* parent */  /* First child process */  setsid();                   /* become session leader */  if ((pid = fork()) < 0)    err_sys_quit(errfd, "ERROR: fork");  if (pid > 0)                /* first child */    exit(0);  umask(022);  if (chdir(logdir) < 0)    err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir);}/****************************************************************** * For simplicity, the minimal size of thread pool is considered * as a maximum number of spare threads (max_wait_threads) that * will be created upon server startup. The pool size can grow up * to the max_threads value. Note that this is a per listening * socket limit. It is also possible to limit the total number of * threads for all sockets rather than impose a per socket limit. */static void set_thread_throttling(void){  /*   * Calculate total values across all processes.   * All numbers are per listening socket.   */  if (max_wait_threads == 0)    max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count;  /* Assuming that each client session needs FD_PER_THREAD file descriptors */  if (max_threads == 0)    max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count;  if (max_wait_threads > max_threads)    max_wait_threads = max_threads;  /*   * Now calculate per-process values.   */  if (max_wait_threads % vp_count)    max_wait_threads = max_wait_threads / vp_count + 1;  else    max_wait_threads = max_wait_threads / vp_count;  if (max_threads % vp_count)    max_threads = max_threads / vp_count + 1;  else    max_threads = max_threads / vp_count;  if (min_wait_threads > max_wait_threads)    min_wait_threads = max_wait_threads;}/******************************************************************/static void create_listeners(void){  int i, n, sock;  char *c;  struct sockaddr_in serv_addr;  struct hostent *hp;  short port;  for (i = 0; i < sk_count; i++) {    port = 0;    if ((c = strchr(srv_socket[i].addr, ':')) != NULL) {      *c++ = '\0';      port = (short) atoi(c);    }    if (srv_socket[i].addr[0] == '\0')      srv_socket[i].addr = "0.0.0.0";    if (port == 0)      port = SERV_PORT_DEFAULT;    /* Create server socket */    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)      err_sys_quit(errfd, "ERROR: can't create socket: socket");    n = 1;    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0)      err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt");    memset(&serv_addr, 0, sizeof(serv_addr));    serv_addr.sin_family = AF_INET;    serv_addr.sin_port = htons(port);    serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr);    if (serv_addr.sin_addr.s_addr == INADDR_NONE) {      /* not dotted-decimal */      if ((hp = gethostbyname(srv_socket[i].addr)) == NULL)	err_quit(errfd, "ERROR: can't resolve address: %s",		 srv_socket[i].addr);      memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length);    }    srv_socket[i].port = port;    /* Do bind and listen */    if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)      err_sys_quit(errfd, "ERROR: can't bind to address %s, port %d",		   srv_socket[i].addr, port);    if (listen(sock, listenq_size) < 0)      err_sys_quit(errfd, "ERROR: listen");    /* Create file descriptor object from OS socket */    if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL)      err_sys_quit(errfd, "ERROR: st_netfd_open_socket");    /*     * On some platforms (e.g. IRIX, Linux) accept() serialization is never     * needed for any OS version.  In that case st_netfd_serialize_accept()     * is just a no-op. Also see the comment above.     */    if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0)      err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept");  }}/******************************************************************/static void change_user(void){  struct passwd *pw;  if ((pw = getpwnam(username)) == NULL)    err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username);  if (setgid(pw->pw_gid) < 0)    err_sys_quit(errfd, "ERROR: can't change group id: setgid");  if (setuid(pw->pw_uid) < 0)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -