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 if (dbsize > maxblks) 467 usage(sp, gettext("size (-l) is too big")); 468 469 rval = meta_db_attach(sp, dbnlp, options, NULL, dbcnt, 470 dbsize, sysfilename, ep); 471 if (rval) { 472 metafreenamelist(dbnlp); 473 break; 474 } 475 dbsize = saved_dbsize; 476 dbcnt = saved_dbcnt; 477 default_size = saved_default_size; 478 479 metafreenamelist(dbnlp); 480 } 481 } 482 483 done: 484 if (! metaislocalset(sp)) { 485 cl_sk = cl_get_setkey(sp->setno, sp->setname); 486 if (MD_MNSET_DESC(sd)) { 487 /* 488 * Unlock diskset by resuming 489 * class 1 messages across the diskset. 490 */ 491 if (suspend1_flag) { 492 nd = sd->sd_nodelist; 493 while (nd) { 494 if (!(nd->nd_flags & 495 MD_MN_NODE_ALIVE)) { 496 nd = nd->nd_next; 497 continue; 498 } 499 500 if (clnt_mdcommdctl(nd->nd_nodename, 501 COMMDCTL_RESUME, sp, 502 MD_MSG_CLASS1, 503 MD_MSCF_NO_FLAGS, &xep)) { 504 mde_perror(&xep, ""); 505 mdclrerror(&xep); 506 } 507 nd = nd->nd_next; 508 } 509 } 510 nd = sd->sd_nodelist; 511 while (nd) { 512 if ((force & MDFORCE_LOCAL) && 513 strcmp(nd->nd_nodename, mynode()) != 0) { 514 nd = nd->nd_next; 515 continue; 516 } 517 if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { 518 nd = nd->nd_next; 519 continue; 520 } 521 522 if (clnt_unlock_set(nd->nd_nodename, cl_sk, 523 &xep)) 524 mdclrerror(&xep); 525 nd = nd->nd_next; 526 } 527 } else { 528 for (i = 0; i < MD_MAXSIDES; i++) { 529 /* Skip empty slots */ 530 if (sd->sd_nodes[i][0] == '\0') 531 continue; 532 533 if ((force & MDFORCE_LOCAL) && 534 strcmp(sd->sd_nodes[i], mynode()) != 0) 535 continue; 536 537 if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, 538 &xep)) 539 mdclrerror(&xep); 540 } 541 } 542 cl_set_setkey(NULL); 543 } 544 545 return (rval); 546 } 547 548 static int 549 info( 550 mdsetname_t *sp, 551 enum mddb_cmd cmd, 552 int print_headers, 553 int print_footers, 554 md_error_t *ep 555 ) 556 { 557 md_replicalist_t *rlp = NULL; 558 md_replicalist_t *rl; 559 md_replica_t *r; 560 int i; 561 char *unk_str = NULL; 562 563 /* get list of replicas, quit if none */ 564 if (metareplicalist(sp, (MD_BASICNAME_OK | PRINT_FAST), &rlp, ep) < 0) 565 return (-1); 566 else if (rlp == NULL) 567 return (0); 568 569 if (print_headers) { 570 (void) printf("\t%5.5s\t\t%9.9s\t%11.11s\n", gettext("flags"), 571 gettext("first blk"), gettext("block count")); 572 } 573 574 unk_str = gettext("unknown"); 575 for (rl = rlp; rl != NULL; rl = rl->rl_next) { 576 r = rl->rl_repp; 577 578 for (i = 0; i < MDDB_FLAGS_LEN; i++) { 579 if (r->r_flags & (1 << i)) 580 (void) putchar(MDDB_FLAGS_STRING[i]); 581 else 582 (void) putchar(' '); 583 } 584 585 if ((r->r_blkno == -1) && (r->r_nblk == -1)) { 586 (void) printf("\t%7.7s\t\t%7.7s\t", unk_str, unk_str); 587 } else if (r->r_nblk == -1) { 588 (void) printf("\t%ld\t\t%7.7s\t", r->r_blkno, unk_str); 589 } else { 590 (void) printf("\t%ld\t\t%ld\t", r->r_blkno, r->r_nblk); 591 } 592 593 (void) printf("\t%s\n", r->r_namep->bname); 594 595 } 596 597 metafreereplicalist(rlp); 598 599 if (cmd == infoshort) 600 return (0); 601 602 if (!print_footers) 603 return (0); 604 605 (void) printf(gettext( 606 " r - replica does not have device relocation information\n" 607 " o - replica active prior to last mddb configuration change\n" 608 " u - replica is up to date\n" 609 " l - locator for this replica was read successfully\n" 610 " c - replica's location was in %s\n" 611 " p - replica's location was patched in kernel\n" 612 " m - replica is master, this is replica selected as input\n" 613 " t - tagged data is associated with the replica\n" 614 " W - replica has device write errors\n" 615 " a - replica is active, commits are occurring to this replica\n" 616 " M - replica had problem with master blocks\n" 617 " D - replica had problem with data blocks\n" 618 " F - replica had format problems\n" 619 " S - replica is too small to hold current data base\n" 620 " R - replica had device read errors\n" 621 " B - tagged data associated with the replica is not valid\n"), 622 META_DBCONF); 623 return (0); 624 } 625 626 int 627 main(int argc, char **argv) 628 { 629 mdsetname_t *sp = NULL; 630 int c; 631 enum mddb_cmd cmd = none; 632 char *sname = MD_LOCAL_NAME; 633 char *cffilename = NULL; 634 char *sysfilename = NULL; 635 int forceflg = FALSE; 636 mdchkopts_t options = 0; 637 md_error_t status = mdnullerror; 638 md_error_t *ep = &status; 639 int error; 640 md_set_desc *sd; 641 int multi_node = 0; 642 643 /* 644 * Get the locale set up before calling any other routines 645 * with messages to ouput. Just in case we're not in a build 646 * environment, make sure that TEXT_DOMAIN gets set to 647 * something. 648 */ 649 #if !defined(TEXT_DOMAIN) 650 #define TEXT_DOMAIN "SYS_TEST" 651 #endif 652 (void) setlocale(LC_ALL, ""); 653 (void) textdomain(TEXT_DOMAIN); 654 655 if (sdssc_bind_library() == SDSSC_OKAY) 656 if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY, 657 &error) == SDSSC_PROXY_DONE) 658 exit(error); 659 660 /* parse args */ 661 optind = 1; 662 opterr = 1; 663 664 /* initialize */ 665 if (md_init(argc, argv, 0, 1, ep) != 0) { 666 mde_perror(ep, ""); 667 md_exit(sp, 1); 668 } 669 670 /* parse args */ 671 optind = 1; 672 opterr = 1; 673 while ((c = getopt(argc, argv, "ac:dfhik:l:ps:?")) != -1) { 674 switch (c) { 675 case 'a': 676 cmd = attach; 677 break; 678 case 'c': 679 break; 680 case 'd': 681 cmd = detach; 682 break; 683 case 'f': 684 forceflg = TRUE; 685 break; 686 case 'h': 687 usage(sp, (char *)0); 688 break; 689 case 'i': 690 cmd = infolong; 691 break; 692 case 'k': 693 sysfilename = optarg; 694 break; 695 case 'l': 696 break; 697 case 'p': 698 cmd = patch; 699 break; 700 case 's': 701 sname = optarg; 702 break; 703 704 case '?': 705 if (optopt == '?') 706 usage(sp, NULL); 707 /*FALLTHROUGH*/ 708 default: 709 usage(sp, gettext("unknown command")); 710 } 711 } 712 if (cmd == none) 713 cmd = infoshort; 714 715 /* get set context */ 716 if ((sp = metasetname(sname, ep)) == NULL) { 717 mde_perror(ep, ""); 718 md_exit(sp, 1); 719 } 720 721 /* print status */ 722 if (cmd == infoshort || cmd == infolong) { 723 if (optind != argc) 724 usage(sp, gettext( 725 "too many arguments")); 726 727 if (info(sp, cmd, 1, 1, ep)) { 728 mde_perror(ep, ""); 729 md_exit(sp, 1); 730 } 731 732 if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) { 733 mde_perror(ep, ""); 734 md_exit(sp, 1); 735 } 736 737 md_exit(sp, 0); 738 } 739 740 if (meta_check_root(ep) != 0) { 741 mde_perror(ep, ""); 742 md_exit(sp, 1); 743 } 744 745 if (! metaislocalset(sp)) { 746 if ((sd = metaget_setdesc(sp, ep)) == NULL) { 747 mde_perror(ep, ""); 748 md_exit(sp, 1); 749 } 750 if (MD_MNSET_DESC(sd)) { 751 multi_node = 1; 752 } 753 } 754 755 /* 756 * Adjust lock for traditional and local diskset. 757 * 758 * A MN diskset does not use the set meta_lock but instead 759 * uses the clnt_lock of rpc.metad and the suspend/resume 760 * feature of the rpc.mdcommd. Can't use set meta_lock since 761 * class 1 messages are grabbing this lock and if this thread 762 * is holding the set meta_lock then no rpc.mdcommd suspend 763 * can occur. 764 */ 765 if ((!multi_node) && (meta_lock(sp, TRUE, ep) != 0)) { 766 mde_perror(ep, ""); 767 md_exit(sp, 1); 768 } 769 770 /* check for ownership */ 771 if (meta_check_ownership(sp, ep) != 0) { 772 mde_perror(ep, ""); 773 md_exit(sp, 1); 774 } 775 776 /* snarf MDDB locations */ 777 if (cmd != patch) { 778 if (meta_setup_db_locations(ep) != 0) { 779 if (! mdismddberror(ep, MDE_DB_STALE)) { 780 if (forceflg == FALSE) { 781 mde_perror(ep, ""); 782 md_exit(sp, 1); 783 } 784 options = MDCHK_ALLOW_NODBS; 785 } 786 mdclrerror(ep); 787 } 788 } 789 790 /* patch MDDB locations */ 791 if (cmd == patch) { 792 if (optind < (argc - 1)) { 793 usage(sp, gettext( 794 "too many arguments to -p")); 795 } 796 797 if (optind == (argc - 1)) 798 cffilename = argv[optind]; 799 800 if (metaislocalset(sp)) { 801 if (meta_db_patch(sysfilename, cffilename, 1, ep)) { 802 mde_perror(ep, ""); 803 md_exit(sp, 1); 804 } 805 } 806 } 807 808 /* add/delete replicas */ 809 if (cmd == attach || cmd == detach) { 810 if (chngdb(sp, cmd, argc, argv, options, ep)) { 811 mde_perror(ep, ""); 812 md_exit(sp, 1); 813 } 814 } 815 816 md_exit(sp, 0); 817 /*NOTREACHED*/ 818 return (0); 819 } 820