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 * Utility to import SVM disksets into an active SVM configuration. 31 */ 32 33 #include <assert.h> 34 #include <strings.h> 35 #include <string.h> 36 #include <meta.h> 37 #include <sys/utsname.h> 38 #include <sys/lvm/md_mddb.h> 39 #include <sys/lvm/md_names.h> 40 #include <sdssc.h> 41 42 static md_im_drive_info_t *overlap_disks = NULL; 43 44 static void 45 usage(mdsetname_t *sp, char *string) 46 { 47 if ((string != NULL) && (*string != '\0')) 48 md_eprintf("%s\n", string); 49 50 (void) fprintf(stderr, 51 "%s:\t%s -s setname [-n] [-f] [-v] [%s...]\n", 52 gettext("usage"), myname, gettext("disk")); 53 (void) fprintf(stderr, " %s -r [%s...]\n", 54 myname, gettext("disk")); 55 (void) fprintf(stderr, " %s -?\n", myname); 56 (void) fprintf(stderr, " %s -V\n", myname); 57 58 md_exit(sp, (string == NULL) ? 0 : 1); 59 } 60 61 static void 62 print_version(mdsetname_t *sp) 63 { 64 struct utsname curname; 65 66 if (uname(&curname) == -1) { 67 md_eprintf("%s\n", strerror(errno)); 68 md_exit(sp, 1); 69 } 70 71 (void) fprintf(stderr, "%s %s\n", myname, curname.version); 72 73 md_exit(sp, 0); 74 } 75 76 /* 77 * Returns 0 if there is no overlap, 1 otherwise 78 */ 79 static int 80 set_disk_overlap(md_im_set_desc_t *misp) 81 { 82 83 md_im_set_desc_t *next, *isp = misp; 84 md_im_drive_info_t *set_dr, *next_set_dr, **chain; 85 int is_overlap = 0; 86 87 for (; isp != NULL; isp = isp->mis_next) { 88 for (next = isp->mis_next; next != NULL; next = next->mis_next) { 89 90 for (set_dr = isp->mis_drives; set_dr != NULL; 91 set_dr = set_dr->mid_next) { 92 93 for (next_set_dr = next->mis_drives; 94 next_set_dr != NULL; 95 next_set_dr = next_set_dr->mid_next) { 96 if (strcmp(set_dr->mid_dnp->cname, 97 next_set_dr->mid_dnp->cname) == 0) { 98 /* 99 * Chain it, skip if already there 100 */ 101 if (overlap_disks == NULL) { 102 set_dr->overlap = NULL; 103 overlap_disks = set_dr; 104 } else { 105 for (chain = &overlap_disks; 106 *chain != NULL; 107 chain = &(*chain)->overlap) { 108 if (strcmp(set_dr->mid_dnp->cname, 109 (*chain)->mid_dnp->cname) 110 == 0) 111 break; 112 } 113 114 if (*chain == NULL) { 115 *chain = set_dr; 116 set_dr->overlap = NULL; 117 } 118 } 119 if (!is_overlap) 120 is_overlap = 1; 121 } 122 } 123 } 124 } 125 } 126 127 return (is_overlap); 128 } 129 130 static void 131 report_overlap_recommendation() 132 { 133 mddb_mb_t *mbp; 134 md_error_t status = mdnullerror; 135 md_error_t *ep = &status; 136 md_im_drive_info_t *d; 137 138 (void) fprintf(stdout, "%s\n", gettext("Warning: The following disks " 139 "have been detected in more than one set.\n" 140 "Import recommendation based upon set creation time.\n" 141 "Proceed with the import with caution.")); 142 143 /* 144 * Look at all overlapping disks. Determine which slice 145 * would have a replica on it. i.e. either slice 7 or 6. 146 * Then read the master block. If the disk doesn't have a 147 * metadb on it, the master block is a dummy master block. 148 * Both dummy or normal master block contain the timestamp 149 * which is what we are after. Use this timestamp to issue 150 * the appropriate recommendation. 151 */ 152 mbp = Malloc(DEV_BSIZE); 153 for (d = overlap_disks; d != NULL; d = d->overlap) { 154 mdname_t *rsp; 155 uint_t sliceno; 156 int fd = -1; 157 158 if (meta_replicaslice(d->mid_dnp, &sliceno, ep) != 0) 159 continue; 160 161 if (d->mid_dnp->vtoc.parts[sliceno].size == 0) 162 continue; 163 164 if ((rsp = metaslicename(d->mid_dnp, sliceno, ep)) == NULL) 165 continue; 166 if ((fd = open(rsp->rname, O_RDONLY| O_NDELAY)) < 0) 167 continue; 168 if (read_master_block(ep, fd, mbp, DEV_BSIZE) <= 0) { 169 (void) close(fd); 170 mdclrerror(ep); 171 continue; 172 } 173 (void) close(fd); 174 fprintf(stdout, " %s ", d->mid_dnp->cname); 175 (void) fprintf(stdout, "%s: %s\n", 176 gettext(" - recommend importing with set " 177 "created at "), meta_print_time((md_timeval32_t *) 178 (&(mbp->mb_setcreatetime)))); 179 } 180 Free(mbp); 181 } 182 183 184 int 185 main(int argc, char *argv[]) 186 { 187 char c; 188 md_error_t status = mdnullerror; 189 md_error_t *ep = &status; 190 mdsetname_t *sp = NULL; 191 char *setname_new = NULL; 192 int report_only = 0; 193 int version = 0; 194 bool_t dry_run = 0; 195 md_im_names_t cnames = { 0, NULL }; 196 int err_on_prune = 0; 197 mddrivenamelist_t *dnlp = NULL; 198 mddrivenamelist_t *dp; 199 mddrivenamelist_t *skiph = NULL; 200 mddrivenamelist_t **skipt = &skiph; 201 int rscount = 0; 202 int hasreplica; 203 md_im_set_desc_t *misp = NULL; 204 md_im_set_desc_t **mispp = &misp; 205 mhd_mhiargs_t mhiargs = defmhiargs; 206 int have_multiple_sets = 0; 207 int force = 0; 208 int overlap = 0; 209 int partial = 0; 210 uint_t imp_flags = 0; 211 int set_count = 0; 212 213 /* 214 * Get the locale set up before calling any other routines 215 * with messages to output. Just in case we're not in a build 216 * environment, make sure that TEXT_DOMAIN gets set to 217 * something. 218 */ 219 #if !defined(TEXT_DOMAIN) 220 #define TEXT_DOMAIN "SYS_TEST" 221 #endif 222 (void) setlocale(LC_ALL, ""); 223 (void) textdomain(TEXT_DOMAIN); 224 225 /* 226 * Check to see if the libsds_sc.so is bound on the 227 * current system. If it is, it means the system is 228 * part of a cluster. 229 * 230 * The import operation is currently not supported 231 * in a SunCluster environment. 232 */ 233 if (sdssc_bind_library() != SDSSC_NOT_BOUND) { 234 printf(gettext( 235 "%s: Import operation not supported under SunCluster\n"), 236 argv[0]); 237 exit(0); 238 } 239 240 /* initialize */ 241 if (md_init(argc, argv, 0, 1, ep) != 0) { 242 mde_perror(ep, ""); 243 md_exit(sp, 1); 244 } 245 246 optind = 1; 247 opterr = 1; 248 249 while ((c = getopt(argc, argv, "frns:vV?")) != -1) { 250 switch (c) { 251 252 case 'f': 253 force = 1; 254 break; 255 256 case 'n': 257 dry_run = 1; 258 break; 259 260 case 'r': 261 report_only = 1; 262 imp_flags |= META_IMP_REPORT; 263 break; 264 265 case 's': 266 setname_new = optarg; 267 break; 268 269 case 'v': 270 imp_flags |= META_IMP_VERBOSE; 271 break; 272 273 case 'V': 274 version = 1; 275 break; 276 277 case '?': 278 default: 279 usage(sp, NULL); 280 break; 281 } 282 } 283 284 if (version == 1) 285 print_version(sp); 286 287 /* Detect conflicting options */ 288 if ((dry_run != 0) && (report_only != 0)) 289 usage(sp, gettext("The -n and -r options conflict.")); 290 291 if ((report_only != 0) && (setname_new != NULL)) 292 usage(sp, gettext("The -r and -s options conflict.")); 293 294 if ((report_only == 0) && (setname_new == NULL)) 295 usage(sp, gettext("You must specify either -r or -s.")); 296 297 /* Don't do any real work if we don't have root privilege */ 298 if (meta_check_root(ep) != 0) { 299 mde_perror(ep, ""); 300 md_exit(sp, 1); 301 } 302 303 if (meta_setup_db_locations(ep) != 0) { 304 mde_perror(ep, ""); 305 if (mdismddberror(ep, MDE_DB_STALE)) 306 md_exit(sp, 66); 307 if (! mdiserror(ep, MDE_MDDB_CKSUM)) 308 md_exit(sp, 1); 309 } 310 311 /* 312 * Read remaining arguments into drive name list, otherwise 313 * call routine to list all drives in system. 314 */ 315 if (argc > optind) { 316 int i; 317 318 /* For user specified disks, they MUST not be in use */ 319 err_on_prune = 1; 320 321 /* All remaining args should be disks */ 322 cnames.min_count = argc - optind; 323 cnames.min_names = Malloc(cnames.min_count * sizeof (char *)); 324 325 for (i = 0; i < cnames.min_count; i++, optind++) { 326 mddrivename_t *dnp; 327 dnp = metadrivename(&sp, argv[optind], ep); 328 if (dnp == NULL) { 329 mde_perror(ep, ""); 330 md_exit(sp, 1); 331 } else { 332 cnames.min_names[i] = dnp->rname; 333 } 334 } 335 } else { 336 if (meta_list_disks(ep, &cnames) != 0) { 337 mde_perror(ep, ""); 338 md_exit(sp, 1); 339 } 340 } 341 342 /* 343 * If the user specified disks on the command line, min_count will be 344 * greater than zero. If they didn't, it should be safe to assume that 345 * the system in question has at least one drive detected by the 346 * snapshot code, or we would have barfed earlier initializing the 347 * metadb. 348 */ 349 assert(cnames.min_count > 0); 350 351 /* 352 * Prune the list: 353 * - get rid of drives in current svm configuration 354 * - get rid of mounted drives 355 * - get rid of swap drives 356 * - get rid of drives in other sets 357 * 358 * If drives were specified on the command line, it should be 359 * an error to find in-use disks in the list. (err_on_prune) 360 * 361 * On return from meta_prune_cnames call, dnlp 362 * will have candidate for replica scan. 363 */ 364 dnlp = meta_prune_cnames(ep, &cnames, err_on_prune); 365 366 /* 367 * Doctor the drive string in the error structure to list all of the 368 * unused disks, rather than just one. The output will be done in the 369 * following !mdisok() block. 370 */ 371 if (mdisdserror(ep, MDE_DS_DRIVEINUSE)) { 372 md_ds_error_t *ip = 373 &ep->info.md_error_info_t_u.ds_error; 374 char *dlist; 375 int sizecnt = 0; 376 377 sizecnt += strlen(ip->drive); 378 for (dp = dnlp->next; dp != NULL; dp = dp->next) { 379 sizecnt += 2; /* for the ", " */ 380 sizecnt += strlen(dp->drivenamep->cname); 381 } 382 383 dlist = Malloc(sizecnt); 384 385 strlcpy(dlist, ip->drive, sizecnt); 386 Free(ip->drive); 387 388 dlist += strlen(ip->drive); 389 for (dp = dnlp->next; dp != NULL; dp = dp->next) { 390 strlcat(dlist, ", ", sizecnt); 391 strlcat(dlist, dp->drivenamep->cname, sizecnt); 392 } 393 394 ip->drive = Strdup(dlist); 395 } 396 397 /* Don't continue if we're already hosed */ 398 if (!mdisok(ep)) { 399 mde_perror(ep, ""); 400 md_exit(sp, 1); 401 } 402 403 /* ...or if there's nothing to scan */ 404 if (dnlp == NULL) { 405 md_eprintf("%s\n", gettext("no unused disks detected")); 406 md_exit(sp, 0); 407 } 408 409 /* Scan qualified disks */ 410 for (dp = dnlp; dp != NULL; dp = dp->next) { 411 mddrivenamelist_t *slp; 412 413 /* is the current drive on the skip list? */ 414 for (slp = skiph; slp != NULL; slp = slp->next) { 415 if (dp->drivenamep == slp->drivenamep) 416 goto skipdisk; 417 } 418 419 /* 420 * In addition to updating the misp list, either verbose or 421 * standard output will be generated. 422 * 423 */ 424 hasreplica = meta_get_and_report_set_info(dp, mispp, 0, 425 imp_flags, &set_count, ep); 426 427 /* 428 * If current disk is part of a partial diskset, 429 * meta_get_set_info returns an ENOTSUP for this disk. 430 * Import of partial disksets isn't supported yet, 431 * so do NOT put this disk onto any list being set up 432 * by metaimport. The partial diskset error message will 433 * only be printed once when the first partial diskset is 434 * detected. If the user is actually trying to import the 435 * partial diskset, print the error and exit; otherwise, 436 * print the error and continue. 437 */ 438 if (hasreplica == ENOTSUP) { 439 if (report_only) { 440 if (!partial) { 441 mde_perror(ep, ""); 442 partial = 1; 443 } 444 mdclrerror(ep); 445 goto skipdisk; 446 } else { 447 mde_perror(ep, ""); 448 md_exit(sp, 1); 449 } 450 } 451 452 if (hasreplica < 0) { 453 mde_perror(ep, ""); 454 mdclrerror(ep); 455 } else { 456 md_im_set_desc_t *p; 457 md_im_drive_info_t *d; 458 459 rscount += hasreplica; 460 461 /* Eliminate duplicate reporting */ 462 if (hasreplica > 0) { 463 md_timeval32_t firstdisktime; 464 465 /* 466 * Go to the tail for the current set 467 */ 468 for (p = misp; p->mis_next != NULL; 469 p = p->mis_next); 470 firstdisktime = 471 p->mis_drives->mid_setcreatetimestamp; 472 for (d = p->mis_drives; 473 d != NULL; 474 d = d->mid_next) { 475 /* 476 * if the mb_setcreatetime for a disk 477 * is not the same as the first disk 478 * in the set, don't put it on the 479 * skip list. This disk probably 480 * doesn't really belong in this set 481 * and we'll want to look at it again 482 * to figure out where it does belong. 483 */ 484 if ((d->mid_setcreatetimestamp.tv_sec != 485 firstdisktime.tv_sec) || 486 (d->mid_setcreatetimestamp.tv_usec 487 != firstdisktime.tv_usec)) 488 continue; 489 skipt = 490 meta_drivenamelist_append_wrapper( 491 skipt, d->mid_dnp); 492 } 493 } 494 } 495 496 skipdisk: 497 ; 498 } 499 500 /* 501 * Now have entire list of disks associated with diskset including 502 * disks listed in mddb locator blocks and namespace. Before importing 503 * diskset need to recheck that none of these disks is already in use. 504 * If a disk is found that is already in use, print error and exit. 505 */ 506 if (!report_only) { 507 md_im_set_desc_t *p; 508 md_im_drive_info_t *d; 509 mddrivename_t *dnp; 510 511 for (p = misp; p != NULL; p = p->mis_next) { 512 for (d = p->mis_drives; d != NULL; d = d->mid_next) { 513 dnp = d->mid_dnp; 514 if (meta_imp_drvused(sp, dnp, ep)) { 515 (void) mddserror(ep, 516 MDE_DS_DRIVEINUSE, 0, NULL, 517 dnp->cname, NULL); 518 mde_perror(ep, ""); 519 md_exit(sp, 0); 520 } 521 } 522 } 523 } 524 525 /* 526 * If there are no unconfigured sets, then our work here is done. 527 * Hopefully this is friendlier than just not printing anything at all. 528 */ 529 if (rscount == 0) { 530 /* 531 * If we've found partial disksets but no complete disksets, 532 * we don't want this to print. 533 */ 534 if (!partial) { 535 md_eprintf("%s\n", gettext("no unconfigured sets " 536 "detected")); 537 } 538 md_exit(sp, 0); 539 } 540 541 /* 542 * We'll need this info for both the report content and the import 543 * decision. By the time we're here, misp should NOT be NULL (or we 544 * would have exited in the rscount == 0 test above). 545 */ 546 assert(misp != NULL); 547 if (misp->mis_next != NULL) { 548 have_multiple_sets = 1; 549 } 550 /* 551 * Generate the appropriate (verbose or not) report for all sets 552 * detected. If we're planning on importing later, only include the 553 * "suggested import" command if multiple sets were detected. (That 554 * way, when we error out later, we have still provided useful 555 * information.) 556 */ 557 558 /* 559 * Now we should have all the unconfigured sets detected 560 * check for the overlapping 561 */ 562 if (have_multiple_sets) { 563 /* Printing out how many candidate disksets we found. */ 564 if (imp_flags & META_IMP_REPORT) { 565 (void) printf("%s: %i\n\n", 566 gettext("Number of disksets eligible for import"), 567 set_count); 568 } 569 570 overlap = set_disk_overlap(misp); 571 if (overlap) { 572 report_overlap_recommendation(); 573 } 574 575 if (!report_only) { 576 md_eprintf("%s\n\n", gettext("multiple unconfigured " 577 "sets detected.\nRerun the command with the " 578 "suggested options for the desired set.")); 579 } 580 } 581 582 583 /* 584 * If it's a report-only request, we're done. If it's an import 585 * request, make sure that we only have one entry in the set list. 586 */ 587 588 if (report_only) { 589 md_exit(sp, 0); 590 } else if (have_multiple_sets) { 591 md_exit(sp, 1); 592 } 593 594 if (setname_new == NULL) { 595 usage(sp, gettext("You must specify a new set name.")); 596 } 597 598 (void) meta_imp_set(misp, setname_new, force, dry_run, ep); 599 600 if (dry_run) { 601 md_exit(sp, 0); 602 } 603 604 if (!mdisok(ep)) { 605 mde_perror(ep, ""); 606 md_exit(sp, 1); 607 } 608 609 if ((sp = metasetname(setname_new, ep)) == NULL) { 610 mde_perror(ep, ""); 611 md_exit(sp, 1); 612 } 613 614 if (meta_lock_nowait(sp, ep) != 0) { 615 mde_perror(ep, ""); 616 md_exit(sp, 10); /* special errcode */ 617 } 618 619 if (meta_set_take(sp, &mhiargs, 0, 0, &status)) { 620 mde_perror(&status, ""); 621 md_exit(sp, 1); 622 } 623 624 md_exit(sp, 0); 625 /*NOTREACHED*/ 626 return (0); 627 } 628