corosync  2.4.4
exec/votequorum.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009-2015 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Authors: Christine Caulfield (ccaulfie@redhat.com)
7  * Fabio M. Di Nitto (fdinitto@redhat.com)
8  *
9  * This software licensed under BSD license, the text of which follows:
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are met:
13  *
14  * - Redistributions of source code must retain the above copyright notice,
15  * this list of conditions and the following disclaimer.
16  * - Redistributions in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  * - Neither the name of the MontaVista Software, Inc. nor the names of its
20  * contributors may be used to endorse or promote products derived from this
21  * software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <stdint.h>
42 #include <unistd.h>
43 
44 #include <qb/qbipc_common.h>
45 
46 #include "quorum.h"
47 #include <corosync/corodefs.h>
48 #include <corosync/list.h>
49 #include <corosync/logsys.h>
50 #include <corosync/coroapi.h>
51 #include <corosync/icmap.h>
52 #include <corosync/votequorum.h>
54 
55 #include "service.h"
56 #include "util.h"
57 
58 LOGSYS_DECLARE_SUBSYS ("VOTEQ");
59 
60 /*
61  * interface with corosync
62  */
63 
64 static struct corosync_api_v1 *corosync_api;
65 
66 /*
67  * votequorum global config vars
68  */
69 
70 
71 static char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN];
72 static struct cluster_node *qdevice = NULL;
73 static unsigned int qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
74 static unsigned int qdevice_sync_timeout = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
75 static uint8_t qdevice_can_operate = 1;
76 static void *qdevice_reg_conn = NULL;
77 static uint8_t qdevice_master_wins = 0;
78 
79 static uint8_t two_node = 0;
80 
81 static uint8_t wait_for_all = 0;
82 static uint8_t wait_for_all_status = 0;
83 
84 static enum {ATB_NONE, ATB_LOWEST, ATB_HIGHEST, ATB_LIST} auto_tie_breaker = ATB_NONE, initial_auto_tie_breaker = ATB_NONE;
85 static int lowest_node_id = -1;
86 static int highest_node_id = -1;
87 
88 #define DEFAULT_LMS_WIN 10000
89 static uint8_t last_man_standing = 0;
90 static uint32_t last_man_standing_window = DEFAULT_LMS_WIN;
91 
92 static uint8_t allow_downscale = 0;
93 static uint32_t ev_barrier = 0;
94 
95 static uint8_t ev_tracking = 0;
96 static uint32_t ev_tracking_barrier = 0;
97 static int ev_tracking_fd = -1;
98 
99 /*
100  * votequorum_exec defines/structs/forward definitions
101  */
104  struct qb_ipc_request_header header __attribute__((aligned(8)));
105  uint32_t nodeid;
106  uint32_t votes;
107  uint32_t expected_votes;
108  uint32_t flags;
109 } __attribute__((packed));
110 
112  struct qb_ipc_request_header header __attribute__((aligned(8)));
113  uint32_t nodeid;
114  uint32_t value;
115  uint8_t param;
116  uint8_t _pad0;
117  uint8_t _pad1;
118  uint8_t _pad2;
119 } __attribute__((packed));
120 
122  struct qb_ipc_request_header header __attribute__((aligned(8)));
123  uint32_t operation;
125 } __attribute__((packed));
126 
128  struct qb_ipc_request_header header __attribute__((aligned(8)));
131 } __attribute__((packed));
132 
133 /*
134  * votequorum_exec onwire version (via totem)
135  */
136 
137 #include "votequorum.h"
138 
139 /*
140  * votequorum_exec onwire messages (via totem)
141  */
142 
143 #define MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO 0
144 #define MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE 1
145 #define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG 2
146 #define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE 3
147 
148 static void votequorum_exec_send_expectedvotes_notification(void);
149 static int votequorum_exec_send_quorum_notification(void *conn, uint64_t context);
150 static int votequorum_exec_send_nodelist_notification(void *conn, uint64_t context);
151 
152 #define VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES 1
153 #define VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES 2
154 #define VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA 3
155 
156 static int votequorum_exec_send_reconfigure(uint8_t param, unsigned int nodeid, uint32_t value);
157 
158 /*
159  * used by req_exec_quorum_qdevice_reg
160  */
161 #define VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER 0
162 #define VOTEQUORUM_QDEVICE_OPERATION_REGISTER 1
163 
164 /*
165  * votequorum internal node status/view
166  */
167 
168 #define NODE_FLAGS_QUORATE 1
169 #define NODE_FLAGS_LEAVING 2
170 #define NODE_FLAGS_WFASTATUS 4
171 #define NODE_FLAGS_FIRST 8
172 #define NODE_FLAGS_QDEVICE_REGISTERED 16
173 #define NODE_FLAGS_QDEVICE_ALIVE 32
174 #define NODE_FLAGS_QDEVICE_CAST_VOTE 64
175 #define NODE_FLAGS_QDEVICE_MASTER_WINS 128
176 
177 typedef enum {
181 } nodestate_t;
182 
183 struct cluster_node {
184  int node_id;
185  nodestate_t state;
186  uint32_t votes;
187  uint32_t expected_votes;
188  uint32_t flags;
189  struct list_head list;
190 };
191 
192 /*
193  * votequorum internal quorum status
194  */
195 
196 static uint8_t quorum;
197 static uint8_t cluster_is_quorate;
198 
199 /*
200  * votequorum membership data
201  */
202 
203 static struct cluster_node *us;
204 static struct list_head cluster_members_list;
205 static unsigned int quorum_members[PROCESSOR_COUNT_MAX];
206 static unsigned int previous_quorum_members[PROCESSOR_COUNT_MAX];
207 static unsigned int atb_nodelist[PROCESSOR_COUNT_MAX];
208 static int quorum_members_entries = 0;
209 static int previous_quorum_members_entries = 0;
210 static int atb_nodelist_entries = 0;
211 static struct memb_ring_id quorum_ringid;
212 
213 /*
214  * pre allocate all cluster_nodes + one for qdevice
215  */
216 static struct cluster_node cluster_nodes[PROCESSOR_COUNT_MAX+2];
217 static int cluster_nodes_entries = 0;
218 
219 /*
220  * votequorum tracking
221  */
222 struct quorum_pd {
223  unsigned char track_flags;
226  struct list_head list;
227  void *conn;
228 };
229 
230 static struct list_head trackers_list;
231 
232 /*
233  * votequorum timers
234  */
235 
236 static corosync_timer_handle_t qdevice_timer;
237 static int qdevice_timer_set = 0;
238 static corosync_timer_handle_t last_man_standing_timer;
239 static int last_man_standing_timer_set = 0;
240 static int sync_nodeinfo_sent = 0;
241 static int sync_wait_for_poll_or_timeout = 0;
242 
243 /*
244  * Service Interfaces required by service_message_handler struct
245  */
246 
247 static int sync_in_progress = 0;
248 
249 static void votequorum_sync_init (
250  const unsigned int *trans_list,
251  size_t trans_list_entries,
252  const unsigned int *member_list,
253  size_t member_list_entries,
254  const struct memb_ring_id *ring_id);
255 
256 static int votequorum_sync_process (void);
257 static void votequorum_sync_activate (void);
258 static void votequorum_sync_abort (void);
259 
260 static quorum_set_quorate_fn_t quorum_callback;
261 
262 /*
263  * votequorum_exec handler and definitions
264  */
265 
266 static char *votequorum_exec_init_fn (struct corosync_api_v1 *api);
267 static int votequorum_exec_exit_fn (void);
268 static int votequorum_exec_send_nodeinfo(uint32_t nodeid);
269 
270 static void message_handler_req_exec_votequorum_nodeinfo (
271  const void *message,
272  unsigned int nodeid);
273 static void exec_votequorum_nodeinfo_endian_convert (void *message);
274 
275 static void message_handler_req_exec_votequorum_reconfigure (
276  const void *message,
277  unsigned int nodeid);
278 static void exec_votequorum_reconfigure_endian_convert (void *message);
279 
280 static void message_handler_req_exec_votequorum_qdevice_reg (
281  const void *message,
282  unsigned int nodeid);
283 static void exec_votequorum_qdevice_reg_endian_convert (void *message);
284 
285 static void message_handler_req_exec_votequorum_qdevice_reconfigure (
286  const void *message,
287  unsigned int nodeid);
288 static void exec_votequorum_qdevice_reconfigure_endian_convert (void *message);
289 
290 static struct corosync_exec_handler votequorum_exec_engine[] =
291 {
292  { /* 0 */
293  .exec_handler_fn = message_handler_req_exec_votequorum_nodeinfo,
294  .exec_endian_convert_fn = exec_votequorum_nodeinfo_endian_convert
295  },
296  { /* 1 */
297  .exec_handler_fn = message_handler_req_exec_votequorum_reconfigure,
298  .exec_endian_convert_fn = exec_votequorum_reconfigure_endian_convert
299  },
300  { /* 2 */
301  .exec_handler_fn = message_handler_req_exec_votequorum_qdevice_reg,
302  .exec_endian_convert_fn = exec_votequorum_qdevice_reg_endian_convert
303  },
304  { /* 3 */
305  .exec_handler_fn = message_handler_req_exec_votequorum_qdevice_reconfigure,
306  .exec_endian_convert_fn = exec_votequorum_qdevice_reconfigure_endian_convert
307  },
308 };
309 
310 /*
311  * Library Handler and Functions Definitions
312  */
313 
314 static int quorum_lib_init_fn (void *conn);
315 
316 static int quorum_lib_exit_fn (void *conn);
317 
318 static void qdevice_timer_fn(void *arg);
319 
320 static void message_handler_req_lib_votequorum_getinfo (void *conn,
321  const void *message);
322 
323 static void message_handler_req_lib_votequorum_setexpected (void *conn,
324  const void *message);
325 
326 static void message_handler_req_lib_votequorum_setvotes (void *conn,
327  const void *message);
328 
329 static void message_handler_req_lib_votequorum_trackstart (void *conn,
330  const void *message);
331 
332 static void message_handler_req_lib_votequorum_trackstop (void *conn,
333  const void *message);
334 
335 static void message_handler_req_lib_votequorum_qdevice_register (void *conn,
336  const void *message);
337 
338 static void message_handler_req_lib_votequorum_qdevice_unregister (void *conn,
339  const void *message);
340 
341 static void message_handler_req_lib_votequorum_qdevice_update (void *conn,
342  const void *message);
343 
344 static void message_handler_req_lib_votequorum_qdevice_poll (void *conn,
345  const void *message);
346 
347 static void message_handler_req_lib_votequorum_qdevice_master_wins (void *conn,
348  const void *message);
349 
350 static struct corosync_lib_handler quorum_lib_service[] =
351 {
352  { /* 0 */
353  .lib_handler_fn = message_handler_req_lib_votequorum_getinfo,
355  },
356  { /* 1 */
357  .lib_handler_fn = message_handler_req_lib_votequorum_setexpected,
359  },
360  { /* 2 */
361  .lib_handler_fn = message_handler_req_lib_votequorum_setvotes,
363  },
364  { /* 3 */
365  .lib_handler_fn = message_handler_req_lib_votequorum_trackstart,
367  },
368  { /* 4 */
369  .lib_handler_fn = message_handler_req_lib_votequorum_trackstop,
371  },
372  { /* 5 */
373  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_register,
375  },
376  { /* 6 */
377  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_unregister,
379  },
380  { /* 7 */
381  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_update,
383  },
384  { /* 8 */
385  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_poll,
387  },
388  { /* 9 */
389  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_master_wins,
391  }
392 };
393 
394 static struct corosync_service_engine votequorum_service_engine = {
395  .name = "corosync vote quorum service v1.0",
396  .id = VOTEQUORUM_SERVICE,
397  .priority = 2,
398  .private_data_size = sizeof (struct quorum_pd),
399  .allow_inquorate = CS_LIB_ALLOW_INQUORATE,
400  .flow_control = COROSYNC_LIB_FLOW_CONTROL_REQUIRED,
401  .lib_init_fn = quorum_lib_init_fn,
402  .lib_exit_fn = quorum_lib_exit_fn,
403  .lib_engine = quorum_lib_service,
404  .lib_engine_count = sizeof (quorum_lib_service) / sizeof (struct corosync_lib_handler),
405  .exec_init_fn = votequorum_exec_init_fn,
406  .exec_exit_fn = votequorum_exec_exit_fn,
407  .exec_engine = votequorum_exec_engine,
408  .exec_engine_count = sizeof (votequorum_exec_engine) / sizeof (struct corosync_exec_handler),
409  .sync_init = votequorum_sync_init,
410  .sync_process = votequorum_sync_process,
411  .sync_activate = votequorum_sync_activate,
412  .sync_abort = votequorum_sync_abort
413 };
414 
416 {
417  return (&votequorum_service_engine);
418 }
419 
420 static struct default_service votequorum_service[] = {
421  {
422  .name = "corosync_votequorum",
423  .ver = 0,
425  },
426 };
427 
428 /*
429  * common/utility macros/functions
430  */
431 
432 #define max(a,b) (((a) > (b)) ? (a) : (b))
433 
434 #define list_iterate(v, head) \
435  for (v = (head)->next; v != head; v = v->next)
436 
437 static void node_add_ordered(struct cluster_node *newnode)
438 {
439  struct cluster_node *node = NULL;
440  struct list_head *tmp;
441  struct list_head *newlist = &newnode->list;
442 
443  ENTER();
444 
445  list_iterate(tmp, &cluster_members_list) {
446  node = list_entry(tmp, struct cluster_node, list);
447  if (newnode->node_id < node->node_id) {
448  break;
449  }
450  }
451 
452  if (!node) {
453  list_add(&newnode->list, &cluster_members_list);
454  } else {
455  newlist->prev = tmp->prev;
456  newlist->next = tmp;
457  tmp->prev->next = newlist;
458  tmp->prev = newlist;
459  }
460 
461  LEAVE();
462 }
463 
464 static struct cluster_node *allocate_node(unsigned int nodeid)
465 {
466  struct cluster_node *cl = NULL;
467  struct list_head *tmp;
468 
469  ENTER();
470 
471  if (cluster_nodes_entries <= PROCESSOR_COUNT_MAX + 1) {
472  cl = (struct cluster_node *)&cluster_nodes[cluster_nodes_entries];
473  cluster_nodes_entries++;
474  } else {
475  list_iterate(tmp, &cluster_members_list) {
476  cl = list_entry(tmp, struct cluster_node, list);
477  if (cl->state == NODESTATE_DEAD) {
478  break;
479  }
480  }
481  /*
482  * this should never happen
483  */
484  if (!cl) {
485  log_printf(LOGSYS_LEVEL_CRIT, "Unable to find memory for node %u data!!", nodeid);
486  goto out;
487  }
488  list_del(tmp);
489  }
490 
491  memset(cl, 0, sizeof(struct cluster_node));
492  cl->node_id = nodeid;
493  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
494  node_add_ordered(cl);
495  }
496 
497 out:
498  LEAVE();
499 
500  return cl;
501 }
502 
503 static struct cluster_node *find_node_by_nodeid(unsigned int nodeid)
504 {
505  struct cluster_node *node;
506  struct list_head *tmp;
507 
508  ENTER();
509 
510  if (nodeid == us->node_id) {
511  LEAVE();
512  return us;
513  }
514 
515  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
516  LEAVE();
517  return qdevice;
518  }
519 
520  list_iterate(tmp, &cluster_members_list) {
521  node = list_entry(tmp, struct cluster_node, list);
522  if (node->node_id == nodeid) {
523  LEAVE();
524  return node;
525  }
526  }
527 
528  LEAVE();
529  return NULL;
530 }
531 
532 static void get_lowest_node_id(void)
533 {
534  struct cluster_node *node = NULL;
535  struct list_head *tmp;
536 
537  ENTER();
538 
539  lowest_node_id = us->node_id;
540 
541  list_iterate(tmp, &cluster_members_list) {
542  node = list_entry(tmp, struct cluster_node, list);
543  if ((node->state == NODESTATE_MEMBER) &&
544  (node->node_id < lowest_node_id)) {
545  lowest_node_id = node->node_id;
546  }
547  }
548  log_printf(LOGSYS_LEVEL_DEBUG, "lowest node id: %d us: %d", lowest_node_id, us->node_id);
549  icmap_set_uint32("runtime.votequorum.lowest_node_id", lowest_node_id);
550 
551  LEAVE();
552 }
553 
554 static void get_highest_node_id(void)
555 {
556  struct cluster_node *node = NULL;
557  struct list_head *tmp;
558 
559  ENTER();
560 
561  highest_node_id = us->node_id;
562 
563  list_iterate(tmp, &cluster_members_list) {
564  node = list_entry(tmp, struct cluster_node, list);
565  if ((node->state == NODESTATE_MEMBER) &&
566  (node->node_id > highest_node_id)) {
567  highest_node_id = node->node_id;
568  }
569  }
570  log_printf(LOGSYS_LEVEL_DEBUG, "highest node id: %d us: %d", highest_node_id, us->node_id);
571  icmap_set_uint32("runtime.votequorum.highest_node_id", highest_node_id);
572 
573  LEAVE();
574 }
575 
576 static int check_low_node_id_partition(void)
577 {
578  struct cluster_node *node = NULL;
579  struct list_head *tmp;
580  int found = 0;
581 
582  ENTER();
583 
584  list_iterate(tmp, &cluster_members_list) {
585  node = list_entry(tmp, struct cluster_node, list);
586  if ((node->state == NODESTATE_MEMBER) &&
587  (node->node_id == lowest_node_id)) {
588  found = 1;
589  }
590  }
591 
592  LEAVE();
593  return found;
594 }
595 
596 static int check_high_node_id_partition(void)
597 {
598  struct cluster_node *node = NULL;
599  struct list_head *tmp;
600  int found = 0;
601 
602  ENTER();
603 
604  list_iterate(tmp, &cluster_members_list) {
605  node = list_entry(tmp, struct cluster_node, list);
606  if ((node->state == NODESTATE_MEMBER) &&
607  (node->node_id == highest_node_id)) {
608  found = 1;
609  }
610  }
611 
612  LEAVE();
613  return found;
614 }
615 
616 static int is_in_nodelist(int nodeid, unsigned int *members, int entries)
617 {
618  int i;
619  ENTER();
620 
621  for (i=0; i<entries; i++) {
622  if (nodeid == members[i]) {
623  LEAVE();
624  return 1;
625  }
626  }
627  LEAVE();
628  return 0;
629 }
630 
631 /*
632  * The algorithm for a list of tie-breaker nodes is:
633  * travel the list of nodes in the auto_tie_breaker list,
634  * if the node IS in our current partition, check if the
635  * nodes earlier in the atb list are in the 'previous' partition;
636  * If none are found then we are safe to be quorate, if any are
637  * then we cannot be as we don't know if that node is up or down.
638  * If we don't have a node in the current list we are NOT quorate.
639  * Obviously if we find the first node in the atb list in our
640  * partition then we are quorate.
641  *
642  * Special cases lowest nodeid, and highest nodeid are handled separately.
643  */
644 static int check_auto_tie_breaker(void)
645 {
646  int i, j;
647  int res;
648  ENTER();
649 
650  if (auto_tie_breaker == ATB_LOWEST) {
651  res = check_low_node_id_partition();
652  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LOWEST decision: %d", res);
653  LEAVE();
654  return res;
655  }
656  if (auto_tie_breaker == ATB_HIGHEST) {
657  res = check_high_node_id_partition();
658  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_HIGHEST decision: %d", res);
659  LEAVE();
660  return res;
661  }
662 
663  /* Assume ATB_LIST, we should never be called for ATB_NONE */
664  for (i=0; i < atb_nodelist_entries; i++) {
665  if (is_in_nodelist(atb_nodelist[i], quorum_members, quorum_members_entries)) {
666  /*
667  * Node is in our partition, if any of its predecessors are
668  * in the previous quorum partition then it might be in the
669  * 'other half' (as we've got this far without seeing it here)
670  * and so we can't be quorate.
671  */
672  for (j=0; j<i; j++) {
673  if (is_in_nodelist(atb_nodelist[j], previous_quorum_members, previous_quorum_members_entries)) {
674  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found node %d in previous partition but not here, quorum denied", atb_nodelist[j]);
675  LEAVE();
676  return 0;
677  }
678  }
679 
680  /*
681  * None of the other list nodes were in the previous partition, if there
682  * are enough votes, we can be quorate
683  */
684  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found node %d in current partition, we can be quorate", atb_nodelist[i]);
685  LEAVE();
686  return 1;
687  }
688  }
689  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found no list nodes in current partition, we cannot be quorate");
690  LEAVE();
691  return 0;
692 }
693 
694 /*
695  * atb_string can be either:
696  * 'lowest'
697  * 'highest'
698  * a list of nodeids
699  */
700 static void parse_atb_string(char *atb_string)
701 {
702  char *ptr;
703  long num;
704 
705  ENTER();
706  auto_tie_breaker = ATB_NONE;
707 
708  if (!strcmp(atb_string, "lowest"))
709  auto_tie_breaker = ATB_LOWEST;
710 
711  if (!strcmp(atb_string, "highest"))
712  auto_tie_breaker = ATB_HIGHEST;
713 
714  if (atoi(atb_string)) {
715 
716  atb_nodelist_entries = 0;
717  ptr = atb_string;
718  do {
719  num = strtol(ptr, &ptr, 10);
720  if (num) {
721  log_printf(LOGSYS_LEVEL_DEBUG, "ATB nodelist[%d] = %d", atb_nodelist_entries, num);
722  atb_nodelist[atb_nodelist_entries++] = num;
723  }
724  } while (num);
725 
726  if (atb_nodelist_entries) {
727  auto_tie_breaker = ATB_LIST;
728  }
729  }
730  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
731  log_printf(LOGSYS_LEVEL_DEBUG, "ATB type = %d", auto_tie_breaker);
732 
733  /* Make sure we got something */
734  if (auto_tie_breaker == ATB_NONE) {
735  log_printf(LOGSYS_LEVEL_WARNING, "auto_tie_breaker_nodes is not valid. It must be 'lowest', 'highest' or a space-separated list of node IDs. auto_tie_breaker is disabled");
736  auto_tie_breaker = ATB_NONE;
737  }
738  LEAVE();
739 }
740 
741 static int check_qdevice_master(void)
742 {
743  struct cluster_node *node = NULL;
744  struct list_head *tmp;
745  int found = 0;
746 
747  ENTER();
748 
749  list_iterate(tmp, &cluster_members_list) {
750  node = list_entry(tmp, struct cluster_node, list);
751  if ((node->state == NODESTATE_MEMBER) &&
754  found = 1;
755  }
756  }
757 
758  LEAVE();
759  return found;
760 }
761 
762 static void decode_flags(uint32_t flags)
763 {
764  ENTER();
765 
767  "flags: quorate: %s Leaving: %s WFA Status: %s First: %s Qdevice: %s QdeviceAlive: %s QdeviceCastVote: %s QdeviceMasterWins: %s",
768  (flags & NODE_FLAGS_QUORATE)?"Yes":"No",
769  (flags & NODE_FLAGS_LEAVING)?"Yes":"No",
770  (flags & NODE_FLAGS_WFASTATUS)?"Yes":"No",
771  (flags & NODE_FLAGS_FIRST)?"Yes":"No",
772  (flags & NODE_FLAGS_QDEVICE_REGISTERED)?"Yes":"No",
773  (flags & NODE_FLAGS_QDEVICE_ALIVE)?"Yes":"No",
774  (flags & NODE_FLAGS_QDEVICE_CAST_VOTE)?"Yes":"No",
775  (flags & NODE_FLAGS_QDEVICE_MASTER_WINS)?"Yes":"No");
776 
777  LEAVE();
778 }
779 
780 /*
781  * load/save are copied almost pristine from totemsrp,c
782  */
783 static int load_ev_tracking_barrier(void)
784 {
785  int res = 0;
786  char filename[PATH_MAX];
787 
788  ENTER();
789 
790  snprintf(filename, sizeof(filename) - 1, "%s/ev_tracking", get_run_dir());
791 
792  ev_tracking_fd = open(filename, O_RDWR, 0700);
793  if (ev_tracking_fd != -1) {
794  res = read (ev_tracking_fd, &ev_tracking_barrier, sizeof(uint32_t));
795  close(ev_tracking_fd);
796  if (res == sizeof (uint32_t)) {
797  LEAVE();
798  return 0;
799  }
800  }
801 
802  ev_tracking_barrier = 0;
803  umask(0);
804  ev_tracking_fd = open (filename, O_CREAT|O_RDWR, 0700);
805  if (ev_tracking_fd != -1) {
806  res = write (ev_tracking_fd, &ev_tracking_barrier, sizeof (uint32_t));
807  if ((res == -1) || (res != sizeof (uint32_t))) {
809  "Unable to write to %s", filename);
810  }
811  close(ev_tracking_fd);
812  LEAVE();
813  return 0;
814  }
816  "Unable to create %s file", filename);
817 
818  LEAVE();
819 
820  return -1;
821 }
822 
823 static void update_wait_for_all_status(uint8_t wfa_status)
824 {
825  ENTER();
826 
827  wait_for_all_status = wfa_status;
828  if (wait_for_all_status) {
830  } else {
831  us->flags &= ~NODE_FLAGS_WFASTATUS;
832  }
833  icmap_set_uint8("runtime.votequorum.wait_for_all_status",
834  wait_for_all_status);
835 
836  LEAVE();
837 }
838 
839 static void update_two_node(void)
840 {
841  ENTER();
842 
843  icmap_set_uint8("runtime.votequorum.two_node", two_node);
844 
845  LEAVE();
846 }
847 
848 static void update_ev_barrier(uint32_t expected_votes)
849 {
850  ENTER();
851 
852  ev_barrier = expected_votes;
853  icmap_set_uint32("runtime.votequorum.ev_barrier", ev_barrier);
854 
855  LEAVE();
856 }
857 
858 static void update_qdevice_can_operate(uint8_t status)
859 {
860  ENTER();
861 
862  qdevice_can_operate = status;
863  icmap_set_uint8("runtime.votequorum.qdevice_can_operate", qdevice_can_operate);
864 
865  LEAVE();
866 }
867 
868 static void update_qdevice_master_wins(uint8_t allow)
869 {
870  ENTER();
871 
872  qdevice_master_wins = allow;
873  icmap_set_uint8("runtime.votequorum.qdevice_master_wins", qdevice_master_wins);
874 
875  LEAVE();
876 }
877 
878 static void update_ev_tracking_barrier(uint32_t ev_t_barrier)
879 {
880  int res;
881 
882  ENTER();
883 
884  ev_tracking_barrier = ev_t_barrier;
885  icmap_set_uint32("runtime.votequorum.ev_tracking_barrier", ev_tracking_barrier);
886 
887  if (lseek (ev_tracking_fd, 0, SEEK_SET) != 0) {
889  "Unable to update ev_tracking_barrier on disk data!!!");
890  LEAVE();
891  return;
892  }
893 
894  res = write (ev_tracking_fd, &ev_tracking_barrier, sizeof (uint32_t));
895  if (res != sizeof (uint32_t)) {
897  "Unable to update ev_tracking_barrier on disk data!!!");
898  }
899 #ifdef HAVE_FDATASYNC
900  fdatasync(ev_tracking_fd);
901 #else
902  fsync(ev_tracking_fd);
903 #endif
904 
905  LEAVE();
906 }
907 
908 /*
909  * quorum calculation core bits
910  */
911 
912 static int calculate_quorum(int allow_decrease, unsigned int max_expected, unsigned int *ret_total_votes)
913 {
914  struct list_head *nodelist;
915  struct cluster_node *node;
916  unsigned int total_votes = 0;
917  unsigned int highest_expected = 0;
918  unsigned int newquorum, q1, q2;
919  unsigned int total_nodes = 0;
920 
921  ENTER();
922 
923  if ((allow_downscale) && (allow_decrease) && (max_expected)) {
924  max_expected = max(ev_barrier, max_expected);
925  }
926 
927  list_iterate(nodelist, &cluster_members_list) {
928  node = list_entry(nodelist, struct cluster_node, list);
929 
930  log_printf(LOGSYS_LEVEL_DEBUG, "node %u state=%d, votes=%u, expected=%u",
931  node->node_id, node->state, node->votes, node->expected_votes);
932 
933  if (node->state == NODESTATE_MEMBER) {
934  highest_expected = max(highest_expected, node->expected_votes);
935  total_votes += node->votes;
936  total_nodes++;
937  }
938  }
939 
941  log_printf(LOGSYS_LEVEL_DEBUG, "node 0 state=1, votes=%u", qdevice->votes);
942  total_votes += qdevice->votes;
943  total_nodes++;
944  }
945 
946  if (max_expected > 0) {
947  highest_expected = max_expected;
948  }
949 
950  /*
951  * This quorum calculation is taken from the OpenVMS Cluster Systems
952  * manual, but, then, you guessed that didn't you
953  */
954  q1 = (highest_expected + 2) / 2;
955  q2 = (total_votes + 2) / 2;
956  newquorum = max(q1, q2);
957 
958  /*
959  * Normally quorum never decreases but the system administrator can
960  * force it down by setting expected votes to a maximum value
961  */
962  if (!allow_decrease) {
963  newquorum = max(quorum, newquorum);
964  }
965 
966  /*
967  * The special two_node mode allows each of the two nodes to retain
968  * quorum if the other fails. Only one of the two should live past
969  * fencing (as both nodes try to fence each other in split-brain.)
970  * Also: if there are more than two nodes, force us inquorate to avoid
971  * any damage or confusion.
972  */
973  if (two_node && total_nodes <= 2) {
974  newquorum = 1;
975  }
976 
977  if (ret_total_votes) {
978  *ret_total_votes = total_votes;
979  }
980 
981  LEAVE();
982  return newquorum;
983 }
984 
985 static void update_node_expected_votes(int new_expected_votes)
986 {
987  struct list_head *nodelist;
988  struct cluster_node *node;
989 
990  if (new_expected_votes) {
991  list_iterate(nodelist, &cluster_members_list) {
992  node = list_entry(nodelist, struct cluster_node, list);
993 
994  if (node->state == NODESTATE_MEMBER) {
995  node->expected_votes = new_expected_votes;
996  }
997  }
998  }
999 }
1000 
1001 static void are_we_quorate(unsigned int total_votes)
1002 {
1003  int quorate;
1004  int quorum_change = 0;
1005 
1006  ENTER();
1007 
1008  /*
1009  * wait for all nodes to show up before granting quorum
1010  */
1011 
1012  if ((wait_for_all) && (wait_for_all_status)) {
1013  if (total_votes != us->expected_votes) {
1015  "Waiting for all cluster members. "
1016  "Current votes: %d expected_votes: %d",
1017  total_votes, us->expected_votes);
1018  cluster_is_quorate = 0;
1019  return;
1020  }
1021  update_wait_for_all_status(0);
1022  }
1023 
1024  if (quorum > total_votes) {
1025  quorate = 0;
1026  } else {
1027  quorate = 1;
1028  get_lowest_node_id();
1029  get_highest_node_id();
1030  }
1031 
1032  if ((auto_tie_breaker != ATB_NONE) &&
1033  /* Must be a half (or half-1) split */
1034  (total_votes == (us->expected_votes / 2)) &&
1035  /* If the 'other' partition in a split might have quorum then we can't run ATB */
1036  (previous_quorum_members_entries - quorum_members_entries < quorum) &&
1037  (check_auto_tie_breaker() == 1)) {
1038  quorate = 1;
1039  }
1040 
1041  if ((qdevice_master_wins) &&
1042  (!quorate) &&
1043  (check_qdevice_master() == 1)) {
1044  log_printf(LOGSYS_LEVEL_DEBUG, "node is quorate as part of master_wins partition");
1045  quorate = 1;
1046  }
1047 
1048  if (cluster_is_quorate && !quorate) {
1049  quorum_change = 1;
1050  log_printf(LOGSYS_LEVEL_DEBUG, "quorum lost, blocking activity");
1051  }
1052  if (!cluster_is_quorate && quorate) {
1053  quorum_change = 1;
1054  log_printf(LOGSYS_LEVEL_DEBUG, "quorum regained, resuming activity");
1055  }
1056 
1057  cluster_is_quorate = quorate;
1058  if (cluster_is_quorate) {
1059  us->flags |= NODE_FLAGS_QUORATE;
1060  } else {
1061  us->flags &= ~NODE_FLAGS_QUORATE;
1062  }
1063 
1064  if (wait_for_all) {
1065  if (quorate) {
1066  update_wait_for_all_status(0);
1067  } else {
1068  update_wait_for_all_status(1);
1069  }
1070  }
1071 
1072  if ((quorum_change) &&
1073  (sync_in_progress == 0)) {
1074  quorum_callback(quorum_members, quorum_members_entries,
1075  cluster_is_quorate, &quorum_ringid);
1076  votequorum_exec_send_quorum_notification(NULL, 0L);
1077  }
1078 
1079  LEAVE();
1080 }
1081 
1082 static void get_total_votes(unsigned int *totalvotes, unsigned int *current_members)
1083 {
1084  unsigned int total_votes = 0;
1085  unsigned int cluster_members = 0;
1086  struct list_head *nodelist;
1087  struct cluster_node *node;
1088 
1089  ENTER();
1090 
1091  list_iterate(nodelist, &cluster_members_list) {
1092  node = list_entry(nodelist, struct cluster_node, list);
1093  if (node->state == NODESTATE_MEMBER) {
1094  cluster_members++;
1095  total_votes += node->votes;
1096  }
1097  }
1098 
1099  if (qdevice->votes) {
1100  total_votes += qdevice->votes;
1101  cluster_members++;
1102  }
1103 
1104  *totalvotes = total_votes;
1105  *current_members = cluster_members;
1106 
1107  LEAVE();
1108 }
1109 
1110 /*
1111  * Recalculate cluster quorum, set quorate and notify changes
1112  */
1113 static void recalculate_quorum(int allow_decrease, int by_current_nodes)
1114 {
1115  unsigned int total_votes = 0;
1116  unsigned int cluster_members = 0;
1117 
1118  ENTER();
1119 
1120  get_total_votes(&total_votes, &cluster_members);
1121 
1122  if (!by_current_nodes) {
1123  cluster_members = 0;
1124  }
1125 
1126  /*
1127  * Keep expected_votes at the highest number of votes in the cluster
1128  */
1129  log_printf(LOGSYS_LEVEL_DEBUG, "total_votes=%d, expected_votes=%d", total_votes, us->expected_votes);
1130  if (total_votes > us->expected_votes) {
1131  us->expected_votes = total_votes;
1132  votequorum_exec_send_expectedvotes_notification();
1133  }
1134 
1135  if ((ev_tracking) &&
1136  (us->expected_votes > ev_tracking_barrier)) {
1137  update_ev_tracking_barrier(us->expected_votes);
1138  }
1139 
1140  quorum = calculate_quorum(allow_decrease, cluster_members, &total_votes);
1141  update_node_expected_votes(cluster_members);
1142 
1143  are_we_quorate(total_votes);
1144 
1145  LEAVE();
1146 }
1147 
1148 /*
1149  * configuration bits and pieces
1150  */
1151 
1152 static int votequorum_read_nodelist_configuration(uint32_t *votes,
1153  uint32_t *nodes,
1154  uint32_t *expected_votes)
1155 {
1156  icmap_iter_t iter;
1157  const char *iter_key;
1158  char tmp_key[ICMAP_KEYNAME_MAXLEN];
1159  uint32_t our_pos, node_pos;
1160  uint32_t nodecount = 0;
1161  uint32_t nodelist_expected_votes = 0;
1162  uint32_t node_votes = 0;
1163  int res = 0;
1164 
1165  ENTER();
1166 
1167  if (icmap_get_uint32("nodelist.local_node_pos", &our_pos) != CS_OK) {
1169  "No nodelist defined or our node is not in the nodelist");
1170  return 0;
1171  }
1172 
1173  iter = icmap_iter_init("nodelist.node.");
1174 
1175  while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1176 
1177  res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
1178  if (res != 2) {
1179  continue;
1180  }
1181 
1182  if (strcmp(tmp_key, "ring0_addr") != 0) {
1183  continue;
1184  }
1185 
1186  nodecount++;
1187 
1188  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.quorum_votes", node_pos);
1189  if (icmap_get_uint32(tmp_key, &node_votes) != CS_OK) {
1190  node_votes = 1;
1191  }
1192 
1193  nodelist_expected_votes = nodelist_expected_votes + node_votes;
1194 
1195  if (node_pos == our_pos) {
1196  *votes = node_votes;
1197  }
1198  }
1199 
1200  *expected_votes = nodelist_expected_votes;
1201  *nodes = nodecount;
1202 
1203  icmap_iter_finalize(iter);
1204 
1205  LEAVE();
1206 
1207  return 1;
1208 }
1209 
1210 static int votequorum_qdevice_is_configured(uint32_t *qdevice_votes)
1211 {
1212  char *qdevice_model = NULL;
1213  int ret = 0;
1214 
1215  ENTER();
1216 
1217  if (icmap_get_string("quorum.device.model", &qdevice_model) == CS_OK) {
1218  if (strlen(qdevice_model)) {
1219  if (icmap_get_uint32("quorum.device.votes", qdevice_votes) != CS_OK) {
1220  *qdevice_votes = -1;
1221  }
1222  if (icmap_get_uint32("quorum.device.timeout", &qdevice_timeout) != CS_OK) {
1223  qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
1224  }
1225  if (icmap_get_uint32("quorum.device.sync_timeout", &qdevice_sync_timeout) != CS_OK) {
1226  qdevice_sync_timeout = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
1227  }
1228  update_qdevice_can_operate(1);
1229  ret = 1;
1230  }
1231 
1232  free(qdevice_model);
1233  }
1234 
1235  LEAVE();
1236 
1237  return ret;
1238 }
1239 
1240 #define VOTEQUORUM_READCONFIG_STARTUP 0
1241 #define VOTEQUORUM_READCONFIG_RUNTIME 1
1242 
1243 static char *votequorum_readconfig(int runtime)
1244 {
1245  uint32_t node_votes = 0, qdevice_votes = 0;
1246  uint32_t node_expected_votes = 0, expected_votes = 0;
1247  uint32_t node_count = 0;
1248  uint8_t atb = 0;
1249  int have_nodelist, have_qdevice;
1250  char *atb_string = NULL;
1251  char *error = NULL;
1252 
1253  ENTER();
1254 
1255  log_printf(LOGSYS_LEVEL_DEBUG, "Reading configuration (runtime: %d)", runtime);
1256 
1257  /*
1258  * Set the few things we re-read at runtime back to their defaults
1259  */
1260  if (runtime) {
1261  two_node = 0;
1262  expected_votes = 0;
1263  /* auto_tie_breaker cannot be changed by config reload, but
1264  * we automatically disable it on odd-sized clusters without
1265  * wait_for_all.
1266  * We may need to re-enable it when membership changes to ensure
1267  * that auto_tie_breaker is consistent across all nodes */
1268  auto_tie_breaker = initial_auto_tie_breaker;
1269  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1270  }
1271 
1272  /*
1273  * gather basic data here
1274  */
1275  icmap_get_uint32("quorum.expected_votes", &expected_votes);
1276  have_nodelist = votequorum_read_nodelist_configuration(&node_votes, &node_count, &node_expected_votes);
1277  have_qdevice = votequorum_qdevice_is_configured(&qdevice_votes);
1278  icmap_get_uint8("quorum.two_node", &two_node);
1279 
1280  /*
1281  * do config verification and enablement
1282  */
1283 
1284  if ((!have_nodelist) && (!expected_votes)) {
1285  if (!runtime) {
1286  error = (char *)"configuration error: nodelist or quorum.expected_votes must be configured!";
1287  } else {
1288  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: nodelist or quorum.expected_votes must be configured!");
1289  log_printf(LOGSYS_LEVEL_CRIT, "will continue with current runtime data");
1290  }
1291  goto out;
1292  }
1293 
1294  /*
1295  * two_node and qdevice are not compatible in the same config.
1296  * try to make an educated guess of what to do
1297  */
1298 
1299  if ((two_node) && (have_qdevice)) {
1300  if (!runtime) {
1301  error = (char *)"configuration error: two_node and quorum device cannot be configured at the same time!";
1302  goto out;
1303  } else {
1304  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: two_node and quorum device cannot be configured at the same time!");
1306  log_printf(LOGSYS_LEVEL_CRIT, "quorum device is registered, disabling two_node");
1307  two_node = 0;
1308  } else {
1309  log_printf(LOGSYS_LEVEL_CRIT, "quorum device is not registered, allowing two_node");
1310  update_qdevice_can_operate(0);
1311  }
1312  }
1313  }
1314 
1315  /*
1316  * Enable special features
1317  */
1318  if (!runtime) {
1319  if (two_node) {
1320  wait_for_all = 1;
1321  }
1322 
1323  icmap_get_uint8("quorum.allow_downscale", &allow_downscale);
1324  icmap_get_uint8("quorum.wait_for_all", &wait_for_all);
1325  icmap_get_uint8("quorum.last_man_standing", &last_man_standing);
1326  icmap_get_uint32("quorum.last_man_standing_window", &last_man_standing_window);
1327  icmap_get_uint8("quorum.expected_votes_tracking", &ev_tracking);
1328  icmap_get_uint8("quorum.auto_tie_breaker", &atb);
1329  icmap_get_string("quorum.auto_tie_breaker_node", &atb_string);
1330 
1331  /* auto_tie_breaker defaults to LOWEST */
1332  if (atb) {
1333  auto_tie_breaker = ATB_LOWEST;
1334  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1335  }
1336  else {
1337  auto_tie_breaker = ATB_NONE;
1338  if (atb_string) {
1340  "auto_tie_breaker_node: is meaningless if auto_tie_breaker is set to 0");
1341  }
1342  }
1343 
1344  if (atb && atb_string) {
1345  parse_atb_string(atb_string);
1346  }
1347  free(atb_string);
1348  initial_auto_tie_breaker = auto_tie_breaker;
1349 
1350  /* allow_downscale requires ev_tracking */
1351  if (allow_downscale) {
1352  ev_tracking = 1;
1353  }
1354 
1355  if (ev_tracking) {
1356  if (load_ev_tracking_barrier() < 0) {
1357  LEAVE();
1358  return ((char *)"Unable to load ev_tracking file!");
1359  }
1360  update_ev_tracking_barrier(ev_tracking_barrier);
1361  }
1362 
1363  }
1364 
1365  /* two_node and auto_tie_breaker are not compatible as two_node uses
1366  * a fence race to decide quorum whereas ATB decides based on node id
1367  */
1368  if (two_node && auto_tie_breaker != ATB_NONE) {
1369  log_printf(LOGSYS_LEVEL_CRIT, "two_node and auto_tie_breaker are both specified but are not compatible.");
1370  log_printf(LOGSYS_LEVEL_CRIT, "two_node has been disabled, please fix your corosync.conf");
1371  two_node = 0;
1372  }
1373 
1374  /* If ATB is set and the cluster has an odd number of nodes then wait_for_all needs
1375  * to be set so that an isolated half+1 without the tie breaker node
1376  * does not have quorum on reboot.
1377  */
1378  if ((auto_tie_breaker != ATB_NONE) && (node_expected_votes % 2) &&
1379  (!wait_for_all)) {
1380  if (last_man_standing) {
1381  /* if LMS is set too, it's a fatal configuration error. We can't dictate to the user what
1382  * they might want so we'll just quit.
1383  */
1384  log_printf(LOGSYS_LEVEL_CRIT, "auto_tie_breaker is set, the cluster has an odd number of nodes\n");
1385  log_printf(LOGSYS_LEVEL_CRIT, "and last_man_standing is also set. With this situation a better\n");
1386  log_printf(LOGSYS_LEVEL_CRIT, "solution would be to disable LMS, leave ATB enabled, and also\n");
1387  log_printf(LOGSYS_LEVEL_CRIT, "enable wait_for_all (mandatory for ATB in odd-numbered clusters).\n");
1388  log_printf(LOGSYS_LEVEL_CRIT, "Due to this ambiguity, corosync will fail to start. Please fix your corosync.conf\n");
1389  error = (char *)"configuration error: auto_tie_breaker & last_man_standing not available in odd sized cluster";
1390  goto out;
1391  }
1392  else {
1393  log_printf(LOGSYS_LEVEL_CRIT, "auto_tie_breaker is set and the cluster has an odd number of nodes.\n");
1394  log_printf(LOGSYS_LEVEL_CRIT, "wait_for_all needs to be set for this configuration but it is missing\n");
1395  log_printf(LOGSYS_LEVEL_CRIT, "Therefore auto_tie_breaker has been disabled. Please fix your corosync.conf\n");
1396  auto_tie_breaker = ATB_NONE;
1397  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1398  }
1399  }
1400 
1401  /*
1402  * quorum device is not compatible with last_man_standing and auto_tie_breaker
1403  * neither lms or atb can be set at runtime, so there is no need to check for
1404  * runtime incompatibilities, but qdevice can be configured _after_ LMS and ATB have
1405  * been enabled at startup.
1406  */
1407 
1408  if ((have_qdevice) && (last_man_standing)) {
1409  if (!runtime) {
1410  error = (char *)"configuration error: quorum.device is not compatible with last_man_standing";
1411  goto out;
1412  } else {
1413  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with last_man_standing");
1414  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1415  update_qdevice_can_operate(0);
1416  }
1417  }
1418 
1419  if ((have_qdevice) && (auto_tie_breaker != ATB_NONE)) {
1420  if (!runtime) {
1421  error = (char *)"configuration error: quorum.device is not compatible with auto_tie_breaker";
1422  goto out;
1423  } else {
1424  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with auto_tie_breaker");
1425  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1426  update_qdevice_can_operate(0);
1427  }
1428  }
1429 
1430  if ((have_qdevice) && (allow_downscale)) {
1431  if (!runtime) {
1432  error = (char *)"configuration error: quorum.device is not compatible with allow_downscale";
1433  goto out;
1434  } else {
1435  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with allow_downscale");
1436  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1437  update_qdevice_can_operate(0);
1438  }
1439  }
1440 
1441  /*
1442  * if user specifies quorum.expected_votes + quorum.device but NOT the device.votes
1443  * we don't know what the quorum device should vote.
1444  */
1445 
1446  if ((expected_votes) && (have_qdevice) && (qdevice_votes == -1)) {
1447  if (!runtime) {
1448  error = (char *)"configuration error: quorum.device.votes must be specified when quorum.expected_votes is set";
1449  goto out;
1450  } else {
1451  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes must be specified when quorum.expected_votes is set");
1452  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1453  update_qdevice_can_operate(0);
1454  }
1455  }
1456 
1457  /*
1458  * if user specifies a node list with uneven votes and no device.votes
1459  * we cannot autocalculate the votes
1460  */
1461 
1462  if ((have_qdevice) &&
1463  (qdevice_votes == -1) &&
1464  (have_nodelist) &&
1465  (node_count != node_expected_votes)) {
1466  if (!runtime) {
1467  error = (char *)"configuration error: quorum.device.votes must be specified when not all nodes votes 1";
1468  goto out;
1469  } else {
1470  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes must be specified when not all nodes votes 1");
1471  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1472  update_qdevice_can_operate(0);
1473  }
1474  }
1475 
1476  /*
1477  * validate quorum device votes vs expected_votes
1478  */
1479 
1480  if ((qdevice_votes > 0) && (expected_votes)) {
1481  int delta = expected_votes - qdevice_votes;
1482  if (delta < 2) {
1483  if (!runtime) {
1484  error = (char *)"configuration error: quorum.device.votes is too high or expected_votes is too low";
1485  goto out;
1486  } else {
1487  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes is too high or expected_votes is too low");
1488  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1489  update_qdevice_can_operate(0);
1490  }
1491  }
1492  }
1493 
1494  /*
1495  * automatically calculate device votes and adjust expected_votes from nodelist
1496  */
1497 
1498  if ((have_qdevice) &&
1499  (qdevice_votes == -1) &&
1500  (!expected_votes) &&
1501  (have_nodelist) &&
1502  (node_count == node_expected_votes)) {
1503  qdevice_votes = node_expected_votes - 1;
1504  node_expected_votes = node_expected_votes + qdevice_votes;
1505  }
1506 
1507  /*
1508  * set this node votes and expected_votes
1509  */
1510  log_printf(LOGSYS_LEVEL_DEBUG, "ev_tracking=%d, ev_tracking_barrier = %d: expected_votes = %d\n", ev_tracking, ev_tracking_barrier, expected_votes);
1511 
1512  if (ev_tracking) {
1513  expected_votes = ev_tracking_barrier;
1514  }
1515 
1516  if (have_nodelist) {
1517  us->votes = node_votes;
1518  us->expected_votes = node_expected_votes;
1519  } else {
1520  us->votes = 1;
1521  icmap_get_uint32("quorum.votes", &us->votes);
1522  }
1523 
1524  if (expected_votes) {
1526  }
1527 
1528  /*
1529  * set qdevice votes
1530  */
1531 
1532  if (!have_qdevice) {
1533  qdevice->votes = 0;
1534  }
1535 
1536  if (qdevice_votes != -1) {
1537  qdevice->votes = qdevice_votes;
1538  }
1539 
1540  update_ev_barrier(us->expected_votes);
1541  update_two_node();
1542  if (wait_for_all) {
1543  update_wait_for_all_status(1);
1544  }
1545 
1546 out:
1547  LEAVE();
1548  return error;
1549 }
1550 
1551 static void votequorum_refresh_config(
1552  int32_t event,
1553  const char *key_name,
1554  struct icmap_notify_value new_val,
1555  struct icmap_notify_value old_val,
1556  void *user_data)
1557 {
1558  int old_votes, old_expected_votes;
1559  uint8_t reloading;
1560  uint8_t cancel_wfa;
1561 
1562  ENTER();
1563 
1564  /*
1565  * If a full reload is in progress then don't do anything until it's done and
1566  * can reconfigure it all atomically
1567  */
1568  if (icmap_get_uint8("config.totemconfig_reload_in_progress", &reloading) == CS_OK && reloading) {
1569  return ;
1570  }
1571 
1572  icmap_get_uint8("quorum.cancel_wait_for_all", &cancel_wfa);
1573  if (strcmp(key_name, "quorum.cancel_wait_for_all") == 0 &&
1574  cancel_wfa >= 1) {
1575  icmap_set_uint8("quorum.cancel_wait_for_all", 0);
1576  votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA,
1577  us->node_id, 0);
1578  return;
1579  }
1580 
1581  old_votes = us->votes;
1582  old_expected_votes = us->expected_votes;
1583 
1584  /*
1585  * Reload the configuration
1586  */
1587  votequorum_readconfig(VOTEQUORUM_READCONFIG_RUNTIME);
1588 
1589  /*
1590  * activate new config
1591  */
1592  votequorum_exec_send_nodeinfo(us->node_id);
1593  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
1594  if (us->votes != old_votes) {
1595  votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES,
1596  us->node_id, us->votes);
1597  }
1598  if (us->expected_votes != old_expected_votes) {
1599  votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES,
1600  us->node_id, us->expected_votes);
1601  }
1602 
1603  LEAVE();
1604 }
1605 
1606 static void votequorum_exec_add_config_notification(void)
1607 {
1608  icmap_track_t icmap_track_nodelist = NULL;
1609  icmap_track_t icmap_track_quorum = NULL;
1610  icmap_track_t icmap_track_reload = NULL;
1611 
1612  ENTER();
1613 
1614  icmap_track_add("nodelist.",
1616  votequorum_refresh_config,
1617  NULL,
1618  &icmap_track_nodelist);
1619 
1620  icmap_track_add("quorum.",
1622  votequorum_refresh_config,
1623  NULL,
1624  &icmap_track_quorum);
1625 
1626  icmap_track_add("config.totemconfig_reload_in_progress",
1628  votequorum_refresh_config,
1629  NULL,
1630  &icmap_track_reload);
1631 
1632  LEAVE();
1633 }
1634 
1635 /*
1636  * votequorum_exec core
1637  */
1638 
1639 static int votequorum_exec_send_reconfigure(uint8_t param, unsigned int nodeid, uint32_t value)
1640 {
1641  struct req_exec_quorum_reconfigure req_exec_quorum_reconfigure;
1642  struct iovec iov[1];
1643  int ret;
1644 
1645  ENTER();
1646 
1647  req_exec_quorum_reconfigure.nodeid = nodeid;
1648  req_exec_quorum_reconfigure.value = value;
1649  req_exec_quorum_reconfigure.param = param;
1650  req_exec_quorum_reconfigure._pad0 = 0;
1651  req_exec_quorum_reconfigure._pad1 = 0;
1652  req_exec_quorum_reconfigure._pad2 = 0;
1653 
1654  req_exec_quorum_reconfigure.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE);
1655  req_exec_quorum_reconfigure.header.size = sizeof(req_exec_quorum_reconfigure);
1656 
1657  iov[0].iov_base = (void *)&req_exec_quorum_reconfigure;
1658  iov[0].iov_len = sizeof(req_exec_quorum_reconfigure);
1659 
1660  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1661 
1662  LEAVE();
1663  return ret;
1664 }
1665 
1666 static int votequorum_exec_send_nodeinfo(uint32_t nodeid)
1667 {
1668  struct req_exec_quorum_nodeinfo req_exec_quorum_nodeinfo;
1669  struct iovec iov[1];
1670  struct cluster_node *node;
1671  int ret;
1672 
1673  ENTER();
1674 
1675  node = find_node_by_nodeid(nodeid);
1676  if (!node) {
1677  return -1;
1678  }
1679 
1680  req_exec_quorum_nodeinfo.nodeid = nodeid;
1681  req_exec_quorum_nodeinfo.votes = node->votes;
1682  req_exec_quorum_nodeinfo.expected_votes = node->expected_votes;
1683  req_exec_quorum_nodeinfo.flags = node->flags;
1684  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
1685  decode_flags(node->flags);
1686  }
1687 
1688  req_exec_quorum_nodeinfo.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO);
1689  req_exec_quorum_nodeinfo.header.size = sizeof(req_exec_quorum_nodeinfo);
1690 
1691  iov[0].iov_base = (void *)&req_exec_quorum_nodeinfo;
1692  iov[0].iov_len = sizeof(req_exec_quorum_nodeinfo);
1693 
1694  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1695 
1696  LEAVE();
1697  return ret;
1698 }
1699 
1700 static int votequorum_exec_send_qdevice_reconfigure(const char *oldname, const char *newname)
1701 {
1702  struct req_exec_quorum_qdevice_reconfigure req_exec_quorum_qdevice_reconfigure;
1703  struct iovec iov[1];
1704  int ret;
1705 
1706  ENTER();
1707 
1708  req_exec_quorum_qdevice_reconfigure.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE);
1709  req_exec_quorum_qdevice_reconfigure.header.size = sizeof(req_exec_quorum_qdevice_reconfigure);
1710  strcpy(req_exec_quorum_qdevice_reconfigure.oldname, oldname);
1711  strcpy(req_exec_quorum_qdevice_reconfigure.newname, newname);
1712 
1713  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reconfigure;
1714  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reconfigure);
1715 
1716  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1717 
1718  LEAVE();
1719  return ret;
1720 }
1721 
1722 static int votequorum_exec_send_qdevice_reg(uint32_t operation, const char *qdevice_name_req)
1723 {
1724  struct req_exec_quorum_qdevice_reg req_exec_quorum_qdevice_reg;
1725  struct iovec iov[1];
1726  int ret;
1727 
1728  ENTER();
1729 
1730  req_exec_quorum_qdevice_reg.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG);
1731  req_exec_quorum_qdevice_reg.header.size = sizeof(req_exec_quorum_qdevice_reg);
1732  req_exec_quorum_qdevice_reg.operation = operation;
1733  strcpy(req_exec_quorum_qdevice_reg.qdevice_name, qdevice_name_req);
1734 
1735  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reg;
1736  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reg);
1737 
1738  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1739 
1740  LEAVE();
1741  return ret;
1742 }
1743 
1744 static int votequorum_exec_send_quorum_notification(void *conn, uint64_t context)
1745 {
1746  struct res_lib_votequorum_quorum_notification *res_lib_votequorum_notification;
1747  struct list_head *tmp;
1748  struct cluster_node *node;
1749  int i = 0;
1750  int cluster_members = 0;
1751  int size;
1752  char buf[sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * (PROCESSOR_COUNT_MAX + 2)];
1753 
1754  ENTER();
1755 
1756  log_printf(LOGSYS_LEVEL_DEBUG, "Sending quorum callback, quorate = %d", cluster_is_quorate);
1757 
1758  list_iterate(tmp, &cluster_members_list) {
1759  node = list_entry(tmp, struct cluster_node, list);
1760  cluster_members++;
1761  }
1763  cluster_members++;
1764  }
1765 
1766  size = sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * cluster_members;
1767 
1768  res_lib_votequorum_notification = (struct res_lib_votequorum_quorum_notification *)&buf;
1769  res_lib_votequorum_notification->quorate = cluster_is_quorate;
1770  res_lib_votequorum_notification->context = context;
1771  res_lib_votequorum_notification->node_list_entries = cluster_members;
1772  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_QUORUM_NOTIFICATION;
1773  res_lib_votequorum_notification->header.size = size;
1774  res_lib_votequorum_notification->header.error = CS_OK;
1775 
1776  /* Send all known nodes and their states */
1777  list_iterate(tmp, &cluster_members_list) {
1778  node = list_entry(tmp, struct cluster_node, list);
1779  res_lib_votequorum_notification->node_list[i].nodeid = node->node_id;
1780  res_lib_votequorum_notification->node_list[i++].state = node->state;
1781  }
1783  res_lib_votequorum_notification->node_list[i].nodeid = VOTEQUORUM_QDEVICE_NODEID;
1784  res_lib_votequorum_notification->node_list[i++].state = qdevice->state;
1785  }
1786 
1787  /* Send it to all interested parties */
1788  if (conn) {
1789  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1790  LEAVE();
1791  return ret;
1792  } else {
1793  struct quorum_pd *qpd;
1794 
1795  list_iterate(tmp, &trackers_list) {
1796  qpd = list_entry(tmp, struct quorum_pd, list);
1797  res_lib_votequorum_notification->context = qpd->tracking_context;
1798  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1799  }
1800  }
1801 
1802  LEAVE();
1803 
1804  return 0;
1805 }
1806 
1807 static int votequorum_exec_send_nodelist_notification(void *conn, uint64_t context)
1808 {
1809  struct res_lib_votequorum_nodelist_notification *res_lib_votequorum_notification;
1810  int i = 0;
1811  int size;
1812  struct list_head *tmp;
1813  char buf[sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries];
1814 
1815  ENTER();
1816 
1817  log_printf(LOGSYS_LEVEL_DEBUG, "Sending nodelist callback. ring_id = %d/%lld", quorum_ringid.rep.nodeid, quorum_ringid.seq);
1818 
1819  size = sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries;
1820 
1821  res_lib_votequorum_notification = (struct res_lib_votequorum_nodelist_notification *)&buf;
1822  res_lib_votequorum_notification->node_list_entries = quorum_members_entries;
1823  res_lib_votequorum_notification->ring_id.nodeid = quorum_ringid.rep.nodeid;
1824  res_lib_votequorum_notification->ring_id.seq = quorum_ringid.seq;
1825  res_lib_votequorum_notification->context = context;
1826 
1827  for (i=0; i<quorum_members_entries; i++) {
1828  res_lib_votequorum_notification->node_list[i] = quorum_members[i];
1829  }
1830 
1831  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_NODELIST_NOTIFICATION;
1832  res_lib_votequorum_notification->header.size = size;
1833  res_lib_votequorum_notification->header.error = CS_OK;
1834 
1835  /* Send it to all interested parties */
1836  if (conn) {
1837  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1838  LEAVE();
1839  return ret;
1840  } else {
1841  struct quorum_pd *qpd;
1842 
1843  list_iterate(tmp, &trackers_list) {
1844  qpd = list_entry(tmp, struct quorum_pd, list);
1845  res_lib_votequorum_notification->context = qpd->tracking_context;
1846  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1847  }
1848  }
1849 
1850  LEAVE();
1851 
1852  return 0;
1853 }
1854 
1855 static void votequorum_exec_send_expectedvotes_notification(void)
1856 {
1857  struct res_lib_votequorum_expectedvotes_notification res_lib_votequorum_expectedvotes_notification;
1858  struct quorum_pd *qpd;
1859  struct list_head *tmp;
1860 
1861  ENTER();
1862 
1863  log_printf(LOGSYS_LEVEL_DEBUG, "Sending expected votes callback");
1864 
1865  res_lib_votequorum_expectedvotes_notification.header.id = MESSAGE_RES_VOTEQUORUM_EXPECTEDVOTES_NOTIFICATION;
1866  res_lib_votequorum_expectedvotes_notification.header.size = sizeof(res_lib_votequorum_expectedvotes_notification);
1867  res_lib_votequorum_expectedvotes_notification.header.error = CS_OK;
1868  res_lib_votequorum_expectedvotes_notification.expected_votes = us->expected_votes;
1869 
1870  list_iterate(tmp, &trackers_list) {
1871  qpd = list_entry(tmp, struct quorum_pd, list);
1872  res_lib_votequorum_expectedvotes_notification.context = qpd->tracking_context;
1873  corosync_api->ipc_dispatch_send(qpd->conn, &res_lib_votequorum_expectedvotes_notification,
1874  sizeof(struct res_lib_votequorum_expectedvotes_notification));
1875  }
1876 
1877  LEAVE();
1878 }
1879 
1880 static void exec_votequorum_qdevice_reconfigure_endian_convert (void *message)
1881 {
1882  ENTER();
1883 
1884  LEAVE();
1885 }
1886 
1887 static void message_handler_req_exec_votequorum_qdevice_reconfigure (
1888  const void *message,
1889  unsigned int nodeid)
1890 {
1892 
1893  ENTER();
1894 
1895  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice name change req from node %u [from: %s to: %s]",
1896  nodeid,
1897  req_exec_quorum_qdevice_reconfigure->oldname,
1898  req_exec_quorum_qdevice_reconfigure->newname);
1899 
1900  if (!strcmp(req_exec_quorum_qdevice_reconfigure->oldname, qdevice_name)) {
1901  log_printf(LOGSYS_LEVEL_DEBUG, "Allowing qdevice rename");
1902  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
1903  strcpy(qdevice_name, req_exec_quorum_qdevice_reconfigure->newname);
1904  /*
1905  * TODO: notify qdevices about name change?
1906  * this is not relevant for now and can wait later on since
1907  * qdevices are local only and libvotequorum is not final
1908  */
1909  }
1910 
1911  LEAVE();
1912 }
1913 
1914 static void exec_votequorum_qdevice_reg_endian_convert (void *message)
1915 {
1917 
1918  ENTER();
1919 
1920  req_exec_quorum_qdevice_reg->operation = swab32(req_exec_quorum_qdevice_reg->operation);
1921 
1922  LEAVE();
1923 }
1924 
1925 static void message_handler_req_exec_votequorum_qdevice_reg (
1926  const void *message,
1927  unsigned int nodeid)
1928 {
1930  struct res_lib_votequorum_status res_lib_votequorum_status;
1931  int wipe_qdevice_name = 1;
1932  struct cluster_node *node = NULL;
1933  struct list_head *tmp;
1934  cs_error_t error = CS_OK;
1935 
1936  ENTER();
1937 
1938  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice op %u req from node %u [%s]",
1939  req_exec_quorum_qdevice_reg->operation,
1940  nodeid, req_exec_quorum_qdevice_reg->qdevice_name);
1941 
1942  switch(req_exec_quorum_qdevice_reg->operation)
1943  {
1945  if (nodeid != us->node_id) {
1946  if (!strlen(qdevice_name)) {
1947  log_printf(LOGSYS_LEVEL_DEBUG, "Remote qdevice name recorded");
1948  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1949  }
1950  LEAVE();
1951  return;
1952  }
1953 
1954  /*
1955  * protect against the case where we broadcast qdevice registration
1956  * to new memebers, we receive the message back, but there is no registration
1957  * connection in progress
1958  */
1960  LEAVE();
1961  return;
1962  }
1963 
1964  /*
1965  * this should NEVER happen
1966  */
1967  if (!qdevice_reg_conn) {
1968  log_printf(LOGSYS_LEVEL_WARNING, "Unable to determine origin of the qdevice register call!");
1969  LEAVE();
1970  return;
1971  }
1972 
1973  /*
1974  * registering our own device in this case
1975  */
1976  if (!strlen(qdevice_name)) {
1977  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1978  }
1979 
1980  /*
1981  * check if it is our device or something else
1982  */
1983  if ((!strncmp(req_exec_quorum_qdevice_reg->qdevice_name,
1984  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
1986  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
1987  votequorum_exec_send_nodeinfo(us->node_id);
1988  } else {
1990  "A new qdevice with different name (new: %s old: %s) is trying to register!",
1991  req_exec_quorum_qdevice_reg->qdevice_name, qdevice_name);
1992  error = CS_ERR_EXIST;
1993  }
1994 
1995  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
1996  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
1997  res_lib_votequorum_status.header.error = error;
1998  corosync_api->ipc_response_send(qdevice_reg_conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
1999  qdevice_reg_conn = NULL;
2000  break;
2002  list_iterate(tmp, &cluster_members_list) {
2003  node = list_entry(tmp, struct cluster_node, list);
2004  if ((node->state == NODESTATE_MEMBER) &&
2006  wipe_qdevice_name = 0;
2007  }
2008  }
2009 
2010  if (wipe_qdevice_name) {
2011  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2012  }
2013 
2014  break;
2015  }
2016  LEAVE();
2017 }
2018 
2019 static void exec_votequorum_nodeinfo_endian_convert (void *message)
2020 {
2021  struct req_exec_quorum_nodeinfo *nodeinfo = message;
2022 
2023  ENTER();
2024 
2025  nodeinfo->nodeid = swab32(nodeinfo->nodeid);
2026  nodeinfo->votes = swab32(nodeinfo->votes);
2027  nodeinfo->expected_votes = swab32(nodeinfo->expected_votes);
2028  nodeinfo->flags = swab32(nodeinfo->flags);
2029 
2030  LEAVE();
2031 }
2032 
2033 static void message_handler_req_exec_votequorum_nodeinfo (
2034  const void *message,
2035  unsigned int sender_nodeid)
2036 {
2037  const struct req_exec_quorum_nodeinfo *req_exec_quorum_nodeinfo = message;
2038  struct cluster_node *node = NULL;
2039  int old_votes;
2040  int old_expected;
2041  uint32_t old_flags;
2042  nodestate_t old_state;
2043  int new_node = 0;
2044  int allow_downgrade = 0;
2045  int by_node = 0;
2046  unsigned int nodeid = req_exec_quorum_nodeinfo->nodeid;
2047 
2048  ENTER();
2049 
2050  log_printf(LOGSYS_LEVEL_DEBUG, "got nodeinfo message from cluster node %u", sender_nodeid);
2051  log_printf(LOGSYS_LEVEL_DEBUG, "nodeinfo message[%u]: votes: %d, expected: %d flags: %d",
2052  nodeid,
2053  req_exec_quorum_nodeinfo->votes,
2054  req_exec_quorum_nodeinfo->expected_votes,
2055  req_exec_quorum_nodeinfo->flags);
2056 
2057  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
2058  decode_flags(req_exec_quorum_nodeinfo->flags);
2059  }
2060 
2061  node = find_node_by_nodeid(nodeid);
2062  if (!node) {
2063  node = allocate_node(nodeid);
2064  new_node = 1;
2065  }
2066  if (!node) {
2067  corosync_api->error_memory_failure();
2068  LEAVE();
2069  return;
2070  }
2071 
2072  if (new_node) {
2073  old_votes = 0;
2074  old_expected = 0;
2075  old_state = NODESTATE_DEAD;
2076  old_flags = 0;
2077  } else {
2078  old_votes = node->votes;
2079  old_expected = node->expected_votes;
2080  old_state = node->state;
2081  old_flags = node->flags;
2082  }
2083 
2084  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
2085  struct cluster_node *sender_node = find_node_by_nodeid(sender_nodeid);
2086 
2087  assert(sender_node != NULL);
2088 
2089  if ((!cluster_is_quorate) &&
2090  (sender_node->flags & NODE_FLAGS_QUORATE)) {
2091  node->votes = req_exec_quorum_nodeinfo->votes;
2092  } else {
2093  node->votes = max(node->votes, req_exec_quorum_nodeinfo->votes);
2094  }
2095  goto recalculate;
2096  }
2097 
2098  /* Update node state */
2099  node->flags = req_exec_quorum_nodeinfo->flags;
2100  node->votes = req_exec_quorum_nodeinfo->votes;
2101  node->state = NODESTATE_MEMBER;
2102 
2103  if (node->flags & NODE_FLAGS_LEAVING) {
2104  node->state = NODESTATE_LEAVING;
2105  allow_downgrade = 1;
2106  by_node = 1;
2107  }
2108 
2109  if ((!cluster_is_quorate) &&
2110  (node->flags & NODE_FLAGS_QUORATE)) {
2111  allow_downgrade = 1;
2112  us->expected_votes = req_exec_quorum_nodeinfo->expected_votes;
2113  }
2114 
2115  if (node->flags & NODE_FLAGS_QUORATE || (ev_tracking)) {
2116  node->expected_votes = req_exec_quorum_nodeinfo->expected_votes;
2117  } else {
2118  node->expected_votes = us->expected_votes;
2119  }
2120 
2121  if ((last_man_standing) && (node->votes > 1)) {
2122  log_printf(LOGSYS_LEVEL_WARNING, "Last Man Standing feature is supported only when all"
2123  "cluster nodes votes are set to 1. Disabling LMS.");
2124  last_man_standing = 0;
2125  if (last_man_standing_timer_set) {
2126  corosync_api->timer_delete(last_man_standing_timer);
2127  last_man_standing_timer_set = 0;
2128  }
2129  }
2130 
2131 recalculate:
2132  if ((new_node) ||
2133  (nodeid == us->node_id) ||
2134  (node->flags & NODE_FLAGS_FIRST) ||
2135  (old_votes != node->votes) ||
2136  (old_expected != node->expected_votes) ||
2137  (old_flags != node->flags) ||
2138  (old_state != node->state)) {
2139  recalculate_quorum(allow_downgrade, by_node);
2140  }
2141 
2142  if ((wait_for_all) &&
2143  (!(node->flags & NODE_FLAGS_WFASTATUS)) &&
2144  (node->flags & NODE_FLAGS_QUORATE)) {
2145  update_wait_for_all_status(0);
2146  }
2147 
2148  LEAVE();
2149 }
2150 
2151 static void exec_votequorum_reconfigure_endian_convert (void *message)
2152 {
2153  struct req_exec_quorum_reconfigure *reconfigure = message;
2154 
2155  ENTER();
2156 
2157  reconfigure->nodeid = swab32(reconfigure->nodeid);
2158  reconfigure->value = swab32(reconfigure->value);
2159 
2160  LEAVE();
2161 }
2162 
2163 static void message_handler_req_exec_votequorum_reconfigure (
2164  const void *message,
2165  unsigned int nodeid)
2166 {
2168  struct cluster_node *node;
2169 
2170  ENTER();
2171 
2172  log_printf(LOGSYS_LEVEL_DEBUG, "got reconfigure message from cluster node %u for %u",
2173  nodeid, req_exec_quorum_reconfigure->nodeid);
2174 
2175  switch(req_exec_quorum_reconfigure->param)
2176  {
2178  update_node_expected_votes(req_exec_quorum_reconfigure->value);
2179 
2180  votequorum_exec_send_expectedvotes_notification();
2181  update_ev_barrier(req_exec_quorum_reconfigure->value);
2182  if (ev_tracking) {
2183  us->expected_votes = max(us->expected_votes, ev_tracking_barrier);
2184  }
2185  recalculate_quorum(1, 0); /* Allow decrease */
2186  break;
2187 
2189  node = find_node_by_nodeid(req_exec_quorum_reconfigure->nodeid);
2190  if (!node) {
2191  LEAVE();
2192  return;
2193  }
2194  node->votes = req_exec_quorum_reconfigure->value;
2195  recalculate_quorum(1, 0); /* Allow decrease */
2196  break;
2197 
2199  update_wait_for_all_status(0);
2200  log_printf(LOGSYS_LEVEL_INFO, "wait_for_all_status reset by user on node %d.",
2201  req_exec_quorum_reconfigure->nodeid);
2202  recalculate_quorum(0, 0);
2203 
2204  break;
2205 
2206  }
2207 
2208  LEAVE();
2209 }
2210 
2211 static int votequorum_exec_exit_fn (void)
2212 {
2213  int ret = 0;
2214 
2215  ENTER();
2216 
2217  /*
2218  * tell the other nodes we are leaving
2219  */
2220 
2221  if (allow_downscale) {
2222  us->flags |= NODE_FLAGS_LEAVING;
2223  ret = votequorum_exec_send_nodeinfo(us->node_id);
2224  }
2225 
2226  if ((ev_tracking) && (ev_tracking_fd != -1)) {
2227  close(ev_tracking_fd);
2228  }
2229 
2230 
2231  LEAVE();
2232  return ret;
2233 }
2234 
2235 static void votequorum_set_icmap_ro_keys(void)
2236 {
2237  icmap_set_ro_access("quorum.allow_downscale", CS_FALSE, CS_TRUE);
2238  icmap_set_ro_access("quorum.wait_for_all", CS_FALSE, CS_TRUE);
2239  icmap_set_ro_access("quorum.last_man_standing", CS_FALSE, CS_TRUE);
2240  icmap_set_ro_access("quorum.last_man_standing_window", CS_FALSE, CS_TRUE);
2241  icmap_set_ro_access("quorum.expected_votes_tracking", CS_FALSE, CS_TRUE);
2242  icmap_set_ro_access("quorum.auto_tie_breaker", CS_FALSE, CS_TRUE);
2243  icmap_set_ro_access("quorum.auto_tie_breaker_node", CS_FALSE, CS_TRUE);
2244 }
2245 
2246 static char *votequorum_exec_init_fn (struct corosync_api_v1 *api)
2247 {
2248  char *error = NULL;
2249 
2250  ENTER();
2251 
2252  /*
2253  * make sure we start clean
2254  */
2255  list_init(&cluster_members_list);
2256  list_init(&trackers_list);
2257  qdevice = NULL;
2258  us = NULL;
2259  memset(cluster_nodes, 0, sizeof(cluster_nodes));
2260 
2261  /*
2262  * Allocate a cluster_node for qdevice
2263  */
2264  qdevice = allocate_node(VOTEQUORUM_QDEVICE_NODEID);
2265  if (!qdevice) {
2266  LEAVE();
2267  return ((char *)"Could not allocate node.");
2268  }
2269  qdevice->votes = 0;
2270  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2271 
2272  /*
2273  * Allocate a cluster_node for us
2274  */
2275  us = allocate_node(corosync_api->totem_nodeid_get());
2276  if (!us) {
2277  LEAVE();
2278  return ((char *)"Could not allocate node.");
2279  }
2280 
2281  icmap_set_uint32("runtime.votequorum.this_node_id", us->node_id);
2282 
2283  us->state = NODESTATE_MEMBER;
2284  us->votes = 1;
2285  us->flags |= NODE_FLAGS_FIRST;
2286 
2287  error = votequorum_readconfig(VOTEQUORUM_READCONFIG_STARTUP);
2288  if (error) {
2289  return error;
2290  }
2291  recalculate_quorum(0, 0);
2292 
2293  /*
2294  * Set RO keys in icmap
2295  */
2296  votequorum_set_icmap_ro_keys();
2297 
2298  /*
2299  * Listen for changes
2300  */
2301  votequorum_exec_add_config_notification();
2302 
2303  /*
2304  * Start us off with one node
2305  */
2306  votequorum_exec_send_nodeinfo(us->node_id);
2307 
2308  LEAVE();
2309 
2310  return (NULL);
2311 }
2312 
2313 /*
2314  * votequorum service core
2315  */
2316 
2317 static void votequorum_last_man_standing_timer_fn(void *arg)
2318 {
2319  ENTER();
2320 
2321  last_man_standing_timer_set = 0;
2322  if (cluster_is_quorate) {
2323  recalculate_quorum(1,1);
2324  }
2325 
2326  LEAVE();
2327 }
2328 
2329 static void votequorum_sync_init (
2330  const unsigned int *trans_list, size_t trans_list_entries,
2331  const unsigned int *member_list, size_t member_list_entries,
2332  const struct memb_ring_id *ring_id)
2333 {
2334  int i, j;
2335  int found;
2336  int left_nodes;
2337  struct cluster_node *node;
2338 
2339  ENTER();
2340 
2341  sync_in_progress = 1;
2342  sync_nodeinfo_sent = 0;
2343  sync_wait_for_poll_or_timeout = 0;
2344 
2345  if (member_list_entries > 1) {
2346  us->flags &= ~NODE_FLAGS_FIRST;
2347  }
2348 
2349  /*
2350  * we don't need to track which nodes have left directly,
2351  * since that info is in the node db, but we need to know
2352  * if somebody has left for last_man_standing
2353  */
2354  left_nodes = 0;
2355  for (i = 0; i < quorum_members_entries; i++) {
2356  found = 0;
2357  for (j = 0; j < member_list_entries; j++) {
2358  if (quorum_members[i] == member_list[j]) {
2359  found = 1;
2360  break;
2361  }
2362  }
2363  if (found == 0) {
2364  left_nodes = 1;
2365  node = find_node_by_nodeid(quorum_members[i]);
2366  if (node) {
2367  node->state = NODESTATE_DEAD;
2368  }
2369  }
2370  }
2371 
2372  if (last_man_standing) {
2373  if (((member_list_entries >= quorum) && (left_nodes)) ||
2374  ((member_list_entries <= quorum) && (auto_tie_breaker != ATB_NONE) && (check_low_node_id_partition() == 1))) {
2375  if (last_man_standing_timer_set) {
2376  corosync_api->timer_delete(last_man_standing_timer);
2377  last_man_standing_timer_set = 0;
2378  }
2379  corosync_api->timer_add_duration((unsigned long long)last_man_standing_window*1000000,
2380  NULL, votequorum_last_man_standing_timer_fn,
2381  &last_man_standing_timer);
2382  last_man_standing_timer_set = 1;
2383  }
2384  }
2385 
2386  memcpy(previous_quorum_members, quorum_members, sizeof(unsigned int) * quorum_members_entries);
2387  previous_quorum_members_entries = quorum_members_entries;
2388 
2389  memcpy(quorum_members, member_list, sizeof(unsigned int) * member_list_entries);
2390  quorum_members_entries = member_list_entries;
2391  memcpy(&quorum_ringid, ring_id, sizeof(*ring_id));
2392 
2394  /*
2395  * Reset poll timer. Sync waiting is interrupted on valid qdevice poll or after timeout
2396  */
2397  if (qdevice_timer_set) {
2398  corosync_api->timer_delete(qdevice_timer);
2399  }
2400  corosync_api->timer_add_duration((unsigned long long)qdevice_sync_timeout*1000000, qdevice,
2401  qdevice_timer_fn, &qdevice_timer);
2402  qdevice_timer_set = 1;
2403  sync_wait_for_poll_or_timeout = 1;
2404 
2405  log_printf(LOGSYS_LEVEL_INFO, "waiting for quorum device %s poll (but maximum for %u ms)",
2406  qdevice_name, qdevice_sync_timeout);
2407  }
2408 
2409  LEAVE();
2410 }
2411 
2412 static int votequorum_sync_process (void)
2413 {
2414  if (!sync_nodeinfo_sent) {
2415  votequorum_exec_send_nodeinfo(us->node_id);
2416  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
2417  if (strlen(qdevice_name)) {
2418  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2419  qdevice_name);
2420  }
2421  votequorum_exec_send_nodelist_notification(NULL, 0LL);
2422  sync_nodeinfo_sent = 1;
2423  }
2424 
2425  if (us->flags & NODE_FLAGS_QDEVICE_REGISTERED && sync_wait_for_poll_or_timeout) {
2426  /*
2427  * Waiting for qdevice to poll with new ringid or timeout
2428  */
2429 
2430  return (-1);
2431  }
2432 
2433  return 0;
2434 }
2435 
2436 static void votequorum_sync_activate (void)
2437 {
2438  recalculate_quorum(0, 0);
2439  quorum_callback(quorum_members, quorum_members_entries,
2440  cluster_is_quorate, &quorum_ringid);
2441  votequorum_exec_send_quorum_notification(NULL, 0L);
2442 
2443  sync_in_progress = 0;
2444 }
2445 
2446 static void votequorum_sync_abort (void)
2447 {
2448 
2449 }
2450 
2452  quorum_set_quorate_fn_t q_set_quorate_fn)
2453 {
2454  char *error;
2455 
2456  ENTER();
2457 
2458  if (q_set_quorate_fn == NULL) {
2459  return ((char *)"Quorate function not set");
2460  }
2461 
2462  corosync_api = api;
2463  quorum_callback = q_set_quorate_fn;
2464 
2465  error = corosync_service_link_and_init(corosync_api,
2466  &votequorum_service[0]);
2467  if (error) {
2468  return (error);
2469  }
2470 
2471  LEAVE();
2472 
2473  return (NULL);
2474 }
2475 
2476 /*
2477  * Library Handler init/fini
2478  */
2479 
2480 static int quorum_lib_init_fn (void *conn)
2481 {
2482  struct quorum_pd *pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2483 
2484  ENTER();
2485 
2486  list_init (&pd->list);
2487  pd->conn = conn;
2488 
2489  LEAVE();
2490  return (0);
2491 }
2492 
2493 static int quorum_lib_exit_fn (void *conn)
2494 {
2495  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2496 
2497  ENTER();
2498 
2499  if (quorum_pd->tracking_enabled) {
2500  list_del (&quorum_pd->list);
2501  list_init (&quorum_pd->list);
2502  }
2503 
2504  LEAVE();
2505 
2506  return (0);
2507 }
2508 
2509 /*
2510  * library internal functions
2511  */
2512 
2513 static void qdevice_timer_fn(void *arg)
2514 {
2515  ENTER();
2516 
2517  if ((!(us->flags & NODE_FLAGS_QDEVICE_ALIVE)) ||
2518  (!qdevice_timer_set)) {
2519  LEAVE();
2520  return;
2521  }
2522 
2525  log_printf(LOGSYS_LEVEL_INFO, "lost contact with quorum device %s", qdevice_name);
2526  votequorum_exec_send_nodeinfo(us->node_id);
2527 
2528  qdevice_timer_set = 0;
2529  sync_wait_for_poll_or_timeout = 0;
2530 
2531  LEAVE();
2532 }
2533 
2534 /*
2535  * Library Handler Functions
2536  */
2537 
2538 static void message_handler_req_lib_votequorum_getinfo (void *conn, const void *message)
2539 {
2541  struct res_lib_votequorum_getinfo res_lib_votequorum_getinfo;
2542  struct cluster_node *node;
2543  unsigned int highest_expected = 0;
2544  unsigned int total_votes = 0;
2545  cs_error_t error = CS_OK;
2546  uint32_t nodeid = req_lib_votequorum_getinfo->nodeid;
2547 
2548  ENTER();
2549 
2550  log_printf(LOGSYS_LEVEL_DEBUG, "got getinfo request on %p for node %u", conn, req_lib_votequorum_getinfo->nodeid);
2551 
2552  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
2553  nodeid = us->node_id;
2554  }
2555 
2556  node = find_node_by_nodeid(nodeid);
2557  if (node) {
2558  struct cluster_node *iternode;
2559  struct list_head *nodelist;
2560 
2561  list_iterate(nodelist, &cluster_members_list) {
2562  iternode = list_entry(nodelist, struct cluster_node, list);
2563 
2564  if (iternode->state == NODESTATE_MEMBER) {
2565  highest_expected =
2566  max(highest_expected, iternode->expected_votes);
2567  total_votes += iternode->votes;
2568  }
2569  }
2570 
2571  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2572  total_votes += qdevice->votes;
2573  }
2574 
2575  switch(node->state) {
2576  case NODESTATE_MEMBER:
2577  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_MEMBER;
2578  break;
2579  case NODESTATE_DEAD:
2580  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_DEAD;
2581  break;
2582  case NODESTATE_LEAVING:
2583  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_LEAVING;
2584  break;
2585  default:
2586  res_lib_votequorum_getinfo.state = node->state;
2587  break;
2588  }
2589  res_lib_votequorum_getinfo.state = node->state;
2590  res_lib_votequorum_getinfo.votes = node->votes;
2591  res_lib_votequorum_getinfo.expected_votes = node->expected_votes;
2592  res_lib_votequorum_getinfo.highest_expected = highest_expected;
2593 
2594  res_lib_votequorum_getinfo.quorum = quorum;
2595  res_lib_votequorum_getinfo.total_votes = total_votes;
2596  res_lib_votequorum_getinfo.flags = 0;
2597  res_lib_votequorum_getinfo.nodeid = node->node_id;
2598 
2599  if (two_node) {
2600  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_TWONODE;
2601  }
2602  if (cluster_is_quorate) {
2603  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QUORATE;
2604  }
2605  if (wait_for_all) {
2606  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_WAIT_FOR_ALL;
2607  }
2608  if (last_man_standing) {
2609  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_LAST_MAN_STANDING;
2610  }
2611  if (auto_tie_breaker != ATB_NONE) {
2612  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_AUTO_TIE_BREAKER;
2613  }
2614  if (allow_downscale) {
2615  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_ALLOW_DOWNSCALE;
2616  }
2617 
2618  memset(res_lib_votequorum_getinfo.qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2619  strcpy(res_lib_votequorum_getinfo.qdevice_name, qdevice_name);
2620  res_lib_votequorum_getinfo.qdevice_votes = qdevice->votes;
2621 
2622  if (node->flags & NODE_FLAGS_QDEVICE_REGISTERED) {
2623  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_REGISTERED;
2624  }
2625  if (node->flags & NODE_FLAGS_QDEVICE_ALIVE) {
2626  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_ALIVE;
2627  }
2628  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2629  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_CAST_VOTE;
2630  }
2631  if (node->flags & NODE_FLAGS_QDEVICE_MASTER_WINS) {
2632  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_MASTER_WINS;
2633  }
2634  } else {
2635  error = CS_ERR_NOT_EXIST;
2636  }
2637 
2638  res_lib_votequorum_getinfo.header.size = sizeof(res_lib_votequorum_getinfo);
2639  res_lib_votequorum_getinfo.header.id = MESSAGE_RES_VOTEQUORUM_GETINFO;
2640  res_lib_votequorum_getinfo.header.error = error;
2641  corosync_api->ipc_response_send(conn, &res_lib_votequorum_getinfo, sizeof(res_lib_votequorum_getinfo));
2642  log_printf(LOGSYS_LEVEL_DEBUG, "getinfo response error: %d", error);
2643 
2644  LEAVE();
2645 }
2646 
2647 static void message_handler_req_lib_votequorum_setexpected (void *conn, const void *message)
2648 {
2650  struct res_lib_votequorum_status res_lib_votequorum_status;
2651  cs_error_t error = CS_OK;
2652  unsigned int newquorum;
2653  unsigned int total_votes;
2654  uint8_t allow_downscale_status = 0;
2655 
2656  ENTER();
2657 
2658  allow_downscale_status = allow_downscale;
2659  allow_downscale = 0;
2660 
2661  /*
2662  * Validate new expected votes
2663  */
2664  newquorum = calculate_quorum(1, req_lib_votequorum_setexpected->expected_votes, &total_votes);
2665  allow_downscale = allow_downscale_status;
2666  if (newquorum < total_votes / 2 ||
2667  newquorum > total_votes) {
2668  error = CS_ERR_INVALID_PARAM;
2669  goto error_exit;
2670  }
2671  update_node_expected_votes(req_lib_votequorum_setexpected->expected_votes);
2672 
2673  votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES, us->node_id,
2674  req_lib_votequorum_setexpected->expected_votes);
2675 
2676 error_exit:
2677  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2678  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2679  res_lib_votequorum_status.header.error = error;
2680  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2681 
2682  LEAVE();
2683 }
2684 
2685 static void message_handler_req_lib_votequorum_setvotes (void *conn, const void *message)
2686 {
2688  struct res_lib_votequorum_status res_lib_votequorum_status;
2689  struct cluster_node *node;
2690  unsigned int newquorum;
2691  unsigned int total_votes;
2692  unsigned int saved_votes;
2693  cs_error_t error = CS_OK;
2694  unsigned int nodeid;
2695 
2696  ENTER();
2697 
2698  nodeid = req_lib_votequorum_setvotes->nodeid;
2699  node = find_node_by_nodeid(nodeid);
2700  if (!node) {
2701  error = CS_ERR_NAME_NOT_FOUND;
2702  goto error_exit;
2703  }
2704 
2705  /*
2706  * Check votes is valid
2707  */
2708  saved_votes = node->votes;
2709  node->votes = req_lib_votequorum_setvotes->votes;
2710 
2711  newquorum = calculate_quorum(1, 0, &total_votes);
2712 
2713  if (newquorum < total_votes / 2 ||
2714  newquorum > total_votes) {
2715  node->votes = saved_votes;
2716  error = CS_ERR_INVALID_PARAM;
2717  goto error_exit;
2718  }
2719 
2720  votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES, nodeid,
2721  req_lib_votequorum_setvotes->votes);
2722 
2723 error_exit:
2724  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2725  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2726  res_lib_votequorum_status.header.error = error;
2727  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2728 
2729  LEAVE();
2730 }
2731 
2732 static void message_handler_req_lib_votequorum_trackstart (void *conn,
2733  const void *message)
2734 {
2736  struct res_lib_votequorum_status res_lib_votequorum_status;
2737  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2738  cs_error_t error = CS_OK;
2739 
2740  ENTER();
2741 
2742  /*
2743  * If an immediate listing of the current cluster membership
2744  * is requested, generate membership list
2745  */
2746  if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CURRENT ||
2747  req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES) {
2748  log_printf(LOGSYS_LEVEL_DEBUG, "sending initial status to %p", conn);
2749  votequorum_exec_send_nodelist_notification(conn, req_lib_votequorum_trackstart->context);
2750  votequorum_exec_send_quorum_notification(conn, req_lib_votequorum_trackstart->context);
2751  }
2752 
2753  if (quorum_pd->tracking_enabled) {
2754  error = CS_ERR_EXIST;
2755  goto response_send;
2756  }
2757 
2758  /*
2759  * Record requests for tracking
2760  */
2761  if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES ||
2762  req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES_ONLY) {
2763 
2764  quorum_pd->track_flags = req_lib_votequorum_trackstart->track_flags;
2765  quorum_pd->tracking_enabled = 1;
2766  quorum_pd->tracking_context = req_lib_votequorum_trackstart->context;
2767 
2768  list_add (&quorum_pd->list, &trackers_list);
2769  }
2770 
2771 response_send:
2772  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2773  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2774  res_lib_votequorum_status.header.error = error;
2775  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2776 
2777  LEAVE();
2778 }
2779 
2780 static void message_handler_req_lib_votequorum_trackstop (void *conn,
2781  const void *message)
2782 {
2783  struct res_lib_votequorum_status res_lib_votequorum_status;
2784  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2785  int error = CS_OK;
2786 
2787  ENTER();
2788 
2789  if (quorum_pd->tracking_enabled) {
2790  error = CS_OK;
2791  quorum_pd->tracking_enabled = 0;
2792  list_del (&quorum_pd->list);
2793  list_init (&quorum_pd->list);
2794  } else {
2795  error = CS_ERR_NOT_EXIST;
2796  }
2797 
2798  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2799  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2800  res_lib_votequorum_status.header.error = error;
2801  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2802 
2803  LEAVE();
2804 }
2805 
2806 static void message_handler_req_lib_votequorum_qdevice_register (void *conn,
2807  const void *message)
2808 {
2810  struct res_lib_votequorum_status res_lib_votequorum_status;
2811  cs_error_t error = CS_OK;
2812 
2813  ENTER();
2814 
2815  if (!qdevice_can_operate) {
2816  log_printf(LOGSYS_LEVEL_INFO, "Registration of quorum device is disabled by incorrect corosync.conf. See logs for more information");
2817  error = CS_ERR_ACCESS;
2818  goto out;
2819  }
2820 
2822  if ((!strncmp(req_lib_votequorum_qdevice_register->name,
2823  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
2824  goto out;
2825  } else {
2827  "A new qdevice with different name (new: %s old: %s) is trying to re-register!",
2828  req_lib_votequorum_qdevice_register->name, qdevice_name);
2829  error = CS_ERR_EXIST;
2830  goto out;
2831  }
2832  } else {
2833  if (qdevice_reg_conn != NULL) {
2835  "Registration request already in progress");
2836  error = CS_ERR_TRY_AGAIN;
2837  goto out;
2838  }
2839  qdevice_reg_conn = conn;
2840  if (votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2841  req_lib_votequorum_qdevice_register->name) != 0) {
2843  "Unable to send qdevice registration request to cluster");
2844  error = CS_ERR_TRY_AGAIN;
2845  qdevice_reg_conn = NULL;
2846  } else {
2847  LEAVE();
2848  return;
2849  }
2850  }
2851 
2852 out:
2853 
2854  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2855  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2856  res_lib_votequorum_status.header.error = error;
2857  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2858 
2859  LEAVE();
2860 }
2861 
2862 static void message_handler_req_lib_votequorum_qdevice_unregister (void *conn,
2863  const void *message)
2864 {
2866  struct res_lib_votequorum_status res_lib_votequorum_status;
2867  cs_error_t error = CS_OK;
2868 
2869  ENTER();
2870 
2872  if (strncmp(req_lib_votequorum_qdevice_unregister->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2873  error = CS_ERR_INVALID_PARAM;
2874  goto out;
2875  }
2876  if (qdevice_timer_set) {
2877  corosync_api->timer_delete(qdevice_timer);
2878  qdevice_timer_set = 0;
2879  sync_wait_for_poll_or_timeout = 0;
2880  }
2885  votequorum_exec_send_nodeinfo(us->node_id);
2886  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER,
2887  req_lib_votequorum_qdevice_unregister->name);
2888  } else {
2889  error = CS_ERR_NOT_EXIST;
2890  }
2891 
2892 out:
2893  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2894  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2895  res_lib_votequorum_status.header.error = error;
2896  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2897 
2898  LEAVE();
2899 }
2900 
2901 static void message_handler_req_lib_votequorum_qdevice_update (void *conn,
2902  const void *message)
2903 {
2905  struct res_lib_votequorum_status res_lib_votequorum_status;
2906  cs_error_t error = CS_OK;
2907 
2908  ENTER();
2909 
2911  if (strncmp(req_lib_votequorum_qdevice_update->oldname, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2912  error = CS_ERR_INVALID_PARAM;
2913  goto out;
2914  }
2915  votequorum_exec_send_qdevice_reconfigure(req_lib_votequorum_qdevice_update->oldname,
2916  req_lib_votequorum_qdevice_update->newname);
2917  } else {
2918  error = CS_ERR_NOT_EXIST;
2919  }
2920 
2921 out:
2922  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2923  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2924  res_lib_votequorum_status.header.error = error;
2925  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2926 
2927  LEAVE();
2928 }
2929 
2930 static void message_handler_req_lib_votequorum_qdevice_poll (void *conn,
2931  const void *message)
2932 {
2934  struct res_lib_votequorum_status res_lib_votequorum_status;
2935  cs_error_t error = CS_OK;
2936  uint32_t oldflags;
2937 
2938  ENTER();
2939 
2940  if (!qdevice_can_operate) {
2941  error = CS_ERR_ACCESS;
2942  goto out;
2943  }
2944 
2946  if (!(req_lib_votequorum_qdevice_poll->ring_id.nodeid == quorum_ringid.rep.nodeid &&
2947  req_lib_votequorum_qdevice_poll->ring_id.seq == quorum_ringid.seq)) {
2948  log_printf(LOGSYS_LEVEL_DEBUG, "Received poll ring id (%u.%"PRIu64") != last sync "
2949  "ring id (%u.%"PRIu64"). Ignoring poll call.",
2950  req_lib_votequorum_qdevice_poll->ring_id.nodeid, req_lib_votequorum_qdevice_poll->ring_id.seq,
2951  quorum_ringid.rep.nodeid, quorum_ringid.seq);
2952  error = CS_ERR_MESSAGE_ERROR;
2953  goto out;
2954  }
2955  if (strncmp(req_lib_votequorum_qdevice_poll->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2956  error = CS_ERR_INVALID_PARAM;
2957  goto out;
2958  }
2959 
2960  if (qdevice_timer_set) {
2961  corosync_api->timer_delete(qdevice_timer);
2962  qdevice_timer_set = 0;
2963  }
2964 
2965  oldflags = us->flags;
2966 
2968 
2969  if (req_lib_votequorum_qdevice_poll->cast_vote) {
2971  } else {
2973  }
2974 
2975  if (us->flags != oldflags) {
2976  votequorum_exec_send_nodeinfo(us->node_id);
2977  }
2978 
2979  corosync_api->timer_add_duration((unsigned long long)qdevice_timeout*1000000, qdevice,
2980  qdevice_timer_fn, &qdevice_timer);
2981  qdevice_timer_set = 1;
2982  sync_wait_for_poll_or_timeout = 0;
2983  } else {
2984  error = CS_ERR_NOT_EXIST;
2985  }
2986 
2987 out:
2988  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2989  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2990  res_lib_votequorum_status.header.error = error;
2991  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2992 
2993  LEAVE();
2994 }
2995 
2996 static void message_handler_req_lib_votequorum_qdevice_master_wins (void *conn,
2997  const void *message)
2998 {
3000  struct res_lib_votequorum_status res_lib_votequorum_status;
3001  cs_error_t error = CS_OK;
3002  uint32_t oldflags = us->flags;
3003 
3004  ENTER();
3005 
3006  if (!qdevice_can_operate) {
3007  error = CS_ERR_ACCESS;
3008  goto out;
3009  }
3010 
3012  if (strncmp(req_lib_votequorum_qdevice_master_wins->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
3013  error = CS_ERR_INVALID_PARAM;
3014  goto out;
3015  }
3016 
3017  if (req_lib_votequorum_qdevice_master_wins->allow) {
3019  } else {
3021  }
3022 
3023  if (us->flags != oldflags) {
3024  votequorum_exec_send_nodeinfo(us->node_id);
3025  }
3026 
3027  update_qdevice_master_wins(req_lib_votequorum_qdevice_master_wins->allow);
3028  } else {
3029  error = CS_ERR_NOT_EXIST;
3030  }
3031 
3032 out:
3033  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
3034  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
3035  res_lib_votequorum_status.header.error = error;
3036  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
3037 
3038  LEAVE();
3039 }
uint32_t expected_votes
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
void *(* ipc_private_data_get)(void *conn)
Definition: coroapi.h:256
#define VOTEQUORUM_INFO_QUORATE
#define TOTEM_AGREED
Definition: coroapi.h:102
#define CS_TRUE
Definition: corotypes.h:54
const char * name
Definition: coroapi.h:492
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_READCONFIG_STARTUP
const char * get_run_dir(void)
Definition: util.c:174
void(* timer_delete)(corosync_timer_handle_t timer_handle)
Definition: coroapi.h:241
int(* timer_add_duration)(unsigned long long nanoseconds_in_future, void *data, void(*timer_nf)(void *data), corosync_timer_handle_t *handle)
Definition: coroapi.h:229
const char * icmap_iter_next(icmap_iter_t iter, size_t *value_len, icmap_value_types_t *type)
Return next item in iterator iter.
Definition: icmap.c:1103
#define NODE_FLAGS_WFASTATUS
#define LOGSYS_LEVEL_INFO
Definition: logsys.h:73
uint32_t value
#define CS_FALSE
Definition: corotypes.h:53
struct list_head * next
Definition: list.h:47
#define NODE_FLAGS_QUORATE
#define VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT
The corosync_service_engine struct.
Definition: coroapi.h:491
struct list_head list
void icmap_iter_finalize(icmap_iter_t iter)
Finalize iterator.
Definition: icmap.c:1124
The req_lib_votequorum_qdevice_master_wins struct.
#define VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER
#define MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE
#define max(a, b)
int(* ipc_response_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:258
#define list_iterate(v, head)
char * votequorum_init(struct corosync_api_v1 *api, quorum_set_quorate_fn_t q_set_quorate_fn)
nodestate_t
#define VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA
int tracking_enabled
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define CS_TRACK_CURRENT
Definition: corotypes.h:87
The req_lib_votequorum_qdevice_unregister struct.
#define NODE_FLAGS_QDEVICE_MASTER_WINS
nodestate_t state
The res_lib_votequorum_quorum_notification struct.
The corosync_lib_handler struct.
Definition: coroapi.h:468
#define VOTEQUORUM_INFO_LAST_MAN_STANDING
struct message_header header
Definition: totemsrp.c:60
#define VOTEQUORUM_INFO_WAIT_FOR_ALL
#define NODE_FLAGS_QDEVICE_CAST_VOTE
uint32_t operation
The res_lib_votequorum_status struct.
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE
The corosync_exec_handler struct.
Definition: coroapi.h:476
#define VOTEQUORUM_INFO_TWONODE
int(* totem_mcast)(const struct iovec *iovec, unsigned int iov_len, unsigned int guarantee)
Definition: coroapi.h:281
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: list.h:46
#define VOTEQUORUM_INFO_QDEVICE_REGISTERED
#define log_printf(level, format, args...)
Definition: logsys.h:320
void(* exec_handler_fn)(const void *msg, unsigned int nodeid)
Definition: coroapi.h:477
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_QDEVICE_NODEID
#define VOTEQUORUM_INFO_QDEVICE_MASTER_WINS
#define VOTEQUORUM_NODESTATE_MEMBER
#define CS_TRACK_CHANGES
Definition: corotypes.h:88
#define SERVICE_ID_MAKE(a, b)
Definition: coroapi.h:459
#define ICMAP_TRACK_DELETE
Definition: icmap.h:77
#define ICMAP_KEYNAME_MAXLEN
Maximum length of key in icmap.
Definition: icmap.h:48
void(* quorum_set_quorate_fn_t)(const unsigned int *view_list, size_t view_list_entries, int quorate, struct memb_ring_id *)
Definition: exec/quorum.h:42
#define VOTEQUORUM_QDEVICE_OPERATION_REGISTER
cs_error_t icmap_get_uint8(const char *key_name, uint8_t *u8)
Definition: icmap.c:842
void(* error_memory_failure)(void) __attribute__((noreturn))
Definition: coroapi.h:423
#define VOTEQUORUM_INFO_ALLOW_DOWNSCALE
#define LOGSYS_LEVEL_WARNING
Definition: logsys.h:71
#define ICMAP_TRACK_MODIFY
Definition: icmap.h:78
#define VOTEQUORUM_INFO_QDEVICE_ALIVE
cs_error_t icmap_set_uint32(const char *key_name, uint32_t value)
Definition: icmap.c:611
void * user_data
Definition: sam.c:127
struct list_head list
unsigned int(* totem_nodeid_get)(void)
Definition: coroapi.h:275
unsigned int nodeid
Definition: coroapi.h:112
#define CS_TRACK_CHANGES_ONLY
Definition: corotypes.h:89
#define ICMAP_TRACK_ADD
Definition: icmap.h:76
The req_lib_votequorum_getinfo struct.
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Linked list API.
struct totem_ip_address rep
Definition: coroapi.h:123
#define COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
Definition: coroapi.h:157
The req_lib_votequorum_qdevice_update struct.
cs_error_t
The cs_error_t enum.
Definition: corotypes.h:94
unsigned char track_flags
#define LOGSYS_LEVEL_DEBUG
Definition: logsys.h:74
LOGSYS_DECLARE_SUBSYS("VOTEQ")
The req_lib_votequorum_setvotes struct.
The corosync_api_v1 struct.
Definition: coroapi.h:225
cs_error_t icmap_get_uint32(const char *key_name, uint32_t *u32)
Definition: icmap.c:866
uint8_t param
The req_lib_votequorum_setexpected struct.
uint32_t quorate
Definition: sam.c:134
#define swab32(x)
The swab32 macro.
Definition: swab.h:51
#define VOTEQUORUM_INFO_AUTO_TIE_BREAKER
struct corosync_service_engine * votequorum_get_service_engine_ver0(void)
The res_lib_votequorum_expectedvotes_notification struct.
#define ENTER
Definition: logsys.h:321
The req_lib_votequorum_qdevice_register struct.
char * corosync_service_link_and_init(struct corosync_api_v1 *corosync_api, struct default_service *service)
Link and initialize a service.
Definition: service.c:117
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_NODESTATE_LEAVING
#define PROCESSOR_COUNT_MAX
Definition: coroapi.h:96
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG
The memb_ring_id struct.
Definition: coroapi.h:122
#define VOTEQUORUM_READCONFIG_RUNTIME
struct list_head * prev
Definition: list.h:48
#define MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO
The req_lib_votequorum_trackstart struct.
#define VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES
#define VOTEQUORUM_QDEVICE_MAX_NAME_LEN
qb_loop_timer_handle corosync_timer_handle_t
corosync_timer_handle_t
Definition: coroapi.h:74
The req_lib_votequorum_qdevice_poll struct.
cs_error_t icmap_get_string(const char *key_name, char **str)
Shortcut for icmap_get for string type.
Definition: icmap.c:896
#define LOGSYS_LEVEL_CRIT
Definition: logsys.h:69
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define NODE_FLAGS_LEAVING
#define list_entry(ptr, type, member)
Definition: list.h:84
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define COROSYNC_LIB_FLOW_CONTROL_REQUIRED
Definition: coroapi.h:156
#define LOGSYS_LEVEL_NOTICE
Definition: logsys.h:72
unsigned long long seq
Definition: coroapi.h:124
cs_error_t icmap_set_uint8(const char *key_name, uint8_t value)
Definition: icmap.c:587
void(* lib_handler_fn)(void *conn, const void *msg)
Definition: coroapi.h:469
The res_lib_votequorum_getinfo struct.
#define VOTEQUORUM_NODESTATE_DEAD
cs_error_t icmap_set_ro_access(const char *key_name, int prefix, int ro_access)
Set read-only access for given key (key_name) or prefix, If prefix is set.
Definition: icmap.c:1233
#define VOTEQUORUM_INFO_QDEVICE_CAST_VOTE
int(* ipc_dispatch_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:263
#define VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT
const char * name
Definition: service.h:43
icmap_iter_t icmap_iter_init(const char *prefix)
Initialize iterator with given prefix.
Definition: icmap.c:1097
struct memb_ring_id ring_id
Definition: totemsrp.c:64
uint64_t tracking_context
#define VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES
#define DEFAULT_LMS_WIN
#define LEAVE
Definition: logsys.h:322
#define NODE_FLAGS_QDEVICE_ALIVE
qb_map_iter_t * icmap_iter_t
Itterator type.
Definition: icmap.h:123
Structure passed as new_value and old_value in change callback.
Definition: icmap.h:91
#define NODE_FLAGS_QDEVICE_REGISTERED
cs_error_t icmap_track_add(const char *key_name, int32_t track_type, icmap_notify_fn_t notify_fn, void *user_data, icmap_track_t *icmap_track)
Add tracking function for given key_name.
Definition: icmap.c:1167
#define NODE_FLAGS_FIRST
struct qb_ipc_request_header header __attribute__((aligned(8)))
#define ICMAP_TRACK_PREFIX
Whole prefix is tracked, instead of key only (so "totem." tracking means that "totem.nodeid", "totem.version", ...
Definition: icmap.h:85