]> sjero.net Git - iperf/blob - src/Listener.cpp
DCCP support for iperf
[iperf] / src / Listener.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  *
47  * Listener.cpp
48  * by Mark Gates <mgates@nlanr.net> 
49  * &  Ajay Tirumala <tirumala@ncsa.uiuc.edu> 
50  * ------------------------------------------------------------------- 
51  * Listener sets up a socket listening on the server host. For each 
52  * connected socket that accept() returns, this creates a Server 
53  * socket and spawns a thread for it. 
54  * 
55  * Changes to the latest version. Listener will run as a daemon 
56  * Multicast Server is now Multi-threaded 
57  * ------------------------------------------------------------------- 
58  * headers 
59  * uses 
60  *   <stdlib.h> 
61  *   <stdio.h> 
62  *   <string.h> 
63  *   <errno.h> 
64  * 
65  *   <sys/types.h> 
66  *   <unistd.h> 
67  * 
68  *   <netdb.h> 
69  *   <netinet/in.h> 
70  *   <sys/socket.h> 
71  * ------------------------------------------------------------------- */ 
72
73
74 #define HEADERS() 
75
76 #include "headers.h" 
77 #include "Listener.hpp"
78 #include "SocketAddr.h"
79 #include "PerfSocket.hpp"
80 #include "List.h"
81 #include "util.h" 
82
83 /* ------------------------------------------------------------------- 
84  * Stores local hostname and socket info. 
85  * ------------------------------------------------------------------- */ 
86
87 Listener::Listener( thread_Settings *inSettings ) {
88
89     mClients = inSettings->mThreads;
90     mBuf = NULL;
91     mSettings = inSettings;
92
93     // initialize buffer
94     mBuf = new char[ mSettings->mBufLen ];
95
96     // open listening socket 
97     Listen( ); 
98     ReportSettings( inSettings );
99
100 } // end Listener 
101
102 /* ------------------------------------------------------------------- 
103  * Delete memory (buffer). 
104  * ------------------------------------------------------------------- */ 
105 Listener::~Listener() {
106     if ( mSettings->mSock != INVALID_SOCKET ) {
107         int rc = close( mSettings->mSock );
108         WARN_errno( rc == SOCKET_ERROR, "close" );
109         mSettings->mSock = INVALID_SOCKET;
110     }
111     DELETE_ARRAY( mBuf );
112 } // end ~Listener 
113
114 /* ------------------------------------------------------------------- 
115  * Listens for connections and starts Servers to handle data. 
116  * For TCP, each accepted connection spawns a Server thread. 
117  * For datagram-oriented protocols, spawn a new Server thread.
118  * ------------------------------------------------------------------- */ 
119 void Listener::Run(void)
120 {
121 #ifdef sun
122     if ( ( isPacketOriented( mSettings ) &&
123            isMulticast( mSettings )      &&
124           !isSingleUDP( mSettings )    ) || isSingleUDP( mSettings ) ) {
125         UDPSingleServer();
126 #else
127     if ( isSingleUDP( mSettings ) ) {
128         UDPSingleServer();
129 #endif
130     } else {
131         bool client = false,
132              mCount = (mSettings->mThreads != 0);
133         thread_Settings *tempSettings = NULL;
134         Iperf_ListEntry *exist, *listtemp;
135         client_hdr* hdr;
136
137         
138         if ( mSettings->mHost != NULL ) {
139             client = true;
140             SockAddr_remoteAddr( mSettings );
141         }
142         Settings_Copy( mSettings, &server );
143         server->mThreadMode = kMode_Server;
144     
145     
146         // Accept each packet, 
147         // If there is no existing client, then start  
148         // a new thread to service the new client 
149         // The listener runs in a single thread 
150         // Thread per client model is followed 
151         do {
152             // Get a new socket
153             Accept( server );
154             if ( server->mSock == INVALID_SOCKET ) {
155                 break;
156             }
157             if ( sInterupted != 0 ) {
158                 close( server->mSock );
159                 break;
160             }
161             // Reset Single Client Stuff
162             if ( isSingleClient( mSettings ) && clients == NULL ) {
163                 mSettings->peer = server->peer;
164                 mClients--;
165                 client = true;
166                 // Once all the server threads exit then quit
167                 // Must keep going in case this client sends
168                 // more streams
169                 if ( mClients == 0 ) {
170                     thread_release_nonterm( 0 );
171                     mClients = 1;
172                 }
173             }
174             // Verify that it is allowed
175             if ( client ) {
176                 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer, 
177                                               (sockaddr*) &server->peer ) ) {
178                     // Not allowed try again
179                     close( server->mSock );
180                     if ( isConnectionLess( mSettings ) ) {
181                         mSettings->mSock = -1;
182                         Listen();
183                     }
184                     continue;
185                 }
186             }
187     
188             // Create an entry for the connection list
189             listtemp = new Iperf_ListEntry;
190             memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
191             listtemp->next = NULL;
192     
193             // See if we need to do summing
194             Mutex_Lock( &clients_mutex );
195             exist = Iperf_hostpresent( &server->peer, clients); 
196     
197             if ( exist != NULL ) {
198                 // Copy group ID
199                 listtemp->holder = exist->holder;
200                 server->multihdr = exist->holder;
201             } else {
202                 server->mThreads = 0;
203                 Mutex_Lock( &groupCond );
204                 groupID--;
205                 listtemp->holder = InitMulti( server, groupID );
206                 server->multihdr = listtemp->holder;
207                 Mutex_Unlock( &groupCond );
208             }
209     
210             // Store entry in connection list
211             Iperf_pushback( listtemp, &clients ); 
212             Mutex_Unlock( &clients_mutex ); 
213     
214             tempSettings = NULL;
215             if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
216                 if ( !isConnectionLess(mSettings)) {
217                     hdr = (client_hdr*) mBuf;
218
219                     // TCP/DCCP does not have the info yet
220                     if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
221                         Settings_GenerateClientSettings( server, &tempSettings, hdr );
222                     }
223                 } else {
224                     hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
225                     Settings_GenerateClientSettings( server, &tempSettings, hdr );
226                 }
227             }
228     
229             if ( tempSettings != NULL ) {
230                 client_init( tempSettings );
231                 if ( tempSettings->mMode == kTest_DualTest ) {
232 #ifdef HAVE_THREAD
233                     server->runNow =  tempSettings;
234 #else
235                     server->runNext = tempSettings;
236 #endif
237                 } else {
238                     server->runNext =  tempSettings;
239                 }
240             }
241     
242             // Start the server
243             thread_start( server );
244     
245             // create a new socket
246             if ( isConnectionLess( mSettings ) ) {
247                 mSettings->mSock = -1; 
248                 Listen( );
249             }
250     
251             // Prep for next connection
252             if ( !isSingleClient( mSettings ) ) {
253                 mClients--;
254             }
255             Settings_Copy( mSettings, &server );
256             server->mThreadMode = kMode_Server;
257         } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
258     
259         Settings_Destroy( server );
260     }
261 } // end Run 
262
263 /* -------------------------------------------------------------------
264  * Setup a socket listening on a port.
265  * For connection-oriented protocols, this calls bind() and listen().
266  * For connection-less protocols, this just calls bind().
267  * If inLocalhost is not null, bind to that address rather than the
268  * wildcard server address, specifying what incoming interface to
269  * accept connections on.
270  * ------------------------------------------------------------------- */
271 void Listener::Listen()
272 {
273     int rc;
274     int boolean = 1;
275     Socklen_t len = sizeof(boolean);
276
277     SockAddr_localAddr( mSettings );
278     MakeSocket( mSettings );
279
280     SetSocketOptions( mSettings );
281
282     // reuse the address, so we can run if a former server was killed off
283     setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
284
285     // listen for connections (TCP/DCCP only).
286     rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
287     WARN_errno( rc == SOCKET_ERROR, "bind" );
288
289     // default backlog traditionally 5
290     if ( !isConnectionLess( mSettings ) ) {
291         rc = listen( mSettings->mSock, 5 );
292         WARN_errno( rc == SOCKET_ERROR, "listen" );
293     }
294
295     // if multicast, join the group
296     if ( SockAddr_isMulticast( &mSettings->local ) ) {
297         McastJoin( );
298     }
299 } // end Listen
300
301 /* -------------------------------------------------------------------
302  * Joins the multicast group, with the default interface.
303  * ------------------------------------------------------------------- */
304
305 void Listener::McastJoin( ) {
306 #ifdef HAVE_MULTICAST
307     if ( !SockAddr_isIPv6( &mSettings->local ) ) {
308         struct ip_mreq mreq;
309
310         memcpy( &mreq.imr_multiaddr, SockAddr_get_in_addr( &mSettings->local ), 
311                 sizeof(mreq.imr_multiaddr));
312
313         mreq.imr_interface.s_addr = htonl( INADDR_ANY );
314
315         int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
316                              (char*) &mreq, sizeof(mreq));
317         WARN_errno( rc == SOCKET_ERROR, "multicast join" );
318     }
319 #ifdef HAVE_IPV6_MULTICAST
320       else {
321         struct ipv6_mreq mreq;
322
323         memcpy( &mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr( &mSettings->local ), 
324                 sizeof(mreq.ipv6mr_multiaddr));
325
326         mreq.ipv6mr_interface = 0;
327
328         int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
329                              (char*) &mreq, sizeof(mreq));
330         WARN_errno( rc == SOCKET_ERROR, "multicast join" );
331     }
332 #endif
333 #endif
334 }
335 // end McastJoin
336
337 /* -------------------------------------------------------------------
338  * Sets the Multicast TTL for outgoing packets.
339  * ------------------------------------------------------------------- */
340
341 void Listener::McastSetTTL( int val ) {
342 #ifdef HAVE_MULTICAST
343     if ( !SockAddr_isIPv6( &mSettings->local ) ) {
344         int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
345                              (char*) &val, sizeof(val));
346         WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
347     }
348 #ifdef HAVE_IPV6_MULTICAST
349       else {
350         int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
351                              (char*) &val, sizeof(val));
352         WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
353     }
354 #endif
355 #endif
356 }
357 // end McastSetTTL
358
359 /* -------------------------------------------------------------------
360  * After Listen() has setup mSock, this will block
361  * until a new connection arrives.
362  * ------------------------------------------------------------------- */
363
364 void Listener::Accept( thread_Settings *server )
365 {
366     server->size_peer = sizeof(iperf_sockaddr); 
367
368     if ( isConnectionLess( server ) ) {
369         /* ------------------------------------------------------------------- 
370          * Do the equivalent of an accept() call for connection-less sockets.
371          * This waits on a listening datagram socket until we get a datagram.
372          * ------------------------------------------------------------------- */
373         int rc;
374         Iperf_ListEntry *exist;
375         int32_t datagramID;
376
377         server->mSock = INVALID_SOCKET;
378         while ( server->mSock == INVALID_SOCKET ) {
379             rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, 
380                            (struct sockaddr*) &server->peer, &server->size_peer );
381             FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
382
383             Mutex_Lock( &clients_mutex );
384     
385             // Handle connection for datagram-based sockets.
386             exist = Iperf_present( &server->peer, clients);
387             datagramID = ntohl( ((dgram_record*) mBuf)->id );
388             if ( exist == NULL && datagramID >= 0 ) {
389                 server->mSock = mSettings->mSock;
390                 int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
391                                   server->size_peer );
392                 FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
393             } else {
394                 server->mSock = INVALID_SOCKET;
395             }
396             Mutex_Unlock( &clients_mutex );
397         }
398     } else {
399         // Handles interupted accepts. Returns the newly connected socket.
400         server->mSock = INVALID_SOCKET;
401     
402         while ( server->mSock == INVALID_SOCKET ) {
403             // accept a connection
404             server->mSock = accept( mSettings->mSock, 
405                                     (sockaddr*) &server->peer, &server->size_peer );
406             if ( server->mSock == INVALID_SOCKET &&  errno == EINTR ) {
407                 continue;
408             }
409         }
410     }
411     server->size_local = sizeof(iperf_sockaddr); 
412     getsockname( server->mSock, (sockaddr*) &server->local, 
413                  &server->size_local );
414 } // end Accept
415
416 void Listener::UDPSingleServer( ) {
417     
418     bool client = false, mCount = (mSettings->mThreads != 0);
419     thread_Settings *tempSettings = NULL;
420     Iperf_ListEntry *exist, *listtemp;
421     int rc;
422     int32_t datagramID;
423     ReportStruct *reportstruct = new ReportStruct;
424     dgram_record *dgram_hdr = (dgram_record *)mBuf;
425     
426
427     assert( isPacketOriented( mSettings ) );
428
429     if ( mSettings->mHost != NULL ) {
430         client = true;
431         SockAddr_remoteAddr( mSettings );
432     }
433     Settings_Copy( mSettings, &server );
434     server->mThreadMode = kMode_Server;
435
436
437     // Accept each packet, 
438     // If there is no existing client, then start  
439     // a new report to service the new client 
440     // The listener runs in a single thread 
441     Mutex_Lock( &clients_mutex );
442     do {
443         // Get next packet
444         while ( sInterupted == 0) {
445             server->size_peer = sizeof( iperf_sockaddr );
446             rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, 
447                            (struct sockaddr*) &server->peer, &server->size_peer );
448             WARN_errno( rc == SOCKET_ERROR, "recvfrom" );
449             if ( rc == SOCKET_ERROR ) {
450                 return;
451             }
452         
453         
454             // Handle connection for datagram-based sockets.
455             exist = Iperf_present( &server->peer, clients);
456             datagramID = ntohl( dgram_hdr->id );
457             if ( datagramID >= 0 ) {
458                 if ( exist != NULL ) {
459                     // read the datagram ID and sentTime out of the buffer 
460                     reportstruct->packetID = datagramID; 
461                     reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
462                     reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
463         
464                     reportstruct->packetLen = rc;
465                     gettimeofday( &(reportstruct->packetTime), NULL );
466         
467                     ReportPacket( exist->server->reporthdr, reportstruct );
468                 } else {
469                     Mutex_Lock( &groupCond );
470                     groupID--;
471                     server->mSock = -groupID;
472                     Mutex_Unlock( &groupCond );
473                     server->size_local = sizeof(iperf_sockaddr); 
474                     getsockname( mSettings->mSock, (sockaddr*) &server->local, 
475                                  &server->size_local );
476                     break;
477                 }
478             } else {
479                 if ( exist != NULL ) {
480                     // read the datagram ID and sentTime out of the buffer 
481                     reportstruct->packetID = -datagramID; 
482                     reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
483                     reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
484         
485                     reportstruct->packetLen = rc;
486                     gettimeofday( &(reportstruct->packetTime), NULL );
487         
488                     ReportPacket( exist->server->reporthdr, reportstruct );
489                     // stop timing 
490                     gettimeofday( &(reportstruct->packetTime), NULL );
491                     CloseReport( exist->server->reporthdr, reportstruct );
492         
493                     if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
494                         server_hdr *hdr = (server_hdr *)(dgram_hdr + 1);
495                         Transfer_Info *stats = GetReport( exist->server->reporthdr );
496         
497                         hdr->flags        = htonl( HEADER_VERSION1 );
498                         hdr->total_len1   = htonl( (long) (stats->TotalLen >> 32) );
499                         hdr->total_len2   = htonl( (long) (stats->TotalLen & 0xFFFFFFFF) );
500                         hdr->stop_sec     = htonl( (long) stats->endTime );
501                         hdr->stop_usec    = htonl( (long)((stats->endTime - (long)stats->endTime)
502                                                           * rMillion));
503                         hdr->error_cnt    = htonl( stats->cntError );
504                         hdr->outorder_cnt = htonl( stats->cntOutofOrder );
505                         hdr->datagrams    = htonl( stats->cntDatagrams );
506                         hdr->jitter1      = htonl( (long) stats->jitter );
507                         hdr->jitter2      = htonl( (long) ((stats->jitter - (long)stats->jitter) 
508                                                            * rMillion) );
509         
510                     }
511                     EndReport( exist->server->reporthdr );
512                     exist->server->reporthdr = NULL;
513                     Iperf_delete( &(exist->server->peer), &clients );
514
515                 } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
516                     server_hdr *hdr = (server_hdr *) (dgram_hdr + 1);
517                     hdr->flags = htonl( 0 );
518                 }
519                 sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
520                         (struct sockaddr*) &server->peer, server->size_peer);
521             }
522         }
523         if ( server->mSock == INVALID_SOCKET ) {
524             break;
525         }
526         if ( sInterupted != 0 ) {
527             close( server->mSock );
528             break;
529         }
530         // Reset Single Client Stuff
531         if ( isSingleClient( mSettings ) && clients == NULL ) {
532             mSettings->peer = server->peer;
533             mClients--;
534             client = true;
535             // Once all the server threads exit then quit
536             // Must keep going in case this client sends
537             // more streams
538             if ( mClients == 0 ) {
539                 thread_release_nonterm( 0 );
540                 mClients = 1;
541             }
542         }
543         // Verify that it is allowed
544         if ( client ) {
545             if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer, 
546                                           (sockaddr*) &server->peer ) ) {
547                 // Not allowed try again
548                 connect( mSettings->mSock, 
549                          (sockaddr*) &server->peer, 
550                          server->size_peer );
551                 close( mSettings->mSock );
552                 mSettings->mSock = -1; 
553                 Listen( );
554                 continue;
555             }
556         }
557
558         // Create an entry for the connection list
559         listtemp = new Iperf_ListEntry;
560         memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
561         listtemp->server = server;
562         listtemp->next = NULL;
563
564         // See if we need to do summing
565         exist = Iperf_hostpresent( &server->peer, clients); 
566
567         if ( exist != NULL ) {
568             // Copy group ID
569             listtemp->holder = exist->holder;
570             server->multihdr = exist->holder;
571         } else {
572             server->mThreads = 0;
573             Mutex_Lock( &groupCond );
574             groupID--;
575             listtemp->holder = InitMulti( server, groupID );
576             server->multihdr = listtemp->holder;
577             Mutex_Unlock( &groupCond );
578         }
579
580         // Store entry in connection list
581         Iperf_pushback( listtemp, &clients ); 
582
583         tempSettings = NULL;
584         if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
585             client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
586
587             Settings_GenerateClientSettings(server, &tempSettings, hdr);
588         }
589
590
591         if ( tempSettings != NULL ) {
592             client_init( tempSettings );
593             if ( tempSettings->mMode == kTest_DualTest ) {
594 #ifdef HAVE_THREAD
595                 thread_start( tempSettings );
596 #else
597                 server->runNext = tempSettings;
598 #endif
599             } else {
600                 server->runNext =  tempSettings;
601             }
602         }
603         server->reporthdr = InitReport( server );
604
605         // Prep for next connection
606         if ( !isSingleClient( mSettings ) ) {
607             mClients--;
608         }
609         Settings_Copy( mSettings, &server );
610         server->mThreadMode = kMode_Server;
611     } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
612     Mutex_Unlock( &clients_mutex );
613
614     Settings_Destroy( server );
615 }
616
617 /* -------------------------------------------------------------------- 
618  * Run the server as a daemon  
619  * --------------------------------------------------------------------*/ 
620
621 void Listener::runAsDaemon(const char *pname, int facility) {
622     pid_t pid; 
623
624     /* Create a child process & if successful, exit from the parent process */ 
625     if ( (pid = fork()) == -1 ) {
626         fprintf( stderr, "error in first child create\n");     
627         exit(0); 
628     } else if ( pid != 0 ) {
629         exit(0); 
630     }
631
632     /* Try becoming the session leader, once the parent exits */
633     if ( setsid() == -1 ) {           /* Become the session leader */ 
634         fprintf( stderr, "Cannot change the session group leader\n"); 
635     } else {
636     } 
637     signal(SIGHUP,SIG_IGN); 
638
639
640     /* Now fork() and get released from the terminal */  
641     if ( (pid = fork()) == -1 ) {
642         fprintf( stderr, "error\n");   
643         exit(0); 
644     } else if ( pid != 0 ) {
645         exit(0); 
646     }
647
648     chdir("."); 
649     fprintf( stderr, "Running Iperf Server as a daemon\n"); 
650     fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid())); 
651     fflush(stderr); 
652
653     fclose(stdin); 
654 }
655