aboutsummaryrefslogtreecommitdiffstats
path: root/src/server_lds.c
blob: 8d34accc7a8f8b42222274fa27514d1bf6f123e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * @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 <open62541/plugin/log_stdout.h>
#include <open62541/server.h>

#include <signal.h>
#include <stdlib.h>

volatile UA_Boolean running = true;

static void
stopHandler (int sig)
{
  running = false;
}

int
main (int argc, char *argv[])
{
  signal (SIGINT, stopHandler);
  signal (SIGTERM, stopHandler);

  if (argc < 2 || argc > 3)
    {
      UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Usage: %s <config-file> [log-level]", argv[0]);
      return EXIT_FAILURE;
    }

  const char *logLevelStr = (argc == 3) ? argv[2] : "info";
  int logLevel = parseLogLevel (logLevelStr);
  if (logLevel < 0)
    {
      UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Unknown log level: %s "
                    "(expected trace, debug, info, warning, error, fatal)",
                    logLevelStr);
      return EXIT_FAILURE;
    }

  Config cfg;
  if (configLoad (argv[1], &cfg) != 0)
    return EXIT_FAILURE;

  int port = configRequireInt (&cfg, "port", "ServerLDS");
  const char *applicationUri
      = configRequire (&cfg, "applicationUri", "ServerLDS");
  int cleanupTimeout = configRequireInt (&cfg, "cleanupTimeout", "ServerLDS");

  if (!applicationUri || port < 0 || cleanupTimeout < 0)
    {
      configFree (&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 (cleanupTimeout <= 10)
    {
      UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Cleanup timeout must be > 10 seconds (got %d)",
                    cleanupTimeout);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  SecurityConfig sec;
  if (parseSecurityConfig (&cfg, "ServerLDS", false, &sec) != 0)
    {
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  AuthConfig auth;
  if (parseAuthConfig (&cfg, "ServerLDS", &auth) != 0)
    {
      freeTrustStore (sec.trustPaths, sec.trustSize);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  UA_StatusCode retval;
  UA_Server *server = createServer ((UA_UInt16)port, applicationUri,
                                    sec.certPath ? &sec : NULL, true, &retval);
  if (!server)
    {
      freeTrustStore (sec.trustPaths, sec.trustSize);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  UA_ServerConfig *serverConfig = UA_Server_getConfig (server);
  serverConfig->logging->context = (void *)(uintptr_t)logLevel;

  /* 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. */
  serverConfig->verifyRequestTimestamp = UA_RULEHANDLING_WARN;

  retval = configureAccessControl (serverConfig, &auth);
  if (retval != UA_STATUSCODE_GOOD)
    {
      UA_Server_delete (server);
      freeTrustStore (sec.trustPaths, sec.trustSize);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  /* Mark this server as a Discovery Server so clients can identify it. */
  serverConfig->applicationDescription.applicationType
      = UA_APPLICATIONTYPE_DISCOVERYSERVER;

  /* Time (seconds) after which stale registrations are removed.  Must
     exceed the registering server's re-register interval. */
  serverConfig->discoveryCleanupTimeout = cleanupTimeout;

  retval = UA_Server_run (server, &running);

  UA_Server_delete (server);
  freeTrustStore (sec.trustPaths, sec.trustSize);
  configFree (&cfg);
  return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}