xref: /titanic_41/usr/src/cmd/lvm/util/metaimport.c (revision 159cf8a6ecac7ecbb601c9653abfd0fa878075d8)
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 static void
184 report_standard(md_im_set_desc_t *s, int do_cmd, int overlap)
185 {
186 	md_im_drive_info_t	*d;
187 	md_im_replica_info_t	*r;
188 	md_im_drive_info_t	*good_disk = NULL;
189 	int			i;
190 	md_timeval32_t		firstdisktime;
191 
192 	for (i = 0; s != NULL; s = s->mis_next, i++) {
193 		int	time_conflict = 0;
194 
195 		/* Choose the best drive to use for the import command */
196 		for (good_disk = NULL, d = s->mis_drives;
197 		    d != NULL; d = d->mid_next) {
198 			if (good_disk == NULL) {
199 				for (r = d->mid_replicas;
200 				    r != NULL;
201 				    r = r->mir_next) {
202 					if (r->mir_flags & MDDB_F_ACTIVE) {
203 						good_disk = d;
204 						break;
205 					}
206 				}
207 			}
208 		}
209 
210 		/*
211 		 * Make the distinction between a regular diskset and
212 		 * a replicated diskset.
213 		 */
214 		if (s->mis_flags & MD_IM_SET_REPLICATED) {
215 			(void) fprintf(stdout, "%s :\n",
216 			gettext("Replicated diskset found containing disks"));
217 		} else {
218 			(void) fprintf(stdout, "%s :\n",
219 			gettext("Regular diskset found containing disks"));
220 		}
221 
222 
223 		/*
224 		 * Save the set creation time from the first disk in the
225 		 * diskset and compare the set creation time on all other
226 		 * disks in the set to that. If they are the same, the
227 		 * disk really belongs here. If they are different the
228 		 * disk probably belongs to a different set and we'll
229 		 * need to print out a warning.
230 		 */
231 		firstdisktime = s->mis_drives->mid_setcreatetimestamp;
232 		for (d = s->mis_drives; d != NULL; d = d->mid_next) {
233 			if ((firstdisktime.tv_sec ==
234 			    d->mid_setcreatetimestamp.tv_sec) &&
235 			    (firstdisktime.tv_usec ==
236 			    d->mid_setcreatetimestamp.tv_usec)) {
237 				(void) fprintf(stdout, "  %s\n",
238 				    d->mid_dnp->cname);
239 			} else {
240 				(void) fprintf(stdout, "  %s *\n",
241 				    d->mid_dnp->cname);
242 				time_conflict = 1;
243 			}
244 		}
245 
246 		if (time_conflict) {
247 			fprintf(stdout, "* WARNING: This disk has been reused "
248 			    "in another set.\n  Import may corrupt data in the "
249 			    "disk set.\n");
250 		}
251 
252 		if (overlap) {
253 			(void) fprintf(stdout, "%s: %s\n",
254 			    gettext("Diskset creation time"),
255 		    meta_print_time(&s->mis_drives->mid_replicas->
256 			mir_timestamp));
257 		}
258 
259 		/*
260 		 * when do_cmd is true, we are not actually importing
261 		 * a disk set, but want to print out extra information
262 		 */
263 		if (do_cmd) {
264 			/*
265 			 * TRANSLATION_NOTE
266 			 *
267 			 * The translation of the phrase "For more information
268 			 * about this set" will be followed by a ":" and a
269 			 * suggested command (untranslatable) that the user
270 			 * may use to request additional information.
271 			 */
272 			(void) fprintf(stdout, "%s:\n  %s -r -v %s\n",
273 			    gettext("For more information about this set"),
274 			    myname, good_disk->mid_dnp->cname);
275 
276 			/*
277 			 * TRANSLATION_NOTE
278 			 *
279 			 * The translation of the phrase "To import this set"
280 			 * will be followed by a ":" and a suggested command
281 			 * (untranslatable) that the user may use to import
282 			 * the specified diskset.
283 			 */
284 			(void) fprintf(stdout, "%s:\n  %s -s <newsetname> %s\n",
285 			    gettext("To import this set"), myname,
286 			    good_disk->mid_dnp->cname);
287 		}
288 
289 		(void) fprintf(stdout, "\n");
290 	}
291 
292 	if (overlap) {
293 		report_overlap_recommendation();
294 	}
295 }
296 
297 static void
298 report_verbose(md_im_set_desc_t *s, int do_cmd, int overlap)
299 {
300 	md_im_drive_info_t	*d;
301 	md_im_replica_info_t	*r;
302 	md_im_drive_info_t	*good_disk;
303 	static const char	fmt1[] = "%-*.*s %12.12s %12.12s %s\n";
304 	static const char	fmt2[] = "%-*.*s %12d %12d ";
305 	int			dlen = 0;
306 	int			f;
307 
308 	for (; s != NULL; s = s->mis_next) {
309 
310 		/*
311 		 * Run through the drives in this set to find the one with the
312 		 * longest common name and the one we want to consider "best"
313 		 */
314 		for (d = s->mis_drives, good_disk = NULL;
315 		    d != NULL; d = d->mid_next) {
316 			dlen = max(dlen, strlen(d->mid_dnp->cname));
317 			for (r = d->mid_replicas; r != NULL; r = r->mir_next) {
318 				if ((good_disk == NULL) &&
319 				    (r->mir_flags & MDDB_F_ACTIVE)) {
320 					good_disk = d;
321 					break;
322 				}
323 			}
324 		}
325 
326 		if (do_cmd) {
327 			(void) fprintf(stdout, "%s: %s -s <newsetname> %s\n",
328 				gettext("To import this set"), myname,
329 				good_disk->mid_dnp->cname);
330 		}
331 
332 		(void) fprintf(stdout, "%s: %s\n", gettext("Last update"),
333 		    meta_print_time(&good_disk->mid_replicas->mir_timestamp));
334 
335 
336 		/* Make sure the length will hold the column heading */
337 		dlen = max(dlen, strlen(gettext("Device")));
338 
339 		(void) fprintf(stdout, fmt1, dlen, dlen, gettext("Device"),
340 		    gettext("offset"), gettext("length"),
341 		    gettext("replica flags"));
342 
343 		for (d = s->mis_drives; d != NULL; d = d->mid_next) {
344 
345 			if (d->mid_replicas != NULL) {
346 				for (r = d->mid_replicas;
347 				    r != NULL;
348 				    r = r->mir_next) {
349 					(void) fprintf(stdout, fmt2, dlen, dlen,
350 					    (r == d->mid_replicas) ?
351 					    d->mid_dnp->cname : "",
352 					    r->mir_offset, r->mir_length);
353 
354 					for (f = 0; f < MDDB_FLAGS_LEN; f++) {
355 						(void) putchar(
356 						    (r->mir_flags & (1 << f)) ?
357 						    MDDB_FLAGS_STRING[f] : ' ');
358 					}
359 
360 					(void) fprintf(stdout, "\n");
361 				}
362 			} else {
363 				(void) fprintf(stdout, fmt1,
364 				    dlen, dlen, d->mid_dnp->cname,
365 				    gettext("no replicas"), "", "");
366 			}
367 		}
368 
369 		if (overlap) {
370 			(void) fprintf(stdout, "%s: %s\n",
371 			    gettext("Diskset creation time"),
372 		    meta_print_time(&s->mis_drives->mid_replicas->
373 			mir_timestamp));
374 		}
375 
376 		(void) fprintf(stdout, "\n");
377 	}
378 
379 	if (overlap) {
380 		report_overlap_recommendation();
381 	}
382 }
383 
384 int
385 main(int argc, char *argv[])
386 {
387 	char			c;
388 	md_error_t		status = mdnullerror;
389 	md_error_t		*ep = &status;
390 	mdsetname_t		*sp = NULL;
391 	char			*setname_new = NULL;
392 	int			report_only = 0;
393 	int			verbose = 0;
394 	int			version = 0;
395 	bool_t			dry_run = 0;
396 	md_im_names_t		cnames = { 0, NULL };
397 	int			err_on_prune = 0;
398 	mddrivenamelist_t	*dnlp = NULL;
399 	mddrivenamelist_t	*dp;
400 	mddrivenamelist_t	*skiph = NULL;
401 	mddrivenamelist_t	**skipt = &skiph;
402 	int			rscount = 0;
403 	int			hasreplica;
404 	md_im_set_desc_t	*misp = NULL;
405 	md_im_set_desc_t	**mispp = &misp;
406 	mhd_mhiargs_t		mhiargs = defmhiargs;
407 	int			have_multiple_sets = 0;
408 	int			force = 0;
409 	int			overlap = 0;
410 	int			partial = 0;
411 
412 	/*
413 	 * Get the locale set up before calling any other routines
414 	 * with messages to output.  Just in case we're not in a build
415 	 * environment, make sure that TEXT_DOMAIN gets set to
416 	 * something.
417 	 */
418 #if !defined(TEXT_DOMAIN)
419 #define	TEXT_DOMAIN "SYS_TEST"
420 #endif
421 	(void) setlocale(LC_ALL, "");
422 	(void) textdomain(TEXT_DOMAIN);
423 
424 	/*
425 	 * Check to see if the libsds_sc.so is bound on the
426 	 * current system. If it is, it means the system is
427 	 * part of a cluster.
428 	 *
429 	 * The import operation is currently not supported
430 	 * in a SunCluster environment.
431 	 */
432 	if (sdssc_bind_library() != SDSSC_NOT_BOUND) {
433 		printf(gettext(
434 		    "%s: Import operation not supported under SunCluster\n"),
435 		    argv[0]);
436 		exit(0);
437 	}
438 
439 	/* initialize */
440 	if (md_init(argc, argv, 0, 1, ep) != 0) {
441 		mde_perror(ep, "");
442 		md_exit(sp, 1);
443 	}
444 
445 	optind = 1;
446 	opterr = 1;
447 
448 	while ((c = getopt(argc, argv, "frns:vV?")) != -1) {
449 		switch (c) {
450 
451 		case 'f':
452 			force = 1;
453 			break;
454 
455 		case 'n':
456 			dry_run = 1;
457 			break;
458 
459 		case 'r':
460 			report_only = 1;
461 			break;
462 
463 		case 's':
464 			setname_new = optarg;
465 			break;
466 
467 		case 'v':
468 			verbose = 1;
469 			break;
470 
471 		case 'V':
472 			version = 1;
473 			break;
474 
475 		case '?':
476 		default:
477 			usage(sp, NULL);
478 			break;
479 		}
480 	}
481 
482 	if (version == 1)
483 		print_version(sp);
484 
485 	/* Detect conflicting options */
486 	if ((dry_run != 0) && (report_only != 0))
487 		usage(sp, gettext("The -n and -r options conflict."));
488 
489 	if ((report_only != 0) && (setname_new != NULL))
490 		usage(sp, gettext("The -r and -s options conflict."));
491 
492 	if ((report_only == 0) && (setname_new == NULL))
493 		usage(sp, gettext("You must specify either -r or -s."));
494 
495 	/* Don't do any real work if we don't have root privilege */
496 	if (meta_check_root(ep) != 0) {
497 		mde_perror(ep, "");
498 		md_exit(sp, 1);
499 	}
500 
501 	if (meta_setup_db_locations(ep) != 0) {
502 		mde_perror(ep, "");
503 		if (mdismddberror(ep, MDE_DB_STALE))
504 			md_exit(sp, 66);
505 		if (! mdiserror(ep, MDE_MDDB_CKSUM))
506 			md_exit(sp, 1);
507 	}
508 
509 	/*
510 	 * Read remaining arguments into drive name list, otherwise
511 	 * call routine to list all drives in system.
512 	 */
513 	if (argc > optind) {
514 		int i;
515 
516 		/* For user specified disks, they MUST not be in use */
517 		err_on_prune = 1;
518 
519 		/* All remaining args should be disks */
520 		cnames.min_count = argc - optind;
521 		cnames.min_names = Malloc(cnames.min_count * sizeof (char *));
522 
523 		for (i = 0; i < cnames.min_count; i++, optind++) {
524 			mddrivename_t *dnp;
525 			dnp = metadrivename(&sp, argv[optind], ep);
526 			if (dnp == NULL) {
527 				mde_perror(ep, "");
528 				md_exit(sp, 1);
529 			} else {
530 				cnames.min_names[i] = dnp->rname;
531 			}
532 		}
533 	} else {
534 		if (meta_list_disks(ep, &cnames) != 0) {
535 			mde_perror(ep, "");
536 			md_exit(sp, 1);
537 		}
538 	}
539 
540 	/*
541 	 * If the user specified disks on the command line, min_count will be
542 	 * greater than zero.  If they didn't, it should be safe to assume that
543 	 * the system in question has at least one drive detected by the
544 	 * snapshot code, or we would have barfed earlier initializing the
545 	 * metadb.
546 	 */
547 	assert(cnames.min_count > 0);
548 
549 	/*
550 	 * Prune the list:
551 	 * - get rid of drives in current svm configuration
552 	 * - get rid of mounted drives
553 	 * - get rid of swap drives
554 	 * - get rid of drives in other sets
555 	 *
556 	 * If drives were specified on the command line, it should be
557 	 * an error to find in-use disks in the list.  (err_on_prune)
558 	 *
559 	 * On return from meta_prune_cnames call, dnlp
560 	 * will have candidate for replica scan.
561 	 */
562 	dnlp = meta_prune_cnames(ep, &cnames, err_on_prune);
563 
564 	/*
565 	 * Doctor the drive string in the error structure to list all of the
566 	 * unused disks, rather than just one.  The output will be done in the
567 	 * following !mdisok() block.
568 	 */
569 	if (mdisdserror(ep, MDE_DS_DRIVEINUSE)) {
570 		md_ds_error_t		*ip =
571 		    &ep->info.md_error_info_t_u.ds_error;
572 		char			*dlist;
573 		int			sizecnt = 0;
574 
575 		sizecnt += strlen(ip->drive);
576 		for (dp = dnlp->next; dp != NULL; dp = dp->next) {
577 			sizecnt += 2; /* for the ", " */
578 			sizecnt += strlen(dp->drivenamep->cname);
579 		}
580 
581 		dlist = Malloc(sizecnt);
582 
583 		strlcpy(dlist, ip->drive, sizecnt);
584 		Free(ip->drive);
585 
586 		dlist += strlen(ip->drive);
587 		for (dp = dnlp->next; dp != NULL; dp = dp->next) {
588 			strlcat(dlist, ", ", sizecnt);
589 			strlcat(dlist, dp->drivenamep->cname, sizecnt);
590 		}
591 
592 		ip->drive = Strdup(dlist);
593 	}
594 
595 	/* Don't continue if we're already hosed */
596 	if (!mdisok(ep)) {
597 		mde_perror(ep, "");
598 		md_exit(sp, 1);
599 	}
600 
601 	/* ...or if there's nothing to scan */
602 	if (dnlp == NULL) {
603 		md_eprintf("%s\n", gettext("no unused disks detected"));
604 		md_exit(sp, 0);
605 	}
606 
607 	/* Scan qualified disks */
608 	for (dp = dnlp; dp != NULL; dp = dp->next) {
609 		mddrivenamelist_t *slp;
610 
611 		/* is the current drive on the skip list? */
612 		for (slp = skiph; slp != NULL; slp = slp->next) {
613 		    if (dp->drivenamep == slp->drivenamep)
614 			    goto skipdisk;
615 		}
616 
617 		hasreplica = meta_get_set_info(dp, mispp, 0, ep);
618 
619 		/*
620 		 * If current disk is part of a partial diskset,
621 		 * meta_get_set_info returns an ENOTSUP for this disk.
622 		 * Import of partial disksets isn't supported yet,
623 		 * so do NOT put this disk onto any list being set up
624 		 * by metaimport. The partial diskset error message will
625 		 * only be printed once when the first partial diskset is
626 		 * detected. If the user is actually trying to import the
627 		 * partial diskset, print the error and exit; otherwise,
628 		 * print the error and continue.
629 		 */
630 		if (hasreplica == ENOTSUP) {
631 			if (report_only) {
632 			    if (!partial) {
633 				mde_perror(ep, "");
634 				partial = 1;
635 			    }
636 			    mdclrerror(ep);
637 			    goto skipdisk;
638 			} else {
639 			    mde_perror(ep, "");
640 			    md_exit(sp, 1);
641 			}
642 		}
643 
644 		if (hasreplica < 0) {
645 			mde_perror(ep, "");
646 			mdclrerror(ep);
647 		} else {
648 			md_im_set_desc_t	*p;
649 			md_im_drive_info_t	*d;
650 
651 			rscount += hasreplica;
652 
653 			/* Eliminate duplicate reporting */
654 			if (hasreplica > 0) {
655 				md_timeval32_t	firstdisktime;
656 
657 				/*
658 				 * Go to the tail for the current set
659 				 */
660 				for (p = misp; p->mis_next != NULL;
661 				    p = p->mis_next);
662 				firstdisktime =
663 				    p->mis_drives->mid_setcreatetimestamp;
664 				for (d = p->mis_drives;
665 				    d != NULL;
666 				    d = d->mid_next) {
667 					/*
668 					 * if the mb_setcreatetime for a disk
669 					 * is not the same as the first disk
670 					 * in the set, don't put it on the
671 					 * skip list. This disk probably
672 					 * doesn't really belong in this set
673 					 * and we'll want to look at it again
674 					 * to figure out where it does belong.
675 					 */
676 					if ((d->mid_setcreatetimestamp.tv_sec !=
677 					    firstdisktime.tv_sec) ||
678 					    (d->mid_setcreatetimestamp.tv_usec
679 					    != firstdisktime.tv_usec))
680 						continue;
681 					skipt =
682 					    meta_drivenamelist_append_wrapper(
683 						skipt, d->mid_dnp);
684 				}
685 			}
686 		}
687 
688 skipdisk:
689 		;
690 	}
691 
692 	/*
693 	 * Now have entire list of disks associated with diskset including
694 	 * disks listed in mddb locator blocks and namespace. Before importing
695 	 * diskset need to recheck that none of these disks is already in use.
696 	 * If a disk is found that is already in use, print error and exit.
697 	 */
698 	if (!report_only) {
699 		md_im_set_desc_t	*p;
700 		md_im_drive_info_t	*d;
701 		mddrivename_t		*dnp;
702 
703 		for (p = misp; p != NULL; p = p->mis_next) {
704 			for (d = p->mis_drives; d != NULL; d = d->mid_next) {
705 				dnp = d->mid_dnp;
706 				if (meta_imp_drvused(sp, dnp, ep)) {
707 					(void) mddserror(ep,
708 						MDE_DS_DRIVEINUSE, 0, NULL,
709 						dnp->cname, NULL);
710 					mde_perror(ep, "");
711 					md_exit(sp, 0);
712 				}
713 			}
714 		}
715 	}
716 
717 	/*
718 	 * If there are no unconfigured sets, then our work here is done.
719 	 * Hopefully this is friendlier than just not printing anything at all.
720 	 */
721 	if (rscount == 0) {
722 		/*
723 		 * If we've found partial disksets but no complete disksets,
724 		 * we don't want this to print.
725 		 */
726 		if (!partial) {
727 			md_eprintf("%s\n", gettext("no unconfigured sets "
728 			    "detected"));
729 		}
730 		md_exit(sp, 0);
731 	}
732 
733 	/*
734 	 * We'll need this info for both the report content and the import
735 	 * decision.  By the time we're here, misp should NOT be NULL (or we
736 	 * would have exited in the rscount == 0 test above).
737 	 */
738 	assert(misp != NULL);
739 	if (misp->mis_next != NULL) {
740 		have_multiple_sets = 1;
741 	}
742 
743 	/*
744 	 * Generate the appropriate (verbose or not) report for all sets
745 	 * detected.  If we're planning on importing later, only include the
746 	 * "suggested import" command if multiple sets were detected.  (That
747 	 * way, when we error out later, we have still provided useful
748 	 * information.)
749 	 */
750 
751 	/*
752 	 * Now we should have all the unconfigured sets detected
753 	 * check for the overlapping
754 	 */
755 	if (have_multiple_sets) {
756 		overlap = set_disk_overlap(misp);
757 		if (!report_only) {
758 			md_eprintf("%s\n\n", gettext("multiple unconfigured "
759 			    "sets detected.\nRerun the command with the "
760 			    "suggested options for the desired set."));
761 		}
762 	}
763 
764 	if (verbose) {
765 	    report_verbose(misp, (report_only || have_multiple_sets), overlap);
766 	} else {
767 	    report_standard(misp, (report_only || have_multiple_sets), overlap);
768 	}
769 
770 	/*
771 	 * If it's a report-only request, we're done.  If it's an import
772 	 * request, make sure that we only have one entry in the set list.
773 	 */
774 
775 	if (report_only) {
776 		md_exit(sp, 0);
777 	} else if (have_multiple_sets) {
778 		md_exit(sp, 1);
779 	}
780 
781 	if (setname_new == NULL) {
782 		usage(sp, gettext("You must specify a new set name."));
783 	}
784 
785 	(void) meta_imp_set(misp, setname_new, force, dry_run, ep);
786 
787 	if (dry_run) {
788 		md_exit(sp, 0);
789 	}
790 
791 	if (!mdisok(ep)) {
792 		mde_perror(ep, "");
793 		md_exit(sp, 1);
794 	}
795 
796 	if ((sp = metasetname(setname_new, ep)) == NULL) {
797 		mde_perror(ep, "");
798 		md_exit(sp, 1);
799 	}
800 
801 	if (meta_lock_nowait(sp, ep) != 0) {
802 		mde_perror(ep, "");
803 		md_exit(sp, 10);	/* special errcode */
804 	}
805 
806 	if (meta_set_take(sp, &mhiargs, 0, 0, &status)) {
807 		mde_perror(&status, "");
808 		md_exit(sp, 1);
809 	}
810 
811 	md_exit(sp, 0);
812 	/*NOTREACHED*/
813 	return (0);
814 }
815