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 */
51mhd_mhiargs_t	defmhiargs = {
52	1000,			/* failfast */
53	{ 6000, 6000, 30000 }	/* take ownership */
54};
55
56/* RPC timeouts */
57static md_timeval32_t	tk_own_timeout  = { 24 * 60 * 60, 0 };	/* 1 day */
58static md_timeval32_t	rel_own_timeout = { 24 * 60 * 60, 0 };	/* 1 day */
59
60/*
61 * RPC handle
62 */
63typedef struct {
64	char	*hostname;
65	CLIENT	*clientp;
66} mhd_handle_t;
67
68/*
69 * close RPC connection
70 */
71static void
72close_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 */
90static mhd_handle_t *
91open_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 */
126int
127mhstealerror(
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 */
155out:
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 */
165static int
166do_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 */
180int
181meta_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 */
266out:
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 */
277int
278tk_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 */
311int
312meta_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 */
388out:
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 */
399int
400rel_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 */
432int
433meta_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 */
515out:
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 */
526md_disk_status_list_t *
527meta_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 */
551void
552meta_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 */
568void
569meta_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 */
580static int
581compare_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
629u:	return (strcmp(di1->dif_name, di2->dif_name));
630}
631
632static void
633sort_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 */
646int
647meta_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 */
700out:
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
708static void
709load_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*/
779int
780meta_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 */
839out:
840	meta_free_drive_info_list(&list);
841	return (rval);
842}
843