Browse Source

Merge remote-tracking branch 'upstream/master' into test/fuzz

Stefan Profanter 7 years ago
parent
commit
15593d142a
48 changed files with 965 additions and 932 deletions
  1. 19 4
      CMakeLists.txt
  2. 1 1
      deps/mdnsd
  3. 2 2
      deps/pcg_basic.h
  4. 8 3
      examples/CMakeLists.txt
  5. 1 1
      examples/discovery/server_multicast.c
  6. 4 1
      examples/server_nodeset.c
  7. 1 0
      examples/tutorial_server_variabletype.c
  8. 2 2
      include/ua_server_deprecated.h
  9. 1 1
      plugins/ua_network_tcp.c
  10. 2 0
      src/server/ua_mdns.c
  11. 12 12
      src/server/ua_nodestore_concurrent.c
  12. 2 2
      src/server/ua_securechannel_manager.c
  13. 10 12
      src/server/ua_server_binary.c
  14. 17 13
      src/server/ua_server_internal.h
  15. 29 0
      src/server/ua_server_utils.c
  16. 3 3
      src/server/ua_server_worker.c
  17. 0 4
      src/server/ua_services.h
  18. 94 78
      src/server/ua_services_attribute.c
  19. 45 47
      src/server/ua_services_call.c
  20. 8 4
      src/server/ua_services_discovery.c
  21. 10 10
      src/server/ua_services_discovery_multicast.c
  22. 85 87
      src/server/ua_services_nodemanagement.c
  23. 2 1
      src/server/ua_services_securechannel.c
  24. 8 4
      src/server/ua_services_session.c
  25. 173 173
      src/server/ua_services_subscription.c
  26. 31 48
      src/server/ua_services_view.c
  27. 2 2
      src/server/ua_session_manager.c
  28. 30 290
      src/server/ua_subscription.c
  29. 1 3
      src/server/ua_subscription.h
  30. 265 0
      src/server/ua_subscription_datachange.c
  31. 11 7
      src/ua_securechannel.h
  32. 10 7
      src/ua_session.h
  33. 2 2
      src/ua_types.c
  34. 6 36
      src/ua_types_encoding_binary.c
  35. 6 7
      tests/check_chunking.c
  36. 1 1
      tests/check_client.c
  37. 27 22
      tests/check_discovery.c
  38. 5 9
      tests/check_nodestore.c
  39. 1 1
      tests/check_server_binary_messages.c
  40. 1 1
      tests/check_server_userspace.c
  41. 3 3
      tests/check_services_attributes.c
  42. 1 1
      tests/check_services_view.c
  43. 11 11
      tests/check_types_builtin.c
  44. 1 1
      tests/check_types_custom.c
  45. 1 1
      tests/check_types_range.c
  46. 1 1
      tests/testing_networklayers.c
  47. 7 11
      tools/pyUANamespace/ua_namespace.py
  48. 2 2
      tools/pyUANamespace/ua_node_types.py

+ 19 - 4
CMakeLists.txt

@@ -162,14 +162,28 @@ endif(UA_ENABLE_MULTITHREADING)
 
 if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
     # Compiler
-    add_definitions(-std=c99 -pipe -Wall -Wextra -Werror -Wformat -Wno-unused-parameter
-                    -Wno-unused-function -Wno-unused-label -Wpointer-arith -Wreturn-type -Wsign-compare
-                    -Wmultichar -Wstrict-overflow -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes
-                    -Winit-self -Wuninitialized -Wformat-security -Wformat-nonliteral)
+    add_definitions(-std=c99 -pipe
+                    -Wall -Wextra -Werror -Wpedantic
+                    -Wno-static-in-inline # clang doesn't like the use of static inline methods inside static inline methods
+                    -Wno-overlength-strings # may happen in the nodeset compiler when complex values are directly encoded
+                    -Wno-unused-parameter # some methods may require unused arguments to cast to a method pointer
+                    -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls
+                    -Wformat -Wformat-security -Wformat-nonliteral
+                    -Wuninitialized -Winit-self
+                    -Wcast-qual
+                    -Wstrict-overflow
+                    -Wnested-externs
+                    -Wmultichar
+                    -Wundef
+                    -Wc++-compat)
      if(NOT WIN32 AND NOT CYGWIN AND NOT QNXNTO)
         add_definitions(-Wshadow -Wconversion -fvisibility=hidden -fPIC)
      endif()
 
+     if(UA_ENABLE_AMALGAMATION)
+         add_definitions(-Wno-unused-function)
+     endif()
+
     # Linker
     set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # cmake sets -rdynamic by default
 
@@ -302,6 +316,7 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c
                 # subscriptions
                 ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_datachange.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
                 # client
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client.c

+ 1 - 1
deps/mdnsd

@@ -1 +1 @@
-Subproject commit 47d76ed02243527d20930779c0e2242b77f011ca
+Subproject commit b72f2c573add9aa6e58cd019f7e5c5c9b84cb585

+ 2 - 2
deps/pcg_basic.h

@@ -26,7 +26,7 @@
 
 #include "ms_stdint.h"
 
-#if __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -40,7 +40,7 @@ typedef struct pcg_state_setseq_64 {
 void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initial_state, uint64_t initseq);
 uint32_t pcg32_random_r(pcg32_random_t* rng);
 
-#if __cplusplus
+#ifdef __cplusplus
 }
 #endif
 

+ 8 - 3
examples/CMakeLists.txt

@@ -9,7 +9,7 @@ include_directories(${PROJECT_BINARY_DIR})
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples)
 
 macro(add_example EXAMPLE_NAME EXAMPLE_SOURCE)
-  add_executable(${EXAMPLE_NAME} ${STATIC_OBJECTS} ${EXAMPLE_SOURCE})
+  add_executable(${EXAMPLE_NAME} ${STATIC_OBJECTS} ${EXAMPLE_SOURCE} ${ARGN})
   target_link_libraries(${EXAMPLE_NAME} open62541 ${open62541_LIBRARIES})
   assign_source_group(${EXAMPLE_SOURCE})
   add_dependencies(${EXAMPLE_NAME} open62541_amalgamation)
@@ -65,7 +65,11 @@ add_example(server_repeated_job server_repeated_job.c)
 
 add_example(server_inheritance server_inheritance.c)
 
-if(NOT BUILD_SHARED_open62541_LIBRARIES AND UA_BUILD_EXAMPLES_NODESET_COMPILER)
+if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
+  if(BUILD_SHARED_LIBS)
+    message(FATAL_ERROR "The nodeset compiler currently requires static linking to access internal API")
+  endif()
+
   # example information model from nodeset xml
   add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
                     PRE_BUILD
@@ -84,8 +88,9 @@ if(NOT BUILD_SHARED_open62541_LIBRARIES AND UA_BUILD_EXAMPLES_NODESET_COMPILER)
                             ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
 
   # needs internal methods which are not exported in the dynamic lib
-  add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c)
+  add_example(server_nodeset ${PROJECT_BINARY_DIR}/src_generated/nodeset.c server_nodeset.c)
   target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
+  set_target_properties(server_nodeset PROPERTIES COMPILE_FLAGS "-Wno-pedantic -Wno-sign-conversion")
 endif()
 
 if(UA_BUILD_SELFSIGNED_CERTIFICATE)

+ 1 - 1
examples/discovery/server_multicast.c

@@ -77,7 +77,7 @@ serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean is
 
     if (discovery_url != NULL)
         free(discovery_url);
-    discovery_url = malloc(serverOnNetwork->discoveryUrl.length + 1);
+    discovery_url = (char*)malloc(serverOnNetwork->discoveryUrl.length + 1);
     memcpy(discovery_url, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length);
     discovery_url[serverOnNetwork->discoveryUrl.length] = 0;
 }

+ 4 - 1
examples/server_nodeset.c

@@ -3,7 +3,10 @@
 
 #include <signal.h>
 #include <stdlib.h>
-#include "open62541.h"
+#include "ua_server.h"
+#include "ua_log_stdout.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
 
 /* Files nodeset.h and nodeset.c are created from server_nodeset.xml in the
  * /src_generated directory by CMake */

+ 1 - 0
examples/tutorial_server_variabletype.c

@@ -135,6 +135,7 @@ int main(void) {
     addVariableType2DPoint(server);
     addVariable(server);
     addVariableFail(server);
+    writeVariable(server);
 
     UA_Server_run(server, &running);
     UA_Server_delete(server);

+ 2 - 2
include/ua_server_deprecated.h

@@ -45,12 +45,12 @@ UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
                          UA_UInt32 interval, UA_Guid *jobId) {
     return UA_Server_addRepeatedCallback(server, job.job.methodCall.method,
                                          job.job.methodCall.data, interval,
-                                         (UA_UInt64*)jobId);
+                                         (UA_UInt64*)(uintptr_t)jobId);
 }
 
 UA_DEPRECATED static UA_INLINE UA_StatusCode
 UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId) {
-    return UA_Server_removeRepeatedCallback(server, *(UA_UInt64*)&jobId);
+    return UA_Server_removeRepeatedCallback(server, *(UA_UInt64*)(uintptr_t)&jobId);
 }
 
 #ifdef __cplusplus

+ 1 - 1
plugins/ua_network_tcp.c

@@ -64,7 +64,7 @@
 # define UA_fd_isset(fd, fds) FD_ISSET(fd, fds)
 #endif
 
-#if UNDER_CE
+#ifdef UNDER_CE
 # define errno WSAGetLastError()
 #endif
 

+ 2 - 0
src/server/ua_mdns.c

@@ -111,10 +111,12 @@ mdns_record_add_or_get(UA_Server *server, const char *record, const char *server
     return listEntry;
 }
 
+#ifdef UA_ENABLE_MULTITHREADING
 static void
 delayedFree(UA_Server *server, void *data) {
     UA_free(data);
 }
+#endif
 
 static void
 mdns_record_remove(UA_Server *server, const char *record,

+ 12 - 12
src/server/ua_nodestore_concurrent.c

@@ -16,9 +16,9 @@ struct nodeEntry {
     UA_Node node; ///< Might be cast from any _bigger_ UA_Node* type. Allocate enough memory!
 };
 
-static struct nodeEntry * instantiateEntry(UA_NodeClass class) {
+static struct nodeEntry * instantiateEntry(UA_NodeClass nc) {
     size_t size = sizeof(struct nodeEntry) - sizeof(UA_Node);
-    switch(class) {
+    switch(nc) {
     case UA_NODECLASS_OBJECT:
         size += sizeof(UA_ObjectNode);
         break;
@@ -46,10 +46,10 @@ static struct nodeEntry * instantiateEntry(UA_NodeClass class) {
     default:
         return NULL;
     }
-    struct nodeEntry *entry = UA_calloc(1, size);
+    struct nodeEntry *entry = (struct nodeEntry*)UA_calloc(1, size);
     if(!entry)
         return NULL;
-    entry->node.nodeClass = class;
+    entry->node.nodeClass = nc;
     return entry;
 }
 
@@ -91,8 +91,8 @@ void UA_NodeStore_delete(UA_NodeStore *ns) {
     UA_RCU_LOCK();
 }
 
-UA_Node * UA_NodeStore_newNode(UA_NodeClass class) {
-    struct nodeEntry *entry = instantiateEntry(class);
+UA_Node * UA_NodeStore_newNode(UA_NodeClass nc) {
+    struct nodeEntry *entry = instantiateEntry(nc);
     if(!entry)
         return NULL;
     return (UA_Node*)&entry->node;
@@ -209,15 +209,15 @@ UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     struct nodeEntry *entry = (struct nodeEntry*)iter.node;
     if(!entry)
         return NULL;
-    struct nodeEntry *new = instantiateEntry(entry->node.nodeClass);
-    if(!new)
+    struct nodeEntry *n = instantiateEntry(entry->node.nodeClass);
+    if(!n)
         return NULL;
-    if(UA_Node_copyAnyNodeClass(&entry->node, &new->node) != UA_STATUSCODE_GOOD) {
-        deleteEntry(&new->rcu_head);
+    if(UA_Node_copyAnyNodeClass(&entry->node, &n->node) != UA_STATUSCODE_GOOD) {
+        deleteEntry(&n->rcu_head);
         return NULL;
     }
-    new->orig = entry;
-    return &new->node;
+    n->orig = entry;
+    return &n->node;
 }
 
 void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {

+ 2 - 2
src/server/ua_securechannel_manager.c

@@ -62,7 +62,7 @@ UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime
             (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_MSEC_TO_DATETIME);
         if(timeout < nowMonotonic || !entry->channel.connection) {
             UA_LOG_INFO_CHANNEL(cm->server->config.logger, &entry->channel,
-                                "SecureChannel has timed out");
+                                "SecureChannel has timed out", NULL);
             removeSecureChannel(cm, entry);
         } else if(entry->channel.nextSecurityToken.tokenId > 0) {
             UA_SecureChannel_revolveTokens(&entry->channel);
@@ -77,7 +77,7 @@ static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
         if(LIST_EMPTY(&(entry->channel.sessions))){
             UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, &entry->channel,
                                  "Channel was purged since maxSecureChannels was "
-                                 "reached and channel had no session attached");
+                                 "reached and channel had no session attached", NULL);
             removeSecureChannel(cm, entry);
             UA_assert(entry != LIST_FIRST(&cm->channels));
             return true;

+ 10 - 12
src/server/ua_server_binary.c

@@ -394,7 +394,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
         if(requestTypeId.identifier.numeric == 787) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
                                 "Client requested a subscription, " \
-                                "but those are not enabled in the build");
+                                "but those are not enabled in the build", NULL);
         } else {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
                                 "Unknown request with type identifier %i",
@@ -419,7 +419,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
                              server->config.customDataTypes);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
-                             "Could not decode the request");
+                             "Could not decode the request", NULL);
         sendError(channel, msg, requestPos, responseType, requestId, retval);
         return;
     }
@@ -447,7 +447,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
         if(!session) {
             UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
                                  "Trying to activate a session that is " \
-                                 "not known in the server");
+                                 "not known in the server", NULL);
             sendError(channel, msg, requestPos, responseType,
                       requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
             UA_deleteMembers(request, requestType);
@@ -493,7 +493,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     /* The session is bound to another channel */
     if(session->channel != channel) {
         UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
-                             "Client tries to use an obsolete securechannel");
+                             "Client tries to use an obsolete securechannel", NULL);
         sendError(channel, msg, requestPos, responseType,
                   requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
         UA_deleteMembers(request, requestType);
@@ -567,26 +567,24 @@ UA_Server_processSecureChannelMessage(UA_Server *server, UA_SecureChannel *chann
     }
     case UA_MESSAGETYPE_HEL:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Cannot process a HEL on an open channel");
+                             "Cannot process a HEL on an open channel", NULL);
         break;
     case UA_MESSAGETYPE_OPN:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Process an OPN on an open channel");
+                             "Process an OPN on an open channel", NULL);
         processOPN(server, channel->connection, channel->securityToken.channelId, message);
         break;
     case UA_MESSAGETYPE_MSG:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Process a MSG", channel->connection->sockfd);
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Process a MSG", NULL);
         processMSG(server, channel, requestId, message);
         break;
     case UA_MESSAGETYPE_CLO:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Process a CLO", channel->connection->sockfd);
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Process a CLO", NULL);
         Service_CloseSecureChannel(server, channel);
         break;
     default:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
-                             "Unknown message type");
+                             "Unknown message type", NULL);
     }
 }
 
@@ -696,7 +694,7 @@ void
 UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
                                UA_ByteString *message) {
     /* Allocate the memory for the callback data */
-    ConnectionMessage *cm = UA_malloc(sizeof(ConnectionMessage));
+    ConnectionMessage *cm = (ConnectionMessage*)UA_malloc(sizeof(ConnectionMessage));
 
     /* If malloc failed, execute immediately */
     if(!cm) {

+ 17 - 13
src/server/ua_server_internal.h

@@ -218,6 +218,17 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode,
  * class, typeId is set to UA_NODEID_NULL. */
 void getNodeType(UA_Server *server, const UA_Node *node, UA_NodeId *typeId);
 
+typedef void (*UA_ServiceOperation)(UA_Server *server, UA_Session *session,
+                                    const void *requestOperation, void *responseOperation);
+
+UA_StatusCode
+UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
+                                   UA_ServiceOperation operationCallback,
+                                   const size_t *requestOperations,
+                                   const UA_DataType *requestOperationsType,
+                                   size_t *responseOperations,
+                                   const UA_DataType *responseOperationsType);
+
 /***************************************/
 /* Check Information Model Consistency */
 /***************************************/
@@ -236,9 +247,6 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
                           const UA_UInt32 *constraintArrayDimensions,
                           size_t testArrayDimensionsSize,
                           const UA_UInt32 *testArrayDimensions);
