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