1 /*
2 * Copyright (c) 2026 IBM, Inc.
3 *
4 * All rights reserved.
5 *
6 * Authors: Thomas Jones <thomas.jones@ibm.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 <config.h>
36
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <inttypes.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdbool.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <limits.h>
48
49 #include <corosync/totem/totem.h>
50 #include <corosync/cfg.h>
51 #include <corosync/cmap.h>
52 #include <corosync/quorum.h>
53 #include <corosync/votequorum.h>
54 #include "util.h"
55
56 typedef enum {
57 CMD_SHOWSTATUS,
58 CMD_SETINFO,
59 CMD_CLEARINFO,
60 CMD_MONITOR
61 } command_t;
62
63 static void show_usage(const char *name)
64 {
65 printf("usage: \n");
66 printf("%s <options>\n", name);
67 printf("\n");
68 printf(" options:\n");
69 printf("\n");
70 printf(" -s show current extra info as strings\n");
71 printf(" -m constantly monitor for updates\n");
72 printf(" -S <info> set the extra info on the node running the command\n");
73 printf(" -c clear the extra info on the node running the command\n");
74 printf(" -h show this help text\n");
75 printf(" -V show version and exit\n");
76 printf("\n");
77 }
78
79 static cmap_handle_t cmap_handle;
80
81 /*
82 * votequorum bits
83 */
84 static void vq_qdevice_extra_info_fn(
85 votequorum_handle_t handle,
86 uint64_t context,
87 uint32_t nodeid,
88 uint32_t ei_size,
89 void *extra_info);
90 static void vq_quorum_notification_fn_t (
91 votequorum_handle_t handle,
92 uint64_t context,
93 uint32_t quorate,
94 uint32_t node_list_entries,
95 votequorum_node_t node_list[]);
96 static void vq_notification_fn(
97 votequorum_handle_t handle,
98 uint64_t context,
99 votequorum_ring_id_t ring_id,
100 uint32_t node_list_entries,
101 uint32_t node_list[]);
102
103 static votequorum_handle_t v_handle;
104 static votequorum_model_v1_data_t v_callbacks = {
105 .model = VOTEQUORUM_MODEL_V1,
106 .votequorum_quorum_notify_fn = vq_quorum_notification_fn_t,
107 .votequorum_expectedvotes_notify_fn = NULL,
108 .votequorum_nodelist_notify_fn = vq_notification_fn,
109 .votequorum_qdevice_extra_info_fn = vq_qdevice_extra_info_fn,
110 };
111
112 static bool g_quorate = false;
113 static bool g_vq_called = false;
114 static bool g_vq_quorum_called = false;
115 static uint32_t g_node_list_entries = 0;
116 static uint32_t *g_node_list = NULL;
117
118 static uint32_t g_vqnode_list_entries = 0;
119 static votequorum_node_t *g_vqnode_list = NULL;
120
121 static void vq_notification_fn(
122 votequorum_handle_t handle,
123 uint64_t context,
124 votequorum_ring_id_t ring_id,
125 uint32_t node_list_entries,
126 uint32_t node_list[])
127 {
128 (void)handle; (void)context; (void)ring_id;
129 if(g_node_list) {
130 free(g_node_list);
131 }
132
133 g_node_list_entries = node_list_entries;
134 g_node_list = malloc(sizeof(*node_list) * node_list_entries);
135 memcpy(g_node_list, node_list, sizeof(*node_list) * node_list_entries);
136
137 g_vq_called = true;
138 }
139
140 static void vq_quorum_notification_fn_t (
141 votequorum_handle_t handle,
142 uint64_t context,
143 uint32_t quorate,
144 uint32_t node_list_entries,
145 votequorum_node_t node_list[])
146 {
147 if(g_vqnode_list) {
148 free(g_vqnode_list);
149 }
150 g_vqnode_list_entries = node_list_entries;
151 g_vqnode_list = malloc(sizeof(*node_list) * node_list_entries);
152 memcpy(g_vqnode_list, node_list, sizeof(*node_list) * node_list_entries);
153
154 g_quorate = quorate;
155
156 g_vq_quorum_called = true;
157 }
158
159 static int get_quorum_type(char *quorum_type, size_t quorum_type_len)
160 {
161 int err;
162 char *str = NULL;
163
164 if ((quorum_type == NULL) || (quorum_type_len <= 0)) {
165 return -1;
166 }
167
168 err = cmap_get_string(cmap_handle, "quorum.provider", &str);
169 if (err != CS_OK) {
170 goto out;
171 }
172
173 if (str == NULL) {
174 return -1;
175 }
176
177 strncpy(quorum_type, str, quorum_type_len - 1);
178 free(str);
179
180 return 0;
181 out:
182 return err;
183 }
184
185 /*
186 * Returns 1 if 'votequorum' is active. The called then knows that
187 * votequorum calls should work and can provide extra information
188 */
189 static int using_votequorum(void)
190 {
191 char quorumtype[256];
192 int using_voteq;
193
194 memset(quorumtype, 0, sizeof(quorumtype));
195
196 if (get_quorum_type(quorumtype, sizeof(quorumtype))) {
197 return -1;
198 }
199
200 if (strcmp(quorumtype, "corosync_votequorum") == 0) {
201 using_voteq = 1;
202 } else {
203 using_voteq = 0;
204 }
205
206 return using_voteq;
207 }
208
209 static int init_vq_info(void)
210 {
211 int err;
212
213 err = votequorum_trackstart(v_handle, 0LL, CS_TRACK_CURRENT);
214 if (err != CS_OK) {
215 fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
216 goto exit;
217 }
218
219 g_vq_called = false;
220 g_vq_quorum_called = false;
221 while ((!g_vq_called || !g_vq_quorum_called) && err == CS_OK) {
222 err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
223 if (err != CS_OK) {
224 fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
225 goto exit;
226 }
227 }
228
229 exit:
230 if (err != CS_OK) {
231 return EXIT_FAILURE;
232 }
233 return EXIT_SUCCESS;
234 }
235
236 static void print_extra_info(uint32_t ei_size, const char *extra_info)
237 {
238 if (ei_size == 0) {
239 printf("<unset>");
240 return;
241 }
242
243 if (strncmp(extra_info, "Qdsk", 4) == 0) {
244 printf("<Qdisk> key:%s", extra_info+5);
245 return;
246 }
247
248 bool is_printable = extra_info[ei_size-1] == '\0';
249 for (uint32_t i = 0; i < ei_size - 1; i++) {
250 if(!isprint((unsigned char)extra_info[i])) {
251 is_printable = false;
252 }
253 }
254 if (is_printable) {
255 printf("<unknown> %s", extra_info);
256 return;
257 }
258
259 printf("<unknown> %" PRIu32 " bytes: ", ei_size);
260 for (uint32_t i = 0; i < ei_size; i++) {
261 printf("%02hhX ", (unsigned char)extra_info[i]);
262 }
263 }
264
265 static int show_status(void)
266 {
267 int err = CS_OK;
268
269 if (init_vq_info()) {
270 return EXIT_FAILURE;
271 }
272
273 for (uint32_t i = 0; i < g_vqnode_list_entries; i++) {
274 uint32_t ei_size = 0;
275 char buf[VOTEQUORUM_QDEVICE_EXTRA_NODEINFO_MAXSIZE+1] = { 0 };
276 err = votequorum_get_qdevice_extra_info (v_handle, g_vqnode_list[i].nodeid, &ei_size, buf);
277
278 printf(CS_PRI_NODE_ID_PADDED " ", g_vqnode_list[i].nodeid);
279 if (err == CS_OK && ei_size) {
280 print_extra_info(ei_size, buf);
281 printf("\n");
282 } else {
283 printf("<unset>\n");
284 }
285 }
286
287 if (err != CS_OK) {
288 return EXIT_FAILURE;
289 }
290
291 return EXIT_SUCCESS;
292 }
293
294 bool g_monitor_output = false;
295
296 static void vq_qdevice_extra_info_fn(
297 votequorum_handle_t handle,
298 uint64_t context,
299 uint32_t nodeid,
300 uint32_t ei_size,
301 void *extra_info)
302 {
303 if (g_monitor_output) {
304 printf(CS_PRI_NODE_ID_PADDED " ", nodeid);
305 print_extra_info(ei_size, extra_info);
306 printf("\n");
307 }
308 }
309
310 static int monitor_status(void)
311 {
312 int err;
313
314 printf("Watching for updates:\n");
315
316 g_monitor_output = true;
317
318 err = votequorum_trackstart(v_handle, UINT64_C(0), CS_TRACK_CURRENT | CS_TRACK_CHANGES);
319 if (err != CS_OK) {
320 fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
321 goto exit;
322 }
323
324 while (1) {
325 err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
326 if (err != CS_OK) {
327 fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
328 goto exit;
329 }
330 }
331
332 exit:
333 if (err != CS_OK) {
334 return EXIT_FAILURE;
335 }
336
|
CID (unavailable; MK=178a03c5dceb081652adbb7e015d60e9) (#1 of 1): Logically dead code (DEADCODE): |
337 return EXIT_SUCCESS;
338 }
339
340 static int set_info(const char *extra_info)
341 {
342 int err;
343
344 if (init_vq_info()) {
345 return EXIT_FAILURE;
346 }
347
348 uint32_t ei_size = 0;
349 if (extra_info) {
350 ei_size = strlen(extra_info) + 1;
351 }
352
353 err = votequorum_set_qdevice_extra_info(v_handle, ei_size, extra_info);
354 if (err != CS_OK) {
355 fprintf(stderr, "Failed to set extra info %s\n", cs_strerror(err));
356 return EXIT_FAILURE;
357 }
358
359 return EXIT_SUCCESS;
360 }
361
362 static int init_all(void)
363 {
364 if (cmap_initialize(&cmap_handle) != CS_OK) {
365 fprintf(stderr, "Cannot initialize CMAP service\n");
366 cmap_handle = 0;
367 goto out;
368 }
369
370 if (using_votequorum() <= 0) {
371 goto out;
372 }
373
374 if (votequorum_model_initialize(&v_handle, &v_callbacks) != CS_OK) {
375 fprintf(stderr, "Cannot initialise VOTEQUORUM service\n");
376 v_handle = 0;
377 goto out;
378 }
379
380 return 0;
381 out:
382 return -1;
383 }
384
385 static void close_all(void) {
386 if (cmap_handle) {
387 cmap_finalize(cmap_handle);
388 }
389 if (v_handle) {
390 votequorum_finalize(v_handle);
391 }
392 }
393
394 int main(int argc, char *argv[])
395 {
396 const char *options = "VhsmcS:";
397 int opt;
398 int ret = 0;
399 command_t command_opt = CMD_SHOWSTATUS;
400 const char *extra_info = NULL;
401
402 while ( (opt = getopt(argc, argv, options)) != -1 ) {
403 switch (opt) {
404 case 's':
405 command_opt = CMD_SHOWSTATUS;
406 break;
407 case 'm':
408 command_opt = CMD_MONITOR;
409 break;
410 case 'c':
411 command_opt = CMD_CLEARINFO;
412 break;
413 case 'S':
414 command_opt = CMD_SETINFO;
415 extra_info = strdup(optarg);
416 break;
417 case 'V':
418 printf("corosync-qdeitool version: %s\n", VERSION);
419 exit(EXIT_SUCCESS);
420 break;
421 case 'h':
422 show_usage(argv[0]);
423 exit(EXIT_SUCCESS);
424 break;
425 case ':':
426 case '?':
427 default:
428 show_usage(argv[0]);
429 exit(EXIT_FAILURE);
430 break;
431 }
432 }
433
434 if (init_all()) {
435 close_all();
436 exit(EXIT_FAILURE);
437 }
438
439 switch (command_opt) {
440 case CMD_SHOWSTATUS:
441 ret = show_status();
442 break;
443 case CMD_CLEARINFO:
444 case CMD_SETINFO:
445 ret = set_info(extra_info);
446 break;
447 case CMD_MONITOR:
448 ret = monitor_status();
449 break;
450 default:
451 break;
452 }
453
454 close_all();
455 return ret;
456 }
457