1 
2 /*
3  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This software is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <crm_internal.h>
21 
22 #include <sys/param.h>
23 
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <libgen.h>
32 
33 #include <crm/crm.h>
34 #include <crm/msg_xml.h>
35 #include <crm/common/xml.h>
36 
37 #include <crm/common/mainloop.h>
38 
39 #include <crm/cib.h>
40 
41 int message_timer_id = -1;
42 int message_timeout_ms = 30 * 1000;
43 
44 GMainLoop *mainloop = NULL;
45 crm_ipc_t *crmd_channel = NULL;
46 char *admin_uuid = NULL;
47 
48 void usage(const char *cmd, int exit_status);
49 gboolean do_init(void);
50 int do_work(void);
51 void crmadmin_ipc_connection_destroy(gpointer user_data);
52 
53 int admin_msg_callback(const char *buffer, ssize_t length, gpointer userdata);
54 char *pluralSection(const char *a_section);
55 xmlNode *handleCibMod(void);
56 int do_find_node_list(xmlNode * xml_node);
57 gboolean admin_message_timeout(gpointer data);
58 gboolean is_node_online(xmlNode * node_state);
59 
60 enum debug {
61     debug_none,
62     debug_dec,
63     debug_inc
64 };
65 
66 gboolean BE_VERBOSE = FALSE;
67 int expected_responses = 1;
68 
69 gboolean BASH_EXPORT = FALSE;
70 gboolean DO_HEALTH = FALSE;
71 gboolean DO_RESET = FALSE;
72 gboolean DO_RESOURCE = FALSE;
73 gboolean DO_ELECT_DC = FALSE;
74 gboolean DO_WHOIS_DC = FALSE;
75 gboolean DO_NODE_LIST = FALSE;
76 gboolean BE_SILENT = FALSE;
77 gboolean DO_RESOURCE_LIST = FALSE;
78 enum debug DO_DEBUG = debug_none;
79 const char *crmd_operation = NULL;
80 
81 xmlNode *msg_options = NULL;
82 
83 const char *standby_on_off = "on";
84 const char *admin_verbose = XML_BOOLEAN_FALSE;
85 char *id = NULL;
86 char *disconnect = NULL;
87 char *dest_node = NULL;
88 char *rsc_name = NULL;
89 char *crm_option = NULL;
90 
91 int operation_status = 0;
92 const char *sys_to = NULL;
93 
94 /* *INDENT-OFF* */
95 static struct crm_option long_options[] = {
96     /* Top-level Options */
97     {"help",    0, 0, '?', "\tThis text"},
98     {"version", 0, 0, '$', "\tVersion information"  },
99     {"quiet",   0, 0, 'q', "\tDisplay only the essential query information"},
100     {"verbose", 0, 0, 'V', "\tIncrease debug output"},
101 
102     {"-spacer-",	1, 0, '-', "\nCommands:"},
103     /* daemon options */
104     {"debug_inc", 1, 0, 'i', "Increase the crmd's debug level on the specified host"},
105     {"debug_dec", 1, 0, 'd', "Decrease the crmd's debug level on the specified host"},
106     {"status",    1, 0, 'S', "Display the status of the specified node." },
107     {"-spacer-",  1, 0, '-', "\n\tResult is the node's internal FSM state which can be useful for debugging\n"},
108     {"dc_lookup", 0, 0, 'D', "Display the uname of the node co-ordinating the cluster."},
109     {"-spacer-",  1, 0, '-', "\n\tThis is an internal detail and is rarely useful to administrators except when deciding on which node to examine the logs.\n"},
110     {"nodes",     0, 0, 'N', "\tDisplay the uname of all member nodes"},
111     {"election",  0, 0, 'E', "(Advanced) Start an election for the cluster co-ordinator"},
112     {"kill",      1, 0, 'K', "(Advanced) Shut down the crmd (not the rest of the clusterstack ) on the specified node"},
113     {"health",    0, 0, 'H', NULL, 1},
114 
115     {"-spacer-",	1, 0, '-', "\nAdditional Options:"},
116     {XML_ATTR_TIMEOUT, 1, 0, 't', "Time (in milliseconds) to wait before declaring the operation failed"},
117     {"bash-export", 0, 0, 'B', "Create Bash export entries of the form 'export uname=uuid'\n"},
118 
119     {"-spacer-",  1, 0, '-', "Notes:"},
120     {"-spacer-",  1, 0, '-', " The -i,-d,-K and -E commands are rarely used and may be removed in future versions."},
121 
122     {0, 0, 0, 0}
123 };
124 /* *INDENT-ON* */
125 
126 int
main(int argc,char ** argv)127 main(int argc, char **argv)
128 {
129     int option_index = 0;
130     int argerr = 0;
131     int flag;
132 
133     crm_log_cli_init("crmadmin");
134     crm_set_options(NULL, "command [options]", long_options,
135                     "Development tool for performing some crmd-specific commands."
136                     "\n  Likely to be replaced by crm_node in the future");
137     if (argc < 2) {
138         crm_help('?', EX_USAGE);
139     }
140 
141     while (1) {
142         flag = crm_get_option(argc, argv, &option_index);
143         if (flag == -1)
144             break;
145 
146         switch (flag) {
147             case 'V':
148                 BE_VERBOSE = TRUE;
149                 admin_verbose = XML_BOOLEAN_TRUE;
150                 crm_bump_log_level(argc, argv);
151                 break;
152             case 't':
153                 message_timeout_ms = atoi(optarg);
154                 if (message_timeout_ms < 1) {
155                     message_timeout_ms = 30 * 1000;
156                 }
157                 break;
158 
159             case '$':
160             case '?':
161                 crm_help(flag, EX_OK);
162                 break;
163             case 'D':
164                 DO_WHOIS_DC = TRUE;
165                 break;
166             case 'B':
167                 BASH_EXPORT = TRUE;
168                 break;
169             case 'K':
170                 DO_RESET = TRUE;
171                 crm_trace("Option %c => %s", flag, optarg);
172                 dest_node = strdup(optarg);
173                 crmd_operation = CRM_OP_LOCAL_SHUTDOWN;
174                 break;
175             case 'q':
176                 BE_SILENT = TRUE;
177                 break;
178             case 'i':
179                 DO_DEBUG = debug_inc;
180                 crm_trace("Option %c => %s", flag, optarg);
181                 dest_node = strdup(optarg);
182                 break;
183             case 'd':
184                 DO_DEBUG = debug_dec;
185                 crm_trace("Option %c => %s", flag, optarg);
186                 dest_node = strdup(optarg);
187                 break;
188             case 'S':
189                 DO_HEALTH = TRUE;
190                 crm_trace("Option %c => %s", flag, optarg);
191                 dest_node = strdup(optarg);
192                 break;
193             case 'E':
194                 DO_ELECT_DC = TRUE;
195                 break;
196             case 'N':
197                 DO_NODE_LIST = TRUE;
198                 break;
199             case 'H':
200                 DO_HEALTH = TRUE;
201                 break;
202             default:
203                 printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
204                 ++argerr;
205                 break;
206         }
207     }
208 
209     if (optind < argc) {
210         printf("non-option ARGV-elements: ");
211         while (optind < argc)
212             printf("%s ", argv[optind++]);
213         printf("\n");
214     }
215 
216     if (optind > argc) {
217         ++argerr;
218     }
219 
220     if (argerr) {
221         crm_help('?', EX_USAGE);
222     }
223 
224     if (do_init()) {
225         int res = 0;
226 
227         res = do_work();
228         if (res > 0) {
229             /* wait for the reply by creating a mainloop and running it until
230              * the callbacks are invoked...
231              */
232             mainloop = g_main_new(FALSE);
233             crm_trace("Waiting for %d replies from the local CRM", expected_responses);
234 
235             message_timer_id = g_timeout_add(message_timeout_ms, admin_message_timeout, NULL);
236 
237             g_main_run(mainloop);
238 
239         } else if (res < 0) {
240             crm_err("No message to send");
241             operation_status = -1;
242         }
243     } else {
244         crm_warn("Init failed, could not perform requested operations");
245         operation_status = -2;
246     }
247 
248     crm_trace("%s exiting normally", crm_system_name);
249     return operation_status;
250 }
251 
252 int
do_work(void)253 do_work(void)
254 {
255     int ret = 1;
256 
257     /* construct the request */
258     xmlNode *msg_data = NULL;
259     gboolean all_is_good = TRUE;
260 
261     msg_options = create_xml_node(NULL, XML_TAG_OPTIONS);
262     crm_xml_add(msg_options, XML_ATTR_VERBOSE, admin_verbose);
263     crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0");
264 
265     if (DO_HEALTH == TRUE) {
266         crm_trace("Querying the system");
267 
268         sys_to = CRM_SYSTEM_DC;
269 
270         if (dest_node != NULL) {
271             sys_to = CRM_SYSTEM_CRMD;
272             crmd_operation = CRM_OP_PING;
273 
274             if (BE_VERBOSE) {
275                 expected_responses = 1;
276             }
277 
278             crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0");
279 
280         } else {
281             crm_info("Cluster-wide health not available yet");
282             all_is_good = FALSE;
283         }
284 
285     } else if (DO_ELECT_DC) {
286         /* tell the local node to initiate an election */
287 
288         dest_node = NULL;
289         sys_to = CRM_SYSTEM_CRMD;
290         crmd_operation = CRM_OP_VOTE;
291 
292         crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0");
293         ret = 0;                /* no return message */
294 
295     } else if (DO_WHOIS_DC) {
296         dest_node = NULL;
297         sys_to = CRM_SYSTEM_DC;
298         crmd_operation = CRM_OP_PING;
299         crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0");
300 
301     } else if (DO_NODE_LIST) {
302 
303         cib_t *the_cib = cib_new();
304         xmlNode *output = NULL;
305 
306         int rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
307 
308         if (rc != pcmk_ok) {
309             return -1;
310         }
311 
312         rc = the_cib->cmds->query(the_cib, NULL, &output, cib_scope_local | cib_sync_call);
313         if(rc == pcmk_ok) {
314             do_find_node_list(output);
315 
316             free_xml(output);
317         }
318         the_cib->cmds->signoff(the_cib);
319         crm_exit(rc);
320 
321     } else if (DO_RESET) {
322         /* tell dest_node to initiate the shutdown procedure
323          *
324          * if dest_node is NULL, the request will be sent to the
325          *   local node
326          */
327         sys_to = CRM_SYSTEM_CRMD;
328         crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0");
329 
330         ret = 0;                /* no return message */
331 
332     } else if (DO_DEBUG == debug_inc) {
333         /* tell dest_node to increase its debug level
334          *
335          * if dest_node is NULL, the request will be sent to the
336          *   local node
337          */
338         sys_to = CRM_SYSTEM_CRMD;
339         crmd_operation = CRM_OP_DEBUG_UP;
340 
341         ret = 0;                /* no return message */
342 
343     } else if (DO_DEBUG == debug_dec) {
344         /* tell dest_node to increase its debug level
345          *
346          * if dest_node is NULL, the request will be sent to the
347          *   local node
348          */
349         sys_to = CRM_SYSTEM_CRMD;
350         crmd_operation = CRM_OP_DEBUG_DOWN;
351 
352         ret = 0;                /* no return message */
353 
354     } else {
355         crm_err("Unknown options");
356         all_is_good = FALSE;
357     }
358 
359     if (all_is_good == FALSE) {
360         crm_err("Creation of request failed.  No message to send");
361         return -1;
362     }
363 
364 /* send it */
365     if (crmd_channel == NULL) {
366         crm_err("The IPC connection is not valid, cannot send anything");
367         return -1;
368     }
369 
370     if (sys_to == NULL) {
371         if (dest_node != NULL) {
372             sys_to = CRM_SYSTEM_CRMD;
373         } else {
374             sys_to = CRM_SYSTEM_DC;
375         }
376     }
377 
378     {
379         xmlNode *cmd = create_request(crmd_operation, msg_data, dest_node, sys_to,
380                                       crm_system_name, admin_uuid);
381 
382         crm_ipc_send(crmd_channel, cmd, 0, 0, NULL);
383         free_xml(cmd);
384     }
385 
386     return ret;
387 }
388 
389 void
crmadmin_ipc_connection_destroy(gpointer user_data)390 crmadmin_ipc_connection_destroy(gpointer user_data)
391 {
392     crm_err("Connection to CRMd was terminated");
393     if (mainloop) {
394         g_main_quit(mainloop);
395     } else {
396         crm_exit(ENOTCONN);
397     }
398 }
399 
400 struct ipc_client_callbacks crm_callbacks = {
401     .dispatch = admin_msg_callback,
402     .destroy = crmadmin_ipc_connection_destroy
403 };
404 
405 gboolean
do_init(void)406 do_init(void)
407 {
408     mainloop_io_t *source =
409         mainloop_add_ipc_client(CRM_SYSTEM_CRMD, G_PRIORITY_DEFAULT, 0, NULL, &crm_callbacks);
410 
411     admin_uuid = crm_getpid_s();
412 
413     crmd_channel = mainloop_get_ipc_client(source);
414 
415     if (DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST) {
416         return TRUE;
417 
418     } else if (crmd_channel != NULL) {
419         xmlNode *xml = create_hello_message(admin_uuid, crm_system_name, "0", "1");
420 
421         crm_ipc_send(crmd_channel, xml, 0, 0, NULL);
422         return TRUE;
423     }
424     return FALSE;
425 }
426 
427 static bool
validate_crm_message(xmlNode * msg,const char * sys,const char * uuid,const char * msg_type)428 validate_crm_message(xmlNode * msg, const char *sys, const char *uuid, const char *msg_type)
429 {
430     const char *type = NULL;
431     const char *crm_msg_reference = NULL;
432 
433     if (msg == NULL) {
434         return FALSE;
435     }
436 
437     type = crm_element_value(msg, F_CRM_MSG_TYPE);
438     crm_msg_reference = crm_element_value(msg, XML_ATTR_REFERENCE);
439 
440     if (type == NULL) {
441         crm_info("No message type defined.");
442         return FALSE;
443 
444     } else if (msg_type != NULL && strcasecmp(msg_type, type) != 0) {
445         crm_info("Expecting a (%s) message but received a (%s).", msg_type, type);
446         return FALSE;
447     }
448 
449     if (crm_msg_reference == NULL) {
450         crm_info("No message crm_msg_reference defined.");
451         return FALSE;
452     }
453 
454     return TRUE;
455 }
456 
457 int
admin_msg_callback(const char * buffer,ssize_t length,gpointer userdata)458 admin_msg_callback(const char *buffer, ssize_t length, gpointer userdata)
459 {
460     static int received_responses = 0;
461     xmlNode *xml = string2xml(buffer);
462 
463     received_responses++;
464     g_source_remove(message_timer_id);
465 
466     crm_log_xml_trace(xml, "ipc");
467 
468     if (xml == NULL) {
469         crm_info("XML in IPC message was not valid... " "discarding.");
470 
471     } else if (validate_crm_message(xml, crm_system_name, admin_uuid, XML_ATTR_RESPONSE) == FALSE) {
472         crm_trace("Message was not a CRM response. Discarding.");
473 
474     } else if (DO_HEALTH) {
475         xmlNode *data = get_message_xml(xml, F_CRM_DATA);
476         const char *state = crm_element_value(data, XML_PING_ATTR_CRMDSTATE);
477 
478         printf("Status of %s@%s: %s (%s)\n",
479                crm_element_value(data, XML_PING_ATTR_SYSFROM),
480                crm_element_value(xml, F_CRM_HOST_FROM),
481                state, crm_element_value(data, XML_PING_ATTR_STATUS));
482 
483         if (BE_SILENT && state != NULL) {
484             fprintf(stderr, "%s\n", state);
485         }
486 
487     } else if (DO_WHOIS_DC) {
488         const char *dc = crm_element_value(xml, F_CRM_HOST_FROM);
489 
490         if (dc != NULL) {
491             if (BE_SILENT == FALSE) {
492                 printf("Designated Controller is: ");
493             }
494             printf("%s\n", dc);
495         }
496         crm_exit(pcmk_ok);
497     }
498 
499     free_xml(xml);
500 
501     if (received_responses >= expected_responses) {
502         crm_trace("Received expected number (%d) of messages from Heartbeat."
503                   "  Exiting normally.", expected_responses);
504         crm_exit(pcmk_ok);
505     }
506 
507     message_timer_id = g_timeout_add(message_timeout_ms, admin_message_timeout, NULL);
508     return 0;
509 }
510 
511 gboolean
admin_message_timeout(gpointer data)512 admin_message_timeout(gpointer data)
513 {
514     fprintf(stderr, "No messages received in %d seconds.. aborting\n",
515             (int)message_timeout_ms / 1000);
516     crm_err("No messages received in %d seconds", (int)message_timeout_ms / 1000);
517     operation_status = -3;
518     g_main_quit(mainloop);
519     return FALSE;
520 }
521 
522 gboolean
is_node_online(xmlNode * node_state)523 is_node_online(xmlNode * node_state)
524 {
525     const char *uname = crm_element_value(node_state, XML_ATTR_UNAME);
526     const char *join_state = crm_element_value(node_state, XML_NODE_JOIN_STATE);
527     const char *exp_state = crm_element_value(node_state, XML_NODE_EXPECTED);
528     const char *crm_state = crm_element_value(node_state, XML_NODE_IS_PEER);
529     const char *ccm_state = crm_element_value(node_state, XML_NODE_IN_CLUSTER);
530 
531     if (safe_str_neq(join_state, CRMD_JOINSTATE_DOWN)
532         && crm_is_true(ccm_state)
533         && safe_str_eq(crm_state, "online")) {
534         crm_trace("Node %s is online", uname);
535         return TRUE;
536     }
537     crm_trace("Node %s: ccm=%s join=%s exp=%s crm=%s",
538               uname, crm_str(ccm_state),
539               crm_str(join_state), crm_str(exp_state), crm_str(crm_state));
540     crm_trace("Node %s is offline", uname);
541     return FALSE;
542 }
543 
544 int
do_find_node_list(xmlNode * xml_node)545 do_find_node_list(xmlNode * xml_node)
546 {
547     int found = 0;
548     xmlNode *node = NULL;
549     xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
550 
551     for (node = __xml_first_child_element(nodes); node != NULL;
552          node = __xml_next_element(node)) {
553 
554         if (crm_str_eq((const char *)node->name, XML_CIB_TAG_NODE, TRUE)) {
555 
556             if (BASH_EXPORT) {
557                 printf("export %s=%s\n",
558                        crm_element_value(node, XML_ATTR_UNAME),
559                        crm_element_value(node, XML_ATTR_ID));
560             } else {
561                 printf("%s node: %s (%s)\n",
562                        crm_element_value(node, XML_ATTR_TYPE),
563                        crm_element_value(node, XML_ATTR_UNAME),
564                        crm_element_value(node, XML_ATTR_ID));
565             }
566             found++;
567         }
568     }
569 
570     if (found == 0) {
571         printf("NO nodes configured\n");
572     }
573 
574     return found;
575 }
576