ua_services_discovery.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2014-2016 (c) Sten Grüner
  7. * Copyright 2014, 2017 (c) Florian Palm
  8. * Copyright 2016 (c) Oleksiy Vasylyev
  9. * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
  10. * Copyright 2017 (c) frax2222
  11. * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
  12. */
  13. #include "ua_server_internal.h"
  14. #include "ua_services.h"
  15. #ifdef UA_ENABLE_DISCOVERY
  16. #include "ua_client_internal.h"
  17. static UA_StatusCode
  18. setApplicationDescriptionFromRegisteredServer(const UA_FindServersRequest *request,
  19. UA_ApplicationDescription *target,
  20. const UA_RegisteredServer *registeredServer) {
  21. UA_ApplicationDescription_init(target);
  22. UA_StatusCode retval = UA_String_copy(&registeredServer->serverUri, &target->applicationUri);
  23. if(retval != UA_STATUSCODE_GOOD)
  24. return retval;
  25. retval = UA_String_copy(&registeredServer->productUri, &target->productUri);
  26. if(retval != UA_STATUSCODE_GOOD)
  27. return retval;
  28. // if the client requests a specific locale, select the corresponding server name
  29. if(request->localeIdsSize) {
  30. UA_Boolean appNameFound = false;
  31. for(size_t i =0; i<request->localeIdsSize && !appNameFound; i++) {
  32. for(size_t j =0; j<registeredServer->serverNamesSize; j++) {
  33. if(UA_String_equal(&request->localeIds[i], &registeredServer->serverNames[j].locale)) {
  34. retval = UA_LocalizedText_copy(&registeredServer->serverNames[j],
  35. &target->applicationName);
  36. if(retval != UA_STATUSCODE_GOOD)
  37. return retval;
  38. appNameFound = true;
  39. break;
  40. }
  41. }
  42. }
  43. // server does not have the requested local, therefore we can select the
  44. // most suitable one
  45. if(!appNameFound && registeredServer->serverNamesSize) {
  46. retval = UA_LocalizedText_copy(&registeredServer->serverNames[0],
  47. &target->applicationName);
  48. if(retval != UA_STATUSCODE_GOOD)
  49. return retval;
  50. }
  51. } else if(registeredServer->serverNamesSize) {
  52. // just take the first name
  53. retval = UA_LocalizedText_copy(&registeredServer->serverNames[0], &target->applicationName);
  54. if(retval != UA_STATUSCODE_GOOD)
  55. return retval;
  56. }
  57. target->applicationType = registeredServer->serverType;
  58. retval = UA_String_copy(&registeredServer->gatewayServerUri, &target->gatewayServerUri);
  59. if(retval != UA_STATUSCODE_GOOD)
  60. return retval;
  61. // TODO where do we get the discoveryProfileUri for application data?
  62. target->discoveryUrlsSize = registeredServer->discoveryUrlsSize;
  63. if(registeredServer->discoveryUrlsSize) {
  64. size_t duSize = sizeof(UA_String) * registeredServer->discoveryUrlsSize;
  65. target->discoveryUrls = (UA_String *)UA_malloc(duSize);
  66. if(!target->discoveryUrls)
  67. return UA_STATUSCODE_BADOUTOFMEMORY;
  68. for(size_t i = 0; i < registeredServer->discoveryUrlsSize; i++) {
  69. retval = UA_String_copy(&registeredServer->discoveryUrls[i], &target->discoveryUrls[i]);
  70. if(retval != UA_STATUSCODE_GOOD)
  71. return retval;
  72. }
  73. }
  74. return retval;
  75. }
  76. #endif
  77. static UA_StatusCode
  78. setApplicationDescriptionFromServer(UA_ApplicationDescription *target, const UA_Server *server) {
  79. /* Copy ApplicationDescription from the config */
  80. UA_StatusCode result = UA_ApplicationDescription_copy(&server->config.applicationDescription, target);
  81. if(result != UA_STATUSCODE_GOOD)
  82. return result;
  83. /* Add the discoveryUrls from the networklayers only if discoveryUrl
  84. * not already present and to avoid redundancy */
  85. if(!target->discoveryUrlsSize) {
  86. size_t discSize = sizeof(UA_String) * (target->discoveryUrlsSize + server->config.networkLayersSize);
  87. UA_String* disc = (UA_String *)UA_realloc(target->discoveryUrls, discSize);
  88. if(!disc)
  89. return UA_STATUSCODE_BADOUTOFMEMORY;
  90. size_t existing = target->discoveryUrlsSize;
  91. target->discoveryUrls = disc;
  92. target->discoveryUrlsSize += server->config.networkLayersSize;
  93. for(size_t i = 0; i < server->config.networkLayersSize; i++) {
  94. UA_ServerNetworkLayer* nl = &server->config.networkLayers[i];
  95. UA_String_copy(&nl->discoveryUrl, &target->discoveryUrls[existing + i]);
  96. }
  97. }
  98. return UA_STATUSCODE_GOOD;
  99. }
  100. void Service_FindServers(UA_Server *server, UA_Session *session,
  101. const UA_FindServersRequest *request,
  102. UA_FindServersResponse *response) {
  103. UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Processing FindServersRequest");
  104. /* Return the server itself? */
  105. UA_Boolean foundSelf = false;
  106. if(request->serverUrisSize) {
  107. for(size_t i = 0; i < request->serverUrisSize; i++) {
  108. if(UA_String_equal(&request->serverUris[i],
  109. &server->config.applicationDescription.applicationUri)) {
  110. foundSelf = true;
  111. break;
  112. }
  113. }
  114. } else {
  115. foundSelf = true;
  116. }
  117. #ifndef UA_ENABLE_DISCOVERY
  118. if(!foundSelf)
  119. return;
  120. UA_ApplicationDescription *ad = UA_ApplicationDescription_new();
  121. if(!ad) {
  122. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  123. return;
  124. }
  125. UA_StatusCode retval = setApplicationDescriptionFromServer(ad, server);
  126. if(retval != UA_STATUSCODE_GOOD) {
  127. UA_ApplicationDescription_delete(ad);
  128. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  129. return;
  130. }
  131. response->servers = ad;
  132. response->serversSize = 1;
  133. return;
  134. #else
  135. /* Temporarily store all the pointers which we found to avoid reiterating
  136. * through the list */
  137. size_t foundServersSize = 0;
  138. UA_STACKARRAY(UA_RegisteredServer*, foundServers, server->discoveryManager.registeredServersSize+1);
  139. registeredServer_list_entry* current;
  140. LIST_FOREACH(current, &server->discoveryManager.registeredServers, pointers) {
  141. if(request->serverUrisSize) {
  142. /* If client only requested a specific set of servers */
  143. for(size_t i = 0; i < request->serverUrisSize; i++) {
  144. if(UA_String_equal(&current->registeredServer.serverUri, &request->serverUris[i])) {
  145. foundServers[foundServersSize] = &current->registeredServer;
  146. foundServersSize++;
  147. break;
  148. }
  149. }
  150. } else {
  151. /* Return all registered servers */
  152. foundServers[foundServersSize] = &current->registeredServer;
  153. foundServersSize++;
  154. }
  155. }
  156. size_t allocSize = foundServersSize;
  157. if(foundSelf)
  158. allocSize++;
  159. /* Nothing to do? */
  160. if(allocSize == 0)
  161. return;
  162. /* Allocate memory */
  163. response->servers = (UA_ApplicationDescription*)UA_Array_new(allocSize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
  164. if(!response->servers) {
  165. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  166. return;
  167. }
  168. response->serversSize = allocSize;
  169. /* Copy into the response. TODO: Evaluate return codes */
  170. size_t pos = 0;
  171. if(foundSelf) {
  172. setApplicationDescriptionFromServer(&response->servers[pos++], server);
  173. }
  174. for(size_t i = 0; i < foundServersSize; i++) {
  175. setApplicationDescriptionFromRegisteredServer(request, &response->servers[pos++], foundServers[i]);
  176. }
  177. #endif
  178. }
  179. void
  180. Service_GetEndpoints(UA_Server *server, UA_Session *session,
  181. const UA_GetEndpointsRequest *request,
  182. UA_GetEndpointsResponse *response) {
  183. /* If the client expects to see a specific endpointurl, mirror it back. If
  184. not, clone the endpoints with the discovery url of all networklayers. */
  185. const UA_String *endpointUrl = &request->endpointUrl;
  186. if(endpointUrl->length > 0) {
  187. UA_LOG_DEBUG_SESSION(&server->config.logger, session,
  188. "Processing GetEndpointsRequest with endpointUrl "
  189. UA_PRINTF_STRING_FORMAT, UA_PRINTF_STRING_DATA(*endpointUrl));
  190. } else {
  191. UA_LOG_DEBUG_SESSION(&server->config.logger, session,
  192. "Processing GetEndpointsRequest with an empty endpointUrl");
  193. }
  194. /* test if the supported binary profile shall be returned */
  195. size_t reSize = sizeof(UA_Boolean) * server->config.endpointsSize;
  196. UA_STACKARRAY(UA_Boolean, relevant_endpoints, reSize);
  197. memset(relevant_endpoints, 0, reSize);
  198. size_t relevant_count = 0;
  199. if(request->profileUrisSize == 0) {
  200. for(size_t j = 0; j < server->config.endpointsSize; ++j)
  201. relevant_endpoints[j] = true;
  202. relevant_count = server->config.endpointsSize;
  203. } else {
  204. for(size_t j = 0; j < server->config.endpointsSize; ++j) {
  205. for(size_t i = 0; i < request->profileUrisSize; ++i) {
  206. if(!UA_String_equal(&request->profileUris[i],
  207. &server->config.endpoints[j].transportProfileUri))
  208. continue;
  209. relevant_endpoints[j] = true;
  210. ++relevant_count;
  211. break;
  212. }
  213. }
  214. }
  215. if(relevant_count == 0) {
  216. response->endpointsSize = 0;
  217. return;
  218. }
  219. /* Clone the endpoint for each networklayer? */
  220. size_t clone_times = 1;
  221. UA_Boolean nl_endpointurl = false;
  222. if(endpointUrl->length == 0) {
  223. clone_times = server->config.networkLayersSize;
  224. nl_endpointurl = true;
  225. }
  226. response->endpoints =
  227. (UA_EndpointDescription*)UA_Array_new(relevant_count * clone_times,
  228. &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
  229. if(!response->endpoints) {
  230. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  231. return;
  232. }
  233. response->endpointsSize = relevant_count * clone_times;
  234. size_t k = 0;
  235. UA_StatusCode retval;
  236. for(size_t i = 0; i < clone_times; ++i) {
  237. if(nl_endpointurl)
  238. endpointUrl = &server->config.networkLayers[i].discoveryUrl;
  239. for(size_t j = 0; j < server->config.endpointsSize; ++j) {
  240. if(!relevant_endpoints[j])
  241. continue;
  242. retval = UA_EndpointDescription_copy(&server->config.endpoints[j],
  243. &response->endpoints[k]);
  244. if(retval != UA_STATUSCODE_GOOD)
  245. goto error;
  246. retval = UA_String_copy(endpointUrl, &response->endpoints[k].endpointUrl);
  247. if(retval != UA_STATUSCODE_GOOD)
  248. goto error;
  249. ++k;
  250. }
  251. }
  252. return;
  253. error:
  254. response->responseHeader.serviceResult = retval;
  255. UA_Array_delete(response->endpoints, response->endpointsSize,
  256. &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
  257. response->endpoints = NULL;
  258. response->endpointsSize = 0;
  259. }
  260. #ifdef UA_ENABLE_DISCOVERY
  261. static void
  262. process_RegisterServer(UA_Server *server, UA_Session *session,
  263. const UA_RequestHeader* requestHeader,
  264. const UA_RegisteredServer *requestServer,
  265. const size_t requestDiscoveryConfigurationSize,
  266. const UA_ExtensionObject *requestDiscoveryConfiguration,
  267. UA_ResponseHeader* responseHeader,
  268. size_t *responseConfigurationResultsSize,
  269. UA_StatusCode **responseConfigurationResults,
  270. size_t *responseDiagnosticInfosSize,
  271. UA_DiagnosticInfo *responseDiagnosticInfos) {
  272. /* Find the server from the request in the registered list */
  273. registeredServer_list_entry* current;
  274. registeredServer_list_entry *registeredServer_entry = NULL;
  275. LIST_FOREACH(current, &server->discoveryManager.registeredServers, pointers) {
  276. if(UA_String_equal(&current->registeredServer.serverUri, &requestServer->serverUri)) {
  277. registeredServer_entry = current;
  278. break;
  279. }
  280. }
  281. UA_MdnsDiscoveryConfiguration *mdnsConfig = NULL;
  282. const UA_String* mdnsServerName = NULL;
  283. if(requestDiscoveryConfigurationSize) {
  284. *responseConfigurationResults =
  285. (UA_StatusCode *)UA_Array_new(requestDiscoveryConfigurationSize,
  286. &UA_TYPES[UA_TYPES_STATUSCODE]);
  287. if(!(*responseConfigurationResults)) {
  288. responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  289. return;
  290. }
  291. *responseConfigurationResultsSize = requestDiscoveryConfigurationSize;
  292. for(size_t i = 0; i < requestDiscoveryConfigurationSize; i++) {
  293. const UA_ExtensionObject *object = &requestDiscoveryConfiguration[i];
  294. if(!mdnsConfig && (object->encoding == UA_EXTENSIONOBJECT_DECODED ||
  295. object->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
  296. (object->content.decoded.type == &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION])) {
  297. mdnsConfig = (UA_MdnsDiscoveryConfiguration *)object->content.decoded.data;
  298. mdnsServerName = &mdnsConfig->mdnsServerName;
  299. (*responseConfigurationResults)[i] = UA_STATUSCODE_GOOD;
  300. } else {
  301. (*responseConfigurationResults)[i] = UA_STATUSCODE_BADNOTSUPPORTED;
  302. }
  303. }
  304. }
  305. if(!mdnsServerName && requestServer->serverNamesSize)
  306. mdnsServerName = &requestServer->serverNames[0].text;
  307. if(!mdnsServerName) {
  308. responseHeader->serviceResult = UA_STATUSCODE_BADSERVERNAMEMISSING;
  309. return;
  310. }
  311. if(requestServer->discoveryUrlsSize == 0) {
  312. responseHeader->serviceResult = UA_STATUSCODE_BADDISCOVERYURLMISSING;
  313. return;
  314. }
  315. if(requestServer->semaphoreFilePath.length) {
  316. #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE
  317. char* filePath = (char*)
  318. UA_malloc(sizeof(char)*requestServer->semaphoreFilePath.length+1);
  319. if(!filePath) {
  320. UA_LOG_ERROR_SESSION(&server->config.logger, session,
  321. "Cannot allocate memory for semaphore path. Out of memory.");
  322. responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  323. return;
  324. }
  325. memcpy(filePath, requestServer->semaphoreFilePath.data, requestServer->semaphoreFilePath.length );
  326. filePath[requestServer->semaphoreFilePath.length] = '\0';
  327. if(!UA_fileExists( filePath )) {
  328. responseHeader->serviceResult = UA_STATUSCODE_BADSEMPAHOREFILEMISSING;
  329. UA_free(filePath);
  330. return;
  331. }
  332. UA_free(filePath);
  333. #else
  334. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_CLIENT,
  335. "Ignoring semaphore file path. open62541 not compiled "
  336. "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON");
  337. #endif
  338. }
  339. #ifdef UA_ENABLE_DISCOVERY_MULTICAST
  340. if(server->config.discovery.mdnsEnable) {
  341. for(size_t i = 0; i < requestServer->discoveryUrlsSize; i++) {
  342. /* create TXT if is online and first index, delete TXT if is offline and last index */
  343. UA_Boolean updateTxt = (requestServer->isOnline && i==0) ||
  344. (!requestServer->isOnline && i==requestServer->discoveryUrlsSize);
  345. UA_Server_updateMdnsForDiscoveryUrl(server, mdnsServerName, mdnsConfig,
  346. &requestServer->discoveryUrls[i],
  347. requestServer->isOnline, updateTxt);
  348. }
  349. }
  350. #endif
  351. if(!requestServer->isOnline) {
  352. // server is shutting down. Remove it from the registered servers list
  353. if(!registeredServer_entry) {
  354. // server not found, show warning
  355. UA_LOG_WARNING_SESSION(&server->config.logger, session,
  356. "Could not unregister server %.*s. Not registered.",
  357. (int)requestServer->serverUri.length, requestServer->serverUri.data);
  358. responseHeader->serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  359. return;
  360. }
  361. if(server->discoveryManager.registerServerCallback)
  362. server->discoveryManager.
  363. registerServerCallback(requestServer,
  364. server->discoveryManager.registerServerCallbackData);
  365. // server found, remove from list
  366. LIST_REMOVE(registeredServer_entry, pointers);
  367. UA_RegisteredServer_deleteMembers(&registeredServer_entry->registeredServer);
  368. #ifndef UA_ENABLE_MULTITHREADING
  369. UA_free(registeredServer_entry);
  370. server->discoveryManager.registeredServersSize--;
  371. #else
  372. UA_atomic_subSize(&server->discoveryManager.registeredServersSize, 1);
  373. registeredServer_entry->delayedCleanup.callback = NULL; /* only free the structure */
  374. UA_WorkQueue_enqueueDelayed(&server->workQueue, &registeredServer_entry->delayedCleanup);
  375. #endif
  376. responseHeader->serviceResult = UA_STATUSCODE_GOOD;
  377. return;
  378. }
  379. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  380. if(!registeredServer_entry) {
  381. // server not yet registered, register it by adding it to the list
  382. UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Registering new server: %.*s",
  383. (int)requestServer->serverUri.length, requestServer->serverUri.data);
  384. registeredServer_entry =
  385. (registeredServer_list_entry *)UA_malloc(sizeof(registeredServer_list_entry));
  386. if(!registeredServer_entry) {
  387. responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  388. return;
  389. }
  390. LIST_INSERT_HEAD(&server->discoveryManager.registeredServers, registeredServer_entry, pointers);
  391. #ifndef UA_ENABLE_MULTITHREADING
  392. server->discoveryManager.registeredServersSize++;
  393. #else
  394. UA_atomic_addSize(&server->discoveryManager.registeredServersSize, 1);
  395. #endif
  396. } else {
  397. UA_RegisteredServer_deleteMembers(&registeredServer_entry->registeredServer);
  398. }
  399. // Always call the callback, if it is set.
  400. // Previously we only called it if it was a new register call. It may be the case that this endpoint
  401. // registered before, then crashed, restarts and registeres again. In that case the entry is not deleted
  402. // and the callback would not be called.
  403. if(server->discoveryManager.registerServerCallback)
  404. server->discoveryManager.
  405. registerServerCallback(requestServer,
  406. server->discoveryManager.registerServerCallbackData);
  407. // copy the data from the request into the list
  408. UA_RegisteredServer_copy(requestServer, &registeredServer_entry->registeredServer);
  409. registeredServer_entry->lastSeen = UA_DateTime_nowMonotonic();
  410. responseHeader->serviceResult = retval;
  411. }
  412. void Service_RegisterServer(UA_Server *server, UA_Session *session,
  413. const UA_RegisterServerRequest *request,
  414. UA_RegisterServerResponse *response) {
  415. UA_LOG_DEBUG_SESSION(&server->config.logger, session,
  416. "Processing RegisterServerRequest");
  417. process_RegisterServer(server, session, &request->requestHeader, &request->server, 0,
  418. NULL, &response->responseHeader, 0, NULL, 0, NULL);
  419. }
  420. void Service_RegisterServer2(UA_Server *server, UA_Session *session,
  421. const UA_RegisterServer2Request *request,
  422. UA_RegisterServer2Response *response) {
  423. UA_LOG_DEBUG_SESSION(&server->config.logger, session,
  424. "Processing RegisterServer2Request");
  425. process_RegisterServer(server, session, &request->requestHeader, &request->server,
  426. request->discoveryConfigurationSize, request->discoveryConfiguration,
  427. &response->responseHeader, &response->configurationResultsSize,
  428. &response->configurationResults, &response->diagnosticInfosSize,
  429. response->diagnosticInfos);
  430. }
  431. /* Cleanup server registration: If the semaphore file path is set, then it just
  432. * checks the existence of the file. When it is deleted, the registration is
  433. * removed. If there is no semaphore file, then the registration will be removed
  434. * if it is older than 60 minutes. */
  435. void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) {
  436. UA_DateTime timedOut = nowMonotonic;
  437. // registration is timed out if lastSeen is older than 60 minutes (default
  438. // value, can be modified by user).
  439. if(server->config.discovery.cleanupTimeout)
  440. timedOut -= server->config.discovery.cleanupTimeout*UA_DATETIME_SEC;
  441. registeredServer_list_entry* current, *temp;
  442. LIST_FOREACH_SAFE(current, &server->discoveryManager.registeredServers, pointers, temp) {
  443. UA_Boolean semaphoreDeleted = false;
  444. #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE
  445. if(current->registeredServer.semaphoreFilePath.length) {
  446. size_t fpSize = sizeof(char)*current->registeredServer.semaphoreFilePath.length+1;
  447. // todo: malloc may fail: return a statuscode
  448. char* filePath = (char *)UA_malloc(fpSize);
  449. if(filePath) {
  450. memcpy(filePath, current->registeredServer.semaphoreFilePath.data,
  451. current->registeredServer.semaphoreFilePath.length );
  452. filePath[current->registeredServer.semaphoreFilePath.length] = '\0';
  453. semaphoreDeleted = UA_fileExists(filePath) == false;
  454. UA_free(filePath);
  455. } else {
  456. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  457. "Cannot check registration semaphore. Out of memory");
  458. }
  459. }
  460. #endif
  461. if(semaphoreDeleted || (server->config.discovery.cleanupTimeout &&
  462. current->lastSeen < timedOut)) {
  463. if(semaphoreDeleted) {
  464. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  465. "Registration of server with URI %.*s is removed because "
  466. "the semaphore file '%.*s' was deleted.",
  467. (int)current->registeredServer.serverUri.length,
  468. current->registeredServer.serverUri.data,
  469. (int)current->registeredServer.semaphoreFilePath.length,
  470. current->registeredServer.semaphoreFilePath.data);
  471. } else {
  472. // cppcheck-suppress unreadVariable
  473. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  474. "Registration of server with URI %.*s has timed out and is removed.",
  475. (int)current->registeredServer.serverUri.length,
  476. current->registeredServer.serverUri.data);
  477. }
  478. LIST_REMOVE(current, pointers);
  479. UA_RegisteredServer_deleteMembers(&current->registeredServer);
  480. #ifndef UA_ENABLE_MULTITHREADING
  481. UA_free(current);
  482. server->discoveryManager.registeredServersSize--;
  483. #else
  484. UA_atomic_subSize(&server->discoveryManager.registeredServersSize, 1);
  485. current->delayedCleanup.callback = NULL; /* Only free the structure */
  486. UA_WorkQueue_enqueueDelayed(&server->workQueue, &current->delayedCleanup);
  487. #endif
  488. }
  489. }
  490. }
  491. /* Called by the UA_Server callback. The OPC UA specification says:
  492. *
  493. * > If an error occurs during registration (e.g. the Discovery Server is not running) then the Server
  494. * > must periodically re-attempt registration. The frequency of these attempts should start at 1 second
  495. * > but gradually increase until the registration frequency is the same as what it would be if not
  496. * > errors occurred. The recommended approach would double the period each attempt until reaching the maximum.
  497. *
  498. * We will do so by using the additional data parameter which holds information
  499. * if the next interval is default or if it is a repeated call. */
  500. static void
  501. periodicServerRegister(UA_Server *server, void *data) {
  502. UA_assert(data != NULL);
  503. struct PeriodicServerRegisterCallback *cb = (struct PeriodicServerRegisterCallback *)data;
  504. UA_StatusCode retval = UA_Client_connect_noSession(cb->client, cb->discovery_server_url);
  505. if (retval == UA_STATUSCODE_GOOD) {
  506. /* Register
  507. You can also use a semaphore file. That file must exist. When the file is
  508. deleted, the server is automatically unregistered. The semaphore file has
  509. to be accessible by the discovery server
  510. UA_StatusCode retval = UA_Server_register_discovery(server,
  511. "opc.tcp://localhost:4840", "/path/to/some/file");
  512. */
  513. retval = UA_Server_register_discovery(server, cb->client, NULL);
  514. }
  515. if (cb->client->state == UA_CLIENTSTATE_CONNECTED) {
  516. UA_StatusCode retval1 = UA_Client_disconnect(cb->client);
  517. if(retval1 != UA_STATUSCODE_GOOD) {
  518. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  519. "Could not disconnect client from register server. StatusCode %s",
  520. UA_StatusCode_name(retval));
  521. }
  522. }
  523. /* Registering failed */
  524. if(retval != UA_STATUSCODE_GOOD) {
  525. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  526. "Could not register server with discovery server. "
  527. "Is the discovery server started? StatusCode %s",
  528. UA_StatusCode_name(retval));
  529. /* If the server was previously registered, retry in one second,
  530. * else, double the previous interval */
  531. UA_Double nextInterval = 1000.0;
  532. if(!cb->registered)
  533. nextInterval = cb->this_interval * 2;
  534. /* The interval should be smaller than the default interval */
  535. if(nextInterval > cb->default_interval)
  536. nextInterval = cb->default_interval;
  537. cb->this_interval = nextInterval;
  538. UA_Server_changeRepeatedCallbackInterval(server, cb->id, nextInterval);
  539. return;
  540. }
  541. /* Registering succeeded */
  542. UA_LOG_DEBUG(&server->config.logger, UA_LOGCATEGORY_SERVER,
  543. "Server successfully registered. Next periodical register will be in %d seconds",
  544. (int)(cb->default_interval/1000));
  545. if(!cb->registered) {
  546. retval = UA_Server_changeRepeatedCallbackInterval(server, cb->id, cb->default_interval);
  547. /* If changing the interval fails, try again after the next registering */
  548. if(retval == UA_STATUSCODE_GOOD)
  549. cb->registered = true;
  550. }
  551. }
  552. UA_StatusCode
  553. UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
  554. struct UA_Client *client,
  555. const char* discoveryServerUrl,
  556. UA_Double intervalMs,
  557. UA_Double delayFirstRegisterMs,
  558. UA_UInt64 *periodicCallbackId) {
  559. /* No valid server URL */
  560. if(!discoveryServerUrl) {
  561. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  562. "No discovery server URL provided");
  563. return UA_STATUSCODE_BADINTERNALERROR;
  564. }
  565. if (client->connection.state != UA_CONNECTION_CLOSED)
  566. return UA_STATUSCODE_BADINVALIDSTATE;
  567. /* check if we are already registering with the given discovery url and remove the old periodic call */
  568. {
  569. periodicServerRegisterCallback_entry *rs, *rs_tmp;
  570. LIST_FOREACH_SAFE(rs, &server->discoveryManager.
  571. periodicServerRegisterCallbacks, pointers, rs_tmp) {
  572. if(strcmp(rs->callback->discovery_server_url, discoveryServerUrl) == 0) {
  573. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  574. "There is already a register callback for '%s' in place. Removing the older one.", discoveryServerUrl);
  575. UA_Server_removeRepeatedCallback(server, rs->callback->id);
  576. LIST_REMOVE(rs, pointers);
  577. UA_free(rs->callback->discovery_server_url);
  578. UA_free(rs->callback);
  579. UA_free(rs);
  580. break;
  581. }
  582. }
  583. }
  584. /* Allocate and initialize */
  585. struct PeriodicServerRegisterCallback* cb =
  586. (struct PeriodicServerRegisterCallback*)
  587. UA_malloc(sizeof(struct PeriodicServerRegisterCallback));
  588. if(!cb)
  589. return UA_STATUSCODE_BADOUTOFMEMORY;
  590. /* Start repeating a failed register after 1s, then increase the delay. Set
  591. * to 500ms, as the delay is doubled before changing the callback
  592. * interval.*/
  593. cb->this_interval = 500.0;
  594. cb->default_interval = intervalMs;
  595. cb->registered = false;
  596. cb->client = client;
  597. size_t len = strlen(discoveryServerUrl);
  598. cb->discovery_server_url = (char*)UA_malloc(len+1);
  599. if (!cb->discovery_server_url) {
  600. UA_free(cb);
  601. return UA_STATUSCODE_BADOUTOFMEMORY;
  602. }
  603. memcpy(cb->discovery_server_url, discoveryServerUrl, len+1);
  604. /* Add the callback */
  605. UA_StatusCode retval =
  606. UA_Server_addRepeatedCallback(server, periodicServerRegister,
  607. cb, delayFirstRegisterMs, &cb->id);
  608. if(retval != UA_STATUSCODE_GOOD) {
  609. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  610. "Could not create periodic job for server register. "
  611. "StatusCode %s", UA_StatusCode_name(retval));
  612. UA_free(cb);
  613. return retval;
  614. }
  615. #ifndef __clang_analyzer__
  616. // the analyzer reports on LIST_INSERT_HEAD a use after free false positive
  617. periodicServerRegisterCallback_entry *newEntry =
  618. (periodicServerRegisterCallback_entry *)UA_malloc(sizeof(periodicServerRegisterCallback_entry));
  619. if(!newEntry) {
  620. UA_Server_removeRepeatedCallback(server, cb->id);
  621. UA_free(cb);
  622. return UA_STATUSCODE_BADOUTOFMEMORY;
  623. }
  624. newEntry->callback = cb;
  625. LIST_INSERT_HEAD(&server->discoveryManager.periodicServerRegisterCallbacks, newEntry, pointers);
  626. #endif
  627. if(periodicCallbackId)
  628. *periodicCallbackId = cb->id;
  629. return UA_STATUSCODE_GOOD;
  630. }
  631. void
  632. UA_Server_setRegisterServerCallback(UA_Server *server,
  633. UA_Server_registerServerCallback cb,
  634. void* data) {
  635. server->discoveryManager.registerServerCallback = cb;
  636. server->discoveryManager.registerServerCallbackData = data;
  637. }
  638. #endif /* UA_ENABLE_DISCOVERY */