]> sjero.net Git - iperf/blob - src/main.cpp
Original 2.0.2 iperf sources
[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 }
100
101 // global variables only accessed within this file
102
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
106 nthread_t sThread;
107 // The main thread uses this function to wait 
108 // for all other threads to complete
109 void waitUntilQuit( void );
110
111 /* -------------------------------------------------------------------
112  * main()
113  *      Entry point into Iperf
114  *
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 ) {
122
123     // Set SIGTERM and SIGINT to call our user interrupt function
124     my_signal( SIGTERM, Sig_Interupt );
125     my_signal( SIGINT,  Sig_Interupt );
126
127 #ifndef WIN32
128     // Ignore broken pipes
129     signal(SIGPIPE,SIG_IGN);
130 #else
131     // Start winsock
132     WSADATA wsaData;
133     int rc = WSAStartup( 0x202, &wsaData );
134     WARN_errno( rc == SOCKET_ERROR, "WSAStartup" );
135         if (rc == SOCKET_ERROR)
136                 return 0;
137
138     // Tell windows we want to handle our own signals
139     SetConsoleCtrlHandler( sig_dispatcher, true );
140 #endif
141
142     // Initialize global mutexes and conditions
143     Condition_Initialize ( &ReportCond );
144     Mutex_Initialize( &groupCond );
145     Mutex_Initialize( &clients_mutex );
146
147     // Initialize the thread subsystem
148     thread_init( );
149
150     // Initialize the interrupt handling thread to 0
151     sThread = thread_zeroid();
152
153     // perform any cleanup when quitting Iperf
154     atexit( cleanup );
155
156     // Allocate the "global" settings
157     thread_Settings* ext_gSettings = new thread_Settings;
158
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 );
165
166     // Check for either having specified client or server
167     if ( ext_gSettings->mThreadMode == kMode_Client 
168          || ext_gSettings->mThreadMode == kMode_Listener ) {
169 #ifdef WIN32
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);
175             return 0;
176         }
177
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");
183
184                 return 0;
185             }
186         }
187 #endif
188         // initialize client(s)
189         if ( ext_gSettings->mThreadMode == kMode_Client ) {
190             client_init( ext_gSettings );
191         }
192
193 #ifdef HAVE_THREAD
194         // start up the reporter and client(s) or listener
195         {
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;
200
201             // Have the reporter launch the client or listener
202             into->runNow = ext_gSettings;
203             
204             // Start all the threads that are ready to go
205             thread_start( into );
206         }
207 #else
208         // No need to make a reporter thread because we don't have threads
209         thread_start( ext_gSettings );
210 #endif
211     } else {
212         // neither server nor client mode was specified
213         // print usage and exit
214
215 #ifdef WIN32
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[] =
220         {
221             { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
222             { NULL, NULL}
223         };
224
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
231 #endif
232         fprintf( stderr, usage_short, argv[0], argv[0] );
233
234         return 0;
235     }
236
237     // wait for other (client, server) threads to complete
238     thread_joinall();
239     
240     // all done!
241     return 0;
242 } // end main
243
244 /* -------------------------------------------------------------------
245  * Signal handler sets the sInterupted flag, so the object can
246  * respond appropriately.. [static]
247  * ------------------------------------------------------------------- */
248
249 void Sig_Interupt( int inSigno ) {
250 #ifdef HAVE_THREAD
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() ) ) {
257         sig_exit( inSigno );
258     }
259
260     // global variable used by threads to see if they were interrupted
261     sInterupted = 1;
262
263     // with threads, stop waiting for non-terminating threads
264     // (ie Listener Thread)
265     thread_release_nonterm( 1 );
266
267 #else
268     // without threads, just exit quietly, same as sig_exit()
269     sig_exit( inSigno );
270 #endif
271 }
272
273 /* -------------------------------------------------------------------
274  * Any necesary cleanup before Iperf quits. Called at program exit,
275  * either by exit() or terminating main().
276  * ------------------------------------------------------------------- */
277
278 void cleanup( void ) {
279 #ifdef WIN32
280     // Shutdown Winsock
281     WSACleanup();
282 #endif
283     // clean up the list of clients
284     Iperf_destroy ( &clients );
285
286     // shutdown the thread subsystem
287     thread_destroy( );
288 } // end cleanup
289
290 #ifdef WIN32
291 /*--------------------------------------------------------------------
292  * ServiceStart
293  *
294  * each time starting the service, this is the entry point of the service.
295  * Start the service, certainly it is on server-mode
296  * 
297  *-------------------------------------------------------------------- */
298 VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
299     
300     // report the status to the service control manager.
301     //
302     if ( !ReportStatusToSCMgr(
303                              SERVICE_START_PENDING, // service state
304                              NO_ERROR,              // exit code
305                              3000) )                 // wait hint
306         goto clean;
307
308     thread_Settings* ext_gSettings = new thread_Settings;
309
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 );
316
317     // report the status to the service control manager.
318     //
319     if ( !ReportStatusToSCMgr(
320                              SERVICE_START_PENDING, // service state
321                              NO_ERROR,              // exit code
322                              3000) )                 // wait hint
323         goto clean;
324
325     // if needed, redirect the output into a specified file
326     if ( !isSTDOUT( ext_gSettings ) ) {
327         redirect( ext_gSettings->mOutputFileName );
328     }
329
330     // report the status to the service control manager.
331     //
332     if ( !ReportStatusToSCMgr(
333                              SERVICE_START_PENDING, // service state
334                              NO_ERROR,              // exit code
335                              3000) )                 // wait hint
336         goto clean;
337     
338     // initialize client(s)
339     if ( ext_gSettings->mThreadMode == kMode_Client ) {
340         client_init( ext_gSettings );
341     }
342
343     // start up the reporter and client(s) or listener
344     {
345         thread_Settings *into = NULL;
346 #ifdef HAVE_THREAD
347         Settings_Copy( ext_gSettings, &into );
348         into->mThreadMode = kMode_Reporter;
349         into->runNow = ext_gSettings;
350 #else
351         into = ext_gSettings;
352 #endif
353         thread_start( into );
354     }
355     
356     // report the status to the service control manager.
357     //
358     if ( !ReportStatusToSCMgr(
359                              SERVICE_RUNNING,       // service state
360                              NO_ERROR,              // exit code
361                              0) )                    // wait hint
362         goto clean;
363
364     clean:
365     // wait for other (client, server) threads to complete
366     thread_joinall();
367 }
368
369
370 //
371 //  FUNCTION: ServiceStop
372 //
373 //  PURPOSE: Stops the service
374 //
375 //  PARAMETERS:
376 //    none
377 //
378 //  RETURN VALUE:
379 //    none
380 //
381 //  COMMENTS:
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.
388 //    
389 VOID ServiceStop() {
390 #ifdef HAVE_THREAD
391     Sig_Interupt( 1 );
392 #else
393     sig_exit(1);
394 #endif
395 }
396
397 #endif
398
399
400
401
402