sndradm.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <values.h>
#include <limits.h>
#include <fcntl.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <stdarg.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/nsctl/rdc_io.h>
#include <sys/nsctl/rdc_ioctl.h>
#include <sys/nsctl/rdc_prot.h>
#include <sys/nsctl/cfg.h>
#include <sys/unistat/spcs_s.h>
#include <sys/unistat/spcs_s_u.h>
#include <sys/unistat/spcs_errors.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <rpc/rpc_com.h>
#include <rpc/rpc.h>
#include <sys/nsctl/librdc.h>
#include <sys/nsctl/nsc_hash.h>
#include "rdcadm.h"
/*
* support for the special cluster tag "local" to be used with -C in a
* cluster for local volumes.
*/
#define RDC_LOCAL_TAG "local"
typedef struct volcount_s {
int count;
} volcount_t;
hash_node_t **volhash = NULL;
/*
* rdc_islocal is only pertinent while creating the pairs array.
* after all the pairs are set, its value is useless, retaining
* the last value it was set to.
* its only reason in life is to suppress an error message in 2
* places where the inappropriate becomes appropriate (a supplied
* ctag which does not match an implied one cfg_dgame()). This
* happens when C "local" is supplied. It is then used to make an
* error message clearer. A
* gettext("set %s does not match", rdc_islocal < 1?dga:dgb) situation
*/
static int rdc_islocal = 0;
char *program;
#define min(a, b) ((a) > (b) ? (b) : (a))
static char place_holder[] = "-"; /* cfg place holder value */
/*
* config file user level Dual copy pair structure
*/
typedef struct _sd_dual_pair {
char fhost[MAX_RDC_HOST_SIZE]; /* Hostname for primary device */
char fnetaddr[RDC_MAXADDR]; /* Host netaddr for primary device */
char ffile[NSC_MAXPATH]; /* Primary device */
char fbitmap[NSC_MAXPATH]; /* Primary bitmap device */
char thost[MAX_RDC_HOST_SIZE]; /* Hostname for secondary device */
char tnetaddr[RDC_MAXADDR]; /* Host netaddr for secondary device */
char tfile[NSC_MAXPATH]; /* Secondary device */
char tbitmap[NSC_MAXPATH]; /* Secondary bitmap device */
char directfile[NSC_MAXPATH]; /* Local FCAL direct IO volume */
char group[NSC_MAXPATH]; /* Group name */
char ctag[MAX_RDC_HOST_SIZE]; /* Cluster resource name tag */
char diskqueue[NSC_MAXPATH]; /* Disk Queue volume */
int doasync; /* Device is in sync/async mode */
} _sd_dual_pair_t;
#define EXTRA_ARGS 6 /* g grp C ctag q diskqueue */
static int rdc_operation(
CFGFILE *, char *, char *, char *, char *, char *, char *,
int, int, char *, char *, char *, char *, int *, int);
int read_config(int, char *, char *, char *);
static int read_libcfg(int, char *, char *);
int prompt_user(int, int);
static void rdc_check_dgislocal(char *);
void process_clocal(char *);
static void usage(void);
void q_usage(int);
static void load_rdc_vols(CFGFILE *);
static void unload_rdc_vols();
static int perform_autosv();
static void different_devs(char *, char *);
static void validate_name(CFGFILE *, char *);
static void set_autosync(int, char *, char *, char *);
static int autosync_is_on(char *tohost, char *tofile);
static void enable_autosync(char *fhost, char *ffile, char *thost, char *tfile);
static void checkgfields(CFGFILE *, int, char *, char *, char *, char *,
char *, char *, char *, char *, char *);
static void checkgfield(CFGFILE *, int, char *, char *, char *);
static int rdc_bitmapset(char *, char *, char *, int, nsc_off_t);
static int parse_cfg_buf(char *, _sd_dual_pair_t *, char *);
static void verify_groupname(char *grp);
extern char *basename(char *);
int rdc_maxsets;
static _sd_dual_pair_t *pair_list;
struct netbuf svaddr;
struct netbuf *svp;
struct netconfig nconf;
struct netconfig *conf;
struct knetconfig knconf;
static char *reconfig_pbitmap = NULL;
static char *reconfig_sbitmap = NULL;
#ifdef _RDC_CAMPUS
static char *reconfig_direct = NULL;
#endif
static char *reconfig_group = NULL;
static char reconfig_ctag[MAX_RDC_HOST_SIZE];
static int reconfig_doasync = -1;
static int clustered = 0;
static int proto_test = 0;
int allow_role = 0;
static char *
rdc_print_state(rdc_set_t *urdc)
{
if (!urdc)
return ("");
if (urdc->sync_flags & RDC_VOL_FAILED)
return (gettext("volume failed"));
else if (urdc->sync_flags & RDC_FCAL_FAILED)
return (gettext("fcal failed"));
else if (urdc->bmap_flags & RDC_BMP_FAILED)
return (gettext("bitmap failed"));
else if (urdc->flags & RDC_DISKQ_FAILED)
return (gettext("disk queue failed"));
else if (urdc->flags & RDC_LOGGING) {
if (urdc->sync_flags & RDC_SYNC_NEEDED)
return (gettext("need sync"));
else if (urdc->sync_flags & RDC_RSYNC_NEEDED)
return (gettext("need reverse sync"));
else if (urdc->flags & RDC_QUEUING)
return (gettext("queuing"));
else
return (gettext("logging"));
} else if ((urdc->flags & RDC_SLAVE) && (urdc->flags & RDC_SYNCING)) {
if (urdc->flags & RDC_PRIMARY)
return (gettext("reverse syncing"));
else
return (gettext("syncing"));
} else if (urdc->flags & RDC_SYNCING) {
if (urdc->flags & RDC_PRIMARY)
return (gettext("syncing"));
else
return (gettext("reverse syncing"));
}
return (gettext("replicating"));
}
static int
rdc_print(int file_format, int verbose, char *group_arg, char *ctag_arg,
char *user_shost, char *user_sdev, CFGFILE *cfgp)
{
rdc_status_t *rdc_status;
spcs_s_info_t ustatus;
rdc_set_t *urdc;
size_t size;
int i, rc, max;
char *tohost, *tofile;
_sd_dual_pair_t pair;
char *tmptohost = pair.thost;
char *tmptofile = pair.tfile;
char *fromhost = pair.fhost;
char *fromfile = pair.ffile;
char *frombitmap = pair.fbitmap;
char *tobitmap = pair.tbitmap;
char *directfile = pair.directfile;
char *group = pair.group;
char *diskqueue = pair.diskqueue;
char *ctag = pair.ctag;
CFGFILE *cfg;
int j;
int setnumber;
char key[CFG_MAX_KEY];
char buf[CFG_MAX_BUF];
char sync[16];
int match, found;
size = sizeof (rdc_status_t) + (sizeof (rdc_set_t) * (rdc_maxsets - 1));
match = (user_shost != NULL || user_sdev != NULL);
found = 0;
if (user_shost == NULL && user_sdev != NULL)
user_shost = "";
else if (user_shost != NULL && user_sdev == NULL)
user_sdev = "";
rdc_status = malloc(size);
if (!rdc_status) {
rdc_err(NULL,
gettext("unable to allocate %ld bytes"), size);
}
rdc_status->nset = rdc_maxsets;
ustatus = spcs_s_ucreate();
rc = RDC_IOCTL(RDC_STATUS, rdc_status, 0, 0, 0, 0, ustatus);
if (rc == SPCS_S_ERROR) {
rdc_err(&ustatus, gettext("statistics error"));
}
spcs_s_ufree(&ustatus);
max = min(rdc_status->nset, rdc_maxsets);
if (cfgp != NULL) {
cfg = cfgp;
cfg_rewind(cfg, CFG_SEC_CONF);
} else {
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
}
for (i = 0; i < max; i++) {
urdc = &rdc_status->rdc_set[i];
if (!(urdc->flags & RDC_ENABLED))
continue;
if (match &&
(strcmp(user_shost, urdc->secondary.intf) != 0 ||
strcmp(user_sdev, urdc->secondary.file) != 0))
continue;
tohost = urdc->secondary.intf;
tofile = urdc->secondary.file;
found = 1;
/* get sndr entries until shost, sfile match */
for (j = 0; j < rdc_maxsets; j++) {
setnumber = j + 1;
(void) snprintf(key, sizeof (key),
"sndr.set%d", setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
break;
}
if (parse_cfg_buf(buf, &pair, NULL))
rdc_err(NULL, gettext("cfg input error"));
if (strcmp(tmptofile, tofile) != 0)
continue;
if (strcmp(tmptohost, tohost) != 0)
continue;
if (pair.doasync == 0)
strcpy(sync, "sync");
else
strcpy(sync, "async");
/* Got the matching entry */
break;
}
if (j == rdc_maxsets)
continue; /* not found in config */
if (strcmp(group_arg, "") != 0 &&
strncmp(group_arg, group, NSC_MAXPATH) != 0)
continue;
if (strcmp(ctag_arg, "") != 0 &&
strncmp(ctag_arg, ctag, MAX_RDC_HOST_SIZE) != 0)
continue;
if (file_format) {
(void) printf("%s %s %s %s %s %s %s %s",
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
directfile, sync);
if (strlen(group) != 0)
(void) printf(" g %s", group);
if ((strlen(ctag) != 0) && (ctag[0] != '-'))
(void) printf(" C %s", ctag);
if (strlen(diskqueue) != 0)
(void) printf(" q %s", diskqueue);
(void) printf("\n");
continue;
}
if (strcmp(group_arg, "") != 0 &&
strncmp(group_arg, urdc->group_name, NSC_MAXPATH) != 0)
continue;
if (!(urdc->flags & RDC_PRIMARY)) {
(void) printf(gettext("%s\t<-\t%s:%s\n"),
urdc->secondary.file, urdc->primary.intf,
urdc->primary.file);
} else {
(void) printf(gettext("%s\t->\t%s:%s\n"),
urdc->primary.file, urdc->secondary.intf,
urdc->secondary.file);
}
if (!verbose)
continue;
if (urdc->autosync)
(void) printf(gettext("autosync: on"));
else
(void) printf(gettext("autosync: off"));
(void) printf(gettext(", max q writes: %lld"), urdc->maxqitems);
(void) printf(gettext(", max q fbas: %lld"), urdc->maxqfbas);
(void) printf(gettext(", async threads: %d"),
urdc->asyncthr);
(void) printf(gettext(", mode: %s"),
pair.doasync ? "async" : "sync");
if (strlen(urdc->group_name) != 0)
(void) printf(gettext(", group: %s"), urdc->group_name);
if ((strlen(ctag) != 0) && (ctag[0] != '-'))
(void) printf(gettext(", ctag: %s"), ctag);
if (strlen(urdc->disk_queue) != 0) {
(void) printf(gettext(", %s diskqueue: %s"),
(urdc->flags & RDC_QNOBLOCK) ? gettext("non blocking") :
gettext("blocking"), urdc->disk_queue);
}
(void) printf(gettext(", state: %s"), rdc_print_state(urdc));
(void) printf(gettext("\n"));
}
if (!cfgp)
cfg_close(cfg);
free(rdc_status);
if (match && !found) {
rdc_warn(NULL, gettext("unable to find set %s:%s"),
user_shost, user_sdev);
}
return (0);
}
int
parse_extras(int argc, char *args[], int i)
{
int gflag = 0;
int Cflag = 0;
int qflag = 0;
int j;
strcpy(pair_list[i].ctag, "");
strcpy(pair_list[i].group, "");
strcpy(pair_list[i].diskqueue, "");
if (argc == 0)
return (0);
if (argc != 2 && argc != 4 && argc != 6)
return (-1);
for (j = 0; j < argc; j += 2) {
if (strcmp(args[j], "g") == 0) {
if (gflag)
return (-1);
strncpy(pair_list[i].group, args[j + 1], NSC_MAXPATH);
gflag = 1;
}
if (strcmp(args[j], "C") == 0) {
if (!clustered)
return (-1);
if (Cflag)
return (-1);
strncpy(pair_list[i].ctag, args[j + 1],
MAX_RDC_HOST_SIZE);
process_clocal(pair_list[i].ctag);
Cflag = 1;
}
if (strcmp(args[j], "q") == 0) {
if (qflag)
return (-1);
strncpy(pair_list[i].diskqueue, args[j + 1],
NSC_MAXPATH);
qflag = 1;
}
}
return (0);
}
static int
parse_cfg_buf(char *buf, _sd_dual_pair_t *pair, char *lghn)
{
int rc = 0;
char sync[16];
char options[64], *p, *q;
int len;
rc = sscanf(buf, "%s %s %s %s %s %s %s %s %s %s %s %s", pair->fhost,
pair->ffile, pair->fbitmap, pair->thost, pair->tfile,
pair->tbitmap, pair->directfile, sync, pair->group,
pair->ctag, options, pair->diskqueue);
if (rc != 12)
rdc_err(NULL, gettext("cfg input error"));
if (strcmp(pair->diskqueue, place_holder) == 0)
strcpy(pair->diskqueue, "");
if (strcmp(pair->group, place_holder) == 0)
strcpy(pair->group, "");
if (strcmp(sync, "sync") == 0)
pair->doasync = 0;
else if (strcmp(sync, "async") == 0)
pair->doasync = 1;
else {
rdc_err(NULL,
gettext("set %s:%s neither sync nor async"),
pair->thost, pair->tfile);
}
if (lghn && (p = strstr(options, "lghn="))) {
p += 5;
q = strchr(p, ';');
if (q) {
/* LINTED p & q limited to options[64] */
len = q - p;
} else {
len = strlen(p);
}
strncpy(lghn, p, len);
lghn[len] = '\0';
} else if (lghn) {
*lghn = '\0';
}
return (0);
}
static int
ctag_check(char *fromhost, char *fromfile, char *frombitmap, char *tohost,
char *tofile, char *tobitmap, char *ctag, char *diskq)
{
char *file_dgname;
char *bmp_dgname;
char *que_dgname;
char *localfile;
char file_buf[MAX_RDC_HOST_SIZE];
char bmp_buf[MAX_RDC_HOST_SIZE];
char que_buf[NSC_MAXPATH];
int is_primary;
struct hostent *hp;
char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
if (!clustered)
return (0);
hp = gethost_byname(fromhost);
strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
hp = gethost_byname(tohost);
strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
if (!self_check(fromname) && !self_check(toname)) {
/*
* If we could get a list of logical hosts on this cluster
* then we could print something intelligent about where
* the volume is mastered. For now, just print some babble
* about the fact that we have no idea.
*/
rdc_err(NULL,
gettext("either %s:%s or %s:%s is not local"),
fromhost, fromfile, tohost, tofile);
}
is_primary = self_check(fromname);
/*
* If implicit disk group name and no ctag specified by user,
* we set the ctag to it.
* If implicit disk group name, it must match any supplied ctag.
*/
localfile = is_primary ? fromfile : tofile;
file_dgname = cfg_dgname(localfile, file_buf, sizeof (file_buf));
if (file_dgname && strlen(file_dgname))
rdc_check_dgislocal(file_dgname);
/*
* Autogenerate a ctag, if not "-C local" or no "-C " specified
*/
if (!rdc_islocal && !strlen(ctag) && file_dgname && strlen(file_dgname))
strncpy(ctag, file_dgname, MAX_RDC_HOST_SIZE);
/*
* making an exception here for users giving the "local"tag
* this overrides this error message. (rdc_islocal ! = 1)
*/
if (!rdc_islocal && strlen(ctag) &&
file_dgname && strlen(file_dgname) &&
strncmp(ctag, file_dgname, MAX_RDC_HOST_SIZE)) {
rdc_warn(NULL, gettext("ctag \"%s\" does not "
"match disk group name \"%s\" of volume %s"), ctag,
file_dgname, localfile);
return (-1);
}
/*
* Do we have a non-volume managed disk without -C local specified?
*/
if (!rdc_islocal && (!file_dgname || !strlen(file_dgname))) {
rdc_err(NULL, gettext("volume \"%s\" is not part"
" of a disk group,\nplease specify resource ctag\n"),
localfile);
}
/*
* Do we have a volume managed disk with -C local?
*/
if (rdc_islocal && file_dgname && (strlen(file_dgname) > 0)) {
rdc_err(NULL, gettext(
"volume \"%s\" is part of a disk group\n"), localfile);
}
/*
* Local bitmap must also have same ctag.
*/
localfile = is_primary ? frombitmap : tobitmap;
bmp_dgname = cfg_dgname(localfile, bmp_buf, sizeof (bmp_buf));
if (bmp_dgname && strlen(bmp_dgname))
rdc_check_dgislocal(bmp_dgname);
/*
* Assure that if the primary has a device group, so must the bitmap
*/
if ((file_dgname && strlen(file_dgname)) &&
(!bmp_dgname || !strlen(bmp_dgname))) {
rdc_warn(NULL, gettext("bitmap %s is not in disk group \"%s\""),
localfile, rdc_islocal < 1?file_dgname:ctag);
return (-1);
}
/*
* Assure that if the if there is a ctag, it must match the bitmap
*/
if (!rdc_islocal && strlen(ctag) &&
bmp_dgname && strlen(bmp_dgname) &&
strncmp(ctag, bmp_dgname, MAX_RDC_HOST_SIZE)) {
rdc_warn(NULL, gettext("ctag \"%s\" does not "
"match disk group name \"%s\" of bitmap %s"), ctag,
bmp_dgname, localfile);
return (-1);
}
/*
* If this is the SNDR primary and there is a local disk queue
*/
if (is_primary && diskq[0]) {
/*
* Local disk queue must also have same ctag.
*/
que_dgname = cfg_dgname(diskq, que_buf, sizeof (que_buf));
if (que_dgname && strlen(que_dgname))
rdc_check_dgislocal(que_dgname);
/*
* Assure that if the primary has a device group, so must
* the disk queue
*/
if ((file_dgname && strlen(file_dgname)) &&
(!que_dgname || !strlen(que_dgname))) {
rdc_warn(NULL, gettext("disk queue %s is not in disk "
"group \"%s\""), diskq,
rdc_islocal < 1?file_dgname:ctag);
return (-1);
}
/*
* Assure that if the if there is a ctag, it must match
* the disk queue
*/
if (!rdc_islocal && strlen(ctag) &&
que_dgname && strlen(que_dgname) &&
strncmp(ctag, que_dgname, MAX_RDC_HOST_SIZE)) {
rdc_warn(NULL, gettext("ctag \"%s\" does not "
"match disk group name \"%s\" of disk queue %s"),
ctag, que_dgname, diskq);
return (-1);
}
}
return (0);
}
#define DISKQ_OKAY 0
#define DISKQ_FAIL 1
#define DISKQ_REWRITEG 2
/*
* check that newq is compatable with the groups current disk queue.
* Newq is incompatable if it is set and the groups queue is set and the queues
* are different.
*
* if newq is not set but should be, it will be set to the correct value.
* returns:
* DISK_REWRITEG entire group needs to take new value of disk_queue
* DISKQ_OKAY newq contains a value that matches the group.
* DISKQ_FAIL disk queues are incompatible.
*/
static int
check_diskqueue(CFGFILE *cfg, char *newq, char *newgroup)
{
int i, setnumber;
_sd_dual_pair_t pair;
char *group = pair.group;
char *diskqueue = pair.diskqueue;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int open_cfg = cfg == NULL ? 1 : 0;
if (newgroup == NULL || *newgroup == '\0') {
if (*newq == '\0')
return (DISKQ_OKAY); /* okay, */
newgroup = "--nomatch--";
}
if (open_cfg) {
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
}
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
/*
* I think this is quicker than
* having to double dip into the config
*/
if (parse_cfg_buf(buf, &pair, NULL))
rdc_err(NULL, gettext("cfg input error"));
if (strncmp(group, newgroup, NSC_MAXPATH) != 0) {
if (((strncmp(diskqueue, newq, NSC_MAXPATH) == 0)) &&
(diskqueue[0] != '\0')) {
if (open_cfg)
cfg_close(cfg);
return (DISKQ_FAIL);
}
continue;
}
if (*newq == '\0') {
if (diskqueue[0] != '\0')
strncpy(newq, diskqueue, NSC_MAXPATH);
if (open_cfg)
cfg_close(cfg);
return (DISKQ_OKAY); /* okay, */
}
if (open_cfg)
cfg_close(cfg);
if (diskqueue[0] == '\0') /* no queue here */
return (DISKQ_REWRITEG);
return (strncmp(diskqueue, newq, NSC_MAXPATH)
== 0 ? DISKQ_OKAY : DISKQ_FAIL);
}
if (open_cfg)
cfg_close(cfg);
return (DISKQ_OKAY);
}
int
pair_diskqueue_check(int newpair)
{
int i, j;
int rc;
for (i = 0; i < newpair; i++) {
if (strcmp(pair_list[i].group, pair_list[newpair].group) != 0)
continue;
if (strcmp(pair_list[i].diskqueue, pair_list[newpair].diskqueue)
== 0)
return (DISKQ_OKAY); /* matches existing group */
if ((pair_list[newpair].group[0] != '\0') &&
(pair_list[newpair].diskqueue[0] != '\0') &&
(pair_list[i].diskqueue[0] != '\0')) {
rdc_warn(NULL,
gettext("disk queue %s does not match %s "
"skipping set"), pair_list[newpair].diskqueue,
pair_list[i].diskqueue);
return (DISKQ_FAIL);
}
if ((strcmp(pair_list[newpair].diskqueue, "") == 0) &&
pair_list[newpair].group[0] != '\0') {
strncpy(pair_list[newpair].diskqueue,
pair_list[i].diskqueue, NSC_MAXPATH);
return (DISKQ_OKAY); /* changed to existing group que */
}
if (strcmp(pair_list[i].diskqueue, "") == 0) {
for (j = 0; j < newpair; j++) {
if ((pair_list[j].group[0] != '\0') &&
(strncmp(pair_list[j].group,
pair_list[newpair].group,
NSC_MAXPATH) == 0)) {
strncpy(pair_list[j].diskqueue,
pair_list[newpair].diskqueue,
NSC_MAXPATH);
}
}
return (DISKQ_OKAY);
}
break; /* no problem with pair_list sets */
}
/* now check with already configured sets */
rc = check_diskqueue(NULL, pair_list[newpair].diskqueue,
pair_list[newpair].group);
if (rc == DISKQ_REWRITEG) {
for (i = 0; i < newpair; i++) {
if (strcmp(pair_list[i].group,
pair_list[newpair].group) != 0)
continue;
strncpy(pair_list[i].diskqueue,
pair_list[newpair].diskqueue, NSC_MAXPATH);
}
}
return (rc);
}
int
ii_set_exists(CFGFILE *cfg, char *ma, char *sh, char *bm)
{
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
char master[NSC_MAXPATH];
char shadow[NSC_MAXPATH];
char bitmap[NSC_MAXPATH];
int i;
for (i = 1; ; i++) {
(void) snprintf(key, sizeof (key), "ii.set%d", i);
bzero(&master, sizeof (master));
bzero(&shadow, sizeof (shadow));
bzero(&bitmap, sizeof (bitmap));
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
(void) sscanf(buf, "%s %s %s", master, shadow, bitmap);
if (strcmp(master, ma) != 0)
continue;
if (strcmp(shadow, sh) != 0)
continue;
if (strcmp(bitmap, bm) != 0)
continue;
return (1);
}
return (0);
}
void
rdc_ii_config(int argc, char **argv)
{
char *master;
char *shadow;
char *bitmap;
char c;
CFGFILE *cfg;
int i;
int setnumber;
char key[CFG_MAX_KEY];
char buf[CFG_MAX_BUF];
int found;
int sev;
/* Parse the rest of the arguments to see what to do */
if (argc - optind != 4) {
usage();
exit(1);
}
c = *argv[optind];
switch (c) {
case 'd':
/* Delete an ndr_ii entry */
master = argv[++optind];
shadow = argv[++optind];
bitmap = argv[++optind];
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_WRLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
found = 0;
/* get ndr_ii entries until a match is found */
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
(void) snprintf(key, sizeof (key),
"ndr_ii.set%d.secondary",
setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, master) != 0)
continue;
/* Got a matching entry */
(void) snprintf(key, sizeof (key),
"ndr_ii.set%d.shadow",
setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, shadow) != 0)
continue;
(void) snprintf(key, sizeof (key),
"ndr_ii.set%d.bitmap",
setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, bitmap) != 0)
continue;
(void) snprintf(key, sizeof (key),
"ndr_ii.set%d", setnumber);
if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
rdc_warn(NULL,
gettext("unable to remove \"%s\" "
"from configuration storage: %s"),
key, cfg_error(&sev));
} else {
if (cfg_commit(cfg) < 0)
rdc_err(NULL,
gettext("ndr_ii set %s %s %s "
"not deconfigured."),
master, shadow, bitmap);
else
spcs_log("sndr", NULL,
gettext("ndr_ii set %s %s %s "
"has been deconfigured."),
master, shadow, bitmap);
}
found = 1;
break;
}
if (!found) {
rdc_err(NULL,
gettext("did not find matching ndr_ii "
"entry for %s %s %s"), master, shadow, bitmap);
}
cfg_close(cfg);
break;
case 'a':
/* Add an ndr_ii entry */
master = argv[++optind];
shadow = argv[++optind];
bitmap = argv[++optind];
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_WRLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
found = 0;
/* get ndr_ii entries in case a match is found */
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
(void) snprintf(key, sizeof (key),
"ndr_ii.set%d.secondary",
setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, master) == 0) {
rdc_err(NULL,
gettext("found matching ndr_ii "
"entry for %s"), master);
}
}
/*
* check to see if this is using a sndr bitmap.
* kind of a courtesy check, as the ii copy would fail anyway
* excepting the case where they had actually configured
* ii/sndr that way, in which case they are broken
* before we get here
*/
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
/*
* Checking local bitmaps
*/
(void) snprintf(key, sizeof (key), "sndr.set%d.phost",
setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if (self_check(buf)) {
(void) snprintf(key, sizeof (key),
"sndr.set%d.pbitmap",
setnumber);
} else {
(void) snprintf(key, sizeof (key),
"sndr.set%d.sbitmap",
setnumber);
}
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0)
break;
if ((strcmp(buf, bitmap) == 0) ||
(strcmp(buf, master) == 0) ||
(strcmp(buf, shadow) == 0)) {
rdc_err(NULL,
gettext("%s is already configured "
"as a Remote Mirror bitmap"), buf);
}
}
if (!ii_set_exists(cfg, master, shadow, bitmap)) {
rdc_warn(NULL, gettext("Point-in-Time Copy set "
"%s %s %s is not already configured. Remote "
"Mirror will attempt to configure this set when "
"a sync is issued to it. The results of that "
"operation will be in /var/adm/ds.log"),
master, shadow, bitmap);
spcs_log("sndr", NULL, gettext("Point-in-Time Copy set "
"%s %s %s is not already configured. Remote "
"Mirror will attempt to configure this set when "
"a sync is issued to it. The results of that "
"operation will be in /var/adm/ds.log"),
master, shadow, bitmap);
} else {
spcs_log("sndr", NULL, gettext("ndr_ii set "
"%s %s %s has been configured."),
master, shadow, bitmap);
}
/*
* Prior to insertion in ndr_ii entry, if in a Sun Cluster
* assure device groups are the same and cluster tag is set
*/
if (clustered && !rdc_islocal) {
char mst_dg[NSC_MAXPATH] = {0};
char shd_dg[NSC_MAXPATH] = {0};
char bmp_dg[NSC_MAXPATH] = {0};
if (!(cfg_dgname(master, mst_dg, sizeof (mst_dg)) &&
cfg_dgname(shadow, shd_dg, sizeof (shd_dg)) &&
cfg_dgname(bitmap, bmp_dg, sizeof (bmp_dg))))
rdc_warn(NULL, gettext("ndr_ii: %s %s %s are "
"not in a device group"),
master, shadow, bitmap);
else if (strcmp(mst_dg, bmp_dg) ||
strcmp(mst_dg, shd_dg))
rdc_warn(NULL, gettext("ndr_ii: %s %s %s are "
"not in different device groups"),
master, shadow, bitmap);
else {
cfg_resource(cfg, shd_dg);
(void) snprintf(buf, sizeof (buf),
"%s %s %s update %s",
master, shadow, bitmap, shd_dg);
}
} else {
(void) snprintf(buf, sizeof (buf), "%s %s %s update",
master, shadow, bitmap);
}
if ((cfg_put_cstring(cfg, "ndr_ii", buf, strlen(buf)) < 0) ||
(cfg_commit(cfg) < 0))
rdc_warn(NULL, gettext("unable to add \"%s\" to "
"configuration storage: %s"),
buf, cfg_error(&sev));
cfg_close(cfg);
break;
default:
usage();
exit(1);
}
}
void
check_rdcbitmap(int cmd, char *hostp, char *bmp)
{
int i;
CFGFILE *cfg;
int entries;
char **entry;
char *host, *pri, *sec, *sbm, *bit, *mas, *sha, *ovr;
char *shost, *buf, *que;
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
/*
* look into II config to see if this is being used elsewhere
*/
entry = NULL;
entries = cfg_get_section(cfg, &entry, "ii");
for (i = 0; i < entries; i++) {
buf = entry[i];
mas = strtok(buf, " "); /* master */
sha = strtok(NULL, " "); /* shadow */
bit = strtok(NULL, " "); /* bitmap */
(void) strtok(NULL, " "); /* mode */
ovr = strtok(NULL, " "); /* overflow */
/*
* got master, shadow, overflow, and bitmap, now compare
*/
if ((strcmp(bmp, mas) == 0) ||
(strcmp(bmp, sha) == 0) ||
(strcmp(bmp, ovr) == 0) ||
(strcmp(bmp, bit) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is in use by"
" Point-in-Time Copy"), bmp);
}
free(buf);
}
if (entries)
free(entry);
/*
* and last but not least, make sure sndr is not using vol for anything
*/
entry = NULL;
entries = cfg_get_section(cfg, &entry, "sndr");
for (i = 0; i < entries; i++) {
buf = entry[i];
/*
* I think this is quicker than
* having to double dip into the config
*/
host = strtok(buf, " "); /* phost */
pri = strtok(NULL, " "); /* primary */
bit = strtok(NULL, " "); /* pbitmap */
shost = strtok(NULL, " "); /* shost */
sec = strtok(NULL, " "); /* secondary */
sbm = strtok(NULL, " "); /* sbitmap */
(void) strtok(NULL, " "); /* type */
(void) strtok(NULL, " "); /* mode */
(void) strtok(NULL, " "); /* group */
(void) strtok(NULL, " "); /* cnode */
(void) strtok(NULL, " "); /* options */
que = strtok(NULL, " "); /* diskq */
if (cmd == RDC_CMD_ENABLE) {
if (self_check(host)) {
if ((strcmp(bmp, pri) == 0) ||
(strcmp(bmp, que) == 0) ||
(strcmp(bmp, bit) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network Data "
"Replicator"), bmp);
}
} else {
if ((strcmp(bmp, sec) == 0) ||
(strcmp(bmp, sbm) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network Data "
"Replicator"), bmp);
}
}
} else if (cmd == RDC_CMD_RECONFIG) {
/*
* read this logic 1000 times and consider
* multi homed, one to many, many to one (marketing)
* etc, etc, before changing
*/
if (self_check(hostp)) {
if (self_check(host)) {
if ((strcmp(bmp, pri) == 0) ||
(strcmp(bmp, que) == 0) ||
(strcmp(bmp, bit) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
} else {
if ((strcmp(hostp, shost) == 0) &&
(strcmp(bmp, sec) == 0) ||
(strcmp(bmp, sbm) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
}
} else { /* self_check(hostp) failed */
if (self_check(host)) {
if ((strcmp(shost, hostp) == 0) &&
(strcmp(bmp, sec) == 0) ||
(strcmp(bmp, sbm) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
} else {
if ((strcmp(host, hostp) == 0) &&
(strcmp(bmp, pri) == 0) ||
(strcmp(bmp, que) == 0) ||
(strcmp(bmp, bit) == 0)) {
rdc_err(NULL,
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
}
}
}
free(buf);
}
cfg_close(cfg);
if (entries)
free(entry);
}
int
check_intrange(char *arg) {
int i;
for (i = 0; i < strlen(arg); i++) {
if (arg[i] < '0' || arg[i] > '9') {
rdc_warn(NULL, "not a valid number, must be a "
"decimal between 1 and %d", MAXINT);
return (0);
}
}
errno = 0;
i = (int)strtol(arg, NULL, 10);
if ((errno) || (i < 1) || (i > MAXINT)) {
rdc_warn(NULL, "not a valid number, must be a decimal "
"between 1 and %d", MAXINT);
return (0);
}
return (1);
}
void
rewrite_group_diskqueue(CFGFILE *cfg, _sd_dual_pair_t *pair, char *diskqueue)
{
int set;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
_sd_dual_pair_t tmpair;
for (set = 1; /*CSTYLED*/; set++) {
bzero(buf, CFG_MAX_BUF);
bzero(&tmpair, sizeof (tmpair));
(void) snprintf(key, sizeof (key), "sndr.set%d", set);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
break;
}
if (parse_cfg_buf(buf, &tmpair, NULL))
continue;
if (pair->group && pair->group[0]) {
if (strcmp(pair->group, tmpair.group) != 0)
continue; /* not the group we want */
} else { /* no group specified */
if (strcmp(pair->thost, tmpair.thost) != 0)
continue;
if (strcmp(pair->tfile, tmpair.tfile) != 0)
continue;
}
(void) sprintf(key, "sndr.set%d.diskq", set);
if (cfg_put_cstring(cfg, key, diskqueue,
strlen(diskqueue)) < 0) {
perror(cfg_error(NULL));
}
}
}
void
diskq_subcmd(int subcmd, char *qvol, char *group_arg, char *ctag_arg,
char *tohost_arg, char *tofile_arg)
{
int found = 0;
int setnumber = 0;
char key[CFG_MAX_KEY];
char buf[CFG_MAX_BUF];
int i;
int rc;
int option = 0;
_sd_dual_pair_t pair;
CFGFILE *cfg;
char *ctag = NULL;
int resourced = 0;
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_WRLOCK))
rdc_err(NULL,
gettext("unable to lock configuration"));
redo:
if (cfg_load_svols(cfg) < 0 ||
cfg_load_dsvols(cfg) < 0 ||
cfg_load_shadows(cfg) < 0)
rdc_err(NULL,
gettext("Unable to parse config filer"));
load_rdc_vols(cfg);
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
bzero(buf, CFG_MAX_BUF);
(void) snprintf(key, sizeof (key),
"sndr.set%d", setnumber);
rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
if (rc < 0)
break;
if (parse_cfg_buf(buf, &pair, NULL))
continue;
if (strlen(group_arg) == 0) {
if (strcmp(tohost_arg, pair.thost) == 0 &&
strcmp(tofile_arg, pair.tfile) == 0) {
strcpy(group_arg, pair.group);
found = 1;
break;
}
} else {
if (strcmp(group_arg, pair.group) == 0) {
found = 1;
break;
}
}
}
if (!found) {
if (strlen(group_arg) == 0) {
rdc_err(NULL,
gettext("Unable to find %s:%s in "
"configuration storage"),
tohost_arg, tofile_arg);
} else {
rdc_err(NULL,
gettext("Unable to find group %s in "
"configuration storage"), group_arg);
}
}
if (!resourced && strlen(pair.ctag)) { /* uh-oh... */
cfg_unload_svols(cfg);
cfg_unload_dsvols(cfg);
cfg_unload_shadows(cfg);
unload_rdc_vols();
cfg_resource(cfg, pair.ctag);
ctag = strdup(pair.ctag);
resourced = 1;
setnumber = 0;
goto redo;
}
if (clustered && !rdc_islocal) {
if (strcmp(ctag_arg, "") &&
strncmp(ctag_arg, pair.ctag, MAX_RDC_HOST_SIZE))
rdc_warn(NULL, gettext("ctags %s and %s "
"do not match, proceeding with operation based "
"on existing set information"), ctag_arg, ctag);
}
switch (subcmd) {
case RDC_CMD_ADDQ:
if (clustered && (ctag_check(pair.fhost, pair.ffile,
pair.fbitmap, pair.thost, pair.tfile, pair.tbitmap,
pair.ctag, qvol) < 0))
exit(1);
if (strlen(pair.diskqueue) > 0) {
rdc_err(NULL, gettext("Remote Mirror set already "
"has a disk queue"));
}
if (check_diskqueue(cfg, qvol, group_arg) == DISKQ_FAIL) {
rdc_err(NULL,
gettext("diskqueue %s is incompatible"), qvol);
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
if (cfg_vol_disable(cfg, qvol, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove disk "
"queue [%s] from configuration"), qvol);
rdc_err(NULL, gettext("Add disk queue operation "
"failed"));
}
if (nsc_lookup(volhash, qvol) == NULL) {
if (cfg_vol_enable(cfg, qvol, ctag, "sndr") < 0) {
rdc_err(NULL, gettext("Add disk queue "
"operation failed"));
}
}
rewrite_group_diskqueue(cfg, &pair, qvol);
spcs_log("sndr", NULL, gettext("Remote Mirror: added "
"diskqueue %s to set %s:%s and its group"), qvol,
pair.thost, pair.tfile);
break;
case RDC_OPT_FORCE_QINIT:
if (strlen(pair.diskqueue) == 0) {
rdc_err(NULL, gettext("Remote Mirror set does not "
"have a disk queue"));
}
subcmd = RDC_CMD_INITQ;
option = RDC_OPT_FORCE_QINIT;
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, subcmd, option,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
exit(1);
}
break;
case RDC_CMD_INITQ:
if (strlen(pair.diskqueue) == 0) {
rdc_err(NULL, gettext("Remote Mirror set does not "
"have a disk queue"));
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
exit(1);
}
break;
case RDC_CMD_REMQ:
if (strlen(pair.diskqueue) == 0) {
rdc_err(NULL, gettext("Remote Mirror set does not "
"have a disk queue"));
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
exit(1);
}
if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove disk queue "
"[%s] from configuration"), pair.diskqueue);
rewrite_group_diskqueue(cfg, &pair, place_holder);
spcs_log("sndr", NULL, gettext("Remote Mirror: removed "
"diskqueue from set %s:%s and its group"), pair.thost,
pair.tfile);
break;
case RDC_CMD_KILLQ:
if (strlen(pair.diskqueue) == 0) {
rdc_err(NULL, gettext("Remote Mirror set does not "
"have a disk queue"));
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, subcmd, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
rdc_err(NULL, gettext("Failed to remove disk queue"));
}
if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove disk queue "
"[%s] from configuration"), pair.diskqueue);
rewrite_group_diskqueue(cfg, &pair, place_holder);
spcs_log("sndr", NULL, gettext("Remote Mirror: forcibly "
"removed diskqueue from set %s:%s and its group "),
pair.thost, pair.tfile);
break;
case RDC_CMD_REPQ:
if (clustered && (ctag_check(pair.fhost, pair.ffile,
pair.fbitmap, pair.thost, pair.tfile, pair.tbitmap,
pair.ctag, qvol) < 0))
exit(1);
if (strlen(pair.diskqueue) == 0) {
rdc_err(NULL, gettext("Remote Mirror set does not "
"have a disk queue"));
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, RDC_CMD_REMQ, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
rdc_err(NULL, gettext("Failed to remove disk queue"));
}
if (cfg_vol_disable(cfg, pair.diskqueue, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove disk queue "
"[%s] from configuration"), pair.diskqueue);
rewrite_group_diskqueue(cfg, &pair, place_holder);
/* commit here, enable may fail */
if (cfg_commit(cfg) < 0) {
rdc_err(NULL, gettext("commit replace disk queue %s "
"with %s failed"), pair.diskqueue, qvol);
}
if (check_diskqueue(cfg, qvol, group_arg) == DISKQ_FAIL) {
rdc_err(NULL,
gettext("cannot replace disk queue %s with %s"),
pair.diskqueue, qvol);
}
if (rdc_operation(cfg, pair.fhost, pair.ffile, pair.fbitmap,
pair.thost, pair.tfile, pair.tbitmap, RDC_CMD_ADDQ, 0,
pair.directfile, pair.group, pair.ctag, qvol, &pair.doasync,
0) < 0) {
if (cfg_vol_disable(cfg, qvol, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove disk "
"queue [%s] from configuration"), qvol);
rdc_err(NULL, gettext("Failed to add new disk queue"));
}
if (nsc_lookup(volhash, qvol) == NULL)
if (cfg_vol_enable(cfg, qvol, ctag, "sndr") < 0) {
rdc_err(NULL, gettext("Replace disk queue "
"operation failed"));
}
rewrite_group_diskqueue(cfg, &pair, qvol);
spcs_log("sndr", NULL, gettext("Remote Mirror: replaced "
"diskqueue for set %s:%s and its group with %s"),
pair.thost, pair.tfile, qvol);
break;
}
cfg_unload_svols(cfg);
cfg_unload_dsvols(cfg);
cfg_unload_shadows(cfg);
unload_rdc_vols();
if (cfg_commit(cfg) < 0)
rdc_err(NULL, gettext("commit failed on disk queue operation"));
cfg_close(cfg);
if (ctag)
free(ctag);
}
void
spcslog_sync(rdcconfig_t *sets, int start, int type)
{
rdcconfig_t *setp = sets;
while (setp) {
if (start) {
spcs_log("sndr", NULL,
gettext("%s %s %s %s %s %s %s %s\nSync Started"),
program, rdc_decode_flag(RDC_CMD_COPY, type),
setp->phost, setp->pfile, setp->pbmp,
setp->shost, setp->sfile, setp->sbmp);
} else {
spcs_log("sndr", NULL,
gettext("%s %s %s %s %s %s %s %s\nSync Ended"),
program, rdc_decode_flag(RDC_CMD_COPY, type),
setp->phost, setp->pfile, setp->pbmp,
setp->shost, setp->sfile, setp->sbmp);
}
setp = setp->next;
}
}
void
spcslog_tunable(char *shost, char *svol)
{
if (qblock == RDC_OPT_SET_QNOBLOCK)
spcs_log("sndr", NULL, gettext("diskqueue "
"set to non blocking for %s:%s and any members "
"of it's group"), shost, svol);
else if (qblock == RDC_OPT_CLR_QNOBLOCK)
spcs_log("sndr", NULL, gettext("diskqueue "
"set to blocking for %s:%s and any members "
"of it's group"), shost, svol);
if (maxqfbas)
spcs_log("sndr", NULL, gettext("maxqfbas set to %d for %s:%s"),
maxqfbas, shost, svol);
if (maxqitems)
spcs_log("sndr", NULL, gettext("maxwrites set to %d for %s:%s"),
maxqitems, shost, svol);
if (asyncthr)
spcs_log("sndr", NULL, gettext("%d async threads configured "
"for %s:%s"), asyncthr, shost, svol);
}
int
set_qblock(char *blockarg)
{
if (strcmp(blockarg, "block") == 0)
qblock = RDC_OPT_CLR_QNOBLOCK;
else if (strcmp(blockarg, "noblock") == 0)
qblock = RDC_OPT_SET_QNOBLOCK;
else
return (1);
return (0);
}
static void
rdc_force_disable(CFGFILE *cfg, char *phost, char *pvol, char *pbmp,
char *shost, char *svol, char *sbmp, char *ctag, char *lhname)
{
rdc_config_t parms;
spcs_s_info_t ustatus;
volcount_t *vc;
char *datavol = NULL;
char *bmpvol = NULL;
int on_pri = 0;
int on_sec = 0;
/* are we on the primary or secondary host? */
if (ctag && *ctag && *lhname) {
if (strcmp(phost, lhname) == 0) {
on_pri = 1;
} else if (strcmp(shost, lhname) == 0) {
on_sec = 1;
}
} else if (self_check(phost)) {
on_pri = 1;
} else if (self_check(shost)) {
on_sec = 1;
}
if (on_pri) {
datavol = pvol;
bmpvol = pbmp;
} else if (on_sec) {
datavol = svol;
bmpvol = sbmp;
} else {
rdc_err(NULL, gettext("Unable to determine whether current "
"node is primary or secondary"));
}
/* set up parms structure */
parms.command = RDC_CMD_DISABLE;
strncpy(parms.rdc_set->primary.intf, phost, MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->primary.file, pvol, NSC_MAXPATH);
strncpy(parms.rdc_set->secondary.intf, shost, MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->secondary.file, svol, NSC_MAXPATH);
ustatus = spcs_s_ucreate();
parms.options = RDC_OPT_FORCE_DISABLE;
/*
* We are now going to 'force' the kernel to disable the set. By
* setting the RDC_OPT_FORCE_DISABLE flag, the kernel will bypass some
* of the checks that are normally done when attempting to disable
* a set. We need to do this force option in a cluster environment
* when the logical hostname for the primary or secondary volume
* is no longer available.
*/
spcs_log("sndr", NULL, "%s sndradm -d %s %s %s %s %s %s",
gettext("FORCE DISABLE"), phost, pvol, pbmp, shost, svol, sbmp);
rdc_warn(NULL, gettext("Forcing set disable"));
if (RDC_IOCTL(RDC_CONFIG, &parms, 0, 0, 0, 0, ustatus) != SPCS_S_OK)
rdc_warn(&ustatus, gettext("set %s:%s not enabled in kernel"),
shost, svol);
/* if we get to this point, then a set was disabled. try sv-disable */
vc = nsc_lookup(volhash, datavol);
if (vc && (1 == vc->count))
if (cfg_vol_disable(cfg, datavol, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove data volume "
"[%s] from configuration"), datavol);
vc = nsc_lookup(volhash, bmpvol);
if (vc && (1 == vc->count))
if (cfg_vol_disable(cfg, bmpvol, ctag, "sndr") < 0)
rdc_warn(NULL, gettext("Failed to remove bitmap "
"[%s] from configuration"), bmpvol);
}
void
check_rdcsecondary(char *secondary)
{
int i;
CFGFILE *cfg;
int entries;
char **entry;
char *sha;
char *buf;
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("error opening config"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL, gettext("error locking config"));
entry = NULL;
entries = cfg_get_section(cfg, &entry, "ii");
for (i = 0; i < entries; i++) {
buf = entry[i];
(void) strtok(buf, " "); /* master */
sha = strtok(NULL, " "); /* shadow */
if (strcmp(secondary, sha) == 0) {
rdc_err(NULL,
gettext("secondary %s is in use by"
" Point-in-Time Copy"), secondary);
}
free(buf);
}
if (entries)
free(entry);
cfg_close(cfg);
}
int
main(int argc, char *argv[])
{
char config_file[FILENAME_MAX];
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 group[NSC_MAXPATH];
char ctag[MAX_RDC_HOST_SIZE];
char options_cfg[CFG_MAX_BUF];
char fromnetaddr[RDC_MAXADDR];
char tonetaddr[RDC_MAXADDR];
char tmphost[MAX_RDC_HOST_SIZE];
char tmpfile[NSC_MAXPATH];
char tmpbitmap[NSC_MAXPATH];
char diskqueue[NSC_MAXPATH];
char lhname[MAX_RDC_HOST_SIZE];
char mode[16];
rdc_version_t rdc_version;
int pairs;
int pid;
int flag = 0;
int fflag = 0;
int reverse = 0;
int nflag = 0;
int iflag = 0;
int doasync;
int pflag = 0;
int vflag = 0;
int verbose = 0;
int errflag = 0;
int cfgflag = 0;
int cfg_success;
int Iflag = 0;
char c;
char inval = 0;
int found;
int rc;
int geflag = 0;
int qflag = 0;
char *qarg;
int Bflag = 0;
char *bitfile;
CFGFILE *cfg = NULL;
int i;
int setnumber;
char key[CFG_MAX_KEY];
char buf[CFG_MAX_BUF];
char ctag_arg[MAX_RDC_HOST_SIZE];
char group_arg[NSC_MAXPATH];
int file_format = 0;
int sev;
int diskq_group = DISKQ_OKAY;
int extra_argc;
char *ctag_p, *group_p, *diskqueue_p;
char *required;
char *role_env;
int checksetfields = -1;
nsc_off_t boffset = 0;
int oflag = 0;
rdcconfig_t *sets = NULL;
rdcconfig_t *sets_p = NULL;
rdc_rc_t *rclist = NULL;
rdc_rc_t *rcp = NULL;
int host_not_found = 0;
(void) setlocale(LC_ALL, "");
(void) textdomain("rdc");
role_env = getenv("SNDR_ROLE_REVERSE");
if (role_env && strcmp(role_env, "sndr_allow_reverse") == 0)
allow_role = 1;
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);
}
if ((clustered = cfg_iscluster()) < 0) {
rdc_err(NULL, gettext("unable to ascertain environment"));
}
strcpy(ctag_arg, "");
strcpy(group_arg, "");
bzero(ctag, MAX_RDC_HOST_SIZE);
bzero(reconfig_ctag, MAX_RDC_HOST_SIZE);
bzero(diskqueue, NSC_MAXPATH);
rdc_maxsets = rdc_get_maxsets();
if (rdc_maxsets == -1) {
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);
}
bzero(group, sizeof (group));
bzero(diskqueue, sizeof (diskqueue));
qblock = 0;
while ((c =
#ifdef DEBUG
getopt(argc, argv, "A:B:C:D:EF:HIO:PRUW:a:bdef:g:hilmno:pq:rsuvw"))
#else
getopt(argc, argv, "A:B:C:D:EF:HIO:PRUW:a:bdef:g:hilmno:pq:rsuvw"))
#endif
!= -1) {
switch (c) {
case 'B':
if (!allow_role || flag) {
inval = 1;
break;
}
bitfile = optarg;
Bflag = 1;
flag = RDC_BITMAPOP;
break;
case 'H':
/* 'h' was already assigned */
if (flag)
inval = 1;
flag = RDC_CMD_HEALTH;
break;
case 'I':
/* List or edit ndr_ii configuration entries */
Iflag = 1;
break;
case 'R':
if (flag)
inval = 1;
flag = RDC_CMD_RECONFIG;
break;
#ifdef DEBUG
case 'U': /* UDP support */
proto_test = 1;
break;
#endif
case 'F':
if (flag && flag != RDC_CMD_TUNABLE)
inval = 1;
flag = RDC_CMD_TUNABLE;
if (check_intrange(optarg))
maxqfbas = atoi(optarg);
else
exit(1);
break;
case 'W':
if (flag && flag != RDC_CMD_TUNABLE)
inval = 1;
flag = RDC_CMD_TUNABLE;
if (check_intrange(optarg))
maxqitems = atoi(optarg);
else
exit(1);
break;
case 'A':
if (flag && flag != RDC_CMD_TUNABLE)
inval = 1;
flag = RDC_CMD_TUNABLE;
if (check_intrange(optarg))
asyncthr = atoi(optarg);
else
exit(1);
break;
case 'D':
if (flag && flag != RDC_CMD_TUNABLE)
inval = 1;
flag = RDC_CMD_TUNABLE;
if (set_qblock(optarg)) {
usage();
exit(1);
}
iflag |= qblock;
break;
case 'a':
if (flag && flag != RDC_CMD_TUNABLE)
inval = 1;
flag = RDC_CMD_TUNABLE;
if (strcmp(optarg, "off") == 0)
autosync = AUTOSYNC_OFF;
else if (strcmp(optarg, "on") == 0)
autosync = AUTOSYNC_ON;
else
inval = 1;
break;
case 'C':
if (clustered) {
strncpy(ctag_arg, optarg, MAX_RDC_HOST_SIZE);
process_clocal(ctag_arg);
} else
inval = 1;
break;
case 'g':
if (flag == RDC_CMD_ENABLE)
inval = 1;
geflag = 1;
strncpy(group_arg, optarg, NSC_MAXPATH);
verify_groupname(group_arg);
break;
case 'b':
/* ignore */
break;
case 'n':
nflag = 1;
break;
case 'd':
if (flag)
inval = 1;
flag = RDC_CMD_DISABLE;
break;
case 'e':
if (flag || geflag)
inval = 1;
flag = RDC_CMD_ENABLE;
iflag |= RDC_OPT_SETBMP;
break;
case 'E':
if (flag)
inval = 1;
flag = RDC_CMD_ENABLE;
iflag |= RDC_OPT_CLRBMP;
break;
case 'f':
fflag = 1;
strcpy(config_file, optarg);
break;
case 'h':
usage();
exit(0);
break;
case 'l':
if (flag)
inval = 1;
flag = RDC_CMD_LOG;
break;
case 'm':
if (flag)
inval = 1;
flag = RDC_CMD_COPY;
iflag |= RDC_OPT_FULL;
break;
case 'O':
case 'o':
if (!allow_role || oflag) {
inval = 1;
break;
}
if (c == 'o') {
oflag = RDC_BITMAPOR;
} else {
oflag = RDC_BITMAPSET;
}
boffset = strtoull(optarg, NULL, 0);
break;
case 'P':
if (flag)
inval = 1;
pflag = 1;
verbose = 1;
break;
case 'p':
if (flag)
inval = 1;
pflag = 1;
break;
case 'q':
if (flag)
inval = 1;
flag = RDC_CMD_INITQ;
qflag = optind;
qarg = optarg;
break;
case 'i':
if (flag)
inval = 1;
pflag = 1;
file_format = 1;
break;
case 'r':
reverse = 1;
iflag |= RDC_OPT_REVERSE;
break;
case 's':
if (flag)
inval = 1;
flag = RDC_CMD_STATUS;
nflag = 1; /* No prompt for a status */
break;
case 'u':
if (flag)
inval = 1;
flag = RDC_CMD_COPY;
iflag |= RDC_OPT_UPDATE;
break;
case 'v':
if (flag)
inval = 1;
pflag = 1;
vflag = 1;
break;
case 'w':
if (flag)
inval = 1;
flag = RDC_CMD_WAIT;
break;
case '?':
errflag++;
}
}
if (inval || ((flag != RDC_BITMAPOP) && oflag)) {
rdc_warn(NULL, gettext("invalid argument combination"));
errflag = 1;
}
if (flag && Iflag) {
/* Mutually incompatible */
usage();
exit(1);
}
if (Iflag) {
rdc_ii_config(argc, argv);
exit(0);
}
if (vflag) {
spcs_s_info_t ustatus;
ustatus = spcs_s_ucreate();
rc = RDC_IOCTL(RDC_VERSION, &rdc_version, 0, 0, 0, 0, ustatus);
if (rc == SPCS_S_ERROR) {
rdc_err(&ustatus, gettext("statistics error"));
}
spcs_s_ufree(&ustatus);
#ifdef DEBUG
(void) printf(gettext("Remote Mirror version %d.%d.%d.%d\n"),
rdc_version.major, rdc_version.minor,
rdc_version.micro, rdc_version.baseline);
#else
if (rdc_version.micro) {
(void) printf(gettext(
"Remote Mirror version %d.%d.%d\n"),
rdc_version.major,
rdc_version.minor,
rdc_version.micro);
} else {
(void) printf(gettext("Remote Mirror version %d.%d\n"),
rdc_version.major, rdc_version.minor);
}
#endif
exit(0);
}
if (!(flag || pflag) || errflag) {
usage();
exit(1);
}
if (pflag && !fflag && (argc - optind) == 0) {
/* print with no set specified */
exit(rdc_print(file_format, verbose,
group_arg, ctag_arg, NULL, NULL, NULL));
}
if (qflag) { /* change disk queue setting */
int subcmd = 0;
int offset = 0;
char *ptr;
char *qvol;
char tohost_arg[MAX_RDC_HOST_SIZE];
char tofile_arg[NSC_MAXPATH];
if (strcmp("a", qarg) == 0) {
subcmd = RDC_CMD_ADDQ;
offset = 1;
} else if (strcmp("d", qarg) == 0) {
subcmd = RDC_CMD_REMQ;
offset = 0;
} else if (strcmp("r", qarg) == 0) {
subcmd = RDC_CMD_REPQ;
offset = 1;
} else {
rdc_warn(NULL, " %s Invalid qopt", qarg);
q_usage(1);
exit(1);
}
if (strlen(group_arg) == 0) {
/* pick out single set as shost:svol */
ptr = strtok(argv[qflag + offset], ":");
if (ptr)
strncpy(tohost_arg, ptr, MAX_RDC_HOST_SIZE);
else {
rdc_warn(NULL, gettext("Bad host specified"));
q_usage(1);
exit(1);
}
ptr = strtok(NULL, ":");
if (ptr)
strncpy(tofile_arg, ptr, NSC_MAXPATH);
else {
rdc_warn(NULL, gettext("Bad set specified"));
q_usage(1);
exit(1);
}
}
qvol = argv[qflag];
if ((qvol == NULL) && (subcmd != RDC_CMD_REMQ)) {
rdc_warn(NULL, gettext("missing queue volume"));
q_usage(1);
exit(1);
}
diskq_subcmd(subcmd, qvol, group_arg, ctag_arg,
tohost_arg, tofile_arg);
exit(0);
}
if (flag == RDC_CMD_RECONFIG && !fflag) {
/* See what is to be reconfigured */
if (argc - optind == 0)
flag = RDC_CMD_RESET;
else {
if (argc - optind < 2) {
usage();
exit(1);
}
c = *argv[optind++];
if (argv[optind -1][1] != '\0') {
usage();
exit(2);
}
switch (c) {
case 'b':
if (argc - optind < 2) {
usage();
exit(1);
}
if (*argv[optind] == 'p')
reconfig_pbitmap = argv[++optind];
else if (*argv[optind] == 's')
reconfig_sbitmap = argv[++optind];
else {
usage();
exit(1);
}
optind++;
break;
#ifdef _RDC_CAMPUS
case 'd':
reconfig_direct = argv[optind++];
break;
#endif
case 'g':
reconfig_group = argv[optind++];
verify_groupname(reconfig_group);
break;
case 'C':
if (clustered) {
strncpy(reconfig_ctag, argv[optind++],
MAX_RDC_HOST_SIZE);
process_clocal(reconfig_ctag);
} else {
usage();
exit(1);
}
break;
case 'm':
if (strcmp(argv[optind], "sync") == 0)
reconfig_doasync = 0;
else if (strcmp(argv[optind], "async") == 0)
reconfig_doasync = 1;
else {
usage();
exit(1);
}
optind++;
break;
case 'r':
if (allow_role) {
iflag |= RDC_OPT_REVERSE_ROLE;
break;
}
default:
usage();
exit(1);
}
}
}
if (fflag) {
checksetfields = 1;
if ((argc - optind) != 0) {
usage();
exit(1);
}
} else {
if ((argc - optind) == 0) {
/* Use libcfg to figure out what to operate on */
cfgflag = 1;
#ifdef DEBUG
rdc_warn(NULL, gettext("using current config"));
#endif
checksetfields = 0;
} else {
if ((argc - optind) < 8 && (argc - optind) != 1) {
usage();
exit(1);
}
}
}
if (cfgflag) {
if (flag == RDC_CMD_ADDQ ||
flag == RDC_CMD_REMQ ||
flag == RDC_CMD_KILLQ ||
flag == RDC_CMD_INITQ) {
rdc_err(NULL, gettext("can not use current config "
"for disk queue operations"));
}
} else if (fflag) {
if (flag == RDC_CMD_ADDQ ||
flag == RDC_CMD_REMQ ||
flag == RDC_CMD_KILLQ ||
flag == RDC_CMD_INITQ) {
rdc_err(NULL, gettext("can not use a config file "
"for disk queue operations"));
}
}
if (cfgflag) {
if (flag == RDC_CMD_ENABLE) {
rdc_err(NULL, gettext("can not use current config "
"for enable command"));
}
if ((flag == RDC_CMD_RECONFIG) && (reconfig_pbitmap ||
reconfig_sbitmap)) {
rdc_err(NULL, gettext("can not use current config "
"for bitmap reconfiguration"));
}
if (flag == RDC_BITMAPOP) {
rdc_err(NULL, gettext("can not use current config "
"for bitmap set command"));
}
pairs = read_libcfg(flag, group_arg, ctag_arg);
if (pairs == 0) {
(void) fprintf(stderr,
gettext("no matching Remote Mirror sets found "
"in config\n"));
exit(1);
}
} else if (!fflag) {
/*
* Format is either:
*
* tohost:tofile
*
* or something like this for example:
*
* fromhost fromfile frombitmap tohost tofile tobitmap ip sync
* g group C ctag
*/
if (argc - optind == 1) {
char tohost_arg[MAX_RDC_HOST_SIZE];
char tofile_arg[NSC_MAXPATH];
char *ptr;
checksetfields = 0;
if (flag == RDC_CMD_ENABLE) {
rdc_err(NULL,
gettext("must specify full set details for "
"enable command"));
}
ptr = strtok(argv[optind], ":");
if (ptr)
strncpy(tohost_arg, ptr, MAX_RDC_HOST_SIZE);
else {
rdc_err(NULL, gettext("Bad host specified"));
}
ptr = strtok(NULL, ":");
if (ptr)
strncpy(tofile_arg, ptr, NSC_MAXPATH);
else {
rdc_err(NULL, gettext("Bad set specified"));
}
/* Now look up tohost:tofile via libcfg */
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL,
gettext("unable to lock configuration"));
setnumber = 0;
found = 0;
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
bzero(buf, CFG_MAX_BUF);
(void) snprintf(key, sizeof (key),
"sndr.set%d", setnumber);
rc = cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF);
if (rc < 0)
break;
(void) snprintf(key, sizeof (key),
"sndr.set%d.shost", setnumber);
(void) cfg_get_cstring(cfg, key, tohost,
sizeof (tohost));
if (strncmp(tohost, tohost_arg, NSC_MAXPATH))
continue;
(void) snprintf(key, sizeof (key),
"sndr.set%d.secondary", setnumber);
(void) cfg_get_cstring(cfg, key, tofile,
sizeof (tofile));
if (strncmp(tofile, tofile_arg, NSC_MAXPATH))
continue;
found = 1;
(void) snprintf(key, sizeof (key),
"sndr.set%d.phost", setnumber);
(void) cfg_get_cstring(cfg, key, fromhost,
sizeof (fromhost));
(void) snprintf(key, sizeof (key),
"sndr.set%d.primary", setnumber);
(void) cfg_get_cstring(cfg, key, fromfile,
sizeof (fromfile));
(void) snprintf(key, sizeof (key),
"sndr.set%d.pbitmap", setnumber);
(void) cfg_get_cstring(cfg, key, frombitmap,
sizeof (frombitmap));
(void) snprintf(key, sizeof (key),
"sndr.set%d.sbitmap", setnumber);
(void) cfg_get_cstring(cfg, key, tobitmap,
sizeof (tobitmap));
(void) snprintf(key, sizeof (key),
"sndr.set%d.type", setnumber);
(void) cfg_get_cstring(cfg, key, directfile,
sizeof (directfile));
if (strcmp(directfile, "ip") == 0)
strcpy(directfile, "");
(void) snprintf(key, sizeof (key),
"sndr.set%d.mode", setnumber);
(void) cfg_get_cstring(
cfg, key, mode, sizeof (mode));
(void) snprintf(key, sizeof (key),
"sndr.set%d.group", setnumber);
(void) cfg_get_cstring(cfg, key, group,
sizeof (group));
if (strcmp(group_arg, "") &&
strncmp(group_arg, group, NSC_MAXPATH))
continue;
(void) snprintf(key, sizeof (key),
"sndr.set%d.cnode", setnumber);
(void) cfg_get_cstring(
cfg, key, ctag, sizeof (ctag));
if ((strlen(ctag_arg) > 0) &&
(strcmp(ctag_arg, ctag) != 0))
rdc_err(NULL,
gettext("ctags %s and %s "
"do not match"), ctag_arg, ctag);
if (strcmp(mode, "sync") == 0)
doasync = 0;
else if (strcmp(mode, "async") == 0)
doasync = 1;
else {
rdc_err(NULL,
gettext("set %s:%s neither sync "
"nor async"), tohost, tofile);
}
break;
}
cfg_close(cfg);
if (!found) {
rdc_err(NULL,
gettext("set %s:%s not found in config"),
tohost_arg, tofile_arg);
}
} else {
checksetfields = 1;
strncpy(fromhost, argv[optind], MAX_RDC_HOST_SIZE);
strncpy(fromfile, argv[optind+1], NSC_MAXPATH);
strncpy(frombitmap, argv[optind+2], NSC_MAXPATH);
strncpy(tohost, argv[optind+3], MAX_RDC_HOST_SIZE);
strncpy(tofile, argv[optind+4], NSC_MAXPATH);
strncpy(tobitmap, argv[optind+5], NSC_MAXPATH);
/* Check the length of entries from the command line */
if ((fromhost[MAX_RDC_HOST_SIZE - 1] != '\0') ||
(tohost[MAX_RDC_HOST_SIZE - 1] != '\0')) {
rdc_err(NULL,
gettext("hostname is longer than %d "
"characters\n"), (MAX_RDC_HOST_SIZE - 1));
}
/* Check if it's ip address -- not allowed */
if ((inet_addr(fromhost) != (in_addr_t)(-1)) ||
(inet_addr(tohost) != (in_addr_t)(-1))) {
rdc_err(NULL, gettext(
"The hostname specified is invalid.\n"
"See 'man inet(3SOCKET)'"));
}
if ((fromfile[NSC_MAXPATH - 1] != '\0') ||
(tofile[NSC_MAXPATH - 1] != '\0') ||
(frombitmap[NSC_MAXPATH - 1] != '\0') ||
(tobitmap[NSC_MAXPATH - 1] != '\0')) {
rdc_err(NULL, gettext("device name is longer "
"than %d characters\n"), (NSC_MAXPATH - 1));
}
#ifdef _RDC_CAMPUS
if (argv[optind+6][0] == '/') {
/* FCAL directio */
strncpy(directfile, argv[optind+6],
NSC_MAXPATH);
} else if (strcmp(argv[optind+6], "ip") != 0) {
#else
if (strcmp(argv[optind+6], "ip") != 0) {
#endif
usage();
exit(1);
} else
strcpy(directfile, "ip");
if (strcmp(argv[optind+7], "sync") == 0)
doasync = 0;
else if (strcmp(argv[optind+7], "async") == 0)
doasync = 1;
else {
usage();
exit(1);
}
/*
* At this point, we could have a set which is
* clustered, but neither a 'C ctag' or '-C ctag' has
* been specified. To avoid clobbering the ctag if a
* dscfg operation is done in the future, we should get
* the ctag out of the config at this point. To do this,
* set the cluster resource filter to NULL to look at
* all sets in the config, pulling out the ctag for the
* set matching shost:svol. If the set is not found,
* fail here. Note, we skip this set on an enable as the
* set is not yet in the config, so no need to waste
* time.
*/
if ((argc - optind == 8) && clustered &&
(flag != RDC_CMD_ENABLE)) {
int setnumber;
char key[CFG_MAX_KEY];
if ((cfg = cfg_open(NULL)) == NULL) {
rdc_err(NULL,
gettext("unable to access configuration"));
}
if (!cfg_lock(cfg, CFG_RDLOCK)) {
rdc_err(NULL,
gettext("unable to lock configuration"));
}
cfg_resource(cfg, NULL);
if ((setnumber =
find_setnumber_in_libcfg(cfg, NULL, tohost,
tofile)) < 0) {
cfg_close(cfg);
rdc_err(NULL,
gettext("unable to find Remote "
"Mirror set "
"%s:%s in config"),
tohost, tofile);
}
(void) snprintf(key, sizeof (key),
"sndr.set%d.cnode", setnumber);
if (cfg_get_cstring(cfg, key, ctag_arg,
MAX_RDC_HOST_SIZE) < 0) {
cfg_close(cfg);
rdc_err(NULL,
gettext("unable to determine ctag "
"for Remote Mirror set %s:%s"),
tohost, tofile);
}
rdc_islocal = strcmp(ctag_arg, "-") ? 0 : 1;
cfg_close(cfg);
}
extra_argc = argc - optind;
if (extra_argc < 8 || extra_argc > 14 ||
extra_argc % 2 != 0) {
usage();
exit(1);
}
/*
* Loop through all of the extra arguments specified
* on the command line, setting the appropriate values
* for valid entries. If an unrecognized argument is
* detected, abort with error. Note: This hack should be
* removed and we should not accept these entries as
* arguments, they should be passed in as switches.
*/
for (i = (8 + optind); i < argc; i += 2) {
/* string case statement */
if (strcmp(argv[i], "g") == 0) {
strncpy(group, argv[i + 1], NSC_MAXPATH);
if (group[NSC_MAXPATH - 1] != '\0') {
rdc_err(NULL, gettext("group name is "
"longer than %d characters\n"),
(NSC_MAXPATH - 1));
}
} else if (strcmp(argv[i], "C") == 0) {
if (!clustered) {
usage();
exit(1);
}
strncpy(ctag, argv[i + 1],
MAX_RDC_HOST_SIZE);
if (ctag[MAX_RDC_HOST_SIZE - 1] != '\0') {
rdc_err(NULL, gettext("cluster name "
"is longer than %d characters\n"),
(MAX_RDC_HOST_SIZE - 1));
}
process_clocal(ctag);
/*
* well here is something.
* what if they went sndradm -C local
* host a b host a b ip sync C foobar?
* they might be confused
* lets stop them if ctag_arg and ctag
* don't match and forgive if they are
* the same, below also.
*/
if ((strlen(ctag_arg) > 0) &&
(strcmp(ctag_arg, ctag) != 0)) {
rdc_err(NULL, gettext("ctags "
"%s and %s do not match "),
ctag_arg, ctag);
}
} else if (strcmp(argv[i], "q") == 0) {
strncpy(diskqueue, argv[i + 1],
NSC_MAXPATH);
if (diskqueue[NSC_MAXPATH - 1] != '\0') {
rdc_err(NULL, gettext("diskq name is "
"longer than %d characters\n"),
(NSC_MAXPATH - 1));
}
} else {
/* Unrecognized argument */
usage();
exit(1);
}
}
}
/*
* Are we able to determine the existance of either
* of these host addresses?
*/
if (gethost_netaddrs(fromhost, tohost,
(char *)&fromnetaddr, (char *)&tonetaddr) < 0) {
(void) fprintf(stderr, "\n");
rdc_warn(NULL, gettext("unable to determine IP "
"addresses for either host %s or host %s"),
fromhost, tohost);
if (flag != RDC_CMD_DISABLE)
exit(1);
else
host_not_found = 1;
}
/*
* Are we running on neither host?
*/
if (!self_check(fromhost) && !self_check(tohost)) {
if (flag == RDC_CMD_DISABLE) {
(void) fprintf(stderr, "\n");
rdc_warn(NULL, gettext("Not running on either host "
"%s or host %s"), fromhost, tohost);
host_not_found = 1;
}
}
/*
* at this point, hopfully it is safe to say that
* if a ctag was supplied via -C tag it is safe to
* move it from ctag_arg to ctag. If it was passed in
* at the end and the beginning of the cli, it must
* match, as per checks above. if it was not passed
* in at the end, but at the beginning, we can deal.
* this should handle the case of shost:svol.
* which is the main reason for this.
*
* there are 3 cases: passed in by cli, checked just above.
* using libdscfg, you must pass in -C tag to have
* ctag_check pass.
* finally a file. same rules as libdscfg.
*/
if ((strlen(ctag) == 0) && (strlen(ctag_arg) > 0))
(void) strcpy(ctag, ctag_arg);
if (flag == RDC_CMD_RECONFIG) {
if (reconfig_pbitmap) {
strncpy(frombitmap, reconfig_pbitmap,
NSC_MAXPATH);
check_rdcbitmap(flag, fromhost, frombitmap);
}
if (reconfig_sbitmap) {
strncpy(tobitmap, reconfig_sbitmap,
NSC_MAXPATH);
check_rdcbitmap(flag, tohost, tobitmap);
}
#ifdef _RDC_CAMPUS
if (reconfig_direct)
strncpy(directfile, reconfig_direct,
NSC_MAXPATH);
#endif
if (reconfig_group)
strncpy(group, reconfig_group, NSC_MAXPATH);
if (strlen(reconfig_ctag) > 0)
strncpy(ctag, reconfig_ctag,
MAX_RDC_HOST_SIZE);
if (reconfig_doasync != -1)
doasync = reconfig_doasync;
}
if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
if (ctag_check(fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap, ctag, diskqueue) < 0)
exit(1);
if ((diskq_group = check_diskqueue(NULL, diskqueue,
group)) == DISKQ_FAIL) {
rdc_err(NULL, gettext("disk queue %s is "
"incompatible with existing queue"),
diskqueue);
}
}
pairs = 1;
} else {
pairs = read_config(flag, config_file, group_arg, ctag_arg);
if (pairs == 0) {
rdc_err(NULL, gettext("%s contains no "
"matching Remote Mirror sets"), config_file);
}
}
if (!nflag && !pflag && prompt_user(flag, iflag) == -1)
exit(1);
while (pairs--) {
if (cfgflag || fflag) {
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(group, pair_list[pairs].group, NSC_MAXPATH);
strncpy(ctag, pair_list[pairs].ctag, MAX_RDC_HOST_SIZE);
strncpy(diskqueue, pair_list[pairs].diskqueue,
NSC_MAXPATH);
bcopy(pair_list[pairs].fnetaddr, fromnetaddr,
RDC_MAXADDR);
bcopy(pair_list[pairs].tnetaddr, tonetaddr,
RDC_MAXADDR);
doasync = pair_list[pairs].doasync;
}
if (pflag) {
static int first = 1;
if (first) {
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL,
gettext("unable to lock configuration"));
first = 0;
}
(void) rdc_print(file_format, verbose,
group_arg, ctag_arg, tohost, tofile, cfg);
if (pairs == 0) {
cfg_close(cfg);
exit(0);
}
/* short circuit the rest of the command loop */
continue;
}
if (Bflag) {
int ret;
ret = rdc_bitmapset(tohost, tofile, bitfile, oflag,
boffset);
exit(ret);
}
if ((fflag || cfgflag) && flag == RDC_CMD_RECONFIG) {
char orig_fbmp[MAXHOSTNAMELEN];
char orig_tbmp[MAXHOSTNAMELEN];
int ret;
rdc_config_t parms;
spcs_s_info_t ustatus;
parms.command = RDC_CMD_STATUS;
parms.rdc_set->netconfig = NULL;
strncpy(parms.rdc_set->primary.intf, fromhost,
MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->secondary.intf, tohost,
MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->primary.file, fromfile,
NSC_MAXPATH);
strncpy(parms.rdc_set->secondary.file, tofile,
NSC_MAXPATH);
ustatus = spcs_s_ucreate();
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
rdc_err(NULL, gettext("unable to get set status"
" before reconfig operation"));
}
strncpy(orig_fbmp, parms.rdc_set->primary.bitmap,
NSC_MAXPATH);
strncpy(orig_tbmp, parms.rdc_set->secondary.bitmap,
NSC_MAXPATH);
if (strncmp(orig_fbmp, frombitmap, NSC_MAXPATH) != 0)
check_rdcbitmap(flag, fromhost, frombitmap);
if (strncmp(orig_tbmp, tobitmap, NSC_MAXPATH) != 0)
check_rdcbitmap(flag, tohost, tobitmap);
spcs_s_ufree(&ustatus);
}
/*
* take a peek in the config to see if
* the bitmap is being used elsewhere
*/
if (flag == RDC_CMD_ENABLE) {
struct stat stb;
/*
* just for fun, lets see if some silly person
* specified the same vol and bitmap
*/
if ((strcmp(fromfile, frombitmap) == 0) ||
(strcmp(tofile, tobitmap) == 0))
rdc_err(NULL, gettext("volumes and bitmaps"
" must not match"));
if (self_check(fromhost)) {
check_rdcbitmap(flag, fromhost, frombitmap);
if (stat(fromfile, &stb) != 0) {
rdc_err(NULL,
gettext("unable to access %s: %s"),
fromfile, strerror(errno));
}
if (!S_ISCHR(stb.st_mode)) {
rdc_err(NULL,
gettext("%s is not a character device"),
fromfile);
}
} else { /* on the secondary */
check_rdcbitmap(flag, tohost, tobitmap);
/* extra check for secondary vol */
check_rdcsecondary(tofile);
if (stat(tofile, &stb) != 0) {
rdc_err(NULL,
gettext("unable to access %s: %s"),
tofile, strerror(errno));
}
if (!S_ISCHR(stb.st_mode)) {
rdc_err(NULL,
gettext("%s is not a character device"),
tofile);
}
}
}
if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_DISABLE ||
flag == RDC_CMD_RECONFIG) {
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL,
gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_WRLOCK))
rdc_err(NULL,
gettext("unable to lock configuration"));
cfg_resource(cfg, clustered ? ctag : NULL);
} else
cfg = NULL;
if (cfg && perform_autosv() &&
(flag == RDC_CMD_ENABLE || flag == RDC_CMD_DISABLE ||
flag == RDC_CMD_RECONFIG)) {
if (cfg_load_svols(cfg) < 0 ||
cfg_load_dsvols(cfg) < 0 ||
cfg_load_shadows(cfg) < 0)
rdc_err(NULL,
gettext("Unable to parse config filer"));
load_rdc_vols(cfg);
}
cfg_success = (cfg == NULL);
if (cfg && flag == RDC_CMD_ENABLE) {
/* Enabled, so add the set via libcfg */
/* Build a new sndr entry and put it */
group_p = *group? group : place_holder;
diskqueue_p = *diskqueue? diskqueue : place_holder;
if ((diskqueue_p == place_holder) &&
(group_p != place_holder)) {
get_group_diskq(cfg, group_p, diskqueue);
if (*diskqueue)
diskqueue_p = diskqueue;
}
/*
* format in pconfig is:
* phost.primary.pbitmap.shost.secondary.
* sbitmap.type.mode.group.cnode.options.diskq
*/
(void) snprintf(buf, sizeof (buf),
"%s %s %s %s %s %s %s %s %s %s - %s",
fromhost, fromfile, frombitmap, tohost, tofile,
tobitmap, directfile,
doasync? "async" : "sync", group_p,
clustered? ctag : "-", diskqueue_p);
if (cfg_put_cstring(cfg, "sndr", buf, strlen(buf)) < 0)
rdc_warn(NULL,
gettext("unable to add \"%s\" to "
"configuration storage: %s"),
buf, cfg_error(&sev));
setnumber = find_setnumber_in_libcfg(cfg, clustered?
ctag : NULL, tohost, tofile);
if (setnumber < 0)
rdc_warn(NULL,
gettext("unable to add \"%s\" to "
"configuration storage: %s"),
diskqueue_p, cfg_error(&sev));
else
cfg_success = 1;
/* Add cluster aware info */
if (clustered && !rdc_islocal) {
(void) snprintf(key, sizeof (key),
"sndr.set%d.options", setnumber);
if (self_check(fromhost)) {
if (cfg_put_options(cfg, CFG_SEC_CONF,
key, "lghn", fromhost) < 0) {
rdc_err(NULL,
gettext("unable to add "
"\"%s\" to configuration "
"storage: %s"),
fromhost, cfg_error(&sev));
}
} else if (self_check(tohost)) {
if (cfg_put_options(cfg, CFG_SEC_CONF,
key, "lghn", tohost) < 0) {
rdc_err(NULL,
gettext("unable to add "
"\"%s\" to configuration "
"storage: %s"),
fromhost, cfg_error(&sev));
}
}
}
} else if (cfg && flag == RDC_CMD_DISABLE) {
found = 0;
/* Disabled, so delete the set via libcfg */
/* get sndr entries until shost, sfile match */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
(void) snprintf(key, sizeof (key), "sndr.set%d",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0) {
break;
}
(void) snprintf(key, sizeof (key),
"sndr.set%d.secondary", setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, tofile) != 0)
continue;
(void) snprintf(key, sizeof (key),
"sndr.set%d.shost",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, tohost) != 0)
continue;
found = 1;
#ifdef DEBUG
if (checksetfields == -1) {
rdc_err(NULL,
gettext("checksetfields not set"));
}
#endif
if (checksetfields) {
checkgfields(cfg, setnumber, fromhost,
fromfile, frombitmap, tobitmap,
directfile, (doasync == 1)
? "async" : "sync", group, ctag,
diskqueue);
}
/* perform cluster specific options */
if (clustered) {
/* get the logical host, if set */
(void) snprintf(key, sizeof (key),
"sndr.set%d.options", setnumber);
(void) cfg_get_single_option(cfg,
CFG_SEC_CONF, key, "lghn",
lhname, MAX_RDC_HOST_SIZE);
/* figure out the cluster tag, if any */
(void) snprintf(key, sizeof (key),
"sndr.set%d.cnode", setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, ctag))
rdc_err(NULL, gettext("ctags %s"
" and %s do not match"),
buf, ctag);
} else {
*lhname = '\0';
*ctag = '\0';
}
/* figure out the disk queue, if any */
(void) snprintf(key, sizeof (key),
"sndr.set%d.diskq",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strlen(buf) > 0) {
strncpy(diskqueue, buf, NSC_MAXPATH);
} else {
*diskqueue = '\0';
}
(void) snprintf(key, sizeof (key), "sndr.set%d",
setnumber);
if (cfg_put_cstring(cfg, key, NULL, 0) < 0)
rdc_warn(NULL,
gettext("unable to remove \"%s\" "
"from configuration storage: %s"),
buf, cfg_error(&sev));
else
cfg_success = 1;
break;
}
if (found == 0) {
rdc_err(NULL,
gettext("Unable to find %s:%s in "
"configuration storage"),
tohost, tofile);
}
if (host_not_found) {
rdc_force_disable(cfg, fromhost, fromfile,
frombitmap, tohost, tofile, tobitmap, ctag,
lhname);
if (cfg_commit(cfg) < 0)
rdc_err(NULL, gettext("commit on "
"force disable failed"));
cfg_close(cfg);
return (0);
}
} else if (cfg && flag == RDC_CMD_RECONFIG) {
/* Update relevant cfg record */
cfg_resource(cfg, NULL);
/* get sndr entries until shost, sfile match */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
(void) snprintf(key, sizeof (key), "sndr.set%d",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0) {
break;
}
(void) snprintf(key, sizeof (key),
"sndr.set%d.secondary", setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, tofile) != 0)
continue;
(void) snprintf(key, sizeof (key),
"sndr.set%d.shost",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (strcmp(buf, tohost) != 0)
continue;
(void) snprintf(key, sizeof (key),
"sndr.set%d.cnode",
setnumber);
if (cfg_get_cstring(cfg, key, buf,
CFG_MAX_BUF) < 0)
break;
if (reconfig_ctag[0] == '\0')
strncpy(ctag, buf, sizeof (ctag));
if (doasync)
strcpy(mode, "async");
else
strcpy(mode, "sync");
if (strcmp(directfile, "") == 0)
strcpy(directfile, "ip");
group_p = strlen(group) > 0 ? group :
place_holder;
/*
* if we are reconfigging out altogether,
* get rid of the diskqueue
*/
if (group_p == place_holder)
diskqueue_p = place_holder;
else
diskqueue_p = strlen(diskqueue) > 0 ?
diskqueue : place_holder;
/*
* do a little diskq dance here for reconfigs
* that did not specify the diskqueue whilst
* reconfigging ...
*/
if ((diskqueue_p == place_holder) &&
(group_p != place_holder)) {
get_group_diskq(cfg, group_p,
diskqueue);
diskqueue_p = strlen(diskqueue) > 0 ?
diskqueue : place_holder;
}
(void) snprintf(key, sizeof (key),
"sndr.set%d.options", setnumber);
if (cfg_get_cstring(cfg, key, options_cfg,
CFG_MAX_BUF) < 0) {
break;
}
ctag_p = strlen(ctag) > 0 ?
ctag : place_holder;
(void) snprintf(buf, sizeof (buf),
"%s %s %s %s %s %s %s %s %s %s %s %s",
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
directfile, mode, group_p,
ctag_p, options_cfg, diskqueue_p);
(void) snprintf(key, sizeof (key), "sndr.set%d",
setnumber);
if (cfg_put_cstring(cfg, key, buf,
strlen(buf)) < 0)
rdc_warn(NULL,
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
buf, cfg_error(&sev));
else
cfg_success = 1;
break;
}
}
if (cfg_success) {
if (cfg && perform_autosv()) {
if (self_check(fromhost)) {
if (diskqueue[0] &&
(strcmp(diskqueue, fromfile) == 0) ||
(strcmp(diskqueue, frombitmap) == 0)) {
rdc_err(NULL, gettext("disk "
"queue volume %s must not "
"match any primary Remote "
"Mirror volume or bitmap"),
diskqueue);
}
if (diskqueue[0]) {
different_devs(fromfile, diskqueue);
different_devs(frombitmap, diskqueue);
validate_name(cfg, diskqueue);
}
different_devs(fromfile, frombitmap);
validate_name(cfg, fromfile);
validate_name(cfg, frombitmap);
} else {
different_devs(tofile, tobitmap);
validate_name(cfg, tofile);
validate_name(cfg, tobitmap);
}
}
/*
* okay, if the command is sync, just build
* a list of rdcconfig_t's after the pairs--
* loop is done, we will pass this list to
* librdc to multithread the syncs (after
* forking off a daemonish type process
* that waits for the libcall to complete
* ints of interest:
* flag ie RDC_CMD_COPY, iflag RDC_OPT_UPDATE,
* reverse RDC_OPT_REVERSE, RDC_OPT_FORWARD
* if necessary, turn autosync back on
*/
if (flag == RDC_CMD_COPY) {
if (autosync_is_on(tohost, tofile) ==
AUTOSYNC_ON)
enable_autosync(fromhost, fromfile,
tohost, tofile);
if (sets == NULL) {
sets_p = sets =
rdc_alloc_config(fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap, "mode", "group", "ctag",
"options", 0);
if (sets_p == NULL) {
rdc_err(NULL,
gettext("rdc config alloc"
"failed %s"), rdc_error(NULL));
}
continue;
}
sets_p = sets_p->next =
rdc_alloc_config(fromhost, fromfile,
frombitmap, tohost, tofile, tobitmap,
"mode", "group", "ctag", "options", 0);
if (sets_p == NULL) {
rdc_err(NULL, gettext("rdc config alloc"
"failed %s"), rdc_error(NULL));
}
continue;
}
/*
* block incoming signals until after the possible
* cfg_commit is done
*/
block_sigs();
if (rdc_operation(cfg, fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap, flag, iflag, directfile,
group, ctag, diskqueue, &doasync, reverse) < 0) {
;
/*EMPTY*/
} else if (cfg) {
if (diskq_group == DISKQ_REWRITEG) {
rewrite_group_diskqueue(cfg,
&pair_list[pairs], diskqueue);
}
if (perform_autosv() &&
(flag == RDC_CMD_ENABLE ||
flag == RDC_CMD_DISABLE ||
flag == RDC_CMD_RECONFIG)) {
unload_rdc_vols();
cfg_unload_shadows();
cfg_unload_dsvols();
cfg_unload_svols();
}
if ((iflag & RDC_OPT_REVERSE_ROLE) != 0 &&
allow_role) {
bzero(tmphost, MAX_RDC_HOST_SIZE);
bzero(tmpfile, NSC_MAXPATH);
bzero(tmpbitmap, NSC_MAXPATH);
strncpy(tmphost, fromhost,
MAX_RDC_HOST_SIZE);
strncpy(tmpfile, fromfile, NSC_MAXPATH);
strncpy(tmpbitmap, frombitmap,
NSC_MAXPATH);
strncpy(fromhost, tohost,
MAX_RDC_HOST_SIZE);
strncpy(fromfile, tofile, NSC_MAXPATH);
strncpy(frombitmap, tobitmap,
NSC_MAXPATH);
strncpy(tohost, tmphost,
MAX_RDC_HOST_SIZE);
strncpy(tofile, tmpfile, NSC_MAXPATH);
strncpy(tobitmap, tmpbitmap,
NSC_MAXPATH);
group_p = strlen(group) > 0 ? group :
place_holder;
diskqueue_p = strlen(diskqueue) > 0 ?
diskqueue : place_holder;
ctag_p = strlen(ctag) > 0 ?
ctag : place_holder;
(void) snprintf(buf, sizeof (buf), "%s "
"%s %s %s %s %s %s %s %s %s %s %s",
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
directfile, mode, group_p,
ctag_p, options_cfg, diskqueue_p);
(void) snprintf(key, sizeof (key),
"sndr.set%d", setnumber);
if (cfg_put_cstring(cfg, key, buf,
strlen(buf)) < 0)
rdc_err(NULL,
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
buf, cfg_error(&sev));
}
if (cfg_commit(cfg) < 0) {
rdc_err(NULL, gettext("commit on role "
"reversal failed"));
}
}
unblock_sigs();
}
if (cfg) {
cfg_close(cfg);
}
}
if (flag == RDC_CMD_COPY) {
pid = fork();
if (pid == -1) { /* error forking */
perror("fork");
exit(1);
}
} else {
exit(0);
}
if (pid > 0) /* parent process */
exit(0);
spcslog_sync(sets, 1, iflag);
if (iflag & RDC_OPT_REVERSE) {
if (iflag & RDC_OPT_UPDATE)
rclist = rdc_ursync(sets);
else
rclist = rdc_rsync(sets);
} else if (iflag & RDC_OPT_UPDATE) {
rclist = rdc_usync(sets);
} else
rclist = rdc_fsync(sets);
rcp = rclist;
while (rcp) {
if (rcp->rc < 0) {
/* rclist->msg has already been gettext'd */
(void) fprintf(stderr,
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
rcp->set.phost, rcp->set.pfile, rcp->set.pbmp,
rcp->set.shost, rcp->set.sfile, rcp->set.sbmp);
rdc_warn(NULL, "%s", rcp->msg);
spcs_log("sndr", NULL, "%s", rcp->msg);
}
rcp = rcp->next;
}
spcslog_sync(sets, 0, iflag);
if (sets)
rdc_free_config(sets, RDC_FREEALL);
if (rclist)
rdc_free_rclist(rclist);
return (0);
}
/*
* process_clocal()
* pre: a non null string
* post: if the string is "local"
* then it is converted to "-"
* and rdc_islocal is set to 1
* if not rdc_islocal set to 0
*/
void
process_clocal(char *ctag)
{
/*
* Check for the special cluster tag and convert into the
* internal representation.
*/
if (ctag != NULL && strcmp(ctag, RDC_LOCAL_TAG) == 0) {
strcpy(ctag, "-");
rdc_islocal = 1;
} else {
rdc_islocal = 0;
}
}
static void
rdc_check_dgislocal(char *dgname)
{
char *othernode;
int rc;
/*
* check where this disk service is mastered
*/
rc = cfg_dgname_islocal(dgname, &othernode);
if (rc < 0) {
rdc_err(NULL, gettext("unable to find "
"disk service, %s: %s"), dgname, strerror(errno));
}
if (rc == 0) {
rdc_err(NULL, gettext("disk service, %s, is "
"active on node \"%s\"\nPlease re-issue "
"the command on that node"), dgname, othernode);
}
}
static void
different_devs(char *dev1, char *dev2)
{
struct stat buf1, buf2;
if (stat(dev1, &buf1) < 0) {
spcs_log("sndr", NULL, gettext("Remote Mirror: can't stat %s"),
dev1);
rdc_err(NULL, gettext("Remote Mirror: can't stat %s"), dev1);
}
if (stat(dev2, &buf2) < 0) {
spcs_log("sndr", NULL, gettext("Remote Mirror: can't stat %s"),
dev2);
rdc_err(NULL, gettext("Remote Mirror: can't stat %s"), dev2);
}
if (buf1.st_rdev == buf2.st_rdev) {
spcs_log("sndr", NULL, gettext("Remote Mirror: '%s' and '%s' "
"refer to the same device"), dev1, dev2);
rdc_err(NULL, gettext("Remote Mirror: '%s' and '%s' refer to "
"the same device"), dev1, dev2);
}
}
static void
validate_name(CFGFILE *cfg, char *vol)
{
char *altname;
int rc;
if (!cfg) {
rdc_err(NULL, gettext("Remote Mirror: null cfg ptr in "
"validate_name"));
}
rc = cfg_get_canonical_name(cfg, vol, &altname);
if (rc < 0) {
spcs_log("sndr", NULL, gettext("Remote Mirror: unable to parse "
"config file\n"));
rdc_err(NULL, gettext("Remote Mirror: unable to parse config "
"file\n"));
}
if (rc) {
spcs_log("sndr", NULL, gettext("Remote Mirror: '%s': already "
"configured as '%s'"), vol, altname);
rdc_err(NULL, gettext("Remote Mirror: The volume '%s' has been "
"configured previously as '%s'. Re-enter command with "
"the latter name."), vol, altname);
}
}
/*
* Add the autosync value to the option field for the sndr set specified by
* tohost:tofile.
*
* ASSUMPTIONS:
* - cfg file is available to take a write lock.
* - set is already configured in dscfg
*
* INPUTS:
* autosync_val - value to set autosync to
* tohost - secondary host
* tofile - secondary volume
*
* OUTPUTS:
* none.
*
*/
static void
set_autosync(int autosync_val, char *tohost, char *tofile, char *ctag)
{
CFGFILE *cfg;
char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
char tag[CFG_MAX_BUF], val[CFG_MAX_BUF];
char auto_tag[CFG_MAX_BUF];
_sd_dual_pair_t pair;
_sd_dual_pair_t tmpair;
int setnumber, options = 0, already_set = 0, cfg_success = 0;
int set;
/* verify valid autosync request */
if ((autosync_val != AUTOSYNC_ON) && (autosync_val != AUTOSYNC_OFF)) {
#ifdef DEBUG
rdc_warn(NULL,
gettext("set_autosync called with improper value"));
#endif
return;
}
if ((cfg = cfg_open(NULL)) == NULL) {
rdc_err(NULL, gettext("unable to access configuration"));
}
if (!cfg_lock(cfg, CFG_WRLOCK)) {
rdc_err(NULL, gettext("unable to lock configuration"));
}
if (clustered) {
cfg_resource(cfg, ctag);
} else {
cfg_resource(cfg, NULL);
}
/* find set number in config */
if ((setnumber = find_setnumber_in_libcfg(cfg, clustered? ctag : NULL,
tohost, tofile)) < 0) {
cfg_close(cfg);
rdc_err(NULL, gettext("unable to find Remote Mirror set %s:%s: "
"in config"), tohost, tofile);
}
(void) snprintf(key, sizeof (key), "sndr.set%d.options", setnumber);
(void) snprintf(auto_tag, sizeof (auto_tag), "auto");
/* Check if there are any options already set, including ours */
if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag, CFG_MAX_BUF, val,
CFG_MAX_BUF) >= 0) {
options = 1;
do {
if (strcmp(tag, auto_tag) == 0) {
already_set = 1;
}
} while (cfg_get_options(cfg, CFG_SEC_CONF, NULL, tag,
CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0);
}
/* options already exist, edit ours out */
if (options && already_set) {
char *p, *q;
int need_to_clear_buf = 1;
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
rdc_err(NULL, gettext("unable to get options field "
"for Remote Mirror set %s:%s"), tohost, tofile);
}
/* parse out our options, all of the form "auto=" */
p = strdup(buf);
bzero(buf, sizeof (buf));
q = strtok(p, ";");
do {
/* if another tag/value exists, keep it */
if (strncmp(auto_tag, q, 4) != 0) {
strcat(buf, q);
strcat(buf, ";");
need_to_clear_buf = 0;
}
} while (q = strtok(NULL, ";"));
free(p);
/* if we were the only option, clear the field */
if (need_to_clear_buf) {
strcat(buf, "-");
}
if (cfg_put_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
rdc_err(NULL, gettext("unable to clear autosync value "
"in config for Remote Mirror set %s:%s"), tohost,
tofile);
} else {
cfg_success = 1;
}
}
/* autosync is not present in options field, add if on is requested */
if (autosync_val == AUTOSYNC_ON) {
if (cfg_put_options(cfg, CFG_SEC_CONF, key, auto_tag, "on")
< 0) {
rdc_err(NULL, gettext("unable to update autosync value "
"in config for Remote Mirror set %s:%s"), tohost,
tofile);
} else {
cfg_success = 1;
}
}
/* if we are in a group, update any other sets in the same group */
do {
bzero(&pair, sizeof (pair));
bzero(buf, CFG_MAX_BUF);
(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
break;
}
if (parse_cfg_buf(buf, &pair, NULL))
break;
if (pair.group == NULL) /* not in a group */
break;
if (!pair.group[0])
break; /* not in a group */
for (set = 1; /*CSTYLED*/; set++) {
if (set == setnumber)
continue;
bzero(buf, CFG_MAX_BUF);
options = 0;
already_set = 0;
(void) snprintf(key, sizeof (key), "sndr.set%d", set);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
break; /* last set processed */
}
bzero(&tmpair, sizeof (tmpair));
if (parse_cfg_buf(buf, &tmpair, NULL))
break;
if (strcmp(pair.group, tmpair.group) != 0)
continue; /* not the group we want */
(void) snprintf(key, sizeof (key), "sndr.set%d.options",
set);
/*
* Check if there are any options already set,
* including ours
*/
if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag,
CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0) {
options = 1;
do {
if (strcmp(tag, auto_tag) == 0) {
already_set = 1;
}
} while (cfg_get_options(cfg, CFG_SEC_CONF,
NULL, tag, CFG_MAX_BUF, val,
CFG_MAX_BUF) >= 0);
}
/* options already exist, edit ours out */
if (options && already_set) {
char *p, *q;
int need_to_clear_buf = 1;
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF)
< 0) {
rdc_err(NULL, gettext("unable to get "
"options field for Remote Mirror set "
"%s:%s"), tmpair.thost, tmpair.tfile);
}
/*
* parse out our options, all of the
* form "auto="
*/
p = strdup(buf);
bzero(buf, sizeof (buf));
q = strtok(p, ";");
do {
/*
* if another tag/value exists,
* keep it
*/
if (strncmp(auto_tag, q, 4) != 0) {
strcat(buf, q);
strcat(buf, ";");
need_to_clear_buf = 0;
}
} while (q = strtok(NULL, ";"));
free(p);
/*
* if we were the only option,
* clear the field
*/
if (need_to_clear_buf) {
strcat(buf, "-");
}
if (cfg_put_cstring(cfg, key, buf, CFG_MAX_BUF)
< 0) {
rdc_err(NULL, gettext("unable to clear "
"autosync value in config for "
"Remote Mirror set %s:%s"),
tmpair.thost, tmpair.tfile);
cfg_success = 0;
}
}
/*
* autosync is not present in options field,
* add if on is requested
*/
if (autosync_val == AUTOSYNC_ON) {
if (cfg_put_options(cfg, CFG_SEC_CONF, key,
auto_tag, "on") < 0) {
rdc_err(NULL, gettext("unable to update"
" autosync value in config for "
"Remote Mirror set %s:%s"),
tmpair.thost,
tmpair.tfile);
cfg_success = 0;
}
}
}
/* CONSTCOND */
} while (0);
if (cfg_success) {
if (cfg_commit(cfg) < 0) {
rdc_err(NULL, gettext("commit on role reversal failed"));
}
}
cfg_close(cfg);
}
/*
* Check to see if autosync is on for set specified by tohost:tofile.
*
* ASSUMPTIONS:
* config is available to take a read lock against it.
*
* INPUTS:
* tohost - secondary host
* tofile - secondary volume
*
* OUTPUTS:
* -1 error
* AUTOSYNC_ON if autosync is on
* AUTOSYNC_OFF if autosync is off
*/
static int
autosync_is_on(char *tohost, char *tofile)
{
CFGFILE *cfg;
int setnumber, autosync_val = AUTOSYNC_OFF;
char key[CFG_MAX_KEY];
char tag[CFG_MAX_BUF], val[CFG_MAX_BUF];
if ((cfg = cfg_open(NULL)) == NULL) {
rdc_err(NULL, gettext("unable to access configuration"));
}
if (!cfg_lock(cfg, CFG_RDLOCK)) {
cfg_close(cfg);
rdc_err(NULL, gettext("unable to lock configuration"));
}
if ((setnumber = find_setnumber_in_libcfg(cfg, NULL, tohost, tofile)) <
0) {
cfg_close(cfg);
rdc_err(NULL, gettext("cannot find Remote Mirror set %s:%s in "
"config"), tohost, tofile);
}
(void) snprintf(key, CFG_MAX_KEY, "sndr.set%d.options", setnumber);
if (cfg_get_options(cfg, CFG_SEC_CONF, key, tag, CFG_MAX_BUF, val,
CFG_MAX_BUF) >= 0) {
do {
if (strcmp(tag, "auto") == 0) {
if (strcmp(val, "on") == 0) {
autosync_val = AUTOSYNC_ON;
}
break;
}
} while (cfg_get_options(cfg, CFG_SEC_CONF, NULL, tag,
CFG_MAX_BUF, val, CFG_MAX_BUF) >= 0);
}
cfg_close(cfg);
return (autosync_val);
}
void
enable_autosync(char *fhost, char *ffile, char *thost, char *tfile)
{
rdc_config_t parms;
spcs_s_info_t ustat;
rdc_addr_t *p;
ustat = spcs_s_ucreate();
parms.command = RDC_CMD_TUNABLE;
p = &parms.rdc_set[0].primary;
strncpy(p->intf, fhost, MAX_RDC_HOST_SIZE);
strncpy(p->file, ffile, MAX_RDC_HOST_SIZE);
p = &parms.rdc_set[0].secondary;
strncpy(p->intf, thost, NSC_MAXPATH);
strncpy(p->file, tfile, NSC_MAXPATH);
parms.rdc_set[0].autosync = 1;
parms.rdc_set[0].maxqfbas = -1;
parms.rdc_set[0].maxqitems = -1;
parms.rdc_set[0].asyncthr = -1;
parms.rdc_set[0].netconfig = NULL;
if ((RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustat)) !=
SPCS_S_OK) {
rdc_warn(&ustat, gettext("failed to update autosync for"
" Remote Mirror set %s:%s"), thost, tfile);
spcs_log("sndr", &ustat, gettext("failed to update autosync for"
" Remote Mirror set %s:%s"), thost, tfile);
}
spcs_s_ufree(&ustat);
}
static int
rdc_operation(CFGFILE *cfg, char *fromhost, char *fromfile, char *frombitmap,
char *tohost, char *tofile, char *tobitmap,
int flag, int iflag,
char *directfile, char *group, char *ctag, char *diskqueue,
int *doasync, int reverse)
{
const int getaddr = (flag == RDC_CMD_ENABLE);
const int rpcbind = !getaddr;
rdc_config_t parms;
int ret;
spcs_s_info_t ustatus;
struct hostent *hp;
char fromname[MAXHOSTNAMELEN], toname[MAXHOSTNAMELEN];
char orig_fbmp[MAXHOSTNAMELEN], orig_tbmp[MAXHOSTNAMELEN];
char orig_diskq[NSC_MAXPATH];
struct t_info tinfo;
int success = 1;
int autosync_toggle_needed = 0;
char *vol1, *vol2, *vol3;
conf = &nconf;
hp = gethost_byname(fromhost);
strncpy(fromname, hp->h_name, MAXHOSTNAMELEN);
hp = gethost_byname(tohost);
strncpy(toname, hp->h_name, MAXHOSTNAMELEN);
if (self_check(fromname) && self_check(toname)) {
rdc_err(NULL, gettext("both %s and %s are local"),
fromhost, tohost);
}
/* we have to find out what to sv disable after reconfig */
if (flag == RDC_CMD_RECONFIG) {
parms.command = RDC_CMD_STATUS;
parms.rdc_set->netconfig = NULL;
strncpy(parms.rdc_set->primary.intf, fromhost,
MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->secondary.intf, tohost,
MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->primary.file, fromfile,
NSC_MAXPATH);
strncpy(parms.rdc_set->secondary.file, tofile,
NSC_MAXPATH);
ustatus = spcs_s_ucreate();
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
rdc_err(NULL, gettext("unable to get set status"
" before reconfig operation"));
}
strncpy(orig_fbmp, parms.rdc_set->primary.bitmap,
NSC_MAXPATH);
strncpy(orig_tbmp, parms.rdc_set->secondary.bitmap,
NSC_MAXPATH);
strncpy(orig_diskq, parms.rdc_set->disk_queue, NSC_MAXPATH);
}
/*
* another terrible addition, if we are reconfigging mode
* and not logging, just give up.
*/
if ((reconfig_doasync != -1) &&
(!(parms.rdc_set->flags & RDC_LOGGING))) {
rdc_err(NULL, gettext("cannot reconfigure sync/async, "
"Remote Mirror set not logging"));
spcs_log("sndr", NULL, gettext("cannot reconfigure sync/async, "
"Remote Mirror set not logging"));
}
/*
* Now build up the address for each host including port and transport
*/
if (getaddr) {
svp = get_addr(toname, RDC_PROGRAM, RDC_VERS_MIN,
&conf, proto_test ? NC_UDP:NULL, "rdc", &tinfo,
rpcbind);
if (svp == NULL) {
rdc_warn(NULL, gettext("unable to determine network "
"information for %s"), toname);
#ifdef DEBUG
(void) printf("get_addr failed for Ver 4 %s\n", toname);
#endif
return (-1);
}
svaddr = *svp;
} else {
bzero(&svaddr, sizeof (svaddr));
}
parms.rdc_set->secondary.addr.len = svaddr.len;
parms.rdc_set->secondary.addr.maxlen =
svaddr.maxlen;
parms.rdc_set->secondary.addr.buf =
(void *)svaddr.buf;
#ifdef DEBUG_ADDR
(void) fprintf(stderr, "secondary buf %x len %d\n",
svaddr.buf, svaddr.len);
for (i = 0; i < svaddr.len; i++)
(void) printf("%u ", svaddr.buf[i]);
(void) printf("\n");
#endif
if (getaddr) {
svp = get_addr(fromname, RDC_PROGRAM, RDC_VERS_MIN,
&conf, proto_test ? NC_UDP: NULL, "rdc", &tinfo,
rpcbind);
if (svp == NULL) {
#ifdef DEBUG
(void) printf("get_addr failed for Ver 4 %s\n",
fromname);
#endif
return (-1);
}
svaddr = *svp;
}
parms.rdc_set->primary.addr.len = svaddr.len;
parms.rdc_set->primary.addr.maxlen = svaddr.maxlen;
parms.rdc_set->primary.addr.buf = (void *)svaddr.buf;
#ifdef DEBUG_ADDR
(void) fprintf(stderr, "primary buf %x len %d\n",
svaddr.buf, svaddr.len);
for (i = 0; i < svaddr.len; i++)
(void) printf("%u ", svaddr.buf[i]);
(void) printf("\n");
#endif
if (getaddr) {
(void) convert_nconf_to_knconf(conf, &knconf);
#ifdef DEBUG_ADDR
(void) printf("knconf %x %s %s %x\n", knconf.knc_semantics,
knconf.knc_protofmly, knconf.knc_proto, knconf.knc_rdev);
#endif
parms.rdc_set->netconfig = &knconf;
} else {
parms.rdc_set->netconfig = NULL;
}
if (!self_check(fromname) && !self_check(toname)) {
if (!clustered)
rdc_err(NULL, gettext("neither %s nor %s is local"),
fromhost, tohost);
else {
/*
* IF we could get a list of logical hosts on this cluster
* Then we could print something intelligent about where
* the volume is mastered. For now, just print some babble
* about the fact that we have no idea.
*/
rdc_err(NULL,
gettext("either %s:%s or %s:%s is not local"),
fromhost, fromfile, tohost, tofile);
}
}
strncpy(parms.rdc_set->primary.intf, fromhost, MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH);
strncpy(parms.rdc_set->primary.bitmap, frombitmap, NSC_MAXPATH);
strncpy(parms.rdc_set->secondary.intf, tohost, MAX_RDC_HOST_SIZE);
strncpy(parms.rdc_set->secondary.file, tofile, NSC_MAXPATH);
strncpy(parms.rdc_set->secondary.bitmap, tobitmap, NSC_MAXPATH);
if ((group == NULL) || ((strcmp(group, "-")) == 0))
parms.rdc_set->group_name[0] = 0;
else
strncpy(parms.rdc_set->group_name, group, NSC_MAXPATH);
if (self_check(tohost) &&
(strlen(diskqueue) > 0) && (diskqueue[0] != '-'))
if ((flag == RDC_CMD_ENABLE) || (flag == RDC_CMD_ADDQ))
rdc_err(NULL, gettext("enabling disk queue on a Remote"
" Mirror secondary is not allowed (%s)"),
diskqueue);
if ((diskqueue == NULL) || ((strcmp(diskqueue, "-")) == 0))
parms.rdc_set->disk_queue[0] = 0;
else
strncpy(parms.rdc_set->disk_queue, diskqueue, NSC_MAXPATH);
parms.rdc_set->maxqfbas = maxqfbas;
parms.rdc_set->maxqitems = maxqitems;
parms.rdc_set->asyncthr = asyncthr;
/* set up the permanent set id for this set */
if (flag == RDC_CMD_ENABLE) {
char key[CFG_MAX_KEY];
char setid[64];
int set;
parms.rdc_set->setid = get_new_cfg_setid(cfg);
if (parms.rdc_set->setid <= 0) {
rdc_err(NULL, gettext("unable to obtain unique set id "
"for %s:%s"), tohost, tofile);
}
if ((set = find_setnumber_in_libcfg(cfg, clustered? ctag : NULL,
tohost, tofile)) < 0) {
rdc_err(NULL, gettext("unable to store unique set id"
" for %s:%s"), tohost, tofile);
}
(void) snprintf(key, sizeof (key), "sndr.set%d.options", set);
(void) snprintf(setid, sizeof (setid), "%d",
parms.rdc_set->setid);
if (cfg_put_options(cfg, CFG_SEC_CONF, key, "setid",
setid) < 0) {
rdc_err(NULL, gettext("unable to store unique set "
"id for %s:%s: %s"), tohost, tofile,
gettext(cfg_error(NULL)));
}
} else if (flag != RDC_CMD_DISABLE) { /* set already gone from cfg */
parms.rdc_set->setid = get_cfg_setid(cfg, ctag, tohost, tofile);
if (parms.rdc_set->setid <= 0) {
rdc_err(NULL, gettext("unable to obtain unique set id "
"for %s:%s"), tohost, tofile);
}
}
/*
* Always set autosync flag to default so nothing gets messed up. If
* we are doing an autosync operation, it'll all get taken care of
* then.
*/
parms.rdc_set->autosync = AUTOSYNC;
/* gethostid(3c) is defined to return a 32bit value */
parms.rdc_set->syshostid = (int32_t)gethostid();
parms.command = 0;
parms.options = iflag;
parms.command = flag;
if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
if (*doasync)
parms.options |= RDC_OPT_ASYNC;
else
parms.options |= RDC_OPT_SYNC;
} else if (flag == RDC_CMD_COPY) {
if (reverse)
parms.options |= RDC_OPT_REVERSE;
else
parms.options |= RDC_OPT_FORWARD;
}
if (self_check(fromname)) {
if (flag == RDC_CMD_COPY && reverse && mounted(fromfile))
rdc_err(NULL, gettext("can not start reverse sync"
" as a file system is mounted on %s"),
fromfile);
parms.options |= RDC_OPT_PRIMARY;
if (strcmp(directfile, "ip") == 0)
parms.rdc_set->direct_file[0] = 0; /* no directfile */
else
strncpy(parms.rdc_set->direct_file, directfile,
NSC_MAXPATH);
} else {
parms.options |= RDC_OPT_SECONDARY;
parms.rdc_set->direct_file[0] = 0; /* no fcal directio */
}
if ((asyncthr || maxqitems || maxqfbas || qblock) &&
(parms.options & RDC_OPT_SECONDARY)) {
rdc_err(NULL, gettext("changing queue parameters may "
" only be done on a primary Remote Mirror host"));
spcs_log("sndr", NULL, gettext("changing queue parameters may "
" only be done on a primary Remote Mirror host"));
}
ustatus = spcs_s_ucreate();
if (flag == RDC_CMD_COPY) {
parms.command = RDC_CMD_STATUS;
ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
if ((ret != SPCS_S_OK) ||
!(parms.rdc_set->flags & RDC_LOGGING)) {
rdc_err(NULL, gettext("can not start sync"
" as Remote Mirror set %s:%s is not logging"),
tohost, tofile);
}
spcs_log("sndr", NULL,
gettext("%s %s %s %s %s %s %s %s\nStarting"),
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap);
parms.command = RDC_CMD_COPY;
}
if ((flag == RDC_CMD_COPY) &&
(autosync_is_on(tohost, tofile) == AUTOSYNC_ON)) {
/* check if autosync needs to be turned on when doing a copy/update */
parms.rdc_set->autosync = AUTOSYNC_ON;
autosync_toggle_needed = 1;
} else if ((flag == RDC_CMD_LOG) &&
(autosync_is_on(tohost, tofile) == AUTOSYNC_ON)) {
/* check if autosync needs to be turned off when going to logging */
parms.rdc_set->autosync = AUTOSYNC_OFF;
autosync_toggle_needed = 1;
} else if (((autosync == AUTOSYNC_ON) || (autosync == AUTOSYNC_OFF)) &&
(flag == RDC_CMD_TUNABLE)) {
/*
* Request to change the autosync value. cfg file will be
* available at this point. If autosync request is to turn off,
* mark off in both the config and the kernel regardless of
* the state of the set. If the request is to turn autosync on,
* set in the kernel if the set is not in logging mode.
*
* XXX
* If the set is in logging mode because of a network
* failure, we will not know. Therefore, a manual update
* will have to be issued to enable autosync in the
* kernel.
* XXX
*/
set_autosync(autosync, tohost, tofile, ctag);
if (autosync == AUTOSYNC_OFF) {
parms.rdc_set->autosync = AUTOSYNC_OFF;
} else if (autosync == AUTOSYNC_ON) {
parms.command = RDC_CMD_STATUS;
ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0,
ustatus);
if (ret != SPCS_S_OK) {
rdc_err(NULL, gettext("can not determine "
"status of Remote Mirror set %s:%s"),
tohost, tofile);
}
/* need to reset the tunables after a status ioctl */
parms.rdc_set->autosync = autosync;
parms.rdc_set->maxqfbas = maxqfbas;
parms.rdc_set->maxqitems = maxqitems;
parms.rdc_set->asyncthr = asyncthr;
/*
* if in logging mode, just update config, kernel will
* be updated with the next copy/update request.
*/
if (parms.rdc_set->flags & RDC_LOGGING) {
parms.rdc_set->autosync = AUTOSYNC;
} else {
parms.rdc_set->autosync = AUTOSYNC_ON;
}
parms.command = flag;
}
}
if (autosync_toggle_needed) {
parms.command = RDC_CMD_TUNABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
spcs_log("sndr", NULL, gettext("failed to update "
"autosync for Remote Mirror set %s:%s"), tohost,
tofile);
rdc_err(NULL, gettext("failed to update autosync for "
"Remote Mirror set %s:%s"), tohost, tofile);
}
/* reset command and default autosync flags */
parms.rdc_set->autosync = AUTOSYNC;
parms.command = flag;
}
errno = 0;
ret = RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus);
if ((ret != SPCS_S_OK) && (flag != RDC_CMD_HEALTH)) {
(void) fprintf(stderr,
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile, tobitmap);
if (errno == RDC_EEINVAL) {
spcs_log("sndr", NULL,
"%s %s %s %s %s %s %s %s\n%s",
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
gettext("invalid command option"));
rdc_err(&ustatus,
gettext("Remote Mirror: invalid command option "
"'%s'"), rdc_decode_flag(flag,
parms.options));
} else {
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap);
if ((flag == RDC_CMD_RECONFIG) &&
(!(iflag & RDC_OPT_REVERSE_ROLE))) {
success = 0;
rdc_warn(&ustatus, 0);
} else
rdc_err(&ustatus, 0);
}
}
if ((flag == RDC_CMD_RECONFIG) && (iflag & RDC_OPT_REVERSE_ROLE) == 0) {
parms.command = RDC_CMD_STATUS;
if (RDC_IOCTL(RDC_CONFIG, &parms, NULL, 0, 0, 0, ustatus) ==
SPCS_S_OK) {
char shostbuf[CFG_MAX_BUF];
char svolbuf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int i, numels;
int cfgsuccess = 1;
/*
* okeydoke, at this point we could have a reconfig
* gone bad. libdscfg does not know about this.
* parms contains the kernel picture, and we know
* what we tried to reconfig. find out where it went
* wrong, find the set in libdscfg, update it. We'll
* issue a warning, then return 0 (eventually).
* this will allow libdscfg to be committed with the
* good info. got it?
* BTW: the only time we can run into this multiple
* reconfig attempt failure is IF we reconfig from file
* and some thing goes wrong with one of the reconfigs
*/
/* find the set in libdscfg */
numels = cfg_get_num_entries(cfg, "sndr");
/* yes, numels could be -1 */
for (i = 1; i < numels; i++) {
bzero(shostbuf, sizeof (shostbuf));
bzero(svolbuf, sizeof (svolbuf));
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.shost", i);
(void) cfg_get_cstring(cfg, key, &shostbuf,
sizeof (shostbuf));
if (strncmp(shostbuf, tohost, sizeof (tohost)))
continue;
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.secondary", i);
(void) cfg_get_cstring(cfg, key, &svolbuf,
sizeof (svolbuf));
if (strncmp(svolbuf, tofile, NSC_MAXPATH))
continue;
break;
/*
* found it, now i contains the set offset.
* i, being the variable, not bad english.
*/
}
/* shouldn't happen */
if ((numels < 1) || (i > numels)) {
rdc_warn(NULL, gettext("unable to retrieve "
"set from configuration database"));
/*
* yuck. but indents are pushing the envelope
* we should not be updating config
* if we did not find the entry
* the error will have to do
*/
cfgsuccess = 0;
goto notfound;
}
/*
* now, put all the correct names back for errors etc.
* also, sock them into dscfg, if the the config was a
* success for one, it will be a redundant but harmless
*/
/*
* we could not have reconfigged mode if we
* are not logging, AND the kernel CAN return
* sync as the status of an async set if it is
* currently syncing.. Hence the flags & RDC_LOGGING
*/
if (parms.rdc_set->flags & RDC_LOGGING) {
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.mode", i);
if (parms.rdc_set->flags & RDC_ASYNC) {
*doasync = 1;
if (cfg_put_cstring(cfg, key, "async",
strlen("async")) < 0) {
cfgsuccess = 0;
}
} else {
*doasync = 0;
if (cfg_put_cstring(cfg, key, "sync",
strlen("sync")) < 0) {
cfgsuccess = 0;
}
}
}
#ifdef _RDC_CAMPUS
if (*parms.rdc_set->direct_file) {
strncpy(directfile, parms.rdc_set->direct_file,
NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.type", i);
if (cfg_put_cstring(cfg, key, directfile,
strlen(directfile)) < 0)
cfgsuccess = 0;
} else {
strncpy(directfile, "-", NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.type", i);
if (cfg_put_cstring(cfg, key, directfile,
strlen(directfile)) < 0)
cfgsuccess = 0;
}
#endif
if (*parms.rdc_set->group_name) {
strncpy(group, parms.rdc_set->group_name,
NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.group", i);
if (cfg_put_cstring(cfg, key, group,
strlen(group)) < 0)
cfgsuccess = 0;
} else {
strncpy(group, "-", NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.group", i);
if (cfg_put_cstring(cfg, key, group,
strlen(group)) < 0)
cfgsuccess = 0;
}
if (*parms.rdc_set->disk_queue) {
strncpy(diskqueue, parms.rdc_set->disk_queue,
NSC_MAXPATH);
} else {
strncpy(diskqueue, "-", NSC_MAXPATH);
}
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.diskq", i);
if (cfg_put_cstring(cfg, key, diskqueue,
strlen(diskqueue)) < 0)
cfgsuccess = 0;
strncpy(frombitmap, parms.rdc_set->primary.bitmap,
NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.pbitmap", i);
if (cfg_put_cstring(cfg, key, frombitmap,
strlen(frombitmap)) < 0)
cfgsuccess = 0;
strncpy(tobitmap, parms.rdc_set->secondary.bitmap,
NSC_MAXPATH);
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.sbitmap", i);
if (cfg_put_cstring(cfg, key, tobitmap,
strlen(tobitmap)) < 0)
cfgsuccess = 0;
bzero(key, sizeof (key));
(void) snprintf(key, sizeof (key),
"sndr.set%d.cnode", i);
if (clustered)
if (cfg_put_cstring(cfg, key, ctag,
strlen(ctag)) < 0)
cfgsuccess = 0;
notfound:
if (cfgsuccess == 0) {
rdc_warn(NULL, gettext("unable to update "
"configuration storage"));
}
} else {
spcs_log("sndr", NULL,
"%s %s %s %s %s %s %s %s\n%s",
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
gettext("unable to update config file"));
rdc_err(&ustatus,
gettext("Remote Mirror: unable to update "
"config file"));
}
}
if (flag == RDC_CMD_HEALTH && errno == 0) {
(void) fprintf(stderr,
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile, tobitmap);
if (ret == RDC_ACTIVE)
(void) fprintf(stderr, "Active\n");
else if (ret == RDC_INACTIVE)
(void) fprintf(stderr, "Inactive\n");
else
(void) fprintf(stderr, "Unknown\n");
} else if (ret != SPCS_S_OK) {
if (errno == RDC_EEINVAL) {
spcs_log("sndr", NULL,
"%s %s %s %s %s %s %s %s\n%s",
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap,
gettext("invalid command option"));
rdc_err(&ustatus,
gettext("Remote Mirror: invalid command option "
"'%s'"),
rdc_decode_flag(flag, parms.options));
}
}
if (flag == RDC_CMD_STATUS) {
(void) fprintf(stderr,
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile, tobitmap);
(void) fprintf(stderr, "flags 0x%x\n", parms.rdc_set->flags |
parms.rdc_set->sync_flags | parms.rdc_set->bmap_flags);
} else if (success) {
spcs_log("sndr", NULL,
gettext("%s %s %s %s %s %s %s %s\nSuccessful"),
program, rdc_decode_flag(flag, parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap);
if (flag == RDC_CMD_TUNABLE)
spcslog_tunable(tohost, tofile);
}
if (cfg && perform_autosv()) {
spcs_s_ufree(&ustatus);
/* figure out which are the local volumes */
if (parms.options & RDC_OPT_PRIMARY) {
vol1 = fromfile;
vol2 = frombitmap;
if ((diskqueue && diskqueue[0]) &&
(strncmp(diskqueue, "-", 1) != 0))
vol3 = diskqueue;
else
vol3 = NULL;
} else {
vol1 = tofile;
vol2 = tobitmap;
vol3 = NULL;
if ((flag == RDC_CMD_ENABLE) &&
(strlen(diskqueue) > 0) &&
(strncmp(diskqueue, "-", 1)) != 0) {
rdc_warn(NULL,
gettext("enabling a disk queue on a "
"Remote Mirror secondary is not allowed. "
"(%s) ignored"), diskqueue);
}
}
if (flag == RDC_CMD_ENABLE) {
ustatus = spcs_s_ucreate();
/*
* SV-enable all local volumes
* if the sv_enables fail, disable the sndr vols
* that we just enabled
* and return -1 so the cfg_commit() won't happen
*/
if (nsc_lookup(volhash, vol1) == NULL) {
if (cfg_vol_enable(cfg, vol1, ctag, "sndr")
< 0) {
spcs_log("sndr", NULL,
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
vol1, tohost, tofile);
/*
* warn here, but we are going to exit
* we want to catch any errors on the
* way down, then exit
*/
rdc_warn(NULL,
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
vol1, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost,
fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_err(&ustatus, 0);
}
/*
* ok, we should've reported any errs
* exit explictly
*/
exit(1);
}
}
if (vol2 && nsc_lookup(volhash, vol2) == NULL) {
if (cfg_vol_enable(cfg, vol2, ctag, "sndr")
< 0) {
spcs_log("sndr", NULL,
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
vol1, tohost, tofile);
/*
* warn here, but we are going to exit
* we want to catch any errors on the
* way down, then exit
*/
rdc_warn(NULL,
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
vol2, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost,
fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_err(&ustatus, 0);
}
/*
* ok, we should've reported any errs
* exit explictly
*/
exit(1);
}
}
if (vol3 && nsc_lookup(volhash, diskqueue) == NULL) {
if (cfg_vol_enable(cfg, diskqueue, ctag, "sndr")
< 0) {
spcs_log("sndr", NULL,
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
diskqueue, tohost, tofile);
if (cfg_vol_disable(cfg, vol1, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove volume [%s] from "
"configuration"), vol1);
if (cfg_vol_disable(cfg, vol2, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove volume [%s] from "
"configuration"), vol2);
/*
* warn here, but we are going to exit
* we want to catch any errors on the
* way down, then exit
*/
rdc_warn(NULL,
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
diskqueue, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost,
fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_err(&ustatus, 0);
}
/*
* ok, we should've reported any errs
* exit explictly
*/
exit(1);
}
}
} else if (flag == RDC_CMD_DISABLE) {
/*
* If we're no longer using a volume, SV-disable it
*/
volcount_t *vc;
vc = nsc_lookup(volhash, vol1);
if (vc && (1 == vc->count)) {
if (cfg_vol_disable(cfg, vol1, ctag, "sndr")
< 0)
rdc_warn(NULL, gettext("Failed to "
"remove volume [%s] from "
"configuration"), vol1);
} else if (!vc) {
rdc_warn(NULL,
gettext("Unable to find %s in config"),
vol1);
}
if (vol2) {
vc = nsc_lookup(volhash, vol2);
if (vc && (1 == vc->count)) {
if (cfg_vol_disable(cfg, vol2, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove volume [%s] from "
"configuration"), vol2);
} else if (!vc) {
rdc_warn(NULL, gettext("Unable to find"
" %s in config"), vol2);
}
}
if (diskqueue != NULL && strlen(diskqueue) > 0) {
vc = nsc_lookup(volhash, diskqueue);
if (vc && (1 == vc->count)) {
if (cfg_vol_disable(cfg, diskqueue, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove disk queue [%s] from "
"configuration"), diskqueue);
} else if (!vc) {
rdc_warn(NULL, gettext("Unable to find"
" %s in config"), diskqueue);
}
}
/* WARNING about to go to 4 space indenting */
} else if (flag == RDC_CMD_RECONFIG) {
volcount_t *vc;
/* disable ex-bitmaps, enable new bitmaps */
if (parms.options & RDC_OPT_PRIMARY) {
if (strcmp(orig_fbmp, frombitmap) != 0) {
vc = nsc_lookup(volhash, orig_fbmp);
if (vc && (vc->count == 1)) {
if (cfg_vol_disable(cfg, orig_fbmp, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove bitmap [%s] from "
"configuration"), orig_fbmp);
} else if (!vc) {
rdc_warn(NULL, gettext("Unable to find "
"%s in config"), orig_fbmp);
}
if (nsc_lookup(volhash, frombitmap) == NULL) {
if (cfg_vol_enable(cfg, frombitmap, ctag,
"sndr") < 0) {
spcs_log("sndr", NULL,
"reconfig sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
frombitmap, tohost, tofile);
rdc_warn(NULL,
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
frombitmap, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost,
fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_warn(&ustatus, 0);
}
exit(1);
}
}
} else if ((orig_diskq[0] != '\0') &&
(strcmp(orig_diskq, diskqueue) != 0)) {
vc = nsc_lookup(volhash, orig_diskq);
if (vc && (vc->count == 1)) {
if (cfg_vol_disable(cfg, orig_diskq, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove disk queue [%s] from "
"configuration"), orig_diskq);
} else if (!vc) {
rdc_warn(NULL, gettext("Unable to find "
"%s in config"), orig_diskq);
}
if (vol3 &&
(nsc_lookup(volhash, diskqueue) == NULL)) {
if (cfg_vol_enable(cfg, diskqueue, ctag,
"sndr") < 0) {
spcs_log("sndr", NULL, "reconfig sv "
"enable of diskqueue %s failed, "
"disabling Remote Mirror set %s:%s",
diskqueue, tohost, tofile);
rdc_warn(NULL, "reconfig sv "
"enable of diskqueue %s failed."
"disabling Remote Mirror set %s:%s",
diskqueue, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost,
fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_warn(&ustatus, 0);
}
exit(1);
}
}
}
} else if (flag != RDC_OPT_PRIMARY) {
if (strcmp(orig_tbmp, tobitmap) != 0) {
vc = nsc_lookup(volhash, orig_tbmp);
if (vc && (vc->count == 1)) {
if (cfg_vol_disable(cfg, orig_tbmp, ctag,
"sndr") < 0)
rdc_warn(NULL, gettext("Failed to "
"remove bitmap [%s] from "
"configuration"), orig_tbmp);
} else if (!vc) {
rdc_warn(NULL,
gettext("Unable to find %s in config"),
orig_tbmp);
}
if (nsc_lookup(volhash, tobitmap) == NULL) {
if (cfg_vol_enable(cfg, tobitmap, ctag,
"sndr") < 0) {
spcs_log("sndr", NULL,
"reconfig sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
tobitmap, tohost, tofile);
rdc_warn(NULL,
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
tobitmap, tohost, tofile);
parms.command = RDC_CMD_DISABLE;
ret = RDC_IOCTL(RDC_CONFIG, &parms,
NULL, 0, 0, 0, ustatus);
if (ret != SPCS_S_OK) {
(void) fprintf(stderr,
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
fromhost, fromfile,
frombitmap, tohost, tofile,
tobitmap);
spcs_log("sndr", &ustatus,
"%s %s %s %s %s %s %s %s",
program,
rdc_decode_flag(parms.command,
parms.options),
fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap);
rdc_warn(&ustatus, 0);
}
exit(1);
}
}
}
}
/* END 4 space indenting */
}
}
spcs_s_ufree(&ustatus);
return (0);
}
/*
* read_config()
*
* DESCRIPTION: Read the lines in a configuration file and return the
* pairs of devices to be mirrored/enabled/disabled/updated.
* The format for the configuration file is as follows:
*
* fromhost fromfile frombitmap tohost tofile tobitmap
*
* where fromfile is the primary device which is local to the
* fromhost subsystem, tofile is the secondary device which is
* local to the tohost subsystem, and type is 1 if the device
* a simckd device or 0 otherwise. Any line preceeded by a '#'
* is considered to be a comment.
*
* Inputs:
* char *config_file Name of configuration file for rdcadm
*
* Outputs:
* int i Number of pairs of devices
*
* Side Effects: The 0 to i-1 entries in the pair_list are filled.
*
*/
int
read_config(int flag, char *config_file, char *group_arg, char *ctag_arg)
{
int ret;
char dsk_flagstr[NSC_MAXPATH];
char line[1024], tmp_line[1024];
char fromhost[MAX_RDC_HOST_SIZE];
char fromfile[NSC_MAXPATH];
char frombitmap[NSC_MAXPATH];
char tohost[MAX_RDC_HOST_SIZE];
char tofile[NSC_MAXPATH];
char tobitmap[NSC_MAXPATH];
char directfile[NSC_MAXPATH];
char sync[16];
int doasync;
FILE *fp;
int i, j;
char *extra_args[EXTRA_ARGS];
char *tmp, *split_str = " \t\n";
for (j = 0; j < EXTRA_ARGS; j++)
extra_args[j] = malloc(NSC_MAXPATH);
if (!(fp = fopen(config_file, "r"))) {
rdc_err(NULL, gettext("error opening %s"), config_file);
}
i = 0;
while (fgets(line, sizeof (line), fp)) {
if (line[0] == '#') /* this is a comment */
continue;
ret = 0;
strcpy(tmp_line, line);
if ((tmp = strtok(tmp_line, split_str)) != NULL) {
if (strlen(tmp) >= MAX_RDC_HOST_SIZE) {
(void) printf(gettext("hostname is longer than %d "
"characters\n"), (MAX_RDC_HOST_SIZE - 1));
continue;
}
strncpy(fromhost, tmp, (MAX_RDC_HOST_SIZE - 1));
fromhost[(MAX_RDC_HOST_SIZE - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
if (strlen(tmp) >= NSC_MAXPATH) {
(void) printf(gettext(
"device name is longer than %d "
"characters\n"), (NSC_MAXPATH - 1));
continue;
}
strncpy(fromfile, tmp, (NSC_MAXPATH - 1));
fromfile[(NSC_MAXPATH - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
if (strlen(tmp) >= NSC_MAXPATH) {
(void) printf(gettext(
"device name is longer than %d "
"characters\n"), (NSC_MAXPATH - 1));
continue;
}
strncpy(frombitmap, tmp, (NSC_MAXPATH - 1));
frombitmap[(NSC_MAXPATH - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
if (strlen(tmp) >= MAX_RDC_HOST_SIZE) {
(void) printf(gettext(
"hostname is longer than %d "
"characters\n"), (MAX_RDC_HOST_SIZE - 1));
continue;
}
strncpy(tohost, tmp, (MAX_RDC_HOST_SIZE - 1));
tohost[(MAX_RDC_HOST_SIZE - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
if (strlen(tmp) >= NSC_MAXPATH) {
(void) printf(gettext(
"device name is longer than %d "
"characters\n"), (NSC_MAXPATH - 1));
continue;
}
strncpy(tofile, tmp, (NSC_MAXPATH - 1));
tofile[(NSC_MAXPATH - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
if (strlen(tmp) >= NSC_MAXPATH) {
(void) printf(gettext(
"device name is longer than %d "
"characters\n"), (NSC_MAXPATH - 1));
continue;
}
strncpy(tobitmap, tmp, (NSC_MAXPATH - 1));
tobitmap[(NSC_MAXPATH - 1)] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
strncpy(dsk_flagstr, tmp, 15);
dsk_flagstr[15] = '\0';
ret++;
}
if ((tmp = strtok(NULL, split_str)) != NULL) {
strncpy(sync, tmp, 15);
sync[15] = '\0';
ret++;
}
for (j = 0; j < EXTRA_ARGS; j++) {
if ((tmp = strtok(NULL, split_str)) != NULL) {
strncpy(extra_args[j], tmp, (NSC_MAXPATH - 1));
extra_args[j][(NSC_MAXPATH - 1)] = '\0';
ret++;
}
}
if (ret == 0) /* this is a blank line */
continue;
if (ret < 8) {
(void) fclose(fp);
rdc_warn(NULL,
gettext("invalid format in %s"), config_file);
rdc_err(NULL, "%s", line);
}
if (i >= rdc_maxsets) {
(void) fclose(fp);
rdc_err(NULL,
gettext("number of Remote Mirror sets exceeds %d"),
rdc_maxsets);
}
#ifdef _RDC_CAMPUS
if (dsk_flagstr[0] == '/') {
/* fcal directio */
strncpy(directfile, dsk_flagstr, NSC_MAXPATH);
} else if (strcmp(dsk_flagstr, "ip") != 0) {
#else
if (strcmp(dsk_flagstr, "ip") != 0) {
#endif
(void) fclose(fp);
rdc_err(NULL,
#ifdef _RDC_CAMPUS
gettext("ip/fcal specification missing"));
#else
gettext("ip specification missing"));
#endif
} else
strcpy(directfile, "ip");
if (strcmp(sync, "sync") == 0)
doasync = 0;
else if (strcmp(sync, "async") == 0)
doasync = 1;
else {
(void) fclose(fp);
rdc_err(NULL,
gettext("sync/async specification missing"));
}
strncpy(pair_list[i].fhost, fromhost, MAX_RDC_HOST_SIZE);
strncpy(pair_list[i].ffile, fromfile, NSC_MAXPATH);
strncpy(pair_list[i].fbitmap, frombitmap, NSC_MAXPATH);
strncpy(pair_list[i].thost, tohost, MAX_RDC_HOST_SIZE);
strncpy(pair_list[i].tfile, tofile, NSC_MAXPATH);
strncpy(pair_list[i].tbitmap, tobitmap, NSC_MAXPATH);
strncpy(pair_list[i].directfile, directfile, NSC_MAXPATH);
pair_list[i].doasync = doasync;
if (gethost_netaddrs(fromhost, tohost,
(char *)pair_list[i].fnetaddr,
(char *)pair_list[i].tnetaddr) < 0) {
(void) fclose(fp);
rdc_err(NULL, gettext("unable to determine IP "
"addresses for hosts %s, %s"), fromhost, tohost);
}
if (parse_extras(ret - 8, extra_args, i) < 0) {
(void) fclose(fp);
rdc_err(NULL, gettext("illegal option in:\n%s"),
line);
}
if (flag == RDC_CMD_ENABLE || flag == RDC_CMD_RECONFIG) {
if (ctag_check(fromhost, fromfile, frombitmap,
tohost, tofile, tobitmap, pair_list[i].ctag,
pair_list[i].diskqueue) < 0)
continue; /* Ignore illegal sets */
if (pair_diskqueue_check(i))
continue; /* ignore sets with incorrect diskq */
}
/* Filter according to ctag and group arguments */
if (strcmp(ctag_arg, "") &&
strncmp(ctag_arg, pair_list[i].ctag,
MAX_RDC_HOST_SIZE))
continue;
if (strcmp(group_arg, "") &&
strncmp(group_arg, pair_list[i].group, NSC_MAXPATH))
continue;
i++;
}
(void) fclose(fp);
for (j = 0; j < EXTRA_ARGS; j++)
free(extra_args[j]);
return (i);
}
/*
* read_libcfg()
*
* DESCRIPTION: Read the relevant config info via libcfg
*
* Outputs:
* int i Number of pairs of devices
*
* Side Effects: The 0 to i-1 entries in the pair_list are filled.
*
*/
static int
read_libcfg(int flag, char *group_arg, char *ctag_arg)
{
int rc;
CFGFILE *cfg;
int i;
_sd_dual_pair_t *pairp;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int setnumber;
if ((cfg = cfg_open(NULL)) == NULL)
rdc_err(NULL, gettext("unable to access configuration"));
if (!cfg_lock(cfg, CFG_RDLOCK))
rdc_err(NULL, gettext("unable to lock configuration"));
if (strcmp(ctag_arg, ""))
cfg_resource(cfg, ctag_arg);
else {
cfg_resource(cfg, NULL);
}
setnumber = 0;
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
bzero(buf, CFG_MAX_BUF);
(void) snprintf(key, sizeof (key), "sndr.set%d", setnumber);
rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
if (rc < 0)
break;
pairp = &pair_list[i];
if (parse_cfg_buf(buf, pairp, NULL))
continue;
if (strcmp(group_arg, "") &&
strncmp(group_arg, pairp->group, NSC_MAXPATH))
continue;
if (flag == RDC_CMD_RECONFIG) {
if (reconfig_pbitmap)
strncpy(pairp->fbitmap, reconfig_pbitmap,
NSC_MAXPATH);
if (reconfig_sbitmap)
strncpy(pairp->tbitmap, reconfig_sbitmap,
NSC_MAXPATH);
#ifdef _RDC_CAMPUS
if (reconfig_direct)
strncpy(directfile, reconfig_direct,
NSC_MAXPATH);
#endif
if (reconfig_group)
strncpy(pairp->group, reconfig_group,
NSC_MAXPATH);
if (strlen(reconfig_ctag) > 0)
strncpy(pairp->ctag, reconfig_ctag,
MAX_RDC_HOST_SIZE);
if (reconfig_doasync != -1)
pairp->doasync = reconfig_doasync;
}
if (ctag_check(pairp->fhost, pairp->ffile,
pairp->fbitmap, pairp->thost, pairp->tfile,
pairp->tbitmap, pairp->ctag, pairp->diskqueue) < 0)
continue; /* Ignore illegal sets */
if (gethost_netaddrs(pairp->fhost, pairp->thost,
(char *)pairp->fnetaddr,
(char *)pairp->tnetaddr) < 0) {
rdc_err(NULL, gettext("unable to determine IP "
"addresses for hosts %s, %s"), pairp->fhost,
pairp->thost);
}
i++;
}
cfg_close(cfg);
return (i);
}
void
q_usage(int prhdr)
{
if (prhdr)
(void) fprintf(stderr, gettext("disk queue usage:\n"));
(void) fprintf(stderr,
gettext("\t%s -g <group> -q a <vol>\t\tadd disk queue to "
"group\n"), program);
(void) fprintf(stderr,
gettext("\t%s -g <group> -q d \t\tremove disk queue from"
" group\n"), program);
(void) fprintf(stderr,
gettext("\t%s -g <group> -q r <newvol>\treplace disk queue for a"
" group\n"), program);
(void) fprintf(stderr,
gettext("\t%s -q a <vol> <shost>:<sdev>\tadd disk queue to "
"a set\n"), program);
(void) fprintf(stderr,
gettext("\t%s -q d <shost>:<sdev>\t\tremove disk queue from "
"a set\n"), program);
(void) fprintf(stderr,
gettext("\t%s -q r <newvol> <shost>:<sdev>\treplace disk queue for "
"a set\n"), program);
}
static void
usage()
{
(void) fprintf(stderr, gettext("usage:\n"));
(void) fprintf(stderr,
gettext("\t%s [opts] -a {on | off} [set]\t"
"set autosync\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -A <asyncthr> [set]\t"
"set the number of asynchronous\n\t\t\t\t\t\tthreads\n"),
program);
(void) fprintf(stderr,
gettext("\t%s [opts] -d [set]\t\t\t"
"disable\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -e [set]\t\t\t"
"enable with bits in bitmap set\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -E [set]\t\t\t"
"enable with bits in bitmap clear\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -F <maxqfbas> [set]\t"
"set maximum fbas to queue\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -D {block | noblock} [set]\t"
"set disk queue blocking mode\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -H [set]\t\t\t"
"report link health\n"), program);
(void) fprintf(stderr,
gettext("\t%s -h\t\t\t\tusage message\n"), program);
(void) fprintf(stderr,
gettext("\t%s -I a <master> <shadow> <bitmap>\t"
"add ndr_ii config entry\n"), program);
(void) fprintf(stderr,
gettext("\t%s -I d <master> <shadow> <bitmap>\t"
"delete ndr_ii config entry\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -i [set]\t\t\t"
"print sets in config file format\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -l [set]\t\t\t"
"enter logging mode\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -m [set]\t\t\t"
"full sync\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -m -r [set]\t\t"
"full reverse sync\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -P [set]\t\t\t"
"print sets verbose\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -p [set]\t\t\t"
"print sets\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -R\t\t\t"
"reset error conditions\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -R b p <bitmap> [set]\t"
"reconfig primary bitmap\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -R b s <bitmap> [set]\t"
"reconfig secondary bitmap\n"), program);
if (clustered)
(void) fprintf(stderr,
gettext("\t%s [opts] -R C <ctag> [set]\t"
"reconfig cluster tag\n"), program);
#ifdef _RDC_CAMPUS
(void) fprintf(stderr,
gettext("\t%s [opts] -R d <pathname> [set]\t"
"reconfig campus direct file\n"), program);
#endif
(void) fprintf(stderr,
gettext("\t%s [opts] -R -f <volset-file> \t"
"reconfig from file\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -R g <group> [set]\t"
"reconfig group\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -R m {sync|async} [set]\t"
"reconfig mode\n"), program);
if (allow_role)
(void) fprintf(stderr,
gettext("\t%s [opts] -R r [set]\t\t"
"reverse roles\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -u [set]\t\t\t"
"update sync\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -u -r [set]\t\t"
"update reverse sync\n"), program);
(void) fprintf(stderr,
gettext("\t%s -v\t\t\t\tdisplay version\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -W <maxwrites> [set]\t"
"set maximum writes to queue\n"), program);
(void) fprintf(stderr,
gettext("\t%s [opts] -w [set]\t\t\t"
"wait\n"), program);
q_usage(0);
(void) fprintf(stderr, gettext("\nopts:\n"));
(void) fprintf(stderr, gettext("\t-n\t\tnon-interactive mode "
"(not valid for print operations)\n"));
(void) fprintf(stderr, gettext(
"\t-g <group>\toperate on sets in group only "
"(not valid for enable\n\t\t\toperations)\n"));
if (clustered)
(void) fprintf(stderr,
gettext("\t-C <ctag>\tignore sets not in cluster ctag "
"(not valid for enable\n\t\t\toperations)\n"));
(void) fprintf(stderr, gettext("\nset:\n"));
if (clustered)
(void) fprintf(stderr,
gettext("\t<phost> <pdev> <pbmp> "
#ifdef _RDC_CAMPUS
"<shost> <sdev> <sbmp> {ip | <directfile>} "
#else
"<shost> <sdev> <sbmp> ip "
#endif
"\\\n\t\t{sync | async} [g <group>] [q <qdev>] "
"[C <ctag>]\n"));
else
(void) fprintf(stderr,
gettext("\t<phost> <pdev> <pbmp> "
#ifdef _RDC_CAMPUS
"<shost> <sdev> <sbmp> {ip | <directfile>} "
#else
"<shost> <sdev> <sbmp> ip "
#endif
"\\\n\t\t{sync | async} [g <group>] [q <qdev>]\n"));
(void) fprintf(stderr,
gettext("\t<shost>:<sdev>\t\t"
"operate on set matching shost and sdev\n"));
(void) fprintf(stderr,
gettext("\t-f volset-file\t\t"
"operate on all sets specified in config file\n"
"\t\t\t\tnote: not valid for single set operations. See\n"
"\t\t\t\t%s(1RDC).\n"), program);
(void) fprintf(stderr,
gettext("\t<no arg>\t\toperate on all configured sets\n"));
}
int
prompt_user(int flag, int options)
{
int c;
switch (flag) {
case RDC_CMD_DISABLE:
(void) printf(gettext("Disable Remote Mirror? (Y/N) [N]: "));
break;
case RDC_CMD_ENABLE:
(void) printf(gettext("Enable Remote Mirror? (Y/N) [N]: "));
break;
case RDC_CMD_HEALTH:
(void) printf(gettext("Report Remote Mirror link health? (Y/N)"
"[N]: "));
break;
case RDC_CMD_COPY:
if (options & RDC_OPT_FULL) {
if (options & RDC_OPT_REVERSE)
(void) printf(gettext("Overwrite primary with"
" secondary? (Y/N) [N]: "));
else
(void) printf(gettext("Overwrite secondary with"
" primary? (Y/N) [N]: "));
} else {
if (options & RDC_OPT_REVERSE)
(void) printf(gettext("Refresh primary with"
" secondary? (Y/N) [N]: "));
else
(void) printf(gettext("Refresh secondary with"
" primary? (Y/N) [N]: "));
}
break;
case RDC_CMD_RECONFIG:
(void) printf(gettext(
"Perform Remote Mirror reconfiguration? (Y/N) [N]: "));
break;
case RDC_CMD_RESET:
(void) printf(gettext("Perform Remote Mirror reset? (Y/N) "
"[N]: "));
break;
case RDC_CMD_TUNABLE:
(void) printf(gettext("Change Remote Mirror tunable? (Y/N) "
"[N]: "));
break;
case RDC_CMD_LOG:
(void) printf(gettext(
"Put Remote Mirror into logging mode? (Y/N) [N]: "));
break;
case RDC_CMD_WAIT:
(void) printf(gettext(
"Wait for Remote Mirror sync completion? (Y/N) [N]: "));
break;
default:
(void) printf(gettext("Perform Remote Mirror operation? (Y/N) "
"[N]: "));
}
c = getchar();
if ((c != 'y') && (c != 'Y')) {
(void) printf("\n");
return (-1);
}
return (0);
}
static void
load_rdc_vols(CFGFILE *cfg)
{
int set;
char key[ CFG_MAX_KEY ];
char buf[ CFG_MAX_BUF ];
_sd_dual_pair_t pair;
char *vol, *bmp;
char *host1 = pair.fhost, *host2 = pair.thost;
char *diskqueue = pair.diskqueue;
volcount_t *volcount;
char lghn[ MAX_RDC_HOST_SIZE ];
if (volhash) {
return;
}
cfg_rewind(cfg, CFG_SEC_CONF);
volhash = nsc_create_hash();
for (set = 1; /*CSTYLED*/; set++) {
(void) snprintf(key, CFG_MAX_KEY, "sndr.set%d", set);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF)) {
break;
}
if (parse_cfg_buf(buf, &pair, lghn))
continue;
vol = pair.ffile;
bmp = pair.fbitmap;
/* use lghn if possible */
if (*lghn) {
if (strcmp(host2, lghn) == 0) {
vol = pair.tfile;
bmp = pair.tbitmap;
}
} else if (!self_check(host1)) {
/* next one had better be ours */
vol = pair.tfile;
bmp = pair.tbitmap;
if (!self_check(host2)) {
rdc_warn(NULL,
gettext("config error: neither %s nor %s"
" is localhost"), host1, host2);
continue;
}
}
/* primary vol may be used more than once */
volcount = (volcount_t *)nsc_lookup(volhash, vol);
if (volcount) {
volcount->count++;
} else {
volcount = (volcount_t *)malloc(sizeof (volcount_t));
volcount->count = 1;
(void) nsc_insert_node(volhash, volcount, vol);
}
/* bitmap ought to be only used once */
volcount = (volcount_t *)nsc_lookup(volhash, bmp);
if (volcount) {
/* argh */
volcount->count++;
} else {
volcount = (volcount_t *)malloc(sizeof (volcount_t));
volcount->count = 1;
(void) nsc_insert_node(volhash, volcount, bmp);
}
if (strcmp(diskqueue, place_holder) == 0)
continue;
/* diskqueue vol may be used more than once */
volcount = (volcount_t *)nsc_lookup(volhash, diskqueue);
if (volcount) {
volcount->count++;
} else {
volcount = (volcount_t *)malloc(sizeof (volcount_t));
volcount->count = 1;
(void) nsc_insert_node(volhash, volcount, diskqueue);
}
}
}
static void
unload_rdc_vols()
{
nsc_remove_all(volhash, free);
volhash = 0;
}
static int
perform_autosv()
{
if (!clustered) {
return (1);
} else {
return (cfg_issuncluster());
}
}
/*
* Check the user supplied fields against those in the dscfg for
* this set.
* Never returns on an error.
*/
static void
checkgfields(CFGFILE *cfg, int setnumber, char *fromhost, char *fromfile,
char *frombitmap, char *tobitmap, char *type, char *mode, char *group,
char *ctag, char *diskq)
{
if (fromhost[0])
checkgfield(cfg, setnumber, "phost",
gettext("primary host"), fromhost);
if (fromfile[0])
checkgfield(cfg, setnumber, "primary",
gettext("primary volume"), fromfile);
if (frombitmap[0])
checkgfield(cfg, setnumber, "pbitmap",
gettext("primary bitmap"), frombitmap);
if (tobitmap[0])
checkgfield(cfg, setnumber, "sbitmap",
gettext("secondary bitmap"), tobitmap);
if (type[0])
checkgfield(cfg, setnumber, "type",
gettext("type of connection"), type);
if (mode[0])
checkgfield(cfg, setnumber, "mode",
gettext("mode of connection"), mode);
if (group[0])
checkgfield(cfg, setnumber, "group",
gettext("group"), group);
if (ctag[0])
checkgfield(cfg, setnumber, "cnode",
gettext("cluster tag"), ctag);
if (diskq[0])
checkgfield(cfg, setnumber, "diskq",
gettext("disk queue volume"), diskq);
}
/*
* Check the 'fname' field in the dscfg file for set number 'setnumber'
* If it does not match the user's data, 'data', then print the error
* message using the friendly field name 'ufield'.
* Never returns on an error.
*/
static void
checkgfield(CFGFILE *cfg, int setnumber, char *fname, char *ufield, char *data)
{
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
(void) snprintf(key, sizeof (key), "sndr.set%d.%s", setnumber, fname);
if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
rdc_err(NULL, gettext("unable to fetch data for key %s"),
key);
}
if (strcmp(buf, data) != 0) {
rdc_err(NULL,
gettext("the value specified for the %s field is not\nthe "
"same as that contained within the configuration storage "
"file for this set.\nYou specified \"%s\" "
"expected \"%s\"."),
ufield, data, buf);
}
}
/*
* load and send the contents of the bitmap file to the kernel.
*/
static int
rdc_bitmapset(char *tohost, char *tofile, char *bitmap, int op,
nsc_off_t offset)
{
rdc_bitmap_op_t bmop;
int fd;
void *buffer;
int buffersz;
struct stat s;
int n;
int ret;
/*
* open bitmap file for reading.
*/
if ((fd = open(bitmap, O_RDONLY)) < 0) {
rdc_warn(NULL, gettext("Unable to open bitmap file %s"),
bitmap);
return (1);
}
fstat(fd, &s);
if (S_ISREG(s.st_mode) == 0) {
rdc_warn(NULL, gettext("Bitmap %s is not a regular file"),
bitmap);
(void) close(fd);
return (1);
}
if (op == 0) {
op = RDC_BITMAPOR;
}
/*
* use the file size to allocate buffer. This
* size should be a multiple of FBA, but don't check
* it here.
*/
buffersz = s.st_size;
buffer = malloc(buffersz);
if (buffer == NULL) {
rdc_warn(NULL, gettext("Unable to allocate %d bytes "
"for bitmap file %s"), buffersz, bitmap);
(void) close(fd);
return (1);
}
n = read(fd, buffer, buffersz);
(void) close(fd);
if (n != buffersz) {
rdc_warn(NULL, gettext("Unable to read the bitmap file, "
"read returned %d instead of %d"),
n, buffersz);
free(buffer);
return (1);
}
bmop.offset = offset;
bmop.op = op;
strncpy(bmop.sechost, tohost, MAX_RDC_HOST_SIZE);
strncpy(bmop.secfile, tofile, NSC_MAXPATH);
bmop.len = buffersz;
bmop.addr = (unsigned long)buffer;
ret = rdc_ioctl_simple(RDC_BITMAPOP, &bmop);
free(buffer);
if (ret < 0) {
rdc_warn(NULL, gettext("Setting bitmap ioctl failed for set "
"%s:%s"), tohost, tofile);
switch (errno) {
case EIO:
rdc_warn(NULL, gettext("One of the sets is not "
"enabled"));
break;
case ENXIO:
rdc_warn(NULL, gettext("One of the sets is not "
"logging"));
break;
default:
break;
}
} else {
ret = 0;
}
if (ret)
ret = 1;
return (ret);
}
/*
* verify_groupname: Check the group name for the following rules:
* 1. The name does not start with a '-'
* 2. The name does not contain any space characters as defined by
* isspace(3C).
*
* If either of these rules are broken, error immediately.
*/
static void
verify_groupname(char *grp)
{
int i;
if (grp[0] == '-') {
rdc_err(NULL, gettext("group name cannot start with a '-'"));
}
for (i = 0; grp[i] != '\0'; i++) {
if (isspace(grp[i])) {
rdc_err(NULL, gettext("group name cannot contain a "
"space"));
}
}
}