root/lib/pacemaker/pcmk_fence.c

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

DEFINITIONS

This source file includes following definitions.
  1. handle_level
  2. reduce_fence_history
  3. notify_callback
  4. fence_callback
  5. async_fence_helper
  6. pcmk__request_fencing
  7. pcmk_request_fencing
  8. pcmk__fence_history
  9. pcmk_fence_history
  10. pcmk__fence_installed
  11. pcmk_fence_installed
  12. pcmk__fence_last
  13. pcmk_fence_last
  14. pcmk__fence_list_targets
  15. pcmk_fence_list_targets
  16. pcmk__fence_metadata
  17. pcmk_fence_metadata
  18. pcmk__fence_registered
  19. pcmk_fence_registered
  20. pcmk__fence_register_level
  21. pcmk_fence_register_level
  22. pcmk__fence_unregister_level
  23. pcmk_fence_unregister_level
  24. pcmk__fence_validate
  25. pcmk_fence_validate
  26. pcmk__get_fencing_history

   1 /*
   2  * Copyright 2009-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 #include <crm/common/mainloop.h>
  12 #include <crm/common/results.h>
  13 #include <crm/common/output.h>
  14 #include <crm/common/output_internal.h>
  15 #include <crm/stonith-ng.h>
  16 #include <crm/fencing/internal.h>
  17 
  18 #include <glib.h>
  19 #include <libxml/tree.h>
  20 #include <pacemaker.h>
  21 #include <pacemaker-internal.h>
  22 
  23 static const int st_opts = st_opt_sync_call | st_opt_allow_suicide;
  24 
  25 static GMainLoop *mainloop = NULL;
  26 
  27 static struct {
  28     stonith_t *st;
  29     const char *target;
  30     const char *action;
  31     char *name;
  32     unsigned int timeout;
  33     unsigned int tolerance;
  34     int delay;
  35     pcmk__action_result_t result;
  36 } async_fence_data = { NULL, };
  37 
  38 static int
  39 handle_level(stonith_t *st, const char *target, int fence_level,
     /* [previous][next][first][last][top][bottom][index][help] */
  40              const stonith_key_value_t *devices, bool added)
  41 {
  42     const char *node = NULL;
  43     const char *pattern = NULL;
  44     const char *name = NULL;
  45     char *value = NULL;
  46     int rc = pcmk_rc_ok;
  47 
  48     if (target == NULL) {
  49         // Not really possible, but makes static analysis happy
  50         return EINVAL;
  51     }
  52 
  53     /* Determine if targeting by attribute, node name pattern or node name */
  54     value = strchr(target, '=');
  55     if (value != NULL)  {
  56         name = target;
  57         *value++ = '\0';
  58     } else if (*target == '@') {
  59         pattern = target + 1;
  60     } else {
  61         node = target;
  62     }
  63 
  64     /* Register or unregister level as appropriate */
  65     if (added) {
  66         rc = st->cmds->register_level_full(st, st_opts, node, pattern,
  67                                            name, value, fence_level,
  68                                            devices);
  69     } else {
  70         rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
  71                                          name, value, fence_level);
  72     }
  73 
  74     return pcmk_legacy2rc(rc);
  75 }
  76 
  77 static stonith_history_t *
  78 reduce_fence_history(stonith_history_t *history)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80     stonith_history_t *new, *hp, *np;
  81 
  82     if (!history) {
  83         return history;
  84     }
  85 
  86     new = history;
  87     hp = new->next;
  88     new->next = NULL;
  89 
  90     while (hp) {
  91         stonith_history_t *hp_next = hp->next;
  92 
  93         hp->next = NULL;
  94 
  95         for (np = new; ; np = np->next) {
  96             if ((hp->state == st_done) || (hp->state == st_failed)) {
  97                 /* action not in progress */
  98                 if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei)
  99                     && pcmk__str_eq(hp->action, np->action, pcmk__str_none)
 100                     && (hp->state == np->state)
 101                     && ((hp->state == st_done)
 102                         || pcmk__str_eq(hp->delegate, np->delegate,
 103                                         pcmk__str_casei))) {
 104                         /* purge older hp */
 105                         stonith_history_free(hp);
 106                         break;
 107                 }
 108             }
 109 
 110             if (!np->next) {
 111                 np->next = hp;
 112                 break;
 113             }
 114         }
 115         hp = hp_next;
 116     }
 117 
 118     return new;
 119 }
 120 
 121 static void
 122 notify_callback(stonith_t * st, stonith_event_t * e)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei)
 125         && pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_none)) {
 126 
 127         pcmk__set_result(&async_fence_data.result,
 128                          stonith__event_exit_status(e),
 129                          stonith__event_execution_status(e),
 130                          stonith__event_exit_reason(e));
 131         g_main_loop_quit(mainloop);
 132     }
 133 }
 134 
 135 static void
 136 fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138     pcmk__set_result(&async_fence_data.result, stonith__exit_status(data),
 139                      stonith__execution_status(data),
 140                      stonith__exit_reason(data));
 141     g_main_loop_quit(mainloop);
 142 }
 143 
 144 static gboolean
 145 async_fence_helper(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147     stonith_t *st = async_fence_data.st;
 148     int call_id = 0;
 149     int rc = stonith_api_connect_retry(st, async_fence_data.name, 10);
 150     int timeout = 0;
 151 
 152     if (rc != pcmk_ok) {
 153         g_main_loop_quit(mainloop);
 154         pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
 155                          PCMK_EXEC_NOT_CONNECTED, pcmk_strerror(rc));
 156         return TRUE;
 157     }
 158 
 159     st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE,
 160                                     notify_callback);
 161 
 162     call_id = st->cmds->fence_with_delay(st,
 163                                          st_opt_allow_suicide,
 164                                          async_fence_data.target,
 165                                          async_fence_data.action,
 166                                          async_fence_data.timeout/1000,
 167                                          async_fence_data.tolerance/1000,
 168                                          async_fence_data.delay);
 169 
 170     if (call_id < 0) {
 171         g_main_loop_quit(mainloop);
 172         pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
 173                          PCMK_EXEC_ERROR, pcmk_strerror(call_id));
 174         return TRUE;
 175     }
 176 
 177     timeout = async_fence_data.timeout / 1000;
 178     if (async_fence_data.delay > 0) {
 179         timeout += async_fence_data.delay;
 180     }
 181     st->cmds->register_callback(st, call_id, timeout, st_opt_timeout_updates,
 182                                 NULL, "callback", fence_callback);
 183     return TRUE;
 184 }
 185 
 186 int
 187 pcmk__request_fencing(stonith_t *st, const char *target, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 188                       const char *name, unsigned int timeout,
 189                       unsigned int tolerance, int delay, char **reason)
 190 {
 191     crm_trigger_t *trig;
 192     int rc = pcmk_rc_ok;
 193 
 194     async_fence_data.st = st;
 195     async_fence_data.name = strdup(name);
 196     async_fence_data.target = target;
 197     async_fence_data.action = action;
 198     async_fence_data.timeout = timeout;
 199     async_fence_data.tolerance = tolerance;
 200     async_fence_data.delay = delay;
 201     pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR, PCMK_EXEC_UNKNOWN,
 202                      NULL);
 203 
 204     trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
 205     mainloop_set_trigger(trig);
 206 
 207     mainloop = g_main_loop_new(NULL, FALSE);
 208     g_main_loop_run(mainloop);
 209 
 210     free(async_fence_data.name);
 211 
 212     if (reason != NULL) {
 213         // Give the caller ownership of the exit reason
 214         *reason = async_fence_data.result.exit_reason;
 215         async_fence_data.result.exit_reason = NULL;
 216     }
 217     rc = stonith__result2rc(&async_fence_data.result);
 218     pcmk__reset_result(&async_fence_data.result);
 219     return rc;
 220 }
 221 
 222 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 223 int
 224 pcmk_request_fencing(stonith_t *st, const char *target, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 225                      const char *name, unsigned int timeout,
 226                      unsigned int tolerance, int delay, char **reason)
 227 {
 228     return pcmk__request_fencing(st, target, action, name, timeout, tolerance,
 229                                  delay, reason);
 230 }
 231 #endif
 232 
 233 int
 234 pcmk__fence_history(pcmk__output_t *out, stonith_t *st, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 235                     unsigned int timeout, int verbose, bool broadcast,
 236                     bool cleanup)
 237 {
 238     stonith_history_t *history = NULL, *hp, *latest = NULL;
 239     int rc = pcmk_rc_ok;
 240     int opts = 0;
 241 
 242     if (cleanup) {
 243         out->info(out, "cleaning up fencing-history%s%s",
 244                   target ? " for node " : "", target ? target : "");
 245     }
 246     if (broadcast) {
 247         out->info(out, "gather fencing-history from all nodes");
 248     }
 249 
 250     stonith__set_call_options(opts, target, st_opts);
 251     if (cleanup) {
 252         stonith__set_call_options(opts, target, st_opt_cleanup);
 253     }
 254     if (broadcast) {
 255         stonith__set_call_options(opts, target, st_opt_broadcast);
 256     }
 257     if (pcmk__str_eq(target, "*", pcmk__str_none)) {
 258         target = NULL;
 259     }
 260     rc = st->cmds->history(st, opts, target, &history, (timeout / 1000));
 261 
 262     if (cleanup) {
 263         // Cleanup doesn't return a history list
 264         stonith_history_free(history);
 265         return pcmk_legacy2rc(rc);
 266     }
 267 
 268     out->begin_list(out, "event", "events", "Fencing history");
 269 
 270     history = stonith__sort_history(history);
 271     for (hp = history; hp; hp = hp->next) {
 272         if (hp->state == st_done) {
 273             latest = hp;
 274         }
 275 
 276         if (out->is_quiet(out) || !verbose) {
 277             continue;
 278         }
 279 
 280         out->message(out, "stonith-event", hp, true, false,
 281                      stonith__later_succeeded(hp, history),
 282                      (uint32_t) pcmk_show_failed_detail);
 283         out->increment_list(out);
 284     }
 285 
 286     if (latest) {
 287         if (out->is_quiet(out)) {
 288             out->message(out, "stonith-event", latest, false, true, NULL,
 289                          (uint32_t) pcmk_show_failed_detail);
 290         } else if (!verbose) { // already printed if verbose
 291             out->message(out, "stonith-event", latest, false, false, NULL,
 292                          (uint32_t) pcmk_show_failed_detail);
 293             out->increment_list(out);
 294         }
 295     }
 296 
 297     out->end_list(out);
 298 
 299     stonith_history_free(history);
 300     return pcmk_legacy2rc(rc);
 301 }
 302 
 303 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 304 int
 305 pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 306                    unsigned int timeout, bool quiet, int verbose,
 307                    bool broadcast, bool cleanup)
 308 {
 309     pcmk__output_t *out = NULL;
 310     int rc = pcmk_rc_ok;
 311 
 312     rc = pcmk__xml_output_new(&out, xml);
 313     if (rc != pcmk_rc_ok) {
 314         return rc;
 315     }
 316 
 317     stonith__register_messages(out);
 318 
 319     out->quiet = quiet;
 320 
 321     rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast,
 322                              cleanup);
 323     pcmk__xml_output_finish(out, xml);
 324     return rc;
 325 }
 326 #endif
 327 
 328 int
 329 pcmk__fence_installed(pcmk__output_t *out, stonith_t *st, unsigned int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 330 {
 331     stonith_key_value_t *devices = NULL;
 332     int rc = pcmk_rc_ok;
 333 
 334     rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices,
 335                                (timeout / 1000));
 336     // rc is a negative error code or a positive number of agents
 337     if (rc < 0) {
 338         return pcmk_legacy2rc(rc);
 339     }
 340 
 341     out->begin_list(out, "fence device", "fence devices",
 342                     "Installed fence devices");
 343     for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
 344         out->list_item(out, "device", "%s", iter->value);
 345     }
 346     out->end_list(out);
 347 
 348     stonith_key_value_freeall(devices, 1, 1);
 349     return pcmk_rc_ok;
 350 }
 351 
 352 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 353 int
 354 pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 355 {
 356     pcmk__output_t *out = NULL;
 357     int rc = pcmk_rc_ok;
 358 
 359     rc = pcmk__xml_output_new(&out, xml);
 360     if (rc != pcmk_rc_ok) {
 361         return rc;
 362     }
 363 
 364     stonith__register_messages(out);
 365 
 366     rc = pcmk__fence_installed(out, st, timeout);
 367     pcmk__xml_output_finish(out, xml);
 368     return rc;
 369 }
 370 #endif
 371 
 372 int
 373 pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 374 {
 375     time_t when = 0;
 376 
 377     if (target == NULL) {
 378         return pcmk_rc_ok;
 379     }
 380 
 381     if (as_nodeid) {
 382         when = stonith_api_time(atol(target), NULL, FALSE);
 383     } else {
 384         when = stonith_api_time(0, target, FALSE);
 385     }
 386 
 387     return out->message(out, "last-fenced", target, when);
 388 }
 389 
 390 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 391 int
 392 pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid)
     /* [previous][next][first][last][top][bottom][index][help] */
 393 {
 394     pcmk__output_t *out = NULL;
 395     int rc = pcmk_rc_ok;
 396 
 397     rc = pcmk__xml_output_new(&out, xml);
 398     if (rc != pcmk_rc_ok) {
 399         return rc;
 400     }
 401 
 402     stonith__register_messages(out);
 403 
 404     rc = pcmk__fence_last(out, target, as_nodeid);
 405     pcmk__xml_output_finish(out, xml);
 406     return rc;
 407 }
 408 #endif
 409 
 410 int
 411 pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
     /* [previous][next][first][last][top][bottom][index][help] */
 412                          const char *device_id, unsigned int timeout)
 413 {
 414     GList *targets = NULL;
 415     char *lists = NULL;
 416     int rc = pcmk_rc_ok;
 417 
 418     rc = st->cmds->list(st, st_opts, device_id, &lists, timeout/1000);
 419     if (rc != pcmk_rc_ok) {
 420         return pcmk_legacy2rc(rc);
 421     }
 422 
 423     targets = stonith__parse_targets(lists);
 424 
 425     out->begin_list(out, "fence target", "fence targets", "Fence Targets");
 426     while (targets != NULL) {
 427         out->list_item(out, NULL, "%s", (const char *) targets->data);
 428         targets = targets->next;
 429     }
 430     out->end_list(out);
 431 
 432     free(lists);
 433     return rc;
 434 }
 435 
 436 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 437 int
 438 pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st, const char *device_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 439                         unsigned int timeout)
 440 {
 441     pcmk__output_t *out = NULL;
 442     int rc = pcmk_rc_ok;
 443 
 444     rc = pcmk__xml_output_new(&out, xml);
 445     if (rc != pcmk_rc_ok) {
 446         return rc;
 447     }
 448 
 449     stonith__register_messages(out);
 450 
 451     rc = pcmk__fence_list_targets(out, st, device_id, timeout);
 452     pcmk__xml_output_finish(out, xml);
 453     return rc;
 454 }
 455 #endif
 456 
 457 int
 458 pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, const char *agent,
     /* [previous][next][first][last][top][bottom][index][help] */
 459                      unsigned int timeout)
 460 {
 461     char *buffer = NULL;
 462     int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
 463                                 timeout/1000);
 464 
 465     if (rc != pcmk_rc_ok) {
 466         return pcmk_legacy2rc(rc);
 467     }
 468 
 469     out->output_xml(out, "metadata", buffer);
 470     free(buffer);
 471     return rc;
 472 }
 473 
 474 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 475 int
 476 pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, const char *agent,
     /* [previous][next][first][last][top][bottom][index][help] */
 477                     unsigned int timeout)
 478 {
 479     pcmk__output_t *out = NULL;
 480     int rc = pcmk_rc_ok;
 481 
 482     rc = pcmk__xml_output_new(&out, xml);
 483     if (rc != pcmk_rc_ok) {
 484         return rc;
 485     }
 486 
 487     stonith__register_messages(out);
 488 
 489     rc = pcmk__fence_metadata(out, st, agent, timeout);
 490     pcmk__xml_output_finish(out, xml);
 491     return rc;
 492 }
 493 #endif
 494 
 495 int
 496 pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 497                        unsigned int timeout)
 498 {
 499     stonith_key_value_t *devices = NULL;
 500     int rc = pcmk_rc_ok;
 501 
 502     rc = st->cmds->query(st, st_opts, target, &devices, timeout/1000);
 503     /* query returns a negative error code or a positive number of results. */
 504     if (rc < 0) {
 505         return pcmk_legacy2rc(rc);
 506     }
 507 
 508     out->begin_list(out, "fence device", "fence devices",
 509                     "Registered fence devices");
 510     for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
 511         out->list_item(out, "device", "%s", iter->value);
 512     }
 513     out->end_list(out);
 514 
 515     stonith_key_value_freeall(devices, 1, 1);
 516 
 517     /* Return pcmk_rc_ok here, not the number of results.  Callers probably
 518      * don't care.
 519      */
 520     return pcmk_rc_ok;
 521 }
 522 
 523 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 524 int
 525 pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 526                       unsigned int timeout)
 527 {
 528     pcmk__output_t *out = NULL;
 529     int rc = pcmk_rc_ok;
 530 
 531     rc = pcmk__xml_output_new(&out, xml);
 532     if (rc != pcmk_rc_ok) {
 533         return rc;
 534     }
 535 
 536     stonith__register_messages(out);
 537 
 538     rc = pcmk__fence_registered(out, st, target, timeout);
 539     pcmk__xml_output_finish(out, xml);
 540     return rc;
 541 }
 542 #endif
 543 
 544 int
 545 pcmk__fence_register_level(stonith_t *st, const char *target, int fence_level,
     /* [previous][next][first][last][top][bottom][index][help] */
 546                            const stonith_key_value_t *devices)
 547 {
 548     return handle_level(st, target, fence_level, devices, true);
 549 }
 550 
 551 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 552 int
 553 pcmk_fence_register_level(stonith_t *st, const char *target, int fence_level,
     /* [previous][next][first][last][top][bottom][index][help] */
 554                           const stonith_key_value_t *devices)
 555 {
 556     return pcmk__fence_register_level(st, target, fence_level, devices);
 557 }
 558 #endif
 559 
 560 int
 561 pcmk__fence_unregister_level(stonith_t *st, const char *target, int fence_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 562 {
 563     return handle_level(st, target, fence_level, NULL, false);
 564 }
 565 
 566 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 567 int
 568 pcmk_fence_unregister_level(stonith_t *st, const char *target, int fence_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     return pcmk__fence_unregister_level(st, target, fence_level);
 571 }
 572 #endif
 573 
 574 int
 575 pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
     /* [previous][next][first][last][top][bottom][index][help] */
 576                      const char *id, const stonith_key_value_t *params,
 577                      unsigned int timeout)
 578 {
 579     char *output = NULL;
 580     char *error_output = NULL;
 581     int rc;
 582 
 583     rc  = st->cmds->validate(st, st_opt_sync_call, id, NULL, agent, params,
 584                              timeout/1000, &output, &error_output);
 585     out->message(out, "validate", agent, id, output, error_output, rc);
 586     return pcmk_legacy2rc(rc);
 587 }
 588 
 589 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 590 int
 591 pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
     /* [previous][next][first][last][top][bottom][index][help] */
 592                     const char *id, const stonith_key_value_t *params,
 593                     unsigned int timeout)
 594 {
 595     pcmk__output_t *out = NULL;
 596     int rc = pcmk_rc_ok;
 597 
 598     rc = pcmk__xml_output_new(&out, xml);
 599     if (rc != pcmk_rc_ok) {
 600         return rc;
 601     }
 602 
 603     stonith__register_messages(out);
 604 
 605     rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
 606     pcmk__xml_output_finish(out, xml);
 607     return rc;
 608 }
 609 #endif
 610 
 611 int
 612 pcmk__get_fencing_history(stonith_t *st, stonith_history_t **stonith_history,
     /* [previous][next][first][last][top][bottom][index][help] */
 613                           enum pcmk__fence_history fence_history)
 614 {
 615     int rc = pcmk_rc_ok;
 616 
 617     if ((st == NULL) || (st->state == stonith_disconnected)) {
 618         rc = ENOTCONN;
 619     } else if (fence_history != pcmk__fence_history_none) {
 620         rc = st->cmds->history(st, st_opt_sync_call, NULL, stonith_history,
 621                                120);
 622 
 623         rc = pcmk_legacy2rc(rc);
 624         if (rc != pcmk_rc_ok) {
 625             return rc;
 626         }
 627 
 628         *stonith_history = stonith__sort_history(*stonith_history);
 629         if (fence_history == pcmk__fence_history_reduced) {
 630             *stonith_history = reduce_fence_history(*stonith_history);
 631         }
 632     }
 633 
 634     return rc;
 635 }

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