-UA_Boolean
-compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
-                   const UA_NodeId *constraintDataType);
 
 UA_StatusCode
 compatibleValueRankArrayDimensions(UA_Int32 valueRank, size_t arrayDimensionsSize);
@@ -247,9 +255,6 @@ UA_Boolean
 compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
                    const UA_NodeId *constraintDataType);
 
-UA_StatusCode
-compatibleValueRankArrayDimensions(UA_Int32 valueRank, size_t arrayDimensionsSize);
-
 UA_StatusCode
 writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
                         UA_Int32 valueRank, UA_Int32 constraintValueRank);
@@ -257,9 +262,6 @@ writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
 UA_StatusCode
 compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
 
-UA_StatusCode
-compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
-
 /*******************/
 /* Single-Services */
 /*******************/
@@ -272,11 +274,13 @@ void Service_Browse_single(UA_Server *server, UA_Session *session,
                            const UA_BrowseDescription *descr,
                            UA_UInt32 maxrefs, UA_BrowseResult *result);
 
-void Service_Read_single(UA_Server *server, UA_Session *session,
-                         UA_TimestampsToReturn timestamps,
-                         const UA_ReadValueId *id, UA_DataValue *v);
+UA_DataValue
+UA_Server_readWithSession(UA_Server *server, UA_Session *session,
+                          const UA_ReadValueId *item,
+                          UA_TimestampsToReturn timestamps);
 
-/* Periodic task to clean up the discovery registry */
+/* Checks if a registration timed out and removes that registration.
+ * Should be called periodically in main loop */
 void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic);
 
 # ifdef UA_ENABLE_DISCOVERY_MULTICAST

+ 29 - 0
src/server/ua_server_utils.c

@@ -231,3 +231,32 @@ UA_Server_editNode(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 #endif
 }
+
+UA_StatusCode
+UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
+                                   UA_ServiceOperation operationCallback,
+                                   const size_t *requestOperations,
+                                   const UA_DataType *requestOperationsType,
+                                   size_t *responseOperations,
+                                   const UA_DataType *responseOperationsType) {
+    size_t ops = *requestOperations;
+    if(ops == 0)
+        return UA_STATUSCODE_BADNOTHINGTODO;
+
+    /* No padding after size_t */
+    void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t));
+    *respPos = UA_Array_new(ops, responseOperationsType);
+    if(!(*respPos))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    *responseOperations = ops;
+    uintptr_t respOp = (uintptr_t)*respPos;
+    /* No padding after size_t */
+    uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t));
+    for(size_t i = 0; i < ops; i++) {
+        operationCallback(server, session, (void*)reqOp, (void*)respOp);
+        reqOp += requestOperationsType->memSize;
+        respOp += responseOperationsType->memSize;
+    }
+    return UA_STATUSCODE_GOOD;
+}

+ 3 - 3
src/server/ua_server_worker.c

@@ -119,7 +119,7 @@ UA_Server_workerCallback(UA_Server *server, UA_ServerCallback callback,
     callback(server, data);
 #else
     /* Execute immediately if memory could not be allocated */
-    WorkerCallback *dc = UA_malloc(sizeof(WorkerCallback));
+    WorkerCallback *dc = (WorkerCallback*)UA_malloc(sizeof(WorkerCallback));
     if(!dc) {
         callback(server, data);
         return;
@@ -195,7 +195,7 @@ UA_Server_delayedCallback(UA_Server *server, UA_ServerCallback callback,
                           void *data) {
     size_t dcsize = sizeof(WorkerCallback) +
         (sizeof(UA_UInt32) * server->config.nThreads);
-    WorkerCallback *dc = UA_malloc(dcsize);
+    WorkerCallback *dc = (WorkerCallback*)UA_malloc(dcsize);
     if(!dc)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
@@ -286,7 +286,7 @@ UA_Server_run_startup(UA_Server *server) {
                 "Spinning up %u worker thread(s)", server->config.nThreads);
     pthread_cond_init(&server->dispatchQueue_condition, 0);
     pthread_mutex_init(&server->dispatchQueue_mutex, 0);
-    server->workers = UA_malloc(server->config.nThreads * sizeof(UA_Worker));
+    server->workers = (UA_Worker*)UA_malloc(server->config.nThreads * sizeof(UA_Worker));
     if(!server->workers)
         return UA_STATUSCODE_BADOUTOFMEMORY;
     for(size_t i = 0; i < server->config.nThreads; ++i) {

+ 0 - 4
src/server/ua_services.h

@@ -69,10 +69,6 @@ void Service_RegisterServer(UA_Server *server, UA_Session *session,
                             const UA_RegisterServerRequest *request,
                             UA_RegisterServerResponse *response);
 
-/* Checks if a registration timed out and removes that registration.
- * Should be called periodically in main loop */
-void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic);
-
 /* This Service allows a Server to register its DiscoveryUrls and capabilities
  * with a Discovery Server. It extends the registration information from
  * RegisterServer with information necessary for FindServersOnNetwork. */

+ 94 - 78
src/server/ua_services_attribute.c

@@ -91,9 +91,8 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
 
     /* Enum allows Int32 (only) */
     UA_NodeId enumNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMERATION);
-    if (isNodeInTree(server->nodestore, constraintDataType, &enumNodeId, &subtypeId, 1)) {
+    if(isNodeInTree(server->nodestore, constraintDataType, &enumNodeId, &subtypeId, 1))
         return UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId);
-    }
 
     return isNodeInTree(server->nodestore, dataType, constraintDataType, &subtypeId, 1);
 }
