blob: 874fe60e66f8859b1180c01cfe90cb1c776496e2 [file] [log] [blame]
/*---------------------------------------------------------------
* 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 "List.h"
#include "util.h"
#ifdef WIN32
#include "service.h"
#endif
/* -------------------------------------------------------------------
* prototypes
* ------------------------------------------------------------------- */
// Function called at exit to clean up as much as possible
void cleanup( void );
/* -------------------------------------------------------------------
* 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 groupCond;
// Condition used to signify advances of the current
// records being accessed in a report and also to
// serialize modification of the report list
Condition ReportCond;
Condition ReportDoneCond;
}
// 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;
// The main thread uses this function to wait
// for all other threads to complete
void waitUntilQuit( void );
/* -------------------------------------------------------------------
* 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 );
my_signal( SIGALRM, Sig_Interupt );
#ifndef WIN32
// 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
Condition_Initialize ( &ReportCond );
Condition_Initialize ( &ReportDoneCond );
Mutex_Initialize( &groupCond );
Mutex_Initialize( &clients_mutex );
// 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
thread_Settings* 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( argc, argv, ext_gSettings );
// Check for either having specified client or server
if ( ext_gSettings->mThreadMode == kMode_Client
|| ext_gSettings->mThreadMode == kMode_Listener ) {
#ifdef WIN32
// Start the server as a daemon
// Daemon mode for non-windows in handled
// in the listener_spawn function
if ( isDaemon( ext_gSettings ) ) {
CmdInstallService(argc, argv);
return 0;
}
// Remove the Windows service if requested
if ( isRemoveService( ext_gSettings ) ) {
// remove the service
if ( CmdRemoveService() ) {
fprintf(stderr, "IPerf Service is removed.\n");
return 0;
}
}
#endif
// initialize client(s)
if ( ext_gSettings->mThreadMode == kMode_Client ) {
client_init( ext_gSettings );
}
#ifdef HAVE_THREAD
// start up the reporter and client(s) or listener
{
thread_Settings *into = NULL;
// Create the settings structure for the reporter thread
Settings_Copy( ext_gSettings, &into );
into->mThreadMode = kMode_Reporter;
// Have the reporter launch the client or listener
into->runNow = ext_gSettings;
// Start all the threads that are ready to go
thread_start( into );
}
#else
// No need to make a reporter thread because we don't have threads
thread_start( ext_gSettings );
#endif
} else {
// 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[] =
{
{ TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
{ NULL, NULL}
};
// Only attempt to start the service if "-D" was specified
if ( !isDaemon(ext_gSettings) ||
// starting the service by SCM, there is no arguments will be passed in.
// the arguments will pass into Service_Main entry.
!StartServiceCtrlDispatcher(dispatchTable) )
// If the service failed to start then print usage
#endif
fprintf( stderr, usage_short, argv[0], argv[0] );
return 0;
}
// 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 ( 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 = 1;
// with threads, stop waiting for non-terminating threads
// (ie Listener Thread)
thread_release_nonterm( 1 );
#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( void ) {
#ifdef WIN32
// Shutdown Winsock
WSACleanup();
#endif
// clean up the list of clients
Iperf_destroy ( &clients );
// shutdown the thread subsystem
thread_destroy( );
} // end cleanup