vqmain.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  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. static int stdin_read_fn(int32_t fd, int32_t revents, void *data);
  25. void totemknet_configure_log_level(void);
  26. /* 'Keep the compiler happy' time */
  27. const char *corosync_get_config_file(void);
  28. /* One of these per partition */
  29. struct vq_partition {
  30. TAILQ_HEAD(, vq_node) nodelist;
  31. struct memb_ring_id ring_id;
  32. int num;
  33. };
  34. /* One of these per node */
  35. struct vq_node {
  36. vq_object_t instance;
  37. unsigned int nodeid;
  38. int fd;
  39. struct vq_partition *partition;
  40. TAILQ_ENTRY(vq_node) entries;
  41. /* Last status */
  42. int last_quorate;
  43. struct memb_ring_id last_ring_id;
  44. int last_view_list[MAX_NODES];
  45. int last_view_list_entries;
  46. };
  47. static struct vq_partition partitions[MAX_PARTITIONS];
  48. static qb_loop_t *poll_loop;
  49. static int autofence;
  50. static int check_for_quorum;
  51. static FILE *output_file;
  52. static int sync_cmds = 1;
  53. static qb_loop_timer_handle kb_timer;
  54. static int waiting_for_sync = 0;
  55. static int is_tty;
  56. static int assert_on_timeout;
  57. static uint64_t command_timeout = 250000000L;
  58. static struct vq_node *find_by_pid(pid_t pid);
  59. static void send_partition_to_nodes(struct vq_partition *partition, int newring);
  60. static void start_kb_input_timeout(void *data);
  61. static void finish_wait_timeout(void *data);
  62. #ifndef HAVE_READLINE_READLINE_H
  63. #define INPUT_BUF_SIZE 1024
  64. static char input_buf[INPUT_BUF_SIZE];
  65. static size_t input_buf_term = 0;
  66. #endif
  67. /* 'Keep the compiler happy' time */
  68. static char corosync_config_file[PATH_MAX + 1] = COROSYSCONFDIR "/corosync.conf";
  69. const char *corosync_get_config_file(void)
  70. {
  71. return (corosync_config_file);
  72. }
  73. /* Tell all non-quorate nodes to quit */
  74. static void force_fence(void)
  75. {
  76. int i;
  77. struct vq_node *vqn;
  78. for (i=0; i<MAX_PARTITIONS; i++) {
  79. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  80. vq_quit_if_inquorate(vqn->instance);
  81. }
  82. }
  83. }
  84. /* Save quorum state from the incoming message */
  85. static void save_quorum_state(struct vq_node *node, struct vqsim_quorum_msg *qmsg)
  86. {
  87. node->last_quorate = qmsg->quorate;
  88. memcpy(&node->last_ring_id, &qmsg->ring_id, sizeof(struct memb_ring_id));
  89. memcpy(node->last_view_list, qmsg->view_list, sizeof(int) * qmsg->view_list_entries);
  90. node->last_view_list_entries = qmsg->view_list_entries;
  91. /* If at least one node is quorate and autofence is enabled, then fence everyone who is not quorate */
  92. if (check_for_quorum && qmsg->quorate & autofence) {
  93. check_for_quorum = 0;
  94. force_fence();
  95. }
  96. }
  97. /* Print current node state */
  98. static void print_quorum_state(struct vq_node *node)
  99. {
  100. int i;
  101. if (node->last_quorate < 0) {
  102. fprintf(output_file, "%d:" CS_PRI_NODE_ID ": q=UNINITIALIZED\n",
  103. node->partition->num, node->nodeid);
  104. return;
  105. }
  106. fprintf(output_file, "%d:" CS_PRI_NODE_ID ": q=%d ring=[" CS_PRI_RING_ID "] ", node->partition->num, node->nodeid, node->last_quorate,
  107. node->last_ring_id.nodeid, (uint64_t)node->last_ring_id.seq);
  108. fprintf(output_file, "nodes=[");
  109. for (i = 0; i < node->last_view_list_entries; i++) {
  110. if (i) {
  111. fprintf(output_file, " ");
  112. }
  113. fprintf(output_file, CS_PRI_NODE_ID, node->last_view_list[i]);
  114. }
  115. fprintf(output_file, "]\n");
  116. }
  117. static void propogate_vq_message(struct vq_node *vqn, const char *msg, int len)
  118. {
  119. struct vq_node *other_vqn;
  120. ssize_t write_res;
  121. /* Send it to everyone in that node's partition (including itself) */
  122. TAILQ_FOREACH(other_vqn, &vqn->partition->nodelist, entries) {
  123. write_res = write(other_vqn->fd, msg, len);
  124. /*
  125. * Read counterpart is not ready for receiving non-complete message so
  126. * ensure all required information was send.
  127. */
  128. assert(write_res == len);
  129. }
  130. }
  131. static void cmd_show_prompt_if_needed(void)
  132. {
  133. qb_loop_timer_del(poll_loop, kb_timer);
  134. if (is_tty) {
  135. printf("vqsim> ");
  136. fflush(stdout);
  137. } else {
  138. printf("#vqsim> ");
  139. fflush(stdout);
  140. }
  141. }
  142. void resume_kb_input(int show_status)
  143. {
  144. /* If running synchronously, we don't display
  145. the quorum messages as they come in. So run 'show' commamnd
  146. */
  147. if (show_status && waiting_for_sync) {
  148. cmd_show_node_states();
  149. }
  150. waiting_for_sync = 0;
  151. if (qb_loop_poll_add(poll_loop,
  152. QB_LOOP_MED,
  153. STDIN_FILENO,
  154. POLLIN | POLLERR,
  155. NULL,
  156. stdin_read_fn)) {
  157. if (errno != EEXIST) {
  158. perror("qb_loop_poll_add1 returned error");
  159. }
  160. }
  161. /* Always shows the prompt here, cos we cleared waiting_for_sync */
  162. cmd_show_prompt_if_needed();
  163. }
  164. /* Return true (1) if all nodes in each partition have the same ring id, false(0) otherwise */
  165. static int all_nodes_consistent(void)
  166. {
  167. int i;
  168. struct vq_node *vqn;
  169. struct memb_ring_id last_ring_id;
  170. for (i=0; i<MAX_PARTITIONS; i++) {
  171. memset(&last_ring_id, 0, sizeof(last_ring_id));
  172. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  173. if (last_ring_id.seq &&
  174. last_ring_id.seq != vqn->last_ring_id.seq) {
  175. return 0;
  176. }
  177. last_ring_id.seq = vqn->last_ring_id.seq;
  178. }
  179. }
  180. return 1;
  181. }
  182. static int vq_parent_read_fn(int32_t fd, int32_t revents, void *data)
  183. {
  184. char msgbuf[8192];
  185. int msglen;
  186. struct vqsim_msg_header *msg;
  187. struct vqsim_quorum_msg *qmsg;
  188. struct vq_node *vqn = data;
  189. if (revents == POLLIN) {
  190. msglen = read(fd, msgbuf, sizeof(msgbuf));
  191. if (msglen < 0) {
  192. perror("read failed");
  193. } else if (msglen < sizeof(*msg)) {
  194. fprintf(stderr, "Received message is too short\n");
  195. } else {
  196. msg = (void*)msgbuf;
  197. switch (msg->type) {
  198. case VQMSG_QUORUM:
  199. qmsg = (void*)msgbuf;
  200. /*
  201. * Check length of message.
  202. * SOCK_SEQPACKET is used so this check is not strictly needed.
  203. */
  204. if (msglen < sizeof(*qmsg) ||
  205. qmsg->view_list_entries > MAX_NODES ||
  206. msglen < sizeof(*qmsg) + sizeof(qmsg->view_list[0]) * qmsg->view_list_entries) {
  207. fprintf(stderr, "Received quorum message is too short or corrupted\n");
  208. return (0);
  209. }
  210. save_quorum_state(vqn, qmsg);
  211. if (!sync_cmds) {
  212. print_quorum_state(vqn);
  213. }
  214. /* Have the partitions stabilised? */
  215. if (sync_cmds && waiting_for_sync &&
  216. all_nodes_consistent()) {
  217. qb_loop_timer_del(poll_loop, kb_timer);
  218. resume_kb_input(sync_cmds);
  219. }
  220. break;
  221. case VQMSG_EXEC:
  222. /* Message from votequorum, pass around the partition */
  223. propogate_vq_message(vqn, msgbuf, msglen);
  224. break;
  225. case VQMSG_QUIT:
  226. case VQMSG_SYNC:
  227. case VQMSG_QDEVICE:
  228. case VQMSG_QUORUMQUIT:
  229. /* not used here */
  230. break;
  231. }
  232. }
  233. }
  234. if (revents == POLLERR) {
  235. fprintf(stderr, "pollerr on " CS_PRI_NODE_ID "\n", vqn->nodeid);
  236. }
  237. return 0;
  238. }
  239. /* Dummy routine to keep the linker happy */
  240. void totemknet_configure_log_level(void)
  241. {
  242. }
  243. static int read_corosync_conf(void)
  244. {
  245. int res;
  246. const char *error_string;
  247. int err = icmap_init();
  248. if (!err) {
  249. fprintf(stderr, "icmap_init failed\n");
  250. }
  251. /* Load corosync.conf */
  252. logsys_format_set(NULL);
  253. res = coroparse_configparse(icmap_get_global_map(), &error_string);
  254. if (res == -1) {
  255. log_printf (LOGSYS_LEVEL_INFO, "Error loading corosync.conf %s", error_string);
  256. return -1;
  257. }
  258. else {
  259. res = corosync_log_config_read (&error_string);
  260. if (res < 0) {
  261. log_printf (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string);
  262. syslog (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string);
  263. }
  264. else {
  265. logsys_config_apply();
  266. }
  267. }
  268. if (logsys_thread_start() != 0) {
  269. log_printf (LOGSYS_LEVEL_ERROR, "Can't initialize log thread");
  270. return -1;
  271. }
  272. return 0;
  273. }
  274. static void remove_node(struct vq_node *node)
  275. {
  276. struct vq_partition *part;
  277. part = node->partition;
  278. /* Remove from partition list */
  279. TAILQ_REMOVE(&part->nodelist, node, entries);
  280. free(node);
  281. /* Rebuild quorum */
  282. send_partition_to_nodes(part, 1);
  283. }
  284. static int32_t sigchld_handler(int32_t sig, void *data)
  285. {
  286. pid_t pid;
  287. int status;
  288. struct vq_node *vqn;
  289. const char *exit_status="";
  290. char text[132];
  291. pid = wait(&status);
  292. if (WIFEXITED(status)) {
  293. vqn = find_by_pid(pid);
  294. if (vqn) {
  295. switch (WEXITSTATUS(status)) {
  296. case 0:
  297. exit_status = "(on request)";
  298. break;
  299. case 1:
  300. exit_status = "(autofenced)";
  301. break;
  302. default:
  303. sprintf(text, "(exit code %d)", WEXITSTATUS(status));
  304. break;
  305. }
  306. printf("%d:" CS_PRI_NODE_ID ": Quit %s\n", vqn->partition->num, vqn->nodeid, exit_status);
  307. remove_node(vqn);
  308. }
  309. else {
  310. fprintf(stderr, "Unknown child %d exited with status %d\n", pid, WEXITSTATUS(status));
  311. }
  312. }
  313. if (WIFSIGNALED(status)) {
  314. vqn = find_by_pid(pid);
  315. if (vqn) {
  316. printf("%d:" CS_PRI_NODE_ID " exited on signal %d%s\n", vqn->partition->num, vqn->nodeid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":"");
  317. remove_node(vqn);
  318. }
  319. else {
  320. fprintf(stderr, "Unknown child %d exited with status %d%s\n", pid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":"");
  321. }
  322. }
  323. return 0;
  324. }
  325. static void send_partition_to_nodes(struct vq_partition *partition, int newring)
  326. {
  327. struct vq_node *vqn;
  328. int nodelist[MAX_NODES];
  329. int nodes = 0;
  330. int first = 1;
  331. if (newring) {
  332. /* Simulate corosync incrementing the seq by 4 for added authenticity */
  333. partition->ring_id.seq += 4;
  334. }
  335. /* Build the node list */
  336. TAILQ_FOREACH(vqn, &partition->nodelist, entries) {
  337. nodelist[nodes++] = vqn->nodeid;
  338. if (first) {
  339. partition->ring_id.nodeid = vqn->nodeid;
  340. first = 0;
  341. }
  342. }
  343. TAILQ_FOREACH(vqn, &partition->nodelist, entries) {
  344. vq_set_nodelist(vqn->instance, &partition->ring_id, nodelist, nodes);
  345. }
  346. }
  347. static void init_partitions(void)
  348. {
  349. int i;
  350. for (i=0; i<MAX_PARTITIONS; i++) {
  351. TAILQ_INIT(&partitions[i].nodelist);
  352. partitions[i].ring_id.nodeid = 1000+i;
  353. partitions[i].ring_id.seq = 0;
  354. partitions[i].num = i;
  355. }
  356. }
  357. static pid_t create_node(int nodeid, int partno)
  358. {
  359. struct vq_node *newvq;
  360. newvq = malloc(sizeof(struct vq_node));
  361. if (newvq) {
  362. newvq->last_quorate = -1; /* mark "uninitialized" */
  363. newvq->instance = vq_create_instance(poll_loop, nodeid);
  364. if (!newvq->instance) {
  365. fprintf(stderr,
  366. "ERR: could not create vq instance nodeid " CS_PRI_NODE_ID "\n",
  367. nodeid);
  368. free(newvq);
  369. return (pid_t) -1;
  370. }
  371. newvq->partition = &partitions[partno];
  372. newvq->nodeid = nodeid;
  373. newvq->fd = vq_get_parent_fd(newvq->instance);
  374. TAILQ_INSERT_TAIL(&partitions[partno].nodelist, newvq, entries);
  375. if (qb_loop_poll_add(poll_loop,
  376. QB_LOOP_MED,
  377. newvq->fd,
  378. POLLIN | POLLERR,
  379. newvq,
  380. vq_parent_read_fn)) {
  381. perror("qb_loop_poll_add returned error");
  382. return (pid_t) -1;
  383. }
  384. /* Send sync with all the nodes so far in it. */
  385. send_partition_to_nodes(&partitions[partno], 1);
  386. return vq_get_pid(newvq->instance);
  387. }
  388. return (pid_t) -1;
  389. }
  390. static size_t create_nodes_from_config(void)
  391. {
  392. icmap_iter_t iter;
  393. char tmp_key[ICMAP_KEYNAME_MAXLEN];
  394. uint32_t node_pos;
  395. uint32_t nodeid;
  396. const char *iter_key;
  397. int res;
  398. pid_t pid;
  399. size_t ret = 0;
  400. init_partitions();
  401. iter = icmap_iter_init("nodelist.node.");
  402. while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
  403. res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
  404. if (res != 2) {
  405. continue;
  406. }
  407. if (strcmp(tmp_key, "ring0_addr") != 0) {
  408. continue;
  409. }
  410. snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
  411. if (icmap_get_uint32(tmp_key, &nodeid) == CS_OK) {
  412. pid = create_node(nodeid, 0);
  413. if (pid == (pid_t) -1) {
  414. fprintf(stderr,
  415. "ERR: nodeid " CS_PRI_NODE_ID " could not be spawned\n",
  416. nodeid);
  417. exit(1);
  418. }
  419. ret++;
  420. }
  421. }
  422. icmap_iter_finalize(iter);
  423. return ret;
  424. }
  425. static struct vq_node *find_node(int nodeid)
  426. {
  427. int i;
  428. struct vq_node *vqn;
  429. for (i=0; i<MAX_PARTITIONS; i++) {
  430. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  431. if (vqn->nodeid == nodeid) {
  432. return vqn;
  433. }
  434. }
  435. }
  436. return NULL;
  437. }
  438. static struct vq_node *find_by_pid(pid_t pid)
  439. {
  440. int i;
  441. struct vq_node *vqn;
  442. for (i=0; i<MAX_PARTITIONS; i++) {
  443. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  444. if (vq_get_pid(vqn->instance) == pid) {
  445. return vqn;
  446. }
  447. }
  448. }
  449. return NULL;
  450. }
  451. /* Routines called from the parser */
  452. /*
  453. * The parser calls this before running a command where
  454. * we might have to wait for a result to come back.
  455. */
  456. void cmd_start_sync_command()
  457. {
  458. if (sync_cmds) {
  459. qb_loop_poll_del(poll_loop, STDIN_FILENO);
  460. qb_loop_timer_add(poll_loop,
  461. QB_LOOP_MED,
  462. command_timeout,
  463. NULL,
  464. finish_wait_timeout,
  465. &kb_timer);
  466. waiting_for_sync = 1;
  467. }
  468. }
  469. int cmd_start_new_node(int nodeid, int partition)
  470. {
  471. struct vq_node *node;
  472. node = find_node(nodeid);
  473. if (node) {
  474. fprintf(stderr, "ERR: nodeid " CS_PRI_NODE_ID " already exists in partition %d\n", nodeid, node->partition->num);
  475. return -1;
  476. }
  477. if (create_node(nodeid, partition) == -1) {
  478. return -1;
  479. }
  480. return 0;
  481. }
  482. void cmd_stop_all_nodes()
  483. {
  484. int i;
  485. struct vq_node *vqn;
  486. for (i=0; i<MAX_PARTITIONS; i++) {
  487. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  488. vq_quit(vqn->instance);
  489. }
  490. }
  491. }
  492. void cmd_show_node_states()
  493. {
  494. int i;
  495. struct vq_node *vqn;
  496. for (i=0; i<MAX_PARTITIONS; i++) {
  497. TAILQ_FOREACH(vqn, &partitions[i].nodelist, entries) {
  498. print_quorum_state(vqn);
  499. }
  500. }
  501. fprintf(output_file, "#autofence: %s\n", autofence?"on":"off");
  502. }
  503. int cmd_stop_node(int nodeid)
  504. {
  505. struct vq_node *node;
  506. node = find_node(nodeid);
  507. if (!node) {
  508. fprintf(stderr, "ERR: nodeid " CS_PRI_NODE_ID " is not up\n", nodeid);
  509. return -1;
  510. }
  511. /* Remove processor */
  512. vq_quit(node->instance);
  513. /* Node will be removed when the child process exits */
  514. return 0;
  515. }
  516. /* Move all nodes in 'nodelist' into partition 'partition' */
  517. void cmd_move_nodes(int partition, int num_nodes, int *nodelist)
  518. {
  519. int i;
  520. struct vq_node *node;
  521. struct vq_node *vqn;
  522. int total_nodes = num_nodes;
  523. /* Work out the number of nodes affected */
  524. TAILQ_FOREACH(vqn, &partitions[partition].nodelist, entries) {
  525. total_nodes++;
  526. }
  527. for (i=0; i<num_nodes; i++) {
  528. node = find_node(nodelist[i]);
  529. if (node) {
  530. /* Remove it from the current partition */
  531. TAILQ_REMOVE(&node->partition->nodelist, node, entries);
  532. /* Add it to the new partition */
  533. TAILQ_INSERT_TAIL(&partitions[partition].nodelist, node, entries);
  534. node->partition = &partitions[partition];
  535. }
  536. else {
  537. printf("ERR: node " CS_PRI_NODE_ID " does not exist\n", nodelist[i]);
  538. }
  539. }
  540. }
  541. /* Take all the nodes in part2 and join them to part1 */
  542. void cmd_join_partitions(int part1, int part2)
  543. {
  544. struct vq_node *vqn;
  545. while (!TAILQ_EMPTY(&partitions[part2].nodelist)) {
  546. vqn = TAILQ_FIRST(&partitions[part2].nodelist);
  547. TAILQ_REMOVE(&vqn->partition->nodelist, vqn, entries);
  548. TAILQ_INSERT_TAIL(&partitions[part1].nodelist, vqn, entries);
  549. vqn->partition = &partitions[part1];
  550. }
  551. }
  552. void cmd_set_autofence(int onoff)
  553. {
  554. autofence = onoff;
  555. fprintf(output_file, "#autofence: %s\n", onoff?"on":"off");
  556. }
  557. void cmd_set_sync(int onoff)
  558. {
  559. autofence = onoff;
  560. fprintf(output_file, "#sync: %s\n", onoff?"on":"off");
  561. sync_cmds = onoff;
  562. }
  563. void cmd_set_assert(int onoff)
  564. {
  565. assert_on_timeout = onoff;
  566. }
  567. void cmd_update_all_partitions(int newring)
  568. {
  569. int i;
  570. check_for_quorum = 1;
  571. for (i=0; i<MAX_PARTITIONS; i++) {
  572. send_partition_to_nodes(&partitions[i], newring);
  573. }
  574. }
  575. void cmd_qdevice_poll(int nodeid, int onoff)
  576. {
  577. struct vq_node *node;
  578. node = find_node(nodeid);
  579. if (node) {
  580. vq_set_qdevice(node->instance, &node->partition->ring_id, onoff);
  581. }
  582. }
  583. /* If we get called then a command has timed-out */
  584. static void finish_wait_timeout(void *data)
  585. {
  586. if (command_timeout) {
  587. fprintf(stderr, "ERR: Partition(s) not stable within timeout\n");
  588. if (assert_on_timeout) {
  589. exit(2);
  590. }
  591. }
  592. resume_kb_input(sync_cmds);
  593. }
  594. void cmd_set_timeout(uint64_t seconds)
  595. {
  596. command_timeout = seconds * QB_TIME_NS_IN_MSEC;
  597. }
  598. /* ---------------------------------- */
  599. #ifndef HAVE_READLINE_READLINE_H
  600. static void dummy_read_char(void);
  601. static void dummy_read_char()
  602. {
  603. int c, flush = 0;
  604. while (!flush) {
  605. c = getchar();
  606. if (++input_buf_term >= INPUT_BUF_SIZE) {
  607. if (c != '\n' && c != EOF)
  608. fprintf(stderr, "User input overflows the limit: %zu\n",
  609. (size_t) INPUT_BUF_SIZE);
  610. input_buf[INPUT_BUF_SIZE - 1] = '\0';
  611. flush = 1;
  612. } else if (c == '\n' || c == EOF) {
  613. input_buf[input_buf_term - 1] = '\0';
  614. flush = 1;
  615. } else {
  616. input_buf[input_buf_term - 1] = c;
  617. }
  618. }
  619. parse_input_command((c == EOF) ? NULL : input_buf);
  620. input_buf_term = 0;
  621. }
  622. #endif
  623. static int stdin_read_fn(int32_t fd, int32_t revents, void *data)
  624. {
  625. #ifdef HAVE_READLINE_READLINE_H
  626. /* Send it to readline */
  627. rl_callback_read_char();
  628. #else
  629. dummy_read_char();
  630. #endif
  631. return 0;
  632. }
  633. static void start_kb_input_timeout(void *data)
  634. {
  635. resume_kb_input(1);
  636. }
  637. static void usage(char *program)
  638. {
  639. printf("Usage:\n");
  640. printf("\n");
  641. printf("%s [-c <config-file>] [-o <output-file>]\n", program);
  642. printf("\n");
  643. printf(" -c config file. defaults to /etc/corosync/corosync.conf\n");
  644. printf(" -o output file. defaults to stdout\n");
  645. printf(" -n no synchronization (on adding a node)\n");
  646. printf(" -h display this help text\n");
  647. printf("\n");
  648. printf("%s always takes input from STDIN, but cannot use a file.\n", program);
  649. printf("If you want to script it then use\n cat | %s\n", program);
  650. printf("\n");
  651. }
  652. int main(int argc, char **argv)
  653. {
  654. qb_loop_signal_handle sigchld_qb_handle;
  655. int ch;
  656. char *output_file_name = NULL;
  657. while ((ch = getopt (argc, argv, "c:o:nh")) != EOF) {
  658. switch (ch) {
  659. case 'c':
  660. if (strlen(optarg) >= sizeof(sizeof(corosync_config_file) - 1)) {
  661. fprintf(stderr, "Corosync config file path too long\n");
  662. exit(1);
  663. }
  664. strncpy(corosync_config_file, optarg, sizeof(corosync_config_file) - 1);
  665. break;
  666. case 'o':
  667. output_file_name = optarg;
  668. break;
  669. case 'n':
  670. sync_cmds = 0;
  671. break;
  672. default:
  673. usage(argv[0]);
  674. exit(0);
  675. }
  676. }
  677. if (output_file_name) {
  678. output_file = fopen(output_file_name, "w");
  679. if (!output_file) {
  680. fprintf(stderr, "Unable to open %s for output: %s\n", output_file_name, strerror(errno));
  681. exit(3);
  682. }
  683. }
  684. else {
  685. output_file = stdout;
  686. }
  687. is_tty = isatty(STDIN_FILENO);
  688. qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
  689. QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG);
  690. qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
  691. qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
  692. QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG);
  693. poll_loop = qb_loop_create();
  694. /* SIGCHLD handler to reap sub-processes and reconfigure the cluster */
  695. qb_loop_signal_add(poll_loop,
  696. QB_LOOP_MED,
  697. SIGCHLD,
  698. NULL,
  699. sigchld_handler,
  700. &sigchld_qb_handle);
  701. #ifdef HAVE_READLINE_READLINE_H
  702. /* Readline will deal with completed lines when they arrive */
  703. /*
  704. * For scripting add '#' to the start of the prompt so that
  705. * parsers can ignore input lines
  706. */
  707. rl_already_prompted = 1;
  708. if (is_tty) {
  709. rl_callback_handler_install("vqsim> ", parse_input_command);
  710. } else {
  711. rl_callback_handler_install("#vqsim> ", parse_input_command);
  712. }
  713. #endif
  714. /* Create a full cluster of nodes from corosync.conf */
  715. read_corosync_conf();
  716. if (create_nodes_from_config() && sync_cmds) {
  717. /* Delay kb input handling by 1 second when we've just
  718. added the nodes from corosync.conf; expect that
  719. the delay will be cancelled substantially earlier
  720. once they all have reported their quorum info
  721. (the delay is in fact a failsafe input enabler here) */
  722. qb_loop_timer_add(poll_loop,
  723. QB_LOOP_MED,
  724. 1000000000,
  725. NULL,
  726. start_kb_input_timeout,
  727. &kb_timer);
  728. waiting_for_sync = 1;
  729. } else {
  730. resume_kb_input(0);
  731. }
  732. qb_loop_run(poll_loop);
  733. return 0;
  734. }