]>
Commit | Line | Data |
---|---|---|
9a1955a7 | 1 | /* |
6f55a064 | 2 | * Copyright (c) 2015-2020 Red Hat, Inc. |
9a1955a7 JF |
3 | * |
4 | * All rights reserved. | |
5 | * | |
6 | * Author: Jan Friesse (jfriesse@redhat.com) | |
7 | * | |
8 | * This software licensed under BSD license, the text of which follows: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions are met: | |
12 | * | |
13 | * - Redistributions of source code must retain the above copyright notice, | |
14 | * this list of conditions and the following disclaimer. | |
15 | * - Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * - Neither the name of the Red Hat, Inc. nor the names of its | |
19 | * contributors may be used to endorse or promote products derived from this | |
20 | * software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
32 | * THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <err.h> | |
36 | #include <errno.h> | |
37 | #include <getopt.h> | |
3b21d758 | 38 | #include <limits.h> |
9a1955a7 JF |
39 | #include <signal.h> |
40 | #include <unistd.h> | |
41 | ||
42 | #include "qnet-config.h" | |
43 | ||
44 | #include "dynar.h" | |
45 | #include "dynar-str.h" | |
46 | #include "dynar-getopt-lex.h" | |
bb9dabf9 | 47 | #include "log.h" |
9a1955a7 JF |
48 | #include "nss-sock.h" |
49 | #include "pr-poll-array.h" | |
50 | #include "qnetd-advanced-settings.h" | |
51 | #include "qnetd-algorithm.h" | |
52 | #include "qnetd-instance.h" | |
53 | #include "qnetd-ipc.h" | |
9a1955a7 JF |
54 | #include "qnetd-client-net.h" |
55 | #include "qnetd-client-msg-received.h" | |
9a1955a7 JF |
56 | #include "utils.h" |
57 | #include "msg.h" | |
58 | ||
59 | #ifdef HAVE_LIBSYSTEMD | |
60 | #include <systemd/sd-daemon.h> | |
61 | #endif | |
62 | ||
63 | /* | |
64 | * This is global variable used for comunication with main loop and signal (calls close) | |
65 | */ | |
66 | struct qnetd_instance *global_instance; | |
67 | ||
68 | enum tlv_decision_algorithm_type | |
69 | qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE] = { | |
70 | TLV_DECISION_ALGORITHM_TYPE_TEST, | |
71 | TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, | |
72 | TLV_DECISION_ALGORITHM_TYPE_2NODELMS, | |
73 | TLV_DECISION_ALGORITHM_TYPE_LMS, | |
74 | }; | |
75 | ||
76 | static void | |
77 | qnetd_err_nss(void) | |
78 | { | |
79 | ||
bb9dabf9 | 80 | log_nss(LOG_CRIT, "NSS error"); |
9a1955a7 JF |
81 | |
82 | exit(1); | |
83 | } | |
84 | ||
85 | static void | |
86 | qnetd_warn_nss(void) | |
87 | { | |
88 | ||
bb9dabf9 | 89 | log_nss(LOG_WARNING, "NSS warning"); |
9a1955a7 JF |
90 | } |
91 | ||
6f55a064 JF |
92 | static int |
93 | server_socket_poll_loop_read_cb(PRFileDesc *prfd, void *user_data1, void *user_data2) | |
9a1955a7 | 94 | { |
6f55a064 | 95 | struct qnetd_instance *instance = (struct qnetd_instance *)user_data1; |
9a1955a7 | 96 | |
6f55a064 | 97 | qnetd_client_net_accept(instance); |
9a1955a7 | 98 | |
6f55a064 JF |
99 | return (0); |
100 | } | |
9a1955a7 | 101 | |
6f55a064 JF |
102 | static int |
103 | server_socket_poll_loop_write_cb(PRFileDesc *prfd, void *user_data1, void *user_data2) | |
104 | { | |
9a1955a7 | 105 | |
6f55a064 JF |
106 | /* |
107 | * Poll write on listen socket -> fatal error | |
108 | */ | |
109 | log(LOG_CRIT, "POLL_WRITE on listening socket"); | |
9a1955a7 | 110 | |
6f55a064 | 111 | return (-1); |
9a1955a7 JF |
112 | } |
113 | ||
114 | static int | |
6f55a064 | 115 | server_socket_poll_loop_err_cb(PRFileDesc *prfd, short revents, void *user_data1, void *user_data2) |
9a1955a7 | 116 | { |
9a1955a7 | 117 | |
6f55a064 | 118 | if (revents != POLLNVAL) { |
9a1955a7 | 119 | /* |
6f55a064 JF |
120 | * Poll ERR on listening socket is fatal error. |
121 | * POLL_NVAL is used as a signal to quit poll loop. | |
9a1955a7 | 122 | */ |
6f55a064 JF |
123 | log(LOG_CRIT, "POLL_ERR (%u) on listening socket", revents); |
124 | } else { | |
125 | log(LOG_DEBUG, "Listening socket is closed"); | |
9a1955a7 JF |
126 | } |
127 | ||
6f55a064 | 128 | return (-1); |
9a1955a7 JF |
129 | } |
130 | ||
131 | static void | |
132 | signal_int_handler(int sig) | |
133 | { | |
134 | ||
bb9dabf9 | 135 | log(LOG_DEBUG, "SIGINT received - closing server IPC socket"); |
9a1955a7 JF |
136 | |
137 | qnetd_ipc_close(global_instance); | |
138 | } | |
139 | ||
140 | static void | |
141 | signal_term_handler(int sig) | |
142 | { | |
143 | ||
bb9dabf9 | 144 | log(LOG_DEBUG, "SIGTERM received - closing server IPC socket"); |
9a1955a7 JF |
145 | |
146 | qnetd_ipc_close(global_instance); | |
147 | } | |
148 | ||
149 | static void | |
150 | signal_handlers_register(void) | |
151 | { | |
152 | struct sigaction act; | |
153 | ||
154 | act.sa_handler = signal_int_handler; | |
155 | sigemptyset(&act.sa_mask); | |
156 | act.sa_flags = SA_RESTART; | |
157 | ||
158 | sigaction(SIGINT, &act, NULL); | |
159 | ||
160 | act.sa_handler = signal_term_handler; | |
161 | sigemptyset(&act.sa_mask); | |
162 | act.sa_flags = SA_RESTART; | |
163 | ||
164 | sigaction(SIGTERM, &act, NULL); | |
165 | } | |
166 | ||
167 | static void | |
168 | usage(void) | |
169 | { | |
170 | ||
171 | printf("usage: %s [-46dfhv] [-l listen_addr] [-p listen_port] [-s tls]\n", QNETD_PROGRAM_NAME); | |
172 | printf("%14s[-c client_cert_required] [-m max_clients] [-S option=value[,option2=value2,...]]\n", ""); | |
173 | } | |
174 | ||
175 | static void | |
176 | display_version(void) | |
177 | { | |
178 | enum msg_type *supported_messages; | |
179 | size_t no_supported_messages; | |
180 | size_t zi; | |
181 | ||
182 | msg_get_supported_messages(&supported_messages, &no_supported_messages); | |
183 | printf("Corosync Qdevice Network Daemon, version '%s'\n\n", VERSION); | |
184 | printf("Supported algorithms: "); | |
185 | for (zi = 0; zi < QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE; zi++) { | |
186 | if (zi != 0) { | |
187 | printf(", "); | |
188 | } | |
189 | printf("%s (%u)", | |
190 | tlv_decision_algorithm_type_to_str(qnetd_static_supported_decision_algorithms[zi]), | |
191 | qnetd_static_supported_decision_algorithms[zi]); | |
192 | } | |
193 | printf("\n"); | |
194 | printf("Supported message types: "); | |
195 | for (zi = 0; zi < no_supported_messages; zi++) { | |
196 | if (zi != 0) { | |
197 | printf(", "); | |
198 | } | |
199 | printf("%s (%u)", msg_type_to_str(supported_messages[zi]), supported_messages[zi]); | |
200 | } | |
201 | printf("\n"); | |
202 | } | |
203 | ||
204 | static void | |
205 | cli_parse_long_opt(struct qnetd_advanced_settings *advanced_settings, const char *long_opt) | |
206 | { | |
207 | struct dynar_getopt_lex lex; | |
208 | struct dynar dynar_long_opt; | |
209 | const char *opt; | |
210 | const char *val; | |
211 | int res; | |
212 | ||
213 | dynar_init(&dynar_long_opt, strlen(long_opt) + 1); | |
214 | if (dynar_str_cpy(&dynar_long_opt, long_opt) != 0) { | |
215 | errx(1, "Can't alloc memory for long option"); | |
216 | } | |
217 | ||
218 | dynar_getopt_lex_init(&lex, &dynar_long_opt); | |
219 | ||
220 | while (dynar_getopt_lex_token_next(&lex) == 0 && strcmp(dynar_data(&lex.option), "") != 0) { | |
221 | opt = dynar_data(&lex.option); | |
222 | val = dynar_data(&lex.value); | |
223 | ||
224 | res = qnetd_advanced_settings_set(advanced_settings, opt, val); | |
225 | switch (res) { | |
226 | case -1: | |
227 | errx(1, "Unknown option '%s'", opt); | |
228 | break; | |
229 | case -2: | |
230 | errx(1, "Invalid value '%s' for option '%s'", val, opt); | |
231 | break; | |
232 | } | |
233 | } | |
234 | ||
235 | dynar_getopt_lex_destroy(&lex); | |
236 | dynar_destroy(&dynar_long_opt); | |
237 | } | |
238 | ||
239 | static void | |
240 | cli_parse(int argc, char * const argv[], char **host_addr, uint16_t *host_port, int *foreground, | |
241 | int *debug_log, int *bump_log_priority, enum tlv_tls_supported *tls_supported, | |
242 | int *client_cert_required, size_t *max_clients, PRIntn *address_family, | |
243 | struct qnetd_advanced_settings *advanced_settings) | |
244 | { | |
245 | int ch; | |
9a1955a7 JF |
246 | long long int tmpll; |
247 | ||
248 | *host_addr = NULL; | |
249 | *host_port = QNETD_DEFAULT_HOST_PORT; | |
250 | *foreground = 0; | |
251 | *debug_log = 0; | |
252 | *bump_log_priority = 0; | |
253 | *tls_supported = QNETD_DEFAULT_TLS_SUPPORTED; | |
254 | *client_cert_required = QNETD_DEFAULT_TLS_CLIENT_CERT_REQUIRED; | |
255 | *max_clients = QNETD_DEFAULT_MAX_CLIENTS; | |
256 | *address_family = PR_AF_UNSPEC; | |
257 | ||
258 | while ((ch = getopt(argc, argv, "46dfhvc:l:m:p:S:s:")) != -1) { | |
259 | switch (ch) { | |
260 | case '4': | |
261 | *address_family = PR_AF_INET; | |
262 | break; | |
263 | case '6': | |
264 | *address_family = PR_AF_INET6; | |
265 | break; | |
266 | case 'f': | |
267 | *foreground = 1; | |
268 | break; | |
269 | case 'd': | |
270 | if (*debug_log) { | |
271 | *bump_log_priority = 1; | |
272 | } | |
273 | *debug_log = 1; | |
274 | break; | |
275 | case 'c': | |
276 | if ((*client_cert_required = utils_parse_bool_str(optarg)) == -1) { | |
277 | errx(1, "client_cert_required should be on/yes/1, off/no/0"); | |
278 | } | |
279 | break; | |
280 | case 'l': | |
281 | free(*host_addr); | |
282 | *host_addr = strdup(optarg); | |
283 | if (*host_addr == NULL) { | |
284 | errx(1, "Can't alloc memory for host addr string"); | |
285 | } | |
286 | break; | |
287 | case 'm': | |
3b21d758 | 288 | if (utils_strtonum(optarg, 0, LLONG_MAX, &tmpll) == -1) { |
9a1955a7 JF |
289 | errx(1, "max clients value %s is invalid", optarg); |
290 | } | |
3b21d758 | 291 | |
9a1955a7 JF |
292 | *max_clients = (size_t)tmpll; |
293 | break; | |
294 | case 'p': | |
3b21d758 JF |
295 | if (utils_strtonum(optarg, 1, UINT16_MAX, &tmpll) == -1) { |
296 | errx(1, "host port must be in range 1-%u", UINT16_MAX); | |
9a1955a7 | 297 | } |
3b21d758 JF |
298 | |
299 | *host_port = tmpll; | |
9a1955a7 JF |
300 | break; |
301 | case 'S': | |
302 | cli_parse_long_opt(advanced_settings, optarg); | |
303 | break; | |
304 | case 's': | |
305 | if (strcasecmp(optarg, "on") == 0) { | |
306 | *tls_supported = QNETD_DEFAULT_TLS_SUPPORTED; | |
307 | } else if (strcasecmp(optarg, "off") == 0) { | |
308 | *tls_supported = TLV_TLS_UNSUPPORTED; | |
309 | } else if (strcasecmp(optarg, "req") == 0) { | |
310 | *tls_supported = TLV_TLS_REQUIRED; | |
311 | } else { | |
312 | errx(1, "tls must be one of on, off, req"); | |
313 | } | |
314 | break; | |
315 | case 'v': | |
316 | display_version(); | |
317 | exit(1); | |
318 | break; | |
319 | case 'h': | |
320 | case '?': | |
321 | usage(); | |
322 | exit(1); | |
323 | break; | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | int | |
329 | main(int argc, char * const argv[]) | |
330 | { | |
331 | struct qnetd_instance instance; | |
332 | struct qnetd_advanced_settings advanced_settings; | |
333 | char *host_addr; | |
334 | uint16_t host_port; | |
335 | int foreground; | |
336 | int debug_log; | |
337 | int bump_log_priority; | |
338 | enum tlv_tls_supported tls_supported; | |
339 | int client_cert_required; | |
340 | size_t max_clients; | |
341 | PRIntn address_family; | |
342 | int lock_file; | |
343 | int another_instance_running; | |
5128d318 | 344 | int log_target; |
6f55a064 | 345 | int poll_res; |
9a1955a7 JF |
346 | |
347 | if (qnetd_advanced_settings_init(&advanced_settings) != 0) { | |
348 | errx(1, "Can't alloc memory for advanced settings"); | |
349 | } | |
350 | ||
351 | cli_parse(argc, argv, &host_addr, &host_port, &foreground, &debug_log, &bump_log_priority, | |
352 | &tls_supported, &client_cert_required, &max_clients, &address_family, &advanced_settings); | |
353 | ||
5128d318 | 354 | log_target = LOG_TARGET_SYSLOG; |
9a1955a7 | 355 | if (foreground) { |
5128d318 JF |
356 | log_target |= LOG_TARGET_STDERR; |
357 | } | |
358 | ||
c8d19612 | 359 | if (log_init(QNETD_PROGRAM_NAME, log_target, LOG_DAEMON) == -1) { |
5128d318 | 360 | errx(1, "Can't initialize logging"); |
9a1955a7 JF |
361 | } |
362 | ||
bb9dabf9 JF |
363 | log_set_debug(debug_log); |
364 | log_set_priority_bump(bump_log_priority); | |
9a1955a7 | 365 | |
31cc2172 JF |
366 | /* |
367 | * Check that it's possible to open NSS dir if needed | |
368 | */ | |
369 | if (nss_sock_check_db_dir((tls_supported != TLV_TLS_UNSUPPORTED ? | |
370 | advanced_settings.nss_db_dir : NULL)) != 0) { | |
bb9dabf9 | 371 | log_err(LOG_ERR, "Can't open NSS DB directory"); |
31cc2172 | 372 | |
6f55a064 | 373 | return (1); |
31cc2172 JF |
374 | } |
375 | ||
9a1955a7 JF |
376 | /* |
377 | * Daemonize | |
378 | */ | |
379 | if (!foreground) { | |
380 | utils_tty_detach(); | |
381 | } | |
382 | ||
383 | if ((lock_file = utils_flock(advanced_settings.lock_file, getpid(), | |
384 | &another_instance_running)) == -1) { | |
385 | if (another_instance_running) { | |
bb9dabf9 | 386 | log(LOG_ERR, "Another instance is running"); |
9a1955a7 | 387 | } else { |
bb9dabf9 | 388 | log_err(LOG_ERR, "Can't acquire lock"); |
9a1955a7 JF |
389 | } |
390 | ||
6f55a064 | 391 | return (1); |
9a1955a7 JF |
392 | } |
393 | ||
bb9dabf9 | 394 | log(LOG_DEBUG, "Initializing nss"); |
9a1955a7 JF |
395 | if (nss_sock_init_nss((tls_supported != TLV_TLS_UNSUPPORTED ? |
396 | advanced_settings.nss_db_dir : NULL)) != 0) { | |
397 | qnetd_err_nss(); | |
398 | } | |
399 | ||
400 | if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { | |
401 | qnetd_err_nss(); | |
402 | } | |
403 | ||
404 | if (qnetd_instance_init(&instance, tls_supported, client_cert_required, | |
405 | max_clients, &advanced_settings) == -1) { | |
bb9dabf9 | 406 | log(LOG_ERR, "Can't initialize qnetd"); |
6f55a064 | 407 | return (1); |
9a1955a7 JF |
408 | } |
409 | instance.host_addr = host_addr; | |
410 | instance.host_port = host_port; | |
411 | ||
412 | if (tls_supported != TLV_TLS_UNSUPPORTED && qnetd_instance_init_certs(&instance) == -1) { | |
413 | qnetd_err_nss(); | |
414 | } | |
415 | ||
bb9dabf9 | 416 | log(LOG_DEBUG, "Initializing local socket"); |
9a1955a7 JF |
417 | if (qnetd_ipc_init(&instance) != 0) { |
418 | return (1); | |
419 | } | |
420 | ||
bb9dabf9 | 421 | log(LOG_DEBUG, "Creating listening socket"); |
9a1955a7 JF |
422 | instance.server.socket = nss_sock_create_listen_socket(instance.host_addr, |
423 | instance.host_port, address_family); | |
424 | if (instance.server.socket == NULL) { | |
425 | qnetd_err_nss(); | |
426 | } | |
427 | ||
428 | if (nss_sock_set_non_blocking(instance.server.socket) != 0) { | |
429 | qnetd_err_nss(); | |
430 | } | |
431 | ||
432 | if (PR_Listen(instance.server.socket, instance.advanced_settings->listen_backlog) != | |
433 | PR_SUCCESS) { | |
434 | qnetd_err_nss(); | |
435 | } | |
436 | ||
6f55a064 JF |
437 | if (pr_poll_loop_add_prfd(&instance.main_poll_loop, instance.server.socket, POLLIN, |
438 | NULL, | |
439 | server_socket_poll_loop_read_cb, | |
440 | server_socket_poll_loop_write_cb, | |
441 | server_socket_poll_loop_err_cb, | |
442 | &instance, NULL) != 0) { | |
443 | log(LOG_ERR, "Can't add server socket to main poll loop"); | |
444 | ||
445 | return (1); | |
446 | } | |
447 | ||
9a1955a7 JF |
448 | global_instance = &instance; |
449 | signal_handlers_register(); | |
450 | ||
bb9dabf9 | 451 | log(LOG_DEBUG, "Registering algorithms"); |
9a1955a7 | 452 | if (qnetd_algorithm_register_all() != 0) { |
6f55a064 | 453 | return (1); |
9a1955a7 JF |
454 | } |
455 | ||
bb9dabf9 | 456 | log(LOG_DEBUG, "QNetd ready to provide service"); |
9a1955a7 JF |
457 | |
458 | #ifdef HAVE_LIBSYSTEMD | |
459 | sd_notify(0, "READY=1"); | |
460 | #endif | |
461 | ||
462 | /* | |
463 | * MAIN LOOP | |
464 | */ | |
6f55a064 JF |
465 | while ((poll_res = pr_poll_loop_exec(&instance.main_poll_loop)) == 0) { |
466 | } | |
467 | ||
468 | if (poll_res == -2) { | |
469 | log(LOG_CRIT, "pr_poll_loop_exec returned -2 - internal error"); | |
470 | return (1); | |
9a1955a7 JF |
471 | } |
472 | ||
473 | /* | |
474 | * Cleanup | |
475 | */ | |
6f55a064 | 476 | log(LOG_DEBUG, "Destroying qnetd ipc"); |
9a1955a7 JF |
477 | qnetd_ipc_destroy(&instance); |
478 | ||
6f55a064 | 479 | log(LOG_DEBUG, "Closing server socket"); |
9a1955a7 JF |
480 | if (PR_Close(instance.server.socket) != PR_SUCCESS) { |
481 | qnetd_warn_nss(); | |
482 | } | |
483 | ||
484 | CERT_DestroyCertificate(instance.server.cert); | |
485 | SECKEY_DestroyPrivateKey(instance.server.private_key); | |
486 | ||
487 | SSL_ClearSessionCache(); | |
488 | ||
489 | SSL_ShutdownServerSessionIDCache(); | |
490 | ||
491 | qnetd_instance_destroy(&instance); | |
492 | ||
493 | qnetd_advanced_settings_destroy(&advanced_settings); | |
494 | ||
495 | if (NSS_Shutdown() != SECSuccess) { | |
496 | qnetd_warn_nss(); | |
497 | } | |
498 | ||
499 | if (PR_Cleanup() != PR_SUCCESS) { | |
500 | qnetd_warn_nss(); | |
501 | } | |
502 | ||
6f55a064 | 503 | log(LOG_DEBUG, "Closing log"); |
bb9dabf9 | 504 | log_close(); |
9a1955a7 JF |
505 | |
506 | return (0); | |
507 | } |