1 /*---------------------------------------------------------------
2 * Copyright (c) 1999,2000,2001,2002,2003
3 * The Board of Trustees of the University of Illinois
5 *---------------------------------------------------------------
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software (Iperf) and associated
8 * documentation files (the "Software"), to deal in the Software
9 * without restriction, including without limitation the
10 * rights to use, copy, modify, merge, publish, distribute,
11 * sublicense, and/or sell copies of the Software, and to permit
12 * persons to whom the Software is furnished to do
13 * so, subject to the following conditions:
16 * Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and
18 * the following disclaimers.
21 * Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimers in the documentation and/or other materials
24 * provided with the distribution.
27 * Neither the names of the University of Illinois, NCSA,
28 * nor the names of its contributors may be used to endorse
29 * or promote products derived from this Software without
30 * specific prior written permission.
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
34 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
36 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
38 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 * ________________________________________________________________
41 * National Laboratory for Applied Network Research
42 * National Center for Supercomputing Applications
43 * University of Illinois at Urbana-Champaign
44 * http://www.ncsa.uiuc.edu
45 * ________________________________________________________________
47 * by Mark Gates <mgates@nlanr.net>
48 * & Ajay Tirumala <tirumala@ncsa.uiuc.edu>
49 * -------------------------------------------------------------------
50 * main does initialization and creates the various objects that will
51 * actually run the iperf program, then waits in the Joinall().
52 * -------------------------------------------------------------------
59 * ------------------------------------------------------------------- */
65 #include "Settings.hpp"
66 #include "PerfSocket.hpp"
68 #include "Condition.h"
69 #include "Timestamp.hpp"
70 #include "Listener.hpp"
78 /* -------------------------------------------------------------------
80 * ------------------------------------------------------------------- */
81 // Function called at exit to clean up as much as possible
84 /* -------------------------------------------------------------------
86 * ------------------------------------------------------------------- */
88 // Global flag to signal a user interrupt
90 // Global ID that we increment to be used
91 // as identifier for SUM reports
93 // Mutex to protect access to the above ID
95 // Condition used to signify advances of the current
96 // records being accessed in a report and also to
97 // serialize modification of the report list
101 // global variables only accessed within this file
103 // Thread that received the SIGTERM or SIGINT signal
104 // Used to ensure that if multiple threads receive the
105 // signal we do not prematurely exit
107 // The main thread uses this function to wait
108 // for all other threads to complete
109 void waitUntilQuit( void );
111 /* -------------------------------------------------------------------
113 * Entry point into Iperf
115 * sets up signal handlers
116 * initialize global locks and conditions
117 * parses settings from environment and command line
118 * starts up server or client thread
119 * waits for all threads to complete
120 * ------------------------------------------------------------------- */
121 int main( int argc, char **argv ) {
123 // Set SIGTERM and SIGINT to call our user interrupt function
124 my_signal( SIGTERM, Sig_Interupt );
125 my_signal( SIGINT, Sig_Interupt );
128 // Ignore broken pipes
129 signal(SIGPIPE,SIG_IGN);
133 int rc = WSAStartup( 0x202, &wsaData );
134 WARN_errno( rc == SOCKET_ERROR, "WSAStartup" );
135 if (rc == SOCKET_ERROR)
138 // Tell windows we want to handle our own signals
139 SetConsoleCtrlHandler( sig_dispatcher, true );
142 // Initialize global mutexes and conditions
143 Condition_Initialize ( &ReportCond );
144 Mutex_Initialize( &groupCond );
145 Mutex_Initialize( &clients_mutex );
147 // Initialize the thread subsystem
150 // Initialize the interrupt handling thread to 0
151 sThread = thread_zeroid();
153 // perform any cleanup when quitting Iperf
156 // Allocate the "global" settings
157 thread_Settings* ext_gSettings = new thread_Settings;
159 // Initialize settings to defaults
160 Settings_Initialize( ext_gSettings );
161 // read settings from environment variables
162 Settings_ParseEnvironment( ext_gSettings );
163 // read settings from command-line parameters
164 Settings_ParseCommandLine( argc, argv, ext_gSettings );
166 // Check for either having specified client or server
167 if ( ext_gSettings->mThreadMode == kMode_Client
168 || ext_gSettings->mThreadMode == kMode_Listener ) {
170 // Start the server as a daemon
171 // Daemon mode for non-windows in handled
172 // in the listener_spawn function
173 if ( isDaemon( ext_gSettings ) ) {
174 CmdInstallService(argc, argv);
178 // Remove the Windows service if requested
179 if ( isRemoveService( ext_gSettings ) ) {
180 // remove the service
181 if ( CmdRemoveService() ) {
182 fprintf(stderr, "IPerf Service is removed.\n");
188 // initialize client(s)
189 if ( ext_gSettings->mThreadMode == kMode_Client ) {
190 client_init( ext_gSettings );
194 // start up the reporter and client(s) or listener
196 thread_Settings *into = NULL;
197 // Create the settings structure for the reporter thread
198 Settings_Copy( ext_gSettings, &into );
199 into->mThreadMode = kMode_Reporter;
201 // Have the reporter launch the client or listener
202 into->runNow = ext_gSettings;
204 // Start all the threads that are ready to go
205 thread_start( into );
208 // No need to make a reporter thread because we don't have threads
209 thread_start( ext_gSettings );
212 // neither server nor client mode was specified
213 // print usage and exit
216 // In Win32 we also attempt to start a previously defined service
217 // Starting in 2.0 to restart a previously defined service
218 // you must call iperf with "iperf -D" or using the environment variable
219 SERVICE_TABLE_ENTRY dispatchTable[] =
221 { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
225 // Only attempt to start the service if "-D" was specified
226 if ( !isDaemon(ext_gSettings) ||
227 // starting the service by SCM, there is no arguments will be passed in.
228 // the arguments will pass into Service_Main entry.
229 !StartServiceCtrlDispatcher(dispatchTable) )
230 // If the service failed to start then print usage
232 fprintf( stderr, usage_short, argv[0], argv[0] );
237 // wait for other (client, server) threads to complete
244 /* -------------------------------------------------------------------
245 * Signal handler sets the sInterupted flag, so the object can
246 * respond appropriately.. [static]
247 * ------------------------------------------------------------------- */
249 void Sig_Interupt( int inSigno ) {
251 // We try to not allow a single interrupt handled by multiple threads
252 // to completely kill the app so we save off the first thread ID
253 // then that is the only thread that can supply the next interrupt
254 if ( thread_equalid( sThread, thread_zeroid() ) ) {
255 sThread = thread_getid();
256 } else if ( thread_equalid( sThread, thread_getid() ) ) {
260 // global variable used by threads to see if they were interrupted
263 // with threads, stop waiting for non-terminating threads
264 // (ie Listener Thread)
265 thread_release_nonterm( 1 );
268 // without threads, just exit quietly, same as sig_exit()
273 /* -------------------------------------------------------------------
274 * Any necesary cleanup before Iperf quits. Called at program exit,
275 * either by exit() or terminating main().
276 * ------------------------------------------------------------------- */
278 void cleanup( void ) {
283 // clean up the list of clients
284 Iperf_destroy ( &clients );
286 // shutdown the thread subsystem
291 /*--------------------------------------------------------------------
294 * each time starting the service, this is the entry point of the service.
295 * Start the service, certainly it is on server-mode
297 *-------------------------------------------------------------------- */
298 VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
300 // report the status to the service control manager.
302 if ( !ReportStatusToSCMgr(
303 SERVICE_START_PENDING, // service state
304 NO_ERROR, // exit code
308 thread_Settings* ext_gSettings = new thread_Settings;
310 // Initialize settings to defaults
311 Settings_Initialize( ext_gSettings );
312 // read settings from environment variables
313 Settings_ParseEnvironment( ext_gSettings );
314 // read settings from command-line parameters
315 Settings_ParseCommandLine( dwArgc, lpszArgv, ext_gSettings );
317 // report the status to the service control manager.
319 if ( !ReportStatusToSCMgr(
320 SERVICE_START_PENDING, // service state
321 NO_ERROR, // exit code
325 // if needed, redirect the output into a specified file
326 if ( !isSTDOUT( ext_gSettings ) ) {
327 redirect( ext_gSettings->mOutputFileName );
330 // report the status to the service control manager.
332 if ( !ReportStatusToSCMgr(
333 SERVICE_START_PENDING, // service state
334 NO_ERROR, // exit code
338 // initialize client(s)
339 if ( ext_gSettings->mThreadMode == kMode_Client ) {
340 client_init( ext_gSettings );
343 // start up the reporter and client(s) or listener
345 thread_Settings *into = NULL;
347 Settings_Copy( ext_gSettings, &into );
348 into->mThreadMode = kMode_Reporter;
349 into->runNow = ext_gSettings;
351 into = ext_gSettings;
353 thread_start( into );
356 // report the status to the service control manager.
358 if ( !ReportStatusToSCMgr(
359 SERVICE_RUNNING, // service state
360 NO_ERROR, // exit code
365 // wait for other (client, server) threads to complete
371 // FUNCTION: ServiceStop
373 // PURPOSE: Stops the service
382 // If a ServiceStop procedure is going to
383 // take longer than 3 seconds to execute,
384 // it should spawn a thread to execute the
385 // stop code, and return. Otherwise, the
386 // ServiceControlManager will believe that
387 // the service has stopped responding.