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
99 Condition ReportDoneCond;
102 // global variables only accessed within this file
104 // Thread that received the SIGTERM or SIGINT signal
105 // Used to ensure that if multiple threads receive the
106 // signal we do not prematurely exit
108 // The main thread uses this function to wait
109 // for all other threads to complete
110 void waitUntilQuit( void );
112 /* -------------------------------------------------------------------
114 * Entry point into Iperf
116 * sets up signal handlers
117 * initialize global locks and conditions
118 * parses settings from environment and command line
119 * starts up server or client thread
120 * waits for all threads to complete
121 * ------------------------------------------------------------------- */
122 int main( int argc, char **argv ) {
124 // Set SIGTERM and SIGINT to call our user interrupt function
125 my_signal( SIGTERM, Sig_Interupt );
126 my_signal( SIGINT, Sig_Interupt );
129 // Ignore broken pipes
130 signal(SIGPIPE,SIG_IGN);
134 int rc = WSAStartup( 0x202, &wsaData );
135 WARN_errno( rc == SOCKET_ERROR, "WSAStartup" );
136 if (rc == SOCKET_ERROR)
139 // Tell windows we want to handle our own signals
140 SetConsoleCtrlHandler( sig_dispatcher, true );
143 // Initialize global mutexes and conditions
144 Condition_Initialize ( &ReportCond );
145 Condition_Initialize ( &ReportDoneCond );
146 Mutex_Initialize( &groupCond );
147 Mutex_Initialize( &clients_mutex );
149 // Initialize the thread subsystem
152 // Initialize the interrupt handling thread to 0
153 sThread = thread_zeroid();
155 // perform any cleanup when quitting Iperf
158 // Allocate the "global" settings
159 thread_Settings* ext_gSettings = new thread_Settings;
161 // Initialize settings to defaults
162 Settings_Initialize( ext_gSettings );
163 // read settings from environment variables
164 Settings_ParseEnvironment( ext_gSettings );
165 // read settings from command-line parameters
166 Settings_ParseCommandLine( argc, argv, ext_gSettings );
168 // Check for either having specified client or server
169 if ( ext_gSettings->mThreadMode == kMode_Client
170 || ext_gSettings->mThreadMode == kMode_Listener ) {
172 // Start the server as a daemon
173 // Daemon mode for non-windows in handled
174 // in the listener_spawn function
175 if ( isDaemon( ext_gSettings ) ) {
176 CmdInstallService(argc, argv);
180 // Remove the Windows service if requested
181 if ( isRemoveService( ext_gSettings ) ) {
182 // remove the service
183 if ( CmdRemoveService() ) {
184 fprintf(stderr, "IPerf Service is removed.\n");
190 // initialize client(s)
191 if ( ext_gSettings->mThreadMode == kMode_Client ) {
192 client_init( ext_gSettings );
196 // start up the reporter and client(s) or listener
198 thread_Settings *into = NULL;
199 // Create the settings structure for the reporter thread
200 Settings_Copy( ext_gSettings, &into );
201 into->mThreadMode = kMode_Reporter;
203 // Have the reporter launch the client or listener
204 into->runNow = ext_gSettings;
206 // Start all the threads that are ready to go
207 thread_start( into );
210 // No need to make a reporter thread because we don't have threads
211 thread_start( ext_gSettings );
214 // neither server nor client mode was specified
215 // print usage and exit
218 // In Win32 we also attempt to start a previously defined service
219 // Starting in 2.0 to restart a previously defined service
220 // you must call iperf with "iperf -D" or using the environment variable
221 SERVICE_TABLE_ENTRY dispatchTable[] =
223 { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
227 // Only attempt to start the service if "-D" was specified
228 if ( !isDaemon(ext_gSettings) ||
229 // starting the service by SCM, there is no arguments will be passed in.
230 // the arguments will pass into Service_Main entry.
231 !StartServiceCtrlDispatcher(dispatchTable) )
232 // If the service failed to start then print usage
234 fprintf( stderr, usage_short, argv[0], argv[0] );
239 // wait for other (client, server) threads to complete
246 /* -------------------------------------------------------------------
247 * Signal handler sets the sInterupted flag, so the object can
248 * respond appropriately.. [static]
249 * ------------------------------------------------------------------- */
251 void Sig_Interupt( int inSigno ) {
253 // We try to not allow a single interrupt handled by multiple threads
254 // to completely kill the app so we save off the first thread ID
255 // then that is the only thread that can supply the next interrupt
256 if ( thread_equalid( sThread, thread_zeroid() ) ) {
257 sThread = thread_getid();
258 } else if ( thread_equalid( sThread, thread_getid() ) ) {
262 // global variable used by threads to see if they were interrupted
265 // with threads, stop waiting for non-terminating threads
266 // (ie Listener Thread)
267 thread_release_nonterm( 1 );
270 // without threads, just exit quietly, same as sig_exit()
275 /* -------------------------------------------------------------------
276 * Any necesary cleanup before Iperf quits. Called at program exit,
277 * either by exit() or terminating main().
278 * ------------------------------------------------------------------- */
280 void cleanup( void ) {
285 // clean up the list of clients
286 Iperf_destroy ( &clients );
288 // shutdown the thread subsystem
293 /*--------------------------------------------------------------------
296 * each time starting the service, this is the entry point of the service.
297 * Start the service, certainly it is on server-mode
299 *-------------------------------------------------------------------- */
300 VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
302 // report the status to the service control manager.
304 if ( !ReportStatusToSCMgr(
305 SERVICE_START_PENDING, // service state
306 NO_ERROR, // exit code
310 thread_Settings* ext_gSettings = new thread_Settings;
312 // Initialize settings to defaults
313 Settings_Initialize( ext_gSettings );
314 // read settings from environment variables
315 Settings_ParseEnvironment( ext_gSettings );
316 // read settings from command-line parameters
317 Settings_ParseCommandLine( dwArgc, lpszArgv, ext_gSettings );
319 // report the status to the service control manager.
321 if ( !ReportStatusToSCMgr(
322 SERVICE_START_PENDING, // service state
323 NO_ERROR, // exit code
327 // if needed, redirect the output into a specified file
328 if ( !isSTDOUT( ext_gSettings ) ) {
329 redirect( ext_gSettings->mOutputFileName );
332 // report the status to the service control manager.
334 if ( !ReportStatusToSCMgr(
335 SERVICE_START_PENDING, // service state
336 NO_ERROR, // exit code
340 // initialize client(s)
341 if ( ext_gSettings->mThreadMode == kMode_Client ) {
342 client_init( ext_gSettings );
345 // start up the reporter and client(s) or listener
347 thread_Settings *into = NULL;
349 Settings_Copy( ext_gSettings, &into );
350 into->mThreadMode = kMode_Reporter;
351 into->runNow = ext_gSettings;
353 into = ext_gSettings;
355 thread_start( into );
358 // report the status to the service control manager.
360 if ( !ReportStatusToSCMgr(
361 SERVICE_RUNNING, // service state
362 NO_ERROR, // exit code
367 // wait for other (client, server) threads to complete
373 // FUNCTION: ServiceStop
375 // PURPOSE: Stops the service
384 // If a ServiceStop procedure is going to
385 // take longer than 3 seconds to execute,
386 // it should spawn a thread to execute the
387 // stop code, and return. Otherwise, the
388 // ServiceControlManager will believe that
389 // the service has stopped responding.