]> git.proxmox.com Git - mirror_corosync-qdevice.git/blame - qdevices/qdevice-net-instance.c
init: Fix init scripts to work with containers
[mirror_corosync-qdevice.git] / qdevices / qdevice-net-instance.c
CommitLineData
9a1955a7
JF
1/*
2 * Copyright (c) 2015-2017 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
c5081836
JF
35#include <limits.h>
36
9a1955a7
JF
37#include "qdevice-config.h"
38#include "qdevice-log.h"
39#include "qdevice-net-instance.h"
40#include "qnet-config.h"
41#include "utils.h"
42#include "qdevice-net-poll-array-user-data.h"
43#include "qdevice-ipc.h"
44
45/*
46 * Needed for creating nspr handle from unix fd
47 */
48#include <private/pprio.h>
49
50int
51qdevice_net_instance_init(struct qdevice_net_instance *instance,
52 enum tlv_tls_supported tls_supported,
53 enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval,
54 uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
55 const char *host_addr, uint16_t host_port, const char *cluster_name,
56 const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout,
57 int force_ip_version, int cmap_fd, int votequorum_fd, int local_socket_fd,
58 const struct qdevice_advanced_settings *advanced_settings,
59 int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
60 int heuristics_pipe_log_recv_fd)
61{
62
63 memset(instance, 0, sizeof(*instance));
64
65 instance->advanced_settings = advanced_settings;
66 instance->decision_algorithm = decision_algorithm;
67 instance->heartbeat_interval = heartbeat_interval;
68 instance->sync_heartbeat_interval = sync_heartbeat_interval;
69 instance->cast_vote_timer_interval = cast_vote_timer_interval;
70 instance->cast_vote_timer = NULL;
71 instance->host_addr = host_addr;
72 instance->host_port = host_port;
73 instance->cluster_name = cluster_name;
74 instance->connect_timeout = connect_timeout;
75 instance->last_msg_seq_num = 1;
76 instance->echo_request_expected_msg_seq_num = 1;
77 instance->echo_reply_received_msg_seq_num = 1;
78 instance->force_ip_version = force_ip_version;
79 instance->last_echo_reply_received_time = ((time_t) -1);
80 instance->connected_since_time = ((time_t) -1);
81
82 memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker));
83
84 dynar_init(&instance->receive_buffer, advanced_settings->net_initial_msg_receive_size);
85
86 send_buffer_list_init(&instance->send_buffer_list, advanced_settings->net_max_send_buffers,
87 advanced_settings->net_initial_msg_send_size);
88
89 timer_list_init(&instance->main_timer_list);
90
91 pr_poll_array_init(&instance->poll_array, sizeof(struct qdevice_net_poll_array_user_data));
92
93 instance->tls_supported = tls_supported;
94
95 if ((instance->cmap_poll_fd = PR_CreateSocketPollFd(cmap_fd)) == NULL) {
96 qdevice_log_nss(LOG_CRIT, "Can't create NSPR cmap poll fd");
97 return (-1);
98 }
99
100 if ((instance->votequorum_poll_fd = PR_CreateSocketPollFd(votequorum_fd)) == NULL) {
101 qdevice_log_nss(LOG_CRIT, "Can't create NSPR votequorum poll fd");
102 return (-1);
103 }
104
105 if ((instance->ipc_socket_poll_fd = PR_CreateSocketPollFd(local_socket_fd)) == NULL) {
106 qdevice_log_nss(LOG_CRIT, "Can't create NSPR IPC socket poll fd");
107 return (-1);
108 }
109
110 if ((instance->heuristics_pipe_cmd_send_poll_fd =
111 PR_CreateSocketPollFd(heuristics_pipe_cmd_send_fd)) == NULL) {
112 qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command send poll fd");
113 return (-1);
114 }
115
116 if ((instance->heuristics_pipe_cmd_recv_poll_fd =
117 PR_CreateSocketPollFd(heuristics_pipe_cmd_recv_fd)) == NULL) {
118 qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command recv poll fd");
119 return (-1);
120 }
121
122 if ((instance->heuristics_pipe_log_recv_poll_fd =
123 PR_CreateSocketPollFd(heuristics_pipe_log_recv_fd)) == NULL) {
124 qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe log recv poll fd");
125 return (-1);
126 }
127
128 return (0);
129}
130
131void
132qdevice_net_instance_clean(struct qdevice_net_instance *instance)
133{
134
135 dynar_clean(&instance->receive_buffer);
136
137 send_buffer_list_free(&instance->send_buffer_list);
138
139 instance->skipping_msg = 0;
140 instance->msg_already_received_bytes = 0;
141 instance->echo_request_expected_msg_seq_num = instance->echo_reply_received_msg_seq_num;
142 instance->using_tls = 0;
143 instance->tls_client_cert_sent = 0;
144 instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT;
145
146 instance->schedule_disconnect = 0;
147 instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNDEFINED;
148 instance->last_echo_reply_received_time = ((time_t) -1);
149 instance->connected_since_time = ((time_t) -1);
150}
151
152int
153qdevice_net_instance_destroy(struct qdevice_net_instance *instance)
154{
155 struct unix_socket_client *ipc_client;
156 const struct unix_socket_client_list *ipc_client_list;
157 struct qdevice_ipc_user_data *qdevice_ipc_user_data;
158 PRFileDesc *prfd;
159
160 ipc_client_list = &instance->qdevice_instance_ptr->local_ipc.clients;
161
162 TAILQ_FOREACH(ipc_client, ipc_client_list, entries) {
163 qdevice_ipc_user_data = (struct qdevice_ipc_user_data *)ipc_client->user_data;
164 prfd = (PRFileDesc *)qdevice_ipc_user_data->model_data;
165
166 if (PR_DestroySocketPollFd(prfd) != PR_SUCCESS) {
167 qdevice_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd");
168 }
169 }
170
171 dynar_destroy(&instance->receive_buffer);
172
173 send_buffer_list_free(&instance->send_buffer_list);
174
175 pr_poll_array_destroy(&instance->poll_array);
176
177 timer_list_free(&instance->main_timer_list);
178
179 free((void *)instance->cluster_name);
180 free((void *)instance->host_addr);
181
182 if (PR_DestroySocketPollFd(instance->votequorum_poll_fd) != PR_SUCCESS) {
183 qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
184 }
185
186 if (PR_DestroySocketPollFd(instance->cmap_poll_fd) != PR_SUCCESS) {
187 qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
188 }
189
190 if (PR_DestroySocketPollFd(instance->ipc_socket_poll_fd) != PR_SUCCESS) {
191 qdevice_log_nss(LOG_WARNING, "Unable to close local socket poll fd");
192 }
193
194 if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_send_poll_fd) != PR_SUCCESS) {
195 qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command send poll fd");
196 return (-1);
197 }
198
199 if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_recv_poll_fd) != PR_SUCCESS) {
200 qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command recv poll fd");
201 return (-1);
202 }
203
204 if (PR_DestroySocketPollFd(instance->heuristics_pipe_log_recv_poll_fd) != PR_SUCCESS) {
205 qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe log recv poll fd");
206 return (-1);
207 }
208
209 return (0);
210}
211
212int
213qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance)
214{
215 char *str;
216 cmap_handle_t cmap_handle;
217 enum tlv_tls_supported tls_supported;
218 int i;
c5081836 219 long long int lli;
9a1955a7
JF
220 enum tlv_decision_algorithm_type decision_algorithm;
221 struct tlv_tie_breaker tie_breaker;
222 uint32_t heartbeat_interval;
223 uint32_t sync_heartbeat_interval;
224 uint32_t cast_vote_timer_interval;
225 char *host_addr;
226 int host_port;
9a1955a7
JF
227 char *cluster_name;
228 uint32_t connect_timeout;
229 struct qdevice_net_instance *net_instance;
230 int force_ip_version;
231
232 cmap_handle = instance->cmap_handle;
233
234 net_instance = malloc(sizeof(*net_instance));
235 if (net_instance == NULL) {
236 qdevice_log(LOG_ERR, "Can't alloc qdevice_net_instance");
237 return (-1);
238 }
239
240 /*
241 * Check tls
242 */
243 tls_supported = QDEVICE_NET_DEFAULT_TLS_SUPPORTED;
244
245 if (cmap_get_string(cmap_handle, "quorum.device.net.tls", &str) == CS_OK) {
246 if ((i = utils_parse_bool_str(str)) == -1) {
247 if (strcasecmp(str, "required") != 0) {
248 free(str);
249 qdevice_log(LOG_ERR, "quorum.device.net.tls value is not valid.");
250
251 goto error_free_instance;
252 } else {
253 tls_supported = TLV_TLS_REQUIRED;
254 }
255 } else {
256 if (i == 1) {
257 tls_supported = TLV_TLS_SUPPORTED;
258 } else {
259 tls_supported = TLV_TLS_UNSUPPORTED;
260 }
261 }
262
263 free(str);
264 }
265
266 /*
267 * Host
268 */
269 if (cmap_get_string(cmap_handle, "quorum.device.net.host", &str) != CS_OK) {
270 qdevice_log(LOG_ERR, "Qdevice net daemon address is not defined (quorum.device.net.host)");
271 goto error_free_instance;
272 }
273 host_addr = str;
274
275 if (cmap_get_string(cmap_handle, "quorum.device.net.port", &str) == CS_OK) {
5aaabea8 276 if (utils_strtonum(str, 1, UINT16_MAX, &lli) == -1) {
c5081836 277 qdevice_log(LOG_ERR, "quorum.device.net.port must be in range 1-%u", UINT16_MAX);
9a1955a7
JF
278 free(str);
279 goto error_free_host_addr;
280 }
281 free(str);
c5081836
JF
282
283 host_port = lli;
9a1955a7
JF
284 } else {
285 host_port = QNETD_DEFAULT_HOST_PORT;
286 }
287
288 /*
289 * Cluster name
290 */
291 if (cmap_get_string(cmap_handle, "totem.cluster_name", &str) != CS_OK) {
292 qdevice_log(LOG_ERR, "Cluster name (totem.cluster_name) has to be defined.");
293 goto error_free_host_addr;
294 }
295 cluster_name = str;
296
297 /*
298 * Adjust qdevice timeouts to better suit qnetd
299 */
300 cast_vote_timer_interval = instance->heartbeat_interval * 0.5;
301 heartbeat_interval = instance->heartbeat_interval * 0.8;
302 if (heartbeat_interval < instance->advanced_settings->net_heartbeat_interval_min) {
303 qdevice_log(LOG_WARNING, "Heartbeat interval too small %"PRIu32". Adjusting to %"PRIu32".",
304 heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_min);
305 heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_min;
306 }
307 if (heartbeat_interval > instance->advanced_settings->net_heartbeat_interval_max) {
308 qdevice_log(LOG_WARNING, "Heartbeat interval too big %"PRIu32". Adjusting to %"PRIu32".",
309 heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_max);
310 heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_max;
311 }
312 sync_heartbeat_interval = instance->sync_heartbeat_interval * 0.8;
313
314 /*
315 * Choose decision algorithm
316 */
317 if (cmap_get_string(cmap_handle, "quorum.device.net.algorithm", &str) != CS_OK) {
318 decision_algorithm = QDEVICE_NET_DEFAULT_ALGORITHM;
319 } else {
320 if (strcmp(str, "test") == 0) {
321 decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_TEST;
322 } else if (strcmp(str, "ffsplit") == 0) {
323 decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_FFSPLIT;
324 } else if (strcmp(str, "2nodelms") == 0) {
325 decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_2NODELMS;
326 } else if (strcmp(str, "lms") == 0) {
327 decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_LMS;
328 } else {
329 qdevice_log(LOG_ERR, "Unknown decision algorithm %s", str);
330 free(str);
331 goto error_free_cluster_name;
332 }
333
334 free(str);
335 }
336
337 if (decision_algorithm == TLV_DECISION_ALGORITHM_TYPE_TEST &&
338 !instance->advanced_settings->net_test_algorithm_enabled) {
339 qdevice_log(LOG_ERR, "Test algorithm is not enabled. You can force enable it by "
340 "passing -S net_test_algorithm_enabled=on to %s command", QDEVICE_PROGRAM_NAME);
341
342 goto error_free_cluster_name;
343 }
344 /*
345 * Load tie_breaker mode
346 */
347 memset(&tie_breaker, 0, sizeof(tie_breaker));
348
349 if (cmap_get_string(cmap_handle, "quorum.device.net.tie_breaker", &str) != CS_OK) {
350 tie_breaker.mode = QDEVICE_NET_DEFAULT_TIE_BREAKER_MODE;
351 } else {
352 if (strcmp(str, "lowest") == 0) {
353 tie_breaker.mode = TLV_TIE_BREAKER_MODE_LOWEST;
354 } else if (strcmp(str, "highest") == 0) {
355 tie_breaker.mode = TLV_TIE_BREAKER_MODE_HIGHEST;
356 } else {
c5081836 357 if (utils_strtonum(str, 1, UINT32_MAX, &lli) == -1) {
9a1955a7
JF
358 qdevice_log(LOG_ERR, "tie_breaker must be lowest|highest|valid_node_id");
359 free(str);
360 goto error_free_cluster_name;
361 }
362
363 tie_breaker.mode = TLV_TIE_BREAKER_MODE_NODE_ID;
c5081836 364 tie_breaker.node_id = lli;
9a1955a7
JF
365 }
366
367 free(str);
368 }
369
370 /*
371 * Get connect timeout
372 */
373 if (cmap_get_string(cmap_handle, "quorum.device.net.connect_timeout", &str) != CS_OK) {
374 connect_timeout = heartbeat_interval;
375 } else {
c5081836
JF
376 if (utils_strtonum(str, instance->advanced_settings->net_min_connect_timeout,
377 instance->advanced_settings->net_max_connect_timeout, &lli) == -1) {
9a1955a7
JF
378 qdevice_log(LOG_ERR, "connect_timeout must be valid number in "
379 "range <%"PRIu32",%"PRIu32">",
380 instance->advanced_settings->net_min_connect_timeout,
381 instance->advanced_settings->net_max_connect_timeout);
382 free(str);
383 goto error_free_cluster_name;
384 }
385
c5081836 386 connect_timeout = (uint32_t)lli;
9a1955a7
JF
387
388 free(str);
389 }
390
391 if (cmap_get_string(cmap_handle, "quorum.device.net.force_ip_version", &str) != CS_OK) {
392 force_ip_version = 0;
393 } else {
c5081836
JF
394 if ((utils_strtonum(str, 0, 6, &lli) == -1) ||
395 (lli != 0 && lli != 4 && lli != 6)) {
9a1955a7
JF
396 qdevice_log(LOG_ERR, "force_ip_version must be one of 0|4|6");
397 free(str);
398 goto error_free_cluster_name;
399 }
400
c5081836 401 force_ip_version = lli;
9a1955a7
JF
402
403 free(str);
404 }
405
406 /*
407 * Really initialize instance
408 */
409 if (qdevice_net_instance_init(net_instance,
410 tls_supported, decision_algorithm,
411 heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval,
412 host_addr, host_port, cluster_name, &tie_breaker, connect_timeout,
413 force_ip_version,
414 instance->cmap_poll_fd, instance->votequorum_poll_fd,
415 instance->local_ipc.socket, instance->advanced_settings,
416 instance->heuristics_instance.pipe_cmd_send,
417 instance->heuristics_instance.pipe_cmd_recv,
418 instance->heuristics_instance.pipe_log_recv) == -1) {
419 qdevice_log(LOG_ERR, "Can't initialize qdevice-net instance");
420 goto error_free_instance;
421 }
422
423 net_instance->qdevice_instance_ptr = instance;
424 instance->model_data = net_instance;
425
426 return (0);
427
428error_free_cluster_name:
429 free(cluster_name);
430error_free_host_addr:
431 free(host_addr);
432error_free_instance:
433 free(net_instance);
434 return (-1);
435}