xref: /titanic_41/usr/src/lib/lvm/libmeta/common/meta_mh.c (revision ac88567a7a5bb7f01cf22cf366bc9d6203e24d7a)
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 2004 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  * Just in case we're not in a build environment, make sure that
31  * TEXT_DOMAIN gets set to something.
32  */
33 #if !defined(TEXT_DOMAIN)
34 #define	TEXT_DOMAIN "SYS_TEST"
35 #endif
36 
37 /*
38  * MH ioctl functions
39  */
40 
41 #include <meta.h>
42 #include <metamhd.h>
43 #include <string.h>
44 
45 #include "meta_runtime.h"
46 
47 #define	DEFAULTDEV "/dev/rdsk"
48 /*
49  * default timeout values
50  */
51 mhd_mhiargs_t	defmhiargs = {
52 	1000,			/* failfast */
53 	{ 6000, 6000, 30000 }	/* take ownership */
54 };
55 
56 /* RPC timeouts */
57 static md_timeval32_t	tk_own_timeout  = { 24 * 60 * 60, 0 };	/* 1 day */
58 static md_timeval32_t	rel_own_timeout = { 24 * 60 * 60, 0 };	/* 1 day */
59 
60 /*
61  * RPC handle
62  */
63 typedef struct {
64 	char	*hostname;
65 	CLIENT	*clientp;
66 } mhd_handle_t;
67 
68 /*
69  * close RPC connection
70  */
71 static void
72 close_metamhd(
73 	mhd_handle_t	*hp
74 )
75 {
76 	assert(hp != NULL);
77 	if (hp->hostname != NULL) {
78 		Free(hp->hostname);
79 	}
80 	if (hp->clientp != NULL) {
81 		auth_destroy(hp->clientp->cl_auth);
82 		clnt_destroy(hp->clientp);
83 	}
84 	Free(hp);
85 }
86 
87 /*
88  * open RPC connection to rpc.metamhd
89  */
90 static mhd_handle_t *
91 open_metamhd(
92 	char		*hostname,
93 	md_error_t	*ep
94 )
95 {
96 	CLIENT		*clientp;
97 	mhd_handle_t	*hp;
98 
99 	/* default to local host */
100 	if ((hostname == NULL) || (*hostname == '\0'))
101 		hostname = mynode();
102 
103 	/* open RPC connection */
104 	assert(hostname != NULL);
105 	if ((clientp = meta_client_create(hostname, METAMHD, METAMHD_VERSION,
106 	    "tcp")) == NULL) {
107 		clnt_pcreateerror(hostname);
108 		(void) mdrpccreateerror(ep, hostname, "metamhd clnt_create");
109 		return (NULL);
110 	} else {
111 		auth_destroy(clientp->cl_auth);
112 		clientp->cl_auth = authsys_create_default();
113 		assert(clientp->cl_auth != NULL);
114 	}
115 
116 	/* return connection */
117 	hp = Zalloc(sizeof (*hp));
118 	hp->hostname = Strdup(hostname);
119 	hp->clientp = clientp;
120 	return (hp);
121 }
122 
123 /*
124  * steal and convert mherror_t
125  */
126 int
127 mhstealerror(
128 	mhd_error_t	*mhep,
129 	md_error_t	*ep
130 )
131 {
132 	int		rval = -1;
133 
134 	/* no error */
135 	if (mhep->errnum == 0) {
136 		/* assert(mhep->name == NULL); */
137 		rval = 0;
138 		goto out;
139 	}
140 
141 	/* steal error */
142 	switch (mhep->errnum) {
143 	case MHD_E_MAJORITY:
144 		(void) mderror(ep, MDE_TAKE_OWN, mhep->name);
145 		break;
146 	case MHD_E_RESERVED:
147 		(void) mderror(ep, MDE_RESERVED, mhep->name);
148 		break;
149 	default:
150 		(void) mdsyserror(ep, mhep->errnum, mhep->name);
151 		break;
152 	}
153 
154 	/* cleanup, return success */
155 out:
156 	if (mhep->name != NULL)
157 		Free(mhep->name);
158 	(void) memset(mhep, 0, sizeof (*mhep));
159 	return (rval);
160 }
161 
162 /*
163  * should we do MHIOCTLs ?
164  */
165 static int
166 do_mhioctl()
167 {
168 	if (getenv("MD_NOMHIOCTL") != NULL) {
169 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
170 		    "NOT doing MH ioctls\n"));
171 		(void) fflush(stderr);
172 		return (0);
173 	}
174 	return (1);
175 }
176 
177 /*
178  * take ownership of drives
179  */
180 int
181 meta_take_own(
182 	char			*sname,
183 	mddrivenamelist_t	*dnlp,
184 	mhd_mhiargs_t		*mhiargsp,
185 	int			partial_set,
186 	md_error_t		*ep
187 )
188 {
189 	mddrivenamelist_t	*p;
190 	uint_t			ndev = 0;
191 	mhd_tkown_args_t	args;
192 	mhd_error_t		mherror;
193 	mhd_set_t		*mhsp = &args.set;
194 	uint_t			i;
195 	char			*e;
196 	mhd_handle_t		*hp = NULL;
197 	int			rval = -1;
198 
199 	/*
200 	 * RFE 4126509.  Check the runtime parameters to see if
201 	 * they're set to disable MHIOCTKOWN ioctl() operations
202 	 * on the disks.  If so, return immediately without
203 	 * performing the operations.
204 	 */
205 
206 	if (do_owner_ioctls() == B_FALSE) {
207 		return (0);
208 	}
209 
210 	/* count drives, get set */
211 	for (p = dnlp; (p != NULL); p = p->next)
212 		++ndev;
213 	if (ndev == 0)
214 		return (0);
215 
216 	/* initialize */
217 	(void) memset(&args, 0, sizeof (args));
218 	(void) memset(&mherror, 0, sizeof (mherror));
219 
220 	/* build arguments */
221 	mhsp->setname = Strdup(sname);
222 	mhsp->drives.drives_len = ndev;
223 	mhsp->drives.drives_val
224 	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
225 	for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) {
226 		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
227 	}
228 	args.timeouts = *mhiargsp;
229 	args.ff_mode = MHD_FF_DRIVER;
230 	if (((e = getenv("MD_DEBUG")) != NULL) &&
231 	    ((e = strstr(e, "FAILFAST=")) != NULL) &&
232 	    ((e = strchr(e, '=')) != NULL)) {
233 		++e;
234 		if (strcmp(e, "NONE") == 0)
235 			args.ff_mode = MHD_FF_NONE;
236 		else if (strcmp(e, "DRIVER") == 0)
237 			args.ff_mode = MHD_FF_DRIVER;
238 		else if (strcmp(e, "DEBUG") == 0)
239 			args.ff_mode = MHD_FF_DEBUG;
240 		else if (strcmp(e, "HALT") == 0)
241 			args.ff_mode = MHD_FF_HALT;
242 		else if (strcmp(e, "PANIC") == 0)
243 			args.ff_mode = MHD_FF_PANIC;
244 	}
245 	if (partial_set)
246 		args.options |= MHD_PARTIAL_SET;
247 	if (((e = getenv("MD_DEBUG")) != NULL) &&
248 	    (strstr(e, "NOTHREAD") != NULL)) {
249 		args.options |= MHD_SERIAL;
250 	}
251 
252 	/* open connection */
253 	if ((hp = open_metamhd(NULL, ep)) == NULL)
254 		return (-1);
255 	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);
256 
257 	/* take ownership */
258 	if (mhd_tkown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) {
259 		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
260 		    "metamhd tkown");
261 	} else if (mhstealerror(&mherror, ep) == 0) {
262 		rval = 0;	/* success */
263 	}
264 
265 	/* cleanup, return success */
266 out:
267 	xdr_free(xdr_mhd_tkown_args_t, (char *)&args);
268 	xdr_free(xdr_mhd_error_t, (char *)&mherror);
269 	if (hp != NULL)
270 		close_metamhd(hp);
271 	return (rval);
272 }
273 
274 /*
275  * take ownership of drives
276  */
277 int
278 tk_own_bydd(
279 	mdsetname_t		*sp,
280 	md_drive_desc		*ddlp,
281 	mhd_mhiargs_t		*mhiargsp,
282 	int			partial_set,
283 	md_error_t		*ep
284 )
285 {
286 	mddrivenamelist_t	*dnlp = NULL;
287 	mddrivenamelist_t	**tailpp = &dnlp;
288 	md_drive_desc		*p;
289 	int			rval;
290 
291 	/*
292 	 * Add the drivename struct to the end of the
293 	 * drivenamelist but keep a pointer to the last
294 	 * element so that we don't incur the overhead
295 	 * of traversing the list each time
296 	 */
297 	for (p = ddlp; (p != NULL); p = p->dd_next)
298 		tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp);
299 
300 	/* take ownership */
301 	rval = meta_take_own(sp->setname, dnlp, mhiargsp, partial_set, ep);
302 
303 	/* cleanup, return success */
304 	metafreedrivenamelist(dnlp);
305 	return (rval);
306 }
307 
308 /*
309  * release ownership of drives
310  */
311 int
312 meta_rel_own(
313 	char			*sname,
314 	mddrivenamelist_t	*dnlp,
315 	int			partial_set,
316 	md_error_t		*ep
317 )
318 {
319 	mddrivenamelist_t	*p;
320 	uint_t			ndev = 0;
321 	mhd_relown_args_t	args;
322 	mhd_error_t		mherror;
323 	mhd_set_t		*mhsp = &args.set;
324 	uint_t			i;
325 	char			*e;
326 	mhd_handle_t		*hp = NULL;
327 	int			rval = -1;
328 
329 	/*
330 	 * RFE 4126509.  Check the runtime parameters to see if
331 	 * they're set to disable MHIOCRELEASE and MHIOCENFAILFAST
332 	 * ioctl() operations on the disks.  If so, return
333 	 * immediately without performing the operations.
334 	 */
335 
336 	if (do_owner_ioctls() == B_FALSE) {
337 		return (0);
338 	}
339 
340 	/*
341 	 * if not doing ioctls (HK 98/10/28: the following code tests
342 	 * an environment variable, and was apparently inserted to
343 	 * make testing easier.)
344 	 */
345 
346 	if (! do_mhioctl())
347 		return (0);
348 
349 	/* count drives, get set */
350 	for (p = dnlp; (p != NULL); p = p->next)
351 		++ndev;
352 	if (ndev == 0)
353 		return (0);
354 
355 	/* initialize */
356 	(void) memset(&args, 0, sizeof (args));
357 	(void) memset(&mherror, 0, sizeof (mherror));
358 
359 	/* build arguments */
360 	mhsp->setname = Strdup(sname);
361 	mhsp->drives.drives_len = ndev;
362 	mhsp->drives.drives_val
363 	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
364 	for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) {
365 		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
366 	}
367 	if (partial_set)
368 		args.options |= MHD_PARTIAL_SET;
369 	if (((e = getenv("MD_DEBUG")) != NULL) &&
370 	    (strstr(e, "NOTHREAD") != NULL)) {
371 		args.options |= MHD_SERIAL;
372 	}
373 
374 	/* open connection */
375 	if ((hp = open_metamhd(NULL, ep)) == NULL)
376 		return (-1);
377 	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&rel_own_timeout);
378 
379 	/* take ownership */
380 	if (mhd_relown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) {
381 		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
382 		    "metamhd relown");
383 	} else if (mhstealerror(&mherror, ep) == 0) {
384 		rval = 0;	/* success */
385 	}
386 
387 	/* cleanup, return success */
388 out:
389 	xdr_free(xdr_mhd_relown_args_t, (char *)&args);
390 	xdr_free(xdr_mhd_error_t, (char *)&mherror);
391 	if (hp != NULL)
392 		close_metamhd(hp);
393 	return (rval);
394 }
395 
396 /*
397  * release ownership of drives
398  */
399 int
400 rel_own_bydd(
401 	mdsetname_t		*sp,
402 	md_drive_desc		*ddlp,
403 	int			partial_set,
404 	md_error_t		*ep
405 )
406 {
407 	mddrivenamelist_t	*dnlp = NULL;
408 	mddrivenamelist_t	**tailpp = &dnlp;
409 	md_drive_desc		*p;
410 	int			rval;
411 
412 	/*
413 	 * Add the drivename struct to the end of the
414 	 * drivenamelist but keep a pointer to the last
415 	 * element so that we don't incur the overhead
416 	 * of traversing the list each time
417 	 */
418 	for (p = ddlp; (p != NULL); p = p->dd_next)
419 		tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp);
420 
421 	/* release ownership */
422 	rval = meta_rel_own(sp->setname, dnlp, partial_set, ep);
423 
424 	/* cleanup, return success */
425 	metafreedrivenamelist(dnlp);
426 	return (rval);
427 }
428 
429 /*
430  * get status of drives
431  */
432 int
433 meta_status_own(
434 	char			*sname,
435 	md_disk_status_list_t	*dslp,
436 	int			partial_set,
437 	md_error_t		*ep
438 )
439 {
440 	md_disk_status_list_t	*p;
441 	uint_t			ndev = 0;
442 	mhd_status_args_t	args;
443 	mhd_status_res_t	results;
444 	mhd_error_t		*mhep = &results.status;
445 	mhd_set_t		*mhsp = &args.set;
446 	uint_t			i;
447 	char			*e;
448 	mhd_handle_t		*hp = NULL;
449 	int			rval = -1;
450 
451 	/* if not doing ioctls */
452 	if (! do_mhioctl())
453 		return (0);
454 
455 	/* count drives, get set */
456 	for (p = dslp; (p != NULL); p = p->next)
457 		++ndev;
458 	if (ndev == 0)
459 		return (0);
460 
461 	/* initialize */
462 	(void) memset(&args, 0, sizeof (args));
463 	(void) memset(&results, 0, sizeof (results));
464 
465 	/* build arguments */
466 	mhsp->setname = Strdup(sname);
467 	mhsp->drives.drives_len = ndev;
468 	mhsp->drives.drives_val
469 	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
470 	for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) {
471 		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
472 	}
473 	if (partial_set)
474 		args.options |= MHD_PARTIAL_SET;
475 	if (((e = getenv("MD_DEBUG")) != NULL) &&
476 	    (strstr(e, "NOTHREAD") != NULL)) {
477 		args.options |= MHD_SERIAL;
478 	}
479 
480 	/* open connection */
481 	if ((hp = open_metamhd(NULL, ep)) == NULL)
482 		return (-1);
483 	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);
484 
485 	/* get status */
486 	if (mhd_status_1(&args, &results, hp->clientp) != RPC_SUCCESS) {
487 		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
488 		    dgettext(TEXT_DOMAIN, "metamhd status"));
489 		goto out;
490 	} else if (mhstealerror(mhep, ep) != 0) {
491 		goto out;
492 	}
493 
494 	/* do something with it */
495 	assert(results.results.results_len == ndev);
496 	for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) {
497 		mhd_drive_status_t	*resp = &results.results.results_val[i];
498 		mddrivename_t		*dp = p->drivenamep;
499 		mhd_error_t		mherror;
500 
501 		/* make sure we have the right drive */
502 		assert(strcmp(dp->rname, resp->drive) == 0);
503 
504 		/* copy status */
505 		if (resp->errnum != 0) {
506 			(void) memset(&mherror, 0, sizeof (mherror));
507 			mherror.errnum = resp->errnum;
508 			mherror.name = Strdup(resp->drive);
509 			(void) mhstealerror(&mherror, &p->status);
510 		}
511 	}
512 	rval = 0;		/* success */
513 
514 	/* cleanup, return success */
515 out:
516 	xdr_free(xdr_mhd_status_args_t, (char *)&args);
517 	xdr_free(xdr_mhd_status_res_t, (char *)&results);
518 	if (hp != NULL)
519 		close_metamhd(hp);
520 	return (rval);
521 }
522 
523 /*
524  * build disk status list from drivename list
525  */
526 md_disk_status_list_t *
527 meta_drive_to_disk_status_list(
528 	mddrivenamelist_t	*dnlp
529 )
530 {
531 	md_disk_status_list_t	*head = NULL;
532 	md_disk_status_list_t	**tailp = &head;
533 	mddrivenamelist_t	*p;
534 
535 	/* copy list */
536 	for (p = dnlp; (p != NULL); p = p->next) {
537 		md_disk_status_list_t	*dsp;
538 
539 		dsp = *tailp = Zalloc(sizeof (*dsp));
540 		tailp = &dsp->next;
541 		dsp->drivenamep = p->drivenamep;
542 	}
543 
544 	/* return list */
545 	return (head);
546 }
547 
548 /*
549  * free disk status list
550  */
551 void
552 meta_free_disk_status_list(
553 	md_disk_status_list_t	*dslp
554 )
555 {
556 	md_disk_status_list_t	*next = NULL;
557 
558 	for (/* void */; (dslp != NULL); dslp = next) {
559 		next = dslp->next;
560 		mdclrerror(&dslp->status);
561 		Free(dslp);
562 	}
563 }
564 
565 /*
566  * free drive info list
567  */
568 void
569 meta_free_drive_info_list(
570 	mhd_drive_info_list_t	*listp
571 )
572 {
573 	xdr_free(xdr_mhd_drive_info_list_t, (char *)listp);
574 	(void) memset(listp, 0, sizeof (*listp));
575 }
576 
577 /*
578  * sort drive info list
579  */
580 static int
581 compare_drives(
582 	const void		*p1,
583 	const void		*p2
584 )
585 {
586 	const mhd_drive_info_t	*di1 = p1;
587 	const mhd_drive_info_t	*di2 = p2;
588 	const char		*n1 = di1->dif_name;
589 	const char		*n2 = di2->dif_name;
590 	uint_t			c1 = 0, t1 = 0, d1 = 0, s1 = 0;
591 	uint_t			c2 = 0, t2 = 0, d2 = 0, s2 = 0;
592 	uint_t			l, cl;
593 
594 	if (n1 == NULL)
595 		n1 = "";
596 	if (n2 == NULL)
597 		n2 = "";
598 
599 	/* attempt to sort correctly for c0t1d0s0 .vs. c0t18d0s0 */
600 	if ((n1 = strrchr(n1, '/')) == NULL)
601 		goto u;
602 	n1 += (n1[1] != 'c') ? 2 : 1;
603 	cl = strlen(n1);
604 	if ((sscanf(n1, "c%ut%ud%us%u%n", &c1, &t1, &d1, &s1, &l) != 4 &&
605 	    sscanf(n1, "c%ud%us%u%n", &c1, &d1, &s1, &l) != 3 &&
606 	    sscanf(n1, "c%ut%ud%u%n", &c1, &t1, &d1, &l) != 3 &&
607 	    sscanf(n1, "c%ud%u%n", &c1, &d1, &l) != 2) || (l != cl))
608 		goto u;
609 
610 	if ((n2 = strrchr(n2, '/')) == NULL)
611 		goto u;
612 	n2 += (n2[1] != 'c') ? 2 : 1;
613 	cl = strlen(n2);
614 	if ((sscanf(n2, "c%ut%ud%us%u%n", &c2, &t2, &d2, &s2, &l) != 4 &&
615 	    sscanf(n2, "c%ud%us%u%n", &c2, &d2, &s2, &l) != 3 &&
616 	    sscanf(n2, "c%ut%ud%u%n", &c2, &t2, &d2, &l) != 3 &&
617 	    sscanf(n2, "c%ud%u%n", &c2, &d2, &l) != 2) || (l != cl))
618 		goto u;
619 	if (c1 != c2)
620 		return ((c1 > c2) ? 1 : -1);
621 	if (t1 != t2)
622 		return ((t1 > t2) ? 1 : -1);
623 	if (d1 != d2)
624 		return ((d1 > d2) ? 1 : -1);
625 	if (s1 != s2)
626 		return ((s1 > s2) ? 1 : -1);
627 	return (0);
628 
629 u:	return (strcmp(di1->dif_name, di2->dif_name));
630 }
631 
632 static void
633 sort_drives(
634 	mhd_drive_info_list_t	*listp
635 )
636 {
637 	qsort(listp->mhd_drive_info_list_t_val,
638 	    listp->mhd_drive_info_list_t_len,
639 	    sizeof (*listp->mhd_drive_info_list_t_val),
640 	    compare_drives);
641 }
642 
643 /*
644  * return list of all drives
645  */
646 int
647 meta_list_drives(
648 	char			*hostname,
649 	char			*path,
650 	mhd_did_flags_t		flags,
651 	mhd_drive_info_list_t	*listp,
652 	md_error_t		*ep
653 )
654 {
655 	mhd_list_args_t		args;
656 	mhd_list_res_t		results;
657 	mhd_error_t		*mhep = &results.status;
658 	mhd_handle_t		*hp = NULL;
659 	int			rval = -1;
660 
661 	/* if not doing ioctls */
662 	if (! do_mhioctl())
663 		return (0);
664 
665 	/* initialize */
666 	(void) memset(&args, 0, sizeof (args));
667 	(void) memset(&results, 0, sizeof (results));
668 
669 	/* build arguments */
670 	if (path == NULL)
671 		path = getenv("MD_DRIVE_ROOT");
672 	if ((path != NULL) && (*path != '\0'))
673 		args.path = Strdup(path);
674 	args.flags = flags;
675 
676 	/* open connection */
677 	if ((hp = open_metamhd(hostname, ep)) == NULL)
678 		return (-1);
679 	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);
680 
681 	/* get list */
682 	if (mhd_list_1(&args, &results, hp->clientp) != RPC_SUCCESS) {
683 		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
684 		    dgettext(TEXT_DOMAIN, "metamhd list"));
685 		goto out;
686 	} else if (mhstealerror(mhep, ep) != 0) {
687 		goto out;
688 	}
689 
690 	/* sort list */
691 	sort_drives(&results.results);
692 
693 	/* steal list */
694 	*listp = results.results;
695 	results.results.mhd_drive_info_list_t_len = 0;
696 	results.results.mhd_drive_info_list_t_val = NULL;
697 	rval = listp->mhd_drive_info_list_t_len;	/* success */
698 
699 	/* cleanup, return success */
700 out:
701 	xdr_free(xdr_mhd_list_args_t, (char *)&args);
702 	xdr_free(xdr_mhd_list_res_t, (char *)&results);
703 	if (hp != NULL)
704 		close_metamhd(hp);
705 	return (rval);
706 }
707 
708 static void
709 load_paths_to_metamhd()
710 {
711 	FILE			*cfp;		/* config file pointer */
712 	char			buf[BUFSIZ],
713 				*p,
714 				*x;
715 	mhd_drive_info_list_t	list;
716 	md_error_t		ep;
717 	mhd_did_flags_t		flags = MHD_DID_SERIAL;
718 
719 	if ((cfp = fopen(METADEVPATH, "r")) != NULL) {
720 		/*
721 		 * Read each line from the file. Lines will be either
722 		 * comments or path names to pass to rpc.metamhd. If
723 		 * path names check to see if their a colon seperate
724 		 * list of names which must be processed one at a time.
725 		 */
726 
727 		while (fgets(buf, BUFSIZ, cfp) != NULL) {
728 			if (buf[0] == '#') {
729 				/*
730 				 * Ignore comment lines
731 				 */
732 				continue;
733 
734 			} else if (strchr(buf, ':') != NULL) {
735 				p = buf;
736 				while ((x = strchr(p, ':')) != NULL) {
737 					*x = '\0';
738 					(void) memset(&ep, '\0', sizeof (ep));
739 					(void) meta_list_drives(NULL, p, 0,
740 					    &list, &ep);
741 					meta_free_drive_info_list(&list);
742 					p = x + 1;
743 				}
744 				/*
745 				 * We won't pick up the last path name
746 				 * because the line ends with a newline
747 				 * not a ':'. So p will still point to
748 				 * a valid path in this case. Copy the
749 				 * data that p points to to the beginning
750 				 * of the buf and let the default case
751 				 * handle this buffer.
752 				 * NOTE:
753 				 * If the file does end with a ":\n", p at
754 				 * will point to the newline. The default
755 				 * cause would then set the newline to a
756 				 * NULL which is okay because meta_list_drives
757 				 * interprets a null string as /dev/rdsk.
758 				 */
759 				(void) memcpy(buf, p, strlen(p));
760 			}
761 			/*
762 			 * Remove any newlines in the buffer.
763 			 */
764 			if ((p = strchr(buf, '\n')) != NULL)
765 				*p = '\0';
766 			(void) memset(&ep, '\0', sizeof (ep));
767 			(void) memset(&list, '\0', sizeof (list));
768 			(void) meta_list_drives(NULL, buf, flags, &list, &ep);
769 			meta_free_drive_info_list(&list);
770 		}
771 		(void) fclose(cfp);
772 	}
773 }
774 
775 /*
776  * build list of all drives in set
777  */
778 /*ARGSUSED*/
779 int
780 meta_get_drive_names(
781 	mdsetname_t		*sp,
782 	mddrivenamelist_t	**dnlpp,
783 	int			options,
784 	md_error_t		*ep
785 )
786 {
787 	mhd_did_flags_t		flags = MHD_DID_SERIAL;
788 	mhd_drive_info_list_t	list;
789 	mhd_drive_info_t	*mp;
790 	uint_t			i;
791 	unsigned		cnt = 0;
792 	int			rval = -1;
793 	mddrivenamelist_t	**tailpp = dnlpp;
794 
795 	/* must have a set */
796 	assert(sp != NULL);
797 
798 	load_paths_to_metamhd();
799 	(void) memset(&list, 0, sizeof (list));
800 	if ((meta_list_drives(NULL, NULL, flags, &list, ep)) < 0)
801 		return (-1);
802 
803 	/* find drives in set */
804 	for (i = 0; (i < list.mhd_drive_info_list_t_len); ++i) {
805 		mddrivename_t		*dnp;
806 		mdname_t		*np;
807 
808 		mp = &list.mhd_drive_info_list_t_val[i];
809 
810 		if (mp->dif_id.did_flags & MHD_DID_DUPLICATE)
811 			continue;
812 
813 		/* quietly skip drives which don't conform */
814 		if ((dnp = metadrivename(&sp, mp->dif_name, ep)) == NULL) {
815 			mdclrerror(ep);
816 			continue;
817 		}
818 
819 		/* check in set */
820 		if ((np = metaslicename(dnp, MD_SLICE0, ep)) == NULL)
821 			goto out;
822 		if (meta_check_inset(sp, np, ep) != 0) {
823 			mdclrerror(ep);
824 			continue;
825 		}
826 
827 		/*
828 		 * Add the drivename struct to the end of the
829 		 * drivenamelist but keep a pointer to the last
830 		 * element so that we don't incur the overhead
831 		 * of traversing the list each time
832 		 */
833 		tailpp = meta_drivenamelist_append_wrapper(tailpp, dnp);
834 		++cnt;
835 	}
836 	rval = cnt;
837 
838 	/* cleanup, return error */
839 out:
840 	meta_free_drive_info_list(&list);
841 	return (rval);
842 }
843