fixed bugs in address list configuration and use
[public/netxms.git] / src / server / core / poll.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2015 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: poll.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Node poller queue (polls new nodes)
27 */
28 Queue g_nodePollerQueue;
29
30 /**
31 * Thread pool for pollers
32 */
33 ThreadPool *g_pollerThreadPool = NULL;
34
35 /**
36 * Active pollers
37 */
38 static HashMap<UINT64, PollerInfo> s_pollers(false);
39 static MUTEX s_pollerLock = MutexCreate();
40
41 /**
42 * Poller info destructor - will unregister poller and decrease ref count on object
43 */
44 PollerInfo::~PollerInfo()
45 {
46 MutexLock(s_pollerLock);
47 s_pollers.remove(CAST_FROM_POINTER(this, UINT64));
48 MutexUnlock(s_pollerLock);
49 m_object->decRefCount();
50 }
51
52 /**
53 * Register active poller
54 */
55 PollerInfo *RegisterPoller(PollerType type, NetObj *object)
56 {
57 PollerInfo *p = new PollerInfo(type, object);
58 object->incRefCount();
59 MutexLock(s_pollerLock);
60 s_pollers.set(CAST_FROM_POINTER(p, UINT64), p);
61 MutexUnlock(s_pollerLock);
62 return p;
63 }
64
65 /**
66 * Show poller information on console
67 */
68 static EnumerationCallbackResult ShowPollerInfo(const void *key, const void *object, void *arg)
69 {
70 static const TCHAR *pollerType[] = { _T("STAT"), _T("CONF"), _T("INST"), _T("ROUT"), _T("DISC"), _T("BSVC"), _T("COND"), _T("TOPO") };
71
72 PollerInfo *p = (PollerInfo *)object;
73 NetObj *o = p->getObject();
74
75 TCHAR name[32];
76 nx_strncpy(name, o->getName(), 31);
77 ConsolePrintf((CONSOLE_CTX)arg, _T("%s | %9d | %-30s | %s\n"), pollerType[p->getType()], o->getId(), name, p->getStatus());
78
79 return _CONTINUE;
80 }
81
82 /**
83 * Get poller diagnostic
84 */
85 void ShowPollers(CONSOLE_CTX console)
86 {
87 ConsoleWrite(console, _T("Type | Object ID | Object name | Status\n")
88 _T("-----+-----------+--------------------------------+--------------------------\n"));
89 MutexLock(s_pollerLock);
90 s_pollers.forEach(ShowPollerInfo, console);
91 MutexUnlock(s_pollerLock);
92 }
93
94 /**
95 * Helper for AddThreadPoolMonitoringParameters
96 */
97 inline TCHAR *BuildParamName(const TCHAR *format, const TCHAR *pool, TCHAR *buffer)
98 {
99 _sntprintf(buffer, 256, format, pool);
100 return buffer;
101 }
102
103 /**
104 * Create management node object
105 */
106 static void CreateManagementNode(const InetAddress& addr)
107 {
108 TCHAR buffer[256];
109
110 Node *node = new Node(addr, NF_IS_LOCAL_MGMT, 0, 0, 0);
111 NetObjInsert(node, true, false);
112 node->setName(GetLocalHostName(buffer, 256));
113
114 PollerInfo *p = RegisterPoller(POLLER_TYPE_CONFIGURATION, node);
115 p->startExecution();
116 node->configurationPoll(NULL, 0, p, addr.getMaskBits());
117 delete p;
118
119 node->unhide();
120 g_dwMgmtNode = node->getId(); // Set local management node ID
121 PostEvent(EVENT_NODE_ADDED, node->getId(), NULL);
122
123 // Bind to the root of service tree
124 g_pServiceRoot->AddChild(node);
125 node->AddParent(g_pServiceRoot);
126 }
127
128 /**
129 * Callback to clear incorrectly set local management node flag
130 */
131 static void CheckMgmtFlagCallback(NetObj *object, void *data)
132 {
133 if ((g_dwMgmtNode != object->getId()) && ((Node *)object)->isLocalManagement())
134 {
135 ((Node *)object)->clearLocalMgmtFlag();
136 DbgPrintf(2, _T("Incorrectly set flag NF_IS_LOCAL_MGMT cleared from node %s [%d]"),
137 object->getName(), object->getId());
138 }
139 }
140
141 /**
142 * Comparator to find management node object in existing nodes
143 */
144 static bool LocalMgmtNodeComparator(NetObj *object, void *data)
145 {
146 return ((Node *)object)->isLocalManagement();
147 }
148
149 /**
150 * Check if management server node presented in node list
151 */
152 void CheckForMgmtNode()
153 {
154 InterfaceList *pIfList;
155 Node *node;
156 int i;
157
158 pIfList = GetLocalInterfaceList();
159 if (pIfList != NULL)
160 {
161 for(i = 0; i < pIfList->size(); i++)
162 {
163 InterfaceInfo *iface = pIfList->get(i);
164 if (iface->type == IFTYPE_SOFTWARE_LOOPBACK)
165 continue;
166 if ((node = FindNodeByIP(0, &iface->ipAddrList)) != NULL)
167 {
168 // Check management node flag
169 if (!(node->getFlags() & NF_IS_LOCAL_MGMT))
170 {
171 node->setLocalMgmtFlag();
172 DbgPrintf(1, _T("Local management node %s [%d] was not have NF_IS_LOCAL_MGMT flag set"), node->getName(), node->getId());
173 }
174 g_dwMgmtNode = node->getId(); // Set local management node ID
175 break;
176 }
177 }
178 if (i == pIfList->size()) // No such node
179 {
180 // Find interface with IP address
181 for(i = 0; i < pIfList->size(); i++)
182 {
183 InterfaceInfo *iface = pIfList->get(i);
184 if ((iface->type == IFTYPE_SOFTWARE_LOOPBACK) || (iface->ipAddrList.size() == 0))
185 continue;
186
187 for(int j = 0; j < iface->ipAddrList.size(); j++)
188 {
189 const InetAddress& addr = iface->ipAddrList.get(j);
190 if (addr.isValidUnicast())
191 {
192 CreateManagementNode(addr);
193 i = pIfList->size(); // stop walking interface list
194 break;
195 }
196 }
197 }
198 }
199 delete pIfList;
200 }
201
202 if (g_dwMgmtNode != 0)
203 {
204 // Check that other nodes does not have NF_IS_LOCAL_MGMT flag set
205 g_idxNodeById.forEach(CheckMgmtFlagCallback, NULL);
206 }
207 else
208 {
209 // Management node cannot be found or created. This can happen
210 // if management node currently does not have IP addresses (for example,
211 // it's a Windows machine which is disconnected from the network).
212 // In this case, try to find any node with NF_IS_LOCAL_MGMT flag, or create
213 // new one without interfaces
214 NetObj *mgmtNode = g_idxNodeById.find(LocalMgmtNodeComparator, NULL);
215 if (mgmtNode != NULL)
216 {
217 g_dwMgmtNode = mgmtNode->getId();
218 }
219 else
220 {
221 CreateManagementNode(InetAddress());
222 }
223 }
224 }
225
226 /**
227 * Comparator for poller queue elements
228 */
229 static bool PollerQueueElementComparator(void *key, void *element)
230 {
231 return ((InetAddress *)key)->equals(((NEW_NODE *)element)->ipAddr);
232 }
233
234 /**
235 * Check potential new node from ARP cache or routing table
236 */
237 static void CheckPotentialNode(Node *node, const InetAddress& ipAddr, UINT32 ifIndex, BYTE *macAddr = NULL)
238 {
239 TCHAR buffer[64];
240
241 DbgPrintf(6, _T("DiscoveryPoller(): checking potential node %s at %d"), ipAddr.toString(buffer), ifIndex);
242 if (ipAddr.isValid() && !ipAddr.isBroadcast() && !ipAddr.isLoopback() && !ipAddr.isMulticast() &&
243 (FindNodeByIP(node->getZoneId(), ipAddr) == NULL) && !IsClusterIP(node->getZoneId(), ipAddr) &&
244 (g_nodePollerQueue.find((void *)&ipAddr, PollerQueueElementComparator) == NULL))
245 {
246 Interface *pInterface = node->findInterfaceByIndex(ifIndex);
247 if (pInterface != NULL)
248 {
249 const InetAddress& interfaceAddress = pInterface->getIpAddressList()->findSameSubnetAddress(ipAddr);
250 if (interfaceAddress.isValidUnicast())
251 {
252 DbgPrintf(6, _T("DiscoveryPoller(): interface found: %s [%d] addr=%s/%d ifIndex=%d"),
253 pInterface->getName(), pInterface->getId(), interfaceAddress.toString(buffer), interfaceAddress.getMaskBits(), pInterface->getIfIndex());
254 if (!ipAddr.isSubnetBroadcast(interfaceAddress.getMaskBits()))
255 {
256 NEW_NODE *pInfo;
257 TCHAR buffer[64];
258
259 pInfo = (NEW_NODE *)malloc(sizeof(NEW_NODE));
260 pInfo->ipAddr = ipAddr;
261 pInfo->ipAddr.setMaskBits(interfaceAddress.getMaskBits());
262 pInfo->zoneId = node->getZoneId();
263 pInfo->ignoreFilter = FALSE;
264 if (macAddr == NULL)
265 memset(pInfo->bMacAddr, 0, MAC_ADDR_LENGTH);
266 else
267 memcpy(pInfo->bMacAddr, macAddr, MAC_ADDR_LENGTH);
268 DbgPrintf(5, _T("DiscoveryPoller(): new node queued: %s/%d"),
269 pInfo->ipAddr.toString(buffer), pInfo->ipAddr.getMaskBits());
270 g_nodePollerQueue.put(pInfo);
271 }
272 else
273 {
274 DbgPrintf(6, _T("DiscoveryPoller(): potential node %s rejected - broadcast/multicast address"), ipAddr.toString(buffer));
275 }
276 }
277 else
278 {
279 DbgPrintf(6, _T("DiscoveryPoller(): interface object found but IP address not found"));
280 }
281 }
282 else
283 {
284 DbgPrintf(6, _T("DiscoveryPoller(): interface object not found"));
285 }
286 }
287 else
288 {
289 DbgPrintf(6, _T("DiscoveryPoller(): potential node %s rejected"), ipAddr.toString(buffer));
290 }
291 }
292
293 /**
294 * Check host route
295 * Host will be added if it is directly connected
296 */
297 static void CheckHostRoute(Node *node, ROUTE *route)
298 {
299 TCHAR buffer[16];
300 Interface *iface;
301
302 DbgPrintf(6, _T("DiscoveryPoller(): checking host route %s at %d"), IpToStr(route->dwDestAddr, buffer), route->dwIfIndex);
303 iface = node->findInterfaceByIndex(route->dwIfIndex);
304 if ((iface != NULL) && iface->getIpAddressList()->findSameSubnetAddress(route->dwDestAddr).isValidUnicast())
305 {
306 CheckPotentialNode(node, route->dwDestAddr, route->dwIfIndex);
307 }
308 else
309 {
310 DbgPrintf(6, _T("DiscoveryPoller(): interface object not found for host route"));
311 }
312 }
313
314 /**
315 * Discovery poller
316 */
317 static void DiscoveryPoller(void *arg)
318 {
319 PollerInfo *poller = (PollerInfo *)arg;
320 poller->startExecution();
321
322 if (IsShutdownInProgress())
323 {
324 delete poller;
325 return;
326 }
327
328 Node *node = (Node *)poller->getObject();
329 if (node->getRuntimeFlags() & NDF_DELETE_IN_PROGRESS)
330 {
331 node->setDiscoveryPollTimeStamp();
332 delete poller;
333 return;
334 }
335
336 DbgPrintf(4, _T("Starting discovery poll for node %s (%s) in zone %d"),
337 node->getName(), (const TCHAR *)node->getIpAddress().toString(), (int)node->getZoneId());
338
339 // Retrieve and analize node's ARP cache
340 ARP_CACHE *pArpCache = node->getArpCache();
341 if (pArpCache != NULL)
342 {
343 for(UINT32 i = 0; i < pArpCache->dwNumEntries; i++)
344 if (memcmp(pArpCache->pEntries[i].bMacAddr, "\xFF\xFF\xFF\xFF\xFF\xFF", 6)) // Ignore broadcast addresses
345 CheckPotentialNode(node, pArpCache->pEntries[i].ipAddr, pArpCache->pEntries[i].dwIndex, pArpCache->pEntries[i].bMacAddr);
346 DestroyArpCache(pArpCache);
347 }
348
349 // Retrieve and analize node's routing table
350 DbgPrintf(5, _T("Discovery poll for node %s (%s) - reading routing table"),
351 node->getName(), (const TCHAR *)node->getIpAddress().toString());
352 ROUTING_TABLE *rt = node->getRoutingTable();
353 if (rt != NULL)
354 {
355 for(int i = 0; i < rt->iNumEntries; i++)
356 {
357 CheckPotentialNode(node, rt->pRoutes[i].dwNextHop, rt->pRoutes[i].dwIfIndex);
358 if ((rt->pRoutes[i].dwDestMask == 0xFFFFFFFF) && (rt->pRoutes[i].dwDestAddr != 0))
359 CheckHostRoute(node, &rt->pRoutes[i]);
360 }
361 DestroyRoutingTable(rt);
362 }
363
364 DbgPrintf(4, _T("Finished discovery poll for node %s (%s)"),
365 node->getName(), (const TCHAR *)node->getIpAddress().toString());
366 node->setDiscoveryPollTimeStamp();
367 delete poller;
368 }
369
370 /**
371 * Check given address range with ICMP ping for new nodes
372 */
373 static void CheckRange(const InetAddressListElement& range)
374 {
375 if (range.getBaseAddress().getFamily() != AF_INET)
376 {
377 DbgPrintf(4, _T("Active discovery on range %s skipped - only IPv4 ranges supported"), (const TCHAR *)range.toString());
378 return;
379 }
380
381 UINT32 from = range.getBaseAddress().getAddressV4();
382 UINT32 to;
383 if (range.getType() == InetAddressListElement_SUBNET)
384 {
385 from++;
386 to = range.getBaseAddress().getSubnetBroadcast().getAddressV4() - 1;
387 }
388 else
389 {
390 to = range.getEndAddress().getAddressV4();
391 }
392
393 TCHAR ipAddr1[16], ipAddr2[16];
394 DbgPrintf(4, _T("Starting active discovery check on range %s - %s"), IpToStr(from, ipAddr1), IpToStr(to, ipAddr2));
395
396 for(UINT32 curr = from; (curr <= to) && !IsShutdownInProgress(); curr++)
397 {
398 InetAddress addr = InetAddress(curr);
399 if (IcmpPing(addr, 3, g_icmpPingTimeout, NULL, g_icmpPingSize) == ICMP_SUCCESS)
400 {
401 DbgPrintf(5, _T("Active discovery - node %s responds to ICMP ping"), addr.toString(ipAddr1));
402 if (FindNodeByIP(0, addr) == NULL)
403 {
404 Subnet *pSubnet;
405
406 pSubnet = FindSubnetForNode(0, addr);
407 if (pSubnet != NULL)
408 {
409 if (!pSubnet->getIpAddress().equals(addr) &&
410 !addr.isSubnetBroadcast(pSubnet->getIpAddress().getMaskBits()))
411 {
412 NEW_NODE *pInfo;
413
414 pInfo = (NEW_NODE *)malloc(sizeof(NEW_NODE));
415 pInfo->ipAddr = addr;
416 pInfo->ipAddr.setMaskBits(pSubnet->getIpAddress().getMaskBits());
417 pInfo->zoneId = 0; /* FIXME: add correct zone ID */
418 pInfo->ignoreFilter = FALSE;
419 memset(pInfo->bMacAddr, 0, MAC_ADDR_LENGTH);
420 g_nodePollerQueue.put(pInfo);
421 }
422 }
423 else
424 {
425 NEW_NODE *pInfo;
426
427 pInfo = (NEW_NODE *)malloc(sizeof(NEW_NODE));
428 pInfo->ipAddr = addr;
429 pInfo->zoneId = 0; /* FIXME: add correct zone ID */
430 pInfo->ignoreFilter = FALSE;
431 memset(pInfo->bMacAddr, 0, MAC_ADDR_LENGTH);
432 g_nodePollerQueue.put(pInfo);
433 }
434 }
435 }
436 }
437
438 DbgPrintf(4, _T("Finished active discovery check on range %s - %s"), IpToStr(from, ipAddr1), IpToStr(to, ipAddr2));
439 }
440
441 /**
442 * Active discovery poller thread
443 */
444 static THREAD_RESULT THREAD_CALL ActiveDiscoveryPoller(void *arg)
445 {
446 int nInterval = ConfigReadInt(_T("ActiveDiscoveryInterval"), 7200);
447
448 // Main loop
449 while(!IsShutdownInProgress())
450 {
451 if (SleepAndCheckForShutdown(nInterval))
452 break;
453
454 if (!(g_flags & AF_ACTIVE_NETWORK_DISCOVERY))
455 continue;
456
457 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
458 DB_RESULT hResult = DBSelect(hdb, _T("SELECT addr_type,addr1,addr2 FROM address_lists WHERE list_type=1"));
459 DBConnectionPoolReleaseConnection(hdb);
460 if (hResult != NULL)
461 {
462 int nRows = DBGetNumRows(hResult);
463 for(int i = 0; (i < nRows) && !IsShutdownInProgress(); i++)
464 {
465 CheckRange(InetAddressListElement(hResult, i));
466 }
467 DBFreeResult(hResult);
468 }
469 }
470 return THREAD_OK;
471 }
472
473 /**
474 * Callback for queueing objects for polling
475 */
476 static void QueueForPolling(NetObj *object, void *data)
477 {
478 if (IsShutdownInProgress())
479 return;
480
481 switch(object->getObjectClass())
482 {
483 case OBJECT_NODE:
484 {
485 Node *node = (Node *)object;
486 if (node->isReadyForConfigurationPoll())
487 {
488 node->lockForConfigurationPoll();
489 DbgPrintf(6, _T("Node %d \"%s\" queued for configuration poll"), (int)node->getId(), node->getName());
490 ThreadPoolExecute(g_pollerThreadPool, node, &Node::configurationPoll, RegisterPoller(POLLER_TYPE_CONFIGURATION, node));
491 }
492 if (node->isReadyForInstancePoll())
493 {
494 node->lockForInstancePoll();
495 DbgPrintf(6, _T("Node %d \"%s\" queued for instance discovery poll"), (int)node->getId(), node->getName());
496 ThreadPoolExecute(g_pollerThreadPool, node, &Node::instanceDiscoveryPoll, RegisterPoller(POLLER_TYPE_INSTANCE_DISCOVERY, node));
497 }
498 if (node->isReadyForStatusPoll())
499 {
500 node->lockForStatusPoll();
501 DbgPrintf(6, _T("Node %d \"%s\" queued for status poll"), (int)node->getId(), node->getName());
502 ThreadPoolExecute(g_pollerThreadPool, node, &Node::statusPoll, RegisterPoller(POLLER_TYPE_STATUS, node));
503 }
504 if (node->isReadyForRoutePoll())
505 {
506 node->lockForRoutePoll();
507 DbgPrintf(6, _T("Node %d \"%s\" queued for routing table poll"), (int)node->getId(), node->getName());
508 ThreadPoolExecute(g_pollerThreadPool, node, &Node::routingTablePoll, RegisterPoller(POLLER_TYPE_ROUTING_TABLE, node));
509 }
510 if (node->isReadyForDiscoveryPoll())
511 {
512 node->lockForDiscoveryPoll();
513 DbgPrintf(6, _T("Node %d \"%s\" queued for discovery poll"), (int)node->getId(), node->getName());
514 ThreadPoolExecute(g_pollerThreadPool, DiscoveryPoller, RegisterPoller(POLLER_TYPE_DISCOVERY, node));
515 }
516 if (node->isReadyForTopologyPoll())
517 {
518 node->lockForTopologyPoll();
519 DbgPrintf(6, _T("Node %d \"%s\" queued for topology poll"), (int)node->getId(), node->getName());
520 ThreadPoolExecute(g_pollerThreadPool, node, &Node::topologyPoll, RegisterPoller(POLLER_TYPE_TOPOLOGY, node));
521 }
522 }
523 break;
524 case OBJECT_CONDITION:
525 {
526 Condition *cond = (Condition *)object;
527 if (cond->isReadyForPoll())
528 {
529 cond->lockForPoll();
530 DbgPrintf(6, _T("Condition %d \"%s\" queued for poll"), (int)object->getId(), object->getName());
531 ThreadPoolExecute(g_pollerThreadPool, cond, &Condition::doPoll, RegisterPoller(POLLER_TYPE_CONDITION, cond));
532 }
533 }
534 break;
535 case OBJECT_CLUSTER:
536 {
537 Cluster *cluster = (Cluster *)object;
538 if (cluster->isReadyForStatusPoll())
539 {
540 cluster->lockForStatusPoll();
541 DbgPrintf(6, _T("Cluster %d \"%s\" queued for status poll"), (int)cluster->getId(), cluster->getName());
542 ThreadPoolExecute(g_pollerThreadPool, cluster, &Cluster::statusPoll, RegisterPoller(POLLER_TYPE_STATUS, cluster));
543 }
544 }
545 break;
546 case OBJECT_BUSINESSSERVICE:
547 {
548 BusinessService *service = (BusinessService *)object;
549 if (service->isReadyForPolling())
550 {
551 service->lockForPolling();
552 DbgPrintf(6, _T("Business service %d \"%s\" queued for poll"), (int)object->getId(), object->getName());
553 ThreadPoolExecute(g_pollerThreadPool, service, &BusinessService::poll, RegisterPoller(POLLER_TYPE_BUSINESS_SERVICE, service));
554 }
555 }
556 break;
557 default:
558 break;
559 }
560 }
561
562 /**
563 * Node and condition queuing thread
564 */
565 THREAD_RESULT THREAD_CALL PollManager(void *pArg)
566 {
567 g_pollerThreadPool = ThreadPoolCreate(ConfigReadInt(_T("PollerThreadPoolBaseSize"), 10), ConfigReadInt(_T("PollerThreadPoolMaxSize"), 250), _T("POLLERS"));
568
569 // Start active discovery poller
570 ThreadCreate(ActiveDiscoveryPoller, 0, NULL);
571
572 UINT32 watchdogId = WatchdogAddThread(_T("Poll Manager"), 60);
573 int counter = 0;
574
575 while(!IsShutdownInProgress())
576 {
577 if (SleepAndCheckForShutdown(5))
578 break; // Shutdown has arrived
579 WatchdogNotify(watchdogId);
580
581 // Check for management node every 10 minutes
582 counter++;
583 if (counter % 120 == 0)
584 {
585 counter = 0;
586 CheckForMgmtNode();
587 }
588
589 // Walk through objects and queue them for status
590 // and/or configuration poll
591 g_idxObjectById.forEach(QueueForPolling, NULL);
592 }
593
594 g_nodePollerQueue.clear();
595 g_nodePollerQueue.put(INVALID_POINTER_VALUE);
596
597 ThreadPoolDestroy(g_pollerThreadPool);
598 DbgPrintf(1, _T("PollManager: main thread terminated"));
599 return THREAD_OK;
600 }
601
602 /**
603 * Reset discovery poller after configuration change
604 */
605 void ResetDiscoveryPoller()
606 {
607 NEW_NODE *pInfo;
608
609 // Clear node poller queue
610 while((pInfo = (NEW_NODE *)g_nodePollerQueue.get()) != NULL)
611 {
612 if (pInfo != INVALID_POINTER_VALUE)
613 free(pInfo);
614 }
615
616 // Reload discovery parameters
617 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
618 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
619 g_flags |= AF_ENABLE_NETWORK_DISCOVERY;
620 else
621 g_flags &= ~AF_ENABLE_NETWORK_DISCOVERY;
622
623 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
624 g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
625 else
626 g_flags &= ~AF_ACTIVE_NETWORK_DISCOVERY;
627
628 if (ConfigReadInt(_T("UseSNMPTrapsForDiscovery"), 0))
629 g_flags |= AF_SNMP_TRAP_DISCOVERY;
630 else
631 g_flags &= ~AF_SNMP_TRAP_DISCOVERY;
632 }