vqmain.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. #include <config.h>
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <qb/qblog.h>
  6. #include <qb/qbloop.h>
  7. #include <sys/poll.h>
  8. #include <netinet/in.h>
  9. #include <sys/queue.h>
  10. #ifdef HAVE_READLINE_READLINE_H
  11. #include <readline/readline.h>
  12. #else
  13. #include <unistd.h> /* isatty */
  14. #endif
  15. #include "../exec/votequorum.h"
  16. #include "../exec/service.h"
  17. #include <corosync/logsys.h>
  18. #include <corosync/coroapi.h>
  19. #include "icmap.h"
  20. #include "vqsim.h"
  21. /* Easier than including the config file with a ton of conflicting dependencies */
  22. extern int coroparse_configparse (icmap_map_t config_map, const char **error_string);
  23. extern int corosync_log_config_read (const char **error_string);
  24. /* 'Keep the compiler happy' time */
  25. const char *corosync_get_config_file(void);
  26. /* One of these per partition */
  27. struct vq_partition {
  28. TAILQ_HEAD(, vq_node) nodelist;
  29. struct memb_ring_id ring_id;
  30. int num;
  31. };
  32. /* One of these per node */
  33. struct vq_node {
  34. vq_object_t instance;
  35. unsigned int nodeid;
  36. int fd;
  37. struct vq_partition *partition;
  38. TAILQ_ENTRY(vq_node) entries;
  39. /* Last status */
  40. int last_quorate;
  41. struct memb_ring_id last_ring_id;
  42. int last_view_list[MAX_NODES];
  43. int last_view_list_entries;
  44. };
  45. static struct vq_partition partitions[MAX_PARTITIONS];
  46. static qb_loop_t *poll_loop;
  47. static int autofence;
  48. static int check_for_quorum;
  49. static FILE *output_file;
  50. static int nosync;
  51. static qb_loop_timer_handle kb_timer;
  52. static ssize_t wait_count;
  53. static ssize_t wait_count_to_unblock;
  54. static struct vq_node *find_by_pid(pid_t pid);
  55. static void send_partition_to_nodes(struct vq_partition *partition, int newring);
  56. static void start_kb_input(void);
  57. static void start_kb_input_timeout(void *data);
  58. #ifndef HAVE_READLINE_READLINE_H
  59. #define INPUT_BUF_SIZE 1024
  60. static char input_buf[INPUT_BUF_SIZE];
  61. static size_t input_buf_term = 0;
  62. static int is_tty;
  63. #endif
  64. /* 'Keep the compiler happy' time */
  65. static char corosync_config_file[PATH_MAX + 1] = COROSYSCONFDIR "/corosync.conf";
  66. const char *corosync_get_config_file(void)
  67. {
  68. return (corosync_config_file);
  69. }
  70. /* Tell all non-quorate nodes to quit */
  71. static void force_fence(void)
  72. {
  73. int i;
  74. struct vq_node *vqn;
  75. for (i=0; i<MAX_PARTITIONS; i++) {
  76. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  77. vq_quit_if_inquorate(vqn->instance);
  78. }
  79. }
  80. }
  81. /* Save quorum state from the incoming message */
  82. static void save_quorum_state(struct vq_node *node, struct vqsim_quorum_msg *qmsg)
  83. {
  84. node->last_quorate = qmsg->quorate;
  85. memcpy(&node->last_ring_id, &qmsg->ring_id, sizeof(struct memb_ring_id));
  86. memcpy(node->last_view_list, qmsg->view_list, sizeof(int) * qmsg->view_list_entries);
  87. node->last_view_list_entries = qmsg->view_list_entries;
  88. /* If at least one node is quorate and autofence is enabled, then fence everyone who is not quorate */
  89. if (check_for_quorum && qmsg->quorate & autofence) {
  90. check_for_quorum = 0;
  91. force_fence();
  92. }
  93. }
  94. /* Print current node state */
  95. static void print_quorum_state(struct vq_node *node)
  96. {
  97. int i;
  98. if (node->last_quorate < 0) {
  99. fprintf(output_file, "%d:%02d: q=UNINITIALIZED\n",
  100. node->partition->num, node->nodeid);
  101. return;
  102. }
  103. fprintf(output_file, "%d:%02d: q=%d ring=[%d/%lld] ", node->partition->num, node->nodeid, node->last_quorate,
  104. node->last_ring_id.nodeid, node->last_ring_id.seq);
  105. fprintf(output_file, "nodes=[");
  106. for (i = 0; i < node->last_view_list_entries; i++) {
  107. if (i) {
  108. fprintf(output_file, " ");
  109. }
  110. fprintf(output_file, "%d", node->last_view_list[i]);
  111. }
  112. fprintf(output_file, "]\n");
  113. }
  114. static void propogate_vq_message(struct vq_node *vqn, const char *msg, int len)
  115. {
  116. struct vq_node *other_vqn;
  117. /* Send it to everyone in that node's partition (including itself) */
  118. TAILQ_FOREACH(other_vqn, &vqn->partition->nodelist, entries) {
  119. write(other_vqn->fd, msg, len);
  120. }
  121. }
  122. static int vq_parent_read_fn(int32_t fd, int32_t revents, void *data)
  123. {
  124. char msgbuf[8192];
  125. int msglen;
  126. struct vqsim_msg_header *msg;
  127. struct vqsim_quorum_msg *qmsg;
  128. struct vq_node *vqn = data;
  129. if (revents == POLLIN) {
  130. msglen = read(fd, msgbuf, sizeof(msgbuf));
  131. if (msglen < 0) {
  132. perror("read failed");
  133. }
  134. if (msglen > 0) {
  135. msg = (void*)msgbuf;
  136. switch (msg->type) {
  137. case VQMSG_QUORUM:
  138. if (!nosync && --wait_count_to_unblock <= 0)
  139. qb_loop_timer_del(poll_loop, kb_timer);
  140. qmsg = (void*)msgbuf;
  141. save_quorum_state(vqn, qmsg);
  142. print_quorum_state(vqn);
  143. if (!nosync && wait_count_to_unblock <= 0)
  144. start_kb_input();
  145. break;
  146. case VQMSG_EXEC:
  147. /* Message from votequorum, pass around the partition */
  148. propogate_vq_message(vqn, msgbuf, msglen);
  149. break;
  150. case VQMSG_QUIT:
  151. case VQMSG_SYNC:
  152. case VQMSG_QDEVICE:
  153. case VQMSG_QUORUMQUIT:
  154. /* not used here */
  155. break;
  156. }
  157. }
  158. }
  159. if (revents == POLLERR) {
  160. fprintf(stderr, "pollerr on %d\n", vqn->nodeid);
  161. }
  162. return 0;
  163. }
  164. static int read_corosync_conf(void)
  165. {
  166. int res;
  167. const char *error_string;
  168. int err = icmap_init();
  169. if (!err) {
  170. fprintf(stderr, "icmap_init failed\n");
  171. }
  172. /* Load corosync.conf */
  173. logsys_format_set(NULL);
  174. res = coroparse_configparse(icmap_get_global_map(), &error_string);
  175. if (res == -1) {
  176. log_printf (LOGSYS_LEVEL_INFO, "Error loading corosyc.conf %s", error_string);
  177. return -1;
  178. }
  179. else {
  180. res = corosync_log_config_read (&error_string);
  181. if (res < 0) {
  182. log_printf (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string);
  183. syslog (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string);
  184. }
  185. else {
  186. logsys_config_apply();
  187. }
  188. }
  189. if (logsys_thread_start() != 0) {
  190. log_printf (LOGSYS_LEVEL_ERROR, "Can't initialize log thread");
  191. return -1;
  192. }
  193. return 0;
  194. }
  195. static void remove_node(struct vq_node *node)
  196. {
  197. struct vq_partition *part;
  198. part = node->partition;
  199. /* Remove from partition list */
  200. TAILQ_REMOVE(&part->nodelist, node, entries);
  201. free(node);
  202. wait_count--;
  203. /* Rebuild quorum */
  204. send_partition_to_nodes(part, 1);
  205. }
  206. static int32_t sigchld_handler(int32_t sig, void *data)
  207. {
  208. pid_t pid;
  209. int status;
  210. struct vq_node *vqn;
  211. const char *exit_status="";
  212. char text[132];
  213. pid = wait(&status);
  214. if (WIFEXITED(status)) {
  215. vqn = find_by_pid(pid);
  216. if (vqn) {
  217. switch (WEXITSTATUS(status)) {
  218. case 0:
  219. exit_status = "(on request)";
  220. break;
  221. case 1:
  222. exit_status = "(autofenced)";
  223. break;
  224. default:
  225. sprintf(text, "(exit code %d)", WEXITSTATUS(status));
  226. break;
  227. }
  228. printf("%d:%02d Quit %s\n", vqn->partition->num, vqn->nodeid, exit_status);
  229. remove_node(vqn);
  230. }
  231. else {
  232. fprintf(stderr, "Unknown child %d exited with status %d\n", pid, WEXITSTATUS(status));
  233. }
  234. }
  235. if (WIFSIGNALED(status)) {
  236. vqn = find_by_pid(pid);
  237. if (vqn) {
  238. printf("%d:%02d exited on signal %d%s\n", vqn->partition->num, vqn->nodeid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":"");
  239. remove_node(vqn);
  240. }
  241. else {
  242. fprintf(stderr, "Unknown child %d exited with status %d%s\n", pid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":"");
  243. }
  244. }
  245. return 0;
  246. }
  247. static void send_partition_to_nodes(struct vq_partition *partition, int newring)
  248. {
  249. struct vq_node *vqn;
  250. int nodelist[MAX_NODES];
  251. int nodes = 0;
  252. int first = 1;
  253. if (newring) {
  254. /* Simulate corosync incrementing the seq by 4 for added authenticity */
  255. partition->ring_id.seq += 4;
  256. }
  257. /* Build the node list */
  258. TAILQ_FOREACH(vqn, &partition->nodelist, entries) {
  259. nodelist[nodes++] = vqn->nodeid;
  260. if (first) {
  261. partition->ring_id.nodeid = vqn->nodeid;
  262. first = 0;
  263. }
  264. }
  265. TAILQ_FOREACH(vqn, &partition->nodelist, entries) {
  266. vq_set_nodelist(vqn->instance, &partition->ring_id, nodelist, nodes);
  267. }
  268. }
  269. static void init_partitions(void)
  270. {
  271. int i;
  272. for (i=0; i<MAX_PARTITIONS; i++) {
  273. TAILQ_INIT(&partitions[i].nodelist);
  274. partitions[i].ring_id.nodeid = 1000+i;
  275. partitions[i].ring_id.seq = 0;
  276. partitions[i].num = i;
  277. }
  278. }
  279. static pid_t create_node(int nodeid, int partno)
  280. {
  281. struct vq_node *newvq;
  282. newvq = malloc(sizeof(struct vq_node));
  283. if (newvq) {
  284. if (!nosync) {
  285. /* Number of expected "quorum" vq messages is a square
  286. of the total nodes count, so increment the node
  287. counter and set new square of this value as
  288. a "to observe" counter */
  289. wait_count++;
  290. wait_count_to_unblock = wait_count * wait_count;
  291. }
  292. newvq->last_quorate = -1; /* mark "uninitialized" */
  293. newvq->instance = vq_create_instance(poll_loop, nodeid);
  294. if (!newvq->instance) {
  295. fprintf(stderr,
  296. "ERR: could not create vq instance nodeid %d\n",
  297. nodeid);
  298. return (pid_t) -1;
  299. }
  300. newvq->partition = &partitions[partno];
  301. newvq->nodeid = nodeid;
  302. newvq->fd = vq_get_parent_fd(newvq->instance);
  303. TAILQ_INSERT_TAIL(&partitions[partno].nodelist, newvq, entries);
  304. if (qb_loop_poll_add(poll_loop,
  305. QB_LOOP_MED,
  306. newvq->fd,
  307. POLLIN | POLLERR,
  308. newvq,
  309. vq_parent_read_fn)) {
  310. perror("qb_loop_poll_add returned error");
  311. return (pid_t) -1;
  312. }
  313. /* Send sync with all the nodes so far in it. */
  314. send_partition_to_nodes(&partitions[partno], 1);
  315. return vq_get_pid(newvq->instance);
  316. }
  317. return (pid_t) -1;
  318. }
  319. static size_t create_nodes_from_config(void)
  320. {
  321. icmap_iter_t iter;
  322. char tmp_key[ICMAP_KEYNAME_MAXLEN];
  323. uint32_t node_pos;
  324. uint32_t nodeid;
  325. const char *iter_key;
  326. int res;
  327. pid_t pid;
  328. size_t ret = 0;
  329. init_partitions();
  330. iter = icmap_iter_init("nodelist.node.");
  331. while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
  332. res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
  333. if (res != 2) {
  334. continue;
  335. }
  336. if (strcmp(tmp_key, "ring0_addr") != 0) {
  337. continue;
  338. }
  339. snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
  340. if (icmap_get_uint32(tmp_key, &nodeid) == CS_OK) {
  341. pid = create_node(nodeid, 0);
  342. if (pid == (pid_t) -1) {
  343. fprintf(stderr,
  344. "ERR: nodeid %d could not be spawned\n",
  345. nodeid);
  346. exit(1);
  347. }
  348. ret++;
  349. }
  350. }
  351. icmap_iter_finalize(iter);
  352. return ret;
  353. }
  354. static struct vq_node *find_node(int nodeid)
  355. {
  356. int i;
  357. struct vq_node *vqn;
  358. for (i=0; i<MAX_PARTITIONS; i++) {
  359. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  360. if (vqn->nodeid == nodeid) {
  361. return vqn;
  362. }
  363. }
  364. }
  365. return NULL;
  366. }
  367. static struct vq_node *find_by_pid(pid_t pid)
  368. {
  369. int i;
  370. struct vq_node *vqn;
  371. for (i=0; i<MAX_PARTITIONS; i++) {
  372. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  373. if (vq_get_pid(vqn->instance) == pid) {
  374. return vqn;
  375. }
  376. }
  377. }
  378. return NULL;
  379. }
  380. /* Routines called from the parser */
  381. void cmd_start_new_node(int nodeid, int partition)
  382. {
  383. struct vq_node *node;
  384. node = find_node(nodeid);
  385. if (node) {
  386. fprintf(stderr, "ERR: nodeid %d already exists in partition %d\n", nodeid, node->partition->num);
  387. return;
  388. }
  389. qb_loop_poll_del(poll_loop, STDIN_FILENO);
  390. create_node(nodeid, partition);
  391. if (!nosync) {
  392. /* Delay kb input handling by 0.25 second when we've just
  393. added a node; expect that the delay will be cancelled
  394. substantially earlier once it has reported its quorum info
  395. (the delay is in fact a failsafe input enabler here) */
  396. qb_loop_timer_add(poll_loop,
  397. QB_LOOP_MED,
  398. 250000000,
  399. NULL,
  400. start_kb_input_timeout,
  401. &kb_timer);
  402. }
  403. }
  404. void cmd_stop_all_nodes()
  405. {
  406. int i;
  407. struct vq_node *vqn;
  408. for (i=0; i<MAX_PARTITIONS; i++) {
  409. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  410. vq_quit(vqn->instance);
  411. }
  412. }
  413. }
  414. void cmd_show_node_states()
  415. {
  416. int i;
  417. struct vq_node *vqn;
  418. for (i=0; i<MAX_PARTITIONS; i++) {
  419. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  420. print_quorum_state(vqn);
  421. }
  422. }
  423. fprintf(output_file, "#autofence: %s\n", autofence?"on":"off");
  424. }
  425. void cmd_stop_node(int nodeid)
  426. {
  427. struct vq_node *node;
  428. node = find_node(nodeid);
  429. if (!node) {
  430. fprintf(stderr, "ERR: nodeid %d is not up\n", nodeid);
  431. return;
  432. }
  433. /* Remove processor */
  434. vq_quit(node->instance);
  435. /* Node will be removed when the child process exits */
  436. }
  437. /* Move all nodes in 'nodelist' into partition 'partition' */
  438. void cmd_move_nodes(int partition, int num_nodes, int *nodelist)
  439. {
  440. int i;
  441. struct vq_node *node;
  442. for (i=0; i<num_nodes; i++) {
  443. node = find_node(nodelist[i]);
  444. if (node) {
  445. /* Remove it from the current partition */
  446. TAILQ_REMOVE(&node->partition->nodelist, node, entries);
  447. /* Add it to the new partition */
  448. TAILQ_INSERT_TAIL(&partitions[partition].nodelist, node, entries);
  449. node->partition = &partitions[partition];
  450. }
  451. else {
  452. printf("ERR: node %d does not exist\n", nodelist[i]);
  453. }
  454. }
  455. }
  456. /* Take all the nodes in part2 and join them to part1 */
  457. void cmd_join_partitions(int part1, int part2)
  458. {
  459. struct vq_node *vqn;
  460. /* TAILQ_FOREACH is not delete safe *sigh* */
  461. retry:
  462. TAILQ_FOREACH(vqn, &partitions[part2].nodelist, entries) {
  463. TAILQ_REMOVE(&vqn->partition->nodelist, vqn, entries);
  464. TAILQ_INSERT_TAIL(&partitions[part1].nodelist, vqn, entries);
  465. vqn->partition = &partitions[part1];
  466. goto retry;
  467. }
  468. }
  469. void cmd_set_autofence(int onoff)
  470. {
  471. autofence = onoff;
  472. fprintf(output_file, "#autofence: %s\n", onoff?"on":"off");
  473. }
  474. void cmd_update_all_partitions(int newring)
  475. {
  476. int i;
  477. check_for_quorum = 1;
  478. for (i=0; i<MAX_PARTITIONS; i++) {
  479. send_partition_to_nodes(&partitions[i], newring);
  480. }
  481. }
  482. void cmd_qdevice_poll(int nodeid, int onoff)
  483. {
  484. struct vq_node *node;
  485. node = find_node(nodeid);
  486. if (node) {
  487. vq_set_qdevice(node->instance, &node->partition->ring_id, onoff);
  488. }
  489. }
  490. /* ---------------------------------- */
  491. #ifndef HAVE_READLINE_READLINE_H
  492. static void dummy_read_char(void);
  493. static void dummy_read_char()
  494. {
  495. int c, flush = 0;
  496. while (!flush) {
  497. c = getchar();
  498. if (++input_buf_term >= INPUT_BUF_SIZE) {
  499. if (c != '\n' && c != EOF)
  500. fprintf(stderr, "User input overflows the limit: %zu\n",
  501. (size_t) INPUT_BUF_SIZE);
  502. input_buf[INPUT_BUF_SIZE - 1] = '\0';
  503. flush = 1;
  504. } else if (c == '\n' || c == EOF) {
  505. input_buf[input_buf_term - 1] = '\0';
  506. flush = 1;
  507. } else {
  508. input_buf[input_buf_term - 1] = c;
  509. }
  510. }
  511. parse_input_command((c == EOF) ? NULL : input_buf);
  512. input_buf_term = 0;
  513. if (is_tty) {
  514. printf("vqsim> ");
  515. fflush(stdout);
  516. }
  517. }
  518. #endif
  519. static int stdin_read_fn(int32_t fd, int32_t revents, void *data)
  520. {
  521. #ifdef HAVE_READLINE_READLINE_H
  522. /* Send it to readline */
  523. rl_callback_read_char();
  524. #else
  525. dummy_read_char();
  526. #endif
  527. return 0;
  528. }
  529. static void start_kb_input(void)
  530. {
  531. wait_count_to_unblock = 0;
  532. #ifdef HAVE_READLINE_READLINE_H
  533. /* Readline will deal with completed lines when they arrive */
  534. rl_callback_handler_install("vqsim> ", parse_input_command);
  535. #else
  536. if (is_tty) {
  537. printf("vqsim> ");
  538. fflush(stdout);
  539. }
  540. #endif
  541. /* Send stdin to readline */
  542. if (qb_loop_poll_add(poll_loop,
  543. QB_LOOP_MED,
  544. STDIN_FILENO,
  545. POLLIN | POLLERR,
  546. NULL,
  547. stdin_read_fn)) {
  548. if (errno != EEXIST) {
  549. perror("qb_loop_poll_add1 returned error");
  550. }
  551. }
  552. }
  553. static void start_kb_input_timeout(void *data)
  554. {
  555. // fprintf(stderr, "Waiting for nodes to report status timed out\n");
  556. start_kb_input();
  557. }
  558. static void usage(char *program)
  559. {
  560. printf("Usage:\n");
  561. printf("\n");
  562. printf("%s [-f <config-file>] [-o <output-file>]\n", program);
  563. printf("\n");
  564. printf(" -f config file. defaults to /etc/corosync/corosync.conf\n");
  565. printf(" -o output file. defaults to stdout\n");
  566. printf(" -n no synchronization (on adding a node)\n");
  567. printf(" -h display this help text\n");
  568. printf("\n");
  569. }
  570. int main(int argc, char **argv)
  571. {
  572. qb_loop_signal_handle sigchld_qb_handle;
  573. int ch;
  574. char *output_file_name = NULL;
  575. while ((ch = getopt (argc, argv, "f:o:nh")) != EOF) {
  576. switch (ch) {
  577. case 'f':
  578. strncpy(corosync_config_file, optarg, sizeof(corosync_config_file));
  579. break;
  580. case 'o':
  581. output_file_name = optarg;
  582. break;
  583. case 'n':
  584. nosync = 1;
  585. break;
  586. default:
  587. usage(argv[0]);
  588. exit(0);
  589. }
  590. }
  591. if (output_file_name) {
  592. output_file = fopen(output_file_name, "w");
  593. if (!output_file) {
  594. fprintf(stderr, "Unable to open %s for output: %s\n", output_file_name, strerror(errno));
  595. exit(-1);
  596. }
  597. }
  598. else {
  599. output_file = stdout;
  600. }
  601. #ifndef HAVE_READLINE_READLINE_H
  602. is_tty = isatty(STDIN_FILENO);
  603. #endif
  604. qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
  605. QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG);
  606. qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
  607. qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
  608. QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG);
  609. poll_loop = qb_loop_create();
  610. /* SIGCHLD handler to reap sub-processes and reconfigure the cluster */
  611. qb_loop_signal_add(poll_loop,
  612. QB_LOOP_MED,
  613. SIGCHLD,
  614. NULL,
  615. sigchld_handler,
  616. &sigchld_qb_handle);
  617. /* Create a full cluster of nodes from corosync.conf */
  618. read_corosync_conf();
  619. if (create_nodes_from_config() && !nosync) {
  620. /* Delay kb input handling by 1 second when we've just
  621. added the nodes from corosync.conf; expect that
  622. the delay will be cancelled substantially earlier
  623. once they all have reported their quorum info
  624. (the delay is in fact a failsafe input enabler here) */
  625. qb_loop_timer_add(poll_loop,
  626. QB_LOOP_MED,
  627. 1000000000,
  628. NULL,
  629. start_kb_input_timeout,
  630. &kb_timer);
  631. } else {
  632. start_kb_input();
  633. }
  634. qb_loop_run(poll_loop);
  635. return 0;
  636. }