xref: /titanic_41/usr/src/cmd/lvm/util/metadb.c (revision fd9cb95cbb2f626355a60efb9d02c5f0a33c10e6)
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  * Metadevice database utility.
31  */
32 
33 #include <meta.h>
34 #define	MDDB
35 #include <sys/lvm/md_mddb.h>
36 #include <sdssc.h>
37 
38 enum mddb_cmd {none, attach, detach, patch, infolong, infoshort};
39 
40 extern int	procsigs(int block, sigset_t *oldsigs, md_error_t *ep);
41 
42 static void
43 usage(
44 	mdsetname_t	*sp,
45 	char		*string
46 )
47 {
48 	if ((string != NULL) && (*string != '\0'))
49 		md_eprintf("%s\n", string);
50 
51 	(void) fprintf(stderr, gettext(
52 "usage:  %s [-s setname] -a [options] mddbnnn\n"
53 "	%s [-s setname] -a [options] device ...\n"
54 "	%s [-s setname] -d [options] mddbnnn\n"
55 "	%s [-s setname] -d [options] device ...\n"
56 "	%s [-s setname] -i \n"
57 "	%s -p [options] [ mddb.cf-file ]\n"
58 "options:\n"
59 "-c count	number of replicas (for use with -a only)\n"
60 "-f		force adding or deleting of replicas\n"
61 "-k filename	alternate /etc/system file\n"
62 "-l length	specify size of replica (for use with -a only)\n"),
63 	    myname, myname, myname, myname, myname, myname);
64 
65 	md_exit(sp, (string == NULL) ? 0 : 1);
66 }
67 
68 static mdname_t *
69 make_dbname(
70 	mdsetname_t	*sp,
71 	mdnamelist_t	**nlp,
72 	char		*name,
73 	md_error_t	*ep
74 )
75 {
76 	mdname_t	*np;
77 
78 	if ((np = metaname(&sp, name, ep)) == NULL)
79 		return (NULL);
80 
81 	return (metanamelist_append(nlp, np));
82 }
83 
84 static mdnamelist_t *
85 get_dbnames_fromfile(
86 	mdsetname_t	*sp,
87 	mdnamelist_t	**nlp,
88 	char		*tabname,
89 	int		*dbsize,
90 	int		*dbcnt,
91 	int		*default_size,
92 	md_error_t	*ep
93 )
94 {
95 	md_tab_t	*tabp = NULL;
96 	md_tab_line_t	*linep = NULL;
97 	int		argc;
98 	char		**argv;
99 	char		*context;
100 	int		save = optind;
101 	int		c;
102 
103 	/* look in md.tab */
104 	if ((tabp = meta_tab_parse(NULL, ep)) == NULL) {
105 		if (! mdissyserror(ep, ENOENT))
106 			mde_perror(ep, "");
107 		mdclrerror(ep);
108 		return (NULL);
109 	}
110 
111 	if ((linep = meta_tab_find(sp, tabp, tabname, TAB_MDDB)) == NULL) {
112 		(void) mdsyserror(ep, ENOENT, tabname);
113 		goto out;
114 	}
115 	argc = linep->argc;
116 	argv = linep->argv;
117 	context = linep->context;
118 
119 	/* parse up entry */
120 	optind = 1;
121 	opterr = 1;
122 	while ((c = getopt(argc, argv, "c:l:")) != -1) {
123 		switch (c) {
124 		case 'c':
125 			if (sscanf(optarg, "%d", dbcnt) != 1) {
126 				md_eprintf("%s: %s\n",
127 				    context, gettext("bad format"));
128 				usage(sp, "");
129 			}
130 			break;
131 
132 		case 'l':
133 			if (sscanf(optarg, "%d", dbsize) != 1) {
134 				md_eprintf("%s: %s\n",
135 				    context, gettext("bad format"));
136 				usage(sp, "");
137 			}
138 			*default_size = FALSE;
139 			break;
140 
141 		default:
142 			usage(sp, "");
143 		}
144 	}
145 	argc -= optind;
146 	argv += optind;
147 	for (; (argc > 0); --argc, ++argv) {
148 		char	*token = argv[0];
149 
150 		if (make_dbname(sp, nlp, token, ep) == NULL) {
151 			metafreenamelist(*nlp);
152 			*nlp = NULL;
153 			goto out;
154 		}
155 	}
156 
157 	/* cleanup, return list */
158 out:
159 	if (tabp != NULL)
160 		meta_tab_free(tabp);
161 	optind = save;
162 	return (*nlp);
163 }
164 
165 /*
166  * built list of all devices which are to be detached
167  */
168 static mdnamelist_t *
169 build_a_namelist(
170 	mdsetname_t	*sp,
171 	int		argc,
172 	char		**argv,
173 	md_error_t	*ep
174 )
175 {
176 	int		i;
177 	int		dbsize, dbcnt, default_size;
178 	mdnamelist_t	*dbnlp = NULL;
179 
180 	for (i = 0; i < argc; i++) {
181 		if (strncmp(argv[i], "mddb", 4) == 0) {
182 			if (get_dbnames_fromfile(sp, &dbnlp, argv[i],
183 			    &dbsize, &dbcnt, &default_size, ep) == NULL) {
184 				/* don't freelist here - already been done */
185 				return (NULL);
186 			}
187 			continue;
188 		}
189 		if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) {
190 			metafreenamelist(dbnlp);
191 			return (NULL);
192 		}
193 	}
194 
195 	return (dbnlp);
196 }
197 
198 
199 /*
200  * built the next list of devices which are to be attached
201  * that have the same size and count of replicas.
202  */
203 static mdnamelist_t *
204 build_next_namelist(
205 	mdsetname_t	*sp,
206 	int		argc,
207 	char		**argv,
208 	int		*arg_index,
209 	int		*dbsize,
210 	int		*dbcnt,
211 	int		*default_size,
212 	md_error_t	*ep
213 )
214 {
215 	int		i;
216 	mdnamelist_t	*dbnlp = NULL;
217 
218 	for (i = *arg_index; i < argc; i++) {
219 		if (strncmp(argv[i], "mddb", 4) == 0) {
220 			/*
221 			 * If we have stuff in the namelist
222 			 * return it before processing the mddb entry.
223 			 */
224 			if (dbnlp) {
225 				*arg_index = i;
226 				return (dbnlp);
227 			}
228 			if (get_dbnames_fromfile(sp, &dbnlp, argv[i],
229 			    dbsize, dbcnt, default_size, ep) == NULL) {
230 				/* don't freelist here - already been done */
231 				return (NULL);
232 			}
233 			*arg_index = i + 1;
234 			return (dbnlp);
235 		}
236 		if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) {
237 			metafreenamelist(dbnlp);
238 			return (NULL);
239 		}
240 	}
241 	*arg_index = argc;
242 	return (dbnlp);
243 }
244 
245 
246 static int
247 chngdb(
248 	mdsetname_t	*sp,
249 	enum mddb_cmd	cmd,
250 	int		argc,
251 	char		*argv[],
252 	uint_t		options,
253 	md_error_t	*ep
254 )
255 {
256 	int		c;
257 	int		i;
258 	md_error_t	xep = mdnullerror;
259 	mdnamelist_t	*dbnlp = NULL;
260 	int		dbsize = MD_DBSIZE;
261 	int		maxblks = MDDB_MAXBLKS;
262 	int		minblks = MDDB_MINBLKS;
263 	int		dbcnt = 1;
264 	mdforceopts_t	force = MDFORCE_NONE;
265 	int		rval = 0;
266 	char		*sysfilename = NULL;
267 	int		default_size = TRUE;
268 	md_set_desc	*sd;
269 	md_setkey_t	*cl_sk;
270 	md_mnnode_desc	*nd;
271 	int		suspend1_flag = 0;
272 
273 	/* reset and parse args */
274 	optind = 1;
275 	opterr = 1;
276 	while ((c = getopt(argc, argv, "ac:dfk:pl:s:")) != -1) {
277 		switch (c) {
278 		case 'a':
279 			break;
280 		case 'c':
281 			if (sscanf(optarg, "%d", &dbcnt) != 1) {
282 				md_eprintf("%s: %s\n",
283 				    optarg, gettext("bad format"));
284 				usage(sp, "");
285 			}
286 			break;
287 		case 'd':
288 			break;
289 		case 'f':
290 			force = MDFORCE_LOCAL;
291 			break;
292 		case 'k':
293 			sysfilename = optarg;
294 			break;
295 		case 'l':
296 			if (sscanf(optarg, "%d", &dbsize) != 1) {
297 				md_eprintf("%s: %s\n",
298 				    optarg, gettext("bad format"));
299 				usage(sp, "");
300 			}
301 			default_size = FALSE;
302 			break;
303 		case 'p':
304 			break;
305 		case 's':
306 			break;
307 		default:
308 			usage(sp, "");
309 		}
310 	}
311 
312 	/*
313 	 * If it is a multinode diskset, use appropriate metadb size.
314 	 */
315 	if (! metaislocalset(sp)) {
316 		if ((sd = metaget_setdesc(sp, ep)) == NULL)
317 			return (-1);
318 
319 		if (MD_MNSET_DESC(sd)) {
320 			maxblks = MDDB_MN_MAXBLKS;
321 			minblks = MDDB_MN_MINBLKS;
322 			if (default_size)
323 				dbsize = MD_MN_DBSIZE;
324 		}
325 	}
326 
327 	if (dbsize > maxblks)
328 		usage(sp, gettext("size (-l) is too big"));
329 
330 
331 	if (dbsize < minblks)
332 		usage(sp, gettext("size (-l) is too small"));
333 
334 	if (dbcnt < 1)
335 		usage(sp, gettext(
336 		    "count (-c) must be 1 or more"));
337 
338 
339 	argc -= optind;
340 	argv += optind;
341 	if (argc <= 0) {
342 		usage(sp, gettext(
343 		    "no devices specified to attach or detach"));
344 	}
345 
346 	if (! metaislocalset(sp)) {
347 
348 		if (MD_MNSET_DESC(sd)) {
349 			md_error_t xep = mdnullerror;
350 			sigset_t sigs;
351 
352 			/* Make sure we are blocking all signals */
353 			if (procsigs(TRUE, &sigs, &xep) < 0)
354 				mdclrerror(&xep);
355 
356 			/*
357 			 * Lock out other metaset or metadb commands
358 			 * across the diskset.
359 			 */
360 			nd = sd->sd_nodelist;
361 			while (nd) {
362 				if ((force & MDFORCE_LOCAL) &&
363 				    strcmp(nd->nd_nodename, mynode()) != 0) {
364 					nd = nd->nd_next;
365 					continue;
366 				}
367 
368 				if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
369 					nd = nd->nd_next;
370 					continue;
371 				}
372 
373 				if (clnt_lock_set(nd->nd_nodename, sp, ep)) {
374 					rval = -1;
375 					goto done;
376 				}
377 				nd = nd->nd_next;
378 			}
379 			/*
380 			 * Lock out other meta* commands by suspending
381 			 * class 1 messages across the diskset.
382 			 */
383 			nd = sd->sd_nodelist;
384 			while (nd) {
385 				if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
386 					nd = nd->nd_next;
387 					continue;
388 				}
389 
390 				if (clnt_mdcommdctl(nd->nd_nodename,
391 				    COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1,
392 				    MD_MSCF_NO_FLAGS, ep)) {
393 					rval = -1;
394 					goto done;
395 				}
396 				suspend1_flag = 1;
397 				nd = nd->nd_next;
398 			}
399 		} else {
400 			/* Lock the set on current set members */
401 			for (i = 0; i < MD_MAXSIDES; i++) {
402 				/* Skip empty slots */
403 				if (sd->sd_nodes[i][0] == '\0')
404 					continue;
405 
406 				if ((force & MDFORCE_LOCAL) &&
407 				    strcmp(sd->sd_nodes[i], mynode()) != 0)
408 					continue;
409 
410 				if (clnt_lock_set(sd->sd_nodes[i], sp, ep)) {
411 					rval = -1;
412 					goto done;
413 				}
414 			}
415 		}
416 
417 		force |= MDFORCE_SET_LOCKED;
418 		options |= MDCHK_SET_LOCKED;
419 	}
420 
421 	if (cmd == detach) {
422 		if ((dbnlp = build_a_namelist(sp, argc, argv, ep)) == NULL) {
423 			rval = -1;
424 			goto done;
425 		}
426 
427 		rval = meta_db_detach(sp, dbnlp, force, sysfilename, ep);
428 
429 		metafreenamelist(dbnlp);
430 	}
431 
432 	if (cmd == attach) {
433 		daddr_t	nblks = 0;
434 		int	arg_index = 0;
435 		int	saved_dbsize = dbsize;
436 		int	saved_dbcnt = dbcnt;
437 		int	saved_default_size = default_size;
438 
439 		if (force & MDFORCE_LOCAL)
440 			options |= MDCHK_SET_FORCE;
441 
442 		if (default_size)
443 			if ((nblks = meta_db_minreplica(sp, ep)) < 0)
444 				mdclrerror(ep);
445 		/*
446 		 * Loop through build a new namelist
447 		 * for each "mddb" entry or the devices list
448 		 * on the command line.  This allows each "mddb"
449 		 * entry to have unique dbsize and dbcnt.
450 		 */
451 		while (arg_index < argc) {
452 
453 			dbnlp = build_next_namelist(sp, argc, argv,
454 			    &arg_index, &dbsize, &dbcnt, &default_size, ep);
455 			if (dbnlp == NULL) {
456 				rval = -1;
457 				goto done;
458 			}
459 			/*
460 			 * If using the default size,
461 			 *   then let's adjust the default to the minimum
462 			 *   size currently in use.
463 			 */
464 			if (default_size && (nblks > 0))
465 				dbsize = nblks;	/* adjust replica size */
466 
467 			rval = meta_db_attach(sp, dbnlp, options, NULL, dbcnt,
468 			    dbsize, sysfilename, ep);
469 			if (rval) {
470 				metafreenamelist(dbnlp);
471 				break;
472 			}
473 			dbsize = saved_dbsize;
474 			dbcnt = saved_dbcnt;
475 			default_size = saved_default_size;
476 
477 			metafreenamelist(dbnlp);
478 		}
479 	}
480 
481 done:
482 	if (! metaislocalset(sp)) {
483 		cl_sk = cl_get_setkey(sp->setno, sp->setname);
484 		if (MD_MNSET_DESC(sd)) {
485 			/*
486 			 * Unlock diskset by resuming
487 			 * class 1 messages across the diskset.
488 			 */
489 			if (suspend1_flag) {
490 				nd = sd->sd_nodelist;
491 				while (nd) {
492 					if (!(nd->nd_flags &
493 					    MD_MN_NODE_ALIVE)) {
494 						nd = nd->nd_next;
495 						continue;
496 					}
497 
498 					if (clnt_mdcommdctl(nd->nd_nodename,
499 					    COMMDCTL_RESUME, sp,
500 					    MD_MSG_CLASS1,
501 					    MD_MSCF_NO_FLAGS, &xep)) {
502 						mde_perror(&xep, "");
503 						mdclrerror(&xep);
504 					}
505 					nd = nd->nd_next;
506 				}
507 			}
508 			nd = sd->sd_nodelist;
509 			while (nd) {
510 				if ((force & MDFORCE_LOCAL) &&
511 				    strcmp(nd->nd_nodename, mynode()) != 0) {
512 					nd = nd->nd_next;
513 					continue;
514 				}
515 				if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
516 					nd = nd->nd_next;
517 					continue;
518 				}
519 
520 				if (clnt_unlock_set(nd->nd_nodename, cl_sk,
521 				    &xep))
522 					mdclrerror(&xep);
523 				nd = nd->nd_next;
524 			}
525 		} else {
526 			for (i = 0; i < MD_MAXSIDES; i++) {
527 				/* Skip empty slots */
528 				if (sd->sd_nodes[i][0] == '\0')
529 					continue;
530 
531 				if ((force & MDFORCE_LOCAL) &&
532 				    strcmp(sd->sd_nodes[i], mynode()) != 0)
533 					continue;
534 
535 				if (clnt_unlock_set(sd->sd_nodes[i], cl_sk,
536 				    &xep))
537 					mdclrerror(&xep);
538 			}
539 		}
540 		cl_set_setkey(NULL);
541 	}
542 
543 	return (rval);
544 }
545 
546 static int
547 info(
548 	mdsetname_t	*sp,
549 	enum mddb_cmd	cmd,
550 	int		print_headers,
551 	int		print_footers,
552 	md_error_t	*ep
553 )
554 {
555 	md_replicalist_t	*rlp = NULL;
556 	md_replicalist_t	*rl;
557 	md_replica_t		*r;
558 	int			i;
559 	char			*unk_str = NULL;
560 
561 	/* get list of replicas, quit if none */
562 	if (metareplicalist(sp, (MD_BASICNAME_OK | PRINT_FAST), &rlp, ep) < 0)
563 		return (-1);
564 	else if (rlp == NULL)
565 		return (0);
566 
567 	if (print_headers) {
568 		(void) printf("\t%5.5s\t\t%9.9s\t%11.11s\n", gettext("flags"),
569 		    gettext("first blk"), gettext("block count"));
570 	}
571 
572 	unk_str = gettext("unknown");
573 	for (rl = rlp; rl != NULL; rl = rl->rl_next) {
574 		r = rl->rl_repp;
575 
576 		for (i = 0; i < MDDB_FLAGS_LEN; i++) {
577 			if (r->r_flags & (1 << i))
578 				(void) putchar(MDDB_FLAGS_STRING[i]);
579 			else
580 				(void) putchar(' ');
581 		}
582 
583 		if ((r->r_blkno == -1) && (r->r_nblk == -1)) {
584 			(void) printf("\t%7.7s\t\t%7.7s\t", unk_str, unk_str);
585 		} else if (r->r_nblk == -1) {
586 			(void) printf("\t%ld\t\t%7.7s\t", r->r_blkno, unk_str);
587 		} else {
588 			(void) printf("\t%ld\t\t%ld\t", r->r_blkno, r->r_nblk);
589 		}
590 
591 		(void) printf("\t%s\n", r->r_namep->bname);
592 
593 	}
594 
595 	metafreereplicalist(rlp);
596 
597 	if (cmd == infoshort)
598 		return (0);
599 
600 	if (!print_footers)
601 		return (0);
602 
603 	(void) printf(gettext(
604 	    " r - replica does not have device relocation information\n"
605 	    " o - replica active prior to last mddb configuration change\n"
606 	    " u - replica is up to date\n"
607 	    " l - locator for this replica was read successfully\n"
608 	    " c - replica's location was in %s\n"
609 	    " p - replica's location was patched in kernel\n"
610 	    " m - replica is master, this is replica selected as input\n"
611 	    " W - replica has device write errors\n"
612 	    " a - replica is active, commits are occurring to this replica\n"
613 	    " M - replica had problem with master blocks\n"
614 	    " D - replica had problem with data blocks\n"
615 	    " F - replica had format problems\n"
616 	    " S - replica is too small to hold current data base\n"
617 	    " R - replica had device read errors\n"),
618 	    META_DBCONF);
619 	return (0);
620 }
621 
622 int
623 main(int argc, char **argv)
624 {
625 	mdsetname_t	*sp = NULL;
626 	int		c;
627 	enum mddb_cmd	cmd = none;
628 	char		*sname = MD_LOCAL_NAME;
629 	char		*cffilename = NULL;
630 	char		*sysfilename = NULL;
631 	int		forceflg = FALSE;
632 	mdchkopts_t	options = 0;
633 	md_error_t	status = mdnullerror;
634 	md_error_t	*ep = &status;
635 	int		error;
636 	md_set_desc	*sd;
637 	int		multi_node = 0;
638 
639 	/*
640 	 * Get the locale set up before calling any other routines
641 	 * with messages to ouput.  Just in case we're not in a build
642 	 * environment, make sure that TEXT_DOMAIN gets set to
643 	 * something.
644 	 */
645 #if !defined(TEXT_DOMAIN)
646 #define	TEXT_DOMAIN "SYS_TEST"
647 #endif
648 	(void) setlocale(LC_ALL, "");
649 	(void) textdomain(TEXT_DOMAIN);
650 
651 	if (sdssc_bind_library() == SDSSC_OKAY)
652 		if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
653 		    &error) == SDSSC_PROXY_DONE)
654 			exit(error);
655 
656 	/* parse args */
657 	optind = 1;
658 	opterr = 1;
659 
660 	/* initialize */
661 	if (md_init(argc, argv, 0, 1, ep) != 0) {
662 		mde_perror(ep, "");
663 		md_exit(sp, 1);
664 	}
665 
666 	/* parse args */
667 	optind = 1;
668 	opterr = 1;
669 	while ((c = getopt(argc, argv, "ac:dfhik:l:ps:?")) != -1) {
670 		switch (c) {
671 		case 'a':
672 			cmd = attach;
673 			break;
674 		case 'c':
675 			break;
676 		case 'd':
677 			cmd = detach;
678 			break;
679 		case 'f':
680 			forceflg = TRUE;
681 			break;
682 		case 'h':
683 			usage(sp, (char *)0);
684 			break;
685 		case 'i':
686 			cmd = infolong;
687 			break;
688 		case 'k':
689 			sysfilename = optarg;
690 			break;
691 		case 'l':
692 			break;
693 		case 'p':
694 			cmd = patch;
695 			break;
696 		case 's':
697 			sname = optarg;
698 			break;
699 
700 		case '?':
701 			if (optopt == '?')
702 				usage(sp, NULL);
703 			/*FALLTHROUGH*/
704 		default:
705 			usage(sp, gettext("unknown command"));
706 		}
707 	}
708 	if (cmd == none)
709 		cmd = infoshort;
710 
711 	/* get set context */
712 	if ((sp = metasetname(sname, ep)) == NULL) {
713 		mde_perror(ep, "");
714 		md_exit(sp, 1);
715 	}
716 
717 	/* print status */
718 	if (cmd == infoshort || cmd == infolong) {
719 		if (optind != argc)
720 			usage(sp, gettext(
721 				"too many arguments"));
722 
723 		if (info(sp, cmd, 1, 1, ep)) {
724 			mde_perror(ep, "");
725 			md_exit(sp, 1);
726 		}
727 
728 		if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) {
729 			mde_perror(ep, "");
730 			md_exit(sp, 1);
731 		}
732 
733 		md_exit(sp, 0);
734 	}
735 
736 	if (meta_check_root(ep) != 0) {
737 		mde_perror(ep, "");
738 		md_exit(sp, 1);
739 	}
740 
741 	if (! metaislocalset(sp)) {
742 		if ((sd = metaget_setdesc(sp, ep)) == NULL) {
743 			mde_perror(ep, "");
744 			md_exit(sp, 1);
745 		}
746 		if (MD_MNSET_DESC(sd)) {
747 			multi_node = 1;
748 		}
749 	}
750 
751 	/*
752 	 * Adjust lock for traditional and local diskset.
753 	 *
754 	 * A MN diskset does not use the set meta_lock but instead
755 	 * uses the clnt_lock of rpc.metad and the suspend/resume
756 	 * feature of the rpc.mdcommd.  Can't use set meta_lock since
757 	 * class 1 messages are grabbing this lock and if this thread
758 	 * is holding the set meta_lock then no rpc.mdcommd suspend
759 	 * can occur.
760 	 */
761 	if ((!multi_node) && (meta_lock(sp, TRUE, ep) != 0)) {
762 		mde_perror(ep, "");
763 		md_exit(sp, 1);
764 	}
765 
766 	/* check for ownership */
767 	if (meta_check_ownership(sp, ep) != 0) {
768 		mde_perror(ep, "");
769 		md_exit(sp, 1);
770 	}
771 
772 	/* snarf MDDB locations */
773 	if (cmd != patch) {
774 		if (meta_setup_db_locations(ep) != 0) {
775 			if (! mdismddberror(ep, MDE_DB_STALE)) {
776 				if (forceflg == FALSE) {
777 					mde_perror(ep, "");
778 					md_exit(sp, 1);
779 				}
780 				options = MDCHK_ALLOW_NODBS;
781 			}
782 			mdclrerror(ep);
783 		}
784 	}
785 
786 	/* patch MDDB locations */
787 	if (cmd == patch) {
788 		if (optind < (argc - 1)) {
789 			usage(sp, gettext(
790 			    "too many arguments to -p"));
791 		}
792 
793 		if (optind == (argc - 1))
794 			cffilename = argv[optind];
795 
796 		if (metaislocalset(sp)) {
797 			if (meta_db_patch(sysfilename, cffilename, 1, ep)) {
798 				mde_perror(ep, "");
799 				md_exit(sp, 1);
800 			}
801 		}
802 	}
803 
804 	/* add/delete replicas */
805 	if (cmd == attach || cmd == detach) {
806 		if (chngdb(sp, cmd, argc, argv, options, ep)) {
807 			mde_perror(ep, "");
808 			md_exit(sp, 1);
809 		}
810 	}
811 
812 	md_exit(sp, 0);
813 	/*NOTREACHED*/
814 	return (0);
815 }
816