浏览代码

worker threads for multithreading, use UA_malloc instead of UA_alloc

Julius Pfrommer 10 年之前
父节点
当前提交
a457f31c0f
共有 51 个文件被更改,包括 1807 次插入1403 次删除
  1. 42 26
      CMakeLists.txt
  2. 14 25
      README.md
  3. 0 83
      cmake/FindLibUV.cmake
  4. 262 243
      examples/networklayer_tcp.c
  5. 7 13
      examples/networklayer_tcp.h
  6. 0 180
      examples/networklayer_tcp_concurrent.c
  7. 24 33
      examples/opcuaServer.c
  8. 13 18
      include/ua_connection.h
  9. 1 2
      include/ua_log.h
  10. 132 24
      include/ua_server.h
  11. 34 31
      include/ua_types.h
  12. 8 8
      src/server/ua_nodestore.c
  13. 52 44
      src/server/ua_nodestore.h
  14. 40 49
      src/server/ua_nodestore_concurrent.c
  15. 30 34
      src/server/ua_securechannel_manager.c
  16. 2 4
      src/server/ua_securechannel_manager.h
  17. 65 20
      src/server/ua_server.c
  18. 2 2
      src/server/ua_server_addressspace.c
  19. 119 106
      src/server/ua_server_binary.c
  20. 67 6
      src/server/ua_server_internal.h
  21. 456 0
      src/server/ua_server_worker.c
  22. 46 44
      src/server/ua_services.h
  23. 12 11
      src/server/ua_services_attribute.c
  24. 9 2
      src/server/ua_services_discovery.c
  25. 0 40
      src/server/ua_services_internal.h
  26. 4 5
      src/server/ua_services_nodemanagement.c
  27. 1 1
      src/server/ua_services_session.c
  28. 4 4
      src/server/ua_services_view.c
  29. 1 1
      src/server/ua_session_manager.c
  30. 1 11
      src/server/ua_session_manager.h
  31. 5 5
      src/ua_config.h.in
  32. 0 21
      src/ua_connection.c
  33. 30 3
      src/ua_securechannel.c
  34. 7 1
      src/ua_securechannel.h
  35. 47 24
      src/ua_session.c
  36. 5 1
      src/ua_session.h
  37. 13 6
      src/ua_transport.c
  38. 69 61
      src/ua_types.c
  39. 4 10
      src/ua_types_encoding_binary.c
  40. 22 32
      src/ua_types_encoding_binary.h
  41. 5 5
      src/ua_types_internal.h
  42. 24 9
      src/ua_util.c
  43. 9 59
      src/ua_util.h
  44. 1 1
      tests/CMakeLists.txt
  45. 5 5
      tests/check_builtin.c
  46. 4 4
      tests/check_memory.c
  47. 19 19
      tests/check_nodestore.c
  48. 1 1
      tests/check_stack.c
  49. 87 64
      tools/generate_builtin.py
  50. 1 1
      tools/generate_namespace.py
  51. 1 1
      tools/schema/Custom.Opc.Ua.Transport.bsd

+ 42 - 26
CMakeLists.txt

@@ -3,10 +3,20 @@ cmake_minimum_required(VERSION 2.8.8)
 
 
 project(open62541 C)
 project(open62541 C)
 set(open62541_VERSION_MAJOR 0)
 set(open62541_VERSION_MAJOR 0)
-set(open62541_VERSION_MINOR 1)
+set(open62541_VERSION_MINOR 0)
+set(NVIM_VERSION_PATCH 0)
 
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
+
+# Set available build types for CMake GUIs.
+# A different build type can still be set by -DCMAKE_BUILD_TYPE=...
+set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+
+# Set default build type.
+if (NOT CMAKE_BUILD_TYPE)
+    message(STATUS "CMAKE_BUILD_TYPE not given; setting to 'RelWithDebInfo'.")
+    set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build." FORCE)
+endif()
 
 
 # main sources of libopen62541
 # main sources of libopen62541
 include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
 include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -15,18 +25,18 @@ file(GLOB_RECURSE exported_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
 file(GLOB_RECURSE headers "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
 file(GLOB_RECURSE headers "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
 file(GLOB generated_headers "${PROJECT_BINARY_DIR}/src_generated/*.h")
 file(GLOB generated_headers "${PROJECT_BINARY_DIR}/src_generated/*.h")
 set(lib_sources src/ua_types.c
 set(lib_sources src/ua_types.c
+                src/ua_util.c
                 src/ua_types_encoding_binary.c
                 src/ua_types_encoding_binary.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
                 src/ua_transport.c
                 src/ua_transport.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
-				src/ua_connection.c
                 src/ua_securechannel.c
                 src/ua_securechannel.c
                 src/ua_session.c
                 src/ua_session.c
-                src/ua_util.c
                 src/server/ua_server.c
                 src/server/ua_server.c
 				src/server/ua_server_addressspace.c
 				src/server/ua_server_addressspace.c
 				src/server/ua_server_binary.c
 				src/server/ua_server_binary.c
+                src/server/ua_server_worker.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_session_manager.c
                 src/server/ua_session_manager.c
                 src/server/ua_services_attribute.c
                 src/server/ua_services_attribute.c
@@ -48,6 +58,8 @@ add_definitions(-std=c99 -pedantic -pipe -Wall -Wextra -Werror -Wformat
         add_definitions(-Wformat-nonliteral)
         add_definitions(-Wformat-nonliteral)
         set (CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--gc-sections")
         set (CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--gc-sections")
         set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
         set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections")
+    else()
+        add_definitions(-Wno-gnu-statement-expression)
     endif()
     endif()
 	if(NOT WIN32)
 	if(NOT WIN32)
 	    add_definitions(-fstack-protector -fPIC -fvisibility=hidden)
 	    add_definitions(-fstack-protector -fPIC -fvisibility=hidden)
@@ -55,15 +67,24 @@ add_definitions(-std=c99 -pedantic -pipe -Wall -Wextra -Werror -Wformat
 endif()
 endif()
 
 
 # build settings
 # build settings
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules")
 set(generate_src_options) # the options for the tools that generate code from xml-schema definitions
 set(generate_src_options) # the options for the tools that generate code from xml-schema definitions
 
 
+## set debug flag
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+    set(UA_DEBUG ON)
+else()
+    set(UA_DEBUG OFF)
+endif()
+
 ## extensions
 ## extensions
 option(EXTENSION_STATELESS "Enable stateless extension" OFF)
 option(EXTENSION_STATELESS "Enable stateless extension" OFF)
 if(EXTENSION_STATELESS)
 if(EXTENSION_STATELESS)
 	message(STATUS "Extensions: enabling stateless")
 	message(STATUS "Extensions: enabling stateless")
 	add_definitions(-DEXTENSION_STATELESS)
 	add_definitions(-DEXTENSION_STATELESS)
 	add_executable(statelessClient $<TARGET_OBJECTS:open62541-objects> examples/statelessClient.c)
 	add_executable(statelessClient $<TARGET_OBJECTS:open62541-objects> examples/statelessClient.c)
+    if(MULTITHREADING)
+        target_link_libraries(statelessClient urcu-cds urcu urcu-common pthread)
+    endif()
 endif()
 endif()
 
 
 ## self-signed certificates
 ## self-signed certificates
@@ -104,18 +125,19 @@ if(ENABLE_XML_ENCODING)
     list(APPEND generate_src_options "--with-xml")
     list(APPEND generate_src_options "--with-xml")
 endif()
 endif()
 
 
-### json
-option(ENABLE_JSON_ENCODING "Enable JSON-encoding of the UA types" OFF)
-if(ENABLE_JSON_ENCODING)
-    MATH(EXPR UA_ENCODING_AMOUNT "${UA_ENCODING_AMOUNT}+1")
-	include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/ongoing")
-    list(APPEND lib_sources src/ongoing/ua_types_encoding_json.c)
-    list(APPEND generate_src_options "--with-json")
-endif(ENABLE_JSON_ENCODING)
+# ### json
+# option(ENABLE_JSON_ENCODING "Enable JSON-encoding of the UA types" OFF)
+# if(ENABLE_JSON_ENCODING)
+#     MATH(EXPR UA_ENCODING_AMOUNT "${UA_ENCODING_AMOUNT}+1")
+# 	include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/ongoing")
+#     list(APPEND lib_sources src/ongoing/ua_types_encoding_json.c)
+#     list(APPEND generate_src_options "--with-json")
+# endif(ENABLE_JSON_ENCODING)
 
 
 ## multithreading
 ## multithreading
 option(MULTITHREADING "Enable multithreading" OFF)
 option(MULTITHREADING "Enable multithreading" OFF)
 if(MULTITHREADING)
 if(MULTITHREADING)
+    set(UA_MULTITHREADING ON)
     find_package(Threads REQUIRED)
     find_package(Threads REQUIRED)
     list(APPEND lib_sources src/server/ua_nodestore_concurrent.c)
     list(APPEND lib_sources src/server/ua_nodestore_concurrent.c)
 else()
 else()
@@ -125,7 +147,7 @@ endif()
 add_library(open62541-objects OBJECT ${lib_sources}) # static version that exports all symbols
 add_library(open62541-objects OBJECT ${lib_sources}) # static version that exports all symbols
 add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-objects>)
 add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-objects>)
 target_compile_definitions(open62541 INTERFACE
 target_compile_definitions(open62541 INTERFACE
-  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:open62541_EXPORTS> # the UA_EXPORT macro is different when building the lib or using the lib
+  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:UA_DYNAMIC_LINKING> # the UA_EXPORT macro is different when building the lib or using the lib
 )
 )
 
 
 ## logging
 ## logging
@@ -162,7 +184,7 @@ include_directories("${PROJECT_BINARY_DIR}/src_generated")
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                    PRE_BUILD
                    PRE_BUILD
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated
+                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 
 
@@ -176,8 +198,8 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
                    PRE_BUILD
                    PRE_BUILD
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated
-                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
+                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport
+                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
 
 
 # build example client
 # build example client
@@ -189,8 +211,7 @@ if(CLIENT)
     # internal functions that are not exported to the shared lib.
     # internal functions that are not exported to the shared lib.
 	add_executable(exampleClient $<TARGET_OBJECTS:open62541-objects> examples/opcuaClient.c)
 	add_executable(exampleClient $<TARGET_OBJECTS:open62541-objects> examples/opcuaClient.c)
     if(MULTITHREADING)
     if(MULTITHREADING)
-        find_package(LibUV REQUIRED)
-        target_link_libraries(exampleClient urcu-cds urcu uv)
+        target_link_libraries(exampleClient urcu-cds urcu urcu-common pthread)
     endif()
     endif()
 endif()
 endif()
 
 
@@ -198,20 +219,15 @@ endif()
 option(EXAMPLESERVER "Build a test server" OFF)
 option(EXAMPLESERVER "Build a test server" OFF)
 if(EXAMPLESERVER)
 if(EXAMPLESERVER)
 set(server_sources examples/opcuaServer.c
 set(server_sources examples/opcuaServer.c
+                   examples/networklayer_tcp.c
                    examples/logger_stdout.c)
                    examples/logger_stdout.c)
-if(NOT MULTITHREADING)
-    list(APPEND server_sources examples/networklayer_tcp.c)
-else()
-    list(APPEND server_sources examples/networklayer_tcp_concurrent.c)
-endif()
 add_executable(exampleServer ${server_sources} ${exported_headers} ${generated_headers})
 add_executable(exampleServer ${server_sources} ${exported_headers} ${generated_headers})
 target_link_libraries(exampleServer open62541)
 target_link_libraries(exampleServer open62541)
 if(WIN32)
 if(WIN32)
     target_link_libraries(exampleServer ws2_32)
     target_link_libraries(exampleServer ws2_32)
 endif(WIN32)
 endif(WIN32)
 if(MULTITHREADING)
 if(MULTITHREADING)
-    find_package(LibUV REQUIRED)
-    target_link_libraries(exampleServer urcu-cds urcu uv)
+    target_link_libraries(exampleServer urcu-cds urcu urcu-common pthread)
 endif()
 endif()
 endif()
 endif()
 
 

+ 14 - 25
README.md

@@ -35,49 +35,38 @@ Or even better, help us improving open62541 by sending bug reports/bugfixes on g
 #include "logger_stdout.h"
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 #include "networklayer_tcp.h"
 
 
+#define WORKER_THREADS 2 /* if multithreading is enabled */
+#define PORT 16664
+
 UA_Boolean running = UA_TRUE;
 UA_Boolean running = UA_TRUE;
-void stopHandler(int sign) {
+void signalHandler(int sign) {
 	running = UA_FALSE;
 	running = UA_FALSE;
 }
 }
 
 
-void serverCallback(UA_Server *server) {
-    // add your maintenance functionality here
-    printf("does whatever servers do\n");
-}
-
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
+    /* catch ctrl-c */
+	signal(SIGINT, signalHandler);
 
 
     /* init the server */
     /* init the server */
-	#define PORT 16664
-	UA_String endpointUrl;
-	UA_String_copyprintf("opc.tcp://127.0.0.1:%i", &endpointUrl, PORT);
-	UA_Server *server = UA_Server_new(&endpointUrl, NULL);
+	UA_Server *server = UA_Server_new();
+    NetworklayerTCP *nl = NetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT);
+    UA_Server_addNetworkLayer(server, nl);
 
 
     /* add a variable node */
     /* add a variable node */
     UA_Int32 myInteger = 42;
     UA_Int32 myInteger = 42;
     UA_String myIntegerName;
     UA_String myIntegerName;
     UA_STRING_STATIC(myIntegerName, "The Answer");
     UA_STRING_STATIC(myIntegerName, "The Answer");
     UA_Server_addScalarVariableNode(server,
     UA_Server_addScalarVariableNode(server,
-                 /* browse name, the value and the datatype's vtable */
+                 /* the browse name, the value, and the datatype vtable */
                  &myIntegerName, (void*)&myInteger, &UA_TYPES[UA_INT32],
                  &myIntegerName, (void*)&myInteger, &UA_TYPES[UA_INT32],
-                 /* the parent node where the variable shall be attached */
+                 /* the parent node of the variable */
                  &UA_NODEIDS[UA_OBJECTSFOLDER],
                  &UA_NODEIDS[UA_OBJECTSFOLDER],
                  /* the (hierarchical) referencetype from the parent */
                  /* the (hierarchical) referencetype from the parent */
-                 &UA_NODEIDS[UA_HASCOMPONENT]);
-
-    /* attach a network layer */
-	NetworklayerTCP* nl = NetworklayerTCP_new(UA_ConnectionConfig_standard, PORT);
-	printf("Server started, connect to to opc.tcp://127.0.0.1:%i\n", PORT);
+                 &UA_NODEIDS[UA_ORGANIZES]);
 
 
     /* run the server loop */
     /* run the server loop */
-	struct timeval callback_interval = {1, 0}; // 1 second
-	NetworkLayerTCP_run(nl, server, callback_interval, serverCallback, &running);
-    
-    /* clean up */
-	NetworklayerTCP_delete(nl);
+    UA_StatusCode retval = UA_Server_run(server, WORKER_THREADS, &running);
 	UA_Server_delete(server);
 	UA_Server_delete(server);
-    UA_String_deleteMembers(&endpointUrl);
-	return 0;
+	return retval;
 }
 }
 ```
 ```

+ 0 - 83
cmake/FindLibUV.cmake

@@ -1,83 +0,0 @@
-# - Try to find libuv
-# Once done, this will define
-#
-#  LIBUV_FOUND - system has libuv
-#  LIBUV_INCLUDE_DIRS - the libuv include directories
-#  LIBUV_LIBRARIES - link these to use libuv
-#
-# Set the LIBUV_USE_STATIC variable to specify if static libraries should
-# be preferred to shared ones.
-
-find_package(PkgConfig)
-if (PKG_CONFIG_FOUND)
-    pkg_check_modules(PC_LIBUV QUIET libuv)
-endif()
-
-find_path(LIBUV_INCLUDE_DIR uv.h
-  HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS}
-  ${LIMIT_SEARCH})
-
-# If we're asked to use static linkage, add libuv.a as a preferred library name.
-if(LIBUV_USE_STATIC)
-  list(APPEND LIBUV_NAMES
-    "${CMAKE_STATIC_LIBRARY_PREFIX}uv${CMAKE_STATIC_LIBRARY_SUFFIX}")
-endif(LIBUV_USE_STATIC)
-
-list(APPEND LIBUV_NAMES uv)
-
-find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES}
-  HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS}
-  ${LIMIT_SEARCH})
-
-mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)
-
-set(LIBUV_LIBRARIES ${LIBUV_LIBRARY})
-set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR})
-
-# Deal with the fact that libuv.pc is missing important dependency information.
-
-include(CheckLibraryExists)
-
-check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL)
-if(HAVE_LIBDL)
-  list(APPEND LIBUV_LIBRARIES dl)
-endif()
-
-check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT)
-if(HAVE_LIBKSTAT)
-  list(APPEND LIBUV_LIBRARIES kstat)
-endif()
-
-check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM)
-if(HAVE_LIBKVM)
-  list(APPEND LIBUV_LIBRARIES kvm)
-endif()
-
-check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL)
-if(HAVE_LIBNSL)
-  list(APPEND LIBUV_LIBRARIES nsl)
-endif()
-
-check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT)
-if(HAVE_LIBPERFSTAT)
-  list(APPEND LIBUV_LIBRARIES perfstat)
-endif()
-
-check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT)
-if(HAVE_LIBRT)
-  list(APPEND LIBUV_LIBRARIES rt)
-endif()
-
-check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE)
-if(HAVE_LIBSENDFILE)
-  list(APPEND LIBUV_LIBRARIES sendfile)
-endif()
-
-include(FindPackageHandleStandardArgs)
-
-# handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args(LibUV DEFAULT_MSG
-                                  LIBUV_LIBRARY LIBUV_INCLUDE_DIR)
-
-mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY)

+ 262 - 243
examples/networklayer_tcp.c

@@ -1,140 +1,195 @@
-/*
+ /*
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
  */
 
 
-#ifdef WIN32
+#include <stdlib.h> // malloc, free
+#ifdef _WIN32
 #include <malloc.h>
 #include <malloc.h>
 #include <winsock2.h>
 #include <winsock2.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <Windows.h>
 #include <Windows.h>
 #include <ws2tcpip.h>
 #include <ws2tcpip.h>
 #define CLOSESOCKET(S) closesocket(S)
 #define CLOSESOCKET(S) closesocket(S)
-#define IOCTLSOCKET ioctlsocket
 #else
 #else
 #include <sys/select.h> 
 #include <sys/select.h> 
-#include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <sys/socketvar.h>
 #include <sys/socketvar.h>
 #include <sys/ioctl.h>
 #include <sys/ioctl.h>
 #include <unistd.h> // read, write, close
 #include <unistd.h> // read, write, close
 #define CLOSESOCKET(S) close(S)
 #define CLOSESOCKET(S) close(S)
-#define IOCTLSOCKET ioctl
-#endif /* WIN32 */
+#endif
 
 
-#include <stdlib.h> // exit
+#include <arpa/inet.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <errno.h> // errno, EINTR
 #include <errno.h> // errno, EINTR
-#include <memory.h> // memset
 #include <fcntl.h> // fcntl
 #include <fcntl.h> // fcntl
 
 
-#include "ua_securechannel.h"
-#include "networklayer_tcp.h"
+#include "networklayer_tcp.h" // UA_MULTITHREADING is defined in here
 
 
-typedef struct TCPConnection {
-	UA_Int32 sockfd;
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
+#define MAXBACKLOG 100
+
+struct Networklayer_TCP;
+
+/* Forwarded to the server as a (UA_Connection) and used for callbacks back into
+   the networklayer */
+typedef struct {
 	UA_Connection connection;
 	UA_Connection connection;
+	UA_Int32 sockfd;
+	struct NetworkLayerTCP *layer;
 } TCPConnection;
 } TCPConnection;
 
 
-struct NetworklayerTCP {
-	UA_ConnectionConfig localConf;
-	UA_UInt32 port;
+/* Internal mapping of sockets to connections */
+typedef struct {
+    TCPConnection *connection;
+    UA_Int32 sockfd;
+} ConnectionLink;
+
+typedef struct NetworkLayerTCP {
+	UA_ConnectionConfig conf;
 	fd_set fdset;
 	fd_set fdset;
 	UA_Int32 serversockfd;
 	UA_Int32 serversockfd;
 	UA_Int32 highestfd;
 	UA_Int32 highestfd;
-	UA_UInt32 connectionsSize;
-	TCPConnection *connections;
-};
+    UA_UInt16 conLinksSize;
+    ConnectionLink *conLinks;
+    UA_UInt32 port;
+    /* We remove the connection links only in the main thread. Attach
+       to-be-deleted links with atomic operations */
+    struct deleteLink {
+        UA_Int32 sockfd;
+        struct deleteLink *next;
+    } *deleteLinkList;
+} NetworkLayerTCP;
 
 
-/** This structure is stored in the UA_Connection for callbacks into the
-	network layer. */
-typedef struct TCPConnectionHandle {
-	UA_Int32 sockfd;
-	NetworklayerTCP *layer;
-} TCPConnectionHandle;
-
-NetworklayerTCP *NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port) {
-    NetworklayerTCP *newlayer = malloc(sizeof(NetworklayerTCP));
-    if(newlayer == NULL)
-        return NULL;
-	newlayer->localConf = localConf;
-	newlayer->port = port;
-	newlayer->connectionsSize = 0;
-	newlayer->connections = NULL;
-	return newlayer;
+static UA_StatusCode setNonBlocking(int sockid) {
+#ifdef _WIN32
+	u_long iMode = 1;
+	if(ioctlsocket(sockid, FIONBIO, &iMode) != NO_ERROR)
+		return UA_STATUSCODE_BADINTERNALERROR;
+#else
+	int opts = fcntl(sockid,F_GETFL);
+	if(opts < 0 || fcntl(sockid,F_SETFL,opts|O_NONBLOCK) < 0)
+		return UA_STATUSCODE_BADINTERNALERROR;
+#endif
+	return UA_STATUSCODE_GOOD;
 }
 }
 
 
-void NetworklayerTCP_delete(NetworklayerTCP *layer) {
-	for(UA_UInt32 index = 0;index < layer->connectionsSize;index++) {
-		shutdown(layer->connections[index].sockfd, 2);
-        if(layer->connections[index].connection.channel)
-            layer->connections[index].connection.channel->connection = NULL;
-        UA_Connection_deleteMembers(&layer->connections[index].connection);
-		CLOSESOCKET(layer->connections[index].sockfd);
-	}
-	free(layer->connections);
-	free(layer);
+static void freeConnectionCallback(UA_Server *server, TCPConnection *connection) {
+    free(connection);
 }
 }
 
 
-void closeCallback(TCPConnectionHandle *handle);
-void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf);
-
-static UA_StatusCode NetworklayerTCP_add(NetworklayerTCP *layer, UA_Int32 newsockfd) {
-    layer->connectionsSize++;
-	layer->connections = realloc(layer->connections, sizeof(TCPConnection) * layer->connectionsSize);
-	TCPConnection *newconnection = &layer->connections[layer->connectionsSize-1];
-	newconnection->sockfd = newsockfd;
-
-	struct TCPConnectionHandle *callbackhandle;
-    if(!(callbackhandle = malloc(sizeof(struct TCPConnectionHandle))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    callbackhandle->layer = layer;
-	callbackhandle->sockfd = newsockfd;
-	UA_Connection_init(&newconnection->connection, layer->localConf, callbackhandle,
-					   (UA_Connection_closeCallback)closeCallback, (UA_Connection_writeCallback)writeCallback);
-	return UA_STATUSCODE_GOOD;
+// after every select, reset the set of sockets we want to listen on
+static void setFDSet(NetworkLayerTCP *layer) {
+	FD_ZERO(&layer->fdset);
+	FD_SET(layer->serversockfd, &layer->fdset);
+	layer->highestfd = layer->serversockfd;
+	for(UA_Int32 i=0;i<layer->conLinksSize;i++) {
+		FD_SET(layer->conLinks[i].sockfd, &layer->fdset);
+		if(layer->conLinks[i].sockfd > layer->highestfd)
+			layer->highestfd = layer->conLinks[i].sockfd;
+	}
 }
 }
 
 
-// copy the array of connections, but _loose_ one. This does not close the
-// actual socket.
-static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 sockfd) {
-	UA_UInt32 index;
-	for(index = 0;index < layer->connectionsSize;index++) {
-		if(layer->connections[index].sockfd == sockfd)
-			break;
-	}
+// the callbacks are thread-safe if UA_MULTITHREADING is defined
+void closeConnection(TCPConnection *handle);
+void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf);
 
 
-    if(index == layer->connectionsSize)
-        return UA_STATUSCODE_BADINTERNALERROR;
+static UA_StatusCode NetworkLayerTCP_add(NetworkLayerTCP *layer, UA_Int32 newsockfd) {
+    setNonBlocking(newsockfd);
+    TCPConnection *c = malloc(sizeof(TCPConnection));
+	c->sockfd = newsockfd;
+    c->layer = layer;
+    c->connection.state = UA_CONNECTION_OPENING;
+    c->connection.localConf = layer->conf;
+    c->connection.channel = UA_NULL;
+    c->connection.close = (void (*)(void*))closeConnection;
+    c->connection.write = (void (*)(void*, UA_ByteStringArray))writeCallback;
 
 
-    if(layer->connections[index].connection.channel)
-        layer->connections[index].connection.channel->connection = NULL;
+    layer->conLinks = realloc(layer->conLinks, sizeof(ConnectionLink)*(layer->conLinksSize+1));
+    layer->conLinks[layer->conLinksSize].connection = c;
+    layer->conLinks[layer->conLinksSize].sockfd = newsockfd;
+    layer->conLinksSize++;
+	return UA_STATUSCODE_GOOD;
+}
 
 
-	UA_Connection_deleteMembers(&layer->connections[index].connection);
+// Takes the linked list of closed connections and returns the work for the server loop
+static UA_UInt32 batchDeleteLinks(NetworkLayerTCP *layer, UA_WorkItem **returnWork) {
+    UA_WorkItem *work = malloc(sizeof(UA_WorkItem)*layer->conLinksSize);
+#ifdef UA_MULTITHREADING
+    struct deleteLink *d = uatomic_xchg(&layer->deleteLinkList, UA_NULL);
+#else
+    struct deleteLink *d = layer->deleteLinkList;
+    layer->deleteLinkList = UA_NULL;
+#endif
+    UA_UInt32 count = 0;
+    while(d) {
+        UA_Int32 i;
+        for(i = 0;i<layer->conLinksSize;i++) {
+            if(layer->conLinks[i].sockfd == d->sockfd)
+                break;
+        }
+        if(i < layer->conLinksSize) {
+            TCPConnection *c = layer->conLinks[i].connection;
+            layer->conLinksSize--;
+            layer->conLinks[i] = layer->conLinks[layer->conLinksSize];
+            work[count] = (UA_WorkItem)
+                {.type = UA_WORKITEMTYPE_DELAYEDMETHODCALL,
+                 .work.methodCall = {.data = c,
+                                     .method = (void (*)(UA_Server*,void*))freeConnectionCallback} };
+        }
+        struct deleteLink *oldd = d;
+        d = d->next;
+        free(oldd);
+        count++;
+    }
+    *returnWork = work;
+    return count;
+}
 
 
-    layer->connectionsSize--;
-	TCPConnection *newconnections;
-    newconnections = malloc(sizeof(TCPConnection) * layer->connectionsSize);
-	memcpy(newconnections, layer->connections, sizeof(TCPConnection) * index);
-	memcpy(&newconnections[index], &layer->connections[index+1],
-           sizeof(TCPConnection) * (layer->connectionsSize - index));
-    free(layer->connections);
-	layer->connections = newconnections;
-	return UA_STATUSCODE_GOOD;
+#ifdef UA_MULTITHREADING
+void closeConnection(TCPConnection *handle) {
+    if(uatomic_xchg(&handle->connection.state, UA_CONNECTION_CLOSING) == UA_CONNECTION_CLOSING)
+        return;
+    
+    UA_Connection_detachSecureChannel(&handle->connection);
+	shutdown(handle->sockfd,2);
+	CLOSESOCKET(handle->sockfd);
+
+    // Remove the link later in the main thread
+    struct deleteLink *d = malloc(sizeof(struct deleteLink));
+    d->sockfd = handle->sockfd;
+    while(1) {
+        d->next = handle->layer->deleteLinkList;
+        if(uatomic_cmpxchg(&handle->layer->deleteLinkList, d->next, d) == d->next)
+            break;
+    }
 }
 }
+#else
+void closeConnection(TCPConnection *handle) {
+    if(handle->connection.state == UA_CONNECTION_CLOSING)
+        return;
+    handle->connection.state = UA_CONNECTION_CLOSING;
 
 
-/** Callback function */
-void closeCallback(TCPConnectionHandle *handle) {
+    UA_Connection_detachSecureChannel(&handle->connection);
 	shutdown(handle->sockfd,2);
 	shutdown(handle->sockfd,2);
 	CLOSESOCKET(handle->sockfd);
 	CLOSESOCKET(handle->sockfd);
-	NetworklayerTCP_remove(handle->layer, handle->sockfd);
+
+    // Remove the link later in the main thread
+    struct deleteLink *d = malloc(sizeof(struct deleteLink));
+    d->sockfd = handle->sockfd;
+    d->next = handle->layer->deleteLinkList;
+    handle->layer->deleteLinkList = d;
 }
 }
+#endif
 
 
-/** Callback function */
-void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
-	UA_UInt32 total_len = 0;
-	UA_UInt32 nWritten = 0;
-#ifdef WIN32
+/** Accesses only the sockfd in the handle. Can be run from parallel threads. */
+void writeCallback(TCPConnection *handle, UA_ByteStringArray gather_buf) {
+	UA_UInt32 total_len = 0, nWritten = 0;
+#ifdef _WIN32
 	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
 	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
 	int result = 0;
 	int result = 0;
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
@@ -142,20 +197,21 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 		buf[i].len = gather_buf.strings[i].length;
 		buf[i].len = gather_buf.strings[i].length;
 		total_len += gather_buf.strings[i].length;
 		total_len += gather_buf.strings[i].length;
 	}
 	}
-	while (nWritten < total_len) {
-		UA_UInt32 n=0;
+	while(nWritten < total_len) {
+		UA_UInt32 n = 0;
 		do {
 		do {
-			result = WSASend(handle->sockfd, buf, gather_buf.stringsSize , (LPDWORD)&n, 0, NULL, NULL);
+			result = WSASend(handle->sockfd, buf, gather_buf.stringsSize ,
+                             (LPDWORD)&n, 0, NULL, NULL);
 			if(result != 0)
 			if(result != 0)
-				printf("NL_TCP_Writer - Error WSASend, code: %d \n", WSAGetLastError());
-		} while (errno == EINTR);
+				printf("Error WSASend, code: %d \n", WSAGetLastError());
+		} while(errno == EINTR);
 		nWritten += n;
 		nWritten += n;
 	}
 	}
 #else
 #else
 	struct iovec iov[gather_buf.stringsSize];
 	struct iovec iov[gather_buf.stringsSize];
 	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
 	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
