]> sjero.net Git - iperf/blob - src/main.cpp
Fix CPU Usage Bug
[iperf] / src / main.cpp
1 /*--------------------------------------------------------------- 
2  * Copyright (c) 1999,2000,2001,2002,2003                              
3  * The Board of Trustees of the University of Illinois            
4  * All Rights Reserved.                                           
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: 
14  *
15  *     
16  * Redistributions of source code must retain the above 
17  * copyright notice, this list of conditions and 
18  * the following disclaimers. 
19  *
20  *     
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. 
25  * 
26  *     
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. 
31  * 
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  * ________________________________________________________________ 
46  * main.cpp
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  * -------------------------------------------------------------------
53  * headers
54  * uses
55  *   <stdlib.h>
56  *   <string.h>
57  *
58  *   <signal.h>
59  * ------------------------------------------------------------------- */
60
61 #define HEADERS()
62
63 #include "headers.h"
64
65 #include "Settings.hpp"
66 #include "PerfSocket.hpp"
67 #include "Locale.h"
68 #include "Condition.h"
69 #include "Timestamp.hpp"
70 #include "Listener.hpp"
71 #include "List.h"
72 #include "util.h"
73
74 #ifdef WIN32
75 #include "service.h"
76 #endif 
77
78 /* -------------------------------------------------------------------
79  * prototypes
80  * ------------------------------------------------------------------- */
81 // Function called at exit to clean up as much as possible
82 void cleanup( void );
83
84 /* -------------------------------------------------------------------
85  * global variables
86  * ------------------------------------------------------------------- */
87 extern "C" {
88     // Global flag to signal a user interrupt
89     int sInterupted = 0;
90     // Global ID that we increment to be used 
91     // as identifier for SUM reports
92     int groupID = 0;
93     // Mutex to protect access to the above ID
94     Mutex groupCond;
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
98     Condition ReportCond;
99     Condition ReportDoneCond;
100 }
101
102 // global variables only accessed within this file
103
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
107 nthread_t sThread;
108 // The main thread uses this function to wait 
109 // for all other threads to complete
110 void waitUntilQuit( void );
111
112 /* -------------------------------------------------------------------
113  * main()
114  *      Entry point into Iperf
115  *
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 ) {
123
124     // Set SIGTERM and SIGINT to call our user interrupt function
125     my_signal( SIGTERM, Sig_Interupt );
126     my_signal( SIGINT,  Sig_Interupt );
127
128 #ifndef WIN32
129     // Ignore broken pipes
130     signal(SIGPIPE,SIG_IGN);
131 #else
132     // Start winsock
133     WSADATA wsaData;
134     int rc = WSAStartup( 0x202, &wsaData );
135     WARN_errno( rc == SOCKET_ERROR, "WSAStartup" );
136         if (rc == SOCKET_ERROR)
137                 return 0;
138
139     // Tell windows we want to handle our own signals
140     SetConsoleCtrlHandler( sig_dispatcher, true );
141 #endif
142
143     // Initialize global mutexes and conditions
144     Condition_Initialize ( &ReportCond );
145     Condition_Initialize ( &ReportDoneCond );
146     Mutex_Initialize( &groupCond );
147     Mutex_Initialize( &clients_mutex );
148
149     // Initialize the thread subsystem
150     thread_init( );
151
152     // Initialize the interrupt handling thread to 0
153     sThread = thread_zeroid();
154
155     // perform any cleanup when quitting Iperf
156     atexit( cleanup );
157
158     // Allocate the "global" settings
159     thread_Settings* ext_gSettings = new thread_Settings;
160
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 );
167
168     // Check for either having specified client or server
169     if ( ext_gSettings->mThreadMode == kMode_Client 
170          || ext_gSettings->mThreadMode == kMode_Listener ) {
171 #ifdef WIN32
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);
177             return 0;
178         }
179
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");
185
186                 return 0;
187             }
188         }
189 #endif
190         // initialize client(s)
191         if ( ext_gSettings->mThreadMode == kMode_Client ) {
192             client_init( ext_gSettings );
193         }
194
195 #ifdef HAVE_THREAD
196         // start up the reporter and client(s) or listener
197         {
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;
202
203             // Have the reporter launch the client or listener
204             into->runNow = ext_gSettings;
205             
206             // Start all the threads that are ready to go
207             thread_start( into );
208         }
209 #else
210         // No need to make a reporter thread because we don't have threads
211         thread_start( ext_gSettings );
212 #endif
213     } else {
214         // neither server nor client mode was specified
215         // print usage and exit
216
217 #ifdef WIN32
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[] =
222         {
223             { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
224             { NULL, NULL}
225         };
226
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
233 #endif
234         fprintf( stderr, usage_short, argv[0], argv[0] );
235
236         return 0;
237     }
238
239     // wait for other (client, server) threads to complete
240     thread_joinall();
241     
242     // all done!
243     return 0;
244 } // end main
245
246 /* -------------------------------------------------------------------
247  * Signal handler sets the sInterupted flag, so the object can
248  * respond appropriately.. [static]
249  * ------------------------------------------------------------------- */
250
251 void Sig_Interupt( int inSigno ) {
252 #ifdef HAVE_THREAD
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() ) ) {
259         sig_exit( inSigno );
260     }
261
262     // global variable used by threads to see if they were interrupted
263     sInterupted = 1;
264
265     // with threads, stop waiting for non-terminating threads
266     // (ie Listener Thread)
267     thread_release_nonterm( 1 );
268
269 #else
270     // without threads, just exit quietly, same as sig_exit()
271     sig_exit( inSigno );
272 #endif
273 }
274
275 /* -------------------------------------------------------------------
276  * Any necesary cleanup before Iperf quits. Called at program exit,
277  * either by exit() or terminating main().
278  * ------------------------------------------------------------------- */
279
280 void cleanup( void ) {
281 #ifdef WIN32
282     // Shutdown Winsock
283     WSACleanup();
284 #endif
285     // clean up the list of clients
286     Iperf_destroy ( &clients );
287
288     // shutdown the thread subsystem
289     thread_destroy( );
290 } // end cleanup
291
292 #ifdef WIN32
293 /*--------------------------------------------------------------------
294  * ServiceStart
295  *
296  * each time starting the service, this is the entry point of the service.
297  * Start the service, certainly it is on server-mode
298  * 
299  *-------------------------------------------------------------------- */
300 VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
301     
302     // report the status to the service control manager.
303     //
304     if ( !ReportStatusToSCMgr(
305                              SERVICE_START_PENDING, // service state
306                              NO_ERROR,              // exit code
307                              3000) )                 // wait hint
308         goto clean;
309
310     thread_Settings* ext_gSettings = new thread_Settings;
311
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 );
318
319     // report the status to the service control manager.
320     //
321     if ( !ReportStatusToSCMgr(
322                              SERVICE_START_PENDING, // service state
323                              NO_ERROR,              // exit code
324                              3000) )                 // wait hint
325         goto clean;
326
327     // if needed, redirect the output into a specified file
328     if ( !isSTDOUT( ext_gSettings ) ) {
329         redirect( ext_gSettings->mOutputFileName );
330     }
331
332     // report the status to the service control manager.
333     //
334     if ( !ReportStatusToSCMgr(
335                              SERVICE_START_PENDING, // service state
336                              NO_ERROR,              // exit code
337                              3000) )                 // wait hint
338         goto clean;
339     
340     // initialize client(s)
341     if ( ext_gSettings->mThreadMode == kMode_Client ) {
342         client_init( ext_gSettings );
343     }
344
345     // start up the reporter and client(s) or listener
346     {
347         thread_Settings *into = NULL;
348 #ifdef HAVE_THREAD
349         Settings_Copy( ext_gSettings, &into );
350         into->mThreadMode = kMode_Reporter;
351         into->runNow = ext_gSettings;
352 #else
353         into = ext_gSettings;
354 #endif
355         thread_start( into );
356     }
357     
358     // report the status to the service control manager.
359     //
360     if ( !ReportStatusToSCMgr(
361                              SERVICE_RUNNING,       // service state
362                              NO_ERROR,              // exit code
363                              0) )                    // wait hint
364         goto clean;
365
366     clean:
367     // wait for other (client, server) threads to complete
368     thread_joinall();
369 }
370
371
372 //
373 //  FUNCTION: ServiceStop
374 //
375 //  PURPOSE: Stops the service
376 //
377 //  PARAMETERS:
378 //    none
379 //
380 //  RETURN VALUE:
381 //    none
382 //
383 //  COMMENTS:
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.
390 //    
391 VOID ServiceStop() {
392 #ifdef HAVE_THREAD
393     Sig_Interupt( 1 );
394 #else
395     sig_exit(1);
396 #endif
397 }
398
399 #endif
400
401
402
403
404