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