@@ -158,7 +157,7 @@ compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank) {
 static UA_StatusCode
 compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) {
     /* empty arrays (-1) always match */
-    if(value->data == NULL)
+    if(!value->data)
         return UA_STATUSCODE_GOOD;
 
     size_t arrayDims = value->arrayDimensionsSize;
@@ -193,7 +192,8 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
    the description */
 static const UA_Variant *
 convertToMatchingValue(UA_Server *server, const UA_Variant *value,
-                       const UA_NodeId *targetDataTypeId, UA_Variant *editableValue) {
+                       const UA_NodeId *targetDataTypeId,
+                       UA_Variant *editableValue) {
     const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId);
     if(!targetDataType)
         return NULL;
@@ -298,7 +298,8 @@ readArrayDimensionsAttribute(const UA_VariableNode *vn, UA_DataValue *v) {
 
 static UA_StatusCode
 writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
-                              size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions) {
+                              size_t arrayDimensionsSize,
+                              UA_UInt32 *arrayDimensions) {
     /* If this is a variabletype, there must be no instances or subtypes of it
      * when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
@@ -306,7 +307,8 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
         return UA_STATUSCODE_BADINTERNALERROR;
 
     /* Check that the array dimensions match with the valuerank */
-    UA_StatusCode retval = compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize);
+    UA_StatusCode retval =
+        compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "The current value rank does not match the new array dimensions");
@@ -351,7 +353,8 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
     /* Ok, apply */
     UA_UInt32 *oldArrayDimensions = node->arrayDimensions;
     retval = UA_Array_copy(arrayDimensions, arrayDimensionsSize,
-                           (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
+                           (void**)&node->arrayDimensions,
+                           &UA_TYPES[UA_TYPES_UINT32]);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     UA_free(oldArrayDimensions);
@@ -373,8 +376,8 @@ writeValueRankAttributeWithVT(UA_Server *server, UA_VariableNode *node,
 }
 
 UA_StatusCode
-writeValueRankAttribute(UA_Server *server, UA_VariableNode *node, UA_Int32 valueRank,
-                        UA_Int32 constraintValueRank) {
+writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
+                        UA_Int32 valueRank, UA_Int32 constraintValueRank) {
     /* If this is a variabletype, there must be no instances or subtypes of it
        when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
@@ -421,7 +424,8 @@ writeValueRankAttribute(UA_Server *server, UA_VariableNode *node, UA_Int32 value
 
 static UA_StatusCode
 writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
-                       const UA_NodeId *dataType, const UA_NodeId *constraintDataType) {
+                       const UA_NodeId *dataType,
+                       const UA_NodeId *constraintDataType) {
     /* If this is a variabletype, there must be no instances or subtypes of it
        when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
@@ -475,8 +479,8 @@ writeDataTypeAttributeWithVT(UA_Server *server, UA_VariableNode *node,
 /*******************/
 
 static UA_StatusCode
-readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v,
-                           UA_NumericRange *rangeptr) {
+readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn,
+                           UA_DataValue *v, UA_NumericRange *rangeptr) {
     if(vn->value.data.callback.onRead) {
         UA_RCU_UNLOCK();
         vn->value.data.callback.onRead(vn->value.data.callback.handle,
@@ -512,7 +516,8 @@ readValueAttributeFromDataSource(const UA_VariableNode *vn, UA_DataValue *v,
 
 static UA_StatusCode
 readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
-                           UA_TimestampsToReturn timestamps, const UA_String *indexRange,
+                           UA_TimestampsToReturn timestamps,
+                           const UA_String *indexRange,
                            UA_DataValue *v) {
     /* Compute the index range */
     UA_NumericRange range;
@@ -538,8 +543,10 @@ readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
 }
 
 UA_StatusCode
-readValueAttribute(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v) {
-    return readValueAttributeComplete(server, vn, UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
+readValueAttribute(UA_Server *server, const UA_VariableNode *vn,
+                   UA_DataValue *v) {
+    return readValueAttributeComplete(server, vn, UA_TIMESTAMPSTORETURN_NEITHER,
+                                      NULL, v);
 }
 
 static UA_StatusCode
@@ -635,22 +642,22 @@ writeValueAttribute(UA_Server *server, UA_VariableNode *node,
             const UA_VariableNode *writtenNode;
 #ifdef UA_ENABLE_MULTITHREADING
             /* Reopen the node to see the changes (multithreading only) */
-            writtenNode = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &node->nodeId);
+            writtenNode = (const UA_VariableNode*)
+                UA_NodeStore_get(server->nodestore, &node->nodeId);
 #else
             writtenNode = node; /* The node is written in-situ (TODO: this might
                                    change with the nodestore plugin approach) */
 #endif
             UA_RCU_UNLOCK();
             writtenNode->value.data.callback.onWrite(writtenNode->value.data.callback.handle,
-                                                     writtenNode->nodeId,
-                                                     &writtenNode->value.data.value.value, rangeptr);
+                          writtenNode->nodeId, &writtenNode->value.data.value.value, rangeptr);
             UA_RCU_LOCK();
         }
     } else {
         if(node->value.dataSource.write) {
             UA_RCU_UNLOCK();
             retval = node->value.dataSource.write(node->value.dataSource.handle,
-                                                  node->nodeId, &editableValue.value, rangeptr);
+                                      node->nodeId, &editableValue.value, rangeptr);
             UA_RCU_LOCK();
         } else {
             retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
@@ -718,15 +725,18 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
 static const UA_String binEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 /* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_TimestampsToReturn op_timestampsToReturn;
+
 #define CHECK_NODECLASS(CLASS)                                  \
     if(!(node->nodeClass & (CLASS))) {                          \
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
         break;                                                  \
     }
 
-void Service_Read_single(UA_Server *server, UA_Session *session,
-                         const UA_TimestampsToReturn timestamps,
-                         const UA_ReadValueId *id, UA_DataValue *v) {
+static void
+Operation_Read(UA_Server *server, UA_Session *session,
+               const UA_ReadValueId *id, UA_DataValue *v) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Read the attribute %i", id->attributeId);
 
@@ -803,13 +813,14 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_VALUE: {
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
+        UA_Byte userAccessLevel = getUserAccessLevel(server, session,
+                                                     (const UA_VariableNode*)node);
         if(!(userAccessLevel & (UA_ACCESSLEVELMASK_READ))) {
             retval = UA_STATUSCODE_BADUSERACCESSDENIED;
             break;
         }
         retval = readValueAttributeComplete(server, (const UA_VariableNode*)node,
-                                            timestamps, &id->indexRange, v);
+                                            op_timestampsToReturn, &id->indexRange, v);
         break;
     }
     case UA_ATTRIBUTEID_DATATYPE:
@@ -833,7 +844,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_USERACCESSLEVEL: {
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
+        UA_Byte userAccessLevel = getUserAccessLevel(server, session,
+                                                     (const UA_VariableNode*)node);
         UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
         break; }
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
@@ -853,7 +865,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_USEREXECUTABLE: {
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node);
+        UA_Boolean userExecutable = getUserExecutable(server, session,
+                                                      (const UA_MethodNode*)node);
         UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break; }
     default:
@@ -870,16 +883,16 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
     v->hasValue = true;
 
     /* Create server timestamp */
-    if(timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
-       timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
+    if(op_timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
+       op_timestampsToReturn == UA_TIMESTAMPSTORETURN_BOTH) {
         v->serverTimestamp = UA_DateTime_now();
         v->hasServerTimestamp = true;
     }
 
     /* Handle source time stamp */
     if(id->attributeId == UA_ATTRIBUTEID_VALUE) {
-        if (timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
-            timestamps == UA_TIMESTAMPSTORETURN_NEITHER) {
+        if(op_timestampsToReturn == UA_TIMESTAMPSTORETURN_SERVER ||
+           op_timestampsToReturn == UA_TIMESTAMPSTORETURN_NEITHER) {
             v->hasSourceTimestamp = false;
             v->hasSourcePicoseconds = false;
         } else if(!v->hasSourceTimestamp) {
@@ -891,41 +904,33 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
 
 void Service_Read(UA_Server *server, UA_Session *session,
                   const UA_ReadRequest *request, UA_ReadResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ReadRequest");
-    if(request->nodesToReadSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing ReadRequest", NULL);
 
-    /* check if the timestampstoreturn is valid */
-    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+    /* Check if the timestampstoreturn is valid */
+    op_timestampsToReturn = request->timestampsToReturn;
+    if(op_timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
-    size_t size = request->nodesToReadSize;
-    response->results = (UA_DataValue *)UA_Array_new(size, &UA_TYPES[UA_TYPES_DATAVALUE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = size;
-
+    /* Check if maxAge is valid */
     if(request->maxAge < 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADMAXAGEINVALID;
         return;
     }
 
-    for(size_t i = 0;i < size;++i) {
-            Service_Read_single(server, session, request->timestampsToReturn,
-                                &request->nodesToRead[i], &response->results[i]);
-    }
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_Read,
+                  &request->nodesToReadSize, &UA_TYPES[UA_TYPES_READVALUEID],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
 
 #ifdef UA_ENABLE_NONSTANDARD_STATELESS
     /* Add an expiry header for caching */
     if(session->sessionId.namespaceIndex == 0 &&
        session->sessionId.identifierType == UA_NODEIDTYPE_NUMERIC &&
-       session->sessionId.identifier.numeric == 0){
+       session->sessionId.identifier.numeric == 0) {
         UA_ExtensionObject additionalHeader;
         UA_ExtensionObject_init(&additionalHeader);
         additionalHeader.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
@@ -963,18 +968,26 @@ void Service_Read(UA_Server *server, UA_Session *session,
 #endif
 }
 
-/* Exposes the Read service to local users */
 UA_DataValue
-UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
-               UA_TimestampsToReturn timestamps) {
+UA_Server_readWithSession(UA_Server *server, UA_Session *session,
+                          const UA_ReadValueId *item,
+                          UA_TimestampsToReturn timestamps) {
     UA_DataValue dv;
     UA_DataValue_init(&dv);
+    op_timestampsToReturn = timestamps;
     UA_RCU_LOCK();
-    Service_Read_single(server, &adminSession, timestamps, item, &dv);
+    Operation_Read(server, session, item, &dv);
     UA_RCU_UNLOCK();
     return dv;
 }
 
+/* Exposes the Read service to local users */
+UA_DataValue
+UA_Server_read(UA_Server *server, const UA_ReadValueId *item,
+               UA_TimestampsToReturn timestamps) {
+    return UA_Server_readWithSession(server, &adminSession, item, timestamps);
+}
+
 /* Used in inline functions exposing the Read service with more syntactic sugar
  * for individual attributes */
 UA_StatusCode
@@ -1112,7 +1125,8 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
         CHECK_USERWRITEMASK(UA_WRITEMASK_INVERSENAME);
         CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
         UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
-        UA_LocalizedText_copy((const UA_LocalizedText *)value, &((UA_ReferenceTypeNode*)node)->inverseName);
+        UA_LocalizedText_copy((const UA_LocalizedText *)value,
+                              &((UA_ReferenceTypeNode*)node)->inverseName);
         break;
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
@@ -1130,7 +1144,8 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         if(node->nodeClass == UA_NODECLASS_VARIABLE) {
             /* The access to a value variable is granted via the AccessLevel Byte */
-            UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
+            UA_Byte userAccessLevel = getUserAccessLevel(server, session,
+                                                         (const UA_VariableNode*)node);
             if(!(userAccessLevel & (UA_ACCESSLEVELMASK_WRITE))) {
                 retval = UA_STATUSCODE_BADUSERACCESSDENIED;
                 break;
@@ -1145,13 +1160,15 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_USERWRITEMASK(UA_WRITEMASK_DATATYPE);
         CHECK_DATATYPE_SCALAR(NODEID);
-        retval = writeDataTypeAttributeWithVT(server, (UA_VariableNode*)node, (const UA_NodeId*)value);
+        retval = writeDataTypeAttributeWithVT(server, (UA_VariableNode*)node,
+                                              (const UA_NodeId*)value);
         break;
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_USERWRITEMASK(UA_WRITEMASK_VALUERANK);
         CHECK_DATATYPE_SCALAR(INT32);
-        retval = writeValueRankAttributeWithVT(server, (UA_VariableNode*)node, *(const UA_Int32*)value);
+        retval = writeValueRankAttributeWithVT(server, (UA_VariableNode*)node,
+                                               *(const UA_Int32*)value);
         break;
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
@@ -1196,34 +1213,32 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
     return retval;
 }
 
+static void
+Operation_Write(UA_Server *server, UA_Session *session,
+                UA_WriteValue *wv, UA_StatusCode *result) {
+    *result = UA_Server_editNode(server, session, &wv->nodeId,
+                        (UA_EditNodeCallback)copyAttributeIntoNode, wv);
+}
+
 void
 Service_Write(UA_Server *server, UA_Session *session,
-              const UA_WriteRequest *request, UA_WriteResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing WriteRequest");
-    if(request->nodesToWriteSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    response->results = (UA_StatusCode *)UA_Array_new(request->nodesToWriteSize,
-                                                      &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->nodesToWriteSize;
+              const UA_WriteRequest *request,
+              UA_WriteResponse *response) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing WriteRequest", NULL);
 
-    for(size_t i = 0; i < request->nodesToWriteSize; ++i)
-        response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
-                                                  (UA_EditNodeCallback)copyAttributeIntoNode,
-                                                  &request->nodesToWrite[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_Write,
+                  &request->nodesToWriteSize, &UA_TYPES[UA_TYPES_WRITEVALUE],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
 UA_Server_write(UA_Server *server, const UA_WriteValue *value) {
     UA_StatusCode retval =
         UA_Server_editNode(server, &adminSession, &value->nodeId,
-                           (UA_EditNodeCallback)copyAttributeIntoNode, value);
+                  (UA_EditNodeCallback)copyAttributeIntoNode, value);
     return retval;
 }
 
@@ -1240,7 +1255,8 @@ __UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
     wvalue.value.hasValue = true;
     if(attr_type != &UA_TYPES[UA_TYPES_VARIANT]) {
         /* hacked cast. the target WriteValue is used as const anyway */
-        UA_Variant_setScalar(&wvalue.value.value, (void*)(uintptr_t)attr, attr_type);
+        UA_Variant_setScalar(&wvalue.value.value,
+                             (void*)(uintptr_t)attr, attr_type);
     } else {
         wvalue.value.value = *(const UA_Variant*)attr;
     }

+ 45 - 47
src/server/ua_services_call.c

@@ -59,39 +59,45 @@ argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequi
     return retval;
 }
 
-static UA_StatusCode
-callMethod(UA_Server *server, UA_Session *session,
-           const UA_CallMethodRequest *request,
-           UA_CallMethodResult *result) {
+static void
+Operation_CallMethod(UA_Server *server, UA_Session *session,
+                     const UA_CallMethodRequest *request,
+                     UA_CallMethodResult *result) {
     /* Get/verify the method node */
     const UA_MethodNode *methodCalled =
         (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
     if(!methodCalled)
-        return UA_STATUSCODE_BADMETHODINVALID;
+        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
     if(methodCalled->nodeClass != UA_NODECLASS_METHOD)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
+        result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
     if(!methodCalled->attachedMethod)
-        return UA_STATUSCODE_BADINTERNALERROR;
+        result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
 
     /* Get/verify the object node */
     const UA_ObjectNode *object =
         (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId);
-    if(!object)
-        return UA_STATUSCODE_BADNODEIDINVALID;
+    if(!object) {
+        result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
+        return;
+    }
     if(object->nodeClass != UA_NODECLASS_OBJECT &&
-       object->nodeClass != UA_NODECLASS_OBJECTTYPE)
-        return UA_STATUSCODE_BADNODECLASSINVALID;
+       object->nodeClass != UA_NODECLASS_OBJECTTYPE) {
+        result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
+        return;
+    }
 
     /* Verify access rights */
     UA_Boolean executable = methodCalled->executable;
     if(session != &adminSession)
         executable = executable &&
             server->config.accessControl.getUserExecutableOnObject(&session->sessionId,
-                                                                   session->sessionHandle,
-                                                                   &request->objectId,
-                                                                   &request->methodId);
-    if(!executable)
-        return UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+                                 session->sessionHandle, &request->objectId, &request->methodId);
+    if(!executable) {
+        result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+        return;
+    }
 
     /* Verify method/object relations. Object must have a hasComponent or a
      * subtype of hasComponent reference to the method node. Therefore, check
@@ -114,23 +120,24 @@ callMethod(UA_Server *server, UA_Session *session,
             }
         }
     }
-    if(!found)
-        return UA_STATUSCODE_BADMETHODINVALID;
+    if(!found) {
+        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
+        return;
+    }
 
     /* Verify Input Argument count, types and sizes */
     const UA_VariableNode *inputArguments =
         getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
-
     if(!inputArguments) {
         if(request->inputArgumentsSize > 0)
-            return UA_STATUSCODE_BADINVALIDARGUMENT;
+            result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
     } else {
-        UA_StatusCode retval = argumentsConformsToDefinition(server, inputArguments,
-                                                             request->inputArgumentsSize,
-                                                             request->inputArguments);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+        result->statusCode = argumentsConformsToDefinition(server, inputArguments,
+                                                           request->inputArgumentsSize,
+                                                           request->inputArguments);
     }
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
 
     /* Allocate the output arguments */
     result->outputArgumentsSize = 0; /* the default */
@@ -140,8 +147,10 @@ callMethod(UA_Server *server, UA_Session *session,
         result->outputArguments =
             (UA_Variant*)UA_Array_new(outputArguments->value.data.value.value.arrayLength,
                                       &UA_TYPES[UA_TYPES_VARIANT]);
-        if(!result->outputArguments)
-            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(!result->outputArguments) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            return;
+        }
         result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
     }
 
@@ -149,7 +158,7 @@ callMethod(UA_Server *server, UA_Session *session,
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
 #endif
-    UA_StatusCode retval =
+    result->statusCode =
         methodCalled->attachedMethod(methodCalled->methodHandle, &object->nodeId,
                                      &session->sessionId, session->sessionHandle,
                                      request->inputArgumentsSize, request->inputArguments,
@@ -159,30 +168,19 @@ callMethod(UA_Server *server, UA_Session *session,
 #endif
 
     /* TODO: Verify Output matches the argument definition */
-    return retval;
 }
 
 void Service_Call(UA_Server *server, UA_Session *session,
                   const UA_CallRequest *request,
                   UA_CallResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CallRequest");
-
-    if(request->methodsToCallSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    response->results = (UA_CallMethodResult*)UA_Array_new(request->methodsToCallSize,
-                                                           &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->methodsToCallSize;
-
-    for(size_t i = 0; i < request->methodsToCallSize; ++i)
-        response->results[i].statusCode =
-            callMethod(server, session, &request->methodsToCall[i], &response->results[i]);
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing CallRequest", NULL);
+
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_CallMethod,
+                  &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
 }
 
 #endif /* UA_ENABLE_METHODCALLS */

+ 8 - 4
src/server/ua_services_discovery.c

@@ -105,7 +105,7 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
                          const UA_FindServersRequest *request,
                          UA_FindServersResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing FindServersRequest");
+                         "Processing FindServersRequest", NULL);
 
     size_t foundServersSize = 0;
     UA_ApplicationDescription *foundServers = NULL;
@@ -242,7 +242,7 @@ void Service_GetEndpoints(UA_Server *server, UA_Session *session,
                              UA_PRINTF_STRING_FORMAT, UA_PRINTF_STRING_DATA(*endpointUrl));
     } else {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "Processing GetEndpointsRequest with an empty endpointUrl");
+                             "Processing GetEndpointsRequest with an empty endpointUrl", NULL);
     }
 
     /* test if the supported binary profile shall be returned */
@@ -316,10 +316,12 @@ void Service_GetEndpoints(UA_Server *server, UA_Session *session,
 
 #ifdef UA_ENABLE_DISCOVERY
 
+#ifdef UA_ENABLE_MULTITHREADING
 static void
 freeEntry(UA_Server *server, void *entry) {
     UA_free(entry);
 }
+#endif
 
 static void
 process_RegisterServer(UA_Server *server, UA_Session *session,
@@ -477,7 +479,8 @@ process_RegisterServer(UA_Server *server, UA_Session *session,
 void Service_RegisterServer(UA_Server *server, UA_Session *session,
                             const UA_RegisterServerRequest *request,
                             UA_RegisterServerResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RegisterServerRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing RegisterServerRequest", NULL);
     process_RegisterServer(server, session, &request->requestHeader, &request->server, 0,
                            NULL, &response->responseHeader, 0, NULL, 0, NULL);
 }
@@ -485,7 +488,8 @@ void Service_RegisterServer(UA_Server *server, UA_Session *session,
 void Service_RegisterServer2(UA_Server *server, UA_Session *session,
                             const UA_RegisterServer2Request *request,
                              UA_RegisterServer2Response *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RegisterServer2Request");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing RegisterServer2Request", NULL);
     process_RegisterServer(server, session, &request->requestHeader, &request->server,
                            request->discoveryConfigurationSize, request->discoveryConfiguration,
                            &response->responseHeader, &response->configurationResultsSize,

+ 10 - 10
src/server/ua_services_discovery_multicast.c

@@ -350,18 +350,18 @@ createFullServiceDomain(char *outServiceDomain, size_t maxLen,
     }
 
     /* Copy into outServiceDomain */
-    size_t pos = 0;
-    memcpy(&outServiceDomain[pos], servername->data, servernameLen);
-    pos += servernameLen;
+    size_t offset = 0;
+    memcpy(&outServiceDomain[offset], servername->data, servernameLen);
+    offset += servernameLen;
     if(hostnameLen > 0) {
-        memcpy(&outServiceDomain[pos], "-", 1);
-        ++pos;
-        memcpy(&outServiceDomain[pos], hostname->data, hostnameLen);
-        pos += hostnameLen;
+        memcpy(&outServiceDomain[offset], "-", 1);
+        ++offset;
+        memcpy(&outServiceDomain[offset], hostname->data, hostnameLen);
+        offset += hostnameLen;
     }
-    memcpy(&outServiceDomain[pos], "._opcua-tcp._tcp.local.", 23);
-    pos += 23;
-    outServiceDomain[pos] = 0;
+    memcpy(&outServiceDomain[offset], "._opcua-tcp._tcp.local.", 23);
+    offset += 23;
+    outServiceDomain[offset] = 0;
 }
 
 /* Check if mDNS already has an entry for given hostname and port combination */

+ 85 - 87
src/server/ua_services_nodemanagement.c

@@ -25,17 +25,18 @@ static UA_StatusCode
 deleteNode(UA_Server *server, UA_Session *session,
            const UA_NodeId *nodeId, UA_Boolean deleteReferences);
 
-static UA_StatusCode
+static void
 addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item);
+             const UA_AddReferencesItem *item,
+             UA_StatusCode *retval);
 
 static UA_StatusCode
 deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
                       const UA_DeleteReferencesItem *item);
 
-static UA_StatusCode
+static void
 deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item);
+                const UA_DeleteReferencesItem *item, UA_StatusCode *retval);
 
 /**********************/
 /* Consistency Checks */
@@ -56,7 +57,7 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
     if(!parent) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Parent node not found");
+                            "AddNodes: Parent node not found", NULL);
         return UA_STATUSCODE_BADPARENTNODEIDINVALID;
     }
 
@@ -65,21 +66,21 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
         (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
     if(!referenceType) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Reference type to the parent not found");
+                            "AddNodes: Reference type to the parent not found", NULL);
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
     /* Check if the referencetype is a reference type node */
     if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Reference type to the parent invalid");
+                            "AddNodes: Reference type to the parent invalid", NULL);
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
     /* Check that the reference type is not abstract */
     if(referenceType->isAbstract == true) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Abstract reference type to the parent not allowed");
+                            "AddNodes: Abstract reference type to the parent not allowed", NULL);
         return UA_STATUSCODE_BADREFERENCENOTALLOWED;
     }
 
@@ -93,14 +94,14 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
         if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: New type node need to have a "
-                                "HasSubType reference");
+                                "HasSubType reference", NULL);
             return UA_STATUSCODE_BADREFERENCENOTALLOWED;
         }
         /* supertype needs to be of the same node type  */
         if(parent->nodeClass != nodeClass) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: New type node needs to be of the same "
-                                "node type as the parent");
+                                "node type as the parent", NULL);
             return UA_STATUSCODE_BADPARENTNODEIDINVALID;
         }
         return UA_STATUSCODE_GOOD;
@@ -112,7 +113,7 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     if(!isNodeInTree(server->nodestore, referenceTypeId,
                      &hierarchicalReference, &subtypeId, 1)) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Reference type is not hierarchical");
+                            "AddNodes: Reference type is not hierarchical", NULL);
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
@@ -194,7 +195,7 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
     if(node->valueRank == 0 &&
        (!value.hasValue || !value.value.type || UA_Variant_isScalar(&value.value))) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Use a default ValueRank of -2");
+                            "AddNodes: Use a default ValueRank of -2", NULL);
         UA_RCU_UNLOCK();
         retval = UA_Server_writeValueRank(server, node->nodeId, -2);
         UA_RCU_LOCK();
@@ -223,7 +224,7 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
     /* Fix the variable: If no datatype is given, use the datatype of the vt */
     if(UA_NodeId_isNull(&node->dataType)) {
         UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: "
-                            "Use a default DataType (from the TypeDefinition)");
+                            "Use a default DataType (from the TypeDefinition)", NULL);
         UA_RCU_UNLOCK();
         retval = UA_Server_writeDataType(server, node->nodeId, vt->dataType);
         UA_RCU_LOCK();
@@ -435,7 +436,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         newItem.isForward = true;
         newItem.targetNodeId = rd->nodeId;
         newItem.targetNodeClass = UA_NODECLASS_METHOD;
-        retval = addReference(server, session, &newItem);
+        addReference(server, session, &newItem, &retval);
     } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
               rd->nodeClass == UA_NODECLASS_OBJECT) {
         /* Copy the node */
@@ -555,7 +556,7 @@ instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
             UA_RCU_UNLOCK();
             UA_Server_editNode(server, session, nodeId,
                                (UA_EditNodeCallback)setObjectInstanceHandle,
-                               olm->constructor);
+                               (void*)(uintptr_t)olm->constructor);
             UA_RCU_LOCK();
         }
     }
@@ -567,7 +568,7 @@ instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
     addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
     addref.isForward = true;
     addref.targetNodeId.nodeId = *typeId;
-    retval = addReference(server, session, &addref);
+    addReference(server, session, &addref, &retval);
 
     /* Call custom callback */
     if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
@@ -752,7 +753,7 @@ Service_AddNode_begin(UA_Server *server, UA_Session *session,
     /* Check the namespaceindex */
     if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Namespace invalid");
+                            "AddNodes: Namespace invalid", NULL);
         result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
         return;
     }
@@ -774,7 +775,7 @@ Service_AddNode_begin(UA_Server *server, UA_Session *session,
     result->statusCode = UA_NodeId_copy(&node->nodeId, &result->addedNodeId);
     if(result->statusCode != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: Could not copy the nodeid");
+                            "AddNodes: Could not copy the nodeid", NULL);
         deleteNode(server, &adminSession, &node->nodeId, true);
     }
 }
