forked from KunYi/external_iperf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
403 lines (347 loc) · 13.4 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*---------------------------------------------------------------
* 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;
}
// 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 );
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
#ifdef WIN32
/*--------------------------------------------------------------------
* ServiceStart
*
* each time starting the service, this is the entry point of the service.
* Start the service, certainly it is on server-mode
*
*-------------------------------------------------------------------- */
VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
// report the status to the service control manager.
//
if ( !ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000) ) // wait hint
goto clean;
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( dwArgc, lpszArgv, ext_gSettings );
// report the status to the service control manager.
//
if ( !ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000) ) // wait hint
goto clean;
// if needed, redirect the output into a specified file
if ( !isSTDOUT( ext_gSettings ) ) {
redirect( ext_gSettings->mOutputFileName );
}
// report the status to the service control manager.
//
if ( !ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000) ) // wait hint
goto clean;
// initialize client(s)
if ( ext_gSettings->mThreadMode == kMode_Client ) {
client_init( ext_gSettings );
}
// start up the reporter and client(s) or listener
{
thread_Settings *into = NULL;
#ifdef HAVE_THREAD
Settings_Copy( ext_gSettings, &into );
into->mThreadMode = kMode_Reporter;
into->runNow = ext_gSettings;
#else
into = ext_gSettings;
#endif
thread_start( into );
}
// report the status to the service control manager.
//
if ( !ReportStatusToSCMgr(
SERVICE_RUNNING, // service state
NO_ERROR, // exit code
0) ) // wait hint
goto clean;
clean:
// wait for other (client, server) threads to complete
thread_joinall();
}
//
// FUNCTION: ServiceStop
//
// PURPOSE: Stops the service
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
// If a ServiceStop procedure is going to
// take longer than 3 seconds to execute,
// it should spawn a thread to execute the
// stop code, and return. Otherwise, the
// ServiceControlManager will believe that
// the service has stopped responding.
//
VOID ServiceStop() {
#ifdef HAVE_THREAD
Sig_Interupt( 1 );
#else
sig_exit(1);
#endif
}
#endif