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