ua_services_discovery.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  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 = setApplicationDescriptionFromServer(&foundServers[0], server);
  156. if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  157. UA_free(foundServers);
  158. if (foundServerFilteredPointer)
  159. UA_free(foundServerFilteredPointer);
  160. return;
  161. }
  162. }
  163. #ifdef UA_ENABLE_DISCOVERY
  164. size_t currentIndex = 0;
  165. if (addSelf)
  166. currentIndex++;
  167. // add all the registered servers to the list
  168. if (foundServerFilteredPointer) {
  169. // use filtered list because client only requested specific uris
  170. // -1 because foundServersSize also includes this self server
  171. size_t iterCount = addSelf ? foundServersSize - 1 : foundServersSize;
  172. for (size_t i = 0; i < iterCount; i++) {
  173. response->responseHeader.serviceResult =
  174. setApplicationDescriptionFromRegisteredServer(request, &foundServers[currentIndex++],
  175. foundServerFilteredPointer[i]);
  176. if (response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  177. UA_free(foundServers);
  178. UA_free(foundServerFilteredPointer);
  179. return;
  180. }
  181. }
  182. UA_free(foundServerFilteredPointer);
  183. foundServerFilteredPointer = NULL;
  184. } else {
  185. registeredServer_list_entry* current;
  186. LIST_FOREACH(current, &server->registeredServers, pointers) {
  187. response->responseHeader.serviceResult =
  188. setApplicationDescriptionFromRegisteredServer(request, &foundServers[currentIndex++],
  189. &current->registeredServer);
  190. if (response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  191. UA_free(foundServers);
  192. return;
  193. }
  194. }
  195. }
  196. #endif
  197. }
  198. if (foundServerFilteredPointer)
  199. UA_free(foundServerFilteredPointer);
  200. response->servers = foundServers;
  201. response->serversSize = foundServersSize;
  202. }
  203. void Service_GetEndpoints(UA_Server *server, UA_Session *session,
  204. const UA_GetEndpointsRequest *request,
  205. UA_GetEndpointsResponse *response) {
  206. /* If the client expects to see a specific endpointurl, mirror it back. If
  207. not, clone the endpoints with the discovery url of all networklayers. */
  208. const UA_String *endpointUrl = &request->endpointUrl;
  209. if(endpointUrl->length > 0) {
  210. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  211. "Processing GetEndpointsRequest with endpointUrl "
  212. UA_PRINTF_STRING_FORMAT, UA_PRINTF_STRING_DATA(*endpointUrl));
  213. } else {
  214. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  215. "Processing GetEndpointsRequest with an empty endpointUrl");
  216. }
  217. /* test if the supported binary profile shall be returned */
  218. size_t reSize = sizeof(UA_Boolean) * server->endpointDescriptionsSize;
  219. UA_Boolean *relevant_endpoints = (UA_Boolean *)UA_alloca(reSize);
  220. memset(relevant_endpoints, 0, sizeof(UA_Boolean) * server->endpointDescriptionsSize);
  221. size_t relevant_count = 0;
  222. if(request->profileUrisSize == 0) {
  223. for(size_t j = 0; j < server->endpointDescriptionsSize; ++j)
  224. relevant_endpoints[j] = true;
  225. relevant_count = server->endpointDescriptionsSize;
  226. } else {
  227. for(size_t j = 0; j < server->endpointDescriptionsSize; ++j) {
  228. for(size_t i = 0; i < request->profileUrisSize; ++i) {
  229. if(!UA_String_equal(&request->profileUris[i],
  230. &server->endpointDescriptions[j].transportProfileUri))
  231. continue;
  232. relevant_endpoints[j] = true;
  233. ++relevant_count;
  234. break;
  235. }
  236. }
  237. }
  238. if(relevant_count == 0) {
  239. response->endpointsSize = 0;
  240. return;
  241. }
  242. /* Clone the endpoint for each networklayer? */
  243. size_t clone_times = 1;
  244. UA_Boolean nl_endpointurl = false;
  245. if(endpointUrl->length == 0) {
  246. clone_times = server->config.networkLayersSize;
  247. nl_endpointurl = true;
  248. }
  249. response->endpoints =
  250. (UA_EndpointDescription*)UA_Array_new(relevant_count * clone_times,
  251. &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
  252. if(!response->endpoints) {
  253. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  254. return;
  255. }
  256. response->endpointsSize = relevant_count * clone_times;
  257. size_t k = 0;
  258. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  259. for(size_t i = 0; i < clone_times; ++i) {
  260. if(nl_endpointurl)
  261. endpointUrl = &server->config.networkLayers[i].discoveryUrl;
  262. for(size_t j = 0; j < server->endpointDescriptionsSize; ++j) {
  263. if(!relevant_endpoints[j])
  264. continue;
  265. retval |= UA_EndpointDescription_copy(&server->endpointDescriptions[j],
  266. &response->endpoints[k]);
  267. retval |= UA_String_copy(endpointUrl, &response->endpoints[k].endpointUrl);
  268. ++k;
  269. }
  270. }
  271. if(retval != UA_STATUSCODE_GOOD) {
  272. response->responseHeader.serviceResult = retval;
  273. UA_Array_delete(response->endpoints, response->endpointsSize,
  274. &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
  275. response->endpoints = NULL;
  276. response->endpointsSize = 0;
  277. return;
  278. }
  279. }
  280. #ifdef UA_ENABLE_DISCOVERY
  281. static void
  282. process_RegisterServer(UA_Server *server, UA_Session *session,
  283. const UA_RequestHeader* requestHeader,
  284. const UA_RegisteredServer *requestServer,
  285. const size_t requestDiscoveryConfigurationSize,
  286. const UA_ExtensionObject *requestDiscoveryConfiguration,
  287. UA_ResponseHeader* responseHeader,
  288. size_t *responseConfigurationResultsSize,
  289. UA_StatusCode **responseConfigurationResults,
  290. size_t *responseDiagnosticInfosSize,
  291. UA_DiagnosticInfo *responseDiagnosticInfos) {
  292. /* Find the server from the request in the registered list */
  293. registeredServer_list_entry* current;
  294. registeredServer_list_entry *registeredServer_entry = NULL;
  295. LIST_FOREACH(current, &server->registeredServers, pointers) {
  296. if (UA_String_equal(&current->registeredServer.serverUri, &requestServer->serverUri)) {
  297. registeredServer_entry = current;
  298. break;
  299. }
  300. }
  301. UA_MdnsDiscoveryConfiguration *mdnsConfig = NULL;
  302. const UA_String* mdnsServerName = NULL;
  303. if(requestDiscoveryConfigurationSize) {
  304. *responseConfigurationResultsSize = requestDiscoveryConfigurationSize;
  305. *responseConfigurationResults =
  306. (UA_StatusCode *)UA_Array_new(requestDiscoveryConfigurationSize,
  307. &UA_TYPES[UA_TYPES_STATUSCODE]);
  308. for(size_t i = 0; i < requestDiscoveryConfigurationSize; i++) {
  309. const UA_ExtensionObject *object = &requestDiscoveryConfiguration[i];
  310. if(!mdnsConfig && (object->encoding == UA_EXTENSIONOBJECT_DECODED ||
  311. object->encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
  312. (object->content.decoded.type == &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION])) {
  313. mdnsConfig = (UA_MdnsDiscoveryConfiguration *)object->content.decoded.data;
  314. mdnsServerName = &mdnsConfig->mdnsServerName;
  315. *responseConfigurationResults[i] = UA_STATUSCODE_GOOD;
  316. } else {
  317. *responseConfigurationResults[i] = UA_STATUSCODE_BADNOTSUPPORTED;
  318. }
  319. }
  320. }
  321. if(!mdnsServerName && requestServer->serverNamesSize)
  322. mdnsServerName = &requestServer->serverNames[0].text;
  323. if(!mdnsServerName) {
  324. responseHeader->serviceResult = UA_STATUSCODE_BADSERVERNAMEMISSING;
  325. return;
  326. }
  327. if(requestServer->discoveryUrlsSize == 0) {
  328. responseHeader->serviceResult = UA_STATUSCODE_BADDISCOVERYURLMISSING;
  329. return;
  330. }
  331. if(requestServer->semaphoreFilePath.length) {
  332. #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE
  333. // todo: malloc may fail: return a statuscode
  334. char* filePath = (char *)UA_malloc(sizeof(char)*requestServer->semaphoreFilePath.length+1);
  335. memcpy(filePath, requestServer->semaphoreFilePath.data, requestServer->semaphoreFilePath.length );
  336. filePath[requestServer->semaphoreFilePath.length] = '\0';
  337. if(access( filePath, 0 ) == -1) {
  338. responseHeader->serviceResult = UA_STATUSCODE_BADSEMPAHOREFILEMISSING;
  339. UA_free(filePath);
  340. return;
  341. }
  342. UA_free(filePath);
  343. #else
  344. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_CLIENT,
  345. "Ignoring semaphore file path. open62541 not compiled "
  346. "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON");
  347. #endif
  348. }
  349. #ifdef UA_ENABLE_DISCOVERY_MULTICAST
  350. if(server->config.applicationDescription.applicationType == UA_APPLICATIONTYPE_DISCOVERYSERVER) {
  351. // todo: malloc may fail: return a statuscode
  352. char* mdnsServer = (char *)UA_malloc(sizeof(char) * mdnsServerName->length + 1);
  353. memcpy(mdnsServer, mdnsServerName->data, mdnsServerName->length);
  354. mdnsServer[mdnsServerName->length] = '\0';
  355. for(size_t i = 0; i < requestServer->discoveryUrlsSize; i++) {
  356. /* create TXT if is online and first index, delete TXT if is offline and last index */
  357. UA_Boolean updateTxt = (requestServer->isOnline && i==0) ||
  358. (!requestServer->isOnline && i==requestServer->discoveryUrlsSize);
  359. UA_Discovery_update_MdnsForDiscoveryUrl(server, mdnsServer, mdnsConfig,
  360. requestServer->discoveryUrls[i],
  361. requestServer->isOnline, updateTxt);
  362. }
  363. UA_free(mdnsServer);
  364. }
  365. #endif
  366. if(!requestServer->isOnline) {
  367. // server is shutting down. Remove it from the registered servers list
  368. if(!registeredServer_entry) {
  369. // server not found, show warning
  370. UA_LOG_WARNING_SESSION(server->config.logger, session,
  371. "Could not unregister server %.*s. Not registered.",
  372. (int)requestServer->serverUri.length, requestServer->serverUri.data);
  373. responseHeader->serviceResult = UA_STATUSCODE_BADNOTFOUND;
  374. return;
  375. }
  376. if(server->registerServerCallback)
  377. server->registerServerCallback(requestServer, server->registerServerCallbackData);
  378. // server found, remove from list
  379. LIST_REMOVE(registeredServer_entry, pointers);
  380. UA_RegisteredServer_deleteMembers(&registeredServer_entry->registeredServer);
  381. #ifndef UA_ENABLE_MULTITHREADING
  382. UA_free(registeredServer_entry);
  383. server->registeredServersSize--;
  384. #else
  385. server->registeredServersSize = uatomic_add_return(&server->registeredServersSize, -1);
  386. UA_Server_delayedFree(server, registeredServer_entry);
  387. #endif
  388. responseHeader->serviceResult = UA_STATUSCODE_GOOD;
  389. return;
  390. }
  391. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  392. if (!registeredServer_entry) {
  393. // server not yet registered, register it by adding it to the list
  394. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Registering new server: %.*s",
  395. (int)requestServer->serverUri.length, requestServer->serverUri.data);
  396. registeredServer_entry =
  397. (registeredServer_list_entry *)UA_malloc(sizeof(registeredServer_list_entry));
  398. if(!registeredServer_entry) {
  399. responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  400. return;
  401. }
  402. LIST_INSERT_HEAD(&server->registeredServers, registeredServer_entry, pointers);
  403. #ifndef UA_ENABLE_MULTITHREADING
  404. server->registeredServersSize++;
  405. #else
  406. server->registeredServersSize = uatomic_add_return(&server->registeredServersSize, 1);
  407. #endif
  408. if(server->registerServerCallback)
  409. server->registerServerCallback(requestServer, server->registerServerCallbackData);
  410. } else {
  411. UA_RegisteredServer_deleteMembers(&registeredServer_entry->registeredServer);
  412. }
  413. // copy the data from the request into the list
  414. UA_RegisteredServer_copy(requestServer, &registeredServer_entry->registeredServer);
  415. registeredServer_entry->lastSeen = UA_DateTime_nowMonotonic();
  416. responseHeader->serviceResult = retval;
  417. }
  418. void Service_RegisterServer(UA_Server *server, UA_Session *session,
  419. const UA_RegisterServerRequest *request,
  420. UA_RegisterServerResponse *response) {
  421. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RegisterServerRequest");
  422. process_RegisterServer(server, session, &request->requestHeader, &request->server, 0,
  423. NULL, &response->responseHeader, 0, NULL, 0, NULL);
  424. }
  425. void Service_RegisterServer2(UA_Server *server, UA_Session *session,
  426. const UA_RegisterServer2Request *request,
  427. UA_RegisterServer2Response *response) {
  428. UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RegisterServer2Request");
  429. process_RegisterServer(server, session, &request->requestHeader, &request->server,
  430. request->discoveryConfigurationSize, request->discoveryConfiguration,
  431. &response->responseHeader, &response->configurationResultsSize,
  432. &response->configurationResults, &response->diagnosticInfosSize,
  433. response->diagnosticInfos);
  434. }
  435. /* Cleanup server registration: If the semaphore file path is set, then it just
  436. * checks the existence of the file. When it is deleted, the registration is
  437. * removed. If there is no semaphore file, then the registration will be removed
  438. * if it is older than 60 minutes. */
  439. void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) {
  440. UA_DateTime timedOut = nowMonotonic;
  441. // registration is timed out if lastSeen is older than 60 minutes (default
  442. // value, can be modified by user).
  443. if(server->config.discoveryCleanupTimeout)
  444. timedOut -= server->config.discoveryCleanupTimeout*UA_SEC_TO_DATETIME;
  445. registeredServer_list_entry* current, *temp;
  446. LIST_FOREACH_SAFE(current, &server->registeredServers, pointers, temp) {
  447. UA_Boolean semaphoreDeleted = UA_FALSE;
  448. #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE
  449. if(current->registeredServer.semaphoreFilePath.length) {
  450. size_t fpSize = sizeof(char)*current->registeredServer.semaphoreFilePath.length+1;
  451. // todo: malloc may fail: return a statuscode
  452. char* filePath = (char *)UA_malloc(fpSize);
  453. memcpy(filePath, current->registeredServer.semaphoreFilePath.data,
  454. current->registeredServer.semaphoreFilePath.length );
  455. filePath[current->registeredServer.semaphoreFilePath.length] = '\0';
  456. #ifdef UNDER_CE
  457. FILE *fp = fopen(filePath,"rb");
  458. semaphoreDeleted = (fp==NULL);
  459. if(fp)
  460. fclose(fp);
  461. #else
  462. semaphoreDeleted = access( filePath, 0 ) == -1;
  463. #endif
  464. UA_free(filePath);
  465. }
  466. #endif
  467. if(semaphoreDeleted || (server->config.discoveryCleanupTimeout &&
  468. current->lastSeen < timedOut)) {
  469. if(semaphoreDeleted) {
  470. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  471. "Registration of server with URI %.*s is removed because "
  472. "the semaphore file '%.*s' was deleted.",
  473. (int)current->registeredServer.serverUri.length,
  474. current->registeredServer.serverUri.data,
  475. (int)current->registeredServer.semaphoreFilePath.length,
  476. current->registeredServer.semaphoreFilePath.data);
  477. } else {
  478. // cppcheck-suppress unreadVariable
  479. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  480. "Registration of server with URI %.*s has timed out and is removed.",
  481. (int)current->registeredServer.serverUri.length,
  482. current->registeredServer.serverUri.data);
  483. }
  484. LIST_REMOVE(current, pointers);
  485. UA_RegisteredServer_deleteMembers(&current->registeredServer);
  486. #ifndef UA_ENABLE_MULTITHREADING
  487. UA_free(current);
  488. server->registeredServersSize--;
  489. #else
  490. server->registeredServersSize = uatomic_add_return(&server->registeredServersSize, -1);
  491. UA_Server_delayedFree(server, current);
  492. #endif
  493. }
  494. }
  495. }
  496. struct PeriodicServerRegisterJob {
  497. UA_UInt32 default_interval;
  498. UA_Guid job_id;
  499. UA_Job *job;
  500. UA_UInt32 this_interval;
  501. const char* discovery_server_url;
  502. };
  503. /* Called by the UA_Server job. The OPC UA specification says:
  504. *
  505. * > If an error occurs during registration (e.g. the Discovery Server is not running) then the Server
  506. * > must periodically re-attempt registration. The frequency of these attempts should start at 1 second
  507. * > but gradually increase until the registration frequency is the same as what it would be if not
  508. * > errors occurred. The recommended approach would double the period each attempt until reaching the maximum.
  509. *
  510. * We will do so by using the additional data parameter which holds information
  511. * if the next interval is default or if it is a repeaded call. */
  512. static void
  513. periodicServerRegister(UA_Server *server, void *data) {
  514. if(!data) {
  515. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  516. "Data parameter must be not NULL for periodic server register");
  517. return;
  518. }
  519. struct PeriodicServerRegisterJob *retryJob = (struct PeriodicServerRegisterJob *)data;
  520. if(retryJob->job != NULL) {
  521. // remove the retry job because we don't want to fire it again.
  522. UA_Server_removeRepeatedJob(server, retryJob->job_id);
  523. }
  524. // fixme: remove magic urls
  525. const char * server_url;
  526. if(retryJob->discovery_server_url != NULL)
  527. server_url = retryJob->discovery_server_url;
  528. else
  529. server_url = "opc.tcp://localhost:4840";
  530. UA_StatusCode retval = UA_Server_register_discovery(server, server_url, NULL);
  531. // You can also use a semaphore file. That file must exist. When the file is
  532. // deleted, the server is automatically unregistered. The semaphore file has
  533. // to be accessible by the discovery server
  534. //
  535. // UA_StatusCode retval = UA_Server_register_discovery(server,
  536. // "opc.tcp://localhost:4840", "/path/to/some/file");
  537. if(retval != UA_STATUSCODE_GOOD) {
  538. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  539. "Could not register server with discovery server. "
  540. "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval));
  541. // first retry in 1 second
  542. UA_UInt32 nextInterval = 1;
  543. if (retryJob->job != NULL)
  544. // double the interval for the next retry
  545. nextInterval = retryJob->this_interval*2;
  546. // as long as next retry is smaller than default interval, retry
  547. if (nextInterval < retryJob->default_interval) {
  548. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  549. "Retrying registration in %d seconds", nextInterval);
  550. // todo: malloc may fail: return a statuscode
  551. struct PeriodicServerRegisterJob *newRetryJob =
  552. (struct PeriodicServerRegisterJob *)UA_malloc(sizeof(struct PeriodicServerRegisterJob));
  553. newRetryJob->job = (UA_Job *)UA_malloc(sizeof(UA_Job));
  554. newRetryJob->default_interval = retryJob->default_interval;
  555. newRetryJob->this_interval = nextInterval;
  556. newRetryJob->discovery_server_url = retryJob->discovery_server_url;
  557. newRetryJob->job->type = UA_JOBTYPE_METHODCALL;
  558. newRetryJob->job->job.methodCall.method = periodicServerRegister;
  559. newRetryJob->job->job.methodCall.data = newRetryJob;
  560. UA_Server_addRepeatedJob(server, *newRetryJob->job,
  561. nextInterval*1000, &newRetryJob->job_id);
  562. }
  563. } else {
  564. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  565. "Server successfully registered. Next periodical register will be in %d seconds",
  566. (int)(retryJob->default_interval/1000));
  567. }
  568. if(retryJob->job) {
  569. UA_free(retryJob->job);
  570. UA_free(retryJob);
  571. }
  572. }
  573. UA_StatusCode
  574. UA_Server_addPeriodicServerRegisterJob(UA_Server *server,
  575. const char* discoveryServerUrl,
  576. const UA_UInt32 intervalMs,
  577. const UA_UInt32 delayFirstRegisterMs,
  578. UA_Guid* periodicJobId) {
  579. if(server->periodicServerRegisterJob != NULL)
  580. return UA_STATUSCODE_BADINTERNALERROR;
  581. // registering the server should be done periodically. Approx. every 10
  582. // minutes. The first call will be in 10 Minutes.
  583. UA_Job job;
  584. job.type = UA_JOBTYPE_METHODCALL;
  585. job.job.methodCall.method = periodicServerRegister;
  586. job.job.methodCall.data = NULL;
  587. server->periodicServerRegisterJob =
  588. (struct PeriodicServerRegisterJob *)UA_malloc(sizeof(struct PeriodicServerRegisterJob));
  589. server->periodicServerRegisterJob->job = NULL;
  590. server->periodicServerRegisterJob->this_interval = 0;
  591. server->periodicServerRegisterJob->default_interval = intervalMs;
  592. server->periodicServerRegisterJob->discovery_server_url = discoveryServerUrl;
  593. job.job.methodCall.data = server->periodicServerRegisterJob;
  594. UA_StatusCode retval =
  595. UA_Server_addRepeatedJob(server, job,
  596. intervalMs, &server->periodicServerRegisterJob->job_id);
  597. if(retval != UA_STATUSCODE_GOOD) {
  598. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  599. "Could not create periodic job for server register. "
  600. "StatusCode %s", UA_StatusCode_name(retval));
  601. return retval;
  602. }
  603. if(periodicJobId)
  604. UA_Guid_copy(&server->periodicServerRegisterJob->job_id, periodicJobId);
  605. if(delayFirstRegisterMs > 0) {
  606. // Register the server with the discovery server.
  607. // Delay this first registration until the server is fully initialized
  608. // will be freed in the callback
  609. // todo: malloc may fail: return a statuscode
  610. struct PeriodicServerRegisterJob *newRetryJob =
  611. (struct PeriodicServerRegisterJob *)UA_malloc(sizeof(struct PeriodicServerRegisterJob));
  612. newRetryJob->job = (UA_Job*)UA_malloc(sizeof(UA_Job));
  613. newRetryJob->this_interval = 1;
  614. newRetryJob->default_interval = intervalMs;
  615. newRetryJob->job->type = UA_JOBTYPE_METHODCALL;
  616. newRetryJob->job->job.methodCall.method = periodicServerRegister;
  617. newRetryJob->job->job.methodCall.data = newRetryJob;
  618. newRetryJob->discovery_server_url = discoveryServerUrl;
  619. retval = UA_Server_addRepeatedJob(server, *newRetryJob->job,
  620. delayFirstRegisterMs, &newRetryJob->job_id);
  621. if (retval != UA_STATUSCODE_GOOD) {
  622. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  623. "Could not create first job for server register. "
  624. "StatusCode %s", UA_StatusCode_name(retval));
  625. return retval;
  626. }
  627. }
  628. return UA_STATUSCODE_GOOD;
  629. }
  630. void
  631. UA_Server_setRegisterServerCallback(UA_Server *server, UA_Server_registerServerCallback cb,
  632. void* data) {
  633. server->registerServerCallback = cb;
  634. server->registerServerCallbackData = data;
  635. }
  636. #endif // UA_ENABLE_DISCOVERY