Browse Source

Server: Add a configurable delay to the server shutdown

In order to notify clients about an imminent shutdown.
Julius Pfrommer 4 years ago
parent
commit
caa20e1c4f

+ 2 - 0
examples/server_ctt.c

@@ -766,6 +766,8 @@ int main(int argc, char **argv) {
     config.applicationDescription.applicationUri =
         UA_String_fromChars("urn:open62541.server.application");
 
+    config.shutdownDelay = 5000.0; /* 5s */
+
     UA_Server *server = UA_Server_newWithConfig(&config);
     if(server == NULL)
         return EXIT_FAILURE;

+ 4 - 0
include/open62541/server_config.h

@@ -92,6 +92,10 @@ struct UA_ServerConfig {
     UA_ApplicationDescription applicationDescription;
     UA_ByteString serverCertificate;
 
+    UA_Double shutdownDelay; /* Delay in ms from the shutdown signal (ctrl-c)
+                                until the actual shutdown. Clients need to be
+                                able to get a notification ahead of time. */
+
     /* Rule Handling */
     UA_RuleHandling verifyRequestTimestamp; /* Verify that the server sends a
                                              * timestamp in the request header */

+ 2 - 0
plugins/ua_config_default.c

@@ -108,6 +108,8 @@ setDefaultConfig(UA_ServerConfig *conf) {
     conf->nThreads = 1;
     conf->logger = UA_Log_Stdout_;
 
+    conf->shutdownDelay = 0.0;
+
     /* Server Description */
     conf->buildInfo.productUri = UA_STRING_ALLOC(PRODUCT_URI);
     conf->buildInfo.manufacturerName = UA_STRING_ALLOC(MANUFACTURER_NAME);

+ 25 - 1
src/server/ua_server.c

@@ -291,6 +291,19 @@ UA_Server_newWithConfig(const UA_ServerConfig *config) {
     return UA_Server_init(server);
 }
 
+/* Returns if the server should be shut down immediately */
+static UA_Boolean
+setServerShutdown(UA_Server *server) {
+    if(server->endTime != 0)
+        return false;
+    if(server->config.shutdownDelay == 0)
+        return true;
+    UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                   "Shutting down the server with a delay of %i ms", (int)server->config.shutdownDelay);
+    server->endTime = UA_DateTime_now() + (UA_DateTime)(server->config.shutdownDelay * UA_DATETIME_MSEC);
+    return false;
+}
+
 /*******************/
 /* Timed Callbacks */
 /*******************/
@@ -581,6 +594,13 @@ UA_Server_run_shutdown(UA_Server *server) {
     return UA_STATUSCODE_GOOD;
 }
 
+static UA_Boolean
+testShutdownCondition(UA_Server *server) {
+    if(server->endTime == 0)
+        return false;
+    return (UA_DateTime_now() > server->endTime);
+}
+
 UA_StatusCode
 UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) {
     UA_StatusCode retval = UA_Server_run_startup(server);
@@ -589,7 +609,7 @@ UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) {
 #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
     size_t loopCount = 0;
 #endif
-    while(*running) {
+    while(!testShutdownCondition(server)) {
 #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
         if(loopCount == 0) {
             VALGRIND_DO_LEAK_CHECK;
@@ -598,6 +618,10 @@ UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) {
         loopCount %= UA_VALGRIND_INTERACTIVE_INTERVAL;
 #endif
         UA_Server_run_iterate(server, true);
+        if(!*running) {
+            if(setServerShutdown(server))
+                break;
+        }
     }
     return UA_Server_run_shutdown(server);
 }

+ 2 - 0
src/server/ua_server_internal.h

@@ -59,6 +59,8 @@ struct UA_Server {
     /* Config */
     UA_ServerConfig config;
     UA_DateTime startTime;
+    UA_DateTime endTime; /* Zeroed out. If a time is set, then the server shuts
+                          * down once the time has been reached */
 
     /* Nodestore */
     void *nsCtx;

+ 44 - 14
src/server/ua_server_ns0.c

@@ -279,14 +279,44 @@ readStatus(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
     UA_assert(nodeId->identifierType == UA_NODEIDTYPE_NUMERIC);
 
     switch(nodeId->identifier.numeric) {
+    case UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN: {
+        UA_UInt32 *shutdown = UA_UInt32_new();
+        if(!shutdown)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(server->endTime != 0)
+            *shutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC);
+        value->value.data = shutdown;
+        value->value.type = &UA_TYPES[UA_TYPES_UINT32];
+        value->hasValue = true;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    case UA_NS0ID_SERVER_SERVERSTATUS_STATE: {
+        UA_ServerState *state = UA_ServerState_new();
+        if(!state)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(server->endTime != 0)
+            *state = UA_SERVERSTATE_SHUTDOWN;
+        value->value.data = state;
+        value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
+        value->hasValue = true;
+        return UA_STATUSCODE_GOOD;
+    }
+
     case UA_NS0ID_SERVER_SERVERSTATUS: {
         UA_ServerStatusDataType *statustype = UA_ServerStatusDataType_new();
         if(!statustype)
             return UA_STATUSCODE_BADOUTOFMEMORY;
         statustype->startTime = server->startTime;
         statustype->currentTime = UA_DateTime_now();
+
         statustype->state = UA_SERVERSTATE_RUNNING;
         statustype->secondsTillShutdown = 0;
+        if(server->endTime != 0) {
+            statustype->state = UA_SERVERSTATE_SHUTDOWN;
+            statustype->secondsTillShutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC);
+        }
+
         value->value.data = statustype;
         value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE];
         value->hasValue = true;
@@ -561,14 +591,6 @@ readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionC
 }
 #endif /* defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) */
 
-static UA_StatusCode
-writeNs0Variable(UA_Server *server, UA_UInt32 id, void *v, const UA_DataType *type) {
-    UA_Variant var;
-    UA_Variant_init(&var);
-    UA_Variant_setScalar(&var, v, type);
-    return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var);
-}
-
 UA_StatusCode
 writeNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v,
                       size_t length, const UA_DataType *type) {
@@ -661,6 +683,14 @@ UA_Server_minimalServerObject(UA_Server *server) {
 
 #else
 
+static UA_StatusCode
+writeNs0Variable(UA_Server *server, UA_UInt32 id, void *v, const UA_DataType *type) {
+    UA_Variant var;
+    UA_Variant_init(&var);
+    UA_Variant_setScalar(&var, v, type);
+    return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var);
+}
+
 static void
 addModellingRules(UA_Server *server) {
     /* Test if the ModellingRules folder was added. (Only for the full ns0.) */
@@ -766,9 +796,9 @@ UA_Server_initNS0(UA_Server *server) {
                  UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), currentTime);
 
     /* State */
-    UA_ServerState state = UA_SERVERSTATE_RUNNING;
-    retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_STATE,
-                               &state, &UA_TYPES[UA_TYPES_SERVERSTATE]);
+    retVal |= UA_Server_setVariableNode_dataSource(server,
+                 UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
+                                                   serverStatus);
 
     /* BuildInfo */
     retVal |= UA_Server_setVariableNode_dataSource(server,
@@ -807,9 +837,9 @@ UA_Server_initNS0(UA_Server *server) {
 #ifdef UA_GENERATED_NAMESPACE_ZERO
 
     /* SecondsTillShutdown */
-    UA_UInt32 secondsTillShutdown = 0;
-    retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN,
-                               &secondsTillShutdown, &UA_TYPES[UA_TYPES_UINT32]);
+    retVal |= UA_Server_setVariableNode_dataSource(server,
+                 UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN),
+                                                   serverStatus);
 
     /* ShutDownReason */
     UA_LocalizedText shutdownReason;