1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * hotspare maintenance 31 */ 32 33 #include <meta.h> 34 #include <sdssc.h> 35 36 /* 37 * possible actions 38 */ 39 enum metahs_op { 40 NONE, 41 ADD_A_HS, 42 DELETE_A_HS, 43 ENABLE_A_HS, 44 REPLACE_A_HS, 45 STATUS_A_HSP 46 }; 47 48 /* 49 * report status of a hotspare pool 50 */ 51 static int 52 status_hsp( 53 mdsetname_t *sp, 54 mdhspname_t *hspnp, 55 md_error_t *ep 56 ) 57 { 58 mdprtopts_t options = (PRINT_HEADER | PRINT_SUBDEVS | PRINT_DEVID); 59 mdnamelist_t *nlp = NULL; 60 61 /* must have set */ 62 assert(sp != NULL); 63 assert(sp->setno == HSP_SET(hspnp->hsp)); 64 65 /* print status */ 66 if (meta_hsp_print(sp, hspnp, &nlp, NULL, stdout, options, ep) != 0) 67 return (-1); 68 69 /* return success */ 70 return (0); 71 } 72 73 /* 74 * print usage message 75 */ 76 static void 77 usage( 78 mdsetname_t *sp, 79 int eval 80 ) 81 { 82 (void) fprintf(stderr, gettext("\ 83 usage: %s [-s setname] -a hot_spare_pool [component...]\n\ 84 %s [-s setname] -a \"all\" component...\n\ 85 %s [-s setname] -d hot_spare_pool [component...]\n\ 86 %s [-s setname] -d \"all\" component...\n\ 87 %s [-s setname] -e component...\n\ 88 %s [-s setname] -r hot_spare_pool component_old component_new\n\ 89 %s [-s setname] -r \"all\" component_old component_new\n\ 90 %s [-s setname] -i [hot_spare_pool...]\n"), 91 myname, myname, myname, myname, myname, myname, myname, myname); 92 md_exit(sp, eval); 93 } 94 95 /* 96 * check for "all" 97 */ 98 static int 99 is_all(char *s) 100 { 101 if ((strcoll(s, gettext("all")) == 0) || 102 (strcoll(s, gettext("ALL")) == 0)) 103 return (1); 104 return (0); 105 } 106 107 /* 108 * parse args and add hotspares 109 */ 110 static int 111 add_hotspares( 112 mdsetname_t **spp, 113 int argc, 114 char *argv[], 115 mdcmdopts_t options, 116 md_error_t *ep 117 ) 118 { 119 mdhspnamelist_t *hspnlp = NULL; 120 mdnamelist_t *nlp = NULL; 121 int cnt; 122 mdhspnamelist_t *p; 123 int rval = -1; 124 125 /* get hotspare pool name(s) */ 126 if (argc < 1) 127 usage(*spp, 1); 128 if ((argc > 1) && is_all(argv[0])) { 129 if ((*spp == NULL) && 130 ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) { 131 return (-1); 132 } 133 /* check for ownership */ 134 assert(*spp != NULL); 135 if (meta_check_ownership(*spp, ep) != 0) 136 return (-1); 137 138 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) { 139 return (-1); 140 } else if (cnt == 0) { 141 return (mderror(ep, MDE_NO_HSPS, NULL)); 142 } 143 } else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0], 144 ep)) < 0) { 145 return (-1); 146 } 147 assert(cnt > 0); 148 --argc, ++argv; 149 150 assert(*spp != NULL); 151 152 /* grab set lock */ 153 if (meta_lock(*spp, TRUE, ep)) 154 return (-1); 155 156 /* check for ownership */ 157 if (meta_check_ownership(*spp, ep) != 0) 158 return (-1); 159 160 /* get hotspares */ 161 if (metanamelist(spp, &nlp, argc, argv, ep) < 0) { 162 goto out; 163 } 164 165 /* add hotspares */ 166 for (p = hspnlp; (p != NULL); p = p->next) { 167 mdhspname_t *hspnp = p->hspnamep; 168 169 if (meta_hs_add(*spp, hspnp, nlp, options, ep) != 0) 170 goto out; 171 } 172 rval = 0; 173 174 /* cleanup, return success */ 175 out: 176 if (hspnlp != NULL) 177 metafreehspnamelist(hspnlp); 178 if (nlp != NULL) 179 metafreenamelist(nlp); 180 return (rval); 181 } 182 183 /* 184 * parse args and delete hotspares 185 */ 186 static int 187 delete_hotspares( 188 mdsetname_t **spp, 189 int argc, 190 char *argv[], 191 mdcmdopts_t options, 192 md_error_t *ep 193 ) 194 { 195 mdhspnamelist_t *hspnlp = NULL; 196 mdnamelist_t *nlp = NULL; 197 int cnt; 198 mdhspnamelist_t *p; 199 int rval = -1; 200 201 /* get hotspare pool name(s) */ 202 if (argc < 1) 203 usage(*spp, 1); 204 if ((argc > 1) && is_all(argv[0])) { 205 if ((*spp == NULL) && 206 ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) { 207 return (-1); 208 } 209 /* check for ownership */ 210 assert(*spp != NULL); 211 if (meta_check_ownership(*spp, ep) != 0) 212 return (-1); 213 214 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) { 215 return (-1); 216 } else if (cnt == 0) { 217 return (mderror(ep, MDE_NO_HSPS, NULL)); 218 } 219 } else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0], 220 ep)) < 0) { 221 return (-1); 222 } 223 assert(cnt > 0); 224 --argc, ++argv; 225 226 assert(*spp != NULL); 227 228 /* grab set lock */ 229 if (meta_lock(*spp, TRUE, ep)) 230 return (-1); 231 232 /* check for ownership */ 233 if (meta_check_ownership(*spp, ep) != 0) 234 return (-1); 235 236 /* get hotspares */ 237 if (metanamelist(spp, &nlp, argc, argv, ep) < 0) { 238 goto out; 239 } 240 241 /* delete hotspares */ 242 cnt = 0; 243 for (p = hspnlp; (p != NULL); p = p->next) { 244 mdhspname_t *hspnp = p->hspnamep; 245 246 if (meta_hs_delete(*spp, hspnp, nlp, options, ep) != 0) { 247 if (mdisdeverror(ep, MDE_INVAL_HS)) 248 mdclrerror(ep); 249 else 250 goto out; 251 } else { 252 ++cnt; 253 } 254 } 255 256 /* make sure we got some */ 257 if ((nlp != NULL) && (cnt == 0)) { 258 (void) mddeverror(ep, MDE_INVAL_HS, nlp->namep->dev, 259 nlp->namep->cname); 260 goto out; 261 } 262 263 /* success */ 264 rval = 0; 265 266 /* cleanup, return success */ 267 out: 268 if (hspnlp != NULL) 269 metafreehspnamelist(hspnlp); 270 if (nlp != NULL) 271 metafreenamelist(nlp); 272 return (rval); 273 } 274 275 /* 276 * parse args and enable hotspares 277 */ 278 static int 279 enable_hotspares( 280 mdsetname_t **spp, 281 int argc, 282 char *argv[], 283 mdcmdopts_t options, 284 md_error_t *ep 285 ) 286 { 287 mdnamelist_t *nlp = NULL; 288 int rval = -1; 289 290 /* enable hotspares */ 291 if (argc < 1) 292 usage(*spp, 1); 293 294 /* get list of hotspares */ 295 if (metanamelist(spp, &nlp, argc, argv, ep) < 0) 296 goto out; 297 assert(nlp != NULL); 298 299 assert(*spp != NULL); 300 301 /* grab set lock */ 302 if (meta_lock(*spp, TRUE, ep)) 303 return (-1); 304 305 /* check for ownership */ 306 if (meta_check_ownership(*spp, ep) != 0) 307 return (-1); 308 309 /* enable hotspares */ 310 rval = meta_hs_enable(*spp, nlp, options, ep); 311 312 /* cleanup, return success */ 313 out: 314 metafreenamelist(nlp); 315 return (rval); 316 } 317 318 /* 319 * parse args and replace hotspares 320 */ 321 static int 322 replace_hotspares( 323 mdsetname_t **spp, 324 int argc, 325 char *argv[], 326 mdcmdopts_t options, 327 md_error_t *ep 328 ) 329 { 330 mdhspnamelist_t *hspnlp = NULL; 331 int cnt; 332 mdname_t *oldnp; 333 mdname_t *newnp; 334 mdhspnamelist_t *p; 335 int rval = -1; 336 337 /* get hotspare pool name(s) */ 338 if (argc != 3) 339 usage(*spp, 1); 340 if (is_all(argv[0])) { 341 if ((*spp == NULL) && 342 ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) { 343 return (-1); 344 } 345 /* check for ownership */ 346 assert(*spp != NULL); 347 if (meta_check_ownership(*spp, ep) != 0) 348 return (-1); 349 350 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) { 351 return (-1); 352 } else if (cnt == 0) { 353 return (mderror(ep, MDE_NO_HSPS, NULL)); 354 } 355 } else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0], 356 ep)) < 0) { 357 return (-1); 358 } 359 assert(cnt > 0); 360 361 assert(*spp != NULL); 362 363 /* grab set lock */ 364 if (meta_lock(*spp, TRUE, ep)) 365 return (-1); 366 367 /* check for ownership */ 368 if (meta_check_ownership(*spp, ep) != 0) 369 return (-1); 370 371 /* get old component */ 372 if ((oldnp = metaname(spp, argv[1], ep)) == NULL) 373 goto out; 374 375 /* get new component */ 376 if ((newnp = metaname(spp, argv[2], ep)) == NULL) 377 goto out; 378 379 /* replace hotspares */ 380 cnt = 0; 381 for (p = hspnlp; (p != NULL); p = p->next) { 382 mdhspname_t *hspnp = p->hspnamep; 383 384 if (meta_hs_replace(*spp, hspnp, oldnp, newnp, options, ep) 385 != 0) { 386 if (mdisdeverror(ep, MDE_INVAL_HS)) 387 mdclrerror(ep); 388 else 389 goto out; 390 } else { 391 ++cnt; 392 } 393 } 394 395 /* make sure we got some */ 396 if (cnt == 0) { 397 (void) mddeverror(ep, MDE_INVAL_HS, oldnp->dev, oldnp->cname); 398 goto out; 399 } 400 401 /* success */ 402 rval = 0; 403 404 /* cleanup, return success */ 405 out: 406 if (hspnlp != NULL) 407 metafreehspnamelist(hspnlp); 408 return (rval); 409 } 410 411 /* 412 * print_hsp_devid will collect the information for each underlying 413 * physical device for all the hotspare pools and print out the 414 * device relocation information 415 * INPUT: 416 * mdsetname_t *sp set the hsp is in 417 * mdhspnamelist_t *hspnlp list of hsp 418 * FILE *fp where to print to 419 * md_error_t *ep errors 420 * RETURN: 421 * 0 SUCCESS 422 * -1 ERROR 423 */ 424 static int 425 print_hsp_devid( 426 mdsetname_t *sp, 427 mdhspnamelist_t *hspnlp, 428 FILE *fp, 429 md_error_t *ep 430 ) 431 { 432 mddevid_t *ldevidp = NULL; 433 int retval = 0; 434 mdhspnamelist_t *p; 435 mddevid_t *nextp; 436 437 /* for all hotspare pools */ 438 for (p = hspnlp; (p != NULL); p = p->next) { 439 mdhspname_t *hspnp = p->hspnamep; 440 uint_t hsi; 441 442 /* for all hotspares within a pool */ 443 for (hsi = 0; 444 hsi < hspnp->unitp->hotspares.hotspares_len; hsi++) { 445 mdname_t *hsname; 446 447 hsname = 448 hspnp->unitp->hotspares.hotspares_val[hsi].hsnamep; 449 450 meta_create_non_dup_list(hsname, &ldevidp); 451 } 452 } 453 454 retval = meta_print_devid(sp, fp, ldevidp, ep); 455 456 /* cleanup */ 457 for (nextp = ldevidp; nextp != NULL; ldevidp = nextp) { 458 Free(ldevidp->ctdname); 459 nextp = ldevidp->next; 460 Free(ldevidp); 461 } 462 return (retval); 463 } 464 465 /* 466 * parse args and status hotspares 467 */ 468 static int 469 status_hotspares( 470 mdsetname_t **spp, 471 int argc, 472 char *argv[], 473 md_error_t *ep 474 ) 475 { 476 mdhspnamelist_t *hspnlp = NULL; 477 int cnt; 478 mdhspnamelist_t *p; 479 int rval = -1; 480 481 /* get hotspare pool name(s) */ 482 if (argc == 0) { 483 if ((*spp == NULL) && 484 ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) { 485 return (-1); 486 } 487 /* check for ownership */ 488 assert(*spp != NULL); 489 if (meta_check_ownership(*spp, ep) != 0) 490 return (-1); 491 492 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) { 493 return (-1); 494 } else if (cnt == 0) { 495 return (mderror(ep, MDE_NO_HSPS, NULL)); 496 } 497 } else if ((cnt = metahspnamelist(spp, &hspnlp, argc, argv, ep)) < 0) { 498 return (-1); 499 } 500 assert(cnt > 0); 501 502 /* check for ownership */ 503 assert(*spp != NULL); 504 if (meta_check_ownership(*spp, ep) != 0) 505 return (-1); 506 507 /* status hotspare pools */ 508 for (p = hspnlp; (p != NULL); p = p->next) { 509 mdhspname_t *hspnp = p->hspnamep; 510 511 if (status_hsp(*spp, hspnp, ep) != 0) 512 goto out; 513 } 514 515 if (print_hsp_devid(*spp, hspnlp, stdout, ep) == 0) { 516 rval = 0; 517 } 518 519 /* cleanup, return success */ 520 out: 521 if (hspnlp != NULL) 522 metafreehspnamelist(hspnlp); 523 return (rval); 524 } 525 526 /* 527 * parse args and doit 528 */ 529 int 530 main( 531 int argc, 532 char **argv 533 ) 534 { 535 char *sname = MD_LOCAL_NAME; 536 mdsetname_t *sp = NULL; 537 enum metahs_op which_op = NONE; 538 mdcmdopts_t options = (MDCMD_PRINT | MDCMD_DOIT); 539 int c; 540 md_error_t status = mdnullerror; 541 md_error_t *ep = &status; 542 int error; 543 bool_t called_thru_rpc = FALSE; 544 char *cp; 545 546 /* 547 * Get the locale set up before calling any other routines 548 * with messages to ouput. Just in case we're not in a build 549 * environment, make sure that TEXT_DOMAIN gets set to 550 * something. 551 */ 552 #if !defined(TEXT_DOMAIN) 553 #define TEXT_DOMAIN "SYS_TEST" 554 #endif 555 (void) setlocale(LC_ALL, ""); 556 (void) textdomain(TEXT_DOMAIN); 557 558 559 if ((cp = strstr(argv[0], ".rpc_call")) == NULL) { 560 if (sdssc_bind_library() == SDSSC_OKAY) 561 if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY, 562 &error) == SDSSC_PROXY_DONE) 563 exit(error); 564 } else { 565 *cp = '\0'; /* cut off ".rpc_call" */ 566 called_thru_rpc = TRUE; 567 } 568 569 /* initialize */ 570 if (md_init(argc, argv, 0, 1, ep) != 0) { 571 mde_perror(ep, ""); 572 md_exit(sp, 1); 573 } 574 575 /* parse args */ 576 optind = 1; 577 opterr = 1; 578 while ((c = getopt(argc, argv, "hs:aderin?")) != -1) { 579 switch (c) { 580 case 'h': 581 usage(sp, 0); 582 break; 583 584 case 's': 585 sname = optarg; 586 break; 587 588 case 'a': 589 if (which_op != NONE) 590 usage(sp, 1); 591 which_op = ADD_A_HS; 592 break; 593 594 case 'd': 595 if (which_op != NONE) 596 usage(sp, 1); 597 which_op = DELETE_A_HS; 598 break; 599 600 case 'e': 601 if (which_op != NONE) 602 usage(sp, 1); 603 which_op = ENABLE_A_HS; 604 break; 605 606 case 'r': 607 if (which_op != NONE) 608 usage(sp, 1); 609 which_op = REPLACE_A_HS; 610 break; 611 612 case 'i': 613 if (which_op != NONE) 614 usage(sp, 1); 615 which_op = STATUS_A_HSP; 616 break; 617 618 case 'n': 619 if (called_thru_rpc == TRUE) { 620 options &= ~MDCMD_DOIT; 621 } else { 622 usage(sp, 1); 623 } 624 break; 625 626 627 case '?': 628 if (optopt == '?') 629 usage(sp, 0); 630 /*FALLTHROUGH*/ 631 default: 632 usage(sp, 1); 633 break; 634 } 635 } 636 637 /* get set context */ 638 if ((sp = metasetname(sname, ep)) == NULL) { 639 mde_perror(ep, ""); 640 md_exit(sp, 1); 641 } 642 643 /* 644 * Send the command to all nodes if the -s argument refers to a MN 645 * set or the next argument refers to MN set hotspare name ( argc 646 * greater than optind if there is a next argument) 647 */ 648 if ((called_thru_rpc == FALSE) && 649 (meta_is_mn_set(sp, ep) || ((argc > optind) && 650 meta_is_mn_name(&sp, argv[optind], ep)))) { 651 int i; 652 int newargc; 653 int result; 654 char **newargv; 655 656 /* 657 * If we are dealing with a MN set and we were not 658 * called thru an rpc call, we are just to send this 659 * command string to the master of the set and let it 660 * deal with it. 661 * First we send out a dryrun version of this command. 662 * If that returns success, we know it succeeded on all 663 * nodes and it is safe to do the real command now. 664 */ 665 newargv = calloc(argc+1, sizeof (char *)); 666 newargv[0] = "metahs"; 667 newargv[1] = "-n"; /* always do "-n" first */ 668 newargc = 2; 669 for (i = 1; i < argc; i++, newargc++) 670 newargv[newargc] = argv[i]; 671 result = meta_mn_send_command(sp, newargc, newargv, 672 MD_DISP_STDERR | MD_DRYRUN, NO_CONTEXT_STRING, ep); 673 674 /* If we found a problem don't do it for real */ 675 if (result != 0) { 676 md_exit(sp, result); 677 } 678 679 /* 680 * Do it for real now. Remove "-n" from the arguments and 681 * MD_DRYRUN from the flags. If this fails the master must panic 682 * as the mddbs may be inconsistent. 683 */ 684 newargv[1] = ""; /* this was "-n" before */ 685 result = meta_mn_send_command(sp, newargc, newargv, 686 MD_DISP_STDERR | MD_RETRY_BUSY | MD_PANIC_WHEN_INCONSISTENT, 687 NO_CONTEXT_STRING, ep); 688 free(newargv); 689 690 /* No further action required */ 691 md_exit(sp, result); 692 } 693 694 argc -= optind; 695 argv += optind; 696 if (which_op == NONE) 697 usage(sp, 1); 698 699 if (which_op == STATUS_A_HSP) { 700 if (status_hotspares(&sp, argc, argv, ep) != 0) { 701 mde_perror(ep, ""); 702 md_exit(sp, 1); 703 } 704 md_exit(sp, 0); 705 } 706 707 if (meta_check_root(ep) != 0) { 708 mde_perror(ep, ""); 709 md_exit(sp, 1); 710 } 711 712 713 /* dispatch */ 714 switch (which_op) { 715 716 case ADD_A_HS: 717 if (add_hotspares(&sp, argc, argv, options, ep) != 0) { 718 mde_perror(ep, ""); 719 md_exit(sp, 1); 720 } 721 break; 722 723 case DELETE_A_HS: 724 if (delete_hotspares(&sp, argc, argv, options, ep) != 0) { 725 mde_perror(ep, ""); 726 md_exit(sp, 1); 727 } 728 break; 729 730 case ENABLE_A_HS: 731 if (enable_hotspares(&sp, argc, argv, options, ep) != 0) { 732 mde_perror(ep, ""); 733 md_exit(sp, 1); 734 } 735 break; 736 737 case REPLACE_A_HS: 738 if (replace_hotspares(&sp, argc, argv, options, ep) != 0) { 739 mde_perror(ep, ""); 740 md_exit(sp, 1); 741 } 742 break; 743 744 default: 745 assert(0); 746 break; 747 } 748 749 /* update md.cf */ 750 out: 751 if (meta_update_md_cf(sp, ep) != 0) { 752 mde_perror(ep, ""); 753 md_exit(sp, 1); 754 } 755 md_exit(sp, 0); 756 /*NOTREACHED*/ 757 return (0); 758 } 759