unified_network.cpp

FatDevil, 10/04/2010 12:08 am

Download (79 kB)

 
1
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2
// Copyright (C) 2010  Winch Gate Property Limited
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU Affero General Public License as
6
// published by the Free Software Foundation, either version 3 of the
7
// License, or (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU Affero General Public License for more details.
13
//
14
// You should have received a copy of the GNU Affero General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
#include "stdnet.h"
18

19
#include <limits>
20

21
#include "nel/misc/entity_id.h" // for createMessage()
22
#include "nel/misc/variable.h"
23
#include "nel/misc/thread.h"
24
#include "nel/misc/mutex.h"
25

26
#include "nel/net/unified_network.h"
27
#include "nel/net/module_common.h"
28
#include "nel/net/naming_client.h"
29
#ifdef NL_OS_UNIX
30
#include <sched.h>
31
#endif
32

33
using namespace std;
34
using namespace NLMISC;
35

36

37
namespace NLNET {
38

39
static size_t ThreadCreator = 0;
40

41
static const uint64 AppIdDeadConnection = 0xDEAD;
42

43
uint32 TotalCallbackCalled = 0;
44

45
uint32 TimeInCallback =0;
46

47
#ifdef NL_OS_UNIX
48
/// Yield method (Unix only)
49
CVariable<uint32> UseYieldMethod("nel", "UseYieldMethod", "0=select 1=usleep 2=nanosleep 3=sched_yield 4=none", 0, 0, true );
50
#endif
51

52
/// Reduce sending lag
53
CVariable<bool> FlushSendsBeforeSleep("nel", "FlushSendsBeforeSleep", "If true, send buffers will be flushed before sleep, not in next update", true, 0, true );
54

55
/// Network congestion monitoring
56
CVariable<uint> L5TotalBytesInLowLevelSendQueues("nel", "L5TotalBytesInLowLevelSendQueues", "Number of bytes pending in send queues (postponed by non-blocking send()) for network congestion monitoring. N/A if FlushSendsBeforeSleep disabled)", 0, 0, true );
57

58
/// Receiving size limit
59
CVariablePtr<uint32> DefaultMaxExpectedBlockSize("nel", "DefaultMaxExpectedBlockSize", "If receiving more than this value in bytes, the connection will be dropped", &CBufNetBase::DefaultMaxExpectedBlockSize, true );
60

61
/// Sending size limit
62
CVariablePtr<uint32> DefaultMaxSentBlockSize("nel", "DefaultMaxSentBlockSize", "If sending more than this value in bytes, the program may be stopped", &CBufNetBase::DefaultMaxSentBlockSize, true );
63

64
#define AUTOCHECK_DISPLAY nlwarning
65
//#define AUTOCHECK_DISPLAY CUnifiedNetwork::getInstance()->displayInternalTables (), nlerror
66

67
const TServiceId        TServiceId::InvalidId = TServiceId(std::numeric_limits<uint16>::max());
68

69
//
70
// Callbacks from NAMING SERVICE
71
//
72

73
// when a service registers
74
void        uNetRegistrationBroadcast(const string &name, TServiceId sid, const vector<CInetAddress> &addr)
75
{
76
        nldebug ("HNETL5: + naming %s-%u '%s'", name.c_str(), sid.get(), vectorCInetAddressToString(addr).c_str ());
77

78
        CUnifiedNetwork *uni= CUnifiedNetwork::getInstance();
79

80
        if (uni->_SId == sid)
81
        {
82
                // it's me! don't add me!!!
83
                return;
84
        }
85

86
        // add the unified connection
87

88
        if(sid.get() >= uni->_IdCnx.size ())
89
                uni->_IdCnx.resize (sid.get()+1);
90

91
        if (uni->_IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
92
        {
93
                uni->_IdCnx[sid.get()] = CUnifiedNetwork::CUnifiedConnection(name, sid, false);
94
                uni->_UsedConnection.push_back (sid);
95
        }
96

97
        if (!uni->_IdCnx[sid.get()].ExtAddress.empty ())
98
                AUTOCHECK_DISPLAY ("HNETL5: %s-%u already inserted in the table with '%s'", name.c_str(), sid.get(), vectorCInetAddressToString (uni->_IdCnx[sid.get()].ExtAddress).c_str ());
99

100
        // set the list of external addresses
101

102
        nlassert (!addr.empty());
103

104
        uni->_IdCnx[sid.get()].ExtAddress = addr;
105

106
        // associate nid with ext address
107
        uni->_IdCnx[sid.get()].setupNetworkAssociation (uni->_NetworkAssociations, uni->_DefaultNetwork);
108
}
109

110
// when a service unregisters
111
void        uNetUnregistrationBroadcast(const string &name, TServiceId sid, const vector<CInetAddress> &addr)
112
{
113
        nldebug ("HNETL5: - naming %s-%u '%s'", name.c_str(), sid.get(), vectorCInetAddressToString (addr).c_str ());
114

115
        // get the service connection
116
        CUnifiedNetwork *uni = CUnifiedNetwork::getInstance();
117

118
        CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
119
        if (uc == 0) return;        // should never happen, the getUnifiedConnection() will generate a AUTOCHECK_DISPLAY
120

121
        // call the user callback
122

123
        uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
124

125
        if(!uc->Connections.empty ())
126
        {
127
                // set all connection to dead, now, all messages received on this socket will be ignored and closed
128
                for (uint i = 0; i < uc->Connections.size (); ++i)
129
                {
130
                        if (uc->Connections[i].valid())
131
                                uc->Connections[i].setAppId (AppIdDeadConnection);
132
                }
133

134
                //
135
                // It's the first connection that added the _NamedCnx so if there s no connection, no need to
136
                // remove entry in _NamedCnx
137
                //
138

139
                uni->removeNamedCnx (uc->ServiceName, uc->ServiceId);
140
        }
141

142
        // remove the _UsedConnection
143
        bool found = false;
144
        for (vector<TServiceId>::iterator it = uni->_UsedConnection.begin (); it != uni->_UsedConnection.end(); it++)
145
        {
146
                if (*it == uc->ServiceId)
147
                {
148
                        found = true;
149
                        uni->_UsedConnection.erase (it);
150
                        break;
151
                }
152
        }
153
        if (!found)
154
                AUTOCHECK_DISPLAY ("HNETL5: can't find the sid %hu in the _UsedConnection", uc->ServiceId.get());
155

156
        // reset the unified connection
157
        uc->reset ();
158
}
159

160

161
//
162
// Callbacks from connection/disconnection services
163
//
164

165
void        uncbConnection(TSockId from, void * /* arg */)
166
{
167
        nlinfo ("HNETL5: + connec '%s'", from->asString().c_str());
168

169
        from->setAppId (AppIdDeadConnection);
170
}
171

172
void        uncbDisconnection(TSockId from, void * /* arg */)
173
{
174
        if(from->appId () == AppIdDeadConnection)
175
        {
176
                nlinfo ("HNETL5: - connec '%s'", from->asString().c_str());
177
        }
178
        else
179
        {
180
                CUnifiedNetwork        *uni = CUnifiedNetwork::getInstance();
181
                TServiceId                sid(uint16(from->appId()));
182
                CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
183
                if (uc == 0)
184
                {
185
                        nlinfo ("HNETL5: - connec '%s' sid %hu", from->asString().c_str(), sid.get());
186
                }
187
                else
188
                {
189
                        nlinfo ("HNETL5: - connec '%s' %s-%hu", from->asString().c_str(), uc->ServiceName.c_str (), sid.get());
190

191
                        if (uc->IsExternal)
192
                        {
193
                                if (!uc->AutoRetry)
194
                                {
195
                                        // If it s a external service with no auto retry, remove the connection
196

197
                                        // call the user callback
198
                                        uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
199

200
                                        uni->removeNamedCnx (uc->ServiceName, uc->ServiceId);
201

202
                                        uni->_ConnectionToReset.push_back(uc->ServiceId);
203
                                }
204
                                else
205
                                {
206
                                        // call the user callback
207
                                        uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
208
                                }
209
                        }
210
                        else
211
                        {
212
                                // reset the connection
213
                                uint i;
214
                                for (i = 0; i < uc->Connections.size (); i++)
215
                                {
216
                                        if (uc->Connections[i].valid() && uc->Connections[i].CbNetBase->getSockId(uc->Connections[i].HostId) == from)
217
                                        {
218
                                                if (uc->Connections[i].IsServerConnection)
219
                                                {
220
                                                        // we have to remove the stuffs now because HostId will not be accessible later
221
                                                        uc->Connections[i].reset();
222
                                                }
223
                                                else
224
                                                {
225
                                                        // if it s a client, we can't delete now because the callback client is currently in use
226
                                                        // only disconnect
227
                                                        if(uc->Connections[i].CbNetBase->connected ())
228
                                                        {
229
                                                                uc->Connections[i].CbNetBase->disconnect (uc->Connections[i].HostId);
230
                                                        }
231
                                                }
232
                                                break;
233
                                        }
234
                                }
235
                                if (i == uc->Connections.size ())
236
                                {
237
                                        AUTOCHECK_DISPLAY ("HNETL5: received a disconnection from a service but the connection is not in my list!");
238
                                }
239
                        }
240
                }
241

242
                from->setAppId (AppIdDeadConnection);
243
        }
244
}
245

246
//
247
// Callback from identification services
248
//
249

250
void        uncbServiceIdentification(CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
251
{
252
        string                inSName;
253
        TServiceId        inSid;
254

255
        if (from->appId () != AppIdDeadConnection)
256
                AUTOCHECK_DISPLAY ("HNETL5: received a connect ident from an unknown connection 0x%"NL_I64"X", from->appId ());
257

258
        // recover the service name and id
259
        msgin.serial(inSName);
260
        msgin.serial(inSid);
261
        uint8 pos;
262
        msgin.serial (pos);
263
        bool isExternal;
264
        msgin.serial (isExternal);
265

266
        nlinfo ("HNETL5: + connect ident '%s' %s-%hu pos %hu ext %d", from->asString().c_str(), inSName.c_str(), inSid.get(), (uint16)pos, (uint8)isExternal);
267

268
        if(isExternal)
269
        {
270
                nlassert (pos == 0);
271
        }
272

273
        if (inSid.get() == 0)
274
        {
275
                if (isExternal)
276
                {
277
                        inSid = CUnifiedNetwork::getInstance ()->_ExtSId;
278
                        CUnifiedNetwork::getInstance ()->_ExtSId.set(CUnifiedNetwork::getInstance ()->_ExtSId.get()+1);
279
                        // the following was an nlwarning but this is in fact desired behavior and not an error so we have changed to nlinfo
280
                        nlinfo ("HNETL5: Received a connection from a service with a SId 0, we give him the SId %d", inSid.get());
281
                }
282
                else
283
                {
284
                        nlwarning ("HNETL5: Received a connection from a service with a SId 0 and wasn't external, disconnecting it");
285
                        netbase.disconnect();
286
                        return;
287
                }
288
        }
289

290
        from->setAppId(inSid.get());
291

292
        // add a new connection to the list
293
        CUnifiedNetwork                *uni= CUnifiedNetwork::getInstance();
294

295
        if(inSid.get() >= uni->_IdCnx.size ())
296
        {
297
                uni->_IdCnx.resize (inSid.get()+1);
298
        }
299

300
        switch(uni->_IdCnx[inSid.get()].State)
301
        {
302
        case CUnifiedNetwork::CUnifiedConnection::NotUsed:                // add the new unified connection
303
                uni->_IdCnx[inSid.get()] = CUnifiedNetwork::CUnifiedConnection(inSName, inSid, isExternal);
304
                uni->_UsedConnection.push_back (inSid);
305
                break;
306
        default:
307
                break;
308
        }
309

310
        if (uni->_IdCnx[inSid.get()].IsExternal != isExternal)
311
        {
312
                AUTOCHECK_DISPLAY ("HNETL5: Receive a connection that is not totally external %d %d", uni->_IdCnx[inSid.get()].IsExternal, isExternal);
313
                return;
314
        }
315

316
        bool FirstConnection = (uni->_IdCnx[inSid.get()].Connections.size () == 0);
317

318
        // add the connection to the already inserted unified connection
319
        if (pos >= uni->_IdCnx[inSid.get()].Connections.size ())
320
        {
321
                uni->_IdCnx[inSid.get()].Connections.resize(pos+1);
322
        }
323
        uni->_IdCnx[inSid.get()].Connections[pos] = CUnifiedNetwork::CUnifiedConnection::TConnection(&netbase, from);
324

325
        // If the connection is external, we'll never receive the ExtAddress by the naming service, so add it manually
326
        if (isExternal)
327
        {
328
                uni->_IdCnx[inSid.get()].ExtAddress.push_back (netbase.hostAddress (from));
329
                uni->_IdCnx[inSid.get()].setupNetworkAssociation (uni->_NetworkAssociations, uni->_DefaultNetwork);
330
        }
331

332
        // send the callback to the user with the first connection
333
        if (FirstConnection)
334
        {
335
                // insert the name in the map to be able to send message with the name
336
                uni->addNamedCnx (inSName, inSid);
337

338
                uni->callServiceUpCallback (inSName, inSid);
339
        }
340
}
341

342
// the callbacks wrapper
343
void        uncbMsgProcessing(CMessage &msgin, TSockId from, CCallbackNetBase &/* netbase */)
344
{
345
        if (from->appId() == AppIdDeadConnection)
346
        {
347
                AUTOCHECK_DISPLAY ("HNETL5: Receive a message from a dead connection");
348
                return;
349
        }
350

351
        CUnifiedNetwork                                                                        *uni = CUnifiedNetwork::getInstance();
352
        TServiceId                                                                                sid(uint16(from->appId()));
353
        CUnifiedNetwork::TMsgMappedCallback::iterator        itcb;
354

355
        itcb = uni->_Callbacks.find(msgin.getName());
356
        if (itcb == uni->_Callbacks.end())
357
        {
358
                // the callback doesn't exist
359
                nlwarning ("HNETL5: Can't find callback '%s' called by service %hu", msgin.getName().c_str(), sid.get());
360
        }
361
        else
362
        {
363
                CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
364

365
                if (uc == 0)
366
                {
367
                        nlwarning ("HNETL5: Received a message from a service %hu that is not ready (bad appid? 0x%"NL_I64"X)", sid.get(), from->appId ());
368
                        return;
369
                }
370
                if((*itcb).second == 0)
371
                {
372
                        nlwarning ("HNETL5: Received message %s from a service %hu but the associated callback is NULL", msgin.getName ().c_str(), sid.get());
373
                        return;
374
                }
375

376
                {
377
                        static map<string, CHTimer> timers;
378
                        map<string, CHTimer>::iterator it;
379

380
                        {
381
                                H_AUTO(L5UCHTimerOverhead);
382
                                string callbackName = "USRCB_" + msgin.getName();
383
                                it = timers.find(callbackName);
384
                                if(it == timers.end())
385
                                {
386
                                        it = timers.insert(make_pair(callbackName, CHTimer(NULL))).first;
387
                                        (*it).second.setName((*it).first.c_str());
388
                                }
389
                        }
390

391
                        {
392
                                H_AUTO(L5UserCallback);
393

394
                                TTime before = CTime::getLocalTime();
395

396
                                (*it).second.before();
397
//                                const std::string &cbName = itcb->first;
398
                                (*itcb).second (msgin, uc->ServiceName, sid);
399
                                (*it).second.after();
400

401
                                TTime after = CTime::getLocalTime();
402

403
                                // sum the time used to do callback
404
                                TimeInCallback += uint32((after-before));
405
                        }
406
                }
407

408
                uc->TotalCallbackCalled++;
409
                TotalCallbackCalled++;
410
        }
411
}
412

413

414
TCallbackItem        unServerCbArray[] =
415
{
416
        { "UN_SIDENT", uncbServiceIdentification }
417
};
418

419

420
//
421
// Alive check thread
422
//
423

424
class CAliveCheck : public NLMISC::IRunnable
425
{
426
public:
427
        CAliveCheck() : ExitRequired(false)        { }
428

429
        virtual void        run();
430
        virtual                        ~CAliveCheck()        { }
431

432
        volatile bool        ExitRequired;
433

434
        static CAliveCheck*        Thread;
435

436
        struct CCheckAddress
437
        {
438
                CCheckAddress() : ConnectionId(0xDEAD), NeedCheck(false), AddressValid(false) { }
439
                CInetAddress        Address;
440
                std::string                ServiceName;
441
                TServiceId                ServiceId;
442
                uint                        ConnectionId;
443
                uint                        ConnectionIndex;
444
                volatile bool        NeedCheck;
445
                volatile bool        AddressValid;
446
        };
447

448
        CCheckAddress                CheckList[128];
449

450
        void                        checkService(CInetAddress address, uint connectionId, uint connectionIndex, const std::string &service, TServiceId id);
451

452
};
453

454
CAliveCheck*        CAliveCheck::Thread = NULL;
455
IThread*                AliveThread = NULL;
456

457

458
void        CAliveCheck::run()
459
{
460
        // setup thread
461
        Thread = this;
462

463
        TTime        t = CTime::getLocalTime();
464

465
        while (!ExitRequired)
466
        {
467
                if (CTime::getLocalTime() - t < 10000)
468
                {
469
                        nlSleep(100);
470
                        continue;
471
                }
472

473
                uint        i;
474
                for (i=0; i<sizeof(CheckList)/sizeof(CheckList[0]); ++i)
475
                {
476
                        if (CheckList[i].NeedCheck && !CheckList[i].AddressValid)
477
                        {
478
                                try
479
                                {
480
                                        CCallbackClient        cbc;
481
                                        cbc.connect(CheckList[i].Address);
482
                                        // success (no exception)
483
                                        CheckList[i].AddressValid = true;
484
                                        cbc.disconnect();
485
                                }
486
                                catch (ESocketConnectionFailed &e)
487
                                {
488
#if FINAL_VERSION
489
                                        nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", CheckList[i].ServiceName.c_str(), CheckList[i].ServiceId.get(), e.what ());
490
#else
491
                                        nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", CheckList[i].ServiceName.c_str(), CheckList[i].ServiceId.get(), e.what ());
492
#endif
493
                                }
494
                        }
495
                }
496

497
                t = CTime::getLocalTime();
498
        }
499

500
        ExitRequired = false;
501
}
502

503
void        CAliveCheck::checkService(CInetAddress address, uint connectionId, uint connectionIndex, const std::string &service, TServiceId id)
504
{
505
        uint        i;
506
        for (i=0; i<sizeof(CheckList)/sizeof(CheckList[0]); ++i)
507
        {
508
                if (CheckList[i].NeedCheck)
509
                        continue;
510

511
                CheckList[i].Address = address;
512
                CheckList[i].ConnectionId = connectionId;
513
                CheckList[i].ConnectionIndex = connectionIndex;
514
                CheckList[i].ServiceName = service;
515
                CheckList[i].ServiceId = id;
516

517
                CheckList[i].AddressValid = false;
518
                CheckList[i].NeedCheck = true;
519

520
                return;
521
        }
522
}
523

524

525
//
526
//
527
//
528

529
bool        CUnifiedNetwork::init(const CInetAddress *addr, CCallbackNetBase::TRecordingState rec,
530
                                                          const string &shortName, uint16 port, TServiceId &sid)
531
{
532
        // the commands can now be invoked
533
        registerCommandsHandler();
534

535
        if (_Initialised)
536
        {
537
                AUTOCHECK_DISPLAY ("HNETL5: Unified network layer already initialized");
538
                return true;
539
        }
540

541
        ThreadCreator = NLMISC::getThreadId();
542

543
        vector<CInetAddress> laddr = CInetAddress::localAddresses();
544

545
        _RecordingState = rec;
546
        _Name = shortName;
547
        _SId = sid;
548

549
        if (addr != 0)
550
                _NamingServiceAddr = *addr;
551

552
        // if the address isn't 0, uses the naming service
553
        if (_NamingServiceAddr.isValid ())
554
        {
555
                // connect the callback to know when a new service comes in or goes down
556
                CNamingClient::setRegistrationBroadcastCallback(uNetRegistrationBroadcast);
557
                CNamingClient::setUnregistrationBroadcastCallback(uNetUnregistrationBroadcast);
558

559
                // connect to the naming service (may generate a ESocketConnectionFailed exception)
560
                CNamingClient::connect(_NamingServiceAddr, _RecordingState, laddr);
561

562
                if (port == 0)
563
                        port = CNamingClient::queryServicePort ();
564
        }
565

566
#ifdef NL_OS_UNIX
567
        /// Init the main pipe to select() on data available
568
        if ( ::pipe( _MainDataAvailablePipe ) != 0 )
569
                nlwarning( "Unable to create main D.A. pipe" );
570
        //nldebug( "Pipe: created" );
571
#endif
572

573
        // setup the server callback only if server port != 0, otherwise there's no server callback
574
        _ServerPort = port;
575

576
        if(_ServerPort != 0)
577
        {
578
                nlassert (_CbServer == 0);
579
                _CbServer = new CCallbackServer( CCallbackNetBase::Off, "", true, false ); // don't init one pipe per connection
580
#ifdef NL_OS_UNIX
581
                _CbServer->setExternalPipeForDataAvailable( _MainDataAvailablePipe ); // the main pipe is shared for all connections
582
                //nldebug( "Pipe: set (server %p)", _CbServer );
583
#endif
584
                bool retry = false;
585
                do
586
                {
587
                        retry = false;
588
                        try
589
                        {
590
                                _CbServer->init(port);
591
                        }
592
                        catch (ESocket &)
593
                        {
594
                                nlwarning("Failed to init the listen socket on port %u, is the service already running ?", port);
595
                                // wait a little before retrying
596
                                nlSleep(5000);
597

598
                                retry = true;
599
                        }
600
                } while(retry);
601

602
                _CbServer->addCallbackArray(unServerCbArray, 1);                                // the service ident callback
603
                _CbServer->setDefaultCallback(uncbMsgProcessing);                                // the default callback wrapper
604
                _CbServer->setConnectionCallback(uncbConnection, NULL);
605
                _CbServer->setDisconnectionCallback(uncbDisconnection, NULL);
606
        }
607
        else
608
        {
609
                nlinfo ("HNETL5: ServerPort is 0 so I don't create a CCallbackServer");
610
        }
611

612
        if (CNamingClient::connected())
613
        {
614
                // register the service
615
                for (uint i = 0; i < laddr.size(); i++)
616
                        laddr[i].setPort(_ServerPort);
617

618
                if (_SId.get() == 0)
619
                {
620
                        if ( ! CNamingClient::registerService(_Name, laddr, _SId) )
621
                        {
622
                                nlinfo ("HNETL5: Registration denied");
623
                                return false;
624
                        }
625
                }
626
                else
627
                {
628
                        CNamingClient::registerServiceWithSId(_Name, laddr, _SId);
629
                }
630

631
                sid = _SId;
632

633
                nlinfo ("HNETL5: Server '%s' added, registered and listen to port %hu", _Name.c_str (), _ServerPort);
634
        }
635

636
        AliveThread = IThread::create(new CAliveCheck(), 1024*4);
637
        AliveThread->start();
638

639
        _Initialised = true;
640
        return true;
641
}
642

643
void        CUnifiedNetwork::connect()
644
{
645
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::connect() whereas it is not initialised yet"));
646

647
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
648

649
        if (CNamingClient::connected())
650
        {
651
                // get the services list
652
                const list<CNamingClient::CServiceEntry>        &services = CNamingClient::getRegisteredServices();
653

654
                // connects to the registered services
655
                list<CNamingClient::CServiceEntry>::const_iterator        its;
656

657
                // don't connect to itself
658
                for (its = services.begin(); its != services.end(); ++its)
659
                {
660
                        if (_SId != (*its).SId)
661
                        {
662
                                // add service with name, address, ident, not external, service id, and not autoretry (obsolete)
663
                                // we put the last true because the name callback should already inserted it by uNetRegistrationBroadcast()
664
                                addService((*its).Name, (*its).Addr, true, false, (*its).SId, false, true);
665
                        }
666
                        else
667
                        {
668
                                // don't process services received after mine because they'll connect to me
669
                                break;
670
                        }
671
                }
672
        }
673
}
674

675
void        CUnifiedNetwork::release(bool mustFlushSendQueues, const std::vector<std::string>& namesOfOnlyServiceToFlushSending)
676
{
677
        if (!_Initialised)
678
                return;
679

680
        // the commands can't be invoked
681
        unregisterCommandsHandler();
682

683
        // terminate the auto reconnection thread
684
        if (CAliveCheck::Thread)
685
        {
686
                CAliveCheck::Thread->ExitRequired = true;
687
                AliveThread->wait();
688
                delete CAliveCheck::Thread;
689
                delete AliveThread;
690
        }
691

692
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
693

694
        // Ensure all outgoing data are sent before disconnecting, if requested
695
        if ( mustFlushSendQueues )
696
        {
697
                nlinfo( "HNETL5: Flushing sending queues..." );
698
                float totalBytes = 0;
699
                uint bytesRemaining, i=0;
700
                while ( (bytesRemaining = tryFlushAllQueues( namesOfOnlyServiceToFlushSending )) != 0 )
701
                {
702
                        if ( i == 0 )
703
                                totalBytes = (float)bytesRemaining;
704
                        if ( i % 20 == 0 )
705
                        {
706
                                nldebug( "%.1f%% of %.3f KB flushed so far", // display without HNETL5 to bypass filters!
707
                                        ((float)(bytesRemaining-totalBytes))/totalBytes, totalBytes / 1024.0f );
708
                        }
709

710
                        ++i;
711

712
                        nlSleep( 100 );
713
                }
714
                nldebug( "HNETL5: Flush done in %u steps", i+1 );
715
        }
716

717
        // disconnect all clients
718
        if(_CbServer)
719
        {
720
                _CbServer->disconnect(InvalidSockId);
721
                delete _CbServer;
722
                _CbServer = 0;
723
        }
724

725
        // disconnect all connections to servers
726
        for (uint i = 0; i<_IdCnx.size(); ++i)
727
        {
728
                if (_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
729
                {
730
                        for(uint j = 0 ; j < _IdCnx[i].Connections.size (); j++)
731
                        {
732
                                if (_IdCnx[i].Connections[j].valid() && !_IdCnx[i].Connections[j].IsServerConnection)
733
                                {
734
                                        if (_IdCnx[i].Connections[j].CbNetBase->connected ())
735
                                                _IdCnx[i].Connections[j].CbNetBase->disconnect();
736

737
                                        delete _IdCnx[i].Connections[j].CbNetBase;
738
                                }
739
                        }
740
                        _IdCnx[i].Connections.clear ();
741
                }
742
        }
743

744
        // clear all other data
745
        _IdCnx.clear();
746
        _UsedConnection.clear ();
747
        _NamedCnx.clear();
748
        _UpCallbacks.clear();
749
        _DownCallbacks.clear();
750
        _Callbacks.clear();
751

752
        // disconnect the connection with the naming service
753
        if (CNamingClient::connected ())
754
                CNamingClient::disconnect ();
755

756
#ifdef NL_OS_UNIX
757
        ::close( _MainDataAvailablePipe[PipeRead] );
758
        ::close( _MainDataAvailablePipe[PipeWrite] );
759
#endif
760
}
761

762
void        CUnifiedNetwork::addService(const string &name, const CInetAddress &addr, bool sendId, bool external, TServiceId sid, bool autoRetry, bool shouldBeAlreayInserted)
763
{
764
        vector <CInetAddress> addrs;
765
        addrs.push_back (addr);
766
        addService (name, addrs, sendId, external, sid, autoRetry, shouldBeAlreayInserted);
767
}
768

769
void        CUnifiedNetwork::addService(const string &name, const vector<CInetAddress> &addr, bool sendId, bool external, TServiceId sid, bool autoRetry, bool shouldBeAlreayInserted)
770
{
771
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::addService() whereas it is not initialised yet"));
772

773
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
774

775
        if (external)
776
        {
777
                sid = _ExtSId;
778
                _ExtSId.set(_ExtSId.get()+1);
779
        }
780

781
        nlinfo("HNETL5: addService %s-%hu '%s'", name.c_str(), sid.get(), vectorCInetAddressToString(addr).c_str());
782

783
        if (external && addr.size () != 1)
784
        {
785
                AUTOCHECK_DISPLAY ("HNETL5: Can't add external service with more than one connection");
786
        }
787

788
        // add the entry in the unified connection table
789

790
        if (sid.get() >= _IdCnx.size())
791
                _IdCnx.resize(sid.get()+1);
792

793
        CUnifiedConnection        *uc = &_IdCnx[sid.get()];
794

795
        // at this point it s possible that the service already added in the _IdCnx by the uNetRegistrationBroadcast()
796

797
        if (shouldBeAlreayInserted && _IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)  AUTOCHECK_DISPLAY ("HNETL5: the unified connection should already set by the naming reg broadcast and is not (%hu)", sid.get());
798
        if (!shouldBeAlreayInserted && _IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)  AUTOCHECK_DISPLAY ("HNETL5: the unified connection should not already set but is (%hu)", sid.get());
799

800
        if (_IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
801
        {
802
                *uc = CUnifiedConnection(name, sid, external);
803
                _UsedConnection.push_back (sid);
804
        }
805
        else
806
        {
807
                // If the entry already set, check that all is correct
808
                if (name != uc->ServiceName) AUTOCHECK_DISPLAY ("HNETL5: name are different in addService %s %s", name.c_str (), uc->ServiceName.c_str ());
809
                if (sid != uc->ServiceId) AUTOCHECK_DISPLAY ("HNETL5: sid are different in addService %hu %hu", sid.get(), uc->ServiceId.get());
810
                if (addr != uc->ExtAddress) AUTOCHECK_DISPLAY ("HNETL5: external addr are different in addService '%s' '%s'", vectorCInetAddressToString(addr).c_str(), vectorCInetAddressToString(uc->ExtAddress).c_str ());
811
        }
812
        uc->AutoRetry = autoRetry;
813
        uc->SendId = sendId;
814
        uc->ExtAddress = addr;
815
        nlassert (!addr.empty());
816

817
        // associate nid with ext address
818
        uc->setupNetworkAssociation (_NetworkAssociations, _DefaultNetwork);
819

820
        // connect to all connection
821
        bool        connectSuccess;
822

823
        if (uc->Connections.size () < addr.size ())
824
        {
825
                uc->Connections.resize (addr.size ());
826
        }
827

828
        vector<CInetAddress> laddr = CInetAddress::localAddresses();
829

830
        for (uint i = 0; i < addr.size(); i++)
831
        {
832
                // first we have to look if we have a network that can established the connection
833

834
                uint j = 0;
835
                // it s 127.0.0.1, it s ok
836
                if (!addr[i].is127001 ())
837
                {
838
                        for (j = 0; j < laddr.size (); j++)
839
                        {
840
                                if (laddr[j].internalNetAddress () == addr[i].internalNetAddress ())
841
                                {
842
                                        // it's ok, we can try
843
                                        break;
844
                                }
845
                        }
846

847
                        // If we don't found a valid network, we'll try with the first one.
848
                        // It's happen, for example, when you try to connect to a service that is not in the network but use IP translation
849
                        if (j == laddr.size ())
850
                        {
851
                                nlwarning ("HNETL5: I can't access '%s' because I haven't a net card on this network, we'll use the first network", addr[i].asString ().c_str ());
852
                                j = 0;
853
                        }
854
                }
855

856
                // Create a new connection with the service, setup callback and connect
857
                CCallbackClient        *cbc = new CCallbackClient( CCallbackNetBase::Off, "", true, false ); // don't init one pipe per connection
858
#ifdef NL_OS_UNIX
859
                cbc->setExternalPipeForDataAvailable( _MainDataAvailablePipe ); // the main pipe is shared for all connections
860
                //nldebug( "Pipe: set (client %p)", cbc );
861
#endif
862
                cbc->setDisconnectionCallback(uncbDisconnection, NULL);
863
                cbc->setDefaultCallback(uncbMsgProcessing);
864
                cbc->getSockId()->setAppId(sid.get());
865

866
                try
867
                {
868
                        cbc->connect(addr[i]);
869
                        connectSuccess = true;
870
                }
871
                catch (ESocketConnectionFailed &e)
872
                {
873
                        nlwarning ("HNETL5: can't connect to %s (sid %u) now (%s) '%s'", name.c_str(), sid.get(), e.what (), addr[i].asString ().c_str());
874
                        connectSuccess = false;
875
                }
876

877
                if (!connectSuccess && !autoRetry)
878
                {
879
                        nlwarning ("HNETL5: Can't add service because no retry and can't connect");
880
                        delete cbc;
881
                }
882
                else
883
                {
884
                        uc->Connections[i] = CUnifiedNetwork::CUnifiedConnection::TConnection(cbc);
885
                }
886

887
                if (connectSuccess && sendId)
888
                {
889
                        // send identification to the service
890
                        CMessage        msg("UN_SIDENT");
891
                        msg.serial(_Name);
892
                        TServiceId                ssid = _SId;
893
                        if (uc->IsExternal)
894
                        {
895
                                // in the case that the service is external, we can't send our sid because the external service can
896
                                // have other connection with the same sid (for example, LS can have 2 WS with same sid => sid = 0 and leave
897
                                // the other side to find a good number
898
                                ssid.set(0);
899
                        }
900
                        msg.serial(ssid);        // serializes a 16 bits service id
901
                        uint8 pos = uint8(j);
902
                        msg.serial(pos);        // send the position in the connection table
903
                        msg.serial (uc->IsExternal);
904
                        cbc->send (msg);
905
                }
906
        }
907

908
        if (addr.size () != uc->Connections.size())
909
        {
910
                nlwarning ("HNETL5: Can't connect to all connections to the service %d/%d", addr.size (), uc->Connections.size());
911
        }
912

913
        bool cntok = false;
914
        for (uint j = 0; j < uc->Connections.size(); j++)
915
        {
916
                if (uc->Connections[j].CbNetBase != NULL)
917
                {
918
                        if (uc->Connections[j].CbNetBase->connected ())
919
                        {
920
                                cntok = true;
921
                                break;
922
                        }
923
                }
924
        }
925

926
        if (cntok)
927
        {
928
                // add the name only if at least one connection is ok
929
                addNamedCnx (name, sid);
930

931
                callServiceUpCallback (name, sid); // global callback ("*") will be called even for external service
932
        }
933

934
        nldebug ("HNETL5: addService was successful");
935
}
936
//
937
//
938
//
939

940
#define        TIME_BLOCK(tick, instr) \
941
{ \
942
        TTicks        _time_block_before = CTime::getPerformanceTime(); \
943
        instr ; \
944
        TTicks        _time_block_after = CTime::getPerformanceTime(); \
945
        tick += (_time_block_after - _before); \
946
}
947

948
/*
949
 *
950
 */
951
void        CUnifiedNetwork::update(TTime timeout)
952
{
953
        H_AUTO(CUnifiedNetworkUpdate);
954

955
        H_BEFORE(UNMisc1);
956
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::update() whereas it is not initialised yet"));
957

958
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
959

960
        bool        enableRetry;        // true every 5 seconds to reconnect if necessary
961

962
        // Compute the real timeout based on the next update timeout
963
        TTime t0 = CTime::getLocalTime ();
964
        if (timeout > 0)
965
        {
966
                if (_NextUpdateTime == 0)
967
                {
968
                        _NextUpdateTime = t0 + timeout;
969
                }
970
                else
971
                {
972
                        TTime err = t0 - _NextUpdateTime;
973
                        _NextUpdateTime += timeout;
974

975
                        // if we are too late, resync to the next value
976
                        while (err > timeout)
977
                        {
978
                                err -= timeout;
979
                                _NextUpdateTime += timeout;
980
                        }
981

982
                        timeout -= err;
983
                        if (timeout < 0) timeout = 0;
984
                }
985
        }
986
        TTime remainingTime = timeout;
987

988
        // check if we need to retry to connect to the client
989
        enableRetry = (t0-_LastRetry > 5000);
990
        if (enableRetry)
991
                _LastRetry = t0;
992

993
        H_AFTER(UNMisc1);
994

995
        H_BEFORE(UNNamingCheck);
996
        // Try to reconnect to the naming service if connection lost
997
        if (_NamingServiceAddr.isValid ())
998
        {
999
                if (CNamingClient::connected ())
1000
                {
1001
                        CNamingClient::update ();
1002
                }
1003
                else if (enableRetry)
1004
                {
1005
                        H_AUTO(L5NSReconnect);
1006
                        try
1007
                        {
1008
                                vector<CInetAddress> laddr = CInetAddress::localAddresses();
1009
                                CNamingClient::connect (_NamingServiceAddr, _RecordingState, laddr);
1010
                                // re-register the service
1011
                                for (uint i = 0; i < laddr.size(); i++)
1012
                                        laddr[i].setPort(_ServerPort);
1013
                                CNamingClient::resendRegisteration (_Name, laddr, _SId);
1014
                        }
1015
                        catch (ESocketConnectionFailed &)
1016
                        {
1017
                                nlwarning ("HNETL5: Could not connect to the Naming Service (%s). Retrying in a few seconds...", _NamingServiceAddr.asString().c_str());
1018
                        }
1019
                }
1020
        }
1021
        H_AFTER(UNNamingCheck);
1022

1023
        H_BEFORE(UNUpdateCnx);
1024
        for(;;)
1025
        {
1026
                H_AUTO(L5OneLoopUpdate);
1027

1028
                if (CAliveCheck::Thread)
1029
                {
1030
                        uint        i;
1031
                        for (i=0; i<sizeof(CAliveCheck::Thread->CheckList)/sizeof(CAliveCheck::Thread->CheckList[0]); ++i)
1032
                        {
1033
                                CAliveCheck::CCheckAddress        &address = CAliveCheck::Thread->CheckList[i];
1034
                                if (address.NeedCheck && address.AddressValid)
1035
                                {
1036
                                        CUnifiedConnection &uc = _IdCnx[address.ConnectionId];
1037
                                        if (uc.ServiceName == address.ServiceName &&
1038
                                                uc.ServiceId == address.ServiceId &&
1039
                                                uc.ValidRequested)
1040
                                        {
1041
                                                uc.ValidRequested = false;
1042
                                                uc.ValidExternal = true;
1043
                                        }
1044

1045
                                        address.NeedCheck = false;
1046
                                        address.AddressValid = false;
1047
                                }
1048
                        }
1049
                }
1050

1051
                // update all server connections
1052
                if (_CbServer)
1053
                {
1054
                        _CbServer->update2((sint32)remainingTime, 0);
1055
                }
1056

1057
                // update all client connections
1058
                for (uint k = 0; k<_UsedConnection.size(); ++k)
1059
                {
1060
                        H_AUTO(UNBrowseConnections);
1061
                        nlassert (_IdCnx[_UsedConnection[k].get()].State == CUnifiedNetwork::CUnifiedConnection::Ready);
1062
                        for (uint j = 0; j < _IdCnx[_UsedConnection[k].get()].Connections.size (); j++)
1063
                        {
1064
                                // WARNING : don't take a reference in the outside loop because
1065
                                //                        _IdCnx can be resized by execution of a callback
1066
                                CUnifiedConnection &uc = _IdCnx[_UsedConnection[k].get()];
1067
                                nlassert(_IdCnx[_UsedConnection[k].get()].Connections.size() > j);
1068
                                CUnifiedConnection::TConnection &conn = _IdCnx[_UsedConnection[k].get()].Connections[j];
1069
                                H_AUTO(UNBrowseSubConnections);
1070
                                if (!conn.valid())
1071
                                        continue;
1072

1073
                                if (conn.IsServerConnection)
1074
                                        continue;
1075

1076
                                nlassert(uc.Connections.size() > j);
1077
                                if (conn.CbNetBase->connected ())
1078
                                {
1079
                                nlassert(uc.Connections.size() > j);
1080
                                        conn.CbNetBase->update2((sint32)remainingTime, 0);
1081
                                }
1082
                                else if (enableRetry && uc.AutoRetry)
1083
                                {
1084
                                        if (uc.ValidExternal)
1085
                                        {
1086
                                                uc.ValidExternal = false;
1087
                                                uc.ValidRequested = false;
1088
                                                autoReconnect( uc, j );
1089
                                        }
1090
                                        else if (!uc.ValidRequested && CAliveCheck::Thread)
1091
                                        {
1092
                                                uc.ValidRequested = true;
1093
                                                CAliveCheck::Thread->checkService(uc.ExtAddress[j], _UsedConnection[k].get(), j, uc.ServiceName, uc.ServiceId);
1094
                                        }
1095
                                }
1096
                        }
1097
                }
1098

1099
                // reset closed client connection
1100
                for (uint i=0; i<_ConnectionToReset.size(); ++i)
1101
                {
1102
                        // remove the _UsedConnection
1103
                        bool found = false;
1104
                        for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1105
                        {
1106
                                if (*it == _IdCnx[_ConnectionToReset[i].get()].ServiceId)
1107
                                {
1108
                                        found = true;
1109
                                        _UsedConnection.erase (it);
1110
                                        break;
1111
                                }
1112
                        }
1113
                        if (!found)
1114
                                AUTOCHECK_DISPLAY ("HNETL5: can't find the sid %hu in the _UsedConnection", _IdCnx[_ConnectionToReset[i].get()].ServiceId.get());
1115
                        _IdCnx[_ConnectionToReset[i].get()].reset();
1116
                }
1117
                _ConnectionToReset.clear();
1118

1119
                enableRetry = false;
1120

1121
                if ( FlushSendsBeforeSleep.get() )
1122
                {
1123
                        // Flush all connections
1124
                        L5TotalBytesInLowLevelSendQueues = tryFlushAllQueues();
1125
                }
1126

1127
                //      t0 --------- previousTime -------------------------- t0 + timeout
1128
                //                                    prevRemainingTime
1129
                //
1130
                //      t0 -------------- currentTime ---------------------- t0 + timeout
1131
                //                                        remainingTime
1132
                TTime prevRemainingTime = remainingTime;
1133
                TTime currentTime = CTime::getLocalTime();
1134
                remainingTime = t0 + timeout - currentTime;
1135

1136
                // If it's the end (or if the Unix system time was changed forwards), don't sleep (currentTime > t0 + timeout)
1137
                if ( remainingTime <= 0 )
1138
                        break;
1139

1140
                // If the Unix system time was changed backwards, don't wait more than requested and don't provide an erroneous time to the sleep function that would fail (currentTime < previousTime)
1141
                if ( remainingTime > prevRemainingTime )
1142
                {
1143
                        // Restart at previousTime
1144
                        nldebug( "Backward time sync detected (at least -%"NL_I64"d ms)", remainingTime - prevRemainingTime );
1145
                        remainingTime = prevRemainingTime;
1146
                        t0 = currentTime - (timeout - remainingTime);
1147
                }
1148

1149
#ifdef NL_OS_UNIX
1150
                // Sleep until the time expires or we receive a message
1151
                H_BEFORE(L5UpdateSleep);
1152
                switch ( UseYieldMethod.get() )
1153
                {
1154
                case 0: sleepUntilDataAvailable( remainingTime ); break; // accurate sleep with select()
1155
                case 1: ::usleep(1000); break; // 20 ms
1156
                case 2: nlSleep(1); break; // 10 ms (by nanosleep, but 20 ms measured on kernel 2.4.20)
1157
                case 3:        ::sched_yield(); break; // makes all slow (at least on kernel 2.4.20) !
1158
                default: break; // don't sleep at all, makes all slow!
1159
                }
1160
                H_AFTER(L5UpdateSleep);
1161
#else
1162
                // Enable windows multithreading before rescanning all connections
1163
                H_TIME(L5UpdateSleep, nlSleep(1);); // 0 (yield) would be too harmful to other applications
1164
#endif
1165
        }
1166
        H_AFTER(UNUpdateCnx);
1167

1168
        H_TIME(UNAutoCheck, autoCheck(););
1169
}
1170

1171

1172
/*
1173
 * Auto-reconnect
1174
 */
1175
void CUnifiedNetwork::autoReconnect( CUnifiedConnection &uc, uint connectionIndex )
1176
{
1177
        H_AUTO(L5AutoReconnect);
1178
        try
1179
        {
1180
                CCallbackClient *cbc = (CCallbackClient *)uc.Connections[connectionIndex].CbNetBase;
1181
                cbc->connect(uc.ExtAddress[connectionIndex]);
1182
                uc.Connections[connectionIndex].CbNetBase->getSockId()->setAppId(uc.ServiceId.get());
1183
                nlinfo ("HNETL5: reconnection to %s-%hu success", uc.ServiceName.c_str(), uc.ServiceId.get());
1184

1185
                // add the name only if at least one connection is ok
1186
                if (!haveNamedCnx (uc.ServiceName, uc.ServiceId))
1187
                        addNamedCnx (uc.ServiceName, uc.ServiceId);
1188

1189
                // resend the identification is necessary
1190
                if (uc.SendId)
1191
                {
1192
                        // send identification to the service
1193
                        CMessage        msg("UN_SIDENT");
1194
                        msg.serial(_Name);
1195

1196
                        TServiceId                ssid = _SId;
1197
                        if (uc.IsExternal)
1198
                        {
1199
                                // in the case that the service is external, we can't send our sid because the external service can
1200
                                // have other connection with the same sid (for example, LS can have 2 WS with same sid => sid = 0 and leave
1201
                                // the other side to find a good number
1202
                                ssid.set(0);
1203
                        }
1204
                        msg.serial(ssid);        // serializes a 16 bits service id
1205
                        uint8 pos = uint8(connectionIndex);
1206
                        msg.serial(pos);        // send the position in the connection table
1207
                        msg.serial (uc.IsExternal);
1208
                        uc.Connections[connectionIndex].CbNetBase->send (msg, uc.Connections[connectionIndex].HostId);
1209
                }
1210

1211
                // call the user callback
1212
                callServiceUpCallback (uc.ServiceName, uc.ServiceId);
1213
        }
1214
        catch (ESocketConnectionFailed &e)
1215
        {
1216
#if FINAL_VERSION
1217
                nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", uc.ServiceName.c_str(), uc.ServiceId.get(), e.what ());
1218
#else
1219
                nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", uc.ServiceName.c_str(), uc.ServiceId.get(), e.what ());
1220
#endif
1221
        }
1222
}
1223

1224
#ifdef NL_OS_UNIX
1225
/*
1226
 *
1227
 */
1228
void CUnifiedNetwork::sleepUntilDataAvailable( TTime msecMax )
1229
{
1230
        // Prevent looping infinitely if an erroneous time was provided
1231
        if ( msecMax > 999 ) // limit not told in Linux man but here: http://docs.hp.com/en/B9106-90009/select.2.html
1232
                msecMax = 999;
1233

1234
        // Prepare for select()
1235
        fd_set readers;
1236
        FD_ZERO( &readers );
1237
        FD_SET( _MainDataAvailablePipe[PipeRead], &readers );
1238
        SOCKET descmax = _MainDataAvailablePipe[PipeRead] + 1;
1239

1240
        // Select
1241
        timeval tv;
1242
        tv.tv_sec = 0;
1243
        tv.tv_usec = msecMax * 1000;
1244
        //nldebug( "Select %u ms", (uint)msecMax );
1245
        //TTime before = CTime::getLocalTime();
1246
        int res = ::select( descmax+1, &readers, NULL, NULL, &tv );
1247
        if ( res == -1 )
1248
                nlwarning( "HNETL5: Select failed in sleepUntilDataAvailable");
1249
        //nldebug( "Slept %u ms", (uint)(CTime::getLocalTime()-before) );
1250
}
1251
#endif
1252

1253

1254

1255
bool CUnifiedNetwork::isConnectionConnected(TServiceId sid) const
1256
{
1257
        // a Connected connection is a connection that is Ready but that is not yet connected (serverUp will be called latter via L5).
1258
        return sid.get() < _IdCnx.size()
1259
                && _IdCnx[sid.get()].State == CUnifiedConnection::Ready
1260
                && !_IdCnx[sid.get()].Connections.empty();
1261
}
1262
//
1263
//
1264
//
1265
uint8 CUnifiedNetwork::findConnectionId (TServiceId sid, uint8 nid)
1266
{
1267
        if (_IdCnx[sid.get()].Connections.size () == 0)
1268
        {
1269
                nlwarning ("HNETL5: Can't send message to %s because no connection are available", _IdCnx[sid.get()].ServiceName.c_str ());
1270
                return 0xFF;
1271
        }
1272

1273
        // by default, connection id will be the default one
1274
        uint8 connectionId = _IdCnx[sid.get()].DefaultNetwork;
1275

1276
        if (nid == 0xFF)
1277
        {
1278
                // it s often happen because they didn't set a good network configuration, so it s in debug to disable it easily
1279
                //nldebug ("HNETL5: nid %hu, will use the default connection %hu", (uint16)nid, (uint16)connectionId);
1280
        }
1281
        else if (nid >= _IdCnx[sid.get()].NetworkConnectionAssociations.size())
1282
        {
1283
                nlwarning ("HNETL5: No net association for nid %hu, use the default connection %hu", (uint16)nid, (uint16)connectionId);
1284
        }
1285
        else
1286
        {
1287
                if (_IdCnx[sid.get()].NetworkConnectionAssociations[nid] >= _IdCnx[sid.get()].Connections.size ())
1288
                {
1289
                        nlwarning ("HNETL5: Can't send message to %s because nid %d point on a bad connection (%d and only have %d cnx), use default connection", _IdCnx[sid.get()].ServiceName.c_str (), nid, connectionId, _IdCnx[sid.get()].Connections.size ());
1290
                }
1291
                else
1292
                {
1293
                        connectionId = _IdCnx[sid.get()].NetworkConnectionAssociations[nid];
1294
                }
1295
        }
1296

1297
        if (connectionId >= _IdCnx[sid.get()].Connections.size() || !_IdCnx[sid.get()].Connections[connectionId].valid() || !_IdCnx[sid.get()].Connections[connectionId].CbNetBase->connected())
1298
        {
1299
                // there's a problem with the selected connectionID, so try to find a valid one
1300
                nlwarning ("HNETL5: Can't find selected connection id %hu to send message to %s because connection is not valid or connected, find a valid connection id", (uint16)connectionId, _IdCnx[sid.get()].ServiceName.c_str ());
1301

1302
                for (connectionId = 0; connectionId < _IdCnx[sid.get()].Connections.size(); connectionId++)
1303
                {
1304
                        if (_IdCnx[sid.get()].Connections[connectionId].valid() && _IdCnx[sid.get()].Connections[connectionId].CbNetBase->connected())
1305
                        {
1306
                                // we found one at last, use this one
1307
                                //nldebug ("HNETL5: Ok, we found a valid connectionid, use %hu",  (uint16)connectionId);
1308
                                break;
1309
                        }
1310
                }
1311

1312
                if (connectionId == _IdCnx[sid.get()].Connections.size())
1313
                {
1314
                        nlwarning ("HNETL5: Can't send message to %s because default connection is not exist, valid or connected", _IdCnx[sid.get()].ServiceName.c_str ());
1315
                        return 0xFF;
1316
                }
1317
        }
1318
        return connectionId;
1319
}
1320

1321

1322
//
1323
//
1324
//
1325

1326
uint        CUnifiedNetwork::send(const string &serviceName, const CMessage &msgout, bool warnIfNotFound, uint8 nid)
1327
{
1328
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(const string&, const CMessage&) whereas it is not initialised yet"));
1329

1330
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1331

1332
        TNameMappedConnection::const_iterator                                                                it;
1333
        pair<TNameMappedConnection::const_iterator,TNameMappedConnection::const_iterator>        range;
1334
        range = _NamedCnx.equal_range(serviceName);
1335

1336
        uint found = 0;
1337
        if (range.first != _NamedCnx.end())
1338
        {
1339
                for (it=range.first; it!=range.second; ++it)
1340
                {
1341
                        TServiceId        sid = it->second;
1342
                        if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1343
                        {
1344
                                // It often happen when the service is down (connection broke and the naming not already say that it s down)
1345
                                // In this case, just warn
1346
                                nlwarning ("HNETL5: Can't send %s to the service '%s' because it was in the _NamedCnx but not in _IdCnx (means that the service is down)", msgout.getName().c_str(), serviceName.c_str ());
1347
                                return false;
1348
                        }
1349

1350
                        ++found;
1351

1352
                        uint8 connectionId = findConnectionId (sid, nid);
1353
                        if (connectionId == 0xff)        // failed
1354
                        {
1355
                                nlwarning ("HNETL5: Can't send %s message to %hu because no connection available", msgout.getName().c_str(), sid.get());
1356
                                continue;
1357
                        }
1358

1359
                        _IdCnx[sid.get()].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[sid.get()].Connections[connectionId].HostId);
1360
                }
1361
        }
1362

1363
        if (!found && warnIfNotFound)
1364
                nlwarning ("HNETL5: can't find service %s to send message %s", serviceName.c_str(), msgout.getName().c_str());
1365

1366
        return found;
1367
}
1368

1369
bool        CUnifiedNetwork::send(TServiceId sid, const CMessage &msgout, uint8 nid)
1370
{
1371
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(TServiceId, const CMessage&) whereas it is not initialised yet"));
1372

1373
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1374

1375
        if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1376
        {
1377
                // Happens when trying to send a message to an unknown service id
1378
                nlwarning ("HNETL5: Can't send %s to the service '%hu' because not in _IdCnx", msgout.getName().c_str(), sid.get());
1379
                return false;
1380
        }
1381

1382
        uint8 connectionId = findConnectionId (sid, nid);
1383
        if (connectionId == 0xff)        // failed
1384
        {
1385
                nlwarning ("HNETL5: Can't send %s to the service '%hu' because no connection available", msgout.getName().c_str(), sid.get());
1386
                return false;
1387
        }
1388

1389
        _IdCnx[sid.get()].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[sid.get()].Connections[connectionId].HostId);
1390
        return true;
1391
}
1392

1393
void        CUnifiedNetwork::sendAll(const CMessage &msgout, uint8 nid)
1394
{
1395
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(const CMessage&) whereas it is not initialised yet"));
1396

1397
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1398

1399
        for (TServiceId::size_type i=0; i<_IdCnx.size(); ++i)
1400
        {
1401
                if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1402
                {
1403
                        uint8 connectionId = findConnectionId (TServiceId(i), nid);
1404
                        if (connectionId == 0xff)        // failed
1405
                        {
1406
                                nlwarning ("HNETL5: Can't send message to %u because no connection available", i);
1407
                                continue;
1408
                        }
1409

1410
                        _IdCnx[i].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[i].Connections[connectionId].HostId);
1411
                }
1412
        }
1413
}
1414

1415

1416
/* Flush all the sending queues, and report the number of bytes still pending.
1417
 * To ensure all data are sent before stopping a service, you may want to repeat
1418
 * calling this method evenly until it returns 0.
1419
 * \param namesOfOnlyServiceToFlushSending If not empty, only the send queues to the
1420
 * services specified (by short name) will be flushed.
1421
 */
1422
uint        CUnifiedNetwork::tryFlushAllQueues(const std::vector<std::string>& namesOfOnlyServiceToFlushSending)
1423
{
1424
        H_AUTO(L5FlushAll);
1425
        uint bytesRemaining = 0;
1426
        for (uint k = 0; k<_UsedConnection.size(); ++k)
1427
        {
1428
                H_AUTO(UNFABrowseConnections);
1429
                CUnifiedConnection &uc = _IdCnx[_UsedConnection[k].get()];
1430

1431
                // Skip the connection if it is not found in the 'only' list (except if the list is empty)
1432
                if ( (! namesOfOnlyServiceToFlushSending.empty()) &&
1433
                         (std::find( namesOfOnlyServiceToFlushSending.begin(), namesOfOnlyServiceToFlushSending.end(), uc.ServiceName ) == namesOfOnlyServiceToFlushSending.end()) )
1434
                         continue;
1435

1436
                nlassert (uc.State == CUnifiedNetwork::CUnifiedConnection::Ready);
1437
                for (uint j = 0; j < uc.Connections.size (); j++)
1438
                {
1439
                        H_AUTO(UNFABrowseSubConnections);
1440
                        if (!uc.Connections[j].valid())
1441
                                continue;
1442

1443
                        if (uc.Connections[j].CbNetBase->connected ())
1444
                        {
1445
                                uint bytesRemainingLocal;
1446
                                uc.Connections[j].CbNetBase->flush(uc.Connections[j].HostId, &bytesRemainingLocal);
1447
                                bytesRemaining += bytesRemainingLocal;
1448
                        }
1449
                }
1450
        }
1451
        return bytesRemaining;
1452
}
1453

1454

1455
//
1456
//
1457
//
1458

1459
void        CUnifiedNetwork::addCallbackArray (const TUnifiedCallbackItem *callbackarray, sint arraysize)
1460
{
1461
        uint        i;
1462

1463
        for (i=0; i<(uint)arraysize; ++i)
1464
                _Callbacks.insert(make_pair(string(callbackarray[i].Key),callbackarray[i].Callback));
1465
}
1466

1467

1468
void        CUnifiedNetwork::setServiceUpCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg, bool back)
1469
{
1470
        nlassert (cb != NULL);
1471
        if (serviceName == "*")
1472
        {
1473
                if (back)
1474
                        _UpUniCallback.push_back (make_pair(cb, arg));
1475
                else
1476
                        _UpUniCallback.insert (_UpUniCallback.begin(), make_pair(cb, arg));
1477
        }
1478
        else
1479
        {
1480
                if (back)
1481
                        _UpCallbacks[serviceName].push_back (make_pair(cb, arg));
1482
                else
1483
                        _UpCallbacks[serviceName].insert (_UpCallbacks[serviceName].begin(), make_pair(cb, arg));
1484
        }
1485
}
1486

1487
void        CUnifiedNetwork::removeServiceUpCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg)
1488
{
1489
        if (serviceName == "*")
1490
        {
1491
                uint i;
1492
                for (i=0; i<_UpUniCallback.size(); ++i)
1493
                {
1494
                        if (_UpUniCallback[i].first == cb && _UpUniCallback[i].second == arg)
1495
                        {
1496
                                // we found it
1497
                                _UpUniCallback.erase(_UpUniCallback.begin()+i);
1498
                                break;
1499
                        }
1500
                }
1501
                if (i == _UpUniCallback.size())
1502
                {
1503
                        nlwarning("HNETL5 : can't remove service up callback, not found");
1504
                }
1505
        }
1506
        else
1507
        {
1508
                if (_UpCallbacks.find(serviceName) != _UpCallbacks.end())
1509
                {
1510
                        std::list<TCallbackArgItem> &list = _UpCallbacks[serviceName];
1511
                        std::list<TCallbackArgItem>::iterator first(list.begin()), last(list.end());
1512
                        for (; first != last; ++first)
1513
                        {
1514
                                if (first->first == cb && first->second == arg)
1515
                                {
1516
                                        list.erase(first);
1517
                                        break;
1518
                                }
1519
                        }
1520

1521
                        if (first == last)
1522
                        {
1523
                                nlwarning("HNETL5 : can't remove service up callback, not found");
1524
                        }
1525

1526
                        if (list.empty())
1527
                        {
1528
                                // no more callback for this service
1529
                                _UpCallbacks.erase(serviceName);
1530
                        }
1531
                }
1532
                else
1533
                {
1534
                        nlwarning("HNETL5 : can't remove service up callback, not found");
1535
                }
1536
        }
1537
}
1538