-		iov[i].iov_base = gather_buf.strings[i].data;
-		iov[i].iov_len = gather_buf.strings[i].length;
+		iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data,
+                                 .iov_len = gather_buf.strings[i].length};
 		total_len += gather_buf.strings[i].length;
 		total_len += gather_buf.strings[i].length;
 	}
 	}
 	struct msghdr message = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = iov,
 	struct msghdr message = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = iov,
@@ -164,113 +220,15 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 	while (nWritten < total_len) {
 	while (nWritten < total_len) {
 		UA_Int32 n = 0;
 		UA_Int32 n = 0;
 		do {
 		do {
-			n = sendmsg(handle->sockfd, &message, 0);
-		} while (n == -1L && errno == EINTR);
-
-		if (n >= 0) {
-			// TODO: handle incompletely send messages
-			/* nWritten += n; */
-			break;
-		} else {
-			// TODO: error handling
-			break;
-		}
+            n = sendmsg(handle->sockfd, &message, 0);
+        } while (n == -1L && errno == EINTR);
+        nWritten += n;
 	}
 	}
 #endif
 #endif
-    for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++)
-        free(gather_buf.strings[i].data);
 }
 }
 
 
-static UA_StatusCode setNonBlocking(int sockid) {
-#ifdef WIN32
-	u_long iMode = 1;
-	int opts = IOCTLSOCKET(sockid, FIONBIO, &iMode);
-	if (opts != NO_ERROR){
-		printf("ioctlsocket failed with error: %ld\n", opts);
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	return UA_STATUSCODE_GOOD;
-#else
-	int opts = fcntl(sockid,F_GETFL);
-	if (opts < 0) {
-		perror("fcntl(F_GETFL)");
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	opts = (opts | O_NONBLOCK);
-	if (fcntl(sockid,F_SETFL,opts) < 0) {
-		perror("fcntl(F_SETFL)");
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-	return UA_STATUSCODE_GOOD;
-#endif
-}
-
-void readConnection(NetworklayerTCP *layer, UA_Server *server, TCPConnection *entry) {
-	UA_ByteString readBuffer;
-    readBuffer.data = malloc(layer->localConf.recvBufferSize);
-#ifdef WIN32
-	readBuffer.length = recv(entry->sockfd, (char *)readBuffer.data,
-							 layer->localConf.recvBufferSize, 0);
-#else
-	readBuffer.length = read(entry->sockfd, readBuffer.data, layer->localConf.recvBufferSize);
-#endif
-	if (errno != 0) {
-		shutdown(entry->sockfd,2);
-		CLOSESOCKET(entry->sockfd);
-		NetworklayerTCP_remove(layer, entry->sockfd);
-	} else {
-		if(readBuffer.length>0){ //data received to process?
-			UA_Server_processBinaryMessage(server, &entry->connection, &readBuffer);
-		}
-	}
-    readBuffer.length = layer->localConf.recvBufferSize; // because this was malloc'd. Length=0 would lead to errors.
-	UA_ByteString_deleteMembers(&readBuffer);
-}
-
-void worksocks(NetworklayerTCP *layer, UA_Server *server, UA_UInt32 workamount) {
-	// accept new connections
-	if(FD_ISSET(layer->serversockfd,&layer->fdset)) {
-		struct sockaddr_in cli_addr;
-		socklen_t cli_len = sizeof(cli_addr);
-		int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
-		if (newsockfd >= 0) {
-			setNonBlocking(newsockfd);
-			NetworklayerTCP_add(layer, newsockfd);
-		}
-		workamount--;
-	}
-
-	// read from established sockets
-	for(UA_UInt32 i=0;i<layer->connectionsSize && workamount > 0;i++) {
-		if(FD_ISSET(layer->connections[i].sockfd, &layer->fdset)) {
-			UA_Int32 n = 0;
-			IOCTLSOCKET(layer->connections[i].sockfd, FIONREAD, &n);
-			if(n==0){ /* the socket has been closed by the client - remove the socket from the socket list */
-				layer->connections[i].connection.close(layer->connections[i].connection.callbackHandle);
-			}else{
-				readConnection(layer, server, &layer->connections[i]);
-			}
-			workamount--;
-		}
-	}
-}
-
-// after every select, reset the set of sockets we want to listen on
-void setFDSet(NetworklayerTCP *layer) {
-	FD_ZERO(&layer->fdset);
-	FD_SET(layer->serversockfd, &layer->fdset);
-	layer->highestfd = layer->serversockfd;
-	for(UA_UInt32 i=0;i<layer->connectionsSize;i++) {
-		FD_SET(layer->connections[i].sockfd, &layer->fdset);
-		if(layer->connections[i].sockfd > layer->highestfd)
-			layer->highestfd = layer->connections[i].sockfd;
-	}
-    layer->highestfd++;
-}
-
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running) {
-#ifdef WIN32
+UA_StatusCode NetworkLayerTCP_start(NetworkLayerTCP *layer) {
+#ifdef _WIN32
 	WORD wVersionRequested;
 	WORD wVersionRequested;
 	WSADATA wsaData;
 	WSADATA wsaData;
 	wVersionRequested = MAKEWORD(2, 2);
 	wVersionRequested = MAKEWORD(2, 2);
@@ -285,65 +243,126 @@ UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, str
 		return UA_STATUSCODE_BADINTERNALERROR;
 		return UA_STATUSCODE_BADINTERNALERROR;
 	} 
 	} 
 #endif
 #endif
-	struct sockaddr_in serv_addr;
-	memset((void *)&serv_addr, sizeof(serv_addr), 1);
-	serv_addr.sin_family = AF_INET;
-	serv_addr.sin_addr.s_addr = INADDR_ANY;
-	serv_addr.sin_port = htons(layer->port);
+
+	const struct sockaddr_in serv_addr = {
+        .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
+        .sin_port = htons(layer->port), .sin_zero = {0}};
 
 
 	int optval = 1;
 	int optval = 1;
-	if(setsockopt(layer->serversockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
+	if(setsockopt(layer->serversockfd, SOL_SOCKET,
+                  SO_REUSEADDR, (const char *)&optval,
+                  sizeof(optval)) == -1) {
 		perror("setsockopt");
 		perror("setsockopt");
 		CLOSESOCKET(layer->serversockfd);
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 	}
 		
 		
-	if(bind(layer->serversockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
+	if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
+            sizeof(serv_addr)) < 0) {
 		perror("binding");
 		perror("binding");
 		CLOSESOCKET(layer->serversockfd);
 		CLOSESOCKET(layer->serversockfd);
 		return UA_STATUSCODE_BADINTERNALERROR;
 		return UA_STATUSCODE_BADINTERNALERROR;
 	}
 	}
 
 
-#define MAXBACKLOG 10
 	setNonBlocking(layer->serversockfd);
 	setNonBlocking(layer->serversockfd);
 	listen(layer->serversockfd, MAXBACKLOG);
 	listen(layer->serversockfd, MAXBACKLOG);
+    printf("Listening for TCP connections on %s:%d\n",
+           inet_ntoa(serv_addr.sin_addr),
+           ntohs(serv_addr.sin_port));
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_Int32 NetworkLayerTCP_getWork(NetworkLayerTCP *layer, UA_WorkItem **workItems,
+                                 UA_UInt16 timeout) {
+    UA_WorkItem *items = UA_NULL;
+    UA_Int32 itemsCount = batchDeleteLinks(layer, &items);
+    setFDSet(layer);
+    struct timeval tmptv = {0, timeout};
+    UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+
+    if(resultsize < 0) {
+        *workItems = items;
+        return itemsCount;
+    }
 
 
-	while (*running) {
-		setFDSet(layer);
-		struct timeval tmptv = tv;
-		UA_Int32 resultsize = select(layer->highestfd, &layer->fdset, NULL, NULL, &tmptv);
-		if (resultsize <= 0) {
-#ifdef WIN32
-			UA_Int32 err = (resultsize == SOCKET_ERROR) ? WSAGetLastError() : 0;
-			switch (err) {
-			case WSANOTINITIALISED:
-			case WSAEFAULT:
-			case WSAENETDOWN:
-			case WSAEINVAL:
-			case WSAEINTR:
-			case WSAEINPROGRESS:
-			case WSAENOTSOCK:
+	// accept new connections (can only be a single one)
+	if(FD_ISSET(layer->serversockfd,&layer->fdset)) {
+		resultsize--;
+		struct sockaddr_in cli_addr;
+		socklen_t cli_len = sizeof(cli_addr);
+		int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
+		if (newsockfd >= 0)
+			NetworkLayerTCP_add(layer, newsockfd);
+	}
+    
+    items = realloc(items, sizeof(UA_WorkItem)*(itemsCount+resultsize));
+
+	// read from established sockets
+    UA_Int32 j = itemsCount;
+    UA_ByteString buf = {-1, NULL};
+	for(UA_Int32 i=0;i<layer->conLinksSize && j<itemsCount+resultsize;i++) {
+		if(!(FD_ISSET(layer->conLinks[i].sockfd, &layer->fdset)))
+            continue;
+
+        if(buf.data == NULL)
+            buf.data = malloc(layer->conf.recvBufferSize);
+        
+#ifdef _WIN32
+        buf.length = recv(layer->conLinks[i].connection.sockfd, (char *)buf.data,
+                          layer->conf.recvBufferSize, 0);
 #else
 #else
-			UA_Int32 err = errno;
-			switch (err) {
-			case EBADF:
-			case EINTR:
-			case EINVAL:
+        buf.length = read(layer->conLinks[i].sockfd, buf.data, layer->conf.recvBufferSize);
 #endif
 #endif
-				// todo: handle errors
-				printf("UA_Stack_msgLoop - result=%d, errno={%d,%s}\n", resultsize, errno, strerror(errno));
-				break;
-			default:
-				// timeout
-				worker(server);
-			}
-		} else { // activity on listener or client socket
-			worksocks(layer, server, resultsize);
-			worker(server);
-		}
-	}
-#ifdef WIN32
+
+        if (errno != 0 || buf.length == 0) {
+            closeConnection(layer->conLinks[i].connection); // work is returned in the next iteration
+        } else {
+            items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE;
+            items[j].work.binaryNetworkMessage.message = buf;
+            items[j].work.binaryNetworkMessage.connection = &layer->conLinks[i].connection->connection;
+            buf.data = NULL;
+            j++;
+        }
+    }
+
+    if(buf.data != NULL)
+        free(buf.data);
+
+    if(j == 0) {
+        free(items);
+        *workItems = NULL;
+    } else
+        *workItems = items;
+    return j;
+}
+
+UA_Int32 NetworkLayerTCP_stop(NetworkLayerTCP * layer, UA_WorkItem **workItems) {
+	for(UA_Int32 index = 0;index < layer->conLinksSize;index++)
+        closeConnection(layer->conLinks[index].connection);
+#ifdef _WIN32
 	WSACleanup();
 	WSACleanup();
 #endif
 #endif
-	return UA_STATUSCODE_GOOD;
+    return batchDeleteLinks(layer, workItems);
+}
+
+void NetworkLayerTCP_delete(NetworkLayerTCP *layer) {
+	free(layer->conLinks);
+	free(layer);
+}
+
+UA_NetworkLayer NetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
+    NetworkLayerTCP *tcplayer = malloc(sizeof(NetworkLayerTCP));
+	tcplayer->conf = conf;
+	tcplayer->conLinksSize = 0;
+	tcplayer->conLinks = NULL;
+    tcplayer->port = port;
+    tcplayer->deleteLinkList = UA_NULL;
+
+    UA_NetworkLayer nl;
+    nl.nlHandle = tcplayer;
+    nl.start = (UA_StatusCode (*)(void*))NetworkLayerTCP_start;
+    nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) NetworkLayerTCP_getWork;
+    nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) NetworkLayerTCP_stop;
+    nl.delete = (void (*)(void*))NetworkLayerTCP_delete;
+    return nl;
 }
 }

+ 7 - 13
examples/networklayer_tcp.h

@@ -6,23 +6,17 @@
 #ifndef NETWORKLAYERTCP_H_
 #ifndef NETWORKLAYERTCP_H_
 #define NETWORKLAYERTCP_H_
 #define NETWORKLAYERTCP_H_
 
 
-#ifdef WIN32
-  #include "winsock2.h"
-#else
-  #include <sys/mman.h>
-  #include <sys/wait.h>
-  #include <unistd.h>
-  #include <sys/time.h>
+#ifdef __cplusplus
+extern "C" {
 #endif
 #endif
 
 
 #include "ua_server.h"
 #include "ua_server.h"
 
 
-struct NetworklayerTCP;
-typedef struct NetworklayerTCP NetworklayerTCP;
+/** @brief Create the TCP networklayer and listen to the specified port */
+UA_NetworkLayer NetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
 
 
-NetworklayerTCP * NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port);
-void NetworklayerTCP_delete(NetworklayerTCP *layer);
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running);
+#ifdef __cplusplus
+} // extern "C"
+#endif
 
 
 #endif /* NETWORKLAYERTCP_H_ */
 #endif /* NETWORKLAYERTCP_H_ */

+ 0 - 180
examples/networklayer_tcp_concurrent.c

@@ -1,180 +0,0 @@
-/*
- * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
- */
-
-#define _GNU_SOURCE
-
-#include <uv.h>
-#include <assert.h>
-#include <malloc.h>
-#include "ua_transport.h"
-#include "ua_statuscodes.h"
-#include "networklayer_tcp.h"
-
-struct NetworklayerTCP {
-    UA_Server *server;
-    uv_loop_t *uvloop;
-    uv_tcp_t uvserver;
-    UA_Boolean *running;
-	UA_ConnectionConfig localConf;
-	UA_UInt32 port;
-	UA_UInt32 connectionsSize;
-};
-
-NetworklayerTCP * NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port) {
-    NetworklayerTCP *newlayer = malloc(sizeof(NetworklayerTCP));
-    if(newlayer) {
-        newlayer->localConf = localConf;
-        newlayer->port = port;
-    }
-	return newlayer;
-}
-
-void NetworklayerTCP_delete(NetworklayerTCP *layer) {
-    free(layer);
-}
-
-// callback structure to delete the buffer after the asynchronous write finished
-typedef struct {
-	uv_write_t req;
-    unsigned int bufsize;
-    uv_buf_t *bufs;
-} write_req_t;
-
-static void on_close(uv_handle_t * handle) {
-	if (handle->data) {
-        //UA_Connection_deleteMembers((UA_Connection*) handle->data);
-        free(handle->data);
-    }
-    free(handle);
-}
-
-static void closeHandle(void *handle) {
-    uv_close((uv_handle_t *)handle, on_close);
-}
-
-
-static void after_shutdown(uv_shutdown_t * req, int status) {
-    uv_close((uv_handle_t *) req->handle, on_close);
-    free(req);
-}
-
-static void after_write(uv_write_t * req, int status) {
-    write_req_t *wr = (write_req_t*)req; // todo: use container_of
-    for(UA_UInt32 i=0;i<wr->bufsize;i++)
-        free(wr->bufs[i].base);
-    free(wr->bufs);
-
-    if (status) {
-		printf("uv_write error");
-		uv_close((uv_handle_t *) req->handle, on_close);
-	}
-
-    free(wr);
-}
-
-static void writeHandle(void *handle, const UA_ByteStringArray buf) {
-    uv_buf_t *uv_bufs = malloc(buf.stringsSize * sizeof(uv_buf_t));
-    for(UA_UInt32 i=0; i<buf.stringsSize; i++) {
-        uv_bufs[i].len = buf.strings[i].length;
-        uv_bufs[i].base = (char*)buf.strings[i].data;
-    }
-
-	write_req_t *wr = malloc(sizeof(write_req_t));
-    wr->bufsize = buf.stringsSize;
-    wr->bufs = uv_bufs;
-
-	if(uv_write(&wr->req, (uv_stream_t*)handle, uv_bufs, buf.stringsSize, after_write))
-        printf("uv_write failed");
-}
-
-static void handle_message(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) {
-    if (nread < 0) {
-		printf("connection ended");
-		if (buf.base)
-            free(buf.base);
-		uv_shutdown_t *req = malloc(sizeof(uv_shutdown_t));
-		uv_shutdown(req, handle, after_shutdown);
-		return;
-    }
-    if (nread == 0) {
-		free(buf.base);
-		return;
-    }
-
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    UA_Server *server = (UA_Server*) layer->server;
-    UA_Connection *connection = (UA_Connection*) handle->data;
-    UA_ByteString readBuffer;
-    readBuffer.length = nread; // the buffer might be longer
-    readBuffer.data = (UA_Byte*)buf.base;
-    UA_Server_processBinaryMessage(server, connection, &readBuffer);
-
-    free(buf.base);
-	return;
-}
-
-static uv_buf_t read_alloc(uv_handle_t * handle, size_t suggested_size) {
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    UA_UInt32 receive_bufsize = layer->localConf.recvBufferSize;
-	char* buf = malloc(sizeof(char)*receive_bufsize);
-    return uv_buf_init(buf, receive_bufsize);
-}
-
-static void on_connection(uv_stream_t *server, int status) {
-    if (status) {
-        printf("Connect error");
-        return;
-    }
-    uv_loop_t *loop = server->loop;
-    NetworklayerTCP *layer= (NetworklayerTCP*)loop->data;
-
-    uv_tcp_t *stream = malloc(sizeof(uv_tcp_t));
-    if(uv_tcp_init(loop, stream))
-        return;
-
-    UA_Connection *connection = malloc(sizeof(UA_Connection));
-    UA_Connection_init(connection, layer->localConf, stream, closeHandle, writeHandle);
-    stream->data = connection;
-
-    assert(uv_accept(server, (uv_stream_t*)stream) == 0);
-    assert(uv_read_start((uv_stream_t*)stream, read_alloc, handle_message) == 0);
-}
-
-void check_running(uv_timer_t* handle, int status) {
-    NetworklayerTCP *layer = (NetworklayerTCP*)handle->loop->data;
-    if(!*layer->running)
-        uv_stop(layer->uvloop);
-}
-
-UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
-                                  void(*worker)(UA_Server*), UA_Boolean *running) {
-    layer->uvloop = uv_default_loop();
-    layer->server = server;
-    struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", layer->port);
-    if(uv_tcp_init(layer->uvloop, &layer->uvserver)) {
-		printf("Socket creation error\n");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    
-    if(uv_tcp_bind(&layer->uvserver, addr)) {
-        printf("Bind error\n");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-#define MAXBACKLOG 10
-    if(uv_listen((uv_stream_t*)&layer->uvserver, MAXBACKLOG, on_connection)) {
-        printf("Listen error");
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    layer->uvloop->data = (void*)layer; // so we can get the pointer to the server
-    layer->running = running;
-    
-    uv_timer_t timer_check_running;
-    uv_timer_init(layer->uvloop, &timer_check_running);
-    uv_timer_start(&timer_check_running, check_running, 0, 500);
-    
-    uv_run(layer->uvloop, UV_RUN_DEFAULT);
-    return UA_STATUSCODE_GOOD;
-}

+ 24 - 33
examples/opcuaServer.c

@@ -16,20 +16,13 @@
 #include "logger_stdout.h"
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 #include "networklayer_tcp.h"
 
 
-#ifdef MULTITHREADING
-#include <urcu.h>
-#endif
-
 UA_Boolean running = 1;
 UA_Boolean running = 1;
 
 
 void stopHandler(int sign) {
 void stopHandler(int sign) {
+    printf("Received Ctrl-C\n");
 	running = 0;
 	running = 0;
 }
 }
 
 
-void serverCallback(UA_Server *server) {
-    //	printf("does whatever servers do\n");
-}
-
 UA_ByteString loadCertificate() {
 UA_ByteString loadCertificate() {
     UA_ByteString certificate = UA_STRING_NULL;
     UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
 	FILE *fp = NULL;
@@ -37,7 +30,7 @@ UA_ByteString loadCertificate() {
 	fp=fopen("localhost.der", "rb");
 	fp=fopen("localhost.der", "rb");
 
 
 	if(!fp) {
 	if(!fp) {
-        errno = 0; // otherwise we think sth went wrong on the tcp socket level
+        errno = 0; // we read errno also from the tcp layer...
         return certificate;
         return certificate;
     }
     }
 
 
@@ -52,25 +45,30 @@ UA_ByteString loadCertificate() {
 
 
     return certificate;
     return certificate;
 }
 }
+
+void testCallback(UA_Server *server, void *data) {
+       printf("testcallback\n");
+}
+
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {
-#ifdef MULTITHREADING
-    rcu_register_thread();
-#endif
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
 
-	UA_String endpointUrl;
-    UA_String_copycstring("opc.tcp://localhost:16664",&endpointUrl);
-	UA_ByteString certificate = loadCertificate();
-	UA_Server *server = UA_Server_new(&endpointUrl, &certificate);
+	UA_Server *server = UA_Server_new();
+    UA_Server_setServerCertificate(server, loadCertificate());
+    UA_Server_addNetworkLayer(server, NetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = testCallback, .data = UA_NULL} };
+    UA_Server_addRepeatedWorkItem(server, &work, 20000000); // call every 2 sec
 
 
 	//add a node to the adresspace
 	//add a node to the adresspace
-    UA_Int32 *myInteger = malloc(sizeof(UA_Int32));
+    UA_Int32 *myInteger = UA_Int32_new();
     *myInteger = 42;
     *myInteger = 42;
     UA_QualifiedName myIntegerName;
     UA_QualifiedName myIntegerName;
-    UA_QualifiedName_copycstring("the answer is",&myIntegerName);
-    UA_Server_addScalarVariableNode(server, &myIntegerName, myInteger, &UA_TYPES[UA_INT32],
-                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_ORGANIZES]);
-    UA_QualifiedName_deleteMembers(&myIntegerName);
+    UA_QUALIFIEDNAME_STATIC(myIntegerName, "the answer");
+    UA_Server_addScalarVariableNode(server, &myIntegerName,
+                                    myInteger, &UA_TYPES[UA_INT32],
+                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
+                                    &UA_NODEIDS[UA_ORGANIZES]);
     
     
 #ifdef BENCHMARK
 #ifdef BENCHMARK
     UA_UInt32 nodeCount = 500;
     UA_UInt32 nodeCount = 500;
@@ -89,21 +87,14 @@ int main(int argc, char** argv) {
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         tmpNode->value.storage.data.arrayLength = 1;
         tmpNode->value.storage.data.arrayLength = 1;
-        UA_Server_addNode(server, (const UA_Node**)&tmpNode, &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
+        UA_Server_addNode(server, (const UA_Node**)&tmpNode,
+                          &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
                           &UA_NODEIDS[UA_HASCOMPONENT]);
                           &UA_NODEIDS[UA_HASCOMPONENT]);
     }
     }
 #endif
 #endif
-	
-	#define PORT 16664
-	NetworklayerTCP* nl = NetworklayerTCP_new(UA_ConnectionConfig_standard, PORT);
-	printf("Server started, connect to to opc.tcp://127.0.0.1:%i\n", PORT);
-	struct timeval callback_interval = {1, 0}; // 1 second
-	UA_Int32 retval = NetworkLayerTCP_run(nl, server, callback_interval, serverCallback, &running);
+
+    UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
 	UA_Server_delete(server);
-	NetworklayerTCP_delete(nl);
-    UA_String_deleteMembers(&endpointUrl);
-#ifdef MULTITHREADING
-    rcu_unregister_thread();
-#endif
+
 	return retval;
 	return retval;
 }
 }

+ 13 - 18
include/ua_connection.h

@@ -22,11 +22,11 @@ extern "C" {
 
 
 #include "ua_types.h"
 #include "ua_types.h"
 
 
-/** @defgroup communication Communication */
-
 /**
 /**
- * @ingroup communication
- * @defgroup connection Connection */
+ * @defgroup communication Communication
+ *
+ * @{
+ */
 
 
 /** Used for zero-copy communication. The array of bytestrings is sent over the
 /** Used for zero-copy communication. The array of bytestrings is sent over the
    network as a single buffer. */
    network as a single buffer. */
@@ -36,9 +36,9 @@ typedef struct UA_ByteStringArray {
 } UA_ByteStringArray;
 } UA_ByteStringArray;
 
 
 typedef enum UA_ConnectionState {
 typedef enum UA_ConnectionState {
-    UA_CONNECTION_OPENING,
-    UA_CONNECTION_CLOSING,
-    UA_CONNECTION_ESTABLISHED
+    UA_CONNECTION_OPENING, ///< The port is open, but we haven't received a HEL
+    UA_CONNECTION_ESTABLISHED, ///< The port is open and the connection configured
+    UA_CONNECTION_CLOSING, ///< The port has been closed and the connection will be deleted
 } UA_ConnectionState;
 } UA_ConnectionState;
 
 
 typedef struct UA_ConnectionConfig {
 typedef struct UA_ConnectionConfig {
@@ -49,30 +49,25 @@ typedef struct UA_ConnectionConfig {
     UA_UInt32 maxChunkCount;
     UA_UInt32 maxChunkCount;
 } UA_ConnectionConfig;
 } UA_ConnectionConfig;
 
 
-extern UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
+extern const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard;
 
 
 /* Forward declaration */
 /* Forward declaration */
 struct UA_SecureChannel;
 struct UA_SecureChannel;
 typedef struct UA_SecureChannel UA_SecureChannel;
 typedef struct UA_SecureChannel UA_SecureChannel;
 
 
-typedef void (*UA_Connection_writeCallback)(void *handle, const UA_ByteStringArray buf);
-typedef void (*UA_Connection_closeCallback)(void *handle);
-
 typedef struct UA_Connection {
 typedef struct UA_Connection {
     UA_ConnectionState  state;
     UA_ConnectionState  state;
     UA_ConnectionConfig localConf;
     UA_ConnectionConfig localConf;
     UA_ConnectionConfig remoteConf;
     UA_ConnectionConfig remoteConf;
     UA_SecureChannel   *channel;
     UA_SecureChannel   *channel;
-    void *callbackHandle;
-    UA_Connection_writeCallback write;
-    UA_Connection_closeCallback close;
+    void (*write)(void *connection, UA_ByteStringArray buf);
+    void (*close)(void *connection);
 } UA_Connection;
 } UA_Connection;
 
 
-UA_StatusCode UA_EXPORT UA_Connection_init(UA_Connection *connection, UA_ConnectionConfig localConf, void *callbackHandle,
-                                         UA_Connection_closeCallback close, UA_Connection_writeCallback write);
-void UA_EXPORT UA_Connection_deleteMembers(UA_Connection *connection);
+void UA_EXPORT UA_Connection_detachSecureChannel(UA_Connection *connection);
+// void UA_Connection_attachSecureChannel(UA_Connection *connection);
 
 
-// todo: closing a binaryconnection that was closed on the network level
+/** @} */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 1 - 2
include/ua_log.h

@@ -27,8 +27,7 @@ extern "C" {
  *
  *
  * @defgroup logging Logging
  * @defgroup logging Logging
  *
  *
- * @brief Logging functionality is externally provided to the open62541 libary.
- * The server contains a logger-struct with function callbacks
+ * @brief Custom logging solutions can be "plugged in" with this interface
  */
  */
 
 
 typedef enum UA_LoggerCategory {
 typedef enum UA_LoggerCategory {

+ 132 - 24
include/ua_server.h

@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (C) 2014 the contributors as stated in the AUTHORS file
  * Copyright (C) 2014 the contributors as stated in the AUTHORS file
  *
  *
  * This file is part of open62541. open62541 is free software: you can
  * This file is part of open62541. open62541 is free software: you can
@@ -21,6 +21,7 @@ extern "C" {
 #endif
 #endif
 
 
 #include "ua_types.h"
 #include "ua_types.h"
+#include "ua_util.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated.h"
 #include "ua_connection.h"
 #include "ua_connection.h"
 #include "ua_log.h"
 #include "ua_log.h"
@@ -28,50 +29,157 @@ extern "C" {
 /**
 /**
  * @defgroup server Server
  * @defgroup server Server
  *
  *
- * @brief This module describes the server object and functions to interact with * it.
- *
  * @{
  * @{
  */
  */
 
 
 struct UA_Server;
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 typedef struct UA_Server UA_Server;
 
 
-UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate);
+UA_Server UA_EXPORT * UA_Server_new();
+void UA_EXPORT UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
-void UA_EXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
-                                              const UA_ByteString *msg);
 
 
 /**
 /**
- * @brief Adds a node to the server's address space
+ * Runs the main loop of the server. In each iteration, this calls into the
+ * networklayers to see if work have arrived and checks if timed events need to
+ * be triggered.
+ *
+ * @param server The server object
+ * @param nThreads The number of worker threads. Is ignored if MULTITHREADING is
+ * not activated.
+ * @param running Points to a booloean value on the heap. When running is set to
+ * false, the worker threads and the main loop close and the server is shut
+ * down.
+ * @return Indicates whether the server shut down cleanly
+ *
+ */
+UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
+
+/**
+ * Add a node to the server's address space
  *
  *
  * If adding the node succeeds, the pointer to the node is set to null. If the
  * If adding the node succeeds, the pointer to the node is set to null. If the
  * original nodeid is null (ns=0,i=0), a unique new nodeid is created for the
  * original nodeid is null (ns=0,i=0), a unique new nodeid is created for the
  * node and returned in the AddNodesResult struct. */
  * node and returned in the AddNodesResult struct. */
-UA_AddNodesResult UA_EXPORT
-UA_Server_addNode(UA_Server *server, const UA_Node **node, const UA_ExpandedNodeId *parentNodeId,
-                  const UA_NodeId *referenceTypeId);
+UA_AddNodesResult UA_EXPORT UA_Server_addNode(UA_Server *server, const UA_Node **node,
+                                              const UA_ExpandedNodeId *parentNodeId,
+                                              const UA_NodeId *referenceTypeId);
 
 
-/** @brief Adds a reference to the server's address space */
-UA_StatusCode UA_EXPORT
-UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
+/** Add a reference to the server's address space */
+UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
 
 
 /**
 /**
- * @brief Adds a VariableNode to the server's address space that points to a
- * scalar value. The value must lie on the heap and cannot be reused afterwards
- * as it becomes attached to the lifecycle of the VariableNode */
-void UA_EXPORT
-UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
-                                const UA_TypeVTable *vt, const UA_ExpandedNodeId *parentNodeId,
-                                const UA_NodeId *referenceTypeId );
+ * Add a scalar variable (node) to the server's address space
+ *
+ * The value must lie on the heap and must not be reused after adding it, as it
+ * becomes attached to the lifecycle of the VariableNode. */
+void UA_EXPORT UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName,
+                                               void *value, const UA_TypeVTable *vt,
+                                               const UA_ExpandedNodeId *parentNodeId,
+                                               const UA_NodeId *referenceTypeId );
+
+/** Work that is run in the main loop (singlethreaded) or dispatched to a worker
+    thread. */
+typedef struct UA_WorkItem {
+    enum {
+        UA_WORKITEMTYPE_NOTHING,
+        UA_WORKITEMTYPE_BINARYNETWORKMESSAGE,
+        UA_WORKITEMTYPE_METHODCALL,
+        UA_WORKITEMTYPE_DELAYEDMETHODCALL,
+    } type;
+    union {
+        struct {
+            UA_Connection *connection;
+            UA_ByteString message;
+        } binaryNetworkMessage;
+        struct {
+            void * data;
+            void (*method)(UA_Server *server, void *data);
+        } methodCall;
+    } work;
+} UA_WorkItem;
+
+/**
+ * Add work that is executed at a given time in the future. If the indicated
+ * time lies in the past, the work is executed immediately.
+ *
+ * The work pointer is not freed but copied to an internal representation
+ */
+UA_Guid UA_EXPORT UA_Server_addTimedWorkItem(UA_Server *server, UA_WorkItem *work, UA_DateTime time);
+
+/**
+ *  Add work that is executed repeatedly with the given interval (in 100ns). If
+ *  work with the same repetition interval already exists, the first execution
+ *  might occur sooner.
+ *
+ * The work pointer is not freed but copied to an internal representation
+ */
+UA_Guid UA_EXPORT UA_Server_addRepeatedWorkItem(UA_Server *server, UA_WorkItem *work, UA_UInt32 interval);
+
+/** Remove timed or repeated work */
+UA_Boolean UA_EXPORT UA_Server_removeWorkItem(UA_Server *server, UA_Guid workId);
+
+/**
+ * Interface to the binary network layers. This structure is returned from the
+ * function that initializes the network layer. The layer is already bound to a
+ * specific port and listening. The functions in the structure are never called
+ * in parallel but only sequentially from the server's main loop. So the network
+ * layer does not need to be thread-safe.
+ */
+typedef struct {
+    void *nlHandle;
+
+    /**
+     * Starts listening on the the networklayer.
+     *
+     * @return Returns UA_STATUSCODE_GOOD or an error code.
+     */
+    UA_StatusCode (*start)(void *nlHandle);
+    
+    /**
+     * Gets called from the main server loop and returns the work that
+     * accumulated (messages and close events) for dispatch. The networklayer
+     * does not wait on connections but returns immediately the work that
+     * accumulated.
+     *
+     * @param workItems When the returned integer is positive, *workItems points
+     * to an array of WorkItems of the returned size.
+     * @param timeout The timeout during which an event must arrive.
+     * @return The size of the returned workItems array. If the result is
+     * negative, an error has occured.
+     */
+    UA_Int32 (*getWork)(void *nlhandle, UA_WorkItem **workItems, UA_UInt16 timeout);
+
+    /**
+     * Closes the network connection and returns all the work that needs to
+     * be finished before the network layer can be safely deleted.
+     *
+     * @param workItems When the returned integer is positive, *workItems points
+     * to an array of WorkItems of the returned size.
+     * @return The size of the returned workItems array. If the result is
+     * negative, an error has occured.
+     */
+    UA_Int32 (*stop)(void *nlhandle, UA_WorkItem **workItems);
+
+    /** Deletes the network layer. Call only after a successfull shutdown. */
+    void (*delete)(void *nlhandle);
+} UA_NetworkLayer;
+
+/**
+ * Adds a network layer to the server. The network layer is destroyed together
+ * with the server. Do not use it after adding it as it might be moved around on
+ * the heap.
+ */
+void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_NetworkLayer networkLayer);
+
 /** @} */
 /** @} */
 
 
 /**
 /**
- * @ingroup server
+ * @ingroup nodestore
  *
  *
  * @defgroup external_nodestore External Nodestore
  * @defgroup external_nodestore External Nodestore
  *
  *
- * @brief This modules describes the VTable and function signatures to add an
- * external nodestore to the server.
+ * @brief An external application that manages its own data and data model
  *
  *
  * To plug in outside data sources, one can use
  * To plug in outside data sources, one can use
  *
  *
@@ -82,7 +190,7 @@ UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName,
  * the "local" nodestore of open62541. Namespace Zero is always in the local
  * the "local" nodestore of open62541. Namespace Zero is always in the local
  * nodestore.
  * nodestore.
  *
  *
- *  @{
+ * @{
  */
  */
 
 
 typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)
 typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)

+ 34 - 31
include/ua_types.h

@@ -20,41 +20,38 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+#include "ua_config.h"
+
 #include <stdint.h>
 #include <stdint.h>
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #include <stdio.h>
 #include <stdio.h>
 #endif
 #endif
 
 
-#include "ua_config.h"
-    
 /**
 /**
  * @defgroup types Datatypes
  * @defgroup types Datatypes
  *
  *
- * @brief This module describes the datatypes used in OPC UA. There is a
- * division into built-in datatypes (integers, strings, etc.) and more complex
- * datatypes that are comprise of built-in datatypes (structs defined in the OPC
- * UA standard).
- *
- * All datatypes follow the same schema in the naming of relevant functions.
+ * @brief The built-in datatypes. The remaining datatypes are autogenerated from
+ * XML descriptions as they are all enums or structures made up of the built-in
+ * datatypes.
  *
  *
- * DO NOT CALL THESE FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY
- * PERMITTED.
+ * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
+ * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
  *
  *
- * - <type>_new: Allocates the memory for the type and runs <type>_init on the
- *     returned variable. Returns null if no memory could be allocated.
+ * - _new: Allocates the memory for the type and runs _init on the returned
+ *   variable. Returns null if no memory could be allocated.
  *
  *
- * - <type>_init: Sets all members to a "safe" standard, usually zero. Arrays
- *   (e.g. for strings) are set to a length of -1.
+ * - _init: Sets all members to a "safe" standard, usually zero. Arrays (e.g.
+ *   for strings) are set to a length of -1.
  *
  *
- * - <type>_copy: Copies a datatype. This performs a deep copy that iterates
- *    over the members. Copying into variants with an external data source is
- *    not permitted. If copying fails, a deleteMembers is performed and an error
+ * - _copy: Copies a datatype. This performs a deep copy that iterates over the
+ *    members. Copying into variants with an external data source is not
+ *    permitted. If copying fails, a deleteMembers is performed and an error
  *    code returned.
  *    code returned.
  *
  *
- * - <type>_delete: Frees the memory where the datatype was stored. This
- *   performs an _deleteMembers internally if required.
+ * - _delete: Frees the memory where the datatype was stored. This performs an
+ *   _deleteMembers internally if required.
  *
  *
- * - <type>_deleteMembers: Frees the memory of dynamically sized members (e.g. a
+ * - _deleteMembers: Frees the memory of dynamically sized members (e.g. a
  *   string) of a datatype. This is useful when the datatype was allocated on
  *   string) of a datatype. This is useful when the datatype was allocated on
  *   the stack, whereas the dynamically sized members is heap-allocated. To
  *   the stack, whereas the dynamically sized members is heap-allocated. To
  *   reuse the variable, the remaining members (not dynamically allocated) need
  *   reuse the variable, the remaining members (not dynamically allocated) need
@@ -118,8 +115,12 @@ typedef struct {
     UA_Byte *data;
     UA_Byte *data;
 } UA_String;
 } UA_String;
 
 
-/** @brief An instance in time. */
-typedef UA_Int64 UA_DateTime; //100 nanosecond resolution
+/** @brief An instance in time.
+ *
+ * A DateTime value is encoded as a 64-bit signed integer which represents the
+ * number of 100 nanosecond intervals since January 1, 1601 (UTC).
+ */
+typedef UA_Int64 UA_DateTime; // 100 nanosecond resolution
 
 
 /** @brief A 16 byte value that can be used as a globally unique identifier. */
 /** @brief A 16 byte value that can be used as a globally unique identifier. */
 typedef struct {
 typedef struct {
@@ -278,14 +279,14 @@ typedef void UA_InvalidType;
 /* Functions */
 /* Functions */
 /*************/
 /*************/
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #define PRINTTYPE(TYPE) void UA_EXPORT TYPE##_print(const TYPE *p, FILE *stream);
 #define PRINTTYPE(TYPE) void UA_EXPORT TYPE##_print(const TYPE *p, FILE *stream);
 #define PRINTTYPE_NOEXPORT(TYPE) void TYPE##_print(const TYPE *p, FILE *stream);
 #define PRINTTYPE_NOEXPORT(TYPE) void TYPE##_print(const TYPE *p, FILE *stream);
 #else
 #else
 #define PRINTTYPE(TYPE)
 #define PRINTTYPE(TYPE)
 #define PRINTTYPE_NOEXPORT(TYPE)
 #define PRINTTYPE_NOEXPORT(TYPE)
 #endif
 #endif
-    
+
 #define UA_TYPE_PROTOTYPES(TYPE)                                     \
 #define UA_TYPE_PROTOTYPES(TYPE)                                     \
     TYPE UA_EXPORT * TYPE##_new();                                   \
     TYPE UA_EXPORT * TYPE##_new();                                   \
     void UA_EXPORT TYPE##_init(TYPE * p);                            \
     void UA_EXPORT TYPE##_init(TYPE * p);                            \
@@ -343,7 +344,7 @@ UA_TYPE_PROTOTYPES(UA_InvalidType)
 UA_StatusCode UA_EXPORT UA_String_copycstring(char const *src, UA_String *dst);
 UA_StatusCode UA_EXPORT UA_String_copycstring(char const *src, UA_String *dst);
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_String_printf(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printf(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx_hex(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx_hex(char const *label, const UA_String *string);
@@ -359,7 +360,7 @@ typedef struct UA_DateTimeStruct {
     UA_Int16 min;
     UA_Int16 min;
     UA_Int16 hour;
     UA_Int16 hour;
     UA_Int16 day;
     UA_Int16 day;
-    UA_Int16 mounth;
+    UA_Int16 month;
     UA_Int16 year;
     UA_Int16 year;
 } UA_DateTimeStruct;
 } UA_DateTimeStruct;
 UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
 UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
@@ -367,11 +368,13 @@ UA_StatusCode UA_EXPORT UA_DateTime_toString(UA_DateTime time, UA_String *timeSt
 
 
 /* Guid */
 /* Guid */
 UA_Boolean UA_EXPORT UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
 UA_Boolean UA_EXPORT UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);
+/** Do not use for security-critical entropy! */
+UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed);
 
 
 /* ByteString */
 /* ByteString */
 UA_Boolean UA_EXPORT UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2);
 UA_Boolean UA_EXPORT UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2);
 UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
 UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_ByteString_printf(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printf(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx_hex(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx_hex(char *label, const UA_ByteString *string);
@@ -389,7 +392,7 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
         VARIABLE.namespaceIndex = 0;                   \
         VARIABLE.namespaceIndex = 0;                   \
         UA_STRING_STATIC(VARIABLE.name, STRING); } while(0)
         UA_STRING_STATIC(VARIABLE.name, STRING); } while(0)
 UA_StatusCode UA_EXPORT UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *dst);
 UA_StatusCode UA_EXPORT UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *dst);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn);
 void UA_EXPORT UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn);
 #endif
 #endif
 
 