@@ -805,7 +806,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
     if((node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_OBJECT) &&
        UA_NodeId_isNull(typeDefinition)) {
         UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: Use a default "
-                            "TypeDefinition for the Variable/Object");
+                            "TypeDefinition for the Variable/Object", NULL);
         if(node->nodeClass == UA_NODECLASS_VARIABLE)
             typeDefinition = &baseDataVariableType;
         else
@@ -817,7 +818,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
                                                 parentNodeId, referenceTypeId);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: The parent reference is invalid");
+                            "AddNodes: The parent reference is invalid", NULL);
         deleteNode(server, &adminSession, nodeId, true);
         return retval;
     }
@@ -851,7 +852,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
             const UA_VariableNode *vn = (const UA_VariableNode*)node;
             if(!(vn->accessLevel & (UA_ACCESSLEVELMASK_READ))) {
                 UA_LOG_INFO_SESSION(server->config.logger, session,
-                                    "AddNodes: Set the AccessLevel to readable by default");
+                                    "AddNodes: Set the AccessLevel to readable by default", NULL);
                 UA_Byte readable = vn->accessLevel | (UA_ACCESSLEVELMASK_READ);
                 UA_Server_writeAccessLevel(server, vn->nodeId, readable);
             }
@@ -866,10 +867,10 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
         ref_item.referenceTypeId = *referenceTypeId;
         ref_item.isForward = false;
         ref_item.targetNodeId.nodeId = *parentNodeId;
-        retval = addReference(server, session, &ref_item);
+        addReference(server, session, &ref_item, &retval);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "AddNodes: Adding reference to parent failed");
+                                "AddNodes: Adding reference to parent failed", NULL);
             deleteNode(server, &adminSession, nodeId, true);
             return retval;
         }
@@ -903,7 +904,8 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing AddNodesRequest");
+                         "Processing AddNodesRequest", NULL);
+
     if(request->nodesToAddSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -917,7 +919,6 @@ void Service_AddNodes(UA_Server *server, UA_Session *session,
         return;
     }
 
-
     response->resultsSize = size;
     for(size_t i = 0; i < size; ++i) {
             Service_AddNodes_single(server, session, &request->nodesToAdd[i],
@@ -1279,21 +1280,24 @@ addOneWayReference(UA_Server *server, UA_Session *session,
     return addOneWayNodeReferences(node, item);
 }
 
-static UA_StatusCode
+static void
 addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item) {
+             const UA_AddReferencesItem *item,
+             UA_StatusCode *retval) {
     /* Currently no expandednodeids are allowed */
-    if(item->targetServerUri.length > 0)
-        return UA_STATUSCODE_BADNOTIMPLEMENTED;
+    if(item->targetServerUri.length > 0) {
+        *retval = UA_STATUSCODE_BADNOTIMPLEMENTED;
+        return;
+    }
 
     /* Add the first direction */
     UA_RCU_UNLOCK();
-    UA_StatusCode retval =
+    *retval =
         UA_Server_editNode(server, session, &item->sourceNodeId,
                            (UA_EditNodeCallback)addOneWayReference, item);
     UA_RCU_LOCK();
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+    if(*retval != UA_STATUSCODE_GOOD)
+        return;
 
     /* Add the second direction */
     UA_AddReferencesItem secondItem;
@@ -1304,12 +1308,13 @@ addReference(UA_Server *server, UA_Session *session,
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
     UA_RCU_UNLOCK();
-    retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
-                                (UA_EditNodeCallback)addOneWayReference, &secondItem);
+    *retval =
+        UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                           (UA_EditNodeCallback)addOneWayReference, &secondItem);
     UA_RCU_LOCK();
 
     /* remove reference if the second direction failed */
-    if(retval != UA_STATUSCODE_GOOD) {
+    if(*retval != UA_STATUSCODE_GOOD) {
         UA_DeleteReferencesItem deleteItem;
         deleteItem.sourceNodeId = item->sourceNodeId;
         deleteItem.referenceTypeId = item->referenceTypeId;
@@ -1322,31 +1327,20 @@ addReference(UA_Server *server, UA_Session *session,
                            (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
         UA_RCU_LOCK();
     }
-    return retval;
 }
 
 void Service_AddReferences(UA_Server *server, UA_Session *session,
                            const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing AddReferencesRequest");
-    if(request->referencesToAddSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    /* Create the results array */
-    response->results =
-        (UA_StatusCode*)UA_malloc(sizeof(UA_StatusCode) * request->referencesToAddSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->referencesToAddSize;
-
-    for(size_t i = 0; i < response->resultsSize; ++i)
-        response->results[i] =
-            addReference(server, session, &request->referencesToAdd[i]);
+                         "Processing AddReferencesRequest", NULL);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                                           (UA_ServiceOperation) addReference,
+                                           &request->referencesToAddSize,
+                                           &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
+                                           &response->resultsSize,
+                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1360,8 +1354,10 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
     item.referenceTypeId = refTypeId;
     item.isForward = isForward;
     item.targetNodeId = targetId;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_RCU_LOCK();
-    UA_StatusCode retval = addReference(server, &adminSession, &item);
+    addReference(server, &adminSession, &item, &retval);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1376,13 +1372,14 @@ removeReferences(UA_Server *server, UA_Session *session,
     UA_DeleteReferencesItem item;
     UA_DeleteReferencesItem_init(&item);
     item.targetNodeId.nodeId = node->nodeId;
+    UA_StatusCode dummy;
     for(size_t i = 0; i < node->referencesSize; ++i) {
         UA_NodeReferenceKind *refs = &node->references[i];
         item.isForward = refs->isInverse;
         item.referenceTypeId = refs->referenceTypeId;
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
             item.sourceNodeId = refs->targetIds[j].nodeId;
-            deleteReference(server, session, &item);
+            deleteReference(server, session, &item, &dummy);
         }
     }
 }
@@ -1428,7 +1425,8 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session,
                          const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteNodesRequest");
+                         "Processing DeleteNodesRequest", NULL);
+
     if(request->nodesToDeleteSize == 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -1482,7 +1480,9 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
 
             /* One matching target remaining */
             if(refs->targetIdsSize > 0) {
-                if  (j-1 != refs->targetIdsSize) // avoid valgrind error: Source and destination overlap in memcpy
+                if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
+                                               // and destination overlap in
+                                               // memcpy
                     refs->targetIds[j-1] = refs->targetIds[refs->targetIdsSize];
                 return UA_STATUSCODE_GOOD;
             }
@@ -1492,7 +1492,9 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
             UA_NodeId_deleteMembers(&refs->referenceTypeId);
             node->referencesSize--;
             if(node->referencesSize > 0) {
-                if (i-1 != node->referencesSize) // avoid valgrind error: Source and destination overlap in memcpy
+                if(i-1 != node->referencesSize) // avoid valgrind error: Source
+                                                // and destination overlap in
+                                                // memcpy
                     node->references[i-1] = node->references[node->referencesSize];
                 return UA_STATUSCODE_GOOD;
             }
@@ -1506,24 +1508,27 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
     return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
 }
 
-static UA_StatusCode
+static void
 deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item) {
-    UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
-                                              (UA_EditNodeCallback)deleteOneWayReference, item);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+                const UA_DeleteReferencesItem *item,
+                UA_StatusCode *retval) {
+    *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+                                 (UA_EditNodeCallback)deleteOneWayReference, item);
+    if(*retval != UA_STATUSCODE_GOOD)
+        return;
+
     if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
-        return retval;
+        return;
+
     UA_DeleteReferencesItem secondItem;
     UA_DeleteReferencesItem_init(&secondItem);
     secondItem.isForward = !item->isForward;
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     secondItem.referenceTypeId = item->referenceTypeId;
-    return UA_Server_editNode(server, session, &secondItem.sourceNodeId,
-                              (UA_EditNodeCallback)deleteOneWayReference,
-                              &secondItem);
+    *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+                                 (UA_EditNodeCallback)deleteOneWayReference,
+                                 &secondItem);
 }
 
 void
@@ -1531,23 +1536,14 @@ Service_DeleteReferences(UA_Server *server, UA_Session *session,
                          const UA_DeleteReferencesRequest *request,
                          UA_DeleteReferencesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteReferencesRequest");
-    if(request->referencesToDeleteSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->referencesToDeleteSize;
-    response->results = (UA_StatusCode*)UA_malloc(sizeof(UA_StatusCode) * size);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;;
-        return;
-    }
-    response->resultsSize = size;
-
-    for(size_t i = 0; i < size; ++i)
-        response->results[i] =
-            deleteReference(server, session, &request->referencesToDelete[i]);
+                         "Processing DeleteReferencesRequest", NULL);
+    response->responseHeader.serviceResult =
+        UA_Server_processServiceOperations(server, session,
+                                           (UA_ServiceOperation)deleteReference,
+                                           &request->referencesToDeleteSize,
+                                           &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
+                                           &response->resultsSize,
+                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1561,8 +1557,10 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
     item.isForward = isForward;
     item.targetNodeId = targetNodeId;
     item.deleteBidirectional = deleteBidirectional;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_RCU_LOCK();
-    UA_StatusCode retval = deleteReference(server, &adminSession, &item);
+    deleteReference(server, &adminSession, &item, &retval);
     UA_RCU_UNLOCK();
     return retval;
 }

+ 2 - 1
src/server/ua_services_securechannel.c

@@ -41,6 +41,7 @@ void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
 
 /* The server does not send a CloseSecureChannel response */
 void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel) {
-    UA_LOG_INFO_CHANNEL(server->config.logger, channel, "CloseSecureChannel");
+    UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                        "CloseSecureChannel", NULL);
     UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId);
 }

+ 8 - 4
src/server/ua_services_session.c

@@ -31,7 +31,8 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     response->responseHeader.serviceResult =
         UA_SessionManager_createSession(&server->sessionManager, channel, request, &newSession);
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Processing CreateSessionRequest failed");
+        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                             "Processing CreateSessionRequest failed", NULL);
         return;
     }
 
@@ -81,7 +82,8 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
 
     /* Detach the old SecureChannel */
     if(session->channel && session->channel != channel) {
-        UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: Detach from old channel");
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "ActivateSession: Detach from old channel", NULL);
         UA_SecureChannel_detachSession(session->channel, session);
     }
 
@@ -89,13 +91,15 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
     UA_SecureChannel_attachSession(channel, session);
     session->activated = true;
     UA_Session_updateLifetime(session);
-    UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: Session activated");
+    UA_LOG_INFO_SESSION(server->config.logger, session,
+                        "ActivateSession: Session activated", NULL);
 }
 
 void
 Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
                      UA_CloseSessionResponse *response) {
-    UA_LOG_INFO_SESSION(server->config.logger, session, "CloseSession");
+    UA_LOG_INFO_SESSION(server->config.logger, session,
+                        "CloseSession", NULL);
     /* Callback into userland access control */
     server->config.accessControl.closeSession(&session->sessionId, session->sessionHandle);
     response->responseHeader.serviceResult =

+ 173 - 173
src/server/ua_services_subscription.c

@@ -61,7 +61,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     if(!newSubscription) {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "Processing CreateSubscriptionRequest failed");
+                             "Processing CreateSubscriptionRequest failed", NULL);
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
@@ -92,7 +92,8 @@ Service_ModifySubscription(UA_Server *server, UA_Session *session,
                            const UA_ModifySubscriptionRequest *request,
                            UA_ModifySubscriptionResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing ModifySubscriptionRequest");
+                         "Processing ModifySubscriptionRequest", NULL);
+
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -108,37 +109,39 @@ Service_ModifySubscription(UA_Server *server, UA_Session *session,
     response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount;
 }
 
+static UA_THREAD_LOCAL UA_Boolean op_publishingEnabled;
+
+static void
+Operation_SetPublishingMode(UA_Server *Server, UA_Session *session,
+                            UA_UInt32 *subscriptionId,
+                            UA_StatusCode *result) {
+    UA_Subscription *sub =
+        UA_Session_getSubscriptionByID(session, *subscriptionId);
+    if(!sub) {
+        *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        return;
+    }
+
+    /* Reset the subscription lifetime */
+    sub->currentLifetimeCount = 0; 
+
+    /* Set the publishing mode */
+    sub->publishingEnabled = op_publishingEnabled;
+}
+
 void
 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
                           const UA_SetPublishingModeRequest *request,
                           UA_SetPublishingModeResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing SetPublishingModeRequest");
-    if(request->subscriptionIdsSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->subscriptionIdsSize;
-    response->results = (UA_StatusCode *)UA_Array_new(size, &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-
-    response->resultsSize = size;
-    for(size_t i = 0; i < size; ++i) {
-        UA_Subscription *sub =
-            UA_Session_getSubscriptionByID(session, request->subscriptionIds[i]);
-        if(!sub) {
-            response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-            continue;
-        }
-        if(sub->publishingEnabled != request->publishingEnabled) {
-            sub->publishingEnabled = request->publishingEnabled;
-            sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
-        }
-    }
+                         "Processing SetPublishingModeRequest", NULL);
+
+    op_publishingEnabled = request->publishingEnabled;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_SetPublishingMode,
+                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 static void
@@ -194,18 +197,19 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 /* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_Subscription *op_sub;
+static UA_THREAD_LOCAL UA_TimestampsToReturn op_timestampsToReturn2;
+
 static void
-Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
-                                    UA_Subscription *sub,
-                                    const UA_TimestampsToReturn timestampsToReturn,
-                                    const UA_MonitoredItemCreateRequest *request,
-                                    UA_MonitoredItemCreateResult *result) {
+Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session,
+                              const UA_MonitoredItemCreateRequest *request,
+                              UA_MonitoredItemCreateResult *result) {
     /* Make an example read to get errors in the itemToMonitor. Allow return
      * codes "good" and "uncertain", as well as a list of statuscodes that might
      * be repaired inside the data source. */
-    UA_DataValue v;
-    UA_DataValue_init(&v);
-    Service_Read_single(server, session, timestampsToReturn, &request->itemToMonitor, &v);
+    UA_DataValue v = UA_Server_readWithSession(server, session, &request->itemToMonitor,
+                                               op_timestampsToReturn2);
     if(v.hasStatus && (v.status >> 30) > 1 &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR &&
@@ -244,13 +248,13 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,
         MonitoredItem_delete(server, newMon);
         return;
     }
-    newMon->subscription = sub;
+    newMon->subscription = op_sub;
     newMon->attributeID = request->itemToMonitor.attributeId;
-    newMon->itemId = ++(sub->lastMonitoredItemId);
-    newMon->timestampsToReturn = timestampsToReturn;
+    newMon->itemId = ++(op_sub->lastMonitoredItemId);
+    newMon->timestampsToReturn = op_timestampsToReturn2;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
                              &request->requestedParameters);
-    LIST_INSERT_HEAD(&sub->monitoredItems, newMon, listEntry);
+    LIST_INSERT_HEAD(&op_sub->monitoredItems, newMon, listEntry);
 
     /* Create the first sample */
     if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
@@ -268,45 +272,39 @@ Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                              const UA_CreateMonitoredItemsRequest *request,
                              UA_CreateMonitoredItemsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing CreateMonitoredItemsRequest");
+                         "Processing CreateMonitoredItemsRequest", NULL);
 
     /* Check if the timestampstoreturn is valid */
