Esempio n. 1
0
/*
 * Disable the device from the kernel configuration.
 *
 * RETURN:
 *   0 on success
 *   non-zero on failure.
 *
 * Failures are reported to the user.
 */
static int
disable_dev(const caddr_t path)
{
	struct stat stb;
	sv_conf_t svc;
	int fd;

	sv_check_cluster(path);

	if (stat(path, &stb) < 0) {
		svc.svc_major = (major_t)-1;
		svc.svc_minor = (minor_t)-1;
	} else {
		svc.svc_major = major(stb.st_rdev);
		svc.svc_minor = minor(stb.st_rdev);
	}

	if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
		warn(NULL, gettext("unable to open %s: %s"),
			sv_rpath, strerror(errno));
		return (-1);
	}

	(void) strcpy(svc.svc_path, path);
	svc.svc_error = spcs_s_ucreate();

	/*
	 * Issue the ioctl to attempt to disable this device.  Note that all
	 * the libdscfg details are handled elsewhere.
	 */
	if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) {
		if (errno != SV_EDISABLED) {
			spcs_log("sv", &svc.svc_error,
					gettext("%s: unable to disable %s"),
					program, path);

			warn(&svc.svc_error,
					gettext("unable to disable %s"), path);
			(void) close(fd);
			return (-1);
		}
	}

	spcs_log("sv", NULL, gettext("%s: disabled %s"), program, path);

	spcs_s_ufree(&svc.svc_error);
	(void) close(fd);

	return (0);
}
Esempio n. 2
0
static void
lock_stat()
{
	char *lt = "Unknown";
	struct lockdaemon *ldp;
	int i;

	switch (the_lock.type) {
	case LOCK_NOTLOCKED:
		lt = "not locked";
		break;
	case LOCK_READ:
		lt = "read locked";
		break;
	case LOCK_WRITE:
		lt = "write locked";
		break;
	}
	spcs_log("cfglockd", NULL, "Lock is %s (%d)", lt, the_lock.type);
	spcs_log("cfglockd", NULL, "There are %d holders of the lock",
	    the_lock.nholders);
	if (the_lock.nholders > 0) {
		for (i = 0; i < the_lock.nholders; i++)
			spcs_log("cfglockd", NULL, "holding_pid[%d] = %6d", i,
			    the_lock.holding_pid[i]);
	}
	spcs_log("cfglockd", NULL, "holder daemon was %s port %hu, remote %x",
	    dp_addr(&the_lock.holder), the_lock.holder.sin_port,
	    the_lock.remote_daemon);
	spcs_log("cfglockd", NULL, "Lock queue, %d requests", next_req);
	for (i = 0; i < next_req; i++) {
		spcs_log("cfglockd", NULL, "request %d type %d order %d", i,
		    lock_queue[i].type, lock_queue[i].order);
		spcs_log("cfglockd", NULL, "  client %s port %hu, pid %d",
		    dp_addr(&lock_queue[i].remote),
		    lock_queue[i].remote.sin_port, lock_queue[i].pid);
	}
	spcs_log("cfglockd", NULL, "Daemon list");

	for (i = 0, ldp = daemon_list; i < MAX_DAEMONS; i++, ldp++) {
		if (ldp->inuse == 0)
			break;
		spcs_log("cfglockd", NULL, "daemon %d, %s port %hu", i,
		    dp_addr(&ldp->host), ldp->host.sin_port);
		spcs_log("cfglockd", NULL,
		    "  up %d timeout %ld missed %d state %d\n", ldp->up,
		    ldp->timeout, ticker - ldp->timeout, ldp->state);
	}
}
Esempio n. 3
0
static int
is_duplicate(cfglockd_t type, pid_t pid, uint8_t seq)
{
	struct unlock_s *bufp;
	int i;

	if (!pid) {
		return (0);
	}

	for (i = 0, bufp = unlock_buf; bufp->pid && i < MAX_UNLOCK;
	    i++, bufp++) {
		if (bufp->pid == pid && bufp->seq == seq) {
			/* throw message away */
#ifdef DEBUG
			spcs_log("cfglockd", NULL,
			    "duplicate '%d' request received from %d",
			    type, pid);
#endif
			return (1);
		}
	}

	/* add it to the list */
	bcopy(unlock_buf, &unlock_buf[ 1 ],
	    sizeof (unlock_buf) - sizeof (struct unlock_s));
	(*unlock_buf).pid = pid;
	(*unlock_buf).seq = seq;

	return (0);
}
Esempio n. 4
0
static void
term(int sig)
{
	(void) unlink(CFG_PIDFILE);
	spcs_log("cfglockd", NULL, "pid %d terminate on signal %d", getpid(),
	    sig);
	exit(0);
}
Esempio n. 5
0
void
init_host_list(void)
{
	host_list = calloc(1, sizeof (host_list_t));
	if (host_list == NULL) {
		spcs_log("sndr", NULL,
		    gettext("host list not initialized, cannot run"));
		rdc_err(NULL, gettext("host list not initialized, cannot run"));
	}
	(void) mutex_init(&host_list->hosts_mutex, USYNC_THREAD, NULL);
}
Esempio n. 6
0
static void
daemon_alive(daemonaddr_t *daemon, int32_t order)
{
	struct lockdaemon *ldp;
	int i;

	for (i = 0, ldp = daemon_list; i < MAX_DAEMONS; i++, ldp++) {
		if (ldp->inuse == 0)
			break;
		if (cmp_addr(&(ldp->host), daemon) == 0) {
			ldp->order = order;
			ldp->timeout = ticker;
			if (ldp->up == 0) {
				spcs_log("cfglockd", NULL,
				    "daemon restarted on %s\n",
				    dp_addr(daemon));
				DPF((stderr, "daemon restarted on %s\n",
				    dp_addr(daemon)));
				ldp->up = 1;
				goto come_up;
			}
			return;
		}
	}
	/* new daemon has announced itself */
	if (i < MAX_DAEMONS) {
		DPF((stderr, "new daemon on %s\n", dp_addr(daemon)));
		spcs_log("cfglockd", NULL,
		    "new daemon on %s\n", dp_addr(daemon));
		ldp->host = *daemon;
		ldp->inuse = 1;
		ldp->timeout = ticker;
		ldp->order = order;
	} else {
		/* problem, more daemons than expected */
		i++;
	}
come_up:
	if (the_lock.type == LOCK_WRITE && the_lock.remote_daemon == NULL)
		send_lockmsg(WRITE_LOCK, (pid_t)0, daemon, 0);
}
Esempio n. 7
0
int
get_cfg_setid(CFGFILE *cfg, char *ctag, char *tohost, char *tofile)
{
	int setnum = 0;
	int close_cfg = 0;
	char key[CFG_MAX_KEY];
	char setid[64];

	if (cfg == NULL) {
		close_cfg = 1;
		if ((cfg = cfg_open(NULL)) == NULL) {
			return (-1); /* message printed by caller */
		}
		if (!cfg_lock(cfg, CFG_RDLOCK)) {
			cfg_close(cfg);
			return (-1);
		}
	}
	setnum = find_setnumber_in_libcfg(cfg, ctag, tohost, tofile);
	if (setnum < 0)
		return (setnum);

	(void) snprintf(key, CFG_MAX_KEY, "sndr.set%d.options", setnum);
	if (cfg_get_single_option(cfg, CFG_SEC_CONF, key, "setid",
		    setid, sizeof (setid)) < 0) {
		if (close_cfg)
			cfg_close(cfg);

		spcs_log("sndr", NULL,
		    gettext("%s unable to get unique setid "
		    "for %s:%s"), program, tohost, tofile);
		return (-1);

	}
	if (close_cfg)
		cfg_close(cfg);

	return (atoi(setid));

}
Esempio n. 8
0
static void
send_lockmsg(int cmd, pid_t pid, daemonaddr_t *dp, uint8_t seq)
{
	struct lock_msg message_buf;
	int rc;

	if (msgtrace && debugfile) {
		time_t t = time(0);
		(void) fprintf(debugfile, "%19.19s send %-9.9s to   %s\n",
		    ctime(&t), lockd_msg(cmd), dp_addr(dp));
	}
	DPF((stderr, "send %d to %s port %hu\n", cmd,
	    dp_addr(dp), dp->sin_port));
	message_buf.message = cmd;
	message_buf.pid = pid;
	message_buf.order = order;
	message_buf.seq = seq;
	do {
		rc = sendto(lock_soc, &message_buf, sizeof (message_buf), 0,
		    (struct sockaddr *)dp, sizeof (struct sockaddr));
	} while (rc == -1 && errno == EINTR);
	if (rc == -1)
		spcs_log("cfglockd", NULL, "sendto rc -1 errno %d", errno);
}
Esempio n. 9
0
static void
init(int argc, char *argv[])
{
#if defined(_SunOS_5_6) || defined(_SunOS_5_7) || defined(_SunOS_5_8)
	struct rlimit rl;
#endif
	int	c, i, x;
	int	rc;
	char	*cp = NULL;
	struct	itimerval	tv;
	struct	timeval		tp;
	socklen_t len = sizeof (thishost);
	int	exe = 0;
	pid_t	pid;
	FILE	*fp;

	lstate = (getenv("LOCKD_STATE") != NULL);
	msgtrace = (getenv("LOCKD_MSG") != NULL);

	/*
	 * Fork off a child that becomes the daemon.
	 */

#ifndef TTY_MESSAGES
	if ((rc = fork()) > 0)
		exit(0);
	else if (rc < 0) {
		spcs_log("cfglockd", NULL, "can't fork %d", errno);
		(void) fprintf(stderr, gettext("dscfglockd: cannot fork: %s\n"),
		    strerror(errno));
		exit(1);
	}
#endif

	/*
	 * In child - become daemon.
	 */

#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8)
	/* use closefrom(3C) from PSARC/2000/193 when possible */
	closefrom(CLOSE_FD);
#else
	(void) getrlimit(RLIMIT_NOFILE, &rl);
	for (i = CLOSE_FD; i < rl.rlim_max; i++)
		(void) close(i);
#endif

#ifdef DEBUG
#ifndef	TTY_MESSAGES
	(void) open("/dev/console", O_WRONLY|O_APPEND);
	(void) dup(0);
	(void) dup(0);
#endif
#endif
	(void) close(0);

	if (msgtrace || lstate) {
		debugfile = fopen("/var/tmp/dscfglockd.out", "a");
		if (debugfile) {
			time_t t = time(0);
			setbuf(debugfile, (char *)0);
			(void) fprintf(debugfile, "%19.19s dscfglockd start\n",
			    ctime(&t));
		}
	}

	(void) setpgrp();
	spcs_log("cfglockd", NULL, "new lock daemon, pid %d", getpid());

	/*
	 * Catch as unexpected all signals apart from SIGTERM.
	 */

	for (i = 1; i < _sys_nsig; i++)
		(void) sigset(i, unexpected);
	(void) sigset(SIGTERM, term);

	for (i = 0; (c = getopt(argc, argv, "df:e:")) != EOF; i++) {
		switch (c) {
		case 'd':
			debug = 1;
			break;
		case 'e':
			exe = 1;
			if (cp) {
				usage();
			}
			cp = optarg;
			break;
		case 'f':
			if (cp) {
				usage();
			}
			cp = optarg;
			break;
		default:
			usage();
			break;
		}
	}

	ifc = (struct ifconf *)malloc(sizeof (struct ifconf));
	if (ifc == NULL) {
		perror(CFG_PIDFILE);
		DPF((stderr, "Can't open pid file\n"));
		exit(1);
	}
	(void) memset((char *)ifc, 0, sizeof (struct ifconf));
	getmyaddrs(ifc);

	/*
	 * if (lockdaemonalive()) {
	 *	(void) fprintf(stderr, "%s: %s\n", program,
	 *		gettext("There is already a live lockdaemon"));
	 *	exit(1);
	 * }
	 */
	if ((fp = fopen(CFG_PIDFILE, "w")) == NULL) {
		perror(CFG_PIDFILE);
		DPF((stderr, "Can't open pid file\n"));
		exit(1);
	}
	(void) fprintf(fp, "%ld\n", getpid());
	(void) fclose(fp);

	/* order should be set to node number within cluster */
	order = cfg_iscluster();
	cfg_lfinit();

	if (!order) {
		(void) gettimeofday(&tp, NULL);
		srand48(tp.tv_usec);
		order = lrand48();
		if (debugfile) {
			(void) fprintf(debugfile, "WARNING: order number "
			    "is 0 -- changing randomly to %d\n", order);
		}
	}
	c = 0;
	for (i = 0; (x = cfg_filelock(i, 0)) != CFG_LF_EOF; i++) {
		if (x == CFG_LF_AGAIN) {
			cfg_readpid(i, &pid);
			if (c++ == 0)
				spcs_log("cfglockd", NULL,
				    "init .dscfg.lck slot %d pid %d locked",
				    i, pid);
			DPF((stderr, "client process %ld still alive\n", pid));
			continue; /* can't take lock, must be still alive */
		}
		cfg_writepid(i, 0);
		(void) cfg_fileunlock(i);
	}

	tv.it_interval.tv_sec = TIMEOUT_SECS;
	tv.it_interval.tv_usec = 0;
	tv.it_value = tv.it_interval;

	bzero(unlock_buf, sizeof (unlock_buf));
	next_q = 0;
	build_daemon_list(cp, exe);
	if ((lock_soc = socket(pf_inet, SOCK_DGRAM, 0)) < 0) {
		(void) fprintf(stderr, "%s: %s\n", program,
		    gettext("failed to create socket"));
		perror("socket");
		spcs_log("cfglockd", NULL, "couldn't create socket");
		exit(1);
	}
	thishost.sin_family = AF_INET;
	thishost.sin_addr.s_addr = INADDR_ANY;
	thishost.sin_port = htons(lock_port);
	rc = bind(lock_soc, (struct sockaddr *)&thishost, sizeof (thishost));
	if (rc < 0) {
		perror("bind");
		spcs_log("cfglockd", NULL, "couldn't bind");
		exit(1);
	}
	if (getsockname(lock_soc, (struct sockaddr *)&thishost, &len) < 0)
		perror("getsockname");
	send_aliveall();
	(void) sigset(SIGALRM, keepalive);
	(void) setitimer(ITIMER_REAL, &tv, NULL);
	/*
	 * wait 2 time outs before allowing a lock to find if someone else
	 * currently has the lock.
	 */
}
Esempio n. 10
0
static void
unexpected(int sig)
{
	spcs_log("cfglockd", NULL, "pid %d unexpected signal %d, ignoring",
	    getpid(), sig);
}
Esempio n. 11
0
static void
build_daemon_list(char *cf_file, int exe)
{
	FILE *fp;
	char	host[1024];
	int	port;
	int	i;
	struct	hostent *hp;
	struct lockdaemon *ldp;

	if ((hp = gethostbyname("localhost")) == NULL) {
		(void) fprintf(stderr, "%s: Can't find hostent for %s\n",
		    program, "localhost");
		spcs_log("cfglockd", NULL, "couldn't find localhost");
		exit(1);
	}

	(void) memcpy(&(localhost.sin_addr.s_addr), *(hp->h_addr_list),
	    sizeof (localhost.sin_addr));
	if (cf_file == NULL) {
		(void) endhostent();
		return;
	}
	if (exe) {
		if ((fp = popen(cf_file, "r")) == NULL) {
			perror(cf_file);
			(void) fprintf(stderr,
			    "%s: Can't open config program\n", program);
			spcs_log("cfglockd", NULL, "couldn't read config");
			exit(1);
		}
	} else {
		if ((fp = fopen(cf_file, "r")) == NULL) {
			perror(cf_file);
			(void) fprintf(stderr, "%s: Can't open config file\n",
			    program);
			spcs_log("cfglockd", NULL, "couldn't read config");
			exit(1);
		}
	}
	ldp = daemon_list;
	while ((i = fscanf(fp, "%s %d\n", host, &port)) != EOF) {
		if (host[0] == '#')	/* line starting with # are comments */
			continue;
		if (i == 1) {
			port = lock_port;
		} else {
			if (strcmp(host, "localhost") == 0) {
				lock_port = port;
				continue;
			}
		}

		if ((hp = gethostbyname(host)) == NULL) {
			(void) fprintf(stderr,
			    "%s: Can't find hostent for %s\n", program, host);
			continue;
		}

		(void) memcpy(&(ldp->host.sin_addr.s_addr), *(hp->h_addr_list),
		    sizeof (ldp->host.sin_addr));
		DPF((stderr, "daemon: %s\t%s\n",
		    inet_ntoa(ldp->host.sin_addr), hp->h_name));
		if (islocalhost(&(ldp->host))) {
			DPF((stderr, "is an alias for this host, skipping\n"));
			continue;
		}
		ldp->host.sin_port = htons((short)port);
		ldp->host.sin_family = hp->h_addrtype;
		ldp->inuse = 1;
		ldp->up = 1;
		ldp++;
	}
	if (exe)
		(void) pclose(fp);
	else
		(void) fclose(fp);
	(void) endhostent();
}
Esempio n. 12
0
static int
enable_dev(sv_name_t *svn)
{
	char buf[CFG_MAX_BUF];
	struct stat stb;
	sv_conf_t svc;
	int fd;
	int sev;
	int rc;
	char *lcltag;
	char *altname;

	sv_check_cluster(svn->svn_path);
	sv_cfg_open(CFG_WRLOCK);

	bzero(&svc, sizeof (svc));

	if (stat(svn->svn_path, &stb) != 0) {
		warn(NULL, gettext("unable to access %s: %s"),
			svn->svn_path, strerror(errno));
		return (1);
	}

	if (!S_ISCHR(stb.st_mode)) {
		warn(NULL, gettext("%s is not a character device - ignored"),
		    svn->svn_path);
		return (1);
	}

	svc.svc_major = major(stb.st_rdev);
	svc.svc_minor = minor(stb.st_rdev);
	(void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path));

	fd = open(sv_rpath, O_RDONLY);
	if (fd < 0) {
		warn(NULL, gettext("unable to open %s: %s"),
			svn->svn_path, strerror(errno));
		return (1);
	}

	svc.svc_flag = svn->svn_mode;
	svc.svc_error = spcs_s_ucreate();

	/* first, check for duplicates */
	rc = cfg_get_canonical_name(cfg, svn->svn_path, &altname);
	if (rc < 0) {
		spcs_log("sv", NULL, gettext("Unable to parse config file"));
		warn(NULL, gettext("Unable to parse config file"));
		(void) close(fd);
		return (1);
	}
	if (rc) {
		error(NULL, gettext("'%s' has already been configured as "
		    "'%s'.  Re-enter command with the latter name."),
		    svn->svn_path, altname);
	}

	/* secondly, try to insert it into the dsvol config */
	if (implicit_tag && *implicit_tag) {
		lcltag = implicit_tag;
	} else if (cfg_cluster_tag && *cfg_cluster_tag) {
		lcltag = cfg_cluster_tag;
	} else {
		lcltag = "-";
	}
	rc = cfg_add_user(cfg, svn->svn_path, lcltag, "sv");
	if (CFG_USER_ERR == rc) {
		spcs_log("sv", NULL,
		    gettext("%s: unable to put %s into dsvol cfg"),
		    program, svn->svn_path);
		warn(NULL, gettext("unable to put %s into dsvol cfg"),
		    svn->svn_path);
		(void) close(fd);
		return (1);
	}
	cfg_changed = 1;

	if (CFG_USER_OK == rc) {
		/* success */
		(void) close(fd);
		return (0);
	}

	if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) {
		if ((CFG_USER_REPEAT == rc) && (SV_EENABLED == errno)) {
			/* it's ok -- we were just double-checking */
			(void) close(fd);
			return (0);
		}

		spcs_log("sv", &svc.svc_error,
		    gettext("%s: unable to enable %s"),
		    program, svn->svn_path);

		warn(&svc.svc_error, gettext("unable to enable %s"),
			svn->svn_path);

		/* remove it from dsvol, if we're the ones who put it in */
		if (CFG_USER_FIRST == rc) {
			(void) cfg_rem_user(cfg, svn->svn_path, lcltag, "sv");
		}
		(void) close(fd);
		return (1);
	}

	spcs_log("sv", NULL, gettext("%s: enabled %s"),
	    program, svn->svn_path);

	if (implicit_tag != NULL) {
#ifdef DEBUG
		if (cfg_cluster_tag != NULL) {
			error(NULL,
			    gettext("enable_dev: -C %s AND implicit_tag %s!"),
			    cfg_cluster_tag, implicit_tag);
		}
#endif

		(void) snprintf(buf, sizeof (buf), "%s - %s",
		    svc.svc_path, implicit_tag);
	} else {
		(void) strcpy(buf, svc.svc_path);
	}

	rc = 0;
	if (cfg_put_cstring(cfg, "sv", buf, sizeof (buf)) < 0) {
		warn(NULL,
		    gettext("unable to add %s to configuration storage: %s"),
		    svc.svc_path, cfg_error(&sev));
		rc = 1;
	}

	cfg_changed = 1;
	spcs_s_ufree(&svc.svc_error);
	(void) close(fd);

	return (rc);
}
Esempio n. 13
0
static void
compare_tag(char *path)
{
	char buf[CFG_MAX_BUF], vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
	char key[CFG_MAX_KEY];
	int found, setnumber, i;
	char *tag;

	sv_check_cluster(path);
	cfg_resource(cfg, (char *)NULL);	/* reset */
	cfg_rewind(cfg, CFG_SEC_CONF);

#ifdef DEBUG
	if (cfg_cluster_tag != NULL && implicit_tag != NULL) {
		error(NULL, gettext("compare_tag: -C %s AND implicit_tag %s!"),
		    cfg_cluster_tag, implicit_tag);
	}
#endif

	if (cfg_cluster_tag != NULL)
		tag = cfg_cluster_tag;
	else if (implicit_tag != NULL)
		tag = implicit_tag;
	else
		tag = "-";

	found = 0;
	for (i = 0; i < sv_max_devices; i++) {
		setnumber = i + 1;
		(void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
			break;
		}

		if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
			continue;
		}

		if (strcmp(path, vol) == 0) {
			found = 1;
			break;
		}
	}

	if (!found) {
		warn(NULL, gettext("unable to find %s in the configuration"),
		    path);
		return;
	}

	/* have name match, compare cnode to new tag */

	if (strcmp(tag, cnode) == 0) {
		/* cluster tags match */
		return;
	}

	/* need to change the cluster tag */

	(void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
	if (cfg_put_cstring(cfg, key, tag, strlen(tag)) < 0) {
		warn(NULL,
		    gettext("unable to change cluster tag for %s"), path);
		return;
	}

	cfg_changed = 1;

	/* change "-" tags to "" for display purposes */

	if (strcmp(tag, "-") == 0)
		tag = "";

	if (strcmp(cnode, "-") == 0)
		(void) strcpy(cnode, "");

	(void) printf(
	    gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\"\n"),
	    program, path, cnode, tag);

	spcs_log("sv", NULL,
	    gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\""),
	    program, path, cnode, tag);
}
Esempio n. 14
0
int
main(int argc, char *argv[])
#endif
{
	char fromhost[MAX_RDC_HOST_SIZE];
	char tohost[MAX_RDC_HOST_SIZE];
	char fromfile[NSC_MAXPATH];
	char tofile[NSC_MAXPATH];
	char frombitmap[NSC_MAXPATH];
	char tobitmap[NSC_MAXPATH];
	char directfile[NSC_MAXPATH];
	char diskqueue[NSC_MAXPATH];
	char group[NSC_MAXPATH];
	char lhost[MAX_RDC_HOST_SIZE];
	int pairs;
	int pid;
	int flag = 0;
	int doasync;
	int rc;
	char *required;
	int setid;

	(void) setlocale(LC_ALL, "");
	(void) textdomain("rdc");

	program = basename(argv[0]);

	rc = rdc_check_release(&required);
	if (rc < 0) {
		rdc_err(NULL,
		    gettext("unable to determine the current "
		    "Solaris release: %s\n"), strerror(errno));
	} else if (rc == FALSE) {
		rdc_err(NULL,
		    gettext("incorrect Solaris release (requires %s)\n"),
		    required);
	}

	rdc_maxsets = rdc_get_maxsets();
	if (rdc_maxsets == -1) {
		spcs_log("sndr", NULL,
		    gettext("%s unable to get maxsets value from kernel"),
		    program);

		rdc_err(NULL,
		    gettext("unable to get maxsets value from kernel"));
	}

	pair_list = calloc(rdc_maxsets, sizeof (*pair_list));
	if (pair_list == NULL) {
		rdc_err(NULL,
		    gettext(
			"unable to allocate pair_list"
			" array for %d sets"),
			rdc_maxsets);
	}

	if (parseopts(argc, argv, &flag))
		return (1);
	pairs = read_libcfg(flag);

	if (flag == RDC_CMD_FIXSETIDS) {
		if (pairs) {
			spcs_log("sndr", NULL, gettext("Fixed %d Remote Mirror"
				    " set IDs"), pairs);
#ifdef DEBUG
			rdc_warn(NULL, gettext("Fixed %d Remote Mirror set "
				    "IDs"), pairs);
#endif
		}
		return (0);
	}

	if (pairs == 0) {
#ifdef DEBUG
		rdc_err(NULL,
		    gettext("Config contains no dual copy sets"));
#else
		return (0);
#endif
	}

	while (pairs--) {
		pid = fork();
		if (pid == -1) {		/* error forking */
			perror("fork");
			continue;
		}

		if (pid > 0)		/* this is parent process */
			continue;

/*
 * At this point, this is the child process.  Do the operation
 */

		strncpy(fromfile,
			pair_list[pairs].ffile, NSC_MAXPATH);
		strncpy(tofile,
			pair_list[pairs].tfile, NSC_MAXPATH);
		strncpy(frombitmap,
			pair_list[pairs].fbitmap, NSC_MAXPATH);
		strncpy(fromhost,
			pair_list[pairs].fhost, MAX_RDC_HOST_SIZE);
		strncpy(tohost,
			pair_list[pairs].thost, MAX_RDC_HOST_SIZE);
		strncpy(tobitmap,
			pair_list[pairs].tbitmap, NSC_MAXPATH);
		strncpy(directfile,
			pair_list[pairs].directfile, NSC_MAXPATH);
		strncpy(diskqueue,
			pair_list[pairs].diskqueue, NSC_MAXPATH);
		strncpy(group,
			pair_list[pairs].group, NSC_MAXPATH);
		strncpy(lhost,
			pair_list[pairs].lhost, MAX_RDC_HOST_SIZE);

		doasync = pair_list[pairs].doasync;
		setid = pair_list[pairs].setid;
		if (rdc_operation(fromhost, fromfile, frombitmap,
		    tohost, tofile, tobitmap, flag, directfile, group,
		    diskqueue, doasync, lhost, setid)
		    < 0) {
			exit(255);
		}

		exit(0);
	}

	while ((wait((int *)0) > 0))
		;
	return (0);
}