@@ -411,7 +414,7 @@ void UA_EXPORT UA_Array_delete(void *p, UA_Int32 noElements, const UA_TypeVTable
 
 
 /* @brief The destination array is allocated with size noElements. */
 /* @brief The destination array is allocated with size noElements. */
 UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst);
 UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst);
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream);
 void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream);
 #endif
 #endif
 
 
@@ -438,7 +441,7 @@ struct UA_TypeVTable {
     UA_StatusCode (*copy)(void const *src, void *dst);
     UA_StatusCode (*copy)(void const *src, void *dst);
     void          (*delete)(void *p);
     void          (*delete)(void *p);
     void          (*deleteMembers)(void *p);
     void          (*deleteMembers)(void *p);
-#ifdef DEBUG
+#ifdef UA_DEBUG
     void          (*print)(const void *p, FILE *stream);
     void          (*print)(const void *p, FILE *stream);
 #endif
 #endif
     UA_UInt32  memSize;                        // size of the struct
     UA_UInt32  memSize;                        // size of the struct

+ 8 - 8
src/server/ua_nodestore.c

@@ -35,7 +35,7 @@ static hash_t const primes[] = {
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
 };
 };
 
 
-static INLINE UA_Int16 higher_prime_index(hash_t n) {
+static UA_Int16 higher_prime_index(hash_t n) {
     UA_UInt16 low  = 0;
     UA_UInt16 low  = 0;
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
     while(low != high) {
     while(low != high) {
@@ -103,10 +103,10 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_Int32 nsize = primes[nindex];
     UA_Int32 nsize = primes[nindex];
     struct nodeEntry **nentries;
     struct nodeEntry **nentries;
-    if(!(nentries = UA_alloc(sizeof(struct nodeEntry *) * nsize)))
+    if(!(nentries = UA_malloc(sizeof(struct nodeEntry *) * nsize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
-    memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
+    UA_memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
     struct nodeEntry **oentries = ns->entries;
     struct nodeEntry **oentries = ns->entries;
     ns->entries = nentries;
     ns->entries = nentries;
     ns->size    = nsize;
     ns->size    = nsize;
@@ -196,9 +196,9 @@ static INLINE struct nodeEntry * nodeEntryFromNode(const UA_Node *node) {
     }
     }
 
 
     struct nodeEntry *entry;
     struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_NULL;
         return UA_NULL;
-    memcpy((void *)&entry->node, node, nodesize);
+    UA_memcpy((void *)&entry->node, node, nodesize);
     UA_free((void*)node);
     UA_free((void*)node);
     return entry;
     return entry;
 }
 }
@@ -209,17 +209,17 @@ static INLINE struct nodeEntry * nodeEntryFromNode(const UA_Node *node) {
 
 
 UA_NodeStore * UA_NodeStore_new() {
 UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
     UA_NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
+    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
         return UA_NULL;
         return UA_NULL;
 
 
     ns->sizePrimeIndex = higher_prime_index(32);
     ns->sizePrimeIndex = higher_prime_index(32);
     ns->size = primes[ns->sizePrimeIndex];
     ns->size = primes[ns->sizePrimeIndex];
     ns->count = 0;
     ns->count = 0;
-    if(!(ns->entries = UA_alloc(sizeof(struct nodeEntry *) * ns->size))) {
+    if(!(ns->entries = UA_malloc(sizeof(struct nodeEntry *) * ns->size))) {
         UA_free(ns);
         UA_free(ns);
         return UA_NULL;
         return UA_NULL;
     }
     }
-    memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
+    UA_memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
     return ns;
     return ns;
 }
 }
 
 

+ 52 - 44
src/server/ua_nodestore.h

@@ -2,73 +2,81 @@
 #define UA_NODESTORE_H_
 #define UA_NODESTORE_H_
 
 
 #include "ua_types_generated.h"
 #include "ua_types_generated.h"
-#include "ua_util.h"
 
 
 /**
 /**
-   @ingroup server
-
-   @defgroup nodestore NodeStore
-
-   @brief The nodestore contains the nodes in the UA address space. Internally,
-   it is based on a hash-map that maps nodes to their nodeid.
-
-   ATTENTION! You need to allocate single nodes on the heap (with _new) before
-   adding them to the nodestore with _insert or _replace. The node is then
-   copied to a new (managed) location in the nodestore and the original memory
-   is freed. The nodes in the nodestore are immutable. To change the content of
-   a node, it needs to be replaced as a whole.
-
-   ATTENTION! Every node you _get from the nodestore needs to be _released when
-   it is no longer needed. Otherwise, we can't know if somebody still uses it
-   (especially in multi-threaded environments).
+ * @ingroup server
+ *
+ * @defgroup nodestore NodeStore
+ *
+ * @brief Stores the nodes in the address space. Internally, it is based on a
+ * hash-map that maps nodes to their nodeid.
+ *
+ * Nodes need to be allocated on the heap before adding them to the nodestore
+ * with. When adding, the node is copied to a new (managed) location in the
+ * nodestore and the original memory is freed. The nodes in the nodestore are
+ * immutable. To change the content of a node, it needs to be replaced as a
+ * whole.
+ *
+ * Every node you _get from the nodestore needs to be _released when it is no
+ * longer needed. In the background, reference counting is used to know if
+ * somebody still uses the node in multi-threaded environments.
+ *
+ * @{
  */
  */
 
 
 struct UA_NodeStore;
 struct UA_NodeStore;
 typedef struct UA_NodeStore UA_NodeStore;
 typedef struct UA_NodeStore UA_NodeStore;
 
 
-/** @brief Create a new namespace */
+/** Create a new namespace */
 UA_NodeStore * UA_NodeStore_new();
 UA_NodeStore * UA_NodeStore_new();
 
 
-/** @brief Delete the namespace and all nodes in it */
+/** Delete the namespace and all nodes in it */
 void UA_NodeStore_delete(UA_NodeStore *ns);
 void UA_NodeStore_delete(UA_NodeStore *ns);
 
 
-/** @brief Insert a new node into the namespace
-
-    With the getManaged flag, the node pointer is replaced with the managed
-    pointer. Otherwise, it is set to UA_NULL.
-
-    If the nodeid is zero, then a fresh numeric nodeid from namespace 1 is
-    assigned. */
+/**
+ * Inserts a new node into the namespace. With the getManaged flag, the node
+ * pointer is replaced with the managed pointer. Otherwise, it is set to
+ * UA_NULL. If the nodeid is zero, then a fresh numeric nodeid from namespace 1
+ * is assigned.
+ */
 UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged);
 UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged);
 
 
-/** @brief Replace an existing node in the nodestore
-
-    With the getManaged flag, the node pointer is replaced with the managed
-    pointer. Otherwise, it is set to UA_NULL.
-
-    If the return value is UA_STATUSCODE_BADINTERNALERROR, try again. Presumably
-    the oldNode was already replaced by another thread.
-*/
+/**
+ * Replace an existing node in the nodestore. With the getManaged flag, the node
+ * pointer is replaced with the managed pointer. Otherwise, it is set to
+ * UA_NULL. If the return value is UA_STATUSCODE_BADINTERNALERROR, try again.
+ * Presumably the oldNode was already replaced by another thread.
+ */
 UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, const UA_Node **node, UA_Boolean getManaged);
 UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, const UA_Node **node, UA_Boolean getManaged);
 
 
-/** @brief Remove a node from the namespace. Always succeeds, even if the node
-    was not found. */
+/**
+ * Remove a node from the namespace. Always succeeds, even if the node was not
+ * found.
+ */
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 
-/** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
-    They can only be replaced. After the Node is no longer used, the locked
-    entry needs to be released. */
+/**
+ * Retrieve a node (read-only) from the namespace. Nodes are immutable. They
+ * can only be replaced. After the Node is no longer used, the locked entry
+ * needs to be released.
+ */
 const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
 const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 
-/** @brief Release a managed node. Do never insert a node that isn't stored in a
-    namespace. */
+/**
+ * Release a managed node. Do never insert a node that isn't stored in a
+ * namespace.
+ */
 void UA_NodeStore_release(const UA_Node *managed);
 void UA_NodeStore_release(const UA_Node *managed);
 
 
-/** @brief A function that can be evaluated on all entries in a namespace via
-    UA_NodeStore_iterate. Note that the visitor is read-only on the nodes. */
+/**
+ * A function that can be evaluated on all entries in a namespace via
+ * UA_NodeStore_iterate. Note that the visitor is read-only on the nodes.
+ */
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 
 
-/** @brief Iterate over all nodes in a namespace. */
+/** Iterate over all nodes in a namespace. */
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 
 
+/** @} */
+
 #endif /* UA_NODESTORE_H_ */
 #endif /* UA_NODESTORE_H_ */

+ 40 - 49
src/server/ua_nodestore_concurrent.c

@@ -1,11 +1,12 @@
-#include "ua_nodestore.h"
-#include "ua_util.h"
-
+#define _LGPL_SOURCE
 #include <urcu.h>
 #include <urcu.h>
 #include <urcu/compiler.h> // for caa_container_of
 #include <urcu/compiler.h> // for caa_container_of
 #include <urcu/uatomic.h>
 #include <urcu/uatomic.h>
 #include <urcu/rculfhash.h>
 #include <urcu/rculfhash.h>
 
 
+#include "ua_nodestore.h"
+#include "ua_util.h"
+
 #define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
 #define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
 
 
 struct nodeEntry {
 struct nodeEntry {
@@ -72,7 +73,6 @@ static void markDead(struct rcu_head *head) {
 
 
     node_deleteMembers(&entry->node);
     node_deleteMembers(&entry->node);
     UA_free(entry);
     UA_free(entry);
-    return;
 }
 }
 
 
 /* Free the entry if it is dead and nobody uses it anymore */
 /* Free the entry if it is dead and nobody uses it anymore */
@@ -83,12 +83,11 @@ void UA_NodeStore_release(const UA_Node *managed) {
 
 
     node_deleteMembers(managed);
     node_deleteMembers(managed);
     UA_free(entry);
     UA_free(entry);
-    return;
 }
 }
 
 
 UA_NodeStore * UA_NodeStore_new() {
 UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
     UA_NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
+    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
         return UA_NULL;
         return UA_NULL;
 
 
     /* 32 is the minimum size for the hashtable. */
     /* 32 is the minimum size for the hashtable. */
@@ -103,21 +102,19 @@ UA_NodeStore * UA_NodeStore_new() {
 void UA_NodeStore_delete(UA_NodeStore *ns) {
 void UA_NodeStore_delete(UA_NodeStore *ns) {
     struct cds_lfht      *ht = ns->ht;
     struct cds_lfht      *ht = ns->ht;
     struct cds_lfht_iter  iter;
     struct cds_lfht_iter  iter;
-    struct cds_lfht_node *found_htn;
 
 
     rcu_read_lock();
     rcu_read_lock();
     cds_lfht_first(ht, &iter);
     cds_lfht_first(ht, &iter);
-    while(iter.node != UA_NULL) {
-        found_htn = cds_lfht_iter_get_node(&iter);
-        if(!cds_lfht_del(ht, found_htn)) {
-            struct nodeEntry *entry = caa_container_of(found_htn, struct nodeEntry, htn);
+    while(iter.node) {
+        if(!cds_lfht_del(ht, iter.node)) {
+            struct nodeEntry *entry = caa_container_of(iter.node, struct nodeEntry, htn);
             call_rcu(&entry->rcu_head, markDead);
             call_rcu(&entry->rcu_head, markDead);
         }
         }
         cds_lfht_next(ht, &iter);
         cds_lfht_next(ht, &iter);
     }
     }
     rcu_read_unlock();
     rcu_read_unlock();
-
     cds_lfht_destroy(ht, UA_NULL);
     cds_lfht_destroy(ht, UA_NULL);
+
     UA_free(ns);
     UA_free(ns);
 }
 }
 
 
@@ -154,7 +151,7 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boo
     }
     }
 
 
     struct nodeEntry *entry;
     struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     memcpy((void*)&entry->node, *node, nodesize);
     memcpy((void*)&entry->node, *node, nodesize);
 
 
@@ -183,20 +180,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boo
         long before, after;
         long before, after;
         rcu_read_lock();
         rcu_read_lock();
         cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
         cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
-        rcu_read_unlock();
         identifier++;
         identifier++;
 
 
         ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
         ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
         while(UA_TRUE) {
         while(UA_TRUE) {
             hash_t nhash = hash(&entry->node.nodeId);
             hash_t nhash = hash(&entry->node.nodeId);
-            rcu_read_lock();
             result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
             result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
-            rcu_read_unlock();
             if(result == &entry->htn)
             if(result == &entry->htn)
                 break;
                 break;
 
 
             ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
             ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
         }
         }
+        rcu_read_unlock();
     }
     }
 
 
     UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
     UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
@@ -240,58 +235,56 @@ UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode,
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
-    struct nodeEntry *entry;
-    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+    struct nodeEntry *newEntry;
+    if(!(newEntry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    memcpy((void*)&entry->node, *node, nodesize);
+    memcpy((void*)&newEntry->node, *node, nodesize);
 
 
-    cds_lfht_node_init(&entry->htn);
-    entry->refcount = ALIVE_BIT;
+    cds_lfht_node_init(&newEntry->htn);
+    newEntry->refcount = ALIVE_BIT;
     if(getManaged) // increase the counter before adding the node
     if(getManaged) // increase the counter before adding the node
-        entry->refcount++;
+        newEntry->refcount++;
 
 
     hash_t h = hash(&(*node)->nodeId);
     hash_t h = hash(&(*node)->nodeId);
 
 
     struct cds_lfht_iter iter;
     struct cds_lfht_iter iter;
     rcu_read_lock();
     rcu_read_lock();
     cds_lfht_lookup(ns->ht, h, compare, &(*node)->nodeId, &iter);
     cds_lfht_lookup(ns->ht, h, compare, &(*node)->nodeId, &iter);
-    struct cds_lfht_node *result = cds_lfht_iter_get_node(&iter);
 
 
     /* No node found that can be replaced */
     /* No node found that can be replaced */
-    if(!result) {
+    if(!iter.node) {
         rcu_read_unlock();
         rcu_read_unlock();
-        UA_free(entry);
+        UA_free(newEntry);
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     }
     }
 
 
-    struct nodeEntry *found_entry = (struct nodeEntry *)cds_lfht_iter_get_node(&iter);
+    struct nodeEntry *oldEntry = caa_container_of(iter.node, struct nodeEntry, htn);
     /* The node we found is obsolete*/
     /* The node we found is obsolete*/
-    if(&found_entry->node != oldNode) {
+    if(&oldEntry->node != oldNode) {
         rcu_read_unlock();
         rcu_read_unlock();
-        UA_free(entry);
+        UA_free(newEntry);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
     /* The old node is replaced by a managed node. */
     /* The old node is replaced by a managed node. */
-    if(cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &entry->htn) == 0) {
-        struct nodeEntry *entry = caa_container_of(result, struct nodeEntry, htn);
-        /* If an entry got replaced, mark it as dead. */
-        call_rcu(&entry->rcu_head, markDead);
+    if(cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &newEntry->htn) != 0) {
+        /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
         rcu_read_unlock();
         rcu_read_unlock();
-
-        UA_free((UA_Node *)*node);
-        if(getManaged)
-            *node = &entry->node;
-        else
-            *node = UA_NULL;
-
-        return UA_STATUSCODE_GOOD;
+        UA_free(newEntry);
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
-    
-    /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
+        
+    /* If an entry got replaced, mark it as dead. */
+    call_rcu(&oldEntry->rcu_head, markDead);
     rcu_read_unlock();
     rcu_read_unlock();
-    UA_free(entry);
-    return UA_STATUSCODE_BADINTERNALERROR;
+
+    UA_free((UA_Node *)*node); // was copied to newEntry and is obsolete
+    if(getManaged)
+        *node = &newEntry->node;
+    else
+        *node = UA_NULL;
+
+    return UA_STATUSCODE_GOOD;
 }
 }
 
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
@@ -299,16 +292,14 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     struct cds_lfht_iter iter;
     struct cds_lfht_iter iter;
 
 
     rcu_read_lock();
     rcu_read_lock();
-    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
-    struct cds_lfht_node *found_htn = cds_lfht_iter_get_node(&iter);
-
     /* If this fails, then the node has already been removed. */
     /* If this fails, then the node has already been removed. */
-    if(!found_htn || cds_lfht_del(ns->ht, found_htn) != 0) {
+    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
+    if(!iter.node || cds_lfht_del(ns->ht, iter.node) != 0) {
         rcu_read_unlock();
         rcu_read_unlock();
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     }
     }
 
 
-    struct nodeEntry *entry = caa_container_of(found_htn, struct nodeEntry, htn);
+    struct nodeEntry *entry = caa_container_of(iter.node, struct nodeEntry, htn);
     call_rcu(&entry->rcu_head, markDead);
     call_rcu(&entry->rcu_head, markDead);
     rcu_read_unlock();
     rcu_read_unlock();
 
 

+ 30 - 34
src/server/ua_securechannel_manager.c

@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "ua_securechannel_manager.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session.h"
 #include "ua_session.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
@@ -9,11 +11,10 @@ struct channel_list_entry {
 
 
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId, UA_String *endpointUrl) {
+                                           UA_UInt32 startTokenId) {
     LIST_INIT(&cm->channels);
     LIST_INIT(&cm->channels);
     cm->lastChannelId      = startChannelId;
     cm->lastChannelId      = startChannelId;
     cm->lastTokenId        = startTokenId;
     cm->lastTokenId        = startTokenId;
-    UA_String_copy(endpointUrl, &cm->endpointUrl);
     cm->maxChannelLifetime = tokenLifetime;
     cm->maxChannelLifetime = tokenLifetime;
     cm->maxChannelCount    = maxChannelCount;
     cm->maxChannelCount    = maxChannelCount;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -31,17 +32,34 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
         UA_free(entry);
         UA_free(entry);
         entry = LIST_FIRST(&cm->channels);
         entry = LIST_FIRST(&cm->channels);
     }
     }
-    UA_String_deleteMembers(&cm->endpointUrl);
 }
 }
 
 
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm,
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm,
                                            UA_Connection                     *conn,
                                            UA_Connection                     *conn,
                                            const UA_OpenSecureChannelRequest *request,
                                            const UA_OpenSecureChannelRequest *request,
                                            UA_OpenSecureChannelResponse      *response) {
                                            UA_OpenSecureChannelResponse      *response) {
-    struct channel_list_entry *entry = UA_alloc(sizeof(struct channel_list_entry));
-    if(!entry)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+    switch(request->securityMode) {
+    case UA_MESSAGESECURITYMODE_INVALID:
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
+        return response->responseHeader.serviceResult;
+
+        // fall through and handle afterwards
+    /* case UA_MESSAGESECURITYMODE_NONE: */
+    /*     UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce); */
+    /*     break; */
+
+    case UA_MESSAGESECURITYMODE_SIGN:
+    case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
+        return response->responseHeader.serviceResult;
 
 
+    default:
+        // do nothing
+        break;
+    }
+
+    struct channel_list_entry *entry = UA_malloc(sizeof(struct channel_list_entry));
+    if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY;
     UA_SecureChannel_init(&entry->channel);
     UA_SecureChannel_init(&entry->channel);
 
 
     entry->channel.connection = conn;
     entry->channel.connection = conn;
@@ -53,22 +71,7 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm
         request->requestedLifetime > cm->maxChannelLifetime ?
         request->requestedLifetime > cm->maxChannelLifetime ?
         cm->maxChannelLifetime : request->requestedLifetime;
         cm->maxChannelLifetime : request->requestedLifetime;
 
 
-    switch(request->securityMode) {
-    case UA_MESSAGESECURITYMODE_INVALID:
-        printf("UA_SecureChannel_processOpenRequest - client demands invalid \n");
-        break;
-
-    case UA_MESSAGESECURITYMODE_NONE:
-        UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
-        break;
-
-    case UA_MESSAGESECURITYMODE_SIGN:
-    case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
-        printf("UA_SecureChannel_processOpenRequest - client demands signed & encrypted \n");
-        //TODO check if senderCertificate and ReceiverCertificateThumbprint are present
-        break;
-    }
-
+    UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
     UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None",
     UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None",
                           (UA_String *)&entry->channel.serverAsymAlgSettings.securityPolicyUri);
                           (UA_String *)&entry->channel.serverAsymAlgSettings.securityPolicyUri);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
     LIST_INSERT_HEAD(&cm->channels, entry, pointers);