-    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+    op_timestampsToReturn2 = request->timestampsToReturn;
+    if(op_timestampsToReturn2 > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    /* Find the subscription */
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    if(request->itemsToCreateSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    op_sub->currentLifetimeCount = 0;
 
-    response->results = (UA_MonitoredItemCreateResult *)UA_Array_new(request->itemsToCreateSize,
-                                     &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->itemsToCreateSize;
-
-    for(size_t i = 0; i < request->itemsToCreateSize; ++i)
-        Service_CreateMonitoredItems_single(server, session, sub, request->timestampsToReturn,
-                                            &request->itemsToCreate[i], &response->results[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_CreateMonitoredItem,
+                  &request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
 }
 
 static void
-Service_ModifyMonitoredItems_single(UA_Server *server, UA_Session *session, UA_Subscription *sub,
-                                    const UA_MonitoredItemModifyRequest *request,
-                                    UA_MonitoredItemModifyResult *result) {
-    UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemId);
+Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session,
+                              const UA_MonitoredItemModifyRequest *request,
+                              UA_MonitoredItemModifyResult *result) {
+    /* Get the MonitoredItem */
+    UA_MonitoredItem *mon =
+        UA_Subscription_getMonitoredItem(op_sub, request->monitoredItemId);
     if(!mon) {
         result->statusCode = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
@@ -322,79 +320,77 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   UA_ModifyMonitoredItemsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing ModifyMonitoredItemsRequest");
+                         "Processing ModifyMonitoredItemsRequest", NULL);
 
-    /* check if the timestampstoreturn is valid */
+    /* Check if the timestampstoreturn is valid */
     if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
     /* Get the subscription */
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    if(request->itemsToModifySize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    op_sub->currentLifetimeCount = 0;
 
-    response->results = (UA_MonitoredItemModifyResult *)UA_Array_new(request->itemsToModifySize,
-                                     &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_ModifyMonitoredItem,
+                  &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
+}
+
+/* Get the additional argument into the operation */
+static UA_THREAD_LOCAL UA_MonitoringMode op_monitoringMode;
+
+static void
+Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
+                            UA_UInt32 *monitoredItemId,
+                            UA_StatusCode *result) {
+    UA_MonitoredItem *mon =
+        UA_Subscription_getMonitoredItem(op_sub, *monitoredItemId);
+    if(!mon) {
+        *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
     }
-    response->resultsSize = request->itemsToModifySize;
 
-    for(size_t i = 0; i < request->itemsToModifySize; ++i)
-        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i],
-                                            &response->results[i]);
+    if(mon->monitoringMode == op_monitoringMode)
+        return;
 
+    mon->monitoringMode = op_monitoringMode;
+    if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
+        MonitoredItem_registerSampleCallback(server, mon);
+    else
+        MonitoredItem_unregisterSampleCallback(server, mon);
 }
 
 void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
                                const UA_SetMonitoringModeRequest *request,
                                UA_SetMonitoringModeResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing SetMonitoringMode");
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
-        return;
-    }
-
-    if(request->monitoredItemIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing SetMonitoringMode", NULL);
 
-    response->results = (UA_StatusCode *)UA_Array_new(request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    /* Get the subscription */
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
-    response->resultsSize = request->monitoredItemIdsSize;
 
-    for(size_t i = 0; i < response->resultsSize; ++i) {
-        UA_MonitoredItem *mon =
-            UA_Subscription_getMonitoredItem(sub, request->monitoredItemIds[i]);
-        if(!mon) {
-            response->results[i] = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
-            continue;
-        }
-        if(request->monitoringMode == mon->monitoringMode)
-            continue;
-        mon->monitoringMode = request->monitoringMode;
-        if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
-            MonitoredItem_registerSampleCallback(server, mon);
-        else
-            MonitoredItem_unregisterSampleCallback(server, mon);
-    }
+    /* Reset the subscription lifetime */
+    op_sub->currentLifetimeCount = 0;
+
+    op_monitoringMode = request->monitoringMode;
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_SetMonitoringMode,
+                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 /* TODO: Unify with senderror in ua_server_binary.c */
@@ -413,7 +409,8 @@ subscriptionSendError(UA_SecureChannel *channel, UA_UInt32 requestHandle,
 void
 Service_Publish(UA_Server *server, UA_Session *session,
                 const UA_PublishRequest *request, UA_UInt32 requestId) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing PublishRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing PublishRequest", NULL);
 
     /* Return an error if the session has no subscription */
     if(LIST_EMPTY(&session->serverSubscriptions)) {
@@ -422,10 +419,12 @@ Service_Publish(UA_Server *server, UA_Session *session,
         return;
     }
 
-    UA_PublishResponseEntry *entry = (UA_PublishResponseEntry *)UA_malloc(sizeof(UA_PublishResponseEntry));
+    UA_PublishResponseEntry *entry =
+        (UA_PublishResponseEntry*)UA_malloc(sizeof(UA_PublishResponseEntry));
     if(!entry) {
         subscriptionSendError(session->channel, requestId,
-                              request->requestHeader.requestHandle, UA_STATUSCODE_BADOUTOFMEMORY);
+                              request->requestHeader.requestHandle,
+                              UA_STATUSCODE_BADOUTOFMEMORY);
         return;
     }
     entry->requestId = requestId;
@@ -435,8 +434,9 @@ Service_Publish(UA_Server *server, UA_Session *session,
     UA_PublishResponse_init(response);
     response->responseHeader.requestHandle = request->requestHeader.requestHandle;
     if(request->subscriptionAcknowledgementsSize > 0) {
-        response->results = (UA_StatusCode *)UA_Array_new(request->subscriptionAcknowledgementsSize,
-                                         &UA_TYPES[UA_TYPES_STATUSCODE]);
+        response->results = (UA_StatusCode*)
+            UA_Array_new(request->subscriptionAcknowledgementsSize,
+                         &UA_TYPES[UA_TYPES_STATUSCODE]);
         if(!response->results) {
             UA_free(entry);
             subscriptionSendError(session->channel, requestId,
@@ -459,7 +459,8 @@ Service_Publish(UA_Server *server, UA_Session *session,
             continue;
         }
         /* Remove the acked transmission from the retransmission queue */
-        response->results[i] = UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber);
+        response->results[i] =
+            UA_Subscription_removeRetransmissionMessage(sub, ack->sequenceNumber);
     }
 
     /* Queue the publish response */
@@ -480,85 +481,82 @@ Service_Publish(UA_Server *server, UA_Session *session,
     }
 }
 
+static void
+Operation_DeleteSubscription(UA_Server *server, UA_Session *session,
+                             UA_UInt32 *subscriptionId, UA_StatusCode *result) {
+    *result = UA_Session_deleteSubscription(server, session, *subscriptionId);
+    if(*result == UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "Subscription %u | Subscription deleted",
+                             *subscriptionId);
+    } else {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "Deleting Subscription with Id %u failed with error "
+                             "code %s", *subscriptionId, UA_StatusCode_name(*result));
+    }
+}
+
 void
 Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                             const UA_DeleteSubscriptionsRequest *request,
                             UA_DeleteSubscriptionsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteSubscriptionsRequest");
+                         "Processing DeleteSubscriptionsRequest", NULL);
 
-    if(request->subscriptionIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteSubscription,
+                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 
-    response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->subscriptionIdsSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    /* The session has at least one subscription */
+    if(LIST_FIRST(&session->serverSubscriptions))
         return;
-    }
-    response->resultsSize = request->subscriptionIdsSize;
 
-    for(size_t i = 0; i < request->subscriptionIdsSize; ++i) {
-        response->results[i] = UA_Session_deleteSubscription(server, session, request->subscriptionIds[i]);
-        if(response->results[i] == UA_STATUSCODE_GOOD) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Subscription %u | "
-                                "Subscription deleted", request->subscriptionIds[i]);
-        } else {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Deleting Subscription with Id "
-                                 "%u failed with error code 0x%08x", request->subscriptionIds[i],
-                                 response->results[i]);
-        }
-    }
-
-    /* Send dangling publish responses in a delayed callback if the last
+    /* Send remaining publish responses in a delayed callback if the last
      * subscription was removed */
-    if(LIST_FIRST(&session->serverSubscriptions))
-        return;
-    UA_NodeId *sessionToken = UA_NodeId_new();
-    if(!sessionToken)
-        return;
-    UA_NodeId_copy(&session->authenticationToken, sessionToken);
-    UA_Server_delayedCallback(server,
-                              (UA_ServerCallback)UA_Subscription_answerPublishRequestsNoSubscription,
-                              sessionToken);
+    UA_Server_delayedCallback(server, (UA_ServerCallback)
+                              UA_Subscription_answerPublishRequestsNoSubscription,
+                              session);
+}
+
+static void
+Operation_DeleteMonitoredItem(UA_Server *server, UA_Session *session,
+                              UA_UInt32 *monitoredItemId,
+                              UA_StatusCode *result) {
+    *result = UA_Subscription_deleteMonitoredItem(server, op_sub, *monitoredItemId);
 }
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_DeleteMonitoredItemsRequest *request,
                                   UA_DeleteMonitoredItemsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing DeleteMonitoredItemsRequest");