1539
void        CUnifiedNetwork::setServiceDownCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg, bool back)
1540
{
1541
        nlassert (cb != NULL);
1542
        if (serviceName == "*")
1543
        {
1544
                if (back)
1545
                        _DownUniCallback.push_back (make_pair(cb, arg));
1546
                else
1547
                        _DownUniCallback.insert (_DownUniCallback.begin(), make_pair(cb, arg));
1548
        }
1549
        else
1550
        {
1551
                if (back)
1552
                        _DownCallbacks[serviceName].push_back (make_pair(cb, arg));
1553
                else
1554
                        _DownCallbacks[serviceName].insert (_DownCallbacks[serviceName].begin(), make_pair(cb, arg));
1555
        }
1556
}
1557

1558
void        CUnifiedNetwork::removeServiceDownCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg)
1559
{
1560
        if (serviceName == "*")
1561
        {
1562
                uint i;
1563
                for (i=0; i<_DownUniCallback.size(); ++i)
1564
                {
1565
                        if (_DownUniCallback[i].first == cb && _DownUniCallback[i].second == arg)
1566
                        {
1567
                                // we found it
1568
                                _DownUniCallback.erase(_DownUniCallback.begin()+i);
1569
                                break;
1570
                        }
1571
                }
1572
                if (i == _DownUniCallback.size())
1573
                {
1574
                        nlwarning("HNETL5 : can't remove service down callback, not found");
1575
                }
1576
        }
1577
        else
1578
        {
1579
                if (_DownCallbacks.find(serviceName) != _DownCallbacks.end())
1580
                {
1581
                        std::list<TCallbackArgItem> &list = _DownCallbacks[serviceName];
1582
                        std::list<TCallbackArgItem>::iterator first(list.begin()), last(list.end());
1583
                        for (; first != last; ++first)
1584
                        {
1585
                                if (first->first == cb && first->second == arg)
1586
                                {
1587
                                        list.erase(first);
1588
                                        break;
1589
                                }
1590
                        }
1591

1592
                        if (first == last)
1593
                        {
1594
                                nlwarning("HNETL5 : can't remove service down callback, not found");
1595
                        }
1596

1597
                        if (list.empty())
1598
                        {
1599
                                // no more callback for this service
1600
                                _DownCallbacks.erase(serviceName);
1601
                        }
1602
                }
1603
                else
1604
                {
1605
                        nlwarning("HNETL5 : can't remove service down callback, not found");
1606
                }
1607
        }
1608
}
1609
//
1610
//
1611
//
1612

