xref: /titanic_51/usr/src/cmd/avs/rdc/sndradm.c (revision 570de38f63910201fdd77246630b7aa8f9dc5661)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/utsname.h>
29 #include <sys/wait.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <values.h>
33 #include <limits.h>
34 #include <fcntl.h>
35 #include <strings.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/stat.h>
39 
40 #include <locale.h>
41 #include <langinfo.h>
42 #include <libintl.h>
43 #include <stdarg.h>
44 #include <netdb.h>
45 #include <ctype.h>
46 
47 #include <sys/nsctl/rdc_io.h>
48 #include <sys/nsctl/rdc_ioctl.h>
49 #include <sys/nsctl/rdc_prot.h>
50 
51 #include <sys/nsctl/cfg.h>
52 
53 #include <sys/unistat/spcs_s.h>
54 #include <sys/unistat/spcs_s_u.h>
55 #include <sys/unistat/spcs_errors.h>
56 
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netinet/tcp.h>
61 #include <rpc/rpc_com.h>
62 #include <rpc/rpc.h>
63 
64 #include <sys/nsctl/librdc.h>
65 #include <sys/nsctl/nsc_hash.h>
66 
67 #include "rdcadm.h"
68 
69 /*
70  * support for the special cluster tag "local" to be used with -C in a
71  * cluster for local volumes.
72  */
73 
74 #define	RDC_LOCAL_TAG    "local"
75 
76 typedef struct volcount_s {
77 	int count;
78 } volcount_t;
79 hash_node_t **volhash = NULL;
80 
81 /*
82  * rdc_islocal is only pertinent while creating the pairs array.
83  * after all the pairs are set, its value is useless, retaining
84  * the last value it was set to.
85  * its only reason in life is to suppress an error message in 2
86  * places where the inappropriate becomes appropriate (a supplied
87  * ctag which does not match an implied one cfg_dgame()). This
88  * happens when  C "local" is supplied. It is then used to make an
89  * error message clearer. A
90  * gettext("set %s does not match", rdc_islocal < 1?dga:dgb) situation
91  */
92 static int rdc_islocal = 0;
93 
94 char *program;
95 
96 #define	min(a, b)	((a) > (b) ? (b) : (a))
97 
98 static	char place_holder[] = "-";	/* cfg place holder value */
99 
100 /*
101  * config file user level Dual copy pair structure
102  */
103 typedef struct _sd_dual_pair {
104 	char fhost[MAX_RDC_HOST_SIZE];	/* Hostname for primary device */
105 	char fnetaddr[RDC_MAXADDR];	/* Host netaddr for primary device */
106 	char ffile[NSC_MAXPATH];	/* Primary device */
107 	char fbitmap[NSC_MAXPATH];	/* Primary bitmap device */
108 	char thost[MAX_RDC_HOST_SIZE];	/* Hostname for secondary device */
109 	char tnetaddr[RDC_MAXADDR];	/* Host netaddr for secondary device */
110 	char tfile[NSC_MAXPATH];	/* Secondary device */
111 	char tbitmap[NSC_MAXPATH];	/* Secondary bitmap device */
112 	char directfile[NSC_MAXPATH];	/* Local FCAL direct IO volume */
113 	char group[NSC_MAXPATH];	/* Group name */
114 	char ctag[MAX_RDC_HOST_SIZE];	/* Cluster resource name tag */
115 	char diskqueue[NSC_MAXPATH];	/* Disk Queue volume */
116 	int  doasync;			/* Device is in sync/async mode */
117 } _sd_dual_pair_t;
118 
119 #define	EXTRA_ARGS	6	/* g grp C ctag q diskqueue */
120 
121 static int rdc_operation(
122     CFGFILE *, char *, char *, char *, char *, char *, char *,
123     int, int, char *, char *, char *, char *, int *, int);
124 int read_config(int, char *, char *, char *);
125 static int read_libcfg(int, char *, char *);
126 int prompt_user(int, int);
127 static void rdc_check_dgislocal(char *);
128 void process_clocal(char *);
129 static void usage(void);
130 void q_usage(int);
131 static void load_rdc_vols(CFGFILE *);
132 static void unload_rdc_vols();
133 static int perform_autosv();
134 static void different_devs(char *, char *);
135 static void validate_name(CFGFILE *, char *);
136 static void set_autosync(int, char *, char *, char *);
137 static int autosync_is_on(char *tohost, char *tofile);
138 static void enable_autosync(char *fhost, char *ffile, char *thost, char *tfile);
139 static void checkgfields(CFGFILE *, int, char *, char *, char *, char *,
140     char *, char *, char *, char *, char *);
141 static void checkgfield(CFGFILE *, int, char *, char *, char *);
142 static int rdc_bitmapset(char *, char *, char *, int, nsc_off_t);
143 static int parse_cfg_buf(char *, _sd_dual_pair_t *, char *);
144 static void verify_groupname(char *grp);
145 extern char *basename(char *);
146 
147 int rdc_maxsets;
148 static _sd_dual_pair_t *pair_list;
149 
150 struct netbuf svaddr;
151 struct netbuf *svp;
152 struct netconfig nconf;
153 struct netconfig *conf;
154 struct knetconfig knconf;
155 
156 static char *reconfig_pbitmap = NULL;
157 static char *reconfig_sbitmap = NULL;
158 #ifdef _RDC_CAMPUS
159 static char *reconfig_direct = NULL;
160 #endif
161 static char *reconfig_group = NULL;
162 static char reconfig_ctag[MAX_RDC_HOST_SIZE];
163 static int reconfig_doasync = -1;
164 
165 static int clustered = 0;
166 static int proto_test = 0;
167 int allow_role = 0;
168 
169 
170 static char *
171 rdc_print_state(rdc_set_t *urdc)
172 {
173 	if (!urdc)
174 		return ("");
175 
176 	if (urdc->sync_flags & RDC_VOL_FAILED)
177 		return (gettext("volume failed"));
178 	else if (urdc->sync_flags & RDC_FCAL_FAILED)
179 		return (gettext("fcal failed"));
180 	else if (urdc->bmap_flags & RDC_BMP_FAILED)
181 		return (gettext("bitmap failed"));
182 	else if (urdc->flags & RDC_DISKQ_FAILED)
183 		return (gettext("disk queue failed"));
184 	else if (urdc->flags & RDC_LOGGING) {
185 		if (urdc->sync_flags & RDC_SYNC_NEEDED)
186 			return (gettext("need sync"));
187 		else if (urdc->sync_flags & RDC_RSYNC_NEEDED)
188 			return (gettext("need reverse sync"));
189 		else if (urdc->flags & RDC_QUEUING)
190 			return (gettext("queuing"));
191 		else
192 			return (gettext("logging"));
193 	} else if ((urdc->flags & RDC_SLAVE) && (urdc->flags & RDC_SYNCING)) {
194 		if (urdc->flags & RDC_PRIMARY)
195 			return (gettext("reverse syncing"));
196 		else
197 			return (gettext("syncing"));
198 	} else if (urdc->flags & RDC_SYNCING) {
199 		if (urdc->flags & RDC_PRIMARY)
200 			return (gettext("syncing"));
201 		else
202 			return (gettext("reverse syncing"));
203 	}
204 
205 	return (gettext("replicating"));
206 }
207 
208 
209 static int
210 rdc_print(int file_format, int verbose, char *group_arg, char *ctag_arg,
211     char *user_shost, char *user_sdev, CFGFILE *cfgp)
212 {
213 	rdc_status_t *rdc_status;
214 	spcs_s_info_t ustatus;
215 	rdc_set_t *urdc;
216 	size_t size;
217 	int i, rc, max;
218 	char *tohost, *tofile;
219 	_sd_dual_pair_t pair;
220 	char *tmptohost = pair.thost;
221 	char *tmptofile = pair.tfile;
222 	char *fromhost = pair.fhost;
223 	char *fromfile = pair.ffile;
224 	char *frombitmap = pair.fbitmap;
225 	char *tobitmap = pair.tbitmap;
226 	char *directfile = pair.directfile;
227 	char *group = pair.group;
228 	char *diskqueue = pair.diskqueue;
229 	char *ctag = pair.ctag;
230 	CFGFILE *cfg;
231 	int j;
232 	int setnumber;
233 	char key[CFG_MAX_KEY];
234 	char buf[CFG_MAX_BUF];
235 	char sync[16];
236 	int match, found;
237 
238 	size = sizeof (rdc_status_t) + (sizeof (rdc_set_t) * (rdc_maxsets - 1));
239 	match = (user_shost != NULL || user_sdev != NULL);
240 	found = 0;
241 
242 	if (user_shost == NULL && user_sdev != NULL)
243 		user_shost = "";
244 	else if (user_shost != NULL && user_sdev == NULL)
245 		user_sdev = "";
246 
247 	rdc_status = malloc(size);
248 	if (!rdc_status) {
249 		rdc_err(NULL,
250 			gettext("unable to allocate %ld bytes"), size);
251 	}
252 
253 	rdc_status->nset = rdc_maxsets;
254 	ustatus = spcs_s_ucreate();
255 
256 	rc = RDC_IOCTL(RDC_STATUS, rdc_status, 0, 0, 0, 0, ustatus);
257 	if (rc == SPCS_S_ERROR) {
258 		rdc_err(&ustatus, gettext("statistics error"));
259 	}
260 
261 	spcs_s_ufree(&ustatus);
262 
263 	max = min(rdc_status->nset, rdc_maxsets);
264 
265 	if (cfgp != NULL) {
266 		cfg = cfgp;
267 		cfg_rewind(cfg, CFG_SEC_CONF);
268 	} else {
269 		if ((cfg = cfg_open(NULL)) == NULL)
270 			rdc_err(NULL,
271 			    gettext("unable to access configuration"));
272 
273 		if (!cfg_lock(cfg, CFG_RDLOCK))
274 			rdc_err(NULL, gettext("unable to lock configuration"));
275 	}
276 
277 	for (i = 0; i < max; i++) {
278 		urdc = &rdc_status->rdc_set[i];
279 
280 		if (!(urdc->flags & RDC_ENABLED))
281 			continue;
282 
283 		if (match &&
284 		    (strcmp(user_shost, urdc->secondary.intf) != 0 ||
285 		    strcmp(user_sdev, urdc->secondary.file) != 0))
286 			continue;
287 
288 		tohost = urdc->secondary.intf;
289 		tofile = urdc->secondary.file;
290 		found = 1;
291 
292 		/* get sndr entries until shost, sfile match */
293 		for (j = 0; j < rdc_maxsets; j++) {
294 			setnumber = j + 1;
295 			(void) snprintf(key, sizeof (key),
296 			    "sndr.set%d", setnumber);
297 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
298 				break;
299 			}
300 
301 			if (parse_cfg_buf(buf, &pair, NULL))
302 				rdc_err(NULL, gettext("cfg input error"));
303 
304 			if (strcmp(tmptofile, tofile) != 0)
305 				continue;
306 			if (strcmp(tmptohost, tohost) != 0)
307 				continue;
308 
309 			if (pair.doasync == 0)
310 				(void) strcpy(sync, "sync");
311 			else
312 				(void) strcpy(sync, "async");
313 
314 			/* Got the matching entry */
315 
316 			break;
317 		}
318 
319 		if (j == rdc_maxsets)
320 			continue;	/* not found in config */
321 
322 		if (strcmp(group_arg, "") != 0 &&
323 		    strncmp(group_arg, group, NSC_MAXPATH) != 0)
324 			continue;
325 
326 		if (strcmp(ctag_arg, "") != 0 &&
327 		    strncmp(ctag_arg, ctag, MAX_RDC_HOST_SIZE) != 0)
328 			continue;
329 
330 		if (file_format) {
331 			(void) printf("%s %s %s %s %s %s %s %s",
332 			    fromhost, fromfile, frombitmap,
333 			    tohost, tofile, tobitmap,
334 			    directfile, sync);
335 			if (strlen(group) != 0)
336 				(void) printf(" g %s", group);
337 			if ((strlen(ctag) != 0) && (ctag[0] != '-'))
338 				(void) printf(" C %s", ctag);
339 			if (strlen(diskqueue) != 0)
340 				(void) printf(" q %s", diskqueue);
341 			(void) printf("\n");
342 			continue;
343 		}
344 
345 		if (strcmp(group_arg, "") != 0 &&
346 		    strncmp(group_arg, urdc->group_name, NSC_MAXPATH) != 0)
347 			continue;
348 
349 		if (!(urdc->flags & RDC_PRIMARY)) {
350 			(void) printf(gettext("%s\t<-\t%s:%s\n"),
351 			    urdc->secondary.file, urdc->primary.intf,
352 			    urdc->primary.file);
353 		} else {
354 			(void) printf(gettext("%s\t->\t%s:%s\n"),
355 			    urdc->primary.file, urdc->secondary.intf,
356 			    urdc->secondary.file);
357 		}
358 		if (!verbose)
359 			continue;
360 
361 		if (urdc->autosync)
362 			(void) printf(gettext("autosync: on"));
363 		else
364 			(void) printf(gettext("autosync: off"));
365 
366 		(void) printf(gettext(", max q writes: %lld"), urdc->maxqitems);
367 		(void) printf(gettext(", max q fbas: %lld"), urdc->maxqfbas);
368 		(void) printf(gettext(", async threads: %d"),
369 			urdc->asyncthr);
370 		(void) printf(gettext(", mode: %s"),
371 			pair.doasync ? "async" : "sync");
372 
373 		if (strlen(urdc->group_name) != 0)
374 			(void) printf(gettext(", group: %s"), urdc->group_name);
375 		if ((strlen(ctag) != 0) && (ctag[0] != '-'))
376 			(void) printf(gettext(", ctag: %s"), ctag);
377 		if (strlen(urdc->disk_queue) != 0) {
378 			(void) printf(gettext(", %s diskqueue: %s"),
379 			(urdc->flags & RDC_QNOBLOCK) ? gettext("non blocking") :
380 			gettext("blocking"), urdc->disk_queue);
381 		}
382 
383 		(void) printf(gettext(", state: %s"), rdc_print_state(urdc));
384 		(void) printf(gettext("\n"));
385 
386 	}
387 
388 	if (!cfgp)
389 		cfg_close(cfg);
390 
391 	free(rdc_status);
392 
393 	if (match && !found) {
394 		rdc_warn(NULL, gettext("unable to find set %s:%s"),
395 		    user_shost, user_sdev);
396 	}
397 
398 	return (0);
399 }
400 
401 
402 int
403 parse_extras(int argc, char *args[], int i)
404 {
405 	int gflag = 0;
406 	int Cflag = 0;
407 	int qflag = 0;
408 	int j;
409 
410 	(void) strcpy(pair_list[i].ctag, "");
411 	(void) strcpy(pair_list[i].group, "");
412 	(void) strcpy(pair_list[i].diskqueue, "");
413 
414 	if (argc == 0)
415 		return (0);
416 
417 	if (argc != 2 && argc != 4 && argc != 6)
418 		return (-1);
419 
420 	for (j = 0; j < argc; j += 2) {
421 		if (strcmp(args[j], "g") == 0) {
422 			if (gflag)
423 				return (-1);
424 			(void) strncpy(pair_list[i].group, args[j + 1],
425 			    NSC_MAXPATH);
426 			gflag = 1;
427 		}
428 		if (strcmp(args[j], "C") == 0) {
429 			if (!clustered)
430 				return (-1);
431 			if (Cflag)
432 				return (-1);
433 			(void) strncpy(pair_list[i].ctag, args[j + 1],
434 			    MAX_RDC_HOST_SIZE);
435 			process_clocal(pair_list[i].ctag);
436 			Cflag = 1;
437 		}
438 		if (strcmp(args[j], "q") == 0) {
439 			if (qflag)
440 				return (-1);
441 			(void) strncpy(pair_list[i].diskqueue, args[j + 1],
442 			    NSC_MAXPATH);
443 			qflag = 1;
444 		}
445 	}
446 
447 	return (0);
448 }
449 
450 static int
451 parse_cfg_buf(char *buf, _sd_dual_pair_t *pair, char *lghn)
452 {
453 	int rc = 0;
454 	char sync[16];
455 	char options[64], *p, *q;
456 	int len;
457 
458 	rc = sscanf(buf, "%s %s %s %s %s %s %s %s %s %s %s %s", pair->fhost,
459 		pair->ffile, pair->fbitmap, pair->thost, pair->tfile,
460 		pair->tbitmap, pair->directfile, sync, pair->group,
461 		pair->ctag, options, pair->diskqueue);
462 
463 	if (rc != 12)
464 		rdc_err(NULL, gettext("cfg input error"));
465 
466 	if (strcmp(pair->diskqueue, place_holder) == 0)
467 		(void) strcpy(pair->diskqueue, "");
468 
469 	if (strcmp(pair->group, place_holder) == 0)
470 		(void) strcpy(pair->group, "");
471 
472 	if (strcmp(sync, "sync") == 0)
473 		pair->doasync = 0;
474 	else if (strcmp(sync, "async") == 0)
475 		pair->doasync = 1;
476 	else {
477 		rdc_err(NULL,
478 		    gettext("set %s:%s neither sync nor async"),
479 		    pair->thost, pair->tfile);
480 	}
481 
482 	if (lghn && (p = strstr(options, "lghn="))) {
483 		p += 5;
484 		q = strchr(p, ';');
485 		if (q) {
486 			/* LINTED p & q limited to options[64] */
487 			len = q - p;
488 		} else {
489 			len = strlen(p);
490 		}
491 		(void) strncpy(lghn, p, len);
492 		lghn[len] = '\0';
493 	} else if (lghn) {
494 		*lghn = '\0';
495 	}
496 
497 	return (0);
498 }
499 
500 static int
501 ctag_check(char *fromhost, char *fromfile, char *frombitmap, char *tohost,
502     char *tofile, char *tobitmap, char *ctag, char *diskq)
503 {
504 	char *file_dgname;
505 	char *bmp_dgname;
506 	char *que_dgname;
507 	char *localfile;
508 	char file_buf[MAX_RDC_HOST_SIZE];
509 	char bmp_buf[MAX_RDC_HOST_SIZE];
510 	char que_buf[NSC_MAXPATH];
511 	int is_primary;
512 	struct hostent *hp;
513 	char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
514 
515 	if (!clustered)
516 		return (0);
517 
518 	hp = gethost_byname(fromhost);
519 	(void) strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
520 	hp = gethost_byname(tohost);
521 	(void) strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
522 	if (!self_check(fromname) && !self_check(toname)) {
523 		/*
524 		 * If we could get a list of logical hosts on this cluster
525 		 * then we could print something intelligent about where
526 		 * the volume is mastered. For now, just print some babble
527 		 * about the fact that we have no idea.
528 		 */
529 			rdc_err(NULL,
530 				gettext("either %s:%s or %s:%s is not local"),
531 					fromhost, fromfile, tohost, tofile);
532 	}
533 
534 	is_primary = self_check(fromname);
535 
536 	/*
537 	 * If implicit disk group name and no ctag specified by user,
538 	 * we set the ctag to it.
539 	 * If implicit disk group name, it must match any supplied ctag.
540 	 */
541 	localfile = is_primary ? fromfile : tofile;
542 	file_dgname = cfg_dgname(localfile, file_buf, sizeof (file_buf));
543 	if (file_dgname && strlen(file_dgname))
544 		rdc_check_dgislocal(file_dgname);
545 
546 	/*
547 	 * Autogenerate a ctag, if not "-C local" or no "-C " specified
548 	 */
549 	if (!rdc_islocal && !strlen(ctag) && file_dgname && strlen(file_dgname))
550 		(void) strncpy(ctag, file_dgname, MAX_RDC_HOST_SIZE);
551 
552 	/*
553 	 * making an exception here for users giving the "local"tag
554 	 * this overrides this error message. (rdc_islocal ! = 1)
555 	 */
556 	if (!rdc_islocal && strlen(ctag) &&
557 	    file_dgname && strlen(file_dgname) &&
558 	    strncmp(ctag, file_dgname, MAX_RDC_HOST_SIZE)) {
559 		rdc_warn(NULL, gettext("ctag \"%s\" does not "
560 		    "match disk group name \"%s\" of volume %s"), ctag,
561 		    file_dgname, localfile);
562 		return (-1);
563 	}
564 
565 	/*
566 	 * Do we have a non-volume managed disk without -C local specified?
567 	 */
568 	if (!rdc_islocal && (!file_dgname || !strlen(file_dgname))) {
569 		rdc_err(NULL, gettext("volume \"%s\" is not part"
570 		    " of a disk group,\nplease specify resource ctag\n"),
571 		    localfile);
572 	}
573 
574 	/*
575 	 * Do we have a volume managed disk with -C local?
576 	 */
577 	if (rdc_islocal && file_dgname && (strlen(file_dgname) > 0)) {
578 		rdc_err(NULL, gettext(
579 			"volume \"%s\" is part of a disk group\n"), localfile);
580 	}
581 
582 	/*
583 	 * Local bitmap must also have same ctag.
584 	 */
585 	localfile = is_primary ? frombitmap : tobitmap;
586 	bmp_dgname = cfg_dgname(localfile, bmp_buf, sizeof (bmp_buf));
587 	if (bmp_dgname && strlen(bmp_dgname))
588 		rdc_check_dgislocal(bmp_dgname);
589 
590 	/*
591 	 * Assure that if the primary has a device group, so must the bitmap
592 	 */
593 	if ((file_dgname && strlen(file_dgname)) &&
594 	    (!bmp_dgname || !strlen(bmp_dgname))) {
595 		rdc_warn(NULL, gettext("bitmap %s is not in disk group \"%s\""),
596 			localfile, rdc_islocal < 1?file_dgname:ctag);
597 		return (-1);
598 	}
599 
600 	/*
601 	 * Assure that if the if there is a ctag, it must match the bitmap
602 	 */
603 	if (!rdc_islocal && strlen(ctag) &&
604 	    bmp_dgname && strlen(bmp_dgname) &&
605 	    strncmp(ctag, bmp_dgname, MAX_RDC_HOST_SIZE)) {
606 		rdc_warn(NULL, gettext("ctag \"%s\" does not "
607 		    "match disk group name \"%s\" of bitmap %s"), ctag,
608 		    bmp_dgname, localfile);
609 		return (-1);
610 	}
611 
612 	/*
613 	 * If this is the SNDR primary and there is a local disk queue
614 	 */
615 	if (is_primary && diskq[0]) {
616 
617 		/*
618 		 * Local disk queue must also have same ctag.
619 		 */
620 		que_dgname = cfg_dgname(diskq, que_buf, sizeof (que_buf));
621 		if (que_dgname && strlen(que_dgname))
622 			rdc_check_dgislocal(que_dgname);
623 
624 		/*
625 		 * Assure that if the primary has a device group, so must
626 		 * the disk queue
627 		 */
628 		if ((file_dgname && strlen(file_dgname)) &&
629 		    (!que_dgname || !strlen(que_dgname))) {
630 			rdc_warn(NULL, gettext("disk queue %s is not in disk "
631 			    "group \"%s\""), diskq,
632 			    rdc_islocal < 1?file_dgname:ctag);
633 			return (-1);
634 		}
635 
636 		/*
637 		 * Assure that if the if there is a ctag, it must match
638 		 * the disk queue
639 		 */
640 		if (!rdc_islocal && strlen(ctag) &&
641 		    que_dgname && strlen(que_dgname) &&
642 		    strncmp(ctag, que_dgname, MAX_RDC_HOST_SIZE)) {
643 			rdc_warn(NULL, gettext("ctag \"%s\" does not "
644 			    "match disk group name \"%s\" of disk queue %s"),
645 			    ctag, que_dgname, diskq);
646 			return (-1);
647 		}
648 	}
649 
650 	return (0);
651 }
652 
653 #define	DISKQ_OKAY	0
654 #define	DISKQ_FAIL	1
655 #define	DISKQ_REWRITEG	2
656 /*
657  * check that newq is compatable with the groups current disk queue.
658  * Newq is incompatable if it is set and the groups queue is set and the queues
659  * are different.
660  *
661  * if newq is not set but should be, it will be set to the correct value.
662  * returns:
663  *	DISK_REWRITEG entire group needs to take new value of disk_queue
664  *	DISKQ_OKAY newq contains a value that matches the group.
665  *	DISKQ_FAIL disk queues are incompatible.
666  */
667 static int
668 check_diskqueue(CFGFILE *cfg, char *newq, char *newgroup)
669 {
670 	int i, setnumber;
671 	_sd_dual_pair_t pair;
672 	char *group = pair.group;
673 	char *diskqueue = pair.diskqueue;
674 	char buf[CFG_MAX_BUF];
675 	char key[CFG_MAX_KEY];
676 	int open_cfg = cfg == NULL ? 1 : 0;
677 
678 
679 	if (newgroup == NULL || *newgroup == '\0') {
680 		if (*newq == '\0')
681 			return (DISKQ_OKAY);	/* okay,  */
682 		newgroup = "--nomatch--";
683 	}
684 
685 	if (open_cfg) {
686 		if ((cfg = cfg_open(NULL)) == NULL)
687 			rdc_err(NULL,
688 			    gettext("unable to access configuration"));
689 		if (!cfg_lock(cfg, CFG_RDLOCK))
690 			rdc_err(NULL, gettext("unable to lock configuration"));
691 	}
692 
693 	/*CSTYLED*/
694 	for (i = 0; ; i++) {
695 		setnumber = i + 1;
696 		(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
697 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
698 			break;
699 		/*
700 		 * I think this is quicker than
701 		 * having to double dip into the config
702 		 */
703 		if (parse_cfg_buf(buf, &pair, NULL))
704 			rdc_err(NULL, gettext("cfg input error"));
705 
706 		if (strncmp(group, newgroup, NSC_MAXPATH) != 0) {
707 			if (((strncmp(diskqueue, newq, NSC_MAXPATH) == 0)) &&
708 			    (diskqueue[0] != '\0')) {
709 				if (open_cfg)
710 					cfg_close(cfg);
711 				return (DISKQ_FAIL);
712 			}
713 			continue;
714 		}
715 		if (*newq == '\0') {
716 			if (diskqueue[0] != '\0')
717 				(void) strncpy(newq, diskqueue, NSC_MAXPATH);
718 			if (open_cfg)
719 				cfg_close(cfg);
720 			return (DISKQ_OKAY);	/* okay,  */
721 		}
722 
723 		if (open_cfg)
724 			cfg_close(cfg);
725 		if (diskqueue[0] == '\0')	/* no queue here */
726 			return (DISKQ_REWRITEG);
727 		return (strncmp(diskqueue, newq, NSC_MAXPATH)
728 		    == 0 ? DISKQ_OKAY : DISKQ_FAIL);
729 	}
730 	if (open_cfg)
731 		cfg_close(cfg);
732 	return (DISKQ_OKAY);
733 }
734 
735 
736 int
737 pair_diskqueue_check(int newpair)
738 {
739 	int i, j;
740 	int rc;
741 
742 	for (i = 0; i < newpair; i++) {
743 		if (strcmp(pair_list[i].group, pair_list[newpair].group) != 0)
744 			continue;
745 		if (strcmp(pair_list[i].diskqueue, pair_list[newpair].diskqueue)
746 		    == 0)
747 			return (DISKQ_OKAY); /* matches existing group */
748 		if ((pair_list[newpair].group[0] != '\0') &&
749 		    (pair_list[newpair].diskqueue[0] != '\0') &&
750 		    (pair_list[i].diskqueue[0] != '\0')) {
751 			rdc_warn(NULL,
752 			    gettext("disk queue %s does not match %s "
753 			    "skipping set"), pair_list[newpair].diskqueue,
754 			    pair_list[i].diskqueue);
755 			return (DISKQ_FAIL);
756 		}
757 
758 		if ((strcmp(pair_list[newpair].diskqueue, "") == 0) &&
759 		    pair_list[newpair].group[0] != '\0') {
760 			(void) strncpy(pair_list[newpair].diskqueue,
761 			    pair_list[i].diskqueue, NSC_MAXPATH);
762 			return (DISKQ_OKAY); /* changed to existing group que */
763 		}
764 		if (strcmp(pair_list[i].diskqueue, "") == 0) {
765 			for (j = 0; j < newpair; j++) {
766 				if ((pair_list[j].group[0] != '\0') &&
767 				    (strncmp(pair_list[j].group,
768 				    pair_list[newpair].group,
769 				    NSC_MAXPATH) == 0)) {
770 					(void) strncpy(pair_list[j].diskqueue,
771 					    pair_list[newpair].diskqueue,
772 					    NSC_MAXPATH);
773 				}
774 			}
775 			return (DISKQ_OKAY);
776 		}
777 		break; /* no problem with pair_list sets */
778 
779 	}
780 
781 	/* now check with already configured sets */
782 	rc = check_diskqueue(NULL, pair_list[newpair].diskqueue,
783 	    pair_list[newpair].group);
784 	if (rc == DISKQ_REWRITEG) {
785 		for (i = 0; i < newpair; i++) {
786 			if (strcmp(pair_list[i].group,
787 			    pair_list[newpair].group) != 0)
788 				continue;
789 
790 			(void) strncpy(pair_list[i].diskqueue,
791 			    pair_list[newpair].diskqueue, NSC_MAXPATH);
792 		}
793 	}
794 	return (rc);
795 }
796 
797 int
798 ii_set_exists(CFGFILE *cfg, char *ma, char *sh, char *bm)
799 {
800 	char buf[CFG_MAX_BUF];
801 	char key[CFG_MAX_KEY];
802 	char master[NSC_MAXPATH];
803 	char shadow[NSC_MAXPATH];
804 	char bitmap[NSC_MAXPATH];
805 	int i;
806 
807 	for (i = 1; ; i++) {
808 		(void) snprintf(key, sizeof (key), "ii.set%d", i);
809 		bzero(&master, sizeof (master));
810 		bzero(&shadow, sizeof (shadow));
811 		bzero(&bitmap, sizeof (bitmap));
812 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
813 			break;
814 		(void) sscanf(buf, "%s %s %s", master, shadow, bitmap);
815 		if (strcmp(master, ma) != 0)
816 			continue;
817 		if (strcmp(shadow, sh) != 0)
818 			continue;
819 		if (strcmp(bitmap, bm) != 0)
820 			continue;
821 		return (1);
822 	}
823 	return (0);
824 }
825 
826 void
827 rdc_ii_config(int argc, char **argv)
828 {
829 	char *master;
830 	char *shadow;
831 	char *bitmap;
832 	char c;
833 	CFGFILE *cfg;
834 	int i;
835 	int setnumber;
836 	char key[CFG_MAX_KEY];
837 	char buf[CFG_MAX_BUF];
838 	int found;
839 	int sev;
840 
841 	/* Parse the rest of the arguments to see what to do */
842 
843 	if (argc - optind != 4) {
844 		usage();
845 		exit(1);
846 	}
847 
848 	c = *argv[optind];
849 	switch (c) {
850 	case 'd':
851 		/* Delete an ndr_ii entry */
852 
853 		master = argv[++optind];
854 		shadow = argv[++optind];
855 		bitmap = argv[++optind];
856 
857 		if ((cfg = cfg_open(NULL)) == NULL)
858 			rdc_err(NULL,
859 			    gettext("unable to access configuration"));
860 		if (!cfg_lock(cfg, CFG_WRLOCK))
861 			rdc_err(NULL, gettext("unable to lock configuration"));
862 
863 		found = 0;
864 		/* get ndr_ii entries until a match is found */
865 		/*CSTYLED*/
866 		for (i = 0; ; i++) {
867 			setnumber = i + 1;
868 
869 			(void) snprintf(key, sizeof (key),
870 			    "ndr_ii.set%d.secondary",
871 			    setnumber);
872 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
873 				break;
874 			if (strcmp(buf, master) != 0)
875 				continue;
876 
877 			/* Got a matching entry */
878 
879 			(void) snprintf(key, sizeof (key),
880 			    "ndr_ii.set%d.shadow",
881 			    setnumber);
882 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
883 				break;
884 			if (strcmp(buf, shadow) != 0)
885 				continue;
886 
887 			(void) snprintf(key, sizeof (key),
888 			    "ndr_ii.set%d.bitmap",
889 			    setnumber);
890 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
891 				break;
892 			if (strcmp(buf, bitmap) != 0)
893 				continue;
894 
895 			(void) snprintf(key, sizeof (key),
896 			    "ndr_ii.set%d", setnumber);
897 			if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
898 				rdc_warn(NULL,
899 				    gettext("unable to remove \"%s\" "
900 				    "from configuration storage: %s"),
901 				    key, cfg_error(&sev));
902 				} else {
903 					if (cfg_commit(cfg) < 0)
904 					    rdc_err(NULL,
905 						gettext("ndr_ii set %s %s %s "
906 						    "not deconfigured."),
907 						    master, shadow, bitmap);
908 					else
909 					    spcs_log("sndr", NULL,
910 						gettext("ndr_ii set %s %s %s "
911 						    "has been deconfigured."),
912 						    master, shadow, bitmap);
913 				}
914 			found = 1;
915 			break;
916 		}
917 
918 		if (!found) {
919 			rdc_err(NULL,
920 			    gettext("did not find matching ndr_ii "
921 			    "entry for %s %s %s"), master, shadow, bitmap);
922 		}
923 
924 		cfg_close(cfg);
925 
926 		break;
927 
928 	case 'a':
929 		/* Add an ndr_ii entry */
930 
931 		master = argv[++optind];
932 		shadow = argv[++optind];
933 		bitmap = argv[++optind];
934 
935 		if ((cfg = cfg_open(NULL)) == NULL)
936 			rdc_err(NULL,
937 			    gettext("unable to access configuration"));
938 		if (!cfg_lock(cfg, CFG_WRLOCK))
939 			rdc_err(NULL, gettext("unable to lock configuration"));
940 
941 		found = 0;
942 		/* get ndr_ii entries in case a match is found */
943 		/*CSTYLED*/
944 		for (i = 0; ; i++) {
945 			setnumber = i + 1;
946 
947 			(void) snprintf(key, sizeof (key),
948 			    "ndr_ii.set%d.secondary",
949 			    setnumber);
950 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
951 				break;
952 			if (strcmp(buf, master) == 0) {
953 				rdc_err(NULL,
954 				    gettext("found matching ndr_ii "
955 				    "entry for %s"), master);
956 			}
957 		}
958 		/*
959 		 * check to see if this is using a sndr bitmap.
960 		 * kind of a courtesy check, as the ii copy would fail anyway
961 		 * excepting the case where they had actually configured
962 		 * ii/sndr that way, in which case they are broken
963 		 * before we get here
964 		 */
965 		/*CSTYLED*/
966 		for (i = 0; ; i++) {
967 			setnumber = i + 1;
968 
969 			/*
970 			 * Checking local bitmaps
971 			 */
972 			(void) snprintf(key, sizeof (key), "sndr.set%d.phost",
973 			    setnumber);
974 
975 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
976 				break;
977 			if (self_check(buf)) {
978 				(void) snprintf(key, sizeof (key),
979 				    "sndr.set%d.pbitmap",
980 				    setnumber);
981 			} else {
982 				(void) snprintf(key, sizeof (key),
983 				    "sndr.set%d.sbitmap",
984 				    setnumber);
985 			}
986 
987 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
988 				break;
989 
990 			if ((strcmp(buf, bitmap) == 0) ||
991 			    (strcmp(buf, master) == 0) ||
992 			    (strcmp(buf, shadow) == 0)) {
993 				rdc_err(NULL,
994 				    gettext("%s is already configured "
995 				    "as a Remote Mirror bitmap"), buf);
996 			}
997 		}
998 		if (!ii_set_exists(cfg, master, shadow, bitmap)) {
999 			rdc_warn(NULL, gettext("Point-in-Time Copy set "
1000 			    "%s %s %s is not already configured. Remote "
1001 			    "Mirror will attempt to configure this set when "
1002 			    "a sync is issued to it.  The results of that "
1003 			    "operation will be in /var/adm/ds.log"),
1004 			    master, shadow, bitmap);
1005 			spcs_log("sndr", NULL, gettext("Point-in-Time Copy set "
1006 			    "%s %s %s is not already configured. Remote "
1007 			    "Mirror will attempt to configure this set when "
1008 			    "a sync is issued to it.  The results of that "
1009 			    "operation will be in /var/adm/ds.log"),
1010 			    master, shadow, bitmap);
1011 		} else {
1012 			spcs_log("sndr", NULL, gettext("ndr_ii set "
1013 			    "%s %s %s has been configured."),
1014 			    master, shadow, bitmap);
1015 		}
1016 
1017 		/*
1018 		 * Prior to insertion in ndr_ii entry, if in a Sun Cluster
1019 		 * assure device groups are the same and cluster tag is set
1020 		 */
1021 		if (clustered && !rdc_islocal) {
1022 			char mst_dg[NSC_MAXPATH] = {0};
1023 			char shd_dg[NSC_MAXPATH] = {0};
1024 			char bmp_dg[NSC_MAXPATH] = {0};
1025 
1026 			if (!(cfg_dgname(master, mst_dg, sizeof (mst_dg)) &&
1027 			    cfg_dgname(shadow, shd_dg, sizeof (shd_dg)) &&
1028 			    cfg_dgname(bitmap, bmp_dg, sizeof (bmp_dg))))
1029 				rdc_warn(NULL, gettext("ndr_ii: %s %s %s are "
1030 				    "not in a device group"),
1031 				    master, shadow, bitmap);
1032 			else if (strcmp(mst_dg, bmp_dg) ||
1033 				strcmp(mst_dg, shd_dg))
1034 				rdc_warn(NULL, gettext("ndr_ii: %s %s %s are "
1035 				    "not in different device groups"),
1036 				    master, shadow, bitmap);
1037 			else {
1038 				cfg_resource(cfg, shd_dg);
1039 				(void) snprintf(buf, sizeof (buf),
1040 				    "%s %s %s update %s",
1041 				    master, shadow, bitmap, shd_dg);
1042 			}
1043 		} else {
1044 			(void) snprintf(buf, sizeof (buf), "%s %s %s update",
1045 				master, shadow, bitmap);
1046 		}
1047 
1048 		if ((cfg_put_cstring(cfg, "ndr_ii", buf, strlen(buf)) < 0) ||
1049 		    (cfg_commit(cfg) < 0))
1050 			rdc_warn(NULL, gettext("unable to add \"%s\" to "
1051 				"configuration storage: %s"),
1052 				buf, cfg_error(&sev));
1053 
1054 		cfg_close(cfg);
1055 
1056 		break;
1057 
1058 	default:
1059 		usage();
1060 		exit(1);
1061 	}
1062 }
1063 
1064 void
1065 check_rdcbitmap(int cmd, char *hostp, char *bmp)
1066 {
1067 	int i;
1068 	CFGFILE *cfg;
1069 	int entries;
1070 	char **entry;
1071 	char *host, *pri, *sec, *sbm, *bit, *mas, *sha, *ovr;
1072 	char *shost, *buf, *que;
1073 
1074 	if ((cfg = cfg_open(NULL)) == NULL)
1075 		rdc_err(NULL,
1076 		    gettext("unable to access configuration"));
1077 	if (!cfg_lock(cfg, CFG_RDLOCK))
1078 		rdc_err(NULL, gettext("unable to lock configuration"));
1079 
1080 	/*
1081 	 * look into II config to see if this is being used elsewhere
1082 	 */
1083 	entry = NULL;
1084 	entries = cfg_get_section(cfg, &entry, "ii");
1085 	for (i = 0; i < entries; i++) {
1086 		buf = entry[i];
1087 
1088 		mas = strtok(buf, " ");		/* master */
1089 		sha = strtok(NULL, " ");	/* shadow */
1090 		bit = strtok(NULL, " ");	/* bitmap */
1091 		(void) strtok(NULL, " ");	/* mode */
1092 		ovr = strtok(NULL, " ");	/* overflow */
1093 
1094 		/*
1095 		 * got master, shadow, overflow, and bitmap, now compare
1096 		 */
1097 		if ((strcmp(bmp, mas) == 0) ||
1098 		    (strcmp(bmp, sha) == 0) ||
1099 		    (strcmp(bmp, ovr) == 0) ||
1100 		    (strcmp(bmp, bit) == 0)) {
1101 			rdc_err(NULL,
1102 			    gettext("bitmap %s is in use by"
1103 			    " Point-in-Time Copy"), bmp);
1104 		}
1105 		free(buf);
1106 	}
1107 	if (entries)
1108 		free(entry);
1109 
1110 
1111 	/*
1112 	 * and last but not least, make sure sndr is not using vol for anything
1113 	 */
1114 	entry = NULL;
1115 	entries = cfg_get_section(cfg, &entry, "sndr");
1116 	for (i = 0; i < entries; i++) {
1117 		buf = entry[i];
1118 
1119 		/*
1120 		 * I think this is quicker than
1121 		 * having to double dip into the config
1122 		 */
1123 		host = strtok(buf, " ");	/* phost */
1124 		pri = strtok(NULL, " ");	/* primary */
1125 		bit = strtok(NULL, " ");	/* pbitmap */
1126 		shost = strtok(NULL, " ");	/* shost */
1127 		sec = strtok(NULL, " ");	/* secondary */
1128 		sbm = strtok(NULL, " ");	/* sbitmap */
1129 		(void) strtok(NULL, " ");	/* type */
1130 		(void) strtok(NULL, " ");	/* mode */
1131 		(void) strtok(NULL, " ");	/* group */
1132 		(void) strtok(NULL, " ");	/* cnode */
1133 		(void) strtok(NULL, " ");	/* options */
1134 		que = strtok(NULL, " ");	/* diskq */
1135 
1136 		if (cmd == RDC_CMD_ENABLE) {
1137 			if (self_check(host)) {
1138 				if ((strcmp(bmp, pri) == 0) ||
1139 				    (strcmp(bmp, que) == 0) ||
1140 				    (strcmp(bmp, bit) == 0)) {
1141 					rdc_err(NULL,
1142 					    gettext("bitmap %s is already "
1143 					    "in use by StorEdge Network Data "
1144 					    "Replicator"), bmp);
1145 				}
1146 			} else {
1147 				if ((strcmp(bmp, sec) == 0) ||
1148 				    (strcmp(bmp, sbm) == 0)) {
1149 					rdc_err(NULL,
1150 					    gettext("bitmap %s is already "
1151 					    "in use by StorEdge Network Data "
1152 					    "Replicator"), bmp);
1153 				}
1154 			}
1155 		} else if (cmd == RDC_CMD_RECONFIG) {
1156 
1157 			/*
1158 			 * read this logic 1000 times and consider
1159 			 * multi homed, one to many, many to one (marketing)
1160 			 * etc, etc, before changing
1161 			 */
1162 			if (self_check(hostp)) {
1163 				if (self_check(host)) {
1164 					if ((strcmp(bmp, pri) == 0) ||
1165 					    (strcmp(bmp, que) == 0) ||
1166 					    (strcmp(bmp, bit) == 0)) {
1167 						rdc_err(NULL,
1168 						gettext("bitmap %s is already "
1169 						"in use by StorEdge Network "
1170 						"Data Replicator"), bmp);
1171 					}
1172 				} else {
1173 					if ((strcmp(hostp, shost) == 0) &&
1174 					    (strcmp(bmp, sec) == 0) ||
1175 					    (strcmp(bmp, sbm) == 0)) {
1176 						rdc_err(NULL,
1177 						gettext("bitmap %s is already "
1178 						"in use by StorEdge Network "
1179 						"Data Replicator"), bmp);
1180 
1181 					}
1182 				}
1183 			} else { /* self_check(hostp) failed */
1184 				if (self_check(host)) {
1185 					if ((strcmp(shost, hostp) == 0) &&
1186 					    (strcmp(bmp, sec) == 0) ||
1187 					    (strcmp(bmp, sbm) == 0)) {
1188 						rdc_err(NULL,
1189 						gettext("bitmap %s is already "
1190 						"in use by StorEdge Network "
1191 						"Data Replicator"), bmp);
1192 					}
1193 				} else {
1194 					if ((strcmp(host, hostp) == 0) &&
1195 					    (strcmp(bmp, pri) == 0) ||
1196 					    (strcmp(bmp, que) == 0) ||
1197 					    (strcmp(bmp, bit) == 0)) {
1198 						rdc_err(NULL,
1199 						gettext("bitmap %s is already "
1200 						"in use by StorEdge Network "
1201 						"Data Replicator"), bmp);
1202 					}
1203 				}
1204 			}
1205 
1206 		}
1207 
1208 		free(buf);
1209 	}
1210 	cfg_close(cfg);
1211 
1212 	if (entries)
1213 		free(entry);
1214 }
1215 int
1216 check_intrange(char *arg) {
1217 	int i;
1218 
1219 	for (i = 0; i < strlen(arg); i++) {
1220 		if (arg[i] < '0' || arg[i] > '9') {
1221 			rdc_warn(NULL, "not a valid number, must be a "
1222 			    "decimal between 1 and %d", MAXINT);
1223 			return (0);
1224 		}
1225 	}
1226 	errno = 0;
1227 	i = (int)strtol(arg, NULL, 10);
1228 	if ((errno) || (i < 1) || (i > MAXINT)) {
1229 		rdc_warn(NULL, "not a valid number, must be a decimal "
1230 		    "between 1 and %d", MAXINT);
1231 		return (0);
1232 	}
1233 	return (1);
1234 }
1235 
1236 void
1237 rewrite_group_diskqueue(CFGFILE *cfg, _sd_dual_pair_t *pair, char *diskqueue)
1238 {
1239 	int set;
1240 	char buf[CFG_MAX_BUF];
1241 	char key[CFG_MAX_KEY];
1242 	_sd_dual_pair_t tmpair;
1243 
1244 	for (set = 1; /*CSTYLED*/; set++) {
1245 		bzero(buf, CFG_MAX_BUF);
1246 		bzero(&tmpair, sizeof (tmpair));
1247 
1248 		(void) snprintf(key, sizeof (key), "sndr.set%d", set);
1249 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
1250 			break;
1251 		}
1252 		if (parse_cfg_buf(buf, &tmpair, NULL))
1253 			continue;
1254 		if (pair->group && pair->group[0]) {
1255 			if (strcmp(pair->group, tmpair.group) != 0)
1256 				continue; /* not the group we want */
1257 
1258 		} else { /* no group specified */
1259 			if (strcmp(pair->thost, tmpair.thost) != 0)
1260 				continue;
1261 			if (strcmp(pair->tfile, tmpair.tfile) != 0)
1262 				continue;
1263 		}
1264 
1265 		(void) sprintf(key, "sndr.set%d.diskq", set);
1266 
1267 		if (cfg_put_cstring(cfg, key, diskqueue,
1268 		    strlen(diskqueue)) < 0) {
1269 			perror(cfg_error(NULL));
1270 		}
1271 	}
1272 }
1273 
1274 void
1275 diskq_subcmd(int subcmd, char *qvol, char *group_arg, char *ctag_arg,
1276     char *tohost_arg, char *tofile_arg)
1277 {
1278 	int found = 0;
1279 	int setnumber = 0;
1280 	char key[CFG_MAX_KEY];
1281 	char buf[CFG_MAX_BUF];
1282 	int i;
1283 	int rc;
1284 	int option = 0;
1285 	_sd_dual_pair_t pair;
1286 	CFGFILE *cfg;
1287 	char *ctag = NULL;
1288 	int resourced = 0;
1289 
1290 	if ((cfg = cfg_open(NULL)) == NULL)
1291 		rdc_err(NULL,
1292 		    gettext("unable to access configuration"));
1293 
1294 	if (!cfg_lock(cfg, CFG_WRLOCK))
1295 		rdc_err(NULL,
1296 		    gettext("unable to lock configuration"));
1297 
1298 redo:
1299 	if (cfg_load_svols(cfg) < 0 ||
1300 	    cfg_load_dsvols(cfg) < 0 ||
1301 	    cfg_load_shadows(cfg) < 0)
1302 		rdc_err(NULL,
1303 		    gettext("Unable to parse config filer"));
1304 	load_rdc_vols(cfg);
1305 
1306 	/*CSTYLED*/
1307 	for (i = 0; i < rdc_maxsets;) {
1308 		setnumber++;
1309 
1310 		bzero(buf, CFG_MAX_BUF);
1311 		(void) snprintf(key, sizeof (key),
1312 		    "sndr.set%d", setnumber);
1313 		rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
1314 		if (rc < 0)
1315 			break;
1316 		if (parse_cfg_buf(buf, &pair, NULL))
1317 			continue;
1318 
1319 		if (strlen(group_arg) == 0) {
1320 			if (strcmp(tohost_arg, pair.thost) == 0 &&
1321 			    strcmp(tofile_arg, pair.tfile) == 0) {
1322 				(void) strcpy(group_arg, pair.group);
1323 				found = 1;
1324 				break;
1325 			}
1326 
1327 		} else {
1328 			if (strcmp(group_arg, pair.group) == 0) {
1329 				found = 1;
1330 				break;
1331 			}
1332 		}
1333 	}
1334 
1335 	if (!found) {
1336 		if (strlen(group_arg) == 0) {
1337 			rdc_err(NULL,
1338 			    gettext("Unable to find %s:%s in "
1339 			    "configuration storage"),
1340 			    tohost_arg, tofile_arg);
1341 		} else {
1342 			rdc_err(NULL,
1343 			    gettext("Unable to find group %s in "
1344 			    "configuration storage"), group_arg);
1345 		}
1346 	}
1347 	if (!resourced && strlen(pair.ctag)) { /* uh-oh... */
1348 		cfg_unload_svols(cfg);
1349 		cfg_unload_dsvols(cfg);
1350 		cfg_unload_shadows(cfg);
1351 		unload_rdc_vols();
1352 		cfg_resource(cfg, pair.ctag);
1353 		ctag = strdup(pair.ctag);
1354 		resourced = 1;
1355 		setnumber = 0;
1356 		goto redo;
1357 	}
1358 
1359 	if (clustered && !rdc_islocal) {
1360 		if (strcmp(ctag_arg, "") &&
1361 		    strncmp(ctag_arg, pair.ctag, MAX_RDC_HOST_SIZE))
1362 			rdc_warn(NULL, gettext("ctags %s and %s "
1363 			    "do not match, proceeding with operation based "
1364 			    "on existing set information"), ctag_arg, ctag);
1365 	}
1366 	switch (subcmd) {
1367 	case RDC_CMD_ADDQ:
1368 		if (clustered && (ctag_check(pair.fhost, pair.ffile,
1369 		    pair.fbitmap, pair.thost, pair.tfile, pair.tbitmap,
1370 		    pair.ctag, qvol) < 0))
1371 			exit(1);
1372 
1373 		if (strlen(pair.diskqueue) > 0) {
1374 			rdc_err(NULL, gettext("Remote Mirror set already "
1375 			    "has a disk queue"));
1376 		}
1377 		if (check_diskqueue(cfg, qvol, group_arg) == DISKQ_FAIL) {
1378 			rdc_err(NULL,
1379 			    gettext("diskqueue %s is incompatible"), qvol);
1380 		}
1381 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1382 		    pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
1383 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1384 		    0) < 0) {
1385 			if (cfg_vol_disable(cfg, qvol, ctag, "sndr") < 0)
1386 				rdc_warn(NULL, gettext("Failed to remove disk "
1387 				    "queue [%s] from configuration"), qvol);
1388 			rdc_err(NULL, gettext("Add disk queue operation "
1389 			    "failed"));
1390 		}
1391 		if (nsc_lookup(volhash, qvol) == NULL) {
1392 			if (cfg_vol_enable(cfg, qvol, ctag, "sndr") < 0) {
1393 				rdc_err(NULL, gettext("Add disk queue "
1394 					"operation failed"));
1395 			}
1396 		}
1397 		rewrite_group_diskqueue(cfg, &pair, qvol);
1398 
1399 		spcs_log("sndr", NULL, gettext("Remote Mirror: added "
1400 		    "diskqueue %s to set %s:%s and its group"), qvol,
1401 		    pair.thost, pair.tfile);
1402 		break;
1403 	case RDC_OPT_FORCE_QINIT:
1404 		if (strlen(pair.diskqueue) == 0) {
1405 			rdc_err(NULL, gettext("Remote Mirror set does not "
1406 			    "have a disk queue"));
1407 		}
1408 		subcmd = RDC_CMD_INITQ;
1409 		option = RDC_OPT_FORCE_QINIT;
1410 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1411 		    pair.thost, pair.tfile, pair.tbitmap, subcmd, option,
1412 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1413 		    0) < 0) {
1414 			exit(1);
1415 		}
1416 		break;
1417 	case RDC_CMD_INITQ:
1418 		if (strlen(pair.diskqueue) == 0) {
1419 			rdc_err(NULL, gettext("Remote Mirror set does not "
1420 			    "have a disk queue"));
1421 		}
1422 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1423 		    pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
1424 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1425 		    0) < 0) {
1426 			exit(1);
1427 		}
1428 		break;
1429 	case RDC_CMD_REMQ:
1430 		if (strlen(pair.diskqueue) == 0) {
1431 			rdc_err(NULL, gettext("Remote Mirror set does not "
1432 			    "have a disk queue"));
1433 		}
1434 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1435 		    pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
1436 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1437 		    0) < 0) {
1438 			exit(1);
1439 		}
1440 		if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
1441 			rdc_warn(NULL, gettext("Failed to remove disk queue "
1442 				"[%s] from configuration"), pair.diskqueue);
1443 		rewrite_group_diskqueue(cfg, &pair, place_holder);
1444 
1445 		spcs_log("sndr", NULL, gettext("Remote Mirror: removed "
1446 		    "diskqueue from set %s:%s and its group"), pair.thost,
1447 		    pair.tfile);
1448 		break;
1449 	case RDC_CMD_KILLQ:
1450 		if (strlen(pair.diskqueue) == 0) {
1451 			rdc_err(NULL, gettext("Remote Mirror set does not "
1452 			    "have a disk queue"));
1453 		}
1454 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1455 		    pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
1456 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1457 		    0) < 0) {
1458 			rdc_err(NULL, gettext("Failed to remove disk queue"));
1459 		}
1460 		if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
1461 			rdc_warn(NULL, gettext("Failed to remove disk queue "
1462 				"[%s] from configuration"), pair.diskqueue);
1463 
1464 		rewrite_group_diskqueue(cfg, &pair, place_holder);
1465 
1466 		spcs_log("sndr", NULL, gettext("Remote Mirror: forcibly "
1467 		    "removed diskqueue from set %s:%s and its group "),
1468 		    pair.thost, pair.tfile);
1469 		break;
1470 	case RDC_CMD_REPQ:
1471 		if (clustered && (ctag_check(pair.fhost, pair.ffile,
1472 		    pair.fbitmap, pair.thost, pair.tfile, pair.tbitmap,
1473 		    pair.ctag, qvol) < 0))
1474 			exit(1);
1475 
1476 		if (strlen(pair.diskqueue) == 0) {
1477 			rdc_err(NULL, gettext("Remote Mirror set does not "
1478 			    "have a disk queue"));
1479 		}
1480 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1481 		    pair.thost, pair.tfile, pair.tbitmap, RDC_CMD_REMQ, 0,
1482 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1483 		    0) < 0) {
1484 			rdc_err(NULL, gettext("Failed to remove disk queue"));
1485 		}
1486 		if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
1487 			rdc_warn(NULL, gettext("Failed to remove disk queue "
1488 				"[%s] from configuration"), pair.diskqueue);
1489 
1490 		rewrite_group_diskqueue(cfg, &pair, place_holder);
1491 
1492 		/* commit here, enable may fail */
1493 		if (cfg_commit(cfg) < 0) {
1494 			rdc_err(NULL, gettext("commit replace disk queue %s "
1495 				"with %s failed"), pair.diskqueue, qvol);
1496 		}
1497 
1498 		if (check_diskqueue(cfg, qvol, group_arg) == DISKQ_FAIL) {
1499 			rdc_err(NULL,
1500 			    gettext("cannot replace disk queue %s with %s"),
1501 			    pair.diskqueue, qvol);
1502 		}
1503 		if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
1504 		    pair.thost, pair.tfile, pair.tbitmap, RDC_CMD_ADDQ, 0,
1505 		    pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
1506 		    0) < 0) {
1507 			if (cfg_vol_disable(cfg, qvol, ctag, "sndr") < 0)
1508 				rdc_warn(NULL, gettext("Failed to remove disk "
1509 				    "queue [%s] from configuration"), qvol);
1510 			rdc_err(NULL, gettext("Failed to add new disk queue"));
1511 		}
1512 		if (nsc_lookup(volhash, qvol) == NULL)
1513 			if (cfg_vol_enable(cfg, qvol, ctag, "sndr") < 0) {
1514 				rdc_err(NULL, gettext("Replace disk queue "
1515 					"operation failed"));
1516 			}
1517 
1518 		rewrite_group_diskqueue(cfg, &pair, qvol);
1519 
1520 		spcs_log("sndr", NULL, gettext("Remote Mirror: replaced "
1521 		    "diskqueue for set %s:%s and its group with %s"),
1522 		    pair.thost, pair.tfile, qvol);
1523 		break;
1524 	}
1525 
1526 	cfg_unload_svols(cfg);
1527 	cfg_unload_dsvols(cfg);
1528 	cfg_unload_shadows(cfg);
1529 	unload_rdc_vols();
1530 
1531 	if (cfg_commit(cfg) < 0)
1532 		rdc_err(NULL, gettext("commit failed on disk queue operation"));
1533 
1534 	cfg_close(cfg);
1535 	if (ctag)
1536 		free(ctag);
1537 }
1538 void
1539 spcslog_sync(rdcconfig_t *sets, int start, int type)
1540 {
1541 	rdcconfig_t *setp = sets;
1542 
1543 	while (setp) {
1544 		if (start) {
1545 			spcs_log("sndr", NULL,
1546 			    gettext("%s %s %s %s %s %s %s %s\nSync Started"),
1547 			    program, rdc_decode_flag(RDC_CMD_COPY, type),
1548 			    setp->phost, setp->pfile, setp->pbmp,
1549 			    setp->shost, setp->sfile, setp->sbmp);
1550 		} else {
1551 			spcs_log("sndr", NULL,
1552 			    gettext("%s %s %s %s %s %s %s %s\nSync Ended"),
1553 			    program, rdc_decode_flag(RDC_CMD_COPY, type),
1554 			    setp->phost, setp->pfile, setp->pbmp,
1555 			    setp->shost, setp->sfile, setp->sbmp);
1556 		}
1557 		setp = setp->next;
1558 	}
1559 }
1560 
1561 void
1562 spcslog_tunable(char *shost, char *svol)
1563 {
1564 	if (qblock == RDC_OPT_SET_QNOBLOCK)
1565 		spcs_log("sndr", NULL, gettext("diskqueue "
1566 		    "set to non blocking for %s:%s and any members "
1567 		    "of it's group"), shost, svol);
1568 	else if (qblock == RDC_OPT_CLR_QNOBLOCK)
1569 		spcs_log("sndr", NULL, gettext("diskqueue "
1570 		    "set to blocking for %s:%s and any members "
1571 		    "of it's group"), shost, svol);
1572 
1573 	if (maxqfbas)
1574 		spcs_log("sndr", NULL, gettext("maxqfbas set to %d for %s:%s"),
1575 		    maxqfbas, shost, svol);
1576 	if (maxqitems)
1577 		spcs_log("sndr", NULL, gettext("maxwrites set to %d for %s:%s"),
1578 		    maxqitems, shost, svol);
1579 	if (asyncthr)
1580 		spcs_log("sndr", NULL, gettext("%d async threads configured "
1581 		    "for %s:%s"), asyncthr, shost, svol);
1582 }
1583 
1584 int
1585 set_qblock(char *blockarg)
1586 {
1587 	if (strcmp(blockarg, "block") == 0)
1588 		qblock = RDC_OPT_CLR_QNOBLOCK;
1589 	else if (strcmp(blockarg, "noblock") == 0)
1590 		qblock = RDC_OPT_SET_QNOBLOCK;
1591 	else
1592 		return (1);
1593 
1594 	return (0);
1595 }
1596 
1597 static void
1598 rdc_force_disable(CFGFILE *cfg, char *phost, char *pvol, char *pbmp,
1599     char *shost, char *svol, char *sbmp, char *ctag, char *lhname)
1600 {
1601 	rdc_config_t parms;
1602 	spcs_s_info_t ustatus;
1603 	volcount_t *vc;
1604 	char *datavol = NULL;
1605 	char *bmpvol = NULL;
1606 	int on_pri = 0;
1607 	int on_sec = 0;
1608 
1609 	/* are we on the primary or secondary host? */
1610 	if (ctag && *ctag && *lhname) {
1611 		if (strcmp(phost, lhname) == 0) {
1612 			on_pri = 1;
1613 		} else if (strcmp(shost, lhname) == 0) {
1614 			on_sec = 1;
1615 		}
1616 	} else if (self_check(phost)) {
1617 		on_pri = 1;
1618 	} else if (self_check(shost)) {
1619 		on_sec = 1;
1620 	}
1621 
1622 	if (on_pri) {
1623 		datavol = pvol;
1624 		bmpvol = pbmp;
1625 	} else if (on_sec) {
1626 		datavol = svol;
1627 		bmpvol = sbmp;
1628 	} else {
1629 		rdc_err(NULL, gettext("Unable to determine whether current "
1630 		    "node is primary or secondary"));
1631 	}
1632 
1633 	/* set up parms structure */
1634 	parms.command = RDC_CMD_DISABLE;
1635 	(void) strncpy(parms.rdc_set->primary.intf, phost, MAX_RDC_HOST_SIZE);
1636 	(void) strncpy(parms.rdc_set->primary.file, pvol, NSC_MAXPATH);
1637 	(void) strncpy(parms.rdc_set->secondary.intf, shost, MAX_RDC_HOST_SIZE);
1638 	(void) strncpy(parms.rdc_set->secondary.file, svol, NSC_MAXPATH);
1639 	ustatus = spcs_s_ucreate();
1640 	parms.options = RDC_OPT_FORCE_DISABLE;
1641 
1642 	/*
1643 	 * We are now going to 'force' the kernel to disable the set.  By
1644 	 * setting the RDC_OPT_FORCE_DISABLE flag, the kernel will bypass some
1645 	 * of the checks that are normally done when attempting to disable
1646 	 * a set.  We need to do this force option in a cluster environment
1647 	 * when the logical hostname for the primary or secondary volume
1648 	 * is no longer available.
1649 	 */
1650 	spcs_log("sndr", NULL, "%s sndradm -d %s %s %s %s %s %s",
1651 	    gettext("FORCE DISABLE"), phost, pvol, pbmp, shost, svol, sbmp);
1652 	rdc_warn(NULL, gettext("Forcing set disable"));
1653 	if (RDC_IOCTL(RDC_CONFIG, &parms, 0, 0, 0, 0, ustatus) != SPCS_S_OK)
1654 		rdc_warn(&ustatus, gettext("set %s:%s not enabled in kernel"),
1655 		    shost, svol);
1656 
1657 	/* if we get to this point, then a set was disabled.  try sv-disable */
1658 	vc = nsc_lookup(volhash, datavol);
1659 	if (vc && (1 == vc->count))
1660 		if (cfg_vol_disable(cfg, datavol, ctag, "sndr") < 0)
1661 			rdc_warn(NULL, gettext("Failed to remove data volume "
1662 			    "[%s] from configuration"), datavol);
1663 	vc = nsc_lookup(volhash, bmpvol);
1664 	if (vc && (1 == vc->count))
1665 		if (cfg_vol_disable(cfg, bmpvol, ctag, "sndr") < 0)
1666 			rdc_warn(NULL, gettext("Failed to remove bitmap "
1667 			    "[%s] from configuration"), bmpvol);
1668 }
1669 
1670 void
1671 check_rdcsecondary(char *secondary)
1672 {
1673 	int i;
1674 	CFGFILE *cfg;
1675 	int entries;
1676 	char **entry;
1677 	char *sha;
1678 	char *buf;
1679 
1680 	if ((cfg = cfg_open(NULL)) == NULL)
1681 		rdc_err(NULL,
1682 		    gettext("error opening config"));
1683 	if (!cfg_lock(cfg, CFG_RDLOCK))
1684 		rdc_err(NULL, gettext("error locking config"));
1685 
1686 	entry = NULL;
1687 	entries = cfg_get_section(cfg, &entry, "ii");
1688 	for (i = 0; i < entries; i++) {
1689 		buf = entry[i];
1690 
1691 		(void) strtok(buf, " ");	/* master */
1692 		sha = strtok(NULL, " ");	/* shadow */
1693 		if (strcmp(secondary, sha) == 0) {
1694 			rdc_err(NULL,
1695 			    gettext("secondary %s is in use by"
1696 			    " Point-in-Time Copy"), secondary);
1697 		}
1698 		free(buf);
1699 	}
1700 	if (entries)
1701 		free(entry);
1702 	cfg_close(cfg);
1703 }
1704 
1705 int
1706 main(int argc, char *argv[])
1707 {
1708 	char config_file[FILENAME_MAX];
1709 	char fromhost[MAX_RDC_HOST_SIZE];
1710 	char tohost[MAX_RDC_HOST_SIZE];
1711 	char fromfile[NSC_MAXPATH];
1712 	char tofile[NSC_MAXPATH];
1713 	char frombitmap[NSC_MAXPATH];
1714 	char tobitmap[NSC_MAXPATH];
1715 	char directfile[NSC_MAXPATH];
1716 	char group[NSC_MAXPATH];
1717 	char ctag[MAX_RDC_HOST_SIZE];
1718 	char options_cfg[CFG_MAX_BUF];
1719 	char fromnetaddr[RDC_MAXADDR];
1720 	char tonetaddr[RDC_MAXADDR];
1721 	char tmphost[MAX_RDC_HOST_SIZE];
1722 	char tmpfile[NSC_MAXPATH];
1723 	char tmpbitmap[NSC_MAXPATH];
1724 	char diskqueue[NSC_MAXPATH];
1725 	char lhname[MAX_RDC_HOST_SIZE];
1726 	char mode[16];
1727 	rdc_version_t rdc_version;
1728 	int pairs;
1729 	int pid;
1730 	int flag = 0;
1731 	int fflag = 0;
1732 	int reverse = 0;
1733 	int nflag = 0;
1734 	int iflag = 0;
1735 	int doasync;
1736 	int pflag = 0;
1737 	int vflag = 0;
1738 	int verbose = 0;
1739 	int errflag = 0;
1740 	int cfgflag = 0;
1741 	int cfg_success;
1742 	int Iflag = 0;
1743 	char c;
1744 	char inval = 0;
1745 	int found;
1746 	int rc;
1747 	int geflag = 0;
1748 	int qflag = 0;
1749 	char *qarg;
1750 	int Bflag = 0;
1751 	char *bitfile;
1752 	CFGFILE *cfg = NULL;
1753 	int i;
1754 	int setnumber;
1755 	char key[CFG_MAX_KEY];
1756 	char buf[CFG_MAX_BUF];
1757 	char ctag_arg[MAX_RDC_HOST_SIZE];
1758 	char group_arg[NSC_MAXPATH];
1759 	int file_format = 0;
1760 	int sev;
1761 	int diskq_group = DISKQ_OKAY;
1762 	int extra_argc;
1763 	char *ctag_p, *group_p, *diskqueue_p;
1764 	char *required;
1765 	char *role_env;
1766 	int checksetfields = -1;
1767 	nsc_off_t boffset = 0;
1768 	int oflag = 0;
1769 	rdcconfig_t *sets = NULL;
1770 	rdcconfig_t *sets_p = NULL;
1771 	rdc_rc_t *rclist = NULL;
1772 	rdc_rc_t *rcp = NULL;
1773 	int host_not_found = 0;
1774 
1775 	(void) setlocale(LC_ALL, "");
1776 	(void) textdomain("rdc");
1777 	role_env = getenv("SNDR_ROLE_REVERSE");
1778 	if (role_env && strcmp(role_env, "sndr_allow_reverse") == 0)
1779 		allow_role = 1;
1780 
1781 	program = basename(argv[0]);
1782 
1783 	rc = rdc_check_release(&required);
1784 	if (rc < 0) {
1785 		rdc_err(NULL,
1786 		    gettext("unable to determine the current "
1787 		    "Solaris release: %s\n"), strerror(errno));
1788 	} else if (rc == FALSE) {
1789 		rdc_err(NULL,
1790 		    gettext("incorrect Solaris release (requires %s)\n"),
1791 		    required);
1792 	}
1793 
1794 	if ((clustered = cfg_iscluster()) < 0) {
1795 		rdc_err(NULL, gettext("unable to ascertain environment"));
1796 	}
1797 
1798 	(void) strcpy(ctag_arg, "");
1799 	(void) strcpy(group_arg, "");
1800 	bzero(ctag, MAX_RDC_HOST_SIZE);
1801 	bzero(reconfig_ctag, MAX_RDC_HOST_SIZE);
1802 	bzero(diskqueue, NSC_MAXPATH);
1803 
1804 	rdc_maxsets = rdc_get_maxsets();
1805 	if (rdc_maxsets == -1) {
1806 		rdc_err(NULL,
1807 		    gettext("unable to get maxsets value from kernel"));
1808 	}
1809 
1810 	pair_list = calloc(rdc_maxsets, sizeof (*pair_list));
1811 	if (pair_list == NULL) {
1812 		rdc_err(NULL,
1813 		    gettext("unable to allocate pair_list array for %d sets"),
1814 		    rdc_maxsets);
1815 	}
1816 
1817 	bzero(group, sizeof (group));
1818 	bzero(diskqueue, sizeof (diskqueue));
1819 	qblock = 0;
1820 
1821 	while ((c =
1822 #ifdef DEBUG
1823 	    getopt(argc, argv, "A:B:C:D:EF:HIO:PRUW:a:bdef:g:hilmno:pq:rsuvw"))
1824 #else
1825 	    getopt(argc, argv, "A:B:C:D:EF:HIO:PRUW:a:bdef:g:hilmno:pq:rsuvw"))
1826 #endif
1827 	    != -1) {
1828 		switch (c) {
1829 		case 'B':
1830 			if (!allow_role || flag) {
1831 				inval = 1;
1832 				break;
1833 			}
1834 			bitfile = optarg;
1835 			Bflag = 1;
1836 			flag = RDC_BITMAPOP;
1837 			break;
1838 		case 'H':
1839 			/* 'h' was already assigned */
1840 			if (flag)
1841 				inval = 1;
1842 			flag = RDC_CMD_HEALTH;
1843 			break;
1844 		case 'I':
1845 			/* List or edit ndr_ii configuration entries */
1846 			Iflag = 1;
1847 			break;
1848 		case 'R':
1849 			if (flag)
1850 				inval = 1;
1851 			flag = RDC_CMD_RECONFIG;
1852 			break;
1853 #ifdef DEBUG
1854 		case 'U':		/* UDP support */
1855 			proto_test = 1;
1856 			break;
1857 #endif
1858 		case 'F':
1859 			if (flag && flag != RDC_CMD_TUNABLE)
1860 				inval = 1;
1861 			flag = RDC_CMD_TUNABLE;
1862 
1863 			if (check_intrange(optarg))
1864 				maxqfbas = atoi(optarg);
1865 			else
1866 				exit(1);
1867 
1868 			break;
1869 		case 'W':
1870 			if (flag && flag != RDC_CMD_TUNABLE)
1871 				inval = 1;
1872 			flag = RDC_CMD_TUNABLE;
1873 
1874 			if (check_intrange(optarg))
1875 				maxqitems = atoi(optarg);
1876 			else
1877 				exit(1);
1878 
1879 			break;
1880 		case 'A':
1881 			if (flag && flag != RDC_CMD_TUNABLE)
1882 				inval = 1;
1883 			flag = RDC_CMD_TUNABLE;
1884 
1885 			if (check_intrange(optarg))
1886 				asyncthr = atoi(optarg);
1887 			else
1888 				exit(1);
1889 
1890 			break;
1891 		case 'D':
1892 			if (flag && flag != RDC_CMD_TUNABLE)
1893 				inval = 1;
1894 			flag = RDC_CMD_TUNABLE;
1895 
1896 			if (set_qblock(optarg)) {
1897 				usage();
1898 				exit(1);
1899 			}
1900 			iflag |= qblock;
1901 			break;
1902 		case 'a':
1903 			if (flag && flag != RDC_CMD_TUNABLE)
1904 				inval = 1;
1905 			flag = RDC_CMD_TUNABLE;
1906 			if (strcmp(optarg, "off") == 0)
1907 				autosync = AUTOSYNC_OFF;
1908 			else if (strcmp(optarg, "on") == 0)
1909 				autosync = AUTOSYNC_ON;
1910 			else
1911 				inval = 1;
1912 			break;
1913 		case 'C':
1914 			if (clustered) {
1915 				(void) strncpy(ctag_arg, optarg,
1916 				    MAX_RDC_HOST_SIZE);
1917 				process_clocal(ctag_arg);
1918 			} else
1919 				inval = 1;
1920 			break;
1921 		case 'g':
1922 			if (flag == RDC_CMD_ENABLE)
1923 				inval = 1;
1924 			geflag = 1;
1925 			(void) strncpy(group_arg, optarg, NSC_MAXPATH);
1926 			verify_groupname(group_arg);
1927 			break;
1928 		case 'b':
1929 			/* ignore */
1930 			break;
1931 		case 'n':
1932 			nflag = 1;
1933 			break;
1934 		case 'd':
1935 			if (flag)
1936 				inval = 1;
1937 			flag = RDC_CMD_DISABLE;
1938 			break;
1939 		case 'e':
1940 			if (flag || geflag)
1941 				inval = 1;
1942 			flag = RDC_CMD_ENABLE;
1943 			iflag |= RDC_OPT_SETBMP;
1944 			break;
1945 		case 'E':
1946 			if (flag)
1947 				inval = 1;
1948 			flag = RDC_CMD_ENABLE;
1949 			iflag |= RDC_OPT_CLRBMP;
1950 			break;
1951 		case 'f':
1952 			fflag = 1;
1953 			(void) strcpy(config_file, optarg);
1954 			break;
1955 		case 'h':
1956 			usage();
1957 			exit(0);
1958 			break;
1959 		case 'l':
1960 			if (flag)
1961 				inval = 1;
1962 			flag = RDC_CMD_LOG;
1963 			break;
1964 		case 'm':
1965 			if (flag)
1966 				inval = 1;
1967 			flag = RDC_CMD_COPY;
1968 			iflag |= RDC_OPT_FULL;
1969 			break;
1970 		case 'O':
1971 		case 'o':
1972 
1973 			if (!allow_role || oflag) {
1974 				inval = 1;
1975 				break;
1976 			}
1977 			if (c == 'o') {
1978 				oflag = RDC_BITMAPOR;
1979 			} else {
1980 				oflag = RDC_BITMAPSET;
1981 			}
1982 			boffset = strtoull(optarg, NULL, 0);
1983 			break;
1984 		case 'P':
1985 			if (flag)
1986 				inval = 1;
1987 			pflag = 1;
1988 			verbose = 1;
1989 			break;
1990 		case 'p':
1991 			if (flag)
1992 				inval = 1;
1993 			pflag = 1;
1994 			break;
1995 		case 'q':
1996 			if (flag)
1997 				inval = 1;
1998 			flag = RDC_CMD_INITQ;
1999 			qflag = optind;
2000 			qarg = optarg;
2001 			break;
2002 		case 'i':
2003 			if (flag)
2004 				inval = 1;
2005 			pflag = 1;
2006 			file_format = 1;
2007 			break;
2008 		case 'r':
2009 			reverse = 1;
2010 			iflag |= RDC_OPT_REVERSE;
2011 			break;
2012 		case 's':
2013 			if (flag)
2014 				inval = 1;
2015 			flag = RDC_CMD_STATUS;
2016 			nflag = 1;	/* No prompt for a status */
2017 			break;
2018 		case 'u':
2019 			if (flag)
2020 				inval = 1;
2021 			flag = RDC_CMD_COPY;
2022 			iflag |= RDC_OPT_UPDATE;
2023 			break;
2024 		case 'v':
2025 			if (flag)
2026 				inval = 1;
2027 			pflag = 1;
2028 			vflag = 1;
2029 			break;
2030 		case 'w':
2031 			if (flag)
2032 				inval = 1;
2033 			flag = RDC_CMD_WAIT;
2034 			break;
2035 		case '?':
2036 			errflag++;
2037 		}
2038 	}
2039 
2040 	if (inval || ((flag != RDC_BITMAPOP) && oflag)) {
2041 		rdc_warn(NULL, gettext("invalid argument combination"));
2042 		errflag = 1;
2043 	}
2044 
2045 	if (flag && Iflag) {
2046 		/* Mutually incompatible */
2047 		usage();
2048 		exit(1);
2049 	}
2050 
2051 	if (Iflag) {
2052 		rdc_ii_config(argc, argv);
2053 		exit(0);
2054 	}
2055 
2056 	if (vflag) {
2057 		spcs_s_info_t ustatus;
2058 
2059 		ustatus = spcs_s_ucreate();
2060 		rc = RDC_IOCTL(RDC_VERSION, &rdc_version, 0, 0, 0, 0, ustatus);
2061 		if (rc == SPCS_S_ERROR) {
2062 			rdc_err(&ustatus, gettext("statistics error"));
2063 		}
2064 		spcs_s_ufree(&ustatus);
2065 #ifdef DEBUG
2066 		(void) printf(gettext("Remote Mirror version %d.%d.%d.%d\n"),
2067 		    rdc_version.major, rdc_version.minor,
2068 		    rdc_version.micro, rdc_version.baseline);
2069 #else
2070 		if (rdc_version.micro) {
2071 			(void) printf(gettext(
2072 			    "Remote Mirror version %d.%d.%d\n"),
2073 			    rdc_version.major,
2074 			    rdc_version.minor,
2075 			    rdc_version.micro);
2076 		} else {
2077 			(void) printf(gettext("Remote Mirror version %d.%d\n"),
2078 			    rdc_version.major, rdc_version.minor);
2079 		}
2080 #endif
2081 		exit(0);
2082 	}
2083 
2084 	if (!(flag || pflag) || errflag) {
2085 		usage();
2086 		exit(1);
2087 	}
2088 
2089 	if (pflag && !fflag && (argc - optind) == 0) {
2090 		/* print with no set specified */
2091 		exit(rdc_print(file_format, verbose,
2092 		    group_arg, ctag_arg, NULL, NULL, NULL));
2093 	}
2094 
2095 	if (qflag) {	/* change disk queue setting */
2096 		int	subcmd = 0;
2097 		int	offset = 0;
2098 		char	*ptr;
2099 		char	*qvol;
2100 		char	tohost_arg[MAX_RDC_HOST_SIZE];
2101 		char	tofile_arg[NSC_MAXPATH];
2102 
2103 		if (strcmp("a", qarg) == 0) {
2104 			subcmd = RDC_CMD_ADDQ;
2105 			offset = 1;
2106 		} else if (strcmp("d", qarg) == 0) {
2107 			subcmd = RDC_CMD_REMQ;
2108 			offset = 0;
2109 		} else if (strcmp("r", qarg) == 0) {
2110 			subcmd = RDC_CMD_REPQ;
2111 			offset = 1;
2112 		} else {
2113 			rdc_warn(NULL, " %s Invalid qopt", qarg);
2114 			q_usage(1);
2115 			exit(1);
2116 		}
2117 		if (strlen(group_arg) == 0) {
2118 			/* pick out single set as shost:svol */
2119 			ptr = strtok(argv[qflag + offset], ":");
2120 			if (ptr)
2121 				(void) strncpy(tohost_arg, ptr,
2122 				    MAX_RDC_HOST_SIZE);
2123 			else {
2124 				rdc_warn(NULL, gettext("Bad host specified"));
2125 				q_usage(1);
2126 				exit(1);
2127 			}
2128 			ptr = strtok(NULL, ":");
2129 			if (ptr)
2130 				(void) strncpy(tofile_arg, ptr, NSC_MAXPATH);
2131 			else {
2132 				rdc_warn(NULL, gettext("Bad set specified"));
2133 				q_usage(1);
2134 				exit(1);
2135 			}
2136 		}
2137 
2138 		qvol = argv[qflag];
2139 		if ((qvol == NULL) && (subcmd != RDC_CMD_REMQ)) {
2140 			rdc_warn(NULL, gettext("missing queue volume"));
2141 			q_usage(1);
2142 			exit(1);
2143 		}
2144 		diskq_subcmd(subcmd, qvol, group_arg, ctag_arg,
2145 		    tohost_arg, tofile_arg);
2146 		exit(0);
2147 	}
2148 
2149 	if (flag == RDC_CMD_RECONFIG && !fflag) {
2150 		/* See what is to be reconfigured */
2151 		if (argc - optind == 0)
2152 			flag = RDC_CMD_RESET;
2153 		else {
2154 			if (argc - optind < 2) {
2155 				usage();
2156 				exit(1);
2157 			}
2158 			c = *argv[optind++];
2159 			if (argv[optind -1][1] != '\0') {
2160 				usage();
2161 				exit(2);
2162 			}
2163 			switch (c) {
2164 			case 'b':
2165 				if (argc - optind < 2) {
2166 					usage();
2167 					exit(1);
2168 				}
2169 				if (*argv[optind] == 'p')
2170 					reconfig_pbitmap = argv[++optind];
2171 				else if (*argv[optind] == 's')
2172 					reconfig_sbitmap = argv[++optind];
2173 				else {
2174 					usage();
2175 					exit(1);
2176 				}
2177 				optind++;
2178 				break;
2179 #ifdef _RDC_CAMPUS
2180 			case 'd':
2181 				reconfig_direct = argv[optind++];
2182 				break;
2183 #endif
2184 			case 'g':
2185 				reconfig_group = argv[optind++];
2186 				verify_groupname(reconfig_group);
2187 				break;
2188 			case 'C':
2189 				if (clustered) {
2190 					(void) strncpy(reconfig_ctag,
2191 					    argv[optind++], MAX_RDC_HOST_SIZE);
2192 					process_clocal(reconfig_ctag);
2193 				} else {
2194 					usage();
2195 					exit(1);
2196 				}
2197 				break;
2198 			case 'm':
2199 				if (strcmp(argv[optind], "sync") == 0)
2200 					reconfig_doasync = 0;
2201 				else if (strcmp(argv[optind], "async") == 0)
2202 					reconfig_doasync = 1;
2203 				else {
2204 					usage();
2205 					exit(1);
2206 				}
2207 				optind++;
2208 				break;
2209 			case 'r':
2210 				if (allow_role) {
2211 					iflag |= RDC_OPT_REVERSE_ROLE;
2212 					break;
2213 				}
2214 			default:
2215 				usage();
2216 				exit(1);
2217 			}
2218 		}
2219 	}
2220 	if (fflag) {
2221 		checksetfields = 1;
2222 		if ((argc - optind) != 0) {
2223 			usage();
2224 			exit(1);
2225 		}
2226 	} else {
2227 		if ((argc - optind) == 0) {
2228 			/* Use libcfg to figure out what to operate on */
2229 			cfgflag = 1;
2230 #ifdef DEBUG
2231 			rdc_warn(NULL, gettext("using current config"));
2232 #endif
2233 			checksetfields = 0;
2234 		} else {
2235 			if ((argc - optind) < 8 && (argc - optind) != 1) {
2236 				usage();
2237 				exit(1);
2238 			}
2239 		}
2240 	}
2241 
2242 	if (cfgflag) {
2243 		if (flag == RDC_CMD_ADDQ ||
2244 		    flag == RDC_CMD_REMQ ||
2245 		    flag == RDC_CMD_KILLQ ||
2246 		    flag == RDC_CMD_INITQ) {
2247 			rdc_err(NULL, gettext("can not use current config "
2248 			    "for disk queue operations"));
2249 		}
2250 	} else if (fflag) {
2251 		if (flag == RDC_CMD_ADDQ ||
2252 		    flag == RDC_CMD_REMQ ||
2253 		    flag == RDC_CMD_KILLQ ||
2254 		    flag == RDC_CMD_INITQ) {
2255 			rdc_err(NULL, gettext("can not use a config file "
2256 			    "for disk queue operations"));
2257 		}
2258 	}
2259 	if (cfgflag) {
2260 		if (flag == RDC_CMD_ENABLE) {
2261 			rdc_err(NULL, gettext("can not use current config "
2262 			    "for enable command"));
2263 		}
2264 		if ((flag == RDC_CMD_RECONFIG) && (reconfig_pbitmap ||
2265 		    reconfig_sbitmap)) {
2266 			rdc_err(NULL, gettext("can not use current config "
2267 			    "for bitmap reconfiguration"));
2268 		}
2269 		if (flag == RDC_BITMAPOP) {
2270 			rdc_err(NULL, gettext("can not use current config "
2271 			    "for bitmap set command"));
2272 		}
2273 		pairs = read_libcfg(flag, group_arg, ctag_arg);
2274 		if (pairs == 0) {
2275 			(void) fprintf(stderr,
2276 			    gettext("no matching Remote Mirror sets found "
2277 			    "in config\n"));
2278 			exit(1);
2279 		}
2280 	} else if (!fflag) {
2281 		/*
2282 		 *	Format is either:
2283 		 *
2284 		 * tohost:tofile
2285 		 *
2286 		 *	or something like this for example:
2287 		 *
2288 		 * fromhost fromfile frombitmap tohost tofile tobitmap ip sync
2289 		 *	g group C ctag
2290 		 */
2291 
2292 		if (argc - optind == 1) {
2293 			char tohost_arg[MAX_RDC_HOST_SIZE];
2294 			char tofile_arg[NSC_MAXPATH];
2295 			char *ptr;
2296 
2297 			checksetfields = 0;
2298 			if (flag == RDC_CMD_ENABLE) {
2299 				rdc_err(NULL,
2300 				    gettext("must specify full set details for "
2301 				    "enable command"));
2302 			}
2303 			ptr = strtok(argv[optind], ":");
2304 			if (ptr)
2305 				(void) strncpy(tohost_arg, ptr,
2306 				    MAX_RDC_HOST_SIZE);
2307 			else {
2308 				rdc_err(NULL, gettext("Bad host specified"));
2309 			}
2310 			ptr = strtok(NULL, ":");
2311 			if (ptr)
2312 				(void) strncpy(tofile_arg, ptr, NSC_MAXPATH);
2313 			else {
2314 				rdc_err(NULL, gettext("Bad set specified"));
2315 			}
2316 
2317 			/* Now look up tohost:tofile via libcfg */
2318 
2319 			if ((cfg = cfg_open(NULL)) == NULL)
2320 				rdc_err(NULL,
2321 				    gettext("unable to access configuration"));
2322 
2323 			if (!cfg_lock(cfg, CFG_RDLOCK))
2324 				rdc_err(NULL,
2325 				    gettext("unable to lock configuration"));
2326 
2327 			setnumber = 0;
2328 			found = 0;
2329 			/*CSTYLED*/
2330 			for (i = 0; i < rdc_maxsets;) {
2331 				setnumber++;
2332 
2333 				bzero(buf, CFG_MAX_BUF);
2334 				(void) snprintf(key, sizeof (key),
2335 				    "sndr.set%d", setnumber);
2336 				rc = cfg_get_cstring(cfg, key, buf,
2337 				    CFG_MAX_BUF);
2338 				if (rc < 0)
2339 					break;
2340 
2341 				(void) snprintf(key, sizeof (key),
2342 				    "sndr.set%d.shost", setnumber);
2343 				(void) cfg_get_cstring(cfg, key, tohost,
2344 				    sizeof (tohost));
2345 				if (strncmp(tohost, tohost_arg, NSC_MAXPATH))
2346 					continue;
2347 
2348 				(void) snprintf(key, sizeof (key),
2349 				    "sndr.set%d.secondary", setnumber);
2350 				(void) cfg_get_cstring(cfg, key, tofile,
2351 				    sizeof (tofile));
2352 				if (strncmp(tofile, tofile_arg, NSC_MAXPATH))
2353 					continue;
2354 
2355 				found = 1;
2356 
2357 				(void) snprintf(key, sizeof (key),
2358 				    "sndr.set%d.phost", setnumber);
2359 				(void) cfg_get_cstring(cfg, key, fromhost,
2360 				    sizeof (fromhost));
2361 
2362 				(void) snprintf(key, sizeof (key),
2363 				    "sndr.set%d.primary", setnumber);
2364 				(void) cfg_get_cstring(cfg, key, fromfile,
2365 				    sizeof (fromfile));
2366 
2367 				(void) snprintf(key, sizeof (key),
2368 				    "sndr.set%d.pbitmap", setnumber);
2369 				(void) cfg_get_cstring(cfg, key, frombitmap,
2370 				    sizeof (frombitmap));
2371 
2372 				(void) snprintf(key, sizeof (key),
2373 				    "sndr.set%d.sbitmap", setnumber);
2374 				(void) cfg_get_cstring(cfg, key, tobitmap,
2375 				    sizeof (tobitmap));
2376 
2377 				(void) snprintf(key, sizeof (key),
2378 				    "sndr.set%d.type", setnumber);
2379 				(void) cfg_get_cstring(cfg, key, directfile,
2380 				    sizeof (directfile));
2381 				if (strcmp(directfile, "ip") == 0)
2382 					(void) strcpy(directfile, "");
2383 
2384 				(void) snprintf(key, sizeof (key),
2385 				    "sndr.set%d.mode", setnumber);
2386 				(void) cfg_get_cstring(
2387 				    cfg, key, mode, sizeof (mode));
2388 
2389 				(void) snprintf(key, sizeof (key),
2390 				    "sndr.set%d.group", setnumber);
2391 				(void) cfg_get_cstring(cfg, key, group,
2392 				    sizeof (group));
2393 				if (strcmp(group_arg, "") &&
2394 				    strncmp(group_arg, group, NSC_MAXPATH))
2395 					continue;
2396 				(void) snprintf(key, sizeof (key),
2397 				    "sndr.set%d.cnode", setnumber);
2398 				(void) cfg_get_cstring(
2399 				    cfg, key, ctag, sizeof (ctag));
2400 				if ((strlen(ctag_arg) > 0) &&
2401 				    (strcmp(ctag_arg, ctag) != 0))
2402 					rdc_err(NULL,
2403 					    gettext("ctags %s and %s "
2404 					    "do not match"), ctag_arg, ctag);
2405 
2406 				if (strcmp(mode, "sync") == 0)
2407 					doasync = 0;
2408 				else if (strcmp(mode, "async") == 0)
2409 					doasync = 1;
2410 				else {
2411 					rdc_err(NULL,
2412 					    gettext("set %s:%s neither sync "
2413 					    "nor async"), tohost, tofile);
2414 				}
2415 				break;
2416 			}
2417 			cfg_close(cfg);
2418 			if (!found) {
2419 				rdc_err(NULL,
2420 				    gettext("set %s:%s not found in config"),
2421 				    tohost_arg, tofile_arg);
2422 			}
2423 		} else {
2424 			checksetfields = 1;
2425 			(void) strncpy(fromhost, argv[optind],
2426 			    MAX_RDC_HOST_SIZE);
2427 			(void) strncpy(fromfile, argv[optind+1], NSC_MAXPATH);
2428 			(void) strncpy(frombitmap, argv[optind+2], NSC_MAXPATH);
2429 			(void) strncpy(tohost, argv[optind+3],
2430 			    MAX_RDC_HOST_SIZE);
2431 			(void) strncpy(tofile, argv[optind+4], NSC_MAXPATH);
2432 			(void) strncpy(tobitmap, argv[optind+5], NSC_MAXPATH);
2433 
2434 			/* Check the length of entries from the command line */
2435 			if ((fromhost[MAX_RDC_HOST_SIZE - 1] != '\0') ||
2436 			    (tohost[MAX_RDC_HOST_SIZE - 1] != '\0')) {
2437 				rdc_err(NULL,
2438 				    gettext("hostname is longer than %d "
2439 				    "characters\n"), (MAX_RDC_HOST_SIZE - 1));
2440 			}
2441 
2442 			/* Check if it's ip address -- not allowed */
2443 			if ((inet_addr(fromhost) != (in_addr_t)(-1)) ||
2444 			    (inet_addr(tohost) != (in_addr_t)(-1))) {
2445 				rdc_err(NULL, gettext(
2446 				    "The hostname specified is invalid.\n"
2447 				    "See 'man inet(3SOCKET)'"));
2448 			}
2449 
2450 			if ((fromfile[NSC_MAXPATH - 1] != '\0') ||
2451 			    (tofile[NSC_MAXPATH - 1] != '\0') ||
2452 			    (frombitmap[NSC_MAXPATH - 1] != '\0') ||
2453 			    (tobitmap[NSC_MAXPATH - 1] != '\0')) {
2454 				rdc_err(NULL, gettext("device name is longer "
2455 				"than %d characters\n"), (NSC_MAXPATH - 1));
2456 			}
2457 #ifdef _RDC_CAMPUS
2458 			if (argv[optind+6][0] == '/') {
2459 				/* FCAL directio */
2460 				(void) strncpy(directfile, argv[optind+6],
2461 				    NSC_MAXPATH);
2462 			} else if (strcmp(argv[optind+6], "ip") != 0) {
2463 #else
2464 			if (strcmp(argv[optind+6], "ip") != 0) {
2465 #endif
2466 				usage();
2467 				exit(1);
2468 			} else
2469 				(void) strcpy(directfile, "ip");
2470 
2471 			if (strcmp(argv[optind+7], "sync") == 0)
2472 				doasync = 0;
2473 			else if (strcmp(argv[optind+7], "async") == 0)
2474 				doasync = 1;
2475 			else {
2476 				usage();
2477 				exit(1);
2478 			}
2479 
2480 			/*
2481 			 * At this point, we could have a set which is
2482 			 * clustered, but neither a 'C ctag' or '-C ctag' has
2483 			 * been specified. To avoid clobbering the ctag if a
2484 			 * dscfg operation is done in the future, we should get
2485 			 * the ctag out of the config at this point. To do this,
2486 			 * set the cluster resource filter to NULL to look at
2487 			 * all sets in the config, pulling out the ctag for the
2488 			 * set matching shost:svol. If the set is not found,
2489 			 * fail here. Note, we skip this set on an enable as the
2490 			 * set is not yet in the config, so no need to waste
2491 			 * time.
2492 			 */
2493 			if ((argc - optind == 8) && clustered &&
2494 			    (flag != RDC_CMD_ENABLE)) {
2495 				int setnumber;
2496 				char key[CFG_MAX_KEY];
2497 
2498 				if ((cfg = cfg_open(NULL)) == NULL) {
2499 				    rdc_err(NULL,
2500 				    gettext("unable to access configuration"));
2501 				}
2502 				if (!cfg_lock(cfg, CFG_RDLOCK)) {
2503 				    rdc_err(NULL,
2504 				    gettext("unable to lock configuration"));
2505 				}
2506 
2507 				cfg_resource(cfg, NULL);
2508 
2509 				if ((setnumber =
2510 				    find_setnumber_in_libcfg(cfg, NULL, tohost,
2511 				    tofile)) < 0) {
2512 					cfg_close(cfg);
2513 					rdc_err(NULL,
2514 					    gettext("unable to find Remote "
2515 					    "Mirror set "
2516 					    "%s:%s in config"),
2517 					    tohost, tofile);
2518 				}
2519 
2520 				(void) snprintf(key, sizeof (key),
2521 				    "sndr.set%d.cnode", setnumber);
2522 				if (cfg_get_cstring(cfg, key, ctag_arg,
2523 				    MAX_RDC_HOST_SIZE) < 0) {
2524 					cfg_close(cfg);
2525 					rdc_err(NULL,
2526 					    gettext("unable to determine ctag "
2527 					    "for Remote Mirror set %s:%s"),
2528 					    tohost, tofile);
2529 				}
2530 
2531 				rdc_islocal = strcmp(ctag_arg, "-") ? 0 : 1;
2532 
2533 				cfg_close(cfg);
2534 			}
2535 
2536 			extra_argc = argc - optind;
2537 			if (extra_argc < 8 || extra_argc > 14 ||
2538 			    extra_argc % 2 != 0) {
2539 				usage();
2540 				exit(1);
2541 			}
2542 
2543 			/*
2544 			 * Loop through all of the extra arguments specified
2545 			 * on the command line, setting the appropriate values
2546 			 * for valid entries. If an unrecognized argument is
2547 			 * detected, abort with error. Note: This hack should be
2548 			 * removed and we should not accept these entries as
2549 			 * arguments, they should be passed in as switches.
2550 			 */
2551 			for (i = (8 + optind); i < argc; i += 2) {
2552 				/* string case statement */
2553 				if (strcmp(argv[i], "g") == 0) {
2554 				    (void) strncpy(group, argv[i + 1],
2555 				        NSC_MAXPATH);
2556 				    if (group[NSC_MAXPATH - 1] != '\0') {
2557 					rdc_err(NULL, gettext("group name is "
2558 					"longer than %d characters\n"),
2559 					(NSC_MAXPATH - 1));
2560 				    }
2561 				} else if (strcmp(argv[i], "C") == 0) {
2562 				    if (!clustered) {
2563 					usage();
2564 					exit(1);
2565 				    }
2566 				    (void) strncpy(ctag, argv[i + 1],
2567 					    MAX_RDC_HOST_SIZE);
2568 
2569 				    if (ctag[MAX_RDC_HOST_SIZE - 1] != '\0') {
2570 					rdc_err(NULL, gettext("cluster name "
2571 					"is longer than %d characters\n"),
2572 					(MAX_RDC_HOST_SIZE - 1));
2573 				    }
2574 				    process_clocal(ctag);
2575 
2576 				/*
2577 				 * well here is something.
2578 				 * what if they went sndradm -C local
2579 				 * host a b host a b ip sync C foobar?
2580 				 * they might be confused
2581 				 * lets stop them if ctag_arg and ctag
2582 				 * don't match and forgive if they are
2583 				 * the same, below also.
2584 				 */
2585 				    if ((strlen(ctag_arg) > 0) &&
2586 					(strcmp(ctag_arg, ctag) != 0)) {
2587 					    rdc_err(NULL, gettext("ctags "
2588 						"%s and %s do not match "),
2589 						ctag_arg, ctag);
2590 
2591 				    }
2592 				} else if (strcmp(argv[i], "q") == 0) {
2593 				    (void) strncpy(diskqueue, argv[i + 1],
2594 					    NSC_MAXPATH);
2595 				    if (diskqueue[NSC_MAXPATH - 1] != '\0') {
2596 					rdc_err(NULL, gettext("diskq name is "
2597 					"longer than %d characters\n"),
2598 					(NSC_MAXPATH - 1));
2599 				    }
2600 				} else {
2601 					/* Unrecognized argument */
2602 					usage();
2603 					exit(1);
2604 				}
2605 			}
2606 		}
2607 
2608 		/*
2609 		 * Are we able to determine the existance of either
2610 		 * of these host addresses?
2611 		 */
2612 		if (gethost_netaddrs(fromhost, tohost,
2613 		    (char *)&fromnetaddr, (char *)&tonetaddr) < 0) {
2614 			(void) fprintf(stderr, "\n");
2615 			rdc_warn(NULL, gettext("unable to determine IP "
2616 				"addresses for either host %s or host %s"),
2617 				fromhost, tohost);
2618 
2619 			if (flag != RDC_CMD_DISABLE)
2620 				exit(1);
2621 			else
2622 				host_not_found = 1;
2623 		}
2624 
2625 		/*
2626 		 * Are we running on neither host?
2627 		 */
2628 		if (!self_check(fromhost) && !self_check(tohost)) {
2629 			if (flag == RDC_CMD_DISABLE) {
2630 			(void) fprintf(stderr, "\n");
2631 			rdc_warn(NULL, gettext("Not running on either host "
2632 				"%s or host %s"), fromhost, tohost);
2633 			host_not_found = 1;
2634 			}
2635 		}
2636 
2637 		/*
2638 		 * at this point, hopfully it is safe to say that
2639 		 * if a ctag was supplied via -C tag it is safe to
2640 		 * move it from ctag_arg to ctag. If it was passed in
2641 		 * at the end and the beginning of the cli, it must
2642 		 * match, as per checks above. if it was not passed
2643 		 * in at the end, but at the beginning, we can deal.
2644 		 * this should handle the case of shost:svol.
2645 		 * which is the main reason for this.
2646 		 *
2647 		 * there are 3 cases: passed in by cli, checked just above.
2648 		 * using libdscfg, you must pass in -C tag to have
2649 		 * ctag_check pass.
2650 		 * finally a file. same rules as libdscfg.
2651 		 */
2652 		if ((strlen(ctag) == 0) && (strlen(ctag_arg) > 0))
2653 			(void) strcpy(ctag, ctag_arg);
2654 
2655 		if (flag == RDC_CMD_RECONFIG) {
2656 			if (reconfig_pbitmap) {
2657 				(void) strncpy(frombitmap, reconfig_pbitmap,
2658 				    NSC_MAXPATH);
2659 				check_rdcbitmap(flag, fromhost, frombitmap);
2660 			}
2661 			if (reconfig_sbitmap) {
2662 				(void) strncpy(tobitmap, reconfig_sbitmap,
2663 				    NSC_MAXPATH);
2664 				check_rdcbitmap(flag, tohost, tobitmap);
2665 			}
2666 #ifdef _RDC_CAMPUS
2667 			if (reconfig_direct)
2668 				(void) strncpy(directfile, reconfig_direct,
2669 				    NSC_MAXPATH);
2670 #endif
2671 			if (reconfig_group)
2672 				(void) strncpy(group, reconfig_group,
2673 				    NSC_MAXPATH);
2674 
2675 			if (strlen(reconfig_ctag) > 0)
2676 				(void) strncpy(ctag, reconfig_ctag,
2677 				    MAX_RDC_HOST_SIZE);
2678 			if (reconfig_doasync != -1)
2679 				doasync = reconfig_doasync;
2680 		}
2681 
2682 		if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
2683 			if (ctag_check(fromhost, fromfile, frombitmap,
2684 			    tohost, tofile, tobitmap, ctag, diskqueue) < 0)
2685 				exit(1);
2686 			if ((diskq_group = check_diskqueue(NULL, diskqueue,
2687 			    group)) == DISKQ_FAIL) {
2688 				rdc_err(NULL, gettext("disk queue %s is "
2689 				    "incompatible with existing queue"),
2690 				    diskqueue);
2691 			}
2692 
2693 		}
2694 		pairs = 1;
2695 	} else {
2696 		pairs = read_config(flag, config_file, group_arg, ctag_arg);
2697 		if (pairs == 0) {
2698 			rdc_err(NULL, gettext("%s contains no "
2699 			    "matching Remote Mirror sets"), config_file);
2700 		}
2701 	}
2702 
2703 	if (!nflag && !pflag && prompt_user(flag, iflag) == -1)
2704 		exit(1);
2705 
2706 	while (pairs--) {
2707 
2708 		if (cfgflag || fflag) {
2709 			(void) strncpy(fromfile, pair_list[pairs].ffile,
2710 			    NSC_MAXPATH);
2711 			(void) strncpy(tofile, pair_list[pairs].tfile,
2712 			    NSC_MAXPATH);
2713 			(void) strncpy(frombitmap, pair_list[pairs].fbitmap,
2714 			    NSC_MAXPATH);
2715 			(void) strncpy(fromhost,
2716 			    pair_list[pairs].fhost, MAX_RDC_HOST_SIZE);
2717 			(void) strncpy(tohost, pair_list[pairs].thost,
2718 			    MAX_RDC_HOST_SIZE);
2719 			(void) strncpy(tobitmap, pair_list[pairs].tbitmap,
2720 			    NSC_MAXPATH);
2721 			(void) strncpy(directfile, pair_list[pairs].directfile,
2722 			    NSC_MAXPATH);
2723 			(void) strncpy(group, pair_list[pairs].group,
2724 			    NSC_MAXPATH);
2725 			(void) strncpy(ctag, pair_list[pairs].ctag,
2726 			    MAX_RDC_HOST_SIZE);
2727 			(void) strncpy(diskqueue, pair_list[pairs].diskqueue,
2728 			    NSC_MAXPATH);
2729 
2730 			bcopy(pair_list[pairs].fnetaddr, fromnetaddr,
2731 			    RDC_MAXADDR);
2732 			bcopy(pair_list[pairs].tnetaddr, tonetaddr,
2733 			    RDC_MAXADDR);
2734 
2735 			doasync = pair_list[pairs].doasync;
2736 		}
2737 
2738 		if (pflag) {
2739 			static int first = 1;
2740 
2741 			if (first) {
2742 				if ((cfg = cfg_open(NULL)) == NULL)
2743 				    rdc_err(NULL,
2744 				    gettext("unable to access configuration"));
2745 
2746 				if (!cfg_lock(cfg, CFG_RDLOCK))
2747 				    rdc_err(NULL,
2748 				    gettext("unable to lock configuration"));
2749 
2750 				first = 0;
2751 			}
2752 
2753 			(void) rdc_print(file_format, verbose,
2754 			    group_arg, ctag_arg, tohost, tofile, cfg);
2755 
2756 			if (pairs == 0) {
2757 				cfg_close(cfg);
2758 				exit(0);
2759 			}
2760 
2761 			/* short circuit the rest of the command loop */
2762 			continue;
2763 		}
2764 		if (Bflag) {
2765 			int ret;
2766 			ret = rdc_bitmapset(tohost, tofile, bitfile, oflag,
2767 			    boffset);
2768 			exit(ret);
2769 		}
2770 		if ((fflag || cfgflag) && flag == RDC_CMD_RECONFIG) {
2771 			char orig_fbmp[MAXHOSTNAMELEN];
2772 			char orig_tbmp[MAXHOSTNAMELEN];
2773 			int ret;
2774 			rdc_config_t parms;
2775 			spcs_s_info_t ustatus;
2776 
2777 			parms.command = RDC_CMD_STATUS;
2778 			parms.rdc_set->netconfig = NULL;
2779 			(void) strncpy(parms.rdc_set->primary.intf, fromhost,
2780 			    MAX_RDC_HOST_SIZE);
2781 			(void) strncpy(parms.rdc_set->secondary.intf, tohost,
2782 			    MAX_RDC_HOST_SIZE);
2783 			(void) strncpy(parms.rdc_set->primary.file, fromfile,
2784 			    NSC_MAXPATH);
2785 			(void) strncpy(parms.rdc_set->secondary.file, tofile,
2786 			    NSC_MAXPATH);
2787 			ustatus = spcs_s_ucreate();
2788 			ret = RDC_IOCTL(RDC_CONFIG, &parms,
2789 			    NULL, 0, 0, 0, ustatus);
2790 			if (ret != SPCS_S_OK) {
2791 				rdc_err(NULL, gettext("unable to get set status"
2792 				    " before reconfig operation"));
2793 			}
2794 			(void) strncpy(orig_fbmp, parms.rdc_set->primary.bitmap,
2795 			    NSC_MAXPATH);
2796 			(void) strncpy(orig_tbmp,
2797 			    parms.rdc_set->secondary.bitmap, NSC_MAXPATH);
2798 
2799 			if (strncmp(orig_fbmp, frombitmap, NSC_MAXPATH) != 0)
2800 				check_rdcbitmap(flag, fromhost, frombitmap);
2801 			if (strncmp(orig_tbmp, tobitmap, NSC_MAXPATH) != 0)
2802 				check_rdcbitmap(flag, tohost, tobitmap);
2803 			spcs_s_ufree(&ustatus);
2804 
2805 		}
2806 		/*
2807 		 * take a peek in the config to see if
2808 		 * the bitmap is being used elsewhere
2809 		 */
2810 		if (flag == RDC_CMD_ENABLE) {
2811 			struct stat stb;
2812 			/*
2813 			 * just for fun, lets see if some silly person
2814 			 * specified the same vol and bitmap
2815 			 */
2816 			if ((strcmp(fromfile, frombitmap) == 0) ||
2817 			    (strcmp(tofile, tobitmap) == 0))
2818 				rdc_err(NULL, gettext("volumes and bitmaps"
2819 				    " must not match"));
2820 			if (self_check(fromhost)) {
2821 				check_rdcbitmap(flag, fromhost, frombitmap);
2822 				if (stat(fromfile, &stb) != 0) {
2823 					rdc_err(NULL,
2824 					gettext("unable to access %s: %s"),
2825 					fromfile, strerror(errno));
2826 				}
2827 				if (!S_ISCHR(stb.st_mode)) {
2828 					rdc_err(NULL,
2829 					gettext("%s is not a character device"),
2830 					fromfile);
2831 				}
2832 			} else { /* on the secondary */
2833 				check_rdcbitmap(flag, tohost, tobitmap);
2834 				/* extra check for secondary vol */
2835 				check_rdcsecondary(tofile);
2836 				if (stat(tofile, &stb) != 0) {
2837 					rdc_err(NULL,
2838 					gettext("unable to access %s: %s"),
2839 					tofile, strerror(errno));
2840 				}
2841 				if (!S_ISCHR(stb.st_mode)) {
2842 					rdc_err(NULL,
2843 					gettext("%s is not a character device"),
2844 					tofile);
2845 				}
2846 			}
2847 
2848 		}
2849 
2850 		if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_DISABLE ||
2851 		    flag == RDC_CMD_RECONFIG) {
2852 			if ((cfg = cfg_open(NULL)) == NULL)
2853 				rdc_err(NULL,
2854 				    gettext("unable to access configuration"));
2855 
2856 			if (!cfg_lock(cfg, CFG_WRLOCK))
2857 				rdc_err(NULL,
2858 				    gettext("unable to lock configuration"));
2859 
2860 			cfg_resource(cfg, clustered ? ctag : NULL);
2861 		} else
2862 			cfg = NULL;
2863 
2864 		if (cfg && perform_autosv() &&
2865 		    (flag == RDC_CMD_ENABLE || flag == RDC_CMD_DISABLE ||
2866 		    flag == RDC_CMD_RECONFIG)) {
2867 			if (cfg_load_svols(cfg) < 0 ||
2868 			    cfg_load_dsvols(cfg) < 0 ||
2869 			    cfg_load_shadows(cfg) < 0)
2870 				rdc_err(NULL,
2871 				    gettext("Unable to parse config filer"));
2872 			load_rdc_vols(cfg);
2873 		}
2874 		cfg_success = (cfg == NULL);
2875 		if (cfg && flag == RDC_CMD_ENABLE) {
2876 			/* Enabled, so add the set via libcfg */
2877 
2878 			/* Build a new sndr entry and put it */
2879 			group_p = *group? group : place_holder;
2880 			diskqueue_p = *diskqueue? diskqueue : place_holder;
2881 
2882 			if ((diskqueue_p == place_holder) &&
2883 			    (group_p != place_holder)) {
2884 				get_group_diskq(cfg, group_p, diskqueue);
2885 				if (*diskqueue)
2886 					diskqueue_p = diskqueue;
2887 			}
2888 
2889 			/*
2890 			 * format in pconfig is:
2891 			 *	phost.primary.pbitmap.shost.secondary.
2892 			 *	sbitmap.type.mode.group.cnode.options.diskq
2893 			 */
2894 			(void) snprintf(buf, sizeof (buf),
2895 			    "%s %s %s %s %s %s %s %s %s %s - %s",
2896 			    fromhost, fromfile, frombitmap, tohost, tofile,
2897 			    tobitmap, directfile,
2898 			    doasync? "async" : "sync", group_p,
2899 			    clustered? ctag : "-", diskqueue_p);
2900 
2901 			if (cfg_put_cstring(cfg, "sndr", buf, strlen(buf)) < 0)
2902 				rdc_warn(NULL,
2903 				    gettext("unable to add \"%s\" to "
2904 				    "configuration storage: %s"),
2905 				    buf, cfg_error(&sev));
2906 			setnumber = find_setnumber_in_libcfg(cfg, clustered?
2907 			    ctag : NULL, tohost, tofile);
2908 			if (setnumber < 0)
2909 				rdc_warn(NULL,
2910 				    gettext("unable to add \"%s\" to "
2911 				    "configuration storage: %s"),
2912 				    diskqueue_p, cfg_error(&sev));
2913 
2914 			else
2915 				cfg_success = 1;
2916 
2917 			/* Add cluster aware info */
2918 			if (clustered && !rdc_islocal) {
2919 				(void) snprintf(key, sizeof (key),
2920 				    "sndr.set%d.options", setnumber);
2921 				if (self_check(fromhost)) {
2922 					if (cfg_put_options(cfg, CFG_SEC_CONF,
2923 					    key, "lghn", fromhost) < 0) {
2924 						rdc_err(NULL,
2925 						    gettext("unable to add "
2926 						    "\"%s\" to configuration "
2927 						    "storage: %s"),
2928 						    fromhost, cfg_error(&sev));
2929 					}
2930 				} else if (self_check(tohost)) {
2931 					if (cfg_put_options(cfg, CFG_SEC_CONF,
2932 					    key, "lghn", tohost) < 0) {
2933 						rdc_err(NULL,
2934 						    gettext("unable to add "
2935 						    "\"%s\" to configuration "
2936 						    "storage: %s"),
2937 						    fromhost, cfg_error(&sev));
2938 					}
2939 				}
2940 			}
2941 		} else if (cfg && flag == RDC_CMD_DISABLE) {
2942 			found = 0;
2943 			/* Disabled, so delete the set via libcfg */
2944 
2945 			/* get sndr entries until shost, sfile match */
2946 			for (i = 0; i < rdc_maxsets; i++) {
2947 				setnumber = i + 1;
2948 				(void) snprintf(key, sizeof (key), "sndr.set%d",
2949 				    setnumber);
2950 				if (cfg_get_cstring(cfg, key, buf,
2951 				    CFG_MAX_BUF) < 0) {
2952 					break;
2953 				}
2954 				(void) snprintf(key, sizeof (key),
2955 				    "sndr.set%d.secondary", setnumber);
2956 				if (cfg_get_cstring(cfg, key, buf,
2957 				    CFG_MAX_BUF) < 0)
2958 					break;
2959 				if (strcmp(buf, tofile) != 0)
2960 					continue;
2961 				(void) snprintf(key, sizeof (key),
2962 				    "sndr.set%d.shost",
2963 				    setnumber);
2964 				if (cfg_get_cstring(cfg, key, buf,
2965 				    CFG_MAX_BUF) < 0)
2966 					break;
2967 				if (strcmp(buf, tohost) != 0)
2968 					continue;
2969 				found = 1;
2970 #ifdef DEBUG
2971 				if (checksetfields == -1) {
2972 					rdc_err(NULL,
2973 					    gettext("checksetfields not set"));
2974 				}
2975 #endif
2976 				if (checksetfields) {
2977 					checkgfields(cfg, setnumber, fromhost,
2978 					    fromfile, frombitmap, tobitmap,
2979 					    directfile, (doasync == 1)
2980 					    ? "async" : "sync", group, ctag,
2981 					    diskqueue);
2982 				}
2983 
2984 				/* perform cluster specific options */
2985 				if (clustered) {
2986 					/* get the logical host, if set */
2987 					(void) snprintf(key, sizeof (key),
2988 					    "sndr.set%d.options", setnumber);
2989 					(void) cfg_get_single_option(cfg,
2990 						CFG_SEC_CONF, key, "lghn",
2991 						lhname, MAX_RDC_HOST_SIZE);
2992 
2993 					/* figure out the cluster tag, if any */
2994 					(void) snprintf(key, sizeof (key),
2995 					    "sndr.set%d.cnode", setnumber);
2996 					if (cfg_get_cstring(cfg, key, buf,
2997 					    CFG_MAX_BUF) < 0)
2998 						break;
2999 					if (strcmp(buf, ctag))
3000 						rdc_err(NULL, gettext("ctags %s"
3001 						    " and %s do not match"),
3002 						    buf, ctag);
3003 				} else {
3004 					*lhname = '\0';
3005 					*ctag = '\0';
3006 				}
3007 
3008 				/* figure out the disk queue, if any */
3009 				(void) snprintf(key, sizeof (key),
3010 				    "sndr.set%d.diskq",
3011 				    setnumber);
3012 				if (cfg_get_cstring(cfg, key, buf,
3013 				    CFG_MAX_BUF) < 0)
3014 					break;
3015 				if (strlen(buf) > 0) {
3016 					(void) strncpy(diskqueue, buf,
3017 					    NSC_MAXPATH);
3018 				} else {
3019 					*diskqueue = '\0';
3020 				}
3021 				(void) snprintf(key, sizeof (key), "sndr.set%d",
3022 				    setnumber);
3023 				if (cfg_put_cstring(cfg, key, NULL, 0) < 0)
3024 					rdc_warn(NULL,
3025 					    gettext("unable to remove \"%s\" "
3026 					    "from configuration storage: %s"),
3027 					    buf, cfg_error(&sev));
3028 				else
3029 					cfg_success = 1;
3030 				break;
3031 			}
3032 			if (found == 0) {
3033 				rdc_err(NULL,
3034 				    gettext("Unable to find %s:%s in "
3035 				    "configuration storage"),
3036 				    tohost, tofile);
3037 			}
3038 			if (host_not_found) {
3039 				rdc_force_disable(cfg, fromhost, fromfile,
3040 				    frombitmap, tohost, tofile, tobitmap, ctag,
3041 				    lhname);
3042 				if (cfg_commit(cfg) < 0)
3043 					rdc_err(NULL, gettext("commit on "
3044 						"force disable failed"));
3045 				cfg_close(cfg);
3046 				return (0);
3047 			}
3048 		} else if (cfg && flag == RDC_CMD_RECONFIG) {
3049 			/* Update relevant cfg record */
3050 
3051 			cfg_resource(cfg, NULL);
3052 
3053 			/* get sndr entries until shost, sfile match */
3054 			for (i = 0; i < rdc_maxsets; i++) {
3055 				setnumber = i + 1;
3056 				(void) snprintf(key, sizeof (key), "sndr.set%d",
3057 				    setnumber);
3058 				if (cfg_get_cstring(cfg, key, buf,
3059 				    CFG_MAX_BUF) < 0) {
3060 					break;
3061 				}
3062 				(void) snprintf(key, sizeof (key),
3063 				    "sndr.set%d.secondary", setnumber);
3064 				if (cfg_get_cstring(cfg, key, buf,
3065 				    CFG_MAX_BUF) < 0)
3066 					break;
3067 				if (strcmp(buf, tofile) != 0)
3068 					continue;
3069 				(void) snprintf(key, sizeof (key),
3070 				    "sndr.set%d.shost",
3071 				    setnumber);
3072 				if (cfg_get_cstring(cfg, key, buf,
3073 				    CFG_MAX_BUF) < 0)
3074 					break;
3075 				if (strcmp(buf, tohost) != 0)
3076 					continue;
3077 				(void) snprintf(key, sizeof (key),
3078 				    "sndr.set%d.cnode",
3079 				    setnumber);
3080 				if (cfg_get_cstring(cfg, key, buf,
3081 				    CFG_MAX_BUF) < 0)
3082 					break;
3083 				if (reconfig_ctag[0] == '\0')
3084 					(void) strncpy(ctag, buf,
3085 					    sizeof (ctag));
3086 				if (doasync)
3087 					(void) strcpy(mode, "async");
3088 				else
3089 					(void) strcpy(mode, "sync");
3090 				if (strcmp(directfile, "") == 0)
3091 					(void) strcpy(directfile, "ip");
3092 
3093 				group_p = strlen(group) > 0 ? group :
3094 				    place_holder;
3095 
3096 				/*
3097 				 * if we are reconfigging out altogether,
3098 				 * get rid of the diskqueue
3099 				 */
3100 				if (group_p == place_holder)
3101 					diskqueue_p = place_holder;
3102 				else
3103 					diskqueue_p = strlen(diskqueue) > 0 ?
3104 					    diskqueue : place_holder;
3105 
3106 				/*
3107 				 * do a little diskq dance here for reconfigs
3108 				 * that did not specify the diskqueue whilst
3109 				 * reconfigging ...
3110 				 */
3111 				if ((diskqueue_p == place_holder) &&
3112 				    (group_p != place_holder)) {
3113 					get_group_diskq(cfg, group_p,
3114 					    diskqueue);
3115 					diskqueue_p = strlen(diskqueue) > 0 ?
3116 					    diskqueue : place_holder;
3117 				}
3118 
3119 				(void) snprintf(key, sizeof (key),
3120 				    "sndr.set%d.options", setnumber);
3121 				if (cfg_get_cstring(cfg, key, options_cfg,
3122 				    CFG_MAX_BUF) < 0) {
3123 					break;
3124 				}
3125 
3126 				ctag_p = strlen(ctag) > 0 ?
3127 				    ctag : place_holder;
3128 				(void) snprintf(buf, sizeof (buf),
3129 				    "%s %s %s %s %s %s %s %s %s %s %s %s",
3130 				    fromhost, fromfile, frombitmap,
3131 				    tohost, tofile, tobitmap,
3132 				    directfile, mode, group_p,
3133 				    ctag_p, options_cfg, diskqueue_p);
3134 
3135 				(void) snprintf(key, sizeof (key), "sndr.set%d",
3136 				    setnumber);
3137 				if (cfg_put_cstring(cfg, key, buf,
3138 				    strlen(buf)) < 0)
3139 					rdc_warn(NULL,
3140 					    gettext("unable to update \"%s\" "
3141 					    "in configuration storage: %s"),
3142 					    buf, cfg_error(&sev));
3143 				else
3144 					cfg_success = 1;
3145 				break;
3146 			}
3147 		}
3148 
3149 		if (cfg_success) {
3150 			if (cfg && perform_autosv()) {
3151 				if (self_check(fromhost)) {
3152 				    if (diskqueue[0] &&
3153 					(strcmp(diskqueue, fromfile) == 0) ||
3154 					(strcmp(diskqueue, frombitmap) == 0)) {
3155 						rdc_err(NULL, gettext("disk "
3156 						    "queue volume %s must not "
3157 						    "match any primary Remote "
3158 						    "Mirror volume or bitmap"),
3159 						    diskqueue);
3160 				    }
3161 
3162 				    if (diskqueue[0]) {
3163 					different_devs(fromfile, diskqueue);
3164 					different_devs(frombitmap, diskqueue);
3165 					validate_name(cfg, diskqueue);
3166 				    }
3167 				    different_devs(fromfile, frombitmap);
3168 				    validate_name(cfg, fromfile);
3169 				    validate_name(cfg, frombitmap);
3170 				} else {
3171 				    different_devs(tofile, tobitmap);
3172 				    validate_name(cfg, tofile);
3173 				    validate_name(cfg, tobitmap);
3174 				}
3175 			}
3176 			/*
3177 			 * okay, if the command is sync, just build
3178 			 * a list of rdcconfig_t's after the pairs--
3179 			 * loop is done, we will pass this list to
3180 			 * librdc to multithread the syncs (after
3181 			 * forking off a daemonish type process
3182 			 * that waits for the libcall to complete
3183 			 * ints of interest:
3184 			 * flag ie RDC_CMD_COPY, iflag RDC_OPT_UPDATE,
3185 			 * reverse RDC_OPT_REVERSE, RDC_OPT_FORWARD
3186 			 * if necessary, turn autosync back on
3187 			 */
3188 			if (flag == RDC_CMD_COPY) {
3189 				if (autosync_is_on(tohost, tofile) ==
3190 				    AUTOSYNC_ON)
3191 					enable_autosync(fromhost, fromfile,
3192 					    tohost, tofile);
3193 
3194 				if (sets == NULL) {
3195 					sets_p = sets =
3196 					    rdc_alloc_config(fromhost, fromfile,
3197 					    frombitmap, tohost, tofile,
3198 					    tobitmap, "mode", "group", "ctag",
3199 					    "options", 0);
3200 
3201 					if (sets_p == NULL) {
3202 						rdc_err(NULL,
3203 						gettext("rdc config alloc"
3204 						"failed %s"), rdc_error(NULL));
3205 					}
3206 					continue;
3207 				}
3208 
3209 				sets_p = sets_p->next =
3210 				    rdc_alloc_config(fromhost, fromfile,
3211 				    frombitmap, tohost, tofile, tobitmap,
3212 				    "mode", "group", "ctag", "options", 0);
3213 
3214 				if (sets_p == NULL) {
3215 					rdc_err(NULL, gettext("rdc config alloc"
3216 					"failed %s"), rdc_error(NULL));
3217 				}
3218 				continue;
3219 			}
3220 
3221 			/*
3222 			 * block incoming signals until after the possible
3223 			 * cfg_commit is done
3224 			 */
3225 			block_sigs();
3226 			if (rdc_operation(cfg, fromhost, fromfile, frombitmap,
3227 			    tohost, tofile, tobitmap, flag, iflag, directfile,
3228 			    group, ctag, diskqueue, &doasync, reverse) < 0) {
3229 				;
3230 				/*EMPTY*/
3231 			} else if (cfg) {
3232 				if (diskq_group == DISKQ_REWRITEG) {
3233 					rewrite_group_diskqueue(cfg,
3234 					    &pair_list[pairs], diskqueue);
3235 				}
3236 				if (perform_autosv() &&
3237 				    (flag == RDC_CMD_ENABLE ||
3238 				    flag == RDC_CMD_DISABLE ||
3239 				    flag == RDC_CMD_RECONFIG)) {
3240 					unload_rdc_vols();
3241 					cfg_unload_shadows();
3242 					cfg_unload_dsvols();
3243 					cfg_unload_svols();
3244 				}
3245 				if ((iflag & RDC_OPT_REVERSE_ROLE) != 0 &&
3246 					allow_role) {
3247 					bzero(tmphost, MAX_RDC_HOST_SIZE);
3248 					bzero(tmpfile, NSC_MAXPATH);
3249 					bzero(tmpbitmap, NSC_MAXPATH);
3250 					(void) strncpy(tmphost, fromhost,
3251 						MAX_RDC_HOST_SIZE);
3252 					(void) strncpy(tmpfile, fromfile,
3253 					    NSC_MAXPATH);
3254 					(void) strncpy(tmpbitmap, frombitmap,
3255 					    NSC_MAXPATH);
3256 
3257 					(void) strncpy(fromhost, tohost,
3258 					    MAX_RDC_HOST_SIZE);
3259 					(void) strncpy(fromfile, tofile,
3260 					    NSC_MAXPATH);
3261 					(void) strncpy(frombitmap, tobitmap,
3262 					    NSC_MAXPATH);
3263 
3264 					(void) strncpy(tohost, tmphost,
3265 					    MAX_RDC_HOST_SIZE);
3266 					(void) strncpy(tofile, tmpfile,
3267 					    NSC_MAXPATH);
3268 					(void) strncpy(tobitmap, tmpbitmap,
3269 					    NSC_MAXPATH);
3270 					group_p = strlen(group) > 0 ? group :
3271 					    place_holder;
3272 					diskqueue_p = strlen(diskqueue) > 0 ?
3273 					    diskqueue : place_holder;
3274 					ctag_p = strlen(ctag) > 0 ?
3275 					    ctag : place_holder;
3276 					(void) snprintf(buf, sizeof (buf), "%s "
3277 					    "%s %s %s %s %s %s %s %s %s %s %s",
3278 					    fromhost, fromfile, frombitmap,
3279 					    tohost, tofile, tobitmap,
3280 					    directfile, mode, group_p,
3281 					    ctag_p, options_cfg, diskqueue_p);
3282 
3283 					(void) snprintf(key, sizeof (key),
3284 						"sndr.set%d", setnumber);
3285 					if (cfg_put_cstring(cfg, key, buf,
3286 						strlen(buf)) < 0)
3287 						rdc_err(NULL,
3288 					    gettext("unable to update \"%s\" "
3289 						"in configuration storage: %s"),
3290 						    buf, cfg_error(&sev));
3291 				}
3292 				if (cfg_commit(cfg) < 0) {
3293 					rdc_err(NULL, gettext("commit on role "
3294 					"reversal failed"));
3295 				}
3296 			}
3297 			unblock_sigs();
3298 		}
3299 
3300 		if (cfg) {
3301 			cfg_close(cfg);
3302 		}
3303 
3304 	}
3305 	if (flag == RDC_CMD_COPY) {
3306 		pid = fork();
3307 		if (pid == -1) {		/* error forking */
3308 			perror("fork");
3309 			exit(1);
3310 		}
3311 	} else {
3312 		exit(0);
3313 	}
3314 	if (pid > 0) /* parent process */
3315 		exit(0);
3316 
3317 	spcslog_sync(sets, 1, iflag);
3318 	if (iflag & RDC_OPT_REVERSE) {
3319 		if (iflag & RDC_OPT_UPDATE)
3320 			rclist = rdc_ursync(sets);
3321 		else
3322 			rclist = rdc_rsync(sets);
3323 	} else if (iflag & RDC_OPT_UPDATE) {
3324 		rclist = rdc_usync(sets);
3325 	} else
3326 		rclist = rdc_fsync(sets);
3327 
3328 	rcp = rclist;
3329 	while (rcp) {
3330 		if (rcp->rc < 0) {
3331 			/* rclist->msg has already been gettext'd */
3332 			(void) fprintf(stderr,
3333 			    gettext("Remote Mirror: %s %s %s %s %s %s\n"),
3334 			    rcp->set.phost, rcp->set.pfile, rcp->set.pbmp,
3335 			    rcp->set.shost, rcp->set.sfile, rcp->set.sbmp);
3336 			rdc_warn(NULL, "%s", rcp->msg);
3337 			spcs_log("sndr", NULL, "%s", rcp->msg);
3338 		}
3339 		rcp = rcp->next;
3340 	}
3341 
3342 	spcslog_sync(sets, 0, iflag);
3343 
3344 	if (sets)
3345 		rdc_free_config(sets, RDC_FREEALL);
3346 	if (rclist)
3347 		rdc_free_rclist(rclist);
3348 
3349 	return (0);
3350 }
3351 /*
3352  * process_clocal()
3353  * pre: a non null string
3354  * post: if the string is "local"
3355  * then it is converted to "-"
3356  * and rdc_islocal is set to 1
3357  * if not rdc_islocal set to 0
3358  */
3359 void
3360 process_clocal(char *ctag)
3361 {
3362 	/*
3363 	 * Check for the special cluster tag and convert into the
3364 	 * internal representation.
3365 	 */
3366 
3367 	if (ctag != NULL && strcmp(ctag, RDC_LOCAL_TAG) == 0) {
3368 		(void) strcpy(ctag, "-");
3369 		rdc_islocal = 1;
3370 	} else {
3371 		rdc_islocal = 0;
3372 	}
3373 }
3374 
3375 static void
3376 rdc_check_dgislocal(char *dgname)
3377 {
3378 	char *othernode;
3379 	int rc;
3380 
3381 	/*
3382 	 * check where this disk service is mastered
3383 	 */
3384 
3385 	rc = cfg_dgname_islocal(dgname, &othernode);
3386 	if (rc < 0) {
3387 		rdc_err(NULL, gettext("unable to find "
3388 		    "disk service, %s: %s"), dgname, strerror(errno));
3389 	}
3390 
3391 	if (rc == 0) {
3392 		rdc_err(NULL, gettext("disk service, %s, is "
3393 		    "active on node \"%s\"\nPlease re-issue "
3394 		    "the command on that node"), dgname, othernode);
3395 	}
3396 }
3397 
3398 static void
3399 different_devs(char *dev1, char *dev2)
3400 {
3401 	struct stat buf1, buf2;
3402 
3403 	if (stat(dev1, &buf1) < 0) {
3404 		spcs_log("sndr", NULL, gettext("Remote Mirror: can't stat %s"),
3405 		    dev1);
3406 		rdc_err(NULL, gettext("Remote Mirror: can't stat %s"), dev1);
3407 	}
3408 	if (stat(dev2, &buf2) < 0) {
3409 		spcs_log("sndr", NULL, gettext("Remote Mirror: can't stat %s"),
3410 		    dev2);
3411 		rdc_err(NULL, gettext("Remote Mirror: can't stat %s"), dev2);
3412 	}
3413 	if (buf1.st_rdev == buf2.st_rdev) {
3414 		spcs_log("sndr", NULL, gettext("Remote Mirror: '%s' and '%s' "
3415 		    "refer to the same device"), dev1, dev2);
3416 		rdc_err(NULL, gettext("Remote Mirror: '%s' and '%s' refer to "
3417 		    "the same device"), dev1, dev2);
3418 	}
3419 }
3420 
3421 static void
3422 validate_name(CFGFILE *cfg, char *vol)
3423 {
3424 	char *altname;
3425 	int rc;
3426 
3427 	if (!cfg) {
3428 		rdc_err(NULL, gettext("Remote Mirror: null cfg ptr in "
3429 		    "validate_name"));
3430 	}
3431 
3432 	rc = cfg_get_canonical_name(cfg, vol, &altname);
3433 	if (rc < 0) {
3434 		spcs_log("sndr", NULL, gettext("Remote Mirror: unable to parse "
3435 		    "config file\n"));
3436 		rdc_err(NULL, gettext("Remote Mirror: unable to parse config "
3437 		    "file\n"));
3438 	}
3439 	if (rc) {
3440 		spcs_log("sndr", NULL, gettext("Remote Mirror: '%s': already "
3441 		    "configured as '%s'"), vol, altname);
3442 		rdc_err(NULL, gettext("Remote Mirror: The volume '%s' has been "
3443 		    "configured previously as '%s'.  Re-enter command with "
3444 		    "the latter name."), vol, altname);
3445 	}
3446 }
3447 
3448 /*
3449  * Add the autosync value to the option field for the sndr set specified by
3450  * tohost:tofile.
3451  *
3452  * ASSUMPTIONS:
3453  *      - cfg file is available to take a write lock.
3454  *      - set is already configured in dscfg
3455  *
3456  * INPUTS:
3457  *      autosync_val - value to set autosync to
3458  *      tohost - secondary host
3459  *      tofile - secondary volume
3460  *
3461  * OUTPUTS:
3462  *      none.
3463  *
3464  */
3465 static void
3466 set_autosync(int autosync_val, char *tohost, char *tofile, char *ctag)
3467 {
3468 	CFGFILE *cfg;
3469 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
3470 	char tag[CFG_MAX_BUF], val[CFG_MAX_BUF];
3471 	char auto_tag[CFG_MAX_BUF];
3472 	_sd_dual_pair_t pair;
3473 	_sd_dual_pair_t tmpair;
3474 	int setnumber, options = 0, already_set = 0, cfg_success = 0;
3475 	int set;
3476 
3477 	/* verify valid autosync request */
3478 	if ((autosync_val != AUTOSYNC_ON) && (autosync_val != AUTOSYNC_OFF)) {
3479 #ifdef DEBUG
3480 		rdc_warn(NULL,
3481 		    gettext("set_autosync called with improper value"));
3482 #endif
3483 		return;
3484 	}
3485 
3486 	if ((cfg = cfg_open(NULL)) == NULL) {
3487 		rdc_err(NULL, gettext("unable to access configuration"));
3488 	}
3489 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
3490 		rdc_err(NULL, gettext("unable to lock configuration"));
3491 	}
3492 
3493 	if (clustered) {
3494 		cfg_resource(cfg, ctag);
3495 	} else {
3496 		cfg_resource(cfg, NULL);
3497 	}
3498 
3499 	/* find set number in config */
3500 	if ((setnumber = find_setnumber_in_libcfg(cfg, clustered? ctag : NULL,
3501 	    tohost, tofile)) < 0) {
3502 		cfg_close(cfg);
3503 		rdc_err(NULL, gettext("unable to find Remote Mirror set %s:%s: "
3504 		    "in config"), tohost, tofile);
3505 	}
3506 	(void) snprintf(key, sizeof (key), "sndr.set%d.options", setnumber);
3507 	(void) snprintf(auto_tag, sizeof (auto_tag), "auto");
3508 
3509 	/* Check if there are any options already set, including ours */
3510 	if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag, CFG_MAX_BUF, val,
3511 	    CFG_MAX_BUF) >= 0) {
3512 		options = 1;
3513 
3514 		do {
3515 			if (strcmp(tag, auto_tag) == 0) {
3516 				already_set = 1;
3517 			}
3518 		} while (cfg_get_options(cfg, CFG_SEC_CONF, NULL, tag,
3519 		    CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0);
3520 	}
3521 
3522 	/* options already exist, edit ours out */
3523 	if (options && already_set) {
3524 		char *p, *q;
3525 		int need_to_clear_buf = 1;
3526 
3527 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
3528 			rdc_err(NULL, gettext("unable to get options field "
3529 			    "for Remote Mirror set %s:%s"), tohost, tofile);
3530 		}
3531 
3532 		/* parse out our options, all of the form "auto=" */
3533 		p = strdup(buf);
3534 		bzero(buf, sizeof (buf));
3535 
3536 		q = strtok(p, ";");
3537 		do {
3538 			/* if another tag/value exists, keep it */
3539 			if (strncmp(auto_tag, q, 4) != 0) {
3540 				(void) strcat(buf, q);
3541 				(void) strcat(buf, ";");
3542 				need_to_clear_buf = 0;
3543 			}
3544 		} while (q = strtok(NULL, ";"));
3545 		free(p);
3546 
3547 		/* if we were the only option, clear the field */
3548 		if (need_to_clear_buf) {
3549 			(void) strcat(buf, "-");
3550 		}
3551 
3552 		if (cfg_put_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
3553 			rdc_err(NULL, gettext("unable to clear autosync value "
3554 			    "in config for Remote Mirror set %s:%s"), tohost,
3555 			    tofile);
3556 		} else {
3557 			cfg_success = 1;
3558 		}
3559 	}
3560 
3561 	/* autosync is not present in options field, add if on is requested */
3562 	if (autosync_val == AUTOSYNC_ON) {
3563 		if (cfg_put_options(cfg, CFG_SEC_CONF, key, auto_tag, "on")
3564 		    < 0) {
3565 			rdc_err(NULL, gettext("unable to update autosync value "
3566 			    "in config for Remote Mirror set %s:%s"), tohost,
3567 			    tofile);
3568 		} else {
3569 			cfg_success = 1;
3570 		}
3571 	}
3572 	/* if we are in a group, update any other sets in the same group */
3573 	do {
3574 		bzero(&pair, sizeof (pair));
3575 		bzero(buf, CFG_MAX_BUF);
3576 
3577 		(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
3578 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
3579 			break;
3580 		}
3581 		if (parse_cfg_buf(buf, &pair, NULL))
3582 			break;
3583 		if (pair.group == NULL)	/* not in a group */
3584 			break;
3585 		if (!pair.group[0])
3586 			break;			/* not in a group */
3587 		for (set = 1; /*CSTYLED*/; set++) {
3588 			if (set == setnumber)
3589 				continue;
3590 			bzero(buf, CFG_MAX_BUF);
3591 			options = 0;
3592 			already_set = 0;
3593 
3594 			(void) snprintf(key, sizeof (key), "sndr.set%d", set);
3595 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
3596 				break;	/* last set processed */
3597 			}
3598 			bzero(&tmpair, sizeof (tmpair));
3599 			if (parse_cfg_buf(buf, &tmpair, NULL))
3600 				break;
3601 			if (strcmp(pair.group, tmpair.group) != 0)
3602 				continue; /* not the group we want */
3603 
3604 			(void) snprintf(key, sizeof (key), "sndr.set%d.options",
3605 				set);
3606 			/*
3607 			 * Check if there are any options already set,
3608 			 * including ours
3609 			 */
3610 			if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag,
3611 				CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0) {
3612 				options = 1;
3613 
3614 				do {
3615 					if (strcmp(tag, auto_tag) == 0) {
3616 						already_set = 1;
3617 					}
3618 				} while (cfg_get_options(cfg, CFG_SEC_CONF,
3619 					NULL, tag, CFG_MAX_BUF, val,
3620 					CFG_MAX_BUF) >= 0);
3621 			}
3622 
3623 			/* options already exist, edit ours out */
3624 			if (options && already_set) {
3625 				char *p, *q;
3626 				int need_to_clear_buf = 1;
3627 
3628 				if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF)
3629 				    < 0) {
3630 					rdc_err(NULL, gettext("unable to get "
3631 					"options field for Remote Mirror set "
3632 					"%s:%s"), tmpair.thost, tmpair.tfile);
3633 				}
3634 
3635 				/*
3636 				 * parse out our options, all of the
3637 				 * form "auto="
3638 				 */
3639 				p = strdup(buf);
3640 				bzero(buf, sizeof (buf));
3641 
3642 				q = strtok(p, ";");
3643 				do {
3644 					/*
3645 					 * if another tag/value exists,
3646 					 * keep it
3647 					 */
3648 					if (strncmp(auto_tag, q, 4) != 0) {
3649 						(void) strcat(buf, q);
3650 						(void) strcat(buf, ";");
3651 						need_to_clear_buf = 0;
3652 					}
3653 				} while (q = strtok(NULL, ";"));
3654 				free(p);
3655 
3656 				/*
3657 				 * if we were the only option,
3658 				 * clear the field
3659 				 */
3660 				if (need_to_clear_buf) {
3661 					(void) strcat(buf, "-");
3662 				}
3663 
3664 				if (cfg_put_cstring(cfg, key, buf, CFG_MAX_BUF)
3665 					< 0) {
3666 					rdc_err(NULL, gettext("unable to clear "
3667 						"autosync value in config for "
3668 						"Remote Mirror set %s:%s"),
3669 						tmpair.thost, tmpair.tfile);
3670 					cfg_success = 0;
3671 				}
3672 			}
3673 
3674 			/*
3675 			 * autosync is not present in options field,
3676 			 * add if on is requested
3677 			 */
3678 			if (autosync_val == AUTOSYNC_ON) {
3679 				if (cfg_put_options(cfg, CFG_SEC_CONF, key,
3680 					auto_tag, "on") < 0) {
3681 					rdc_err(NULL, gettext("unable to update"
3682 					    " autosync value in config for "
3683 					    "Remote Mirror set %s:%s"),
3684 					    tmpair.thost,
3685 					    tmpair.tfile);
3686 					cfg_success = 0;
3687 				}
3688 			}
3689 		}
3690 
3691 	/* CONSTCOND */
3692 	} while (0);
3693 	if (cfg_success) {
3694 		if (cfg_commit(cfg) < 0) {
3695 		    rdc_err(NULL, gettext("commit on role reversal failed"));
3696 		}
3697 	}
3698 
3699 	cfg_close(cfg);
3700 }
3701 
3702 /*
3703  * Check to see if autosync is on for set specified by tohost:tofile.
3704  *
3705  * ASSUMPTIONS:
3706  *      config is available to take a read lock against it.
3707  *
3708  * INPUTS:
3709  *      tohost - secondary host
3710  *      tofile - secondary volume
3711  *
3712  * OUTPUTS:
3713  *     -1 error
3714  *      AUTOSYNC_ON if autosync is on
3715  *      AUTOSYNC_OFF if autosync is off
3716  */
3717 static int
3718 autosync_is_on(char *tohost, char *tofile)
3719 {
3720 	CFGFILE *cfg;
3721 	int setnumber, autosync_val = AUTOSYNC_OFF;
3722 	char key[CFG_MAX_KEY];
3723 	char tag[CFG_MAX_BUF], val[CFG_MAX_BUF];
3724 
3725 	if ((cfg = cfg_open(NULL)) == NULL) {
3726 		rdc_err(NULL, gettext("unable to access configuration"));
3727 	}
3728 
3729 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
3730 		cfg_close(cfg);
3731 		rdc_err(NULL, gettext("unable to lock configuration"));
3732 	}
3733 
3734 	if ((setnumber = find_setnumber_in_libcfg(cfg, NULL, tohost, tofile)) <
3735 	    0) {
3736 		cfg_close(cfg);
3737 		rdc_err(NULL, gettext("cannot find Remote Mirror set %s:%s in "
3738 		    "config"), tohost, tofile);
3739 	}
3740 
3741 	(void) snprintf(key, CFG_MAX_KEY, "sndr.set%d.options", setnumber);
3742 	if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag, CFG_MAX_BUF, val,
3743 	    CFG_MAX_BUF) >= 0) {
3744 		do {
3745 			if (strcmp(tag, "auto") == 0) {
3746 				if (strcmp(val, "on") == 0) {
3747 					autosync_val = AUTOSYNC_ON;
3748 				}
3749 				break;
3750 			}
3751 		} while (cfg_get_options(cfg, CFG_SEC_CONF, NULL, tag,
3752 		    CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0);
3753 	}
3754 
3755 	cfg_close(cfg);
3756 	return (autosync_val);
3757 }
3758 
3759 void
3760 enable_autosync(char *fhost, char *ffile, char *thost, char *tfile)
3761 {
3762 	rdc_config_t parms;
3763 	spcs_s_info_t ustat;
3764 	rdc_addr_t *p;
3765 
3766 	ustat = spcs_s_ucreate();
3767 	parms.command = RDC_CMD_TUNABLE;
3768 
3769 	p = &parms.rdc_set[0].primary;
3770 	(void) strncpy(p->intf, fhost, MAX_RDC_HOST_SIZE);
3771 	(void) strncpy(p->file, ffile, MAX_RDC_HOST_SIZE);
3772 
3773 	p = &parms.rdc_set[0].secondary;
3774 	(void) strncpy(p->intf, thost, NSC_MAXPATH);
3775 	(void) strncpy(p->file, tfile, NSC_MAXPATH);
3776 
3777 	parms.rdc_set[0].autosync = 1;
3778 	parms.rdc_set[0].maxqfbas = -1;
3779 	parms.rdc_set[0].maxqitems = -1;
3780 	parms.rdc_set[0].asyncthr = -1;
3781 	parms.rdc_set[0].netconfig = NULL;
3782 
3783 	if ((RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustat)) !=
3784 	    SPCS_S_OK) {
3785 		rdc_warn(&ustat, gettext("failed to update autosync for"
3786 		    " Remote Mirror set %s:%s"), thost, tfile);
3787 		spcs_log("sndr", &ustat, gettext("failed to update autosync for"
3788 		    " Remote Mirror set %s:%s"), thost, tfile);
3789 	}
3790 	spcs_s_ufree(&ustat);
3791 }
3792 
3793 static int
3794 rdc_operation(CFGFILE *cfg, char *fromhost, char *fromfile, char *frombitmap,
3795     char *tohost, char *tofile, char *tobitmap,
3796     int flag, int iflag,
3797     char *directfile, char *group, char *ctag, char *diskqueue,
3798     int *doasync, int reverse)
3799 {
3800 	const int getaddr = (flag == RDC_CMD_ENABLE);
3801 	const int rpcbind = !getaddr;
3802 	rdc_config_t parms;
3803 	int ret;
3804 	spcs_s_info_t ustatus;
3805 	struct hostent *hp;
3806 	char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
3807 	char orig_fbmp[MAXHOSTNAMELEN], orig_tbmp[MAXHOSTNAMELEN];
3808 	char orig_diskq[NSC_MAXPATH];
3809 	struct t_info tinfo;
3810 	int success = 1;
3811 	int autosync_toggle_needed = 0;
3812 	char *vol1, *vol2, *vol3;
3813 
3814 	conf = &nconf;
3815 
3816 	hp = gethost_byname(fromhost);
3817 	(void) strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
3818 	hp = gethost_byname(tohost);
3819 	(void) strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
3820 
3821 	if (self_check(fromname) && self_check(toname)) {
3822 		rdc_err(NULL, gettext("both %s and %s are local"),
3823 		    fromhost, tohost);
3824 	}
3825 
3826 	/* we have to find out what to sv disable after reconfig */
3827 	if (flag == RDC_CMD_RECONFIG) {
3828 
3829 		parms.command = RDC_CMD_STATUS;
3830 		parms.rdc_set->netconfig = NULL;
3831 		(void) strncpy(parms.rdc_set->primary.intf, fromhost,
3832 		    MAX_RDC_HOST_SIZE);
3833 		(void) strncpy(parms.rdc_set->secondary.intf, tohost,
3834 		    MAX_RDC_HOST_SIZE);
3835 		(void) strncpy(parms.rdc_set->primary.file, fromfile,
3836 		    NSC_MAXPATH);
3837 		(void) strncpy(parms.rdc_set->secondary.file, tofile,
3838 		    NSC_MAXPATH);
3839 		ustatus = spcs_s_ucreate();
3840 		ret = RDC_IOCTL(RDC_CONFIG, &parms,
3841 		    NULL, 0, 0, 0, ustatus);
3842 		if (ret != SPCS_S_OK) {
3843 			rdc_err(NULL, gettext("unable to get set status"
3844 			    " before reconfig operation"));
3845 		}
3846 		(void) strncpy(orig_fbmp, parms.rdc_set->primary.bitmap,
3847 		    NSC_MAXPATH);
3848 		(void) strncpy(orig_tbmp, parms.rdc_set->secondary.bitmap,
3849 		    NSC_MAXPATH);
3850 		(void) strncpy(orig_diskq, parms.rdc_set->disk_queue,
3851 		    NSC_MAXPATH);
3852 	}
3853 
3854 	/*
3855 	 * another terrible addition, if we are reconfigging mode
3856 	 * and not logging, just give up.
3857 	 */
3858 	if ((reconfig_doasync != -1) &&
3859 	    (!(parms.rdc_set->flags & RDC_LOGGING))) {
3860 		rdc_err(NULL, gettext("cannot reconfigure sync/async, "
3861 		    "Remote Mirror set not logging"));
3862 		spcs_log("sndr", NULL, gettext("cannot reconfigure sync/async, "
3863 		    "Remote Mirror set not logging"));
3864 	}
3865 
3866 	/*
3867 	 * Now build up the address for each host including port and transport
3868 	 */
3869 	if (getaddr) {
3870 		svp = get_addr(toname, RDC_PROGRAM, RDC_VERS_MIN,
3871 			&conf, proto_test ? NC_UDP:NULL, "rdc", &tinfo,
3872 			rpcbind);
3873 
3874 		if (svp == NULL) {
3875 			rdc_warn(NULL, gettext("unable to determine network "
3876 			    "information for %s"), toname);
3877 #ifdef DEBUG
3878 			(void) printf("get_addr failed for Ver 4 %s\n", toname);
3879 #endif
3880 			return (-1);
3881 		}
3882 		svaddr = *svp;
3883 	} else {
3884 		bzero(&svaddr, sizeof (svaddr));
3885 	}
3886 
3887 	parms.rdc_set->secondary.addr.len = svaddr.len;
3888 	parms.rdc_set->secondary.addr.maxlen =
3889 					svaddr.maxlen;
3890 	parms.rdc_set->secondary.addr.buf =
3891 					(void *)svaddr.buf;
3892 
3893 #ifdef DEBUG_ADDR
3894 	(void) fprintf(stderr, "secondary buf %x len %d\n",
3895 	    svaddr.buf, svaddr.len);
3896 
3897 	for (i = 0; i < svaddr.len; i++)
3898 		(void) printf("%u ", svaddr.buf[i]);
3899 	(void) printf("\n");
3900 #endif
3901 
3902 	if (getaddr) {
3903 		svp = get_addr(fromname, RDC_PROGRAM, RDC_VERS_MIN,
3904 			&conf, proto_test ? NC_UDP: NULL, "rdc", &tinfo,
3905 			rpcbind);
3906 		if (svp == NULL) {
3907 #ifdef DEBUG
3908 			(void) printf("get_addr failed for Ver 4 %s\n",
3909 				fromname);
3910 #endif
3911 			return (-1);
3912 		}
3913 		svaddr = *svp;
3914 	}
3915 
3916 	parms.rdc_set->primary.addr.len = svaddr.len;
3917 	parms.rdc_set->primary.addr.maxlen = svaddr.maxlen;
3918 	parms.rdc_set->primary.addr.buf = (void *)svaddr.buf;
3919 
3920 #ifdef DEBUG_ADDR
3921 	(void) fprintf(stderr, "primary buf %x len %d\n",
3922 	    svaddr.buf, svaddr.len);
3923 	for (i = 0; i < svaddr.len; i++)
3924 		(void) printf("%u ", svaddr.buf[i]);
3925 	(void) printf("\n");
3926 #endif
3927 
3928 	if (getaddr) {
3929 		(void) convert_nconf_to_knconf(conf, &knconf);
3930 #ifdef DEBUG_ADDR
3931 		(void) printf("knconf %x %s %s %x\n", knconf.knc_semantics,
3932 		    knconf.knc_protofmly, knconf.knc_proto, knconf.knc_rdev);
3933 #endif
3934 		parms.rdc_set->netconfig = &knconf;
3935 	} else {
3936 		parms.rdc_set->netconfig = NULL;
3937 	}
3938 
3939 	if (!self_check(fromname) && !self_check(toname)) {
3940 		if (!clustered)
3941 			rdc_err(NULL, gettext("neither %s nor %s is local"),
3942 				fromhost, tohost);
3943 		else {
3944 		/*
3945 		 * IF we could get a list of logical hosts on this cluster
3946 		 * Then we could print something intelligent about where
3947 		 * the volume is mastered. For now, just print some babble
3948 		 * about the fact that we have no idea.
3949 		 */
3950 			rdc_err(NULL,
3951 				gettext("either %s:%s or %s:%s is not local"),
3952 					fromhost, fromfile, tohost, tofile);
3953 		}
3954 	}
3955 
3956 	(void) strncpy(parms.rdc_set->primary.intf, fromhost,
3957 	    MAX_RDC_HOST_SIZE);
3958 	(void) strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH);
3959 	(void) strncpy(parms.rdc_set->primary.bitmap, frombitmap, NSC_MAXPATH);
3960 
3961 	(void) strncpy(parms.rdc_set->secondary.intf, tohost,
3962 	    MAX_RDC_HOST_SIZE);
3963 	(void) strncpy(parms.rdc_set->secondary.file, tofile, NSC_MAXPATH);
3964 	(void) strncpy(parms.rdc_set->secondary.bitmap, tobitmap, NSC_MAXPATH);
3965 
3966 	if ((group == NULL) || ((strcmp(group, "-")) == 0))
3967 		parms.rdc_set->group_name[0] = 0;
3968 	else
3969 		(void) strncpy(parms.rdc_set->group_name, group, NSC_MAXPATH);
3970 
3971 	if (self_check(tohost) &&
3972 	    (strlen(diskqueue) > 0) && (diskqueue[0] != '-'))
3973 		if ((flag == RDC_CMD_ENABLE) || (flag == RDC_CMD_ADDQ))
3974 			rdc_err(NULL, gettext("enabling disk queue on a Remote"
3975 			    " Mirror secondary is not allowed (%s)"),
3976 			    diskqueue);
3977 
3978 	if ((diskqueue == NULL) || ((strcmp(diskqueue, "-")) == 0))
3979 		parms.rdc_set->disk_queue[0] = 0;
3980 	else
3981 		(void) strncpy(parms.rdc_set->disk_queue, diskqueue,
3982 		    NSC_MAXPATH);
3983 
3984 	parms.rdc_set->maxqfbas = maxqfbas;
3985 	parms.rdc_set->maxqitems = maxqitems;
3986 	parms.rdc_set->asyncthr = asyncthr;
3987 	/* set up the permanent set id for this set */
3988 	if (flag == RDC_CMD_ENABLE) {
3989 		char key[CFG_MAX_KEY];
3990 		char setid[64];
3991 		int set;
3992 		parms.rdc_set->setid = get_new_cfg_setid(cfg);
3993 		if (parms.rdc_set->setid <= 0) {
3994 			rdc_err(NULL, gettext("unable to obtain unique set id "
3995 			    "for %s:%s"), tohost, tofile);
3996 		}
3997 		if ((set = find_setnumber_in_libcfg(cfg, clustered? ctag : NULL,
3998 		    tohost, tofile)) < 0) {
3999 			rdc_err(NULL, gettext("unable to store unique set id"
4000 			    " for %s:%s"), tohost, tofile);
4001 		}
4002 		(void) snprintf(key, sizeof (key), "sndr.set%d.options", set);
4003 		(void) snprintf(setid, sizeof (setid), "%d",
4004 		    parms.rdc_set->setid);
4005 
4006 		if (cfg_put_options(cfg, CFG_SEC_CONF, key, "setid",
4007 		    setid) < 0) {
4008 			rdc_err(NULL, gettext("unable to store unique set "
4009 			    "id for %s:%s: %s"), tohost, tofile,
4010 			    gettext(cfg_error(NULL)));
4011 		}
4012 	} else if (flag != RDC_CMD_DISABLE) { /* set already gone from cfg */
4013 		parms.rdc_set->setid = get_cfg_setid(cfg, ctag, tohost, tofile);
4014 		if (parms.rdc_set->setid <= 0) {
4015 			rdc_err(NULL, gettext("unable to obtain unique set id "
4016 			    "for %s:%s"), tohost, tofile);
4017 		}
4018 	}
4019 
4020 	/*
4021 	 * Always set autosync flag to default so nothing gets messed up. If
4022 	 * we are doing an autosync operation, it'll all get taken care of
4023 	 * then.
4024 	 */
4025 	parms.rdc_set->autosync = AUTOSYNC;
4026 
4027 
4028 	/* gethostid(3c) is defined to return a 32bit value */
4029 	parms.rdc_set->syshostid = (int32_t)gethostid();
4030 
4031 	parms.command = 0;
4032 	parms.options = iflag;
4033 	parms.command = flag;
4034 	if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
4035 		if (*doasync)
4036 			parms.options |= RDC_OPT_ASYNC;
4037 		else
4038 			parms.options |= RDC_OPT_SYNC;
4039 	} else if (flag == RDC_CMD_COPY) {
4040 		if (reverse)
4041 			parms.options |= RDC_OPT_REVERSE;
4042 		else
4043 			parms.options |= RDC_OPT_FORWARD;
4044 	}
4045 
4046 	if (self_check(fromname)) {
4047 		if (flag == RDC_CMD_COPY && reverse && mounted(fromfile))
4048 			rdc_err(NULL, gettext("can not start reverse sync"
4049 			    " as a file system is mounted on %s"),
4050 			    fromfile);
4051 		parms.options |= RDC_OPT_PRIMARY;
4052 		if (strcmp(directfile, "ip") == 0)
4053 			parms.rdc_set->direct_file[0] = 0; /* no directfile */
4054 		else
4055 			(void) strncpy(parms.rdc_set->direct_file, directfile,
4056 			    NSC_MAXPATH);
4057 	} else {
4058 		parms.options |= RDC_OPT_SECONDARY;
4059 		parms.rdc_set->direct_file[0] = 0;	/* no fcal directio */
4060 	}
4061 
4062 	if ((asyncthr || maxqitems || maxqfbas || qblock) &&
4063 	    (parms.options & RDC_OPT_SECONDARY)) {
4064 		rdc_err(NULL, gettext("changing queue parameters may "
4065 		    " only be done on a primary Remote Mirror host"));
4066 		spcs_log("sndr", NULL, gettext("changing queue parameters may "
4067 		    " only be done on a primary Remote Mirror host"));
4068 
4069 	}
4070 
4071 	ustatus = spcs_s_ucreate();
4072 
4073 	if (flag == RDC_CMD_COPY) {
4074 		parms.command = RDC_CMD_STATUS;
4075 		ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
4076 		if ((ret != SPCS_S_OK) ||
4077 		    !(parms.rdc_set->flags & RDC_LOGGING)) {
4078 			rdc_err(NULL, gettext("can not start sync"
4079 			    " as Remote Mirror set %s:%s is not logging"),
4080 			    tohost, tofile);
4081 		}
4082 		spcs_log("sndr", NULL,
4083 		    gettext("%s %s %s %s %s %s %s %s\nStarting"),
4084 		    program, rdc_decode_flag(flag, parms.options),
4085 		    fromhost, fromfile, frombitmap,
4086 		    tohost, tofile, tobitmap);
4087 		parms.command = RDC_CMD_COPY;
4088 	}
4089 
4090 	if ((flag == RDC_CMD_COPY) &&
4091 	    (autosync_is_on(tohost, tofile) == AUTOSYNC_ON)) {
4092 	/* check if autosync needs to be turned on when doing a copy/update */
4093 		parms.rdc_set->autosync = AUTOSYNC_ON;
4094 		autosync_toggle_needed = 1;
4095 	} else if ((flag == RDC_CMD_LOG) &&
4096 		(autosync_is_on(tohost, tofile) == AUTOSYNC_ON)) {
4097 	/* check if autosync needs to be turned off when going to logging */
4098 		parms.rdc_set->autosync = AUTOSYNC_OFF;
4099 		autosync_toggle_needed = 1;
4100 	} else if (((autosync == AUTOSYNC_ON) || (autosync == AUTOSYNC_OFF)) &&
4101 	    (flag == RDC_CMD_TUNABLE)) {
4102 		/*
4103 		 * Request to change the autosync value. cfg file will be
4104 		 * available at this point. If autosync request is to turn off,
4105 		 * mark off in both the config and the kernel regardless of
4106 		 * the state of the set. If the request is to turn autosync on,
4107 		 * set in the kernel if the set is not in logging mode.
4108 		 *
4109 		 * XXX
4110 		 *	If the set is in logging mode because of a network
4111 		 *	failure, we will not know. Therefore, a manual update
4112 		 *	will have to be issued to enable autosync in the
4113 		 *	kernel.
4114 		 * XXX
4115 		 */
4116 		set_autosync(autosync, tohost, tofile, ctag);
4117 
4118 		if (autosync == AUTOSYNC_OFF) {
4119 			parms.rdc_set->autosync = AUTOSYNC_OFF;
4120 		} else if (autosync == AUTOSYNC_ON) {
4121 			parms.command = RDC_CMD_STATUS;
4122 			ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0,
4123 			    ustatus);
4124 			if (ret != SPCS_S_OK) {
4125 				rdc_err(NULL, gettext("can not determine "
4126 				    "status of Remote Mirror set %s:%s"),
4127 				    tohost, tofile);
4128 			}
4129 
4130 			/* need to reset the tunables after a status ioctl */
4131 			parms.rdc_set->autosync = autosync;
4132 			parms.rdc_set->maxqfbas = maxqfbas;
4133 			parms.rdc_set->maxqitems = maxqitems;
4134 			parms.rdc_set->asyncthr = asyncthr;
4135 
4136 			/*
4137 			 * if in logging mode, just update config, kernel will
4138 			 * be updated with the next copy/update request.
4139 			 */
4140 			if (parms.rdc_set->flags & RDC_LOGGING) {
4141 				parms.rdc_set->autosync = AUTOSYNC;
4142 			} else {
4143 				parms.rdc_set->autosync = AUTOSYNC_ON;
4144 			}
4145 
4146 			parms.command = flag;
4147 		}
4148 	}
4149 
4150 	if (autosync_toggle_needed) {
4151 		parms.command = RDC_CMD_TUNABLE;
4152 		ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
4153 		if (ret != SPCS_S_OK) {
4154 			spcs_log("sndr", NULL, gettext("failed to update "
4155 			    "autosync for Remote Mirror set %s:%s"), tohost,
4156 			    tofile);
4157 			rdc_err(NULL, gettext("failed to update autosync for "
4158 			    "Remote Mirror set %s:%s"), tohost, tofile);
4159 		}
4160 		/* reset command and default autosync flags */
4161 		parms.rdc_set->autosync = AUTOSYNC;
4162 		parms.command = flag;
4163 	}
4164 
4165 	errno = 0;
4166 	ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
4167 	if ((ret != SPCS_S_OK) && (flag != RDC_CMD_HEALTH)) {
4168 		(void) fprintf(stderr,
4169 			gettext("Remote Mirror: %s %s %s %s %s %s\n"),
4170 			fromhost, fromfile,
4171 			frombitmap, tohost, tofile, tobitmap);
4172 
4173 		if (errno == RDC_EEINVAL) {
4174 			spcs_log("sndr", NULL,
4175 			    "%s %s %s %s %s %s %s %s\n%s",
4176 			    program, rdc_decode_flag(flag, parms.options),
4177 			    fromhost, fromfile, frombitmap,
4178 			    tohost, tofile, tobitmap,
4179 			    gettext("invalid command option"));
4180 			rdc_err(&ustatus,
4181 			    gettext("Remote Mirror: invalid command option "
4182 				    "'%s'"), rdc_decode_flag(flag,
4183 				    parms.options));
4184 		} else {
4185 			spcs_log("sndr", &ustatus,
4186 			    "%s %s %s %s %s %s %s %s",
4187 			    program, rdc_decode_flag(flag, parms.options),
4188 			    fromhost, fromfile, frombitmap,
4189 			    tohost, tofile, tobitmap);
4190 			if ((flag == RDC_CMD_RECONFIG) &&
4191 			    (!(iflag & RDC_OPT_REVERSE_ROLE))) {
4192 				success = 0;
4193 				rdc_warn(&ustatus, 0);
4194 			} else
4195 				rdc_err(&ustatus, 0);
4196 		}
4197 	}
4198 	if ((flag == RDC_CMD_RECONFIG) && (iflag & RDC_OPT_REVERSE_ROLE) == 0) {
4199 		parms.command = RDC_CMD_STATUS;
4200 		if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus) ==
4201 			SPCS_S_OK) {
4202 			char shostbuf[CFG_MAX_BUF];
4203 			char svolbuf[CFG_MAX_BUF];
4204 			char key[CFG_MAX_KEY];
4205 			int i, numels;
4206 			int cfgsuccess = 1;
4207 
4208 			/*
4209 			 * okeydoke, at this point we could have a reconfig
4210 			 * gone bad. libdscfg does not know about this.
4211 			 * parms contains the kernel picture, and we know
4212 			 * what we tried to reconfig. find out where it went
4213 			 * wrong, find the set in libdscfg, update it. We'll
4214 			 * issue a warning, then return 0 (eventually).
4215 			 * this will allow libdscfg to be committed with the
4216 			 * good info. got it?
4217 			 * BTW: the only time we can run into this multiple
4218 			 * reconfig attempt failure is IF we reconfig from file
4219 			 * and some thing goes wrong with one of the reconfigs
4220 			 */
4221 
4222 			/* find the set in libdscfg */
4223 
4224 			numels = cfg_get_num_entries(cfg, "sndr");
4225 			/* yes, numels could be -1 */
4226 			for (i = 1; i < numels; i++) {
4227 				bzero(shostbuf, sizeof (shostbuf));
4228 				bzero(svolbuf, sizeof (svolbuf));
4229 				bzero(key, sizeof (key));
4230 
4231 				(void) snprintf(key, sizeof (key),
4232 				    "sndr.set%d.shost", i);
4233 
4234 				(void) cfg_get_cstring(cfg, key, &shostbuf,
4235 				    sizeof (shostbuf));
4236 				if (strncmp(shostbuf, tohost, sizeof (tohost)))
4237 					continue;
4238 
4239 				bzero(key, sizeof (key));
4240 				(void) snprintf(key, sizeof (key),
4241 				    "sndr.set%d.secondary", i);
4242 				(void) cfg_get_cstring(cfg, key, &svolbuf,
4243 				    sizeof (svolbuf));
4244 				if (strncmp(svolbuf, tofile, NSC_MAXPATH))
4245 					continue;
4246 				break;
4247 
4248 				/*
4249 				 * found it, now i contains the set offset.
4250 				 * i, being the variable, not bad english.
4251 				 */
4252 
4253 			}
4254 			/* shouldn't happen */
4255 			if ((numels < 1) || (i > numels)) {
4256 				rdc_warn(NULL, gettext("unable to retrieve "
4257 				    "set from configuration database"));
4258 				/*
4259 				 * yuck. but indents are pushing the envelope
4260 				 * we should not be updating config
4261 				 * if we did not find the entry
4262 				 * the error will have to do
4263 				 */
4264 				cfgsuccess = 0;
4265 				goto notfound;
4266 			}
4267 
4268 			/*
4269 			 * now, put all the correct names back for errors etc.
4270 			 * also, sock them into dscfg, if the the config was a
4271 			 * success for one, it will be a redundant but harmless
4272 			 */
4273 
4274 			/*
4275 			 * we could not have reconfigged mode if we
4276 			 * are not logging, AND the kernel CAN return
4277 			 * sync as the status of an async set if it is
4278 			 * currently syncing.. Hence the flags & RDC_LOGGING
4279 			 */
4280 			if (parms.rdc_set->flags & RDC_LOGGING) {
4281 				bzero(key, sizeof (key));
4282 				(void) snprintf(key, sizeof (key),
4283 				    "sndr.set%d.mode", i);
4284 				if (parms.rdc_set->flags & RDC_ASYNC) {
4285 					*doasync = 1;
4286 					if (cfg_put_cstring(cfg, key, "async",
4287 					    strlen("async")) < 0) {
4288 						cfgsuccess = 0;
4289 					}
4290 
4291 				} else {
4292 					*doasync = 0;
4293 					if (cfg_put_cstring(cfg, key, "sync",
4294 					    strlen("sync")) < 0) {
4295 						cfgsuccess = 0;
4296 					}
4297 				}
4298 			}
4299 #ifdef _RDC_CAMPUS
4300 			if (*parms.rdc_set->direct_file) {
4301 				(void) strncpy(directfile,
4302 				    parms.rdc_set->direct_file, NSC_MAXPATH);
4303 				bzero(key, sizeof (key));
4304 				(void) snprintf(key, sizeof (key),
4305 				    "sndr.set%d.type", i);
4306 				if (cfg_put_cstring(cfg, key, directfile,
4307 				    strlen(directfile)) < 0)
4308 					cfgsuccess = 0;
4309 			} else {
4310 				(void) strncpy(directfile, "-", NSC_MAXPATH);
4311 				bzero(key, sizeof (key));
4312 				(void) snprintf(key, sizeof (key),
4313 				    "sndr.set%d.type", i);
4314 				if (cfg_put_cstring(cfg, key, directfile,
4315 				    strlen(directfile)) < 0)
4316 					cfgsuccess = 0;
4317 			}
4318 #endif
4319 
4320 			if (*parms.rdc_set->group_name) {
4321 				(void) strncpy(group, parms.rdc_set->group_name,
4322 				    NSC_MAXPATH);
4323 				bzero(key, sizeof (key));
4324 				(void) snprintf(key, sizeof (key),
4325 				    "sndr.set%d.group", i);
4326 				if (cfg_put_cstring(cfg, key, group,
4327 				    strlen(group)) < 0)
4328 					cfgsuccess = 0;
4329 
4330 			} else {
4331 				(void) strncpy(group, "-", NSC_MAXPATH);
4332 				bzero(key, sizeof (key));
4333 				(void) snprintf(key, sizeof (key),
4334 				    "sndr.set%d.group", i);
4335 				if (cfg_put_cstring(cfg, key, group,
4336 				    strlen(group)) < 0)
4337 					cfgsuccess = 0;
4338 			}
4339 
4340 			if (*parms.rdc_set->disk_queue) {
4341 				(void) strncpy(diskqueue,
4342 				    parms.rdc_set->disk_queue, NSC_MAXPATH);
4343 			} else {
4344 				(void) strncpy(diskqueue, "-", NSC_MAXPATH);
4345 			}
4346 			bzero(key, sizeof (key));
4347 			(void) snprintf(key, sizeof (key),
4348 			    "sndr.set%d.diskq", i);
4349 			if (cfg_put_cstring(cfg, key, diskqueue,
4350 			    strlen(diskqueue)) < 0)
4351 				cfgsuccess = 0;
4352 
4353 			(void) strncpy(frombitmap,
4354 			    parms.rdc_set->primary.bitmap, NSC_MAXPATH);
4355 			bzero(key, sizeof (key));
4356 			(void) snprintf(key, sizeof (key),
4357 			    "sndr.set%d.pbitmap", i);
4358 			if (cfg_put_cstring(cfg, key, frombitmap,
4359 			    strlen(frombitmap)) < 0)
4360 				cfgsuccess = 0;
4361 
4362 			(void) strncpy(tobitmap,
4363 			    parms.rdc_set->secondary.bitmap, NSC_MAXPATH);
4364 			bzero(key, sizeof (key));
4365 			(void) snprintf(key, sizeof (key),
4366 			    "sndr.set%d.sbitmap", i);
4367 			if (cfg_put_cstring(cfg, key, tobitmap,
4368 			    strlen(tobitmap)) < 0)
4369 				cfgsuccess = 0;
4370 
4371 			bzero(key, sizeof (key));
4372 			(void) snprintf(key, sizeof (key),
4373 			    "sndr.set%d.cnode", i);
4374 			if (clustered)
4375 				if (cfg_put_cstring(cfg, key, ctag,
4376 				    strlen(ctag)) < 0)
4377 					cfgsuccess = 0;
4378 notfound:
4379 			if (cfgsuccess == 0) {
4380 				rdc_warn(NULL, gettext("unable to update "
4381 				    "configuration storage"));
4382 			}
4383 		} else {
4384 			spcs_log("sndr", NULL,
4385 				"%s %s %s %s %s %s %s %s\n%s",
4386 				program, rdc_decode_flag(flag, parms.options),
4387 				fromhost, fromfile, frombitmap,
4388 				tohost, tofile, tobitmap,
4389 				gettext("unable to update config file"));
4390 			rdc_err(&ustatus,
4391 				gettext("Remote Mirror: unable to update "
4392 				    "config file"));
4393 
4394 		}
4395 	}
4396 
4397 	if (flag == RDC_CMD_HEALTH && errno == 0) {
4398 		(void) fprintf(stderr,
4399 			gettext("Remote Mirror: %s %s %s %s %s %s\n"),
4400 			fromhost, fromfile,
4401 			frombitmap, tohost, tofile, tobitmap);
4402 
4403 		if (ret == RDC_ACTIVE)
4404 			(void) fprintf(stderr, "Active\n");
4405 		else if (ret == RDC_INACTIVE)
4406 			(void) fprintf(stderr, "Inactive\n");
4407 		else
4408 			(void) fprintf(stderr, "Unknown\n");
4409 	} else if (ret != SPCS_S_OK) {
4410 		if (errno == RDC_EEINVAL) {
4411 			spcs_log("sndr", NULL,
4412 			    "%s %s %s %s %s %s %s %s\n%s",
4413 			    program, rdc_decode_flag(flag, parms.options),
4414 			    fromhost, fromfile, frombitmap,
4415 			    tohost, tofile, tobitmap,
4416 			    gettext("invalid command option"));
4417 			rdc_err(&ustatus,
4418 			    gettext("Remote Mirror: invalid command option "
4419 				    "'%s'"),
4420 			    rdc_decode_flag(flag, parms.options));
4421 		}
4422 	}
4423 	if (flag == RDC_CMD_STATUS) {
4424 		(void) fprintf(stderr,
4425 			gettext("Remote Mirror: %s %s %s %s %s %s\n"),
4426 			fromhost, fromfile,
4427 			frombitmap, tohost, tofile, tobitmap);
4428 		(void) fprintf(stderr, "flags 0x%x\n", parms.rdc_set->flags |
4429 		    parms.rdc_set->sync_flags | parms.rdc_set->bmap_flags);
4430 	} else if (success) {
4431 		spcs_log("sndr", NULL,
4432 		    gettext("%s %s %s %s %s %s %s %s\nSuccessful"),
4433 		    program, rdc_decode_flag(flag, parms.options),
4434 		    fromhost, fromfile, frombitmap,
4435 		    tohost, tofile, tobitmap);
4436 		if (flag == RDC_CMD_TUNABLE)
4437 			spcslog_tunable(tohost, tofile);
4438 	}
4439 
4440 	if (cfg && perform_autosv()) {
4441 		spcs_s_ufree(&ustatus);
4442 		/* figure out which are the local volumes */
4443 		if (parms.options & RDC_OPT_PRIMARY) {
4444 			vol1 = fromfile;
4445 			vol2 = frombitmap;
4446 			if ((diskqueue && diskqueue[0]) &&
4447 			    (strncmp(diskqueue, "-", 1) != 0))
4448 				vol3 = diskqueue;
4449 			else
4450 				vol3 = NULL;
4451 		} else {
4452 			vol1 = tofile;
4453 			vol2 = tobitmap;
4454 			vol3 = NULL;
4455 			if ((flag == RDC_CMD_ENABLE) &&
4456 			    (strlen(diskqueue) > 0) &&
4457 			    (strncmp(diskqueue, "-", 1)) != 0) {
4458 				rdc_warn(NULL,
4459 				    gettext("enabling a disk queue on a "
4460 				    "Remote Mirror secondary is not allowed. "
4461 				    "(%s) ignored"), diskqueue);
4462 			}
4463 		}
4464 
4465 		if (flag == RDC_CMD_ENABLE) {
4466 			ustatus = spcs_s_ucreate();
4467 			/*
4468 			 * SV-enable all local volumes
4469 			 * if the sv_enables fail, disable the sndr vols
4470 			 * that we just enabled
4471 			 * and return -1 so the cfg_commit() won't happen
4472 			 */
4473 
4474 			if (nsc_lookup(volhash, vol1) == NULL) {
4475 				if (cfg_vol_enable(cfg, vol1, ctag, "sndr")
4476 				    < 0) {
4477 					spcs_log("sndr", NULL,
4478 					    "sv enable failed for %s, "
4479 					    "disabling Remote Mirror set %s:%s",
4480 					    vol1, tohost, tofile);
4481 					/*
4482 					 * warn here, but we are going to exit
4483 					 * we want to catch any errors on the
4484 					 * way down, then exit
4485 					 */
4486 
4487 					rdc_warn(NULL,
4488 					    "unable to sv enable %s\n"
4489 					    "disabling Remote Mirror set %s:%s",
4490 					    vol1, tohost, tofile);
4491 
4492 					parms.command = RDC_CMD_DISABLE;
4493 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4494 					    NULL, 0, 0, 0, ustatus);
4495 					if (ret != SPCS_S_OK) {
4496 						(void) fprintf(stderr,
4497 						    gettext("Remote Mirror:"
4498 						    " %s %s %s %s %s %s\n"),
4499 						    fromhost, fromfile,
4500 						    frombitmap, tohost, tofile,
4501 						    tobitmap);
4502 						spcs_log("sndr", &ustatus,
4503 						"%s %s %s %s %s %s %s %s",
4504 						program,
4505 						rdc_decode_flag(parms.command,
4506 						parms.options),
4507 						fromhost,
4508 						fromfile, frombitmap,
4509 						tohost, tofile, tobitmap);
4510 						rdc_err(&ustatus, 0);
4511 					}
4512 					/*
4513 					 * ok, we should've reported any errs
4514 					 * exit explictly
4515 					 */
4516 					exit(1);
4517 
4518 				}
4519 			}
4520 			if (vol2 && nsc_lookup(volhash, vol2) == NULL) {
4521 				if (cfg_vol_enable(cfg, vol2, ctag, "sndr")
4522 				    < 0) {
4523 					spcs_log("sndr", NULL,
4524 					    "sv enable failed for %s, "
4525 					    "disabling Remote Mirror set %s:%s",
4526 					    vol1, tohost, tofile);
4527 					/*
4528 					 * warn here, but we are going to exit
4529 					 * we want to catch any errors on the
4530 					 * way down, then exit
4531 					 */
4532 
4533 					rdc_warn(NULL,
4534 					    "unable to sv enable %s\n"
4535 					    "disabling Remote Mirror set %s:%s",
4536 					    vol2, tohost, tofile);
4537 
4538 					parms.command = RDC_CMD_DISABLE;
4539 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4540 					    NULL, 0, 0, 0, ustatus);
4541 					if (ret != SPCS_S_OK) {
4542 						(void) fprintf(stderr,
4543 						    gettext("Remote Mirror:"
4544 						    " %s %s %s %s %s %s\n"),
4545 						    fromhost, fromfile,
4546 						    frombitmap, tohost, tofile,
4547 						    tobitmap);
4548 						spcs_log("sndr", &ustatus,
4549 						"%s %s %s %s %s %s %s %s",
4550 						program,
4551 						rdc_decode_flag(parms.command,
4552 						parms.options),
4553 						fromhost,
4554 						fromfile, frombitmap,
4555 						tohost, tofile, tobitmap);
4556 						rdc_err(&ustatus, 0);
4557 					}
4558 					/*
4559 					 * ok, we should've reported any errs
4560 					 * exit explictly
4561 					 */
4562 					exit(1);
4563 
4564 				}
4565 			}
4566 
4567 			if (vol3 && nsc_lookup(volhash, diskqueue) == NULL) {
4568 				if (cfg_vol_enable(cfg, diskqueue, ctag, "sndr")
4569 				    < 0) {
4570 					spcs_log("sndr", NULL,
4571 					    "sv enable failed for %s, "
4572 					    "disabling Remote Mirror set %s:%s",
4573 					    diskqueue, tohost, tofile);
4574 					if (cfg_vol_disable(cfg, vol1, ctag,
4575 						"sndr") < 0)
4576 					    rdc_warn(NULL, gettext("Failed to "
4577 					    "remove volume [%s] from "
4578 					    "configuration"), vol1);
4579 					if (cfg_vol_disable(cfg, vol2, ctag,
4580 						"sndr") < 0)
4581 					    rdc_warn(NULL, gettext("Failed to "
4582 					    "remove volume [%s] from "
4583 					    "configuration"), vol2);
4584 
4585 					/*
4586 					 * warn here, but we are going to exit
4587 					 * we want to catch any errors on the
4588 					 * way down, then exit
4589 					 */
4590 
4591 					rdc_warn(NULL,
4592 					    "unable to sv enable %s\n"
4593 					    "disabling Remote Mirror set %s:%s",
4594 					    diskqueue, tohost, tofile);
4595 
4596 					parms.command = RDC_CMD_DISABLE;
4597 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4598 					    NULL, 0, 0, 0, ustatus);
4599 					if (ret != SPCS_S_OK) {
4600 						(void) fprintf(stderr,
4601 						    gettext("Remote Mirror:"
4602 						    " %s %s %s %s %s %s\n"),
4603 						    fromhost, fromfile,
4604 						    frombitmap, tohost, tofile,
4605 						    tobitmap);
4606 						spcs_log("sndr", &ustatus,
4607 						"%s %s %s %s %s %s %s %s",
4608 						program,
4609 						rdc_decode_flag(parms.command,
4610 						parms.options),
4611 						fromhost,
4612 						fromfile, frombitmap,
4613 						tohost, tofile, tobitmap);
4614 						rdc_err(&ustatus, 0);
4615 					}
4616 					/*
4617 					 * ok, we should've reported any errs
4618 					 * exit explictly
4619 					 */
4620 					exit(1);
4621 
4622 				}
4623 			}
4624 		} else if (flag == RDC_CMD_DISABLE) {
4625 			/*
4626 			 * If we're no longer using a volume, SV-disable it
4627 			 */
4628 			volcount_t *vc;
4629 
4630 			vc = nsc_lookup(volhash, vol1);
4631 			if (vc && (1 == vc->count)) {
4632 				if (cfg_vol_disable(cfg, vol1, ctag, "sndr")
4633 				    < 0)
4634 					rdc_warn(NULL, gettext("Failed to "
4635 					    "remove volume [%s] from "
4636 					    "configuration"), vol1);
4637 
4638 			} else if (!vc) {
4639 				rdc_warn(NULL,
4640 				    gettext("Unable to find %s in config"),
4641 				    vol1);
4642 			}
4643 
4644 			if (vol2) {
4645 				vc = nsc_lookup(volhash, vol2);
4646 				if (vc && (1 == vc->count)) {
4647 					if (cfg_vol_disable(cfg, vol2, ctag,
4648 						"sndr") < 0)
4649 					rdc_warn(NULL, gettext("Failed to "
4650 					    "remove volume [%s] from "
4651 					    "configuration"), vol2);
4652 				} else if (!vc) {
4653 					rdc_warn(NULL, gettext("Unable to find"
4654 					    " %s in config"), vol2);
4655 				}
4656 			}
4657 
4658 			if (diskqueue != NULL && strlen(diskqueue) > 0) {
4659 				vc = nsc_lookup(volhash, diskqueue);
4660 				if (vc && (1 == vc->count)) {
4661 				if (cfg_vol_disable(cfg, diskqueue, ctag,
4662 					"sndr") < 0)
4663 					rdc_warn(NULL, gettext("Failed to "
4664 					    "remove disk queue [%s] from "
4665 					    "configuration"), diskqueue);
4666 				} else if (!vc) {
4667 					rdc_warn(NULL, gettext("Unable to find"
4668 					    " %s in config"), diskqueue);
4669 				}
4670 			}
4671 		/* WARNING about to go to 4 space indenting */
4672 		} else if (flag == RDC_CMD_RECONFIG) {
4673 			volcount_t *vc;
4674 			/* disable ex-bitmaps, enable new bitmaps */
4675 			if (parms.options & RDC_OPT_PRIMARY) {
4676 			    if (strcmp(orig_fbmp, frombitmap) != 0) {
4677 				vc = nsc_lookup(volhash, orig_fbmp);
4678 				if (vc && (vc->count == 1)) {
4679 				    if (cfg_vol_disable(cfg, orig_fbmp, ctag,
4680 					"sndr") < 0)
4681 					rdc_warn(NULL, gettext("Failed to "
4682 					    "remove bitmap [%s] from "
4683 					    "configuration"), orig_fbmp);
4684 				} else if (!vc) {
4685 				    rdc_warn(NULL, gettext("Unable to find "
4686 				    "%s in config"), orig_fbmp);
4687 				}
4688 				if (nsc_lookup(volhash, frombitmap) == NULL) {
4689 				    if (cfg_vol_enable(cfg, frombitmap, ctag,
4690 					"sndr") < 0) {
4691 					spcs_log("sndr", NULL,
4692 					    "reconfig sv enable failed for %s, "
4693 					    "disabling Remote Mirror set %s:%s",
4694 					    frombitmap, tohost, tofile);
4695 					rdc_warn(NULL,
4696 					    "unable to sv enable %s\n"
4697 					    "disabling Remote Mirror set %s:%s",
4698 					    frombitmap, tohost, tofile);
4699 					parms.command = RDC_CMD_DISABLE;
4700 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4701 					    NULL, 0, 0, 0, ustatus);
4702 					if (ret != SPCS_S_OK) {
4703 					    (void) fprintf(stderr,
4704 						gettext("Remote Mirror:"
4705 						" %s %s %s %s %s %s\n"),
4706 						fromhost, fromfile,
4707 						frombitmap, tohost, tofile,
4708 						tobitmap);
4709 					    spcs_log("sndr", &ustatus,
4710 						"%s %s %s %s %s %s %s %s",
4711 						program,
4712 						rdc_decode_flag(parms.command,
4713 						parms.options),
4714 						fromhost,
4715 						fromfile, frombitmap,
4716 						tohost, tofile, tobitmap);
4717 						rdc_warn(&ustatus, 0);
4718 					}
4719 					exit(1);
4720 				    }
4721 				}
4722 			    } else if ((orig_diskq[0] != '\0') &&
4723 					(strcmp(orig_diskq, diskqueue) != 0)) {
4724 				vc = nsc_lookup(volhash, orig_diskq);
4725 				if (vc && (vc->count == 1)) {
4726 				    if (cfg_vol_disable(cfg, orig_diskq, ctag,
4727 					"sndr") < 0)
4728 					rdc_warn(NULL, gettext("Failed to "
4729 					    "remove disk queue [%s] from "
4730 					    "configuration"), orig_diskq);
4731 				} else if (!vc) {
4732 				    rdc_warn(NULL, gettext("Unable to find "
4733 					"%s in config"), orig_diskq);
4734 				}
4735 				if (vol3 &&
4736 				    (nsc_lookup(volhash, diskqueue) == NULL)) {
4737 				    if (cfg_vol_enable(cfg, diskqueue, ctag,
4738 					"sndr") < 0) {
4739 					spcs_log("sndr", NULL, "reconfig sv "
4740 					    "enable of diskqueue %s failed, "
4741 					    "disabling Remote Mirror set %s:%s",
4742 					    diskqueue, tohost, tofile);
4743 					rdc_warn(NULL, "reconfig sv "
4744 					    "enable of diskqueue %s failed."
4745 					    "disabling Remote Mirror set %s:%s",
4746 					    diskqueue, tohost, tofile);
4747 					parms.command = RDC_CMD_DISABLE;
4748 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4749 					    NULL, 0, 0, 0, ustatus);
4750 					if (ret != SPCS_S_OK) {
4751 					    (void) fprintf(stderr,
4752 						gettext("Remote Mirror:"
4753 						" %s %s %s %s %s %s\n"),
4754 						fromhost, fromfile,
4755 						frombitmap, tohost, tofile,
4756 						tobitmap);
4757 					    spcs_log("sndr", &ustatus,
4758 						"%s %s %s %s %s %s %s %s",
4759 						program,
4760 						rdc_decode_flag(parms.command,
4761 						parms.options),
4762 						fromhost,
4763 						fromfile, frombitmap,
4764 						tohost, tofile, tobitmap);
4765 						rdc_warn(&ustatus, 0);
4766 					}
4767 					exit(1);
4768 				    }
4769 				}
4770 			    }
4771 			} else if (flag != RDC_OPT_PRIMARY) {
4772 			    if (strcmp(orig_tbmp, tobitmap) != 0) {
4773 				vc = nsc_lookup(volhash, orig_tbmp);
4774 				if (vc && (vc->count == 1)) {
4775 				    if (cfg_vol_disable(cfg, orig_tbmp, ctag,
4776 					"sndr") < 0)
4777 					rdc_warn(NULL, gettext("Failed to "
4778 					    "remove bitmap [%s] from "
4779 					    "configuration"), orig_tbmp);
4780 				} else if (!vc) {
4781 				    rdc_warn(NULL,
4782 				    gettext("Unable to find %s in config"),
4783 				    orig_tbmp);
4784 				}
4785 				if (nsc_lookup(volhash, tobitmap) == NULL) {
4786 				    if (cfg_vol_enable(cfg, tobitmap, ctag,
4787 					"sndr") < 0) {
4788 					spcs_log("sndr", NULL,
4789 					    "reconfig sv enable failed for %s, "
4790 					"disabling Remote Mirror set %s:%s",
4791 					tobitmap, tohost, tofile);
4792 					rdc_warn(NULL,
4793 					    "unable to sv enable %s\n"
4794 					    "disabling Remote Mirror set %s:%s",
4795 					    tobitmap, tohost, tofile);
4796 					parms.command = RDC_CMD_DISABLE;
4797 					ret = RDC_IOCTL(RDC_CONFIG, &parms,
4798 					    NULL, 0, 0, 0, ustatus);
4799 					if (ret != SPCS_S_OK) {
4800 					    (void) fprintf(stderr,
4801 						gettext("Remote Mirror:"
4802 						" %s %s %s %s %s %s\n"),
4803 						fromhost, fromfile,
4804 						frombitmap, tohost, tofile,
4805 						tobitmap);
4806 					    spcs_log("sndr", &ustatus,
4807 						"%s %s %s %s %s %s %s %s",
4808 						program,
4809 						rdc_decode_flag(parms.command,
4810 						parms.options),
4811 						fromhost, fromfile, frombitmap,
4812 						tohost, tofile, tobitmap);
4813 						rdc_warn(&ustatus, 0);
4814 					}
4815 					exit(1);
4816 				    }
4817 				}
4818 			    }
4819 			}
4820 		/* END 4 space indenting */
4821 		}
4822 	}
4823 	spcs_s_ufree(&ustatus);
4824 
4825 	return (0);
4826 }
4827 
4828 
4829 /*
4830  * read_config()
4831  *
4832  * DESCRIPTION: Read the lines in a configuration file and return the
4833  *		pairs of devices to be mirrored/enabled/disabled/updated.
4834  *		The format for the configuration file is as follows:
4835  *
4836  *		fromhost fromfile frombitmap tohost tofile tobitmap
4837  *
4838  *		where fromfile is the primary device which is local to the
4839  *		fromhost subsystem, tofile is the secondary device which is
4840  *		local to the tohost subsystem, and type is 1 if the device
4841  *		a simckd device or 0 otherwise.  Any line preceeded by a '#'
4842  *		is considered to be a comment.
4843  *
4844  * Inputs:
4845  *	char *config_file	Name of configuration file for rdcadm
4846  *
4847  * Outputs:
4848  *	int i			Number of pairs of devices
4849  *
4850  * Side Effects: The 0 to i-1 entries in the pair_list are filled.
4851  *
4852  */
4853 
4854 int
4855 read_config(int flag, char *config_file, char *group_arg, char *ctag_arg)
4856 {
4857 	int ret;
4858 	char dsk_flagstr[NSC_MAXPATH];
4859 	char line[1024], tmp_line[1024];
4860 	char fromhost[MAX_RDC_HOST_SIZE];
4861 	char fromfile[NSC_MAXPATH];
4862 	char frombitmap[NSC_MAXPATH];
4863 	char tohost[MAX_RDC_HOST_SIZE];
4864 	char tofile[NSC_MAXPATH];
4865 	char tobitmap[NSC_MAXPATH];
4866 	char directfile[NSC_MAXPATH];
4867 	char sync[16];
4868 	int doasync;
4869 	FILE *fp;
4870 	int i, j;
4871 	char *extra_args[EXTRA_ARGS];
4872 	char *tmp, *split_str = " \t\n";
4873 
4874 	for (j = 0; j < EXTRA_ARGS; j++)
4875 		extra_args[j] = malloc(NSC_MAXPATH);
4876 
4877 	if (!(fp = fopen(config_file, "r"))) {
4878 		rdc_err(NULL, gettext("error opening %s"), config_file);
4879 	}
4880 
4881 	i = 0;
4882 	while (fgets(line, sizeof (line), fp)) {
4883 		if (line[0] == '#')  /* this is a comment */
4884 			continue;
4885 
4886 		ret = 0;
4887 		(void) strcpy(tmp_line, line);
4888 
4889 		if ((tmp = strtok(tmp_line, split_str)) != NULL) {
4890 			if (strlen(tmp) >= MAX_RDC_HOST_SIZE) {
4891 			    (void) printf(gettext("hostname is longer than %d "
4892 				"characters\n"), (MAX_RDC_HOST_SIZE - 1));
4893 			    continue;
4894 			}
4895 			(void) strncpy(fromhost, tmp, (MAX_RDC_HOST_SIZE - 1));
4896 			fromhost[(MAX_RDC_HOST_SIZE - 1)] = '\0';
4897 			ret++;
4898 		}
4899 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4900 			if (strlen(tmp) >= NSC_MAXPATH) {
4901 			    (void) printf(gettext(
4902 				"device name is longer than %d "
4903 				"characters\n"), (NSC_MAXPATH - 1));
4904 			    continue;
4905 			}
4906 			(void) strncpy(fromfile, tmp, (NSC_MAXPATH - 1));
4907 			fromfile[(NSC_MAXPATH - 1)] = '\0';
4908 			ret++;
4909 		}
4910 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4911 			if (strlen(tmp) >= NSC_MAXPATH) {
4912 			    (void) printf(gettext(
4913 				"device name is longer than %d "
4914 				"characters\n"), (NSC_MAXPATH - 1));
4915 			    continue;
4916 			}
4917 			(void) strncpy(frombitmap, tmp, (NSC_MAXPATH - 1));
4918 			frombitmap[(NSC_MAXPATH - 1)] = '\0';
4919 			ret++;
4920 		}
4921 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4922 			if (strlen(tmp) >= MAX_RDC_HOST_SIZE) {
4923 			    (void) printf(gettext(
4924 				"hostname is longer than %d "
4925 				"characters\n"), (MAX_RDC_HOST_SIZE - 1));
4926 			    continue;
4927 			}
4928 			(void) strncpy(tohost, tmp, (MAX_RDC_HOST_SIZE - 1));
4929 			tohost[(MAX_RDC_HOST_SIZE - 1)] = '\0';
4930 			ret++;
4931 		}
4932 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4933 			if (strlen(tmp) >= NSC_MAXPATH) {
4934 			    (void) printf(gettext(
4935 				"device name is longer than %d "
4936 				"characters\n"), (NSC_MAXPATH - 1));
4937 			    continue;
4938 			}
4939 			(void) strncpy(tofile, tmp, (NSC_MAXPATH - 1));
4940 			tofile[(NSC_MAXPATH - 1)] = '\0';
4941 			ret++;
4942 		}
4943 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4944 			if (strlen(tmp) >= NSC_MAXPATH) {
4945 			    (void) printf(gettext(
4946 				"device name is longer than %d "
4947 				"characters\n"), (NSC_MAXPATH - 1));
4948 			    continue;
4949 			}
4950 			(void) strncpy(tobitmap, tmp, (NSC_MAXPATH - 1));
4951 			tobitmap[(NSC_MAXPATH - 1)] = '\0';
4952 			ret++;
4953 		}
4954 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4955 			(void) strncpy(dsk_flagstr, tmp, 15);
4956 			dsk_flagstr[15] = '\0';
4957 			ret++;
4958 		}
4959 		if ((tmp = strtok(NULL, split_str)) != NULL) {
4960 			(void) strncpy(sync, tmp, 15);
4961 			sync[15] = '\0';
4962 			ret++;
4963 		}
4964 		for (j = 0; j < EXTRA_ARGS; j++) {
4965 			if ((tmp = strtok(NULL, split_str)) != NULL) {
4966 				(void) strncpy(extra_args[j], tmp,
4967 				    (NSC_MAXPATH - 1));
4968 				extra_args[j][(NSC_MAXPATH - 1)] = '\0';
4969 				ret++;
4970 			}
4971 		}
4972 
4973 		if (ret == 0) /* this is a blank line */
4974 			continue;
4975 
4976 		if (ret < 8) {
4977 			(void) fclose(fp);
4978 			rdc_warn(NULL,
4979 			    gettext("invalid format in %s"), config_file);
4980 			rdc_err(NULL, "%s", line);
4981 		}
4982 
4983 		if (i >= rdc_maxsets) {
4984 			(void) fclose(fp);
4985 			rdc_err(NULL,
4986 			    gettext("number of Remote Mirror sets exceeds %d"),
4987 			    rdc_maxsets);
4988 		}
4989 
4990 #ifdef _RDC_CAMPUS
4991 		if (dsk_flagstr[0] == '/') {
4992 			/* fcal directio */
4993 			(void) strncpy(directfile, dsk_flagstr, NSC_MAXPATH);
4994 		} else if (strcmp(dsk_flagstr, "ip") != 0) {
4995 #else
4996 		if (strcmp(dsk_flagstr, "ip") != 0) {
4997 #endif
4998 			(void) fclose(fp);
4999 			rdc_err(NULL,
5000 #ifdef _RDC_CAMPUS
5001 			    gettext("ip/fcal specification missing"));
5002 #else
5003 			    gettext("ip specification missing"));
5004 #endif
5005 		} else
5006 			(void) strcpy(directfile, "ip");
5007 
5008 		if (strcmp(sync, "sync") == 0)
5009 			doasync = 0;
5010 		else if (strcmp(sync, "async") == 0)
5011 			doasync = 1;
5012 		else {
5013 			(void) fclose(fp);
5014 			rdc_err(NULL,
5015 			    gettext("sync/async specification missing"));
5016 		}
5017 		(void) strncpy(pair_list[i].fhost, fromhost, MAX_RDC_HOST_SIZE);
5018 		(void) strncpy(pair_list[i].ffile, fromfile, NSC_MAXPATH);
5019 		(void) strncpy(pair_list[i].fbitmap, frombitmap, NSC_MAXPATH);
5020 		(void) strncpy(pair_list[i].thost, tohost, MAX_RDC_HOST_SIZE);
5021 		(void) strncpy(pair_list[i].tfile, tofile, NSC_MAXPATH);
5022 		(void) strncpy(pair_list[i].tbitmap, tobitmap, NSC_MAXPATH);
5023 		(void) strncpy(pair_list[i].directfile, directfile,
5024 		    NSC_MAXPATH);
5025 		pair_list[i].doasync = doasync;
5026 
5027 		if (gethost_netaddrs(fromhost, tohost,
5028 		    (char *)pair_list[i].fnetaddr,
5029 		    (char *)pair_list[i].tnetaddr) < 0) {
5030 			(void) fclose(fp);
5031 			rdc_err(NULL, gettext("unable to determine IP "
5032 			    "addresses for hosts %s, %s"), fromhost, tohost);
5033 		}
5034 
5035 		if (parse_extras(ret - 8, extra_args, i) < 0) {
5036 			(void) fclose(fp);
5037 			rdc_err(NULL, gettext("illegal option in:\n%s"),
5038 			    line);
5039 		}
5040 
5041 		if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
5042 			if (ctag_check(fromhost, fromfile, frombitmap,
5043 			    tohost, tofile, tobitmap, pair_list[i].ctag,
5044 			    pair_list[i].diskqueue) < 0)
5045 				continue; /* Ignore illegal sets */
5046 			if (pair_diskqueue_check(i))
5047 				continue; /* ignore sets with incorrect diskq */
5048 		}
5049 
5050 		/* Filter according to ctag and group arguments */
5051 		if (strcmp(ctag_arg, "") &&
5052 		    strncmp(ctag_arg, pair_list[i].ctag,
5053 		    MAX_RDC_HOST_SIZE))
5054 			continue;
5055 		if (strcmp(group_arg, "") &&
5056 		    strncmp(group_arg, pair_list[i].group, NSC_MAXPATH))
5057 			continue;
5058 
5059 		i++;
5060 	}
5061 	(void) fclose(fp);
5062 	for (j = 0; j < EXTRA_ARGS; j++)
5063 		free(extra_args[j]);
5064 	return (i);
5065 }
5066 
5067 
5068 /*
5069  * read_libcfg()
5070  *
5071  * DESCRIPTION: Read the relevant config info via libcfg
5072  *
5073  * Outputs:
5074  *	int i			Number of pairs of devices
5075  *
5076  * Side Effects: The 0 to i-1 entries in the pair_list are filled.
5077  *
5078  */
5079 static int
5080 read_libcfg(int flag, char *group_arg, char *ctag_arg)
5081 {
5082 	int rc;
5083 	CFGFILE *cfg;
5084 	int i;
5085 	_sd_dual_pair_t *pairp;
5086 	char buf[CFG_MAX_BUF];
5087 	char key[CFG_MAX_KEY];
5088 	int setnumber;
5089 
5090 	if ((cfg = cfg_open(NULL)) == NULL)
5091 		rdc_err(NULL, gettext("unable to access configuration"));
5092 
5093 	if (!cfg_lock(cfg, CFG_RDLOCK))
5094 		rdc_err(NULL, gettext("unable to lock configuration"));
5095 
5096 	if (strcmp(ctag_arg, ""))
5097 		cfg_resource(cfg, ctag_arg);
5098 	else {
5099 		cfg_resource(cfg, NULL);
5100 	}
5101 
5102 	setnumber = 0;
5103 	/*CSTYLED*/
5104 	for (i = 0; i < rdc_maxsets;) {
5105 		setnumber++;
5106 
5107 		bzero(buf, CFG_MAX_BUF);
5108 		(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
5109 		rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
5110 		if (rc < 0)
5111 			break;
5112 
5113 		pairp = &pair_list[i];
5114 		if (parse_cfg_buf(buf, pairp, NULL))
5115 			continue;
5116 
5117 		if (strcmp(group_arg, "") &&
5118 		    strncmp(group_arg, pairp->group, NSC_MAXPATH))
5119 			    continue;
5120 
5121 		if (flag == RDC_CMD_RECONFIG) {
5122 			if (reconfig_pbitmap)
5123 				(void) strncpy(pairp->fbitmap, reconfig_pbitmap,
5124 				    NSC_MAXPATH);
5125 			if (reconfig_sbitmap)
5126 				(void) strncpy(pairp->tbitmap, reconfig_sbitmap,
5127 				    NSC_MAXPATH);
5128 #ifdef _RDC_CAMPUS
5129 			if (reconfig_direct)
5130 				(void) strncpy(directfile, reconfig_direct,
5131 				    NSC_MAXPATH);
5132 #endif
5133 			if (reconfig_group)
5134 				(void) strncpy(pairp->group, reconfig_group,
5135 				    NSC_MAXPATH);
5136 
5137 			if (strlen(reconfig_ctag) > 0)
5138 				(void) strncpy(pairp->ctag, reconfig_ctag,
5139 				    MAX_RDC_HOST_SIZE);
5140 
5141 			if (reconfig_doasync != -1)
5142 				pairp->doasync = reconfig_doasync;
5143 		}
5144 
5145 
5146 		if (ctag_check(pairp->fhost, pairp->ffile,
5147 		    pairp->fbitmap, pairp->thost, pairp->tfile,
5148 		    pairp->tbitmap, pairp->ctag, pairp->diskqueue) < 0)
5149 			continue; /* Ignore illegal sets */
5150 
5151 		if (gethost_netaddrs(pairp->fhost, pairp->thost,
5152 		    (char *)pairp->fnetaddr,
5153 		    (char *)pairp->tnetaddr) < 0) {
5154 			rdc_err(NULL, gettext("unable to determine IP "
5155 			    "addresses for hosts %s, %s"), pairp->fhost,
5156 			    pairp->thost);
5157 		}
5158 
5159 		i++;
5160 	}
5161 
5162 	cfg_close(cfg);
5163 	return (i);
5164 }
5165 
5166 void
5167 q_usage(int prhdr)
5168 {
5169 	if (prhdr)
5170 		(void) fprintf(stderr, gettext("disk queue usage:\n"));
5171 
5172 	(void) fprintf(stderr,
5173 	    gettext("\t%s -g <group> -q a <vol>\t\tadd disk queue to "
5174 	    "group\n"), program);
5175 	(void) fprintf(stderr,
5176 	    gettext("\t%s -g <group> -q d \t\tremove disk queue from"
5177 	    " group\n"), program);
5178 	(void) fprintf(stderr,
5179 	    gettext("\t%s -g <group> -q r <newvol>\treplace disk queue for a"
5180 	    " group\n"), program);
5181 
5182 	(void) fprintf(stderr,
5183 	    gettext("\t%s -q a <vol> <shost>:<sdev>\tadd disk queue to "
5184 	    "a set\n"), program);
5185 	(void) fprintf(stderr,
5186 	    gettext("\t%s -q d <shost>:<sdev>\t\tremove disk queue from "
5187 	    "a set\n"), program);
5188 	(void) fprintf(stderr,
5189 	    gettext("\t%s -q r <newvol> <shost>:<sdev>\treplace disk queue for "
5190 	    "a set\n"), program);
5191 
5192 }
5193 
5194 static void
5195 usage()
5196 {
5197 	(void) fprintf(stderr, gettext("usage:\n"));
5198 
5199 	(void) fprintf(stderr,
5200 	    gettext("\t%s [opts] -a {on | off} [set]\t"
5201 	    "set autosync\n"), program);
5202 
5203 	(void) fprintf(stderr,
5204 	    gettext("\t%s [opts] -A <asyncthr> [set]\t"
5205 	    "set the number of asynchronous\n\t\t\t\t\t\tthreads\n"),
5206 	    program);
5207 
5208 	(void) fprintf(stderr,
5209 	    gettext("\t%s [opts] -d [set]\t\t\t"
5210 	    "disable\n"), program);
5211 
5212 	(void) fprintf(stderr,
5213 	    gettext("\t%s [opts] -e [set]\t\t\t"
5214 	    "enable with bits in bitmap set\n"), program);
5215 
5216 	(void) fprintf(stderr,
5217 	    gettext("\t%s [opts] -E [set]\t\t\t"
5218 	    "enable with bits in bitmap clear\n"), program);
5219 
5220 	(void) fprintf(stderr,
5221 	    gettext("\t%s [opts] -F <maxqfbas> [set]\t"
5222 	    "set maximum fbas to queue\n"), program);
5223 
5224 	(void) fprintf(stderr,
5225 	    gettext("\t%s [opts] -D {block | noblock} [set]\t"
5226 	    "set disk queue blocking mode\n"), program);
5227 
5228 	(void) fprintf(stderr,
5229 	    gettext("\t%s [opts] -H [set]\t\t\t"
5230 	    "report link health\n"), program);
5231 
5232 	(void) fprintf(stderr,
5233 	    gettext("\t%s -h\t\t\t\tusage message\n"), program);
5234 
5235 	(void) fprintf(stderr,
5236 	    gettext("\t%s -I a <master> <shadow> <bitmap>\t"
5237 	    "add ndr_ii config entry\n"), program);
5238 
5239 	(void) fprintf(stderr,
5240 	    gettext("\t%s -I d <master> <shadow> <bitmap>\t"
5241 	    "delete ndr_ii config entry\n"), program);
5242 
5243 	(void) fprintf(stderr,
5244 	    gettext("\t%s [opts] -i [set]\t\t\t"
5245 	    "print sets in config file format\n"), program);
5246 
5247 	(void) fprintf(stderr,
5248 	    gettext("\t%s [opts] -l [set]\t\t\t"
5249 	    "enter logging mode\n"), program);
5250 
5251 	(void) fprintf(stderr,
5252 	    gettext("\t%s [opts] -m [set]\t\t\t"
5253 	    "full sync\n"), program);
5254 
5255 	(void) fprintf(stderr,
5256 	    gettext("\t%s [opts] -m -r [set]\t\t"
5257 	    "full reverse sync\n"), program);
5258 
5259 	(void) fprintf(stderr,
5260 	    gettext("\t%s [opts] -P [set]\t\t\t"
5261 	    "print sets verbose\n"), program);
5262 
5263 	(void) fprintf(stderr,
5264 	    gettext("\t%s [opts] -p [set]\t\t\t"
5265 	    "print sets\n"), program);
5266 
5267 	(void) fprintf(stderr,
5268 	    gettext("\t%s [opts] -R\t\t\t"
5269 	    "reset error conditions\n"), program);
5270 
5271 	(void) fprintf(stderr,
5272 	    gettext("\t%s [opts] -R b p <bitmap> [set]\t"
5273 	    "reconfig primary bitmap\n"), program);
5274 
5275 	(void) fprintf(stderr,
5276 	    gettext("\t%s [opts] -R b s <bitmap> [set]\t"
5277 	    "reconfig secondary bitmap\n"), program);
5278 
5279 	if (clustered)
5280 		(void) fprintf(stderr,
5281 		    gettext("\t%s [opts] -R C <ctag> [set]\t"
5282 		    "reconfig cluster tag\n"), program);
5283 
5284 #ifdef _RDC_CAMPUS
5285 	(void) fprintf(stderr,
5286 	    gettext("\t%s [opts] -R d <pathname> [set]\t"
5287 	    "reconfig campus direct file\n"), program);
5288 #endif
5289 
5290 	(void) fprintf(stderr,
5291 	    gettext("\t%s [opts] -R -f <volset-file> \t"
5292 	    "reconfig from file\n"), program);
5293 
5294 	(void) fprintf(stderr,
5295 	    gettext("\t%s [opts] -R g <group> [set]\t"
5296 	    "reconfig group\n"), program);
5297 
5298 	(void) fprintf(stderr,
5299 	    gettext("\t%s [opts] -R m {sync|async} [set]\t"
5300 	    "reconfig mode\n"), program);
5301 
5302 	if (allow_role)
5303 		(void) fprintf(stderr,
5304 		    gettext("\t%s [opts] -R r [set]\t\t"
5305 		    "reverse roles\n"), program);
5306 
5307 	(void) fprintf(stderr,
5308 	    gettext("\t%s [opts] -u [set]\t\t\t"
5309 	    "update sync\n"), program);
5310 
5311 	(void) fprintf(stderr,
5312 	    gettext("\t%s [opts] -u -r [set]\t\t"
5313 	    "update reverse sync\n"), program);
5314 
5315 	(void) fprintf(stderr,
5316 	    gettext("\t%s -v\t\t\t\tdisplay version\n"), program);
5317 
5318 	(void) fprintf(stderr,
5319 	    gettext("\t%s [opts] -W <maxwrites> [set]\t"
5320 	    "set maximum writes to queue\n"), program);
5321 
5322 	(void) fprintf(stderr,
5323 	    gettext("\t%s [opts] -w [set]\t\t\t"
5324 	    "wait\n"), program);
5325 	q_usage(0);
5326 
5327 	(void) fprintf(stderr, gettext("\nopts:\n"));
5328 	(void) fprintf(stderr, gettext("\t-n\t\tnon-interactive mode "
5329 	    "(not valid for print operations)\n"));
5330 	(void) fprintf(stderr, gettext(
5331 	    "\t-g <group>\toperate on sets in group only "
5332 	    "(not valid for enable\n\t\t\toperations)\n"));
5333 	if (clustered)
5334 		(void) fprintf(stderr,
5335 			gettext("\t-C <ctag>\tignore sets not in cluster ctag "
5336 		"(not valid for enable\n\t\t\toperations)\n"));
5337 
5338 	(void) fprintf(stderr, gettext("\nset:\n"));
5339 	if (clustered)
5340 		(void) fprintf(stderr,
5341 		    gettext("\t<phost> <pdev> <pbmp> "
5342 #ifdef _RDC_CAMPUS
5343 		    "<shost> <sdev> <sbmp> {ip | <directfile>} "
5344 #else
5345 		    "<shost> <sdev> <sbmp> ip "
5346 #endif
5347 		    "\\\n\t\t{sync | async} [g <group>] [q <qdev>] "
5348 		    "[C <ctag>]\n"));
5349 	else
5350 		(void) fprintf(stderr,
5351 		    gettext("\t<phost> <pdev> <pbmp> "
5352 #ifdef _RDC_CAMPUS
5353 		    "<shost> <sdev> <sbmp> {ip | <directfile>} "
5354 #else
5355 		    "<shost> <sdev> <sbmp> ip "
5356 #endif
5357 		    "\\\n\t\t{sync | async} [g <group>] [q <qdev>]\n"));
5358 	(void) fprintf(stderr,
5359 	    gettext("\t<shost>:<sdev>\t\t"
5360 	    "operate on set matching shost and sdev\n"));
5361 	(void) fprintf(stderr,
5362 	    gettext("\t-f volset-file\t\t"
5363 	    "operate on all sets specified in config file\n"
5364 	    "\t\t\t\tnote: not valid for single set operations. See\n"
5365 	    "\t\t\t\t%s(1RDC).\n"), program);
5366 	(void) fprintf(stderr,
5367 	    gettext("\t<no arg>\t\toperate on all configured sets\n"));
5368 }
5369 
5370 int
5371 prompt_user(int flag, int options)
5372 {
5373 	int c;
5374 
5375 	switch (flag) {
5376 	case RDC_CMD_DISABLE:
5377 		(void) printf(gettext("Disable Remote Mirror? (Y/N) [N]: "));
5378 		break;
5379 	case RDC_CMD_ENABLE:
5380 		(void) printf(gettext("Enable Remote Mirror? (Y/N) [N]: "));
5381 		break;
5382 	case RDC_CMD_HEALTH:
5383 		(void) printf(gettext("Report Remote Mirror link health? (Y/N)"
5384 		    "[N]: "));
5385 		break;
5386 	case RDC_CMD_COPY:
5387 		if (options & RDC_OPT_FULL) {
5388 			if (options & RDC_OPT_REVERSE)
5389 				(void) printf(gettext("Overwrite primary with"
5390 				    " secondary? (Y/N) [N]: "));
5391 			else
5392 				(void) printf(gettext("Overwrite secondary with"
5393 				    " primary? (Y/N) [N]: "));
5394 		} else {
5395 			if (options & RDC_OPT_REVERSE)
5396 				(void) printf(gettext("Refresh primary with"
5397 				    " secondary? (Y/N) [N]: "));
5398 			else
5399 				(void) printf(gettext("Refresh secondary with"
5400 				    " primary? (Y/N) [N]: "));
5401 		}
5402 		break;
5403 	case RDC_CMD_RECONFIG:
5404 		(void) printf(gettext(
5405 		    "Perform Remote Mirror reconfiguration? (Y/N) [N]: "));
5406 		break;
5407 	case RDC_CMD_RESET:
5408 		(void) printf(gettext("Perform Remote Mirror reset? (Y/N) "
5409 		    "[N]: "));
5410 		break;
5411 	case RDC_CMD_TUNABLE:
5412 		(void) printf(gettext("Change Remote Mirror tunable? (Y/N) "
5413 		    "[N]: "));
5414 		break;
5415 	case RDC_CMD_LOG:
5416 		(void) printf(gettext(
5417 		    "Put Remote Mirror into logging mode? (Y/N) [N]: "));
5418 		break;
5419 	case RDC_CMD_WAIT:
5420 		(void) printf(gettext(
5421 		    "Wait for Remote Mirror sync completion? (Y/N) [N]: "));
5422 		break;
5423 	default:
5424 		(void) printf(gettext("Perform Remote Mirror operation? (Y/N) "
5425 		    "[N]: "));
5426 	}
5427 
5428 	c = getchar();
5429 	if ((c != 'y') && (c != 'Y')) {
5430 		(void) printf("\n");
5431 		return (-1);
5432 	}
5433 	return (0);
5434 }
5435 
5436 static void
5437 load_rdc_vols(CFGFILE *cfg)
5438 {
5439 	int set;
5440 	char key[ CFG_MAX_KEY ];
5441 	char buf[ CFG_MAX_BUF ];
5442 	_sd_dual_pair_t pair;
5443 	char *vol, *bmp;
5444 	char *host1 = pair.fhost, *host2 = pair.thost;
5445 	char *diskqueue = pair.diskqueue;
5446 	volcount_t *volcount;
5447 	char lghn[ MAX_RDC_HOST_SIZE ];
5448 
5449 	if (volhash) {
5450 		return;
5451 	}
5452 
5453 	cfg_rewind(cfg, CFG_SEC_CONF);
5454 	volhash = nsc_create_hash();
5455 	for (set = 1; /*CSTYLED*/; set++) {
5456 		(void) snprintf(key, CFG_MAX_KEY, "sndr.set%d", set);
5457 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF)) {
5458 			break;
5459 		}
5460 
5461 		if (parse_cfg_buf(buf, &pair, lghn))
5462 			continue;
5463 		vol = pair.ffile;
5464 		bmp = pair.fbitmap;
5465 
5466 		/* use lghn if possible */
5467 		if (*lghn) {
5468 			if (strcmp(host2, lghn) == 0) {
5469 				vol = pair.tfile;
5470 				bmp = pair.tbitmap;
5471 			}
5472 		} else if (!self_check(host1)) {
5473 			/* next one had better be ours */
5474 			vol = pair.tfile;
5475 			bmp = pair.tbitmap;
5476 
5477 			if (!self_check(host2)) {
5478 				rdc_warn(NULL,
5479 				    gettext("config error: neither %s nor %s"
5480 				    " is localhost"), host1, host2);
5481 				continue;
5482 			}
5483 		}
5484 
5485 		/* primary vol may be used more than once */
5486 		volcount = (volcount_t *)nsc_lookup(volhash, vol);
5487 		if (volcount) {
5488 			volcount->count++;
5489 		} else {
5490 			volcount = (volcount_t *)malloc(sizeof (volcount_t));
5491 			volcount->count = 1;
5492 			(void) nsc_insert_node(volhash, volcount, vol);
5493 		}
5494 
5495 		/* bitmap ought to be only used once */
5496 		volcount = (volcount_t *)nsc_lookup(volhash, bmp);
5497 		if (volcount) {
5498 			/* argh */
5499 			volcount->count++;
5500 		} else {
5501 			volcount = (volcount_t *)malloc(sizeof (volcount_t));
5502 			volcount->count = 1;
5503 			(void) nsc_insert_node(volhash, volcount, bmp);
5504 		}
5505 
5506 		if (strcmp(diskqueue, place_holder) == 0)
5507 			continue;
5508 		/* diskqueue vol may be used more than once */
5509 		volcount = (volcount_t *)nsc_lookup(volhash, diskqueue);
5510 		if (volcount) {
5511 			volcount->count++;
5512 		} else {
5513 			volcount = (volcount_t *)malloc(sizeof (volcount_t));
5514 			volcount->count = 1;
5515 			(void) nsc_insert_node(volhash, volcount, diskqueue);
5516 		}
5517 	}
5518 }
5519 
5520 static void
5521 unload_rdc_vols()
5522 {
5523 	nsc_remove_all(volhash, free);
5524 	volhash = 0;
5525 }
5526 
5527 static int
5528 perform_autosv()
5529 {
5530 	if (!clustered) {
5531 		return (1);
5532 	} else {
5533 		return (cfg_issuncluster());
5534 	}
5535 }
5536 
5537 /*
5538  * Check the user supplied fields against those in the dscfg for
5539  * this set.
5540  * Never returns on an error.
5541  */
5542 static void
5543 checkgfields(CFGFILE *cfg, int setnumber, char *fromhost, char *fromfile,
5544     char *frombitmap, char *tobitmap, char *type, char *mode, char *group,
5545     char *ctag, char *diskq)
5546 {
5547 	if (fromhost[0])
5548 		checkgfield(cfg, setnumber, "phost",
5549 		    gettext("primary host"), fromhost);
5550 	if (fromfile[0])
5551 		checkgfield(cfg, setnumber, "primary",
5552 		    gettext("primary volume"), fromfile);
5553 	if (frombitmap[0])
5554 		checkgfield(cfg, setnumber, "pbitmap",
5555 		    gettext("primary bitmap"), frombitmap);
5556 	if (tobitmap[0])
5557 		checkgfield(cfg, setnumber, "sbitmap",
5558 		    gettext("secondary bitmap"), tobitmap);
5559 	if (type[0])
5560 		checkgfield(cfg, setnumber, "type",
5561 		    gettext("type of connection"), type);
5562 	if (mode[0])
5563 		checkgfield(cfg, setnumber, "mode",
5564 		    gettext("mode of connection"), mode);
5565 	if (group[0])
5566 		checkgfield(cfg, setnumber, "group",
5567 		    gettext("group"), group);
5568 	if (ctag[0])
5569 		checkgfield(cfg, setnumber, "cnode",
5570 		    gettext("cluster tag"), ctag);
5571 	if (diskq[0])
5572 		checkgfield(cfg, setnumber, "diskq",
5573 		    gettext("disk queue volume"), diskq);
5574 }
5575 
5576 /*
5577  * Check the 'fname' field in the dscfg file for set number 'setnumber'
5578  * If it does not match the user's data, 'data', then print the error
5579  * message using the friendly field name 'ufield'.
5580  * Never returns on an error.
5581  */
5582 static void
5583 checkgfield(CFGFILE *cfg, int setnumber, char *fname, char *ufield, char *data)
5584 {
5585 	char buf[CFG_MAX_BUF];
5586 	char key[CFG_MAX_KEY];
5587 
5588 	(void) snprintf(key, sizeof (key), "sndr.set%d.%s", setnumber, fname);
5589 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
5590 		rdc_err(NULL, gettext("unable to fetch data for key %s"),
5591 		    key);
5592 	}
5593 	if (strcmp(buf, data) != 0) {
5594 		rdc_err(NULL,
5595 		    gettext("the value specified for the %s field is not\nthe "
5596 		    "same as that contained within the configuration storage "
5597 		    "file for this set.\nYou specified \"%s\" "
5598 		    "expected \"%s\"."),
5599 		    ufield, data, buf);
5600 	}
5601 }
5602 
5603 /*
5604  * load and send the contents of the bitmap file to the kernel.
5605  */
5606 static int
5607 rdc_bitmapset(char *tohost, char *tofile, char *bitmap, int op,
5608     nsc_off_t offset)
5609 {
5610 	rdc_bitmap_op_t bmop;
5611 	int fd;
5612 	void *buffer;
5613 	int buffersz;
5614 	struct stat s;
5615 	int n;
5616 	int ret;
5617 	/*
5618 	 * open bitmap file for reading.
5619 	 */
5620 	if ((fd = open(bitmap, O_RDONLY)) < 0) {
5621 		rdc_warn(NULL, gettext("Unable to open bitmap file %s"),
5622 		    bitmap);
5623 		return (1);
5624 	}
5625 	(void) fstat(fd, &s);
5626 
5627 	if (S_ISREG(s.st_mode) == 0) {
5628 		rdc_warn(NULL, gettext("Bitmap %s is not a regular file"),
5629 		    bitmap);
5630 		(void) close(fd);
5631 		return (1);
5632 	}
5633 
5634 	if (op == 0) {
5635 		op = RDC_BITMAPOR;
5636 	}
5637 	/*
5638 	 * use the file size to allocate buffer. This
5639 	 * size should be a multiple of FBA, but don't check
5640 	 * it here.
5641 	 */
5642 	buffersz = s.st_size;
5643 	buffer = malloc(buffersz);
5644 	if (buffer == NULL) {
5645 		rdc_warn(NULL, gettext("Unable to allocate %d bytes "
5646 		    "for bitmap file %s"), buffersz, bitmap);
5647 		(void) close(fd);
5648 		return (1);
5649 	}
5650 	n = read(fd, buffer, buffersz);
5651 	(void) close(fd);
5652 	if (n != buffersz) {
5653 		rdc_warn(NULL, gettext("Unable to read the bitmap file, "
5654 		    "read returned %d instead of %d"),
5655 		    n, buffersz);
5656 		free(buffer);
5657 		return (1);
5658 	}
5659 	bmop.offset = offset;
5660 	bmop.op = op;
5661 	(void) strncpy(bmop.sechost, tohost, MAX_RDC_HOST_SIZE);
5662 	(void) strncpy(bmop.secfile, tofile, NSC_MAXPATH);
5663 	bmop.len = buffersz;
5664 	bmop.addr = (unsigned long)buffer;
5665 	ret = rdc_ioctl_simple(RDC_BITMAPOP, &bmop);
5666 	free(buffer);
5667 	if (ret < 0) {
5668 		rdc_warn(NULL, gettext("Setting bitmap ioctl failed for set "
5669 		    "%s:%s"), tohost, tofile);
5670 
5671 		switch (errno) {
5672 		case EIO:
5673 			rdc_warn(NULL, gettext("One of the sets is not "
5674 			    "enabled"));
5675 			break;
5676 		case ENXIO:
5677 			rdc_warn(NULL, gettext("One of the sets is not "
5678 			    "logging"));
5679 			break;
5680 		default:
5681 			break;
5682 		}
5683 	} else {
5684 		ret = 0;
5685 	}
5686 	if (ret)
5687 		ret = 1;
5688 	return (ret);
5689 }
5690 
5691 /*
5692  * verify_groupname: Check the group name for the following rules:
5693  *	1. The name does not start with a '-'
5694  *	2. The name does not contain any space characters as defined by
5695  *	   isspace(3C).
5696  *
5697  * If either of these rules are broken, error immediately.
5698  */
5699 static void
5700 verify_groupname(char *grp)
5701 {
5702 	int i;
5703 
5704 	if (grp[0] == '-') {
5705 		rdc_err(NULL, gettext("group name cannot start with a '-'"));
5706 	}
5707 
5708 	for (i = 0; grp[i] != '\0'; i++) {
5709 		if (isspace(grp[i])) {
5710 			rdc_err(NULL, gettext("group name cannot contain a "
5711 			    "space"));
5712 		}
5713 	}
5714 }
5715