@@ -77,7 +80,6 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm
     UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
     UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
     UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
     UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
     UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
-
     conn->channel = &entry->channel;
     conn->channel = &entry->channel;
 
 
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
@@ -87,10 +89,8 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
                                             UA_Connection                     *conn,
                                             UA_Connection                     *conn,
                                             const UA_OpenSecureChannelRequest *request,
                                             const UA_OpenSecureChannelRequest *request,
                                             UA_OpenSecureChannelResponse      *response) {
                                             UA_OpenSecureChannelResponse      *response) {
-
     UA_SecureChannel *channel = conn->channel;
     UA_SecureChannel *channel = conn->channel;
-    if(channel == UA_NULL)
-        return UA_STATUSCODE_BADINTERNALERROR;
+    if(channel == UA_NULL) return UA_STATUSCODE_BADINTERNALERROR;
 
 
     channel->securityToken.tokenId         = cm->lastTokenId++;
     channel->securityToken.tokenId         = cm->lastTokenId++;
     channel->securityToken.createdAt       = UA_DateTime_now(); // todo: is wanted?
     channel->securityToken.createdAt       = UA_DateTime_now(); // todo: is wanted?
@@ -106,17 +106,13 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-UA_StatusCode UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId,
-                                          UA_SecureChannel **channel) {
+UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
     struct channel_list_entry *entry;
     struct channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
     LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(entry->channel.securityToken.channelId == channelId) {
-            *channel = &entry->channel;
-            return UA_STATUSCODE_GOOD;
-        }
+        if(entry->channel.securityToken.channelId == channelId)
+            return &entry->channel;
     }
     }
-    *channel = UA_NULL;
-    return UA_STATUSCODE_BADINTERNALERROR;
+    return UA_NULL;
 }
 }
 
 
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {

+ 2 - 4
src/server/ua_securechannel_manager.h

@@ -10,7 +10,6 @@ typedef struct UA_SecureChannelManager {
     UA_Int32    maxChannelCount;
     UA_Int32    maxChannelCount;
     UA_DateTime maxChannelLifetime;
     UA_DateTime maxChannelLifetime;
     UA_MessageSecurityMode securityMode;
     UA_MessageSecurityMode securityMode;
-    UA_String   endpointUrl;
     UA_DateTime channelLifeTime;
     UA_DateTime channelLifeTime;
     UA_Int32    lastChannelId;
     UA_Int32    lastChannelId;
     UA_UInt32   lastTokenId;
     UA_UInt32   lastTokenId;
@@ -18,7 +17,7 @@ typedef struct UA_SecureChannelManager {
 
 
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
 UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt32 maxChannelCount,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
-                                           UA_UInt32 startTokenId, UA_String *endpointUrl);
+                                           UA_UInt32 startTokenId);
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm);
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm);
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
                                            const UA_OpenSecureChannelRequest *request,
                                            const UA_OpenSecureChannelRequest *request,
@@ -26,8 +25,7 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Conne
 UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
 UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Connection *conn,
                                             const UA_OpenSecureChannelRequest *request,
                                             const UA_OpenSecureChannelRequest *request,
                                             UA_OpenSecureChannelResponse *response);
                                             UA_OpenSecureChannelResponse *response);
-UA_StatusCode UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId,
-                                          UA_SecureChannel **channel);
+UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId);
 
 
 #endif /* UA_CHANNEL_MANAGER_H_ */
 #endif /* UA_CHANNEL_MANAGER_H_ */

+ 65 - 20
src/server/ua_server.c

@@ -1,5 +1,10 @@
+#ifdef UA_MULTITHREADING
+#define _LGPL_SOURCE
+#include <urcu.h>
+//#include <urcu-call-rcu.h>
+#endif
+
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
-#include "ua_services_internal.h" // AddReferences
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_securechannel_manager.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session_manager.h"
 #include "ua_session_manager.h"
@@ -12,7 +17,6 @@
 
 
 static void UA_ExternalNamespace_init(UA_ExternalNamespace *ens) {
 static void UA_ExternalNamespace_init(UA_ExternalNamespace *ens) {
 	ens->index = 0;
 	ens->index = 0;
-    memset(&ens->externalNodeStore, 0, sizeof(UA_ExternalNodeStore));
 	UA_String_init(&ens->url);
 	UA_String_init(&ens->url);
 }
 }
 
 
@@ -21,11 +25,37 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
     ens->externalNodeStore.delete(ens->externalNodeStore.ensHandle);
     ens->externalNodeStore.delete(ens->externalNodeStore.ensHandle);
 }
 }
 
 
+/*****************/
+/* Configuration */
+/*****************/
+
+void UA_Server_addNetworkLayer(UA_Server *server, UA_NetworkLayer networkLayer) {
+    server->nls = UA_realloc(server->nls, sizeof(UA_NetworkLayer)*(server->nlsSize+1));
+    server->nls[server->nlsSize] = networkLayer;
+    server->nlsSize++;
+}
+
+void UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate) {
+    UA_ByteString_copy(&certificate, &server->serverCertificate);
+}
+
 /**********/
 /**********/
 /* Server */
 /* Server */
 /**********/
 /**********/
 
 
 void UA_Server_delete(UA_Server *server) {
 void UA_Server_delete(UA_Server *server) {
+    // The server needs to be stopped before it can be deleted
+
+    // Delete the network layers
+    for(UA_Int32 i=0;i<server->nlsSize;i++) {
+        server->nls[i].delete(server->nls[i].nlHandle);
+    }
+    UA_free(server->nls);
+
+    // Delete the timed work
+    UA_Server_deleteTimedWork(server);
+
+    // Delete all internal data
     UA_ApplicationDescription_deleteMembers(&server->description);
     UA_ApplicationDescription_deleteMembers(&server->description);
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
@@ -33,26 +63,41 @@ void UA_Server_delete(UA_Server *server) {
     UA_ByteString_deleteMembers(&server->serverCertificate);
     UA_ByteString_deleteMembers(&server->serverCertificate);
     UA_Array_delete(server->endpointDescriptions, server->endpointDescriptionsSize, &UA_TYPES[UA_ENDPOINTDESCRIPTION]);
     UA_Array_delete(server->endpointDescriptions, server->endpointDescriptionsSize, &UA_TYPES[UA_ENDPOINTDESCRIPTION]);
     UA_free(server);
     UA_free(server);
+#ifdef UA_MULTITHREADING
+    rcu_barrier(); // wait for all scheduled call_rcu work to complete
+#endif
 }
 }
 
 
-UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate) {
-    UA_Server *server = UA_alloc(sizeof(UA_Server));
+UA_Server * UA_Server_new() {
+    UA_Server *server = UA_malloc(sizeof(UA_Server));
     if(!server)
     if(!server)
         return UA_NULL;
         return UA_NULL;
-    
+
+    LIST_INIT(&server->timedWork);
+#ifdef UA_MULTITHREADING
+    rcu_init();
+	cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+    server->delayedWork = UA_NULL;
+#endif
+
+    // random seed
+    server->random_seed = (UA_UInt32) UA_DateTime_now();
+
+    // networklayers
+    server->nls = UA_NULL;
+    server->nlsSize = 0;
+
+    UA_ByteString_init(&server->serverCertificate);
+        
     // mockup application description
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
     UA_ApplicationDescription_init(&server->description);
-    UA_String_copycstring("urn:servername:open62541:application", &server->description.productUri);
-    UA_String_copycstring("http://open62541.info/applications/4711", &server->description.applicationUri);
-    UA_LocalizedText_copycstring("The open62541 application", &server->description.applicationName);
+    UA_String_copycstring("urn:unconfigured:open62541:application", &server->description.productUri);
+    UA_String_copycstring("http://unconfigured.open62541/applications/", &server->description.applicationUri);
+    UA_LocalizedText_copycstring("Unconfigured open62541 application", &server->description.applicationName);
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->externalNamespacesSize = 0;
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
     server->externalNamespaces = UA_NULL;
 
 
-    UA_ByteString_init(&server->serverCertificate);
-    if(serverCertificate)
-        UA_ByteString_copy(serverCertificate, &server->serverCertificate);
-
     // mockup endpoint description
     // mockup endpoint description
     server->endpointDescriptionsSize = 1;
     server->endpointDescriptionsSize = 1;
     UA_EndpointDescription *endpoint = UA_EndpointDescription_new(); // todo: check return code
     UA_EndpointDescription *endpoint = UA_EndpointDescription_new(); // todo: check return code
@@ -62,16 +107,16 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     UA_String_copycstring("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary", &endpoint->transportProfileUri);
     UA_String_copycstring("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary", &endpoint->transportProfileUri);
 
 
     endpoint->userIdentityTokensSize = 1;
     endpoint->userIdentityTokensSize = 1;
-    endpoint->userIdentityTokens = UA_alloc(sizeof(UA_UserTokenPolicy));
+    endpoint->userIdentityTokens = UA_malloc(sizeof(UA_UserTokenPolicy));
     UA_UserTokenPolicy_init(endpoint->userIdentityTokens);
     UA_UserTokenPolicy_init(endpoint->userIdentityTokens);
     UA_String_copycstring("my-anonymous-policy", &endpoint->userIdentityTokens->policyId); // defined per server
     UA_String_copycstring("my-anonymous-policy", &endpoint->userIdentityTokens->policyId); // defined per server
     endpoint->userIdentityTokens->tokenType = UA_USERTOKENTYPE_ANONYMOUS;
     endpoint->userIdentityTokens->tokenType = UA_USERTOKENTYPE_ANONYMOUS;
 
 
-    UA_String_copy(endpointUrl, &endpoint->endpointUrl);
-    /* The standard says "the HostName specified in the Server Certificate is the
-       same as the HostName contained in the endpointUrl provided in the
-       EndpointDescription */
-    UA_String_copy(&server->serverCertificate, &endpoint->serverCertificate);
+    /* UA_String_copy(endpointUrl, &endpoint->endpointUrl); */
+    /* /\* The standard says "the HostName specified in the Server Certificate is the */
+    /*    same as the HostName contained in the endpointUrl provided in the */
+    /*    EndpointDescription *\/ */
+    /* UA_String_copy(&server->serverCertificate, &endpoint->serverCertificate); */
     UA_ApplicationDescription_copy(&server->description, &endpoint->server);
     UA_ApplicationDescription_copy(&server->description, &endpoint->server);
     server->endpointDescriptions = endpoint;
     server->endpointDescriptions = endpoint;
 
 
@@ -80,7 +125,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
 #define TOKENLIFETIME 10000
 #define TOKENLIFETIME 10000
 #define STARTTOKENID 1
 #define STARTTOKENID 1
     UA_SecureChannelManager_init(&server->secureChannelManager, MAXCHANNELCOUNT,
     UA_SecureChannelManager_init(&server->secureChannelManager, MAXCHANNELCOUNT,
-                                 TOKENLIFETIME, STARTCHANNELID, STARTTOKENID, endpointUrl);
+                                 TOKENLIFETIME, STARTCHANNELID, STARTTOKENID);
 
 
 #define MAXSESSIONCOUNT 1000
 #define MAXSESSIONCOUNT 1000
 #define SESSIONLIFETIME 10000
 #define SESSIONLIFETIME 10000
@@ -410,7 +455,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
     UA_String_copycstring("urn:myServer:myApplication",
     UA_String_copycstring("urn:myServer:myApplication",
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
                           &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
-    UA_UInt32 *dimensions = UA_alloc(sizeof(UA_UInt32));
+    UA_UInt32 *dimensions = UA_malloc(sizeof(UA_UInt32));
     if(dimensions) {
     if(dimensions) {
         *dimensions = 2;
         *dimensions = 2;
         namespaceArray->arrayDimensions = dimensions;
         namespaceArray->arrayDimensions = dimensions;

+ 2 - 2
src/server/ua_server_addressspace.c

@@ -59,7 +59,7 @@ UA_StatusCode addOneWayReferenceWithSession(UA_Server *server, UA_Session *sessi
     if(count < 0)
     if(count < 0)
         count = 0;
         count = 0;
     UA_ReferenceNode *old_refs = newNode->references;
     UA_ReferenceNode *old_refs = newNode->references;
-    UA_ReferenceNode *new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1));
+    UA_ReferenceNode *new_refs = UA_malloc(sizeof(UA_ReferenceNode)*(count+1));
     if(!new_refs) {
     if(!new_refs) {
         nodeVT->delete(newNode);
         nodeVT->delete(newNode);
         UA_NodeStore_release(node);
         UA_NodeStore_release(node);
@@ -119,7 +119,7 @@ UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *s
     if(ensFirst) {
     if(ensFirst) {
         // todo: use external nodestore
         // todo: use external nodestore
     } else
     } else
-        retval = addOneWayReferenceWithSession (server, session, item);
+        retval = addOneWayReferenceWithSession(server, session, item);
 
 
     if(retval) return retval;
     if(retval) return retval;
 
 

+ 119 - 106
src/server/ua_server_binary.c

@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_services.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
@@ -6,6 +8,9 @@
 #include "ua_session_manager.h"
 #include "ua_session_manager.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
+/** Max size of messages that are allocated on the stack */
+#define MAX_STACK_MESSAGE 65536
+
 static UA_StatusCode UA_ByteStringArray_deleteMembers(UA_ByteStringArray *stringarray) {
 static UA_StatusCode UA_ByteStringArray_deleteMembers(UA_ByteStringArray *stringarray) {
     if(!stringarray)
     if(!stringarray)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -18,15 +23,15 @@ static void processHello(UA_Connection *connection, const UA_ByteString *msg,
                          UA_UInt32 *pos) {
                          UA_UInt32 *pos) {
     UA_TcpHelloMessage helloMessage;
     UA_TcpHelloMessage helloMessage;
     if(UA_TcpHelloMessage_decodeBinary(msg, pos, &helloMessage) != UA_STATUSCODE_GOOD) {
     if(UA_TcpHelloMessage_decodeBinary(msg, pos, &helloMessage) != UA_STATUSCODE_GOOD) {
-        connection->close(connection->callbackHandle);
+        connection->close(connection);
         return;
         return;
     }
     }
+
     connection->remoteConf.maxChunkCount   = helloMessage.maxChunkCount;
     connection->remoteConf.maxChunkCount   = helloMessage.maxChunkCount;
     connection->remoteConf.maxMessageSize  = helloMessage.maxMessageSize;
     connection->remoteConf.maxMessageSize  = helloMessage.maxMessageSize;
     connection->remoteConf.protocolVersion = helloMessage.protocolVersion;
     connection->remoteConf.protocolVersion = helloMessage.protocolVersion;
     connection->remoteConf.recvBufferSize  = helloMessage.receiveBufferSize;
     connection->remoteConf.recvBufferSize  = helloMessage.receiveBufferSize;
     connection->remoteConf.sendBufferSize  = helloMessage.sendBufferSize;
     connection->remoteConf.sendBufferSize  = helloMessage.sendBufferSize;
-
     connection->state = UA_CONNECTION_ESTABLISHED;
     connection->state = UA_CONNECTION_ESTABLISHED;
 
 
     // build acknowledge response
     // build acknowledge response
@@ -43,21 +48,22 @@ static void processHello(UA_Connection *connection, const UA_ByteString *msg,
     ackHeader.messageSize = UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage) +
     ackHeader.messageSize = UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage) +
                             UA_TcpMessageHeader_calcSizeBinary(&ackHeader);
                             UA_TcpMessageHeader_calcSizeBinary(&ackHeader);
 
 
-    UA_ByteString ack_msg;
+    // The message is on the stack. That's ok since ack is very small.
+    UA_ByteString ack_msg = (UA_ByteString){.length = ackHeader.messageSize,
+                                            .data = UA_alloca(ackHeader.messageSize)};
     UA_UInt32 tmpPos = 0;
     UA_UInt32 tmpPos = 0;
-    UA_ByteString_newMembers(&ack_msg, ackHeader.messageSize);
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
     UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &ack_msg };
     UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &ack_msg };
-    connection->write(connection->callbackHandle, answer_buf); // the string is freed internall in the (asynchronous) write
+    // the string is freed internall in the (asynchronous) write
+    connection->write(connection, answer_buf);
     UA_TcpHelloMessage_deleteMembers(&helloMessage);
     UA_TcpHelloMessage_deleteMembers(&helloMessage);
 }
 }
 
 
-static void processOpen(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, UA_UInt32 *pos) {
+static void processOpen(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
+                        UA_UInt32 *pos) {
     if(connection->state != UA_CONNECTION_ESTABLISHED) {
     if(connection->state != UA_CONNECTION_ESTABLISHED) {
-        // was hello exchanged before?
-        if(connection->state == UA_CONNECTION_OPENING)
-            connection->close(connection->callbackHandle);
+        connection->close(connection);
         return;
         return;
     }
     }
 
 
@@ -89,7 +95,7 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     UA_TcpMessageHeader respHeader;
     UA_TcpMessageHeader respHeader;
     respHeader.messageType = UA_MESSAGETYPE_OPN;
     respHeader.messageType = UA_MESSAGETYPE_OPN;
     respHeader.isFinal     = 'F';
     respHeader.isFinal     = 'F';
-    respHeader.messageSize = 8+4; //header + securechannelid
+    respHeader.messageSize = 8;
 
 
     UA_ExpandedNodeId responseType = UA_EXPANDEDNODEIDS[UA_OPENSECURECHANNELRESPONSE];
     UA_ExpandedNodeId responseType = UA_EXPANDEDNODEIDS[UA_OPENSECURECHANNELRESPONSE];
     responseType.nodeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
     responseType.nodeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
@@ -100,8 +106,16 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     respHeader.messageSize += UA_OpenSecureChannelResponse_calcSizeBinary(&p);
     respHeader.messageSize += UA_OpenSecureChannelResponse_calcSizeBinary(&p);
 
 
     UA_ByteString resp_msg;
     UA_ByteString resp_msg;
+    UA_Boolean onStack = UA_FALSE;
+
+    if(respHeader.messageSize <= MAX_STACK_MESSAGE) {
+        onStack = UA_TRUE;
+        resp_msg = (UA_ByteString){.length = respHeader.messageSize,
+                                   .data = UA_alloca(respHeader.messageSize)};
+    } else
+        UA_ByteString_newMembers(&resp_msg, respHeader.messageSize);
+
     UA_UInt32 tmpPos = 0;
     UA_UInt32 tmpPos = 0;
-    UA_ByteString_newMembers(&resp_msg, respHeader.messageSize);
     UA_TcpMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
     UA_TcpMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
     UA_UInt32_encodeBinary(&p.securityToken.channelId, &resp_msg, &tmpPos);
     UA_UInt32_encodeBinary(&p.securityToken.channelId, &resp_msg, &tmpPos);
     UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
     UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
@@ -112,9 +126,9 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     UA_OpenSecureChannelRequest_deleteMembers(&r);
     UA_OpenSecureChannelRequest_deleteMembers(&r);
     UA_OpenSecureChannelResponse_deleteMembers(&p);
     UA_OpenSecureChannelResponse_deleteMembers(&p);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-    
-    UA_ByteStringArray answer_buf = { .stringsSize = 1, .strings = &resp_msg };
-    connection->write(connection->callbackHandle, answer_buf);
+    connection->write(connection, (UA_ByteStringArray){ .stringsSize = 1, .strings = &resp_msg });
+    if(!onStack)
+        UA_free(resp_msg.data);
 }
 }
 
 
 static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
 static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
@@ -130,21 +144,30 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
             return;                  \
             return;                  \
         } } while(0)
         } } while(0)
 
 
-#define INVOKE_SERVICE(TYPE) do {                                                                         \
-        UA_##TYPE##Request p;                                                                             \
-        UA_##TYPE##Response r;                                                                            \
-        CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
-        UA_##TYPE##Response_init(&r);                                                                     \
-        init_response_header(&p.requestHeader, &r.responseHeader);                                        \
-        DBG_VERBOSE(printf("Invoke Service: %s\n", # TYPE));                                              \
-        Service_##TYPE(server, channel->session, &p, &r);                                                 \
-        DBG_VERBOSE(printf("Finished Service: %s\n", # TYPE));                                            \
-        UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
-        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
-        UA_##TYPE##Request_deleteMembers(&p);                                                             \
-        UA_##TYPE##Response_deleteMembers(&r);                                                            \
-        responseType = requestType.nodeId.identifier.numeric + 3;                                         \
-} while(0)
+// if the message is small enough, we allocate it on the stack and save a malloc
+#define ALLOC_MESSAGE(MESSAGE, SIZE) do {                               \
+        UA_UInt32 messageSize = SIZE;                                   \
+        if(messageSize <= MAX_STACK_MESSAGE) {                          \
+            messageOnStack = UA_TRUE;                                   \
+            *MESSAGE = (UA_ByteString){.length = messageSize,           \
+                                       .data = UA_alloca(messageSize)}; \
+        } else                                                          \
+            UA_ByteString_newMembers(MESSAGE, messageSize);             \
+    } while(0)
+
+#define INVOKE_SERVICE(TYPE) do {                                       \
+        UA_##TYPE##Request p;                                           \
+        UA_##TYPE##Response r;                                          \
+        CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),;); \
+        UA_##TYPE##Response_init(&r);                                   \
+        init_response_header(&p.requestHeader, &r.responseHeader);      \
+        Service_##TYPE(server, channel->session, &p, &r);               \
+        ALLOC_MESSAGE(message, UA_##TYPE##Response_calcSizeBinary(&r)); \
+        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);     \
+        UA_##TYPE##Request_deleteMembers(&p);                           \
+        UA_##TYPE##Response_deleteMembers(&r);                          \
+        responseType = requestType.nodeId.identifier.numeric + 3;       \
+    } while(0)
 
 
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
 #define INVOKE_STATELESS_SERVICE(TYPE) do { 															  \
 #define INVOKE_STATELESS_SERVICE(TYPE) do { 															  \
@@ -153,9 +176,7 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
         CHECK_PROCESS(UA_##TYPE##Request_decodeBinary(msg, pos, &p),; );                                  \
         UA_##TYPE##Response_init(&r);                                                                     \
         UA_##TYPE##Response_init(&r);                                                                     \
         init_response_header(&p.requestHeader, &r.responseHeader);                                        \
         init_response_header(&p.requestHeader, &r.responseHeader);                                        \
-        DBG_VERBOSE(printf("Anonymous Invoke Service: %s\n", # TYPE));                                    \
         Service_##TYPE(server, &anonymousSession, &p, &r);                                                \
         Service_##TYPE(server, &anonymousSession, &p, &r);                                                \
-        DBG_VERBOSE(printf("Finished Anonymous Service: %s\n", # TYPE));                                  \
         UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
         UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
         UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
         UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
         UA_##TYPE##Request_deleteMembers(&p);                                                             \
         UA_##TYPE##Request_deleteMembers(&p);                                                             \
@@ -173,7 +194,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
     if(connection->channel != UA_NULL && secureChannelId != 0){
     if(connection->channel != UA_NULL && secureChannelId != 0){
 #endif
 #endif
-    UA_SecureChannelManager_get(&server->secureChannelManager, secureChannelId, &channel);
+    channel = UA_SecureChannelManager_get(&server->secureChannelManager, secureChannelId);
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
     }
     }
 #endif
 #endif
@@ -185,7 +206,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     CHECK_PROCESS(UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader),; );
     CHECK_PROCESS(UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader),; );
 
 
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
-    if(connection->channel != UA_NULL && secureChannelId != 0){
+    if(connection->channel != UA_NULL && secureChannelId != 0) {
 #endif
 #endif
     channel->sequenceNumber = sequenceHeader.sequenceNumber;
     channel->sequenceNumber = sequenceHeader.sequenceNumber;
     channel->requestId = sequenceHeader.requestId;
     channel->requestId = sequenceHeader.requestId;
@@ -200,7 +221,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_ExpandedNodeId requestType;
     UA_ExpandedNodeId requestType;
     CHECK_PROCESS(UA_ExpandedNodeId_decodeBinary(msg, pos, &requestType),; );
     CHECK_PROCESS(UA_ExpandedNodeId_decodeBinary(msg, pos, &requestType),; );
     if(requestType.nodeId.identifierType != UA_NODEIDTYPE_NUMERIC) {
     if(requestType.nodeId.identifierType != UA_NODEIDTYPE_NUMERIC) {
-        UA_ExpandedNodeId_deleteMembers(&requestType); // if the nodeidtype is numeric, we do not have to free anything
+        // if the nodeidtype is numeric, we do not have to free anything
+        UA_ExpandedNodeId_deleteMembers(&requestType);
         return;
         return;
     }
     }
 
 
@@ -209,28 +231,29 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_UInt32 responseType;
     UA_UInt32 responseType;
     UA_ByteString *header = &responseBufs[0];
     UA_ByteString *header = &responseBufs[0];
     UA_ByteString *message = &responseBufs[1];
     UA_ByteString *message = &responseBufs[1];
+    UA_Boolean messageOnStack = UA_FALSE;
 
 
     UA_UInt32 sendOffset = 0;
     UA_UInt32 sendOffset = 0;
+
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
-    if(connection->channel == UA_NULL && secureChannelId == 0){
-     //stateless service calls - will pass UA_NULL as session
-     //fixme: maybe we need to pass a magic number instead of UA_NULL e.g. just a 42
-     switch(requestType.nodeId.identifier.numeric - 2) {
-     case UA_READREQUEST_NS0:
+    if(connection->channel == UA_NULL && secureChannelId == 0) {
+    //stateless service calls - will pass UA_NULL as session
+    //fixme: maybe we need to pass a magic number instead of UA_NULL e.g. just a 42
+        switch(requestType.nodeId.identifier.numeric - 2) {
+ case UA_READREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Read);
      INVOKE_STATELESS_SERVICE(Read);
      break;
      break;
-
-     case UA_WRITEREQUEST_NS0:
+ case UA_WRITEREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Write);
      INVOKE_STATELESS_SERVICE(Write);
      break;
      break;
-
-     case UA_BROWSEREQUEST_NS0:
+ case UA_BROWSEREQUEST_NS0:
      INVOKE_STATELESS_SERVICE(Browse);
      INVOKE_STATELESS_SERVICE(Browse);
      break;
      break;
-     }
-     //FIXME: a copy-pasted default case, but I did not want any duplications
-     }else{
+}
+    //FIXME: a copy-pasted default case, but I did not want any duplications
+}else{
 #endif
 #endif
+
     //subtract UA_ENCODINGOFFSET_BINARY for binary encoding
     //subtract UA_ENCODINGOFFSET_BINARY for binary encoding
     switch(requestType.nodeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
     switch(requestType.nodeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
     case UA_GETENDPOINTSREQUEST_NS0: {
     case UA_GETENDPOINTSREQUEST_NS0: {
@@ -240,7 +263,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_GetEndpointsResponse_init(&r);
         UA_GetEndpointsResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_GetEndpoints(server, &p, &r);
         Service_GetEndpoints(server, &p, &r);
-        UA_ByteString_newMembers(message, UA_GetEndpointsResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_GetEndpointsResponse_calcSizeBinary(&r));
         UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset);
         UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset);
         UA_GetEndpointsRequest_deleteMembers(&p);
         UA_GetEndpointsRequest_deleteMembers(&p);
         UA_GetEndpointsResponse_deleteMembers(&r);
         UA_GetEndpointsResponse_deleteMembers(&r);
@@ -255,7 +278,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_CreateSessionResponse_init(&r);
         UA_CreateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         init_response_header(&p.requestHeader, &r.responseHeader);
         Service_CreateSession(server, channel,  &p, &r);
         Service_CreateSession(server, channel,  &p, &r);
-        UA_ByteString_newMembers(message, UA_CreateSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_CreateSessionResponse_calcSizeBinary(&r));
         UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CreateSessionRequest_deleteMembers(&p);
         UA_CreateSessionRequest_deleteMembers(&p);
         UA_CreateSessionResponse_deleteMembers(&r);
         UA_CreateSessionResponse_deleteMembers(&r);
@@ -269,9 +292,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         CHECK_PROCESS(UA_ActivateSessionRequest_decodeBinary(msg, pos, &p),; );
         CHECK_PROCESS(UA_ActivateSessionRequest_decodeBinary(msg, pos, &p),; );
         UA_ActivateSessionResponse_init(&r);
         UA_ActivateSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         init_response_header(&p.requestHeader, &r.responseHeader);
-
         Service_ActivateSession(server, channel,  &p, &r);
         Service_ActivateSession(server, channel,  &p, &r);
-        UA_ByteString_newMembers(message, UA_ActivateSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_ActivateSessionResponse_calcSizeBinary(&r));
         UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_ActivateSessionRequest_deleteMembers(&p);
         UA_ActivateSessionRequest_deleteMembers(&p);
         UA_ActivateSessionResponse_deleteMembers(&r);
         UA_ActivateSessionResponse_deleteMembers(&r);
@@ -285,9 +307,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         CHECK_PROCESS(UA_CloseSessionRequest_decodeBinary(msg, pos, &p),; );
         CHECK_PROCESS(UA_CloseSessionRequest_decodeBinary(msg, pos, &p),; );
         UA_CloseSessionResponse_init(&r);
         UA_CloseSessionResponse_init(&r);
         init_response_header(&p.requestHeader, &r.responseHeader);
         init_response_header(&p.requestHeader, &r.responseHeader);
-
         Service_CloseSession(server, &p, &r);
         Service_CloseSession(server, &p, &r);
-        UA_ByteString_newMembers(message, UA_CloseSessionResponse_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_CloseSessionResponse_calcSizeBinary(&r));
         UA_CloseSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CloseSessionResponse_encodeBinary(&r, message, &sendOffset);
         UA_CloseSessionRequest_deleteMembers(&p);
         UA_CloseSessionRequest_deleteMembers(&p);
         UA_CloseSessionResponse_deleteMembers(&r);
         UA_CloseSessionResponse_deleteMembers(&r);
@@ -295,8 +316,10 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         break;
         break;
     }
     }
 
 
-    case UA_READREQUEST_NS0:
+    case UA_READREQUEST_NS0: {
         INVOKE_SERVICE(Read);
         INVOKE_SERVICE(Read);
+    }
+        //INVOKE_SERVICE(Read);
         break;
         break;
 
 
     case UA_WRITEREQUEST_NS0:
     case UA_WRITEREQUEST_NS0:
@@ -338,7 +361,7 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_ResponseHeader_init(&r);
         UA_ResponseHeader_init(&r);
         r.requestHandle = p.requestHandle;
         r.requestHandle = p.requestHandle;
         r.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
         r.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
-        UA_ByteString_newMembers(message, UA_ResponseHeader_calcSizeBinary(&r));
+        ALLOC_MESSAGE(message, UA_ResponseHeader_calcSizeBinary(&r));
         UA_ResponseHeader_encodeBinary(&r, message, &sendOffset);
         UA_ResponseHeader_encodeBinary(&r, message, &sendOffset);
         UA_RequestHeader_deleteMembers(&p);
         UA_RequestHeader_deleteMembers(&p);
         UA_ResponseHeader_deleteMembers(&r);
         UA_ResponseHeader_deleteMembers(&r);
@@ -347,35 +370,32 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     	break;
     	break;
     }
     }
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
-    }
+}
 #endif
 #endif
     // 5) Build the header
     // 5) Build the header
     UA_NodeId response_nodeid = { .namespaceIndex     = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     UA_NodeId response_nodeid = { .namespaceIndex     = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                                   .identifier.numeric = responseType }; // add 2 for binary encoding
                                   .identifier.numeric = responseType }; // add 2 for binary encoding
 
 
-    UA_ByteString_newMembers(header,
-                             8 + 16 + // message header + 4*32bit secure channel information
-                             UA_NodeId_calcSizeBinary(&response_nodeid));
+    UA_UInt32 headerSize = 8 + 16 + // message header + 4*32bit secure channel information
+        UA_NodeId_calcSizeBinary(&response_nodeid); // the nodeid of a service is always numeric
+    *header = (UA_ByteString){.length = headerSize, .data = UA_alloca(headerSize)};
 
 
     // header
     // header
     UA_TcpMessageHeader respHeader;
     UA_TcpMessageHeader respHeader;
     respHeader.messageType = UA_MESSAGETYPE_MSG;
     respHeader.messageType = UA_MESSAGETYPE_MSG;
     respHeader.isFinal     = 'F';
     respHeader.isFinal     = 'F';
     respHeader.messageSize = header->length + message->length;
     respHeader.messageSize = header->length + message->length;
+
     UA_UInt32 rpos = 0;
     UA_UInt32 rpos = 0;
     UA_TcpMessageHeader_encodeBinary(&respHeader, header, &rpos);
     UA_TcpMessageHeader_encodeBinary(&respHeader, header, &rpos);
 
 
-
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
 	if(connection->channel != UA_NULL && secureChannelId != 0){
 	if(connection->channel != UA_NULL && secureChannelId != 0){
 #endif
 #endif
-
     UA_UInt32_encodeBinary(&channel->securityToken.channelId, header, &rpos); // channel id
     UA_UInt32_encodeBinary(&channel->securityToken.channelId, header, &rpos); // channel id
     UA_UInt32_encodeBinary(&channel->securityToken.tokenId, header, &rpos); // algorithm security header
     UA_UInt32_encodeBinary(&channel->securityToken.tokenId, header, &rpos); // algorithm security header
-
     UA_UInt32_encodeBinary(&channel->sequenceNumber, header, &rpos); // encode sequence header
     UA_UInt32_encodeBinary(&channel->sequenceNumber, header, &rpos); // encode sequence header
-    UA_UInt32_encodeBinary(&channel->requestId, header, &rpos);
-
+    UA_UInt32_encodeBinary(&channel->requestId, header, &rpos); // request id
     UA_NodeId_encodeBinary(&response_nodeid, header, &rpos); // add payload type
     UA_NodeId_encodeBinary(&response_nodeid, header, &rpos); // add payload type
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
 	}else{
 	}else{
@@ -394,12 +414,15 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
     UA_ByteStringArray responseBufArray;
     UA_ByteStringArray responseBufArray;
     responseBufArray.strings = responseBufs; // the content is deleted in the write function (asynchronous)
     responseBufArray.strings = responseBufs; // the content is deleted in the write function (asynchronous)
     responseBufArray.stringsSize = 2;
     responseBufArray.stringsSize = 2;
-    connection->write(connection->callbackHandle, responseBufArray);
+    connection->write(connection, responseBufArray);
+
+    if(!messageOnStack)
+        UA_free(message->data);
 }
 }
 
 
-static void processClose(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, UA_UInt32 *pos) {
+static void processClose(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg,
+                         UA_UInt32 *pos) {
     // just read in the sequenceheader
     // just read in the sequenceheader
-
     UA_UInt32 secureChannelId;
     UA_UInt32 secureChannelId;
     UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
     UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
 
 
@@ -408,63 +431,53 @@ static void processClose(UA_Connection *connection, UA_Server *server, const UA_
 }
 }
 
 
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) {
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) {
-    UA_Int32  retval = UA_STATUSCODE_GOOD;
     UA_UInt32 pos    = 0;
     UA_UInt32 pos    = 0;
     UA_TcpMessageHeader tcpMessageHeader;
     UA_TcpMessageHeader tcpMessageHeader;
-    // todo: test how far pos advanced must be equal to what is said in the messageheader
     do {
     do {
-        retval = UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader);
-        if(tcpMessageHeader.messageSize < 8){
-        	printf("The announced size of the message is illegal (smaller than 8), skipping the whole packet\n");
-        	return;
+        if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader) != UA_STATUSCODE_GOOD) {
+            printf("ERROR: decoding of header failed \n");
+            connection->close(connection);
+            break;
         }
         }
+
         UA_UInt32 targetpos = pos - 8 + tcpMessageHeader.messageSize;
         UA_UInt32 targetpos = pos - 8 + tcpMessageHeader.messageSize;
-        if(retval == UA_STATUSCODE_GOOD) {
-            // none of the process-functions returns an error its all contained inside.
-            switch(tcpMessageHeader.messageType) {
-            case UA_MESSAGETYPE_HEL:
-                processHello(connection, msg, &pos);
-                break;
-
-            case UA_MESSAGETYPE_OPN:
-                processOpen(connection, server, msg, &pos);
-                break;
-
-            case UA_MESSAGETYPE_MSG:
-            	//no break
-                // if this fails, the connection is closed (no break on the case)
-                if(connection->state == UA_CONNECTION_ESTABLISHED &&
-                   connection->channel != UA_NULL) {
-                    processMessage(connection, server, msg, &pos);
-                    break;
-                }
+        switch(tcpMessageHeader.messageType) {
+        case UA_MESSAGETYPE_HEL:
+            processHello(connection, msg, &pos);
+            break;
+
+        case UA_MESSAGETYPE_OPN:
+            processOpen(connection, server, msg, &pos);
+            break;
+
+        case UA_MESSAGETYPE_MSG:
+            if(connection->state == UA_CONNECTION_ESTABLISHED && connection->channel != UA_NULL)
+                processMessage(connection, server, msg, &pos);
+            else {
 #ifdef EXTENSION_STATELESS
 #ifdef EXTENSION_STATELESS
                 //process messages with session zero
                 //process messages with session zero
                 if(connection->state == UA_CONNECTION_OPENING &&
                 if(connection->state == UA_CONNECTION_OPENING &&
                 		connection->channel == UA_NULL) {
                 		connection->channel == UA_NULL) {
                 	processMessage(connection, server, msg, &pos);
                 	processMessage(connection, server, msg, &pos);
                 	//fixme: we need to think about keepalive
                 	//fixme: we need to think about keepalive
-                	connection->close(connection->callbackHandle);
+                	connection->close(connection);
                 	break;
                 	break;
                 }
                 }
+#else
+                connection->close(connection);
 #endif
 #endif
-
-            case UA_MESSAGETYPE_CLO:
-                connection->state = UA_CONNECTION_CLOSING;
-                processClose(connection, server, msg, &pos);
-                connection->close(connection->callbackHandle);
-                return;
             }
             }
-            UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
-        } else {
-            printf("TL_Process - ERROR: decoding of header failed \n");
-            connection->state = UA_CONNECTION_CLOSING;
-            //processClose(connection, server, msg, &pos);
-            connection->close(connection->callbackHandle);
+            break;
+
+        case UA_MESSAGETYPE_CLO:
+            processClose(connection, server, msg, &pos);
+            connection->close(connection);
+            return;
         }
         }
-        // todo: more than one message at once..
+        
+        UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
         if(pos != targetpos) {
         if(pos != targetpos) {
-            printf("The message size was not as announced or an error occurred while processing, skipping to the end of the message.\n");
+            printf("The message size was not as announced or the message could not be processed, skipping to the end of the message.\n");
             pos = targetpos;
             pos = targetpos;
         }
         }
     } while(msg->length > (UA_Int32)pos);
     } while(msg->length > (UA_Int32)pos);