1613
uint64 CUnifiedNetwork::getBytesSent ()
1614
{
1615
        uint64        sent = 0;
1616
        uint        j;
1617

1618
        for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1619
        {
1620
                if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1621
                        for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1622
                                if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1623
                                        sent += _IdCnx[it->get()].Connections[j].CbNetBase->getBytesSent();
1624
        }
1625

1626
        if(_CbServer)
1627
                sent += _CbServer->getBytesSent();
1628
        return sent;
1629
}
1630

1631
uint64 CUnifiedNetwork::getBytesReceived ()
1632
{
1633
        uint64        received = 0;
1634
        uint        j;
1635

1636
        for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1637
        {
1638
                if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1639
                        for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1640
                                if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1641
                                        received += _IdCnx[it->get()].Connections[j].CbNetBase->getBytesReceived();
1642
        }
1643

1644
        if (_CbServer)
1645
                received += _CbServer->getBytesReceived();
1646
        return received;
1647
}
1648

1649
uint64 CUnifiedNetwork::getSendQueueSize ()
1650
{
1651
        uint64        sent = 0;
1652
        uint        j;
1653

1654
        for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1655
        {
1656
                if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1657
                        for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1658
                                if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1659
                                        sent += _IdCnx[it->get()].Connections[j].CbNetBase->getSendQueueSize();
1660
        }
1661

1662
        if (_CbServer)
1663
                sent += _CbServer->getSendQueueSize();
1664
        return sent;
1665
}
1666

