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