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 #include "Settings.hpp"
65 #include "PerfSocket.hpp"
66 #include "Locale.h"
67 #include "Condition.h"
68 #include "Timestamp.hpp"
69 #include "Listener.hpp"
70 #include "active_hosts.h"
71 #include "util.h"
72 #include "Reporter.h"
73 #include "payloads.h"
74
75 #ifdef WIN32
76 #include "service.h"
77 #endif
78
79 /* -------------------------------------------------------------------
80 * prototypes
81 * ------------------------------------------------------------------- */
82 // Function called at exit to clean up as much as possible
83 void cleanup();
84 // signal handlers
85 static void Sig_Interupt(int inSigno);
86
87 /* -------------------------------------------------------------------
88 * global variables
89 * ------------------------------------------------------------------- */
90 extern "C" {
91 // Global flag to signal a user interrupt
92 int sInterupted = 0;
93 // Global ID that we increment to be used
94 // as identifier for SUM reports
95 int groupID = 0;
96 // Mutex to protect access to the above ID
97 Mutex transferid_mutex;
98 // Condition used to signal the reporter thread
99 // when a packet ring is full. Shouldn't really
100 // be needed but is "belts and suspeners"
101 struct Condition ReportCond;
102 // Initialize reporter thread mutex
103 struct AwaitMutex reporter_state;
104 struct AwaitMutex threads_start;
105 struct BarrierMutex transmits_start;
106 }
107
108
109 // global variables only accessed within this file
110
111 // Thread that received the SIGTERM or SIGINT signal
112 // Used to ensure that if multiple threads receive the
113 // signal we do not prematurely exit
114 nthread_t sThread;
115 static thread_Settings* ext_gSettings;
116 // The main thread uses this function to wait
117 // for all other threads to complete
118 void waitUntilQuit();
119
120 /* -------------------------------------------------------------------
121 * main()
122 * Entry point into Iperf
123 *
124 * sets up signal handlers
125 * initialize global locks and conditions
126 * parses settings from environment and command line
127 * starts up server or client thread
128 * waits for all threads to complete
129 * ------------------------------------------------------------------- */
main(int argc,char ** argv)130 int main(int argc, char **argv) {
131
132 // Set SIGTERM and SIGINT to call our user interrupt function
133 my_signal(SIGTERM, Sig_Interupt);
134 my_signal(SIGINT, Sig_Interupt);
135 #ifndef WIN32
136 my_signal(SIGALRM, Sig_Interupt);
137 // Ignore broken pipes
138 signal(SIGPIPE,SIG_IGN);
139 #else
140 // Start winsock
141 WSADATA wsaData;
142 int rc = WSAStartup(0x202, &wsaData);
143 WARN_errno(rc == SOCKET_ERROR, "WSAStartup");
144 if (rc == SOCKET_ERROR)
145 return 0;
146 // Tell windows we want to handle our own signals
147 SetConsoleCtrlHandler(sig_dispatcher, true);
148 #endif
149
150 // Initialize global mutexes and conditions
151 Iperf_initialize_active_table();
152 Condition_Initialize (&ReportCond);
153
154 #ifdef HAVE_THREAD_DEBUG
155 Mutex_Initialize(&packetringdebug_mutex);
156 Mutex_Initialize(&thread_debug_mutex);
157 #endif
158 Mutex_Initialize(&transferid_mutex);
159
160 // Initialize reporter thread mutex
161 reporter_state.ready = 0;
162 threads_start.ready = 0;
163 transmits_start.count = 0;
164 Condition_Initialize(&reporter_state.await);
165 Condition_Initialize(&threads_start.await);
166 Condition_Initialize(&transmits_start.await);
167
168 // Initialize the thread subsystem
169 thread_init();
170
171 // Initialize the interrupt handling thread to 0
172 sThread = thread_zeroid();
173
174 // perform any cleanup when quitting Iperf
175 atexit(cleanup);
176
177 // Allocate the "global" settings
178 ext_gSettings = new thread_Settings;
179 // Default reporting mode here to avoid unitialized warnings
180 // this won't be the actual mode
181 ThreadMode ReporterThreadMode = kMode_Reporter;
182 // Initialize settings to defaults
183 Settings_Initialize(ext_gSettings);
184 // read settings from environment variables
185 Settings_ParseEnvironment(ext_gSettings);
186 // read settings from command-line parameters
187 Settings_ParseCommandLine(argc, argv, ext_gSettings);
188
189 // Check for either having specified client or server
190 if ((ext_gSettings->mThreadMode != kMode_Client) && (ext_gSettings->mThreadMode != kMode_Listener)) {
191 // neither server nor client mode was specified
192 // print usage and exit
193
194 #ifdef WIN32
195 // In Win32 we also attempt to start a previously defined service
196 // Starting in 2.0 to restart a previously defined service
197 // you must call iperf with "iperf -D" or using the environment variable
198 SERVICE_TABLE_ENTRY dispatchTable[] =
199 {
200 { (LPSTR)TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
201 { NULL, NULL}
202 };
203
204 // starting the service by SCM, there is no arguments will be passed in.
205 // the arguments will pass into Service_Main entry.
206 if (!StartServiceCtrlDispatcher(dispatchTable))
207 // If the service failed to start then print usage
208 #endif
209 fprintf(stderr, usage_short, argv[0], argv[0]);
210 return 0;
211 }
212
213 if (!isSTDOUT(ext_gSettings)) {
214 #ifdef HAVE_FREOPEN
215 FILE *fd;
216 fprintf(stdout, "Output from stdout and stderr will be redirected to file %s\n", ext_gSettings->mOutputFileName);
217 fflush(stdout);
218 fd = freopen(ext_gSettings->mOutputFileName, "w", stdout);
219 FAIL_errno(fd == NULL, "freopen stdout\n", ext_gSettings);
220 fd = freopen(ext_gSettings->mOutputFileName, "w", stderr);
221 FAIL_errno(fd == NULL, "freopen stderr\n", ext_gSettings);
222 #else
223 fprintf(stderr, "Output to file not supported\n");
224 #endif
225
226 }
227
228 int mbuflen = (ext_gSettings->mBufLen > MINMBUFALLOCSIZE) ? ext_gSettings->mBufLen : MINMBUFALLOCSIZE;
229 #if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
230 mbuflen += TAPBYTESSLOP;
231 #endif
232 ext_gSettings->mBuf = new char[mbuflen];
233
234 unsetReport(ext_gSettings);
235 switch (ext_gSettings->mThreadMode) {
236 case kMode_Client :
237 if (isDaemon(ext_gSettings)) {
238 fprintf(stderr, "Iperf client cannot be run as a daemon\n");
239 return 0;
240 }
241 // initialize client(s)
242 transmits_start.count = ext_gSettings->mThreads;
243 ext_gSettings->connects_done = &transmits_start;
244 client_init(ext_gSettings);
245 ReporterThreadMode = kMode_ReporterClient;
246 break;
247 case kMode_Listener :
248 #ifdef WIN32
249 // Remove the Windows service if requested
250 if (isRemoveService(ext_gSettings)) {
251 // remove the service
252 if (CmdRemoveService()) {
253 fprintf(stderr, "IPerf Service is removed.\n");
254 }
255 }
256 if (isDaemon(ext_gSettings)) {
257 CmdInstallService(argc, argv);
258 } else if (isRemoveService(ext_gSettings)) {
259 return 0;
260 }
261 #else // *nix system
262 if (isDaemon(ext_gSettings)) {
263 fprintf(stderr, "Running Iperf Server as a daemon\n");
264 // Start the server as a daemon
265 // redirect stdin, stdout and sterr to /dev/null (see dameon and no close flag)
266 if (daemon(1, 0) < 0) {
267 perror("daemon");
268 }
269 }
270 #endif
271 // Start up any parallel listener threads
272 if (ext_gSettings->mPortLast) {
273 listeners_init(ext_gSettings);
274 }
275 break;
276 default :
277 fprintf(stderr, "unknown mode");
278 break;
279 }
280 #ifdef HAVE_THREAD
281 // Last step is to initialize the reporter then start all threads
282 {
283 thread_Settings *into = NULL;
284 // Create the settings structure for the reporter thread
285 Settings_Copy(ext_gSettings, &into, 1);
286 into->mThreadMode = ReporterThreadMode;
287 into->mSumReport = NULL;
288 into->mFullDuplexReport = NULL;
289 // Have the reporter launch the client or listener
290 into->runNow = ext_gSettings;
291 // Start all the threads that are ready to go
292 thread_start_all(into);
293 threads_start.ready = 1;
294 Condition_Signal(&threads_start.await);
295 }
296 #else
297 // No need to make a reporter thread because we don't have threads
298 thread_start(ext_gSettings);
299 #endif
300 // wait for other (client, server) threads to complete
301 thread_joinall();
302 // all done!
303 return 0;
304 } // end main
305
306 /* -------------------------------------------------------------------
307 * Signal handler sets the sInterupted flag, so the object can
308 * respond appropriately.. [static]
309 * ------------------------------------------------------------------- */
310
Sig_Interupt(int inSigno)311 void Sig_Interupt (int inSigno) {
312 #ifdef HAVE_THREAD
313 // We try to not allow a single interrupt handled by multiple threads
314 // to completely kill the app so we save off the first thread ID
315 // then that is the only thread that can supply the next interrupt
316 if ((inSigno == SIGINT) && thread_equalid(sThread, thread_zeroid())) {
317 sThread = thread_getid();
318 } else if (thread_equalid(sThread, thread_getid())) {
319 sig_exit(inSigno);
320 }
321 // global variable used by threads to see if they were interrupted
322 sInterupted = inSigno;
323
324 // Note: ignore alarms per setitimer
325 #if HAVE_DECL_SIGALRM
326 if (inSigno != SIGALRM)
327 #endif
328 // with threads, stop waiting for non-terminating threads
329 // (ie Listener Thread)
330 thread_release_nonterm(inSigno);
331
332 #else
333 // without threads, just exit quietly, same as sig_exit()
334 sig_exit(inSigno);
335 #endif
336 }
337
338 /* -------------------------------------------------------------------
339 * Any necesary cleanup before Iperf quits. Called at program exit,
340 * either by exit() or terminating main().
341 * ------------------------------------------------------------------- */
342
cleanup()343 void cleanup () {
344 #ifdef WIN32
345 // Shutdown Winsock
346 WSACleanup();
347 #endif
348 // clean up the list of active clients
349 Iperf_destroy_active_table();
350 // done actions
351 // Destroy global mutexes and conditions
352
353 Condition_Destroy (&ReportCond);
354 Condition_Destroy(&reporter_state.await);
355 Condition_Destroy(&threads_start.await);
356 Condition_Destroy(&transmits_start.await);
357 #ifdef HAVE_THREAD_DEBUG
358 Mutex_Destroy(&packetringdebug_mutex);
359 Mutex_Destroy(&thread_debug_mutex);
360 #endif
361 Mutex_Destroy(&transferid_mutex);
362 // shutdown the thread subsystem
363 thread_destroy();
364 } // end cleanup
365
366 #ifdef WIN32
367 /*--------------------------------------------------------------------
368 * ServiceStart
369 *
370 * each time starting the service, this is the entry point of the service.
371 * Start the service, certainly it is on server-mode
372 *
373 *-------------------------------------------------------------------- */
ServiceStart(DWORD dwArgc,LPTSTR * lpszArgv)374 VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
375 thread_Settings* ext_gSettings;
376
377 // report the status to the service control manager.
378 //
379 if (!ReportStatusToSCMgr(
380 SERVICE_START_PENDING, // service state
381 NO_ERROR, // exit code
382 3000)) // wait hint
383 goto clean;
384
385 ext_gSettings = new thread_Settings;
386
387 // Initialize settings to defaults
388 Settings_Initialize(ext_gSettings);
389 // read settings from environment variables
390 Settings_ParseEnvironment(ext_gSettings);
391 // read settings from command-line parameters
392 Settings_ParseCommandLine(dwArgc, lpszArgv, ext_gSettings);
393
394 // Arguments will be lost when the service is started by SCM, but
395 // we need to be at least a listener
396 ext_gSettings->mThreadMode = kMode_Listener;
397
398 // report the status to the service control manager.
399 //
400 if (!ReportStatusToSCMgr(
401 SERVICE_START_PENDING, // service state
402 NO_ERROR, // exit code
403 3000)) // wait hint
404 goto clean;
405
406 // if needed, redirect the output into a specified file
407 if (!isSTDOUT(ext_gSettings)) {
408 redirect(ext_gSettings->mOutputFileName);
409 }
410
411 // report the status to the service control manager.
412 //
413 if (!ReportStatusToSCMgr(
414 SERVICE_START_PENDING, // service state
415 NO_ERROR, // exit code
416 3000)) // wait hint
417 goto clean;
418
419 // initialize client(s)
420 if (ext_gSettings->mThreadMode == kMode_Client) {
421 client_init(ext_gSettings);
422 }
423
424 // start up the reporter and client(s) or listener
425 {
426 thread_Settings *into = NULL;
427 #ifdef HAVE_THREAD
428 Settings_Copy(ext_gSettings, &into, 1);
429 into->mThreadMode = kMode_Reporter;
430 into->runNow = ext_gSettings;
431 #else
432 into = ext_gSettings;
433 #endif
434 thread_start_all(into);
435 }
436
437 // report the status to the service control manager.
438 //
439 if (!ReportStatusToSCMgr(
440 SERVICE_RUNNING, // service state
441 NO_ERROR, // exit code
442 0)) // wait hint
443 goto clean;
444
445 clean:
446 // wait for other (client, server) threads to complete
447 thread_joinall();
448 }
449
450 //
451 // FUNCTION: ServiceStop
452 //
453 // PURPOSE: Stops the service
454 //
455 // PARAMETERS:
456 // none
457 //
458 // RETURN VALUE:
459 // none
460 //
461 // COMMENTS:
462 // If a ServiceStop procedure is going to
463 // take longer than 3 seconds to execute,
464 // it should spawn a thread to execute the
465 // stop code, and return. Otherwise, the
466 // ServiceControlManager will believe that
467 // the service has stopped responding.
468 //
ServiceStop()469 VOID ServiceStop() {
470 #ifdef HAVE_THREAD
471 Sig_Interupt(1);
472 #else
473 sig_exit(1);
474 #endif
475 }
476
477 #endif
478