1667
uint64 CUnifiedNetwork::getReceiveQueueSize ()
1668
{
1669
        uint64        received = 0;
1670
        uint        j;
1671

1672
        for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1673
        {
1674
                if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1675
                        for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1676
                                if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1677
                                        received += _IdCnx[it->get()].Connections[j].CbNetBase->getReceiveQueueSize();
1678
        }
1679

1680
        if (_CbServer)
1681
                received += _CbServer->getReceiveQueueSize();
1682
        return received;
1683
}
1684

1685
CCallbackNetBase        *CUnifiedNetwork::getNetBase(const std::string &name, TSockId &host, uint8 nid)
1686
{
1687
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::getNetBase() whereas it is not initialised yet"));
1688

1689
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1690

1691
        sint        count = (sint)_NamedCnx.count(name);
1692

1693
        if (count <= 0)
1694
        {
1695
                nlwarning ("HNETL5: couldn't access the service %s", name.c_str());
1696
                host = InvalidSockId;
1697
                return NULL;
1698
        }
1699
        else if (count > 1)
1700
        {
1701
                nlwarning ("HNETL5: %d services %s to getNetBase, returns the first valid", count, name.c_str());
1702
        }
1703

1704
        TNameMappedConnection::const_iterator        itnmc = _NamedCnx.find(name);
1705

1706
        uint8 connectionId = findConnectionId ((*itnmc).second, nid);
1707
        if (connectionId == 0xff)        // failed
1708
        {
1709
                nlwarning ("HNETL5: Can't getNetBase %s because no connection available", name.c_str());
1710
                host = InvalidSockId;
1711
                return NULL;
1712
        }
1713

1714
        host = _IdCnx[itnmc->second.get()].Connections[connectionId].HostId;
1715
        return _IdCnx[itnmc->second.get()].Connections[connectionId].CbNetBase;
1716
}
1717

