xref: /titanic_41/usr/src/cmd/lvm/util/metainit.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * initialize metadevices
31  */
32 
33 #include <meta.h>
34 
35 #include <sdssc.h>
36 #include <sys/lvm/md_mirror.h>
37 #include <syslog.h>
38 #include "meta_set_prv.h"
39 
40 /*
41  * try to initialize devices
42  */
43 #define	DO_AGAIN	0
44 #define	DONT_DO		1
45 #define	IS_DONE		2
46 
47 /*
48  * mn_send_command
49  *
50  * generate a command of the form "metainit -s setname [-n] [-f] ....."
51  *
52  * If -n option is *not* set, send the metainit command *with -n set* to
53  * all nodes first. Do this with MD_MSGF_STOP_ON_ERROR set.
54  * That means if it fails on one node, it'll return immediately,
55  * reporting the error.
56  * By doing so, we have a dryrun first that has to succeed on every node
57  * before we start the command for real.
58  * This saves us from backing out a metainit command that succeeded on
59  * some nodes but failed on one.
60  */
61 static int
62 mn_send_command(
63 	mdsetname_t	**spp,
64 	int		argc,
65 	char		**argv,
66 	mdcmdopts_t	options,
67 	int		flags,
68 	char		*context,
69 	md_error_t	*ep
70 )
71 {
72 	int		newargc;
73 	char		**newargv;
74 	int		i;
75 	int		ret;
76 	int		dryrun_only = 0;
77 
78 
79 	newargv = calloc(argc+5, sizeof (char *));
80 	newargv[0] = "metainit";
81 	newargv[1] = "-s";
82 	newargv[2] = (*spp)->setname;
83 	newargv[3] = "-n"; /* always do "-n" first */
84 	newargc = 4;
85 	if ((options & MDCMD_DOIT) == 0) {
86 		dryrun_only = 1;
87 	}
88 	if ((options & MDCMD_FORCE) != 0) {
89 		newargv[newargc] = "-f";
90 		newargc++;
91 	}
92 	for (i = 0; i < argc; i++, newargc++)
93 		newargv[newargc] = argv[i];
94 	ret = meta_mn_send_command(*spp, newargc, newargv,
95 	    flags | MD_DRYRUN | MD_NOLOG, context, ep);
96 
97 	if ((dryrun_only == 0) && (ret == 0)) {
98 		/*
99 		 * Do it for real now. Remove "-n" from the arguments and
100 		 * MD_DRYRUN from the flags. If we fail this time the master
101 		 * must panic as the mddbs may be inconsistent.
102 		 */
103 		newargv[3] = ""; /* this was "-n" before */
104 		ret = meta_mn_send_command(*spp, newargc, newargv,
105 		    flags | MD_RETRY_BUSY | MD_PANIC_WHEN_INCONSISTENT,
106 		    context,  ep);
107 	}
108 
109 	free(newargv);
110 	return (ret);
111 }
112 
113 static int
114 init_entries(
115 	mdsetname_t	**spp,
116 	md_tab_t	*tabp,
117 	mdcmdopts_t	options,
118 	uint_t		flags,
119 	bool_t		called_thru_rpc,
120 	md_error_t	*ep
121 )
122 {
123 	uint_t		cnt = 0;
124 	uint_t		line;
125 	int		rval = 0;
126 	int		ret;
127 
128 	/* for all matching entries, which haven't already been done */
129 	for (line = 0; (line < tabp->nlines); ++line) {
130 		md_tab_line_t	*linep = &tabp->lines[line];
131 
132 		/* see if already done */
133 		if (linep->flags != DO_AGAIN)
134 			continue;
135 
136 		/* try it */
137 		if ((called_thru_rpc == FALSE) &&
138 		    meta_is_mn_name(spp, linep->argv[0], ep)) {
139 			/*
140 			 * MN set, send command to all nodes
141 			 * Note that is sp is NULL, meta_is_mn_name() derives
142 			 * sp from linep->argv which is the metadevice arg
143 			 */
144 			ret = mn_send_command(spp, linep->argc, linep->argv,
145 			    options, flags, linep->context, ep);
146 		} else {
147 			ret = meta_init_name(spp, linep->argc, linep->argv,
148 			    options, ep);
149 			if (ret != 0) {
150 				if (!(flags & MD_IGNORE_STDERR)) {
151 					mderrorextra(ep, linep->context);
152 					mde_perror(ep, "");
153 					rval = -1;
154 				}
155 				mdclrerror(ep);
156 			}
157 		}
158 		if (ret == 0) {
159 			linep->flags = IS_DONE;
160 			++cnt;
161 		}
162 	}
163 
164 	/* return success */
165 	if (rval != 0)
166 		return (rval);
167 	return (cnt);
168 }
169 
170 /*
171  * initialize all devices in set
172  */
173 static int
174 init_all(
175 	mdsetname_t	**spp,
176 	mdcmdopts_t	options,
177 	bool_t		called_thru_rpc,
178 	md_error_t	*ep
179 )
180 {
181 	md_tab_t	*tabp = NULL;
182 	size_t		setlen;
183 	uint_t		more;
184 	int		done;
185 	int		eval = -1;
186 
187 	/* create local set, if necessary */
188 	if (*spp == NULL) {
189 		if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL) {
190 			mde_perror(ep, "");
191 			mdclrerror(ep);
192 			return (eval);
193 		}
194 	}
195 	setlen = strlen((*spp)->setname);
196 
197 	/*
198 	 * Only take the lock if this is not a MN set
199 	 * We can only enter this code for a MN set if we are the initiator
200 	 * and in this case, we don't want to take locks.
201 	 */
202 	if (meta_is_mn_set((*spp), ep) == 0) {
203 		/* grab set lock */
204 		if (meta_lock(*spp, TRUE, ep)) {
205 			mde_perror(ep, "");
206 			mdclrerror(ep);
207 			return (eval);
208 		}
209 
210 		/* check for ownership */
211 		if (meta_check_ownership(*spp, ep) != 0) {
212 			mde_perror(ep, "");
213 			mdclrerror(ep);
214 			return (eval);
215 		}
216 
217 		/* lock is held across init_entries */
218 		options |= MDCMD_NOLOCK;
219 	}
220 
221 	/* get md.tab, preen entries */
222 	if ((tabp = meta_tab_parse(NULL, ep)) == NULL) {
223 		mde_perror(ep, "");
224 		mdclrerror(ep);
225 		return (eval);
226 	}
227 
228 	for (more = 0; (more < tabp->nlines); ++more) {
229 		md_tab_line_t	*linep = &tabp->lines[more];
230 		char		*cname = linep->cname;
231 		char		*p;
232 		size_t		len;
233 
234 		/* better have args */
235 		assert((linep->argc > 0) && (linep->argv[0] != NULL));
236 
237 		/* only do metadevices and hotspare pools in set */
238 		if (linep->type & TAB_MD_HSP) {
239 			if ((p = strrchr(cname, '/')) == NULL) {
240 				len = 0;
241 			} else {
242 				len = p - cname;
243 			}
244 			if ((len == setlen) &&
245 			    (strncmp(cname, (*spp)->setname, len) == 0)) {
246 				linep->flags = DO_AGAIN;
247 			} else {
248 				linep->flags = DONT_DO;
249 			}
250 
251 		} else {
252 			linep->flags = DONT_DO;
253 		}
254 	}
255 
256 	eval = 1;
257 
258 	/* while more devices get made */
259 	do {
260 		done = init_entries(spp, tabp, options,
261 		    MD_IGNORE_STDERR|MD_RETRY_BUSY, called_thru_rpc, ep);
262 	} while (done > 0);
263 
264 	/* now do it and report errors */
265 	if (init_entries(spp, tabp, options, MD_RETRY_BUSY,
266 	    called_thru_rpc, ep) >= 0)
267 		eval = 0;	/* success */
268 	mdclrerror(ep);
269 
270 	/* cleanup, return success */
271 out:
272 	meta_tab_free(tabp);
273 	return (eval);
274 }
275 
276 /*
277  * initialize named device or hotspare pool
278  */
279 static int
280 init_name(
281 	mdsetname_t	**spp,
282 	int		argc,
283 	char		*argv[],
284 	mdcmdopts_t	options,
285 	int		called_thru_rpc,
286 	md_error_t	*ep
287 )
288 {
289 	md_tab_t	*tabp = NULL;
290 	md_tab_line_t	*linep = NULL;
291 	int		rval = -1;
292 	int		ret;
293 
294 	/* look in md.tab */
295 	if (argc == 1) {
296 		char		*name = argv[0];
297 
298 		/* get md.tab entries */
299 		if ((tabp = meta_tab_parse(NULL, ep)) == NULL) {
300 			if (! mdissyserror(ep, ENOENT))
301 				return (-1);
302 		}
303 
304 		/* look in md.tab */
305 		if ((linep = meta_tab_find(*spp, tabp, name, TAB_MD_HSP))
306 								!= NULL) {
307 			argc = linep->argc;
308 			argv = linep->argv;
309 		}
310 	}
311 
312 	if ((called_thru_rpc == FALSE) &&
313 	    meta_is_mn_name(spp, argv[0], ep)) {
314 		/*
315 		 * MN set, send command to all nodes
316 		 */
317 		ret = mn_send_command(spp, argc, argv, options,
318 		    MD_DISP_STDERR, NO_CONTEXT_STRING, ep);
319 	} else
320 		ret = meta_init_name(spp, argc, argv, options, ep);
321 
322 	if (ret != 0) {
323 		if (linep != NULL)
324 			mderrorextra(ep, linep->context);
325 		goto out;
326 	}
327 	rval = 0;	/* success */
328 
329 	/* cleanup, return error */
330 out:
331 	if (tabp != NULL)
332 		meta_tab_free(tabp);
333 	return (rval);
334 }
335 
336 /*
337  * print usage message
338  */
339 static void
340 usage(
341 	mdsetname_t	*sp,
342 	int		eval
343 )
344 {
345 #ifndef	lint
346 	(void) fprintf(stderr, gettext("\
347 usage:	%s [-s setname] [-n] [-f] concat/stripe numstripes\n\
348 		width component... [-i interlace]\n\
349 		[width component... [-i interlace]] [-h hotspare_pool]\n\
350 	%s [-s setname] [-n] [-f] mirror -m submirror...\n\
351 		[read_options] [write_options] [pass_num]\n\
352 	%s [-s setname] [-n] [-f] RAID -r component...\n\
353 		[-i interlace] [-h hotspare_pool]\n\
354 		[-k] [-o original_column_count]\n\
355 	%s [-s setname] [-n] [-f] hotspare_pool [hotspare...]\n\
356 	%s [-s setname] [-n] [-f] softpart -p [-A alignment]\n\
357 		[-e] device size|all\n\
358 	%s [-s setname] [-n] [-f] md.tab_entry\n\
359 	%s [-s setname] [-n] [-f] -a\n\
360 	%s -r\n"), myname, myname, myname, myname, myname, myname, myname,
361 	    myname);
362 #endif	/* ! lint */
363 	md_exit(sp, eval);
364 }
365 
366 /*
367  * If we fail during the attempt to take the auto-take disksets
368  * we need to tell the kernel to cleanup the in-core set struct
369  * so that we have a chance to take the set again later.
370  */
371 static void
372 auto_take_cleanup(mdsetname_t *sp, side_t sideno)
373 {
374 	mddb_config_t   c;
375 
376 	(void) memset(&c, 0, sizeof (c));
377 	c.c_setno = sp->setno;
378 	c.c_sideno = sideno;
379 
380 	if (metaioctl(MD_RELEASE_SET, &c, &c.c_mde, NULL) != 0) {
381 		mde_perror(&c.c_mde, "auto_take_cleanup");
382 		return;
383 	}
384 }
385 
386 /*
387  * Take the diskset.
388  *
389  * This is a clean auto-take set, so do the work to take it.
390  * This is a streamlined version of the code in meta_set_take.  We avoid the
391  * need for talking to the rpc.metad since that can't run this early during the
392  * boot.  We don't need to talk to the metad for this diskset since we're the
393  * only host in the set.
394  */
395 static void
396 take_set(md_set_record *sr)
397 {
398 	mdsetname_t		sn;
399 	md_drive_desc		*dd;
400 	md_error_t		error = mdnullerror;
401 	md_replicalist_t	*rlp = NULL;
402 	md_replicalist_t	*rl;
403 	daddr_t			nblks = 0;
404 	md_drive_record		*dr;
405 	side_t			sideno;
406 
407 	/*
408 	 * Several of the functions we call take a sp param so
409 	 * construct one from the set record.
410 	 */
411 	sn.setname = sr->sr_setname;
412 	sn.setno = sr->sr_setno;
413 	sn.setdesc = sr2setdesc(sr);
414 	sn.lockfd = MD_NO_LOCK;
415 
416 	if (sr->sr_flags & MD_SR_MB_DEVID)
417 		dd = metaget_drivedesc(&sn, MD_BASICNAME_OK | PRINT_FAST,
418 		    &error);
419 	else
420 		dd = metaget_drivedesc(&sn, MD_BASICNAME_OK, &error);
421 
422 	if (dd == NULL) {
423 	    mde_perror(&error, "");
424 	    mdclrerror(&error);
425 	    return;
426 	}
427 
428 	/*
429 	 * Skip call to tk_own_bydd.  This talks to rpc.metamhd (which we can't
430 	 * do yet) and is not needed for auto-take disksets since we are not
431 	 * doing SCSI reservations on these drives.
432 	 */
433 
434 	if (setup_db_bydd(&sn, dd, 0, &error) != 0) {
435 	    if (! mdismddberror(&error, MDE_DB_ACCOK) &&
436 		! mdismddberror(&error, MDE_DB_TAGDATA)) {
437 		/*
438 		 * Skip call to rel_own_bydd since that really just
439 		 * calls rpc.metamhd which we don't need to do,
440 		 * so there really isn't anything to rollback here.
441 		 */
442 		mde_perror(&error, "");
443 		mdclrerror(&error);
444 		return;
445 	    }
446 	    mdclrerror(&error);
447 	}
448 
449 	if ((sideno = getmyside(&sn, &error)) == MD_SIDEWILD) {
450 	    mde_perror(&error, "");
451 	    return;
452 	}
453 
454 	if (snarf_set(&sn, FALSE, &error) != 0) {
455 	    if (mdismddberror(&error, MDE_DB_STALE) ||
456 		mdismddberror(&error, MDE_DB_TAGDATA) ||
457 		! mdismddberror(&error, MDE_DB_NODB) &&
458 		! mdismddberror(&error, MDE_DB_NOTOWNER)) {
459 		/*
460 		 * rollback
461 		 * Normally MDE_DB_STALE or MDE_DB_TAGDATA
462 		 * would still keep the set but in this case we don't
463 		 * want to do that.  This will probably result in the
464 		 * boot going in to single-user since we won't have the
465 		 * set so any attempted mounts using the set's metadevices
466 		 * will fail.  However, that is a "good thing" so the
467 		 * sysadmin can fix the set.  Normally they would see
468 		 * all of these problems when they ran the take and be
469 		 * able to immediately fix the problem.
470 		 */
471 		mde_perror(&error, "");
472 		auto_take_cleanup(&sn, sideno);
473 		return;
474 	    }
475 	}
476 
477 	/*
478 	 * Call metareplicalist and upd_dr_dbinfo.
479 	 * Most of that code is only needed to synchronize amongst the multiple
480 	 * hosts in a set, which is not applicable in our case.  But we do a
481 	 * subset here to handle the case when the user had been
482 	 * adding/deleting/balancing mddbs when this node panic'd.  We are
483 	 * synchronizing the ondisk mddbs to the list of drive records stored
484 	 * in the local mddb.
485 	 */
486 	if (metareplicalist(&sn, (MD_BASICNAME_OK | PRINT_FAST), &rlp, &error)
487 	    < 0) {
488 	    /* rollback */
489 	    mde_perror(&error, "");
490 	    auto_take_cleanup(&sn, sideno);
491 	    return;
492 	}
493 
494 	/*
495 	 * The following code is equivalent to upd_dr_dbinfo for syncronizing
496 	 * the local host only.  That function is normally run through the
497 	 * metad with a local and daemon side but we'll do all of the work
498 	 * here.
499 	 */
500 
501 	/* find the smallest existing replica */
502 	for (rl = rlp; rl != NULL; rl = rl->rl_next) {
503 	    md_replica_t *r;
504 
505 	    r = rl->rl_repp;
506 	    nblks = ((nblks == 0) ? r->r_nblk : min(r->r_nblk, nblks));
507 	}
508 
509 	if (nblks <= 0)
510 	    nblks = MD_DBSIZE;
511 
512 	for (dr = sr->sr_drivechain; dr; dr = dr->dr_next) {
513 	    int			dbcnt;
514 	    mddrivename_t	*dnp;
515 	    md_replicalist_t	*rl;
516 
517 		/*
518 		 * The cname style for dnp and replica list will be same since
519 		 * both use the the same flags MD_BASICNAME_OK|PRINT_FAST which
520 		 * will always provide the cached value.
521 		 */
522 	    if ((dnp = metadrivename_withdrkey(&sn, sideno, dr->dr_key,
523 		MD_BASICNAME_OK | PRINT_FAST, &error)) == NULL) {
524 		mde_perror(&error, "");
525 		metafreereplicalist(rlp);
526 		auto_take_cleanup(&sn, sideno);
527 		return;
528 	    }
529 
530 	    dbcnt = 0;
531 	    /* see how many replicas are on this drive */
532 	    for (rl = rlp; rl != NULL; rl = rl->rl_next) {
533 		if (strcmp(rl->rl_repp->r_namep->drivenamep->cname, dnp->cname)
534 		    == 0)
535 		    dbcnt++;
536 	    }
537 
538 	    /* Adjust the fields in the copy */
539 	    dr->dr_dbcnt = dbcnt;
540 	    dr->dr_dbsize = dbcnt > 0 ? nblks : 0;
541 	}
542 
543 	/*
544 	 * If the set doesn't have the MD_SR_MB_DEVID bit set, i.e
545 	 * the drives in the set don't have the device id information,
546 	 * then stick it in if possible.
547 	 *
548 	 * If updating the master block fails for whatever reason, it's
549 	 * okay. It just means the disk(s) in the diskset won't be self
550 	 * identifying.
551 	 */
552 	if (!(sr->sr_flags & MD_SR_MB_DEVID)) {
553 		if (meta_update_mb(&sn, dd, &error) == 0) {
554 			sr->sr_flags |= MD_SR_MB_DEVID;
555 			mdclrerror(&error);
556 		}
557 	}
558 
559 	commitset(sr, FALSE, &error);
560 
561 	metafreereplicalist(rlp);
562 
563 	/*
564 	 * This finishes up the logical equivalent of meta_set_take.
565 	 */
566 	if (meta_resync_all(&sn, MD_DEF_RESYNC_BUF_SIZE, &error) != 0) {
567 	    mde_perror(&error, "");
568 	    mdclrerror(&error);
569 	}
570 }
571 
572 /*
573  * Take the disksets that are marked to be taken at boot time.
574  */
575 static void
576 auto_take_sets()
577 {
578 	int		max_sets;
579 	int		i;
580 	md_error_t	error = mdnullerror;
581 	char		*hostname;
582 
583 	if ((max_sets = get_max_sets(&error)) == 0)
584 	    return;
585 
586 	if (!mdisok(&error)) {
587 	    mde_perror(&error, "");
588 	    return;
589 	}
590 
591 	/* set up so auto-take errors also go to syslog */
592 	openlog("metainit", LOG_ODELAY, LOG_USER);
593 	metasyslog = 1;
594 
595 	hostname = mynode();
596 
597 	/*
598 	 * For each possible set number (skip set 0 which is the unnamed local
599 	 * set), see if we really have a diskset.  If so, check if auto-take
600 	 * is enabled.
601 	 *
602 	 * In order to take the set it must have drives and it must not be
603 	 * stuck in mid-add.  The sr_validate routine within rpc.metad will
604 	 * delete sets that are in mid-add when it runs.
605 	 */
606 	for (i = 1; i < max_sets; i++) {
607 	    md_set_record	*sr;
608 
609 	    if ((sr = metad_getsetbynum(i, &error)) == NULL) {
610 		mdclrerror(&error);
611 		continue;
612 	    }
613 
614 	    if (sr->sr_flags & MD_SR_AUTO_TAKE && !(sr->sr_flags & MD_SR_ADD)) {
615 		int	j;
616 		int	cnt = 0;
617 		int	host_mismatch = 0;
618 		int	take = 0;
619 		md_drive_record	*dr;
620 
621 		/* check for host renames or multiple hosts in set */
622 		for (j = 0; j < MD_MAXSIDES; j++) {
623 		    /* Skip empty slots */
624 		    if (sr->sr_nodes[j][0] == '\0')
625 			continue;
626 
627 		    cnt++;
628 		    if (strcmp(sr->sr_nodes[j], hostname) != 0)
629 			host_mismatch = 1;
630 		}
631 
632 		/* paranoid check that we're the only host in the set */
633 		if (cnt > 1) {
634 			md_eprintf(gettext(
635 		"diskset %s: auto-take enabled and multiple hosts in set\n"),
636 			    sr->sr_setname);
637 			continue;
638 		}
639 
640 		if (host_mismatch) {
641 			/* The host was renamed, repair the set. */
642 			for (j = 0; j < MD_MAXSIDES; j++) {
643 				/* Skip empty slots */
644 				if (sr->sr_nodes[j][0] == '\0')
645 					continue;
646 
647 				(void) strncpy(sr->sr_nodes[j], hostname,
648 				    sizeof (sr->sr_nodes[j]));
649 				commitset(sr, FALSE, &error);
650 				if (!mdisok(&error)) {
651 					mde_perror(&error, "");
652 					mdclrerror(&error);
653 				} else {
654 					md_eprintf(gettext(
655 			"new hostname %s, update auto-take diskset %s\n"),
656 						hostname, sr->sr_setname);
657 				}
658 				break;
659 			}
660 		}
661 
662 		/* set must have at least one drive to be taken */
663 		for (dr = sr->sr_drivechain; dr != NULL; dr = dr->dr_next) {
664 			/* ignore drives in mid-add */
665 			if (!(dr->dr_flags & MD_DR_ADD)) {
666 			    take = 1;
667 			    break;
668 			}
669 		}
670 
671 		if (take)
672 			take_set(sr);
673 		else
674 			md_eprintf(gettext(
675 		"diskset %s: auto-take enabled but set has no drives\n"),
676 			    sr->sr_setname);
677 	    }
678 	}
679 }
680 
681 /*
682  * mainline. crack command line arguments.
683  */
684 int
685 main(
686 	int		argc,
687 	char		*argv[]
688 )
689 {
690 	char		*sname = NULL;
691 	mdsetname_t	*sp = NULL;
692 	enum action {
693 		NONE,
694 		INIT,
695 		ALL
696 	}		todo = NONE;
697 	mdcmdopts_t	options = (MDCMD_DOIT | MDCMD_PRINT);
698 	int		c;
699 	md_error_t	status = mdnullerror;
700 	md_error_t	*ep = &status;
701 
702 	md_error_t	dummystatus = mdnullerror;
703 	md_error_t	*dummyep = &dummystatus;
704 	int		eval = 1;
705 	int		error;
706 	bool_t		called_thru_rpc = FALSE;
707 	char		*cp;
708 
709 	/*
710 	 * Get the locale set up before calling any other routines
711 	 * with messages to ouput.  Just in case we're not in a build
712 	 * environment, make sure that TEXT_DOMAIN gets set to
713 	 * something.
714 	 */
715 #if !defined(TEXT_DOMAIN)
716 #define	TEXT_DOMAIN "SYS_TEST"
717 #endif
718 	(void) setlocale(LC_ALL, "");
719 	(void) textdomain(TEXT_DOMAIN);
720 	if ((cp = strstr(argv[0], ".rpc_call")) != NULL) {
721 		*cp = '\0'; /* cut off ".rpc_call" */
722 		called_thru_rpc = TRUE;
723 	} else {
724 		if (sdssc_bind_library() == SDSSC_OKAY)
725 			if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
726 						&error) == SDSSC_PROXY_DONE)
727 				exit(error);
728 	}
729 
730 	/* initialize */
731 	if (md_init(argc, argv, 0, 1, ep) != 0 ||
732 			meta_check_root(ep) != 0) {
733 		mde_perror(ep, "");
734 		md_exit(sp, 1);
735 	}
736 
737 	/* parse args */
738 	optind = 1;
739 	opterr = 1;
740 	while ((c = getopt(argc, argv, "afhnrs:?")) != -1) {
741 		switch (c) {
742 
743 		/* help */
744 		case 'h':
745 			usage(sp, 0);
746 			break;
747 
748 		/* set name */
749 		case 's':
750 			sname = optarg;
751 			break;
752 
753 		/* all devices in md.tab */
754 		case 'a':
755 			if (todo != NONE)
756 				usage(sp, 1);
757 			todo = ALL;
758 			options |= MDCMD_ALLOPTION;
759 			break;
760 		/* check for validity, but don't really init */
761 		case 'n':
762 			options &= ~MDCMD_DOIT;
763 			break;
764 
765 		/* for recovery */
766 		case 'r':
767 			if (todo != NONE)
768 				usage(sp, 1);
769 			todo = INIT;
770 			break;
771 
772 		/* mounted and swapped components are OK */
773 		case 'f':
774 			options |= MDCMD_FORCE;
775 			break;
776 
777 		case '?':
778 			if (optopt == '?')
779 				usage(sp, 0);
780 			/*FALLTHROUGH*/
781 		default:
782 			usage(sp, 1);
783 			break;
784 		}
785 	}
786 
787 	if (sname != NULL) {
788 		if ((sp = metasetname(sname, ep)) == NULL) {
789 			mde_perror(ep, "");
790 			md_exit(sp, 1);
791 		}
792 	}
793 	argc -= optind;
794 	argv += optind;
795 	if (todo == NONE) {
796 		if (argc <= 0) {
797 			usage(sp, 1);
798 		}
799 	} else if (argc > 0) {
800 		usage(sp, 1);
801 	}
802 
803 
804 	/* setup database locations */
805 	if (meta_setup_db_locations(ep) != 0) {
806 		mde_perror(ep, "");
807 		if (mdismddberror(ep, MDE_DB_STALE))
808 			md_exit(sp, 66);
809 		if (! mdiserror(ep, MDE_MDDB_CKSUM))	/* relatively benign */
810 			md_exit(sp, 1);
811 	}
812 	if (todo == INIT) {		/* load and take auto-take sets */
813 		auto_take_sets();
814 		md_exit(sp, 0);
815 	} else if (todo == ALL) {	/* initialize all devices in md.tab */
816 		eval = init_all(&sp, options, called_thru_rpc, ep);
817 	} else {			/* initialize the named device */
818 		eval = 0;
819 		if (init_name(&sp, argc, argv, options, called_thru_rpc,
820 		    ep) != 0) {
821 			/*
822 			 * If we're dealing with MN metadevices and we are
823 			 * directly called, then the appropriate error message
824 			 * has already been displayed. So just exit.
825 			 */
826 			if (meta_is_mn_set(sp, dummyep) && (!called_thru_rpc)) {
827 				md_exit(sp, 1);
828 			}
829 			mde_perror(ep, "");
830 			mdclrerror(ep);
831 			eval = 1;
832 			goto nomdcf;
833 		}
834 	}
835 
836 domdcf:
837 	/* update md.cf, return success */
838 	if (meta_update_md_cf(sp, ep) != 0) {
839 		mde_perror(ep, "");
840 		eval = 1;
841 	}
842 
843 nomdcf:
844 	md_exit(sp, eval);
845 	/*NOTREACHED*/
846 	return (eval);
847 }
848