| /* |
| |
| Copyright (C) 1993-2007 Hewlett-Packard Company |
| ALL RIGHTS RESERVED. |
| |
| The enclosed software and documentation includes copyrighted works |
| of Hewlett-Packard Co. For as long as you comply with the following |
| limitations, you are hereby authorized to (i) use, reproduce, and |
| modify the software and documentation, and to (ii) distribute the |
| software and documentation, including modifications, for |
| non-commercial purposes only. |
| |
| 1. The enclosed software and documentation is made available at no |
| charge in order to advance the general development of |
| high-performance networking products. |
| |
| 2. You may not delete any copyright notices contained in the |
| software or documentation. All hard copies, and copies in |
| source code or object code form, of the software or |
| documentation (including modifications) must contain at least |
| one of the copyright notices. |
| |
| 3. The enclosed software and documentation has not been subjected |
| to testing and quality control and is not a Hewlett-Packard Co. |
| product. At a future time, Hewlett-Packard Co. may or may not |
| offer a version of the software and documentation as a product. |
| |
| 4. THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS". |
| HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE, |
| REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR |
| DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL |
| PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR |
| DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES, |
| EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE |
| DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF |
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| |
| 5. HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY |
| DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES |
| (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION, |
| MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION. |
| |
| */ |
| |
| #include "netperf_version.h" |
| |
| char netserver_id[]="\ |
| @(#)netserver.c (c) Copyright 1993-2007 Hewlett-Packard Co. Version 2.4.3"; |
| |
| /***********************************************************************/ |
| /* */ |
| /* netserver.c */ |
| /* */ |
| /* This is the server side code for the netperf test package. It */ |
| /* will operate either stand-alone, or as a child of inetd. In this */ |
| /* way, we insure that it can be installed on systems with or without */ |
| /* root permissions (editing inetd.conf). Essentially, this code is */ |
| /* the analog to the netsh.c code. */ |
| /* */ |
| /***********************************************************************/ |
| |
| |
| /************************************************************************/ |
| /* */ |
| /* Global include files */ |
| /* */ |
| /************************************************************************/ |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #if HAVE_STRING_H |
| # if !STDC_HEADERS && HAVE_MEMORY_H |
| # include <memory.h> |
| # endif |
| # include <string.h> |
| #endif |
| #if HAVE_STRINGS_H |
| # include <strings.h> |
| #endif |
| #if HAVE_LIMITS_H |
| # include <limits.h> |
| #endif |
| #include <sys/types.h> |
| #include <stdio.h> |
| #ifndef WIN32 |
| #include <errno.h> |
| #include <signal.h> |
| #endif |
| #if !defined(WIN32) && !defined(__VMS) |
| #include <sys/ipc.h> |
| #endif /* !defined(WIN32) && !defined(__VMS) */ |
| #include <fcntl.h> |
| #ifdef WIN32 |
| #include <time.h> |
| #include <winsock2.h> |
| #define netperf_socklen_t socklen_t |
| /* we need to find some other way to decide to include ws2 */ |
| /* if you are trying to compile on Windows 2000 or NT 4 you will */ |
| /* probably have to define DONT_IPV6 */ |
| #ifndef DONT_IPV6 |
| #include <ws2tcpip.h> |
| #endif /* DONT_IPV6 */ |
| #include <windows.h> |
| #define sleep(x) Sleep((x)*1000) |
| #else |
| #ifndef MPE |
| #include <sys/time.h> |
| #endif /* MPE */ |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| #ifndef DONT_WAIT |
| #include <sys/wait.h> |
| #endif /* DONT_WAIT */ |
| #endif /* WIN32 */ |
| #include <stdlib.h> |
| #ifdef __VMS |
| #include <tcpip$inetdef.h> |
| #include <unixio.h> |
| #endif /* __VMS */ |
| #include "netlib.h" |
| #include "nettest_bsd.h" |
| |
| #ifdef WANT_UNIX |
| #include "nettest_unix.h" |
| #endif /* WANT_UNIX */ |
| |
| #ifdef WANT_DLPI |
| #include "nettest_dlpi.h" |
| #endif /* WANT_DLPI */ |
| |
| #ifdef WANT_SCTP |
| #include "nettest_sctp.h" |
| #endif |
| |
| #include "netsh.h" |
| |
| #ifndef DEBUG_LOG_FILE |
| #ifndef WIN32 |
| #define DEBUG_LOG_FILE "/tmp/netperf.debug" |
| #else |
| #define DEBUG_LOG_FILE "c:\\temp\\netperf.debug" |
| #endif /* WIN32 */ |
| #endif /* DEBUG_LOG_FILE */ |
| |
| /* some global variables */ |
| |
| FILE *afp; |
| char listen_port[10]; |
| extern char *optarg; |
| extern int optind, opterr; |
| |
| #ifndef WIN32 |
| #define SERVER_ARGS "dL:n:p:v:V46" |
| #else |
| #define SERVER_ARGS "dL:n:p:v:V46I:i:" |
| #endif |
| |
| /* perhaps one day we will use this as part of only opening a debug |
| log file if debug is set, of course we have to be wary of the base |
| use of "where" and so probably always need "where" pointing |
| "somewhere" or other. */ |
| void |
| open_debug_file() |
| { |
| #ifndef WIN32 |
| #ifndef PATH_MAX |
| #define PATH_MAX MAX_PATH |
| #endif |
| char FileName[PATH_MAX]; /* for opening the debug log file */ |
| strcpy(FileName, DEBUG_LOG_FILE); |
| |
| if (where != NULL) fflush(where); |
| |
| snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid()); |
| if ((where = fopen(FileName, "w")) == NULL) { |
| perror("netserver: debug file"); |
| exit(1); |
| } |
| |
| chmod(FileName,0644); |
| #endif |
| |
| } |
| /* This routine implements the "main event loop" of the netperf */ |
| /* server code. Code above it will have set-up the control connection */ |
| /* so it can just merrily go about its business, which is to */ |
| /* "schedule" performance tests on the server. */ |
| |
| void |
| process_requests() |
| { |
| |
| float temp_rate; |
| |
| if (debug) open_debug_file(); |
| |
| |
| while (1) { |
| recv_request(); |
| |
| switch (netperf_request.content.request_type) { |
| |
| case DEBUG_ON: |
| netperf_response.content.response_type = DEBUG_OK; |
| /* dump_request already present in recv_request; redundant? */ |
| if (!debug) { |
| debug++; |
| open_debug_file(); |
| dump_request(); |
| } |
| send_response(); |
| break; |
| |
| case DEBUG_OFF: |
| if (debug) |
| debug--; |
| netperf_response.content.response_type = DEBUG_OK; |
| send_response(); |
| /* +SAF why??? */ |
| if (!debug) |
| { |
| fclose(where); |
| #if !defined(WIN32) && !defined(MPE) && !defined(__VMS) |
| /* For Unix: reopen the debug write file descriptor to "/dev/null" */ |
| /* and redirect stdout to it. */ |
| fflush (stdout); |
| where = fopen ("/dev/null", "w"); |
| if (where == NULL) |
| { |
| perror ("netserver: reopening debug fp for writing: /dev/null"); |
| exit (1); |
| } |
| if (close (STDOUT_FILENO) == -1) |
| { |
| perror ("netserver: closing stdout file descriptor"); |
| exit (1); |
| } |
| if (dup (fileno (where)) == -1) |
| { |
| perror ("netserver: duplicate /dev/null write file descr. to stdout"); |
| exit (1); |
| } |
| #endif /* !WIN32 !MPE !__VMS */ |
| } |
| break; |
| |
| case CPU_CALIBRATE: |
| netperf_response.content.response_type = CPU_CALIBRATE; |
| temp_rate = calibrate_local_cpu(0.0); |
| bcopy((char *)&temp_rate, |
| (char *)netperf_response.content.test_specific_data, |
| sizeof(temp_rate)); |
| bcopy((char *)&lib_num_loc_cpus, |
| (char *)netperf_response.content.test_specific_data + sizeof(temp_rate), |
| sizeof(lib_num_loc_cpus)); |
| if (debug) { |
| fprintf(where,"netserver: sending CPU information:"); |
| fprintf(where,"rate is %g num cpu %d\n",temp_rate,lib_num_loc_cpus); |
| fflush(where); |
| } |
| |
| /* we need the cpu_start, cpu_stop in the looper case to kill the */ |
| /* child proceses raj 7/95 */ |
| |
| #ifdef USE_LOOPER |
| cpu_start(1); |
| cpu_stop(1,&temp_rate); |
| #endif /* USE_LOOPER */ |
| |
| send_response(); |
| break; |
| |
| case DO_TCP_STREAM: |
| recv_tcp_stream(); |
| break; |
| |
| case DO_TCP_MAERTS: |
| recv_tcp_maerts(); |
| break; |
| |
| case DO_TCP_RR: |
| recv_tcp_rr(); |
| break; |
| |
| case DO_TCP_CRR: |
| recv_tcp_conn_rr(); |
| break; |
| |
| case DO_TCP_CC: |
| recv_tcp_cc(); |
| break; |
| |
| #ifdef DO_1644 |
| case DO_TCP_TRR: |
| recv_tcp_tran_rr(); |
| break; |
| #endif /* DO_1644 */ |
| |
| #ifdef DO_NBRR |
| case DO_TCP_NBRR: |
| recv_tcp_nbrr(); |
| break; |
| #endif /* DO_NBRR */ |
| |
| case DO_UDP_STREAM: |
| recv_udp_stream(); |
| break; |
| |
| case DO_UDP_RR: |
| recv_udp_rr(); |
| break; |
| |
| #ifdef WANT_DLPI |
| |
| case DO_DLPI_CO_RR: |
| recv_dlpi_co_rr(); |
| break; |
| |
| case DO_DLPI_CL_RR: |
| recv_dlpi_cl_rr(); |
| break; |
| |
| case DO_DLPI_CO_STREAM: |
| recv_dlpi_co_stream(); |
| break; |
| |
| case DO_DLPI_CL_STREAM: |
| recv_dlpi_cl_stream(); |
| break; |
| |
| #endif /* WANT_DLPI */ |
| |
| #ifdef WANT_UNIX |
| |
| case DO_STREAM_STREAM: |
| recv_stream_stream(); |
| break; |
| |
| case DO_STREAM_RR: |
| recv_stream_rr(); |
| break; |
| |
| case DO_DG_STREAM: |
| recv_dg_stream(); |
| break; |
| |
| case DO_DG_RR: |
| recv_dg_rr(); |
| break; |
| |
| #endif /* WANT_UNIX */ |
| |
| #ifdef WANT_XTI |
| case DO_XTI_TCP_STREAM: |
| recv_xti_tcp_stream(); |
| break; |
| |
| case DO_XTI_TCP_RR: |
| recv_xti_tcp_rr(); |
| break; |
| |
| case DO_XTI_UDP_STREAM: |
| recv_xti_udp_stream(); |
| break; |
| |
| case DO_XTI_UDP_RR: |
| recv_xti_udp_rr(); |
| break; |
| |
| #endif /* WANT_XTI */ |
| |
| #ifdef WANT_SCTP |
| case DO_SCTP_STREAM: |
| recv_sctp_stream(); |
| break; |
| |
| case DO_SCTP_STREAM_MANY: |
| recv_sctp_stream_1toMany(); |
| break; |
| |
| case DO_SCTP_RR: |
| recv_sctp_rr(); |
| break; |
| |
| case DO_SCTP_RR_MANY: |
| recv_sctp_rr_1toMany(); |
| break; |
| #endif |
| |
| #ifdef WANT_SDP |
| case DO_SDP_STREAM: |
| recv_sdp_stream(); |
| break; |
| |
| case DO_SDP_MAERTS: |
| recv_sdp_maerts(); |
| break; |
| |
| case DO_SDP_RR: |
| recv_sdp_rr(); |
| break; |
| #endif |
| |
| default: |
| fprintf(where,"unknown test number %d\n", |
| netperf_request.content.request_type); |
| fflush(where); |
| netperf_response.content.serv_errno=998; |
| send_response(); |
| break; |
| |
| } |
| } |
| } |
| |
| /* |
| set_up_server() |
| |
| set-up the server listen socket. we only call this routine if the |
| user has specified a port number on the command line or we believe we |
| are not a child of inetd or its platform-specific equivalent */ |
| |
| /*KC*/ |
| |
| void |
| set_up_server(char hostname[], char port[], int af) |
| { |
| |
| struct addrinfo hints; |
| struct addrinfo *local_res; |
| struct addrinfo *local_res_temp; |
| |
| struct sockaddr_storage peeraddr; |
| netperf_socklen_t peeraddr_len = sizeof(peeraddr); |
| |
| SOCKET server_control; |
| int on=1; |
| int count; |
| int error; |
| int not_listening; |
| |
| #if !defined(WIN32) && !defined(MPE) && !defined(__VMS) |
| FILE *rd_null_fp; /* Used to redirect from "/dev/null". */ |
| FILE *wr_null_fp; /* Used to redirect to "/dev/null". */ |
| #endif /* !WIN32 !MPE !__VMS */ |
| |
| if (debug) { |
| fprintf(stderr, |
| "set_up_server called with host '%s' port '%s' remfam %d\n", |
| hostname, |
| port, |
| af); |
| fflush(stderr); |
| } |
| |
| memset(&hints,0,sizeof(hints)); |
| hints.ai_family = af; |
| hints.ai_socktype = SOCK_STREAM; |
| hints.ai_protocol = IPPROTO_TCP; |
| hints.ai_flags = AI_PASSIVE; |
| |
| count = 0; |
| do { |
| error = getaddrinfo((char *)hostname, |
| (char *)port, |
| &hints, |
| &local_res); |
| count += 1; |
| if (error == EAI_AGAIN) { |
| if (debug) { |
| fprintf(stderr,"Sleeping on getaddrinfo EAI_AGAIN\n"); |
| fflush(stderr); |
| } |
| sleep(1); |
| } |
| } while ((error == EAI_AGAIN) && (count <= 5)); |
| |
| if (error) { |
| fprintf(stderr, |
| "set_up_server: could not resolve remote '%s' port '%s' af %d", |
| hostname, |
| port, |
| af); |
| fprintf(stderr,"\n\tgetaddrinfo returned %d %s\n", |
| error, |
| gai_strerror(error)); |
| exit(-1); |
| } |
| |
| if (debug) { |
| dump_addrinfo(stderr, local_res, hostname, port, af); |
| } |
| |
| not_listening = 1; |
| local_res_temp = local_res; |
| |
| while((local_res_temp != NULL) && (not_listening)) { |
| |
| fprintf(stderr, |
| "Starting netserver at port %s\n", |
| port); |
| |
| server_control = socket(local_res_temp->ai_family,SOCK_STREAM,0); |
| |
| if (server_control == INVALID_SOCKET) { |
| perror("set_up_server could not allocate a socket"); |
| exit(-1); |
| } |
| |
| /* happiness and joy, keep going */ |
| if (setsockopt(server_control, |
| SOL_SOCKET, |
| SO_REUSEADDR, |
| (char *)&on , |
| sizeof(on)) == SOCKET_ERROR) { |
| if (debug) { |
| perror("warning: set_up_server could not set SO_REUSEADDR"); |
| } |
| } |
| /* still happy and joyful */ |
| |
| if ((bind (server_control, |
| local_res_temp->ai_addr, |
| local_res_temp->ai_addrlen) != SOCKET_ERROR) && |
| (listen (server_control,5) != SOCKET_ERROR)) { |
| not_listening = 0; |
| break; |
| } |
| else { |
| /* we consider a bind() or listen() failure a transient and try |
| the next address */ |
| if (debug) { |
| perror("warning: set_up_server failed a bind or listen call\n"); |
| } |
| local_res_temp = local_res_temp->ai_next; |
| continue; |
| } |
| } |
| |
| if (not_listening) { |
| fprintf(stderr, |
| "set_up_server could not establish a listen endpoint for %s port %s with family %s\n", |
| host_name, |
| port, |
| inet_ftos(af)); |
| fflush(stderr); |
| exit(-1); |
| } |
| else { |
| printf("Starting netserver at hostname %s port %s and family %s\n", |
| hostname, |
| port, |
| inet_ftos(af)); |
| } |
| |
| /* |
| setpgrp(); |
| */ |
| |
| #if !defined(WIN32) && !defined(MPE) && !defined(__VMS) |
| /* Flush the standard I/O file descriptors before forking. */ |
| fflush (stdin); |
| fflush (stdout); |
| fflush (stderr); |
| switch (fork()) |
| { |
| case -1: |
| perror("netperf server error"); |
| exit(1); |
| |
| case 0: |
| /* Redirect stdin from "/dev/null". */ |
| rd_null_fp = fopen ("/dev/null", "r"); |
| if (rd_null_fp == NULL) |
| { |
| perror ("netserver: opening for reading: /dev/null"); |
| exit (1); |
| } |
| if (close (STDIN_FILENO) == -1) |
| { |
| perror ("netserver: closing stdin file descriptor"); |
| exit (1); |
| } |
| if (dup (fileno (rd_null_fp)) == -1) |
| { |
| perror ("netserver: duplicate /dev/null read file descr. to stdin"); |
| exit (1); |
| } |
| |
| /* Redirect stdout to the debug write file descriptor. */ |
| if (close (STDOUT_FILENO) == -1) |
| { |
| perror ("netserver: closing stdout file descriptor"); |
| exit (1); |
| } |
| if (dup (fileno (where)) == -1) |
| { |
| perror ("netserver: duplicate the debug write file descr. to stdout"); |
| exit (1); |
| } |
| |
| /* Redirect stderr to "/dev/null". */ |
| wr_null_fp = fopen ("/dev/null", "w"); |
| if (wr_null_fp == NULL) |
| { |
| perror ("netserver: opening for writing: /dev/null"); |
| exit (1); |
| } |
| if (close (STDERR_FILENO) == -1) |
| { |
| perror ("netserver: closing stderr file descriptor"); |
| exit (1); |
| } |
| if (dup (fileno (wr_null_fp)) == -1) |
| { |
| perror ("netserver: dupicate /dev/null write file descr. to stderr"); |
| exit (1); |
| } |
| |
| #ifndef NO_SETSID |
| setsid(); |
| #else |
| setpgrp(); |
| #endif /* NO_SETSID */ |
| |
| /* some OS's have SIGCLD defined as SIGCHLD */ |
| #ifndef SIGCLD |
| #define SIGCLD SIGCHLD |
| #endif /* SIGCLD */ |
| |
| signal(SIGCLD, SIG_IGN); |
| |
| #endif /* !WIN32 !MPE !__VMS */ |
| |
| for (;;) |
| { |
| if ((server_sock=accept(server_control, |
| (struct sockaddr *)&peeraddr, |
| &peeraddr_len)) == INVALID_SOCKET) |
| { |
| printf("server_control: accept failed errno %d\n",errno); |
| exit(1); |
| } |
| #if defined(MPE) || defined(__VMS) |
| /* |
| * Since we cannot fork this process , we cant fire any threads |
| * as they all share the same global data . So we better allow |
| * one request at at time |
| */ |
| process_requests() ; |
| #elif WIN32 |
| { |
| BOOL b; |
| char cmdline[80]; |
| PROCESS_INFORMATION pi; |
| STARTUPINFO si; |
| int i; |
| |
| memset(&si, 0 , sizeof(STARTUPINFO)); |
| si.cb = sizeof(STARTUPINFO); |
| |
| /* Pass the server_sock as stdin for the new process. */ |
| /* Hopefully this will continue to be created with the OBJ_INHERIT attribute. */ |
| si.hStdInput = (HANDLE)server_sock; |
| si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); |
| si.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
| si.dwFlags = STARTF_USESTDHANDLES; |
| |
| /* Build cmdline for child process */ |
| strcpy(cmdline, program); |
| if (verbosity > 1) { |
| snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -v %d", verbosity); |
| } |
| for (i=0; i < debug; i++) { |
| snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -d"); |
| } |
| snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -I %x", (int)(UINT_PTR)server_sock); |
| snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)server_control); |
| snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)where); |
| |
| b = CreateProcess(NULL, /* Application Name */ |
| cmdline, |
| NULL, /* Process security attributes */ |
| NULL, /* Thread security attributes */ |
| TRUE, /* Inherit handles */ |
| 0, /* Creation flags PROCESS_QUERY_INFORMATION, */ |
| NULL, /* Enviornment */ |
| NULL, /* Current directory */ |
| &si, /* StartupInfo */ |
| &pi); |
| if (!b) |
| { |
| perror("CreateProcessfailure: "); |
| exit(1); |
| } |
| |
| /* We don't need the thread or process handles any more; let them */ |
| /* go away on their own timeframe. */ |
| |
| CloseHandle(pi.hThread); |
| CloseHandle(pi.hProcess); |
| |
| /* And close the server_sock since the child will own it. */ |
| |
| close(server_sock); |
| } |
| #else |
| signal(SIGCLD, SIG_IGN); |
| |
| switch (fork()) |
| { |
| case -1: |
| /* something went wrong */ |
| exit(1); |
| case 0: |
| /* we are the child process */ |
| close(server_control); |
| process_requests(); |
| exit(0); |
| break; |
| default: |
| /* we are the parent process */ |
| close(server_sock); |
| /* we should try to "reap" some of our children. on some */ |
| /* systems they are being left as defunct processes. we */ |
| /* will call waitpid, looking for any child process, */ |
| /* with the WNOHANG feature. when waitpid return a zero, */ |
| /* we have reaped all the children there are to reap at */ |
| /* the moment, so it is time to move on. raj 12/94 */ |
| #ifndef DONT_WAIT |
| #ifdef NO_SETSID |
| /* Only call "waitpid()" if "setsid()" is not used. */ |
| while(waitpid(-1, NULL, WNOHANG) > 0) { } |
| #endif /* NO_SETSID */ |
| #endif /* DONT_WAIT */ |
| break; |
| } |
| #endif /* !WIN32 !MPE !__VMS */ |
| } /*for*/ |
| #if !defined(WIN32) && !defined(MPE) && !defined(__VMS) |
| break; /*case 0*/ |
| |
| default: |
| exit (0); |
| |
| } |
| #endif /* !WIN32 !MPE !__VMS */ |
| } |
| |
| #ifdef WIN32 |
| |
| /* With Win2003, WinNT's POSIX subsystem is gone and hence so is */ |
| /* fork. */ |
| |
| /* But hopefully the kernel support will continue to exist for some */ |
| /* time. */ |
| |
| /* We are not counting on the child address space copy_on_write */ |
| /* support, since it isn't exposed except through the NT native APIs */ |
| /* (which is not public). */ |
| |
| /* We will try to use the InheritHandles flag in CreateProcess. It */ |
| /* is in the public API, though it is documented as "must be FALSE". */ |
| |
| /* So where we would have forked, we will now create a new process. */ |
| /* I have added a set of command line switches to specify a list of */ |
| /* handles that the child should close since they shouldn't have */ |
| /* been inherited ("-i#"), and a single switch to specify the handle */ |
| /* for the server_sock ("I#"). */ |
| |
| /* A better alternative would be to re-write NetPerf to be */ |
| /* multi-threaded; i.e., move all of the various NetPerf global */ |
| /* variables in to thread specific structures. But this is a bigger */ |
| /* effort than I want to tackle at this time. (And I doubt that the */ |
| /* HP-UX author sees value in this effort). */ |
| |
| #endif |
| |
| int _cdecl |
| main(int argc, char *argv[]) |
| { |
| |
| int c; |
| int not_inetd = 0; |
| #ifdef WIN32 |
| BOOL child = FALSE; |
| #endif |
| char arg1[BUFSIZ], arg2[BUFSIZ]; |
| #ifndef PATH_MAX |
| #define PATH_MAX MAX_PATH |
| #endif |
| char FileName[PATH_MAX]; /* for opening the debug log file */ |
| |
| struct sockaddr name; |
| netperf_socklen_t namelen = sizeof(name); |
| |
| |
| #ifdef WIN32 |
| WSADATA wsa_data ; |
| |
| /* Initialize the winsock lib ( version 2.2 ) */ |
| if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){ |
| printf("WSAStartup() failed : %d\n", GetLastError()) ; |
| return 1 ; |
| } |
| #endif /* WIN32 */ |
| |
| /* Save away the program name */ |
| program = (char *)malloc(strlen(argv[0]) + 1); |
| if (program == NULL) { |
| printf("malloc(%d) failed!\n", strlen(argv[0]) + 1); |
| return 1 ; |
| } |
| strcpy(program, argv[0]); |
| |
| netlib_init(); |
| |
| /* Scan the command line to see if we are supposed to set-up our own */ |
| /* listen socket instead of relying on inetd. */ |
| |
| /* first set a copy of initial values */ |
| strncpy(local_host_name,"0.0.0.0",sizeof(local_host_name)); |
| local_address_family = AF_UNSPEC; |
| strncpy(listen_port,TEST_PORT,sizeof(listen_port)); |
| |
| while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF) { |
| switch (c) { |
| case '?': |
| case 'h': |
| print_netserver_usage(); |
| exit(1); |
| case 'd': |
| /* we want to set the debug file name sometime */ |
| debug++; |
| break; |
| case 'L': |
| not_inetd = 1; |
| break_args_explicit(optarg,arg1,arg2); |
| if (arg1[0]) { |
| strncpy(local_host_name,arg1,sizeof(local_host_name)); |
| } |
| if (arg2[0]) { |
| local_address_family = parse_address_family(arg2); |
| /* if only the address family was set, we may need to set the |
| local_host_name accordingly. since our defaults are IPv4 |
| this should only be necessary if we have IPv6 support raj |
| 2005-02-07 */ |
| #if defined (AF_INET6) |
| if (!arg1[0]) { |
| strncpy(local_host_name,"::0",sizeof(local_host_name)); |
| } |
| #endif |
| } |
| break; |
| case 'n': |
| shell_num_cpus = atoi(optarg); |
| if (shell_num_cpus > MAXCPUS) { |
| fprintf(stderr, |
| "netserver: This version can only support %d CPUs. Please", |
| MAXCPUS); |
| fprintf(stderr, |
| " increase MAXCPUS in netlib.h and recompile.\n"); |
| fflush(stderr); |
| exit(1); |
| } |
| break; |
| case 'p': |
| /* we want to open a listen socket at a */ |
| /* specified port number */ |
| strncpy(listen_port,optarg,sizeof(listen_port)); |
| not_inetd = 1; |
| break; |
| case '4': |
| local_address_family = AF_INET; |
| break; |
| case '6': |
| #if defined(AF_INET6) |
| local_address_family = AF_INET6; |
| strncpy(local_host_name,"::0",sizeof(local_host_name)); |
| #else |
| local_address_family = AF_UNSPEC; |
| #endif |
| break; |
| case 'v': |
| /* say how much to say */ |
| verbosity = atoi(optarg); |
| break; |
| case 'V': |
| printf("Netperf version %s\n",NETPERF_VERSION); |
| exit(0); |
| break; |
| #ifdef WIN32 |
| /*+*+SAF */ |
| case 'I': |
| child = TRUE; |
| /* This is the handle we expect to inherit. */ |
| /*+*+SAF server_sock = (HANDLE)atoi(optarg); */ |
| break; |
| case 'i': |
| /* This is a handle we should NOT inherit. */ |
| /*+*+SAF CloseHandle((HANDLE)atoi(optarg)); */ |
| break; |
| #endif |
| |
| } |
| } |
| |
| /* +*+SAF I need a better way to find inherited handles I should close! */ |
| /* +*+SAF Use DuplicateHandle to force inheritable attribute (or reset it)? */ |
| |
| /* unlink(DEBUG_LOG_FILE); */ |
| |
| strcpy(FileName, DEBUG_LOG_FILE); |
| |
| #ifndef WIN32 |
| snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid()); |
| if ((where = fopen(FileName, "w")) == NULL) { |
| perror("netserver: debug file"); |
| exit(1); |
| } |
| #else |
| { |
| |
| if (child) { |
| snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%x", getpid()); |
| } |
| |
| /* Hopefully, by closing stdout & stderr, the subsequent |
| fopen calls will get mapped to the correct std handles. */ |
| fclose(stdout); |
| |
| if ((where = fopen(FileName, "w")) == NULL) { |
| perror("netserver: fopen of debug file as new stdout failed!"); |
| exit(1); |
| } |
| |
| fclose(stderr); |
| |
| if ((where = fopen(FileName, "w")) == NULL) { |
| fprintf(stdout, "fopen of debug file as new stderr failed!\n"); |
| exit(1); |
| } |
| } |
| #endif |
| |
| #ifndef WIN32 |
| chmod(DEBUG_LOG_FILE,0644); |
| #endif |
| |
| #if WIN32 |
| if (child) { |
| server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE); |
| } |
| #endif |
| |
| /* if we are not a child of an inetd or the like, then we should |
| open a socket and hang listens off of it. otherwise, we should go |
| straight into processing requests. the do_listen() routine will sit |
| in an infinite loop accepting connections and forking child |
| processes. the child processes will call process_requests */ |
| |
| /* If fd 0 is not a socket then assume we're not being called */ |
| /* from inetd and start server socket on the default port. */ |
| /* this enhancement comes from vwelch@ncsa.uiuc.edu (Von Welch) */ |
| if (not_inetd) { |
| /* the user specified a port number on the command line */ |
| set_up_server(local_host_name,listen_port,local_address_family); |
| } |
| #ifdef WIN32 |
| /* OK, with Win2003 WinNT's POSIX subsystem is gone, and hence so is */ |
| /* fork. But hopefully the kernel support will continue to exist */ |
| /* for some time. We are not counting on the address space */ |
| /* copy_on_write support, since it isn't exposed except through the */ |
| /* NT native APIs (which are not public). We will try to use the */ |
| /* InheritHandles flag in CreateProcess though since this is public */ |
| /* and is used for more than just POSIX so hopefully it won't go */ |
| /* away. */ |
| else if (TRUE) { |
| if (child) { |
| process_requests(); |
| } else { |
| strncpy(listen_port,TEST_PORT,sizeof(listen_port)); |
| set_up_server(local_host_name,listen_port,local_address_family); |
| } |
| } |
| #endif |
| #if !defined(__VMS) |
| else if (getsockname(0, &name, &namelen) == SOCKET_ERROR) { |
| /* we may not be a child of inetd */ |
| if (errno == ENOTSOCK) { |
| strncpy(listen_port,TEST_PORT,sizeof(listen_port)); |
| set_up_server(local_host_name,listen_port,local_address_family); |
| } |
| } |
| #endif /* !defined(__VMS) */ |
| else { |
| /* we are probably a child of inetd, or are being invoked via the |
| VMS auxilliarly server mechanism */ |
| #if !defined(__VMS) |
| server_sock = 0; |
| #else |
| if ( (server_sock = socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET ) |
| { |
| perror("Failed to grab aux server socket" ); |
| exit(1); |
| } |
| |
| #endif /* !defined(__VMS) */ |
| process_requests(); |
| } |
| #ifdef WIN32 |
| /* Cleanup the winsock lib */ |
| WSACleanup(); |
| #endif |
| |
| return(0); |
| } |