/* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) (void) strcpy(sync, "sync"); else (void) 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; (void) strcpy(pair_list[i].ctag, ""); (void) strcpy(pair_list[i].group, ""); (void) 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); (void) 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); (void) 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); (void) 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) (void) strcpy(pair->diskqueue, ""); if (strcmp(pair->group, place_holder) == 0) (void) 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); } (void) 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); (void) strncpy(fromname, hp->h_name, MAXHOSTNAMELEN); hp = gethost_byname(tohost); (void) 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)) (void) 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') (void) 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') { (void) 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)) { (void) 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; (void) 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) { (void) 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; (void) strncpy(parms.rdc_set->primary.intf, phost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->primary.file, pvol, NSC_MAXPATH); (void) strncpy(parms.rdc_set->secondary.intf, shost, MAX_RDC_HOST_SIZE); (void) 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")); } (void) strcpy(ctag_arg, ""); (void) 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) { (void) 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; (void) 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; (void) 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) (void) 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) (void) 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) { (void) 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) (void) strncpy(tohost_arg, ptr, MAX_RDC_HOST_SIZE); else { rdc_err(NULL, gettext("Bad host specified")); } ptr = strtok(NULL, ":"); if (ptr) (void) 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) (void) 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; (void) strncpy(fromhost, argv[optind], MAX_RDC_HOST_SIZE); (void) strncpy(fromfile, argv[optind+1], NSC_MAXPATH); (void) strncpy(frombitmap, argv[optind+2], NSC_MAXPATH); (void) strncpy(tohost, argv[optind+3], MAX_RDC_HOST_SIZE); (void) strncpy(tofile, argv[optind+4], NSC_MAXPATH); (void) 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 */ (void) 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 (void) 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) { (void) 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); } (void) 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) { (void) 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) { (void) strncpy(frombitmap, reconfig_pbitmap, NSC_MAXPATH); check_rdcbitmap(flag, fromhost, frombitmap); } if (reconfig_sbitmap) { (void) strncpy(tobitmap, reconfig_sbitmap, NSC_MAXPATH); check_rdcbitmap(flag, tohost, tobitmap); } #ifdef _RDC_CAMPUS if (reconfig_direct) (void) strncpy(directfile, reconfig_direct, NSC_MAXPATH); #endif if (reconfig_group) (void) strncpy(group, reconfig_group, NSC_MAXPATH); if (strlen(reconfig_ctag) > 0) (void) 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) { (void) strncpy(fromfile, pair_list[pairs].ffile, NSC_MAXPATH); (void) strncpy(tofile, pair_list[pairs].tfile, NSC_MAXPATH); (void) strncpy(frombitmap, pair_list[pairs].fbitmap, NSC_MAXPATH); (void) strncpy(fromhost, pair_list[pairs].fhost, MAX_RDC_HOST_SIZE); (void) strncpy(tohost, pair_list[pairs].thost, MAX_RDC_HOST_SIZE); (void) strncpy(tobitmap, pair_list[pairs].tbitmap, NSC_MAXPATH); (void) strncpy(directfile, pair_list[pairs].directfile, NSC_MAXPATH); (void) strncpy(group, pair_list[pairs].group, NSC_MAXPATH); (void) strncpy(ctag, pair_list[pairs].ctag, MAX_RDC_HOST_SIZE); (void) 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; (void) strncpy(parms.rdc_set->primary.intf, fromhost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->secondary.intf, tohost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH); (void) 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")); } (void) strncpy(orig_fbmp, parms.rdc_set->primary.bitmap, NSC_MAXPATH); (void) 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) { (void) 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') (void) strncpy(ctag, buf, sizeof (ctag)); if (doasync) (void) strcpy(mode, "async"); else (void) strcpy(mode, "sync"); if (strcmp(directfile, "") == 0) (void) 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); (void) strncpy(tmphost, fromhost, MAX_RDC_HOST_SIZE); (void) strncpy(tmpfile, fromfile, NSC_MAXPATH); (void) strncpy(tmpbitmap, frombitmap, NSC_MAXPATH); (void) strncpy(fromhost, tohost, MAX_RDC_HOST_SIZE); (void) strncpy(fromfile, tofile, NSC_MAXPATH); (void) strncpy(frombitmap, tobitmap, NSC_MAXPATH); (void) strncpy(tohost, tmphost, MAX_RDC_HOST_SIZE); (void) strncpy(tofile, tmpfile, NSC_MAXPATH); (void) 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) { (void) 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) { (void) strcat(buf, q); (void) 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) { (void) 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) { (void) strcat(buf, q); (void) 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) { (void) 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; (void) strncpy(p->intf, fhost, MAX_RDC_HOST_SIZE); (void) strncpy(p->file, ffile, MAX_RDC_HOST_SIZE); p = &parms.rdc_set[0].secondary; (void) strncpy(p->intf, thost, NSC_MAXPATH); (void) 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); (void) strncpy(fromname, hp->h_name, MAXHOSTNAMELEN); hp = gethost_byname(tohost); (void) 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; (void) strncpy(parms.rdc_set->primary.intf, fromhost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->secondary.intf, tohost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH); (void) 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")); } (void) strncpy(orig_fbmp, parms.rdc_set->primary.bitmap, NSC_MAXPATH); (void) strncpy(orig_tbmp, parms.rdc_set->secondary.bitmap, NSC_MAXPATH); (void) 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); } } (void) strncpy(parms.rdc_set->primary.intf, fromhost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->primary.file, fromfile, NSC_MAXPATH); (void) strncpy(parms.rdc_set->primary.bitmap, frombitmap, NSC_MAXPATH); (void) strncpy(parms.rdc_set->secondary.intf, tohost, MAX_RDC_HOST_SIZE); (void) strncpy(parms.rdc_set->secondary.file, tofile, NSC_MAXPATH); (void) strncpy(parms.rdc_set->secondary.bitmap, tobitmap, NSC_MAXPATH); if ((group == NULL) || ((strcmp(group, "-")) == 0)) parms.rdc_set->group_name[0] = 0; else (void) 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 (void) 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 (void) 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) { (void) 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 { (void) 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) { (void) 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 { (void) 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) { (void) strncpy(diskqueue, parms.rdc_set->disk_queue, NSC_MAXPATH); } else { (void) 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; (void) 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; (void) 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; (void) 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; } (void) 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; } (void) 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; } (void) 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; } (void) 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; } (void) 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; } (void) strncpy(tobitmap, tmp, (NSC_MAXPATH - 1)); tobitmap[(NSC_MAXPATH - 1)] = '\0'; ret++; } if ((tmp = strtok(NULL, split_str)) != NULL) { (void) strncpy(dsk_flagstr, tmp, 15); dsk_flagstr[15] = '\0'; ret++; } if ((tmp = strtok(NULL, split_str)) != NULL) { (void) strncpy(sync, tmp, 15); sync[15] = '\0'; ret++; } for (j = 0; j < EXTRA_ARGS; j++) { if ((tmp = strtok(NULL, split_str)) != NULL) { (void) 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 */ (void) 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 (void) 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")); } (void) strncpy(pair_list[i].fhost, fromhost, MAX_RDC_HOST_SIZE); (void) strncpy(pair_list[i].ffile, fromfile, NSC_MAXPATH); (void) strncpy(pair_list[i].fbitmap, frombitmap, NSC_MAXPATH); (void) strncpy(pair_list[i].thost, tohost, MAX_RDC_HOST_SIZE); (void) strncpy(pair_list[i].tfile, tofile, NSC_MAXPATH); (void) strncpy(pair_list[i].tbitmap, tobitmap, NSC_MAXPATH); (void) 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) (void) strncpy(pairp->fbitmap, reconfig_pbitmap, NSC_MAXPATH); if (reconfig_sbitmap) (void) strncpy(pairp->tbitmap, reconfig_sbitmap, NSC_MAXPATH); #ifdef _RDC_CAMPUS if (reconfig_direct) (void) strncpy(directfile, reconfig_direct, NSC_MAXPATH); #endif if (reconfig_group) (void) strncpy(pairp->group, reconfig_group, NSC_MAXPATH); if (strlen(reconfig_ctag) > 0) (void) 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 -q a \t\tadd disk queue to " "group\n"), program); (void) fprintf(stderr, gettext("\t%s -g -q d \t\tremove disk queue from" " group\n"), program); (void) fprintf(stderr, gettext("\t%s -g -q r \treplace disk queue for a" " group\n"), program); (void) fprintf(stderr, gettext("\t%s -q a :\tadd disk queue to " "a set\n"), program); (void) fprintf(stderr, gettext("\t%s -q d :\t\tremove disk queue from " "a set\n"), program); (void) fprintf(stderr, gettext("\t%s -q r :\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 [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 [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 \t" "add ndr_ii config entry\n"), program); (void) fprintf(stderr, gettext("\t%s -I d \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 [set]\t" "reconfig primary bitmap\n"), program); (void) fprintf(stderr, gettext("\t%s [opts] -R b s [set]\t" "reconfig secondary bitmap\n"), program); if (clustered) (void) fprintf(stderr, gettext("\t%s [opts] -R C [set]\t" "reconfig cluster tag\n"), program); #ifdef _RDC_CAMPUS (void) fprintf(stderr, gettext("\t%s [opts] -R d [set]\t" "reconfig campus direct file\n"), program); #endif (void) fprintf(stderr, gettext("\t%s [opts] -R -f \t" "reconfig from file\n"), program); (void) fprintf(stderr, gettext("\t%s [opts] -R g [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 [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 \toperate on sets in group only " "(not valid for enable\n\t\t\toperations)\n")); if (clustered) (void) fprintf(stderr, gettext("\t-C \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 " #ifdef _RDC_CAMPUS " {ip | } " #else " ip " #endif "\\\n\t\t{sync | async} [g ] [q ] " "[C ]\n")); else (void) fprintf(stderr, gettext("\t " #ifdef _RDC_CAMPUS " {ip | } " #else " ip " #endif "\\\n\t\t{sync | async} [g ] [q ]\n")); (void) fprintf(stderr, gettext("\t:\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\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); } (void) 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; (void) strncpy(bmop.sechost, tohost, MAX_RDC_HOST_SIZE); (void) 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")); } } }