+ 67 - 6
src/server/ua_server_internal.h

@@ -1,10 +1,18 @@
 #ifndef UA_SERVER_INTERNAL_H_
 #ifndef UA_SERVER_INTERNAL_H_
 #define UA_SERVER_INTERNAL_H_
 #define UA_SERVER_INTERNAL_H_
 
 
+#include "ua_config.h"
+
+#ifdef UA_MULTITHREADING
+#define _LGPL_SOURCE
+#include <urcu.h>
+#include <urcu/wfcqueue.h>
+#endif
+
 #include "ua_server.h"
 #include "ua_server.h"
-#include "ua_nodestore.h"
 #include "ua_session_manager.h"
 #include "ua_session_manager.h"
 #include "ua_securechannel_manager.h"
 #include "ua_securechannel_manager.h"
+#include "ua_nodestore.h"
 
 
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
     that have no mapping defined, the internal nodestore is used by default. */
@@ -14,6 +22,13 @@ typedef struct UA_ExternalNamespace {
 	UA_ExternalNodeStore externalNodeStore;
 	UA_ExternalNodeStore externalNodeStore;
 } UA_ExternalNamespace;
 } UA_ExternalNamespace;
 
 
+// forward declarations
+struct UA_TimedWork;
+typedef struct UA_TimedWork UA_TimedWork;
+
+struct UA_DelayedWork;
+typedef struct UA_DelayedWork UA_DelayedWork;
+
 struct UA_Server {
 struct UA_Server {
     UA_ApplicationDescription description;
     UA_ApplicationDescription description;
     UA_Int32 endpointDescriptionsSize;
     UA_Int32 endpointDescriptionsSize;
@@ -27,13 +42,59 @@ struct UA_Server {
     UA_NodeStore *nodestore;
     UA_NodeStore *nodestore;
     UA_Int32 externalNamespacesSize;
     UA_Int32 externalNamespacesSize;
     UA_ExternalNamespace *externalNamespaces;
     UA_ExternalNamespace *externalNamespaces;
+
+    UA_Int32 nlsSize;
+    UA_NetworkLayer *nls;
+
+    UA_UInt32 random_seed;
+
+#ifdef UA_MULTITHREADING
+    UA_Boolean *running;
+    UA_UInt16 nThreads;
+    UA_UInt32 **workerCounters;
+    UA_DelayedWork *delayedWork;
+
+    // worker threads wait on the queue
+	struct cds_wfcq_head dispatchQueue_head;
+	struct cds_wfcq_tail dispatchQueue_tail;
+#endif
+
+    LIST_HEAD(UA_TimedWorkList, UA_TimedWork) timedWork;
 };
 };
 
 
-UA_AddNodesResult
-UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
-                             const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
+
+UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
+                                               const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+
+UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
+
+void UA_Server_deleteTimedWork(UA_Server *server);
 
 
-UA_StatusCode
-UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
+/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
+typedef enum {
+    UA_ATTRIBUTEID_NODEID                  = 1,
+    UA_ATTRIBUTEID_NODECLASS               = 2,
+    UA_ATTRIBUTEID_BROWSENAME              = 3,
+    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
+    UA_ATTRIBUTEID_DESCRIPTION             = 5,
+    UA_ATTRIBUTEID_WRITEMASK               = 6,
+    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
+    UA_ATTRIBUTEID_ISABSTRACT              = 8,
+    UA_ATTRIBUTEID_SYMMETRIC               = 9,
+    UA_ATTRIBUTEID_INVERSENAME             = 10,
+    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
+    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
+    UA_ATTRIBUTEID_VALUE                   = 13,
+    UA_ATTRIBUTEID_DATATYPE                = 14,
+    UA_ATTRIBUTEID_VALUERANK               = 15,
+    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
+    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
+    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
+    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
+    UA_ATTRIBUTEID_HISTORIZING             = 20,
+    UA_ATTRIBUTEID_EXECUTABLE              = 21,
+    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+} UA_AttributeId;
 
 
 #endif /* UA_SERVER_INTERNAL_H_ */
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 456 - 0
src/server/ua_server_worker.c

@@ -0,0 +1,456 @@
+#include <stdio.h>
+#include "ua_server_internal.h"
+
+/**
+ * There are three types of work:
+ *
+ * 1. Ordinary WorkItems (that are dispatched to worker threads if
+ *    multithreading is activated)
+ *
+ * 2. Timed work that is executed at a precise date (with an optional repetition
+ *    interval)
+ *
+ * 3. Delayed work that is executed at a later time when it is guaranteed that
+ *    all previous work has actually finished (only for multithreading)
+ */
+
+#define MAXTIMEOUT 5000 // max timeout in usec until the next main loop iteration
+#define BATCHSIZE 20 // max size of worklists that are dispatched to workers
+
+static void processWork(UA_Server *server, const UA_WorkItem *work, UA_Int32 workSize) {
+    for(UA_Int32 i = 0;i<workSize;i++) {
+        const UA_WorkItem *item = &work[i];
+        switch(item->type) {
+        case UA_WORKITEMTYPE_BINARYNETWORKMESSAGE:
+            UA_Server_processBinaryMessage(server, item->work.binaryNetworkMessage.connection,
+                                           &item->work.binaryNetworkMessage.message);
+            UA_free(item->work.binaryNetworkMessage.message.data);
+            break;
+
+        case UA_WORKITEMTYPE_METHODCALL:
+        case UA_WORKITEMTYPE_DELAYEDMETHODCALL:
+            item->work.methodCall.method(server, item->work.methodCall.data);
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+/*******************************/
+/* Worker Threads and Dispatch */
+/*******************************/
+
+#ifdef UA_MULTITHREADING
+
+/** Entry in the dipatch queue */
+struct workListNode {
+    struct cds_wfcq_node node; // node for the queue
+    UA_UInt32 workSize;
+    UA_WorkItem *work;
+};
+
+/** Dispatch work to workers. Slices the work up if it contains more than
+    BATCHSIZE items. The work array is freed by the worker threads. */
+static void dispatchWork(UA_Server *server, UA_Int32 workSize, UA_WorkItem *work) {
+    UA_Int32 startIndex = workSize; // start at the end
+    while(workSize > 0) {
+        UA_Int32 size = BATCHSIZE;
+        if(size > workSize)
+            size = workSize;
+        startIndex = startIndex - size;
+        struct workListNode *wln = UA_malloc(sizeof(struct workListNode));
+        if(startIndex > 0) {
+            UA_WorkItem *workSlice = UA_malloc(size * sizeof(UA_WorkItem));
+            UA_memcpy(workSlice, &work[startIndex], size * sizeof(UA_WorkItem));
+            *wln = (struct workListNode){.workSize = size, .work = workSlice};
+        }
+        else {
+            // do not alloc, but forward the original array
+            *wln = (struct workListNode){.workSize = size, .work = work};
+        }
+        cds_wfcq_node_init(&wln->node);
+        cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &wln->node);
+        workSize -= size;
+    } 
+}
+
+// throwaway struct to bring data into the worker threads
+struct workerStartData {
+    UA_Server *server;
+    UA_UInt32 **workerCounter;
+};
+
+/** Waits until work arrives in the dispatch queue (restart after 10ms) and
+    processes it. */
+static void * workerLoop(struct workerStartData *startInfo) {
+   	rcu_register_thread();
+    UA_UInt32 *c = UA_malloc(sizeof(UA_UInt32));
+    uatomic_set(c, 0);
+
+    *startInfo->workerCounter = c;
+    UA_Server *server = startInfo->server;
+    UA_free(startInfo);
+    
+    while(*server->running) {
+        struct workListNode *wln = (struct workListNode*)
+            cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+        if(wln) {
+            processWork(server, wln->work, wln->workSize);
+            UA_free(wln->work);
+            UA_free(wln);
+        }
+        uatomic_inc(c); // increase the workerCounter;
+    }
+   	rcu_unregister_thread();
+    return UA_NULL;
+}
+
+static void emptyDispatchQueue(UA_Server *server) {
+    while(!cds_wfcq_empty(&server->dispatchQueue_head, &server->dispatchQueue_tail)) {
+        struct workListNode *wln = (struct workListNode*)
+            cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
+        processWork(server, wln->work, wln->workSize);
+        UA_free(wln->work);
+        UA_free(wln);
+    }
+}
+
+#endif
+
+/**************/
+/* Timed Work */
+/**************/
+
+struct UA_TimedWork {
+    LIST_ENTRY(UA_TimedWork) pointers;
+    UA_UInt16 workSize;
+    UA_WorkItem *work;
+    UA_Guid *workIds;
+    UA_DateTime time;
+    UA_UInt32 repetitionInterval; // in 100ns resolution, 0 means no repetition
+};
+
+/* The item is copied and not freed by this function. */
+static UA_Guid addTimedWork(UA_Server *server, UA_WorkItem *item, UA_DateTime firstTime,
+                            UA_UInt32 repetitionInterval) {
+    UA_TimedWork *tw, *lastTw = UA_NULL;
+
+    // search for matching entry
+    LIST_FOREACH(tw, &server->timedWork, pointers) {
+        if(tw->repetitionInterval == repetitionInterval &&
+           (repetitionInterval > 0 || tw->time == firstTime))
+            break; // found a matching entry
+        if(tw->time > firstTime) {
+            tw = UA_NULL; // not matchin entry exists
+            lastTw = tw;
+            break;
+        }
+    }
+    
+    if(tw) {
+        // append to matching entry
+        tw->workSize++;
+        tw->work = UA_realloc(tw->work, sizeof(UA_WorkItem)*tw->workSize);
+        tw->workIds = UA_realloc(tw->workIds, sizeof(UA_Guid)*tw->workSize);
+        tw->work[tw->workSize-1] = *item;
+        tw->workIds[tw->workSize-1] = UA_Guid_random(&server->random_seed);
+        return tw->workIds[tw->workSize-1];
+    }
+
+    // create a new entry
+    tw = UA_malloc(sizeof(UA_TimedWork));
+    tw->workSize = 1;
+    tw->time = firstTime;
+    tw->repetitionInterval = repetitionInterval;
+    tw->work = UA_malloc(sizeof(UA_WorkItem));
+    tw->work[0] = *item;
+    tw->workIds = UA_malloc(sizeof(UA_Guid));
+    tw->workIds[0] = UA_Guid_random(&server->random_seed);
+    if(lastTw)
+        LIST_INSERT_AFTER(lastTw, tw, pointers);
+    else
+        LIST_INSERT_HEAD(&server->timedWork, tw, pointers);
+
+    return tw->workIds[0];
+}
+
+// Currently, these functions need to get the server mutex, but should be sufficiently fast
+UA_Guid UA_Server_addTimedWorkItem(UA_Server *server, UA_WorkItem *work, UA_DateTime time) {
+    return addTimedWork(server, work, time, 0);
+}
+
+UA_Guid UA_Server_addRepeatedWorkItem(UA_Server *server, UA_WorkItem *work, UA_UInt32 interval) {
+    return addTimedWork(server, work, UA_DateTime_now() + interval, interval);
+}
+
+/** Dispatches timed work, returns the timeout until the next timed work in ms */
+static UA_UInt16 processTimedWork(UA_Server *server) {
+    UA_DateTime current = UA_DateTime_now();
+    UA_TimedWork *next = LIST_FIRST(&server->timedWork);
+    UA_TimedWork *tw = UA_NULL;
+
+    while(next) {
+        tw = next;
+        if(tw->time > current)
+            break;
+        next = LIST_NEXT(tw, pointers);
+
+#ifdef UA_MULTITHREADING
+        if(tw->repetitionInterval > 0) {
+            // copy the entry and insert at the new location
+            UA_WorkItem *workCopy = UA_malloc(sizeof(UA_WorkItem) * tw->workSize);
+            UA_memcpy(workCopy, tw->work, sizeof(UA_WorkItem) * tw->workSize);
+            dispatchWork(server, tw->workSize, workCopy); // frees the work pointer
+            tw->time += tw->repetitionInterval;
+
+            UA_TimedWork *prevTw = tw; // after which tw do we insert?
+            while(UA_TRUE) {
+                UA_TimedWork *next = LIST_NEXT(prevTw, pointers);
+                if(!next || next->time > tw->time)
+                    break;
+                prevTw = next;
+            }
+            if(prevTw != tw) {
+                LIST_REMOVE(tw, pointers);
+                LIST_INSERT_AFTER(prevTw, tw, pointers);
+            }
+        } else {
+            dispatchWork(server, tw->workSize, tw->work); // frees the work pointer
+            LIST_REMOVE(tw, pointers);
+            UA_free(tw->workIds);
+            UA_free(tw);
+        }
+#else
+        processWork(server,tw->work, tw->workSize); // does not free the work
+        if(tw->repetitionInterval > 0) {
+            tw->time += tw->repetitionInterval;
+            UA_TimedWork *prevTw = tw;
+            while(UA_TRUE) {
+                UA_TimedWork *next = LIST_NEXT(prevTw, pointers);
+                if(!next || next->time > tw->time)
+                    break;
+                prevTw = next;
+            }
+            if(prevTw != tw) {
+                LIST_REMOVE(tw, pointers);
+                LIST_INSERT_AFTER(prevTw, tw, pointers);
+            }
+        } else {
+            LIST_REMOVE(tw, pointers);
+            UA_free(tw->work);
+            UA_free(tw->workIds);
+            UA_free(tw);
+        }
+#endif
+    }
+
+    tw = LIST_FIRST(&server->timedWork);
+    UA_UInt16 timeout = MAXTIMEOUT;
+    if(tw)
+        timeout = (tw->time - current)/10;
+    return timeout;
+}
+
+void UA_Server_deleteTimedWork(UA_Server *server) {
+    UA_TimedWork *tw;
+    while((tw = LIST_FIRST(&server->timedWork))) {
+        LIST_REMOVE(tw, pointers);
+        UA_free(tw->work);
+        UA_free(tw->workIds);
+        UA_free(tw);
+    }
+}
+
+/****************/
+/* Delayed Work */
+/****************/
+
+#ifdef UA_MULTITHREADING
+
+#define DELAYEDWORKSIZE 100 // Collect delayed work until we have DELAYEDWORKSIZE items
+
+struct UA_DelayedWork {
+    UA_DelayedWork *next;
+    UA_UInt32 *workerCounters; // initially UA_NULL until a workitem gets the counters
+    UA_UInt32 workItemsCount; // the size of the array is DELAYEDWORKSIZE, the count may be less
+    UA_WorkItem *workItems; // when it runs full, a new delayedWork entry is created
+};
+
+// Dispatched as a methodcall-WorkItem when the delayedwork is added
+static void getCounters(UA_Server *server, UA_DelayedWork *delayed) {
+    UA_UInt32 *counters = UA_malloc(server->nThreads * sizeof(UA_UInt32));
+    for(UA_UInt16 i = 0;i<server->nThreads;i++)
+        counters[i] = *server->workerCounters[i];
+    delayed->workerCounters = counters;
+}
+
+// Call from the main thread only. This is the only function that modifies
+// server->delayedWork. processDelayedWorkQueue modifies the "next" (after the
+// head).
+static void addDelayedWork(UA_Server *server, UA_WorkItem work) {
+    UA_DelayedWork *dw = server->delayedWork;
+    if(!dw || dw->workItemsCount >= DELAYEDWORKSIZE) {
+        UA_DelayedWork *newwork = UA_malloc(sizeof(UA_DelayedWork));
+        newwork->workItems = UA_malloc(sizeof(UA_WorkItem)*DELAYEDWORKSIZE);
+        newwork->workItemsCount = 0;
+        newwork->workerCounters = UA_NULL;
+        newwork->next = server->delayedWork;
+
+        // dispatch a method that sets the counter
+        if(dw && dw->workItemsCount >= DELAYEDWORKSIZE) {
+            UA_WorkItem *setCounter = UA_malloc(sizeof(UA_WorkItem));
+            *setCounter = (UA_WorkItem)
+                {.type = UA_WORKITEMTYPE_METHODCALL,
+                 .work.methodCall = {.method = (void (*)(UA_Server*, void*))getCounters, .data = dw}};
+            dispatchWork(server, 1, setCounter);
+        }
+
+        server->delayedWork = newwork;
+        dw = newwork;
+    }
+    dw->workItems[dw->workItemsCount] = work;
+    dw->workItemsCount++;
+}
+
+static void processDelayedWork(UA_Server *server) {
+    UA_DelayedWork *dw = server->delayedWork;
+    while(dw) {
+        processWork(server, dw->workItems, dw->workItemsCount);
+        UA_DelayedWork *next = dw->next;
+        UA_free(dw->workerCounters);
+        UA_free(dw->workItems);
+        UA_free(dw);
+        dw = next;
+    }
+}
+
+// Execute this every N seconds (repeated work) to execute delayed work that is ready
+static void dispatchDelayedWork(UA_Server *server, void *data /* not used, but needed for the signature*/) {
+    UA_DelayedWork *dw = UA_NULL;
+    UA_DelayedWork *readydw = UA_NULL;
+    UA_DelayedWork *beforedw = server->delayedWork;
+
+    // start at the second...
+    if(beforedw)
+        dw = beforedw->next;
+
+    // find the first delayedwork where the counters are set and have been moved
+    while(dw) {
+        if(!dw->workerCounters) {
+            beforedw = dw;
+            dw = dw->next;
+            continue;
+        }
+
+        UA_Boolean countersMoved = UA_TRUE;
+        for(UA_UInt16 i=0;i<server->nThreads;i++) {
+            if(*server->workerCounters[i] == dw->workerCounters[i])
+                countersMoved = UA_FALSE;
+                break;
+        }
+        
+        if(countersMoved) {
+            readydw = uatomic_xchg(&beforedw->next, UA_NULL);
+            break;
+        } else {
+            beforedw = dw;
+            dw = dw->next;
+        }
+    }
+
+    // we have a ready entry. all afterwards are also ready
+    while(readydw) {
+        dispatchWork(server, readydw->workItemsCount, readydw->workItems);
+        beforedw = readydw;
+        readydw = readydw->next;
+        UA_free(beforedw->workerCounters);
+        UA_free(beforedw);
+    }
+}
+
+#endif
+
+/********************/
+/* Main Server Loop */
+/********************/
+
+UA_StatusCode UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running) {
+#ifdef UA_MULTITHREADING
+    // 1) Prepare the threads
+    server->running = running; // the threads need to access the variable
+    server->nThreads = nThreads;
+    pthread_t *thr = UA_malloc(nThreads * sizeof(pthread_t));
+    server->workerCounters = UA_malloc(nThreads * sizeof(UA_UInt32 *));
+    for(UA_UInt32 i=0;i<nThreads;i++) {
+        struct workerStartData *startData = UA_malloc(sizeof(struct workerStartData));
+        startData->server = server;
+        startData->workerCounter = &server->workerCounters[i];
+        pthread_create(&thr[i], UA_NULL, (void* (*)(void*))workerLoop, startData);
+    }
+
+    UA_WorkItem processDelayed = {.type = UA_WORKITEMTYPE_METHODCALL,
+                                  .work.methodCall = {.method = dispatchDelayedWork,
+                                                      .data = UA_NULL} };
+    UA_Server_addRepeatedWorkItem(server, &processDelayed, 10000000);
+#endif
+
+    // 2) Start the networklayers
+    for(UA_Int32 i=0;i<server->nlsSize;i++)
+        server->nls[i].start(server->nls[i].nlHandle);
+
+    // 3) The loop
+    while(1) {
+        // 3.1) Process timed work
+        UA_UInt16 timeout = processTimedWork(server);
+
+        // 3.2) Get work from the networklayer and dispatch it
+        for(UA_Int32 i=0;i<server->nlsSize;i++) {
+            UA_NetworkLayer *nl = &server->nls[i];
+            UA_WorkItem *work;
+            UA_Int32 workSize;
+            if(*running) {
+                if(i == server->nlsSize-1)
+                    workSize = nl->getWork(nl->nlHandle, &work, 0);
+                else
+                    workSize = nl->getWork(nl->nlHandle, &work, timeout);
+            } else {
+                workSize = server->nls[i].stop(nl->nlHandle, &work);
+            }
+
+#ifdef UA_MULTITHREADING
+            // Filter out delayed work
+            for(UA_Int32 k=0;k<workSize;k++) {
+                if(work[k].type != UA_WORKITEMTYPE_DELAYEDMETHODCALL)
+                    continue;
+                addDelayedWork(server, work[k]);
+                work[k].type = UA_WORKITEMTYPE_NOTHING;
+            }
+            dispatchWork(server, workSize, work);
+#else
+            processWork(server, work, workSize);
+            UA_free(work);
+#endif
+        }
+
+        // 3.3) Exit?
+        if(!*running)
+            break;
+    }
+
+#ifdef UA_MULTITHREADING
+    // 4) Clean up: Wait until all worker threads finish, then empty the
+    // dispatch queue, then process the remaining delayed work
+    for(UA_UInt32 i=0;i<nThreads;i++) {
+        pthread_join(thr[i], UA_NULL);
+        UA_free(server->workerCounters[i]);
+    }
+    UA_free(server->workerCounters);
+    UA_free(thr);
+    emptyDispatchQueue(server);
+    processDelayedWork(server);
+#endif
+
+    return UA_STATUSCODE_GOOD;
+}

+ 46 - 44
src/server/ua_services.h

@@ -10,7 +10,9 @@
  * @ingroup server
  * @ingroup server
  * @defgroup services Services
  * @defgroup services Services
  *
  *
- * @brief This module describes all the services used to communicate in in OPC UA.
+ * @brief The UA services that can be called from a remote user
+ *
+ * @{
  */
  */
 
 
 /**
 /**
@@ -24,9 +26,8 @@
 // Service_FindServers
 // Service_FindServers
 
 
 /**
 /**
- * @brief This Service returns the Endpoints supported by a Server and all of
- * the configuration information required to establish a SecureChannel and a
- * Session.
+ * Returns the Endpoints supported by a Server and all of the configuration
+ * information required to establish a SecureChannel and a Session.
  */
  */
 void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
 void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
 // Service_RegisterServer
 // Service_RegisterServer
@@ -42,15 +43,17 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
  * @{
  * @{
  */
  */
 
 
-/** @brief This Service is used to open or renew a SecureChannel that can be
-   used to ensure Confidentiality and Integrity for Message exchange during a
-   Session. */
+/**
+ * Open or renew a SecureChannel that can be used to ensure Confidentiality and
+ * Integrity for Message exchange during a Session.
+ */
 void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
 void Service_OpenSecureChannel(UA_Server *server, UA_Connection *connection,
                                const UA_OpenSecureChannelRequest *request,
                                const UA_OpenSecureChannelRequest *request,
                                UA_OpenSecureChannelResponse *response);
                                UA_OpenSecureChannelResponse *response);
 
 
-/** @brief This Service is used to terminate a SecureChannel. */
+/** Used to terminate a SecureChannel. */
 void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
 void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
+
 /** @} */
 /** @} */
 
 
 /**
 /**
@@ -63,26 +66,26 @@ void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
  */
  */
 
 
 /**
 /**
- * @brief This Service is used by an OPC UA Client to create a Session and the
- * Server returns two values which uniquely identify the Session. The first
- * value is the sessionId which is used to identify the Session in the audit
- * logs and in the Server’s address space. The second is the authenticationToken
- * which is used to associate an incoming request with a Session.
+ * Used by an OPC UA Client to create a Session and the Server returns two
+ * values which uniquely identify the Session. The first value is the sessionId
+ * which is used to identify the Session in the audit logs and in the Server’s
+ * address space. The second is the authenticationToken which is used to
+ * associate an incoming request with a Session.
  */
  */
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
                            const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response);
                            const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response);
 
 
 /**
 /**
- * @brief This Service is used by the Client to submit its SoftwareCertificates
- * to the Server for validation and to specify the identity of the user
- * associated with the Session. This Service request shall be issued by the
- * Client before it issues any other Service request after CreateSession.
- * Failure to do so shall cause the Server to close the Session.
+ * Used by the Client to submit its SoftwareCertificates to the Server for
+ * validation and to specify the identity of the user associated with the
+ * Session. This Service request shall be issued by the Client before it issues
+ * any other Service request after CreateSession. Failure to do so shall cause
+ * the Server to close the Session.
  */
  */
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
 
 
-/** @brief This Service is used to terminate a Session. */
+/** Used to terminate a Session. */
 void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
 void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
 // Service_Cancel
 // Service_Cancel
 /** @} */
 /** @} */
@@ -97,16 +100,16 @@ void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *reque
  * @{
  * @{
  */
  */
 
 
-/** @brief This Service is used to add one or more Nodes into the AddressSpace hierarchy. */
+/** Used to add one or more Nodes into the AddressSpace hierarchy. */
 void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
 void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
 
 
-/** @brief This Service is used to add one or more References to one or more Nodes. */
+/** Used to add one or more References to one or more Nodes. */
 void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
 void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
 
 
-/** @brief This Service is used to delete one or more Nodes from the AddressSpace. */
+/** Used to delete one or more Nodes from the AddressSpace. */
 void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response);
 void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response);
 
 
-/** @brief This Service is used to delete one or more References of a Node. */
+/** Used to delete one or more References of a Node. */
 void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response);
 void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response);
 
 
 /** @} */
 /** @} */