1718
CCallbackNetBase        *CUnifiedNetwork::getNetBase(TServiceId sid, TSockId &host, uint8 nid)
1719
{
1720
        nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::getNetBase() whereas it is not initialised yet"));
1721

1722
        if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1723

1724
        if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1725
        {
1726
                nlwarning ("HNETL5: Can't get net base to the service '%hu' because not in _IdCnx", sid.get());
1727
                host = InvalidSockId;
1728
                return NULL;
1729
        }
1730

1731
        uint8 connectionId = findConnectionId (sid, nid);
1732
        if (connectionId == 0xff)        // failed
1733
        {
1734
                nlwarning ("HNETL5: Can't getNetBase %hu because no connection available", sid.get());
1735
                host = InvalidSockId;
1736
                return NULL;
1737
        }
1738

1739
        host = _IdCnx[sid.get()].Connections[connectionId].HostId;
1740
        return _IdCnx[sid.get()].Connections[connectionId].CbNetBase;
1741
}
1742

1743
TUnifiedMsgCallback CUnifiedNetwork::findCallback (const std::string &callbackName)
1744
{
1745
        TMsgMappedCallback::iterator        itcb = _Callbacks.find(callbackName);
1746
        if (itcb == _Callbacks.end())
1747
                return NULL;
1748
        else
1749
                return (*itcb).second;
1750
}
1751

