xref: /titanic_44/usr/src/cmd/avs/rdc/sndrboot.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
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 
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <stdio.h>
30 #include <sys/mnttab.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <fcntl.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include <locale.h>
39 #include <langinfo.h>
40 #include <libintl.h>
41 #include <stdarg.h>
42 #include <netdb.h>
43 #include <ctype.h>
44 #include <sys/utsname.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 #include <sys/nsctl/cfg_cluster.h>
52 
53 #include <sys/unistat/spcs_s.h>
54 #include <sys/unistat/spcs_s_u.h>
55 #include <sys/unistat/spcs_errors.h>
56 
57 #include "rdcadm.h"
58 
59 /*
60  * Special re-use of sndrboot to fix SNDR set IDs during post-patch processing
61  */
62 #define	RDC_CMD_FIXSETIDS	0xFEEDFACE
63 
64 /*
65  * config file user level Dual copy pair structure
66  */
67 typedef struct _sd_dual_pair {
68 	char fhost[MAX_RDC_HOST_SIZE];	/* Hostname for primary device */
69 	char fnetaddr[RDC_MAXADDR];	/* Host netaddr for primary device */
70 	char ffile[NSC_MAXPATH];	/* Primary device */
71 	char fbitmap[NSC_MAXPATH];	/* Primary bitmap device */
72 	char thost[MAX_RDC_HOST_SIZE];	/* Hostname for secondary device */
73 	char tnetaddr[RDC_MAXADDR];	/* Host netaddr for secondary device */
74 	char tfile[NSC_MAXPATH];	/* Secondary device */
75 	char tbitmap[NSC_MAXPATH];	/* Secondary bitmap device */
76 	char directfile[NSC_MAXPATH];	/* Local FCAL direct IO volume */
77 	char diskqueue[NSC_MAXPATH];	/* Disk Queue volume */
78 	char group[NSC_MAXPATH];	/* Group name */
79 	char lhost[MAX_RDC_HOST_SIZE];  /* Logical hostname for cluster */
80 	int  doasync;			/* Device is in sync/async mode */
81 	int  setid;			/* unique setid of this set */
82 } _sd_dual_pair_t;
83 
84 #include <sys/socket.h>
85 #include <netinet/in.h>
86 #include <arpa/inet.h>
87 #include <netinet/tcp.h>
88 #include <rpc/rpc_com.h>
89 #include <rpc/rpc.h>
90 
91 #include <sys/nsctl/librdc.h>
92 
93 char *ctag = NULL;
94 
95 int parseopts(int, char **, int *);
96 static int rdc_operation(char *, char *, char *, char *, char *, char *, int,
97     char *, char *, char *, int, char *, int setid);
98 static int read_libcfg(int);
99 static void usage(void);
100 
101 extern char *basename(char *);
102 
103 int rdc_maxsets;
104 static _sd_dual_pair_t *pair_list;
105 char *program;
106 
107 struct netbuf svaddr;
108 struct netbuf *svp;
109 struct netconfig nconf;
110 struct netconfig *conf;
111 struct knetconfig knconf;
112 static int clustered = 0;
113 static int proto_test = 0;
114 
115 #ifdef lint
116 int
117 sndrboot_lintmain(int argc, char *argv[])
118 #else
119 int
120 main(int argc, char *argv[])
121 #endif
122 {
123 	char fromhost[MAX_RDC_HOST_SIZE];
124 	char tohost[MAX_RDC_HOST_SIZE];
125 	char fromfile[NSC_MAXPATH];
126 	char tofile[NSC_MAXPATH];
127 	char frombitmap[NSC_MAXPATH];
128 	char tobitmap[NSC_MAXPATH];
129 	char directfile[NSC_MAXPATH];
130 	char diskqueue[NSC_MAXPATH];
131 	char group[NSC_MAXPATH];
132 	char lhost[MAX_RDC_HOST_SIZE];
133 	int pairs;
134 	int pid;
135 	int flag = 0;
136 	int doasync;
137 	int rc;
138 	char *required;
139 	int setid;
140 
141 	(void) setlocale(LC_ALL, "");
142 	(void) textdomain("rdc");
143 
144 	program = basename(argv[0]);
145 
146 	rc = rdc_check_release(&required);
147 	if (rc < 0) {
148 		rdc_err(NULL,
149 		    gettext("unable to determine the current "
150 		    "Solaris release: %s\n"), strerror(errno));
151 	} else if (rc == FALSE) {
152 		rdc_err(NULL,
153 		    gettext("incorrect Solaris release (requires %s)\n"),
154 		    required);
155 	}
156 
157 	rdc_maxsets = rdc_get_maxsets();
158 	if (rdc_maxsets == -1) {
159 		spcs_log("sndr", NULL,
160 		    gettext("%s unable to get maxsets value from kernel"),
161 		    program);
162 
163 		rdc_err(NULL,
164 		    gettext("unable to get maxsets value from kernel"));
165 	}
166 
167 	pair_list = calloc(rdc_maxsets, sizeof (*pair_list));
168 	if (pair_list == NULL) {
169 		rdc_err(NULL,
170 		    gettext(
171 			"unable to allocate pair_list"
172 			" array for %d sets"),
173 			rdc_maxsets);
174 	}
175 
176 	if (parseopts(argc, argv, &flag))
177 		return (1);
178 	pairs = read_libcfg(flag);
179 
180 	if (flag == RDC_CMD_FIXSETIDS) {
181 		if (pairs) {
182 			spcs_log("sndr", NULL, gettext("Fixed %d Remote Mirror"
183 				    " set IDs"), pairs);
184 #ifdef DEBUG
185 			rdc_warn(NULL, gettext("Fixed %d Remote Mirror set "
186 				    "IDs"), pairs);
187 #endif
188 		}
189 		return (0);
190 	}
191 
192 	if (pairs == 0) {
193 #ifdef DEBUG
194 		rdc_err(NULL,
195 		    gettext("Config contains no dual copy sets"));
196 #else
197 		return (0);
198 #endif
199 	}
200 
201 	while (pairs--) {
202 		pid = fork();
203 		if (pid == -1) {		/* error forking */
204 			perror("fork");
205 			continue;
206 		}
207 
208 		if (pid > 0)		/* this is parent process */
209 			continue;
210 
211 /*
212  * At this point, this is the child process.  Do the operation
213  */
214 
215 		strncpy(fromfile,
216 			pair_list[pairs].ffile, NSC_MAXPATH);
217 		strncpy(tofile,
218 			pair_list[pairs].tfile, NSC_MAXPATH);
219 		strncpy(frombitmap,
220 			pair_list[pairs].fbitmap, NSC_MAXPATH);
221 		strncpy(fromhost,
222 			pair_list[pairs].fhost, MAX_RDC_HOST_SIZE);
223 		strncpy(tohost,
224 			pair_list[pairs].thost, MAX_RDC_HOST_SIZE);
225 		strncpy(tobitmap,
226 			pair_list[pairs].tbitmap, NSC_MAXPATH);
227 		strncpy(directfile,
228 			pair_list[pairs].directfile, NSC_MAXPATH);
229 		strncpy(diskqueue,
230 			pair_list[pairs].diskqueue, NSC_MAXPATH);
231 		strncpy(group,
232 			pair_list[pairs].group, NSC_MAXPATH);
233 		strncpy(lhost,
234 			pair_list[pairs].lhost, MAX_RDC_HOST_SIZE);
235 
236 		doasync = pair_list[pairs].doasync;
237 		setid = pair_list[pairs].setid;
238 		if (rdc_operation(fromhost, fromfile, frombitmap,
239 		    tohost, tofile, tobitmap, flag, directfile, group,
240 		    diskqueue, doasync, lhost, setid)
241 		    < 0) {
242 			exit(255);
243 		}
244 
245 		exit(0);
246 	}
247 
248 	while ((wait((int *)0) > 0))
249 		;
250 	return (0);
251 }
252 
253 static int
254 rdc_operation(fromhost, fromfile, frombitmap, tohost, tofile,
255     tobitmap, flag, directfile, group, diskqueue, doasync,
256     lhost, setid)
257 char *fromhost, *fromfile, *frombitmap;
258 char *tohost, *tofile, *tobitmap;
259 int flag, doasync;
260 char *directfile;
261 char *group, *diskqueue;
262 int setid;
263 char *lhost;
264 {
265 	const int getaddr = (flag == RDC_CMD_RESUME);
266 	const int rpcbind = !getaddr;
267 	rdc_config_t parms;
268 	int ret;
269 	spcs_s_info_t ustatus;
270 	struct hostent *hp;
271 	char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
272 	struct t_info tinfo;
273 	int i;
274 
275 	conf = &nconf;
276 	bzero(&fromname, MAXHOSTNAMELEN);
277 	bzero(&toname, MAXHOSTNAMELEN);
278 
279 	hp = gethost_byname(fromhost);
280 	if (hp == NULL) {
281 		spcs_log("sndr", NULL,
282 		    gettext("%s gethost_byname failed for %s"),
283 		    program, fromhost);
284 	}
285 	if (strcmp(hp->h_name, fromhost) == 0)
286 		strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
287 	else {
288 	for (i = 0; hp->h_aliases[i] != NULL; i++) {
289 		if (strcmp(hp->h_aliases[i], fromhost) == 0)
290 			strncpy(fromname, hp->h_aliases[i], MAXHOSTNAMELEN);
291 		}
292 	}
293 	if (fromname[0] == '\0') {
294 		spcs_log("sndr", NULL,
295 		    gettext("%s host %s is not local"),
296 		    program, fromhost);
297 		rdc_err(NULL, gettext("Host %s is not local"),
298 		    fromhost);
299 	}
300 	hp = gethost_byname(tohost);
301 	if (hp == NULL) {
302 		spcs_log("sndr", NULL,
303 		    gettext("%s gethost_byname failed for %s"),
304 		    program, tohost);
305 	}
306 	if (strcmp(hp->h_name, tohost) == 0)
307 		strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
308 	else {
309 	for (i = 0; hp->h_aliases[i] != NULL; i++) {
310 		if (strcmp(hp->h_aliases[i], tohost) == 0)
311 			strncpy(toname, hp->h_aliases[i], MAXHOSTNAMELEN);
312 		}
313 	}
314 	if (toname[0] == '\0') {
315 		spcs_log("sndr", NULL,
316 		    gettext("%s host %s is not local"),
317 		    program, tohost);
318 		rdc_err(NULL, gettext("Host %s is not local"),
319 		    tohost);
320 	}
321 
322 	if (self_check(fromname) && self_check(toname)) {
323 		spcs_log("sndr", NULL,
324 		    gettext("%s Both %s and %s are local"),
325 		    program, fromhost, tohost);
326 		rdc_err(NULL, gettext("Both %s and %s are local"),
327 		    fromhost, tohost);
328 	}
329 
330 	/*
331 	 * Now build up the address for each host including port and transport
332 	 */
333 	if (getaddr) {
334 		svp = get_addr(toname, RDC_PROGRAM, RDC_VERS_MIN,
335 			&conf, proto_test?NC_UDP: NULL, "rdc", &tinfo, rpcbind);
336 
337 		if (svp == NULL) {
338 #ifdef DEBUG
339 			(void) printf("get_addr failed for Ver 4 %s\n", toname);
340 #endif
341 			spcs_log("sndr", NULL,
342 			    gettext("%s get_addr failed for Ver 4"),
343 			    program);
344 			return (-1);
345 		}
346 		svaddr = *svp;
347 	} else {
348 		bzero(&svaddr, sizeof (svaddr));
349 	}
350 
351 	parms.rdc_set->secondary.addr.len = svaddr.len;
352 	parms.rdc_set->secondary.addr.maxlen = svaddr.maxlen;
353 	parms.rdc_set->secondary.addr.buf = (void *)svaddr.buf;
354 
355 #ifdef DEBUG_ADDR
356 	(void) fprintf(stderr,
357 		"secondary buf %x len %d\n", svaddr.buf, svaddr.len);
358 
359 	for (i = 0; i < svaddr.len; i++)
360 		(void) printf("%u ", svaddr.buf[i]);
361 	(void) printf("\n");
362 #endif
363 
364 	if (getaddr) {
365 		svp = get_addr(fromname, RDC_PROGRAM, RDC_VERS_MIN,
366 			&conf, proto_test?NC_UDP: NULL, "rdc", &tinfo, rpcbind);
367 		if (svp == NULL) {
368 #ifdef DEBUG
369 			(void) printf("get_addr failed for Ver 4 %s\n",
370 			    fromname);
371 #endif
372 			return (-1);
373 		}
374 		svaddr = *svp;
375 	} else {
376 		;
377 		/*EMPTY*/
378 	}
379 	parms.rdc_set->primary.addr.len = svaddr.len;
380 	parms.rdc_set->primary.addr.maxlen = svaddr.maxlen;
381 	parms.rdc_set->primary.addr.buf =
382 				(void *)svaddr.buf;
383 
384 #ifdef DEBUG_ADDR
385 	(void) fprintf(stderr, "primary buf %x len %d\n",
386 	    svaddr.buf, svaddr.len);
387 	for (i = 0; i < svaddr.len; i++)
388 		(void) printf("%u ", svaddr.buf[i]);
389 	(void) printf("\n");
390 #endif
391 
392 	if (getaddr) {
393 		(void) convert_nconf_to_knconf(conf, &knconf);
394 #ifdef DEBUG_ADDR
395 		(void) printf("knconf %x %s %s %x\n", knconf.knc_semantics,
396 		    knconf.knc_protofmly, knconf.knc_proto, knconf.knc_rdev);
397 #endif
398 		parms.rdc_set->netconfig = &knconf;
399 	} else {
400 		parms.rdc_set->netconfig = NULL;
401 	}
402 	if (!clustered && !self_check(fromname) && !self_check(toname)) {
403 		spcs_log("sndr", NULL,
404 		    gettext("%s Neither %s nor %s is local"),
405 		    program, fromhost, tohost);
406 		rdc_err(NULL, gettext("Neither %s nor %s is local"),
407 		    fromhost, tohost);
408 	}
409 	strncpy(parms.rdc_set->primary.intf, fromhost, MAX_RDC_HOST_SIZE);
410 	strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH);
411 	strncpy(parms.rdc_set->primary.bitmap, frombitmap, NSC_MAXPATH);
412 
413 	strncpy(parms.rdc_set->secondary.intf, tohost, MAX_RDC_HOST_SIZE);
414 	strncpy(parms.rdc_set->secondary.file, tofile, NSC_MAXPATH);
415 	strncpy(parms.rdc_set->secondary.bitmap, tobitmap, NSC_MAXPATH);
416 
417 	strncpy(parms.rdc_set->group_name, group, NSC_MAXPATH);
418 	strncpy(parms.rdc_set->disk_queue, diskqueue, NSC_MAXPATH);
419 
420 	parms.rdc_set->maxqfbas = maxqfbas;
421 	parms.rdc_set->maxqitems = maxqitems;
422 	parms.rdc_set->autosync = autosync;
423 	parms.rdc_set->asyncthr = asyncthr;
424 	parms.rdc_set->setid = setid;
425 
426 	/* gethostid(3c) is defined to return a 32bit value */
427 	parms.rdc_set->syshostid = (int32_t)gethostid();
428 
429 	parms.command = 0;
430 	parms.options = 0;
431 	parms.command = flag;
432 
433 	if (flag == RDC_CMD_RESUME) {
434 		if (doasync)
435 			parms.options |= RDC_OPT_ASYNC;
436 		else
437 			parms.options |= RDC_OPT_SYNC;
438 	}
439 	if (clustered) {
440 		if (!ctag)
441 			goto noconfig;
442 		if (strcmp(ctag, "-") == 0)
443 			goto noconfig;
444 
445 #ifdef DEBUG
446 		(void) fprintf(stderr, "logical hostname: %s\n", lhost);
447 #endif
448 
449 		if (strcmp(lhost, fromname) == 0) {
450 			parms.options |= RDC_OPT_PRIMARY;
451 			strncpy(parms.rdc_set->direct_file, directfile,
452 				NSC_MAXPATH);
453 
454 		} else {
455 			parms.options |= RDC_OPT_SECONDARY;
456 			parms.rdc_set->direct_file[0] = 0; /* no fcal direct */
457 		}
458 	} else {
459 noconfig:
460 		/*
461 		 * If not clustered, don't resume sndr sets with lhost
462 		 */
463 		if ((flag == RDC_CMD_RESUME) && lhost && strlen(lhost))
464 			return (0);
465 
466 		if (self_check(fromname)) {
467 			parms.options |= RDC_OPT_PRIMARY;
468 			strncpy(parms.rdc_set->direct_file, directfile,
469 				NSC_MAXPATH);
470 		} else {
471 			parms.options |= RDC_OPT_SECONDARY;
472 			parms.rdc_set->direct_file[0] = 0; /* no fcal direct */
473 		}
474 	}
475 
476 	ustatus = spcs_s_ucreate();
477 
478 	errno = 0;
479 	ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
480 	if (ret != SPCS_S_OK) {
481 
482 		/* Surpress error messages for suspend on cluster elements */
483 		if ((flag == RDC_CMD_SUSPEND) && (errno == RDC_EALREADY) &&
484 			!clustered && lhost && strlen(lhost)) {
485 			spcs_s_ufree(&ustatus);
486 			return (0);
487 		}
488 
489 		(void) fprintf(stderr,
490 			gettext("Remote Mirror: %s %s %s %s %s %s\n"),
491 			fromhost, fromfile,
492 			frombitmap, tohost, tofile, tobitmap);
493 
494 		if (errno == RDC_EEINVAL) {
495 			spcs_log("sndr", NULL,
496 			    gettext("%s %s %s %s %s %s %s %s\n%s"),
497 			    program, rdc_decode_flag(flag, parms.options),
498 			    fromhost, fromfile, frombitmap,
499 			    tohost, tofile, tobitmap,
500 			    gettext("invalid command option"));
501 			rdc_err(&ustatus,
502 			    gettext("Remote Mirror: invalid command option "
503 				    "'%s'"), rdc_decode_flag(flag,
504 				    parms.options));
505 		} else {
506 			spcs_log("sndr", &ustatus,
507 			    gettext("%s %s %s %s %s %s %s %s"),
508 			    program, rdc_decode_flag(flag, parms.options),
509 			    fromhost, fromfile, frombitmap,
510 			    tohost, tofile, tobitmap);
511 			rdc_err(&ustatus, 0);
512 		}
513 	}
514 
515 	spcs_log("sndr", NULL,
516 	    gettext("%s %s %s %s %s %s %s %s\nSuccessful"),
517 	    program, rdc_decode_flag(flag, parms.options),
518 	    fromhost, fromfile, frombitmap, tohost, tofile, tobitmap);
519 
520 	spcs_s_ufree(&ustatus);
521 	return (0);
522 }
523 /*
524  * assign setid's to any existing
525  * sets without setids, making sure of course NOT to re-use a setid
526  */
527 int
528 update_setids(CFGFILE *cfg, int *no_id, int highest)
529 {
530 	int setid;
531 	char buf[CFG_MAX_BUF];
532 	char key[CFG_MAX_KEY];
533 	char *ctag;
534 
535 	/* If in a Sun Cluster, SetIDs need to have a ctag */
536 	if ((ctag = cfg_get_resource(cfg)) != NULL) {
537 		ctag = strdup(ctag);
538 		cfg_resource(cfg, "setid-ctag");
539 	}
540 
541 	/*
542 	 * Paranoia. IF there are any sets with setids, we don't
543 	 * want to re-use their number.
544 	 */
545 	if (highest > get_new_cfg_setid(cfg)) {
546 		bzero(&buf, sizeof (buf));
547 		(void) sprintf(buf, "%d", highest);
548 		if (cfg_put_cstring(cfg, "setid.set1.value", buf,
549 		    sizeof (buf)) < 0)
550 			rdc_warn(NULL, gettext("sndrboot: Unable to store "
551 			    "new setid"));
552 	}
553 
554 	for (setid = 0; no_id[setid]; setid++) {
555 		bzero(&buf, sizeof (buf));
556 		bzero(&key, sizeof (key));
557 		(void) sprintf(buf, "%d", get_new_cfg_setid(cfg));
558 		(void) sprintf(key, "sndr.set%d.options", no_id[setid]);
559 		if (cfg_put_options(cfg, CFG_SEC_CONF, key, "setid", buf) < 0)
560 			rdc_warn(NULL, gettext("sndrboot: Unable to store "
561 			    "unique setid"));
562 
563 		pair_list[no_id[setid] - 1].setid = atoi(buf);
564 	}
565 
566 	/* Restore old ctag if in a Sun Cluster */
567 	if (ctag) {
568 		cfg_resource(cfg, ctag);
569 		free(ctag);
570 	}
571 
572 	if (cfg_commit(cfg) < 0)
573 		rdc_err(NULL, gettext("sndrboot: Failed to commit setids"));
574 
575 	return (setid);
576 }
577 
578 /*
579  * this is called when the option lghn is no available in libdscfg
580  * that should only happen on an upgrade.
581  * cfg write lock must be held across this function
582  */
583 char *
584 get_lghn(CFGFILE *cfg, char *ctag, int setnum, int flag)
585 {
586 	FILE *pipe;
587 	char rsgrp[SCCONF_MAXSTRINGLEN];
588 	char cmd[SCCONF_MAXSTRINGLEN];
589 	static char lhostname[MAX_RDC_HOST_SIZE];
590 	char key[CFG_MAX_KEY];
591 	int rc;
592 
593 	if (ctag == NULL)
594 		goto fail;
595 
596 	bzero(&lhostname, sizeof (lhostname));
597 
598 	(void) sprintf(rsgrp, "%s-stor-rg", ctag);
599 /* BEGIN CSTYLED */
600 	rc = snprintf(cmd, SCCONF_MAXSTRINGLEN,
601 	    "/usr/cluster/bin/scrgadm -pvv | fgrep HostnameList \
602 | fgrep %s | fgrep value | awk -F: '{ print $4 }'", rsgrp);
603 /* END CSTYLED */
604 
605 	if (rc < 0) {
606 		rdc_err(NULL, gettext("Error getting scrgadm output"));
607 	}
608 
609 	pipe = popen(cmd, "r");
610 
611 	if (pipe == NULL) {
612 		rdc_err(NULL, gettext("Error opening pipe"));
613 	}
614 	rc = fscanf(pipe, "%s", lhostname);
615 	(void) pclose(pipe);
616 
617 	if (rc != 1) {
618 		rdc_err(NULL, gettext("Unable to get logical host"));
619 	}
620 
621 	/* not really failing, but suspend does not have the config lock */
622 	if (flag == RDC_CMD_SUSPEND)
623 		goto fail;
624 
625 	bzero(&key, sizeof (key));
626 	(void) snprintf(key, sizeof (key), "sndr.set%d.options", setnum);
627 	if (cfg_put_options(cfg, CFG_SEC_CONF, key, "lghn", lhostname) < 0)
628 		rdc_warn(NULL, gettext("sndrboot: Unable to store logical "
629 		    "host name in configuration database"));
630 
631 	if (cfg_commit(cfg) < 0)
632 		rdc_err(NULL,
633 		    gettext("sndrboot: Failed to commit logical host name"));
634 
635 fail:
636 	return (lhostname);
637 
638 }
639 
640 /*
641  * read_libcfg()
642  *
643  * DESCRIPTION: Read the relevant config info via libcfg
644  *
645  * Outputs:
646  *	int i			Number of pairs of devices
647  *
648  * Side Effects: The 0 to i-1 entries in the pair_list are filled.
649  *
650  */
651 static int
652 read_libcfg(int flag)
653 {
654 	char fromhost[MAX_RDC_HOST_SIZE];
655 	char fromfile[NSC_MAXPATH];
656 	char frombitmap[NSC_MAXPATH];
657 	char tohost[MAX_RDC_HOST_SIZE];
658 	char tofile[NSC_MAXPATH];
659 	char tobitmap[NSC_MAXPATH];
660 	char directfile[NSC_MAXPATH];
661 	char diskqueue[NSC_MAXPATH];
662 	char group[NSC_MAXPATH];
663 	char lhost[MAX_RDC_HOST_SIZE];
664 	char sync[16];
665 	char setid[64];
666 	int doasync;
667 	CFGFILE *cfg;
668 	int i, j = 0;
669 	int rc;
670 	char buf[CFG_MAX_BUF];
671 	char key[CFG_MAX_KEY];
672 	char dummy[NSC_MAXPATH];
673 	int setnumber;
674 	int numsets;
675 	int highest = 0;
676 	char lghn[5];
677 	int *no_id;
678 
679 
680 	if ((cfg = cfg_open("")) == NULL)
681 		rdc_err(NULL, gettext("Error opening config"));
682 
683 	/*
684 	 * If RDC_CMD_FIXSETIDS, we were called during post-patch install
685 	 * Acquire a write-lock on the cfg_lock(), so the code can attempt
686 	 * to fix setIDs
687 	 */
688 	if (flag == RDC_CMD_FIXSETIDS) {
689 		if (!cfg_lock(cfg, CFG_WRLOCK))
690 			rdc_err(NULL, gettext("Error write locking config"));
691 		cfg_resource(cfg, NULL);
692 	} else {
693 		if (!cfg_lock(cfg, CFG_RDLOCK))
694 			rdc_err(NULL, gettext("Error locking config"));
695 		cfg_resource(cfg, ctag);
696 	}
697 
698 	if ((numsets = cfg_get_num_entries(cfg, "sndr")) < 0)
699 		rdc_err(NULL, gettext("Unable to get set info from config"));
700 
701 	no_id = (int *)calloc(numsets + 1, sizeof (int));
702 	if (!no_id)
703 		rdc_err(NULL, gettext("No memory"));
704 
705 
706 	(void) snprintf(lghn, sizeof (lghn), "lghn");
707 
708 	for (i = 0; i < rdc_maxsets; i++) {
709 		setnumber = i + 1;
710 
711 		bzero(buf, CFG_MAX_BUF);
712 		(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
713 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
714 			break;
715 
716 		rc = sscanf(buf, "%s %s %s %s %s %s %s %s %s %s %s %s",
717 		    fromhost, fromfile, frombitmap, tohost, tofile, tobitmap,
718 			directfile, sync, group, dummy, dummy, diskqueue);
719 		if (rc != 12)
720 			rdc_err(NULL, gettext("cfg input error (%d)"), rc);
721 
722 		if (strcmp(directfile, "ip") == 0)
723 			strcpy(directfile, "");
724 
725 		if (strcmp(group, "-") == 0)
726 			strcpy(group, "");
727 
728 		if (strcmp(diskqueue, "-") == 0)
729 			strcpy(diskqueue, "");
730 
731 		(void) snprintf(key, sizeof (key),
732 			"sndr.set%d.options", setnumber);
733 
734 		if (cfg_get_single_option(cfg, CFG_SEC_CONF, key,
735 			lghn, lhost, MAX_RDC_HOST_SIZE) < 0)
736 			strcpy(lhost,
737 			    get_lghn(cfg, ctag, setnumber, flag));
738 
739 		if (strcmp(sync, "sync") == 0)
740 			doasync = 0;
741 		else if (strcmp(sync, "async") == 0)
742 			doasync = 1;
743 		else {
744 			cfg_close(cfg);
745 			rdc_err(NULL,
746 			    gettext("Set %s:%s neither sync nor async"),
747 			    tohost, tofile);
748 		}
749 
750 		strncpy(pair_list[i].fhost, fromhost,
751 			MAX_RDC_HOST_SIZE);
752 		strncpy(pair_list[i].ffile, fromfile, NSC_MAXPATH);
753 		strncpy(pair_list[i].fbitmap, frombitmap, NSC_MAXPATH);
754 		strncpy(pair_list[i].thost, tohost, MAX_RDC_HOST_SIZE);
755 		strncpy(pair_list[i].tfile, tofile, NSC_MAXPATH);
756 		strncpy(pair_list[i].tbitmap, tobitmap, NSC_MAXPATH);
757 		strncpy(pair_list[i].directfile, directfile,
758 			NSC_MAXPATH);
759 		strncpy(pair_list[i].diskqueue, diskqueue,
760 			NSC_MAXPATH);
761 		strncpy(pair_list[i].group, group, NSC_MAXPATH);
762 		strncpy(pair_list[i].lhost, lhost, MAX_RDC_HOST_SIZE);
763 		pair_list[i].doasync = doasync;
764 
765 		if (cfg_get_single_option(cfg, CFG_SEC_CONF, key, "setid",
766 		    setid, sizeof (setid)) < 0) {
767 			no_id[j++] = setnumber;
768 		}
769 		pair_list[i].setid = atoi(setid);
770 
771 		if (pair_list[i].setid > highest)
772 			highest = pair_list[i].setid;
773 
774 		if (gethost_netaddrs(fromhost, tohost,
775 		    (char *)pair_list[i].fnetaddr,
776 		    (char *)pair_list[i].tnetaddr) < 0) {
777 			cfg_close(cfg);
778 			spcs_log("sndr", NULL,
779 			    gettext("%s unable to determine IP addresses "
780 			    "for hosts %s %s"), program, fromhost, tohost);
781 			rdc_err(NULL, gettext("unable to determine IP "
782 			    "addresses for hosts %s, %s"), fromhost, tohost);
783 		}
784 	}
785 	/*
786 	 * fix any lost set ids if possible, also deal with upgrade
787 	 */
788 	if (j > 0 && flag == RDC_CMD_FIXSETIDS) {
789 		(void) update_setids(cfg, no_id, highest);
790 		i = j;	/* Set number of fixups */
791 	}
792 	free(no_id);
793 	cfg_close(cfg);
794 	return (i);
795 }
796 
797 
798 int
799 parseopts(argc, argv, flag)
800 int argc;
801 char **argv;
802 int *flag;
803 {
804 	int  errflag = 0;
805 	char c;
806 	char inval = 0;
807 #ifdef DEBUG
808 	while ((c = getopt(argc, argv, "C:Urs")) != -1) {
809 #else
810 	while ((c = getopt(argc, argv, "C:rs")) != -1) {
811 #endif
812 		switch (c) {
813 		case 'C':
814 			clustered = TRUE;
815 			ctag = optarg;
816 			break;
817 #ifdef DEBUG
818 		case 'U':
819 			proto_test = 1;
820 			break;
821 #endif
822 		case 'r':
823 			if (*flag)
824 				inval = 1;
825 			*flag = RDC_CMD_RESUME;
826 			break;
827 		case 's':
828 			if (*flag)
829 				inval = 1;
830 			*flag = RDC_CMD_SUSPEND;
831 			break;
832 		case '?':
833 			errflag++;
834 		}
835 	}
836 
837 	/*
838 	 * Special fix to address no SetIds in AVS 3.1 to 3.2 install + patch
839 	 * Adjust set IDs, if someone invokes the following invalid command
840 	 *
841 	 *	/use/sbin/sndrboot -C post-patch-setids -r -s
842 	 *
843 	 * Command will be called in post-install of the patch containing fix
844 	 *
845 	 */
846 	if (clustered && (strcmp(ctag, "post-patch-setids") == 0) &&
847 	    *flag && inval) {
848 		*flag = RDC_CMD_FIXSETIDS;
849 		return (0);
850 	}
851 
852 	if (inval) {
853 		rdc_warn(NULL, gettext("Invalid argument combination"));
854 		errflag = 1;
855 	}
856 
857 	if (!*flag || errflag) {
858 		usage();
859 		return (-1);
860 	}
861 
862 	return (0);
863 }
864 
865 static void
866 usage()
867 {
868 	(void) fprintf(stderr, gettext("usage:\n"));
869 	(void) fprintf(stderr,
870 		gettext("\t%s -r [-C tag]\t\t"
871 			"resume\n"), program);
872 
873 	(void) fprintf(stderr,
874 		gettext("\t%s -s [-C tag]\t\t"
875 			"suspend\n"), program);
876 }
877