@@ -121,14 +124,14 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_D
  */
  */
 
 
 /**
 /**
- * @brief This Service is used to discover the References of a specified Node.
- * The browse can be further limited by the use of a View. This Browse Service
- * also supports a primitive filtering capability.
+ * Used to discover the References of a specified Node. The browse can be
+ * further limited by the use of a View. This Browse Service also supports a
+ * primitive filtering capability.
  */
  */
 void Service_Browse(UA_Server *server, UA_Session *session,
 void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request, UA_BrowseResponse *response);
                     const UA_BrowseRequest *request, UA_BrowseResponse *response);
 
 
-/** @brief This Service is used to translate textual node paths to their respective ids. */
+/** Used to translate textual node paths to their respective ids. */
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
@@ -164,21 +167,19 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
  */
  */
 
 
 /**
 /**
- * @brief This Service is used to read one or more Attributes of one or more
- * Nodes. For constructed Attribute values whose elements are indexed, such as
- * an array, this Service allows Clients to read the entire set of indexed
- * values as a composite, to read individual elements or to read ranges of
- * elements of the composite.
+ * Used to read one or more Attributes of one or more Nodes. For constructed
+ * Attribute values whose elements are indexed, such as an array, this Service
+ * allows Clients to read the entire set of indexed values as a composite, to
+ * read individual elements or to read ranges of elements of the composite.
  */
  */
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response);
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response);
 // Service_HistoryRead
 // Service_HistoryRead
 
 
 /**
 /**
- * @brief This Service is used to write one or more Attributes of one or more
- *  Nodes. For constructed Attribute values whose elements are indexed, such as
- *  an array, this Service allows Clients to write the entire set of indexed
- *  values as a composite, to write individual elements or to write ranges of
- *  elements of the composite.
+ * Used to write one or more Attributes of one or more Nodes. For constructed
+ * Attribute values whose elements are indexed, such as an array, this Service
+ * allows Clients to write the entire set of indexed values as a composite, to
+ * write individual elements or to write ranges of elements of the composite.
  */
  */
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response);
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response);
 // Service_HistoryUpdate
 // Service_HistoryUpdate
@@ -188,7 +189,7 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
  * @name Method Service Set
  * @name Method Service Set
  *
  *
  * The Method Service Set defines the means to invoke methods. A method shall be
  * The Method Service Set defines the means to invoke methods. A method shall be
-   a component of an Object.
+ * a component of an Object.
  *
  *
  * @{
  * @{
  */
  */
@@ -205,12 +206,12 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
  * @{
  * @{
  */
  */
 
 
-/**
- * @brief This Service is used to create and add one or more MonitoredItems to a
- * Subscription. A MonitoredItem is deleted automatically by the Server when the
- * Subscription is deleted. Deleting a MonitoredItem causes its entire set of
- * triggered item links to be deleted, but has no effect on the MonitoredItems
- * referenced by the triggered items.
+/*
+ * Used to create and add one or more MonitoredItems to a Subscription. A
+ * MonitoredItem is deleted automatically by the Server when the Subscription is
+ * deleted. Deleting a MonitoredItem causes its entire set of triggered item
+ * links to be deleted, but has no effect on the MonitoredItems referenced by
+ * the triggered items.
  */
  */
 /* UA_Int32 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session, */
 /* UA_Int32 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session, */
 /*                                       const UA_CreateMonitoredItemsRequest *request, */
 /*                                       const UA_CreateMonitoredItemsRequest *request, */
@@ -246,5 +247,6 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
 // Service_TransferSubscription
 // Service_TransferSubscription
 // Service_DeleteSubscription
 // Service_DeleteSubscription
 /** @} */
 /** @} */
+/** @} */
 
 
 #endif /* UA_SERVICES_H_ */
 #endif /* UA_SERVICES_H_ */

+ 12 - 11
src/server/ua_services_attribute.c

@@ -1,6 +1,6 @@
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
+#include "ua_types_generated.h"
 #include "ua_services.h"
 #include "ua_services.h"
-#include "ua_services_internal.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
@@ -186,23 +186,24 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         return;
         return;
     }
     }
 
 
-    UA_StatusCode retval = UA_Array_new((void**)&response->results,
-                                        request->nodesToReadSize,
-                                        &UA_TYPES[UA_DATAVALUE]);
-    if(retval) {
-        response->responseHeader.serviceResult = retval;
-        return;
+    // if allocated on the stack from the "outside"
+    if(response->resultsSize <= 0) {
+        UA_StatusCode retval = UA_Array_new((void**)&response->results,
+                                            request->nodesToReadSize, &UA_TYPES[UA_DATAVALUE]);
+        if(retval) {
+            response->responseHeader.serviceResult = retval;
+            return;
+        }
     }
     }
 
 
     /* ### Begin External Namespaces */
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToReadSize);
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToReadSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToReadSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToReadSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
         UA_UInt32 indexSize = 0;
         for(UA_Int32 i = 0;i < request->nodesToReadSize;i++) {
         for(UA_Int32 i = 0;i < request->nodesToReadSize;i++) {
-            if(request->nodesToRead[i].nodeId.namespaceIndex !=
-               server->externalNamespaces[j].index)
+            if(request->nodesToRead[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
                 continue;
                 continue;
             isExternal[i] = UA_TRUE;
             isExternal[i] = UA_TRUE;
             indices[indexSize] = i;
             indices[indexSize] = i;
@@ -362,7 +363,7 @@ void Service_Write(UA_Server *server, UA_Session *session,
 
 
     /* ### Begin External Namespaces */
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
         UA_UInt32 indexSize = 0;

+ 9 - 2
src/server/ua_services_discovery.c

@@ -7,8 +7,7 @@ void Service_GetEndpoints(UA_Server                    *server,
                           const UA_GetEndpointsRequest *request,
                           const UA_GetEndpointsRequest *request,
                           UA_GetEndpointsResponse      *response) {
                           UA_GetEndpointsResponse      *response) {
     UA_GetEndpointsResponse_init(response);
     UA_GetEndpointsResponse_init(response);
-    response->endpointsSize = 1;
-    response->endpoints = UA_alloc(sizeof(UA_EndpointDescription));
+    response->endpoints = UA_malloc(sizeof(UA_EndpointDescription));
     if(!response->endpoints) {
     if(!response->endpoints) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
         return;
@@ -16,5 +15,13 @@ void Service_GetEndpoints(UA_Server                    *server,
     if(UA_EndpointDescription_copy(server->endpointDescriptions, response->endpoints) != UA_STATUSCODE_GOOD) {
     if(UA_EndpointDescription_copy(server->endpointDescriptions, response->endpoints) != UA_STATUSCODE_GOOD) {
         UA_free(response->endpoints);
         UA_free(response->endpoints);
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
+    UA_String_deleteMembers(&response->endpoints->endpointUrl);
+    if(UA_String_copy(&request->endpointUrl, &response->endpoints->endpointUrl) != UA_STATUSCODE_GOOD) {
+        UA_EndpointDescription_delete(response->endpoints);
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
     }
     }
+    response->endpointsSize = 1;
 }
 }

+ 0 - 40
src/server/ua_services_internal.h

@@ -1,40 +0,0 @@
-/**
- * @brief This files contains helper functions for the UA-Services that are used
- * internally as well (with a simplified API as no access rights are checked).
- */
-
-#ifndef UA_SERVICES_INTERNAL_H_
-#define UA_SERVICES_INTERNAL_H_
-
-#include "ua_session.h"
-#include "ua_nodestore.h"
-#include "ua_types_generated.h"
-#include "ua_namespace_0.h"
-
-/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
-typedef enum UA_AttributeId {
-    UA_ATTRIBUTEID_NODEID                  = 1,
-    UA_ATTRIBUTEID_NODECLASS               = 2,
-    UA_ATTRIBUTEID_BROWSENAME              = 3,
-    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
-    UA_ATTRIBUTEID_DESCRIPTION             = 5,
-    UA_ATTRIBUTEID_WRITEMASK               = 6,
-    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
-    UA_ATTRIBUTEID_ISABSTRACT              = 8,
-    UA_ATTRIBUTEID_SYMMETRIC               = 9,
-    UA_ATTRIBUTEID_INVERSENAME             = 10,
-    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
-    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
-    UA_ATTRIBUTEID_VALUE                   = 13,
-    UA_ATTRIBUTEID_DATATYPE                = 14,
-    UA_ATTRIBUTEID_VALUERANK               = 15,
-    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
-    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
-    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
-    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
-    UA_ATTRIBUTEID_HISTORIZING             = 20,
-    UA_ATTRIBUTEID_EXECUTABLE              = 21,
-    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
-} UA_AttributeId;
-
-#endif /* UA_SERVICES_INTERNAL_H_ */

+ 4 - 5
src/server/ua_services_nodemanagement.c

@@ -3,7 +3,6 @@
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
-#include "ua_services_internal.h"
 #include "ua_session.h"
 #include "ua_session.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
@@ -244,7 +243,7 @@ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesR
 
 
     /* ### Begin External Namespaces */
     /* ### Begin External Namespaces */
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToAddSize);
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToAddSize);
-    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
+    UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToAddSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToAddSize);
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
         UA_UInt32 indexSize = 0;
@@ -277,19 +276,19 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
 		return;
 		return;
 	}
 	}
-	response->results = UA_alloc(
+	response->results = UA_malloc(
 			sizeof(UA_StatusCode) * request->referencesToAddSize);
 			sizeof(UA_StatusCode) * request->referencesToAddSize);
 	if (!response->results) {
 	if (!response->results) {
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
 		return;
 		return;
 	}
 	}
 	response->resultsSize = request->referencesToAddSize;
 	response->resultsSize = request->referencesToAddSize;
-	memset(response->results, UA_STATUSCODE_GOOD,
+	UA_memset(response->results, UA_STATUSCODE_GOOD,
 			sizeof(UA_StatusCode) * response->resultsSize);
 			sizeof(UA_StatusCode) * response->resultsSize);
 	/* ### Begin External Namespaces */
 	/* ### Begin External Namespaces */
 //UA_Boolean isExternal[MAX_ADDREFERENCES_SIZE];
 //UA_Boolean isExternal[MAX_ADDREFERENCES_SIZE];
 	UA_Boolean isExternal[request->referencesToAddSize];
 	UA_Boolean isExternal[request->referencesToAddSize];
-	memset(isExternal, UA_FALSE,
+	UA_memset(isExternal, UA_FALSE,
 			sizeof(UA_Boolean) * request->referencesToAddSize);
 			sizeof(UA_Boolean) * request->referencesToAddSize);
 	UA_UInt32 indices[request->referencesToAddSize];
 	UA_UInt32 indices[request->referencesToAddSize];
 	for (UA_Int32 j = 0; j < server->externalNamespacesSize; j++) {
 	for (UA_Int32 j = 0; j < server->externalNamespacesSize; j++) {

+ 1 - 1
src/server/ua_services_session.c

@@ -24,7 +24,7 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     UA_ByteString_copy(&server->serverCertificate, &response->serverCertificate);
     UA_ByteString_copy(&server->serverCertificate, &response->serverCertificate);
 
 
     response->serverEndpointsSize = 1;
     response->serverEndpointsSize = 1;
-    response->serverEndpoints = UA_alloc(sizeof(UA_EndpointDescription));
+    response->serverEndpoints = UA_malloc(sizeof(UA_EndpointDescription));
     UA_EndpointDescription_copy(server->endpointDescriptions, response->serverEndpoints);
     UA_EndpointDescription_copy(server->endpointDescriptions, response->serverEndpoints);
     
     
 }
 }

+ 4 - 4
src/server/ua_services_view.c

@@ -84,7 +84,7 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
     UA_UInt32 currentIndex = 0;
     UA_UInt32 currentIndex = 0;
     UA_UInt32 currentLastIndex = 0;
     UA_UInt32 currentLastIndex = 0;
     UA_UInt32 currentArraySize = 20; // should be more than enough. if not, increase the array size.
     UA_UInt32 currentArraySize = 20; // should be more than enough. if not, increase the array size.
-    UA_NodeId *typeArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize);
+    UA_NodeId *typeArray = UA_malloc(sizeof(UA_NodeId) * currentArraySize);
     if(!typeArray)
     if(!typeArray)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
@@ -112,9 +112,9 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
 
 
             if(currentLastIndex + 1 >= currentArraySize) {
             if(currentLastIndex + 1 >= currentArraySize) {
                 // we need to resize the array
                 // we need to resize the array
-                UA_NodeId *newArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize * 2);
+                UA_NodeId *newArray = UA_malloc(sizeof(UA_NodeId) * currentArraySize * 2);
                 if(newArray) {
                 if(newArray) {
-                    memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
+                    UA_memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
                     currentArraySize *= 2;
                     currentArraySize *= 2;
                     UA_free(typeArray);
                     UA_free(typeArray);
                     typeArray = newArray;
                     typeArray = newArray;
@@ -184,7 +184,7 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
     /* We allocate an array that is probably too big. But since most systems
     /* We allocate an array that is probably too big. But since most systems
        have more than enough memory, this has zero impact on speed and
        have more than enough memory, this has zero impact on speed and
        performance. Call Array_delete with the actual content size! */
        performance. Call Array_delete with the actual content size! */
-    browseResult->references = UA_alloc(sizeof(UA_ReferenceDescription) * maxReferences);
+    browseResult->references = UA_malloc(sizeof(UA_ReferenceDescription) * maxReferences);
     if(!browseResult->references) {
     if(!browseResult->references) {
         browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
     } else {
     } else {

+ 1 - 1
src/server/ua_session_manager.c

@@ -87,7 +87,7 @@ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
     if(sessionManager->currentSessionCount >= sessionManager->maxSessionCount)
     if(sessionManager->currentSessionCount >= sessionManager->maxSessionCount)
         return UA_STATUSCODE_BADTOOMANYSESSIONS;
         return UA_STATUSCODE_BADTOOMANYSESSIONS;
 
 
-    struct session_list_entry *newentry = UA_alloc(sizeof(struct session_list_entry));
+    struct session_list_entry *newentry = UA_malloc(sizeof(struct session_list_entry));
     if(!newentry)
     if(!newentry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 

+ 1 - 11
src/server/ua_session_manager.h

@@ -25,20 +25,10 @@ UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
 UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
 UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
                                               UA_NodeId         *sessionId);
                                               UA_NodeId         *sessionId);
 
 
-/**
- * @brief finds the session which is identified by the sessionId
- * @param sessionId the session id is used to identify the unknown session
- * @param session the session object is returned if no error occurs
- * @return error code
- */
+/** Finds the session which is identified by the sessionId */
 UA_StatusCode UA_SessionManager_getSessionById(UA_SessionManager *sessionManager,
 UA_StatusCode UA_SessionManager_getSessionById(UA_SessionManager *sessionManager,
                                                UA_NodeId *sessionId, UA_Session **session);
                                                UA_NodeId *sessionId, UA_Session **session);
 
 
-/**
- * @param token authentication token which is used to get the session object
- * @param session output, session object which is identified by the authentication token
- * @return error code
- */
 UA_StatusCode UA_SessionManager_getSessionByToken(UA_SessionManager *sessionManager,
 UA_StatusCode UA_SessionManager_getSessionByToken(UA_SessionManager *sessionManager,
                                                   UA_NodeId *token, UA_Session **session);
                                                   UA_NodeId *token, UA_Session **session);
 
 

+ 5 - 5
src/ua_config.h.in

@@ -2,9 +2,8 @@
 #define UA_ENCODING_AMOUNT ${UA_ENCODING_AMOUNT}
 #define UA_ENCODING_AMOUNT ${UA_ENCODING_AMOUNT}
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
 
 
-#cmakedefine MULTITHREADING
-#cmakedefine MSVC
-#cmakedefine WIN32
+#cmakedefine UA_DEBUG
+#cmakedefine UA_MULTITHREADING
 
 
 /* Visibility */
 /* Visibility */
 #ifdef MSVC
 #ifdef MSVC
@@ -13,8 +12,9 @@
 #define INLINE inline
 #define INLINE inline
 #endif
 #endif
 
 
-#if defined(_WIN32) || defined(__CYGWIN__)
-  #ifdef open62541_EXPORTS
+/* Function Export */
+#ifdef _WIN32
+  #ifdef UA_DYNAMIC_LINKING
     #ifdef __GNUC__
     #ifdef __GNUC__
       #define UA_EXPORT __attribute__ ((dllexport))
       #define UA_EXPORT __attribute__ ((dllexport))
     #else
     #else

+ 0 - 21
src/ua_connection.c

@@ -1,21 +0,0 @@
-#include "ua_connection.h"
-#include "ua_util.h"
-
-UA_ConnectionConfig UA_ConnectionConfig_standard = { .protocolVersion = 0,    .sendBufferSize = 65536,
-                                                     .recvBufferSize  = 65536, .maxMessageSize = 65536,
-                                                     .maxChunkCount   = 1 };
-
-UA_StatusCode UA_Connection_init(UA_Connection *connection, UA_ConnectionConfig localConf, void *callbackHandle,
-                                 UA_Connection_closeCallback close, UA_Connection_writeCallback write) {
-    connection->state          = UA_CONNECTION_OPENING;
-    connection->localConf      = localConf;
-    connection->channel        = UA_NULL;
-    connection->callbackHandle = callbackHandle;
-    connection->close          = close;
-    connection->write          = write;
-    return UA_STATUSCODE_GOOD;
-}
-
-void UA_Connection_deleteMembers(UA_Connection *connection) {
-    UA_free(connection->callbackHandle);
-}

+ 30 - 3
src/ua_securechannel.c

@@ -1,9 +1,11 @@
-#include <time.h>
-#include <stdlib.h>
 #include "ua_securechannel.h"
 #include "ua_securechannel.h"
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 
 
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->clientAsymAlgSettings);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->clientAsymAlgSettings);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->serverAsymAlgSettings);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->serverAsymAlgSettings);
@@ -32,7 +34,7 @@ UA_Boolean UA_SecureChannel_compare(UA_SecureChannel *sc1, UA_SecureChannel *sc2
 
 
 //TODO implement real nonce generator - DUMMY function
 //TODO implement real nonce generator - DUMMY function
 UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce) {
 UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce) {
-    if(!(nonce->data = UA_alloc(1)))
+    if(!(nonce->data = UA_malloc(1)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     nonce->length  = 1;
     nonce->length  = 1;
     nonce->data[0] = 'a';
     nonce->data[0] = 'a';
@@ -54,3 +56,28 @@ UA_Int32 UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UIn
     channel->sequenceNumber++;
     channel->sequenceNumber++;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
+
+void UA_Connection_detachSecureChannel(UA_Connection *connection) {
+#ifdef UA_MULTITHREADING
+    UA_SecureChannel *channel = connection->channel;
+    if(channel)
+        uatomic_cmpxchg(&channel->connection, connection, UA_NULL);
+    uatomic_set(&connection->channel, UA_NULL);
+#else
+    if(connection->channel)
+        connection->channel->connection = UA_NULL;
+    connection->channel = UA_NULL;
+#endif
+}
+
+void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_cmpxchg(&channel->connection, UA_NULL, connection) == UA_NULL)
+        uatomic_set(&connection->channel, channel);
+#else
+    if(channel->connection != UA_NULL)
+        return;
+    channel->connection = connection;
+    connection->channel = channel;
+#endif
+}

+ 7 - 1
src/ua_securechannel.h

@@ -9,7 +9,7 @@
 /**
 /**
  *  @ingroup communication
  *  @ingroup communication
  *
  *
- *  @defgroup securechannel SecureChannel
+ * @{
  */
  */
 
 
 struct UA_Session;
 struct UA_Session;
@@ -37,4 +37,10 @@ UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce);
 UA_Int32 UA_SecureChannel_updateRequestId(UA_SecureChannel *channel, UA_UInt32 requestId);
 UA_Int32 UA_SecureChannel_updateRequestId(UA_SecureChannel *channel, UA_UInt32 requestId);
 UA_Int32 UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UInt32 sequenceNumber);
 UA_Int32 UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UInt32 sequenceNumber);
 
 
+void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel);
+void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session);
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel);
+
+/** @} */
+
 #endif /* UA_SECURECHANNEL_H_ */
 #endif /* UA_SECURECHANNEL_H_ */

+ 47 - 24
src/ua_session.c

@@ -1,10 +1,11 @@
-#include <time.h>
-#include <stdlib.h>
-
 #include "ua_session.h"
 #include "ua_session.h"
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 
 
+#ifdef UA_MULTITHREADING
+#include <urcu/uatomic.h>
+#endif
+
 UA_Session anonymousSession = {
 UA_Session anonymousSession = {
     .clientDescription =  {.applicationUri = {-1, UA_NULL},
     .clientDescription =  {.applicationUri = {-1, UA_NULL},
                            .productUri = {-1, UA_NULL},
                            .productUri = {-1, UA_NULL},
@@ -42,33 +43,17 @@ UA_Session adminSession = {
     .channel = UA_NULL};
     .channel = UA_NULL};
 
 
 UA_Session * UA_Session_new() {
 UA_Session * UA_Session_new() {
-    UA_Session *s = UA_alloc(sizeof(UA_Session));
+    UA_Session *s = UA_malloc(sizeof(UA_Session));
     if(s) UA_Session_init(s);
     if(s) UA_Session_init(s);
     return s;
     return s;
 }
 }
 
 
-/* mock up function to generate tokens for authentication */
-UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken) {
-    //Random token generation
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    srand(time(UA_NULL));
-
-    UA_Int32 i, r = 0;
+/* TODO: Nobody seems to call this function right now */
+UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken, UA_UInt32 *seed) {
     newToken->namespaceIndex = 0; // where else?
     newToken->namespaceIndex = 0; // where else?
     newToken->identifierType = UA_NODEIDTYPE_GUID;
     newToken->identifierType = UA_NODEIDTYPE_GUID;
-    newToken->identifier.guid.data1 = rand();
-    r = rand();
-    newToken->identifier.guid.data2 = (UA_UInt16)((r>>16) );
-    r = rand();
-    /* UA_Int32 r1 = (r>>16); */
-    /* UA_Int32 r2 = r1 & 0xFFFF; */
-    /* r2 = r2 * 1; */
-    newToken->identifier.guid.data3 = (UA_UInt16)((r>>16) );
-    for(i = 0;i < 8;i++) {
-        r = rand();
-        newToken->identifier.guid.data4[i] = (UA_Byte)((r>>28) );
-    }
-    return retval;
+    newToken->identifier.guid = UA_Guid_random(seed);
+    return UA_STATUSCODE_GOOD;
 }
 }
 
 
 void UA_Session_init(UA_Session *session) {
 void UA_Session_init(UA_Session *session) {
@@ -118,3 +103,41 @@ UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pend
     *pendingLifetime_ms = (session->validTill - UA_DateTime_now())/10000000; //difference in ms
     *pendingLifetime_ms = (session->validTill - UA_DateTime_now())/10000000; //difference in ms
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
+
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel) {
+#ifdef UA_MULTITHREADING
+    UA_Session *session = channel->session;
+    if(session)
+        uatomic_cmpxchg(&session->channel, channel, UA_NULL);
+    uatomic_set(&channel->session, UA_NULL);
+#else
+    if(channel->session)
+        channel->session->channel = UA_NULL;
+    channel->session = UA_NULL;
+#endif
+}
+
+void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_cmpxchg(&session->channel, UA_NULL, channel) == UA_NULL)
+        uatomic_set(&channel->session, session);
+#else
+    if(session->channel != UA_NULL)
+        return;
+    session->channel = channel;
+    channel->session = session;
+#endif
+}
+
+void UA_Session_detachSecureChannel(UA_Session *session) {
+#ifdef UA_MULTITHREADING
+    UA_SecureChannel *channel = session->channel;
+    if(channel)
+        uatomic_cmpxchg(&channel->session, session, UA_NULL);
+    uatomic_set(&session->channel, UA_NULL);
+#else
+    if(session->channel)
+        session->channel->session = UA_NULL;
+    session->channel = UA_NULL;
+#endif
+}

+ 5 - 1
src/ua_session.h

@@ -7,7 +7,7 @@
 /**
 /**
  *  @ingroup communication
  *  @ingroup communication
  *
  *
- *  @defgroup session Session
+ * @{
  */
  */
 
 
 struct UA_Session {
 struct UA_Session {
@@ -40,4 +40,8 @@ UA_StatusCode UA_Session_setExpirationDate(UA_Session *session);
 /** Gets the sessions pending lifetime (calculated from the timeout which was set) */
 /** Gets the sessions pending lifetime (calculated from the timeout which was set) */
 UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pendingLifetime);
 UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pendingLifetime);
 
 
+void UA_Session_detachSecureChannel(UA_Session *session);
+
+/** @} */
+
 #endif /* UA_SESSION_H_ */
 #endif /* UA_SESSION_H_ */

+ 13 - 6
src/ua_transport.c

@@ -1,10 +1,17 @@
-#ifdef DEBUG
+#ifdef UA_DEBUG
 #include <stdio.h>
 #include <stdio.h>
 #endif
 #endif
+#include "ua_connection.h"
 #include "ua_transport.h"
 #include "ua_transport.h"
