/** * @file server_lds.c * @brief Local Discovery Server implementation. * * This program runs an OPC UA Local Discovery Server (LDS) with a configurable * cleanup timeout. Encryption is optional: when certificate, privateKey, and * trustStore are provided, the server offers all security policies; otherwise * it runs with SecurityPolicy#None only. Other OPC UA servers register * with this LDS using the RegisterServer2 service. Clients can query this LDS * using the FindServers service to discover registered servers. */ #include "common.h" #include "config.h" #include #include #include #include volatile UA_Boolean g_running = true; static void _s_stop_handler (int sig) { g_running = false; } int main (int argc, char *argv[]) { signal (SIGINT, _s_stop_handler); signal (SIGTERM, _s_stop_handler); if (argc < 2 || argc > 3) { UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Usage: %s [log-level]", argv[0]); return EXIT_FAILURE; } const char *log_level_str = (argc == 3) ? argv[2] : "info"; int log_level = parse_log_level (log_level_str); if (log_level < 0) { UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown log level: %s " "(expected trace, debug, info, warning, error, fatal)", log_level_str); return EXIT_FAILURE; } config cfg; if (config_load (argv[1], &cfg) != 0) return EXIT_FAILURE; int port = config_require_int (&cfg, "port", "ServerLDS"); const char *application_uri = config_require (&cfg, "applicationUri", "ServerLDS"); int cleanup_timeout = config_require_int (&cfg, "cleanupTimeout", "ServerLDS"); if (!application_uri || port < 0 || cleanup_timeout < 0) { config_free (&cfg); return EXIT_FAILURE; } /* The OPC UA specification requires the cleanup timeout to exceed the register-server interval. open62541 enforces a floor of 10 seconds. */ if (cleanup_timeout <= 10) { UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Cleanup timeout must be > 10 seconds (got %d)", cleanup_timeout); config_free (&cfg); return EXIT_FAILURE; } security_config sec; if (parse_security_config (&cfg, "ServerLDS", false, &sec) != 0) { config_free (&cfg); return EXIT_FAILURE; } auth_config auth; if (parse_auth_config (&cfg, "ServerLDS", &auth) != 0) { free_trust_store (sec.trust_paths, sec.trust_size); config_free (&cfg); return EXIT_FAILURE; } UA_StatusCode retval; UA_Server *server = create_server ((UA_UInt16)port, application_uri, sec.cert_path ? &sec : NULL, true, &retval); if (!server) { free_trust_store (sec.trust_paths, sec.trust_size); config_free (&cfg); return EXIT_FAILURE; } UA_ServerConfig *server_config = UA_Server_getConfig (server); server_config->logging->context = (void *)(uintptr_t)log_level; /* Some OPC UA stacks omit the timestamp in the request header. The default behaviour rejects these requests with BadInvalidTimestamp. Downgrade to a warning so third-party servers can still register. */ server_config->verifyRequestTimestamp = UA_RULEHANDLING_WARN; retval = configure_access_control (server_config, &auth); if (retval != UA_STATUSCODE_GOOD) { UA_Server_delete (server); free_trust_store (sec.trust_paths, sec.trust_size); config_free (&cfg); return EXIT_FAILURE; } /* Mark this server as a Discovery Server so clients can identify it. */ server_config->applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER; /* Time (seconds) after which stale registrations are removed. Must exceed the registering server's re-register interval. */ server_config->discoveryCleanupTimeout = cleanup_timeout; retval = UA_Server_run (server, &g_running); UA_Server_delete (server); free_trust_store (sec.trust_paths, sec.trust_size); config_free (&cfg); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }