LCOV - code coverage report
Current view: top level - pacemaker - pcmk_ticket.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 275 283 97.2 %
Date: 2024-05-07 11:09:47 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 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 <crm/cib/internal.h>
      13             : #include <crm/pengine/internal.h>
      14             : 
      15             : #include <pacemaker.h>
      16             : #include <pacemaker-internal.h>
      17             : 
      18             : #include "libpacemaker_private.h"
      19             : 
      20             : static int
      21          10 : build_ticket_modify_xml(cib_t *cib, const char *ticket_id, xmlNode **ticket_state_xml,
      22             :                         xmlNode **xml_top)
      23             : {
      24          10 :     int rc = pcmk__get_ticket_state(cib, ticket_id, ticket_state_xml);
      25             : 
      26          10 :     if (rc == pcmk_rc_ok || rc == pcmk_rc_duplicate_id) {
      27             :         /* Ticket(s) found - return their state */
      28           9 :         *xml_top = *ticket_state_xml;
      29             : 
      30           1 :     } else if (rc == ENXIO) {
      31             :         /* No ticket found - build the XML needed to create it */
      32           1 :         xmlNode *xml_obj = NULL;
      33             : 
      34           1 :         *xml_top = pcmk__xe_create(NULL, PCMK_XE_STATUS);
      35           1 :         xml_obj = pcmk__xe_create(*xml_top, PCMK_XE_TICKETS);
      36           1 :         *ticket_state_xml = pcmk__xe_create(xml_obj, PCMK__XE_TICKET_STATE);
      37           1 :         crm_xml_add(*ticket_state_xml, PCMK_XA_ID, ticket_id);
      38             : 
      39           1 :         rc = pcmk_rc_ok;
      40             : 
      41             :     } else {
      42             :         /* Some other error occurred - clean up and return */
      43           0 :         free_xml(*ticket_state_xml);
      44             :     }
      45             : 
      46          10 :     return rc;
      47             : }
      48             : 
      49             : static void
      50           4 : add_attribute_xml(pcmk_scheduler_t *scheduler, const char *ticket_id,
      51             :                   GHashTable *attr_set, xmlNode **ticket_state_xml)
      52             : {
      53             :     GHashTableIter hash_iter;
      54           4 :     char *key = NULL;
      55           4 :     char *value = NULL;
      56             : 
      57           4 :     pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
      58             : 
      59           4 :     g_hash_table_iter_init(&hash_iter, attr_set);
      60           9 :     while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
      61           5 :         crm_xml_add(*ticket_state_xml, key, value);
      62             : 
      63           5 :         if (pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)
      64           2 :             && (ticket == NULL || ticket->granted == FALSE)
      65           1 :             && crm_is_true(value)) {
      66             : 
      67           1 :             char *now = pcmk__ttoa(time(NULL));
      68             : 
      69           1 :             crm_xml_add(*ticket_state_xml, PCMK_XA_LAST_GRANTED, now);
      70           1 :             free(now);
      71             :         }
      72             :     }
      73           4 : }
      74             : 
      75             : int
      76          27 : pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
      77             : {
      78          27 :     int rc = pcmk_rc_ok;
      79          27 :     xmlNode *xml_search = NULL;
      80          27 :     char *xpath = NULL;
      81             : 
      82          27 :     CRM_ASSERT(cib!= NULL && state != NULL);
      83          25 :     *state = NULL;
      84             : 
      85          25 :     if (ticket_id != NULL) {
      86          23 :         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS
      87             :                                   "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
      88             :                                   ticket_id);
      89             :     } else {
      90           2 :         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS);
      91             :     }
      92             : 
      93          25 :     rc = cib->cmds->query(cib, xpath, &xml_search,
      94             :                           cib_sync_call | cib_scope_local | cib_xpath);
      95          25 :     rc = pcmk_legacy2rc(rc);
      96             : 
      97          25 :     if (rc == pcmk_rc_ok) {
      98          18 :         crm_log_xml_debug(xml_search, "Match");
      99             : 
     100          18 :         if (xml_search->children != NULL && ticket_id != NULL) {
     101           3 :             rc = pcmk_rc_duplicate_id;
     102             :         }
     103             :     }
     104             : 
     105          25 :     free(xpath);
     106             : 
     107          25 :     *state = xml_search;
     108          25 :     return rc;
     109             : }
     110             : 
     111             : int
     112           3 : pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     113             : {
     114           3 :     int rc = pcmk_rc_ok;
     115           3 :     xmlNode *result = NULL;
     116           3 :     const char *xpath_base = NULL;
     117           3 :     char *xpath = NULL;
     118             : 
     119           3 :     CRM_ASSERT(out != NULL && cib != NULL);
     120             : 
     121           3 :     xpath_base = pcmk_cib_xpath_for(PCMK_XE_CONSTRAINTS);
     122           3 :     CRM_ASSERT(xpath_base != NULL);
     123             : 
     124           3 :     if (ticket_id != NULL) {
     125           2 :         xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET "[@" PCMK_XA_TICKET "=\"%s\"]",
     126             :                                   xpath_base, ticket_id);
     127             :     } else {
     128           1 :         xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET, xpath_base);
     129             :     }
     130             : 
     131           3 :     rc = cib->cmds->query(cib, (const char *) xpath, &result,
     132             :                           cib_sync_call | cib_scope_local | cib_xpath);
     133           3 :     rc = pcmk_legacy2rc(rc);
     134             : 
     135           3 :     if (result != NULL) {
     136           2 :         out->message(out, "ticket-constraints", result);
     137           2 :         free_xml(result);
     138             :     }
     139             : 
     140           3 :     free(xpath);
     141           3 :     return rc;
     142             : }
     143             : 
     144             : int
     145           5 : pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
     146             : {
     147           5 :     pcmk__output_t *out = NULL;
     148           5 :     int rc = pcmk_rc_ok;
     149           5 :     cib_t *cib = NULL;
     150             : 
     151           5 :     rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
     152           5 :     if (rc != pcmk_rc_ok) {
     153           2 :         goto done;
     154             :     }
     155             : 
     156           3 :     rc = pcmk__ticket_constraints(out, cib, ticket_id);
     157             : 
     158           5 : done:
     159           5 :     if (cib != NULL) {
     160           3 :         cib__clean_up_connection(&cib);
     161             :     }
     162             : 
     163           5 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     164           5 :     return rc;
     165             : }
     166             : 
     167             : static int
     168           4 : delete_single_ticket(xmlNode *child, void *userdata)
     169             : {
     170           4 :     int rc = pcmk_rc_ok;
     171           4 :     cib_t *cib = (cib_t *) userdata;
     172             : 
     173           4 :     rc = cib->cmds->remove(cib, PCMK_XE_STATUS, child, cib_sync_call);
     174           4 :     rc = pcmk_legacy2rc(rc);
     175             : 
     176           4 :     return rc;
     177             : }
     178             : 
     179             : int
     180           7 : pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     181             :                     const char *ticket_id, bool force)
     182             : {
     183           7 :     int rc = pcmk_rc_ok;
     184           7 :     xmlNode *state = NULL;
     185             : 
     186           7 :     CRM_ASSERT(cib != NULL && scheduler != NULL);
     187             : 
     188           7 :     if (ticket_id == NULL) {
     189           1 :         return EINVAL;
     190             :     }
     191             : 
     192           6 :     if (!force) {
     193           3 :         pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
     194             : 
     195           3 :         if (ticket == NULL) {
     196           1 :             return ENXIO;
     197             :         }
     198             : 
     199           2 :         if (ticket->granted) {
     200           1 :             return EACCES;
     201             :         }
     202             :     }
     203             : 
     204           4 :     rc = pcmk__get_ticket_state(cib, ticket_id, &state);
     205             : 
     206           4 :     if (rc == pcmk_rc_duplicate_id) {
     207           1 :         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
     208             :                   ticket_id);
     209             : 
     210           3 :     } else if (rc == ENXIO) {
     211           1 :         return pcmk_rc_ok;
     212             : 
     213           2 :     } else if (rc != pcmk_rc_ok) {
     214           0 :         return rc;
     215             :     }
     216             : 
     217           3 :     crm_log_xml_debug(state, "Delete");
     218             : 
     219           3 :     if (rc == pcmk_rc_duplicate_id) {
     220           1 :         rc = pcmk__xe_foreach_child(state, NULL, delete_single_ticket, cib);
     221             :     } else {
     222           2 :         rc = delete_single_ticket(state, cib);
     223             :     }
     224             : 
     225           3 :     if (rc == pcmk_rc_ok) {
     226           3 :         out->info(out, "Cleaned up %s", ticket_id);
     227             :     }
     228             : 
     229           3 :     free_xml(state);
     230           3 :     return rc;
     231             : }
     232             : 
     233             : int
     234           9 : pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
     235             : {
     236           9 :     pcmk_scheduler_t *scheduler = NULL;
     237           9 :     pcmk__output_t *out = NULL;
     238           9 :     cib_t *cib = NULL;
     239           9 :     int rc = pcmk_rc_ok;
     240             : 
     241           9 :     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
     242           9 :     if (rc != pcmk_rc_ok) {
     243           2 :         goto done;
     244             :     }
     245             : 
     246           7 :     rc = pcmk__ticket_delete(out, cib, scheduler, ticket_id, force);
     247             : 
     248           9 : done:
     249           9 :     if (cib != NULL) {
     250           7 :         cib__clean_up_connection(&cib);
     251             :     }
     252             : 
     253           9 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     254           9 :     pe_free_working_set(scheduler);
     255           9 :     return rc;
     256             : }
     257             : 
     258             : int
     259           7 : pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     260             :                       const char *ticket_id, const char *attr_name,
     261             :                       const char *attr_default)
     262             : {
     263           7 :     int rc = pcmk_rc_ok;
     264           7 :     const char *attr_value = NULL;
     265           7 :     pcmk_ticket_t *ticket = NULL;
     266             : 
     267           7 :     CRM_ASSERT(out != NULL && scheduler != NULL);
     268             : 
     269           7 :     if (ticket_id == NULL || attr_name == NULL) {
     270           2 :         return EINVAL;
     271             :     }
     272             : 
     273           5 :     ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
     274             : 
     275           5 :     if (ticket != NULL) {
     276           3 :         attr_value = g_hash_table_lookup(ticket->state, attr_name);
     277             :     }
     278             : 
     279           5 :     if (attr_value != NULL) {
     280           1 :         out->message(out, "ticket-attribute", ticket_id, attr_name, attr_value);
     281           4 :     } else if (attr_default != NULL) {
     282           2 :         out->message(out, "ticket-attribute", ticket_id, attr_name, attr_default);
     283             :     } else {
     284           2 :         rc = ENXIO;
     285             :     }
     286             : 
     287           5 :     return rc;
     288             : }
     289             : 
     290             : int
     291           8 : pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
     292             :                      const char *attr_name, const char *attr_default)
     293             : {
     294           8 :     pcmk_scheduler_t *scheduler = NULL;
     295           8 :     pcmk__output_t *out = NULL;
     296           8 :     int rc = pcmk_rc_ok;
     297             : 
     298           8 :     rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
     299           8 :     if (rc != pcmk_rc_ok) {
     300           1 :         goto done;
     301             :     }
     302             : 
     303           7 :     rc = pcmk__ticket_get_attr(out, scheduler, ticket_id, attr_name, attr_default);
     304             : 
     305           8 : done:
     306           8 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     307           8 :     pe_free_working_set(scheduler);
     308           8 :     return rc;
     309             : }
     310             : 
     311             : int
     312           3 : pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     313             :                   const char *ticket_id, bool details, bool raw)
     314             : {
     315           3 :     int rc = pcmk_rc_ok;
     316             : 
     317           3 :     CRM_ASSERT(out != NULL && scheduler != NULL);
     318             : 
     319           3 :     if (ticket_id != NULL) {
     320           2 :         GHashTable *tickets = NULL;
     321           2 :         pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
     322             : 
     323           2 :         if (ticket == NULL) {
     324           1 :             return ENXIO;
     325             :         }
     326             : 
     327             :         /* The ticket-list message expects a GHashTable, so we'll construct
     328             :          * one with just this single item.
     329             :          */
     330           1 :         tickets = pcmk__strkey_table(free, NULL);
     331           1 :         g_hash_table_insert(tickets, strdup(ticket->id), ticket);
     332           1 :         out->message(out, "ticket-list", tickets, false, raw, details);
     333           1 :         g_hash_table_destroy(tickets);
     334             : 
     335             :     } else {
     336           1 :         out->message(out, "ticket-list", scheduler->tickets, false, raw, details);
     337             :     }
     338             : 
     339           2 :     return rc;
     340             : }
     341             : 
     342             : int
     343           4 : pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
     344             : {
     345           4 :     pcmk_scheduler_t *scheduler = NULL;
     346           4 :     pcmk__output_t *out = NULL;
     347           4 :     int rc = pcmk_rc_ok;
     348             : 
     349           4 :     rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
     350           4 :     if (rc != pcmk_rc_ok) {
     351           1 :         goto done;
     352             :     }
     353             : 
     354           3 :     pe__register_messages(out);
     355             : 
     356             :     /* XML output (which is the only format supported by public API functions
     357             :      * due to the use of pcmk__xml_output_new above) always prints all details,
     358             :      * so just pass false for the last two arguments.
     359             :      */
     360           3 :     rc = pcmk__ticket_info(out, scheduler, ticket_id, false, false);
     361             : 
     362           4 : done:
     363           4 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     364           4 :     pe_free_working_set(scheduler);
     365           4 :     return rc;
     366             : }
     367             : 
     368             : int
     369           8 : pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     370             :                          const char *ticket_id, GList *attr_delete, bool force)
     371             : {
     372           8 :     xmlNode *ticket_state_xml = NULL;
     373           8 :     xmlNode *xml_top = NULL;
     374           8 :     int rc = pcmk_rc_ok;
     375             : 
     376           8 :     CRM_ASSERT(out != NULL && cib != NULL && scheduler != NULL);
     377             : 
     378           8 :     if (ticket_id == NULL) {
     379           1 :         return EINVAL;
     380             :     }
     381             : 
     382             :     /* Nothing to do */
     383           7 :     if (attr_delete == NULL) {
     384           3 :         return pcmk_rc_ok;
     385             :     }
     386             : 
     387           4 :     rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
     388             : 
     389           4 :     if (rc == pcmk_rc_duplicate_id) {
     390           0 :         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
     391           4 :     } else if (rc != pcmk_rc_ok) {
     392           0 :         free_xml(ticket_state_xml);
     393           0 :         return rc;
     394             :     }
     395             : 
     396           7 :     for (GList *list_iter = attr_delete; list_iter != NULL; list_iter = list_iter->next) {
     397           4 :         const char *key = list_iter->data;
     398             : 
     399           4 :         if (!force && pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)) {
     400           1 :             free_xml(ticket_state_xml);
     401           1 :             return EACCES;
     402             :         }
     403             : 
     404           3 :         pcmk__xe_remove_attr(ticket_state_xml, key);
     405             :     }
     406             : 
     407           3 :     crm_log_xml_debug(xml_top, "Replace");
     408           3 :     rc = cib->cmds->replace(cib, PCMK_XE_STATUS, ticket_state_xml, cib_sync_call);
     409           3 :     rc = pcmk_legacy2rc(rc);
     410             : 
     411           3 :     free_xml(xml_top);
     412           3 :     return rc;
     413             : }
     414             : 
     415             : int
     416          10 : pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
     417             : {
     418          10 :     pcmk_scheduler_t *scheduler = NULL;
     419          10 :     pcmk__output_t *out = NULL;
     420          10 :     int rc = pcmk_rc_ok;
     421          10 :     cib_t *cib = NULL;
     422             : 
     423          10 :     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
     424          10 :     if (rc != pcmk_rc_ok) {
     425           2 :         goto done;
     426             :     }
     427             : 
     428           8 :     rc = pcmk__ticket_remove_attr(out, cib, scheduler, ticket_id, attr_delete, force);
     429             : 
     430          10 : done:
     431          10 :     if (cib != NULL) {
     432           8 :         cib__clean_up_connection(&cib);
     433             :     }
     434             : 
     435          10 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     436          10 :     pe_free_working_set(scheduler);
     437          10 :     return rc;
     438             : }
     439             : 
     440             : int
     441           9 : pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     442             :                       const char *ticket_id, GHashTable *attr_set, bool force)
     443             : {
     444           9 :     xmlNode *ticket_state_xml = NULL;
     445           9 :     xmlNode *xml_top = NULL;
     446           9 :     int rc = pcmk_rc_ok;
     447             : 
     448           9 :     CRM_ASSERT(out != NULL && cib != NULL && scheduler != NULL);
     449             : 
     450           9 :     if (ticket_id == NULL) {
     451           1 :         return EINVAL;
     452             :     }
     453             : 
     454             :     /* Nothing to do */
     455           8 :     if (attr_set == NULL || g_hash_table_size(attr_set) == 0) {
     456           2 :         return pcmk_rc_ok;
     457             :     }
     458             : 
     459           6 :     rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
     460             : 
     461           6 :     if (rc == pcmk_rc_duplicate_id) {
     462           0 :         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
     463           6 :     } else if (rc != pcmk_rc_ok) {
     464           0 :         free_xml(ticket_state_xml);
     465           0 :         return rc;
     466             :     }
     467             : 
     468           6 :     if (!force && g_hash_table_lookup(attr_set, PCMK__XA_GRANTED)) {
     469           2 :         free_xml(ticket_state_xml);
     470           2 :         return EACCES;
     471             :     }
     472             : 
     473           4 :     add_attribute_xml(scheduler, ticket_id, attr_set, &ticket_state_xml);
     474             : 
     475           4 :     crm_log_xml_debug(xml_top, "Update");
     476           4 :     rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call);
     477           4 :     rc = pcmk_legacy2rc(rc);
     478             : 
     479           4 :     free_xml(xml_top);
     480           4 :     return rc;
     481             : }
     482             : 
     483             : int
     484          11 : pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
     485             :                      bool force)
     486             : {
     487          11 :     pcmk_scheduler_t *scheduler = NULL;
     488          11 :     pcmk__output_t *out = NULL;
     489          11 :     int rc = pcmk_rc_ok;
     490          11 :     cib_t *cib = NULL;
     491             : 
     492          11 :     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
     493          11 :     if (rc != pcmk_rc_ok) {
     494           2 :         goto done;
     495             :     }
     496             : 
     497           9 :     rc = pcmk__ticket_set_attr(out, cib, scheduler, ticket_id, attr_set, force);
     498             : 
     499          11 : done:
     500          11 :     if (cib != NULL) {
     501           9 :         cib__clean_up_connection(&cib);
     502             :     }
     503             : 
     504          11 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     505          11 :     pe_free_working_set(scheduler);
     506          11 :     return rc;
     507             : }
     508             : 
     509             : int
     510           4 : pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     511             : {
     512           4 :     xmlNode *state_xml = NULL;
     513           4 :     int rc = pcmk_rc_ok;
     514             : 
     515           4 :     CRM_ASSERT(out != NULL && cib != NULL);
     516             : 
     517           4 :     rc = pcmk__get_ticket_state(cib, ticket_id, &state_xml);
     518             : 
     519           4 :     if (rc == pcmk_rc_duplicate_id) {
     520           1 :         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
     521             :                   ticket_id);
     522             :     }
     523             : 
     524           4 :     if (state_xml != NULL) {
     525           3 :         out->message(out, "ticket-state", state_xml);
     526           3 :         free_xml(state_xml);
     527             :     }
     528             : 
     529           4 :     return rc;
     530             : }
     531             : 
     532             : int
     533           6 : pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
     534             : {
     535           6 :     pcmk__output_t *out = NULL;
     536           6 :     int rc = pcmk_rc_ok;
     537           6 :     cib_t *cib = NULL;
     538             : 
     539           6 :     rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
     540           6 :     if (rc != pcmk_rc_ok) {
     541           2 :         goto done;
     542             :     }
     543             : 
     544           4 :     rc = pcmk__ticket_state(out, cib, ticket_id);
     545             : 
     546           6 : done:
     547           6 :     if (cib != NULL) {
     548           4 :         cib__clean_up_connection(&cib);
     549             :     }
     550             : 
     551           6 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     552           6 :     return rc;
     553             : }

Generated by: LCOV version 1.14