root/tools/crm_ticket.c

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

DEFINITIONS

This source file includes following definitions.
  1. attr_value_cb
  2. command_cb
  3. delete_attr_cb
  4. get_attr_cb
  5. grant_standby_cb
  6. set_attr_cb
  7. ticket_grant_warning
  8. ticket_revoke_warning
  9. build_arg_context
  10. main

   1 /*
   2  * Copyright 2012-2024 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 <sys/param.h>
  13 
  14 #include <crm/crm.h>
  15 
  16 #include <stdio.h>
  17 #include <sys/types.h>
  18 #include <unistd.h>
  19 
  20 #include <stdlib.h>
  21 #include <errno.h>
  22 #include <fcntl.h>
  23 #include <libgen.h>
  24 
  25 #include <crm/common/xml.h>
  26 #include <crm/common/ipc.h>
  27 #include <crm/common/cmdline_internal.h>
  28 
  29 #include <crm/cib.h>
  30 #include <crm/cib/internal.h>
  31 #include <crm/pengine/status.h>
  32 #include <crm/pengine/internal.h>
  33 
  34 #include <pacemaker-internal.h>
  35 
  36 GError *error = NULL;
  37 
  38 #define SUMMARY "Perform tasks related to cluster tickets\n\n" \
  39                 "Allows ticket attributes to be queried, modified and deleted."
  40 
  41 struct {
  42     gchar *attr_default;
  43     gchar *attr_id;
  44     char *attr_name;
  45     char *attr_value;
  46     gboolean force;
  47     char *get_attr_name;
  48     gboolean quiet;
  49     gchar *set_name;
  50     char ticket_cmd;
  51     gchar *ticket_id;
  52     gchar *xml_file;
  53 } options = {
  54     .ticket_cmd = 'S'
  55 };
  56 
  57 GList *attr_delete;
  58 GHashTable *attr_set;
  59 bool modified = false;
  60 int cib_options = cib_sync_call;
  61 static pcmk__output_t *out = NULL;
  62 
  63 #define INDENT "                               "
  64 
  65 static pcmk__supported_format_t formats[] = {
  66     PCMK__SUPPORTED_FORMAT_NONE,
  67     PCMK__SUPPORTED_FORMAT_TEXT,
  68     PCMK__SUPPORTED_FORMAT_XML,
  69     { NULL, NULL, NULL }
  70 };
  71 
  72 static gboolean
  73 attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  74     pcmk__str_update(&options.attr_value, optarg);
  75 
  76     if (!options.attr_name || !options.attr_value) {
  77         return TRUE;
  78     }
  79 
  80     pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
  81     pcmk__str_update(&options.attr_name, NULL);
  82     pcmk__str_update(&options.attr_value, NULL);
  83 
  84     modified = true;
  85 
  86     return TRUE;
  87 }
  88 
  89 static gboolean
  90 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  91     if (pcmk__str_any_of(option_name, "--info", "-l", NULL)) {
  92         options.ticket_cmd = 'l';
  93     } else if (pcmk__str_any_of(option_name, "--details", "-L", NULL)) {
  94         options.ticket_cmd = 'L';
  95     } else if (pcmk__str_any_of(option_name, "--raw", "-w", NULL)) {
  96         options.ticket_cmd = 'w';
  97     } else if (pcmk__str_any_of(option_name, "--query-xml", "-q", NULL)) {
  98         options.ticket_cmd = 'q';
  99     } else if (pcmk__str_any_of(option_name, "--constraints", "-c", NULL)) {
 100         options.ticket_cmd = 'c';
 101     } else if (pcmk__str_any_of(option_name, "--cleanup", "-C", NULL)) {
 102         options.ticket_cmd = 'C';
 103     }
 104 
 105     return TRUE;
 106 }
 107 
 108 static gboolean
 109 delete_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 110     attr_delete = g_list_append(attr_delete, strdup(optarg));
 111     modified = true;
 112     return TRUE;
 113 }
 114 
 115 static gboolean
 116 get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 117     pcmk__str_update(&options.get_attr_name, optarg);
 118     options.ticket_cmd = 'G';
 119     return TRUE;
 120 }
 121 
 122 static gboolean
 123 grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 124     if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) {
 125         pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_TRUE);
 126         modified = true;
 127     } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) {
 128         pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_FALSE);
 129         modified = true;
 130     } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) {
 131         pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_TRUE);
 132         modified = true;
 133     } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) {
 134         pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_FALSE);
 135         modified = true;
 136     }
 137 
 138     return TRUE;
 139 }
 140 
 141 static gboolean
 142 set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 143     pcmk__str_update(&options.attr_name, optarg);
 144 
 145     if (!options.attr_name || !options.attr_value) {
 146         return TRUE;
 147     }
 148 
 149     pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
 150     pcmk__str_update(&options.attr_name, NULL);
 151     pcmk__str_update(&options.attr_value, NULL);
 152 
 153     modified = true;
 154 
 155     return TRUE;
 156 }
 157 
 158 static GOptionEntry query_entries[] = {
 159     { "info", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 160       "Display the information of ticket(s)",
 161       NULL },
 162 
 163     { "details", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 164       "Display the details of ticket(s)",
 165       NULL },
 166 
 167     { "raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 168       "Display the IDs of ticket(s)",
 169       NULL },
 170 
 171     { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 172       "Query the XML of ticket(s)",
 173       NULL },
 174 
 175     { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 176       "Display the " PCMK_XE_RSC_TICKET " constraints that apply to ticket(s)",
 177       NULL },
 178 
 179     { NULL }
 180 };
 181 
 182 static GOptionEntry command_entries[] = {
 183     { "grant", 'g', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
 184       "Grant a ticket to this cluster site",
 185       NULL },
 186 
 187     { "revoke", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
 188       "Revoke a ticket from this cluster site",
 189       NULL },
 190 
 191     { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
 192       "Tell this cluster site this ticket is standby",
 193       NULL },
 194 
 195     { "activate", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
 196       "Tell this cluster site this ticket is active",
 197       NULL },
 198 
 199     { NULL }
 200 };
 201 
 202 static GOptionEntry advanced_entries[] = {
 203     { "get-attr", 'G', 0, G_OPTION_ARG_CALLBACK, get_attr_cb,
 204       "Display the named attribute for a ticket",
 205       "ATTRIBUTE" },
 206 
 207     { "set-attr", 'S', 0, G_OPTION_ARG_CALLBACK, set_attr_cb,
 208       "Set the named attribute for a ticket",
 209       "ATTRIBUTE" },
 210 
 211     { "delete-attr", 'D', 0, G_OPTION_ARG_CALLBACK, delete_attr_cb,
 212       "Delete the named attribute for a ticket",
 213       "ATTRIBUTE" },
 214 
 215     { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 216       "Delete all state of a ticket at this cluster site",
 217       NULL },
 218 
 219     { NULL}
 220 };
 221 
 222 static GOptionEntry addl_entries[] = {
 223     { "attr-value", 'v', 0, G_OPTION_ARG_CALLBACK, attr_value_cb,
 224       "Attribute value to use with -S",
 225       "VALUE" },
 226 
 227     { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
 228       "(Advanced) Default attribute value to display if none is found\n"
 229       INDENT "(for use with -G)",
 230       "VALUE" },
 231 
 232     { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force,
 233       "(Advanced) Force the action to be performed",
 234       NULL },
 235 
 236     { "ticket", 't', 0, G_OPTION_ARG_STRING, &options.ticket_id,
 237       "Ticket ID",
 238       "ID" },
 239 
 240     { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.xml_file,
 241       NULL,
 242       NULL },
 243 
 244     { NULL }
 245 };
 246 
 247 static GOptionEntry deprecated_entries[] = {
 248     { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name,
 249       "(Advanced) ID of the " PCMK_XE_INSTANCE_ATTRIBUTES " object to change",
 250       "ID" },
 251 
 252     { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
 253       "(Advanced) ID of the nvpair object to change/delete",
 254       "ID" },
 255 
 256     { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &options.quiet,
 257       "Print only the value on stdout",
 258       NULL },
 259 
 260     { NULL }
 261 };
 262 
 263 static void
 264 ticket_grant_warning(gchar *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     out->err(out, "This command cannot help you verify whether '%s' has "
 267                   "been already granted elsewhere.\n"
 268                   "If you really want to grant '%s' to this site now, and "
 269                   "you know what you are doing,\n"
 270                   "please specify --force.",
 271                   ticket_id, ticket_id);
 272 }
 273 
 274 static void
 275 ticket_revoke_warning(gchar *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277     out->err(out, "Revoking '%s' can trigger the specified '" PCMK_XA_LOSS_POLICY
 278               "'(s) relating to '%s'.\n\n"
 279               "You can check that with:\n"
 280               "crm_ticket --ticket %s --constraints\n\n"
 281               "Otherwise before revoking '%s', you may want to make '%s'"
 282               "standby with:\n"
 283               "crm_ticket --ticket %s --standby\n\n"
 284               "If you really want to revoke '%s' from this site now, and "
 285               "you know what you are doing,\n"
 286               "please specify --force.",
 287               ticket_id, ticket_id, ticket_id, ticket_id, ticket_id,
 288               ticket_id, ticket_id);
 289 }
 290 
 291 static GOptionContext *
 292 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294     GOptionContext *context = NULL;
 295 
 296     const char *description = "Examples:\n\n"
 297                               "Display the info of tickets:\n\n"
 298                               "\tcrm_ticket --info\n\n"
 299                               "Display the detailed info of tickets:\n\n"
 300                               "\tcrm_ticket --details\n\n"
 301                               "Display the XML of 'ticketA':\n\n"
 302                               "\tcrm_ticket --ticket ticketA --query-xml\n\n"
 303                               "Display the " PCMK_XE_RSC_TICKET " constraints that apply to 'ticketA':\n\n"
 304                               "\tcrm_ticket --ticket ticketA --constraints\n\n"
 305                               "Grant 'ticketA' to this cluster site:\n\n"
 306                               "\tcrm_ticket --ticket ticketA --grant\n\n"
 307                               "Revoke 'ticketA' from this cluster site:\n\n"
 308                               "\tcrm_ticket --ticket ticketA --revoke\n\n"
 309                               "Make 'ticketA' standby (the cluster site will treat a granted\n"
 310                               "'ticketA' as 'standby', and the dependent resources will be\n"
 311                               "stopped or demoted gracefully without triggering loss-policies):\n\n"
 312                               "\tcrm_ticket --ticket ticketA --standby\n\n"
 313                               "Activate 'ticketA' from being standby:\n\n"
 314                               "\tcrm_ticket --ticket ticketA --activate\n\n"
 315                               "Get the value of the 'granted' attribute for 'ticketA':\n\n"
 316                               "\tcrm_ticket --ticket ticketA --get-attr granted\n\n"
 317                               "Set the value of the 'standby' attribute for 'ticketA':\n\n"
 318                               "\tcrm_ticket --ticket ticketA --set-attr standby --attr-value true\n\n"
 319                               "Delete the 'granted' attribute for 'ticketA':\n\n"
 320                               "\tcrm_ticket --ticket ticketA --delete-attr granted\n\n"
 321                               "Erase the operation history of 'ticketA' at this cluster site,\n"
 322                               "causing the cluster site to 'forget' the existing ticket state:\n\n"
 323                               "\tcrm_ticket --ticket ticketA --cleanup\n\n";
 324 
 325     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 326     g_option_context_set_description(context, description);
 327 
 328     pcmk__add_arg_group(context, "queries", "Queries:",
 329                         "Show queries", query_entries);
 330     pcmk__add_arg_group(context, "commands", "Commands:",
 331                         "Show command options", command_entries);
 332     pcmk__add_arg_group(context, "advanced", "Advanced Options:",
 333                         "Show advanced options", advanced_entries);
 334     pcmk__add_arg_group(context, "additional", "Additional Options:",
 335                         "Show additional options", addl_entries);
 336     pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
 337                         "Show deprecated options", deprecated_entries);
 338 
 339     return context;
 340 }
 341 
 342 int
 343 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345     pcmk_scheduler_t *scheduler = NULL;
 346     xmlNode *cib_xml_copy = NULL;
 347 
 348     cib_t *cib_conn = NULL;
 349     crm_exit_t exit_code = CRM_EX_OK;
 350     int rc = pcmk_rc_ok;
 351 
 352     GOptionGroup *output_group = NULL;
 353     pcmk__common_args_t *args = NULL;
 354     GOptionContext *context = NULL;
 355     gchar **processed_args = NULL;
 356 
 357     attr_set = pcmk__strkey_table(free, free);
 358     attr_delete = NULL;
 359 
 360     args = pcmk__new_common_args(SUMMARY);
 361     context = build_arg_context(args, &output_group);
 362     processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS");
 363 
 364     pcmk__register_formats(output_group, formats);
 365     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 366         exit_code = CRM_EX_USAGE;
 367         goto done;
 368     }
 369 
 370     pcmk__cli_init_logging("crm_ticket", args->verbosity);
 371 
 372     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 373     if (rc != pcmk_rc_ok) {
 374         exit_code = pcmk_rc2exitc(rc);
 375         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 376                     "Error creating output format %s: %s", args->output_ty,
 377                     pcmk_rc_str(rc));
 378         goto done;
 379     }
 380 
 381     pe__register_messages(out);
 382     pcmk__register_lib_messages(out);
 383 
 384     if (args->version) {
 385         out->version(out, false);
 386         goto done;
 387     }
 388 
 389     scheduler = pcmk_new_scheduler();
 390     if (scheduler == NULL) {
 391         rc = errno;
 392         exit_code = pcmk_rc2exitc(rc);
 393         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 394                     "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
 395         goto done;
 396     }
 397     pcmk__set_scheduler_flags(scheduler, pcmk__sched_no_counts);
 398 
 399     cib_conn = cib_new();
 400     if (cib_conn == NULL) {
 401         exit_code = CRM_EX_DISCONNECT;
 402         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB manager");
 403         goto done;
 404     }
 405 
 406     rc = cib__signon_attempts(cib_conn, cib_command, 5);
 407     rc = pcmk_legacy2rc(rc);
 408 
 409     if (rc != pcmk_rc_ok) {
 410         exit_code = pcmk_rc2exitc(rc);
 411         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB: %s",
 412                     pcmk_rc_str(rc));
 413         goto done;
 414     }
 415 
 416     if (options.xml_file != NULL) {
 417         cib_xml_copy = pcmk__xml_read(options.xml_file);
 418 
 419     } else {
 420         rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy,
 421                                    cib_sync_call);
 422         rc = pcmk_legacy2rc(rc);
 423 
 424         if (rc != pcmk_rc_ok) {
 425             exit_code = pcmk_rc2exitc(rc);
 426             g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not get local CIB: %s",
 427                         pcmk_rc_str(rc));
 428             goto done;
 429         }
 430     }
 431 
 432     rc = pcmk__update_configured_schema(&cib_xml_copy, false);
 433     if (rc != pcmk_rc_ok) {
 434         exit_code = pcmk_rc2exitc(rc);
 435         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 436                     "Could not update local CIB to latest schema version");
 437         goto done;
 438     }
 439 
 440     scheduler->input = cib_xml_copy;
 441     scheduler->priv->now = crm_time_new(NULL);
 442 
 443     cluster_status(scheduler);
 444 
 445     /* For recording the tickets that are referenced in PCMK_XE_RSC_TICKET
 446      * constraints but have never been granted yet.
 447      */
 448     pcmk__unpack_constraints(scheduler);
 449 
 450     if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') {
 451         bool raw = false;
 452         bool details = false;
 453 
 454         if (options.ticket_cmd == 'L') {
 455             details = true;
 456         } else if (options.ticket_cmd == 'w') {
 457             raw = true;
 458         }
 459 
 460         rc = pcmk__ticket_info(out, scheduler, options.ticket_id, details, raw);
 461         exit_code = pcmk_rc2exitc(rc);
 462 
 463         if (rc == ENXIO) {
 464             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 465                         "No such ticket '%s'", options.ticket_id);
 466         } else if (rc != pcmk_rc_ok) {
 467             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 468                         "Could not get ticket info: %s", pcmk_rc_str(rc));
 469         }
 470 
 471     } else if (options.ticket_cmd == 'q') {
 472         rc = pcmk__ticket_state(out, cib_conn, options.ticket_id);
 473 
 474         if (rc != pcmk_rc_ok && rc != pcmk_rc_duplicate_id) {
 475             exit_code = pcmk_rc2exitc(rc);
 476             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 477                         "Could not query ticket XML: %s", pcmk_rc_str(rc));
 478         } else {
 479             exit_code = CRM_EX_OK;
 480         }
 481 
 482     } else if (options.ticket_cmd == 'c') {
 483         rc = pcmk__ticket_constraints(out, cib_conn, options.ticket_id);
 484         exit_code = pcmk_rc2exitc(rc);
 485 
 486         if (rc != pcmk_rc_ok) {
 487             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 488                         "Could not show ticket constraints: %s", pcmk_rc_str(rc));
 489         }
 490 
 491     } else if (options.ticket_cmd == 'G') {
 492         if (options.ticket_id == NULL) {
 493             exit_code = CRM_EX_NOSUCH;
 494             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 495                         "Must supply ticket ID with -t");
 496             goto done;
 497         }
 498 
 499         rc = pcmk__ticket_get_attr(out, scheduler, options.ticket_id,
 500                                    options.get_attr_name, options.attr_default);
 501         exit_code = pcmk_rc2exitc(rc);
 502 
 503     } else if (options.ticket_cmd == 'C') {
 504         if (options.ticket_id == NULL) {
 505             exit_code = CRM_EX_USAGE;
 506             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 507                         "Must supply ticket ID with -t");
 508             goto done;
 509         }
 510 
 511         rc = pcmk__ticket_delete(out, cib_conn, scheduler, options.ticket_id,
 512                                  options.force);
 513         exit_code = pcmk_rc2exitc(rc);
 514 
 515         switch (rc) {
 516             case ENXIO:
 517                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 518                             "No such ticket '%s'", options.ticket_id);
 519                 break;
 520 
 521             case EACCES:
 522                 ticket_revoke_warning(options.ticket_id);
 523                 break;
 524 
 525             case pcmk_rc_ok:
 526             case pcmk_rc_duplicate_id:
 527                 break;
 528 
 529             default:
 530                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 531                             "Could not clean up ticket: %s", pcmk_rc_str(rc));
 532                 break;
 533         }
 534 
 535     } else if (modified) {
 536         if (options.ticket_id == NULL) {
 537             exit_code = CRM_EX_USAGE;
 538             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 539                         "Must supply ticket ID with -t");
 540             goto done;
 541         }
 542 
 543         if (options.attr_value
 544             && (pcmk__str_empty(options.attr_name))) {
 545             exit_code = CRM_EX_USAGE;
 546             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 547                         "Must supply attribute name with -S for -v %s", options.attr_value);
 548             goto done;
 549         }
 550 
 551         if (options.attr_name
 552             && (pcmk__str_empty(options.attr_value))) {
 553             exit_code = CRM_EX_USAGE;
 554             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 555                         "Must supply attribute value with -v for -S %s", options.attr_value);
 556             goto done;
 557         }
 558 
 559         if (attr_delete != NULL) {
 560             rc = pcmk__ticket_remove_attr(out, cib_conn, scheduler, options.ticket_id,
 561                                           attr_delete, options.force);
 562 
 563             if (rc == EACCES) {
 564                 ticket_revoke_warning(options.ticket_id);
 565                 exit_code = CRM_EX_UNSAFE;
 566                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 567                             "Ticket modification not allowed without --force");
 568                 goto done;
 569             }
 570         } else {
 571             rc = pcmk__ticket_set_attr(out, cib_conn, scheduler, options.ticket_id,
 572                                        attr_set, options.force);
 573 
 574             if (rc == EACCES) {
 575                 const char *value = NULL;
 576 
 577                 value = g_hash_table_lookup(attr_set, PCMK__XA_GRANTED);
 578                 if (crm_is_true(value)) {
 579                     ticket_grant_warning(options.ticket_id);
 580                 } else {
 581                     ticket_revoke_warning(options.ticket_id);
 582                 }
 583 
 584                 exit_code = CRM_EX_UNSAFE;
 585                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 586                             "Ticket modification not allowed without --force");
 587                 goto done;
 588             }
 589         }
 590 
 591         exit_code = pcmk_rc2exitc(rc);
 592 
 593         if (rc != pcmk_rc_ok && error == NULL) {
 594             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 595                         "Could not modify ticket: %s", pcmk_rc_str(rc));
 596         }
 597 
 598     } else if (options.ticket_cmd == 'S') {
 599         /* Correct usage was handled in the "if (modified)" block above, so
 600          * this is just for reporting usage errors
 601          */
 602 
 603         if (pcmk__str_empty(options.attr_name)) {
 604             // We only get here if ticket_cmd was left as default
 605             exit_code = CRM_EX_USAGE;
 606             g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Must supply a command");
 607             goto done;
 608         }
 609 
 610         if (options.ticket_id == NULL) {
 611             exit_code = CRM_EX_USAGE;
 612             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 613                         "Must supply ticket ID with -t");
 614             goto done;
 615         }
 616 
 617         if (pcmk__str_empty(options.attr_value)) {
 618             exit_code = CRM_EX_USAGE;
 619             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 620                         "Must supply value with -v for -S %s", options.attr_name);
 621             goto done;
 622         }
 623 
 624     } else {
 625         exit_code = CRM_EX_USAGE;
 626         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 627                     "Unknown command: %c", options.ticket_cmd);
 628     }
 629 
 630  done:
 631     if (attr_set) {
 632         g_hash_table_destroy(attr_set);
 633     }
 634     attr_set = NULL;
 635 
 636     if (attr_delete) {
 637         g_list_free_full(attr_delete, free);
 638     }
 639     attr_delete = NULL;
 640 
 641     pcmk_free_scheduler(scheduler);
 642     scheduler = NULL;
 643 
 644     cib__clean_up_connection(&cib_conn);
 645 
 646     g_strfreev(processed_args);
 647     pcmk__free_arg_context(context);
 648     g_free(options.attr_default);
 649     g_free(options.attr_id);
 650     free(options.attr_name);
 651     free(options.attr_value);
 652     free(options.get_attr_name);
 653     g_free(options.set_name);
 654     g_free(options.ticket_id);
 655     g_free(options.xml_file);
 656 
 657     pcmk__output_and_clear_error(&error, out);
 658 
 659     if (out != NULL) {
 660         out->finish(out, exit_code, true, NULL);
 661         pcmk__output_free(out);
 662     }
 663 
 664     pcmk__unregister_formats();
 665     crm_exit(exit_code);
 666 }

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