-
-    if(request->monitoredItemIdsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+                         "Processing DeleteMonitoredItemsRequest", NULL);
 
     /* Get the subscription */
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!sub) {
+    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!op_sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0;
-    response->results = (UA_StatusCode *)UA_malloc(sizeof(UA_StatusCode) * request->monitoredItemIdsSize);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = request->monitoredItemIdsSize;
+    op_sub->currentLifetimeCount = 0;
 
-    for(size_t i = 0; i < request->monitoredItemIdsSize; ++i)
-        response->results[i] = UA_Subscription_deleteMonitoredItem(server, sub, request->monitoredItemIds[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_DeleteMonitoredItem,
+                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
-void Service_Republish(UA_Server *server, UA_Session *session, const UA_RepublishRequest *request,
+void Service_Republish(UA_Server *server, UA_Session *session,
+                       const UA_RepublishRequest *request,
                        UA_RepublishResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing RepublishRequest");
-    /* get the subscription */
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing RepublishRequest", NULL);
+
+    /* Get the subscription */
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if (!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -574,11 +572,13 @@ void Service_Republish(UA_Server *server, UA_Session *session, const UA_Republis
         if(entry->message.sequenceNumber == request->retransmitSequenceNumber)
             break;
     }
-    if(entry)
-        response->responseHeader.serviceResult =
-            UA_NotificationMessage_copy(&entry->message, &response->notificationMessage);
-    else
+    if(!entry) {
       response->responseHeader.serviceResult = UA_STATUSCODE_BADMESSAGENOTAVAILABLE;
+      return;
+    }
+
+    response->responseHeader.serviceResult =
+        UA_NotificationMessage_copy(&entry->message, &response->notificationMessage);
 }
 
 #endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 31 - 48
src/server/ua_services_view.c

@@ -267,7 +267,7 @@ void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request,
                     UA_BrowseResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing BrowseRequest");
+                         "Processing BrowseRequest", NULL);
 
     if(!UA_NodeId_isNull(&request->view.viewId)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADVIEWIDUNKNOWN;
@@ -306,11 +306,12 @@ UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs,
     return result;
 }
 
+/* Thread-local variables to pass additional arguments into the operation */
+static UA_THREAD_LOCAL UA_Boolean op_releaseContinuationPoint;
+
 static void
-browseNext(UA_Server *server, UA_Session *session,
-           UA_Boolean releaseContinuationPoint,
-           const UA_ByteString *continuationPoint,
-           UA_BrowseResult *result) {
+Operation_BrowseNext(UA_Server *server, UA_Session *session,
+           const UA_ByteString *continuationPoint, UA_BrowseResult *result) {
     /* Find the continuation point */
     ContinuationPointEntry *cp;
     LIST_FOREACH(cp, &session->continuationPoints, pointers) {
@@ -323,7 +324,7 @@ browseNext(UA_Server *server, UA_Session *session,
     }
 
     /* Do the work */
-    if(!releaseContinuationPoint)
+    if(!op_releaseContinuationPoint)
         Service_Browse_single(server, session, cp, NULL, 0, result);
     else
         removeCp(cp, session);
@@ -334,25 +335,15 @@ Service_BrowseNext(UA_Server *server, UA_Session *session,
                    const UA_BrowseNextRequest *request,
                    UA_BrowseNextResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing BrowseNextRequest");
-    if(request->continuationPointsSize == 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
+                         "Processing BrowseNextRequest", NULL);
 
-    /* Allocate the result array */
-    size_t size = request->continuationPointsSize;
-    response->results =
-        (UA_BrowseResult*)UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSERESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = size;
+    op_releaseContinuationPoint = request->releaseContinuationPoints;
 
-    for(size_t i = 0; i < size; ++i)
-        browseNext(server, session, request->releaseContinuationPoints,
-                   &request->continuationPoints[i], &response->results[i]);
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_BrowseNext,
+                  &request->continuationPointsSize, &UA_TYPES[UA_TYPES_BYTESTRING],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]);
 }
 
 UA_BrowseResult
@@ -360,9 +351,10 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
                      const UA_ByteString *continuationPoint) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
+    op_releaseContinuationPoint = releaseContinuationPoint;
     UA_RCU_LOCK();
-    browseNext(server, &adminSession, releaseContinuationPoint,
-               continuationPoint, &result);
+    Operation_BrowseNext(server, &adminSession,
+                         continuationPoint, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -567,8 +559,9 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
 }
 
 static void
-translateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
-                             const UA_BrowsePath *path, UA_BrowsePathResult *result) {
+Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
+                                       const UA_BrowsePath *path,
+                                       UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -653,7 +646,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
     UA_RCU_LOCK();
-    translateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
+    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -663,32 +656,21 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                       const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                       UA_TranslateBrowsePathsToNodeIdsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing TranslateBrowsePathsToNodeIdsRequest");
-    if(request->browsePathsSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    size_t size = request->browsePathsSize;
-    response->results =
-        (UA_BrowsePathResult*)UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-
-    response->resultsSize = size;
-    for(size_t i = 0; i < size; ++i)
-        translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
-                                     &response->results[i]);
+                         "Processing TranslateBrowsePathsToNodeIdsRequest", NULL);
 
+    response->responseHeader.serviceResult = 
+        UA_Server_processServiceOperations(server, session,
+                  (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
+                  &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
+                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session,
                            const UA_RegisterNodesRequest *request,
                            UA_RegisterNodesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing RegisterNodesRequest");
+                         "Processing RegisterNodesRequest", NULL);
+
     //TODO: hang the nodeids to the session if really needed
     if(request->nodesToRegisterSize == 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
@@ -706,7 +688,8 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
                              const UA_UnregisterNodesRequest *request,
                              UA_UnregisterNodesResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing UnRegisterNodesRequest");
+                         "Processing UnRegisterNodesRequest", NULL);
+
     //TODO: remove the nodeids from the session if really needed
     if(request->nodesToUnregisterSize == 0)
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

+ 2 - 2
src/server/ua_session_manager.c

@@ -60,7 +60,7 @@ UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm,
         if(sentry->session.validTill >= nowMonotonic)
             continue;
         UA_LOG_INFO_SESSION(sm->server->config.logger, &sentry->session,
-                            "Session has timed out");
+                            "Session has timed out", NULL);
         sm->server->config.accessControl.closeSession(&sentry->session.sessionId,
                                                       sentry->session.sessionHandle);
         removeSession(sm, sentry);
@@ -78,7 +78,7 @@ UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
         /* Session has timed out */
         if(UA_DateTime_nowMonotonic() > current->session.validTill) {
             UA_LOG_INFO_SESSION(sm->server->config.logger, &current->session,
-                                "Client tries to use a session that has timed out");
+                                "Client tries to use a session that has timed out", NULL);
             return NULL;
         }
 

+ 30 - 290
src/server/ua_subscription.c

@@ -4,274 +4,9 @@
 
 #include "ua_subscription.h"
 #include "ua_server_internal.h"
-#include "ua_types_encoding_binary.h"
-#include "ua_services.h"
-#include "ua_nodestore.h"
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
-#define UA_VALUENCODING_MAXSTACK 512
-
-/*****************/
-/* MonitoredItem */
-/*****************/
-
-UA_MonitoredItem *
-UA_MonitoredItem_new(void) {
-    /* Allocate the memory */
-    UA_MonitoredItem *newItem =
-        (UA_MonitoredItem*)UA_calloc(1, sizeof(UA_MonitoredItem));
-    if(!newItem)
-        return NULL;
-
-    /* Remaining members are covered by calloc zeroing out the memory */
-    newItem->monitoredItemType = UA_MONITOREDITEMTYPE_CHANGENOTIFY; /* currently hardcoded */
-    newItem->timestampsToReturn = UA_TIMESTAMPSTORETURN_SOURCE;
-    TAILQ_INIT(&newItem->queue);
-    return newItem;
-}
-
-void
-MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
-    /* Remove the sampling callback */
-    MonitoredItem_unregisterSampleCallback(server, monitoredItem);
-
-    /* Clear the queued samples */
-    MonitoredItem_queuedValue *val, *val_tmp;
-    TAILQ_FOREACH_SAFE(val, &monitoredItem->queue, listEntry, val_tmp) {
-        TAILQ_REMOVE(&monitoredItem->queue, val, listEntry);
-        UA_DataValue_deleteMembers(&val->value);
-        UA_free(val);
-    }
-    monitoredItem->currentQueueSize = 0;
-
-    /* Remove the monitored item */
-    LIST_REMOVE(monitoredItem, listEntry);
-    UA_String_deleteMembers(&monitoredItem->indexRange);
-    UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
-    UA_NodeId_deleteMembers(&monitoredItem->monitoredNodeId);
-    UA_free(monitoredItem); // TODO: Use a delayed free
-}
-
-static void
-ensureSpaceInMonitoredItemQueue(UA_MonitoredItem *mon) {
-    /* Enough space, nothing to do here */
-    if(mon->currentQueueSize < mon->maxQueueSize)
-        return;
-
-    /* Get the item to remove */
-    MonitoredItem_queuedValue *queueItem;
-    if(mon->discardOldest)
-        queueItem = TAILQ_FIRST(&mon->queue);
-    else
-        queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
-    UA_assert(queueItem);
-
-    /* Remove the item */
-    TAILQ_REMOVE(&mon->queue, queueItem, listEntry);
-    UA_DataValue_deleteMembers(&queueItem->value);
-    UA_free(queueItem);
-    --mon->currentQueueSize;
-}
-
-/* Errors are returned as no change detected */
-static UA_Boolean
-detectValueChangeWithFilter(UA_MonitoredItem *mon, UA_DataValue *value,
-                            UA_ByteString *encoding) {
-    /* Encode the data for comparison */
-    size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]);
-    if(binsize == 0)
-        return false;
-
-    /* Allocate buffer on the heap if necessary */
-    if(binsize > UA_VALUENCODING_MAXSTACK &&
-       UA_ByteString_allocBuffer(encoding, binsize) != UA_STATUSCODE_GOOD)
-        return false;
-
-    /* Encode the value */
-    UA_Byte *bufPos = encoding->data;
-    const UA_Byte *bufEnd = &encoding->data[encoding->length];
-    UA_StatusCode retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE],
-                                           &bufPos, &bufEnd, NULL, NULL);
-    if(retval != UA_STATUSCODE_GOOD)
-        return false;
-
-    /* The value has changed */
-    encoding->length = (uintptr_t)bufPos - (uintptr_t)encoding->data;
-    return !mon->lastSampledValue.data || !UA_String_equal(encoding, &mon->lastSampledValue);
-}
-
-/* Has this sample changed from the last one? The method may allocate additional
- * space for the encoding buffer. Detect the change in encoding->data. */
-static UA_Boolean
-detectValueChange(UA_MonitoredItem *mon, UA_DataValue *value, UA_ByteString *encoding) {
-    /* Apply Filter */
-    UA_Boolean hasValue = value->hasValue;
-    if(mon->trigger == UA_DATACHANGETRIGGER_STATUS)
-        value->hasValue = false;
-
-    UA_Boolean hasServerTimestamp = value->hasServerTimestamp;
-    UA_Boolean hasServerPicoseconds = value->hasServerPicoseconds;
-    value->hasServerTimestamp = false;
-    value->hasServerPicoseconds = false;
-
-    UA_Boolean hasSourceTimestamp = value->hasSourceTimestamp;
-    UA_Boolean hasSourcePicoseconds = value->hasSourcePicoseconds;
-    if(mon->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
-        value->hasSourceTimestamp = false;
-        value->hasSourcePicoseconds = false;
-    }
-
-    /* Detect the Value Change */
-    UA_Boolean res = detectValueChangeWithFilter(mon, value, encoding);
-
-    /* Reset the filter */
-    value->hasValue = hasValue;
-    value->hasServerTimestamp = hasServerTimestamp;
-    value->hasServerPicoseconds = hasServerPicoseconds;
-    value->hasSourceTimestamp = hasSourceTimestamp;
-    value->hasSourcePicoseconds = hasSourcePicoseconds;
-    return res;
-}
-
-/* Returns whether a new sample was created */
-static UA_Boolean
-sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
-                        UA_MonitoredItem *monitoredItem,
-                        UA_DataValue *value,
-                        UA_ByteString *valueEncoding) {
-    /* Store the pointer to the stack-allocated bytestring to see if a heap-allocation
-     * was necessary */
-    UA_Byte *stackValueEncoding = valueEncoding->data;
-
-    /* Has the value changed? */
-    UA_Boolean changed = detectValueChange(monitoredItem, value, valueEncoding);
-    if(!changed)
-        return false;
-
-    /* Allocate the entry for the publish queue */
-    MonitoredItem_queuedValue *newQueueItem =
-        (MonitoredItem_queuedValue *)UA_malloc(sizeof(MonitoredItem_queuedValue));
-    if(!newQueueItem) {
-        UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                               "Subscription %u | MonitoredItem %i | "
-                               "Item for the publishing queue could not be allocated",
-                               sub->subscriptionID, monitoredItem->itemId);
-        return false;
-    }
-
-    /* Copy valueEncoding on the heap for the next comparison (if not already done) */
-    if(valueEncoding->data == stackValueEncoding) {
-        UA_ByteString cbs;
-        if(UA_ByteString_copy(valueEncoding, &cbs) != UA_STATUSCODE_GOOD) {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                                   "Subscription %u | MonitoredItem %i | "
-                                   "ByteString to compare values could not be created",
-                                   sub->subscriptionID, monitoredItem->itemId);
-            UA_free(newQueueItem);
-            return false;
-        }
-        *valueEncoding = cbs;
-    }
-
-    /* Prepare the newQueueItem */
-    if(value->hasValue && value->value.storageType == UA_VARIANT_DATA_NODELETE) {
-        /* Make a deep copy of the value */
-        if(UA_DataValue_copy(value, &newQueueItem->value) != UA_STATUSCODE_GOOD) {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                                   "Subscription %u | MonitoredItem %i | "
-                                   "Item for the publishing queue could not be prepared",
-                                   sub->subscriptionID, monitoredItem->itemId);
-            UA_free(newQueueItem);
-            return false;
-        }
-    } else {
-        newQueueItem->value = *value; /* Just copy the value and do not release it */
-    }
-    newQueueItem->clientHandle = monitoredItem->clientHandle;
-
-    /* <-- Point of no return --> */
-
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Subscription %u | MonitoredItem %u | Sampled a new value",
-                         sub->subscriptionID, monitoredItem->itemId);
-
-    /* Replace the encoding for comparison */
-    UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
-    monitoredItem->lastSampledValue = *valueEncoding;
-
-    /* Add the sample to the queue for publication */
-    ensureSpaceInMonitoredItemQueue(monitoredItem);
-    TAILQ_INSERT_TAIL(&monitoredItem->queue, newQueueItem, listEntry);
-    ++monitoredItem->currentQueueSize;
-    return true;;
-}
-
-void
-UA_MoniteredItem_SampleCallback(UA_Server *server,
-                                UA_MonitoredItem *monitoredItem) {
-    UA_Subscription *sub = monitoredItem->subscription;
-    if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                             "Subscription %u | MonitoredItem %i | "
-                             "Not a data change notification",
-                             sub->subscriptionID, monitoredItem->itemId);
-        return;
-    }
-
-    /* Read the value */
-    UA_ReadValueId rvid;
-    UA_ReadValueId_init(&rvid);
-    rvid.nodeId = monitoredItem->monitoredNodeId;
-    rvid.attributeId = monitoredItem->attributeID;
-    rvid.indexRange = monitoredItem->indexRange;
-    UA_DataValue value;
-    UA_DataValue_init(&value);
-    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn,
-                        &rvid, &value);
-
-    /* Stack-allocate some memory for the value encoding. We might heap-allocate
-     * more memory if needed. This is just enough for scalars and small
-     * structures. */
-    UA_Byte *stackValueEncoding = (UA_Byte *)UA_alloca(UA_VALUENCODING_MAXSTACK);
-    UA_ByteString valueEncoding;
-    valueEncoding.data = stackValueEncoding;
-    valueEncoding.length = UA_VALUENCODING_MAXSTACK;
-
-    /* Create a sample and compare with the last value */
-    UA_Boolean newNotification = sampleCallbackWithValue(server, sub, monitoredItem,
-                                                         &value, &valueEncoding);
-
-    /* Clean up */
-    if(!newNotification) {
-        if(valueEncoding.data != stackValueEncoding)
-            UA_ByteString_deleteMembers(&valueEncoding);
-        UA_DataValue_deleteMembers(&value);
-    }
-}
-
-UA_StatusCode
-MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
-    UA_StatusCode retval =
-        UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_MoniteredItem_SampleCallback,
-                                      mon, (UA_UInt32)mon->samplingInterval, &mon->sampleCallbackId);
-    if(retval == UA_STATUSCODE_GOOD)
-        mon->sampleCallbackIsRegistered = true;
-    return retval;
-}
-
-UA_StatusCode
-MonitoredItem_unregisterSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
-    if(!mon->sampleCallbackIsRegistered)
-        return UA_STATUSCODE_GOOD;
-    mon->sampleCallbackIsRegistered = false;
-    return UA_Server_removeRepeatedCallback(server, mon->sampleCallbackId);
-}
-
-/****************/
-/* Subscription */
-/****************/
-
 UA_Subscription *
 UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID) {
     /* Allocate the memory */
@@ -321,17 +56,22 @@ UA_Subscription_getMonitoredItem(UA_Subscription *sub,
 }
 
 UA_StatusCode
-UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
+UA_Subscription_deleteMonitoredItem(UA_Server *server,
+                                    UA_Subscription *sub,
                                     UA_UInt32 monitoredItemID) {
+    /* Find the MonitoredItem */
     UA_MonitoredItem *mon;
     LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->itemId == monitoredItemID) {
-            LIST_REMOVE(mon, listEntry);
-            MonitoredItem_delete(server, mon);
-            return UA_STATUSCODE_GOOD;
-        }
+        if(mon->itemId == monitoredItemID)
+            break;
     }
-    return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
+    if(!mon)
+        return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
+
+    /* Remove the MonitoredItem */
+    LIST_REMOVE(mon, listEntry);
+    MonitoredItem_delete(server, mon);
+    return UA_STATUSCODE_GOOD;
 }
 
 static size_t
@@ -377,17 +117,21 @@ UA_Subscription_addRetransmissionMessage(UA_Server *server, UA_Subscription *sub
 UA_StatusCode
 UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub,
                                             UA_UInt32 sequenceNumber) {
-    UA_NotificationMessageEntry *entry, *entry_tmp;
-    TAILQ_FOREACH_SAFE(entry, &sub->retransmissionQueue, listEntry, entry_tmp) {
-        if(entry->message.sequenceNumber != sequenceNumber)
-            continue;
-        TAILQ_REMOVE(&sub->retransmissionQueue, entry, listEntry);
-        --sub->retransmissionQueueSize;
-        UA_NotificationMessage_deleteMembers(&entry->message);
-        UA_free(entry);
-        return UA_STATUSCODE_GOOD;
+    /* Find the retransmission message */
+    UA_NotificationMessageEntry *entry;
+    TAILQ_FOREACH(entry, &sub->retransmissionQueue, listEntry) {
+        if(entry->message.sequenceNumber == sequenceNumber)
+            break;
     }
-    return UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
+    if(!entry)
+        return UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
+
+    /* Remove the retransmission message */
+    TAILQ_REMOVE(&sub->retransmissionQueue, entry, listEntry);
+    --sub->retransmissionQueueSize;
+    UA_NotificationMessage_deleteMembers(&entry->message);
+    UA_free(entry);
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
@@ -395,7 +139,7 @@ prepareNotificationMessage(UA_Subscription *sub,
                            UA_NotificationMessage *message,
                            size_t notifications) {
     /* Array of ExtensionObject to hold different kinds of notifications
-       (currently only DataChangeNotifications) */
+     * (currently only DataChangeNotifications) */
     message->notificationData = UA_ExtensionObject_new();
     if(!message->notificationData)
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -596,6 +340,7 @@ UA_StatusCode
 Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub) {
     if(!sub->publishCallbackIsRegistered)
         return UA_STATUSCODE_GOOD;
+
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Unregister subscription "
                          "publishing callback", sub->subscriptionID);
@@ -604,15 +349,10 @@ Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub)
 }
 
 /* When the session has publish requests stored but the last subscription is
-   deleted... Send out empty responses */
+ * deleted... Send out empty responses */
 void
 UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
-                                                    UA_NodeId *sessionToken) {
-    /* Get session */
-    UA_Session *session =
-        UA_SessionManager_getSession(&server->sessionManager, sessionToken);
-    UA_NodeId_delete(sessionToken);
-
+                                                    UA_Session *session) {
     /* No session or there are remaining subscriptions */
     if(!session || LIST_FIRST(&session->serverSubscriptions))
         return;

+ 1 - 3
src/server/ua_subscription.h

@@ -8,7 +8,6 @@
 #include "ua_util.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
-#include "ua_nodes.h"
 #include "ua_session.h"
 
 /*****************/
@@ -134,7 +133,6 @@ UA_StatusCode
 UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequenceNumber);
 
 void
-UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
-                                                    UA_NodeId *sessionToken);
+UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_Session *session);
 
 #endif /* UA_SUBSCRIPTION_H_ */

+ 265 - 0
src/server/ua_subscription_datachange.c

