diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:20:54 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:20:54 +0000 |
commit | 7c720bec5600a9e607c875c670ca30ed351fa4ba (patch) | |
tree | c38c9bedf07616180feee6b91a1dbea038500b54 /src/main.cpp | |
parent | Initial commit. (diff) | |
download | iperf-7c720bec5600a9e607c875c670ca30ed351fa4ba.tar.xz iperf-7c720bec5600a9e607c875c670ca30ed351fa4ba.zip |
Adding upstream version 2.1.9+dfsg.upstream/2.1.9+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7038faa --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,479 @@ +/*--------------------------------------------------------------- + * Copyright (c) 1999,2000,2001,2002,2003 + * The Board of Trustees of the University of Illinois + * All Rights Reserved. + *--------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software (Iperf) and associated + * documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and + * the following disclaimers. + * + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimers in the documentation and/or other materials + * provided with the distribution. + * + * + * Neither the names of the University of Illinois, NCSA, + * nor the names of its contributors may be used to endorse + * or promote products derived from this Software without + * specific prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ________________________________________________________________ + * National Laboratory for Applied Network Research + * National Center for Supercomputing Applications + * University of Illinois at Urbana-Champaign + * http://www.ncsa.uiuc.edu + * ________________________________________________________________ + * main.cpp + * by Mark Gates <mgates@nlanr.net> + * & Ajay Tirumala <tirumala@ncsa.uiuc.edu> + * ------------------------------------------------------------------- + * main does initialization and creates the various objects that will + * actually run the iperf program, then waits in the Joinall(). + * ------------------------------------------------------------------- + * headers + * uses + * <stdlib.h> + * <string.h> + * + * <signal.h> + * ------------------------------------------------------------------- */ + +#define HEADERS() + +#include "headers.h" +#include "Settings.hpp" +#include "PerfSocket.hpp" +#include "Locale.h" +#include "Condition.h" +#include "Timestamp.hpp" +#include "Listener.hpp" +#include "active_hosts.h" +#include "util.h" +#include "Reporter.h" +#include "payloads.h" + +#ifdef WIN32 +#include "service.h" +#endif + +/* ------------------------------------------------------------------- + * prototypes + * ------------------------------------------------------------------- */ +// Function called at exit to clean up as much as possible +void cleanup(); +// signal handlers +static void Sig_Interupt(int inSigno); + +/* ------------------------------------------------------------------- + * global variables + * ------------------------------------------------------------------- */ +extern "C" { + // Global flag to signal a user interrupt + int sInterupted = 0; + // Global ID that we increment to be used + // as identifier for SUM reports + int groupID = 0; + // Mutex to protect access to the above ID + Mutex transferid_mutex; + // Condition used to signal the reporter thread + // when a packet ring is full. Shouldn't really + // be needed but is "belts and suspeners" + struct Condition ReportCond; + // Initialize reporter thread mutex + struct AwaitMutex reporter_state; + struct AwaitMutex threads_start; + struct BarrierMutex transmits_start; +} + + +// global variables only accessed within this file + +// Thread that received the SIGTERM or SIGINT signal +// Used to ensure that if multiple threads receive the +// signal we do not prematurely exit +nthread_t sThread; +static thread_Settings* ext_gSettings; +// The main thread uses this function to wait +// for all other threads to complete +void waitUntilQuit(); + +/* ------------------------------------------------------------------- + * main() + * Entry point into Iperf + * + * sets up signal handlers + * initialize global locks and conditions + * parses settings from environment and command line + * starts up server or client thread + * waits for all threads to complete + * ------------------------------------------------------------------- */ +int main(int argc, char **argv) { + + // Set SIGTERM and SIGINT to call our user interrupt function + my_signal(SIGTERM, Sig_Interupt); + my_signal(SIGINT, Sig_Interupt); +#ifndef WIN32 + my_signal(SIGALRM, Sig_Interupt); + // Ignore broken pipes + signal(SIGPIPE,SIG_IGN); +#else + // Start winsock + WSADATA wsaData; + int rc = WSAStartup(0x202, &wsaData); + WARN_errno(rc == SOCKET_ERROR, "WSAStartup"); + if (rc == SOCKET_ERROR) + return 0; + // Tell windows we want to handle our own signals + SetConsoleCtrlHandler(sig_dispatcher, true); +#endif + + // Initialize global mutexes and conditions + Iperf_initialize_active_table(); + Condition_Initialize (&ReportCond); + +#ifdef HAVE_THREAD_DEBUG + Mutex_Initialize(&packetringdebug_mutex); + Mutex_Initialize(&thread_debug_mutex); +#endif + Mutex_Initialize(&transferid_mutex); + + // Initialize reporter thread mutex + reporter_state.ready = 0; + threads_start.ready = 0; + transmits_start.count = 0; + Condition_Initialize(&reporter_state.await); + Condition_Initialize(&threads_start.await); + Condition_Initialize(&transmits_start.await); + + // Initialize the thread subsystem + thread_init(); + + // Initialize the interrupt handling thread to 0 + sThread = thread_zeroid(); + + // perform any cleanup when quitting Iperf + atexit(cleanup); + + // Allocate the "global" settings + ext_gSettings = new thread_Settings; + // Default reporting mode here to avoid unitialized warnings + // this won't be the actual mode + ThreadMode ReporterThreadMode = kMode_Reporter; + // Initialize settings to defaults + Settings_Initialize(ext_gSettings); + // read settings from environment variables + Settings_ParseEnvironment(ext_gSettings); + // read settings from command-line parameters + Settings_ParseCommandLine(argc, argv, ext_gSettings); + + // Check for either having specified client or server + if ((ext_gSettings->mThreadMode != kMode_Client) && (ext_gSettings->mThreadMode != kMode_Listener)) { + // neither server nor client mode was specified + // print usage and exit + +#ifdef WIN32 + // In Win32 we also attempt to start a previously defined service + // Starting in 2.0 to restart a previously defined service + // you must call iperf with "iperf -D" or using the environment variable + SERVICE_TABLE_ENTRY dispatchTable[] = + { + { (LPSTR)TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main}, + { NULL, NULL} + }; + + // starting the service by SCM, there is no arguments will be passed in. + // the arguments will pass into Service_Main entry. + if (!StartServiceCtrlDispatcher(dispatchTable)) + // If the service failed to start then print usage +#endif + fprintf(stderr, usage_short, argv[0], argv[0]); + return 0; + } + + if (!isSTDOUT(ext_gSettings)) { +#ifdef HAVE_FREOPEN + FILE *fd; + fprintf(stdout, "Output from stdout (but not from stderr) will be redirected to file %s\n", ext_gSettings->mOutputFileName); + fflush(stdout); + fd = freopen(ext_gSettings->mOutputFileName, "w", stdout); + FAIL_errno(fd == NULL, "freopen stdout\n", ext_gSettings); +#else + fprintf(stderr, "Output to file not supported\n"); +#endif + + } + + int mbuflen = (ext_gSettings->mBufLen > MINMBUFALLOCSIZE) ? ext_gSettings->mBufLen : MINMBUFALLOCSIZE; +#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET)) + mbuflen += TAPBYTESSLOP; +#endif + ext_gSettings->mBuf = new char[mbuflen]; + memset(ext_gSettings->mBuf, 0, mbuflen); + + + unsetReport(ext_gSettings); + switch (ext_gSettings->mThreadMode) { + case kMode_Client : + if (isDaemon(ext_gSettings)) { + fprintf(stderr, "Iperf client cannot be run as a daemon\n"); + return 0; + } + // initialize client(s) + transmits_start.count = ext_gSettings->mThreads; + ext_gSettings->connects_done = &transmits_start; + client_init(ext_gSettings); + ReporterThreadMode = kMode_ReporterClient; + break; + case kMode_Listener : +#ifdef WIN32 + // Remove the Windows service if requested + if (isRemoveService(ext_gSettings)) { + // remove the service + if (CmdRemoveService()) { + fprintf(stderr, "IPerf Service is removed.\n"); + } + } + if (isDaemon(ext_gSettings)) { + CmdInstallService(argc, argv); + } else if (isRemoveService(ext_gSettings)) { + return 0; + } +#else // *nix system + if (isDaemon(ext_gSettings)) { + fprintf(stderr, "Running Iperf Server as a daemon\n"); + // Start the server as a daemon + // redirect stdin, stdout and sterr to /dev/null (see dameon and no close flag) + if (daemon(1, 0) < 0) { + perror("daemon"); + } + } +#endif + // Start up any parallel listener threads + if (ext_gSettings->mPortLast) { + listeners_init(ext_gSettings); + } + break; + default : + fprintf(stderr, "unknown mode"); + break; + } +#ifdef HAVE_THREAD + // Last step is to initialize the reporter then start all threads + { + thread_Settings *into = NULL; + // Create the settings structure for the reporter thread + Settings_Copy(ext_gSettings, &into, DEEP_COPY); + into->mThreadMode = ReporterThreadMode; + into->mSumReport = NULL; + into->mFullDuplexReport = NULL; + // Have the reporter launch the client or listener + into->runNow = ext_gSettings; + // Start all the threads that are ready to go + thread_start_all(into); + Condition_Lock(threads_start.await); + threads_start.ready = 1; + Condition_Signal(&threads_start.await); + Condition_Unlock(threads_start.await); + } +#else + // No need to make a reporter thread because we don't have threads + thread_start(ext_gSettings); +#endif + // wait for other (client, server) threads to complete + thread_joinall(); + // all done! + return 0; +} // end main + +/* ------------------------------------------------------------------- + * Signal handler sets the sInterupted flag, so the object can + * respond appropriately.. [static] + * ------------------------------------------------------------------- */ + +void Sig_Interupt (int inSigno) { +#ifdef HAVE_THREAD + // We try to not allow a single interrupt handled by multiple threads + // to completely kill the app so we save off the first thread ID + // then that is the only thread that can supply the next interrupt + if ((inSigno == SIGINT) && thread_equalid(sThread, thread_zeroid())) { + sThread = thread_getid(); + } else if (thread_equalid(sThread, thread_getid())) { + sig_exit(inSigno); + } + // global variable used by threads to see if they were interrupted + sInterupted = inSigno; + + // Note: ignore alarms per setitimer +#if HAVE_DECL_SIGALRM + if (inSigno != SIGALRM) +#endif + // with threads, stop waiting for non-terminating threads + // (ie Listener Thread) + thread_release_nonterm(inSigno); + +#else + // without threads, just exit quietly, same as sig_exit() + sig_exit(inSigno); +#endif +} + +/* ------------------------------------------------------------------- + * Any necesary cleanup before Iperf quits. Called at program exit, + * either by exit() or terminating main(). + * ------------------------------------------------------------------- */ + +void cleanup () { +#ifdef WIN32 + // Shutdown Winsock + WSACleanup(); +#endif + // clean up the list of active clients + Iperf_destroy_active_table(); + // done actions + // Destroy global mutexes and conditions + + Condition_Destroy (&ReportCond); + Condition_Destroy(&reporter_state.await); + Condition_Destroy(&threads_start.await); + Condition_Destroy(&transmits_start.await); +#ifdef HAVE_THREAD_DEBUG + Mutex_Destroy(&packetringdebug_mutex); + Mutex_Destroy(&thread_debug_mutex); +#endif + Mutex_Destroy(&transferid_mutex); + // shutdown the thread subsystem + thread_destroy(); +} // end cleanup + +#ifdef WIN32 +/*-------------------------------------------------------------------- + * ServiceStart + * + * each time starting the service, this is the entry point of the service. + * Start the service, certainly it is on server-mode + * + *-------------------------------------------------------------------- */ +VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) { + thread_Settings* ext_gSettings; + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000)) // wait hint + goto clean; + + ext_gSettings = new thread_Settings; + + // Initialize settings to defaults + Settings_Initialize(ext_gSettings); + // read settings from environment variables + Settings_ParseEnvironment(ext_gSettings); + // read settings from command-line parameters + Settings_ParseCommandLine(dwArgc, lpszArgv, ext_gSettings); + + // Arguments will be lost when the service is started by SCM, but + // we need to be at least a listener + ext_gSettings->mThreadMode = kMode_Listener; + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000)) // wait hint + goto clean; + + // if needed, redirect the output into a specified file + if (!isSTDOUT(ext_gSettings)) { + redirect(ext_gSettings->mOutputFileName); + } + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000)) // wait hint + goto clean; + + // initialize client(s) + if (ext_gSettings->mThreadMode == kMode_Client) { + client_init(ext_gSettings); + } + + // start up the reporter and client(s) or listener + { + thread_Settings *into = NULL; +#ifdef HAVE_THREAD + Settings_Copy(ext_gSettings, &into, DEEP_COPY); + into->mThreadMode = kMode_Reporter; + into->runNow = ext_gSettings; +#else + into = ext_gSettings; +#endif + thread_start_all(into); + } + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_RUNNING, // service state + NO_ERROR, // exit code + 0)) // wait hint + goto clean; + + clean: + // wait for other (client, server) threads to complete + thread_joinall(); +} + +// +// FUNCTION: ServiceStop +// +// PURPOSE: Stops the service +// +// PARAMETERS: +// none +// +// RETURN VALUE: +// none +// +// COMMENTS: +// If a ServiceStop procedure is going to +// take longer than 3 seconds to execute, +// it should spawn a thread to execute the +// stop code, and return. Otherwise, the +// ServiceControlManager will believe that +// the service has stopped responding. +// +VOID ServiceStop() { +#ifdef HAVE_THREAD + Sig_Interupt(1); +#else + sig_exit(1); +#endif +} + +#endif |