xref: /titanic_50/usr/src/cmd/avs/rdc/sndrsyncd.c (revision 3d729aecc03ea6ebb9bd5d56b8dccd24f57daa41)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <fcntl.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <pthread.h>
37 #include <thread.h>
38 
39 #include <locale.h>
40 #include <langinfo.h>
41 #include <libintl.h>
42 #include <stdarg.h>
43 
44 #include <sys/nsctl/rdc_io.h>
45 #include <sys/nsctl/rdc_ioctl.h>
46 #include <sys/nsctl/rdc_prot.h>
47 
48 #include <sys/nsctl/cfg.h>
49 
50 #include <sys/unistat/spcs_s.h>
51 #include <sys/unistat/spcs_s_u.h>
52 #include <sys/unistat/spcs_errors.h>
53 
54 #include <sys/nsctl/librdc.h>
55 
56 #include "rdcadm.h"
57 
58 
59 #define	RDCADM "/usr/sbin/sndradm"
60 #define	IIADM "/usr/sbin/iiadm"
61 
62 #define	UPDATE "update"
63 #define	NOUPDATE "noupdate"
64 
65 #define	RESYNC_SLEEP	(3 * 60)	/* Three minutes */
66 #define	MAIN_SLEEP	(5 * 60)	/* Five minutes */
67 #define	CFG_WAIT_SLEEP	(5)		/* 5 sec */
68 
69 #define	MAXHOSTS 1024
70 mutex_t cfglock = DEFAULTMUTEX;
71 #define	LOCKCFG() (void) mutex_lock(&cfglock);
72 #define	UNLOCKCFG() (void) mutex_unlock(&cfglock);
73 
74 typedef struct host_list_s {
75 	char *hosts[MAXHOSTS];
76 	int numhosts;
77 	int configured[MAXHOSTS];
78 	mutex_t hosts_mutex;
79 } host_list_t;
80 
81 host_list_t *host_list;
82 
83 extern char *basename(char *);
84 int rdc_maxsets;
85 char *program;
86 
87 static int clustered = 0;
88 
89 int isnewhost(char *host);
90 void *wait_sync_event();
91 void *wait_link_down(void *host);
92 void rdc_sync(char *tohost);
93 void remove_from_hostlist(char *host);
94 void sync_start(char *master);
95 void sync_complete(char *master);
96 void cleanup_hostlist();
97 void group_start(char *group);
98 void group_complete(char *group);
99 
100 
101 void
102 init_host_list(void)
103 {
104 	host_list = calloc(1, sizeof (host_list_t));
105 	if (host_list == NULL) {
106 		spcs_log("sndr", NULL,
107 		    gettext("host list not initialized, cannot run"));
108 		rdc_err(NULL, gettext("host list not initialized, cannot run"));
109 	}
110 	(void) mutex_init(&host_list->hosts_mutex, USYNC_THREAD, NULL);
111 }
112 
113 /* ARGSUSED */
114 #ifdef lint
115 void
116 sndrsyncd_lintmain(argc, argv)
117 #else
118 int
119 main(argc, argv)
120 #endif
121 int argc;
122 char **argv;
123 {
124 	rdc_status_t *rdc_info;
125 	int size;
126 	int i;
127 	pid_t pid;
128 	spcs_s_info_t ustatus;
129 	int rc, trc;
130 	int first = 0;
131 	char *required;
132 
133 	(void) setlocale(LC_ALL, "");
134 	(void) textdomain("rdc");
135 
136 	ustatus = spcs_s_ucreate();
137 
138 	program = basename(argv[0]);
139 
140 	init_host_list();
141 
142 	rc = rdc_check_release(&required);
143 	if (rc < 0) {
144 		rdc_err(NULL,
145 		    gettext("unable to determine the current "
146 		    "Solaris release: %s\n"), strerror(errno));
147 		/* NOTREACHED */
148 	} else if (rc == FALSE) {
149 		rdc_err(NULL,
150 		    gettext("incorrect Solaris release (requires %s)\n"),
151 		    required);
152 		/* NOTREACHED */
153 	}
154 
155 	clustered = cfg_iscluster();
156 	if (clustered < 0) {
157 		rdc_err(NULL, gettext("unable to ascertain environment"));
158 	}
159 
160 	rdc_maxsets = rdc_get_maxsets();
161 	if (rdc_maxsets == -1) {
162 		spcs_log("sndr", NULL,
163 		    gettext("%s: unable to get maxsets value from kernel"),
164 		    program);
165 		rdc_err(NULL,
166 		    gettext("unable to get maxsets value from kernel"));
167 	}
168 	size = sizeof (rdc_status_t) + (sizeof (rdc_set_t) * (rdc_maxsets - 1));
169 	rdc_info = malloc(size);
170 	if (rdc_info == NULL) {
171 		spcs_log("sndr", NULL,
172 		    gettext("%s: unable to allocate %ld bytes"),
173 		    program, size);
174 		rdc_err(NULL,
175 			gettext("unable to allocate %ld bytes"), size);
176 	}
177 	bzero(rdc_info, size);
178 
179 	rdc_info->nset = rdc_maxsets;
180 
181 	/*
182 	 * Fork off a child that becomes the daemon.
183 	 */
184 	if ((pid = fork()) > 0)
185 		exit(0);
186 	else if (pid < 0) {
187 		spcs_log("sndr", NULL,
188 		    gettext("%s: cannot fork: %s"),
189 		    program, strerror(errno));
190 		rdc_err(NULL, gettext("cannot fork: %s\n"),
191 		    strerror(errno));
192 	}
193 
194 	/*
195 	 * In child - become daemon.
196 	 */
197 
198 	for (i = 0; i < 3; i++)
199 		(void) close(i);
200 
201 	(void) open("/dev/console", O_WRONLY|O_APPEND);
202 	(void) dup(0);
203 	(void) dup(0);
204 	(void) close(0);
205 
206 	(void) setpgrp();
207 
208 	(void) setlocale(LC_ALL, "");
209 	(void) textdomain("rdc");
210 
211 	/* launch a thread to wait for sync start and sync stop events */
212 
213 	if ((trc = thr_create(NULL, 0, wait_sync_event, NULL,
214 	    THR_BOUND|THR_DETACHED, NULL)) != 0) {
215 		spcs_log("sndr", NULL,
216 		    gettext("%s: unable to create thread wait_sync_event"),
217 		    program);
218 		rdc_warn(NULL,
219 		    gettext("%s unable to create thread wait_sync_event"),
220 		    program);
221 	} else {
222 #ifdef DEBUG
223 		spcs_log("sndr", NULL,
224 		    gettext("%s: thread wait_sync_event started"), program);
225 #endif
226 		;
227 	}
228 
229 	for (;;) {
230 		if (!first) {
231 			first++;
232 			(void) sleep(15);
233 		} else
234 			(void) sleep(MAIN_SLEEP);
235 
236 		bzero(rdc_info, size);
237 		rdc_info->nset = rdc_maxsets;
238 		if (RDC_IOCTL(RDC_STATUS, rdc_info, 0, 0, 0, 0, ustatus)
239 		    != SPCS_S_OK) {
240 			spcs_log("sndr", &ustatus,
241 			    gettext("%s: status ioctl"),
242 			    program);
243 			rdc_warn(&ustatus, gettext("status ioctl"));
244 			continue;
245 		}
246 
247 		cleanup_hostlist(rdc_info); /* remove non-existent hosts */
248 
249 		/*
250 		 * Check all enabled sets to see if a new remote host has
251 		 * appeared.
252 		 */
253 		for (i = 0; i < rdc_maxsets; i++) {
254 			if (!(rdc_info->rdc_set[i].flags & RDC_ENABLED))
255 				continue;
256 			/* spawn a new thread for each new host found */
257 			if (isnewhost(rdc_info->rdc_set[i].secondary.intf)) {
258 				/*
259 				 * right now, we could be here before
260 				 * the database did the write for this set
261 				 * I could check the lock on the database
262 				 * but I am just going to give up some time here
263 				 * instead. Why do the allocations etc, etc
264 				 * if the set is enabled in the kernel and not
265 				 * in the config, we know that this set has the
266 				 * lock. Why bother adding more contention to
267 				 * the lock.
268 				 * this is a daemon, afterall. its got time
269 				 */
270 				(void) sleep(CFG_WAIT_SLEEP);
271 
272 				spcs_log("sndr", NULL,
273 				    gettext("%s: new host found (%s) starting "
274 				    "its autosync thread"), program,
275 				    rdc_info->rdc_set[i].secondary.intf);
276 
277 				trc = thr_create(NULL, 0, wait_link_down,
278 				    (void *) rdc_info->rdc_set[i].\
279 secondary.intf, THR_BOUND|THR_DETACHED, NULL);
280 
281 				if (trc != 0) {
282 					spcs_log("sndr", NULL,
283 					    gettext(
284 					    "%s create new autosync "
285 					    "thread failed"), program);
286 					rdc_warn(NULL, gettext(
287 					    "%s create new autosync "
288 					    "thread failed"), program);
289 				}
290 			}
291 		}
292 	}
293 	/* NOTREACHED */
294 }
295 
296 
297 /*
298  * The kernel wakes up this function every time it detects the link to the
299  * specified host has dropped.
300  */
301 void *
302 wait_link_down(void *thehost)
303 {
304 	char *host = (char *)thehost;
305 	char tmphost[MAX_RDC_HOST_SIZE] = { '\0' };
306 	spcs_s_info_t ustatus;
307 
308 	if (host)
309 		(void) strncpy(tmphost, host, MAX_RDC_HOST_SIZE);
310 
311 	ustatus = spcs_s_ucreate();
312 
313 	/* Never give up */
314 	for (;;) {
315 #ifdef DEBUG
316 		spcs_log("sndr", NULL,
317 		    gettext("%s: awaiting link down ioctl for %s"),
318 		    program, host[0] == '\0' ? tmphost : host);
319 #endif
320 		if (RDC_IOCTL(RDC_LINK_DOWN, host, 0, 0, 0, 0, ustatus)
321 		    != SPCS_S_OK) {
322 			spcs_log("sndr", &ustatus,
323 			    gettext("%s: link down ioctl"),
324 			    program);
325 			rdc_warn(&ustatus, gettext("link down ioctl"));
326 			continue;
327 		}
328 #ifdef DEBUG
329 
330 		spcs_log("sndr", NULL,
331 		    gettext("%s: received link down ioctl for %s"),
332 		    program, host[0] == '\0' ? tmphost : host);
333 #endif
334 		rdc_sync(host[0] == '\0' ? tmphost : host);
335 	}
336 	/* LINTED */
337 }
338 
339 
340 /*
341  * Called when the link to the specified host has dropped.
342  * For all Remote Mirror sets using the link that have autosync on,
343  * issue rdcadm -u commands until they complete successfully.
344  */
345 void
346 rdc_sync(char *tohost)
347 {
348 	rdc_set_t *rdc_set = NULL;
349 	int *sync_done = NULL;
350 	int sets = 0;
351 	int syncs_done = 0;
352 	char cmd[256];
353 	rdc_config_t parms = { 0 };
354 	spcs_s_info_t ustatus;
355 	int i;
356 	int setnumber;
357 	int numfound = 0;
358 	char buf[CFG_MAX_BUF];
359 	char key[CFG_MAX_KEY];
360 	CFGFILE *cfg = NULL;
361 	int size;
362 	int first = 0;
363 	int death = 0;
364 	int cfglocked = 0;
365 
366 	ustatus = spcs_s_ucreate();
367 
368 	size = sizeof (rdc_set_t) * rdc_maxsets;
369 	rdc_set = malloc(size);
370 	if (rdc_set == NULL) {
371 		spcs_log("sndr", NULL,
372 		    gettext("%s: unable to allocate %ld bytes"),
373 		    program, size);
374 		rdc_warn(NULL,
375 			gettext("unable to allocate %ld bytes"), size);
376 		goto done;
377 	}
378 	bzero(rdc_set, size);
379 	size = sizeof (int) * rdc_maxsets;
380 	sync_done = malloc(size);
381 	if (sync_done == NULL) {
382 		spcs_log("sndr", NULL,
383 		    gettext("%s: unable to allocate %ld bytes"),
384 		    program, size);
385 		rdc_warn(NULL,
386 			gettext("unable to allocate %ld bytes"), size);
387 		goto done;
388 	}
389 	bzero(sync_done, size);
390 
391 	/*
392 	 * Get all sndr entries with shost matching tohost, and save the
393 	 * details in an array.
394 	 */
395 	for (i = 0; i < rdc_maxsets; i++) {
396 		setnumber = i + 1;
397 		bzero(buf, sizeof (buf));
398 		bzero(key, sizeof (key));
399 
400 		(void) snprintf(key, sizeof (key), "sndr.set%d.shost",
401 		    setnumber);
402 
403 		if (!cfglocked) {
404 			LOCKCFG();
405 			if ((cfg = cfg_open(NULL)) == NULL) {
406 				spcs_log("sndr", NULL,
407 				    gettext("%s: error opening config"),
408 				    program);
409 
410 				rdc_warn(NULL,
411 				    gettext("error opening config"));
412 				UNLOCKCFG();
413 				goto done;
414 			}
415 
416 			if (!cfg_lock(cfg, CFG_RDLOCK)) {
417 				spcs_log("sndr", NULL,
418 				    gettext("%s: error locking config"),
419 				    program);
420 				rdc_warn(NULL, gettext("error locking config"));
421 				goto done;
422 			}
423 		}
424 
425 		cfglocked = 1;
426 
427 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
428 			if (numfound == 0) /* no matching hosts */
429 				death = 1; /* thread will exit */
430 			break;
431 		}
432 		if (strcmp(buf, tohost) != 0)
433 			continue;
434 
435 		numfound++;
436 		(void) strncpy(rdc_set[sets].secondary.intf, buf,
437 		    MAX_RDC_HOST_SIZE);
438 
439 		/* Got a matching entry */
440 
441 		(void) snprintf(key, sizeof (key), "sndr.set%d.phost",
442 		    setnumber);
443 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
444 			break;
445 		(void) strncpy(rdc_set[sets].primary.intf, buf,
446 		    MAX_RDC_HOST_SIZE);
447 
448 		(void) snprintf(key, sizeof (key), "sndr.set%d.primary",
449 		    setnumber);
450 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
451 			break;
452 		(void) strncpy(rdc_set[sets].primary.file, buf, NSC_MAXPATH);
453 
454 		(void) snprintf(key, sizeof (key), "sndr.set%d.secondary",
455 		    setnumber);
456 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
457 			break;
458 		(void) strncpy(rdc_set[sets].secondary.file, buf, NSC_MAXPATH);
459 
460 		parms.command = RDC_CMD_STATUS;
461 		bcopy((void *)(&rdc_set[sets]), (void *)(&parms.rdc_set[0]),
462 		    sizeof (rdc_set_t));
463 
464 		/*
465 		 * release cfg before diving into the kernel
466 		 * this prevents a possible deadlock when doing
467 		 * a reverse sync whick will wake up the sync_event
468 		 * thread which will try and iiadm -c and hang
469 		 * because we still have the cfg_lock. the timed
470 		 * wait cv in the kernel will fail the sync and things
471 		 * will undeadlock.
472 		 */
473 
474 		cfg_close(cfg);
475 		cfg = NULL;
476 		cfglocked = 0;
477 		UNLOCKCFG();
478 
479 		if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus) < 0) {
480 			continue;
481 		}
482 		if ((parms.rdc_set[0].autosync == 0) ||
483 		    (!(parms.rdc_set[0].flags & RDC_LOGGING))) {
484 			continue;
485 		}
486 
487 		/* Found a suitable set with autosync on, in logging mode */
488 		sets++;
489 	}
490 
491 	if (cfg) {
492 		cfg_close(cfg);
493 		cfg = NULL;
494 		UNLOCKCFG();
495 	}
496 
497 	if (sets == 0) {
498 #ifdef DEBUG
499 		spcs_log("sndr", NULL,
500 		    gettext("%s: no sets requiring autosync found for %s"),
501 		    program, tohost);
502 #endif
503 		if (death) {
504 			spcs_log("sndr", NULL,
505 			    gettext("%s: autosync thread stopping for %s "
506 			    "(host deconfigured)"), program, tohost);
507 		}
508 		goto done;
509 	}
510 
511 	/* Keep issuing rdcadm -u commands until they have all completed */
512 	for (;;) {
513 		if (!first)
514 			first++;
515 		else
516 			(void) sleep(RESYNC_SLEEP);
517 
518 		/* Issue rdcadm -u commands for all remaining sets */
519 		for (i = 0; i < sets; i++) {
520 			if (sync_done[i])
521 				continue;
522 
523 			/*
524 			 * Need to check if autosync was turned off for a set
525 			 * while we were sleeping. We could have the case where
526 			 * an update sync failed and autosync was disabled
527 			 * while we were sleeping and didn't detect the disable.
528 			 * See BugID 4814213.
529 			 */
530 			parms.command = RDC_CMD_STATUS;
531 			bcopy((void *)(&rdc_set[i]),
532 			    (void *)(&parms.rdc_set[0]), sizeof (rdc_set_t));
533 			if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0,
534 			    ustatus) < 0) {
535 				spcs_log("sndr", &ustatus, gettext("%s: "
536 				    "status not available for %s:%s, stopping "
537 				    "this autosync attempt"), program, tohost,
538 				    rdc_set[i].secondary.file);
539 				sync_done[i] = 1;
540 				syncs_done++;
541 				continue;
542 			}
543 			if (!(parms.rdc_set[0].autosync)) {
544 #ifdef DEBUG
545 	spcs_log("sndr", NULL, gettext("%s: autosync disabled during sleep, "
546 	    "stopping attempt for set %s:%s"), program, tohost,
547 	    rdc_set[i].secondary.file);
548 #endif
549 				sync_done[i] = 1;
550 				syncs_done++;
551 				continue;
552 			}
553 
554 			(void) sprintf(cmd, "%s -un %s:%s", RDCADM, tohost,
555 			    rdc_set[i].secondary.file);
556 			spcs_log("sndr", NULL,
557 			    gettext("%s: issuing update sync for %s:%s"),
558 			    program, tohost, rdc_set[i].secondary.file);
559 			(void) system(cmd);
560 		}
561 
562 		/* Issue rdcadm -w commands to wait for updates to finish */
563 		for (i = 0; i < sets; i++) {
564 			if (sync_done[i])
565 				continue;
566 
567 			(void) sprintf(cmd, "%s -wn %s:%s", RDCADM, tohost,
568 			    rdc_set[i].secondary.file);
569 			spcs_log("sndr", NULL,
570 			    gettext("%s: issuing wait for %s:%s"),
571 			    program, tohost, rdc_set[i].secondary.file);
572 
573 			(void) system(cmd);
574 
575 			parms.command = RDC_CMD_STATUS;
576 			bcopy((void *)(&rdc_set[i]),
577 			    (void *)(&parms.rdc_set[0]), sizeof (rdc_set_t));
578 
579 			if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0,
580 			    ustatus) < 0) {
581 				spcs_log("sndr", &ustatus,
582 				    gettext("%s: status not available for "
583 				    "%s:%s, stopping this autosync attempt"),
584 				    program, tohost, rdc_set[i].secondary.file);
585 				sync_done[i] = 1;
586 				syncs_done++;
587 				continue;
588 			}
589 			/* Check if completed OK, failed or autosync off */
590 			if (!(parms.rdc_set[0].autosync) ||
591 			    !(parms.rdc_set[0].flags & RDC_LOGGING) &&
592 			    !(parms.rdc_set[0].flags & RDC_SYNCING)) {
593 				sync_done[i] = 1;
594 				syncs_done++;
595 			}
596 		}
597 
598 		if (syncs_done == sets)
599 			break;		/* All completed OK */
600 	}
601 
602 done:
603 	if (cfg) {
604 		cfg_close(cfg);
605 		UNLOCKCFG();
606 	}
607 	spcs_s_ufree(&ustatus);
608 	if (sync_done)
609 		free(sync_done);
610 	if (rdc_set)
611 		free(rdc_set);
612 	if (death) { /* bye bye */
613 		/*
614 		 * if perhaps we lost some race, lets remove this entry from
615 		 * the list. Then, if something did go wrong, and we did kill
616 		 * a valid thread, it will be detected on the next go around
617 		 * of the thread who is looking for new hosts to spawn threads
618 		 */
619 
620 		remove_from_hostlist(tohost);
621 		thr_exit(0);
622 	}
623 
624 	(void) sleep(RESYNC_SLEEP);
625 }
626 
627 /*
628  * Wait for notification by the kernel of a sync start or a sync completed OK
629  */
630 void *
631 wait_sync_event()
632 {
633 	spcs_s_info_t ustatus;
634 	char master[NSC_MAXPATH];
635 	char group[NSC_MAXPATH];
636 	int state;
637 
638 	ustatus = spcs_s_ucreate();
639 
640 	master[0] = '\0';
641 	group[0] = '\0';
642 
643 	/* Never give up */
644 	for (;;) {
645 		/* Kernel tells us which volume and group the event is for */
646 		state = RDC_IOCTL(RDC_SYNC_EVENT, master, group, 0, 0, 0,
647 		    ustatus);
648 		if (state < SPCS_S_OK) {
649 			if (errno != EAGAIN) {
650 				spcs_log("sndr", &ustatus,
651 				    gettext("%s: update ioctl"),
652 				    program);
653 				rdc_warn(&ustatus, gettext("update ioctl"));
654 				continue;
655 			}
656 			master[0] = '\0';
657 			continue;
658 		}
659 
660 		/*
661 		 * If target is mounted at the start of a sync or reverse sync,
662 		 * return a negative ack.
663 		 */
664 		if ((state == RDC_SYNC_START || state == RDC_RSYNC_START) &&
665 		    mounted(master)) {
666 			spcs_log("sndr", NULL,
667 			    gettext("%s: %s has a file system mounted"),
668 			    program, master);
669 			rdc_warn(NULL,
670 			    gettext("%s has a file system mounted"),
671 			    master);
672 			master[0] = '\0';	/* negative ack */
673 			continue;
674 		}
675 
676 		switch (state) {
677 		case RDC_SYNC_START:
678 			if (group[0])
679 				group_start(group);
680 			else
681 				sync_start(master);
682 			break;
683 
684 		case RDC_SYNC_DONE:
685 			if (group[0])
686 				group_complete(group);
687 			else
688 				sync_complete(master);
689 			break;
690 
691 		default:
692 			break;
693 		}
694 	}
695 	/* LINTED */
696 }
697 
698 
699 /*
700  * A sync has completed OK to a volume not belonging to a group.
701  * Set the state of the ndr_ii config entry to "update".
702  */
703 void
704 sync_complete(char *master)
705 {
706 	CFGFILE *cfg = NULL;
707 	char buf[CFG_MAX_BUF];
708 	char key[CFG_MAX_KEY];
709 	int i;
710 	int setnumber;
711 	int sev;
712 
713 	LOCKCFG();
714 	if ((cfg = cfg_open(NULL)) == NULL) {
715 		spcs_log("sndr", NULL,
716 		    gettext("%s: error opening config"),
717 		    program);
718 		rdc_warn(NULL, gettext("error opening config"));
719 		UNLOCKCFG();
720 		return;
721 	}
722 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
723 		spcs_log("sndr", NULL,
724 		    gettext("%s: error locking config"),
725 		    program);
726 		rdc_warn(NULL, gettext("error locking config"));
727 		cfg_close(cfg);
728 		UNLOCKCFG();
729 		return;
730 	}
731 
732 	/* get ndr_ii entries until a match is found */
733 	for (i = 0; ; i++) {
734 		setnumber = i + 1;
735 
736 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.secondary",
737 		    setnumber);
738 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
739 			break;
740 		if (strcmp(buf, master) != 0)
741 			continue;
742 
743 		/* Found the matching entry */
744 
745 		/*
746 		 * Set state to "update" so that starting another sync will
747 		 * cause a new Point-in-Time Copy snapshot to be taken.
748 		 */
749 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.state",
750 		    setnumber);
751 		if ((cfg_put_cstring(cfg, key, UPDATE, strlen(UPDATE)) < 0) ||
752 		    (cfg_commit(cfg) < 0)) {
753 			spcs_log("sndr", NULL,
754 			    gettext("%s: unable to update \"%s\" "
755 			    "in configuration storage: %s"),
756 			    program, buf, cfg_error(&sev));
757 			rdc_warn(NULL,
758 			    gettext("unable to update \"%s\" "
759 			    "in configuration storage: %s"),
760 			    buf, cfg_error(&sev));
761 		}
762 		break;
763 	}
764 
765 	cfg_close(cfg);
766 	UNLOCKCFG();
767 }
768 
769 
770 /*
771  * Starting a sync to the specified master volume.
772  * Check the ndr_ii config entries to see if a Point-in-Time Copy
773  * snapshot should be taken.
774  */
775 void
776 sync_start(char *master)
777 {
778 	char cmd[256];
779 	char buf[CFG_MAX_BUF];
780 	char key[CFG_MAX_KEY];
781 	CFGFILE *cfg = NULL;
782 	int i;
783 	int setnumber;
784 	int found;
785 	int sev;
786 	char shadow[NSC_MAXPATH];
787 	char bitmap[NSC_MAXPATH];
788 	char *ctag = NULL;
789 
790 	LOCKCFG();
791 	if ((cfg = cfg_open(NULL)) == NULL) {
792 		spcs_log("sndr", NULL,
793 		    gettext("%s: error opening config"),
794 		    program);
795 		rdc_warn(NULL,
796 		    gettext("error opening config"));
797 		UNLOCKCFG();
798 		return;
799 	}
800 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
801 		spcs_log("sndr", NULL,
802 		    gettext("%s: error locking config"),
803 		    program);
804 		rdc_warn(NULL, gettext("error locking config"));
805 		cfg_close(cfg);
806 		UNLOCKCFG();
807 		return;
808 	}
809 
810 	found = 0;
811 	/* get ndr_ii entries until a match is found */
812 	for (i = 0; ; i++) {
813 		setnumber = i + 1;
814 
815 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.secondary",
816 		    setnumber);
817 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
818 			break;
819 		if (strcmp(buf, master) != 0)
820 			continue;
821 
822 		/* Got a matching entry */
823 
824 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.shadow",
825 		    setnumber);
826 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
827 			break;
828 		(void) strncpy(shadow, buf, NSC_MAXPATH);
829 
830 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.bitmap",
831 		    setnumber);
832 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
833 			break;
834 		(void) strncpy(bitmap, buf, NSC_MAXPATH);
835 
836 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.state",
837 		    setnumber);
838 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
839 			break;
840 
841 		/*
842 		 * If an PIT snapshot has already been taken, and syncing did
843 		 * not complete, the state will be "noupdate", to indicate we
844 		 * should not take another one at this point.
845 		 */
846 		if (strcmp(buf, NOUPDATE) != 0)
847 			found = 1;
848 
849 		break;
850 	}
851 
852 	if (!found) {
853 		cfg_close(cfg);
854 		UNLOCKCFG();
855 		return;
856 	}
857 
858 	found = 0;
859 	/* get ii entries until a match is found */
860 	for (i = 0; ; i++) {
861 		setnumber = i + 1;
862 
863 		(void) snprintf(key, sizeof (key), "ii.set%d.shadow",
864 		    setnumber);
865 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
866 			break;
867 		if (strcmp(buf, shadow) != 0)
868 			continue;
869 
870 		/* Matching shadow found, so ii already enabled */
871 		found = 1;
872 		break;
873 	}
874 
875 	if (found) {
876 		/* Already PIT enabled, so just take a snapshot */
877 
878 		/* Get cluster tag of matching entry */
879 		(void) snprintf(key, sizeof (key), "ii.set%d.cnode", setnumber);
880 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) >= 0)
881 			if ((strlen(buf) == 0) || (buf[0] == '-'))
882 					ctag = "-C local";
883 				else
884 					ctag = "";
885 		(void) sprintf(cmd, "%s %s -u s %s", IIADM, ctag, shadow);
886 	} else {
887 		/*
888 		 * If clustered, need to enable PIT Copy sets in the same
889 		 * cluster as the Remote Mirror set
890 		 */
891 
892 		if (clustered) {
893 			/* Find a RM set with master as the local volume */
894 
895 			for (i = 0; i < rdc_maxsets; i++) {
896 				setnumber = i + 1;
897 				(void) snprintf(key, sizeof (key),
898 				    "sndr.set%d.phost", setnumber);
899 				if (cfg_get_cstring(cfg, key, buf,
900 				    CFG_MAX_BUF) < 0)
901 					break;
902 
903 				if (self_check(buf))
904 					(void) snprintf(key, sizeof (key),
905 					    "sndr.set%d.primary", setnumber);
906 				else
907 					(void) snprintf(key, sizeof (key),
908 					    "sndr.set%d.secondary", setnumber);
909 				if (cfg_get_cstring(cfg, key, buf,
910 				    CFG_MAX_BUF) < 0)
911 					break;
912 
913 				if (strcmp(buf, master) != 0)
914 					continue;
915 
916 				/* Get cluster tag of matching entry */
917 
918 				(void) snprintf(key, sizeof (key),
919 				    "sndr.set%d.cnode", setnumber);
920 				if (cfg_get_cstring(cfg, key, buf,
921 				    CFG_MAX_BUF) < 0)
922 					break;
923 				if ((strlen(buf) == 0) || (buf[0] == '-'))
924 					ctag = strdup("local");
925 				else
926 					ctag = strdup(buf);
927 				break;
928 			}
929 		}
930 
931 		/* Not already enabled, so enable a dependent */
932 		if (ctag) {
933 			(void) sprintf(cmd, "%s -C %s -e dep %s %s %s", IIADM,
934 			    ctag, master, shadow, bitmap);
935 			free(ctag);
936 		} else
937 			(void) sprintf(cmd, "%s -e dep %s %s %s", IIADM, master,
938 			    shadow, bitmap);
939 	}
940 
941 	cfg_close(cfg);
942 
943 	if (system(cmd) != 0) {
944 		spcs_log("sndr", NULL,
945 		    gettext("Point-in-Time Copy snapshot failed for %s %s %s."
946 		    " Please check validity of ndr_ii entry"),
947 		    master, shadow, bitmap);
948 		cfg_close(cfg);
949 		UNLOCKCFG();
950 		return;
951 	}
952 
953 	/*
954 	 * PIT Copy enable or update was fine, so update the ndr_ii entry
955 	 * to "noupdate", to prevent invalid point in time copies.
956 	 */
957 
958 	if ((cfg = cfg_open(NULL)) == NULL) {
959 		spcs_log("sndr", NULL,
960 		    gettext("%s: error opening config"),
961 		    program);
962 		rdc_warn(NULL,
963 		    gettext("error opening config"));
964 		UNLOCKCFG();
965 		return;
966 	}
967 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
968 		spcs_log("sndr", NULL,
969 		    gettext("%s: error locking config"),
970 		    program);
971 		rdc_warn(NULL, gettext("error locking config"));
972 		cfg_close(cfg);
973 		UNLOCKCFG();
974 		return;
975 	}
976 
977 	/* get ndr_ii entries until a match is found */
978 	for (i = 0; ; i++) {
979 		setnumber = i + 1;
980 
981 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.shadow",
982 		    setnumber);
983 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
984 			break;
985 		if (strcmp(buf, shadow) != 0)
986 			continue;
987 
988 		/* Found the matching entry */
989 
990 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.state",
991 		    setnumber);
992 		if ((cfg_put_cstring(cfg, key, NOUPDATE,
993 			strlen(NOUPDATE)) < 0) || (cfg_commit(cfg) < 0)) {
994 			spcs_log("sndr", NULL,
995 			    gettext("%s: unable to update \"%s\" "
996 			    "in configuration storage: %s"),
997 			    program, buf, cfg_error(&sev));
998 			rdc_warn(NULL,
999 			    gettext("unable to update \"%s\" "
1000 			    "in configuration storage: %s"),
1001 			    buf, cfg_error(&sev));
1002 		}
1003 		break;
1004 	}
1005 	cfg_close(cfg);
1006 	UNLOCKCFG();
1007 }
1008 
1009 void
1010 cleanup_hostlist(rdc_status_t *rdc_info)
1011 {
1012 	int i, j, k;
1013 	char *host, *exhost;
1014 
1015 
1016 	(void) mutex_lock(&host_list->hosts_mutex);
1017 	for (i = 0; i < host_list->numhosts; i++) {
1018 		int found = 0;
1019 		for (j = 0; (j < rdc_maxsets) && !found; j++) {
1020 			if (!rdc_info->rdc_set[j].flags & RDC_ENABLED)
1021 				continue;
1022 			if ((!host_list->configured[i]) ||
1023 			    (host_list->hosts[i] == '\0')) {
1024 				(void) mutex_unlock(&host_list->hosts_mutex);
1025 				return;
1026 			}
1027 
1028 			host = rdc_info->rdc_set[j].secondary.intf;
1029 			if (strcmp(host_list->hosts[i], host) == 0)
1030 				found++;
1031 		}
1032 		if (j == rdc_maxsets) {
1033 			/*
1034 			 * this set is not in the kernel, so remove from list
1035 			 */
1036 			exhost = host_list->hosts[i];
1037 			if (exhost) {
1038 				free(exhost);
1039 				exhost = NULL;
1040 			}
1041 
1042 			k = i;
1043 			while (k < host_list->numhosts) {
1044 			    host_list->hosts[k] = k < host_list->numhosts - 1 ?
1045 				host_list->hosts[k+1] : NULL;
1046 			    k++;
1047 			}
1048 			host_list->numhosts--;
1049 
1050 			bcopy(&host_list->configured[i+1],
1051 			    &host_list->configured[i],
1052 			    (MAXHOSTS - i + 1) * sizeof (int));
1053 			host_list->configured[MAXHOSTS - 1] = 0;
1054 		}
1055 	}
1056 	(void) mutex_unlock(&host_list->hosts_mutex);
1057 }
1058 
1059 /*
1060  * explicity remove a host from the host list
1061  * also update the configured array
1062  * called in rdc_sync, just before exiting a thread.
1063  */
1064 void
1065 remove_from_hostlist(char *host)
1066 {
1067 	int i, k;
1068 	char *exhost;
1069 
1070 	/* why bother? */
1071 	if ((!host) || (host[0] == '\0'))
1072 		return;
1073 
1074 	(void) mutex_lock(&host_list->hosts_mutex);
1075 	for (i = 0; i < host_list->numhosts; i++) {
1076 		if (strcmp(host, host_list->hosts[i]) == 0) { /* found it */
1077 			exhost = host_list->hosts[i];
1078 			if (exhost) {
1079 				free(exhost);
1080 				exhost = NULL;
1081 			}
1082 			k = i;
1083 			while (k < host_list->numhosts) {
1084 			    host_list->hosts[k] = k < host_list->numhosts - 1 ?
1085 				host_list->hosts[k+1] : NULL;
1086 			    k++;
1087 			}
1088 			host_list->numhosts--;
1089 			bcopy(&host_list->configured[i+1],
1090 			    &host_list->configured[i],
1091 			    (MAXHOSTS - i + 1) * sizeof (int));
1092 			host_list->configured[MAXHOSTS - 1] = 0;
1093 		}
1094 
1095 	}
1096 	(void) mutex_unlock(&host_list->hosts_mutex);
1097 }
1098 /*
1099  * Check to see if this host isn't in our list, so needs a new rdcsyncd proc
1100  */
1101 int
1102 isnewhost(char *host)
1103 {
1104 	int i;
1105 	int new;
1106 
1107 	if (self_check(host)) {
1108 		return (0);
1109 	}
1110 
1111 	(void) mutex_lock(&host_list->hosts_mutex);
1112 	new = 1;
1113 	for (i = 0; i < MAXHOSTS; i++) {
1114 		if (host_list->configured[i] == 0) {
1115 			host_list->configured[i] = 1;
1116 			host_list->hosts[i] = strdup(host);
1117 			host_list->numhosts++;
1118 			break;
1119 		}
1120 		if (strcmp(host, host_list->hosts[i]) == 0) {
1121 			new = 0;
1122 			break;
1123 		}
1124 	}
1125 	(void) mutex_unlock(&host_list->hosts_mutex);
1126 	if (i == MAXHOSTS)
1127 		new = 0;
1128 	return (new);
1129 }
1130 
1131 
1132 /*
1133  * Look for a matching volume name in our remembered list.
1134  */
1135 int
1136 volume_match(char *buf, char **volume_list, int volumes)
1137 {
1138 	int i;
1139 	char *vol;
1140 
1141 	for (i = 0; i < volumes; i++) {
1142 		vol = volume_list[i];
1143 		if (strcmp(buf, vol) == 0) {
1144 			return (1);
1145 		}
1146 	}
1147 	return (0);
1148 }
1149 
1150 
1151 /*
1152  * A sync has completed to a group. We can only update the ndr_ii entries
1153  * if all the members of the group have completed their syncs OK.
1154  * It would be bad to allow some members of the group to have PIT Copy snapshots
1155  * taken and others not, as they need to be consistent.
1156  */
1157 void
1158 group_complete(char *group)
1159 {
1160 	char **volumes = NULL;
1161 	spcs_s_info_t ustatus;
1162 	rdc_config_t parms = { 0 };
1163 	char buf[CFG_MAX_BUF];
1164 	char key[CFG_MAX_KEY];
1165 	CFGFILE *cfg = NULL;
1166 	int i;
1167 	int setnumber;
1168 	int found;
1169 	int replicating = 0;
1170 	char primary[NSC_MAXPATH];
1171 	char secondary[NSC_MAXPATH];
1172 	char phost[MAX_RDC_HOST_SIZE];
1173 	char shost[MAX_RDC_HOST_SIZE];
1174 	rdc_set_t *rdc_set;
1175 	int sev;
1176 	char *local_file;
1177 	int size;
1178 
1179 	ustatus = spcs_s_ucreate();
1180 
1181 	size = sizeof (char *) * rdc_maxsets;
1182 	volumes = malloc(size);
1183 	if (volumes == NULL) {
1184 		spcs_log("sndr", NULL,
1185 		    gettext("%s: unable to allocate %ld bytes"),
1186 		    program, size);
1187 		rdc_warn(NULL,
1188 			gettext("unable to allocate %ld bytes"), size);
1189 		goto done;
1190 	}
1191 	bzero(volumes, size);
1192 
1193 	/*
1194 	 * If all members of this group are replicating
1195 	 * set ii_ndr state to "update". Otherwise leave them alone.
1196 	 */
1197 	LOCKCFG();
1198 	if ((cfg = cfg_open(NULL)) == NULL) {
1199 		spcs_log("sndr", NULL,
1200 		    gettext("%s: error opening lconfig"),
1201 		    program);
1202 		rdc_warn(NULL, gettext("error opening config"));
1203 		UNLOCKCFG();
1204 		goto done;
1205 	}
1206 
1207 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
1208 		spcs_log("sndr", NULL,
1209 		    gettext("%s: error locking config"),
1210 		    program);
1211 		rdc_warn(NULL, gettext("error locking config"));
1212 		goto done;
1213 	}
1214 
1215 	found = 0;
1216 
1217 	/* get all RM entries, with a matching group, that are replicating */
1218 	for (i = 0; i < rdc_maxsets; i++) {
1219 		setnumber = i + 1;
1220 
1221 		(void) snprintf(key, sizeof (key),
1222 		    "sndr.set%d.group", setnumber);
1223 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1224 			break;
1225 
1226 		if (strcmp(buf, group) != 0)
1227 			continue;
1228 
1229 		/* Found a matching entry */
1230 
1231 		(void) snprintf(key, sizeof (key),
1232 		    "sndr.set%d.primary", setnumber);
1233 		if (cfg_get_cstring(cfg, key, primary, sizeof (primary)) < 0)
1234 			break;
1235 		(void) strcpy(parms.rdc_set->primary.file, primary);
1236 
1237 		(void) snprintf(key, sizeof (key),
1238 		    "sndr.set%d.phost", setnumber);
1239 		if (cfg_get_cstring(cfg, key, phost, sizeof (phost)) < 0)
1240 			break;
1241 		(void) strcpy(parms.rdc_set->primary.intf, phost);
1242 
1243 		(void) snprintf(key, sizeof (key),
1244 		    "sndr.set%d.secondary", setnumber);
1245 		if (cfg_get_cstring(cfg, key, secondary,
1246 				sizeof (secondary)) < 0)
1247 			break;
1248 		(void) strcpy(parms.rdc_set->secondary.file, secondary);
1249 
1250 		(void) snprintf(key, sizeof (key),
1251 		    "sndr.set%d.shost", setnumber);
1252 		if (cfg_get_cstring(cfg, key, shost, sizeof (shost)) < 0)
1253 			break;
1254 		(void) strcpy(parms.rdc_set->secondary.intf, shost);
1255 
1256 		parms.command = RDC_CMD_STATUS;
1257 		if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus) < 0) {
1258 			continue;
1259 		}
1260 
1261 		/* We found a matching set */
1262 		found++;
1263 
1264 		if (self_check(phost))
1265 			local_file = primary;
1266 		else
1267 			local_file = secondary;
1268 
1269 		rdc_set = &parms.rdc_set[0];
1270 		if (!(rdc_set->flags & RDC_LOGGING) &&
1271 		    !(rdc_set->flags & RDC_SYNCING)) {
1272 			volumes[replicating] = strdup(local_file);
1273 			if (volumes[replicating] == NULL) {
1274 				size = strlen(local_file);
1275 				spcs_log("sndr", NULL,
1276 				    gettext("%s: unable to allocate %ld bytes"),
1277 				    program, size);
1278 				rdc_warn(NULL,
1279 				    gettext("unable to allocate %ld bytes"),
1280 				    size);
1281 				goto done;
1282 			}
1283 			/* We remember all replicating sets */
1284 			replicating++;
1285 		} else
1286 			break;		/* Not all replicating, so done */
1287 	}
1288 
1289 	if (found != replicating)
1290 		goto done;
1291 
1292 	/* All replicating, so update ndr_ii state fields */
1293 
1294 	cfg_unlock(cfg);
1295 
1296 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
1297 		spcs_log("sndr", NULL,
1298 		    gettext("%s: error locking lconfig"),
1299 		    program);
1300 		rdc_warn(NULL, gettext("error locking config"));
1301 		goto done;
1302 	}
1303 
1304 	/*
1305 	 * Search through the ndr_ii entries for entries
1306 	 * that match the saved secondary volume names.
1307 	 * Set state to "update".
1308 	 */
1309 
1310 	for (i = 0; ; i++) {
1311 		setnumber = i + 1;
1312 
1313 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.secondary",
1314 		    setnumber);
1315 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1316 			break;
1317 
1318 		if (!volume_match(buf, volumes, found)) {
1319 			continue;
1320 		}
1321 
1322 		/* Got a matching entry */
1323 
1324 		(void) snprintf(key, sizeof (key),
1325 		    "ndr_ii.set%d.state", setnumber);
1326 		if ((cfg_put_cstring(cfg, key, UPDATE, strlen(UPDATE)) < 0) ||
1327 		    (cfg_commit(cfg) < 0)) {
1328 			spcs_log("sndr", NULL,
1329 			    gettext("%s: unable to update \"%s\" "
1330 			    "in configuration storage: %s"),
1331 			    program, buf, cfg_error(&sev));
1332 			rdc_warn(NULL,
1333 			    gettext("unable to update \"%s\" "
1334 			    "in configuration storage: %s"),
1335 			    buf, cfg_error(&sev));
1336 		}
1337 	}
1338 
1339 
1340 done:
1341 	if (cfg) {
1342 		cfg_close(cfg);
1343 		UNLOCKCFG();
1344 	}
1345 	spcs_s_ufree(&ustatus);
1346 	if (volumes) {
1347 		for (i = 0; i < replicating; i++)
1348 			free(volumes[i]);
1349 		free(volumes);
1350 	}
1351 }
1352 
1353 
1354 /*
1355  * Sync started to a member of a group.
1356  * If all members of the group are in ndr_ii state "update" then take an PIT
1357  * snapshot on all of them. This will provide a consistent point-in-time
1358  * copy until whatever syncs take place are all completed.
1359  */
1360 void
1361 group_start(char *group)
1362 {
1363 	char **masters = NULL;
1364 	char **shadows = NULL;
1365 	char **bitmaps = NULL;
1366 	char cmd[256];
1367 	char buf[CFG_MAX_BUF];
1368 	char key[CFG_MAX_KEY];
1369 	CFGFILE *cfg = NULL;
1370 	int i;
1371 	int j;
1372 	int setnumber;
1373 	int found;
1374 	int sndr_sets = 0;
1375 	int update_needed = 0;
1376 	int sev;
1377 	char *ctag = NULL;
1378 	int commit = 0;
1379 	int size;
1380 
1381 	size = sizeof (char *) * rdc_maxsets;
1382 	masters = malloc(size);
1383 	if (masters == NULL) {
1384 		spcs_log("sndr", NULL,
1385 		    gettext("%s: unable to allocate %ld bytes"),
1386 		    program, size);
1387 		rdc_warn(NULL,
1388 			gettext("unable to allocate %ld bytes"), size);
1389 		goto done;
1390 	}
1391 	bzero(masters, size);
1392 	shadows = malloc(size);
1393 	if (shadows == NULL) {
1394 		spcs_log("sndr", NULL,
1395 		    gettext("%s: unable to allocate %ld bytes"),
1396 		    program, size);
1397 		rdc_warn(NULL,
1398 			gettext("unable to allocate %ld bytes"), size);
1399 		goto done;
1400 	}
1401 	bzero(shadows, size);
1402 	bitmaps = malloc(size);
1403 	if (bitmaps == NULL) {
1404 		spcs_log("sndr", NULL,
1405 		    gettext("%s: unable to allocate %ld bytes"),
1406 		    program, size);
1407 		rdc_warn(NULL,
1408 			gettext("unable to allocate %ld bytes"), size);
1409 		goto done;
1410 	}
1411 	bzero(bitmaps, size);
1412 
1413 	LOCKCFG();
1414 	if ((cfg = cfg_open(NULL)) == NULL) {
1415 		spcs_log("sndr", NULL,
1416 		    gettext("%s: error opening config"),
1417 		    program);
1418 		rdc_warn(NULL,
1419 		    gettext("error opening config"));
1420 		UNLOCKCFG();
1421 		goto done;
1422 	}
1423 
1424 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
1425 		spcs_log("sndr", NULL,
1426 		    gettext("%s: error locking config"),
1427 		    program);
1428 		rdc_warn(NULL, gettext("error locking config"));
1429 		goto done;
1430 	}
1431 
1432 	/* Now get all Remote Mirror entries with a matching group */
1433 	for (i = 0; i < rdc_maxsets; i++) {
1434 		setnumber = i + 1;
1435 
1436 		(void) snprintf(key, sizeof (key),
1437 		    "sndr.set%d.group", setnumber);
1438 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1439 			break;
1440 
1441 		if (strcmp(buf, group) != 0)
1442 			continue;
1443 
1444 		/* Found a matching entry */
1445 
1446 		(void) snprintf(key, sizeof (key),
1447 		    "sndr.set%d.phost", setnumber);
1448 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0)
1449 			break;
1450 
1451 		if (self_check(buf)) {
1452 			(void) snprintf(key, sizeof (key), "sndr.set%d.primary",
1453 			    setnumber);
1454 		} else {
1455 			(void) snprintf(key, sizeof (key),
1456 			    "sndr.set%d.secondary", setnumber);
1457 		}
1458 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0)
1459 			break;
1460 
1461 		masters[sndr_sets] = strdup(buf);
1462 		if (masters[sndr_sets] == NULL) {
1463 			size = strlen(buf);
1464 			spcs_log("sndr", NULL,
1465 			    gettext("%s: unable to allocate %ld bytes"),
1466 			    program, size);
1467 			rdc_warn(NULL,
1468 				gettext("unable to allocate %ld bytes"), size);
1469 			goto done;
1470 		}
1471 		sndr_sets++;
1472 
1473 		if (ctag == NULL && clustered) {
1474 			/* Get cluster tag of matching entry */
1475 
1476 			(void) snprintf(key, sizeof (key), "sndr.set%d.cnode",
1477 			    setnumber);
1478 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) >= 0)
1479 				ctag = strdup(buf);
1480 		}
1481 	}
1482 
1483 	/*
1484 	 * Search through the ndr_ii entries for entries
1485 	 * that match the saved local volume names and are in "update" state.
1486 	 */
1487 
1488 	update_needed = 0;
1489 
1490 	for (i = 0; ; i++) {
1491 		setnumber = i + 1;
1492 
1493 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.secondary",
1494 		    setnumber);
1495 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1496 			break;
1497 
1498 		if (!volume_match(buf, masters, sndr_sets))
1499 			continue;
1500 
1501 		/* Got a matching entry */
1502 
1503 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.shadow",
1504 		    setnumber);
1505 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1506 			break;
1507 		shadows[update_needed] = strdup(buf);
1508 		if (shadows[update_needed] == NULL) {
1509 			size = strlen(buf);
1510 			spcs_log("sndr", NULL,
1511 			    gettext("%s: unable to allocate %ld bytes"),
1512 			    program, size);
1513 			rdc_warn(NULL,
1514 				gettext("unable to allocate %ld bytes"), size);
1515 			goto done;
1516 		}
1517 
1518 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.bitmap",
1519 		    setnumber);
1520 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
1521 			break;
1522 		}
1523 		bitmaps[update_needed] = strdup(buf);
1524 		if (bitmaps[update_needed] == NULL) {
1525 			size = strlen(buf);
1526 			spcs_log("sndr", NULL,
1527 			    gettext("%s: unable to allocate %ld bytes"),
1528 			    program, size);
1529 			rdc_warn(NULL,
1530 				gettext("unable to allocate %ld bytes"), size);
1531 			goto done;
1532 		}
1533 
1534 		(void) snprintf(key, sizeof (key), "ndr_ii.set%d.state",
1535 		    setnumber);
1536 		if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
1537 			break;
1538 		}
1539 		if (strcmp(buf, UPDATE) != 0) {
1540 			break;
1541 		}
1542 
1543 		update_needed++;
1544 	}
1545 
1546 	if (update_needed != sndr_sets) {
1547 #ifdef DEBUG
1548 		spcs_log("sndr", NULL,
1549 		    gettext("%s: group sync: no Point-in-Time Copy snapshot "
1550 			    "for %s"), program, group);
1551 #endif
1552 		goto done;
1553 	}
1554 
1555 	/* All RM sets in the group have an ndr_ii entry in "update" state */
1556 
1557 	/* Issue PIT Copy snapshot commands for all sets in the group */
1558 	for (j = 0; j < sndr_sets; j++) {
1559 		found = 0;
1560 
1561 		/* get ii entries until a match is found */
1562 		for (i = 0; ; i++) {
1563 			setnumber = i + 1;
1564 
1565 			(void) snprintf(key, sizeof (key), "ii.set%d.shadow",
1566 			    setnumber);
1567 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1568 				break;
1569 			if (strcmp(buf, shadows[j]) != 0)
1570 				continue;
1571 
1572 			/* Matching shadow found, so ii already enabled */
1573 			found = 1;
1574 			break;
1575 		}
1576 
1577 		if (commit)
1578 			if (cfg_commit(cfg) < 0)
1579 				rdc_warn(NULL, gettext("commit config error"));
1580 		cfg_close(cfg);
1581 
1582 		if (found) {
1583 			(void) sprintf(cmd, "%s -u s %s", IIADM, shadows[j]);
1584 		} else {
1585 			if (ctag) {
1586 				(void) sprintf(cmd, "%s -C %s -e dep %s %s %s",
1587 				    IIADM, ctag, masters[j], shadows[j],
1588 				    bitmaps[j]);
1589 				free(ctag);
1590 				ctag = NULL;
1591 			} else
1592 				(void) sprintf(cmd, "%s -e dep %s %s %s", IIADM,
1593 				    masters[j], shadows[j], bitmaps[j]);
1594 		}
1595 
1596 		if (system(cmd) != 0) {
1597 			spcs_log("sndr", NULL,
1598 			    gettext("%s: group sync: Point-in-Time Copy"
1599 			    " snapshot failed for %s"),
1600 			    program, masters[j]);
1601 
1602 			goto done;
1603 		}
1604 
1605 		if ((cfg = cfg_open(NULL)) == NULL) {
1606 			spcs_log("sndr", NULL,
1607 			    gettext("%s: error opening config"),
1608 			    program);
1609 			rdc_warn(NULL,
1610 			    gettext("error opening config"));
1611 			goto done;
1612 		}
1613 		if (!cfg_lock(cfg, CFG_WRLOCK)) {
1614 			spcs_log("sndr", NULL,
1615 			    gettext("%s: error locking config"),
1616 			    program);
1617 			rdc_warn(NULL, gettext("error locking config"));
1618 			goto done;
1619 		}
1620 		commit = 0;
1621 
1622 		/* PIT enable or update was fine, so update the ndr_ii entry */
1623 
1624 		/* get ndr_ii entries until a match is found */
1625 		for (i = 0; ; i++) {
1626 			setnumber = i + 1;
1627 
1628 			(void) snprintf(key, sizeof (key),
1629 			    "ndr_ii.set%d.shadow", setnumber);
1630 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
1631 				break;
1632 			if (strcmp(buf, shadows[j]) != 0)
1633 				continue;
1634 
1635 			/* Found the matching entry */
1636 
1637 			(void) snprintf(key, sizeof (key), "ndr_ii.set%d.state",
1638 			    setnumber);
1639 			if (cfg_put_cstring(cfg, key, NOUPDATE,
1640 			    strlen(NOUPDATE)) < 0) {
1641 				spcs_log("sndr", NULL,
1642 				    gettext("%s: unable to update \"%s\" "
1643 				    "in configuration storage: %s"),
1644 				    program, buf, cfg_error(&sev));
1645 				rdc_warn(NULL,
1646 				    gettext("unable to update \"%s\" "
1647 				    "in configuration storage: %s"),
1648 				    buf, cfg_error(&sev));
1649 			} else
1650 				commit = 1;
1651 			break;
1652 		}
1653 	}
1654 
1655 	if (commit)
1656 		if (cfg_commit(cfg) < 0)
1657 			rdc_warn(NULL, gettext("commit config error"));
1658 
1659 	spcs_log("sndr", NULL,
1660 	    gettext("%s: group sync: Point-in-Time Copy snapshots completed "
1661 		    "for %s"), program, group);
1662 
1663 done:
1664 	if (ctag)
1665 		free(ctag);
1666 
1667 	if (cfg) {
1668 		cfg_close(cfg);
1669 		UNLOCKCFG();
1670 	}
1671 
1672 	if (masters) {
1673 		for (i = 0; i < sndr_sets; i++) {
1674 			if (masters[i])
1675 				free(masters[i]);
1676 		}
1677 		free(masters);
1678 	}
1679 
1680 	if (shadows) {
1681 		for (i = 0; i < update_needed; i++) {
1682 			if (shadows[i])
1683 				free(shadows[i]);
1684 		}
1685 		free(shadows);
1686 	}
1687 
1688 	if (bitmaps) {
1689 		for (i = 0; i < update_needed; i++) {
1690 			if (bitmaps[i])
1691 				free(bitmaps[i]);
1692 		}
1693 		free(bitmaps);
1694 	}
1695 }
1696