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