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 2005 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 * Metadevice database utility. 31 */ 32 33 #include <meta.h> 34 #define MDDB 35 #include <sys/lvm/md_mddb.h> 36 #include <sdssc.h> 37 38 enum mddb_cmd {none, attach, detach, patch, infolong, infoshort}; 39 40 extern int procsigs(int block, sigset_t *oldsigs, md_error_t *ep); 41 42 static void 43 usage( 44 mdsetname_t *sp, 45 char *string 46 ) 47 { 48 if ((string != NULL) && (*string != '\0')) 49 md_eprintf("%s\n", string); 50 51 (void) fprintf(stderr, gettext( 52 "usage: %s [-s setname] -a [options] mddbnnn\n" 53 " %s [-s setname] -a [options] device ...\n" 54 " %s [-s setname] -d [options] mddbnnn\n" 55 " %s [-s setname] -d [options] device ...\n" 56 " %s [-s setname] -i \n" 57 " %s -p [options] [ mddb.cf-file ]\n" 58 "options:\n" 59 "-c count number of replicas (for use with -a only)\n" 60 "-f force adding or deleting of replicas\n" 61 "-k filename alternate /etc/system file\n" 62 "-l length specify size of replica (for use with -a only)\n"), 63 myname, myname, myname, myname, myname, myname); 64 65 md_exit(sp, (string == NULL) ? 0 : 1); 66 } 67 68 static mdname_t * 69 make_dbname( 70 mdsetname_t *sp, 71 mdnamelist_t **nlp, 72 char *name, 73 md_error_t *ep 74 ) 75 { 76 mdname_t *np; 77 78 if ((np = metaname(&sp, name, ep)) == NULL) 79 return (NULL); 80 81 return (metanamelist_append(nlp, np)); 82 } 83 84 static mdnamelist_t * 85 get_dbnames_fromfile( 86 mdsetname_t *sp, 87 mdnamelist_t **nlp, 88 char *tabname, 89 int *dbsize, 90 int *dbcnt, 91 int *default_size, 92 md_error_t *ep 93 ) 94 { 95 md_tab_t *tabp = NULL; 96 md_tab_line_t *linep = NULL; 97 int argc; 98 char **argv; 99 char *context; 100 int save = optind; 101 int c; 102 103 /* look in md.tab */ 104 if ((tabp = meta_tab_parse(NULL, ep)) == NULL) { 105 if (! mdissyserror(ep, ENOENT)) 106 mde_perror(ep, ""); 107 mdclrerror(ep); 108 return (NULL); 109 } 110 111 if ((linep = meta_tab_find(sp, tabp, tabname, TAB_MDDB)) == NULL) { 112 (void) mdsyserror(ep, ENOENT, tabname); 113 goto out; 114 } 115 argc = linep->argc; 116 argv = linep->argv; 117 context = linep->context; 118 119 /* parse up entry */ 120 optind = 1; 121 opterr = 1; 122 while ((c = getopt(argc, argv, "c:l:")) != -1) { 123 switch (c) { 124 case 'c': 125 if (sscanf(optarg, "%d", dbcnt) != 1) { 126 md_eprintf("%s: %s\n", 127 context, gettext("bad format")); 128 usage(sp, ""); 129 } 130 break; 131 132 case 'l': 133 if (sscanf(optarg, "%d", dbsize) != 1) { 134 md_eprintf("%s: %s\n", 135 context, gettext("bad format")); 136 usage(sp, ""); 137 } 138 *default_size = FALSE; 139 break; 140 141 default: 142 usage(sp, ""); 143 } 144 } 145 argc -= optind; 146 argv += optind; 147 for (; (argc > 0); --argc, ++argv) { 148 char *token = argv[0]; 149 150 if (make_dbname(sp, nlp, token, ep) == NULL) { 151 metafreenamelist(*nlp); 152 *nlp = NULL; 153 goto out; 154 } 155 } 156 157 /* cleanup, return list */ 158 out: 159 if (tabp != NULL) 160 meta_tab_free(tabp); 161 optind = save; 162 return (*nlp); 163 } 164 165 /* 166 * built list of all devices which are to be detached 167 */ 168 static mdnamelist_t * 169 build_a_namelist( 170 mdsetname_t *sp, 171 int argc, 172 char **argv, 173 md_error_t *ep 174 ) 175 { 176 int i; 177 int dbsize, dbcnt, default_size; 178 mdnamelist_t *dbnlp = NULL; 179 180 for (i = 0; i < argc; i++) { 181 if (strncmp(argv[i], "mddb", 4) == 0) { 182 if (get_dbnames_fromfile(sp, &dbnlp, argv[i], 183 &dbsize, &dbcnt, &default_size, ep) == NULL) { 184 /* don't freelist here - already been done */ 185 return (NULL); 186 } 187 continue; 188 } 189 if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) { 190 metafreenamelist(dbnlp); 191 return (NULL); 192 } 193 } 194 195 return (dbnlp); 196 } 197 198 199 /* 200 * built the next list of devices which are to be attached 201 * that have the same size and count of replicas. 202 */ 203 static mdnamelist_t * 204 build_next_namelist( 205 mdsetname_t *sp, 206 int argc, 207 char **argv, 208 int *arg_index, 209 int *dbsize, 210 int *dbcnt, 211 int *default_size, 212 md_error_t *ep 213 ) 214 { 215 int i; 216 mdnamelist_t *dbnlp = NULL; 217 218 for (i = *arg_index; i < argc; i++) { 219 if (strncmp(argv[i], "mddb", 4) == 0) { 220 /* 221 * If we have stuff in the namelist 222 * return it before processing the mddb entry. 223 */ 224 if (dbnlp) { 225 *arg_index = i; 226 return (dbnlp); 227 } 228 if (get_dbnames_fromfile(sp, &dbnlp, argv[i], 229 dbsize, dbcnt, default_size, ep) == NULL) { 230 /* don't freelist here - already been done */ 231 return (NULL); 232 } 233 *arg_index = i + 1; 234 return (dbnlp); 235 } 236 if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) { 237 metafreenamelist(dbnlp); 238 return (NULL); 239 } 240 } 241 *arg_index = argc; 242 return (dbnlp); 243 } 244 245 246 static int 247 chngdb( 248 mdsetname_t *sp, 249 enum mddb_cmd cmd, 250 int argc, 251 char *argv[], 252 uint_t options, 253 md_error_t *ep 254 ) 255 { 256 int c; 257 int i; 258 md_error_t xep = mdnullerror; 259 mdnamelist_t *dbnlp = NULL; 260 int dbsize = MD_DBSIZE; 261 int maxblks = MDDB_MAXBLKS; 262 int minblks = MDDB_MINBLKS; 263 int dbcnt = 1; 264 mdforceopts_t force = MDFORCE_NONE; 265 int rval = 0; 266 char *sysfilename = NULL; 267 int default_size = TRUE; 268 md_set_desc *sd; 269 md_setkey_t *cl_sk; 270 md_mnnode_desc *nd; 271 int suspend1_flag = 0; 272 273 /* reset and parse args */ 274 optind = 1; 275 opterr = 1; 276 while ((c = getopt(argc, argv, "ac:dfk:pl:s:")) != -1) { 277 switch (c) { 278 case 'a': 279 break; 280 case 'c': 281 if (sscanf(optarg, "%d", &dbcnt) != 1) { 282 md_eprintf("%s: %s\n", 283 optarg, gettext("bad format")); 284 usage(sp, ""); 285 } 286 break; 287 case 'd': 288 break; 289 case 'f': 290 force = MDFORCE_LOCAL; 291 break; 292 case 'k': 293 sysfilename = optarg; 294 break; 295 case 'l': 296 if (sscanf(optarg, "%d", &dbsize) != 1) { 297 md_eprintf("%s: %s\n", 298 optarg, gettext("bad format")); 299 usage(sp, ""); 300 } 301 default_size = FALSE; 302 break; 303 case 'p': 304 break; 305 case 's': 306 break; 307 default: 308 usage(sp, ""); 309 } 310 } 311 312 /* 313 * If it is a multinode diskset, use appropriate metadb size. 314 */ 315 if (! metaislocalset(sp)) { 316 if ((sd = metaget_setdesc(sp, ep)) == NULL) 317 return (-1); 318 319 if (MD_MNSET_DESC(sd)) { 320 maxblks = MDDB_MN_MAXBLKS; 321 minblks = MDDB_MN_MINBLKS; 322 if (default_size) 323 dbsize = MD_MN_DBSIZE; 324 } 325 } 326 327 if (dbsize > maxblks) 328 usage(sp, gettext("size (-l) is too big")); 329 330 331 if (dbsize < minblks) 332 usage(sp, gettext("size (-l) is too small")); 333 334 if (dbcnt < 1) 335 usage(sp, gettext( 336 "count (-c) must be 1 or more")); 337 338 339 argc -= optind; 340 argv += optind; 341 if (argc <= 0) { 342 usage(sp, gettext( 343 "no devices specified to attach or detach")); 344 } 345 346 if (! metaislocalset(sp)) { 347 348 if (MD_MNSET_DESC(sd)) { 349 md_error_t xep = mdnullerror; 350 sigset_t sigs; 351 352 /* Make sure we are blocking all signals */ 353 if (procsigs(TRUE, &sigs, &xep) < 0) 354 mdclrerror(&xep); 355 356 /* 357 * Lock out other metaset or metadb commands 358 * across the diskset. 359 */ 360 nd = sd->sd_nodelist; 361 while (nd) { 362 if ((force & MDFORCE_LOCAL) && 363 strcmp(nd->nd_nodename, mynode()) != 0) { 364 nd = nd->nd_next; 365 continue; 366 } 367 368 if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { 369 nd = nd->nd_next; 370 continue; 371 } 372 373 if (clnt_lock_set(nd->nd_nodename, sp, ep)) { 374 rval = -1; 375 goto done; 376 } 377 nd = nd->nd_next; 378 } 379 /* 380 * Lock out other meta* commands by suspending 381 * class 1 messages across the diskset. 382 */ 383 nd = sd->sd_nodelist; 384 while (nd) { 385 if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { 386 nd = nd->nd_next; 387 continue; 388 } 389 390 if (clnt_mdcommdctl(nd->nd_nodename, 391 COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1, 392 MD_MSCF_NO_FLAGS, ep)) { 393 rval = -1; 394 goto done; 395 } 396 suspend1_flag = 1; 397 nd = nd->nd_next; 398 } 399 } else { 400 /* Lock the set on current set members */ 401 for (i = 0; i < MD_MAXSIDES; i++) { 402 /* Skip empty slots */ 403 if (sd->sd_nodes[i][0] == '\0') 404 continue; 405 406 if ((force & MDFORCE_LOCAL) && 407 strcmp(sd->sd_nodes[i], mynode()) != 0) 408 continue; 409 410 if (clnt_lock_set(sd->sd_nodes[i], sp, ep)) { 411 rval = -1; 412 goto done; 413 } 414 } 415 } 416 417 force |= MDFORCE_SET_LOCKED; 418 options |= MDCHK_SET_LOCKED; 419 } 420 421 if (cmd == detach) { 422 if ((dbnlp = build_a_namelist(sp, argc, argv, ep)) == NULL) { 423 rval = -1; 424 goto done; 425 } 426 427 rval = meta_db_detach(sp, dbnlp, force, sysfilename, ep); 428 429 metafreenamelist(dbnlp); 430 } 431 432 if (cmd == attach) { 433 daddr_t nblks = 0; 434 int arg_index = 0; 435 int saved_dbsize = dbsize; 436 int saved_dbcnt = dbcnt; 437 int saved_default_size = default_size; 438 439 if (force & MDFORCE_LOCAL) 440 options |= MDCHK_SET_FORCE; 441 442 if (default_size) 443 if ((nblks = meta_db_minreplica(sp, ep)) < 0) 444 mdclrerror(ep); 445 /* 446 * Loop through build a new namelist 447 * for each "mddb" entry or the devices list 448 * on the command line. This allows each "mddb" 449 * entry to have unique dbsize and dbcnt. 450 */ 451 while (arg_index < argc) { 452 453 dbnlp = build_next_namelist(sp, argc, argv, 454 &arg_index, &dbsize, &dbcnt, &default_size, ep); 455 if (dbnlp == NULL) { 456 rval = -1; 457 goto done; 458 } 459 /* 460 * If using the default size, 461 * then let's adjust the default to the minimum 462 * size currently in use. 463 */ 464 if (default_size && (nblks > 0)) 465 dbsize = nblks; /* adjust replica size */ 466 467 rval = meta_db_attach(sp, dbnlp, options, NULL, dbcnt, 468 dbsize, sysfilename, ep); 469 if (rval) { 470 metafreenamelist(dbnlp); 471 break; 472 } 473 dbsize = saved_dbsize; 474 dbcnt = saved_dbcnt; 475 default_size = saved_default_size; 476 477 metafreenamelist(dbnlp); 478 } 479 } 480 481 done: 482 if (! metaislocalset(sp)) { 483 cl_sk = cl_get_setkey(sp->setno, sp->setname); 484 if (MD_MNSET_DESC(sd)) { 485 /* 486 * Unlock diskset by resuming 487 * class 1 messages across the diskset. 488 */ 489 if (suspend1_flag) { 490 nd = sd->sd_nodelist; 491 while (nd) { 492 if (!(nd->nd_flags & 493 MD_MN_NODE_ALIVE)) { 494 nd = nd->nd_next; 495 continue; 496 } 497 498 if (clnt_mdcommdctl(nd->nd_nodename, 499 COMMDCTL_RESUME, sp, 500 MD_MSG_CLASS1, 501 MD_MSCF_NO_FLAGS, &xep)) { 502 mde_perror(&xep, ""); 503 mdclrerror(&xep); 504 } 505 nd = nd->nd_next; 506 } 507 } 508 nd = sd->sd_nodelist; 509 while (nd) { 510 if ((force & MDFORCE_LOCAL) && 511 strcmp(nd->nd_nodename, mynode()) != 0) { 512 nd = nd->nd_next; 513 continue; 514 } 515 if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { 516 nd = nd->nd_next; 517 continue; 518 } 519 520 if (clnt_unlock_set(nd->nd_nodename, cl_sk, 521 &xep)) 522 mdclrerror(&xep); 523 nd = nd->nd_next; 524 } 525 } else { 526 for (i = 0; i < MD_MAXSIDES; i++) { 527 /* Skip empty slots */ 528 if (sd->sd_nodes[i][0] == '\0') 529 continue; 530 531 if ((force & MDFORCE_LOCAL) && 532 strcmp(sd->sd_nodes[i], mynode()) != 0) 533 continue; 534 535 if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, 536 &xep)) 537 mdclrerror(&xep); 538 } 539 } 540 cl_set_setkey(NULL); 541 } 542 543 return (rval); 544 } 545 546 static int 547 info( 548 mdsetname_t *sp, 549 enum mddb_cmd cmd, 550 int print_headers, 551 int print_footers, 552 md_error_t *ep 553 ) 554 { 555 md_replicalist_t *rlp = NULL; 556 md_replicalist_t *rl; 557 md_replica_t *r; 558 int i; 559 char *unk_str = NULL; 560 561 /* get list of replicas, quit if none */ 562 if (metareplicalist(sp, (MD_BASICNAME_OK | PRINT_FAST), &rlp, ep) < 0) 563 return (-1); 564 else if (rlp == NULL) 565 return (0); 566 567 if (print_headers) { 568 (void) printf("\t%5.5s\t\t%9.9s\t%11.11s\n", gettext("flags"), 569 gettext("first blk"), gettext("block count")); 570 } 571 572 unk_str = gettext("unknown"); 573 for (rl = rlp; rl != NULL; rl = rl->rl_next) { 574 r = rl->rl_repp; 575 576 for (i = 0; i < MDDB_FLAGS_LEN; i++) { 577 if (r->r_flags & (1 << i)) 578 (void) putchar(MDDB_FLAGS_STRING[i]); 579 else 580 (void) putchar(' '); 581 } 582 583 if ((r->r_blkno == -1) && (r->r_nblk == -1)) { 584 (void) printf("\t%7.7s\t\t%7.7s\t", unk_str, unk_str); 585 } else if (r->r_nblk == -1) { 586 (void) printf("\t%ld\t\t%7.7s\t", r->r_blkno, unk_str); 587 } else { 588 (void) printf("\t%ld\t\t%ld\t", r->r_blkno, r->r_nblk); 589 } 590 591 (void) printf("\t%s\n", r->r_namep->bname); 592 593 } 594 595 metafreereplicalist(rlp); 596 597 if (cmd == infoshort) 598 return (0); 599 600 if (!print_footers) 601 return (0); 602 603 (void) printf(gettext( 604 " r - replica does not have device relocation information\n" 605 " o - replica active prior to last mddb configuration change\n" 606 " u - replica is up to date\n" 607 " l - locator for this replica was read successfully\n" 608 " c - replica's location was in %s\n" 609 " p - replica's location was patched in kernel\n" 610 " m - replica is master, this is replica selected as input\n" 611 " W - replica has device write errors\n" 612 " a - replica is active, commits are occurring to this replica\n" 613 " M - replica had problem with master blocks\n" 614 " D - replica had problem with data blocks\n" 615 " F - replica had format problems\n" 616 " S - replica is too small to hold current data base\n" 617 " R - replica had device read errors\n"), 618 META_DBCONF); 619 return (0); 620 } 621 622 int 623 main(int argc, char **argv) 624 { 625 mdsetname_t *sp = NULL; 626 int c; 627 enum mddb_cmd cmd = none; 628 char *sname = MD_LOCAL_NAME; 629 char *cffilename = NULL; 630 char *sysfilename = NULL; 631 int forceflg = FALSE; 632 mdchkopts_t options = 0; 633 md_error_t status = mdnullerror; 634 md_error_t *ep = &status; 635 int error; 636 md_set_desc *sd; 637 int multi_node = 0; 638 639 /* 640 * Get the locale set up before calling any other routines 641 * with messages to ouput. Just in case we're not in a build 642 * environment, make sure that TEXT_DOMAIN gets set to 643 * something. 644 */ 645 #if !defined(TEXT_DOMAIN) 646 #define TEXT_DOMAIN "SYS_TEST" 647 #endif 648 (void) setlocale(LC_ALL, ""); 649 (void) textdomain(TEXT_DOMAIN); 650 651 if (sdssc_bind_library() == SDSSC_OKAY) 652 if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY, 653 &error) == SDSSC_PROXY_DONE) 654 exit(error); 655 656 /* parse args */ 657 optind = 1; 658 opterr = 1; 659 660 /* initialize */ 661 if (md_init(argc, argv, 0, 1, ep) != 0) { 662 mde_perror(ep, ""); 663 md_exit(sp, 1); 664 } 665 666 /* parse args */ 667 optind = 1; 668 opterr = 1; 669 while ((c = getopt(argc, argv, "ac:dfhik:l:ps:?")) != -1) { 670 switch (c) { 671 case 'a': 672 cmd = attach; 673 break; 674 case 'c': 675 break; 676 case 'd': 677 cmd = detach; 678 break; 679 case 'f': 680 forceflg = TRUE; 681 break; 682 case 'h': 683 usage(sp, (char *)0); 684 break; 685 case 'i': 686 cmd = infolong; 687 break; 688 case 'k': 689 sysfilename = optarg; 690 break; 691 case 'l': 692 break; 693 case 'p': 694 cmd = patch; 695 break; 696 case 's': 697 sname = optarg; 698 break; 699 700 case '?': 701 if (optopt == '?') 702 usage(sp, NULL); 703 /*FALLTHROUGH*/ 704 default: 705 usage(sp, gettext("unknown command")); 706 } 707 } 708 if (cmd == none) 709 cmd = infoshort; 710 711 /* get set context */ 712 if ((sp = metasetname(sname, ep)) == NULL) { 713 mde_perror(ep, ""); 714 md_exit(sp, 1); 715 } 716 717 /* print status */ 718 if (cmd == infoshort || cmd == infolong) { 719 if (optind != argc) 720 usage(sp, gettext( 721 "too many arguments")); 722 723 if (info(sp, cmd, 1, 1, ep)) { 724 mde_perror(ep, ""); 725 md_exit(sp, 1); 726 } 727 728 if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) { 729 mde_perror(ep, ""); 730 md_exit(sp, 1); 731 } 732 733 md_exit(sp, 0); 734 } 735 736 if (meta_check_root(ep) != 0) { 737 mde_perror(ep, ""); 738 md_exit(sp, 1); 739 } 740 741 if (! metaislocalset(sp)) { 742 if ((sd = metaget_setdesc(sp, ep)) == NULL) { 743 mde_perror(ep, ""); 744 md_exit(sp, 1); 745 } 746 if (MD_MNSET_DESC(sd)) { 747 multi_node = 1; 748 } 749 } 750 751 /* 752 * Adjust lock for traditional and local diskset. 753 * 754 * A MN diskset does not use the set meta_lock but instead 755 * uses the clnt_lock of rpc.metad and the suspend/resume 756 * feature of the rpc.mdcommd. Can't use set meta_lock since 757 * class 1 messages are grabbing this lock and if this thread 758 * is holding the set meta_lock then no rpc.mdcommd suspend 759 * can occur. 760 */ 761 if ((!multi_node) && (meta_lock(sp, TRUE, ep) != 0)) { 762 mde_perror(ep, ""); 763 md_exit(sp, 1); 764 } 765 766 /* check for ownership */ 767 if (meta_check_ownership(sp, ep) != 0) { 768 mde_perror(ep, ""); 769 md_exit(sp, 1); 770 } 771 772 /* snarf MDDB locations */ 773 if (cmd != patch) { 774 if (meta_setup_db_locations(ep) != 0) { 775 if (! mdismddberror(ep, MDE_DB_STALE)) { 776 if (forceflg == FALSE) { 777 mde_perror(ep, ""); 778 md_exit(sp, 1); 779 } 780 options = MDCHK_ALLOW_NODBS; 781 } 782 mdclrerror(ep); 783 } 784 } 785 786 /* patch MDDB locations */ 787 if (cmd == patch) { 788 if (optind < (argc - 1)) { 789 usage(sp, gettext( 790 "too many arguments to -p")); 791 } 792 793 if (optind == (argc - 1)) 794 cffilename = argv[optind]; 795 796 if (metaislocalset(sp)) { 797 if (meta_db_patch(sysfilename, cffilename, 1, ep)) { 798 mde_perror(ep, ""); 799 md_exit(sp, 1); 800 } 801 } 802 } 803 804 /* add/delete replicas */ 805 if (cmd == attach || cmd == detach) { 806 if (chngdb(sp, cmd, argc, argv, options, ep)) { 807 mde_perror(ep, ""); 808 md_exit(sp, 1); 809 } 810 } 811 812 md_exit(sp, 0); 813 /*NOTREACHED*/ 814 return (0); 815 } 816