@@ -0,0 +1,265 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ua_subscription.h"
+#include "ua_server_internal.h"
+#include "ua_types_encoding_binary.h"
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
+
+#define UA_VALUENCODING_MAXSTACK 512
+
+UA_MonitoredItem *
+UA_MonitoredItem_new(void) {
+    /* Allocate the memory */
+    UA_MonitoredItem *newItem =
+        (UA_MonitoredItem*)UA_calloc(1, sizeof(UA_MonitoredItem));
+    if(!newItem)
+        return NULL;
+
+    /* Remaining members are covered by calloc zeroing out the memory */
+    newItem->monitoredItemType = UA_MONITOREDITEMTYPE_CHANGENOTIFY; /* currently hardcoded */
+    newItem->timestampsToReturn = UA_TIMESTAMPSTORETURN_SOURCE;
+    TAILQ_INIT(&newItem->queue);
+    return newItem;
+}
+
+void
+MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
+    /* Remove the sampling callback */
+    MonitoredItem_unregisterSampleCallback(server, monitoredItem);
+
+    /* Clear the queued samples */
+    MonitoredItem_queuedValue *val, *val_tmp;
+    TAILQ_FOREACH_SAFE(val, &monitoredItem->queue, listEntry, val_tmp) {
+        TAILQ_REMOVE(&monitoredItem->queue, val, listEntry);
+        UA_DataValue_deleteMembers(&val->value);
+        UA_free(val);
+    }
+    monitoredItem->currentQueueSize = 0;
+
+    /* Remove the monitored item */
+    LIST_REMOVE(monitoredItem, listEntry);
+    UA_String_deleteMembers(&monitoredItem->indexRange);
+    UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
+    UA_NodeId_deleteMembers(&monitoredItem->monitoredNodeId);
+    UA_free(monitoredItem); // TODO: Use a delayed free
+}
+
+static void
+ensureSpaceInMonitoredItemQueue(UA_MonitoredItem *mon) {
+    /* Enough space, nothing to do here */
+    if(mon->currentQueueSize < mon->maxQueueSize)
+        return;
+
+    /* Get the item to remove */
+    MonitoredItem_queuedValue *queueItem;
+    if(mon->discardOldest)
+        queueItem = TAILQ_FIRST(&mon->queue);
+    else
+        queueItem = TAILQ_LAST(&mon->queue, QueuedValueQueue);
+    UA_assert(queueItem);
+
+    /* Remove the item */
+    TAILQ_REMOVE(&mon->queue, queueItem, listEntry);
+    UA_DataValue_deleteMembers(&queueItem->value);
+    UA_free(queueItem);
+    --mon->currentQueueSize;
+}
+
+/* Errors are returned as no change detected */
+static UA_Boolean
+detectValueChangeWithFilter(UA_MonitoredItem *mon, UA_DataValue *value,
+                            UA_ByteString *encoding) {
+    /* Encode the data for comparison */
+    size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]);
+    if(binsize == 0)
+        return false;
+
+    /* Allocate buffer on the heap if necessary */
+    if(binsize > UA_VALUENCODING_MAXSTACK &&
+       UA_ByteString_allocBuffer(encoding, binsize) != UA_STATUSCODE_GOOD)
+        return false;
+
+    /* Encode the value */
+    UA_Byte *bufPos = encoding->data;
+    const UA_Byte *bufEnd = &encoding->data[encoding->length];
+    UA_StatusCode retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE],
+                                           &bufPos, &bufEnd, NULL, NULL);
+    if(retval != UA_STATUSCODE_GOOD)
+        return false;
+
+    /* The value has changed */
+    encoding->length = (uintptr_t)bufPos - (uintptr_t)encoding->data;
+    return !mon->lastSampledValue.data || !UA_String_equal(encoding, &mon->lastSampledValue);
+}
+
+/* Has this sample changed from the last one? The method may allocate additional
+ * space for the encoding buffer. Detect the change in encoding->data. */
+static UA_Boolean
+detectValueChange(UA_MonitoredItem *mon, UA_DataValue *value, UA_ByteString *encoding) {
+    /* Apply Filter */
+    UA_Boolean hasValue = value->hasValue;
+    if(mon->trigger == UA_DATACHANGETRIGGER_STATUS)
+        value->hasValue = false;
+
+    UA_Boolean hasServerTimestamp = value->hasServerTimestamp;
+    UA_Boolean hasServerPicoseconds = value->hasServerPicoseconds;
+    value->hasServerTimestamp = false;
+    value->hasServerPicoseconds = false;
+
+    UA_Boolean hasSourceTimestamp = value->hasSourceTimestamp;
+    UA_Boolean hasSourcePicoseconds = value->hasSourcePicoseconds;
+    if(mon->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
+        value->hasSourceTimestamp = false;
+        value->hasSourcePicoseconds = false;
+    }
+
+    /* Detect the Value Change */
+    UA_Boolean res = detectValueChangeWithFilter(mon, value, encoding);
+
+    /* Reset the filter */
+    value->hasValue = hasValue;
+    value->hasServerTimestamp = hasServerTimestamp;
+    value->hasServerPicoseconds = hasServerPicoseconds;
+    value->hasSourceTimestamp = hasSourceTimestamp;
+    value->hasSourcePicoseconds = hasSourcePicoseconds;
+    return res;
+}
+
+/* Returns whether a new sample was created */
+static UA_Boolean
+sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
+                        UA_MonitoredItem *monitoredItem,
+                        UA_DataValue *value,
+                        UA_ByteString *valueEncoding) {
+    /* Store the pointer to the stack-allocated bytestring to see if a heap-allocation
+     * was necessary */
+    UA_Byte *stackValueEncoding = valueEncoding->data;
+
+    /* Has the value changed? */
+    UA_Boolean changed = detectValueChange(monitoredItem, value, valueEncoding);
+    if(!changed)
+        return false;
+
+    /* Allocate the entry for the publish queue */
+    MonitoredItem_queuedValue *newQueueItem =
+        (MonitoredItem_queuedValue *)UA_malloc(sizeof(MonitoredItem_queuedValue));
+    if(!newQueueItem) {
+        UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                               "Subscription %u | MonitoredItem %i | "
+                               "Item for the publishing queue could not be allocated",
+                               sub->subscriptionID, monitoredItem->itemId);
+        return false;
+    }
+
+    /* Copy valueEncoding on the heap for the next comparison (if not already done) */
+    if(valueEncoding->data == stackValueEncoding) {
+        UA_ByteString cbs;
+        if(UA_ByteString_copy(valueEncoding, &cbs) != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | MonitoredItem %i | "
+                                   "ByteString to compare values could not be created",
+                                   sub->subscriptionID, monitoredItem->itemId);
+            UA_free(newQueueItem);
+            return false;
+        }
+        *valueEncoding = cbs;
+    }
+
+    /* Prepare the newQueueItem */
+    if(value->hasValue && value->value.storageType == UA_VARIANT_DATA_NODELETE) {
+        /* Make a deep copy of the value */
+        UA_StatusCode retval = UA_DataValue_copy(value, &newQueueItem->value);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | MonitoredItem %i | "
+                                   "Item for the publishing queue could not be prepared",
+                                   sub->subscriptionID, monitoredItem->itemId);
+            UA_free(newQueueItem);
+            return false;
+        }
+    } else {
+        newQueueItem->value = *value; /* Just copy the value and do not release it */
+    }
+    newQueueItem->clientHandle = monitoredItem->clientHandle;
+
+    /* <-- Point of no return --> */
+
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                         "Subscription %u | MonitoredItem %u | Sampled a new value",
+                         sub->subscriptionID, monitoredItem->itemId);
+
+    /* Replace the encoding for comparison */
+    UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
+    monitoredItem->lastSampledValue = *valueEncoding;
+
+    /* Add the sample to the queue for publication */
+    ensureSpaceInMonitoredItemQueue(monitoredItem);
+    TAILQ_INSERT_TAIL(&monitoredItem->queue, newQueueItem, listEntry);
+    ++monitoredItem->currentQueueSize;
+    return true;;
+}
+
+void
+UA_MoniteredItem_SampleCallback(UA_Server *server,
+                                UA_MonitoredItem *monitoredItem) {
+    UA_Subscription *sub = monitoredItem->subscription;
+    if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                             "Subscription %u | MonitoredItem %i | "
+                             "Not a data change notification",
+                             sub->subscriptionID, monitoredItem->itemId);
+        return;
+    }
+
+    /* Read the value */
+    UA_ReadValueId rvid;
+    UA_ReadValueId_init(&rvid);
+    rvid.nodeId = monitoredItem->monitoredNodeId;
+    rvid.attributeId = monitoredItem->attributeID;
+    rvid.indexRange = monitoredItem->indexRange;
+    UA_DataValue value =
+        UA_Server_readWithSession(server, sub->session,
+                                  &rvid, monitoredItem->timestampsToReturn);
+
+    /* Stack-allocate some memory for the value encoding. We might heap-allocate
+     * more memory if needed. This is just enough for scalars and small
+     * structures. */
+    UA_Byte *stackValueEncoding = (UA_Byte *)UA_alloca(UA_VALUENCODING_MAXSTACK);
+    UA_ByteString valueEncoding;
+    valueEncoding.data = stackValueEncoding;
+    valueEncoding.length = UA_VALUENCODING_MAXSTACK;
+
+    /* Create a sample and compare with the last value */
+    UA_Boolean newNotification = sampleCallbackWithValue(server, sub, monitoredItem,
+                                                         &value, &valueEncoding);
+
+    /* Clean up */
+    if(!newNotification) {
+        if(valueEncoding.data != stackValueEncoding)
+            UA_ByteString_deleteMembers(&valueEncoding);
+        UA_DataValue_deleteMembers(&value);
+    }
+}
+
+UA_StatusCode
+MonitoredItem_registerSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
+    UA_StatusCode retval =
+        UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_MoniteredItem_SampleCallback,
+                                      mon, (UA_UInt32)mon->samplingInterval, &mon->sampleCallbackId);
+    if(retval == UA_STATUSCODE_GOOD)
+        mon->sampleCallbackIsRegistered = true;
+    return retval;
+}
+
+UA_StatusCode
+MonitoredItem_unregisterSampleCallback(UA_Server *server, UA_MonitoredItem *mon) {
+    if(!mon->sampleCallbackIsRegistered)
+        return UA_STATUSCODE_GOOD;
+    mon->sampleCallbackIsRegistered = false;
+    return UA_Server_removeRepeatedCallback(server, mon->sampleCallbackId);
+}
+
+#endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 11 - 7
src/ua_securechannel.h

