]> git.proxmox.com Git - mirror_corosync-qdevice.git/blob - qdevices/corosync-qnetd.c
qdevice: Use EXIT_SUCCESS and EXIT_FAILURE codes
[mirror_corosync-qdevice.git] / qdevices / corosync-qnetd.c
1 /*
2 * Copyright (c) 2015-2020 Red Hat, Inc.
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>
38 #include <limits.h>
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"
47 #include "log.h"
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"
54 #include "qnetd-client-net.h"
55 #include "qnetd-client-msg-received.h"
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
80 log_nss(LOG_CRIT, "NSS error");
81
82 exit(EXIT_FAILURE);
83 }
84
85 static void
86 qnetd_warn_nss(void)
87 {
88
89 log_nss(LOG_WARNING, "NSS warning");
90 }
91
92 static int
93 server_socket_poll_loop_read_cb(PRFileDesc *prfd, void *user_data1, void *user_data2)
94 {
95 struct qnetd_instance *instance = (struct qnetd_instance *)user_data1;
96
97 qnetd_client_net_accept(instance);
98
99 return (0);
100 }
101
102 static int
103 server_socket_poll_loop_write_cb(PRFileDesc *prfd, void *user_data1, void *user_data2)
104 {
105
106 /*
107 * Poll write on listen socket -> fatal error
108 */
109 log(LOG_CRIT, "POLL_WRITE on listening socket");
110
111 return (-1);
112 }
113
114 static int
115 server_socket_poll_loop_err_cb(PRFileDesc *prfd, short revents, void *user_data1, void *user_data2)
116 {
117
118 if (revents != POLLNVAL) {
119 /*
120 * Poll ERR on listening socket is fatal error.
121 * POLL_NVAL is used as a signal to quit poll loop.
122 */
123 log(LOG_CRIT, "POLL_ERR (%u) on listening socket", revents);
124 } else {
125 log(LOG_DEBUG, "Listening socket is closed");
126 }
127
128 return (-1);
129 }
130
131 static void
132 signal_int_handler(int sig)
133 {
134
135 log(LOG_DEBUG, "SIGINT received - closing server IPC socket");
136
137 qnetd_ipc_close(global_instance);
138 }
139
140 static void
141 signal_term_handler(int sig)
142 {
143
144 log(LOG_DEBUG, "SIGTERM received - closing server IPC socket");
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(EXIT_FAILURE, "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(EXIT_FAILURE, "Unknown option '%s'", opt);
228 break;
229 case -2:
230 errx(EXIT_FAILURE, "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;
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(EXIT_FAILURE, "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(EXIT_FAILURE, "Can't alloc memory for host addr string");
285 }
286 break;
287 case 'm':
288 if (utils_strtonum(optarg, 0, LLONG_MAX, &tmpll) == -1) {
289 errx(EXIT_FAILURE, "max clients value %s is invalid", optarg);
290 }
291
292 *max_clients = (size_t)tmpll;
293 break;
294 case 'p':
295 if (utils_strtonum(optarg, 1, UINT16_MAX, &tmpll) == -1) {
296 errx(EXIT_FAILURE, "host port must be in range 1-%u", UINT16_MAX);
297 }
298
299 *host_port = tmpll;
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(EXIT_FAILURE, "tls must be one of on, off, req");
313 }
314 break;
315 case 'v':
316 display_version();
317 exit(EXIT_FAILURE);
318 break;
319 case 'h':
320 case '?':
321 usage();
322 exit(EXIT_FAILURE);
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;
344 int log_target;
345 int poll_res;
346
347 if (qnetd_advanced_settings_init(&advanced_settings) != 0) {
348 errx(EXIT_FAILURE, "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
354 log_target = LOG_TARGET_SYSLOG;
355 if (foreground) {
356 log_target |= LOG_TARGET_STDERR;
357 }
358
359 if (log_init(QNETD_PROGRAM_NAME, log_target, LOG_DAEMON) == -1) {
360 errx(EXIT_FAILURE, "Can't initialize logging");
361 }
362
363 log_set_debug(debug_log);
364 log_set_priority_bump(bump_log_priority);
365
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) {
371 log_err(LOG_ERR, "Can't open NSS DB directory");
372
373 return (EXIT_FAILURE);
374 }
375
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) {
386 log(LOG_ERR, "Another instance is running");
387 } else {
388 log_err(LOG_ERR, "Can't acquire lock");
389 }
390
391 return (EXIT_FAILURE);
392 }
393
394 log(LOG_DEBUG, "Initializing nss");
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) {
406 log(LOG_ERR, "Can't initialize qnetd");
407 return (EXIT_FAILURE);
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
416 log(LOG_DEBUG, "Initializing local socket");
417 if (qnetd_ipc_init(&instance) != 0) {
418 return (EXIT_FAILURE);
419 }
420
421 log(LOG_DEBUG, "Creating listening socket");
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
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 (EXIT_FAILURE);
446 }
447
448 global_instance = &instance;
449 signal_handlers_register();
450
451 log(LOG_DEBUG, "Registering algorithms");
452 if (qnetd_algorithm_register_all() != 0) {
453 return (EXIT_FAILURE);
454 }
455
456 log(LOG_DEBUG, "QNetd ready to provide service");
457
458 #ifdef HAVE_LIBSYSTEMD
459 sd_notify(0, "READY=1");
460 #endif
461
462 /*
463 * MAIN LOOP
464 */
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 (EXIT_FAILURE);
471 }
472
473 /*
474 * Cleanup
475 */
476 log(LOG_DEBUG, "Destroying qnetd ipc");
477 qnetd_ipc_destroy(&instance);
478
479 log(LOG_DEBUG, "Closing server socket");
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
503 log(LOG_DEBUG, "Closing log");
504 log_close();
505
506 return (EXIT_SUCCESS);
507 }