-#include "ua_types_internal.h"
+#include "ua_types_macros.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
+// max message size is 64k
+const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard =
+    {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize  = 65536,
+     .maxMessageSize = 65536, .maxChunkCount   = 1};
+
+
 UA_TYPE_DEFAULT(UA_MessageType)
 UA_TYPE_DEFAULT(UA_MessageType)
 UA_UInt32 UA_MessageType_calcSizeBinary(UA_MessageType const *ptr) {
 UA_UInt32 UA_MessageType_calcSizeBinary(UA_MessageType const *ptr) {
     return 3 * sizeof(UA_Byte);
     return 3 * sizeof(UA_Byte);
@@ -13,9 +20,9 @@ UA_UInt32 UA_MessageType_calcSizeBinary(UA_MessageType const *ptr) {
 UA_StatusCode UA_MessageType_encodeBinary(UA_MessageType const *src, UA_ByteString *dst, UA_UInt32 *offset) {
 UA_StatusCode UA_MessageType_encodeBinary(UA_MessageType const *src, UA_ByteString *dst, UA_UInt32 *offset) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Byte tmpBuf[3];
     UA_Byte tmpBuf[3];
-    tmpBuf[0] = (UA_Byte)((((UA_Int32)*src) >> 16) );
-    tmpBuf[1] = (UA_Byte)((((UA_Int32)*src) >> 8));
-    tmpBuf[2] = (UA_Byte)(((UA_Int32)*src));
+    tmpBuf[0] = (UA_Byte)(((UA_Int32)*src) >> 16);
+    tmpBuf[1] = (UA_Byte)(((UA_Int32)*src) >> 8);
+    tmpBuf[2] = (UA_Byte)((UA_Int32)*src);
 
 
     retval |= UA_Byte_encodeBinary(&(tmpBuf[0]), dst, offset);
     retval |= UA_Byte_encodeBinary(&(tmpBuf[0]), dst, offset);
     retval |= UA_Byte_encodeBinary(&(tmpBuf[1]), dst, offset);
     retval |= UA_Byte_encodeBinary(&(tmpBuf[1]), dst, offset);
@@ -35,7 +42,7 @@ UA_StatusCode UA_MessageType_decodeBinary(UA_ByteString const *src, UA_UInt32 *o
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_MessageType_print(const UA_MessageType *p, FILE *stream) {
 void UA_MessageType_print(const UA_MessageType *p, FILE *stream) {
     UA_Byte *b = (UA_Byte *)p;
     UA_Byte *b = (UA_Byte *)p;
     fprintf(stream, "%c%c%c", b[2], b[1], b[0]);
     fprintf(stream, "%c%c%c", b[2], b[1], b[0]);

+ 69 - 61
src/ua_types.c

@@ -1,19 +1,24 @@
 #include <stdarg.h> // va_start, va_end
 #include <stdarg.h> // va_start, va_end
 #include <time.h>
 #include <time.h>
+#include <stdio.h> // printf
+#include <string.h> // strlen
+#define __USE_POSIX
+#include <stdlib.h> // malloc, free
 
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <windows.h>
 #include <windows.h>
 #else
 #else
 #include <sys/time.h>
 #include <sys/time.h>
 #endif
 #endif
 
 
-#ifdef DEBUG
+#include "ua_util.h"
+
+#ifdef UA_DEBUG
 #include <inttypes.h>
 #include <inttypes.h>
 #endif
 #endif
 
 
-#include "ua_util.h"
 #include "ua_types.h"
 #include "ua_types.h"
-#include "ua_types_internal.h"
+#include "ua_types_macros.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
@@ -27,7 +32,7 @@ UA_TYPE_DELETE_DEFAULT(UA_Boolean)
 UA_TYPE_DELETEMEMBERS_NOACTION(UA_Boolean)
 UA_TYPE_DELETEMEMBERS_NOACTION(UA_Boolean)
 UA_TYPE_NEW_DEFAULT(UA_Boolean)
 UA_TYPE_NEW_DEFAULT(UA_Boolean)
 UA_TYPE_COPY_DEFAULT(UA_Boolean)
 UA_TYPE_COPY_DEFAULT(UA_Boolean)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
 void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
     if(*p) fprintf(stream, "UA_TRUE");
     if(*p) fprintf(stream, "UA_TRUE");
     else fprintf(stream, "UA_FALSE");
     else fprintf(stream, "UA_FALSE");
@@ -36,7 +41,7 @@ void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
 
 
 /* SByte */
 /* SByte */
 UA_TYPE_DEFAULT(UA_SByte)
 UA_TYPE_DEFAULT(UA_SByte)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_SByte_print(const UA_SByte *p, FILE *stream) {
 void UA_SByte_print(const UA_SByte *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     UA_SByte x = *p;
     UA_SByte x = *p;
@@ -46,7 +51,7 @@ void UA_SByte_print(const UA_SByte *p, FILE *stream) {
 
 
 /* Byte */
 /* Byte */
 UA_TYPE_DEFAULT(UA_Byte)
 UA_TYPE_DEFAULT(UA_Byte)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Byte_print(const UA_Byte *p, FILE *stream) {
 void UA_Byte_print(const UA_Byte *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%x", *p);
     fprintf(stream, "%x", *p);
@@ -55,7 +60,7 @@ void UA_Byte_print(const UA_Byte *p, FILE *stream) {
 
 
 /* Int16 */
 /* Int16 */
 UA_TYPE_DEFAULT(UA_Int16)
 UA_TYPE_DEFAULT(UA_Int16)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
 void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
     fprintf(stream, "%d", *p);
@@ -64,7 +69,7 @@ void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
 
 
 /* UInt16 */
 /* UInt16 */
 UA_TYPE_DEFAULT(UA_UInt16)
 UA_TYPE_DEFAULT(UA_UInt16)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
 void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
     fprintf(stream, "%u", *p);
@@ -73,7 +78,7 @@ void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
 
 
 /* Int32 */
 /* Int32 */
 UA_TYPE_DEFAULT(UA_Int32)
 UA_TYPE_DEFAULT(UA_Int32)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
 void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
     fprintf(stream, "%d", *p);
@@ -83,7 +88,7 @@ void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
 
 
 /* UInt32 */
 /* UInt32 */
 UA_TYPE_DEFAULT(UA_UInt32)
 UA_TYPE_DEFAULT(UA_UInt32)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
 void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
     fprintf(stream, "%u", *p);
@@ -92,7 +97,7 @@ void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
 
 
 /* Int64 */
 /* Int64 */
 UA_TYPE_DEFAULT(UA_Int64)
 UA_TYPE_DEFAULT(UA_Int64)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
 void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%" PRIi64, *p);
     fprintf(stream, "%" PRIi64, *p);
@@ -101,7 +106,7 @@ void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
 
 
 /* UInt64 */
 /* UInt64 */
 UA_TYPE_DEFAULT(UA_UInt64)
 UA_TYPE_DEFAULT(UA_UInt64)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
 void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%" PRIu64, *p);
     fprintf(stream, "%" PRIu64, *p);
@@ -110,7 +115,7 @@ void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
 
 
 /* Float */
 /* Float */
 UA_TYPE_DEFAULT(UA_Float)
 UA_TYPE_DEFAULT(UA_Float)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Float_print(const UA_Float *p, FILE *stream) {
 void UA_Float_print(const UA_Float *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
     fprintf(stream, "%f", *p);
@@ -119,7 +124,7 @@ void UA_Float_print(const UA_Float *p, FILE *stream) {
 
 
 /* Double */
 /* Double */
 UA_TYPE_DEFAULT(UA_Double)
 UA_TYPE_DEFAULT(UA_Double)
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Double_print(const UA_Double *p, FILE *stream) {
 void UA_Double_print(const UA_Double *p, FILE *stream) {
     if(!p || !stream) return;
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
     fprintf(stream, "%f", *p);
@@ -142,7 +147,7 @@ void UA_String_deleteMembers(UA_String *p) {
 UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
 UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     UA_String_init(dst);
     UA_String_init(dst);
     if(src->length > 0) {
     if(src->length > 0) {
-        if(!(dst->data = UA_alloc(src->length)))
+        if(!(dst->data = UA_malloc(src->length)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
             return UA_STATUSCODE_BADOUTOFMEMORY;
         UA_memcpy((void *)dst->data, src->data, src->length);
         UA_memcpy((void *)dst->data, src->data, src->length);
     }
     }
@@ -150,7 +155,7 @@ UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_print(const UA_String *p, FILE *stream) {
 void UA_String_print(const UA_String *p, FILE *stream) {
     fprintf(stream, "(UA_String){%d,", p->length);
     fprintf(stream, "(UA_String){%d,", p->length);
     if(p->data)
     if(p->data)
@@ -168,9 +173,9 @@ UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
         dst->data = UA_NULL;
         dst->data = UA_NULL;
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
     }
     }
-    dst->data = UA_alloc(length);
+    dst->data = UA_malloc(length);
     if(dst->data != UA_NULL) {
     if(dst->data != UA_NULL) {
-        memcpy(dst->data, src, length);
+        UA_memcpy(dst->data, src, length);
         dst->length = length;
         dst->length = length;
     } else {
     } else {
         dst->length = -1;
         dst->length = -1;
@@ -198,7 +203,7 @@ UA_StatusCode UA_String_copyprintf(char const *fmt, UA_String *dst, ...) {
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     // since glibc 2.1 vsnprintf returns len that would have resulted if buf were large enough
     // since glibc 2.1 vsnprintf returns len that would have resulted if buf were large enough
     len = ( len > UA_STRING_COPYPRINTF_BUFSIZE ? UA_STRING_COPYPRINTF_BUFSIZE : len );
     len = ( len > UA_STRING_COPYPRINTF_BUFSIZE ? UA_STRING_COPYPRINTF_BUFSIZE : len );
-    if(!(dst->data = UA_alloc(dst->length)))
+    if(!(dst->data = UA_malloc(dst->length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     UA_memcpy((void *)dst->data, src, len);
     UA_memcpy((void *)dst->data, src, len);
     dst->length = len;
     dst->length = len;
@@ -216,14 +221,14 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printf(char const *label, const UA_String *string) {
 void UA_String_printf(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=%.*s}\n", label, string->length,
     printf("%s {Length=%d, Data=%.*s}\n", label, string->length,
            string->length, (char *)string->data);
            string->length, (char *)string->data);
 }
 }
 #endif
 #endif
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printx(char const *label, const UA_String *string) {
 void UA_String_printx(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
     if(string->length > 0) {
@@ -237,7 +242,7 @@ void UA_String_printx(char const *label, const UA_String *string) {
 }
 }
 #endif
 #endif
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_String_printx_hex(char const *label, const UA_String *string) {
 void UA_String_printx_hex(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
     if(string->length > 0) {
@@ -252,9 +257,7 @@ void UA_String_printx_hex(char const *label, const UA_String *string) {
 /* DateTime */
 /* DateTime */
 UA_TYPE_AS(UA_DateTime, UA_Int64)
 UA_TYPE_AS(UA_DateTime, UA_Int64)
 
 
-// Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
-#define FILETIME_UNIXTIME_BIAS_SEC 11644473600LL
-// Factors
+#define UNIX_EPOCH_BIAS_SEC 11644473600LL // Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 #define HUNDRED_NANOSEC_PER_USEC 10LL
 #define HUNDRED_NANOSEC_PER_USEC 10LL
 #define HUNDRED_NANOSEC_PER_SEC (HUNDRED_NANOSEC_PER_USEC * 1000000LL)
 #define HUNDRED_NANOSEC_PER_SEC (HUNDRED_NANOSEC_PER_USEC * 1000000LL)
 
 
@@ -274,13 +277,11 @@ int gettimeofday(struct timeval *tp, struct timezone *tzp) {
 }
 }
 #endif
 #endif
 
 
-// IEC 62541-6 §5.2.2.5  A DateTime value shall be encoded as a 64-bit signed integer
-// which represents the number of 100 nanosecond intervals since January 1, 1601 (UTC).
 UA_DateTime UA_DateTime_now() {
 UA_DateTime UA_DateTime_now() {
     UA_DateTime    dateTime;
     UA_DateTime    dateTime;
     struct timeval tv;
     struct timeval tv;
     gettimeofday(&tv, UA_NULL);
     gettimeofday(&tv, UA_NULL);
-    dateTime = (tv.tv_sec + FILETIME_UNIXTIME_BIAS_SEC)
+    dateTime = (tv.tv_sec + UNIX_EPOCH_BIAS_SEC)
                * HUNDRED_NANOSEC_PER_SEC + tv.tv_usec * HUNDRED_NANOSEC_PER_USEC;
                * HUNDRED_NANOSEC_PER_SEC + tv.tv_usec * HUNDRED_NANOSEC_PER_USEC;
     return dateTime;
     return dateTime;
 }
 }
@@ -288,36 +289,30 @@ UA_DateTime UA_DateTime_now() {
 UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime time) {
 UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime time) {
     UA_DateTimeStruct dateTimeStruct;
     UA_DateTimeStruct dateTimeStruct;
     //calcualting the the milli-, micro- and nanoseconds
     //calcualting the the milli-, micro- and nanoseconds
-    UA_DateTime       timeTemp;
-    timeTemp = (time-((time/10)*10))*100; //getting the last digit -> *100 for the 100 nanaseconds resolution
-    dateTimeStruct.nanoSec  = timeTemp;   //123 456 7 -> 700 nanosec;
-    timeTemp = (time-((time/10000)*10000))/10;
-    dateTimeStruct.microSec = timeTemp;   //123 456 7 -> 456 microsec
-    timeTemp = (time-((time/10000000)*10000000))/10000;
-    dateTimeStruct.milliSec = timeTemp;   //123 456 7 -> 123 millisec
+    dateTimeStruct.nanoSec = (time % 10) * 100;
+    dateTimeStruct.microSec = (time % 10000) / 10;
+    dateTimeStruct.milliSec = (time % 10000000) / 10000;
 
 
     //calculating the unix time with #include <time.h>
     //calculating the unix time with #include <time.h>
-    time_t    timeInSec = time/10000000; //converting the nanoseconds time in unixtime
-    struct tm ts;
-    ts = *gmtime(&timeInSec);
+    time_t secSinceUnixEpoch = (time/10000000) - UNIX_EPOCH_BIAS_SEC;
+    struct tm ts = *gmtime(&secSinceUnixEpoch);
     dateTimeStruct.sec    = ts.tm_sec;
     dateTimeStruct.sec    = ts.tm_sec;
     dateTimeStruct.min    = ts.tm_min;
     dateTimeStruct.min    = ts.tm_min;
     dateTimeStruct.hour   = ts.tm_hour;
     dateTimeStruct.hour   = ts.tm_hour;
     dateTimeStruct.day    = ts.tm_mday;
     dateTimeStruct.day    = ts.tm_mday;
-    dateTimeStruct.mounth = ts.tm_mon+1;
+    dateTimeStruct.month  = ts.tm_mon+1;
     dateTimeStruct.year   = ts.tm_year + 1900;
     dateTimeStruct.year   = ts.tm_year + 1900;
-
     return dateTimeStruct;
     return dateTimeStruct;
 }
 }
 
 
 UA_StatusCode UA_DateTime_toString(UA_DateTime time, UA_String *timeString) {
 UA_StatusCode UA_DateTime_toString(UA_DateTime time, UA_String *timeString) {
     // length of the string is 31 (incl. \0 at the end)
     // length of the string is 31 (incl. \0 at the end)
-    if(!(timeString->data = UA_alloc(32)))
+    if(!(timeString->data = UA_malloc(32)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     timeString->length = 31;
     timeString->length = 31;
 
 
     UA_DateTimeStruct tSt = UA_DateTime_toStruct(time);
     UA_DateTimeStruct tSt = UA_DateTime_toStruct(time);
-    sprintf((char*)timeString->data, "%2d/%2d/%4d %2d:%2d:%2d.%3d.%3d.%3d", tSt.mounth, tSt.day, tSt.year,
+    sprintf((char*)timeString->data, "%2d/%2d/%4d %2d:%2d:%2d.%3d.%3d.%3d", tSt.month, tSt.day, tSt.year,
             tSt.hour, tSt.min, tSt.sec, tSt.milliSec, tSt.microSec, tSt.nanoSec);
             tSt.hour, tSt.min, tSt.sec, tSt.milliSec, tSt.microSec, tSt.nanoSec);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
@@ -332,6 +327,19 @@ UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) {
     return UA_FALSE;
     return UA_FALSE;
 }
 }
 
 
+UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed) {
+    UA_Guid result;
+    result.data1 = rand_r(seed);
+    UA_UInt32 r = rand_r(seed);
+    result.data2 = r;
+    result.data3 = r >> 16;
+    UA_UInt32 *fake_int = (UA_UInt32*) &result.data4[0];
+    *fake_int = rand_r(seed);
+    fake_int = (UA_UInt32*) &result.data4[4];
+    *fake_int = rand_r(seed);
+    return result;
+}
+
 void UA_Guid_init(UA_Guid *p) {
 void UA_Guid_init(UA_Guid *p) {
     p->data1 = 0;
     p->data1 = 0;
     p->data2 = 0;
     p->data2 = 0;
@@ -345,7 +353,7 @@ UA_StatusCode UA_Guid_copy(UA_Guid const *src, UA_Guid *dst) {
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Guid_print(const UA_Guid *p, FILE *stream) {
 void UA_Guid_print(const UA_Guid *p, FILE *stream) {
     fprintf(stream, "(UA_Guid){%u, %u %u {%x,%x,%x,%x,%x,%x,%x,%x}}", p->data1, p->data2, p->data3, p->data4[0],
     fprintf(stream, "(UA_Guid){%u, %u %u {%x,%x,%x,%x,%x,%x,%x,%x}}", p->data1, p->data2, p->data3, p->data4[0],
             p->data4[1], p->data4[2], p->data4[3], p->data4[4], p->data4[5], p->data4[6], p->data4[7]);
             p->data4[1], p->data4[2], p->data4[3], p->data4[4], p->data4[5], p->data4[6], p->data4[7]);
@@ -358,19 +366,19 @@ UA_Boolean UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString
     return UA_String_equal((const UA_String *)string1, (const UA_String *)string2);
     return UA_String_equal((const UA_String *)string1, (const UA_String *)string2);
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printf(char *label, const UA_ByteString *string) {
 void UA_ByteString_printf(char *label, const UA_ByteString *string) {
     UA_String_printf(label, (UA_String *)string);
     UA_String_printf(label, (UA_String *)string);
 }
 }
 #endif
 #endif
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printx(char *label, const UA_ByteString *string) {
 void UA_ByteString_printx(char *label, const UA_ByteString *string) {
     UA_String_printx(label, (UA_String *)string);
     UA_String_printx(label, (UA_String *)string);
 }
 }
 #endif
 #endif
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ByteString_printx_hex(char *label, const UA_ByteString *string) {
 void UA_ByteString_printx_hex(char *label, const UA_ByteString *string) {
     UA_String_printx_hex(label, (UA_String *)string);
     UA_String_printx_hex(label, (UA_String *)string);
 }
 }
@@ -384,7 +392,7 @@ UA_ByteString UA_ByteString_securityPoliceNone =
 /** Creates a ByteString of the indicated length. The content is not set to zero. */
 /** Creates a ByteString of the indicated length. The content is not set to zero. */
 UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
 UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
     if(length > 0) {
     if(length > 0) {
-        if(!(p->data = UA_alloc(length)))
+        if(!(p->data = UA_malloc(length)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
             return UA_STATUSCODE_BADOUTOFMEMORY;
         p->length = length;
         p->length = length;
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -465,7 +473,7 @@ void UA_NodeId_deleteMembers(UA_NodeId *p) {
     }
     }
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_NodeId_print(const UA_NodeId *p, FILE *stream) {
 void UA_NodeId_print(const UA_NodeId *p, FILE *stream) {
     fprintf(stream, "(UA_NodeId){");
     fprintf(stream, "(UA_NodeId){");
     switch(p->identifierType) {
     switch(p->identifierType) {
@@ -593,7 +601,7 @@ UA_StatusCode UA_ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNo
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ExpandedNodeId_print(const UA_ExpandedNodeId *p, FILE *stream) {
 void UA_ExpandedNodeId_print(const UA_ExpandedNodeId *p, FILE *stream) {
     fprintf(stream, "(UA_ExpandedNodeId){");
     fprintf(stream, "(UA_ExpandedNodeId){");
     UA_NodeId_print(&p->nodeId, stream);
     UA_NodeId_print(&p->nodeId, stream);
@@ -638,7 +646,7 @@ UA_StatusCode UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *ds
     return UA_String_copycstring(src, &dst->name);
     return UA_String_copycstring(src, &dst->name);
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
 void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
     fprintf(stream, "(UA_QualifiedName){");
     fprintf(stream, "(UA_QualifiedName){");
     UA_UInt16_print(&p->namespaceIndex, stream);
     UA_UInt16_print(&p->namespaceIndex, stream);
@@ -648,7 +656,7 @@ void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
 }
 }
 #endif
 #endif
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn) {
 void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn) {
     printf("%s {NamespaceIndex=%u, Length=%d, Data=%.*s}\n", label, qn->namespaceIndex,
     printf("%s {NamespaceIndex=%u, Length=%d, Data=%.*s}\n", label, qn->namespaceIndex,
            qn->name.length, qn->name.length, (char *)qn->name.data);
            qn->name.length, qn->name.length, (char *)qn->name.data);
@@ -686,7 +694,7 @@ UA_StatusCode UA_LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedTex
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_LocalizedText_print(const UA_LocalizedText *p, FILE *stream) {
 void UA_LocalizedText_print(const UA_LocalizedText *p, FILE *stream) {
     fprintf(stream, "(UA_LocalizedText){");
     fprintf(stream, "(UA_LocalizedText){");
     UA_String_print(&p->locale, stream);
     UA_String_print(&p->locale, stream);
@@ -719,7 +727,7 @@ UA_StatusCode UA_ExtensionObject_copy(UA_ExtensionObject const *src, UA_Extensio
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_ExtensionObject_print(const UA_ExtensionObject *p, FILE *stream) {
 void UA_ExtensionObject_print(const UA_ExtensionObject *p, FILE *stream) {
     fprintf(stream, "(UA_ExtensionObject){");
     fprintf(stream, "(UA_ExtensionObject){");
     UA_NodeId_print(&p->typeId, stream);
     UA_NodeId_print(&p->typeId, stream);
@@ -764,7 +772,7 @@ UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_DataValue_print(const UA_DataValue *p, FILE *stream) {
 void UA_DataValue_print(const UA_DataValue *p, FILE *stream) {
     fprintf(stream, "(UA_DataValue){");
     fprintf(stream, "(UA_DataValue){");
     UA_Byte_print(&p->encodingMask, stream);
     UA_Byte_print(&p->encodingMask, stream);
@@ -887,7 +895,7 @@ UA_StatusCode UA_Variant_copySetArray(UA_Variant *v, const UA_TypeVTable *vt, UA
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Variant_print(const UA_Variant *p, FILE *stream) {
 void UA_Variant_print(const UA_Variant *p, FILE *stream) {
     UA_UInt32 ns0id = UA_ns0ToVTableIndex(&p->vt->typeId);
     UA_UInt32 ns0id = UA_ns0ToVTableIndex(&p->vt->typeId);
     if(p->storageType == UA_VARIANT_DATASOURCE) {
     if(p->storageType == UA_VARIANT_DATASOURCE) {
@@ -939,7 +947,7 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
     dst->encodingMask = src->encodingMask;
     dst->encodingMask = src->encodingMask;
     retval |= UA_StatusCode_copy(&src->innerStatusCode, &dst->innerStatusCode);
     retval |= UA_StatusCode_copy(&src->innerStatusCode, &dst->innerStatusCode);
     if((src->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) && src->innerDiagnosticInfo) {
     if((src->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) && src->innerDiagnosticInfo) {
-        if((dst->innerDiagnosticInfo = UA_alloc(sizeof(UA_DiagnosticInfo))))
+        if((dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo))))
             retval |= UA_DiagnosticInfo_copy(src->innerDiagnosticInfo, dst->innerDiagnosticInfo);
             retval |= UA_DiagnosticInfo_copy(src->innerDiagnosticInfo, dst->innerDiagnosticInfo);
         else
         else
             retval |= UA_STATUSCODE_BADOUTOFMEMORY;
             retval |= UA_STATUSCODE_BADOUTOFMEMORY;
@@ -955,7 +963,7 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_DiagnosticInfo_print(const UA_DiagnosticInfo *p, FILE *stream) {
 void UA_DiagnosticInfo_print(const UA_DiagnosticInfo *p, FILE *stream) {
     fprintf(stream, "(UA_DiagnosticInfo){");
     fprintf(stream, "(UA_DiagnosticInfo){");
     UA_Byte_print(&p->encodingMask, stream);
     UA_Byte_print(&p->encodingMask, stream);
@@ -1002,7 +1010,7 @@ UA_InvalidType * UA_InvalidType_new() {
     return UA_NULL;
     return UA_NULL;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
 void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
     fprintf(stream, "(UA_InvalidType){ERROR (invalid type)}");
     fprintf(stream, "(UA_InvalidType){ERROR (invalid type)}");
 }
 }
@@ -1025,7 +1033,7 @@ UA_StatusCode UA_Array_new(void **p, UA_Int32 noElements, const UA_TypeVTable *v
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     }
 
 
-    if(!(*p = UA_alloc(vt->memSize * noElements)))
+    if(!(*p = UA_malloc(vt->memSize * noElements)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
     UA_Array_init(*p, noElements, vt);
     UA_Array_init(*p, noElements, vt);
@@ -1076,7 +1084,7 @@ UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeV
     return retval;
     return retval;
 }
 }
 
 
-#ifdef DEBUG
+#ifdef UA_DEBUG
 void UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream) {
 void UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream) {
     fprintf(stream, "(%s){", vt->name);
     fprintf(stream, "(%s){", vt->name);
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic

+ 4 - 10
src/ua_types_encoding_binary.c

@@ -1,3 +1,4 @@
+#include <string.h>
 #include "ua_types_encoding_binary.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
@@ -290,15 +291,8 @@ UA_TYPE_DECODEBINARY(UA_Double,
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+4] & 0xFF); // bits 32-39
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+4] & 0xFF); // bits 32-39
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+5] & 0xFF); // bits 40-47
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+5] & 0xFF); // bits 40-47
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+6] & 0x0F); // bits 48-51
                      mantissa = (mantissa / 256.0 ) + (UA_Double)(src->data[*offset+6] & 0x0F); // bits 48-51
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - mantissa=%f\n", mantissa));
                      biasedExponent  = (src->data[*offset+6] & 0xF0) >>  4; // bits 52-55
                      biasedExponent  = (src->data[*offset+6] & 0xF0) >>  4; // bits 52-55
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - biasedExponent52-55=%d, src=%d\n",
-                                        biasedExponent,
-                                        src->data[*offset+6]));
                      biasedExponent |= ((UA_UInt32)(src->data[*offset+7] & 0x7F)) <<  4; // bits 56-62
                      biasedExponent |= ((UA_UInt32)(src->data[*offset+7] & 0x7F)) <<  4; // bits 56-62
-                     DBG_VERBOSE(printf("UA_Double_decodeBinary - biasedExponent56-62=%d, src=%d\n",
-                                        biasedExponent,
-                                        src->data[*offset+7]));
                      sign = ( src->data[*offset+7] & 0x80 ) ? -1.0 : 1.0; // bit 63
                      sign = ( src->data[*offset+7] & 0x80 ) ? -1.0 : 1.0; // bit 63
                      if(biasedExponent >= 1023)
                      if(biasedExponent >= 1023)
                          *dst = (UA_Double)sign * (1 << (biasedExponent-1023)) * (1.0 + mantissa / 8.0 );
                          *dst = (UA_Double)sign * (1 << (biasedExponent-1023)) * (1.0 + mantissa / 8.0 );
@@ -339,7 +333,7 @@ UA_StatusCode UA_String_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset
     if(length > (UA_Int32)(src->length - *offset))
     if(length > (UA_Int32)(src->length - *offset))
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     
     
-    if(!(dst->data = UA_alloc(length)))
+    if(!(dst->data = UA_malloc(length)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
     UA_memcpy(dst->data, &src->data[*offset], length);
     UA_memcpy(dst->data, &src->data[*offset], length);
@@ -907,7 +901,7 @@ UA_StatusCode UA_Variant_decodeBinary(UA_ByteString const *src, UA_UInt32 *offse
     const UA_TypeVTable *vt = &UA_TYPES[typeNs0Id];
     const UA_TypeVTable *vt = &UA_TYPES[typeNs0Id];
 
 
     if(!isArray) {
     if(!isArray) {
-        if(!(data->dataPtr = UA_alloc(vt->memSize)))
+        if(!(data->dataPtr = UA_malloc(vt->memSize)))
             return UA_STATUSCODE_BADOUTOFMEMORY;
             return UA_STATUSCODE_BADOUTOFMEMORY;
         retval |= vt->encodings[UA_ENCODING_BINARY].decode(src, offset, data->dataPtr);
         retval |= vt->encodings[UA_ENCODING_BINARY].decode(src, offset, data->dataPtr);
         if(retval) {
         if(retval) {
@@ -999,7 +993,7 @@ UA_StatusCode UA_DiagnosticInfo_decodeBinary(UA_ByteString const *src, UA_UInt32
         retval |= UA_StatusCode_decodeBinary(src, offset, &dst->innerStatusCode);
         retval |= UA_StatusCode_decodeBinary(src, offset, &dst->innerStatusCode);
     if(dst->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) {
     if(dst->encodingMask & UA_DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO) {
         // innerDiagnosticInfo is a pointer to struct, therefore allocate
         // innerDiagnosticInfo is a pointer to struct, therefore allocate
-        if((dst->innerDiagnosticInfo = UA_alloc(sizeof(UA_DiagnosticInfo)))) {
+        if((dst->innerDiagnosticInfo = UA_malloc(sizeof(UA_DiagnosticInfo)))) {
             if(UA_DiagnosticInfo_decodeBinary(src, offset, dst->innerDiagnosticInfo) != UA_STATUSCODE_GOOD) {
             if(UA_DiagnosticInfo_decodeBinary(src, offset, dst->innerDiagnosticInfo) != UA_STATUSCODE_GOOD) {
                 UA_free(dst->innerDiagnosticInfo);
                 UA_free(dst->innerDiagnosticInfo);
                 dst->innerDiagnosticInfo = UA_NULL;
                 dst->innerDiagnosticInfo = UA_NULL;

+ 22 - 32
src/ua_types_encoding_binary.h

@@ -5,40 +5,34 @@
 
 
 /**
 /**
  * @ingroup types
  * @ingroup types
- * @defgroup encoding Datatype Encoding
  *
  *
- * @brief Datatypes can have several encodings. The methods defined for
- * encodings and their signature are fixed. When supplied with an inappropriate
- * null-pointer, these functions _will crash_. Exceptions are documented for the
- * individual functions.
+ * @defgroup encoding_binary Binary Encoding
+ *
+ * @brief Functions for binary en- and decoding of built-in datatypes as defined
+ * in the standard. The encoding of the remaining datatypes is autogenerated
+ * from XML descriptions.
  *
  *
- * - CalcSize: Returns the size of the (encoded) variable in bytes. This
- *   function is mainly used to allocate the bytestring into which the encoding
- *   is done.
+ * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
+ * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
  *
  *
- * - Encode: Encodes a variable into a bytestring. If an error occurs
+ * - _calcSize: Returns the size of the (encoded) variable in bytes. This length
+ *   is used to allocate the bytestring for later encoding.
+ *
+ * - _encode: Encodes a variable into a bytestring. If an error occurs
  *   (indicated by the return value), the bytestring may be left in an
  *   (indicated by the return value), the bytestring may be left in an
  *   inconsistent state.
  *   inconsistent state.
  *
  *
- * - Decode: Decodes a variable stored in a bytestring. The destination is
+ * - _decode: Decodes a variable stored in a bytestring. The destination is
  *   cleaned up (init) before decoding into it. If an error occurs (indicated by
  *   cleaned up (init) before decoding into it. If an error occurs (indicated by
  *   the return value), the destination is cleaned up (deleteMembers, but no
  *   the return value), the destination is cleaned up (deleteMembers, but no
  *   init) before returning.
  *   init) before returning.
- */
-
-/**
- * @ingroup encoding
- * @defgroup encoding_binary Binary Encoding
- *
- * @brief Functions for binary en- and decoding of built-in datatypes as defined
- * in the standard.
  *
  *
  * @{
  * @{
  */
  */
 
 
-#define UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)       \
+#define UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)        \
     UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p) {    \
     UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p) {    \
-        return TYPE_AS##_calcSizeBinary((TYPE_AS *)p); \
+        return TYPE_AS##_calcSizeBinary((TYPE_AS *)p);  \
     }
     }
 
 
 #define UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)                          \
 #define UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)                          \
@@ -46,17 +40,17 @@
         return TYPE_AS##_encodeBinary((TYPE_AS *)src, dst, offset);     \
         return TYPE_AS##_encodeBinary((TYPE_AS *)src, dst, offset);     \
     }
     }
 
 
-#define UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)                                             \
+#define UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)                          \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst) { \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst) { \
-        return TYPE_AS##_decodeBinary(src, offset, (TYPE_AS *)dst);                        \
+        return TYPE_AS##_decodeBinary(src, offset, (TYPE_AS *)dst);     \
     }
     }
 #define UA_TYPE_BINARY_ENCODING_AS(TYPE, TYPE_AS) \
 #define UA_TYPE_BINARY_ENCODING_AS(TYPE, TYPE_AS) \
     UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)      \
     UA_TYPE_CALCSIZEBINARY_AS(TYPE, TYPE_AS)      \
     UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)        \
     UA_TYPE_ENCODEBINARY_AS(TYPE, TYPE_AS)        \
     UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)
     UA_TYPE_DECODEBINARY_AS(TYPE, TYPE_AS)
 
 
-#define UA_TYPE_BINARY_ENCODING(TYPE)                                                     \
-    UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p);                                        \
+#define UA_TYPE_BINARY_ENCODING(TYPE)                                   \
+    UA_UInt32 TYPE##_calcSizeBinary(TYPE const *p);                     \
     UA_StatusCode TYPE##_encodeBinary(TYPE const *src, UA_ByteString *dst, UA_UInt32 *offset); \
     UA_StatusCode TYPE##_encodeBinary(TYPE const *src, UA_ByteString *dst, UA_UInt32 *offset); \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst);
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, UA_UInt32 *offset, TYPE *dst);
 
 
@@ -86,21 +80,17 @@ UA_TYPE_BINARY_ENCODING(UA_DataValue)
 UA_TYPE_BINARY_ENCODING(UA_Variant)
 UA_TYPE_BINARY_ENCODING(UA_Variant)
 UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
 UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
 
 
-/* Not built-in types */
+/* Not defined in the standard */
 UA_TYPE_BINARY_ENCODING(UA_InvalidType)
 UA_TYPE_BINARY_ENCODING(UA_InvalidType)
 
 
-/*********/
-/* Array */
-/*********/
-
-/* Computes the size of an array (incl. length field) in a binary blob. */
+/** Computes the size of an array (incl. length field) in a binary blob. */
 UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_TypeVTable *vt, const void *data);
 UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_TypeVTable *vt, const void *data);
 
 
-/* @brief Encodes an array into a binary blob. The array size is printed as well. */
+/** Encodes an array into a binary blob. The array size is printed as an int32 before the actual content. */
 UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
 UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
                                     UA_ByteString *dst, UA_UInt32 *offset);
                                     UA_ByteString *dst, UA_UInt32 *offset);
 
 
-/* @brief Decodes an array from a binary blob. The array is allocated automatically before decoding. */
+/** Decodes an array from a binary blob. The array is allocated automatically before decoding. */
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
                                     const UA_TypeVTable *vt, void **dst);
                                     const UA_TypeVTable *vt, void **dst);
 
 

+ 5 - 5
src/ua_types_internal.h

@@ -1,5 +1,5 @@
-#ifndef UA_TYPES_INTERNAL_H_
-#define UA_TYPES_INTERNAL_H_
+#ifndef UA_TYPES_MACROS_H_
+#define UA_TYPES_MACROS_H_
 
 
 /* Macros for type implementations */
 /* Macros for type implementations */
 
 
@@ -12,7 +12,7 @@
     
     
 #define UA_TYPE_NEW_DEFAULT(TYPE)                             \
 #define UA_TYPE_NEW_DEFAULT(TYPE)                             \
     TYPE * TYPE##_new() {                                     \
     TYPE * TYPE##_new() {                                     \
-        TYPE *p = UA_alloc(sizeof(TYPE));                     \
+        TYPE *p = UA_malloc(sizeof(TYPE));                     \
         if(p) TYPE##_init(p);                                 \
         if(p) TYPE##_init(p);                                 \
         return p;                                             \
         return p;                                             \
     }
     }
@@ -56,7 +56,7 @@
         return TYPE_AS##_copy((TYPE_AS *)src, (TYPE_AS *)dst); \
         return TYPE_AS##_copy((TYPE_AS *)src, (TYPE_AS *)dst); \
     }
     }
 
 
-#ifdef DEBUG //print functions only in debug mode
+#ifdef UA_DEBUG //print functions only in debug mode
 #define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)              \
 #define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)              \
     void TYPE##_print(TYPE const *p, FILE *stream) { \
     void TYPE##_print(TYPE const *p, FILE *stream) { \
         TYPE_AS##_print((TYPE_AS *)p, stream);       \
         TYPE_AS##_print((TYPE_AS *)p, stream);       \
@@ -73,4 +73,4 @@
     UA_TYPE_COPY_AS(TYPE, TYPE_AS)          \
     UA_TYPE_COPY_AS(TYPE, TYPE_AS)          \
     UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
     UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
 
 
-#endif /* UA_TYPES_INTERNAL_H_ */
+#endif /* UA_TYPES_MACROS_H_ */

+ 24 - 9
src/ua_util.c

@@ -1,13 +1,28 @@
 #include "ua_util.h"
 #include "ua_util.h"
 
 
+#define __USE_POSIX
+#include <stdlib.h> // malloc, free
+#include <string.h> // memcpy
+
 /* the extern inline in a *.c-file is required for other compilation units to
 /* the extern inline in a *.c-file is required for other compilation units to
    see the inline function. */
    see the inline function. */
-extern INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size);
-
-#ifdef DEBUG
-extern INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l);
-extern INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line);
-#else
-extern INLINE void _UA_free(void *ptr);
-extern INLINE void * _UA_alloc(UA_Int32 size);
-#endif
+
+void UA_free(void *ptr) {
+    free(ptr); // checks if ptr != UA_NULL in the background
+}
+
+void * UA_malloc(UA_UInt32 size) {
+    return malloc(size);
+}
+
+void * UA_realloc(void *ptr, UA_UInt32 size) {
+    return realloc(ptr, size);
+}
+
+void UA_memcpy(void *dst, void const *src, UA_UInt32 size) {
+    memcpy(dst, src, size);
+}
+
+void * UA_memset(void *ptr, UA_Int32 value, UA_UInt32 size) {
+    return memset(ptr, value, size);
+}

+ 9 - 59
src/ua_util.h

@@ -1,15 +1,9 @@
 #ifndef UA_UTIL_H_
 #ifndef UA_UTIL_H_
 #define UA_UTIL_H_
 #define UA_UTIL_H_
 
 
-#include <stdio.h>  // printf
-#include <stdlib.h> // malloc, free
-#include <string.h> // memcpy
 #include <assert.h> // assert
 #include <assert.h> // assert
 
 
-#include "ua_config.h"
-
 #include <stddef.h> /* Needed for sys/queue.h */
 #include <stddef.h> /* Needed for sys/queue.h */
-
 #ifndef WIN32
 #ifndef WIN32
 #include <sys/queue.h>
 #include <sys/queue.h>
 #include <alloca.h>
 #include <alloca.h>
@@ -24,67 +18,23 @@
 #define UA_TRUE (42 == 42)
 #define UA_TRUE (42 == 42)
 #define UA_FALSE (!UA_TRUE)
 #define UA_FALSE (!UA_TRUE)
 
 
-//identifier numbers are different for XML and binary, so we have to substract an offset for comparison
+// subtract from nodeids to get from the encoding to the content
 #define UA_ENCODINGOFFSET_XML 1
 #define UA_ENCODINGOFFSET_XML 1
 #define UA_ENCODINGOFFSET_BINARY 2
 #define UA_ENCODINGOFFSET_BINARY 2
 
 
-/* Debug macros */
-#define DBG_VERBOSE(expression) // omit debug code
-#define DBG_ERR(expression)     // omit debug code
-#define DBG(expression)         // omit debug code
-#if defined(DEBUG)              // --enable-debug=(yes|verbose)
-# undef DBG
-# define DBG(expression) expression
-# undef DBG_ERR
-# define DBG_ERR(expression) expression
-# if defined(VERBOSE)   // --enable-debug=verbose
-#  undef DBG_VERBOSE
-#  define DBG_VERBOSE(expression) expression
-# endif
-#endif
-
-#ifdef DEBUG
 #define UA_assert(ignore) assert(ignore)
 #define UA_assert(ignore) assert(ignore)
-#else
-#define UA_assert(ignore)
-#endif
-
-/* Heap memory functions */
-#ifdef DEBUG
-#define UA_free(ptr) _UA_free(ptr, # ptr, __FILE__, __LINE__)
-INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l) {
-    DBG_VERBOSE(printf("UA_free;%p;;%s;;%s;%d\n", ptr, pname, f, l); fflush(stdout));
-    free(ptr); // checks if ptr != UA_NULL in the background
-}
-#else
-#define UA_free(ptr) _UA_free(ptr)
-INLINE void _UA_free(void *ptr) {
-    free(ptr); // checks if ptr != UA_NULL in the background
-}
-#endif
-
-#ifdef DEBUG
-#define UA_alloc(size) _UA_alloc(size, __FILE__, __LINE__) 
-INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line) {
-    DBG_VERBOSE(printf("UA_alloc - %d;%s;%d\n", size, file, line); fflush(stdout));
-    return malloc(size);
-}
-#else
-#define UA_alloc(size) _UA_alloc(size) 
-INLINE void * _UA_alloc(UA_Int32 size) {
-    return malloc(size);
-}
-#endif
 
 
-INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size) {
-    DBG_VERBOSE(printf("UA_memcpy - %p;%p;%d\n", dst, src, size));
-    memcpy(dst, src, size);
-}
+// these are inlined for release builds
+void UA_free(void *ptr);
+void * UA_malloc(UA_UInt32 size);
+void * UA_realloc(void *ptr, UA_UInt32 size);
+void UA_memcpy(void *dst, void const *src, UA_UInt32 size);
+void * UA_memset(void *ptr, UA_Int32 value, UA_UInt32 size);
 
 
 #ifdef WIN32
 #ifdef WIN32
-#define UA_alloca(size) _alloca(size)
+#define UA_alloca(SIZE) _alloca(SIZE)
 #else
 #else
-#define UA_alloca(size) alloca(size)
+#define UA_alloca(SIZE) alloca(SIZE)
 #endif
 #endif
 
 
 #endif /* UA_UTIL_H_ */
 #endif /* UA_UTIL_H_ */

+ 1 - 1
tests/CMakeLists.txt

@@ -6,7 +6,7 @@ if(NOT WIN32)
     list(APPEND LIBS rt pthread)
     list(APPEND LIBS rt pthread)
 endif(NOT WIN32)
 endif(NOT WIN32)
 if(MULTITHREADING)
 if(MULTITHREADING)
-    list(APPEND LIBS urcu-cds urcu)
+    list(APPEND LIBS urcu-cds urcu urcu-common)
 endif(MULTITHREADING)
 endif(MULTITHREADING)
 
 
 # the unit test are built directly on the open62541 object files. so they can
 # the unit test are built directly on the open62541 object files. so they can

+ 5 - 5
tests/check_builtin.c

@@ -1117,7 +1117,7 @@ START_TEST(UA_DataValue_encodeShallWorkOnExampleWithVariant) {
 END_TEST
 END_TEST
 START_TEST(UA_DateTime_toStructShallWorkOnExample) {
 START_TEST(UA_DateTime_toStructShallWorkOnExample) {
 	// given
 	// given
-	UA_DateTime src = 13974671891234567;
+	UA_DateTime src = 13974671891234567 + (11644473600 * 10000000); // ua counts since 1601, unix since 1970
 	//1397467189... is Mon, 14 Apr 2014 09:19:49 GMT
 	//1397467189... is Mon, 14 Apr 2014 09:19:49 GMT
 	//...1234567 are the milli-, micro- and nanoseconds
 	//...1234567 are the milli-, micro- and nanoseconds
 	UA_DateTimeStruct dst;
 	UA_DateTimeStruct dst;
@@ -1134,7 +1134,7 @@ START_TEST(UA_DateTime_toStructShallWorkOnExample) {
 	ck_assert_int_eq(dst.hour, 9);
 	ck_assert_int_eq(dst.hour, 9);
 
 
 	ck_assert_int_eq(dst.day, 14);
 	ck_assert_int_eq(dst.day, 14);
-	ck_assert_int_eq(dst.mounth, 4);
+	ck_assert_int_eq(dst.month, 4);
 	ck_assert_int_eq(dst.year, 2014);
 	ck_assert_int_eq(dst.year, 2014);
 }
 }
 END_TEST
 END_TEST
@@ -1195,7 +1195,7 @@ START_TEST(UA_Array_copyByteArrayShallWorkOnExample) {
 	UA_Byte  *dstArray;
 	UA_Byte  *dstArray;
 	UA_Int32  size = 5;
 	UA_Int32  size = 5;
 	UA_Int32  i    = 0;
 	UA_Int32  i    = 0;
-	testString.data = UA_alloc(size);
+	testString.data = UA_malloc(size);
 	testString.data[0] = 'O';
 	testString.data[0] = 'O';
 	testString.data[1] = 'P';
 	testString.data[1] = 'P';
 	testString.data[2] = 'C';
 	testString.data[2] = 'C';
@@ -1405,7 +1405,7 @@ START_TEST(UA_Variant_copyShallWorkOnSingleValueExample) {
 	UA_Variant value, copiedValue;
 	UA_Variant value, copiedValue;
 	UA_Variant_init(&value);
 	UA_Variant_init(&value);
 	UA_Variant_init(&copiedValue);
 	UA_Variant_init(&copiedValue);
-	value.storage.data.dataPtr = UA_alloc(sizeof(UA_String));
+	value.storage.data.dataPtr = UA_malloc(sizeof(UA_String));
 	*((UA_String*)value.storage.data.dataPtr) = testString;
 	*((UA_String*)value.storage.data.dataPtr) = testString;
 	value.vt = &UA_TYPES[UA_STRING];
 	value.vt = &UA_TYPES[UA_STRING];
 	value.storage.data.arrayLength = 1;
 	value.storage.data.arrayLength = 1;
@@ -1437,7 +1437,7 @@ START_TEST(UA_Variant_copyShallWorkOn1DArrayExample) {
 	UA_String_copycstring("opc ua", &srcArray[2]);
 	UA_String_copycstring("opc ua", &srcArray[2]);
 
 
 	UA_Int32 *dimensions;
 	UA_Int32 *dimensions;
-	dimensions = UA_alloc(sizeof(UA_Int32));
+	dimensions = UA_malloc(sizeof(UA_Int32));
 	dimensions[0] = 3;
 	dimensions[0] = 3;
 
 
 	UA_Variant value, copiedValue;
 	UA_Variant value, copiedValue;

+ 4 - 4
tests/check_memory.c

@@ -114,14 +114,14 @@ START_TEST(decodeScalarBasicTypeFromRandomBufferShallSucceed) {
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
 	UA_Int32 buflen = 256;
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
-#ifdef WIN32
+#ifdef _WIN32
 	srand(42);
 	srand(42);
 #else
 #else
 	srandom(42);
 	srandom(42);
 #endif
 #endif
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
-#ifdef WIN32
+#ifdef _WIN32
 			UA_UInt32 rnd;
 			UA_UInt32 rnd;
 			rnd = rand();
 			rnd = rand();
 			msg1.data[i] = rnd;
 			msg1.data[i] = rnd;
@@ -147,7 +147,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 retval = UA_STATUSCODE_GOOD;
 	UA_Int32 buflen = 256;
 	UA_Int32 buflen = 256;
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
 	UA_ByteString_newMembers(&msg1, buflen); // fixed size
-#ifdef WIN32
+#ifdef _WIN32
 	srand(42);
 	srand(42);
 #else
 #else
 	srandom(42);
 	srandom(42);
@@ -155,7 +155,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
 	// when
 	// when
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 	for(int n = 0;n < RANDOM_TESTS;n++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
 		for(UA_Int32 i = 0;i < buflen;i++) {
-#ifdef WIN32
+#ifdef _WIN32
 			UA_UInt32 rnd;
 			UA_UInt32 rnd;
 			rnd = rand();
 			rnd = rand();
 			msg1.data[i] = rnd;
 			msg1.data[i] = rnd;

+ 19 - 19
tests/check_nodestore.c

@@ -7,7 +7,7 @@
 #include "ua_util.h"
 #include "ua_util.h"
 #include "check.h"
 #include "check.h"
 
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 #include <pthread.h>
 #include <pthread.h>
 #include <urcu.h>
 #include <urcu.h>
 #endif
 #endif
@@ -60,7 +60,7 @@ START_TEST(replaceNonExistingNode) {
 END_TEST
 END_TEST
 
 
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -74,14 +74,14 @@ START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 	UA_NodeStore_release(n1);
 	UA_NodeStore_release(n1);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(failToFindNodeInOtherUA_NodeStore) {
 START_TEST(failToFindNodeInOtherUA_NodeStore) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -98,14 +98,14 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
 	// finally
 	// finally
 	UA_Node_delete(n);
 	UA_Node_delete(n);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -131,14 +131,14 @@ START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 	UA_NodeStore_release(n3);
 	UA_NodeStore_release(n3);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -165,14 +165,14 @@ START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 	ck_assert_int_eq(visitCnt, 6);
 	ck_assert_int_eq(visitCnt, 6);
 	// finally
 	// finally
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(findNodeInExpandedNamespace) {
 START_TEST(findNodeInExpandedNamespace) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -192,14 +192,14 @@ START_TEST(findNodeInExpandedNamespace) {
 	UA_free((void*)n);
 	UA_free((void*)n);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
 START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -219,14 +219,14 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
 	ck_assert_int_eq(visitCnt, 200);
 	ck_assert_int_eq(visitCnt, 200);
 	// finally
 	// finally
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
 END_TEST
 END_TEST
 
 
 START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
@@ -250,7 +250,7 @@ START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 	// finally
 	// finally
 	UA_free((void *)n6);
 	UA_free((void *)n6);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }
@@ -260,7 +260,7 @@ END_TEST
 /* Performance Profiling Test Cases */
 /* Performance Profiling Test Cases */
 /************************************/
 /************************************/
 
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 struct UA_NodeStoreProfileTest {
 struct UA_NodeStoreProfileTest {
 	UA_NodeStore *ns;
 	UA_NodeStore *ns;
 	UA_Int32 min_val;
 	UA_Int32 min_val;
@@ -290,7 +290,7 @@ void *profileGetThread(void *arg) {
 #endif
 #endif
 
 
 START_TEST(profileGetDelete) {
 START_TEST(profileGetDelete) {
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 
 
@@ -304,7 +304,7 @@ START_TEST(profileGetDelete) {
 	}
 	}
 	clock_t begin, end;
 	clock_t begin, end;
 	begin = clock();
 	begin = clock();
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 #define THREADS 4
 #define THREADS 4
     pthread_t t[THREADS];
     pthread_t t[THREADS];
 	struct UA_NodeStoreProfileTest p[THREADS];
 	struct UA_NodeStoreProfileTest p[THREADS];
@@ -333,7 +333,7 @@ START_TEST(profileGetDelete) {
 
 
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
 
 
-#ifdef MULTITHREADING
+#ifdef UA_MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
 #endif
 #endif
 }
 }

+ 1 - 1
tests/check_stack.c

@@ -58,7 +58,7 @@ UA_Int32 closerCallback(UA_Connection *connection) {
 UA_Int32 stackTestFixture_create(UA_Connection_writeCallback write, UA_Connection_closeCallback close) {
 UA_Int32 stackTestFixture_create(UA_Connection_writeCallback write, UA_Connection_closeCallback close) {
 	UA_UInt32 fixtureHandle = stackTestFixture_getAndMarkFreeHandle();
 	UA_UInt32 fixtureHandle = stackTestFixture_getAndMarkFreeHandle();
 	stackTestFixture *fixture = fixtures[fixtureHandle];
 	stackTestFixture *fixture = fixtures[fixtureHandle];
-	UA_alloc((void**)&fixture->connection, sizeof(UA_Connection));
+	UA_malloc((void**)&fixture->connection, sizeof(UA_Connection));
 	fixture->respMsg.data   = fixture->respMsgBuffer;
 	fixture->respMsg.data   = fixture->respMsgBuffer;
 	fixture->respMsg.length = 0;
 	fixture->respMsg.length = 0;
 	UA_Connection_init(fixture->connection, UA_ConnectionConfig_standard, fixture, close, write);
 	UA_Connection_init(fixture->connection, UA_ConnectionConfig_standard, fixture, close, write);

+ 87 - 64
tools/generate_builtin.py

@@ -9,6 +9,12 @@ from lxml import etree
 import inspect
 import inspect
 import argparse
 import argparse
 
 
+#####################
+# Utility functions #
+#####################
+
+print(sys.argv)
+
 parser = argparse.ArgumentParser()
 parser = argparse.ArgumentParser()
 parser.add_argument('--export-prototypes', action='store_true', help='make the prototypes (init, delete, copy, ..) of generated types visible for users of the library')
 parser.add_argument('--export-prototypes', action='store_true', help='make the prototypes (init, delete, copy, ..) of generated types visible for users of the library')
 parser.add_argument('--with-xml', action='store_true', help='generate xml encoding')
 parser.add_argument('--with-xml', action='store_true', help='generate xml encoding')
@@ -16,16 +22,18 @@ parser.add_argument('--with-json', action='store_true', help='generate json enco
 parser.add_argument('--only-nano', action='store_true', help='generate only the types for the nano profile')
 parser.add_argument('--only-nano', action='store_true', help='generate only the types for the nano profile')
 parser.add_argument('--only-needed', action='store_true', help='generate only types needed for compile')
 parser.add_argument('--only-needed', action='store_true', help='generate only types needed for compile')
 parser.add_argument('--additional-includes', action='store', help='include additional header files (separated by comma)')
 parser.add_argument('--additional-includes', action='store', help='include additional header files (separated by comma)')
-parser.add_argument('types', help='path/to/Opc.Ua.Types.bsd')
+parser.add_argument('xml', help='path/to/Opc.Ua.Types.bsd')
 parser.add_argument('outfile', help='outfile w/o extension')
 parser.add_argument('outfile', help='outfile w/o extension')
 args = parser.parse_args()
 args = parser.parse_args()
+outname = args.outfile.split("/")[-1] 
+inname = args.xml.split("/")[-1]
         
         
 ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
 ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
-tree = etree.parse(args.types)
+tree = etree.parse(args.xml)
 types = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
 types = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
 
 
-fh = open(args.outfile + ".h",'w')
-fc = open(args.outfile + ".c",'w')
+fh = open(args.outfile + "_generated.h",'w')
+fc = open(args.outfile + "_generated.c",'w')
 
 
 # dirty hack. we go up the call frames to access local variables of the calling
 # dirty hack. we go up the call frames to access local variables of the calling
 # function. this allows to shorten code and get %()s replaces with less clutter.
 # function. this allows to shorten code and get %()s replaces with less clutter.
@@ -69,6 +77,10 @@ def camlCase2CCase(item):
 		return "my" + item
 		return "my" + item
 	return item[:1].lower() + item[1:] if item else ''
 	return item[:1].lower() + item[1:] if item else ''
 
 
+################################
+# Type Definition and Encoding #
+################################
+
 # are the types we need already in place? if not, postpone.
 # are the types we need already in place? if not, postpone.
 def printableStructuredType(element):
 def printableStructuredType(element):
     for child in element:
     for child in element:
@@ -78,6 +90,15 @@ def printableStructuredType(element):
                 return False
                 return False
     return True
     return True
 
 
+def printPrototypes(name):
+    if args.export_prototypes:
+        printh("UA_TYPE_PROTOTYPES(%(name)s)")
+    else:
+        printh("UA_TYPE_PROTOTYPES_NOEXPORT(%(name)s)")
+    printh("UA_TYPE_BINARY_ENCODING(%(name)s)")
+    if args.with_xml:
+        printh("UA_TYPE_XML_ENCODING(%(name)s)")
+
 def createEnumerated(element):	
 def createEnumerated(element):	
     valuemap = OrderedDict()
     valuemap = OrderedDict()
     name = "UA_" + element.get("Name")
     name = "UA_" + element.get("Name")
@@ -93,11 +114,7 @@ def createEnumerated(element):
     printh("typedef enum { \n\t" +
     printh("typedef enum { \n\t" +
            ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
            ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
            "\n} " + name + ";")
            "\n} " + name + ";")
-    if args.export_prototypes:
-        printh("UA_TYPE_PROTOTYPES(" + name + ")")
-    else:
-        printh("UA_TYPE_PROTOTYPES_NOEXPORT(" + name + ")")
-    printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
+    printPrototypes(name)
     printc("UA_TYPE_AS(" + name + ", UA_Int32)")
     printc("UA_TYPE_AS(" + name + ", UA_Int32)")
     printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_Int32)")
     printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_Int32)")
     if args.with_xml:
     if args.with_xml:
@@ -113,12 +130,10 @@ def createOpaque(element):
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
             printh("/** @brief " + child.text + " */")
             printh("/** @brief " + child.text + " */")
     printh("typedef UA_ByteString %(name)s;")
     printh("typedef UA_ByteString %(name)s;")
-    printh("UA_TYPE_PROTOTYPES(%(name)s)")
-    printh("UA_TYPE_BINARY_ENCODING(%(name)s)")
+    printPrototypes(name)
     printc("UA_TYPE_AS(%(name)s, UA_ByteString)")
     printc("UA_TYPE_AS(%(name)s, UA_ByteString)")
     printc("UA_TYPE_BINARY_ENCODING_AS(%(name)s, UA_ByteString)")
     printc("UA_TYPE_BINARY_ENCODING_AS(%(name)s, UA_ByteString)")
     if args.with_xml:
     if args.with_xml:
-        printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
         printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
         printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
 UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)\n''')
 UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)\n''')
@@ -172,10 +187,7 @@ def createStructured(element):
         printh("typedef void* %(name)s;")
         printh("typedef void* %(name)s;")
         
         
     # 3) function prototypes
     # 3) function prototypes
-    printh("UA_TYPE_PROTOTYPES(" + name + ")")
-    printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
-    if args.with_xml:
-        printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
+    printPrototypes(name)
 
 
     # 4) CalcSizeBinary
     # 4) CalcSizeBinary
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
@@ -271,7 +283,7 @@ UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
     printc("\treturn retval;\n}\n")
     printc("\treturn retval;\n}\n")
 
 
     # 13) Print
     # 13) Print
-    printc('''#ifdef DEBUG''') 
+    printc('''#ifdef UA_DEBUG''') 
     printc('''void %(name)s_print(const %(name)s *p, FILE *stream) {
     printc('''void %(name)s_print(const %(name)s *p, FILE *stream) {
     fprintf(stream, "(%(name)s){");''')
     fprintf(stream, "(%(name)s){");''')
     for i,(n,t) in enumerate(membermap.iteritems()):
     for i,(n,t) in enumerate(membermap.iteritems()):
@@ -286,28 +298,34 @@ UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
     printc('''\tfprintf(stream, "}");\n}''')
     printc('''\tfprintf(stream, "}");\n}''')
     printc('#endif');
     printc('#endif');
     printc('''\n''')
     printc('''\n''')
+
+##########################
+# Header and Boilerplate #
+##########################
     
     
-	
-shortname = args.outfile.split("/")[-1] 
-print(shortname)
 printh('''/**
 printh('''/**
- * @file %(shortname)s.h
+ * @file %(outname)s_generated.h
  *
  *
  * @brief Autogenerated data types defined in the UA standard
  * @brief Autogenerated data types defined in the UA standard
  *
  *
- * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
- * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
+ * Generated from %(inname)s with script ''' + sys.argv[0] + '''
+ * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  */
  */
 
 
-#ifndef ''' + shortname.upper() + '''_H_
-#define ''' + shortname.upper() + '''_H_
+#ifndef ''' + outname.upper() + '''_GENERATED_H_
+#define ''' + outname.upper() + '''_GENERATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 
 #include "ua_types.h"
 #include "ua_types.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_encoding_binary.h"
 
 
-/** @ingroup ''' + shortname.split("_")[1] + '''
+/**
+ * @ingroup types
  *
  *
- * @defgroup ''' + shortname + ''' Generated Types
+ * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
  *
  *
  * @brief Data structures that are autogenerated from an XML-Schema.
  * @brief Data structures that are autogenerated from an XML-Schema.
  * @{
  * @{
@@ -319,44 +337,39 @@ if args.additional_includes:
         printh("#include \"" + incl + "\"")
         printh("#include \"" + incl + "\"")
 
 
 printc('''/**
 printc('''/**
- * @file '''+sys.argv[2]+'''.c
+ * @file ''' + outname + '''_generated.c
  *
  *
  * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  *
  *
- * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
+ * Generated from %(inname)s with script '''+sys.argv[0]+'''
  * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  */
  */
  
  
-#include "''' + args.outfile.split("/")[-1] + '''.h"
-#include "ua_types_internal.h"
+#include "%(outname)s_generated.h"
+#include "ua_types_macros.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"\n''')
 #include "ua_util.h"\n''')
 
 
-# types for which we create a vector type
-arraytypes = set()
-fields = tree.xpath("//opc:Field", namespaces=ns)
-for field in fields:
-	if field.get("LengthField"):
-		arraytypes.add(stripTypename(field.get("TypeName")))
+##############################
+# Loop over types in the XML #
+##############################
+
+# # plugin handling
+# import os
+# files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
+# plugin_types = []
+# packageForType = OrderedDict()
+#
+# for f in files:
+# 	package = f[:-3]
+# 	exec "import " + package
+# 	exec "pluginSetup = " + package + ".setup()"
+# 	if pluginSetup["pluginType"] == "structuredObject":
+# 		plugin_types.append(pluginSetup["tagName"])
+# 		packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
+# 		print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
 
 
 deferred_types = OrderedDict()
 deferred_types = OrderedDict()
-
-#plugin handling
-import os
-files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
-plugin_types = []
-packageForType = OrderedDict()
-
-for f in files:
-	package = f[:-3]
-	exec "import " + package
-	exec "pluginSetup = " + package + ".setup()"
-	if pluginSetup["pluginType"] == "structuredObject":
-		plugin_types.append(pluginSetup["tagName"])
-		packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
-		print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
-#end plugin handling
-
 for element in types:
 for element in types:
 	name = element.get("Name")
 	name = element.get("Name")
 	if skipType(name):
 	if skipType(name):
@@ -376,16 +389,26 @@ for element in types:
 		existing_types.add(name)
 		existing_types.add(name)
 
 
 for name, element in deferred_types.iteritems():
 for name, element in deferred_types.iteritems():
-    if name in plugin_types:
-        #execute plugin if registered
-	exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
-	if ret == "default":
-            createStructured(element)
-            existing_types.add(name)
-    else:
-	createStructured(element)
-        existing_types.add(name)
-printh('/// @} /* end of group */')
+    # if name in plugin_types:
+    #     #execute plugin if registered
+    #     exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
+    #     if ret == "default":
+    #         createStructured(element)
+    #         existing_types.add(name)
+    # else:
+    createStructured(element)
+    existing_types.add(name)
+
+#############
+# Finish Up #
+#############
+
+printh('''
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+/// @} /* end of group */''')
 printh('#endif')
 printh('#endif')
 
 
 fh.close()
 fh.close()

+ 1 - 1
tools/generate_namespace.py

@@ -188,7 +188,7 @@ for row in rows:
            ",\n.copy=(UA_Int32(*)(void const * ,void*))%(name)s_copy" +
            ",\n.copy=(UA_Int32(*)(void const * ,void*))%(name)s_copy" +
            ",\n.delete=(void(*)(void *))%(name)s_delete" +
            ",\n.delete=(void(*)(void *))%(name)s_delete" +
            ",\n.deleteMembers=(void(*)(void *))%(name)s_deleteMembers" +
            ",\n.deleteMembers=(void(*)(void *))%(name)s_deleteMembers" +
-           ",\n#ifdef DEBUG //FIXME: seems to be okay atm, however a pointer to a noop function would be more safe" + 
+           ",\n#ifdef UA_DEBUG //FIXME: seems to be okay atm, however a pointer to a noop function would be more safe" + 
            "\n.print=(void(*)(const void *, FILE *))%(name)s_print," +
            "\n.print=(void(*)(const void *, FILE *))%(name)s_print," +
            "\n#endif" + 
            "\n#endif" + 
            "\n.memSize=" + ("sizeof(%(name)s)" if (name != "UA_InvalidType") else "0") +
            "\n.memSize=" + ("sizeof(%(name)s)" if (name != "UA_InvalidType") else "0") +

+ 1 - 1
tools/schema/Custom.Opc.Ua.Transport.bsd

@@ -44,7 +44,7 @@
   </opc:StructuredType>
   </opc:StructuredType>
   
   
   <opc:StructuredType Name="AsymmetricAlgorithmSecurityHeader">
   <opc:StructuredType Name="AsymmetricAlgorithmSecurityHeader">
-    <opc:Documentation>Security Header></opc:Documentation>
+    <opc:Documentation>Security Header</opc:Documentation>
     <opc:Field Name="SecurityPolicyUri" TypeName="opc:ByteString" />
     <opc:Field Name="SecurityPolicyUri" TypeName="opc:ByteString" />
     <opc:Field Name="SenderCertificate" TypeName="opc:ByteString" />
     <opc:Field Name="SenderCertificate" TypeName="opc:ByteString" />
     <opc:Field Name="ReceiverCertificateThumbprint" TypeName="opc:ByteString" />
     <opc:Field Name="ReceiverCertificateThumbprint" TypeName="opc:ByteString" />