Home | History | Annotate | Download | only in src
      1 /*
      2 
      3 	   Copyright (C) 1993-2012 Hewlett-Packard Company
      4                          ALL RIGHTS RESERVED.
      5 
      6   The enclosed software and documentation includes copyrighted works
      7   of Hewlett-Packard Co. For as long as you comply with the following
      8   limitations, you are hereby authorized to (i) use, reproduce, and
      9   modify the software and documentation, and to (ii) distribute the
     10   software and documentation, including modifications, for
     11   non-commercial purposes only.
     12 
     13   1.  The enclosed software and documentation is made available at no
     14       charge in order to advance the general development of
     15       high-performance networking products.
     16 
     17   2.  You may not delete any copyright notices contained in the
     18       software or documentation. All hard copies, and copies in
     19       source code or object code form, of the software or
     20       documentation (including modifications) must contain at least
     21       one of the copyright notices.
     22 
     23   3.  The enclosed software and documentation has not been subjected
     24       to testing and quality control and is not a Hewlett-Packard Co.
     25       product. At a future time, Hewlett-Packard Co. may or may not
     26       offer a version of the software and documentation as a product.
     27 
     28   4.  THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS".
     29       HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE,
     30       REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR
     31       DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL
     32       PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR
     33       DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES,
     34       EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE
     35       DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF
     36       MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     37 
     38   5.  HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY
     39       DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
     40       (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION,
     41       MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION.
     42 
     43 */
     44 
     45 #include "netperf_version.h"
     46 
     47 char	netserver_id[]="\
     48 @(#)netserver.c (c) Copyright 1993-2012 Hewlett-Packard Co. Version 2.6.0";
     49 
     50 
     51 #ifdef HAVE_CONFIG_H
     52 #include "config.h"
     53 #endif
     54 
     55 #if HAVE_STRING_H
     56 # if !STDC_HEADERS && HAVE_MEMORY_H
     57 #  include <memory.h>
     58 # endif
     59 # include <string.h>
     60 #endif
     61 
     62 #if HAVE_STRINGS_H
     63 # include <strings.h>
     64 #endif
     65 
     66 #if HAVE_LIMITS_H
     67 # include <limits.h>
     68 #endif
     69 
     70 #if HAVE_SYS_IPC_H
     71 #include <sys/ipc.h>
     72 #endif
     73 
     74 #if HAVE_SYS_IOCTL_H
     75 #include <sys/ioctl.h>
     76 #endif
     77 
     78 #if HAVE_SYS_SOCKET_H
     79 #include <sys/socket.h>
     80 #endif
     81 
     82 #if HAVE_SYS_STAT_H
     83 #include <sys/stat.h>
     84 #endif
     85 
     86 #if HAVE_NETINET_IN_H
     87 #include <netinet/in.h>
     88 #endif
     89 
     90 #if HAVE_NETDB_H
     91 #include <netdb.h>
     92 #endif
     93 
     94 #if HAVE_UNISTD_H
     95 #include <unistd.h>
     96 #endif
     97 
     98 #if HAVE_STDLIB_H
     99 #include <stdlib.h>
    100 #endif
    101 
    102 #if HAVE_ERRNO_H
    103 #include <errno.h>
    104 #endif
    105 
    106 #if HAVE_SIGNAL_H
    107 #include <signal.h>
    108 /* some OS's have SIGCLD defined as SIGCHLD */
    109 #ifndef SIGCLD
    110 #define SIGCLD SIGCHLD
    111 #endif /* SIGCLD */
    112 
    113 #endif
    114 
    115 #if !defined(HAVE_SETSID)
    116 #if HAVE_SYS_WAIT_H
    117 #include <sys/wait.h>
    118 #endif
    119 #endif
    120 
    121 #ifdef WIN32
    122 #include <time.h>
    123 #include <winsock2.h>
    124 
    125 #if HAVE_WS2TCPIP_H
    126 #include <ws2tcpip.h>
    127 #endif
    128 
    129 #include <windows.h>
    130 
    131 #include "missing\stdint.h"
    132 
    133 #define strdup _strdup
    134 #define sleep(x) Sleep((x)*1000)
    135 #define netperf_socklen_t socklen_t
    136 #endif /* WIN32 */
    137 
    138 /* unconditional system includes */
    139 
    140 #include <sys/types.h>
    141 #include <stdio.h>
    142 #include <fcntl.h>
    143 
    144 /* netperf includes */
    145 #include "netlib.h"
    146 #include "nettest_bsd.h"
    147 
    148 #ifdef WANT_UNIX
    149 #include "nettest_unix.h"
    150 #endif /* WANT_UNIX */
    151 
    152 #ifdef WANT_DLPI
    153 #include "nettest_dlpi.h"
    154 #endif /* WANT_DLPI */
    155 
    156 #ifdef WANT_SCTP
    157 #include "nettest_sctp.h"
    158 #endif
    159 
    160 #include "netsh.h"
    161 
    162 #ifndef DEBUG_LOG_FILE_DIR
    163 #if defined(WIN32)
    164 #define DEBUG_LOG_FILE_DIR ""
    165 #elif defined(ANDROID)
    166 #define DEBUG_LOG_FILE_DIR "/data/local/tmp/"
    167 #else
    168 #define DEBUG_LOG_FILE_DIR "/tmp/"
    169 #endif
    170 #endif /* DEBUG_LOG_FILE_DIR */
    171 
    172 #ifndef DEBUG_LOG_FILE
    173 #define DEBUG_LOG_FILE DEBUG_LOG_FILE_DIR"netserver.debug"
    174 #endif
    175 
    176 #if !defined(PATH_MAX)
    177 #define PATH_MAX MAX_PATH
    178 #endif
    179 char     FileName[PATH_MAX];
    180 
    181 char     listen_port[10];
    182 
    183 struct listen_elt {
    184   SOCKET fd;
    185   struct listen_elt *next;
    186 };
    187 
    188 struct listen_elt *listen_list = NULL;
    189 
    190 SOCKET   server_control;
    191 
    192 int      child;   /* are we the child of inetd or a parent netserver?
    193 		     */
    194 int      netperf_daemon;
    195 int      daemon_parent = 0;
    196 int      not_inetd;
    197 int      want_daemonize;
    198 int      spawn_on_accept;
    199 int      suppress_debug = 0;
    200 
    201 extern	char	*optarg;
    202 extern	int	optind, opterr;
    203 
    204 /* char  *passphrase = NULL; */
    205 
    206 static void
    207 init_netserver_globals() {
    208 
    209 #if defined(__VMS) || defined(VMWARE_UW)
    210   spawn_on_accept = 0;
    211   want_daemonize = 0;
    212 #else
    213   spawn_on_accept = 1;
    214 #if defined(WIN32)
    215   /* we only know how to spawn in WIN32, not daemonize */
    216   want_daemonize = 0;
    217 #else
    218   want_daemonize = 1;
    219 #endif /* WIN32 */
    220 #endif /* __VMS || VMWARE_UW */
    221 
    222   child = 0;
    223   not_inetd = 0;
    224   netperf_daemon = 0;
    225 }
    226 
    227 void
    228 unlink_empty_debug_file() {
    229 
    230 #if !defined(WIN32)
    231   struct stat buf;
    232 
    233   if (stat(FileName,&buf)== 0) {
    234 
    235     if (buf.st_size == 0)
    236       unlink(FileName);
    237   }
    238 #endif
    239 }
    240 
    241 /* it is important that set_server_sock() be called before this
    242    routine as we depend on the control socket being dup()ed out of the
    243    way when we go messing about with the streams. */
    244 void
    245 open_debug_file()
    246 {
    247 #if !defined WIN32
    248 #define NETPERF_NULL "/dev/null"
    249 #else
    250 #define NETPERF_NULL "nul"
    251 #endif
    252 
    253   FILE *rd_null_fp;
    254 
    255   if (where != NULL) fflush(where);
    256 
    257   snprintf(FileName,
    258 	   sizeof(FileName),
    259 #if defined(WIN32)
    260 	   "%s\\%s_%d",
    261 	   getenv("TEMP"),
    262 #else
    263 	   "%s_%d",
    264 #endif
    265 	   DEBUG_LOG_FILE,
    266 	   getpid());
    267   if ((where = fopen((suppress_debug) ? NETPERF_NULL : FileName,
    268 		     "w")) == NULL) {
    269     perror("netserver: debug file");
    270     exit(1);
    271   }
    272 
    273 #if !defined(WIN32)
    274 
    275   chmod(FileName,0644);
    276 
    277   /* redirect stdin to "/dev/null" */
    278   rd_null_fp = fopen(NETPERF_NULL,"r");
    279   if (NULL == rd_null_fp) {
    280     fprintf(where,
    281 	    "%s: opening of %s failed: %s (errno %d)\n",
    282 	    __FUNCTION__,
    283 	    NETPERF_NULL,
    284 	    strerror(errno),
    285 	    errno);
    286     fflush(where);
    287     exit(1);
    288   }
    289 
    290   if (close(STDIN_FILENO) == -1) {
    291     fprintf(where,
    292 	    "%s: close of STDIN_FILENO failed: %s (errno %d)\n",
    293 	    __FUNCTION__,
    294 	    strerror(errno),
    295 	    errno);
    296     fflush(where);
    297     exit(1);
    298   }
    299 
    300   if (dup(fileno(rd_null_fp)) == -1) {
    301     fprintf(where,
    302 	    "%s: dup of rd_null_fp to stdin failed: %s (errno %d)\n",
    303 	    __FUNCTION__,
    304 	    strerror(errno),
    305 	    errno);
    306     fflush(where);
    307     exit(1);
    308   }
    309 
    310   /* redirect stdout to "where" */
    311   if (close(STDOUT_FILENO) == -1) {
    312     fprintf(where,
    313 	    "%s: close of STDOUT_FILENO failed: %s (errno %d)\n",
    314 	    __FUNCTION__,
    315 	    strerror(errno),
    316 	    errno);
    317     fflush(where);
    318     exit(1);
    319   }
    320 
    321   if (dup(fileno(where)) == -1) {
    322     fprintf(where,
    323 	    "%s: dup of where to stdout failed: %s (errno %d)\n",
    324 	    __FUNCTION__,
    325 	    strerror(errno),
    326 	    errno);
    327     fflush(where);
    328     exit(1);
    329   }
    330 
    331   /* redirect stderr to "where" */
    332   if (close(STDERR_FILENO) == -1) {
    333     fprintf(where,
    334 	    "%s: close of STDERR_FILENO failed: %s (errno %d)\n",
    335 	    __FUNCTION__,
    336 	    strerror(errno),
    337 	    errno);
    338     fflush(where);
    339     exit(1);
    340   }
    341 
    342   if (dup(fileno(where)) == -1) {
    343     fprintf(where,
    344 	    "%s: dup of where to stderr failed: %s (errno %d)\n",
    345 	    __FUNCTION__,
    346 	    strerror(errno),
    347 	    errno);
    348     fflush(where);
    349     exit(1);
    350   }
    351 
    352 #else
    353 
    354   /* Hopefully, by closing stdout & stderr, the subsequent fopen calls
    355      will get mapped to the correct std handles. */
    356   fclose(stdout);
    357 
    358   if ((where = fopen(FileName, "w")) == NULL) {
    359     perror("netserver: fopen of debug file as new stdout failed!");
    360     exit(1);
    361   }
    362 
    363   fclose(stderr);
    364 
    365   if ((where = fopen(FileName, "w")) == NULL) {
    366     fprintf(stdout, "fopen of debug file as new stderr failed!\n");
    367     exit(1);
    368   }
    369 
    370 #endif
    371 
    372 }
    373 
    374 /* so, either we are a child of inetd in which case server_sock should
    375    be stdin, or we are a child of a netserver parent.  there will be
    376    logic here for all of it, including Windows. it is important that
    377    this be called before open_debug_file() */
    378 
    379 void
    380 set_server_sock() {
    381 
    382   if (debug) {
    383     fprintf(where,
    384 	    "%s: enter\n",
    385 	    __FUNCTION__);
    386     fflush(where);
    387   }
    388 
    389 #ifdef WIN32
    390   server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE);
    391 #elif !defined(__VMS)
    392   if (server_sock != INVALID_SOCKET) {
    393     fprintf(where,"Yo, Iz ain't invalid!\n");
    394     fflush(where);
    395     exit(1);
    396   }
    397 
    398   /* we dup this to up the reference count so when we do redirection
    399      of the io streams we don't accidentally toast the control
    400      connection in the case of our being a child of inetd. */
    401   server_sock = dup(0);
    402 
    403 #else
    404   if ((server_sock =
    405        socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET) {
    406     fprintf(stderr,
    407 	    "%s: failed to grab aux server socket: %s (errno %s)\n",
    408 	    __FUNCTION__,
    409 	    strerror(errno),
    410 	    errno);
    411     fflush(stderr);
    412     exit(1);
    413   }
    414 #endif
    415 
    416 }
    417 
    418 
    419 void
    420 create_listens(char hostname[], char port[], int af) {
    421 
    422   struct addrinfo hints;
    423   struct addrinfo *local_res;
    424   struct addrinfo *local_res_temp;
    425   int count, error;
    426   int on = 1;
    427   SOCKET temp_socket;
    428   struct listen_elt *temp_elt;
    429 
    430   if (debug) {
    431     fprintf(stderr,
    432 	    "%s: called with host '%s' port '%s' family %s(%d)\n",
    433 	    __FUNCTION__,
    434             hostname,
    435 	    port,
    436 	    inet_ftos(af),
    437             af);
    438     fflush(stderr);
    439   }
    440  memset(&hints,0,sizeof(hints));
    441   hints.ai_family = af;
    442   hints.ai_socktype = SOCK_STREAM;
    443   hints.ai_protocol = IPPROTO_TCP;
    444   hints.ai_flags = AI_PASSIVE;
    445 
    446   count = 0;
    447   do {
    448     error = getaddrinfo((char *)hostname,
    449                         (char *)port,
    450                         &hints,
    451                         &local_res);
    452     count += 1;
    453     if (error == EAI_AGAIN) {
    454       if (debug) {
    455         fprintf(stderr,
    456 		"%s: Sleeping on getaddrinfo EAI_AGAIN\n",
    457 		__FUNCTION__);
    458         fflush(stderr);
    459       }
    460       sleep(1);
    461     }
    462   } while ((error == EAI_AGAIN) && (count <= 5));
    463 
    464   if (error) {
    465     if (debug) {
    466 
    467       fprintf(stderr,
    468 	      "%s: could not resolve remote '%s' port '%s' af %d\n"
    469 	      "\tgetaddrinfo returned %s (%d)\n",
    470 	      __FUNCTION__,
    471 	      hostname,
    472 	      port,
    473 	      af,
    474 	      gai_strerror(error),
    475 	      error);
    476 
    477     }
    478     return;
    479   }
    480 
    481   if (debug) {
    482     dump_addrinfo(stderr, local_res, hostname, port, af);
    483   }
    484 
    485   local_res_temp = local_res;
    486 
    487   while (local_res_temp != NULL) {
    488 
    489     temp_socket = socket(local_res_temp->ai_family,SOCK_STREAM,0);
    490 
    491     if (temp_socket == INVALID_SOCKET) {
    492       if (debug) {
    493 	fprintf(stderr,
    494 		"%s could not allocate a socket: %s (errno %d)\n",
    495 		__FUNCTION__,
    496 		strerror(errno),
    497 		errno);
    498 	fflush(stderr);
    499       }
    500       local_res_temp = local_res_temp->ai_next;
    501       continue;
    502     }
    503 
    504     /* happiness and joy, keep going */
    505     if (setsockopt(temp_socket,
    506 		   SOL_SOCKET,
    507 		   SO_REUSEADDR,
    508 		   (char *)&on ,
    509 		   sizeof(on)) == SOCKET_ERROR) {
    510       if (debug) {
    511 	fprintf(stderr,
    512 		"%s: warning: could not set SO_REUSEADDR: %s (errno %d)\n",
    513 		__FUNCTION__,
    514 		strerror(errno),
    515 		errno);
    516 	fflush(stderr);
    517       }
    518     }
    519     /* still happy and joyful */
    520 
    521     if ((bind(temp_socket,
    522 	      local_res_temp->ai_addr,
    523 	      local_res_temp->ai_addrlen) != SOCKET_ERROR) &&
    524 	(listen(temp_socket,1024) != SOCKET_ERROR))  {
    525 
    526       /* OK, now add to the list */
    527       temp_elt = (struct listen_elt *)malloc(sizeof(struct listen_elt));
    528       if (temp_elt) {
    529 	temp_elt->fd = temp_socket;
    530 	if (listen_list) {
    531 	  temp_elt->next = listen_list;
    532 	}
    533 	else {
    534 	  temp_elt->next = NULL;
    535 	}
    536 	listen_list = temp_elt;
    537       }
    538       else {
    539 	fprintf(stderr,
    540 		"%s: could not malloc a listen_elt\n",
    541 		__FUNCTION__);
    542 	fflush(stderr);
    543 	exit(1);
    544       }
    545     }
    546     else {
    547       /* we consider a bind() or listen() failure a transient and try
    548 	 the next address */
    549       if (debug) {
    550 	fprintf(stderr,
    551 		"%s: warning: bind or listen call failure: %s (errno %d)\n",
    552 		__FUNCTION__,
    553 		strerror(errno),
    554 		errno);
    555 	fflush(stderr);
    556       }
    557       close(temp_socket);
    558     }
    559     local_res_temp = local_res_temp->ai_next;
    560   }
    561 
    562 }
    563 
    564 void
    565 setup_listens(char name[], char port[], int af) {
    566 
    567   int do_inet;
    568   int no_name = 0;
    569 #ifdef AF_INET6
    570   int do_inet6;
    571 #endif
    572 
    573   if (debug) {
    574     fprintf(where,
    575 	    "%s: enter\n",
    576 	    __FUNCTION__);
    577     fflush(where);
    578   }
    579 
    580 
    581   if (strcmp(name,"") == 0) {
    582     no_name = 1;
    583     switch (af) {
    584     case AF_UNSPEC:
    585       do_inet = 1;
    586 #ifdef AF_INET6
    587       do_inet6 = 1;
    588 #endif
    589       break;
    590     case AF_INET:
    591       do_inet = 1;
    592 #ifdef AF_INET6
    593       do_inet6 = 0;
    594 #endif
    595       break;
    596 #ifdef AF_INET6
    597     case AF_INET6:
    598       do_inet = 0;
    599       do_inet6 = 1;
    600       break;
    601 #endif
    602     default:
    603       do_inet = 1;
    604     }
    605     /* if we have IPv6, try that one first because it may be a superset */
    606 #ifdef AF_INET6
    607     if (do_inet6)
    608       create_listens("::0",port,AF_INET6);
    609 #endif
    610     if (do_inet)
    611       create_listens("0.0.0.0",port,AF_INET);
    612   }
    613   else {
    614     create_listens(name,port,af);
    615   }
    616 
    617   if (listen_list) {
    618     fprintf(stdout,
    619 	    "Starting netserver with host '%s' port '%s' and family %s\n",
    620 	    (no_name) ? "IN(6)ADDR_ANY" : name,
    621 	    port,
    622 	    inet_ftos(af));
    623     fflush(stdout);
    624   }
    625   else {
    626     fprintf(stderr,
    627 	    "Unable to start netserver with  '%s' port '%s' and family %s\n",
    628 	    (no_name) ? "IN(6)ADDR_ANY" : name,
    629 	    port,
    630 	    inet_ftos(af));
    631     fflush(stderr);
    632     exit(1);
    633   }
    634 }
    635 
    636 SOCKET
    637 set_fdset(struct listen_elt *list, fd_set *fdset) {
    638 
    639   struct listen_elt *temp;
    640   SOCKET max = INVALID_SOCKET;
    641 
    642   FD_ZERO(fdset);
    643 
    644   temp = list;
    645 
    646   if (debug) {
    647     fprintf(where,
    648 	    "%s: enter list %p fd_set %p\n",
    649 	    __FUNCTION__,
    650 	    list,
    651 	    fdset);
    652     fflush(where);
    653   }
    654 
    655   while (temp) {
    656     if (temp->fd > max)
    657       max = temp->fd;
    658 
    659     if (debug) {
    660       fprintf(where,
    661 	      "setting %d in fdset\n",
    662 	      temp->fd);
    663       fflush(where);
    664     }
    665 
    666     FD_SET(temp->fd,fdset);
    667 
    668     temp = temp->next;
    669   }
    670 
    671   return max;
    672 
    673 }
    674 
    675 void
    676 close_listens(struct listen_elt *list) {
    677   struct listen_elt *temp;
    678 
    679   if (debug) {
    680     fprintf(where,
    681 	    "%s: enter\n",
    682 	    __FUNCTION__);
    683     fflush(where);
    684   }
    685 
    686   temp = list;
    687 
    688   while (temp) {
    689     close(temp->fd);
    690     temp = temp->next;
    691   }
    692 }
    693 
    694 static int
    695 recv_passphrase() {
    696 
    697   /* may need to revisit the timeout. we only respond if there is an
    698      error with receiving the passphrase */
    699   if ((recv_request_timed_n(0,20) > 0) &&
    700       (netperf_request.content.request_type == PASSPHRASE) &&
    701       (!strcmp(passphrase,
    702 	       (char *)netperf_request.content.test_specific_data))) {
    703     /* it was okey dokey */
    704     return 0;
    705   }
    706 #if defined(SEND_PASSPHRASE_RESPONSE)
    707   netperf_response.content.response_type = PASSPHRASE;
    708   netperf_response.content.serv_errno = 403;
    709     snprintf((char *)netperf_response.content.test_specific_data,
    710 	     sizeof(netperf_response.content.test_specific_data),
    711 	     "Sorry, unable to match with required passphrase\n");
    712   send_response_n(0);
    713 #endif
    714   fprintf(where,
    715 	  "Unable to match required passphrase.  Closing control connection\n");
    716   fflush(where);
    717 
    718   close(server_sock);
    719   return -1;
    720 }
    721 
    722 /* This routine implements the "main event loop" of the netperf server
    723    code. Code above it will have set-up the control connection so it
    724    can just merrily go about its business, which is to "schedule"
    725    performance tests on the server.  */
    726 
    727 void
    728 process_requests()
    729 {
    730 
    731   float	temp_rate;
    732 
    733   if (debug) {
    734     fprintf(where,
    735 	    "%s: enter\n",
    736 	    __FUNCTION__);
    737     fflush(where);
    738   }
    739 
    740   /* if the netserver was started with a passphrase, look for it in
    741      the first request to arrive.  if there is no passphrase in the
    742      first request we will end-up dumping the control connection. raj
    743      2012-01-23 */
    744 
    745   if ((passphrase != NULL)  && (recv_passphrase()))
    746       return;
    747 
    748   while (1) {
    749 
    750     if (recv_request() <= 0) {
    751       close(server_sock);
    752       return;
    753     }
    754 
    755     switch (netperf_request.content.request_type) {
    756 
    757     case DEBUG_ON:
    758       netperf_response.content.response_type = DEBUG_OK;
    759       if (!suppress_debug) {
    760 	debug++;
    761 
    762 	if (debug == 1) {
    763 	  /* we just flipped-on debugging, dump the request because
    764 	     recv_request/recv_request_n will not have dumped it as its
    765 	     dump_request() call is conditional on debug being set. raj
    766 	     2011-07-08 */
    767 	  dump_request();
    768 	}
    769       }
    770 
    771       send_response();
    772       break;
    773 
    774     case DEBUG_OFF:
    775       if (debug)
    776 	debug--;
    777       netperf_response.content.response_type = DEBUG_OK;
    778       send_response();
    779       /* we used to take the trouble to close the debug file, but SAF
    780 	 asked a good question when he asked "Why?" and since I cannot
    781 	 think of a good reason, I have removed the code. raj
    782 	 2011-07-08 */
    783       break;
    784 
    785     case DO_SYSINFO:
    786       {
    787 	netperf_response.content.response_type = SYSINFO_RESPONSE;
    788 
    789 	snprintf((char *)netperf_response.content.test_specific_data,
    790 		 sizeof(netperf_response.content.test_specific_data),
    791 		 "%c%s%c%s%c%s%c%s",
    792 		 ',',
    793 		 "Deprecated",
    794 		 ','
    795 ,		 "Deprecated",
    796 		 ',',
    797 		 "Deprecated",
    798 		 ',',
    799 		 "Deprecated");
    800 
    801 	send_response_n(0);
    802 	break;
    803       }
    804 
    805     case CPU_CALIBRATE:
    806       netperf_response.content.response_type = CPU_CALIBRATE;
    807       temp_rate = calibrate_local_cpu(0.0);
    808       bcopy((char *)&temp_rate,
    809 	    (char *)netperf_response.content.test_specific_data,
    810 	    sizeof(temp_rate));
    811       bcopy((char *)&lib_num_loc_cpus,
    812 	    (char *)netperf_response.content.test_specific_data +
    813 	            sizeof(temp_rate),
    814 	    sizeof(lib_num_loc_cpus));
    815       if (debug) {
    816 	fprintf(where,
    817 		"netserver: sending CPU information: rate is %g num cpu %d\n",
    818 		temp_rate,
    819 		lib_num_loc_cpus);
    820 	fflush(where);
    821       }
    822 
    823       /* we need the cpu_start, cpu_stop in the looper case to kill
    824          the child proceses raj 7/95 */
    825 
    826 #ifdef USE_LOOPER
    827       cpu_start(1);
    828       cpu_stop(1,&temp_rate);
    829 #endif /* USE_LOOPER */
    830 
    831       send_response();
    832       break;
    833 
    834     case DO_TCP_STREAM:
    835       recv_tcp_stream();
    836       break;
    837 
    838     case DO_TCP_MAERTS:
    839       recv_tcp_maerts();
    840       break;
    841 
    842     case DO_TCP_RR:
    843       recv_tcp_rr();
    844       break;
    845 
    846     case DO_TCP_CRR:
    847       recv_tcp_conn_rr();
    848       break;
    849 
    850     case DO_TCP_CC:
    851       recv_tcp_cc();
    852       break;
    853 
    854 #ifdef DO_1644
    855     case DO_TCP_TRR:
    856       recv_tcp_tran_rr();
    857       break;
    858 #endif /* DO_1644 */
    859 
    860 #ifdef DO_NBRR
    861     case DO_TCP_NBRR:
    862       recv_tcp_nbrr();
    863       break;
    864 #endif /* DO_NBRR */
    865 
    866     case DO_UDP_STREAM:
    867       recv_udp_stream();
    868       break;
    869 
    870     case DO_UDP_RR:
    871       recv_udp_rr();
    872       break;
    873 
    874 #ifdef WANT_DLPI
    875 
    876     case DO_DLPI_CO_RR:
    877       recv_dlpi_co_rr();
    878       break;
    879 
    880     case DO_DLPI_CL_RR:
    881       recv_dlpi_cl_rr();
    882       break;
    883 
    884     case DO_DLPI_CO_STREAM:
    885       recv_dlpi_co_stream();
    886       break;
    887 
    888     case DO_DLPI_CL_STREAM:
    889       recv_dlpi_cl_stream();
    890       break;
    891 
    892 #endif /* WANT_DLPI */
    893 
    894 #ifdef WANT_UNIX
    895 
    896     case DO_STREAM_STREAM:
    897       recv_stream_stream();
    898       break;
    899 
    900     case DO_STREAM_RR:
    901       recv_stream_rr();
    902       break;
    903 
    904     case DO_DG_STREAM:
    905       recv_dg_stream();
    906       break;
    907 
    908     case DO_DG_RR:
    909       recv_dg_rr();
    910       break;
    911 
    912 #endif /* WANT_UNIX */
    913 
    914 #ifdef WANT_XTI
    915     case DO_XTI_TCP_STREAM:
    916       recv_xti_tcp_stream();
    917       break;
    918 
    919     case DO_XTI_TCP_RR:
    920       recv_xti_tcp_rr();
    921       break;
    922 
    923     case DO_XTI_UDP_STREAM:
    924       recv_xti_udp_stream();
    925       break;
    926 
    927     case DO_XTI_UDP_RR:
    928       recv_xti_udp_rr();
    929       break;
    930 
    931 #endif /* WANT_XTI */
    932 
    933 #ifdef WANT_SCTP
    934     case DO_SCTP_STREAM:
    935       recv_sctp_stream();
    936       break;
    937 
    938     case DO_SCTP_STREAM_MANY:
    939       recv_sctp_stream_1toMany();
    940       break;
    941 
    942     case DO_SCTP_RR:
    943       recv_sctp_rr();
    944       break;
    945 
    946     case DO_SCTP_RR_MANY:
    947       recv_sctp_rr_1toMany();
    948       break;
    949 #endif
    950 
    951 #ifdef WANT_SDP
    952     case DO_SDP_STREAM:
    953       recv_sdp_stream();
    954       break;
    955 
    956     case DO_SDP_MAERTS:
    957       recv_sdp_maerts();
    958       break;
    959 
    960     case DO_SDP_RR:
    961       recv_sdp_rr();
    962       break;
    963 #endif
    964 
    965 #ifdef WANT_OMNI
    966     case DO_OMNI:
    967       recv_omni();
    968       break;
    969 #endif
    970 
    971     case PASSPHRASE:
    972       if (debug) {
    973 	fprintf(where,"Ignoring an unexpected passphrase control message\n");
    974 	fflush(where);
    975       }
    976       break;
    977 
    978     default:
    979       fprintf(where,"unknown test number %d\n",
    980 	      netperf_request.content.request_type);
    981       fflush(where);
    982       netperf_response.content.serv_errno=998;
    983       send_response();
    984       break;
    985 
    986     }
    987   }
    988 }
    989 
    990 /* the routine we call when we are going to spawn/fork/whatnot a child
    991    process from the parent netserver daemon. raj 2011-07-08 */
    992 void
    993 spawn_child() {
    994 
    995 #if defined(HAVE_FORK)
    996 
    997   if (debug) {
    998     fprintf(where,
    999 	    "%s: enter\n",
   1000 	    __FUNCTION__);
   1001     fflush(where);
   1002   }
   1003 
   1004 
   1005   /* flush the usual suspects */
   1006   fflush(stdin);
   1007   fflush(stdout);
   1008   fflush(stderr);
   1009   fflush(where);
   1010 
   1011   signal(SIGCLD,SIG_IGN);
   1012 
   1013   switch (fork()) {
   1014   case -1:
   1015     fprintf(where,
   1016 	    "%s: fork() error %s (errno %d)\n",
   1017 	    __FUNCTION__,
   1018 	    strerror(errno),
   1019 	    errno);
   1020     fflush(where);
   1021     exit(1);
   1022 
   1023   case 0:
   1024     /* we are the child, but not of inetd.  we don't know if we are
   1025        the child of a daemonized parent or not, so we still need to
   1026        worry about the standard file descriptors. raj 2011-07-11 */
   1027 
   1028     close_listens(listen_list);
   1029     open_debug_file();
   1030 
   1031     child = 1;
   1032     netperf_daemon = 0;
   1033     process_requests();
   1034     exit(0);
   1035     break;
   1036 
   1037   default:
   1038     /* we are the parent, not a great deal to do here, but we may
   1039        want to reap some children */
   1040 #if !defined(HAVE_SETSID)
   1041     /* Only call "waitpid()" if "setsid()" is not used. */
   1042     while(waitpid(-1, NULL, WNOHANG) > 0) {
   1043       if (debug) {
   1044 	fprintf(where,
   1045 		"%s: reaped a child process\n",
   1046 		__FUNCTION__);
   1047       }
   1048     }
   1049 #endif
   1050     break;
   1051   }
   1052 
   1053 #elif defined(WIN32)
   1054 
   1055   BOOL b;
   1056   char *cmdline;
   1057   int cmdline_length;
   1058   int cmd_index;
   1059   PROCESS_INFORMATION pi;
   1060   STARTUPINFO si;
   1061   int i;
   1062 
   1063   if (debug) {
   1064     fprintf(where,
   1065 	    "%s: enter\n",
   1066 	    __FUNCTION__);
   1067     fflush(where);
   1068   }
   1069 
   1070 
   1071   /* create the cmdline array based on strlen(program) + 80 chars */
   1072   cmdline_length = strlen(program) + 80;
   1073   cmdline = malloc(cmdline_length + 1);  // +1 for trailing null
   1074 
   1075   memset(&si, 0 , sizeof(STARTUPINFO));
   1076   si.cb = sizeof(STARTUPINFO);
   1077 
   1078   /* Pass the server_sock as stdin for the new process.  Hopefully
   1079      this will continue to be created with the OBJ_INHERIT
   1080      attribute. */
   1081   si.hStdInput = (HANDLE)server_sock;
   1082   si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
   1083   si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
   1084   si.dwFlags = STARTF_USESTDHANDLES;
   1085 
   1086   /* Build cmdline for child process */
   1087   strcpy(cmdline, program);
   1088   cmd_index = strlen(cmdline);
   1089   if (verbosity > 1) {
   1090     cmd_index += snprintf(&cmdline[cmd_index],
   1091 			  cmdline_length - cmd_index,
   1092 			  " -v %d",
   1093 			  verbosity);
   1094   }
   1095   for (i=0; i < debug; i++) {
   1096     cmd_index += snprintf(&cmdline[cmd_index],
   1097 			  cmdline_length - cmd_index,
   1098 			  " -d");
   1099   }
   1100   cmd_index += snprintf(&cmdline[cmd_index],
   1101 			cmdline_length - cmd_index,
   1102 			" -I %x",
   1103 			(int)(UINT_PTR)server_sock);
   1104 
   1105   /* are these -i settings even necessary? the command line scanning
   1106      does not seem to do anything with them */
   1107   cmd_index += snprintf(&cmdline[cmd_index],
   1108 			cmdline_length - cmd_index,
   1109 			" -i %x",
   1110 			(int)(UINT_PTR)server_control);
   1111   cmd_index += snprintf(&cmdline[cmd_index],
   1112 			cmdline_length - cmd_index,
   1113 			" -i %x",
   1114 			(int)(UINT_PTR)where);
   1115 
   1116   b = CreateProcess(NULL,    /* Application Name */
   1117 		    cmdline,
   1118 		    NULL,    /* Process security attributes */
   1119 		    NULL,    /* Thread security attributes */
   1120 		    TRUE,    /* Inherit handles */
   1121 		    0,       /* Creation flags
   1122 				PROCESS_QUERY_INFORMATION,  */
   1123 		    NULL,    /* Enviornment */
   1124 		    NULL,    /* Current directory */
   1125 		    &si,     /* StartupInfo */
   1126 		    &pi);
   1127   if (!b)
   1128     {
   1129       perror("CreateProcessfailure: ");
   1130       free(cmdline); /* even though we exit :) */
   1131       exit(1);
   1132     }
   1133 
   1134   /* We don't need the thread or process handles any more;
   1135      let them go away on their own timeframe. */
   1136 
   1137   CloseHandle(pi.hThread);
   1138   CloseHandle(pi.hProcess);
   1139 
   1140   /* the caller/parent will close server_sock */
   1141 
   1142   free(cmdline);
   1143 
   1144 #else
   1145 
   1146   fprintf(where,
   1147 	  "%s called on platform which cannot spawn children\n",
   1148 	  __FUNCTION__);
   1149   fflush(where);
   1150   exit(1);
   1151 
   1152 #endif /* HAVE_FORK */
   1153 }
   1154 
   1155 void
   1156 accept_connection(SOCKET listen_fd) {
   1157 
   1158   struct sockaddr_storage peeraddr;
   1159   netperf_socklen_t peeraddrlen;
   1160 #if defined(SO_KEEPALIVE)
   1161   int on = 1;
   1162 #endif
   1163 
   1164   if (debug) {
   1165     fprintf(where,
   1166 	    "%s: enter\n",
   1167 	    __FUNCTION__);
   1168     fflush(where);
   1169   }
   1170 
   1171   peeraddrlen = sizeof(peeraddr);
   1172 
   1173   /* while server_control is only used by the WIN32 path, but why
   1174      bother ifdef'ing it?  and besides, do we *really* need knowledge
   1175      of server_control in the WIN32 case? do we have to tell the
   1176      child about *all* the listen endpoints? raj 2011-07-08 */
   1177   server_control = listen_fd;
   1178 
   1179   if ((server_sock = accept(listen_fd,
   1180 			   (struct sockaddr *)&peeraddr,
   1181 			    &peeraddrlen)) == INVALID_SOCKET) {
   1182     fprintf(where,
   1183 	    "%s: accept failure: %s (errno %d)\n",
   1184 	    __FUNCTION__,
   1185 	    strerror(errno),
   1186 	    errno);
   1187     fflush(where);
   1188     exit(1);
   1189   }
   1190 
   1191 #if defined(SO_KEEPALIVE)
   1192   /* we are not terribly concerned if this does not work, it is merely
   1193      duct tape added to belts and suspenders. raj 2011-07-08 */
   1194   setsockopt(server_sock,
   1195 	     SOL_SOCKET,
   1196 	     SO_KEEPALIVE,
   1197 	     (const char *)&on,
   1198 	     sizeof(on));
   1199 #endif
   1200 
   1201   if (spawn_on_accept) {
   1202     spawn_child();
   1203     /* spawn_child() only returns when we are the parent */
   1204     close(server_sock);
   1205   }
   1206   else {
   1207     process_requests();
   1208   }
   1209 }
   1210 
   1211 void
   1212 accept_connections() {
   1213 
   1214   fd_set read_fds, write_fds, except_fds;
   1215   SOCKET high_fd, candidate;
   1216   int num_ready;
   1217 
   1218   if (debug) {
   1219     fprintf(where,
   1220 	    "%s: enter\n",
   1221 	    __FUNCTION__);
   1222     fflush(where);
   1223   }
   1224 
   1225   while (1) {
   1226 
   1227     FD_ZERO(&write_fds);
   1228     FD_ZERO(&except_fds);
   1229     high_fd = set_fdset(listen_list,&read_fds);
   1230 
   1231 #if !defined(WIN32)
   1232     num_ready = select(high_fd + 1,
   1233 #else
   1234     num_ready = select(1,
   1235 #endif
   1236 		       &read_fds,
   1237 		       &write_fds,
   1238 		       &except_fds,
   1239 		       NULL);
   1240 
   1241     if (num_ready < 0) {
   1242       fprintf(where,
   1243 	      "%s: select failure: %s (errno %d)\n",
   1244 	      __FUNCTION__,
   1245 	      strerror(errno),
   1246 	      errno);
   1247       fflush(where);
   1248       exit(1);
   1249     }
   1250 
   1251     /* try to keep things simple */
   1252     candidate = 0;
   1253     while ((num_ready) && (candidate <= high_fd)) {
   1254       if (FD_ISSET(candidate,&read_fds)) {
   1255 	accept_connection(candidate);
   1256 	FD_CLR(candidate,&read_fds);
   1257 	num_ready--;
   1258       }
   1259       else {
   1260 	candidate++;
   1261       }
   1262     }
   1263   }
   1264 }
   1265 
   1266 #ifndef WIN32
   1267 #define SERVER_ARGS "DdfhL:n:Np:v:VZ:46"
   1268 #else
   1269 #define SERVER_ARGS "DdfhL:n:Np:v:VZ:46I:i:"
   1270 #endif
   1271 void
   1272 scan_netserver_args(int argc, char *argv[]) {
   1273 
   1274   int c;
   1275   char arg1[BUFSIZ], arg2[BUFSIZ];
   1276 
   1277   if (debug) {
   1278     fprintf(where,
   1279 	    "%s: enter\n",
   1280 	    __FUNCTION__);
   1281     fflush(where);
   1282   }
   1283 
   1284   while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF){
   1285     switch (c) {
   1286     case '?':
   1287     case 'h':
   1288       print_netserver_usage();
   1289       exit(1);
   1290     case 'd':
   1291       debug++;
   1292       suppress_debug = 0;
   1293       break;
   1294     case 'D':
   1295       /* perhaps one of these days we'll take an argument */
   1296       want_daemonize = 0;
   1297       not_inetd = 1;
   1298       break;
   1299     case 'f':
   1300       spawn_on_accept = 0;
   1301       not_inetd = 1;
   1302       break;
   1303 #ifdef WIN32
   1304     case 'I':
   1305       child = TRUE;
   1306       break;
   1307     case 'i':
   1308       break;
   1309 #endif
   1310     case 'L':
   1311       not_inetd = 1;
   1312       break_args_explicit(optarg,arg1,arg2);
   1313       if (arg1[0]) {
   1314 	strncpy(local_host_name,arg1,sizeof(local_host_name));
   1315       }
   1316       if (arg2[0]) {
   1317 	local_address_family = parse_address_family(arg2);
   1318       }
   1319       break;
   1320     case 'n':
   1321       shell_num_cpus = atoi(optarg);
   1322       if (shell_num_cpus > MAXCPUS) {
   1323 	fprintf(stderr,
   1324 		"netserver: This version can only support %d CPUs. Please"
   1325 		"increase MAXCPUS in netlib.h and recompile.\n",
   1326 		MAXCPUS);
   1327 	fflush(stderr);
   1328 	exit(1);
   1329       }
   1330       break;
   1331     case 'N':
   1332       suppress_debug = 1;
   1333       debug = 0;
   1334       break;
   1335     case 'p':
   1336       /* we want to open a listen socket at a specified port number */
   1337       strncpy(listen_port,optarg,sizeof(listen_port));
   1338       not_inetd = 1;
   1339       break;
   1340     case 'Z':
   1341       /* only copy as much of the passphrase as could fit in the
   1342 	 test-specific portion of a control message. Windows does not
   1343 	 seem to have a strndup() so just malloc and strncpy it.  we
   1344 	 weren't checking the strndup() return so won't bother with
   1345 	 checking malloc(). we will though make certain we only
   1346 	 allocated it once in the event that someone puts -Z on the
   1347 	 command line more than once */
   1348       if (passphrase == NULL)
   1349 	passphrase = malloc(sizeof(netperf_request.content.test_specific_data));
   1350       strncpy(passphrase,
   1351 	      optarg,
   1352 	      sizeof(netperf_request.content.test_specific_data));
   1353       passphrase[sizeof(netperf_request.content.test_specific_data) - 1] = '\0';
   1354       break;
   1355     case '4':
   1356       local_address_family = AF_INET;
   1357       break;
   1358     case '6':
   1359 #if defined(AF_INET6)
   1360       local_address_family = AF_INET6;
   1361 #else
   1362       local_address_family = AF_UNSPEC;
   1363 #endif
   1364       break;
   1365     case 'v':
   1366       /* say how much to say */
   1367       verbosity = atoi(optarg);
   1368       break;
   1369     case 'V':
   1370       printf("Netperf version %s\n",NETPERF_VERSION);
   1371       exit(0);
   1372       break;
   1373 
   1374     }
   1375   }
   1376 }
   1377 
   1378 void
   1379 daemonize() {
   1380 #if defined(HAVE_FORK)
   1381 
   1382   if (debug) {
   1383     fprintf(where,
   1384 	    "%s: enter\n",
   1385 	    __FUNCTION__);
   1386     fflush(where);
   1387   }
   1388 
   1389   /* flush the usual suspects */
   1390   fflush(stdin);
   1391   fflush(stdout);
   1392   fflush(stderr);
   1393 
   1394   switch (fork()) {
   1395   case -1:
   1396     fprintf(stderr,
   1397 	    "%s: fork() error %s (errno %d)\n",
   1398 	    __FUNCTION__,
   1399 	    strerror(errno),
   1400 	    errno);
   1401     fflush(stderr);
   1402     exit(1);
   1403   case 0:
   1404 
   1405     /* perhaps belt and suspenders, but if we dump core, perhaps
   1406        better to do so here. we won't worry about the call being
   1407        successful though. raj 2011-07-08 */
   1408     chdir(DEBUG_LOG_FILE_DIR);
   1409 
   1410     /* we are the child. we should get a new "where" to match our new
   1411        pid */
   1412 
   1413     open_debug_file();
   1414 
   1415 #ifdef HAVE_SETSID
   1416       setsid();
   1417 #else
   1418       setpgrp();
   1419 #endif /* HAVE_SETSID */
   1420 
   1421       signal(SIGCLD, SIG_IGN);
   1422 
   1423       /* ok, we can start accepting control connections now */
   1424       accept_connections();
   1425 
   1426   default:
   1427     /* we are the parent, nothing to do but exit? */
   1428     exit(0);
   1429   }
   1430 
   1431 #else
   1432   fprintf(where,
   1433 	  "%s called on platform which cannot daemonize\n",
   1434 	  __FUNCTION__);
   1435   fflush(where);
   1436   exit(1);
   1437 #endif /* HAVE_FORK */
   1438 }
   1439 
   1440 static void
   1441 check_if_inetd() {
   1442 
   1443   if (debug) {
   1444     fprintf(where,
   1445 	    "%s: enter\n",
   1446 	    __FUNCTION__);
   1447     fflush(where);
   1448   }
   1449 
   1450   if (not_inetd) {
   1451     return;
   1452   }
   1453   else {
   1454 #if !defined(WIN32) && !defined(__VMS)
   1455     struct sockaddr_storage name;
   1456     netperf_socklen_t namelen;
   1457 
   1458     namelen = sizeof(name);
   1459     if (getsockname(0,
   1460 		    (struct sockaddr *)&name,
   1461 		    &namelen) == SOCKET_ERROR) {
   1462       not_inetd = 1;
   1463     }
   1464     else {
   1465       not_inetd = 0;
   1466       child = 1;
   1467     }
   1468 #endif
   1469   }
   1470 }
   1471 
   1472 /* OK, so how does all this work you ask?  Well, we are in a maze of
   1473    twisty options, all different.  Netserver can be invoked as a child
   1474    of inetd or the VMS auxiliary server process, or a parent netserver
   1475    process. In those cases, we could/should follow the "child"
   1476    path. However, there are really two "child" paths through the
   1477    netserver code.
   1478 
   1479    When this netserver is a child of a parent netserver in the
   1480    case of *nix, the child process will be created by a
   1481    spawn_child_process() in accept_connections() and will not hit the
   1482    "child" path here in main().
   1483 
   1484    When this netserver is a child of a parent netserver in the case of
   1485    windows, the child process will have been spawned via a
   1486    Create_Process() call in spawn_child_process() in
   1487    accept_connections, but will flow through here again. We rely on
   1488    the scan_netserver_args() call to have noticed the magic option
   1489    that tells us we are a child process.
   1490 
   1491    When this netserver is launched from the command line we will first
   1492    set-up the listen endpoint(s) for the controll connection.  At that
   1493    point we decide if we want to and can become our own daemon, or
   1494    stay attached to the "terminal."  When this happens under *nix, we
   1495    will again take a fork() path via daemonize() and will not come
   1496    back through main().  If we ever learn how to become our own daemon
   1497    under Windows, we will undoubtedly take a Create_Process() path
   1498    again and will come through main() once again - that is what the
   1499    "daemon" case here is all about.
   1500 
   1501    It is hoped that this is all much clearer than the old spaghetti
   1502    code that netserver had become.  raj 2011-07-11 */
   1503 
   1504 
   1505 int _cdecl
   1506 main(int argc, char *argv[]) {
   1507 
   1508 #ifdef WIN32
   1509   WSADATA	wsa_data ;
   1510 
   1511   /* Initialize the winsock lib do we still want version 2.2? */
   1512   if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){
   1513     printf("WSAStartup() failed : %lu\n", GetLastError()) ;
   1514     return -1 ;
   1515   }
   1516 #endif /* WIN32 */
   1517 
   1518   /* Save away the program name */
   1519   program = (char *)malloc(strlen(argv[0]) + 1);
   1520   if (program == NULL) {
   1521     printf("malloc for program name failed!\n");
   1522     return -1 ;
   1523   }
   1524   strcpy(program, argv[0]);
   1525 
   1526   init_netserver_globals();
   1527 
   1528   netlib_init();
   1529 
   1530   strncpy(local_host_name,"",sizeof(local_host_name));
   1531   local_address_family = AF_UNSPEC;
   1532   strncpy(listen_port,TEST_PORT,sizeof(listen_port));
   1533 
   1534   scan_netserver_args(argc, argv);
   1535 
   1536   check_if_inetd();
   1537 
   1538   if (child) {
   1539     /* we are the child of either an inetd or parent netserver via
   1540        spawning (Windows) rather than fork()ing. if we were fork()ed
   1541        we would not be coming through this way. set_server_sock() must
   1542        be called before open_debug_file() or there is a chance that
   1543        we'll toast the descriptor when we do not wish it. */
   1544     set_server_sock();
   1545     open_debug_file();
   1546     process_requests();
   1547   }
   1548   else if (daemon_parent) {
   1549     /* we are the parent daemonized netserver
   1550        process. accept_connections() will decide if we want to spawn a
   1551        child process */
   1552     accept_connections();
   1553   }
   1554   else {
   1555     /* we are the top netserver process, so we have to create the
   1556        listen endpoint(s) and decide if we want to daemonize */
   1557     setup_listens(local_host_name,listen_port,local_address_family);
   1558     if (want_daemonize) {
   1559       daemonize();
   1560     }
   1561     accept_connections();
   1562   }
   1563 
   1564   unlink_empty_debug_file();
   1565 
   1566 #ifdef WIN32
   1567   WSACleanup();
   1568 #endif
   1569 
   1570   return 0;
   1571 
   1572 }
   1573