root/tools/crm_node.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. command_cb
  2. name_cb
  3. remove_cb
  4. PCMK__OUTPUT_ARGS
  5. PCMK__OUTPUT_ARGS
  6. PCMK__OUTPUT_ARGS
  7. PCMK__OUTPUT_ARGS
  8. PCMK__OUTPUT_ARGS
  9. PCMK__OUTPUT_ARGS
  10. PCMK__OUTPUT_ARGS
  11. PCMK__OUTPUT_ARGS
  12. PCMK__OUTPUT_ARGS
  13. PCMK__OUTPUT_ARGS
  14. sort_node
  15. controller_event_cb
  16. run_controller_mainloop
  17. print_node_id
  18. print_node_name
  19. print_quorum
  20. remove_from_section
  21. purge_node_from_cib
  22. purge_node_from
  23. purge_node_from_fencer
  24. remove_node
  25. build_arg_context
  26. main

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <inttypes.h>
  13 #include <stdio.h>
  14 #include <stdlib.h>
  15 #include <errno.h>
  16 #include <sys/types.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/cmdline_internal.h>
  20 #include <crm/common/output_internal.h>
  21 #include <crm/common/mainloop.h>
  22 #include <crm/msg_xml.h>
  23 #include <crm/cib.h>
  24 #include <crm/cib/internal.h>
  25 #include <crm/common/ipc_controld.h>
  26 #include <crm/common/attrd_internal.h>
  27 
  28 #include <pacemaker-internal.h>
  29 
  30 #define SUMMARY "crm_node - Tool for displaying low-level node information"
  31 
  32 struct {
  33     gboolean corosync;
  34     gboolean dangerous_cmd;
  35     gboolean force_flag;
  36     char command;
  37     int nodeid;
  38     char *target_uname;
  39 } options = {
  40     .command = '\0',
  41     .force_flag = FALSE
  42 };
  43 
  44 gboolean command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  45 gboolean name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  46 gboolean remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
  47 
  48 static GError *error = NULL;
  49 static GMainLoop *mainloop = NULL;
  50 static crm_exit_t exit_code = CRM_EX_OK;
  51 static pcmk__output_t *out = NULL;
  52 
  53 #define INDENT "                           "
  54 
  55 static GOptionEntry command_entries[] = {
  56     { "cluster-id", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  57       "Display this node's cluster id",
  58       NULL },
  59     { "list", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  60       "Display all known members (past and present) of this cluster",
  61       NULL },
  62     { "name", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  63       "Display the name used by the cluster for this node",
  64       NULL },
  65     { "partition", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  66       "Display the members of this partition",
  67       NULL },
  68     { "quorum", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
  69       "Display a 1 if our partition has quorum, 0 if not",
  70       NULL },
  71     { "name-for-id", 'N', 0, G_OPTION_ARG_CALLBACK, name_cb,
  72       "Display the name used by the cluster for the node with the specified ID",
  73       "ID" },
  74     { "remove", 'R', 0, G_OPTION_ARG_CALLBACK, remove_cb,
  75       "(Advanced) Remove the (stopped) node with the specified name from Pacemaker's\n"
  76       INDENT "configuration and caches (the node must already have been removed from\n"
  77       INDENT "the underlying cluster stack configuration",
  78       "NAME" },
  79 
  80     { NULL }
  81 };
  82 
  83 static GOptionEntry addl_entries[] = {
  84     { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force_flag,
  85       NULL,
  86       NULL },
  87 #if SUPPORT_COROSYNC
  88     /* Unused and deprecated */
  89     { "corosync", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.corosync,
  90       NULL,
  91       NULL },
  92 #endif
  93 
  94     // @TODO add timeout option for when IPC replies are needed
  95 
  96     { NULL }
  97 };
  98 
  99 static pcmk__supported_format_t formats[] = {
 100     PCMK__SUPPORTED_FORMAT_NONE,
 101     PCMK__SUPPORTED_FORMAT_TEXT,
 102     PCMK__SUPPORTED_FORMAT_XML,
 103     { NULL, NULL, NULL }
 104 };
 105 
 106 gboolean
 107 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 108     if (pcmk__str_eq("-i", option_name, pcmk__str_casei) || pcmk__str_eq("--cluster-id", option_name, pcmk__str_casei)) {
 109         options.command = 'i';
 110     } else if (pcmk__str_eq("-l", option_name, pcmk__str_casei) || pcmk__str_eq("--list", option_name, pcmk__str_casei)) {
 111         options.command = 'l';
 112     } else if (pcmk__str_eq("-n", option_name, pcmk__str_casei) || pcmk__str_eq("--name", option_name, pcmk__str_casei)) {
 113         options.command = 'n';
 114     } else if (pcmk__str_eq("-p", option_name, pcmk__str_casei) || pcmk__str_eq("--partition", option_name, pcmk__str_casei)) {
 115         options.command = 'p';
 116     } else if (pcmk__str_eq("-q", option_name, pcmk__str_casei) || pcmk__str_eq("--quorum", option_name, pcmk__str_casei)) {
 117         options.command = 'q';
 118     } else {
 119         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Unknown param passed to command_cb: %s", option_name);
 120         return FALSE;
 121     }
 122 
 123     return TRUE;
 124 }
 125 
 126 gboolean
 127 name_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 128     options.command = 'N';
 129     pcmk__scan_min_int(optarg, &(options.nodeid), 0);
 130     return TRUE;
 131 }
 132 
 133 gboolean
 134 remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 135     if (optarg == NULL) {
 136         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "-R option requires an argument");
 137         return FALSE;
 138     }
 139 
 140     options.command = 'R';
 141     options.dangerous_cmd = TRUE;
 142     pcmk__str_update(&options.target_uname, optarg);
 143     return TRUE;
 144 }
 145 
 146 PCMK__OUTPUT_ARGS("node-id", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 147 static int
 148 node_id_default(pcmk__output_t *out, va_list args) {
 149     uint32_t node_id = va_arg(args, uint32_t);
 150 
 151     out->info(out, "%" PRIu32, node_id);
 152     return pcmk_rc_ok;
 153 }
 154 
 155 PCMK__OUTPUT_ARGS("node-id", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 156 static int
 157 node_id_xml(pcmk__output_t *out, va_list args) {
 158     uint32_t node_id = va_arg(args, uint32_t);
 159 
 160     char *id_s = crm_strdup_printf("%" PRIu32, node_id);
 161 
 162     pcmk__output_create_xml_node(out, "node-info",
 163                                  "nodeid", id_s,
 164                                  NULL);
 165 
 166     free(id_s);
 167     return pcmk_rc_ok;
 168 }
 169 
 170 PCMK__OUTPUT_ARGS("node-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 171 static int
 172 node_list_default(pcmk__output_t *out, va_list args)
 173 {
 174     GList *nodes = va_arg(args, GList *);
 175 
 176     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 177         pcmk_controld_api_node_t *node = node_iter->data;
 178         out->info(out, "%" PRIu32 " %s %s", node->id, pcmk__s(node->uname, ""),
 179                   pcmk__s(node->state, ""));
 180     }
 181 
 182     return pcmk_rc_ok;
 183 }
 184 
 185 PCMK__OUTPUT_ARGS("node-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 186 static int
 187 node_list_xml(pcmk__output_t *out, va_list args)
 188 {
 189     GList *nodes = va_arg(args, GList *);
 190 
 191     out->begin_list(out, NULL, NULL, "nodes");
 192 
 193     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 194         pcmk_controld_api_node_t *node = node_iter->data;
 195         char *id_s = crm_strdup_printf("%" PRIu32, node->id);
 196 
 197         pcmk__output_create_xml_node(out, "node",
 198                                      "id", id_s,
 199                                      "name", node->uname,
 200                                      "state", node->state,
 201                                      NULL);
 202 
 203         free(id_s);
 204     }
 205 
 206     out->end_list(out);
 207 
 208     return pcmk_rc_ok;
 209 }
 210 
 211 PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 212 static int
 213 node_name_default(pcmk__output_t *out, va_list args) {
 214     uint32_t node_id G_GNUC_UNUSED = va_arg(args, uint32_t);
 215     const char *node_name = va_arg(args, const char *);
 216 
 217     out->info(out, "%s", node_name);
 218     return pcmk_rc_ok;
 219 }
 220 
 221 PCMK__OUTPUT_ARGS("node-name", "uint32_t", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 222 static int
 223 node_name_xml(pcmk__output_t *out, va_list args) {
 224     uint32_t node_id = va_arg(args, uint32_t);
 225     const char *node_name = va_arg(args, const char *);
 226 
 227     char *id_s = crm_strdup_printf("%" PRIu32, node_id);
 228 
 229     pcmk__output_create_xml_node(out, "node-info",
 230                                  "nodeid", id_s,
 231                                  XML_ATTR_UNAME, node_name,
 232                                  NULL);
 233 
 234     free(id_s);
 235     return pcmk_rc_ok;
 236 }
 237 
 238 PCMK__OUTPUT_ARGS("partition-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 239 static int
 240 partition_list_default(pcmk__output_t *out, va_list args)
 241 {
 242     GList *nodes = va_arg(args, GList *);
 243 
 244     GString *buffer = NULL;
 245 
 246     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 247         pcmk_controld_api_node_t *node = node_iter->data;
 248         if (pcmk__str_eq(node->state, "member", pcmk__str_none)) {
 249             pcmk__add_separated_word(&buffer, 128, pcmk__s(node->uname, ""), " ");
 250         }
 251     }
 252 
 253     if (buffer != NULL) {
 254         out->info(out, "%s", buffer->str);
 255         g_string_free(buffer, TRUE);
 256         return pcmk_rc_ok;
 257     }
 258 
 259     return pcmk_rc_no_output;
 260 }
 261 
 262 PCMK__OUTPUT_ARGS("partition-list", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 263 static int
 264 partition_list_xml(pcmk__output_t *out, va_list args)
 265 {
 266     GList *nodes = va_arg(args, GList *);
 267 
 268     out->begin_list(out, NULL, NULL, "nodes");
 269 
 270     for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
 271         pcmk_controld_api_node_t *node = node_iter->data;
 272 
 273         if (pcmk__str_eq(node->state, "member", pcmk__str_none)) {
 274             char *id_s = crm_strdup_printf("%" PRIu32, node->id);
 275 
 276             pcmk__output_create_xml_node(out, "node",
 277                                          "id", id_s,
 278                                          "name", node->uname,
 279                                          "state", node->state,
 280                                          NULL);
 281             free(id_s);
 282         }
 283     }
 284 
 285     out->end_list(out);
 286     return pcmk_rc_ok;
 287 }
 288 
 289 PCMK__OUTPUT_ARGS("quorum", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 290 static int
 291 quorum_default(pcmk__output_t *out, va_list args) {
 292     bool have_quorum = va_arg(args, int);
 293 
 294     out->info(out, "%d", have_quorum);
 295     return pcmk_rc_ok;
 296 }
 297 
 298 PCMK__OUTPUT_ARGS("quorum", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 299 static int
 300 quorum_xml(pcmk__output_t *out, va_list args) {
 301     bool have_quorum = va_arg(args, int);
 302 
 303     pcmk__output_create_xml_node(out, "cluster-info",
 304                                  "quorum", have_quorum ? "true" : "false",
 305                                  NULL);
 306     return pcmk_rc_ok;
 307 }
 308 
 309 static pcmk__message_entry_t fmt_functions[] = {
 310     { "node-id", "default", node_id_default },
 311     { "node-id", "xml", node_id_xml },
 312     { "node-list", "default", node_list_default },
 313     { "node-list", "xml", node_list_xml },
 314     { "node-name", "default", node_name_default },
 315     { "node-name", "xml", node_name_xml },
 316     { "quorum", "default", quorum_default },
 317     { "quorum", "xml", quorum_xml },
 318     { "partition-list", "default", partition_list_default },
 319     { "partition-list", "xml", partition_list_xml },
 320 
 321     { NULL, NULL, NULL }
 322 };
 323 
 324 static gint
 325 sort_node(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     const pcmk_controld_api_node_t *node_a = a;
 328     const pcmk_controld_api_node_t *node_b = b;
 329 
 330     return pcmk__numeric_strcasecmp((node_a->uname? node_a->uname : ""),
 331                                     (node_b->uname? node_b->uname : ""));
 332 }
 333 
 334 static void
 335 controller_event_cb(pcmk_ipc_api_t *controld_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 336                     enum pcmk_ipc_event event_type, crm_exit_t status,
 337                     void *event_data, void *user_data)
 338 {
 339     pcmk_controld_api_reply_t *reply = event_data;
 340 
 341     switch (event_type) {
 342         case pcmk_ipc_event_disconnect:
 343             if (exit_code == CRM_EX_DISCONNECT) { // Unexpected
 344                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 345                             "Lost connection to controller");
 346             }
 347             goto done;
 348             break;
 349 
 350         case pcmk_ipc_event_reply:
 351             break;
 352 
 353         default:
 354             return;
 355     }
 356 
 357     if (status != CRM_EX_OK) {
 358         exit_code = status;
 359         g_set_error(&error, PCMK__EXITC_ERROR, status,
 360                     "Bad reply from controller: %s",
 361                     crm_exit_str(status));
 362         goto done;
 363     }
 364 
 365     if (reply->reply_type != pcmk_controld_reply_nodes) {
 366         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_INDETERMINATE,
 367                     "Unknown reply type %d from controller",
 368                     reply->reply_type);
 369         goto done;
 370     }
 371 
 372     reply->data.nodes = g_list_sort(reply->data.nodes, sort_node);
 373 
 374     if (options.command == 'p') {
 375         out->message(out, "partition-list", reply->data.nodes);
 376     } else if (options.command == 'l') {
 377         out->message(out, "node-list", reply->data.nodes);
 378     }
 379 
 380     // Success
 381     exit_code = CRM_EX_OK;
 382 done:
 383     pcmk_disconnect_ipc(controld_api);
 384     pcmk_quit_main_loop(mainloop, 10);
 385 }
 386 
 387 static void
 388 run_controller_mainloop(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390     pcmk_ipc_api_t *controld_api = NULL;
 391     int rc;
 392 
 393     // Set disconnect exit code to handle unexpected disconnects
 394     exit_code = CRM_EX_DISCONNECT;
 395 
 396     // Create controller IPC object
 397     rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
 398     if (rc != pcmk_rc_ok) {
 399         g_set_error(&error, PCMK__RC_ERROR, rc,
 400                     "Could not connect to controller: %s",
 401                     pcmk_rc_str(rc));
 402         return;
 403     }
 404     pcmk_register_ipc_callback(controld_api, controller_event_cb, NULL);
 405 
 406     // Connect to controller
 407     rc = pcmk__connect_ipc(controld_api, pcmk_ipc_dispatch_main, 5);
 408     if (rc != pcmk_rc_ok) {
 409         exit_code = pcmk_rc2exitc(rc);
 410         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 411                     "Could not connect to %s: %s",
 412                     pcmk_ipc_name(controld_api, true), pcmk_rc_str(rc));
 413         return;
 414     }
 415 
 416     rc = pcmk_controld_api_list_nodes(controld_api);
 417 
 418     if (rc != pcmk_rc_ok) {
 419         pcmk_disconnect_ipc(controld_api);
 420         exit_code = pcmk_rc2exitc(rc);
 421         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 422                     "Could not ping controller: %s", pcmk_rc_str(rc));
 423         return;
 424     }
 425 
 426     // Run main loop to get controller reply via controller_event_cb()
 427     mainloop = g_main_loop_new(NULL, FALSE);
 428     g_main_loop_run(mainloop);
 429     g_main_loop_unref(mainloop);
 430     mainloop = NULL;
 431     pcmk_free_ipc_api(controld_api);
 432 }
 433 
 434 static void
 435 print_node_id(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437     uint32_t nodeid;
 438     int rc = pcmk__query_node_info(out, &nodeid, NULL, NULL, NULL, NULL, NULL,
 439                                    false, 0);
 440 
 441     if (rc != pcmk_rc_ok) {
 442         /* pcmk__query_node_info already sets an error message on the output object,
 443          * so there's no need to call g_set_error here.  That would just create a
 444          * duplicate error message in the output.
 445          */
 446         exit_code = pcmk_rc2exitc(rc);
 447         return;
 448     }
 449 
 450     rc = out->message(out, "node-id", nodeid);
 451 
 452     if (rc != pcmk_rc_ok) {
 453         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node ID: %s",
 454                     pcmk_rc_str(rc));
 455     }
 456 
 457     exit_code = pcmk_rc2exitc(rc);
 458 }
 459 
 460 static void
 461 print_node_name(uint32_t nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 462 {
 463     int rc = pcmk_rc_ok;
 464     char *node_name = NULL;
 465 
 466     if (nodeid == 0) {
 467         // Check environment first (i.e. when called by resource agent)
 468         const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
 469 
 470         if (name != NULL) {
 471             rc = out->message(out, "node-name", 0, name);
 472             goto done;
 473         }
 474     }
 475 
 476     // Otherwise ask the controller
 477 
 478     /* pcmk__query_node_name already sets an error message on the output object,
 479      * so there's no need to call g_set_error here.  That would just create a
 480      * duplicate error message in the output.
 481      */
 482     rc = pcmk__query_node_name(out, nodeid, &node_name, 0);
 483     if (rc != pcmk_rc_ok) {
 484         exit_code = pcmk_rc2exitc(rc);
 485         return;
 486     }
 487 
 488     rc = out->message(out, "node-name", 0, node_name);
 489 
 490 done:
 491     if (node_name != NULL) {
 492         free(node_name);
 493     }
 494 
 495     if (rc != pcmk_rc_ok) {
 496         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print node name: %s",
 497                     pcmk_rc_str(rc));
 498     }
 499 
 500     exit_code = pcmk_rc2exitc(rc);
 501 }
 502 
 503 static void
 504 print_quorum(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 505 {
 506     bool quorum;
 507     int rc = pcmk__query_node_info(out, NULL, NULL, NULL, NULL, &quorum, NULL,
 508                                    false, 0);
 509 
 510     if (rc != pcmk_rc_ok) {
 511         /* pcmk__query_node_info already sets an error message on the output object,
 512          * so there's no need to call g_set_error here.  That would just create a
 513          * duplicate error message in the output.
 514          */
 515         exit_code = pcmk_rc2exitc(rc);
 516         return;
 517     }
 518 
 519     rc = out->message(out, "quorum", quorum);
 520 
 521     if (rc != pcmk_rc_ok) {
 522         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not print quorum status: %s",
 523                     pcmk_rc_str(rc));
 524     }
 525 
 526     exit_code = pcmk_rc2exitc(rc);
 527 }
 528 
 529 /*!
 530  * \internal
 531  * \brief Extend a transaction by removing a node from a CIB section
 532  *
 533  * \param[in,out] cib        Active CIB connection
 534  * \param[in]     element    CIB element containing node name and/or ID
 535  * \param[in]     section    CIB section that \p element is in
 536  * \param[in]     node_name  Name of node to purge (NULL to leave unspecified)
 537  * \param[in]     node_id    Node ID of node to purge (0 to leave unspecified)
 538  *
 539  * \note At least one of node_name and node_id must be specified.
 540  * \return Standard Pacemaker return code
 541  */
 542 static int
 543 remove_from_section(cib_t *cib, const char *element, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
 544                     const char *node_name, long node_id)
 545 {
 546     xmlNode *xml = NULL;
 547     int rc = pcmk_rc_ok;
 548 
 549     xml = create_xml_node(NULL, element);
 550     if (xml == NULL) {
 551         return pcmk_rc_error;
 552     }
 553     crm_xml_add(xml, XML_ATTR_UNAME, node_name);
 554     if (node_id > 0) {
 555         crm_xml_set_id(xml, "%ld", node_id);
 556     }
 557     rc = cib->cmds->remove(cib, section, xml, cib_transaction);
 558     free_xml(xml);
 559     return (rc >= 0)? pcmk_rc_ok : pcmk_legacy2rc(rc);
 560 }
 561 
 562 /*!
 563  * \internal
 564  * \brief Purge a node from CIB
 565  *
 566  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 567  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 568  *
 569  * \note At least one of node_name and node_id must be specified.
 570  * \return Standard Pacemaker return code
 571  */
 572 static int
 573 purge_node_from_cib(const char *node_name, long node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 574 {
 575     int rc = pcmk_rc_ok;
 576     int commit_rc = pcmk_rc_ok;
 577     cib_t *cib = NULL;
 578 
 579     // Connect to CIB and start a transaction
 580     cib = cib_new();
 581     if (cib == NULL) {
 582         return ENOTCONN;
 583     }
 584     rc = cib->cmds->signon(cib, crm_system_name, cib_command);
 585     if (rc == pcmk_ok) {
 586         rc = cib->cmds->init_transaction(cib);
 587     }
 588     if (rc != pcmk_ok) {
 589         rc = pcmk_legacy2rc(rc);
 590         cib__clean_up_connection(&cib);
 591         return rc;
 592     }
 593 
 594     // Remove from configuration and status
 595     rc = remove_from_section(cib, XML_CIB_TAG_NODE, XML_CIB_TAG_NODES,
 596                              node_name, node_id);
 597     if (rc == pcmk_rc_ok) {
 598         rc = remove_from_section(cib, XML_CIB_TAG_STATE, XML_CIB_TAG_STATUS,
 599                                  node_name, node_id);
 600     }
 601 
 602     // Commit the transaction
 603     commit_rc = cib->cmds->end_transaction(cib, (rc == pcmk_rc_ok),
 604                                            cib_sync_call);
 605     cib__clean_up_connection(&cib);
 606 
 607     if ((rc == pcmk_rc_ok) && (commit_rc == pcmk_ok)) {
 608         crm_debug("Purged node %s (%ld) from CIB",
 609                   pcmk__s(node_name, "by ID"), node_id);
 610     }
 611     return rc;
 612 }
 613 
 614 /*!
 615  * \internal
 616  * \brief Purge a node from a single server's peer cache
 617  *
 618  * \param[in] server     IPC server to send request to
 619  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 620  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 621  *
 622  * \note At least one of node_name and node_id must be specified.
 623  * \return Standard Pacemaker return code
 624  */
 625 static int
 626 purge_node_from(enum pcmk_ipc_server server, const char *node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 627                 long node_id)
 628 {
 629     pcmk_ipc_api_t *api = NULL;
 630     int rc;
 631 
 632     rc = pcmk_new_ipc_api(&api, server);
 633     if (rc != pcmk_rc_ok) {
 634         goto done;
 635     }
 636 
 637     rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5);
 638     if (rc != pcmk_rc_ok) {
 639         goto done;
 640     }
 641 
 642     rc = pcmk_ipc_purge_node(api, node_name, node_id);
 643 done:
 644     if (rc != pcmk_rc_ok) { // Debug message already logged on success
 645         g_set_error(&error, PCMK__RC_ERROR, rc,
 646                     "Could not purge node %s from %s: %s",
 647                     pcmk__s(node_name, "by ID"), pcmk_ipc_name(api, true),
 648                     pcmk_rc_str(rc));
 649     }
 650     pcmk_free_ipc_api(api);
 651     return rc;
 652 }
 653 
 654 /*!
 655  * \internal
 656  * \brief Purge a node from the fencer's peer cache
 657  *
 658  * \param[in] node_name  Name of node to purge (or NULL to leave unspecified)
 659  * \param[in] node_id    Node ID of node to purge (or 0 to leave unspecified)
 660  *
 661  * \note At least one of node_name and node_id must be specified.
 662  * \return Standard Pacemaker return code
 663  */
 664 static int
 665 purge_node_from_fencer(const char *node_name, long node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 666 {
 667     int rc = pcmk_rc_ok;
 668     crm_ipc_t *conn = NULL;
 669     xmlNode *cmd = NULL;
 670 
 671     conn = crm_ipc_new("stonith-ng", 0);
 672     if (conn == NULL) {
 673         rc = ENOTCONN;
 674         exit_code = pcmk_rc2exitc(rc);
 675         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 676                     "Could not connect to fencer to purge node %s",
 677                     pcmk__s(node_name, "by ID"));
 678         return rc;
 679     }
 680 
 681     rc = pcmk__connect_generic_ipc(conn);
 682     if (rc != pcmk_rc_ok) {
 683         exit_code = pcmk_rc2exitc(rc);
 684         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 685                     "Could not connect to fencer to purge node %s: %s",
 686                     pcmk__s(node_name, "by ID"), pcmk_rc_str(rc));
 687         crm_ipc_destroy(conn);
 688         return rc;
 689     }
 690 
 691     cmd = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, "stonith-ng",
 692                          crm_system_name, NULL);
 693     if (node_id > 0) {
 694         crm_xml_set_id(cmd, "%ld", node_id);
 695     }
 696     crm_xml_add(cmd, XML_ATTR_UNAME, node_name);
 697 
 698     rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
 699     if (rc >= 0) {
 700         rc = pcmk_rc_ok;
 701         crm_debug("Purged node %s (%ld) from fencer",
 702                   pcmk__s(node_name, "by ID"), node_id);
 703     } else {
 704         rc = pcmk_legacy2rc(rc);
 705         fprintf(stderr, "Could not purge node %s from fencer: %s\n",
 706                 pcmk__s(node_name, "by ID"), pcmk_rc_str(rc));
 707     }
 708     free_xml(cmd);
 709     crm_ipc_close(conn);
 710     crm_ipc_destroy(conn);
 711     return rc;
 712 }
 713 
 714 static void
 715 remove_node(const char *target_uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 716 {
 717     int rc = pcmk_rc_ok;
 718     long nodeid = 0;
 719     const char *node_name = NULL;
 720     char *endptr = NULL;
 721     const enum pcmk_ipc_server servers[] = {
 722         pcmk_ipc_controld,
 723         pcmk_ipc_attrd,
 724     };
 725 
 726     // Check whether node was specified by name or numeric ID
 727     errno = 0;
 728     nodeid = strtol(target_uname, &endptr, 10);
 729     if ((errno != 0) || (endptr == target_uname) || (*endptr != '\0')
 730         || (nodeid <= 0)) {
 731         // It's not a positive integer, so assume it's a node name
 732         nodeid = 0;
 733         node_name = target_uname;
 734     }
 735 
 736     for (int i = 0; i < PCMK__NELEM(servers); ++i) {
 737         rc = purge_node_from(servers[i], node_name, nodeid);
 738         if (rc != pcmk_rc_ok) {
 739             exit_code = pcmk_rc2exitc(rc);
 740             return;
 741         }
 742     }
 743 
 744     // The fencer hasn't been converted to pcmk_ipc_api_t yet
 745     rc = purge_node_from_fencer(node_name, nodeid);
 746     if (rc != pcmk_rc_ok) {
 747         exit_code = pcmk_rc2exitc(rc);
 748         return;
 749     }
 750 
 751     // Lastly, purge the node from the CIB itself
 752     rc = purge_node_from_cib(node_name, nodeid);
 753     exit_code = pcmk_rc2exitc(rc);
 754 }
 755 
 756 static GOptionContext *
 757 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 758     GOptionContext *context = NULL;
 759 
 760     GOptionEntry extra_prog_entries[] = {
 761         { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 762           "Be less descriptive in output.",
 763           NULL },
 764 
 765         { NULL }
 766     };
 767 
 768     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 769 
 770     /* Add the -q option, which cannot be part of the globally supported options
 771      * because some tools use that flag for something else.
 772      */
 773     pcmk__add_main_args(context, extra_prog_entries);
 774 
 775     pcmk__add_arg_group(context, "commands", "Commands:",
 776                         "Show command help", command_entries);
 777     pcmk__add_arg_group(context, "additional", "Additional Options:",
 778                         "Show additional options", addl_entries);
 779     return context;
 780 }
 781 
 782 int
 783 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 784 {
 785     int rc = pcmk_rc_ok;
 786 
 787     GOptionGroup *output_group = NULL;
 788     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 789     gchar **processed_args = pcmk__cmdline_preproc(argv, "NR");
 790     GOptionContext *context = build_arg_context(args, &output_group);
 791 
 792     pcmk__register_formats(output_group, formats);
 793     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 794         exit_code = CRM_EX_USAGE;
 795         goto done;
 796     }
 797 
 798     pcmk__cli_init_logging("crm_node", args->verbosity);
 799 
 800     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 801     if (rc != pcmk_rc_ok) {
 802         exit_code = pcmk_rc2exitc(rc);
 803         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 804                     "Error creating output format %s: %s", args->output_ty,
 805                     pcmk_rc_str(rc));
 806         goto done;
 807     }
 808 
 809     if (!pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname())) {
 810         exit_code = CRM_EX_SOFTWARE;
 811         goto done;
 812     }
 813 
 814     if (args->version) {
 815         out->version(out, false);
 816         goto done;
 817     }
 818 
 819     if (options.command == 0) {
 820         char *help = g_option_context_get_help(context, TRUE, NULL);
 821 
 822         out->err(out, "%s", help);
 823         g_free(help);
 824         exit_code = CRM_EX_USAGE;
 825         goto done;
 826     }
 827 
 828     if (options.dangerous_cmd && options.force_flag == FALSE) {
 829         exit_code = CRM_EX_USAGE;
 830         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 831                     "The supplied command is considered dangerous."
 832                     "  To prevent accidental destruction of the cluster,"
 833                     " the --force flag is required in order to proceed.");
 834         goto done;
 835     }
 836 
 837     pcmk__register_lib_messages(out);
 838     pcmk__register_messages(out, fmt_functions);
 839 
 840     switch (options.command) {
 841         case 'i':
 842             print_node_id();
 843             break;
 844 
 845         case 'n':
 846             print_node_name(0);
 847             break;
 848 
 849         case 'q':
 850             print_quorum();
 851             break;
 852 
 853         case 'N':
 854             print_node_name(options.nodeid);
 855             break;
 856 
 857         case 'R':
 858             remove_node(options.target_uname);
 859             break;
 860 
 861         case 'l':
 862         case 'p':
 863             run_controller_mainloop();
 864             break;
 865 
 866         default:
 867             break;
 868     }
 869 
 870 done:
 871     g_strfreev(processed_args);
 872     pcmk__free_arg_context(context);
 873 
 874     pcmk__output_and_clear_error(&error, out);
 875 
 876     if (out != NULL) {
 877         out->finish(out, exit_code, true, NULL);
 878         pcmk__output_free(out);
 879     }
 880     pcmk__unregister_formats();
 881     return crm_exit(exit_code);
 882 }

/* [previous][next][first][last][top][bottom][index][help] */