1 /*
2 * Copyright (c) 2006-2020 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Steven Dake <sdake@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 MontaVista Software, 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 <config.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <pthread.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <sys/select.h>
46 #include <sys/un.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <limits.h>
50 #include <getopt.h>
51
52 #include <corosync/corotypes.h>
53 #include <corosync/totem/totem.h>
54 #include <corosync/cfg.h>
55 #include <corosync/cmap.h>
56 #include "util.h"
57
58 #define cs_repeat(result, max, code) \
59 do { \
60 int counter = 0; \
61 do { \
62 result = code; \
63 if (result == CS_ERR_TRY_AGAIN) { \
64 sleep(1); \
65 counter++; \
66 } else { \
67 break; \
68 } \
69 } while (counter < max); \
70 } while (0)
71
72 enum user_action {
73 ACTION_NOOP=0,
74 ACTION_LINKSTATUS_GET,
75 ACTION_NODESTATUS_GET,
76 ACTION_RELOAD_CONFIG,
77 ACTION_REOPEN_LOG_FILES,
78 ACTION_SHUTDOW,
79 ACTION_SHOWADDR,
80 ACTION_KILL_NODE,
81 };
82
83 static int node_compare(const void *aptr, const void *bptr)
84 {
85 uint32_t a,b;
86
87 a = *(uint32_t *)aptr;
88 b = *(uint32_t *)bptr;
89
90 return a > b;
91 }
92
93 static int
94 nodestatusget_do (enum user_action action, int brief)
95 {
96 cs_error_t result;
97 corosync_cfg_handle_t handle;
98 cmap_handle_t cmap_handle;
99 char iter_key[CMAP_KEYNAME_MAXLEN];
100 cmap_iter_handle_t iter;
101 unsigned int local_nodeid;
102 unsigned int local_nodeid_index=0;
103 unsigned int other_nodeid_index=0;
104 unsigned int nodeid;
105 int nodeid_match_guard;
106 cmap_value_types_t type;
107 size_t value_len;
108 #ifdef ENABLE_UDPU
109 char *str;
110 #endif
111 char *transport_str = NULL;
112 uint32_t nodeid_list[KNET_MAX_HOST];
113 const char *link_transport[KNET_MAX_LINK];
114 int s = 0;
115 int rc = EXIT_SUCCESS;
116 int transport_number = TOTEM_TRANSPORT_KNET;
117 int i,j;
118 struct corosync_cfg_node_status_v1 node_status;
119
120 result = corosync_cfg_initialize (&handle, NULL);
121 if (result != CS_OK) {
122 fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result);
123 exit (EXIT_FAILURE);
124 }
125
126 result = cmap_initialize (&cmap_handle);
127 if (result != CS_OK) {
128 fprintf (stderr, "Could not initialize corosync cmap API error %d\n", result);
129 exit (EXIT_FAILURE);
130 }
131
132 #ifdef ENABLE_UDPU
133 result = cmap_get_string(cmap_handle, "totem.transport", &str);
134 if (result == CS_OK) {
135 if (strcmp (str, "udpu") == 0) {
136 transport_number = TOTEM_TRANSPORT_UDPU;
137 }
138 if (strcmp (str, "udp") == 0) {
139 transport_number = TOTEM_TRANSPORT_UDP;
140 }
141 transport_str = str;
142 }
143 #endif
144 if (!transport_str) {
145 transport_str = strdup("knet"); /* It's the default */
146 }
147
148 result = corosync_cfg_local_get(handle, &local_nodeid);
149 if (result != CS_OK) {
150 fprintf (stderr, "Could not get the local node id, the error is: %d\n", result);
151 free(transport_str);
152 cmap_finalize(cmap_handle);
153 corosync_cfg_finalize(handle);
154 return EXIT_FAILURE;
155 }
156
157 /* Get a list of nodes. We do it this way rather than using votequorum as cfgtool
158 * needs to be independent of quorum type
159 */
160 result = cmap_iter_init(cmap_handle, "nodelist.node.", &iter);
161 if (result != CS_OK) {
162 fprintf (stderr, "Could not get nodelist from cmap. error %d\n", result);
163 free(transport_str);
164 cmap_finalize(cmap_handle);
165 corosync_cfg_finalize(handle);
166 exit (EXIT_FAILURE);
167 }
168
169 while ((cmap_iter_next(cmap_handle, iter, iter_key, &value_len, &type)) == CS_OK) {
170 nodeid_match_guard = 0;
171 if (sscanf(iter_key, "nodelist.node.%*u.nodeid%n", &nodeid_match_guard) != 0) {
172 continue;
173 }
174 /* check for exact match */
175 if (nodeid_match_guard != strlen(iter_key)) {
176 continue;
177 }
178 if (cmap_get_uint32(cmap_handle, iter_key, &nodeid) == CS_OK) {
179 nodeid_list[s++] = nodeid;
180 }
181 }
182
183 if (s == 0) {
184 fprintf(stderr, "No nodes found in nodelist\n");
185 exit (EXIT_FAILURE);
186 }
187
188 /* It's nice to have these in nodeid order */
189 qsort(nodeid_list, s, sizeof(uint32_t), node_compare);
190
191 /*
192 * Find local and other nodeid index in nodeid_list
193 */
194 for (i = 0; i < s; i++) {
195 if (nodeid_list[i] == local_nodeid) {
196 local_nodeid_index = i;
197 } else {
198 /* Bit of an odd one this. but local node only uses one link (of course, to itself)
199 so if we want to know which links are active across the cluster we need to look
200 at another node (any other) node's link list */
201 other_nodeid_index = i;
202 }
203 }
204
205 /* Get the transport of each link - but set reasonable defaults */
206 if (transport_number == TOTEM_TRANSPORT_KNET) {
207 for (i = 0; i<KNET_MAX_LINK; i++) {
208 link_transport[i] = "udp";
209 }
210 } else {
211 for (i = 0; i<KNET_MAX_LINK; i++) {
212 link_transport[i] = ""; /* No point in displaying "udp" again */
213 }
214 }
215 result = cmap_iter_init(cmap_handle, "totem.interface.", &iter);
216 if (result == CS_OK) { /* it's fine for this to fail, we just use the defaults */
217 while ((cmap_iter_next(cmap_handle, iter, iter_key, &value_len, &type)) == CS_OK) {
218 unsigned int link_number;
219 char *knet_transport;
220 char knet_transport_str[CMAP_KEYNAME_MAXLEN];
221
222 /* transport is (sensibly) indexed by link number */
223 if (sscanf(iter_key, "totem.interface.%u.knet_transport", &link_number) != 1) {
224 continue;
225 }
226 snprintf(knet_transport_str, sizeof(knet_transport_str),
227 "totem.interface.%u.knet_transport", link_number);
228 if (cmap_get_string(cmap_handle, knet_transport_str, &knet_transport) == CS_OK) {
229 link_transport[link_number] = knet_transport;
230 }
231 }
232
233 cmap_iter_finalize(cmap_handle, iter);
234 }
235
236 cmap_finalize(cmap_handle);
237
238 printf ("Local node ID " CS_PRI_NODE_ID ", transport %s\n", local_nodeid, transport_str);
239
240 /* If node status requested then do print node-based info */
241 if (action == ACTION_NODESTATUS_GET) {
242 for (i=0; i<s; i++) {
243 result = corosync_cfg_node_status_get(handle, nodeid_list[i], CFG_NODE_STATUS_V1, &node_status);
244 if (result == CS_OK) {
245 /* Only display node info if it is reachable (and not us) */
246 if (node_status.reachable && node_status.nodeid != local_nodeid) {
247 printf("nodeid: " CS_PRI_NODE_ID "", node_status.nodeid);
248 printf(" reachable");
249 if (node_status.remote) {
250 printf(" remote");
251 }
252 if (node_status.external) {
253 printf(" external");
254 }
255 #ifdef HAVE_KNET_ONWIRE_VER
256 if (transport_number == TOTEM_TRANSPORT_KNET) {
257 printf(" onwire (min/max/cur): %d, %d, %d",
258 node_status.onwire_min,
259 node_status.onwire_max,
260 node_status.onwire_ver);
261 }
262 #endif
263 printf("\n");
264 for (j=0; j<CFG_MAX_LINKS; j++) {
265 if (node_status.link_status[j].enabled) {
266 printf(" LINK: %d %s", j, link_transport[j]);
|
CID (unavailable; MK=5d9c95616e4f464f8eb6fdefe27bd39a) (#2 of 2): Logically dead code (DEADCODE): |
|
(3) Event dead_error_condition: |
The condition "transport_number == TOTEM_TRANSPORT_KNET" must be true. |
|
(4) Event dead_error_line: |
Execution cannot reach the expression """" inside this statement: "printf(" (%s%s%s)", node_st...". |
| Also see events: |
[assignment][const] |
267 printf(" (%s%s%s)",
268 node_status.link_status[j].src_ipaddr,
269 transport_number==TOTEM_TRANSPORT_KNET?"->":"",
270 node_status.link_status[j].dst_ipaddr);
271 if (node_status.link_status[j].enabled) {
272 printf(" enabled");
273 }
274 if (node_status.link_status[j].connected) {
275 printf(" connected");
276 }
277 if (node_status.link_status[j].dynconnected) {
278 printf(" dynconnected");
279 }
280 printf(" mtu: %d\n", node_status.link_status[j].mtu);
281 }
282 }
283 printf("\n");
284 }
285 }
286 }
287 }
288 /* Print in link order */
289 else {
290 struct corosync_cfg_node_status_v1 node_info[s];
291 memset(node_info, 0, sizeof(node_info));
292
293 for (i=0; i<s; i++) {
294 result = corosync_cfg_node_status_get(handle, nodeid_list[i], CFG_NODE_STATUS_V1, &node_info[i]);
295 if (result != CS_OK) {
296 fprintf (stderr, "Could not get the node status for nodeid %d, the error is: %d\n", nodeid_list[i], result);
297 }
298 }
299
300 for (i=0; i<CFG_MAX_LINKS; i++) {
301 if (node_info[other_nodeid_index].link_status[i].enabled) {
302 printf("LINK ID %d %s\n", i, link_transport[i]);
303 if (strcmp(link_transport[i], "sctp") == 0) {
304 printf("**SCTP is deprecated and will be removed in a future release\n");
305 }
306 printf("\taddr\t= %s\n", node_info[other_nodeid_index].link_status[i].src_ipaddr);
307 if (brief) {
308 printf("\tstatus\t= ");
309 for (j=0; j<s; j++) {
310 char status = (node_info[j].link_status[i].enabled |
311 (node_info[j].link_status[i].connected << 1)) + '0';
312 if (j == local_nodeid_index) {
313 status = 'n';
314 }
315 printf("%c", status);
316 }
317 printf("\n");
318 } else {
319 printf("\tstatus:\n");
320 for (j=0; j<s; j++) {
321 printf("\t\tnodeid: " CS_PRI_NODE_ID_PADDED ":\t", node_info[j].nodeid);
322 if (j == local_nodeid_index) {
323 printf("localhost");
324 } else {
325 if (node_info[j].link_status[i].connected) {
326 printf("connected");
327 } else {
328 printf("disconnected");
329 }
330 }
331 printf("\n");
332 }
333 }
334 }
335 }
336 }
337 free(transport_str);
338 corosync_cfg_finalize(handle);
339 return rc;
340 }
341
342
343 static int check_for_reload_errors(void)
344 {
345 cmap_handle_t cmap_handle;
346 cs_error_t result;
347 char *str;
348 int res;
349
350 result = cmap_initialize (&cmap_handle);
351 if (result != CS_OK) {
352 fprintf (stderr, "Could not initialize corosync cmap API error %d\n", result);
353 exit (EXIT_FAILURE);
354 }
355
356 result = cmap_get_string(cmap_handle, "config.reload_error_message", &str);
357 if (result == CS_OK) {
358 printf("ERROR from reload: %s - see syslog for more information\n", str);
359 free(str);
360 res = 1;
361 }
362 else {
363 res = 0;
364 }
365 cmap_finalize(cmap_handle);
366 return res;
367 }
368
369 static int reload_config_do (void)
370 {
371 cs_error_t result;
372 corosync_cfg_handle_t handle;
373 int rc;
374
375 rc = EXIT_SUCCESS;
376
377 printf ("Reloading corosync.conf...\n");
378 result = corosync_cfg_initialize (&handle, NULL);
379 if (result != CS_OK) {
380 fprintf (stderr, "Could not initialize corosync configuration API error %s\n", cs_strerror(result));
381 exit (EXIT_FAILURE);
382 }
383
384 result = corosync_cfg_reload_config (handle);
385 if (result != CS_OK) {
386 fprintf (stderr, "Could not reload configuration. Error %s\n", cs_strerror(result));
387 rc = (int)result;
388 }
389 else {
390 printf ("Done\n");
391 }
392
393 (void)corosync_cfg_finalize (handle);
394
395 if ((rc = check_for_reload_errors())) {
396 fprintf(stderr, "Errors in applying config, corosync.conf might not match the running system\n");
397 }
398
399 return (rc);
400 }
401
402 static int reopen_log_files_do (void)
403 {
404 cs_error_t result;
405 corosync_cfg_handle_t handle;
406 int rc;
407
408 rc = EXIT_SUCCESS;
409
410 result = corosync_cfg_initialize (&handle, NULL);
411 if (result != CS_OK) {
412 fprintf (stderr, "Could not initialize corosync configuration API error %s\n", cs_strerror(result));
413 exit (EXIT_FAILURE);
414 }
415
416 result = corosync_cfg_reopen_log_files (handle);
417 if (result != CS_OK) {
418 fprintf (stderr, "Could not reopen corosync logging files. Error %s\n", cs_strerror(result));
419 rc = (int)result;
420 }
421
422 (void)corosync_cfg_finalize (handle);
423
424 return (rc);
425 }
426
427 static void shutdown_do(int force)
428 {
429 cs_error_t result;
430 corosync_cfg_handle_t handle;
431 corosync_cfg_callbacks_t callbacks;
432 int flag;
433
434 callbacks.corosync_cfg_shutdown_callback = NULL;
435 if (force) {
436 flag = COROSYNC_CFG_SHUTDOWN_FLAG_REGARDLESS;
437 } else {
438 flag = COROSYNC_CFG_SHUTDOWN_FLAG_REQUEST;
439 }
440
441 result = corosync_cfg_initialize (&handle, &callbacks);
442 if (result != CS_OK) {
443 fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result);
444 exit (EXIT_FAILURE);
445 }
446
447 printf ("Shutting down corosync\n");
448 cs_repeat(result, 30, corosync_cfg_try_shutdown (handle, flag));
449 if (result != CS_OK) {
450 fprintf (stderr, "Could not shutdown (error = %d)\n", result);
451 }
452
453 (void)corosync_cfg_finalize (handle);
454 }
455
456 static int showaddrs_do(unsigned int nodeid)
457 {
458 cs_error_t result;
459 corosync_cfg_handle_t handle;
460 int numaddrs;
461 int i;
462 int rc = EXIT_SUCCESS;
463 corosync_cfg_node_address_t addrs[INTERFACE_MAX];
464
465
466 result = corosync_cfg_initialize (&handle, NULL);
467 if (result != CS_OK) {
468 fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result);
469 exit (EXIT_FAILURE);
470 }
471
472 if (corosync_cfg_get_node_addrs(handle, nodeid, INTERFACE_MAX, &numaddrs, addrs) == CS_OK) {
473 for (i=0; i<numaddrs; i++) {
474 char buf[INET6_ADDRSTRLEN];
475 struct sockaddr_storage *ss = (struct sockaddr_storage *)addrs[i].address;
476 struct sockaddr_in *sin = (struct sockaddr_in *)addrs[i].address;
477 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addrs[i].address;
478 void *saddr;
479
480 if (!ss->ss_family) {
481 continue;
482 }
483 if (ss->ss_family == AF_INET6) {
484 saddr = &sin6->sin6_addr;
485 } else {
486 saddr = &sin->sin_addr;
487 }
488
489 inet_ntop(ss->ss_family, saddr, buf, sizeof(buf));
490 if (i != 0) {
491 printf(" ");
492 }
493 printf("%s", buf);
494 }
495 printf("\n");
496 } else {
497 fprintf (stderr, "Could not get node address for nodeid %d\n", nodeid);
498 rc = EXIT_FAILURE;
499 }
500
501
502 (void)corosync_cfg_finalize (handle);
503 return rc;
504 }
505
506
507 static void killnode_do(unsigned int nodeid)
508 {
509 cs_error_t result;
510 corosync_cfg_handle_t handle;
511
512 printf ("Killing node " CS_PRI_NODE_ID "\n", nodeid);
513 result = corosync_cfg_initialize (&handle, NULL);
514 if (result != CS_OK) {
515 fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result);
516 exit (EXIT_FAILURE);
517 }
518 result = corosync_cfg_kill_node (handle, nodeid, "Killed by corosync-cfgtool");
519 if (result != CS_OK) {
520 fprintf (stderr, "Could not kill node (error = %s)\n", cs_strerror(result));
521 exit(EXIT_FAILURE);
522 }
523 (void)corosync_cfg_finalize (handle);
524 }
525
526
527 static void usage_do (void)
528 {
529 printf ("corosync-cfgtool [[-i <interface ip>] [-b] -s] [-R] [-L] [-k nodeid] [-a nodeid] [-h] [-H]\n\n");
530 printf ("A tool for displaying and configuring active parameters within corosync.\n");
531 printf ("options:\n");
532 printf ("\t-i\tFinds only information about the specified interface IP address or link id when used with -s..\n");
533 printf ("\t-s\tDisplays the status of the current links on this node.\n");
534 printf ("\t-n\tDisplays the status of the connected nodes and their links.\n");
535 printf ("\t-b\tDisplays the brief status of the current links on this node when used with -s.\n");
536 printf ("\t-R\tTell all instances of corosync in this cluster to reload corosync.conf.\n");
537 printf ("\t-L\tTell corosync to reopen all logging files.\n");
538 printf ("\t-k\tKill a node identified by node id.\n");
539 printf ("\t-a\tDisplay the IP address(es) of a node\n");
540 printf ("\t-h\tPrint basic usage.\n");
541 printf ("\t-H\tShutdown corosync cleanly on this node.\n");
542 printf ("\t\t--force will shut down corosync regardless of daemon vetos\n");
543 }
544
545 int main (int argc, char *argv[]) {
546 int opt;
547 unsigned int nodeid = 0;
548 char interface_name[128] = "";
549 int rc = EXIT_SUCCESS;
550 enum user_action action = ACTION_NOOP;
551 int brief = 0;
552 long long int l;
553 int option_index = 0;
554 int force_shutdown = 0;
555 const char *options = "i:snbrRLk:a:hH";
556 struct option long_options[] = {
557 {"if", required_argument, 0, 'i'},
558 {"status", no_argument, 0, 's'},
559 {"nodes", no_argument, 0, 'n'},
560 {"brief", no_argument, 0, 'b'},
561 {"reload", no_argument, 0, 'R'},
562 {"reopen", no_argument, 0, 'L'},
563 {"kill", required_argument, 0, 'k'},
564 {"address", required_argument, 0, 'a'},
565 {"shutdown", no_argument, 0, 'H'},
566 {"force", no_argument, 0, 0},
567 {0, 0, 0, 0}
568 };
569
570 while ( (opt = getopt_long(argc, argv, options, long_options, &option_index)) != -1 ) {
571 switch (opt) {
572 case 0: // options with no short equivalent - just --force ATM
573 if (strcmp(long_options[option_index].name, "force") == 0) {
574 force_shutdown = 1;
575 }
576 break;
577 case 'i':
578 strncpy(interface_name, optarg, sizeof(interface_name));
579 interface_name[sizeof(interface_name) - 1] = '\0';
580 break;
581 case 's':
582 action = ACTION_LINKSTATUS_GET;
583 break;
584 case 'n':
585 action = ACTION_NODESTATUS_GET;
586 break;
587 case 'b':
588 brief = 1;
589 break;
590 case 'R':
591 action = ACTION_RELOAD_CONFIG;
592 break;
593 case 'L':
594 action = ACTION_REOPEN_LOG_FILES;
595 break;
596 case 'k':
597 if (util_strtonum(optarg, 1, UINT_MAX, &l) == -1) {
598 fprintf(stderr, "The nodeid was not valid, try a positive number\n");
599 exit(EXIT_FAILURE);
600 }
601 nodeid = l;
602 action = ACTION_KILL_NODE;
603 break;
604 case 'H':
605 action = ACTION_SHUTDOW;
606 break;
607 case 'a':
608 if (util_strtonum(optarg, 1, UINT_MAX, &l) == -1) {
609 fprintf(stderr, "The nodeid was not valid, try a positive number\n");
610 exit(EXIT_FAILURE);
611 }
612 nodeid = l;
613 action = ACTION_SHOWADDR;
614 break;
615 case '?':
616 return (EXIT_FAILURE);
617 break;
618 case 'h':
619 default:
620 break;
621 }
622 }
623 switch(action) {
624 case ACTION_LINKSTATUS_GET:
625 rc = nodestatusget_do(action, brief);
626 break;
627 case ACTION_NODESTATUS_GET:
628 rc = nodestatusget_do(action, brief);
629 break;
630 case ACTION_RELOAD_CONFIG:
631 rc = reload_config_do();
632 break;
633 case ACTION_REOPEN_LOG_FILES:
634 rc = reopen_log_files_do();
635 break;
636 case ACTION_KILL_NODE:
637 killnode_do(nodeid);
638 break;
639 case ACTION_SHUTDOW:
640 shutdown_do(force_shutdown);
641 break;
642 case ACTION_SHOWADDR:
643 rc = showaddrs_do(nodeid);
644 break;
645 case ACTION_NOOP:
646 default:
647 usage_do();
648 break;
649 }
650
651 return (rc);
652 }
653