xref: /titanic_41/usr/src/cmd/lvm/util/metastat.c (revision 61961e0f20c7637a3846bb39786bb9dffa91dfb9)
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 #include <stdio.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <string.h>
33 
34 #include <meta.h>
35 #include <sys/lvm/md_mddb.h>
36 #include <sdssc.h>
37 
38 /*
39  * print metadevice status
40  */
41 
42 
43 #define	MD_PROBE_OPEN_T "probe open test"
44 
45 /* used to keep track of the softparts on the same underlying device */
46 struct sp_base_list {
47 	struct sp_base_list	*next;
48 	char			*base;
49 };
50 
51 /*
52  * Function prototypes
53  */
54 static void probe_all_devs(mdsetname_t *sp);
55 
56 static int print_devid(mdsetname_t *sp, mdnamelist_t *nlp, FILE *fp,
57     md_error_t   *ep);
58 
59 static md_common_t	*get_concise_unit(mdsetname_t *sp, mdname_t *np,
60 			    md_error_t *ep);
61 static void	print_all_sets(mdprtopts_t options, int concise_flag,
62 		    int quiet_flg);
63 static void	print_specific_set(mdsetname_t *sp, mdprtopts_t options,
64 		    int concise_flag, int quiet_flg);
65 static void	print_concise_diskset(mdsetname_t *sp);
66 static void	print_concise_namelist(mdsetname_t *sp, mdnamelist_t **nl,
67 		    char mtype);
68 static void	print_concise_md(int indent, mdsetname_t *sp, mdname_t *np);
69 static void	print_concise_mirror(int indent, mdsetname_t *sp,
70 		    md_mirror_t *mirror);
71 static void	print_concise_raid(int indent, mdsetname_t *sp,
72 		    md_raid_t *raid);
73 static void	print_concise_stripe(int indent, mdsetname_t *sp,
74 		    md_stripe_t *stripe);
75 static void	print_concise_sp(int indent, mdsetname_t *sp, md_sp_t *part);
76 static void	print_concise_trans(int indent, mdsetname_t *sp,
77 		    md_trans_t *trans);
78 static void	print_concise_entry(int indent, char *name, diskaddr_t size,
79 		    char mtype);
80 static void	free_names(mdnamelist_t **nlp);
81 static char	*get_sm_state(md_mirror_t *mirror, int i,
82 		    md_status_t mirror_status, uint_t tstate);
83 static char	*get_raid_col_state(md_raidcol_t *colp, uint_t tstate);
84 static char	*get_stripe_state(md_comp_t *mdcp, uint_t tstate);
85 static char	*get_hs_state(md_hs_t *hsp);
86 static char	*strip_blanks(char *s);
87 static struct sp_base_list *sp_add_done(md_sp_t *part, struct sp_base_list *lp);
88 static int	sp_done(md_sp_t *part, struct sp_base_list *lp);
89 static int	sp_match(md_sp_t *part, struct sp_base_list *lp);
90 static void	sp_free_list(struct sp_base_list *lp);
91 
92 #define	INDENT		4
93 #define	NAMEWIDTH	16
94 
95 /*
96  * print named hotspare pool or metadevice
97  */
98 static int
99 print_name(
100 	mdsetname_t	**spp,
101 	char		*uname,
102 	mdnamelist_t	**nlistpp,
103 	char		*fname,
104 	FILE		*fp,
105 	mdprtopts_t	options,
106 	int		*meta_print_trans_msgp,
107 	mdnamelist_t	**lognlpp,
108 	md_error_t	*ep
109 )
110 {
111 	mdname_t	*namep;
112 	char		*miscname;
113 
114 	/* recurse */
115 	options |= PRINT_SUBDEVS;
116 
117 	/* hotspare pool */
118 	if (is_hspname(uname)) {
119 		mdhspname_t	*hspnamep;
120 
121 		/* get hotsparepool */
122 		if ((hspnamep = metahspname(spp, uname, ep)) == NULL)
123 			return (-1);
124 
125 		/* check for ownership */
126 		assert(*spp != NULL);
127 		if (meta_check_ownership(*spp, ep) != 0)
128 			return (-1);
129 
130 		/* print hotspare pool */
131 		return (meta_hsp_print(*spp, hspnamep, lognlpp, fname, fp,
132 		    options, ep));
133 	}
134 
135 	/* get metadevice */
136 	if (((namep = metaname(spp, uname, ep)) == NULL) ||
137 	    (metachkmeta(namep, ep) != 0))
138 		return (-1);
139 
140 	/* check for ownership */
141 	assert(*spp != NULL);
142 	if (meta_check_ownership(*spp, ep) != 0)
143 		return (-1);
144 
145 	if ((miscname = metagetmiscname(namep, ep)) != NULL) {
146 		if (strcmp(miscname, MD_TRANS) == 0) {
147 			*meta_print_trans_msgp = 1;
148 		}
149 	}
150 
151 	/* print metadevice */
152 	return (meta_print_name(*spp, namep, nlistpp, fname, fp, options,
153 	    lognlpp, ep));
154 }
155 
156 /*
157  * print the per set flags
158  */
159 /*ARGSUSED*/
160 static int
161 print_setstat(
162 	mdsetname_t		**spp,
163 	char			*fname,
164 	FILE			*fp,
165 	mdprtopts_t		options,
166 	md_error_t		*ep
167 )
168 {
169 	int			rval = -1;
170 	char			*cp = NULL;
171 	md_gs_stat_parm_t	gsp;
172 
173 	/* create local set, if necessary */
174 	if (*spp == NULL) {
175 		if (fname != NULL && (cp = strchr(fname, '/')) != NULL)
176 			*cp = '\0';
177 
178 		if (fname != NULL && (*spp = metasetname(fname, ep)) != NULL) {
179 			*cp = '/';
180 		} else {
181 			if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)
182 				return (rval);
183 		}
184 	}
185 
186 	if ((cp = getenv("MD_DEBUG")) == NULL)
187 		return (0);
188 
189 	if (strstr(cp, "SETINFO") == NULL)
190 		return (0);
191 
192 	(void) memset(&gsp, '\0', sizeof (md_gs_stat_parm_t));
193 	gsp.gs_setno = (*spp)->setno;
194 
195 	if (metaioctl(MD_GET_SETSTAT, &gsp, &gsp.gs_mde, NULL) != 0)
196 		return (mdstealerror(ep, &gsp.gs_mde));
197 
198 	if (fprintf(fp, "Status for set %d = ", gsp.gs_setno) == EOF)
199 		goto out;
200 
201 	if (meta_prbits(fp, NULL, gsp.gs_status, MD_SET_STAT_BITS) == EOF)
202 		goto out;
203 
204 
205 	if (fprintf(fp, "\n") == EOF)
206 		goto out;
207 
208 	/* success */
209 	rval = 0;
210 
211 	/* cleanup, return error */
212 out:
213 	if (rval != 0)
214 		(void) mdsyserror(ep, errno, fname);
215 
216 	return (rval);
217 }
218 
219 /*
220  * check_replica_state:
221  * 	If the replica state is stale or the set has been halted
222  * 	this routine returns an error.
223  */
224 static int
225 check_replica_state(mdsetname_t *sp, md_error_t *ep)
226 {
227 	mddb_config_t	c;
228 
229 	(void) memset(&c, 0, sizeof (c));
230 	c.c_id = 0;
231 	c.c_setno = sp->setno;
232 
233 	if (metaioctl(MD_DB_GETDEV, &c, &c.c_mde, NULL) != 0) {
234 		if (mdismddberror(&c.c_mde, MDE_DB_INVALID))
235 			mdstealerror(ep, &c.c_mde);
236 		return (-1);
237 	}
238 
239 	if (c.c_flags & MDDB_C_STALE) {
240 		return (mdmddberror(ep, MDE_DB_STALE, NODEV32, sp->setno,
241 			0, NULL));
242 	} else
243 		return (0);
244 }
245 
246 static void
247 print_trans_msg(mdprtopts_t	options, int	meta_print_trans_msg)
248 {
249 	if (meta_print_trans_msg != 0) {
250 		fprintf(stderr, "\n\n");
251 		if (options & PRINT_SHORT) {
252 			fprintf(stderr, gettext(MD_SHORT_EOF_TRANS_MSG));
253 			fprintf(stderr, gettext(MD_SHORT_EOF_TRANS_WARNING));
254 		} else {
255 			fprintf(stderr, gettext(MD_EOF_TRANS_MSG));
256 			fprintf(stderr, gettext(MD_EOF_TRANS_WARNING));
257 		}
258 	}
259 }
260 
261 /*
262  * print usage message
263  *
264  */
265 static void
266 usage(
267 	mdsetname_t	*sp,
268 	int		eval
269 )
270 {
271 	(void) fprintf(stderr, gettext("\
272 usage:	%s [-s setname] [-a][-c][-B][-r][-i][-p] [-t] [metadevice...]\n"),
273 	    myname);
274 	md_exit(sp, eval);
275 }
276 
277 /*
278  * mainline. crack command line arguments.
279  */
280 int
281 main(
282 	int	argc,
283 	char	*argv[]
284 )
285 {
286 	char		*sname = NULL;
287 	mdsetname_t	*sp = NULL;
288 	mdprtopts_t	options = PRINT_HEADER | PRINT_DEVID | PRINT_FAST;
289 	int		c;
290 	char		*p;
291 	md_error_t	status = mdnullerror;
292 	md_error_t	*ep = &status;
293 	int		eval = 0;
294 	int		inquire = 0;
295 	int		quiet_flg = 0;
296 	int		error;
297 	int		all_sets_flag = 0;
298 	int		concise_flag = 0;
299 	mdnamelist_t	*nlistp = NULL;
300 	mdname_t		*namep;
301 	int		devcnt = 0;
302 	mdnamelist_t	*lognlp = NULL;
303 	uint_t hsi;
304 	int		meta_print_trans_msg = 0;
305 
306 	/*
307 	 * Get the locale set up before calling any other routines
308 	 * with messages to ouput.  Just in case we're not in a build
309 	 * environment, make sure that TEXT_DOMAIN gets set to
310 	 * something.
311 	 */
312 #if !defined(TEXT_DOMAIN)
313 #define	TEXT_DOMAIN "SYS_TEST"
314 #endif
315 	(void) setlocale(LC_ALL, "");
316 	(void) textdomain(TEXT_DOMAIN);
317 
318 	if (sdssc_bind_library() == SDSSC_OKAY)
319 		if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
320 			&error) == SDSSC_PROXY_DONE)
321 			exit(error);
322 
323 	/* initialize */
324 	if (md_init(argc, argv, 0, 1, ep) != 0) {
325 		mde_perror(ep, "");
326 		md_exit(sp, 1);
327 	}
328 
329 	/* parse arguments */
330 	optind = 1;
331 	opterr = 1;
332 	while ((c = getopt(argc, argv, "acSs:hpBrtiq?")) != -1) {
333 		switch (c) {
334 		case 'a':
335 			all_sets_flag++;
336 			break;
337 
338 		case 'c':
339 			concise_flag++;
340 			quiet_flg++;
341 			break;
342 
343 		case 'S':
344 			options |= PRINT_SETSTAT_ONLY;
345 			break;
346 
347 		case 's':
348 			sname = optarg;
349 			break;
350 
351 		case 'h':
352 			usage(sp, 0);
353 			break;
354 
355 		case 'p':
356 			options |= PRINT_SHORT;
357 			options &= ~PRINT_DEVID;
358 			break;
359 
360 		case 't':
361 			options |= PRINT_TIMES;
362 			break;
363 
364 		case 'i':
365 			inquire++;
366 			break;
367 
368 		case 'B':
369 			options |= PRINT_LARGEDEVICES;
370 			break;
371 		case 'r':		/* defunct option */
372 			break;
373 		case 'q':
374 			quiet_flg++;
375 			break;
376 		case '?':
377 			if (optopt == '?')
378 				usage(sp, 0);
379 			/*FALLTHROUGH*/
380 		default:
381 			usage(sp, 1);
382 			break;
383 		}
384 	}
385 	argc -= optind;
386 	argv += optind;
387 
388 	if (sname != NULL) {
389 
390 		if (all_sets_flag) {
391 			fprintf(stderr, gettext("metastat: "
392 			    "incompatible options: -a and -s\n"));
393 			usage(sp, 1);
394 		}
395 
396 		if ((sp = metasetname(sname, ep)) == NULL) {
397 			mde_perror(ep, "");
398 			md_exit(sp, 1);
399 		}
400 	} else {
401 		if ((sp = metasetname(MD_LOCAL_NAME, ep)) == NULL) {
402 			mde_perror(ep, "");
403 			md_exit(sp, 1);
404 		}
405 	}
406 
407 	/* make sure that the mddb is not stale. Else print a warning */
408 
409 	if (check_replica_state(sp, ep)) {
410 		if (mdismddberror(ep, MDE_DB_STALE)) {
411 			fprintf(stdout, gettext(
412 			    "****\nWARNING: Stale "
413 			    "state database replicas. Metastat output "
414 			    "may be inaccurate.\n****\n\n"));
415 		}
416 	}
417 
418 	/* if inquire is set. We probe first */
419 	if (inquire) {
420 		if (geteuid() != 0) {
421 			fprintf(stderr, gettext("metastat: -i "
422 				"option requires super-user privilages\n"));
423 			md_exit(sp, 1);
424 		}
425 		probe_all_devs(sp);
426 	}
427 	/* print debug stuff */
428 	if (((p = getenv("MD_DEBUG")) != NULL) &&
429 	    (strstr(p, "STAT") != NULL)) {
430 		options |= (PRINT_SETSTAT | PRINT_DEBUG | PRINT_TIMES);
431 	}
432 
433 	if ((options & PRINT_SETSTAT) || (options & PRINT_SETSTAT_ONLY)) {
434 		if (print_setstat(&sp, argv[0], stdout, options, ep)) {
435 			mde_perror(ep, "");
436 			md_exit(sp, 1);
437 		}
438 		if (options & PRINT_SETSTAT_ONLY)
439 			md_exit(sp, 0);
440 	}
441 
442 	/* status all devices */
443 	if (argc == 0) {
444 		if (all_sets_flag) {
445 		    print_all_sets(options, concise_flag, quiet_flg);
446 		} else {
447 		    print_specific_set(sp, options, concise_flag, quiet_flg);
448 		}
449 
450 		if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) {
451 			mde_perror(ep, "");
452 			md_exit(sp, 1);
453 		}
454 
455 		/* success */
456 		md_exit(sp, 0);
457 	}
458 	/* print named device types */
459 	while (devcnt < argc) {
460 		char	*uname = argv[devcnt];
461 
462 		if (concise_flag) {
463 		    mdname_t *np;
464 
465 		    np = metaname(&sp, uname, ep);
466 		    if (np == NULL) {
467 			mde_perror(ep, "");
468 			mdclrerror(ep);
469 		    } else {
470 			print_concise_md(0, sp, np);
471 		    }
472 
473 		} else {
474 		    if (print_name(&sp, uname, &nlistp, NULL, stdout, options,
475 			&meta_print_trans_msg, &lognlp, ep) != 0) {
476 			    mde_perror(ep, "");
477 			    mdclrerror(ep);
478 			    eval = 1;
479 		    }
480 		}
481 		++devcnt;
482 	}
483 
484 	/* print metadevice & relocation device id */
485 	if ((options & PRINT_DEVID) && (eval != 1) && !quiet_flg) {
486 		devcnt = 0;
487 
488 		while (devcnt < argc) {
489 			char	*uname = argv[devcnt];
490 
491 			/* hotspare pools */
492 			if (is_hspname(uname)) {
493 				mdhspname_t	*hspnamep;
494 				md_hsp_t	*hsp;
495 
496 				/* get hotsparepool */
497 				if ((hspnamep = metahspname(&sp, uname,
498 					ep)) == NULL)
499 					eval = 1;
500 
501 				if ((hsp = meta_get_hsp(sp, hspnamep,
502 					ep)) == NULL)
503 					eval = 1;
504 
505 				for (hsi = 0;
506 				    hsi < hsp->hotspares.hotspares_len;
507 				    hsi++) {
508 
509 					namep = hsp->hotspares.
510 					    hotspares_val[hsi].hsnamep;
511 
512 					if (!(options & PRINT_LARGEDEVICES)) {
513 						/* meta_getdevs populates the */
514 						/* nlistp structure for use   */
515 						if (meta_getdevs(sp, namep,
516 						    &nlistp, ep) != 0)
517 							eval =  1;
518 					}
519 
520 				}
521 
522 			} else {
523 
524 				/* get metadevice */
525 				if (((namep = metaname(&sp, uname,
526 					ep)) == NULL) ||
527 					(metachkmeta(namep, ep) != 0))
528 					eval = 1;
529 
530 				if (!(options & PRINT_LARGEDEVICES)) {
531 					/* meta_getdevs populates the	*/
532 					/* nlistp structure for use 	*/
533 					if (meta_getdevs(sp, namep, &nlistp, ep)
534 					    != 0)
535 						eval =  1;
536 				}
537 			}
538 
539 			++devcnt;
540 		}
541 		if (print_devid(sp, nlistp, stdout, ep) != 0)
542 			eval =  1;
543 
544 
545 	}
546 
547 	print_trans_msg(options, meta_print_trans_msg);
548 
549 	if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) {
550 		mde_perror(ep, "");
551 		md_exit(sp, 1);
552 	}
553 
554 	/* return success */
555 	md_exit(sp, eval);
556 	/*NOTREACHED*/
557 	return (eval);
558 }
559 
560 static void
561 print_all_sets(mdprtopts_t options, int concise_flag, int quiet_flg)
562 {
563 	uint_t		max_sets;
564 	md_error_t	error = mdnullerror;
565 	int		i;
566 
567 	if ((max_sets = get_max_sets(&error)) == 0) {
568 	    return;
569 	}
570 
571 	if (!mdisok(&error)) {
572 	    mdclrerror(&error);
573 	    return;
574 	}
575 
576 	/* for each possible set number, see if we really have a diskset */
577 	for (i = 0; i < max_sets; i++) {
578 	    mdsetname_t		*sp;
579 
580 	    if ((sp = metasetnosetname(i, &error)) == NULL) {
581 		if (!mdisok(&error) &&
582 		    mdisrpcerror(&error, RPC_PROGNOTREGISTERED)) {
583 		    /* metad rpc program not registered - no metasets */
584 		    break;
585 		}
586 
587 		mdclrerror(&error);
588 		continue;
589 	    }
590 	    mdclrerror(&error);
591 
592 	    if (meta_check_ownership(sp, &error) == 0) {
593 		/* we own the set, so we can print the metadevices */
594 		print_specific_set(sp, options, concise_flag, quiet_flg);
595 		(void) printf("\n");
596 	    }
597 
598 	    metaflushsetname(sp);
599 	}
600 }
601 
602 static void
603 print_specific_set(mdsetname_t *sp, mdprtopts_t options, int concise_flag,
604 	int quiet_flg)
605 {
606 	md_error_t	status = mdnullerror;
607 	md_error_t	*ep = &status;
608 	int		meta_print_trans_msg = 0;
609 
610 	/* default to local set */
611 	if ((sp == NULL) && ((sp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) {
612 	    mde_perror(ep, "");
613 	    md_exit(sp, 1);
614 	}
615 
616 	/* check for ownership */
617 	assert(sp != NULL);
618 	if (meta_check_ownership(sp, ep) != 0) {
619 	    mde_perror(ep, "");
620 	    md_exit(sp, 1);
621 	}
622 
623 	if (concise_flag) {
624 	    print_concise_diskset(sp);
625 
626 	} else {
627 	    mdnamelist_t	*nlistp = NULL;
628 
629 	    /* status devices */
630 	    if (meta_print_all(sp, NULL, &nlistp, stdout, options,
631 		&meta_print_trans_msg, ep) != 0) {
632 		mde_perror(ep, "");
633 		md_exit(sp, 1);
634 	    }
635 
636 	    /* print relocation device id on all dev's */
637 	    if ((options & PRINT_DEVID) && !quiet_flg) {
638 		/*
639 		 * Ignore return value from meta_getalldevs since
640 		 * it will return a failure if even one device cannot
641 		 * be found - which could occur in the case of device
642 		 * failure or a device being powered off during
643 		 * upgrade.  Even if meta_getalldevs fails, the
644 		 * data in nlistp is still valid.
645 		 */
646 		if (!(options & PRINT_LARGEDEVICES)) {
647 		    (void) meta_getalldevs(sp, &nlistp, 0, ep);
648 		}
649 		if (nlistp != NULL) {
650 		    if (print_devid(sp, nlistp, stdout, ep) != 0) {
651 			mde_perror(ep, "");
652 			md_exit(sp, 1);
653 		    }
654 		}
655 	    }
656 	}
657 
658 	print_trans_msg(options, meta_print_trans_msg);
659 }
660 
661 /*
662  * print_devid prints out cxtxdx and devid for devices passed in a
663  * mdnamelist_t structure
664  */
665 static int
666 print_devid(
667 	mdsetname_t  *sp,
668 	mdnamelist_t *nlp,
669 	FILE		 *fp,
670 	md_error_t   *ep
671 )
672 {
673 	int 			retval = 0;
674 	mdnamelist_t		*onlp = NULL;
675 	mddevid_t 		*ldevidp = NULL;
676 	mddevid_t		*nextp;
677 
678 	/* make a non-duplicate list of nlp */
679 	for (onlp = nlp; (onlp != NULL); onlp = onlp->next) {
680 		meta_create_non_dup_list(onlp->namep, &ldevidp);
681 	}
682 
683 	retval = meta_print_devid(sp, fp, ldevidp, ep);
684 
685 	/* cleanup */
686 	for (nextp = ldevidp; nextp != NULL; ldevidp = nextp) {
687 		Free(ldevidp->ctdname);
688 		nextp = ldevidp->next;
689 		Free(ldevidp);
690 	}
691 
692 	return (retval);
693 }
694 
695 /*
696  * probedev issues ioctls for all the metadevices
697  */
698 
699 
700 
701 
702 /*
703  * Failure return's a 1
704  */
705 int
706 hotspare_ok(char *bname)
707 {
708 	int fd;
709 	char buf[512];
710 
711 	if ((fd = open(bname, O_RDONLY)) < 0)
712 		return (0);
713 	if (read(fd, buf, sizeof (buf)) < 0)
714 		return (0);
715 	return (1);
716 }
717 
718 void
719 delete_hotspares_impl(mdsetname_t *sp, mdhspname_t *hspnp, md_hsp_t *hspp)
720 {
721 	md_hs_t *hsp;
722 	uint_t		hsi;
723 	char    *bname;
724 	md_error_t e = mdnullerror;
725 	int deleted_hs = 0;
726 
727 	for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
728 		mdnamelist_t *nlp;
729 
730 		hsp = &hspp->hotspares.hotspares_val[hsi];
731 		bname = hsp->hsnamep->bname;
732 		nlp = NULL;
733 		metanamelist_append(&nlp, hsp->hsnamep);
734 		/* print hotspare */
735 		if (hsp->state == HSS_AVAILABLE) {
736 			if (hotspare_ok(bname))
737 				continue;
738 
739 			fprintf(stderr,
740 				"NOTICE: Hotspare %s in %s has failed.\n"
741 				"\tDeleting %s since it not in use\n\n",
742 				bname, hspnp->hspname, bname);
743 
744 			if (meta_hs_delete(sp, hspnp, nlp, 0, &e) != NULL) {
745 				mde_perror(&e, "");
746 			} else {
747 				deleted_hs++;
748 			}
749 		}
750 	}
751 }
752 
753 
754 
755 /*
756  * Generic routine to issue ioctls
757  */
758 
759 void
760 md_setprobetest(md_probedev_t *iocp)
761 {
762 	(void) strcpy(iocp->test_name, MD_PROBE_OPEN_T);
763 }
764 
765 int
766 md_probe_ioctl(mdsetname_t *sp, mdnamelist_t *nlp, int ndevs, char *drvname)
767 {
768 	mdnamelist_t	*p;
769 	mdname_t	*np;
770 	md_probedev_t	probe_ioc,
771 			*iocp;
772 	int		i,
773 			retval = 0;
774 	/*
775 	 * Allocate space for all the metadevices and fill in
776 	 * the minor numbers.
777 	 */
778 
779 	memset(&probe_ioc, 0, sizeof (probe_ioc));
780 	iocp = &probe_ioc;
781 
782 	if ((iocp->mnum_list = (uintptr_t)calloc(ndevs, sizeof (minor_t)))
783 	    == 0) {
784 		perror("md_probe_ioctl: calloc");
785 		return (-1);
786 	}
787 
788 	MD_SETDRIVERNAME(iocp, drvname, sp->setno);
789 	md_setprobetest(iocp);
790 
791 	iocp->nmdevs = ndevs;
792 
793 	for (p = nlp, i = 0; p; p = p->next, i++) {
794 		np = p->namep;
795 		((minor_t *)(uintptr_t)iocp->mnum_list)[i] =
796 		    meta_getminor(np->dev);
797 	}
798 
799 
800 	if (metaioctl(MD_IOCPROBE_DEV, iocp, &(iocp->mde), NULL) != 0)
801 			retval = -1;
802 	return (retval);
803 }
804 /*
805  *
806  *  - remove p from nlp list
807  *  - put it on the toplp list.
808  *  - update the p to the next element
809  */
810 
811 void
812 add_to_list(mdnamelist_t **curpp, mdnamelist_t **prevpp, mdnamelist_t **newlpp)
813 {
814 	mdnamelist_t	*p, *prevp, *nlp;
815 
816 	p = *curpp;
817 	prevp = *prevpp;
818 	nlp = *newlpp;
819 
820 	if (prevp == p) {
821 		/* if first element reset prevp */
822 			prevp = p->next;
823 			p->next = nlp;
824 			nlp = p;
825 			p = prevp;
826 	} else {
827 		prevp->next = p->next;
828 		p->next = nlp;
829 		nlp = p;
830 		p = prevp->next;
831 	}
832 	*curpp = p;
833 	*prevpp = prevp;
834 	*newlpp = nlp;
835 }
836 /*
837  * Scans the given list of metadeivces and returns a list of top level
838  * metadevices.
839  * Note: The orignal list is not valid at the end and is set to NULL.
840  */
841 
842 int
843 get_toplevel_mds(mdsetname_t *sp, mdnamelist_t **lpp,
844 			mdnamelist_t **top_pp)
845 {
846 	mdnamelist_t	*p, *prevp, *toplp;
847 	int		ntopmd;
848 	md_common_t	*mdp;
849 	md_error_t	e = mdnullerror;
850 
851 	ntopmd = 0;
852 	prevp = p = *lpp;
853 	toplp = NULL;
854 
855 	while (p) {
856 		if ((mdp = meta_get_unit(sp, p->namep, &e)) == NULL) {
857 				prevp = p;
858 				p = p->next;
859 				continue;
860 		}
861 
862 		if (mdp->parent == MD_NO_PARENT) {
863 			/* increment the top level md count. */
864 			ntopmd++;
865 			add_to_list(&p, &prevp, &toplp);
866 		} else {
867 			prevp = p;
868 			p = p->next;
869 		}
870 	}
871 	*lpp = NULL;
872 	*top_pp = toplp;
873 
874 	return (ntopmd);
875 }
876 
877 int
878 get_namelist(mdnamelist_t **transdevlist, mdnamelist_t **devlist,
879 					char *dev_type)
880 {
881 	mdnamelist_t *np, *prevp;
882 	md_error_t	e = mdnullerror;
883 	char		*type_name;
884 	int		i = 0;
885 
886 	prevp = np = *transdevlist;
887 	while (np) {
888 		if ((type_name = metagetmiscname(np->namep, &e)) == NULL) {
889 			*devlist = NULL;
890 			return (-1);
891 		}
892 		if (strcmp(type_name, dev_type) == 0) {
893 			/* move it to the devlist */
894 			add_to_list(&np, &prevp, devlist);
895 			i++;
896 		} else {
897 			prevp = np;
898 			np = np->next;
899 		}
900 	}
901 	return (i);
902 }
903 
904 
905 mdnamelist_t *
906 create_nlp(mdsetname_t *sp)
907 {
908 	mdnamelist_t *np;
909 	md_error_t   e = mdnullerror;
910 
911 	if (np = (mdnamelist_t *)malloc(sizeof (mdnamelist_t))) {
912 		np->next = NULL;
913 		return (np);
914 	} else {
915 		/* error condition below */
916 		mde_perror(&e, "create_nlp: malloc failed\n");
917 		md_exit(sp, 1);
918 	}
919 	return (0);
920 }
921 
922 /*
923  * Create a list of metadevices associated with trans. top_pp points to
924  * this list. The number of components in the list are also returned.
925  */
926 int
927 create_trans_compslist(mdsetname_t *sp, mdnamelist_t **lpp,
928 				mdnamelist_t **top_pp)
929 {
930 	mdnamelist_t	*p, *tailp, *toplp, *newlp;
931 	int		ntoptrans;
932 	md_error_t	e = mdnullerror;
933 	md_trans_t	*tp;
934 
935 	ntoptrans = 0;
936 	p = *lpp;
937 	tailp = toplp = NULL;
938 	/*
939 	 * Scan the current list of trans devices. From that
940 	 * extract all the lower level metadevices and put them on
941 	 * toplp list.
942 	 */
943 
944 	while (p) {
945 		if (tp = meta_get_trans(sp, p->namep, &e)) {
946 			/*
947 			 * Check the master and log devices to see if they
948 			 * are metadevices
949 			 */
950 			if (metaismeta(tp->masternamep)) {
951 				/* get a mdnamelist_t. */
952 				newlp = create_nlp(sp);
953 				newlp->namep = tp->masternamep;
954 				if (toplp == NULL) {
955 					toplp = tailp = newlp;
956 				} else {
957 					tailp->next = newlp;
958 					tailp = newlp;
959 				}
960 				ntoptrans++;
961 			}
962 
963 			if (tp->lognamep && metaismeta(tp->lognamep)) {
964 				newlp = create_nlp(sp);
965 				newlp->namep = tp->lognamep;
966 				if (toplp == NULL) {
967 					toplp = tailp = newlp;
968 				} else {
969 					tailp->next = newlp;
970 					tailp = newlp;
971 				}
972 				ntoptrans++;
973 			}
974 			p = p->next;
975 		}
976 	}
977 	*top_pp = toplp;
978 	return (ntoptrans);
979 }
980 
981 void
982 probe_mirror_devs(mdsetname_t *sp)
983 {
984 	mdnamelist_t	*nlp, *toplp;
985 	int		cnt;
986 	md_error_t	e = mdnullerror;
987 
988 	nlp = toplp = NULL;
989 
990 	if (meta_get_mirror_names(sp, &nlp, 0, &e) > 0) {
991 		/*
992 		 * We have some mirrors to probe
993 		 * get a list of top-level mirrors
994 		 */
995 
996 		cnt = get_toplevel_mds(sp, &nlp, &toplp);
997 		if (cnt && (md_probe_ioctl(sp, toplp, cnt, MD_MIRROR) < 0))
998 				perror("MD_IOCPROBE_DEV");
999 	}
1000 	metafreenamelist(nlp);
1001 	metafreenamelist(toplp);
1002 
1003 }
1004 
1005 void
1006 probe_raid_devs(mdsetname_t *sp)
1007 {
1008 	mdnamelist_t	*nlp, *toplp;
1009 	int		cnt;
1010 	md_error_t	e = mdnullerror;
1011 
1012 	nlp = toplp = NULL;
1013 
1014 	if (meta_get_raid_names(sp, &nlp, 0, &e) > 0) {
1015 		/*
1016 		 * We have some mirrors to probe
1017 		 * get a list of top-level mirrors
1018 		 */
1019 
1020 		cnt = get_toplevel_mds(sp, &nlp, &toplp);
1021 
1022 		if (cnt && (md_probe_ioctl(sp, toplp, cnt, MD_RAID) < 0))
1023 			perror("MD_IOCPROBE_DEV");
1024 	}
1025 	metafreenamelist(nlp);
1026 	metafreenamelist(toplp);
1027 }
1028 
1029 /*
1030  * Trans probes are diffenent. -- so whats new.
1031  * we separate out the master and log device and then issue the
1032  * probe calls.
1033  * Since the underlying device could be disk, stripe, RAID or miror,
1034  * we have to sort them out and then call the ioctl for each.
1035  */
1036 
1037 void
1038 probe_trans_devs(mdsetname_t *sp)
1039 {
1040 	mdnamelist_t	*nlp, *toplp;
1041 	mdnamelist_t	*trans_raidlp, *trans_mmlp, *trans_stripelp;
1042 	int		cnt;
1043 	md_error_t	e = mdnullerror;
1044 
1045 	nlp = toplp = NULL;
1046 	trans_raidlp = trans_mmlp = trans_stripelp = NULL;
1047 
1048 	if (meta_get_trans_names(sp, &nlp, 0, &e) > 0) {
1049 		/*
1050 		 * get a list of master and log metadevices.
1051 		 */
1052 
1053 		cnt = create_trans_compslist(sp, &nlp, &toplp);
1054 
1055 		/* underlying RAID-5 components */
1056 
1057 		cnt = get_namelist(&toplp, &trans_raidlp, MD_RAID);
1058 		if ((cnt > 0) && (md_probe_ioctl(sp, trans_raidlp, cnt,
1059 		    MD_RAID) < 0))
1060 			perror("MD_IOCPROBE_DEV");
1061 
1062 		metafreenamelist(trans_raidlp);
1063 
1064 		/* underlying mirror components */
1065 
1066 		cnt = get_namelist(&toplp, &trans_mmlp, MD_MIRROR);
1067 
1068 		if ((cnt > 0) && (md_probe_ioctl(sp, trans_mmlp, cnt,
1069 		    MD_MIRROR) < 0))
1070 			perror("MD_IOCPROBE_DEV");
1071 
1072 		metafreenamelist(trans_mmlp);
1073 
1074 		/* underlying stripe components */
1075 
1076 		cnt = get_namelist(&toplp, &trans_stripelp, MD_STRIPE);
1077 		if ((cnt > 0) && (md_probe_ioctl(sp, trans_stripelp, cnt,
1078 		    MD_STRIPE) < 0))
1079 			perror("MD_IOCPROBE_DEV");
1080 		metafreenamelist(trans_stripelp);
1081 		metafreenamelist(nlp);
1082 	}
1083 }
1084 
1085 /*
1086  * probe hot spares. This is differs from other approaches since
1087  * there are no read/write routines through md. We check at the physical
1088  * component level and then delete it if its bad.
1089  */
1090 
1091 void
1092 probe_hotspare_devs(mdsetname_t *sp)
1093 {
1094 	mdhspnamelist_t *hspnlp = NULL;
1095 	int		cnt;
1096 	mdhspnamelist_t	*p;
1097 	md_hsp_t	*hspp;
1098 	md_error_t	e = mdnullerror;
1099 
1100 	if ((cnt = meta_get_hsp_names(sp, &hspnlp, 0, &e)) < 0) {
1101 		mderror(&e, MDE_UNIT_NOT_FOUND, NULL);
1102 		return;
1103 	} else if (cnt == 0) {
1104 		mderror(&e, MDE_NO_HSPS, NULL);
1105 		return;
1106 	}
1107 	for (p = hspnlp; (p != NULL); p = p->next) {
1108 		mdhspname_t	*hspnp = p->hspnamep;
1109 
1110 
1111 		if ((hspp = meta_get_hsp(sp, hspnp, &e)) == NULL)
1112 			continue;
1113 
1114 		if (hspp->hotspares.hotspares_len != 0) {
1115 			delete_hotspares_impl(sp, hspnp, hspp);
1116 		}
1117 	}
1118 	metafreehspnamelist(hspnlp);
1119 }
1120 
1121 static void
1122 probe_all_devs(mdsetname_t *sp)
1123 {
1124 	probe_hotspare_devs(sp);
1125 	probe_mirror_devs(sp);
1126 	probe_raid_devs(sp);
1127 	probe_trans_devs(sp);
1128 }
1129 
1130 /*
1131  * The following functions are used to print the concise output
1132  * of the metastat coommand (-c option).
1133  *
1134  * Normally the output for metastat is performed within libmeta via
1135  * the *_report functions within each of the metadevice specific files in
1136  * libmeta.  However, it is usually bad architecture for a library to
1137  * perform output since there are so many different ways that an application
1138  * can choose to do output (e.g. GUI, CLI, CIM, SNMP, etc.).  So, for the
1139  * concise output option we have moved the CLI output to the metastat
1140  * code and just use libmeta as the source of data to be printed.
1141  *
1142  * This function gets all of the different top-level metadevices in the set
1143  * and prints them.  It calls the print_concise_md() function to recursively
1144  * print the metadevices that underly the top-level metadevices.  It does
1145  * special handling for soft partitions so that all of the SPs on the
1146  * same underlying device are grouped and then that underlying device
1147  * is only printed once.
1148  */
1149 static void
1150 print_concise_diskset(mdsetname_t *sp)
1151 {
1152 	md_error_t		error = mdnullerror;
1153 	mdnamelist_t		*nl = NULL;
1154 	mdhspnamelist_t		*hsp_list = NULL;
1155 
1156 	/*
1157 	 * We do extra handling for soft parts since we want to find
1158 	 * all of the SPs on the same underlying device, group them and
1159 	 * print them together before printing the underlying device just
1160 	 * once.  This logic doesn't apply to any other metadevice type.
1161 	 */
1162 	if (meta_get_sp_names(sp, &nl, 0, &error) >= 0) {
1163 	    mdnamelist_t	*nlp;
1164 	    /* keep track of the softparts on the same underlying device */
1165 	    struct sp_base_list	*base_list = NULL;
1166 
1167 	    for (nlp = nl; nlp != NULL; nlp = nlp->next) {
1168 		mdname_t	*mdn;
1169 		md_sp_t		*soft_part;
1170 		mdnamelist_t	*tnlp;
1171 
1172 		mdn = metaname(&sp, nlp->namep->cname, &error);
1173 		mdclrerror(&error);
1174 		if (mdn == NULL) {
1175 		    print_concise_entry(0, nlp->namep->cname, 0, 'p');
1176 		    printf("\n");
1177 		    continue;
1178 		}
1179 
1180 		soft_part = meta_get_sp_common(sp, mdn, 1, &error);
1181 		mdclrerror(&error);
1182 
1183 		if (soft_part == NULL ||
1184 		    MD_HAS_PARENT(soft_part->common.parent) ||
1185 		    sp_done(soft_part, base_list))
1186 		    continue;
1187 
1188 		/* print this soft part */
1189 		print_concise_entry(0, soft_part->common.namep->cname,
1190 		    soft_part->common.size, 'p');
1191 		(void) printf(" %s\n", soft_part->compnamep->cname);
1192 
1193 		/* keep track of the underlying device of this soft part */
1194 		base_list = sp_add_done(soft_part, base_list);
1195 
1196 		/*
1197 		 * now print all of the other soft parts on the same
1198 		 * underlying device
1199 		 */
1200 		for (tnlp = nlp->next; tnlp != NULL; tnlp = tnlp->next) {
1201 		    md_sp_t		*part;
1202 
1203 		    mdn = metaname(&sp, tnlp->namep->cname, &error);
1204 		    mdclrerror(&error);
1205 		    if (mdn == NULL)
1206 			continue;
1207 
1208 		    part = meta_get_sp_common(sp, mdn, 1, &error);
1209 		    mdclrerror(&error);
1210 
1211 		    if (part == NULL || MD_HAS_PARENT(part->common.parent) ||
1212 			! sp_match(part, base_list))
1213 			continue;
1214 
1215 		    /* on the same base so print this soft part */
1216 		    print_concise_entry(0, part->common.namep->cname,
1217 			part->common.size, 'p');
1218 		    (void) printf(" %s\n", part->compnamep->cname);
1219 		}
1220 
1221 		/*
1222 		 * print the common metadevice hierarchy under these soft parts
1223 		 */
1224 		print_concise_md(INDENT, sp, soft_part->compnamep);
1225 	    }
1226 
1227 	    free_names(&nl);
1228 	    sp_free_list(base_list);
1229 	}
1230 	mdclrerror(&error);
1231 
1232 	if (meta_get_trans_names(sp, &nl, 0, &error) >= 0)
1233 	    print_concise_namelist(sp, &nl, 't');
1234 	mdclrerror(&error);
1235 
1236 	if (meta_get_mirror_names(sp, &nl, 0, &error) >= 0)
1237 	    print_concise_namelist(sp, &nl, 'm');
1238 	mdclrerror(&error);
1239 
1240 	if (meta_get_raid_names(sp, &nl, 0, &error) >= 0)
1241 	    print_concise_namelist(sp, &nl, 'r');
1242 	mdclrerror(&error);
1243 
1244 	if (meta_get_stripe_names(sp, &nl, 0, &error) >= 0)
1245 	    print_concise_namelist(sp, &nl, 's');
1246 	mdclrerror(&error);
1247 
1248 	if (meta_get_hsp_names(sp, &hsp_list, 0, &error) >= 0) {
1249 	    mdhspnamelist_t *nlp;
1250 
1251 	    for (nlp = hsp_list; nlp != NULL; nlp = nlp->next) {
1252 		md_hsp_t	*hsp;
1253 
1254 		print_concise_entry(0, nlp->hspnamep->hspname, 0, 'h');
1255 
1256 		hsp = meta_get_hsp_common(sp, nlp->hspnamep, 1, &error);
1257 		mdclrerror(&error);
1258 		if (hsp != NULL) {
1259 		    int	i;
1260 
1261 		    for (i = 0; i < hsp->hotspares.hotspares_len; i++) {
1262 			md_hs_t	*hs;
1263 			char	*state;
1264 
1265 			hs = &hsp->hotspares.hotspares_val[i];
1266 
1267 			(void) printf(" %s", hs->hsnamep->cname);
1268 
1269 			state = get_hs_state(hs);
1270 			if (state != NULL)
1271 			    (void) printf(" (%s)", state);
1272 		    }
1273 		}
1274 
1275 		(void) printf("\n");
1276 	    }
1277 
1278 	    metafreehspnamelist(hsp_list);
1279 	}
1280 }
1281 
1282 /*
1283  * Print the top-level metadevices in the name list for concise output.
1284  */
1285 static void
1286 print_concise_namelist(mdsetname_t *sp, mdnamelist_t **nl, char mtype)
1287 {
1288 	mdnamelist_t	*nlp;
1289 	md_error_t	error = mdnullerror;
1290 
1291 	for (nlp = *nl; nlp != NULL; nlp = nlp->next) {
1292 	    mdname_t	*mdn;
1293 	    md_common_t	*u;
1294 
1295 	    mdn = metaname(&sp, nlp->namep->cname, &error);
1296 	    mdclrerror(&error);
1297 	    if (mdn == NULL) {
1298 		print_concise_entry(0, nlp->namep->cname, 0, mtype);
1299 		printf("\n");
1300 		continue;
1301 	    }
1302 
1303 	    u = get_concise_unit(sp, mdn, &error);
1304 	    mdclrerror(&error);
1305 
1306 	    if (u != NULL && !MD_HAS_PARENT(u->parent))
1307 		print_concise_md(0, sp, mdn);
1308 	}
1309 
1310 	free_names(nl);
1311 }
1312 
1313 /*
1314  * Concise mirror output.
1315  */
1316 static void
1317 print_concise_mirror(int indent, mdsetname_t *sp, md_mirror_t *mirror)
1318 {
1319 	md_error_t	error = mdnullerror;
1320 	int		i;
1321 	md_status_t	status = mirror->common.state;
1322 
1323 	if (mirror == NULL)
1324 	    return;
1325 
1326 	print_concise_entry(indent, mirror->common.namep->cname,
1327 	    mirror->common.size, 'm');
1328 
1329 	for (i = 0; i < NMIRROR; i++) {
1330 	    uint_t	tstate = 0;
1331 	    char	*state;
1332 
1333 	    if (mirror->submirrors[i].submirnamep == NULL)
1334 		continue;
1335 	    (void) printf(" %s", mirror->submirrors[i].submirnamep->cname);
1336 
1337 	    if (mirror->submirrors[i].state & SMS_OFFLINE) {
1338 		(void) printf(gettext(" (offline)"));
1339 		continue;
1340 	    }
1341 
1342 	    if (metaismeta(mirror->submirrors[i].submirnamep))
1343 		(void) meta_get_tstate(mirror->submirrors[i].submirnamep->dev,
1344 		    &tstate, &error);
1345 
1346 	    state = get_sm_state(mirror, i, status, tstate);
1347 	    if (state != NULL)
1348 		(void) printf(" (%s)", state);
1349 	}
1350 
1351 	(void) printf("\n");
1352 
1353 	indent += INDENT;
1354 	for (i = 0; i < NMIRROR; i++) {
1355 	    if (mirror->submirrors[i].submirnamep == NULL)
1356 		continue;
1357 
1358 	    print_concise_md(indent, sp, mirror->submirrors[i].submirnamep);
1359 	}
1360 }
1361 
1362 /*
1363  * Concise raid output.
1364  */
1365 static void
1366 print_concise_raid(int indent, mdsetname_t *sp, md_raid_t *raid)
1367 {
1368 	md_error_t	error = mdnullerror;
1369 	int		i;
1370 	uint_t		tstate = 0;
1371 
1372 	if (raid == NULL)
1373 	    return;
1374 
1375 	print_concise_entry(indent, raid->common.namep->cname,
1376 	    raid->common.size, 'r');
1377 
1378 	if (metaismeta(raid->common.namep))
1379 	    (void) meta_get_tstate(raid->common.namep->dev, &tstate, &error);
1380 
1381 	for (i = 0; i < raid->cols.cols_len; i++) {
1382 	    md_raidcol_t	*colp = &raid->cols.cols_val[i];
1383 	    mdname_t		*namep = ((colp->hsnamep != NULL) ?
1384 				    colp->hsnamep : colp->colnamep);
1385 	    char		*hsname = ((colp->hsnamep != NULL) ?
1386 				    colp->hsnamep->cname : NULL);
1387 	    char		*col_state = NULL;
1388 
1389 	    (void) printf(" %s", colp->colnamep->cname);
1390 
1391 	    if (metaismeta(namep)) {
1392 		uint_t tstate = 0;
1393 
1394 		(void) meta_get_tstate(namep->dev, &tstate, &error);
1395 		col_state = get_raid_col_state(colp, tstate);
1396 
1397 	    } else {
1398 		if (tstate != 0)
1399 		    col_state = "-";
1400 		else
1401 		    col_state = get_raid_col_state(colp, tstate);
1402 	    }
1403 
1404 	    if (col_state != NULL) {
1405 		if (hsname != NULL)
1406 		    (void) printf(" (%s-%s)", col_state, hsname);
1407 		else
1408 		    (void) printf(" (%s)", col_state);
1409 
1410 	    } else if (hsname != NULL) {
1411 		(void) printf(gettext(" (spared-%s)"), hsname);
1412 	    }
1413 	}
1414 
1415 	(void) printf("\n");
1416 
1417 	indent += INDENT;
1418 	for (i = 0; i < raid->cols.cols_len; i++) {
1419 	    print_concise_md(indent, sp, raid->cols.cols_val[i].colnamep);
1420 	}
1421 }
1422 
1423 /*
1424  * Concise stripe output.
1425  */
1426 static void
1427 print_concise_stripe(int indent, mdsetname_t *sp, md_stripe_t *stripe)
1428 {
1429 	md_error_t	error = mdnullerror;
1430 	int		i;
1431 	uint_t		top_tstate = 0;
1432 
1433 	if (stripe == NULL)
1434 	    return;
1435 
1436 	print_concise_entry(indent, stripe->common.namep->cname,
1437 	    stripe->common.size, 's');
1438 
1439 	if (metaismeta(stripe->common.namep))
1440 	    (void) meta_get_tstate(stripe->common.namep->dev, &top_tstate,
1441 		&error);
1442 	mdclrerror(&error);
1443 
1444 	for (i = 0; i < stripe->rows.rows_len; i++) {
1445 	    md_row_t	*rowp;
1446 	    int		j;
1447 
1448 	    rowp = &stripe->rows.rows_val[i];
1449 
1450 	    for (j = 0; j < rowp->comps.comps_len; j++) {
1451 		md_comp_t	*comp;
1452 		uint_t		tstate = 0;
1453 		char		*comp_state = NULL;
1454 		char		*hsname;
1455 
1456 		comp = &rowp->comps.comps_val[j];
1457 		(void) printf(" %s", comp->compnamep->cname);
1458 
1459 		if (metaismeta(comp->compnamep)) {
1460 		    uint_t tstate = 0;
1461 		    (void) meta_get_tstate(comp->compnamep->dev, &tstate,
1462 			&error);
1463 		    comp_state = get_stripe_state(comp, tstate);
1464 		} else {
1465 		    if (top_tstate != 0)
1466 			comp_state = "-";
1467 		    else
1468 			comp_state = get_stripe_state(comp, tstate);
1469 		}
1470 
1471 		hsname = ((comp->hsnamep != NULL) ?
1472 		    comp->hsnamep->cname : NULL);
1473 
1474 		if (comp_state != NULL) {
1475 		    if (hsname != NULL)
1476 			(void) printf(" (%s-%s)", comp_state, hsname);
1477 		    else
1478 			(void) printf(" (%s)", comp_state);
1479 
1480 		} else if (hsname != NULL) {
1481 		    (void) printf(gettext(" (spared-%s)"), hsname);
1482 		}
1483 	    }
1484 	}
1485 
1486 	(void) printf("\n");
1487 
1488 	indent += INDENT;
1489 	for (i = 0; i < stripe->rows.rows_len; i++) {
1490 	    md_row_t	*rowp;
1491 	    int		j;
1492 
1493 	    rowp = &stripe->rows.rows_val[i];
1494 
1495 	    for (j = 0; j < rowp->comps.comps_len; j++) {
1496 		print_concise_md(indent, sp,
1497 		    rowp->comps.comps_val[j].compnamep);
1498 	    }
1499 	}
1500 }
1501 
1502 /*
1503  * Concise soft partition output.
1504  */
1505 static void
1506 print_concise_sp(int indent, mdsetname_t *sp, md_sp_t *part)
1507 {
1508 	if (part == NULL)
1509 	    return;
1510 
1511 	print_concise_entry(indent, part->common.namep->cname,
1512 	    part->common.size, 'p');
1513 
1514 	(void) printf(" %s\n", part->compnamep->cname);
1515 
1516 	print_concise_md(indent + INDENT, sp, part->compnamep);
1517 }
1518 
1519 /*
1520  * Concise trans output.
1521  */
1522 static void
1523 print_concise_trans(int indent, mdsetname_t *sp, md_trans_t *trans)
1524 {
1525 	if (trans == NULL)
1526 	    return;
1527 
1528 	print_concise_entry(indent, trans->common.namep->cname,
1529 	    trans->common.size, 't');
1530 
1531 	if (trans->masternamep != NULL)
1532 	    (void) printf(" %s", trans->masternamep->cname);
1533 
1534 	if (trans->lognamep != NULL)
1535 	    (void) printf(" %s", trans->lognamep->cname);
1536 
1537 	(void) printf("\n");
1538 
1539 	indent += INDENT;
1540 
1541 	print_concise_md(indent, sp, trans->masternamep);
1542 
1543 	print_concise_md(indent, sp, trans->lognamep);
1544 }
1545 
1546 /*
1547  * Recursive function for concise metadevice nested output.
1548  */
1549 static void
1550 print_concise_md(int indent, mdsetname_t *sp, mdname_t *np)
1551 {
1552 	md_error_t	error = mdnullerror;
1553 	md_unit_t	*u;
1554 	md_mirror_t	*mirror;
1555 	md_raid_t	*raid;
1556 	md_sp_t		*soft_part;
1557 	md_stripe_t	*stripe;
1558 	md_trans_t	*trans;
1559 
1560 	if (np == NULL || !metaismeta(np))
1561 	    return;
1562 
1563 	if ((u = meta_get_mdunit(sp, np, &error)) == NULL)
1564 	    return;
1565 
1566 	switch (u->c.un_type) {
1567 	case MD_DEVICE:
1568 	    stripe = meta_get_stripe_common(sp, np, 1, &error);
1569 	    print_concise_stripe(indent, sp, stripe);
1570 	    break;
1571 
1572 	case MD_METAMIRROR:
1573 	    mirror = meta_get_mirror(sp, np, &error);
1574 	    print_concise_mirror(indent, sp, mirror);
1575 	    break;
1576 
1577 	case MD_METATRANS:
1578 	    trans = meta_get_trans_common(sp, np, 1, &error);
1579 	    print_concise_trans(indent, sp, trans);
1580 	    break;
1581 
1582 	case MD_METARAID:
1583 	    raid = meta_get_raid_common(sp, np, 1, &error);
1584 	    print_concise_raid(indent, sp, raid);
1585 	    break;
1586 
1587 	case MD_METASP:
1588 	    soft_part = meta_get_sp_common(sp, np, 1, &error);
1589 	    print_concise_sp(indent, sp, soft_part);
1590 	    break;
1591 
1592 	default:
1593 	    return;
1594 	}
1595 }
1596 
1597 /*
1598  * Print properly indented metadevice name, type and size for concise output.
1599  */
1600 static void
1601 print_concise_entry(int indent, char *name, diskaddr_t size, char mtype)
1602 {
1603 	int	i;
1604 	int	width = NAMEWIDTH;	/* minumum field width for name */
1605 	char	in[MAXPATHLEN];
1606 	char	*sz;
1607 
1608 	in[0] = 0;
1609 	for (i = 0; i < indent; i++)
1610 	    (void) strlcat(in, " ", sizeof (in));
1611 
1612 	/* set up minimum field width. negative for left justified */
1613 	width -= indent;
1614 	if (width < 0)
1615 	    width = 0;		/* overflowed; no minimum field needed */
1616 	else
1617 	    width = 0 - width;	/* make it negative for left justification */
1618 
1619 	if (size == 0) {
1620 	    sz = "-";
1621 	} else {
1622 	    sz = strip_blanks(meta_number_to_string(size, DEV_BSIZE));
1623 	}
1624 
1625 	(void) printf("%s%*s %c %6s", in, width, name, mtype, sz);
1626 }
1627 
1628 /*
1629  * Given a name get the unit for use in concise output.  We use the *_common
1630  * routines in libmeta which allow us to specify the "fast" flag, thereby
1631  * avoiding the DKIOCGGEOM ioctl that normally happens.
1632  */
1633 static md_common_t *
1634 get_concise_unit(mdsetname_t *sp, mdname_t *np, md_error_t *ep)
1635 {
1636 	char		*miscname;
1637 
1638 	/* short circuit */
1639 	if (np->drivenamep->unitp != NULL)
1640 		return (np->drivenamep->unitp);
1641 	if (metachkmeta(np, ep) != 0)
1642 		return (NULL);
1643 
1644 	/* dispatch */
1645 	if ((miscname = metagetmiscname(np, ep)) == NULL)
1646 		return (NULL);
1647 	else if (strcmp(miscname, MD_STRIPE) == 0)
1648 		return ((md_common_t *)meta_get_stripe_common(sp, np, 1, ep));
1649 	else if (strcmp(miscname, MD_MIRROR) == 0)
1650 		return ((md_common_t *)meta_get_mirror(sp, np, ep));
1651 	else if (strcmp(miscname, MD_TRANS) == 0)
1652 		return ((md_common_t *)meta_get_trans_common(sp, np, 1, ep));
1653 	else if (strcmp(miscname, MD_RAID) == 0)
1654 		return ((md_common_t *)meta_get_raid_common(sp, np, 1, ep));
1655 	else if (strcmp(miscname, MD_SP) == 0)
1656 		return ((md_common_t *)meta_get_sp_common(sp, np, 1, ep));
1657 	else {
1658 		(void) mdmderror(ep, MDE_UNKNOWN_TYPE, meta_getminor(np->dev),
1659 		    np->cname);
1660 		return (NULL);
1661 	}
1662 }
1663 
1664 static void
1665 free_names(mdnamelist_t **nlp)
1666 {
1667 	mdnamelist_t *p;
1668 
1669 	for (p = *nlp; p != NULL; p = p->next) {
1670 	    meta_invalidate_name(p->namep);
1671 	    p->namep = NULL;
1672 	}
1673 	metafreenamelist(*nlp);
1674 	*nlp = NULL;
1675 }
1676 
1677 /*
1678  * Submirror state for concise output.
1679  */
1680 static char *
1681 get_sm_state(md_mirror_t *mirror, int i, md_status_t mirror_status,
1682 	uint_t tstate)
1683 {
1684 	sm_state_t	state = mirror->submirrors[i].state;
1685 	uint_t		is_target =
1686 			    mirror->submirrors[i].flags & MD_SM_RESYNC_TARGET;
1687 
1688 	/*
1689 	 * Only return Unavailable if there is no flagged error on the
1690 	 * submirror. If the mirror has received any writes since the submirror
1691 	 * went into Unavailable state a resync is required. To alert the
1692 	 * administrator to this we return a 'Needs maintenance' message.
1693 	 */
1694 	if ((tstate != 0) && (state & SMS_RUNNING))
1695 		return (gettext("unavail"));
1696 
1697 	/* all is well */
1698 	if (state & SMS_RUNNING) {
1699 		if (!(mirror_status & MD_UN_OPT_NOT_DONE) ||
1700 		    ((mirror_status & MD_UN_OPT_NOT_DONE) && !is_target))
1701 			return (NULL);
1702 	}
1703 
1704 	/* resyncing, needs repair */
1705 	if ((state & (SMS_COMP_RESYNC | SMS_ATTACHED_RESYNC |
1706 	    SMS_OFFLINE_RESYNC)) || (mirror_status & MD_UN_OPT_NOT_DONE)) {
1707 		static char buf[MAXPATHLEN];
1708 
1709 		if (mirror_status & MD_UN_RESYNC_ACTIVE) {
1710 
1711 		    if (mirror->common.revision == MD_64BIT_META_DEV) {
1712 			(void) snprintf(buf, sizeof (buf),
1713 			    gettext("resync-%2d.%1d%%"),
1714 			    mirror->percent_done / 10,
1715 			    mirror->percent_done % 10);
1716 		    } else {
1717 			(void) snprintf(buf, sizeof (buf),
1718 			    gettext("resync-%d%%"), mirror->percent_done);
1719 		    }
1720 		    return (buf);
1721 		}
1722 		return (gettext("maint"));
1723 	}
1724 
1725 	/* needs repair */
1726 	if (state & (SMS_COMP_ERRED | SMS_ATTACHED | SMS_OFFLINE))
1727 		return (gettext("maint"));
1728 
1729 	/* unknown */
1730 	return (gettext("unknown"));
1731 }
1732 
1733 /*
1734  * Raid component state for concise output.
1735  */
1736 static char *
1737 get_raid_col_state(md_raidcol_t *colp, uint_t tstate)
1738 {
1739 	if (tstate != 0)
1740 	    return (gettext("unavail"));
1741 
1742 	switch (colp->state) {
1743 	case RCS_INIT:
1744 	    return (gettext("initializing"));
1745 
1746 	case RCS_OKAY:
1747 	    return (NULL);
1748 
1749 	case RCS_INIT_ERRED:
1750 	    /*FALLTHROUGH*/
1751 	case RCS_ERRED:
1752 	    return (gettext("maint"));
1753 
1754 	case RCS_LAST_ERRED:
1755 	    return (gettext("last-erred"));
1756 
1757 	case RCS_RESYNC:
1758 	    return (gettext("resyncing"));
1759 
1760 	default:
1761 	    return (gettext("unknown"));
1762 	}
1763 }
1764 
1765 /*
1766  * Stripe state for concise output.
1767  */
1768 static char *
1769 get_stripe_state(md_comp_t *mdcp, uint_t tstate)
1770 {
1771 	comp_state_t	state = mdcp->state;
1772 
1773 	if (tstate != 0)
1774 	    return ("unavail");
1775 
1776 	switch (state) {
1777 	case CS_OKAY:
1778 	    return (NULL);
1779 	case CS_ERRED:
1780 	    return (gettext("maint"));
1781 	case CS_LAST_ERRED:
1782 	    return (gettext("last-erred"));
1783 	case CS_RESYNC:
1784 	    return (gettext("resyncing"));
1785 	default:
1786 	    return (gettext("invalid"));
1787 	}
1788 }
1789 
1790 /*
1791  * Hostspare state for concise output.
1792  */
1793 static char *
1794 get_hs_state(md_hs_t *hsp)
1795 {
1796 	hotspare_states_t	state = hsp->state;
1797 
1798 	switch (state) {
1799 	case HSS_AVAILABLE:
1800 	    return (NULL);
1801 	case HSS_RESERVED:
1802 	    return (gettext("in-use"));
1803 	case HSS_BROKEN:
1804 	    return (gettext("broken"));
1805 	case HSS_UNUSED:
1806 	default:
1807 	    return (gettext("invalid"));
1808 	}
1809 }
1810 
1811 
1812 /*
1813  * Strip blanks from string.  Used for size field in concise output.
1814  */
1815 static char *
1816 strip_blanks(char *s)
1817 {
1818 	char *p;
1819 
1820 	for (p = s; *p; ) {
1821 	    if (*p == ' ') {
1822 		char *t;
1823 
1824 		for (t = p; *t; t++) {
1825 		    *t = *(t + 1);
1826 		}
1827 
1828 	    } else {
1829 		p++;
1830 	    }
1831 	}
1832 
1833 	return (s);
1834 }
1835 
1836 /*
1837  * Keep track of printed soft partitions for concise output.
1838  */
1839 static struct sp_base_list *
1840 sp_add_done(md_sp_t *part, struct sp_base_list *lp)
1841 {
1842 	struct sp_base_list *n;
1843 
1844 	n = (struct sp_base_list *)malloc(sizeof (struct sp_base_list));
1845 	if (n == NULL)
1846 	    return (lp);
1847 
1848 	if ((n->base = strdup(part->compnamep->cname)) == NULL) {
1849 	    free(n);
1850 	    return (lp);
1851 	}
1852 
1853 	n->next = lp;
1854 
1855 	return (n);
1856 }
1857 
1858 /*
1859  * Keep track of printed soft partitions for concise output.
1860  */
1861 static int
1862 sp_done(md_sp_t *part, struct sp_base_list *lp)
1863 {
1864 	for (; lp != NULL; lp = lp->next) {
1865 	    if (strcmp(lp->base, part->compnamep->cname) == 0)
1866 		return (1);
1867 	}
1868 
1869 	return (0);
1870 }
1871 
1872 /*
1873  * Check the first element for a match.
1874  */
1875 static int
1876 sp_match(md_sp_t *part, struct sp_base_list *lp)
1877 {
1878 	if (lp != NULL && strcmp(lp->base, part->compnamep->cname) == 0)
1879 	    return (1);
1880 
1881 	return (0);
1882 }
1883 
1884 /*
1885  * Free memory used for soft partition printed status in concise output.
1886  */
1887 static void
1888 sp_free_list(struct sp_base_list *lp)
1889 {
1890 	struct sp_base_list *n;
1891 
1892 	for (; lp != NULL; lp = n) {
1893 	    n = lp->next;
1894 	    free(lp->base);
1895 	    free(lp);
1896 	}
1897 }
1898