diff options
Diffstat (limited to 'src/netserver.c')
-rw-r--r-- | src/netserver.c | 1572 |
1 files changed, 1572 insertions, 0 deletions
diff --git a/src/netserver.c b/src/netserver.c new file mode 100644 index 0000000..9534722 --- /dev/null +++ b/src/netserver.c @@ -0,0 +1,1572 @@ +/* + + Copyright (C) 1993-2012 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-2012 Hewlett-Packard Co. Version 2.6.0"; + + +#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 + +#if HAVE_SYS_IPC_H +#include <sys/ipc.h> +#endif + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#if HAVE_NETDB_H +#include <netdb.h> +#endif + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#if HAVE_ERRNO_H +#include <errno.h> +#endif + +#if HAVE_SIGNAL_H +#include <signal.h> +/* some OS's have SIGCLD defined as SIGCHLD */ +#ifndef SIGCLD +#define SIGCLD SIGCHLD +#endif /* SIGCLD */ + +#endif + +#if !defined(HAVE_SETSID) +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#endif + +#ifdef WIN32 +#include <time.h> +#include <winsock2.h> + +#if HAVE_WS2TCPIP_H +#include <ws2tcpip.h> +#endif + +#include <windows.h> + +#include "missing\stdint.h" + +#define strdup _strdup +#define sleep(x) Sleep((x)*1000) +#define netperf_socklen_t socklen_t +#endif /* WIN32 */ + +/* unconditional system includes */ + +#include <sys/types.h> +#include <stdio.h> +#include <fcntl.h> + +/* netperf includes */ +#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_DIR +#if defined(WIN32) +#define DEBUG_LOG_FILE_DIR "" +#elif defined(ANDROID) +#define DEBUG_LOG_FILE_DIR "/data/local/tmp/" +#else +#define DEBUG_LOG_FILE_DIR "/tmp/" +#endif +#endif /* DEBUG_LOG_FILE_DIR */ + +#ifndef DEBUG_LOG_FILE +#define DEBUG_LOG_FILE DEBUG_LOG_FILE_DIR"netserver.debug" +#endif + +#if !defined(PATH_MAX) +#define PATH_MAX MAX_PATH +#endif +char FileName[PATH_MAX]; + +char listen_port[10]; + +struct listen_elt { + SOCKET fd; + struct listen_elt *next; +}; + +struct listen_elt *listen_list = NULL; + +SOCKET server_control; + +int child; /* are we the child of inetd or a parent netserver? + */ +int netperf_daemon; +int daemon_parent = 0; +int not_inetd; +int want_daemonize; +int spawn_on_accept; +int suppress_debug = 0; + +extern char *optarg; +extern int optind, opterr; + +/* char *passphrase = NULL; */ + +static void +init_netserver_globals() { + +#if defined(__VMS) || defined(VMWARE_UW) + spawn_on_accept = 0; + want_daemonize = 0; +#else + spawn_on_accept = 1; +#if defined(WIN32) + /* we only know how to spawn in WIN32, not daemonize */ + want_daemonize = 0; +#else + want_daemonize = 1; +#endif /* WIN32 */ +#endif /* __VMS || VMWARE_UW */ + + child = 0; + not_inetd = 0; + netperf_daemon = 0; +} + +void +unlink_empty_debug_file() { + +#if !defined(WIN32) + struct stat buf; + + if (stat(FileName,&buf)== 0) { + + if (buf.st_size == 0) + unlink(FileName); + } +#endif +} + +/* it is important that set_server_sock() be called before this + routine as we depend on the control socket being dup()ed out of the + way when we go messing about with the streams. */ +void +open_debug_file() +{ +#if !defined WIN32 +#define NETPERF_NULL "/dev/null" +#else +#define NETPERF_NULL "nul" +#endif + + FILE *rd_null_fp; + + if (where != NULL) fflush(where); + + snprintf(FileName, + sizeof(FileName), +#if defined(WIN32) + "%s\\%s_%d", + getenv("TEMP"), +#else + "%s_%d", +#endif + DEBUG_LOG_FILE, + getpid()); + if ((where = fopen((suppress_debug) ? NETPERF_NULL : FileName, + "w")) == NULL) { + perror("netserver: debug file"); + exit(1); + } + +#if !defined(WIN32) + + chmod(FileName,0644); + + /* redirect stdin to "/dev/null" */ + rd_null_fp = fopen(NETPERF_NULL,"r"); + if (NULL == rd_null_fp) { + fprintf(where, + "%s: opening of %s failed: %s (errno %d)\n", + __FUNCTION__, + NETPERF_NULL, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + if (close(STDIN_FILENO) == -1) { + fprintf(where, + "%s: close of STDIN_FILENO failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + if (dup(fileno(rd_null_fp)) == -1) { + fprintf(where, + "%s: dup of rd_null_fp to stdin failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + /* redirect stdout to "where" */ + if (close(STDOUT_FILENO) == -1) { + fprintf(where, + "%s: close of STDOUT_FILENO failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + if (dup(fileno(where)) == -1) { + fprintf(where, + "%s: dup of where to stdout failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + /* redirect stderr to "where" */ + if (close(STDERR_FILENO) == -1) { + fprintf(where, + "%s: close of STDERR_FILENO failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + if (dup(fileno(where)) == -1) { + fprintf(where, + "%s: dup of where to stderr failed: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + +#else + + /* 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 + +} + +/* so, either we are a child of inetd in which case server_sock should + be stdin, or we are a child of a netserver parent. there will be + logic here for all of it, including Windows. it is important that + this be called before open_debug_file() */ + +void +set_server_sock() { + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + +#ifdef WIN32 + server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE); +#elif !defined(__VMS) + if (server_sock != INVALID_SOCKET) { + fprintf(where,"Yo, Iz ain't invalid!\n"); + fflush(where); + exit(1); + } + + /* we dup this to up the reference count so when we do redirection + of the io streams we don't accidentally toast the control + connection in the case of our being a child of inetd. */ + server_sock = dup(0); + +#else + if ((server_sock = + socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET) { + fprintf(stderr, + "%s: failed to grab aux server socket: %s (errno %s)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(stderr); + exit(1); + } +#endif + +} + + +void +create_listens(char hostname[], char port[], int af) { + + struct addrinfo hints; + struct addrinfo *local_res; + struct addrinfo *local_res_temp; + int count, error; + int on = 1; + SOCKET temp_socket; + struct listen_elt *temp_elt; + + if (debug) { + fprintf(stderr, + "%s: called with host '%s' port '%s' family %s(%d)\n", + __FUNCTION__, + hostname, + port, + inet_ftos(af), + 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, + "%s: Sleeping on getaddrinfo EAI_AGAIN\n", + __FUNCTION__); + fflush(stderr); + } + sleep(1); + } + } while ((error == EAI_AGAIN) && (count <= 5)); + + if (error) { + if (debug) { + + fprintf(stderr, + "%s: could not resolve remote '%s' port '%s' af %d\n" + "\tgetaddrinfo returned %s (%d)\n", + __FUNCTION__, + hostname, + port, + af, + gai_strerror(error), + error); + + } + return; + } + + if (debug) { + dump_addrinfo(stderr, local_res, hostname, port, af); + } + + local_res_temp = local_res; + + while (local_res_temp != NULL) { + + temp_socket = socket(local_res_temp->ai_family,SOCK_STREAM,0); + + if (temp_socket == INVALID_SOCKET) { + if (debug) { + fprintf(stderr, + "%s could not allocate a socket: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(stderr); + } + local_res_temp = local_res_temp->ai_next; + continue; + } + + /* happiness and joy, keep going */ + if (setsockopt(temp_socket, + SOL_SOCKET, + SO_REUSEADDR, + (char *)&on , + sizeof(on)) == SOCKET_ERROR) { + if (debug) { + fprintf(stderr, + "%s: warning: could not set SO_REUSEADDR: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(stderr); + } + } + /* still happy and joyful */ + + if ((bind(temp_socket, + local_res_temp->ai_addr, + local_res_temp->ai_addrlen) != SOCKET_ERROR) && + (listen(temp_socket,1024) != SOCKET_ERROR)) { + + /* OK, now add to the list */ + temp_elt = (struct listen_elt *)malloc(sizeof(struct listen_elt)); + if (temp_elt) { + temp_elt->fd = temp_socket; + if (listen_list) { + temp_elt->next = listen_list; + } + else { + temp_elt->next = NULL; + } + listen_list = temp_elt; + } + else { + fprintf(stderr, + "%s: could not malloc a listen_elt\n", + __FUNCTION__); + fflush(stderr); + exit(1); + } + } + else { + /* we consider a bind() or listen() failure a transient and try + the next address */ + if (debug) { + fprintf(stderr, + "%s: warning: bind or listen call failure: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(stderr); + } + close(temp_socket); + } + local_res_temp = local_res_temp->ai_next; + } + +} + +void +setup_listens(char name[], char port[], int af) { + + int do_inet; + int no_name = 0; +#ifdef AF_INET6 + int do_inet6; +#endif + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + + if (strcmp(name,"") == 0) { + no_name = 1; + switch (af) { + case AF_UNSPEC: + do_inet = 1; +#ifdef AF_INET6 + do_inet6 = 1; +#endif + break; + case AF_INET: + do_inet = 1; +#ifdef AF_INET6 + do_inet6 = 0; +#endif + break; +#ifdef AF_INET6 + case AF_INET6: + do_inet = 0; + do_inet6 = 1; + break; +#endif + default: + do_inet = 1; + } + /* if we have IPv6, try that one first because it may be a superset */ +#ifdef AF_INET6 + if (do_inet6) + create_listens("::0",port,AF_INET6); +#endif + if (do_inet) + create_listens("0.0.0.0",port,AF_INET); + } + else { + create_listens(name,port,af); + } + + if (listen_list) { + fprintf(stdout, + "Starting netserver with host '%s' port '%s' and family %s\n", + (no_name) ? "IN(6)ADDR_ANY" : name, + port, + inet_ftos(af)); + fflush(stdout); + } + else { + fprintf(stderr, + "Unable to start netserver with '%s' port '%s' and family %s\n", + (no_name) ? "IN(6)ADDR_ANY" : name, + port, + inet_ftos(af)); + fflush(stderr); + exit(1); + } +} + +SOCKET +set_fdset(struct listen_elt *list, fd_set *fdset) { + + struct listen_elt *temp; + SOCKET max = INVALID_SOCKET; + + FD_ZERO(fdset); + + temp = list; + + if (debug) { + fprintf(where, + "%s: enter list %p fd_set %p\n", + __FUNCTION__, + list, + fdset); + fflush(where); + } + + while (temp) { + if (temp->fd > max) + max = temp->fd; + + if (debug) { + fprintf(where, + "setting %d in fdset\n", + temp->fd); + fflush(where); + } + + FD_SET(temp->fd,fdset); + + temp = temp->next; + } + + return max; + +} + +void +close_listens(struct listen_elt *list) { + struct listen_elt *temp; + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + temp = list; + + while (temp) { + close(temp->fd); + temp = temp->next; + } +} + +static int +recv_passphrase() { + + /* may need to revisit the timeout. we only respond if there is an + error with receiving the passphrase */ + if ((recv_request_timed_n(0,20) > 0) && + (netperf_request.content.request_type == PASSPHRASE) && + (!strcmp(passphrase, + (char *)netperf_request.content.test_specific_data))) { + /* it was okey dokey */ + return 0; + } +#if defined(SEND_PASSPHRASE_RESPONSE) + netperf_response.content.response_type = PASSPHRASE; + netperf_response.content.serv_errno = 403; + snprintf((char *)netperf_response.content.test_specific_data, + sizeof(netperf_response.content.test_specific_data), + "Sorry, unable to match with required passphrase\n"); + send_response_n(0); +#endif + fprintf(where, + "Unable to match required passphrase. Closing control connection\n"); + fflush(where); + + close(server_sock); + return -1; +} + +/* 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) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + /* if the netserver was started with a passphrase, look for it in + the first request to arrive. if there is no passphrase in the + first request we will end-up dumping the control connection. raj + 2012-01-23 */ + + if ((passphrase != NULL) && (recv_passphrase())) + return; + + while (1) { + + if (recv_request() <= 0) { + close(server_sock); + return; + } + + switch (netperf_request.content.request_type) { + + case DEBUG_ON: + netperf_response.content.response_type = DEBUG_OK; + if (!suppress_debug) { + debug++; + + if (debug == 1) { + /* we just flipped-on debugging, dump the request because + recv_request/recv_request_n will not have dumped it as its + dump_request() call is conditional on debug being set. raj + 2011-07-08 */ + dump_request(); + } + } + + send_response(); + break; + + case DEBUG_OFF: + if (debug) + debug--; + netperf_response.content.response_type = DEBUG_OK; + send_response(); + /* we used to take the trouble to close the debug file, but SAF + asked a good question when he asked "Why?" and since I cannot + think of a good reason, I have removed the code. raj + 2011-07-08 */ + break; + + case DO_SYSINFO: + { + netperf_response.content.response_type = SYSINFO_RESPONSE; + + snprintf((char *)netperf_response.content.test_specific_data, + sizeof(netperf_response.content.test_specific_data), + "%c%s%c%s%c%s%c%s", + ',', + "Deprecated", + ',' +, "Deprecated", + ',', + "Deprecated", + ',', + "Deprecated"); + + send_response_n(0); + 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: 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 + +#ifdef WANT_OMNI + case DO_OMNI: + recv_omni(); + break; +#endif + + case PASSPHRASE: + if (debug) { + fprintf(where,"Ignoring an unexpected passphrase control message\n"); + fflush(where); + } + break; + + default: + fprintf(where,"unknown test number %d\n", + netperf_request.content.request_type); + fflush(where); + netperf_response.content.serv_errno=998; + send_response(); + break; + + } + } +} + +/* the routine we call when we are going to spawn/fork/whatnot a child + process from the parent netserver daemon. raj 2011-07-08 */ +void +spawn_child() { + +#if defined(HAVE_FORK) + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + + /* flush the usual suspects */ + fflush(stdin); + fflush(stdout); + fflush(stderr); + fflush(where); + + signal(SIGCLD,SIG_IGN); + + switch (fork()) { + case -1: + fprintf(where, + "%s: fork() error %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + + case 0: + /* we are the child, but not of inetd. we don't know if we are + the child of a daemonized parent or not, so we still need to + worry about the standard file descriptors. raj 2011-07-11 */ + + close_listens(listen_list); + open_debug_file(); + + child = 1; + netperf_daemon = 0; + process_requests(); + exit(0); + break; + + default: + /* we are the parent, not a great deal to do here, but we may + want to reap some children */ +#if !defined(HAVE_SETSID) + /* Only call "waitpid()" if "setsid()" is not used. */ + while(waitpid(-1, NULL, WNOHANG) > 0) { + if (debug) { + fprintf(where, + "%s: reaped a child process\n", + __FUNCTION__); + } + } +#endif + break; + } + +#elif defined(WIN32) + + BOOL b; + char *cmdline; + int cmdline_length; + int cmd_index; + PROCESS_INFORMATION pi; + STARTUPINFO si; + int i; + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + + /* create the cmdline array based on strlen(program) + 80 chars */ + cmdline_length = strlen(program) + 80; + cmdline = malloc(cmdline_length + 1); // +1 for trailing null + + 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); + cmd_index = strlen(cmdline); + if (verbosity > 1) { + cmd_index += snprintf(&cmdline[cmd_index], + cmdline_length - cmd_index, + " -v %d", + verbosity); + } + for (i=0; i < debug; i++) { + cmd_index += snprintf(&cmdline[cmd_index], + cmdline_length - cmd_index, + " -d"); + } + cmd_index += snprintf(&cmdline[cmd_index], + cmdline_length - cmd_index, + " -I %x", + (int)(UINT_PTR)server_sock); + + /* are these -i settings even necessary? the command line scanning + does not seem to do anything with them */ + cmd_index += snprintf(&cmdline[cmd_index], + cmdline_length - cmd_index, + " -i %x", + (int)(UINT_PTR)server_control); + cmd_index += snprintf(&cmdline[cmd_index], + cmdline_length - cmd_index, + " -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: "); + free(cmdline); /* even though we exit :) */ + 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); + + /* the caller/parent will close server_sock */ + + free(cmdline); + +#else + + fprintf(where, + "%s called on platform which cannot spawn children\n", + __FUNCTION__); + fflush(where); + exit(1); + +#endif /* HAVE_FORK */ +} + +void +accept_connection(SOCKET listen_fd) { + + struct sockaddr_storage peeraddr; + netperf_socklen_t peeraddrlen; +#if defined(SO_KEEPALIVE) + int on = 1; +#endif + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + peeraddrlen = sizeof(peeraddr); + + /* while server_control is only used by the WIN32 path, but why + bother ifdef'ing it? and besides, do we *really* need knowledge + of server_control in the WIN32 case? do we have to tell the + child about *all* the listen endpoints? raj 2011-07-08 */ + server_control = listen_fd; + + if ((server_sock = accept(listen_fd, + (struct sockaddr *)&peeraddr, + &peeraddrlen)) == INVALID_SOCKET) { + fprintf(where, + "%s: accept failure: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + +#if defined(SO_KEEPALIVE) + /* we are not terribly concerned if this does not work, it is merely + duct tape added to belts and suspenders. raj 2011-07-08 */ + setsockopt(server_sock, + SOL_SOCKET, + SO_KEEPALIVE, + (const char *)&on, + sizeof(on)); +#endif + + if (spawn_on_accept) { + spawn_child(); + /* spawn_child() only returns when we are the parent */ + close(server_sock); + } + else { + process_requests(); + } +} + +void +accept_connections() { + + fd_set read_fds, write_fds, except_fds; + SOCKET high_fd, candidate; + int num_ready; + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + while (1) { + + FD_ZERO(&write_fds); + FD_ZERO(&except_fds); + high_fd = set_fdset(listen_list,&read_fds); + +#if !defined(WIN32) + num_ready = select(high_fd + 1, +#else + num_ready = select(1, +#endif + &read_fds, + &write_fds, + &except_fds, + NULL); + + if (num_ready < 0) { + fprintf(where, + "%s: select failure: %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(where); + exit(1); + } + + /* try to keep things simple */ + candidate = 0; + while ((num_ready) && (candidate <= high_fd)) { + if (FD_ISSET(candidate,&read_fds)) { + accept_connection(candidate); + FD_CLR(candidate,&read_fds); + num_ready--; + } + else { + candidate++; + } + } + } +} + +#ifndef WIN32 +#define SERVER_ARGS "DdfhL:n:Np:v:VZ:46" +#else +#define SERVER_ARGS "DdfhL:n:Np:v:VZ:46I:i:" +#endif +void +scan_netserver_args(int argc, char *argv[]) { + + int c; + char arg1[BUFSIZ], arg2[BUFSIZ]; + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF){ + switch (c) { + case '?': + case 'h': + print_netserver_usage(); + exit(1); + case 'd': + debug++; + suppress_debug = 0; + break; + case 'D': + /* perhaps one of these days we'll take an argument */ + want_daemonize = 0; + not_inetd = 1; + break; + case 'f': + spawn_on_accept = 0; + not_inetd = 1; + break; +#ifdef WIN32 + case 'I': + child = TRUE; + break; + case 'i': + break; +#endif + 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); + } + break; + case 'n': + shell_num_cpus = atoi(optarg); + if (shell_num_cpus > MAXCPUS) { + fprintf(stderr, + "netserver: This version can only support %d CPUs. Please" + "increase MAXCPUS in netlib.h and recompile.\n", + MAXCPUS); + fflush(stderr); + exit(1); + } + break; + case 'N': + suppress_debug = 1; + debug = 0; + 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 'Z': + /* only copy as much of the passphrase as could fit in the + test-specific portion of a control message. Windows does not + seem to have a strndup() so just malloc and strncpy it. we + weren't checking the strndup() return so won't bother with + checking malloc(). we will though make certain we only + allocated it once in the event that someone puts -Z on the + command line more than once */ + if (passphrase == NULL) + passphrase = malloc(sizeof(netperf_request.content.test_specific_data)); + strncpy(passphrase, + optarg, + sizeof(netperf_request.content.test_specific_data)); + passphrase[sizeof(netperf_request.content.test_specific_data) - 1] = '\0'; + break; + case '4': + local_address_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + local_address_family = AF_INET6; +#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; + + } + } +} + +void +daemonize() { +#if defined(HAVE_FORK) + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + /* flush the usual suspects */ + fflush(stdin); + fflush(stdout); + fflush(stderr); + + switch (fork()) { + case -1: + fprintf(stderr, + "%s: fork() error %s (errno %d)\n", + __FUNCTION__, + strerror(errno), + errno); + fflush(stderr); + exit(1); + case 0: + + /* perhaps belt and suspenders, but if we dump core, perhaps + better to do so here. we won't worry about the call being + successful though. raj 2011-07-08 */ + chdir(DEBUG_LOG_FILE_DIR); + + /* we are the child. we should get a new "where" to match our new + pid */ + + open_debug_file(); + +#ifdef HAVE_SETSID + setsid(); +#else + setpgrp(); +#endif /* HAVE_SETSID */ + + signal(SIGCLD, SIG_IGN); + + /* ok, we can start accepting control connections now */ + accept_connections(); + + default: + /* we are the parent, nothing to do but exit? */ + exit(0); + } + +#else + fprintf(where, + "%s called on platform which cannot daemonize\n", + __FUNCTION__); + fflush(where); + exit(1); +#endif /* HAVE_FORK */ +} + +static void +check_if_inetd() { + + if (debug) { + fprintf(where, + "%s: enter\n", + __FUNCTION__); + fflush(where); + } + + if (not_inetd) { + return; + } + else { +#if !defined(WIN32) && !defined(__VMS) + struct sockaddr_storage name; + netperf_socklen_t namelen; + + namelen = sizeof(name); + if (getsockname(0, + (struct sockaddr *)&name, + &namelen) == SOCKET_ERROR) { + not_inetd = 1; + } + else { + not_inetd = 0; + child = 1; + } +#endif + } +} + +/* OK, so how does all this work you ask? Well, we are in a maze of + twisty options, all different. Netserver can be invoked as a child + of inetd or the VMS auxiliary server process, or a parent netserver + process. In those cases, we could/should follow the "child" + path. However, there are really two "child" paths through the + netserver code. + + When this netserver is a child of a parent netserver in the + case of *nix, the child process will be created by a + spawn_child_process() in accept_connections() and will not hit the + "child" path here in main(). + + When this netserver is a child of a parent netserver in the case of + windows, the child process will have been spawned via a + Create_Process() call in spawn_child_process() in + accept_connections, but will flow through here again. We rely on + the scan_netserver_args() call to have noticed the magic option + that tells us we are a child process. + + When this netserver is launched from the command line we will first + set-up the listen endpoint(s) for the controll connection. At that + point we decide if we want to and can become our own daemon, or + stay attached to the "terminal." When this happens under *nix, we + will again take a fork() path via daemonize() and will not come + back through main(). If we ever learn how to become our own daemon + under Windows, we will undoubtedly take a Create_Process() path + again and will come through main() once again - that is what the + "daemon" case here is all about. + + It is hoped that this is all much clearer than the old spaghetti + code that netserver had become. raj 2011-07-11 */ + + +int _cdecl +main(int argc, char *argv[]) { + +#ifdef WIN32 + WSADATA wsa_data ; + + /* Initialize the winsock lib do we still want version 2.2? */ + if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){ + printf("WSAStartup() failed : %lu\n", GetLastError()) ; + return -1 ; + } +#endif /* WIN32 */ + + /* Save away the program name */ + program = (char *)malloc(strlen(argv[0]) + 1); + if (program == NULL) { + printf("malloc for program name failed!\n"); + return -1 ; + } + strcpy(program, argv[0]); + + init_netserver_globals(); + + netlib_init(); + + strncpy(local_host_name,"",sizeof(local_host_name)); + local_address_family = AF_UNSPEC; + strncpy(listen_port,TEST_PORT,sizeof(listen_port)); + + scan_netserver_args(argc, argv); + + check_if_inetd(); + + if (child) { + /* we are the child of either an inetd or parent netserver via + spawning (Windows) rather than fork()ing. if we were fork()ed + we would not be coming through this way. set_server_sock() must + be called before open_debug_file() or there is a chance that + we'll toast the descriptor when we do not wish it. */ + set_server_sock(); + open_debug_file(); + process_requests(); + } + else if (daemon_parent) { + /* we are the parent daemonized netserver + process. accept_connections() will decide if we want to spawn a + child process */ + accept_connections(); + } + else { + /* we are the top netserver process, so we have to create the + listen endpoint(s) and decide if we want to daemonize */ + setup_listens(local_host_name,listen_port,local_address_family); + if (want_daemonize) { + daemonize(); + } + accept_connections(); + } + + unlink_empty_debug_file(); + +#ifdef WIN32 + WSACleanup(); +#endif + + return 0; + +} |