1752
bool CUnifiedNetwork::isServiceLocal (const std::string &serviceName)
1753
{
1754
        // it s me, of course we are local
1755
        if (serviceName == _Name)
1756
                return true;
1757

1758
        pair<TNameMappedConnection::const_iterator,TNameMappedConnection::const_iterator>        range;
1759
        range = _NamedCnx.equal_range(serviceName);
1760

1761
        if (range.first != _NamedCnx.end())
1762
        {
1763
                TServiceId        sid = (*(range.first)).second;
1764
                return isServiceLocal (sid);
1765
        }
1766

1767
        return false;
1768
}
1769

1770
bool CUnifiedNetwork::isServiceLocal (TServiceId sid)
1771
{
1772
        // it s me, of course we are local
1773
        if (sid == _SId)
1774
                return true;
1775

1776
        if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1777
        {
1778
                return false;
1779
        }
1780

1781
        vector<CInetAddress> laddr = CInetAddress::localAddresses();
1782

1783
        for (uint i = 0; i < laddr.size(); i++)
1784
        {
1785
                for (uint j = 0; j < _IdCnx[sid.get()].ExtAddress.size(); j++)
1786
                {
1787
                        if (_IdCnx[sid.get()].ExtAddress[j].is127001 ())
1788
                                return true;
1789

1790
                        if (_IdCnx[sid.get()].ExtAddress[j].internalIPAddress () == laddr[i].internalIPAddress ())
1791
                                return true;
1792
                }
1793
        }
1794
        return false;
1795
}
1796

1797

1798
/*
1799
 * Return the name of the specified service, or "" if not found
1800
 */
1801
std::string                        CUnifiedNetwork::getServiceName(TServiceId sid)
1802
{
1803
        string s;
1804
        CUnifiedConnection *c = getUnifiedConnection(sid, false);
1805
        if (c)
1806
                s = c->ServiceName;
1807
        return s;
1808
}
1809

1810

1811
/*
1812
 * Return a string identifying the service, using the format "NAME-sid" (or "sid" only if not found)
1813
 */
1814
std::string                        CUnifiedNetwork::getServiceUnifiedName(TServiceId sid)
1815
{
1816
        string s;
1817
        CUnifiedConnection *c = getUnifiedConnection(sid, false);
1818
        if (c)
1819
                s = c->ServiceName + "-";
1820
        s += toString(sid.get());
1821
        return s;
1822
}
1823

1824

1825
//
1826
//
1827
//
1828

1829
//CUnifiedNetwork        *CUnifiedNetwork::_Instance = NULL;
1830
NLMISC_SAFE_SINGLETON_IMPL(CUnifiedNetwork);
1831

1832
bool CUnifiedNetwork::isUsed ()
1833
{
1834
        return (_Instance != NULL);
1835
}
1836

1837
//
1838
//
1839
//
1840

1841
CUnifiedNetwork::CUnifiedConnection        *CUnifiedNetwork::getUnifiedConnection (TServiceId sid, bool warn)
1842
{
1843
        if (sid.get() < _IdCnx.size () && _IdCnx[sid.get()].State == CUnifiedConnection::Ready)
1844
        {
1845
                if (sid != _IdCnx[sid.get()].ServiceId)
1846
                {
1847
                        AUTOCHECK_DISPLAY ("HNETL5: Sid index %hu is not the same that in the entry %hu", sid.get(), _IdCnx[sid.get()].ServiceId.get());
1848
                        return NULL;
1849
                }
1850
                return &_IdCnx[sid.get()];
1851
        }
1852
        else
1853
        {
1854
                if ( warn )
1855
                        nlwarning ("HNETL5: Try to get a bad unified connection (sid %hu is not in the table)", sid.get());
1856
                return NULL;
1857
        }
1858
}
1859

1860
void        CUnifiedNetwork::autoCheck()
1861
{
1862
        H_AUTO(L5UpdateAutoCheck);
1863
        uint i, j;
1864

1865
        for (i = 0; i < _IdCnx.size (); i++)
1866
        {
1867
                if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1868
                {
1869
                        _IdCnx[i].AutoCheck = 1;
1870
                }
1871
                else
1872
                {
1873
                        _IdCnx[i].AutoCheck = 0;
1874
                }
1875
        }
1876

1877
        TNameMappedConnection::iterator        itn;
1878
        for (itn = _NamedCnx.begin(); itn != _NamedCnx.end(); ++itn)
1879
        {
1880
                if ((*itn).first != _IdCnx[itn->second.get()].ServiceName)
1881
                        AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd'",
1882
                                                                        (*itn).first.c_str(),
1883
                                                                        _IdCnx[itn->second.get()].ServiceName.c_str (),
1884
                                                                        itn->second.get());
1885
                if (_IdCnx[itn->second.get()].AutoCheck == 0)
1886
                        AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd'",
1887
                                                                        (*itn).first.c_str(),
1888
                                                                        _IdCnx[itn->second.get()].ServiceName.c_str (),
1889
                                                                        itn->second.get());
1890
                if (_IdCnx[itn->second.get()].AutoCheck > 1)
1891
                        AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd' more than one entry is named with the same name",
1892
                                                                        (*itn).first.c_str(),
1893
                                                                        _IdCnx[itn->second.get()].ServiceName.c_str (),
1894
                                                                        itn->second.get());
1895
                _IdCnx[itn->second.get()].AutoCheck++;
1896
        }
1897

1898
        for (i = 0; i < _UsedConnection.size (); i++)
