Ver código fonte

Allow to register with multiple LDS at the same time (#1165)

Stefan Profanter 7 anos atrás
pai
commit
a1ac70b50a

+ 3 - 0
include/ua_server.h

@@ -535,6 +535,9 @@ UA_Server_unregister_discovery(UA_Server *server, const char* discoveryServerUrl
   * When you manually unregister the server, you also need to cancel the
   * periodic callback, otherwise it will be automatically be registered again.
   *
+  * If you call this method multiple times for the same discoveryServerUrl, the older
+  * periodic callback will be removed.
+  *
   * @param server
   * @param discoveryServerUrl if set to NULL, the default value
   *        'opc.tcp://localhost:4840' will be used

+ 7 - 3
src/server/ua_server.c

@@ -110,8 +110,12 @@ void UA_Server_delete(UA_Server *server) {
         UA_RegisteredServer_deleteMembers(&rs->registeredServer);
         UA_free(rs);
     }
-    if(server->periodicServerRegisterCallback)
-        UA_free(server->periodicServerRegisterCallback);
+    periodicServerRegisterCallback_entry *ps, *ps_tmp;
+    LIST_FOREACH_SAFE(ps, &server->periodicServerRegisterCallbacks, pointers, ps_tmp) {
+        LIST_REMOVE(ps, pointers);
+        UA_free(ps->callback);
+        UA_free(ps);
+    }
 
 # ifdef UA_ENABLE_DISCOVERY_MULTICAST
     if(server->config.applicationDescription.applicationType == UA_APPLICATIONTYPE_DISCOVERYSERVER)
@@ -216,7 +220,7 @@ UA_Server_new(const UA_ServerConfig *config) {
 #ifdef UA_ENABLE_DISCOVERY
     LIST_INIT(&server->registeredServers);
     server->registeredServersSize = 0;
-    server->periodicServerRegisterCallback = NULL;
+    LIST_INIT(&server->periodicServerRegisterCallbacks);
     server->registerServerCallback = NULL;
     server->registerServerCallbackData = NULL;
 #endif

+ 6 - 1
src/server/ua_server_internal.h

@@ -92,6 +92,11 @@ typedef struct serverOnNetwork_hash_entry {
 
 #endif /* UA_ENABLE_DISCOVERY_MULTICAST */
 
+typedef struct periodicServerRegisterCallback_entry {
+    LIST_ENTRY(periodicServerRegisterCallback_entry) pointers;
+    struct PeriodicServerRegisterCallback *callback;
+} periodicServerRegisterCallback_entry;
+
 #endif /* UA_ENABLE_DISCOVERY */
 
 struct UA_Server {
@@ -109,7 +114,7 @@ struct UA_Server {
     /* Discovery */
     LIST_HEAD(registeredServer_list, registeredServer_list_entry) registeredServers; // doubly-linked list of registered servers
     size_t registeredServersSize;
-    struct PeriodicServerRegisterCallback *periodicServerRegisterCallback;
+    LIST_HEAD(periodicServerRegisterCallback_list, periodicServerRegisterCallback_entry) periodicServerRegisterCallbacks; // doubly-linked list of current register callbacks
     UA_Server_registerServerCallback registerServerCallback;
     void* registerServerCallbackData;
 # ifdef UA_ENABLE_DISCOVERY_MULTICAST

+ 33 - 9
src/server/ua_services_discovery.c

@@ -654,12 +654,6 @@ UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
                                             UA_UInt32 intervalMs,
                                             UA_UInt32 delayFirstRegisterMs,
                                             UA_UInt64 *periodicCallbackId) {
-    /* There can be only one callback atm */
-    if(server->periodicServerRegisterCallback) {
-        UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
-                     "There is already a register callback in place");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
 
     /* No valid server URL */
     if(!discoveryServerUrl) {
@@ -668,13 +662,29 @@ UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
+
+    /* check if we are already registering with the given discovery url and remove the old periodic call */
+    {
+        periodicServerRegisterCallback_entry *rs, *rs_tmp;
+        LIST_FOREACH_SAFE(rs, &server->periodicServerRegisterCallbacks, pointers, rs_tmp) {
+            if (strcmp(rs->callback->discovery_server_url, discoveryServerUrl) == 0) {
+                UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                            "There is already a register callback for '%s' in place. Removing the older one.", discoveryServerUrl);
+                UA_Server_removeRepeatedCallback(server, rs->callback->id);
+                LIST_REMOVE(rs, pointers);
+                UA_free(rs->callback);
+                UA_free(rs);
+                break;
+            }
+        }
+    }
+
     /* Allocate and initialize */
     struct PeriodicServerRegisterCallback* cb =
         (struct PeriodicServerRegisterCallback*)
         UA_malloc(sizeof(struct PeriodicServerRegisterCallback));
     if(!cb)
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    server->periodicServerRegisterCallback = cb;
 
     /* Start repeating a failed register after 1s, then increase the delay. Set
      * to 500ms, as the delay is doubled before changing the callback
@@ -684,6 +694,8 @@ UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
     cb->registered = false;
     cb->discovery_server_url = discoveryServerUrl;
 
+
+
     /* Add the callback */
     UA_StatusCode retval =
         UA_Server_addRepeatedCallback(server, periodicServerRegister,
@@ -692,11 +704,23 @@ UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. "
                      "StatusCode %s", UA_StatusCode_name(retval));
-        UA_free(server->periodicServerRegisterCallback);
-        server->periodicServerRegisterCallback = NULL;
+        UA_free(cb);
         return retval;
     }
 
+#ifndef __clang_analyzer__
+    // the analyzer reports on LIST_INSERT_HEAD a use after free false positive
+    periodicServerRegisterCallback_entry *newEntry =
+            (periodicServerRegisterCallback_entry *)UA_malloc(sizeof(periodicServerRegisterCallback_entry));
+    if(!newEntry) {
+        UA_Server_removeRepeatedCallback(server, cb->id);
+        UA_free(cb);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+    newEntry->callback = cb;
+    LIST_INSERT_HEAD(&server->periodicServerRegisterCallbacks, newEntry, pointers);
+#endif
+
     if(periodicCallbackId)
         *periodicCallbackId = cb->id;
     return UA_STATUSCODE_GOOD;