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
* 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 <stdio.h>
#include <errno.h>
#include <values.h>
#include <limits.h>
#include <fcntl.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <stdarg.h>
#include <netdb.h>
#include <ctype.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;
/*
* 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))
/*
* config file user level Dual copy pair structure
*/
typedef struct _sd_dual_pair {
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 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 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 *
{
if (!urdc)
return ("");
return (gettext("volume failed"));
return (gettext("fcal failed"));
return (gettext("bitmap failed"));
return (gettext("disk queue failed"));
return (gettext("need sync"));
return (gettext("need reverse sync"));
return (gettext("queuing"));
else
return (gettext("logging"));
return (gettext("reverse syncing"));
else
return (gettext("syncing"));
return (gettext("syncing"));
else
return (gettext("reverse syncing"));
}
return (gettext("replicating"));
}
static int
{
int j;
int setnumber;
char key[CFG_MAX_KEY];
char buf[CFG_MAX_BUF];
char sync[16];
found = 0;
user_shost = "";
user_sdev = "";
if (!rdc_status) {
}
ustatus = spcs_s_ucreate();
if (rc == SPCS_S_ERROR) {
}
} else {
gettext("unable to access configuration"));
}
for (i = 0; i < max; i++) {
continue;
if (match &&
continue;
found = 1;
/* get sndr entries until shost, sfile match */
for (j = 0; j < rdc_maxsets; j++) {
setnumber = j + 1;
break;
}
continue;
continue;
else
/* Got the matching entry */
break;
}
if (j == rdc_maxsets)
continue; /* not found in config */
continue;
continue;
if (file_format) {
(void) printf("%s %s %s %s %s %s %s %s",
directfile, sync);
(void) printf("\n");
continue;
}
continue;
} else {
}
if (!verbose)
continue;
else
}
}
if (!cfgp)
}
return (0);
}
int
{
int gflag = 0;
int Cflag = 0;
int qflag = 0;
int j;
if (argc == 0)
return (0);
return (-1);
for (j = 0; j < argc; j += 2) {
if (gflag)
return (-1);
gflag = 1;
}
if (!clustered)
return (-1);
if (Cflag)
return (-1);
Cflag = 1;
}
if (qflag)
return (-1);
qflag = 1;
}
}
return (0);
}
static int
{
int rc = 0;
char sync[16];
char options[64], *p, *q;
int len;
if (rc != 12)
else {
gettext("set %s:%s neither sync nor async"),
}
p += 5;
q = strchr(p, ';');
if (q) {
/* LINTED p & q limited to options[64] */
len = q - p;
} else {
}
} else if (lghn) {
*lghn = '\0';
}
return (0);
}
static int
{
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;
if (!clustered)
return (0);
/*
* 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.
*/
gettext("either %s:%s or %s:%s is not local"),
}
/*
* 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.
*/
/*
* Autogenerate a ctag, if not "-C local" or no "-C " specified
*/
/*
* making an exception here for users giving the "local"tag
* this overrides this error message. (rdc_islocal ! = 1)
*/
"match disk group name \"%s\" of volume %s"), ctag,
return (-1);
}
/*
* Do we have a non-volume managed disk without -C local specified?
*/
" of a disk group,\nplease specify resource ctag\n"),
}
/*
* Do we have a volume managed disk with -C local?
*/
"volume \"%s\" is part of a disk group\n"), localfile);
}
/*
* Local bitmap must also have same ctag.
*/
/*
* Assure that if the primary has a device group, so must the bitmap
*/
return (-1);
}
/*
* Assure that if the if there is a ctag, it must match the bitmap
*/
"match disk group name \"%s\" of bitmap %s"), ctag,
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.
*/
/*
* Assure that if the primary has a device group, so must
* the disk queue
*/
"group \"%s\""), diskq,
return (-1);
}
/*
* Assure that if the if there is a ctag, it must match
* the disk queue
*/
"match disk group name \"%s\" of disk queue %s"),
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
{
int i, setnumber;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
if (*newq == '\0')
return (DISKQ_OKAY); /* okay, */
newgroup = "--nomatch--";
}
if (open_cfg) {
gettext("unable to access configuration"));
}
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
break;
/*
* I think this is quicker than
* having to double dip into the config
*/
(diskqueue[0] != '\0')) {
if (open_cfg)
return (DISKQ_FAIL);
}
continue;
}
if (*newq == '\0') {
if (diskqueue[0] != '\0')
if (open_cfg)
return (DISKQ_OKAY); /* okay, */
}
if (open_cfg)
return (DISKQ_REWRITEG);
== 0 ? DISKQ_OKAY : DISKQ_FAIL);
}
if (open_cfg)
return (DISKQ_OKAY);
}
int
{
int i, j;
int rc;
for (i = 0; i < newpair; i++) {
continue;
== 0)
return (DISKQ_OKAY); /* matches existing group */
gettext("disk queue %s does not match %s "
return (DISKQ_FAIL);
}
return (DISKQ_OKAY); /* changed to existing group que */
}
for (j = 0; j < newpair; j++) {
NSC_MAXPATH) == 0)) {
}
}
return (DISKQ_OKAY);
}
break; /* no problem with pair_list sets */
}
/* now check with already configured sets */
if (rc == DISKQ_REWRITEG) {
for (i = 0; i < newpair; i++) {
continue;
}
}
return (rc);
}
int
{
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++) {
break;
continue;
continue;
continue;
return (1);
}
return (0);
}
void
{
char *master;
char *shadow;
char *bitmap;
char c;
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 */
usage();
exit(1);
}
switch (c) {
case 'd':
/* Delete an ndr_ii entry */
gettext("unable to access configuration"));
found = 0;
/* get ndr_ii entries until a match is found */
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
break;
continue;
/* Got a matching entry */
break;
continue;
"ndr_ii.set%d.bitmap",
break;
continue;
gettext("unable to remove \"%s\" "
"from configuration storage: %s"),
} else {
if (cfg_commit(cfg) < 0)
gettext("ndr_ii set %s %s %s "
"not deconfigured."),
else
gettext("ndr_ii set %s %s %s "
"has been deconfigured."),
}
found = 1;
break;
}
if (!found) {
gettext("did not find matching ndr_ii "
}
break;
case 'a':
/* Add an ndr_ii entry */
gettext("unable to access configuration"));
found = 0;
/* get ndr_ii entries in case a match is found */
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
break;
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
* before we get here
*/
/*CSTYLED*/
for (i = 0; ; i++) {
setnumber = i + 1;
/*
* Checking local bitmaps
*/
break;
if (self_check(buf)) {
"sndr.set%d.pbitmap",
} else {
}
break;
gettext("%s is already configured "
"as a Remote Mirror bitmap"), buf);
}
}
"%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 "
"%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 "
} else {
"%s %s %s has been configured."),
}
/*
* 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};
"not in a device group"),
"not in different device groups"),
else {
"%s %s %s update %s",
}
} else {
}
(cfg_commit(cfg) < 0))
"configuration storage: %s"),
break;
default:
usage();
exit(1);
}
}
void
{
int i;
int entries;
char **entry;
gettext("unable to access configuration"));
/*
* look into II config to see if this is being used elsewhere
*/
for (i = 0; i < entries; i++) {
/*
* got master, shadow, overflow, and bitmap, now compare
*/
gettext("bitmap %s is in use by"
" Point-in-Time Copy"), bmp);
}
}
if (entries)
/*
* and last but not least, make sure sndr is not using vol for anything
*/
for (i = 0; i < entries; i++) {
/*
* I think this is quicker than
* having to double dip into the config
*/
if (cmd == RDC_CMD_ENABLE) {
if (self_check(host)) {
gettext("bitmap %s is already "
"in use by StorEdge Network Data "
"Replicator"), bmp);
}
} else {
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)) {
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
} else {
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
}
} else { /* self_check(hostp) failed */
if (self_check(host)) {
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
} else {
gettext("bitmap %s is already "
"in use by StorEdge Network "
"Data Replicator"), bmp);
}
}
}
}
}
if (entries)
}
int
check_intrange(char *arg) {
int i;
"decimal between 1 and %d", MAXINT);
return (0);
}
}
errno = 0;
"between 1 and %d", MAXINT);
return (0);
}
return (1);
}
void
{
int set;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
break;
}
continue;
continue; /* not the group we want */
} else { /* no group specified */
continue;
continue;
}
}
}
}
void
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;
int resourced = 0;
gettext("unable to access configuration"));
gettext("unable to lock configuration"));
redo:
if (cfg_load_svols(cfg) < 0 ||
cfg_load_dsvols(cfg) < 0 ||
cfg_load_shadows(cfg) < 0)
gettext("Unable to parse config filer"));
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
if (rc < 0)
break;
continue;
found = 1;
break;
}
} else {
found = 1;
break;
}
}
}
if (!found) {
gettext("Unable to find %s:%s in "
"configuration storage"),
} else {
gettext("Unable to find group %s in "
"configuration storage"), group_arg);
}
}
resourced = 1;
setnumber = 0;
goto redo;
}
if (clustered && !rdc_islocal) {
"do not match, proceeding with operation based "
}
switch (subcmd) {
case RDC_CMD_ADDQ:
exit(1);
"has a disk queue"));
}
}
0) < 0) {
"queue [%s] from configuration"), qvol);
"failed"));
}
"operation failed"));
}
}
"diskqueue %s to set %s:%s and its group"), qvol,
break;
case RDC_OPT_FORCE_QINIT:
"have a disk queue"));
}
0) < 0) {
exit(1);
}
break;
case RDC_CMD_INITQ:
"have a disk queue"));
}
0) < 0) {
exit(1);
}
break;
case RDC_CMD_REMQ:
"have a disk queue"));
}
0) < 0) {
exit(1);
}
break;
case RDC_CMD_KILLQ:
"have a disk queue"));
}
0) < 0) {
}
"removed diskqueue from set %s:%s and its group "),
break;
case RDC_CMD_REPQ:
exit(1);
"have a disk queue"));
}
0) < 0) {
}
/* commit here, enable may fail */
if (cfg_commit(cfg) < 0) {
}
gettext("cannot replace disk queue %s with %s"),
}
0) < 0) {
"queue [%s] from configuration"), qvol);
}
"operation failed"));
}
"diskqueue for set %s:%s and its group with %s"),
break;
}
if (cfg_commit(cfg) < 0)
if (ctag)
}
void
{
while (setp) {
if (start) {
gettext("%s %s %s %s %s %s %s %s\nSync Started"),
} else {
gettext("%s %s %s %s %s %s %s %s\nSync Ended"),
}
}
}
void
{
if (qblock == RDC_OPT_SET_QNOBLOCK)
"set to non blocking for %s:%s and any members "
else if (qblock == RDC_OPT_CLR_QNOBLOCK)
"set to blocking for %s:%s and any members "
if (maxqfbas)
if (maxqitems)
if (asyncthr)
}
int
set_qblock(char *blockarg)
{
else
return (1);
return (0);
}
static void
{
volcount_t *vc;
int on_pri = 0;
int on_sec = 0;
/* are we on the primary or secondary host? */
on_pri = 1;
on_sec = 1;
}
} else if (self_check(phost)) {
on_pri = 1;
} else if (self_check(shost)) {
on_sec = 1;
}
if (on_pri) {
} else if (on_sec) {
} else {
"node is primary or secondary"));
}
/* set up parms structure */
ustatus = spcs_s_ucreate();
/*
* 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.
*/
/* if we get to this point, then a set was disabled. try sv-disable */
"[%s] from configuration"), datavol);
"[%s] from configuration"), bmpvol);
}
void
check_rdcsecondary(char *secondary)
{
int i;
int entries;
char **entry;
char *sha;
char *buf;
gettext("error opening config"));
for (i = 0; i < entries; i++) {
gettext("secondary %s is in use by"
" Point-in-Time Copy"), secondary);
}
}
if (entries)
}
int
{
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];
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;
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 *required;
char *role_env;
int checksetfields = -1;
int oflag = 0;
int host_not_found = 0;
(void) textdomain("rdc");
allow_role = 1;
if (rc < 0) {
gettext("unable to determine the current "
gettext("incorrect Solaris release (requires %s)\n"),
required);
}
if ((clustered = cfg_iscluster()) < 0) {
}
if (rdc_maxsets == -1) {
gettext("unable to get maxsets value from kernel"));
}
gettext("unable to allocate pair_list array for %d sets"),
}
qblock = 0;
while ((c =
#ifdef DEBUG
#else
#endif
!= -1) {
switch (c) {
case 'B':
if (!allow_role || flag) {
inval = 1;
break;
}
Bflag = 1;
flag = RDC_BITMAPOP;
break;
case 'H':
/* 'h' was already assigned */
if (flag)
inval = 1;
break;
case 'I':
/* List or edit ndr_ii configuration entries */
Iflag = 1;
break;
case 'R':
if (flag)
inval = 1;
break;
#ifdef DEBUG
case 'U': /* UDP support */
proto_test = 1;
break;
#endif
case 'F':
inval = 1;
if (check_intrange(optarg))
else
exit(1);
break;
case 'W':
inval = 1;
if (check_intrange(optarg))
else
exit(1);
break;
case 'A':
inval = 1;
if (check_intrange(optarg))
else
exit(1);
break;
case 'D':
inval = 1;
if (set_qblock(optarg)) {
usage();
exit(1);
}
break;
case 'a':
inval = 1;
else
inval = 1;
break;
case 'C':
if (clustered) {
} else
inval = 1;
break;
case 'g':
if (flag == RDC_CMD_ENABLE)
inval = 1;
geflag = 1;
break;
case 'b':
/* ignore */
break;
case 'n':
nflag = 1;
break;
case 'd':
if (flag)
inval = 1;
break;
case 'e':
inval = 1;
iflag |= RDC_OPT_SETBMP;
break;
case 'E':
if (flag)
inval = 1;
iflag |= RDC_OPT_CLRBMP;
break;
case 'f':
fflag = 1;
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') {
} else {
}
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;
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;
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++;
}
}
errflag = 1;
}
/* Mutually incompatible */
usage();
exit(1);
}
if (Iflag) {
exit(0);
}
if (vflag) {
ustatus = spcs_s_ucreate();
if (rc == SPCS_S_ERROR) {
}
#ifdef DEBUG
#else
if (rdc_version.micro) {
"Remote Mirror version %d.%d.%d\n"),
} else {
}
#endif
exit(0);
}
usage();
exit(1);
}
/* print with no set specified */
}
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];
offset = 1;
offset = 0;
offset = 1;
} else {
q_usage(1);
exit(1);
}
/* pick out single set as shost:svol */
if (ptr)
else {
q_usage(1);
exit(1);
}
if (ptr)
else {
q_usage(1);
exit(1);
}
}
q_usage(1);
exit(1);
}
exit(0);
}
/* See what is to be reconfigured */
else {
usage();
exit(1);
}
usage();
exit(2);
}
switch (c) {
case 'b':
usage();
exit(1);
}
else {
usage();
exit(1);
}
optind++;
break;
#ifdef _RDC_CAMPUS
case 'd':
break;
#endif
case 'g':
break;
case 'C':
if (clustered) {
} else {
usage();
exit(1);
}
break;
case 'm':
reconfig_doasync = 0;
reconfig_doasync = 1;
else {
usage();
exit(1);
}
optind++;
break;
case 'r':
if (allow_role) {
break;
}
default:
usage();
exit(1);
}
}
}
if (fflag) {
checksetfields = 1;
usage();
exit(1);
}
} else {
/* Use libcfg to figure out what to operate on */
cfgflag = 1;
#ifdef DEBUG
#endif
checksetfields = 0;
} else {
usage();
exit(1);
}
}
}
if (cfgflag) {
if (flag == RDC_CMD_ADDQ ||
flag == RDC_CMD_REMQ ||
flag == RDC_CMD_KILLQ ||
flag == RDC_CMD_INITQ) {
"for disk queue operations"));
}
} else if (fflag) {
if (flag == RDC_CMD_ADDQ ||
flag == RDC_CMD_REMQ ||
flag == RDC_CMD_KILLQ ||
flag == RDC_CMD_INITQ) {
"for disk queue operations"));
}
}
if (cfgflag) {
if (flag == RDC_CMD_ENABLE) {
"for enable command"));
}
reconfig_sbitmap)) {
"for bitmap reconfiguration"));
}
if (flag == RDC_BITMAPOP) {
"for bitmap set command"));
}
if (pairs == 0) {
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
*/
char tohost_arg[MAX_RDC_HOST_SIZE];
char tofile_arg[NSC_MAXPATH];
char *ptr;
checksetfields = 0;
if (flag == RDC_CMD_ENABLE) {
gettext("must specify full set details for "
"enable command"));
}
if (ptr)
else {
}
if (ptr)
else {
}
/* Now look up tohost:tofile via libcfg */
gettext("unable to access configuration"));
gettext("unable to lock configuration"));
setnumber = 0;
found = 0;
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
if (rc < 0)
break;
sizeof (tohost));
continue;
sizeof (tofile));
continue;
found = 1;
sizeof (fromhost));
sizeof (fromfile));
sizeof (frombitmap));
sizeof (tobitmap));
sizeof (directfile));
(void) cfg_get_cstring(
sizeof (group));
continue;
(void) cfg_get_cstring(
gettext("ctags %s and %s "
doasync = 0;
doasync = 1;
else {
gettext("set %s:%s neither sync "
}
break;
}
if (!found) {
gettext("set %s:%s not found in config"),
}
} else {
checksetfields = 1;
/* Check the length of entries from the command line */
gettext("hostname is longer than %d "
}
/* Check if it's ip address -- not allowed */
"The hostname specified is invalid.\n"
"See 'man inet(3SOCKET)'"));
}
}
#ifdef _RDC_CAMPUS
/* FCAL directio */
#else
#endif
usage();
exit(1);
} else
doasync = 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.
*/
(flag != RDC_CMD_ENABLE)) {
int setnumber;
char key[CFG_MAX_KEY];
gettext("unable to access configuration"));
}
gettext("unable to lock configuration"));
}
if ((setnumber =
tofile)) < 0) {
gettext("unable to find Remote "
"Mirror set "
"%s:%s in config"),
}
MAX_RDC_HOST_SIZE) < 0) {
gettext("unable to determine ctag "
"for Remote Mirror set %s:%s"),
}
}
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.
*/
/* string case statement */
"longer than %d characters\n"),
(NSC_MAXPATH - 1));
}
if (!clustered) {
usage();
exit(1);
}
"is longer than %d characters\n"),
(MAX_RDC_HOST_SIZE - 1));
}
/*
* 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.
*/
"%s and %s do not match "),
}
"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?
*/
(char *)&fromnetaddr, (char *)&tonetaddr) < 0) {
"addresses for either host %s or host %s"),
if (flag != RDC_CMD_DISABLE)
exit(1);
else
host_not_found = 1;
}
/*
* Are we running on neither host?
*/
if (flag == RDC_CMD_DISABLE) {
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 (flag == RDC_CMD_RECONFIG) {
if (reconfig_pbitmap) {
}
if (reconfig_sbitmap) {
}
#ifdef _RDC_CAMPUS
if (reconfig_direct)
#endif
if (reconfig_group)
if (strlen(reconfig_ctag) > 0)
if (reconfig_doasync != -1)
}
exit(1);
group)) == DISKQ_FAIL) {
"incompatible with existing queue"),
}
}
pairs = 1;
} else {
if (pairs == 0) {
"matching Remote Mirror sets"), config_file);
}
}
exit(1);
while (pairs--) {
}
if (pflag) {
static int first = 1;
if (first) {
gettext("unable to access configuration"));
gettext("unable to lock configuration"));
first = 0;
}
if (pairs == 0) {
exit(0);
}
/* short circuit the rest of the command loop */
continue;
}
if (Bflag) {
int ret;
boffset);
}
char orig_fbmp[MAXHOSTNAMELEN];
char orig_tbmp[MAXHOSTNAMELEN];
int ret;
ustatus = spcs_s_ucreate();
" before reconfig operation"));
}
}
/*
* take a peek in the config to see if
* the bitmap is being used elsewhere
*/
if (flag == RDC_CMD_ENABLE) {
/*
* just for fun, lets see if some silly person
* specified the same vol and bitmap
*/
" must not match"));
if (self_check(fromhost)) {
gettext("unable to access %s: %s"),
}
gettext("%s is not a character device"),
fromfile);
}
} else { /* on the secondary */
/* extra check for secondary vol */
gettext("unable to access %s: %s"),
}
gettext("%s is not a character device"),
tofile);
}
}
}
flag == RDC_CMD_RECONFIG) {
gettext("unable to access configuration"));
gettext("unable to lock configuration"));
} else
if (cfg && perform_autosv() &&
flag == RDC_CMD_RECONFIG)) {
if (cfg_load_svols(cfg) < 0 ||
cfg_load_dsvols(cfg) < 0 ||
cfg_load_shadows(cfg) < 0)
gettext("Unable to parse config filer"));
}
/* Enabled, so add the set via libcfg */
/* Build a new sndr entry and put it */
if ((diskqueue_p == place_holder) &&
(group_p != place_holder)) {
if (*diskqueue)
}
/*
* format in pconfig is:
* phost.primary.pbitmap.shost.secondary.
* sbitmap.type.mode.group.cnode.options.diskq
*/
"%s %s %s %s %s %s %s %s %s %s - %s",
gettext("unable to add \"%s\" to "
"configuration storage: %s"),
if (setnumber < 0)
gettext("unable to add \"%s\" to "
"configuration storage: %s"),
else
cfg_success = 1;
/* Add cluster aware info */
if (clustered && !rdc_islocal) {
if (self_check(fromhost)) {
gettext("unable to add "
"\"%s\" to configuration "
"storage: %s"),
}
} else if (self_check(tohost)) {
gettext("unable to add "
"\"%s\" to configuration "
"storage: %s"),
}
}
}
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;
CFG_MAX_BUF) < 0) {
break;
}
CFG_MAX_BUF) < 0)
break;
continue;
CFG_MAX_BUF) < 0)
break;
continue;
found = 1;
#ifdef DEBUG
if (checksetfields == -1) {
gettext("checksetfields not set"));
}
#endif
if (checksetfields) {
}
/* perform cluster specific options */
if (clustered) {
/* get the logical host, if set */
(void) cfg_get_single_option(cfg,
/* figure out the cluster tag, if any */
CFG_MAX_BUF) < 0)
break;
" and %s do not match"),
} else {
*lhname = '\0';
*ctag = '\0';
}
/* figure out the disk queue, if any */
"sndr.set%d.diskq",
CFG_MAX_BUF) < 0)
break;
} else {
*diskqueue = '\0';
}
gettext("unable to remove \"%s\" "
"from configuration storage: %s"),
else
cfg_success = 1;
break;
}
if (found == 0) {
gettext("Unable to find %s:%s in "
"configuration storage"),
}
if (host_not_found) {
lhname);
if (cfg_commit(cfg) < 0)
"force disable failed"));
return (0);
}
/* Update relevant cfg record */
/* get sndr entries until shost, sfile match */
for (i = 0; i < rdc_maxsets; i++) {
setnumber = i + 1;
CFG_MAX_BUF) < 0) {
break;
}
CFG_MAX_BUF) < 0)
break;
continue;
CFG_MAX_BUF) < 0)
break;
continue;
CFG_MAX_BUF) < 0)
break;
if (reconfig_ctag[0] == '\0')
if (doasync)
else
/*
* if we are reconfigging out altogether,
* get rid of the diskqueue
*/
if (group_p == place_holder)
else
/*
* 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)) {
}
CFG_MAX_BUF) < 0) {
break;
}
ctag : place_holder;
"%s %s %s %s %s %s %s %s %s %s %s %s",
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
else
cfg_success = 1;
break;
}
}
if (cfg_success) {
if (cfg && perform_autosv()) {
if (self_check(fromhost)) {
if (diskqueue[0] &&
"queue volume %s must not "
"match any primary Remote "
"Mirror volume or bitmap"),
}
if (diskqueue[0]) {
}
} else {
}
}
/*
* 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) {
"options", 0);
gettext("rdc config alloc"
}
continue;
}
"mode", "group", "ctag", "options", 0);
}
continue;
}
/*
* block incoming signals until after the possible
* cfg_commit is done
*/
block_sigs();
;
/*EMPTY*/
} else if (cfg) {
if (diskq_group == DISKQ_REWRITEG) {
}
if (perform_autosv() &&
(flag == RDC_CMD_ENABLE ||
flag == RDC_CMD_DISABLE ||
flag == RDC_CMD_RECONFIG)) {
}
if ((iflag & RDC_OPT_REVERSE_ROLE) != 0 &&
allow_role) {
ctag : place_holder;
"%s %s %s %s %s %s %s %s %s %s %s",
gettext("unable to update \"%s\" "
"in configuration storage: %s"),
}
if (cfg_commit(cfg) < 0) {
"reversal failed"));
}
}
unblock_sigs();
}
if (cfg) {
}
}
if (flag == RDC_CMD_COPY) {
perror("fork");
exit(1);
}
} else {
exit(0);
}
if (pid > 0) /* parent process */
exit(0);
if (iflag & RDC_OPT_REVERSE) {
if (iflag & RDC_OPT_UPDATE)
else
} else if (iflag & RDC_OPT_UPDATE) {
} else
while (rcp) {
/* rclist->msg has already been gettext'd */
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
}
}
if (sets)
if (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.
*/
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
*/
if (rc < 0) {
}
if (rc == 0) {
"active on node \"%s\"\nPlease re-issue "
}
}
static void
{
dev1);
}
dev2);
}
}
}
static void
{
char *altname;
int rc;
if (!cfg) {
"validate_name"));
}
if (rc < 0) {
"config file\n"));
"file\n"));
}
if (rc) {
"configured previously as '%s'. Re-enter command with "
}
}
/*
* 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
{
char auto_tag[CFG_MAX_BUF];
int set;
/* verify valid autosync request */
#ifdef DEBUG
gettext("set_autosync called with improper value"));
#endif
return;
}
}
}
if (clustered) {
} else {
}
/* find set number in config */
}
/* Check if there are any options already set, including ours */
CFG_MAX_BUF) >= 0) {
options = 1;
do {
already_set = 1;
}
}
/* options already exist, edit ours out */
if (options && already_set) {
char *p, *q;
int need_to_clear_buf = 1;
}
/* parse out our options, all of the form "auto=" */
q = strtok(p, ";");
do {
need_to_clear_buf = 0;
}
free(p);
/* if we were the only option, clear the field */
if (need_to_clear_buf) {
}
"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) {
< 0) {
"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 {
break;
}
break;
break;
break; /* not in a group */
continue;
options = 0;
already_set = 0;
break; /* last set processed */
}
break;
continue; /* not the group we want */
set);
/*
* Check if there are any options already set,
* including ours
*/
options = 1;
do {
already_set = 1;
}
CFG_MAX_BUF) >= 0);
}
/* options already exist, edit ours out */
if (options && already_set) {
char *p, *q;
int need_to_clear_buf = 1;
< 0) {
"options field for Remote Mirror set "
}
/*
* parse out our options, all of the
* form "auto="
*/
q = strtok(p, ";");
do {
/*
* keep it
*/
need_to_clear_buf = 0;
}
free(p);
/*
* if we were the only option,
* clear the field
*/
if (need_to_clear_buf) {
}
< 0) {
"autosync value in config for "
"Remote Mirror set %s:%s"),
cfg_success = 0;
}
}
/*
* autosync is not present in options field,
* add if on is requested
*/
if (autosync_val == AUTOSYNC_ON) {
auto_tag, "on") < 0) {
" autosync value in config for "
"Remote Mirror set %s:%s"),
cfg_success = 0;
}
}
}
/* CONSTCOND */
} while (0);
if (cfg_success) {
if (cfg_commit(cfg) < 0) {
}
}
}
/*
* 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
{
char key[CFG_MAX_KEY];
}
}
0) {
}
CFG_MAX_BUF) >= 0) {
do {
}
break;
}
}
return (autosync_val);
}
void
{
rdc_addr_t *p;
ustat = spcs_s_ucreate();
SPCS_S_OK) {
}
}
static int
{
int ret;
char orig_diskq[NSC_MAXPATH];
int success = 1;
int autosync_toggle_needed = 0;
}
/* we have to find out what to sv disable after reconfig */
if (flag == RDC_CMD_RECONFIG) {
ustatus = spcs_s_ucreate();
" before reconfig operation"));
}
}
/*
* another terrible addition, if we are reconfigging mode
* and not logging, just give up.
*/
if ((reconfig_doasync != -1) &&
"Remote Mirror set not logging"));
"Remote Mirror set not logging"));
}
/*
* Now build up the address for each host including port and transport
*/
if (getaddr) {
rpcbind);
"information for %s"), toname);
#ifdef DEBUG
#endif
return (-1);
}
} else {
}
#ifdef DEBUG_ADDR
(void) printf("\n");
#endif
if (getaddr) {
rpcbind);
#ifdef DEBUG
(void) printf("get_addr failed for Ver 4 %s\n",
fromname);
#endif
return (-1);
}
}
#ifdef DEBUG_ADDR
(void) printf("\n");
#endif
if (getaddr) {
#ifdef DEBUG_ADDR
#endif
} else {
}
if (!clustered)
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.
*/
gettext("either %s:%s or %s:%s is not local"),
}
}
else
if (self_check(tohost) &&
" Mirror secondary is not allowed (%s)"),
else
/* set up the permanent set id for this set */
if (flag == RDC_CMD_ENABLE) {
char key[CFG_MAX_KEY];
char setid[64];
int set;
}
}
setid) < 0) {
}
}
}
/*
* 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.
*/
/* gethostid(3c) is defined to return a 32bit value */
if (*doasync)
else
} else if (flag == RDC_CMD_COPY) {
if (reverse)
else
}
if (self_check(fromname)) {
" as a file system is mounted on %s"),
fromfile);
else
} else {
}
" only be done on a primary Remote Mirror host"));
" only be done on a primary Remote Mirror host"));
}
ustatus = spcs_s_ucreate();
if (flag == RDC_CMD_COPY) {
" as Remote Mirror set %s:%s is not logging"),
}
gettext("%s %s %s %s %s %s %s %s\nStarting"),
}
if ((flag == RDC_CMD_COPY) &&
} else if ((flag == RDC_CMD_LOG) &&
/* check if autosync needs to be turned off when going to logging */
(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
*/
if (autosync == AUTOSYNC_OFF) {
} else if (autosync == AUTOSYNC_ON) {
ustatus);
"status of Remote Mirror set %s:%s"),
}
/* need to reset the tunables after a status ioctl */
/*
* if in logging mode, just update config, kernel will
*/
} else {
}
}
}
if (autosync_toggle_needed) {
"autosync for Remote Mirror set %s:%s"), tohost,
tofile);
}
/* reset command and default autosync flags */
}
errno = 0;
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
if (errno == RDC_EEINVAL) {
"%s %s %s %s %s %s %s %s\n%s",
gettext("invalid command option"));
gettext("Remote Mirror: invalid command option "
} else {
"%s %s %s %s %s %s %s %s",
if ((flag == RDC_CMD_RECONFIG) &&
(!(iflag & RDC_OPT_REVERSE_ROLE))) {
success = 0;
} else
}
}
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 */
/* yes, numels could be -1 */
for (i = 1; i < numels; i++) {
sizeof (shostbuf));
continue;
sizeof (svolbuf));
continue;
break;
/*
* found it, now i contains the set offset.
* i, being the variable, not bad english.
*/
}
/* shouldn't happen */
"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
*/
"sndr.set%d.mode", i);
*doasync = 1;
strlen("async")) < 0) {
cfgsuccess = 0;
}
} else {
*doasync = 0;
strlen("sync")) < 0) {
cfgsuccess = 0;
}
}
}
#ifdef _RDC_CAMPUS
strlen(directfile)) < 0)
cfgsuccess = 0;
} else {
strlen(directfile)) < 0)
cfgsuccess = 0;
}
#endif
"sndr.set%d.group", i);
cfgsuccess = 0;
} else {
"sndr.set%d.group", i);
cfgsuccess = 0;
}
} else {
}
"sndr.set%d.diskq", i);
cfgsuccess = 0;
"sndr.set%d.pbitmap", i);
strlen(frombitmap)) < 0)
cfgsuccess = 0;
cfgsuccess = 0;
if (clustered)
cfgsuccess = 0;
if (cfgsuccess == 0) {
"configuration storage"));
}
} else {
"%s %s %s %s %s %s %s %s\n%s",
gettext("unable to update config file"));
gettext("Remote Mirror: unable to update "
"config file"));
}
}
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
if (ret == RDC_ACTIVE)
else if (ret == RDC_INACTIVE)
else
if (errno == RDC_EEINVAL) {
"%s %s %s %s %s %s %s %s\n%s",
gettext("invalid command option"));
gettext("Remote Mirror: invalid command option "
"'%s'"),
}
}
if (flag == RDC_CMD_STATUS) {
gettext("Remote Mirror: %s %s %s %s %s %s\n"),
} else if (success) {
gettext("%s %s %s %s %s %s %s %s\nSuccessful"),
if (flag == RDC_CMD_TUNABLE)
}
if (cfg && perform_autosv()) {
/* figure out which are the local volumes */
vol2 = frombitmap;
else
} else {
if ((flag == RDC_CMD_ENABLE) &&
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
*/
< 0) {
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
/*
* warn here, but we are going to exit
* we want to catch any errors on the
* way down, then exit
*/
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
/*
* ok, we should've reported any errs
* exit explictly
*/
exit(1);
}
}
< 0) {
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
/*
* warn here, but we are going to exit
* we want to catch any errors on the
* way down, then exit
*/
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
/*
* ok, we should've reported any errs
* exit explictly
*/
exit(1);
}
}
< 0) {
"sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
"sndr") < 0)
"remove volume [%s] from "
"configuration"), vol1);
"sndr") < 0)
"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
*/
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
/*
* 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;
< 0)
"remove volume [%s] from "
"configuration"), vol1);
} else if (!vc) {
gettext("Unable to find %s in config"),
vol1);
}
if (vol2) {
"sndr") < 0)
"remove volume [%s] from "
"configuration"), vol2);
} else if (!vc) {
" %s in config"), vol2);
}
}
"sndr") < 0)
"remove disk queue [%s] from "
"configuration"), diskqueue);
} else if (!vc) {
" %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 */
"sndr") < 0)
"remove bitmap [%s] from "
"configuration"), orig_fbmp);
} else if (!vc) {
"%s in config"), orig_fbmp);
}
"sndr") < 0) {
"reconfig sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
exit(1);
}
}
} else if ((orig_diskq[0] != '\0') &&
"sndr") < 0)
"remove disk queue [%s] from "
"configuration"), orig_diskq);
} else if (!vc) {
"%s in config"), orig_diskq);
}
if (vol3 &&
"sndr") < 0) {
"enable of diskqueue %s failed, "
"disabling Remote Mirror set %s:%s",
"enable of diskqueue %s failed."
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
exit(1);
}
}
}
} else if (flag != RDC_OPT_PRIMARY) {
"sndr") < 0)
"remove bitmap [%s] from "
"configuration"), orig_tbmp);
} else if (!vc) {
gettext("Unable to find %s in config"),
}
"sndr") < 0) {
"reconfig sv enable failed for %s, "
"disabling Remote Mirror set %s:%s",
"unable to sv enable %s\n"
"disabling Remote Mirror set %s:%s",
gettext("Remote Mirror:"
" %s %s %s %s %s %s\n"),
tobitmap);
"%s %s %s %s %s %s %s %s",
}
exit(1);
}
}
}
}
/* END 4 space indenting */
}
}
return (0);
}
/*
* read_config()
*
* DESCRIPTION: Read the lines in a configuration file and return the
* 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
{
int ret;
char dsk_flagstr[NSC_MAXPATH];
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;
int i, j;
char *extra_args[EXTRA_ARGS];
for (j = 0; j < EXTRA_ARGS; j++)
}
i = 0;
continue;
ret = 0;
continue;
}
ret++;
}
"device name is longer than %d "
continue;
}
ret++;
}
"device name is longer than %d "
continue;
}
ret++;
}
"hostname is longer than %d "
continue;
}
ret++;
}
"device name is longer than %d "
continue;
}
ret++;
}
"device name is longer than %d "
continue;
}
ret++;
}
ret++;
}
ret++;
}
for (j = 0; j < EXTRA_ARGS; j++) {
ret++;
}
}
if (ret == 0) /* this is a blank line */
continue;
if (ret < 8) {
}
if (i >= rdc_maxsets) {
gettext("number of Remote Mirror sets exceeds %d"),
}
#ifdef _RDC_CAMPUS
if (dsk_flagstr[0] == '/') {
/* fcal directio */
#else
#endif
#ifdef _RDC_CAMPUS
#else
gettext("ip specification missing"));
#endif
} else
doasync = 0;
doasync = 1;
else {
}
}
line);
}
continue; /* Ignore illegal sets */
if (pair_diskqueue_check(i))
continue; /* ignore sets with incorrect diskq */
}
/* Filter according to ctag and group arguments */
continue;
continue;
i++;
}
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
{
int rc;
int i;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
int setnumber;
else {
}
setnumber = 0;
/*CSTYLED*/
for (i = 0; i < rdc_maxsets;) {
setnumber++;
if (rc < 0)
break;
continue;
continue;
if (flag == RDC_CMD_RECONFIG) {
if (reconfig_pbitmap)
if (reconfig_sbitmap)
#ifdef _RDC_CAMPUS
if (reconfig_direct)
#endif
if (reconfig_group)
if (strlen(reconfig_ctag) > 0)
if (reconfig_doasync != -1)
}
continue; /* Ignore illegal sets */
}
i++;
}
return (i);
}
void
{
if (prhdr)
gettext("\t%s -g <group> -q a <vol>\t\tadd disk queue to "
"group\n"), program);
gettext("\t%s -g <group> -q d \t\tremove disk queue from"
" group\n"), program);
gettext("\t%s -g <group> -q r <newvol>\treplace disk queue for a"
" group\n"), program);
gettext("\t%s -q a <vol> <shost>:<sdev>\tadd disk queue to "
"a set\n"), program);
gettext("\t%s -q d <shost>:<sdev>\t\tremove disk queue from "
"a set\n"), program);
gettext("\t%s -q r <newvol> <shost>:<sdev>\treplace disk queue for "
"a set\n"), program);
}
static void
usage()
{
gettext("\t%s [opts] -a {on | off} [set]\t"
"set autosync\n"), program);
gettext("\t%s [opts] -A <asyncthr> [set]\t"
"set the number of asynchronous\n\t\t\t\t\t\tthreads\n"),
program);
gettext("\t%s [opts] -d [set]\t\t\t"
"disable\n"), program);
gettext("\t%s [opts] -e [set]\t\t\t"
"enable with bits in bitmap set\n"), program);
gettext("\t%s [opts] -E [set]\t\t\t"
"enable with bits in bitmap clear\n"), program);
gettext("\t%s [opts] -F <maxqfbas> [set]\t"
"set maximum fbas to queue\n"), program);
gettext("\t%s [opts] -D {block | noblock} [set]\t"
"set disk queue blocking mode\n"), program);
gettext("\t%s [opts] -H [set]\t\t\t"
"report link health\n"), program);
gettext("\t%s -I a <master> <shadow> <bitmap>\t"
"add ndr_ii config entry\n"), program);
gettext("\t%s -I d <master> <shadow> <bitmap>\t"
"delete ndr_ii config entry\n"), program);
gettext("\t%s [opts] -i [set]\t\t\t"
"print sets in config file format\n"), program);
gettext("\t%s [opts] -l [set]\t\t\t"
"enter logging mode\n"), program);
gettext("\t%s [opts] -m [set]\t\t\t"
"full sync\n"), program);
gettext("\t%s [opts] -m -r [set]\t\t"
"full reverse sync\n"), program);
gettext("\t%s [opts] -P [set]\t\t\t"
"print sets verbose\n"), program);
gettext("\t%s [opts] -p [set]\t\t\t"
"print sets\n"), program);
gettext("\t%s [opts] -R\t\t\t"
"reset error conditions\n"), program);
gettext("\t%s [opts] -R b p <bitmap> [set]\t"
"reconfig primary bitmap\n"), program);
gettext("\t%s [opts] -R b s <bitmap> [set]\t"
"reconfig secondary bitmap\n"), program);
if (clustered)
gettext("\t%s [opts] -R C <ctag> [set]\t"
"reconfig cluster tag\n"), program);
#ifdef _RDC_CAMPUS
gettext("\t%s [opts] -R d <pathname> [set]\t"
"reconfig campus direct file\n"), program);
#endif
gettext("\t%s [opts] -R -f <volset-file> \t"
"reconfig from file\n"), program);
gettext("\t%s [opts] -R g <group> [set]\t"
"reconfig group\n"), program);
gettext("\t%s [opts] -R m {sync|async} [set]\t"
"reconfig mode\n"), program);
if (allow_role)
gettext("\t%s [opts] -R r [set]\t\t"
"reverse roles\n"), program);
gettext("\t%s [opts] -u [set]\t\t\t"
"update sync\n"), program);
gettext("\t%s [opts] -u -r [set]\t\t"
"update reverse sync\n"), program);
gettext("\t%s [opts] -W <maxwrites> [set]\t"
"set maximum writes to queue\n"), program);
gettext("\t%s [opts] -w [set]\t\t\t"
"wait\n"), program);
q_usage(0);
"(not valid for print operations)\n"));
"\t-g <group>\toperate on sets in group only "
"(not valid for enable\n\t\t\toperations)\n"));
if (clustered)
gettext("\t-C <ctag>\tignore sets not in cluster ctag "
"(not valid for enable\n\t\t\toperations)\n"));
if (clustered)
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
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"));
gettext("\t<shost>:<sdev>\t\t"
"operate on set matching shost and sdev\n"));
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);
gettext("\t<no arg>\t\toperate on all configured sets\n"));
}
int
{
int c;
switch (flag) {
case RDC_CMD_DISABLE:
break;
case RDC_CMD_ENABLE:
break;
case RDC_CMD_HEALTH:
"[N]: "));
break;
case RDC_CMD_COPY:
if (options & RDC_OPT_FULL) {
if (options & RDC_OPT_REVERSE)
" secondary? (Y/N) [N]: "));
else
" primary? (Y/N) [N]: "));
} else {
if (options & RDC_OPT_REVERSE)
" secondary? (Y/N) [N]: "));
else
" primary? (Y/N) [N]: "));
}
break;
case RDC_CMD_RECONFIG:
"Perform Remote Mirror reconfiguration? (Y/N) [N]: "));
break;
case RDC_CMD_RESET:
"[N]: "));
break;
case RDC_CMD_TUNABLE:
"[N]: "));
break;
case RDC_CMD_LOG:
"Put Remote Mirror into logging mode? (Y/N) [N]: "));
break;
case RDC_CMD_WAIT:
"Wait for Remote Mirror sync completion? (Y/N) [N]: "));
break;
default:
"[N]: "));
}
c = getchar();
if ((c != 'y') && (c != 'Y')) {
(void) printf("\n");
return (-1);
}
return (0);
}
static void
{
int set;
char key[ CFG_MAX_KEY ];
char buf[ CFG_MAX_BUF ];
char lghn[ MAX_RDC_HOST_SIZE ];
if (volhash) {
return;
}
volhash = nsc_create_hash();
break;
}
continue;
/* use lghn if possible */
if (*lghn) {
}
} else if (!self_check(host1)) {
/* next one had better be ours */
if (!self_check(host2)) {
gettext("config error: neither %s nor %s"
continue;
}
}
/* primary vol may be used more than once */
if (volcount) {
} else {
}
/* bitmap ought to be only used once */
if (volcount) {
/* argh */
} else {
}
continue;
/* diskqueue vol may be used more than once */
if (volcount) {
} else {
}
}
}
static void
{
volhash = 0;
}
static int
{
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
{
if (fromhost[0])
if (fromfile[0])
if (frombitmap[0])
if (tobitmap[0])
if (type[0])
if (mode[0])
if (group[0])
if (ctag[0])
if (diskq[0])
}
/*
* 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
{
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
key);
}
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\"."),
}
}
/*
* load and send the contents of the bitmap file to the kernel.
*/
static int
{
int fd;
void *buffer;
int buffersz;
struct stat s;
int n;
int ret;
/*
* open bitmap file for reading.
*/
bitmap);
return (1);
}
bitmap);
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.
*/
return (1);
}
if (n != buffersz) {
"read returned %d instead of %d"),
n, buffersz);
return (1);
}
if (ret < 0) {
switch (errno) {
case EIO:
"enabled"));
break;
case ENXIO:
"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] == '-') {
}
for (i = 0; grp[i] != '\0'; i++) {
"space"));
}
}
}