ua_services_discovery.c 30 KB

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