1 /*
2 
3 	   Copyright (C) 1993-2007 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-2007 Hewlett-Packard Co. Version 2.4.3";
49 
50  /***********************************************************************/
51  /*									*/
52  /*	netserver.c							*/
53  /*									*/
54  /*	This is the server side code for the netperf test package. It	*/
55  /* will operate either stand-alone, or as a child of inetd. In this	*/
56  /* way, we insure that it can be installed on systems with or without	*/
57  /* root permissions (editing inetd.conf). Essentially, this code is	*/
58  /* the analog to the netsh.c code.					*/
59  /*									*/
60  /***********************************************************************/
61 
62 
63 /************************************************************************/
64 /*									*/
65 /*	Global include files						*/
66 /*									*/
67 /************************************************************************/
68 #ifdef HAVE_CONFIG_H
69 #include "config.h"
70 #endif
71 
72 #if HAVE_STRING_H
73 # if !STDC_HEADERS && HAVE_MEMORY_H
74 #  include <memory.h>
75 # endif
76 # include <string.h>
77 #endif
78 #if HAVE_STRINGS_H
79 # include <strings.h>
80 #endif
81 #if HAVE_LIMITS_H
82 # include <limits.h>
83 #endif
84 #include <sys/types.h>
85 #include <stdio.h>
86 #ifndef WIN32
87 #include <errno.h>
88 #include <signal.h>
89 #endif
90 #if !defined(WIN32) && !defined(__VMS)
91 #include <sys/ipc.h>
92 #endif /* !defined(WIN32) && !defined(__VMS) */
93 #include <fcntl.h>
94 #ifdef WIN32
95 #include <time.h>
96 #include <winsock2.h>
97 #define netperf_socklen_t socklen_t
98 /* we need to find some other way to decide to include ws2 */
99 /* if you are trying to compile on Windows 2000 or NT 4 you will */
100 /* probably have to define DONT_IPV6 */
101 #ifndef DONT_IPV6
102 #include <ws2tcpip.h>
103 #endif  /* DONT_IPV6 */
104 #include <windows.h>
105 #define sleep(x) Sleep((x)*1000)
106 #else
107 #ifndef MPE
108 #include <sys/time.h>
109 #endif /* MPE */
110 #include <sys/ioctl.h>
111 #include <sys/socket.h>
112 #include <sys/stat.h>
113 #include <netinet/in.h>
114 #include <netdb.h>
115 #include <unistd.h>
116 #ifndef DONT_WAIT
117 #include <sys/wait.h>
118 #endif /* DONT_WAIT */
119 #endif /* WIN32 */
120 #include <stdlib.h>
121 #ifdef __VMS
122 #include <tcpip$inetdef.h>
123 #include <unixio.h>
124 #endif /* __VMS */
125 #include "netlib.h"
126 #include "nettest_bsd.h"
127 
128 #ifdef WANT_UNIX
129 #include "nettest_unix.h"
130 #endif /* WANT_UNIX */
131 
132 #ifdef WANT_DLPI
133 #include "nettest_dlpi.h"
134 #endif /* WANT_DLPI */
135 
136 #ifdef WANT_SCTP
137 #include "nettest_sctp.h"
138 #endif
139 
140 #include "netsh.h"
141 
142 #ifndef DEBUG_LOG_FILE
143 #ifndef WIN32
144 #define DEBUG_LOG_FILE "/tmp/netperf.debug"
145 #else
146 #define DEBUG_LOG_FILE "c:\\temp\\netperf.debug"
147 #endif  /* WIN32 */
148 #endif /* DEBUG_LOG_FILE */
149 
150  /* some global variables */
151 
152 FILE	*afp;
153 char    listen_port[10];
154 extern	char	*optarg;
155 extern	int	optind, opterr;
156 
157 #ifndef WIN32
158 #define SERVER_ARGS "dL:n:p:v:V46"
159 #else
160 #define SERVER_ARGS "dL:n:p:v:V46I:i:"
161 #endif
162 
163 /* perhaps one day we will use this as part of only opening a debug
164    log file if debug is set, of course we have to be wary of the base
165    use of "where" and so probably always need "where" pointing
166    "somewhere" or other. */
167 void
open_debug_file()168 open_debug_file()
169 {
170 #ifndef WIN32
171 #ifndef PATH_MAX
172 #define PATH_MAX MAX_PATH
173 #endif
174   char FileName[PATH_MAX];   /* for opening the debug log file */
175   strcpy(FileName, DEBUG_LOG_FILE);
176 
177   if (where != NULL) fflush(where);
178 
179   snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid());
180   if ((where = fopen(FileName, "w")) == NULL) {
181     perror("netserver: debug file");
182     exit(1);
183   }
184 
185   chmod(FileName,0644);
186 #endif
187 
188 }
189  /* This routine implements the "main event loop" of the netperf	*/
190  /* server code. Code above it will have set-up the control connection	*/
191  /* so it can just merrily go about its business, which is to		*/
192  /* "schedule" performance tests on the server.				*/
193 
194 void
process_requests()195 process_requests()
196 {
197 
198   float	temp_rate;
199 
200   if (debug)    open_debug_file();
201 
202 
203   while (1) {
204     recv_request();
205 
206     switch (netperf_request.content.request_type) {
207 
208     case DEBUG_ON:
209       netperf_response.content.response_type = DEBUG_OK;
210       /*  dump_request already present in recv_request; redundant? */
211       if (!debug) {
212 	debug++;
213 	open_debug_file();
214 	dump_request();
215       }
216       send_response();
217       break;
218 
219     case DEBUG_OFF:
220       if (debug)
221 	debug--;
222       netperf_response.content.response_type = DEBUG_OK;
223       send_response();
224       /* +SAF why??? */
225       if (!debug)
226       {
227 	fclose(where);
228 #if !defined(WIN32) && !defined(MPE) && !defined(__VMS)
229 	/* For Unix: reopen the debug write file descriptor to "/dev/null" */
230 	/* and redirect stdout to it.					   */
231 	fflush (stdout);
232 	where = fopen ("/dev/null", "w");
233 	if (where == NULL)
234 	{
235 	  perror ("netserver: reopening debug fp for writing: /dev/null");
236 	  exit   (1);
237 	}
238 	if (close (STDOUT_FILENO) == -1)
239 	{
240 	  perror ("netserver: closing stdout file descriptor");
241 	  exit   (1);
242 	}
243 	if (dup (fileno (where))  == -1)
244 	{
245 	  perror ("netserver: duplicate /dev/null write file descr. to stdout");
246 	  exit   (1);
247 	}
248 #endif /* !WIN32 !MPE !__VMS */
249       }
250       break;
251 
252     case CPU_CALIBRATE:
253       netperf_response.content.response_type = CPU_CALIBRATE;
254       temp_rate = calibrate_local_cpu(0.0);
255       bcopy((char *)&temp_rate,
256 	    (char *)netperf_response.content.test_specific_data,
257 	    sizeof(temp_rate));
258       bcopy((char *)&lib_num_loc_cpus,
259 	    (char *)netperf_response.content.test_specific_data + sizeof(temp_rate),
260 	    sizeof(lib_num_loc_cpus));
261       if (debug) {
262 	fprintf(where,"netserver: sending CPU information:");
263 	fprintf(where,"rate is %g num cpu %d\n",temp_rate,lib_num_loc_cpus);
264 	fflush(where);
265       }
266 
267       /* we need the cpu_start, cpu_stop in the looper case to kill the */
268       /* child proceses raj 7/95 */
269 
270 #ifdef USE_LOOPER
271       cpu_start(1);
272       cpu_stop(1,&temp_rate);
273 #endif /* USE_LOOPER */
274 
275       send_response();
276       break;
277 
278     case DO_TCP_STREAM:
279       recv_tcp_stream();
280       break;
281 
282     case DO_TCP_MAERTS:
283       recv_tcp_maerts();
284       break;
285 
286     case DO_TCP_RR:
287       recv_tcp_rr();
288       break;
289 
290     case DO_TCP_CRR:
291       recv_tcp_conn_rr();
292       break;
293 
294     case DO_TCP_CC:
295       recv_tcp_cc();
296       break;
297 
298 #ifdef DO_1644
299     case DO_TCP_TRR:
300       recv_tcp_tran_rr();
301       break;
302 #endif /* DO_1644 */
303 
304 #ifdef DO_NBRR
305     case DO_TCP_NBRR:
306       recv_tcp_nbrr();
307       break;
308 #endif /* DO_NBRR */
309 
310     case DO_UDP_STREAM:
311       recv_udp_stream();
312       break;
313 
314     case DO_UDP_RR:
315       recv_udp_rr();
316       break;
317 
318 #ifdef WANT_DLPI
319 
320     case DO_DLPI_CO_RR:
321       recv_dlpi_co_rr();
322       break;
323 
324     case DO_DLPI_CL_RR:
325       recv_dlpi_cl_rr();
326       break;
327 
328     case DO_DLPI_CO_STREAM:
329       recv_dlpi_co_stream();
330       break;
331 
332     case DO_DLPI_CL_STREAM:
333       recv_dlpi_cl_stream();
334       break;
335 
336 #endif /* WANT_DLPI */
337 
338 #ifdef WANT_UNIX
339 
340     case DO_STREAM_STREAM:
341       recv_stream_stream();
342       break;
343 
344     case DO_STREAM_RR:
345       recv_stream_rr();
346       break;
347 
348     case DO_DG_STREAM:
349       recv_dg_stream();
350       break;
351 
352     case DO_DG_RR:
353       recv_dg_rr();
354       break;
355 
356 #endif /* WANT_UNIX */
357 
358 #ifdef WANT_XTI
359     case DO_XTI_TCP_STREAM:
360       recv_xti_tcp_stream();
361       break;
362 
363     case DO_XTI_TCP_RR:
364       recv_xti_tcp_rr();
365       break;
366 
367     case DO_XTI_UDP_STREAM:
368       recv_xti_udp_stream();
369       break;
370 
371     case DO_XTI_UDP_RR:
372       recv_xti_udp_rr();
373       break;
374 
375 #endif /* WANT_XTI */
376 
377 #ifdef WANT_SCTP
378     case DO_SCTP_STREAM:
379       recv_sctp_stream();
380       break;
381 
382     case DO_SCTP_STREAM_MANY:
383       recv_sctp_stream_1toMany();
384       break;
385 
386     case DO_SCTP_RR:
387       recv_sctp_rr();
388       break;
389 
390     case DO_SCTP_RR_MANY:
391       recv_sctp_rr_1toMany();
392       break;
393 #endif
394 
395 #ifdef WANT_SDP
396     case DO_SDP_STREAM:
397       recv_sdp_stream();
398       break;
399 
400     case DO_SDP_MAERTS:
401       recv_sdp_maerts();
402       break;
403 
404     case DO_SDP_RR:
405       recv_sdp_rr();
406       break;
407 #endif
408 
409     default:
410       fprintf(where,"unknown test number %d\n",
411 	      netperf_request.content.request_type);
412       fflush(where);
413       netperf_response.content.serv_errno=998;
414       send_response();
415       break;
416 
417     }
418   }
419 }
420 
421 /*
422  set_up_server()
423 
424  set-up the server listen socket. we only call this routine if the
425  user has specified a port number on the command line or we believe we
426  are not a child of inetd or its platform-specific equivalent */
427 
428 /*KC*/
429 
430 void
set_up_server(char hostname[],char port[],int af)431 set_up_server(char hostname[], char port[], int af)
432 {
433 
434   struct addrinfo     hints;
435   struct addrinfo     *local_res;
436   struct addrinfo     *local_res_temp;
437 
438   struct sockaddr_storage     peeraddr;
439   netperf_socklen_t                 peeraddr_len = sizeof(peeraddr);
440 
441   SOCKET server_control;
442   int on=1;
443   int count;
444   int error;
445   int not_listening;
446 
447 #if !defined(WIN32) && !defined(MPE) && !defined(__VMS)
448   FILE *rd_null_fp;    /* Used to redirect from "/dev/null". */
449   FILE *wr_null_fp;    /* Used to redirect to   "/dev/null". */
450 #endif /* !WIN32 !MPE !__VMS */
451 
452   if (debug) {
453     fprintf(stderr,
454             "set_up_server called with host '%s' port '%s' remfam %d\n",
455             hostname,
456 	    port,
457             af);
458     fflush(stderr);
459   }
460 
461   memset(&hints,0,sizeof(hints));
462   hints.ai_family = af;
463   hints.ai_socktype = SOCK_STREAM;
464   hints.ai_protocol = IPPROTO_TCP;
465   hints.ai_flags = AI_PASSIVE;
466 
467   count = 0;
468   do {
469     error = getaddrinfo((char *)hostname,
470                         (char *)port,
471                         &hints,
472                         &local_res);
473     count += 1;
474     if (error == EAI_AGAIN) {
475       if (debug) {
476         fprintf(stderr,"Sleeping on getaddrinfo EAI_AGAIN\n");
477         fflush(stderr);
478       }
479       sleep(1);
480     }
481   } while ((error == EAI_AGAIN) && (count <= 5));
482 
483   if (error) {
484     fprintf(stderr,
485 	    "set_up_server: could not resolve remote '%s' port '%s' af %d",
486 	    hostname,
487 	    port,
488 	    af);
489     fprintf(stderr,"\n\tgetaddrinfo returned %d %s\n",
490 	    error,
491 	    gai_strerror(error));
492     exit(-1);
493   }
494 
495   if (debug) {
496     dump_addrinfo(stderr, local_res, hostname, port, af);
497   }
498 
499   not_listening = 1;
500   local_res_temp = local_res;
501 
502   while((local_res_temp != NULL) && (not_listening)) {
503 
504     fprintf(stderr,
505 	    "Starting netserver at port %s\n",
506 	    port);
507 
508     server_control = socket(local_res_temp->ai_family,SOCK_STREAM,0);
509 
510     if (server_control == INVALID_SOCKET) {
511       perror("set_up_server could not allocate a socket");
512       exit(-1);
513     }
514 
515     /* happiness and joy, keep going */
516     if (setsockopt(server_control,
517 		   SOL_SOCKET,
518 		   SO_REUSEADDR,
519 		   (char *)&on ,
520 		   sizeof(on)) == SOCKET_ERROR) {
521       if (debug) {
522 	perror("warning: set_up_server could not set SO_REUSEADDR");
523       }
524     }
525     /* still happy and joyful */
526 
527     if ((bind (server_control,
528 	       local_res_temp->ai_addr,
529 	       local_res_temp->ai_addrlen) != SOCKET_ERROR) &&
530 	(listen (server_control,5) != SOCKET_ERROR))  {
531       not_listening = 0;
532       break;
533     }
534     else {
535       /* we consider a bind() or listen() failure a transient and try
536 	 the next address */
537       if (debug) {
538 	perror("warning: set_up_server failed a bind or listen call\n");
539       }
540       local_res_temp = local_res_temp->ai_next;
541       continue;
542     }
543   }
544 
545   if (not_listening) {
546     fprintf(stderr,
547 	    "set_up_server could not establish a listen endpoint for %s port %s with family %s\n",
548 	    host_name,
549 	    port,
550 	    inet_ftos(af));
551     fflush(stderr);
552     exit(-1);
553   }
554   else {
555     printf("Starting netserver at hostname %s port %s and family %s\n",
556 	   hostname,
557 	   port,
558 	   inet_ftos(af));
559   }
560 
561   /*
562     setpgrp();
563     */
564 
565 #if !defined(WIN32) && !defined(MPE) && !defined(__VMS)
566   /* Flush the standard I/O file descriptors before forking. */
567   fflush (stdin);
568   fflush (stdout);
569   fflush (stderr);
570   switch (fork())
571     {
572     case -1:
573       perror("netperf server error");
574       exit(1);
575 
576     case 0:
577       /* Redirect stdin from "/dev/null". */
578       rd_null_fp = fopen ("/dev/null", "r");
579       if (rd_null_fp == NULL)
580       {
581 	perror ("netserver: opening for reading: /dev/null");
582 	exit   (1);
583       }
584       if (close (STDIN_FILENO) == -1)
585       {
586 	perror ("netserver: closing stdin file descriptor");
587 	exit   (1);
588       }
589       if (dup (fileno (rd_null_fp)) == -1)
590       {
591 	perror ("netserver: duplicate /dev/null read file descr. to stdin");
592 	exit   (1);
593       }
594 
595       /* Redirect stdout to the debug write file descriptor. */
596       if (close (STDOUT_FILENO) == -1)
597       {
598 	perror ("netserver: closing stdout file descriptor");
599 	exit   (1);
600       }
601       if (dup (fileno (where))  == -1)
602       {
603 	perror ("netserver: duplicate the debug write file descr. to stdout");
604 	exit   (1);
605       }
606 
607       /* Redirect stderr to "/dev/null". */
608       wr_null_fp = fopen ("/dev/null", "w");
609       if (wr_null_fp == NULL)
610       {
611 	perror ("netserver: opening for writing: /dev/null");
612 	exit   (1);
613       }
614       if (close (STDERR_FILENO) == -1)
615       {
616 	perror ("netserver: closing stderr file descriptor");
617 	exit   (1);
618       }
619       if (dup (fileno (wr_null_fp))  == -1)
620       {
621 	perror ("netserver: dupicate /dev/null write file descr. to stderr");
622 	exit   (1);
623       }
624 
625 #ifndef NO_SETSID
626       setsid();
627 #else
628       setpgrp();
629 #endif /* NO_SETSID */
630 
631  /* some OS's have SIGCLD defined as SIGCHLD */
632 #ifndef SIGCLD
633 #define SIGCLD SIGCHLD
634 #endif /* SIGCLD */
635 
636       signal(SIGCLD, SIG_IGN);
637 
638 #endif /* !WIN32 !MPE !__VMS */
639 
640       for (;;)
641 	{
642 	  if ((server_sock=accept(server_control,
643 				  (struct sockaddr *)&peeraddr,
644 				  &peeraddr_len)) == INVALID_SOCKET)
645 	    {
646 	      printf("server_control: accept failed errno %d\n",errno);
647 	      exit(1);
648 	    }
649 #if defined(MPE) || defined(__VMS)
650 	  /*
651 	   * Since we cannot fork this process , we cant fire any threads
652 	   * as they all share the same global data . So we better allow
653 	   * one request at at time
654 	   */
655 	  process_requests() ;
656 #elif WIN32
657 		{
658 			BOOL b;
659 			char cmdline[80];
660 			PROCESS_INFORMATION pi;
661 			STARTUPINFO si;
662 			int i;
663 
664 			memset(&si, 0 , sizeof(STARTUPINFO));
665 			si.cb = sizeof(STARTUPINFO);
666 
667 			/* Pass the server_sock as stdin for the new process. */
668 			/* Hopefully this will continue to be created with the OBJ_INHERIT attribute. */
669 			si.hStdInput = (HANDLE)server_sock;
670 			si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
671 			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
672 			si.dwFlags = STARTF_USESTDHANDLES;
673 
674 			/* Build cmdline for child process */
675 			strcpy(cmdline, program);
676 			if (verbosity > 1) {
677 				snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -v %d", verbosity);
678 			}
679 			for (i=0; i < debug; i++) {
680 				snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -d");
681 			}
682 			snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -I %x", (int)(UINT_PTR)server_sock);
683 			snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)server_control);
684 			snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)where);
685 
686 			b = CreateProcess(NULL,	 /* Application Name */
687 					cmdline,
688 					NULL,    /* Process security attributes */
689 					NULL,    /* Thread security attributes */
690 					TRUE,    /* Inherit handles */
691 					0,	   /* Creation flags  PROCESS_QUERY_INFORMATION,  */
692 					NULL,    /* Enviornment */
693 					NULL,    /* Current directory */
694 					&si,	   /* StartupInfo */
695 					&pi);
696 			if (!b)
697 			{
698 				perror("CreateProcessfailure: ");
699 				exit(1);
700 			}
701 
702 			/* We don't need the thread or process handles any more; let them */
703 			/* go away on their own timeframe. */
704 
705 			CloseHandle(pi.hThread);
706 			CloseHandle(pi.hProcess);
707 
708 			/* And close the server_sock since the child will own it. */
709 
710 			close(server_sock);
711 		}
712 #else
713       signal(SIGCLD, SIG_IGN);
714 
715 	  switch (fork())
716 	    {
717 	    case -1:
718 	      /* something went wrong */
719 	      exit(1);
720 	    case 0:
721 	      /* we are the child process */
722 	      close(server_control);
723 	      process_requests();
724 	      exit(0);
725 	      break;
726 	    default:
727 	      /* we are the parent process */
728 	      close(server_sock);
729 	      /* we should try to "reap" some of our children. on some */
730 	      /* systems they are being left as defunct processes. we */
731 	      /* will call waitpid, looking for any child process, */
732 	      /* with the WNOHANG feature. when waitpid return a zero, */
733 	      /* we have reaped all the children there are to reap at */
734 	      /* the moment, so it is time to move on. raj 12/94 */
735 #ifndef DONT_WAIT
736 #ifdef NO_SETSID
737 	      /* Only call "waitpid()" if "setsid()" is not used. */
738 	      while(waitpid(-1, NULL, WNOHANG) > 0) { }
739 #endif /* NO_SETSID */
740 #endif /* DONT_WAIT */
741 	      break;
742 	    }
743 #endif /* !WIN32 !MPE !__VMS */
744 	} /*for*/
745 #if !defined(WIN32) && !defined(MPE) && !defined(__VMS)
746       break; /*case 0*/
747 
748     default:
749       exit (0);
750 
751     }
752 #endif /* !WIN32 !MPE !__VMS */
753 }
754 
755 #ifdef WIN32
756 
757   /* With Win2003, WinNT's POSIX subsystem is gone and hence so is */
758   /* fork. */
759 
760   /* But hopefully the kernel support will continue to exist for some */
761   /* time. */
762 
763   /* We are not counting on the child address space copy_on_write */
764   /* support, since it isn't exposed except through the NT native APIs */
765   /* (which is not public). */
766 
767   /* We will try to use the InheritHandles flag in CreateProcess.  It */
768   /* is in the public API, though it is documented as "must be FALSE". */
769 
770   /* So where we would have forked, we will now create a new process. */
771   /* I have added a set of command line switches to specify a list of */
772   /* handles that the child should close since they shouldn't have */
773   /* been inherited ("-i#"), and a single switch to specify the handle */
774   /* for the server_sock ("I#"). */
775 
776   /* A better alternative would be to re-write NetPerf to be */
777   /* multi-threaded; i.e., move all of the various NetPerf global */
778   /* variables in to thread specific structures.  But this is a bigger */
779   /* effort than I want to tackle at this time.  (And I doubt that the */
780   /* HP-UX author sees value in this effort). */
781 
782 #endif
783 
784 int _cdecl
main(int argc,char * argv[])785 main(int argc, char *argv[])
786 {
787 
788   int	c;
789   int   not_inetd = 0;
790 #ifdef WIN32
791   BOOL  child = FALSE;
792 #endif
793   char arg1[BUFSIZ], arg2[BUFSIZ];
794 #ifndef PATH_MAX
795 #define PATH_MAX MAX_PATH
796 #endif
797   char FileName[PATH_MAX];   /* for opening the debug log file */
798 
799   struct sockaddr name;
800   netperf_socklen_t namelen = sizeof(name);
801 
802 
803 #ifdef WIN32
804 	WSADATA	wsa_data ;
805 
806 	/* Initialize the winsock lib ( version 2.2 ) */
807 	if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){
808 		printf("WSAStartup() failed : %d\n", GetLastError()) ;
809 		return 1 ;
810 	}
811 #endif /* WIN32 */
812 
813 	/* Save away the program name */
814 	program = (char *)malloc(strlen(argv[0]) + 1);
815 	if (program == NULL) {
816 		printf("malloc(%zu) failed!\n", strlen(argv[0]) + 1);
817 		return 1 ;
818 	}
819 	strcpy(program, argv[0]);
820 
821   netlib_init();
822 
823   /* Scan the command line to see if we are supposed to set-up our own */
824   /* listen socket instead of relying on inetd. */
825 
826   /* first set a copy of initial values */
827   strncpy(local_host_name,"0.0.0.0",sizeof(local_host_name));
828   local_address_family = AF_UNSPEC;
829   strncpy(listen_port,TEST_PORT,sizeof(listen_port));
830 
831   while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF) {
832     switch (c) {
833     case '?':
834     case 'h':
835       print_netserver_usage();
836       exit(1);
837     case 'd':
838       /* we want to set the debug file name sometime */
839       debug++;
840       break;
841     case 'L':
842       not_inetd = 1;
843       break_args_explicit(optarg,arg1,arg2);
844       if (arg1[0]) {
845 	strncpy(local_host_name,arg1,sizeof(local_host_name));
846       }
847       if (arg2[0]) {
848 	local_address_family = parse_address_family(arg2);
849 	/* if only the address family was set, we may need to set the
850 	   local_host_name accordingly. since our defaults are IPv4
851 	   this should only be necessary if we have IPv6 support raj
852 	   2005-02-07 */
853 #if defined (AF_INET6)
854 	if (!arg1[0]) {
855 	  strncpy(local_host_name,"::0",sizeof(local_host_name));
856 	}
857 #endif
858       }
859       break;
860     case 'n':
861       shell_num_cpus = atoi(optarg);
862       if (shell_num_cpus > MAXCPUS) {
863 	fprintf(stderr,
864 		"netserver: This version can only support %d CPUs. Please",
865 		MAXCPUS);
866 	fprintf(stderr,
867 		"           increase MAXCPUS in netlib.h and recompile.\n");
868 	fflush(stderr);
869 	exit(1);
870       }
871       break;
872     case 'p':
873       /* we want to open a listen socket at a */
874       /* specified port number */
875       strncpy(listen_port,optarg,sizeof(listen_port));
876       not_inetd = 1;
877       break;
878     case '4':
879       local_address_family = AF_INET;
880       break;
881     case '6':
882 #if defined(AF_INET6)
883       local_address_family = AF_INET6;
884       strncpy(local_host_name,"::0",sizeof(local_host_name));
885 #else
886       local_address_family = AF_UNSPEC;
887 #endif
888       break;
889     case 'v':
890       /* say how much to say */
891       verbosity = atoi(optarg);
892       break;
893     case 'V':
894       printf("Netperf version %s\n",NETPERF_VERSION);
895       exit(0);
896       break;
897 #ifdef WIN32
898 /*+*+SAF */
899 	case 'I':
900 		child = TRUE;
901 		/* This is the handle we expect to inherit. */
902 		/*+*+SAF server_sock = (HANDLE)atoi(optarg); */
903 		break;
904 	case 'i':
905 		/* This is a handle we should NOT inherit. */
906 		/*+*+SAF CloseHandle((HANDLE)atoi(optarg)); */
907 		break;
908 #endif
909 
910     }
911   }
912 
913   /* +*+SAF I need a better way to find inherited handles I should close! */
914   /* +*+SAF Use DuplicateHandle to force inheritable attribute (or reset it)? */
915 
916 /*  unlink(DEBUG_LOG_FILE); */
917 
918   strcpy(FileName, DEBUG_LOG_FILE);
919 
920 #ifndef WIN32
921   snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid());
922   if ((where = fopen(FileName, "w")) == NULL) {
923     perror("netserver: debug file");
924     exit(1);
925   }
926 #else
927   {
928 
929     if (child) {
930       snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%x", getpid());
931     }
932 
933     /* Hopefully, by closing stdout & stderr, the subsequent
934        fopen calls will get mapped to the correct std handles. */
935     fclose(stdout);
936 
937     if ((where = fopen(FileName, "w")) == NULL) {
938       perror("netserver: fopen of debug file as new stdout failed!");
939       exit(1);
940     }
941 
942     fclose(stderr);
943 
944     if ((where = fopen(FileName, "w")) == NULL) {
945       fprintf(stdout, "fopen of debug file as new stderr failed!\n");
946       exit(1);
947     }
948   }
949 #endif
950 
951 #ifndef WIN32
952   chmod(DEBUG_LOG_FILE,0644);
953 #endif
954 
955 #if WIN32
956   if (child) {
957 	  server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE);
958   }
959 #endif
960 
961   /* if we are not a child of an inetd or the like, then we should
962    open a socket and hang listens off of it. otherwise, we should go
963    straight into processing requests. the do_listen() routine will sit
964    in an infinite loop accepting connections and forking child
965    processes. the child processes will call process_requests */
966 
967   /* If fd 0 is not a socket then assume we're not being called */
968   /* from inetd and start server socket on the default port. */
969   /* this enhancement comes from vwelch@ncsa.uiuc.edu (Von Welch) */
970   if (not_inetd) {
971     /* the user specified a port number on the command line */
972     set_up_server(local_host_name,listen_port,local_address_family);
973   }
974 #ifdef WIN32
975   /* OK, with Win2003 WinNT's POSIX subsystem is gone, and hence so is */
976   /* fork.  But hopefully the kernel support will continue to exist */
977   /* for some time.  We are not counting on the address space */
978   /* copy_on_write support, since it isn't exposed except through the */
979   /* NT native APIs (which are not public).  We will try to use the */
980   /* InheritHandles flag in CreateProcess though since this is public */
981   /* and is used for more than just POSIX so hopefully it won't go */
982   /* away. */
983   else if (TRUE) {
984     if (child) {
985       process_requests();
986     } else {
987       strncpy(listen_port,TEST_PORT,sizeof(listen_port));
988       set_up_server(local_host_name,listen_port,local_address_family);
989     }
990   }
991 #endif
992 #if !defined(__VMS)
993   else if (getsockname(0, &name, &namelen) == SOCKET_ERROR) {
994     /* we may not be a child of inetd */
995     if (errno == ENOTSOCK) {
996       strncpy(listen_port,TEST_PORT,sizeof(listen_port));
997       set_up_server(local_host_name,listen_port,local_address_family);
998     }
999   }
1000 #endif /* !defined(__VMS) */
1001   else {
1002     /* we are probably a child of inetd, or are being invoked via the
1003        VMS auxilliarly server mechanism */
1004 #if !defined(__VMS)
1005     server_sock = 0;
1006 #else
1007     if ( (server_sock = socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET )
1008     {
1009       perror("Failed to grab aux server socket" );
1010       exit(1);
1011     }
1012 
1013 #endif /* !defined(__VMS) */
1014     process_requests();
1015   }
1016 #ifdef WIN32
1017 	/* Cleanup the winsock lib */
1018 	WSACleanup();
1019 #endif
1020 
1021   return(0);
1022 }
1023