1899
        {
1900
                if (_IdCnx[_UsedConnection[i].get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1901
                        AUTOCHECK_DISPLAY ("HLNET5: problem with the _UsedConnection syncro sid %d is not used in _IdCnx", _UsedConnection[i].get());
1902
        }
1903

1904
        for (i = 0; i < _IdCnx.size (); i++)
1905
        {
1906
                if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1907
                {
1908
                        for (j = 0; j < _UsedConnection.size (); j++)
1909
                        {
1910
                                if (_UsedConnection[j].get() == i) break;
1911
                        }
1912
                        if (j == _UsedConnection.size ()) AUTOCHECK_DISPLAY ("HLNET5: problem with the _UsedConnection syncro sid %d is not in _UsedConnection", i);
1913
                }
1914
        }
1915

1916
        for (i = 0; i < _IdCnx.size (); i++)
1917
        {
1918
                if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
1919
                {
1920
                        if (_IdCnx[i].ServiceName != "DEAD") AUTOCHECK_DISPLAY ("HLNET5: sid %d name should be DEAD and is '%s'", i, _IdCnx[i].ServiceName.c_str ());
1921
                        if (_IdCnx[i].ServiceId.get() != 0xDEAD) AUTOCHECK_DISPLAY ("HLNET5: sid %d sid should be 0xDEAD and is 0x%X", i, _IdCnx[i].ServiceId.get());
1922
                        if (!_IdCnx[i].Connections.empty ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d connection size should be 0 and is %d", i, _IdCnx[i].Connections.size ());
1923
                        if (!_IdCnx[i].ExtAddress.empty ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d ext addr size should be 0 and is %d", i, _IdCnx[i].ExtAddress.size ());
1924
                        if (_IdCnx[i].AutoCheck != 0) AUTOCHECK_DISPLAY ("HLNET5: sid %d prob with syncro with _NamedCnx", i);
1925
                }
1926
                else if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1927
                {
1928
                        if (_IdCnx[i].ServiceId.get() != i) AUTOCHECK_DISPLAY ("HNETL5: Bad syncro sid index sid entry for %d %d", i, _IdCnx[i].ServiceId.get());
1929

1930
                        if (_IdCnx[i].ServiceName == "DEAD") AUTOCHECK_DISPLAY ("HLNET5: sid %d name should not be DEAD and is '%s'", i, _IdCnx[i].ServiceName.c_str ());
1931
                        if (_IdCnx[i].ServiceId.get() == 0xDEAD) AUTOCHECK_DISPLAY ("HLNET5: sid %d sid should not be 0xDEAD and is 0x%X", i, _IdCnx[i].ServiceId.get());
1932
                        if (!_IdCnx[i].ExtAddress.empty () && _IdCnx[i].Connections.size () > _IdCnx[i].ExtAddress.size()) AUTOCHECK_DISPLAY ("HLNET5: sid %d ext addr size should not be 0 and is %d", i, _IdCnx[i].ExtAddress.size ());
1933

1934
                        if (_IdCnx[i].AutoRetry == true && _IdCnx[i].Connections.size () > 1) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with more than one connection %d", i, _IdCnx[i].Connections.size ());
1935
                        if (_IdCnx[i].AutoRetry == true && _IdCnx[i].IsExternal == false) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with internal connection", i);
1936
                        if (_IdCnx[i].AutoRetry == true && _IdCnx[i].Connections[0].valid() == false) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with invalid connection", i);
1937

1938
                        for (j = 0; j < _IdCnx[i].Connections.size (); j++)
1939
                        {
1940
                                if (_IdCnx[i].Connections[j].valid() && !_IdCnx[i].Connections[j].IsServerConnection && _IdCnx[i].Connections[j].CbNetBase->connected () && _IdCnx[i].Connections[j].getAppId() != i) AUTOCHECK_DISPLAY ("HLNET5: sid %d bad appid %"NL_I64"X", i, _IdCnx[i].Connections[j].getAppId());
1941
                        }
1942

1943
                        for (j = 0; j < _IdCnx[i].NetworkConnectionAssociations.size (); j++)
1944
                        {
1945
                                if (_IdCnx[i].NetworkConnectionAssociations[j] != 0)
1946
                                {
1947
                                        if (_NetworkAssociations[j] != _IdCnx[i].ExtAddress[_IdCnx[i].NetworkConnectionAssociations[j]].internalNetAddress ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d nid %d have address 0x%08x and is not the good connection net 0x%08x", i, j, _NetworkAssociations[j], _IdCnx[i].ExtAddress[_IdCnx[i].NetworkConnectionAssociations[j]].internalNetAddress ());
1948
                                }
1949
                        }
1950
                }
1951
        }
1952
}
1953

1954

1955
void CUnifiedNetwork::displayInternalTables (NLMISC::CLog *log)
1956
{
1957
        uint i, j;
1958
        log->displayNL ("%d Named Connections:", _NamedCnx.size ());
1959
        for (TNameMappedConnection::iterator it = _NamedCnx.begin(); it != _NamedCnx.end (); it++)
1960
        {
1961
                log->displayNL ("> '%s' -> %hu", (*it).first.c_str(), it->second.get());
1962
        }
1963

1964
        uint nbused = 0;
1965
        for (i = 0; i < _IdCnx.size (); i++)
1966
        {
1967
                if(_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
1968
                        nbused++;
1969
        }
1970

1971
        log->displayNL ("%u/%u Unified Connections:", nbused, _IdCnx.size ());
1972
        for (i = 0; i < _IdCnx.size (); i++)
1973
        {
1974
                if(_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
1975
                {
1976
                        _IdCnx[i].display (false, log);
1977
                        for (j = 0; j < _IdCnx[i].NetworkConnectionAssociations.size (); j++)
1978
                        {
1979
                                log->displayNL ("     * nid %d -> cnxn %hu", j, (uint16)_IdCnx[i].NetworkConnectionAssociations[j]);
1980
                        }
1981
                }
1982
        }
1983

1984
        log->displayNL ("%u Used Unified Connections:", _UsedConnection.size());
1985
        for (i = 0; i < _UsedConnection.size (); i++)
1986
        {
1987
                log->displayNL ("> %hu", _UsedConnection[i].get());
1988
        }
1989

1990
        log->displayNL ("%u Network Associations:", _NetworkAssociations.size());
1991
        for (i = 0; i < _NetworkAssociations.size (); i++)
1992
        {
1993
                log->displayNL ("> 0x%08x -> '%s'", _NetworkAssociations[i], internalIPAddressToString (_NetworkAssociations[i]).c_str ());
1994
        }
1995
}
1996

1997
bool CUnifiedNetwork::haveNamedCnx (const std::string &name, TServiceId sid)
1998
{
1999
        CUnifiedNetwork::TNameMappedConnection::iterator                                                                                        it;
2000
        pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator>        range;
2001
        range = _NamedCnx.equal_range(name);
2002

2003
        if (range.first != range.second)
2004
        {
2005
                for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2006
                        ;
2007

2008
                return (it != range.second);
2009
        }
2010
        return false;
2011
}
2012

2013
void CUnifiedNetwork::addNamedCnx (const std::string &name, TServiceId sid)
2014
{
2015
        // check if not already inserted
2016
        CUnifiedNetwork::TNameMappedConnection::iterator                                                                                        it;
2017
        pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator>        range;
2018
        range = _NamedCnx.equal_range(name);
2019

2020
        if (range.first != range.second)
2021
        {
2022
                for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2023
                        ;
2024

2025
                if (it != range.second)
2026
                {
2027
                        AUTOCHECK_DISPLAY ("HNETL5: Try to add 2 times the same connection %s-%hu", name.c_str(), sid.get());
2028
                        return;
2029
                }
2030
        }
2031

2032
        // insert the name in the map to be able to send message with the name
2033
        _NamedCnx.insert(make_pair(name, sid));
2034
}
2035

2036
void CUnifiedNetwork::removeNamedCnx (const std::string &name, TServiceId sid)
2037
{
2038
        // get all map nodes of that service name
2039
        CUnifiedNetwork::TNameMappedConnection::iterator                                                                                        it;
2040
        pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator>        range;
2041
        range = _NamedCnx.equal_range(name);
2042

2043
        // assume not empty
2044
        if (range.first == range.second)
2045
        {
2046
                AUTOCHECK_DISPLAY ("HNETL5: The unified connection %s-%hu wasn't on the _NamedCnx", name.c_str(), sid.get());
2047
                return;
2048
        }
2049

2050
        // select good service id
2051
        for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2052
                ;
2053

2054
        // assume id exists
2055
        if (it == range.second)
2056
        {
2057
                AUTOCHECK_DISPLAY ("HNETL5: The unified connection %s-%hu wasn't on the _NamedCnx", name.c_str(), sid.get());
2058
                return;
2059
        }
2060

2061
        // remove service for map
2062
        _NamedCnx.erase(it);
2063
}
2064

2065
void CUnifiedNetwork::addNetworkAssociation (const string &networkName, uint8 nid)
2066
{
2067
        if (nid >= _NetworkAssociations.size ())
2068
                _NetworkAssociations.resize (nid+1, 0xFF);
2069

2070
        _NetworkAssociations[nid] = stringToInternalIPAddress (networkName);
2071
        nlinfo ("HNETL5: Associate network '%s' 0x%08x '%s' to nid %hu", networkName.c_str(), _NetworkAssociations[nid], internalIPAddressToString (_NetworkAssociations[nid]).c_str(), (uint16)nid);
2072
}
2073

2074
void CUnifiedNetwork::callServiceUpCallback (const std::string &serviceName, TServiceId sid, bool callGlobalCallback)
2075
{
2076
        // now we warn the user
2077
        CUnifiedNetwork::TNameMappedCallback::iterator        it = _UpCallbacks.find(serviceName);
2078
        if (it != _UpCallbacks.end())
2079
        {
2080
                // call it
2081
                for (list<TCallbackArgItem>::iterator it2 = (*it).second.begin(); it2 != (*it).second.end(); it2++)
2082
                {
2083
                        TUnifiedNetCallback        cb = (*it2).first;
2084
                        if (cb)
2085
                                cb(serviceName, sid, (*it2).second);
2086
                        else
2087
                                nlwarning ("HNETL5: User set an empty callback for '%s' service up", serviceName.c_str());
2088
                }
2089
        }
2090

2091
        if(callGlobalCallback)
2092
        {
2093
                for (uint c = 0; c < _UpUniCallback.size (); c++)
2094
                {
2095
                        if (_UpUniCallback[c].first != NULL)
2096
                                _UpUniCallback[c].first (serviceName, sid, _UpUniCallback[c].second);
2097
                        else
2098
                                nlwarning ("HNETL5: User set an empty callback for '*' service up");
2099
                }
2100
        }
2101
}
2102

2103
void CUnifiedNetwork::callServiceDownCallback (const std::string &serviceName, TServiceId sid, bool callGlobalCallback)
2104
{
2105
        // now we warn the user
2106
        CUnifiedNetwork::TNameMappedCallback::iterator        it = _DownCallbacks.find(serviceName);
2107
        if (it != _DownCallbacks.end())
2108
        {
2109
                // call it
2110
                for (list<TCallbackArgItem>::iterator it2 = (*it).second.begin(); it2 != (*it).second.end(); it2++)
2111
                {
2112
                        TUnifiedNetCallback        cb = (*it2).first;
2113
                        if (cb)
2114
                                cb(serviceName, sid, (*it2).second);
2115
                        else
2116
                                nlwarning ("HNETL5: User set an empty callback for '%s' service down", serviceName.c_str());
2117
                }
2118
        }
2119

2120
        if(callGlobalCallback)
2121
        {
2122
                for (uint c = 0; c < _DownUniCallback.size (); c++)
2123
                {
2124
                        if (_DownUniCallback[c].first != NULL)
2125
                                _DownUniCallback[c].first (serviceName, sid, _DownUniCallback[c].second);
2126
                        else
2127
                                nlwarning ("HNETL5: User set an empty callback for '*' service down");
2128
                }
2129
        }
2130
}
2131

2132
void CUnifiedNetwork::CUnifiedConnection::display (bool full, CLog *log)
2133
{
2134
        log->displayNL ("> %s-%hu %s %s %s (%d ExtAddr %d Cnx) TotalCb %d", ServiceName.c_str (), ServiceId.get(), IsExternal?"External":"NotExternal",
2135
                AutoRetry?"AutoRetry":"NoAutoRetry", SendId?"SendId":"NoSendId", ExtAddress.size (), Connections.size (), TotalCallbackCalled);
2136

2137
        uint maxc = (uint)std::max (ExtAddress.size (), Connections.size ());
2138

2139
        for (uint j = 0; j < maxc; j++)
2140
        {
2141
                string base;
2142
                if(j < ExtAddress.size ())
2143
                {
2144
                        base += ExtAddress[j].asString ();
2145
                }
2146
                else
2147
                {
2148
                        base += "NotValid";
2149
                }
2150

2151
                string ext;
2152
                if(j < Connections.size () && Connections[j].valid())
2153
                {
2154
                        if(Connections[j].IsServerConnection)
2155
                        {
2156
                                ext += "Server ";
2157
                        }
2158
                        else
2159
                        {
2160
                                ext += "Client ";
2161
                        }
2162
                        ext += Connections[j].CbNetBase->getSockId (Connections[j].HostId)->asString ();
2163
                        ext += " AppId:" + toString(Connections[j].getAppId());
2164
                        if (Connections[j].CbNetBase->connected ())
2165
                                ext += " Connected";
2166
                        else
2167
                                ext += " NotConnected";
2168
                }
2169
                else
2170
                {
2171
                        ext += "NotValid";
2172
                }
2173

2174
                log->displayNL ("  - %s %s", base.c_str (), ext.c_str ());
2175
                if(full)
2176
                {
2177
                        log->displayNL ("     * ReceiveQueueStat");
2178
                        Connections[j].CbNetBase->displayReceiveQueueStat(log);
2179
                        log->displayNL ("     * SendQueueStat");
2180
                        Connections[j].CbNetBase->displaySendQueueStat(log, Connections[j].HostId);
2181
                        log->displayNL ("     * ThreadStat");
2182
                        Connections[j].CbNetBase->displayThreadStat(log);
2183
                }
2184
        }
2185
}
2186

2187

2188
//
2189
// Commands
2190
//
2191

2192
bool createMessage (CMessage &msgout, const vector<string> &args, CLog &log)
2193
{
2194
        for (uint i = 2; i < args.size (); i+=2)
2195
        {
2196
                string type = args[i+0];
2197
                string value = args[i+1];
2198

2199
                         if (type == "s8")                        { sint8  v; fromString(value, v); msgout.serial (v); }
2200
                else if (type == "s16")                        { sint16 v; fromString(value, v); msgout.serial (v); }
2201
                else if (type == "s32")                        { sint32 v; fromString(value, v); msgout.serial (v); }
2202
                else if (type == "s64")                        { sint64 v; fromString(value, v); msgout.serial (v); }
2203
                else if (type == "u8")                        { uint8  v; fromString(value, v); msgout.serial (v); }
2204
                else if (type == "u16")                        { uint16 v; fromString(value, v); msgout.serial (v); }
2205
                else if (type == "u32")                        { uint32 v; fromString(value, v); msgout.serial (v); }
2206
                else if (type == "u64")                        { uint64 v; fromString(value, v); msgout.serial (v); }
2207
                else if (type == "f")                        { float  v; fromString(value, v); msgout.serial (v); }
2208
                else if (type == "d")                        { double v; fromString(value, v); msgout.serial (v); }
2209
                else if (type == "b")                        { bool   v; fromString(value, v); msgout.serial (v); }
2210
                else if (type == "s")                        { msgout.serial (value); }
2211
                else if (type == "e")                        { CEntityId e; e.fromString(value.c_str()); msgout.serial(e); }
2212
                else { log.displayNL ("type '%s' is not a valid type", type.c_str()); return false; }
2213
        }
2214
        return true;
2215
}
2216

2217

2218
//
2219
// Commands and Variables
2220
//
2221

2222
NLMISC_CATEGORISED_VARIABLE(nel, uint32, TotalCallbackCalled, "Total callback called number on layer 5");
2223

2224
NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, SendQueueSize, "current size in bytes of all send queues")
2225
{
2226
        nlunreferenced(human);
2227
        
2228
        if (get)
2229
        {
2230
                if (!CUnifiedNetwork::isUsed ())
2231
                        *pointer = 0;
2232
                else
2233
                        *pointer = CUnifiedNetwork::getInstance()->getSendQueueSize();
2234
        }
2235
}
2236

2237
NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, ReceiveQueueSize, "current size in bytes of all receive queues")
2238
{
2239
        nlunreferenced(human);
2240

2241
        if (get)
2242
        {
2243
                if (!CUnifiedNetwork::isUsed ())
2244
                        *pointer = 0;
2245
                else
2246
                        *pointer = CUnifiedNetwork::getInstance()->getReceiveQueueSize();
2247
        }
2248
}
2249

2250

2251
NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, ReceivedBytes, "total of bytes received by this service")
2252
{
2253
        nlunreferenced(human);
2254

2255
        if (get)
2256
        {
2257
                if (!CUnifiedNetwork::isUsed ())
2258
                        *pointer = 0;
2259
                else
2260
                        *pointer = CUnifiedNetwork::getInstance()->getBytesReceived ();
2261
        }
2262
}
2263

2264
NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, SentBytes, "total of bytes sent by this service")
2265
{
2266
        nlunreferenced(human);
2267

2268
        if (get)
2269
        {
2270
                if (!CUnifiedNetwork::isUsed ())
2271
                        *pointer = 0;
2272
                else
2273
                        *pointer = CUnifiedNetwork::getInstance()->getBytesSent ();
2274
        }
2275
}
2276

2277

2278
/*
2279
 * Simulate a message that comes from the network.
2280
 *
2281
 * for the bool (b type), you must set the value to 1 or 0
2282
 * for the string (s type), we don't manage space inside a string
2283
 * for stl containers, you have first to put a u32 type that is the size of the container and after all elements
2284
 * (ex: if you want to put a vector<uint16> that have 3 elements: u32 3 u16 10 u16 11 u16 12)
2285
 *
2286
 * ex: msgin 128 REGISTER u32 10 u32 541 u32 45
2287
 * You'll receive a fake message REGISTER that seems to come from the service number 128 with 3 uint32.
2288
 *
2289
 */
2290

2291
NLMISC_CATEGORISED_COMMAND(nel, msgin, "Simulate an input message from another service (ex: msgin 128 REGISTER u32 10 b 1 f 1.5)", "<ServiceName>|<ServiceId> <MessageName> [<ParamType> <Param>]*")
2292
{
2293
        nlunreferenced(rawCommandString);
2294
        nlunreferenced(quiet);
2295
        nlunreferenced(human);
2296

2297
        if(args.size() < 2) return false;
2298

2299
        if (!CUnifiedNetwork::isUsed ())
2300
        {
2301
                log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2302
                return false;
2303
        }
2304

2305
        uint16 sId;
2306
        fromString(args[0], sId);
2307

2308
        TServiceId serviceId(sId);
2309
        string serviceName = args[0].c_str();
2310
        string messageName = args[1].c_str();
2311

2312
        if (serviceId.get() > 255)
2313
        {
2314
                log.displayNL ("Service Id %d must be between [1;255]", serviceId.get());
2315
                return false;
2316
        }
2317

2318
        if ((args.size()-2) % 2 != 0)
2319
        {
2320
                log.displayNL ("The number of parameter must be a multiple of 2");
2321
                return false;
2322
        }
2323

2324
        CMessage msg (messageName);
2325

2326
        if (!createMessage (msg, args, log))
2327
                return false;
2328

2329
        msg.invert ();
2330

2331
        TUnifiedMsgCallback cb = CUnifiedNetwork::getInstance()->findCallback (messageName);
2332

2333
        if (cb == NULL)
2334
        {
2335
                log.displayNL ("Callback for message '%s' is not found", messageName.c_str());
2336
        }
2337
        else
2338
        {
2339
                cb (msg, serviceName, serviceId);
2340
        }
2341

2342
        return true;
2343
}
2344

2345
/*
2346
 * Create a message and send it to the specified service
2347
 *
2348
 * for the bool (b type), you must set the value to 1 or 0
2349
 * for the string (s type), we don't manage space inside a string
2350
 * for stl containers, you have first to put a u32 type that is the size of the container and after all elements
2351
 * (ex: if you want to put a vector<uint16> that have 3 elements: u32 3 u16 10 u16 11 u16 12)
2352
 *
2353
 * ex: msgout 128 REGISTER u32 10 u32 541 u32 45
2354
 * You'll send a real message REGISTER to the service number 128 with 3 uint32.
2355
 *
2356
 */
2357

2358
NLMISC_CATEGORISED_COMMAND(nel, msgout, "Send a message to a specified service (ex: msgout 128 REGISTER u32 10 b 1 f 1.5)", "<ServiceName>|<ServiceId> <MessageName> [<ParamType> <Param>]*")
2359
{
2360
        nlunreferenced(rawCommandString);
2361
        nlunreferenced(quiet);
2362
        nlunreferenced(human);
2363

2364
        if(args.size() < 2) return false;
2365

2366
        if (!CUnifiedNetwork::isUsed ())
2367
        {
2368
                log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2369
                return false;
2370
        }
2371

2372
        uint16 nId;
2373
        fromString(args[0], nId);
2374

2375
        TServiceId serviceId(nId);
2376
        string serviceName = args[0].c_str();
2377
        string messageName = args[1].c_str();
2378

2379
        if (serviceId.get() > 255)
2380
        {
2381
                log.displayNL ("Service Id %d must be between [1;255]", serviceId.get());
2382
                return false;
2383
        }
2384

2385
        if ((args.size()-2) % 2 != 0)
2386
        {
2387
                log.displayNL ("The number of parameter must be a multiple of 2");
2388
                return false;
2389
        }
2390

2391
        CMessage msg (messageName);
2392

2393
        if (!createMessage (msg, args, log))
2394
                return false;
2395

2396
        TSockId host = InvalidSockId;
2397
        CCallbackNetBase *cnb = NULL;
2398

2399
        if (serviceId.get() != 0)
2400
                cnb = CUnifiedNetwork::getInstance()->getNetBase (serviceId, host);
2401
        else
2402
                cnb = CUnifiedNetwork::getInstance()->getNetBase (serviceName, host);
2403

2404
        if (cnb == NULL)
2405
        {
2406
                log.displayNL ("'%s' is a bad <ServiceId> or <ServiceName>", args[0].c_str());
2407
                return false;
2408
        }
2409

2410
        cnb->send (msg, host);
2411

2412
        return true;
2413
}
2414

2415
NLMISC_CATEGORISED_COMMAND(nel, l5QueuesStats, "Displays queues stats of network layer5", "")
2416
{
2417
        nlunreferenced(rawCommandString);
2418
        nlunreferenced(quiet);
2419
        nlunreferenced(human);
2420

2421
        if(args.size() != 0) return false;
2422

2423
        if (!CUnifiedNetwork::isUsed ())
2424
        {
2425
                log.displayNL("Can't display internal table because layer5 is not used");
2426
                return false;
2427
        }
2428

2429
        log.displayNL ("%u Unified Connections:", CUnifiedNetwork::getInstance()->_IdCnx.size ());
2430
        for (uint i = 0; i < CUnifiedNetwork::getInstance()->_IdCnx.size (); i++)
2431
        {
2432
                if(CUnifiedNetwork::getInstance()->_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
2433
                {
2434
                        CUnifiedNetwork::getInstance()->_IdCnx[i].display (true, &log);
2435
                }
2436
        }
2437

2438
        return true;
2439
}
2440

2441

2442
NLMISC_CATEGORISED_COMMAND(nel, l5InternalTables, "Displays internal table of network layer5", "")
2443
{
2444
        nlunreferenced(rawCommandString);
2445
        nlunreferenced(quiet);
2446
        nlunreferenced(human);
2447

2448
        if(args.size() != 0) return false;
2449

2450
        if (!CUnifiedNetwork::isUsed ())
2451
        {
2452
                log.displayNL("Can't display internal table because layer5 is not used");
2453
                return false;
2454
        }
2455

2456
        CUnifiedNetwork::getInstance ()->displayInternalTables(&log);
2457

2458
        return true;
2459
}
2460

2461
NLMISC_CATEGORISED_COMMAND(nel, l5Callback, "Displays all callback registered in layer5", "")
2462
{
2463
        nlunreferenced(rawCommandString);
2464
        nlunreferenced(quiet);
2465
        nlunreferenced(human);
2466

2467
        if(args.size() != 0) return false;
2468

2469
        if (!CUnifiedNetwork::isUsed ())
2470
        {
2471
                log.displayNL("Can't display internal table because layer5 is not used");
2472
                return false;
2473
        }
2474

2475
        log.displayNL ("There're %d registered callbacks:", CUnifiedNetwork::getInstance()->_Callbacks.size());
2476
        uint i = 0;
2477
        for (CUnifiedNetwork::TMsgMappedCallback::iterator it = CUnifiedNetwork::getInstance()->_Callbacks.begin(); it != CUnifiedNetwork::getInstance()->_Callbacks.end(); it++)
2478
        {
2479
                log.displayNL (" %d '%s' %s", i++, (*it).first.c_str(), ((*it).second == NULL?"have a NULL address":""));
2480
        }
2481

2482
        return true;
2483
}
2484

2485
NLMISC_CATEGORISED_COMMAND(nel, isServiceLocal, "Says if a service is local or not compare with this service", "<sid>|<service name>")
2486
{
2487
        nlunreferenced(human);
2488
        nlunreferenced(quiet);
2489
        nlunreferenced(rawCommandString);
2490

2491
        if(args.size() != 1) return false;
2492

2493
        if (!CUnifiedNetwork::isUsed ())
2494
        {
2495
                log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2496
                return false;
2497
        }
2498

2499
        uint16 nId;
2500
        fromString(args[0], nId);
2501

2502
        TServiceId sid(nId);
2503
        if (sid.get() > 0)
2504
        {
2505
                log.displayNL ("Service %s-%hu and sid %s are %son the same computer", CUnifiedNetwork::getInstance ()->_Name.c_str(), CUnifiedNetwork::getInstance ()->_SId.get(), args[0].c_str(), CUnifiedNetwork::getInstance ()->isServiceLocal (sid)?"":"not ");
2506
        }
2507
        else
2508
        {
2509
                log.displayNL ("Service %s-%hu and %s are %son the same computer", CUnifiedNetwork::getInstance ()->_Name.c_str(), CUnifiedNetwork::getInstance ()->_SId.get(), args[0].c_str(), CUnifiedNetwork::getInstance ()->isServiceLocal (args[0])?"":"not ");
2510
        }
2511

2512
        return true;
2513
}
2514

2515
NLMISC_CLASS_COMMAND_IMPL(CUnifiedNetwork, addService)
2516
{
2517
        nlunreferenced(human);
2518
        nlunreferenced(quiet);
2519
        nlunreferenced(args);
2520

2521
        TParsedCommandLine pcl;
2522
        pcl.parseParamList(rawCommandString);
2523

2524
        if (pcl.SubParams.size() != 2)
2525
                return false;
2526

2527
        // syntax is as follow :
2528
        // <serviceName> ( address=<address:port> [sid=<serviceId>] [sendId] [external] [autoRetry] )
2529

2530
        TParsedCommandLine * serviceInfo = pcl.SubParams[1];
2531
        const TParsedCommandLine *address = serviceInfo->getParam("address");
2532
        if (address == NULL)
2533
        {
2534
                log.displayNL("Can't find param 'address'");
2535
                return false;
2536
        }
2537

2538
        CInetAddress ia(address->ParamValue);;
2539
        if (!ia.isValid())
2540
        {
2541
                log.displayNL("Can't parse internet address in '%s'", address->ParamValue.c_str());
2542
                return false;
2543
        }
2544

2545
        TServiceId serviceId(0);
2546
        const TParsedCommandLine *sid = serviceInfo->getParam("sid");
2547
        if (sid != NULL)
2548
        {
2549
                uint16 nId;
2550
                fromString(sid->ParamValue, nId);
2551
                serviceId.set(nId);
2552
        }
2553

2554
        bool sendId = serviceInfo->getParam("sendId") != NULL;
2555
        bool external = serviceInfo->getParam("external") != NULL;
2556
        bool autoRetry = serviceInfo->getParam("autoRetry") != NULL;
2557

2558
        log.displayNL("Adding service '%s' as sid %u with [sendId = %s], [external = %s], [autoRetry = %s]",
2559
                serviceInfo->ParamName.c_str(),
2560
                sid,
2561
                sendId ? "YES" : "NO",
2562
                external ? "YES" : "NO",
2563
                autoRetry ? "YES" : "NO"
2564
                );
2565

2566
        addService(serviceInfo->ParamName,
2567
                                ia,
2568
                                sendId,
2569
                                external,
2570
                                serviceId,
2571
                                autoRetry,
2572
                                false);
2573

2574
        return true;
2575

2576
}
2577

2578

2579
} // NLNET