@@ -84,36 +84,40 @@ UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *c
 
 /**
  * Log Helper
- * ---------- */
+ * ----------
+ * C99 requires at least one element for the variadic argument. If the log
+ * statement has no variable arguments, supply an additional NULL. It will be
+ * ignored by printf. */
+
 #define UA_LOG_TRACE_CHANNEL(LOGGER, CHANNEL, MSG, ...)                 \
     UA_LOG_TRACE(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                  ((CHANNEL)->connection ? CHANNEL->connection->sockfd : 0), \
-                 (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                 (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #define UA_LOG_DEBUG_CHANNEL(LOGGER, CHANNEL, MSG, ...)                 \
     UA_LOG_DEBUG(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                  ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \
-                 (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                 (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #define UA_LOG_INFO_CHANNEL(LOGGER, CHANNEL, MSG, ...)                   \
     UA_LOG_INFO(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                 ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \
-                (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #define UA_LOG_WARNING_CHANNEL(LOGGER, CHANNEL, MSG, ...)               \
     UA_LOG_WARNING(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                    ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \
-                   (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                   (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #define UA_LOG_ERROR_CHANNEL(LOGGER, CHANNEL, MSG, ...)                 \
     UA_LOG_ERROR(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                  ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \
-                 (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                 (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #define UA_LOG_FATAL_CHANNEL(LOGGER, CHANNEL, MSG, ...)                 \
     UA_LOG_FATAL(LOGGER, UA_LOGCATEGORY_SECURECHANNEL, "Connection %i | SecureChannel %i | " MSG, \
                  ((CHANNEL)->connection ? (CHANNEL)->connection->sockfd : 0), \
-                 (CHANNEL)->securityToken.channelId, ##__VA_ARGS__);
+                 (CHANNEL)->securityToken.channelId, __VA_ARGS__);
 
 #ifdef __cplusplus
 } // extern "C"

+ 10 - 7
src/ua_session.h

@@ -85,49 +85,52 @@ UA_Session_getUniqueSubscriptionID(UA_Session *session);
 
 /**
  * Log Helper
- * ---------- */
+ * ----------
+ * C99 requires at least one element for the variadic argument. If the log
+ * statement has no variable arguments, supply an additional NULL. It will be
+ * ignored by printf. */
 
 #define UA_LOG_TRACE_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_TRACE(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                  ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                  UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                 ##__VA_ARGS__);
+                 __VA_ARGS__);
 
 #define UA_LOG_DEBUG_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_DEBUG(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                  ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                  UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                 ##__VA_ARGS__);
+                 __VA_ARGS__);
 
 #define UA_LOG_INFO_SESSION(LOGGER, SESSION, MSG, ...)                  \
     UA_LOG_INFO(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                  ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                  UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                 ##__VA_ARGS__);
+                 __VA_ARGS__);
 
 #define UA_LOG_WARNING_SESSION(LOGGER, SESSION, MSG, ...)               \
     UA_LOG_WARNING(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                    ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                    ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                    UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                   ##__VA_ARGS__);
+                   __VA_ARGS__);
 
 #define UA_LOG_ERROR_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_ERROR(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                  ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                  UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                 ##__VA_ARGS__);
+                 __VA_ARGS__);
 
 #define UA_LOG_FATAL_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_FATAL(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  ((SESSION)->channel ? ((SESSION)->channel->connection ? (SESSION)->channel->connection->sockfd : 0) : 0), \
                  ((SESSION)->channel ? (SESSION)->channel->securityToken.channelId : 0), \
                  UA_PRINTF_GUID_DATA((SESSION)->sessionId.identifier.guid), \
-                 ##__VA_ARGS__);
+                 __VA_ARGS__);
 
 #ifdef __cplusplus
 } // extern "C"

+ 2 - 2
src/ua_types.c

@@ -460,8 +460,8 @@ UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
 static UA_StatusCode
 computeStrides(const UA_Variant *v, const UA_NumericRange range,
                size_t *total, size_t *block, size_t *stride, size_t *first) {
-    /* Test for max array size */
-#if(MAX_SIZE > 0xffffffff) /* 64bit only */
+    /* Test for max array size (64bit only) */
+#if (SIZE_MAX > 0xffffffff)
     if(v->arrayLength > UA_UINT32_MAX)
         return UA_STATUSCODE_BADINTERNALERROR;
 #endif

+ 6 - 36
src/ua_types_encoding_binary.c

@@ -53,12 +53,12 @@ extern const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES
 
 /* Pointer to custom datatypes in the server or client. Set inside
  * UA_decodeBinary */
-UA_THREAD_LOCAL size_t customTypesArraySize;
-UA_THREAD_LOCAL const UA_DataType *customTypesArray;
+static UA_THREAD_LOCAL size_t customTypesArraySize;
+static UA_THREAD_LOCAL const UA_DataType *customTypesArray;
 
 /* Pointers to the current position and the last position in the buffer */
-UA_THREAD_LOCAL UA_Byte *pos;
-UA_THREAD_LOCAL const UA_Byte *end;
+static UA_THREAD_LOCAL UA_Byte *pos;
+static UA_THREAD_LOCAL const UA_Byte *end;
 
 /* In UA_encodeBinaryInternal, we store a pointer to the last "good" position in
  * the buffer. When encoding reaches the end of the buffer, send out a chunk
@@ -82,8 +82,8 @@ UA_THREAD_LOCAL const UA_Byte *end;
  * DiagnosticInfo_encodeBinary */
 
 /* Thread-local buffers used for exchanging the buffer for chunking */
-UA_THREAD_LOCAL UA_exchangeEncodeBuffer exchangeBufferCallback;
-UA_THREAD_LOCAL void *exchangeBufferCallbackHandle;
+static UA_THREAD_LOCAL UA_exchangeEncodeBuffer exchangeBufferCallback;
+static UA_THREAD_LOCAL void *exchangeBufferCallbackHandle;
 
 /* Send the current chunk and replace the buffer */
 static UA_StatusCode
@@ -215,11 +215,6 @@ UInt16_encodeBinary(UA_UInt16 const *src, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_INLINE UA_StatusCode
-Int16_encodeBinary(UA_Int16 const *src, const UA_DataType *_) {
-    return UInt16_encodeBinary((const UA_UInt16*)src, NULL);
-}
-
 static UA_StatusCode
 UInt16_decodeBinary(UA_UInt16 *dst, const UA_DataType *_) {
     if(pos + sizeof(UA_UInt16) > end)
@@ -233,11 +228,6 @@ UInt16_decodeBinary(UA_UInt16 *dst, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_INLINE UA_StatusCode
-Int16_decodeBinary(UA_Int16 *dst) {
-    return UInt16_decodeBinary((UA_UInt16*)dst, NULL);
-}
-
 /* UInt32 */
 static UA_StatusCode
 UInt32_encodeBinary(UA_UInt32 const *src, const UA_DataType *_) {
@@ -257,11 +247,6 @@ Int32_encodeBinary(UA_Int32 const *src) {
     return UInt32_encodeBinary((const UA_UInt32*)src, NULL);
 }
 
-static UA_INLINE UA_StatusCode
-StatusCode_encodeBinary(UA_StatusCode const *src) {
-    return UInt32_encodeBinary((const UA_UInt32*)src, NULL);
-}
-
 static UA_StatusCode
 UInt32_decodeBinary(UA_UInt32 *dst, const UA_DataType *_) {
     if(pos + sizeof(UA_UInt32) > end)
@@ -299,16 +284,6 @@ UInt64_encodeBinary(UA_UInt64 const *src, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_INLINE UA_StatusCode
-Int64_encodeBinary(UA_Int64 const *src) {
-    return UInt64_encodeBinary((const UA_UInt64*)src, NULL);
-}
-
-static UA_INLINE UA_StatusCode
-DateTime_encodeBinary(UA_DateTime const *src) {
-    return UInt64_encodeBinary((const UA_UInt64*)src, NULL);
-}
-
 static UA_StatusCode
 UInt64_decodeBinary(UA_UInt64 *dst, const UA_DataType *_) {
     if(pos + sizeof(UA_UInt64) > end)
@@ -322,11 +297,6 @@ UInt64_decodeBinary(UA_UInt64 *dst, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_INLINE UA_StatusCode
-Int64_decodeBinary(UA_Int64 *dst) {
-    return UInt64_decodeBinary((UA_UInt64*)dst, NULL);
-}
-
 static UA_INLINE UA_StatusCode
 DateTime_decodeBinary(UA_DateTime *dst) {
     return UInt64_decodeBinary((UA_UInt64*)dst, NULL);

+ 6 - 7
tests/check_chunking.c

@@ -34,8 +34,8 @@ START_TEST(encodeArrayIntoFiveChunksShallWork) {
     bufIndex = 0;
     counter = 0;
     dataCount = 0;
-    UA_Int32 *ar = UA_Array_new(arraySize,&UA_TYPES[UA_TYPES_INT32]);
-    buffers = UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
+    UA_Int32 *ar = (UA_Int32*)UA_Array_new(arraySize,&UA_TYPES[UA_TYPES_INT32]);
+    buffers = (UA_ByteString*)UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
     for(size_t i=0;i<chunkCount;i++){
         UA_ByteString_allocBuffer(&buffers[i],chunkSize);
     }
@@ -78,11 +78,11 @@ START_TEST(encodeStringIntoFiveChunksShallWork) {
     counter = 0;
     dataCount = 0;
     UA_String_init(&string);
-    string.data = malloc(stringLength);
+    string.data = (UA_Byte*)malloc(stringLength);
     string.length = stringLength;
     char tmpString[9] = {'o','p','e','n','6','2','5','4','1'};
     //char tmpString[9] = {'1','4','5','2','6','n','e','p','o'};
-    buffers = UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
+    buffers = (UA_ByteString*)UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
     for(size_t i=0;i<chunkCount;i++){
         UA_ByteString_allocBuffer(&buffers[i],chunkSize);
     }
@@ -125,11 +125,10 @@ START_TEST(encodeTwoStringsIntoTenChunksShallWork) {
     counter = 0;
     dataCount = 0;
     UA_String_init(&string);
-    string.data = malloc(stringLength);
+    string.data = (UA_Byte*)malloc(stringLength);
     string.length = stringLength;
     char tmpString[9] = {'o','p','e','n','6','2','5','4','1'};
-    //char tmpString[9] = {'1','4','5','2','6','n','e','p','o'};
-    buffers = UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
+    buffers = (UA_ByteString*)UA_Array_new(chunkCount, &UA_TYPES[UA_TYPES_BYTESTRING]);
     for(size_t i=0;i<chunkCount;i++){
         UA_ByteString_allocBuffer(&buffers[i],chunkSize);
     }

+ 1 - 1
tests/check_client.c

@@ -23,7 +23,7 @@ addVariable(size_t size) {
     /* Define the attribute of the myInteger variable node */
     UA_VariableAttributes attr;
     UA_VariableAttributes_init(&attr);
-    UA_Int32* array = malloc(size * sizeof(UA_Int32));
+    UA_Int32* array = (UA_Int32*)malloc(size * sizeof(UA_Int32));
     memset(array, 0, size * sizeof(UA_Int32));
     UA_Variant_setArray(&attr.value, array, size, &UA_TYPES[UA_TYPES_INT32]);
 

+ 27 - 22
tests/check_discovery.c

@@ -201,7 +201,7 @@ FindAndCheck(const UA_String expectedUris[], size_t expectedUrisSize,
 
     if(filterUri) {
         serverUrisSize = 1;
-        serverUris = UA_malloc(sizeof(UA_String));
+        serverUris = UA_String_new();
         serverUris[0] = UA_String_fromChars(filterUri);
     }
 
@@ -210,7 +210,7 @@ FindAndCheck(const UA_String expectedUris[], size_t expectedUrisSize,
 
     if(filterLocale) {
         localeIdsSize = 1;
-        localeIds = UA_malloc(sizeof(UA_String));
+        localeIds = UA_String_new();
         localeIds[0] = UA_String_fromChars(filterLocale);
     }
 
@@ -251,6 +251,8 @@ FindAndCheck(const UA_String expectedUris[], size_t expectedUrisSize,
     UA_Client_delete(client);
 }
 
+#ifdef UA_ENABLE_DISCOVERY_MULTICAST
+
 static void
 FindOnNetworkAndCheck(UA_String expectedServerNames[], size_t expectedServerNamesSize,
                       const char *filterUri, const char *filterLocale,
@@ -266,7 +268,7 @@ FindOnNetworkAndCheck(UA_String expectedServerNames[], size_t expectedServerName
 
     if(filterCapabilitiesSize) {
         serverCapabilityFilterSize = filterCapabilitiesSize;
-        serverCapabilityFilter = UA_malloc(sizeof(UA_String) * filterCapabilitiesSize);
+        serverCapabilityFilter = (UA_String*)UA_malloc(sizeof(UA_String) * filterCapabilitiesSize);
         for(size_t i = 0; i < filterCapabilitiesSize; i++)
             serverCapabilityFilter[i] = UA_String_fromChars(filterCapabilities[i]);
     }
@@ -314,7 +316,7 @@ GetEndpoints(UA_Client *client, const UA_String* endpointUrl,
     request.endpointUrl = *endpointUrl; // assume the endpointurl outlives the service call
     if (filterTransportProfileUri) {
         request.profileUrisSize = 1;
-        request.profileUris = UA_malloc(sizeof(UA_String));
+        request.profileUris = (UA_String*)UA_malloc(sizeof(UA_String));
         request.profileUris[0] = UA_String_fromChars(filterTransportProfileUri);
     }
 
@@ -341,7 +343,6 @@ GetEndpoints(UA_Client *client, const UA_String* endpointUrl,
     return UA_STATUSCODE_GOOD;
 }
 
-
 static void
 GetEndpointsAndCheck(const char* discoveryUrl, const char* filterTransportProfileUri,
                      const UA_String expectedEndpointUrls[], size_t expectedEndpointUrlsSize) {
@@ -368,13 +369,6 @@ GetEndpointsAndCheck(const char* discoveryUrl, const char* filterTransportProfil
     UA_Client_delete(client);
 }
 
-// Test if discovery server lists himself as registered server, before any other registration.
-START_TEST(Client_find_discovery) {
-    const UA_String expectedUris[] = {UA_STRING("urn:open62541.test.local_discovery_server")};
-    FindAndCheck(expectedUris, 1, NULL, NULL, NULL, NULL);
-}
-END_TEST
-
 // Test if discovery server lists himself as registered server if it is filtered by his uri
 START_TEST(Client_filter_discovery) {
     const UA_String expectedUris[] = {UA_STRING("urn:open62541.test.local_discovery_server")};
@@ -399,16 +393,6 @@ START_TEST(Client_filter_locale) {
 }
 END_TEST
 
-// Test if registered server is returned from LDS
-START_TEST(Client_find_registered) {
-    const UA_String expectedUris[] = {
-        UA_STRING("urn:open62541.test.local_discovery_server"),
-        UA_STRING("urn:open62541.test.server_register")
-    };
-    FindAndCheck(expectedUris, 2, NULL, NULL, NULL, NULL);
-}
-END_TEST
-
 // Test if registered server is returned from LDS using FindServersOnNetwork
 START_TEST(Client_find_on_network_registered) {
     char urls[2][64];
@@ -466,6 +450,25 @@ START_TEST(Client_get_endpoints) {
 }
 END_TEST
 
+#endif
+
+// Test if discovery server lists himself as registered server, before any other registration.
+START_TEST(Client_find_discovery) {
+    const UA_String expectedUris[] = {UA_STRING("urn:open62541.test.local_discovery_server")};
+    FindAndCheck(expectedUris, 1, NULL, NULL, NULL, NULL);
+}
+END_TEST
+
+// Test if registered server is returned from LDS
+START_TEST(Client_find_registered) {
+    const UA_String expectedUris[] = {
+        UA_STRING("urn:open62541.test.local_discovery_server"),
+        UA_STRING("urn:open62541.test.server_register")
+    };
+    FindAndCheck(expectedUris, 2, NULL, NULL, NULL, NULL);
+}
+END_TEST
+
 START_TEST(Util_start_lds) {
     setup_lds();
 }
@@ -483,11 +486,13 @@ START_TEST(Util_wait_timeout) {
 }
 END_TEST
 
+#ifdef UA_ENABLE_DISCOVERY_MULTICAST
 START_TEST(Util_wait_mdns) {
     UA_sleep(1000);
     sleep(1);
 }
 END_TEST
+#endif
 
 START_TEST(Util_wait_startup) {
     UA_sleep(1000);

+ 5 - 9
tests/check_nodestore.c

@@ -36,10 +36,6 @@ static void checkZeroVisitor(const UA_Node* node) {
     if (node == NULL) zeroCnt++;
 }
 
-static void printVisitor(const UA_Node* node) {
-    printf("%d\n", node->nodeId.identifier.numeric);
-}
-
 static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
     UA_Node *p = (UA_Node *)UA_NodeStore_newVariableNode();
     p->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
@@ -213,7 +209,7 @@ static void *profileGetThread(void *arg) {
 #endif
 
 START_TEST(profileGetDelete) {
-#define N 1000000
+#define N 100 /* make bigger to test */
     for(UA_UInt32 i = 0; i < N; i++) {
         UA_Node *n = createNode(0,i);
         UA_NodeStore_insert(ns, n);
@@ -271,10 +267,10 @@ static Suite * namespace_suite (void) {
     tcase_add_test (tc_iterate, iterateOverExpandedNamespaceShallNotVisitEmptyNodes);
     suite_add_tcase (s, tc_iterate);
     
-    /* TCase* tc_profile = tcase_create ("Profile"); */
-    /* tcase_add_checked_fixture(tc_profile, setup, teardown); */
-    /* tcase_add_test (tc_profile, profileGetDelete); */
-    /* suite_add_tcase (s, tc_profile); */
+    TCase* tc_profile = tcase_create ("Profile");
+    tcase_add_checked_fixture(tc_profile, setup, teardown);
+    tcase_add_test (tc_profile, profileGetDelete);
+    suite_add_tcase (s, tc_profile);
 
     return s;
 }

+ 1 - 1
tests/check_server_binary_messages.c

@@ -24,7 +24,7 @@ static UA_ByteString readFile(char *filename) {
         fseek(f, 0, SEEK_END);
         length = ftell(f);
         rewind(f);
-        buf.data = malloc(length);
+        buf.data = (UA_Byte*)malloc(length);
         fread(buf.data, sizeof(char), length, f);
         buf.length = length;
         fclose(f);

+ 1 - 1
tests/check_server_userspace.c

@@ -46,7 +46,7 @@ START_TEST(Server_addNamespace_writeService)
 
     namespaces.data = realloc(namespaces.data, (namespaces.arrayLength + 1) * sizeof(UA_String));
     ++namespaces.arrayLength;
-    UA_String *ns = namespaces.data;
+    UA_String *ns = (UA_String*)namespaces.data;
     ns[namespaces.arrayLength-1] = UA_STRING_ALLOC("test");
     size_t nsSize = namespaces.arrayLength;
 

+ 3 - 3
tests/check_services_attributes.c

@@ -645,11 +645,11 @@ START_TEST(WriteSingleAttributeNodeclass) {
     UA_WriteValue wValue;
     UA_WriteValue_init(&wValue);
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
-    UA_NodeClass class;
-    UA_NodeClass_init(&class);
+    UA_NodeClass nc;
+    UA_NodeClass_init(&nc);
     wValue.attributeId = UA_ATTRIBUTEID_NODECLASS;
     wValue.value.hasValue = true;
-    UA_Variant_setScalar(&wValue.value.value, &class, &UA_TYPES[UA_TYPES_NODECLASS]);
+    UA_Variant_setScalar(&wValue.value.value, &nc, &UA_TYPES[UA_TYPES_NODECLASS]);
     UA_StatusCode retval = UA_Server_write(server, &wValue);
     ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
     UA_Server_delete(server);

+ 1 - 1
tests/check_services_view.c

@@ -140,7 +140,7 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds) {
     UA_BrowsePath browsePath;
     UA_BrowsePath_init(&browsePath);
     browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-    browsePath.relativePath.elements = UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
+    browsePath.relativePath.elements = (UA_RelativePathElement*)UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
     browsePath.relativePath.elementsSize = BROWSE_PATHS_SIZE;
 
     for(size_t i = 0; i < BROWSE_PATHS_SIZE; i++) {

+ 11 - 11
tests/check_types_builtin.c

@@ -851,12 +851,12 @@ START_TEST(UA_String_encodeShallWorkOnExample) {
     // given
     UA_String src;
     src.length = 11;
-    UA_Byte   mem[11] = "ACPLT OPCUA";
-    src.data   = mem;
+    UA_Byte mem[12] = "ACPLT OPCUA";
+    src.data = mem;
 
-    UA_Byte data[] = {  0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
-                        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
-                        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
+    UA_Byte data[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+                       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+                       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
     UA_ByteString dst = { 24, data };
     UA_Byte *pos = dst.data;
     const UA_Byte *end = &dst.data[dst.length];
@@ -1050,7 +1050,7 @@ START_TEST(UA_Array_copyByteArrayShallWorkOnExample) {
     UA_Byte  *dstArray;
     UA_Int32  size = 5;
     UA_Int32  i    = 0;
-    testString.data = UA_malloc(size);
+    testString.data = (UA_Byte*)UA_malloc(size);
     testString.data[0] = 'O';
     testString.data[1] = 'P';
     testString.data[2] = 'C';
@@ -1076,7 +1076,7 @@ END_TEST
 START_TEST(UA_Array_copyUA_StringShallWorkOnExample) {
     // given
     UA_Int32   i, j;
-    UA_String *srcArray = UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
+    UA_String *srcArray = (UA_String*)UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
     UA_String *dstArray;
 
     srcArray[0] = UA_STRING_ALLOC("open");
@@ -1311,13 +1311,13 @@ END_TEST
 
 START_TEST(UA_Variant_copyShallWorkOn1DArrayExample) {
     // given
-    UA_String *srcArray = UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
+    UA_String *srcArray = (UA_String*)UA_Array_new(3, &UA_TYPES[UA_TYPES_STRING]);
     srcArray[0] = UA_STRING_ALLOC("__open");
     srcArray[1] = UA_STRING_ALLOC("_62541");
     srcArray[2] = UA_STRING_ALLOC("opc ua");
 
     UA_UInt32 *dimensions;
-    dimensions = UA_malloc(sizeof(UA_UInt32));
+    dimensions = (UA_UInt32*)UA_malloc(sizeof(UA_UInt32));
     dimensions[0] = 3;
 
     UA_Variant value, copiedValue;
@@ -1359,7 +1359,7 @@ END_TEST
 
 START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     // given
-    UA_Int32 *srcArray = UA_Array_new(6, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Int32 *srcArray = (UA_Int32*)UA_Array_new(6, &UA_TYPES[UA_TYPES_INT32]);
     srcArray[0] = 0;
     srcArray[1] = 1;
     srcArray[2] = 2;
@@ -1367,7 +1367,7 @@ START_TEST(UA_Variant_copyShallWorkOn2DArrayExample) {
     srcArray[4] = 4;
     srcArray[5] = 5;
 
-    UA_UInt32 *dimensions = UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
+    UA_UInt32 *dimensions = (UA_UInt32*)UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
     UA_UInt32 dim1 = 3;
     UA_UInt32 dim2 = 2;
     dimensions[0] = dim1;

+ 1 - 1
tests/check_types_custom.c

@@ -212,4 +212,4 @@ int main(void) {
 
 #ifdef __clang__
 #pragma clang diagnostic pop
-#endif
+#endif

+ 1 - 1
tests/check_types_range.c

@@ -77,7 +77,7 @@ START_TEST(copyIntoStringArrayRange) {
 
     UA_String s1 = UA_STRING("bc");
     UA_String s2 = UA_STRING("xy");
-    UA_String *arr2 = v2.data;
+    UA_String *arr2 = (UA_String*)v2.data;
     ck_assert(UA_String_equal(&arr2[0], &s1));
     ck_assert(UA_String_equal(&arr2[1], &s2));
 

+ 1 - 1
tests/testing_networklayers.c

@@ -8,7 +8,7 @@
 
 static UA_StatusCode
 dummyGetSendBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
-    buf->data = malloc(length);
+    buf->data = (UA_Byte*)malloc(length);
     buf->length = length;
     return UA_STATUSCODE_GOOD;
 }

+ 7 - 11
tools/pyUANamespace/ua_namespace.py

@@ -646,17 +646,13 @@ class opcua_namespace():
 
     header.append('#ifndef '+outfilename.upper()+'_H_')
     header.append('#define '+outfilename.upper()+'_H_')
-    header.append('#ifdef UA_NO_AMALGAMATION')
-    header.append(  '#include "server/ua_server_internal.h"')
-    header.append(  '#include "server/ua_nodes.h"')
-    header.append('  #include "ua_util.h"')
-    header.append('  #include "ua_types.h"')
-    header.append('  #include "ua_types_encoding_binary.h"')
-    header.append('  #include "ua_types_generated_encoding_binary.h"')
-    header.append('  #include "ua_transport_generated_encoding_binary.h"')
-    header.append('#else')
-    header.append('  #include "open62541.h"')
-    header.append('#endif')
+    header.append('')
+    header.append('#include "server/ua_server_internal.h"')
+    header.append('#include "server/ua_nodes.h"')
+    header.append('#include "ua_util.h"')
+    header.append('#include "ua_types_encoding_binary.h"')
+    header.append('#include "ua_types_generated_encoding_binary.h"')
+    header.append('#include "ua_transport_generated_encoding_binary.h"')
     header.append('')
     header.append('/* Definition that (in userspace models) may be ')
     header.append(' * - not included in the amalgamated header or')

+ 2 - 2
tools/pyUANamespace/ua_node_types.py

@@ -1084,7 +1084,7 @@ class opcua_node_variable_t(opcua_node_t):
           code = code + self.value().printOpen62541CCode(bootstrapping)
           return code
     if(bootstrapping):
-      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_alloca(sizeof(UA_Variant));")
+      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = (UA_Variant*)UA_alloca(sizeof(UA_Variant));")
       code.append("UA_Variant_init(" + self.getCodePrintableID() + "_variant);")
     return code
 
@@ -1338,7 +1338,7 @@ class opcua_node_variableType_t(opcua_node_t):
           code = code + self.value().printOpen62541CCode(bootstrapping)
           return code
     if(bootstrapping):
-      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_alloca(sizeof(UA_Variant));")
+      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = (UA_Variant*)UA_alloca(sizeof(UA_Variant));")
       code.append("UA_Variant_init(" + self.getCodePrintableID() + "_variant);")
     return code