/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #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 /* for bit map header format */ #include typedef struct mstcount_s { int count; } mstcount_t; typedef struct shdvol_s { char master[ DSW_NAMELEN ]; } shdvol_t; typedef struct grptag_s { char ctag[ DSW_NAMELEN ]; } grptag_t; hash_node_t **volhash = NULL; #define DSW_TEXT_DOMAIN "II" #include #define RDC_LIB "/usr/lib/librdc.so.1" static int (*self_check)(char *); /* * Support for the special cluster tag "local" to be used with -C in a * cluster for local volumes. */ #define II_LOCAL_TAG "local" #define II_NOT_CLUSTER 1 #define II_CLUSTER 2 #define II_CLUSTER_LCL 3 static char *cfg_cluster_tag = NULL; static CFGFILE *cfg = NULL; void sigterm(int sig); #define SD_BIT_CLR(bmap, bit) (bmap &= ~(1 << bit)) #define SD_BIT_ISSET(bmap, bit) ((bmap & (1 << bit)) != 0) #define MAX_LINE_SIZE 256 /* maximum characters per line in config file */ #define MAX_GROUPS 1024 /* maximum number of groups to support */ #define MAX_CLUSTERS 1024 /* maximum number of resource groups */ unsigned long bm_size; /* size in bytes of bitmap */ unsigned long bm_actual; /* original number of bits in bitmap */ int debug = 0; int dsw_fd; #define LD_II 0x00000001 #define LD_DSVOLS 0x00000002 #define LD_SVOLS 0x00000004 #define LD_SHADOWS 0x00000008 static int reload_vols = 0; static int config_locked = 0; static int last_lock; /* * names for do_copy() flags. */ enum copy_update {Copy = 0, Update}; enum copy_direction {ToShadow = 0, ToMaster}; enum copy_wait {WaitForStart = 0, WaitForEnd}; char *cmdnam; unsigned char *allocate_bitmap(char *); void usage(char *); void enable(char *, char *, char *, char *); int disable(char *); void bitmap_op(char *, int, int, int, int); void print_status(dsw_config_t *, int); int abort_copy(char *); int reset(char *); int overflow(char *); void iiversion(void); int wait_for_copy(char *); int export(char *); void list_volumes(void); void dsw_error(char *, spcs_s_info_t *); void InitEnv(); static void check_dg_is_local(char *dgname); static int check_resource_group(char *volume); static int check_diskgroup(char *path, char *result); static int check_cluster(); static void unload_ii_vols(); static void load_ii_vols(CFGFILE *); static int perform_autosv(); static int is_exported(char *); static void conform_name(char **); static void do_attach(dsw_config_t *); static int ii_lock(CFGFILE *, int); static void verify_groupname(char *grp, int testDash); void dsw_list_clusters(char *); void dsw_enable(int, char **); void dsw_disable(int, char **); void dsw_copy_to_shadow(int, char **); void dsw_update_shadow(int, char **); void dsw_copy_to_master(int, char **); void dsw_update_master(int, char **); void dsw_abort_copy(int, char **); void dsw_display_status(int, char **); void dsw_display_bitmap(int, char **); void dsw_reset(int, char **); void dsw_overflow(int, char **); void dsw_version(int, char **); void dsw_wait(int, char **); void dsw_list_volumes(int, char **); void dsw_list_group_volumes(); void dsw_export(int, char **); void dsw_import(int, char **); void dsw_join(int, char **); void dsw_attach(int, char **); void dsw_detach(int, char **); void dsw_params(int, char **); void dsw_olist(int, char **); void dsw_ostat(int, char **); void dsw_move_2_group(int, char **); void dsw_list_groups(); void check_iishadow(char *); extern char *optarg; extern int optind, opterr, optopt; int Aflg; int Cflg; int CLflg; int Dflg; int Eflg; int Iflg; int Jflg; int Lflg; int Oflg; int Pflg; int Qflg; int Rflg; int aflg; int bflg; int cflg; int dflg; int eflg; int fflg; int gflg; int gLflg; int hflg; int iflg; int lflg; int mflg; int nflg; int pflg; int uflg; int vflg; int wflg; int errflg; #ifdef DEBUG const char single_opts[] = "a:b:c:d:e:f:g:hilmnpu:vw:A:C:D:E:I:J:LO:PQ:R:"; #else /* no b or f flags */ const char single_opts[] = "a:c:d:e:g:hilmnpu:vw:A:C:D:E:I:J:LO:PQ:R:"; #endif const char group_opts[] = "ac:de:ilmnpu:wA:C:DELPR"; const char *opt_list = single_opts; char buf[CFG_MAX_BUF]; char key[CFG_MAX_KEY]; char last_overflow[DSW_NAMELEN]; int setnumber; char *group_name; char **group_volumes; enum copy_direction direction; char *param_delay, *param_unit; char *overflow_file; #ifdef lint int iiadm_lintmain(int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif { int c; int actions = 0; int ac; char *av[1024]; InitEnv(); memset(av, 0, sizeof (av)); cmdnam = argv[0]; while ((c = getopt(argc, argv, opt_list)) != EOF) switch (c) { case 'c': cflg++; actions++; if (strcmp(optarg, "m") == 0) { av[0] = "copy_to_master"; direction = ToMaster; } else if (strcmp(optarg, "s") == 0) { av[0] = "copy_to_shadow"; direction = ToShadow; } else { errflg ++; usage(gettext( "must specify m or s with -c")); } ac = 2; break; case 'd': dflg++; actions++; av[0] = "disable"; av[1] = optarg; ac = 2; break; case 'e': eflg++; actions++; av[0] = "enable"; if (strcmp(optarg, "ind") == 0) av[4] = "independent"; else if (strcmp(optarg, "dep") == 0) av[4] = "dependent"; else { errflg ++; usage(gettext( "must specify ind or dep with -e")); } ac = 1; break; case 'g': gflg++; opt_list = group_opts; group_name = optarg; if (group_name && *group_name == '-') { gLflg = (strcmp("-L", group_name) == 0); if (gLflg) actions++; } verify_groupname(group_name, !gLflg); break; case 'h': hflg++; actions++; break; case 'u': uflg++; actions++; if (strcmp(optarg, "m") == 0) { av[0] = "update_master"; direction = ToMaster; } else if (strcmp(optarg, "s") == 0) { av[0] = "update_shadow"; direction = ToShadow; } else { errflg ++; usage(gettext( "must specify m or s with -u")); } ac = 2; break; case 'i': iflg++; actions++; av[0] = "display_status"; break; case 'l': lflg++; actions++; av[0] = "list_config"; ac = 1; break; case 'm': mflg++; actions++; av[0] = "move_to_group"; ac = 1; break; case 'n': nflg++; break; case 'p': pflg++; break; case 'b': bflg++; actions++; av[0] = "display_bitmap"; av[1] = optarg; ac = 2; break; case 'a': aflg++; actions++; av[0] = "abort_copy"; av[1] = optarg; ac = 2; break; case 'v': vflg++; actions++; av[1] = "version"; ac = 1; break; case 'w': wflg++; actions++; av[0] = "wait"; av[1] = optarg; ac = 2; break; case 'A': Aflg++; actions++; av[0] = "attach"; av[1] = optarg; ac = 2; break; case 'C': Cflg++; cfg_cluster_tag = optarg; if (cfg_cluster_tag && *cfg_cluster_tag == '-') { CLflg = (strcmp("-L", cfg_cluster_tag) == 0); if (CLflg) actions++; } break; case 'D': Dflg++; actions++; av[0] = "detach"; av[1] = optarg; ac = 2; break; case 'O': Oflg++; actions++; av[0] = "overflow"; av[1] = optarg; ac = 2; break; case 'R': Rflg++; actions++; av[0] = "reset"; av[1] = optarg; ac = 2; break; case 'E': Eflg++; actions++; av[0] = "export"; av[1] = optarg; ac = 2; break; case 'I': Iflg++; actions++; av[0] = "import"; av[1] = optarg; ac = 2; break; case 'J': Jflg++; actions++; av[0] = "join"; av[1] = optarg; ac = 2; break; case 'P': Pflg++; actions++; av[0] = "parameter"; ac = 1; break; case 'L': Lflg++; actions++; /* If -g group -L, force error */ if (group_name) actions++; av[0] = "LIST"; ac = 1; break; case 'Q': Qflg++; actions++; av[0] = "query"; av[1] = optarg; ac = 2; break; case '?': errflg++; break; } if (hflg) { usage(NULL); exit(0); } if (errflg) usage(gettext("unrecognized argument")); switch (actions) { case 0: if (argc > 1) usage(gettext("must specify an action flag")); /* default behavior is to list configuration */ lflg++; av[0] = "list_config"; ac = 1; break; case 1: break; default: usage(gettext("too many action flags")); break; } if (gflg && (Iflg || Jflg || Oflg || Qflg)) usage(gettext("can't use a group with this option")); if (!gflg && (mflg)) usage(gettext("must use a group with this option")); /* * Open configuration file. */ if ((cfg = cfg_open(NULL)) == NULL) { perror("unable to access configuration"); exit(2); } /* * Set write locking (CFG_WRLOCK) for: * iiadm -e (enable) * iiadm -d (disable) * iiadm -A (attach overflow) * iiadm -D (detach overflow) * iiadm -g grp -m volume (move volume into group) * iiadm -E (export shadow [needs to update dsvol section]) * iiadm -I (import shadow [ditto]) * iiadm -J (join shadow [ditto]) * read locking (CFG_RDLOCK) for all other commands */ last_lock = (eflg || dflg || mflg || Aflg || Dflg || Eflg || Iflg || Jflg)? CFG_WRLOCK : CFG_RDLOCK; if (!cfg_lock(cfg, last_lock)) { perror("unable to lock configuration"); exit(2); } config_locked = 1; /* * If we are in a cluster, set or derive a valid disk group */ switch (check_cluster()) { case II_CLUSTER: /* * If in a Sun Cluster, can't Import an II shadow * Must be done as -C local */ if (Iflg) dsw_error(gettext( "-I (import) only allowed as -C local"), NULL); /*FALLTHRU*/ case II_CLUSTER_LCL: /* * If a cluster tag was specified or derived, set it */ if (CLflg) { dsw_list_clusters(argv[optind]); cfg_close(cfg); exit(0); } else { cfg_resource(cfg, cfg_cluster_tag); } break; case II_NOT_CLUSTER: if (cfg_cluster_tag != NULL) dsw_error(gettext( "-C is valid only in a Sun Cluster"), NULL); break; default: dsw_error(gettext( "Unexpected return from check_cluster()"), NULL); } /* preload the ii config */ load_ii_vols(cfg); reload_vols |= LD_II; if (eflg) { if (argc - optind != 3) usage(gettext("must specify 3 volumes with -e")); av[1] = argv[optind++]; av[2] = argv[optind++]; av[3] = argv[optind++]; ac = 5; dsw_enable(ac, av); } else if (dflg) { dsw_disable(ac, av); } else if (uflg) { if (argv[optind] == NULL && group_name == NULL) usage(gettext("must specify volume with -u")); for (c = 1; argv[optind] != NULL; optind++) av[c++] = argv[optind]; av[c] = NULL; if (direction == ToMaster) dsw_update_master(ac, av); else dsw_update_shadow(ac, av); } else if (iflg) { if (argv[optind]) { av[1] = argv[optind]; ac = 2; } else ac = 1; dsw_display_status(ac, av); } else if (bflg) { dsw_display_bitmap(ac, av); } else if (cflg) { if (argv[optind] == NULL && group_name == NULL) usage(gettext("must specify volume with -c")); for (c = 1; argv[optind] != NULL; optind++) av[c++] = argv[optind]; av[c] = NULL; if (direction == ToMaster) dsw_copy_to_master(ac, av); else dsw_copy_to_shadow(ac, av); } else if (aflg) { dsw_abort_copy(ac, av); } else if (Eflg) { dsw_export(ac, av); } else if (Iflg) { if (argc - optind != 1) usage(gettext("must specify 2 volumes with -I")); av[2] = argv[optind++]; ac = 3; dsw_import(ac, av); } else if (Aflg) { if (group_name) { if (argc - optind != 0) usage(gettext("must specify overflow volume " \ "when using groups with -A")); ac = 2; } else { if (argc - optind != 1) usage(gettext("specify 2 volumes with -A")); ac = 3; av[2] = argv[optind++]; } dsw_attach(ac, av); } else if (Dflg) { dsw_detach(ac, av); } else if (Jflg) { if (argc - optind != 1) usage(gettext("must specify 2 volumes with -J")); av[2] = argv[optind++]; ac = 3; dsw_join(ac, av); } else if (Pflg) { if (argc - optind == ((group_name) ? 0 : 1)) { av[1] = argv[optind++]; ac = (group_name) ? 0 : 2; } else if (argc - optind == ((group_name) ? 2 : 3)) { av[1] = argv[optind++]; av[2] = argv[optind++]; av[3] = argv[optind++]; ac = (group_name) ? 2 : 4; } else usage(gettext( "must specify delay, unit and shadow with -P")); dsw_params(ac, av); } else if (Oflg) { dsw_overflow(ac, av); } else if (Rflg) { dsw_reset(ac, av); } else if (vflg) { dsw_version(ac, av); } else if (wflg) { dsw_wait(ac, av); } else if (lflg) { if ((gflg) && (!group_name)) dsw_list_group_volumes(); else dsw_list_volumes(ac, av); } else if (Lflg) { dsw_olist(ac, av); } else if (gLflg) { dsw_list_groups(); } else if (Qflg) { dsw_ostat(ac, av); } else if (mflg) { if (argc - optind < 1) usage(gettext("must specify one or more volumes")); for (c = 1; argv[optind] != NULL; optind++) av[c++] = argv[optind]; av[c] = NULL; dsw_move_2_group(ac, av); } if (cfg) cfg_close(cfg); exit(0); return (0); } static int ii_lock(CFGFILE *cfg, int locktype) { last_lock = locktype; return (cfg_lock(cfg, locktype)); } static int do_ioctl(int fd, int cmd, void *arg) { int unlocked = 0; int rc; int save_errno; if (config_locked) { switch (cmd) { case DSWIOC_ENABLE: case DSWIOC_RESUME: case DSWIOC_SUSPEND: case DSWIOC_COPY: case DSWIOC_BITMAP: case DSWIOC_DISABLE: case DSWIOC_SHUTDOWN: case DSWIOC_ABORT: case DSWIOC_RESET: case DSWIOC_OFFLINE: case DSWIOC_WAIT: case DSWIOC_ACOPY: case DSWIOC_EXPORT: case DSWIOC_IMPORT: case DSWIOC_JOIN: case DSWIOC_COPYP: case DSWIOC_OATTACH: case DSWIOC_ODETACH: case DSWIOC_SBITSSET: case DSWIOC_CBITSSET: case DSWIOC_SEGMENT: case DSWIOC_MOVEGRP: case DSWIOC_CHANGETAG: cfg_unlock(cfg); unlocked = 1; break; case DSWIOC_STAT: case DSWIOC_VERSION: case DSWIOC_LIST: case DSWIOC_OCREAT: case DSWIOC_OLIST: case DSWIOC_OSTAT: case DSWIOC_OSTAT2: case DSWIOC_LISTLEN: case DSWIOC_OLISTLEN: case DSWIOC_CLIST: case DSWIOC_GLIST: break; default: fprintf(stderr, "cfg locking needs to be set for %08x\n", cmd); exit(1); break; } } if (unlocked) { /* unload vol hashes */ if (reload_vols & LD_II) unload_ii_vols(); if (reload_vols & LD_SHADOWS) cfg_unload_shadows(); if (reload_vols & LD_DSVOLS) cfg_unload_dsvols(); if (reload_vols & LD_SVOLS) cfg_unload_svols(); } rc = ioctl(fd, cmd, arg); save_errno = errno; if (config_locked && unlocked) { cfg_lock(cfg, last_lock); } if (unlocked) { /* reload vol hashes */ if (reload_vols & LD_SVOLS) cfg_load_svols(cfg); if (reload_vols & LD_DSVOLS) cfg_load_dsvols(cfg); if (reload_vols & LD_SHADOWS) cfg_load_shadows(cfg); if (reload_vols & LD_II) load_ii_vols(cfg); } errno = save_errno; return (rc); } static int get_dsw_config(int setno, dsw_config_t *parms) { char buf[CFG_MAX_BUF]; char key[CFG_MAX_KEY]; bzero(parms, sizeof (dsw_config_t)); (void) snprintf(key, sizeof (key), "ii.set%d.master", setno); if (cfg_get_cstring(cfg, key, parms->master_vol, DSW_NAMELEN) < 0) return (-1); (void) snprintf(key, sizeof (key), "ii.set%d.shadow", setno); (void) cfg_get_cstring(cfg, key, parms->shadow_vol, DSW_NAMELEN); (void) snprintf(key, sizeof (key), "ii.set%d.bitmap", setno); (void) cfg_get_cstring(cfg, key, parms->bitmap_vol, DSW_NAMELEN); (void) snprintf(key, sizeof (key), "ii.set%d.mode", setno); (void) cfg_get_cstring(cfg, key, buf, sizeof (buf)); if (strcmp(buf, "I") == 0) parms->flag |= DSW_GOLDEN; (void) snprintf(key, sizeof (key), "ii.set%d.overflow", setno); (void) cfg_get_cstring(cfg, key, last_overflow, DSW_NAMELEN); (void) snprintf(key, sizeof (key), "ii.set%d.group", setno); (void) cfg_get_cstring(cfg, key, parms->group_name, DSW_NAMELEN); (void) snprintf(key, sizeof (key), "ii.set%d.cnode", setno); (void) cfg_get_cstring(cfg, key, parms->cluster_tag, DSW_NAMELEN); return (0); } static int find_next_cf_line(char *volume, int next) { dsw_config_t cf_line; for (setnumber = next; get_dsw_config(setnumber, &cf_line) == 0; setnumber++) { if (strncmp(volume, cf_line.master_vol, DSW_NAMELEN) == 0 || strncmp(volume, cf_line.shadow_vol, DSW_NAMELEN) == 0 || strncmp(volume, cf_line.bitmap_vol, DSW_NAMELEN) == 0) return (1); } return (0); } int find_any_cf_line(char *volume) { return (find_next_cf_line(volume, 1)); } static int find_next_shadow_line(char *volume, int next) { dsw_config_t cf_line; for (setnumber = next; get_dsw_config(setnumber, &cf_line) == 0; setnumber++) { if (strncmp(volume, cf_line.shadow_vol, DSW_NAMELEN) == 0) return (1); } return (0); } int find_shadow_line(char *volume) { return (find_next_shadow_line(volume, 1)); } /* * this function is designed to be called once, subsequent calls won't * free memory allocated by earlier invocations. */ char * get_overflow_list() { dsw_aioctl_t *acopy_args; int rc, num; num = do_ioctl(dsw_fd, DSWIOC_OLISTLEN, NULL); if (num < 0) dsw_error(gettext("Can't get overflow list length"), NULL); acopy_args = malloc(sizeof (dsw_aioctl_t) + num * DSW_NAMELEN); if (acopy_args == NULL) dsw_error(gettext("Can't get memory for list enquiry"), NULL); acopy_args->count = num; acopy_args->flags = 0; acopy_args->status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_OLIST, acopy_args); if (rc == -1) dsw_error(gettext("Overflow list access failure"), &acopy_args->status); else acopy_args->shadow_vol[DSW_NAMELEN*acopy_args->count] = NULL; return (acopy_args->shadow_vol); } /* * this function is designed to be called once, subsequent calls won't * free memory allocated by earlier invocations. */ int find_group_members(char *group) { int nmembers = 0; int vector_len = 0; group_volumes = NULL; for (setnumber = 1; /*CSTYLED*/; setnumber++) { (void) snprintf(key, sizeof (key), "ii.set%d.group", setnumber); if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) break; if (strcmp(group, buf)) continue; (void) snprintf(key, sizeof (key), "ii.set%d.shadow", setnumber); if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) break; if (nmembers >= vector_len) { vector_len += 10; group_volumes = realloc(group_volumes, (1+vector_len) * sizeof (char *)); } group_volumes[nmembers] = strdup(buf); nmembers++; } if (group_volumes) group_volumes[nmembers] = NULL; /* terminate list */ return (nmembers); } static int find_next_matching_cf_line( char *volume, dsw_config_t *conf, dsw_ioctl_t *io, int next) { dsw_config_t config; if (!find_next_cf_line(volume, next)) { return (0); } if (conf == NULL) conf = &config; (void) get_dsw_config(setnumber, conf); if (io) { strncpy(io->shadow_vol, conf->shadow_vol, DSW_NAMELEN); io->shadow_vol[DSW_NAMELEN] = '\0'; } return (1); } int find_matching_cf_line(char *volume, dsw_config_t *conf, dsw_ioctl_t *io) { return (find_next_matching_cf_line(volume, conf, io, 1)); } int find_shadow_config(char *volume, dsw_config_t *conf, dsw_ioctl_t *io) { dsw_config_t *c, cf; if (io) { bzero(io, sizeof (dsw_ioctl_t)); } c = conf ? conf : &cf; setnumber = 1; /* perform action for each line of the stored config file */ for ((void) snprintf(key, sizeof (key), "ii.set%d.shadow", setnumber); cfg_get_cstring(cfg, key, c->shadow_vol, DSW_NAMELEN) >= 0; (void) snprintf(key, sizeof (key), "ii.set%d.shadow", ++setnumber)) { if (strncmp(volume, c->shadow_vol, DSW_NAMELEN) == 0) { (void) get_dsw_config(setnumber, c); if (check_resource_group(c->bitmap_vol)) { setnumber = 0; continue; } switch (check_cluster()) { case II_CLUSTER: if ((cfg_cluster_tag) && (strcmp(cfg_cluster_tag, c->cluster_tag))) continue; break; case II_CLUSTER_LCL: if (strlen(c->cluster_tag)) continue; break; } if (io) { strncpy(io->shadow_vol, c->shadow_vol, DSW_NAMELEN); io->shadow_vol[DSW_NAMELEN] = '\0'; } return (1); } } return (0); } void add_cfg_entry(dsw_config_t *parms) { /* insert the well-known fields first */ (void) snprintf(buf, sizeof (buf), "%s %s %s %s", parms->master_vol, parms->shadow_vol, parms->bitmap_vol, (parms->flag & DSW_GOLDEN) ? "I" : "D"); if (cfg_put_cstring(cfg, "ii", buf, strlen(buf)) >= 0) { /* if we have a group name, add it */ if (group_name) { if (find_any_cf_line(parms->shadow_vol)) { (void) sprintf(buf, "ii.set%d.group", setnumber); if (cfg_put_cstring(cfg, buf, group_name, strlen(group_name)) < 0) perror("cfg_put_cstring"); } else perror("cfg_location"); } /* commit the record */ (void) cfg_commit(cfg); } else perror("cfg_put_string"); } void remove_iiset(int setno, char *shadow, int shd_exp) { mstcount_t *mdata; shdvol_t *sdata; char sn[CFG_MAX_BUF]; if (perform_autosv()) { if (volhash) { unload_ii_vols(); } load_ii_vols(cfg); if (cfg_load_dsvols(cfg) < 0 || cfg_load_svols(cfg) < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } sdata = (shdvol_t *)nsc_lookup(volhash, shadow); if (sdata) { /* * Handle the normal cases of disabling a set that is * not an imported shadow volume */ if (strcmp(sdata->master, II_IMPORTED_SHADOW)) { /* * Handle multiple-shadows of single master */ mdata = (mstcount_t *) nsc_lookup(volhash, sdata->master); if ((mdata) && (mdata->count == 1)) { if (cfg_vol_disable(cfg, sdata->master, cfg_cluster_tag, "ii") < 0) dsw_error(gettext( "SV disable of master failed"), NULL); } } /* * Handle disk group name of different shadow */ if (shd_exp) { /* * If shadow is exported, then do nothing */ /*EMPTY*/; } else if (cfg_cluster_tag && strcmp(cfg_cluster_tag, "") && cfg_dgname(shadow, sn, sizeof (sn)) && strlen(sn) && strcmp(sn, cfg_cluster_tag)) { /* reload disk group volumes */ cfg_resource(cfg, sn); cfg_unload_dsvols(); cfg_unload_svols(); (void) cfg_load_dsvols(cfg); (void) cfg_load_svols(cfg); if (cfg_vol_disable(cfg, shadow, sn, "ii") < 0) dsw_error(gettext( "SV disable of shadow failed"), NULL); cfg_resource(cfg, cfg_cluster_tag); } else { if (cfg_vol_disable(cfg, shadow, cfg_cluster_tag, "ii") < 0) dsw_error(gettext( "SV disable of shadow failed"), NULL); } } cfg_unload_svols(); cfg_unload_dsvols(); unload_ii_vols(); reload_vols &= ~(LD_SVOLS | LD_DSVOLS | LD_II); } (void) sprintf(key, "ii.set%d", setno); if (cfg_put_cstring(cfg, key, NULL, 0) < 0) { perror("cfg_put_cstring"); } (void) cfg_commit(cfg); } /* * determine if we are running in a Sun Cluster Environment */ int check_cluster() { static int is_cluster = -1; int rc; #ifdef DEBUG char *cdebug = getenv("II_SET_CLUSTER"); #endif /* * If this routine was previously called, just return results */ if (is_cluster != -1) return (is_cluster); /* * See if Sun Cluster was installed on this node */ #ifdef DEBUG if (cdebug) rc = atoi(cdebug); else #endif rc = cfg_iscluster(); if (rc > 0) { /* * Determine if user specified -C local */ if ((cfg_cluster_tag == NULL) || (strcmp(cfg_cluster_tag, II_LOCAL_TAG))) { /* * We're in a Sun Cluster, and no "-C local" */ is_cluster = II_CLUSTER; } else { /* * We're in a Sun Cluster, but "-C local" was specified */ is_cluster = II_CLUSTER_LCL; cfg_cluster_tag = ""; } return (is_cluster); } else if (rc == 0) { /* * Not in a Sun Cluster */ is_cluster = II_NOT_CLUSTER; return (is_cluster); } else { dsw_error(gettext("unable to ascertain environment"), NULL); /*NOTREACHED*/ } /* gcc */ return (is_cluster); } /* * Determine if we need to set a cfg_resource based on this volume */ static int check_resource_group(char *volume) { char diskgroup[CFG_MAX_BUF]; /* * If we are in a cluster, attempt to derive a new resource group */ #ifdef DEBUG if (getenv("II_SET_CLUSTER") || (check_cluster() == II_CLUSTER)) { #else if (check_cluster() == II_CLUSTER) { #endif if (check_diskgroup(volume, diskgroup)) { if (cfg_cluster_tag == NULL) { cfg_cluster_tag = strdup(diskgroup); if (cfg_cluster_tag == NULL) dsw_error(gettext( "Memory allocation failure"), NULL); cfg_resource(cfg, cfg_cluster_tag); return (1); } else { /* * Check dgname and cluster tag from -C are the same. */ if (strcmp(diskgroup, cfg_cluster_tag) != 0) { char error_buffer[128]; (void) snprintf(error_buffer, sizeof (error_buffer), gettext( "-C (%s) does not match disk group " "name (%s) for %s"), cfg_cluster_tag, diskgroup, volume); spcs_log("ii", NULL, error_buffer); dsw_error(error_buffer, NULL); } } } else if (cfg_cluster_tag == NULL) dsw_error(gettext( "Point-in-Time Copy volumes, that are not " "in a device group which has been " "registered with SunCluster, " "require usage of \"-C\""), NULL); } return (0); } static void check_dg_is_local(char *dgname) { char error_buffer[128]; char *othernode; int rc; /* * check where this disk service is mastered */ rc = cfg_dgname_islocal(dgname, &othernode); if (rc < 0) { (void) snprintf(error_buffer, sizeof (error_buffer), gettext("Unable to find disk service:%s"), dgname); dsw_error(error_buffer, NULL); } else if (rc == 0) { (void) snprintf(error_buffer, sizeof (error_buffer), gettext("disk service, %s, is active on node \"%s\"\n" "Please re-issue the command on that node"), dgname, othernode); dsw_error(error_buffer, NULL); } } /* * Carry out cluster based checks for a specified volume, or just * global options. */ static int check_diskgroup(char *path, char *result) { char dgname[CFG_MAX_BUF]; char error_buffer[128]; #ifdef DEBUG char *override = getenv("II_CLUSTER_TAG"); if (override) { strcpy(result, override); return (1); } #endif /* * Check on path name, a returned NULL dgname is valid */ if (cfg_dgname(path, dgname, sizeof (dgname)) == NULL) { (void) snprintf(error_buffer, sizeof (error_buffer), gettext( "unable to determine disk group name for %s"), path); dsw_error(error_buffer, NULL); } if (strcmp(dgname, "") == 0) return (0); /* * See if disk group is local to this node */ check_dg_is_local(dgname); /* * Copy dgname into result */ strcpy(result, dgname); return (1); } /* * sigterm (): traps specified signal , usually termination one */ void sigterm(int sig) { spcs_log("ii", NULL, gettext("%s received signal %d"), cmdnam, sig); exit(1); } /* * sigchild; reap child and collect status. */ volatile pid_t dead_child; int dead_stat; /*ARGSUSED*/ void sigchild(int sig) { dead_child = wait(&dead_stat); } /* * InitEnv(): initializes environment */ void InitEnv() { (void) setlocale(LC_ALL, ""); (void) textdomain(DSW_TEXT_DOMAIN); #ifndef DEBUG sigset(SIGHUP, sigterm); sigset(SIGINT, sigterm); sigset(SIGQUIT, sigterm); sigset(SIGILL, sigterm); sigset(SIGEMT, sigterm); sigset(SIGABRT, sigterm); sigset(SIGFPE, sigterm); sigset(SIGBUS, sigterm); sigset(SIGSEGV, sigterm); sigset(SIGTERM, sigterm); sigset(SIGPWR, sigterm); sigset(SIGSTOP, sigterm); sigset(SIGTSTP, sigterm); #endif dsw_fd = open(DSWDEV, O_RDONLY); if (dsw_fd < 0) { perror(DSWDEV); exit(1); } setsid(); } /* * print an error message, followed by decoded errno then exit. */ void dsw_error(char *str, spcs_s_info_t *status) { char *sp; (void) fprintf(stderr, "%s: %s:\n", cmdnam, str); if (status == NULL) { /* deflect ESRCH */ if (ESRCH == errno) { sp = "Set/volume not found"; } else { sp = strerror(errno); } (void) fprintf(stderr, "%s\n", sp ? sp : ""); } else { spcs_s_report(*status, stderr); spcs_s_ufree(status); } if (cfg) cfg_close(cfg); exit(2); } #undef size void free_bitmap(unsigned char *bitmap) { free(bitmap); } int get_bitmap(master_volume, shd_bitmap, copy_bitmap, size) char *master_volume; unsigned char *shd_bitmap; unsigned char *copy_bitmap; unsigned long size; { dsw_bitmap_t parms; strncpy(parms.shadow_vol, master_volume, DSW_NAMELEN); parms.shadow_vol[DSW_NAMELEN-1] = '\0'; parms.shd_bitmap = shd_bitmap; parms.shd_size = size; parms.copy_bitmap = copy_bitmap; parms.copy_size = size; return (do_ioctl(dsw_fd, DSWIOC_BITMAP, &parms)); } unsigned char * allocate_bitmap(char *shadow_volume) { unsigned char *shd_bitmap; unsigned char *copy_bitmap; unsigned char *p; unsigned char *q; int i; dsw_stat_t args; int stat_flags; strncpy(args.shadow_vol, shadow_volume, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN-1] = '\0'; args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_STAT, &args) == -1) dsw_error(gettext("Stat failed"), &args.status); stat_flags = args.stat; if (stat_flags & DSW_BMPOFFLINE) return (NULL); bm_size = args.size; bm_size = (bm_size + DSW_SIZE-1) / DSW_SIZE; bm_actual = bm_size; bm_size = (bm_size + DSW_BITS-1) / DSW_BITS; spcs_s_ufree(&args.status); p = shd_bitmap = (unsigned char *) malloc(bm_size); if (!shd_bitmap) { perror(gettext("malloc bitmap")); return (NULL); } q = copy_bitmap = (unsigned char *) malloc(bm_size); if (!copy_bitmap) { perror(gettext("malloc bitmap")); free(shd_bitmap); return (NULL); } memset(shd_bitmap, 0, bm_size); memset(copy_bitmap, 0, bm_size); if (get_bitmap(shadow_volume, shd_bitmap, copy_bitmap, bm_size) < 0) { free(copy_bitmap); free(shd_bitmap); return (NULL); } /* * "or" the copy and shadow bitmaps together to return a composite * bitmap that contains the total set of differences between the * volumes. */ for (i = bm_size; i-- > 0; /*CSTYLED*/) *p++ |= *q++; free(copy_bitmap); return (shd_bitmap); } /* * print usage message and exit. */ void usage(char *why) { if (why) { (void) fprintf(stderr, "%s: %s\n", cmdnam, why); (void) fprintf(stderr, "%s\n", gettext("\nBrief summary:")); (void) fprintf(stderr, "%s\n", gettext("\t-e {ind|dep} master_vol shadow_vol " "bitmap_vol")); (void) fprintf(stderr, "%s\n", gettext("\t-[cu {s|m}] volume_set")); (void) fprintf(stderr, "%s\n", gettext("\t-i all")); (void) fprintf(stderr, "%s\n", gettext("\t-[adDEilPRw] volume_set")); (void) fprintf(stderr, "%s\n", gettext("\t-g group_name [options]")); (void) fprintf(stderr, "%s\n", gettext("\t-C cluster_tag [options]")); (void) fprintf(stderr, "%s\n", gettext("\t-[hilLv]")); (void) fprintf(stderr, "%s\n", gettext("\t-[IJ] volume_set bitmap")); (void) fprintf(stderr, "%s\n", gettext("\t-A overflow_vol volume_set")); (void) fprintf(stderr, "%s\n", gettext("\t-[OQ] overflow_vol")); (void) fprintf(stderr, "%s\n", gettext("\t-P {delay} {units} volume_set")); /* assume we came here due to a user mistake */ exit(1); /* NOTREACHED */ } else { (void) fprintf(stdout, "%s\n", gettext("Point-in-Time Copy Administrator CLI options")); (void) fprintf(stdout, "%s\n", gettext("Usage summary:")); (void) fprintf(stdout, "%s\n", gettext("\t-e ind m s b\tenable independent master shadow " "bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-e dep m s b\tenable dependent master shadow " "bitmap")); if (check_cluster() == II_CLUSTER) (void) fprintf(stdout, "%s\n", gettext("\t-ne ind m s b\tenable exportable master " "shadow bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-d v\t\tdisable volume")); (void) fprintf(stdout, "%s\n", gettext("\t-u s v\t\tupdate shadow volume")); (void) fprintf(stdout, "%s\n", gettext("\t-u m v\t\tupdate master volume")); (void) fprintf(stdout, "%s\n", gettext("\t-c s v\t\tcopy to shadow volume")); (void) fprintf(stdout, "%s\n", gettext("\t-c m v\t\tcopy to master volume")); (void) fprintf(stdout, "%s\n", gettext("\t-a v\t\tabort copy volume")); (void) fprintf(stdout, "%s\n", gettext("\t-w v\t\twait volume")); (void) fprintf(stdout, "%s\n", gettext("\t-i v\t\tdisplay volume status")); (void) fprintf(stdout, "%s\n", gettext("\t-i all\t\tdisplay all volume status")); (void) fprintf(stdout, "%s\n", gettext("\t-l\t\tlist all volumes")); (void) fprintf(stdout, "%s\n", gettext("\t-R v\t\treset volume")); (void) fprintf(stdout, "%s\n", gettext("\t-A o v\t\tattach overflow to volume")); (void) fprintf(stdout, "%s\n", gettext("\t-D v\t\tdetach overflow from volume")); (void) fprintf(stdout, "%s\n", gettext("\t-L\t\tlist all overflow volumes")); (void) fprintf(stdout, "%s\n", gettext("\t-O o\t\tinitialize overflow")); (void) fprintf(stdout, "%s\n", gettext("\t-Q o\t\tquery status of overflow")); (void) fprintf(stdout, "%s\n", gettext("\t-E v\t\texport shadow volume")); (void) fprintf(stdout, "%s\n", gettext("\t-I v b\t\timport volume bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-J v b\t\tjoin volume bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-P d u v\tset copy delay/units for volume")); (void) fprintf(stdout, "%s\n", gettext("\t-P v\t\tget copy delay/units for volume")); (void) fprintf(stdout, "%s\n", gettext("\t-C tag\t\tcluster resource tag")); #ifdef DEBUG (void) fprintf(stdout, "%s\n", gettext("\t-b v\t\tdisplay bitmap volume")); (void) fprintf(stdout, "%s\n", gettext("\t-f f\t\tuse private configuration file")); #endif (void) fprintf(stdout, "%s\n", gettext("\t-v\t\tprint software versions")); (void) fprintf(stdout, "%s\n", gettext("\t-n\t\tperform action without question")); (void) fprintf(stdout, "%s\n", gettext("\t-p [-c|-u] {m|s}" "enable PID locking on copy or update")); (void) fprintf(stdout, "%s\n", gettext("\t-p -w v\t\tdisable PID locking")); (void) fprintf(stdout, "%s\n", gettext("\t-h\t\tiiadm usage summary")); (void) fprintf(stdout, "%s\n", gettext("\nUsage summary for options that support " "grouping (-g g):")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -e ind m s b group enable independent " "master shadow bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -e dep m s b group enable dependent " "master shadow bitmap")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -d\t\tdisable group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -u s\tupdate shadow for all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -u m\tupdate master for all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -c s\tcopy to shadow for all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -c m\tcopy to master for all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -a\t\tabort copy for all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -w\t\twait for all volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -i\t\tdisplay status of all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -l\t\tlist all volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\t-g -L\t\tlist all groups")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -m v v\tmove one or more volumes into " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g \"\" -m v\tremove volume from group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -R\t\treset all volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -A o\tattach overflow to all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -D\t\tdetach overflow from all volumes in " "group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -E\t\texport shadow volume for all " "volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -P d u\tset copy delay/units for all " "volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\t-g g -P\t\tget copy delay/units for all " "volumes in group")); (void) fprintf(stdout, "%s\n", gettext("\nLegend summary:")); (void) fprintf(stdout, "%s\n", gettext("\tind\t\tindependent volume set")); (void) fprintf(stdout, "%s\n", gettext("\tdep\t\tdependent volume set")); (void) fprintf(stdout, "%s\n", gettext("\tall\t\tall configured volumes")); (void) fprintf(stdout, "%s\n", gettext("\tm\t\tmaster volume")); (void) fprintf(stdout, "%s\n", gettext("\ts\t\tshadow volume")); (void) fprintf(stdout, "%s\n", gettext("\tv\t\tshadow volume (reference name)")); (void) fprintf(stdout, "%s\n", gettext("\to\t\toverflow volume")); (void) fprintf(stdout, "%s\n", gettext("\tb\t\tbitmap volume")); #ifdef DEBUG (void) fprintf(stdout, "%s\n", gettext("\tf\t\tconfiguration file name")); #endif (void) fprintf(stdout, "%s\n", gettext("\td\t\tdelay tick interval")); (void) fprintf(stdout, "%s\n", gettext("\tu\t\tunit size")); (void) fprintf(stdout, "%s\n", gettext("\tg\t\tgroup name")); /* assume we came here because user request help text */ exit(0); /* NOTREACHED */ } } static char yeschr[MAX_LINE_SIZE + 2]; static char nochr[MAX_LINE_SIZE + 2]; static int yes(void) { int i, b; char ans[MAX_LINE_SIZE + 1]; for (i = 0; /*CSTYLED*/; i++) { b = getchar(); if (b == '\n' || b == '\0' || b == EOF) { if (i < MAX_LINE_SIZE) ans[i] = 0; break; } if (i < MAX_LINE_SIZE) ans[i] = b; } if (i >= MAX_LINE_SIZE) { i = MAX_LINE_SIZE; ans[MAX_LINE_SIZE] = 0; } if ((i == 0) || (strncmp(yeschr, ans, i))) { if (strncmp(nochr, ans, i) == 0) return (0); else if (strncmp(yeschr, ans, i) == 0) return (1); else { (void) fprintf(stderr, "%s %s/%s\n", gettext("You have to respond with"), yeschr, nochr); return (2); } } return (1); } static int convert_int(char *str) { int result, rc; char *buf; buf = (char *)calloc(strlen(str) + 256, sizeof (char)); rc = sscanf(str, "%d%s", &result, buf); if (rc != 1) { (void) sprintf(buf, gettext("'%s' is not a valid number"), str); /* dsw_error calls exit which frees 'buf' */ errno = EINVAL; dsw_error(buf, NULL); } free(buf); return (result); } void check_action(char *will_happen) { int answer; if (nflg || !isatty(fileno(stdin))) return; strncpy(yeschr, nl_langinfo(YESSTR), MAX_LINE_SIZE + 1); strncpy(nochr, nl_langinfo(NOSTR), MAX_LINE_SIZE + 1); /*CONSTCOND*/ while (1) { (void) printf("%s %s/%s ", will_happen, yeschr, nochr); answer = yes(); if (answer == 1 || answer == 0) break; } if (answer == 1) return; exit(1); } enum child_event {Status, CopyStart}; /* * Wait for child process to get to some state, where some state may be: * * Status Set up the shadow enough so that it responds * to status requests. * CopyStart Start copy/update operations. */ int child_wait(pid_t child, enum child_event event, char *volume) { dsw_stat_t args; int rc; strncpy(args.shadow_vol, volume, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN-1] = '\0'; for (; dead_child != child; sleep(1)) { /* poll shadow group with a status ioctl() */ args.status = spcs_s_ucreate(); errno = 0; rc = do_ioctl(dsw_fd, DSWIOC_STAT, &args); spcs_s_ufree(&args.status); if (event == Status) { /* keep polling while we fail with DSW_ENOTFOUND */ if (rc != -1 || errno != DSW_ENOTFOUND) return (0); } else { /* event == CopyStart */ if (rc == -1) { return (1); /* something wrong */ } if (args.stat & DSW_COPYINGP) return (0); /* copying underway */ } } /* child died */ if (WIFEXITED(dead_stat)) return (WEXITSTATUS(dead_stat)); else return (1); } int mounted(char *t) { int rdsk; int i; FILE *mntfp; struct mnttab mntref; struct mnttab mntent; char target[DSW_NAMELEN]; char *s; rdsk = i = 0; for (s = target; i < DSW_NAMELEN && (*s = *t++); i++) { if (*s == 'r' && rdsk == 0) rdsk = 1; else s++; } *s = '\0'; mntref.mnt_special = target; mntref.mnt_mountp = NULL; mntref.mnt_fstype = NULL; mntref.mnt_mntopts = NULL; mntref.mnt_time = NULL; if ((mntfp = fopen("/etc/mnttab", "r")) == NULL) { dsw_error(gettext("Can not check volume against mount table"), NULL); } if (getmntany(mntfp, &mntent, &mntref) != -1) { /* found something before EOF */ (void) fclose(mntfp); return (1); } (void) fclose(mntfp); return (0); } void enable(char *master_volume, char *shadow_volume, char *bitmap_volume, char *copy_type) { dsw_config_t parms; dsw_ioctl_t temp; char *p; int rc; pid_t child; spcs_s_info_t *sp_info; struct stat mstat, sstat, bstat; char mst_dg[DSW_NAMELEN] = {0}; char shd_dg[DSW_NAMELEN] = {0}; char bmp_dg[DSW_NAMELEN] = {0}; int mvol_enabled; char *altname; grptag_t *gdata; bzero(&parms, sizeof (dsw_config_t)); if (strcmp(copy_type, "independent") == 0 || strcmp(copy_type, gettext("independent")) == 0) parms.flag = DSW_GOLDEN; else if (strcmp(copy_type, "dependent") == 0 || strcmp(copy_type, gettext("dependent")) == 0) parms.flag = 0; else dsw_error(gettext("don't understand shadow type"), NULL); /* validate volume names */ if (perform_autosv()) { if (cfg_load_svols(cfg) < 0 || cfg_load_dsvols(cfg) < 0 || cfg_load_shadows(cfg) < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } load_ii_vols(cfg); reload_vols = LD_SVOLS | LD_DSVOLS | LD_SHADOWS | LD_II; /* see if it's been used before under a different name */ conform_name(&master_volume); conform_name(&shadow_volume); rc = cfg_get_canonical_name(cfg, bitmap_volume, &altname); if (rc < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } if (rc) { errno = EBUSY; dsw_error(gettext("Bitmap in use"), NULL); } } /* * If not local, determine disk group names for volumes in II set */ switch (check_cluster()) { case II_CLUSTER: /* * Check if none or all volumes are in a disk group */ rc = 0; if (check_diskgroup(master_volume, mst_dg)) rc++; if (check_diskgroup(shadow_volume, shd_dg)) rc++; if (check_diskgroup(bitmap_volume, bmp_dg)) rc++; if ((rc != 0) && (rc != 3)) dsw_error(gettext( "Not all Point-in-Time Copy volumes are " "in a disk group"), NULL); /* * If volumes are not in a disk group, but are in a * cluster, then "-C ", must be set */ if (rc == 0 && cfg_cluster_tag == NULL) dsw_error(gettext( "Point-in-Time Copy volumes, that are not " "in a device group which has been " "registered with SunCluster, " "require usage of \"-C\""), NULL); /* * the same disk group * If -n, plus mst_dg==bmp_dg, then allow E/I/J in SunCluster */ if ((strcmp(mst_dg, bmp_dg)) || (strcmp(mst_dg, shd_dg) && (!nflg))) dsw_error(gettext( "Volumes are not in same disk group"), NULL); /* * Can never enable the same shadow twice, regardless of * exportable shadow device group movement */ if (find_shadow_line(shadow_volume)) dsw_error(gettext( "Shadow volume is already configured"), NULL); /* * Groups cannot span multiple clusters */ if (group_name && perform_autosv()) { gdata = (grptag_t *)nsc_lookup(volhash, group_name); if (gdata && strncmp(gdata->ctag, mst_dg, DSW_NAMELEN) != 0) { errno = EINVAL; dsw_error(gettext("Group contains sets not " "in the same cluster resource"), NULL); } } /* * Check cluster tag and bitmap disk group * set latter if different */ if (check_resource_group(bitmap_volume)) { /* * Unload and reload in the event cluster tag has * changed */ if (perform_autosv()) { unload_ii_vols(); cfg_unload_shadows(); cfg_unload_dsvols(); cfg_unload_svols(); if (cfg_load_svols(cfg) < 0 || cfg_load_dsvols(cfg) < 0 || cfg_load_shadows(cfg) < 0) { dsw_error(gettext( "Unable to parse config " "file"), NULL); } load_ii_vols(cfg); } } /* * Copy cluster name into config */ strncpy(parms.cluster_tag, cfg_cluster_tag, DSW_NAMELEN); break; case II_CLUSTER_LCL: /* ensure that the -C local won't interfere with the set */ if (group_name && perform_autosv()) { gdata = (grptag_t *)nsc_lookup(volhash, group_name); if (gdata) { if (strlen(gdata->ctag) != 0) { errno = EINVAL; dsw_error(gettext("Unable to put set " "into -C local and specified " "group"), NULL); } } } break; } /* * If we've got a group name, add it into the config */ if (group_name) { strncpy(parms.group_name, group_name, DSW_NAMELEN); } /* * Determine accessability of volumes */ if (stat(master_volume, &mstat) != 0) dsw_error(gettext( "Unable to access master volume"), NULL); if (!S_ISCHR(mstat.st_mode)) dsw_error(gettext( "Master volume is not a character device"), NULL); /* check the shadow_vol hasn't be used as SNDR secondary vol */ check_iishadow(shadow_volume); if (stat(shadow_volume, &sstat) != 0) dsw_error(gettext( "Unable to access shadow volume"), NULL); if (!S_ISCHR(sstat.st_mode)) dsw_error(gettext( "Shadow volume is not a character device"), NULL); if (mounted(shadow_volume)) { errno = EBUSY; dsw_error(gettext( "Shadow volume is mounted, unmount it first"), NULL); } if (mstat.st_rdev == sstat.st_rdev) { errno = EINVAL; dsw_error(gettext( "Master and shadow are the same device"), NULL); } if (stat(bitmap_volume, &bstat) != 0) { dsw_error(gettext("Unable to access bitmap"), NULL); } if (!S_ISCHR(bstat.st_mode)) dsw_error(gettext( "Bitmap volume is not a character device"), NULL); if (S_ISCHR(bstat.st_mode)) { if (mstat.st_rdev == bstat.st_rdev) { errno = EINVAL; dsw_error(gettext( "Master and bitmap are the same device"), NULL); } else if (sstat.st_rdev == bstat.st_rdev) { errno = EINVAL; dsw_error(gettext( "Shadow and bitmap are the same device"), NULL); } } strncpy(parms.master_vol, master_volume, DSW_NAMELEN); strncpy(parms.shadow_vol, shadow_volume, DSW_NAMELEN); strncpy(parms.bitmap_vol, bitmap_volume, DSW_NAMELEN); errno = 0; parms.status = spcs_s_ucreate(); /* * Check that none of the member volumes forms part of another * InstantImage group. * * -- this check has been removed; it is done in the kernel instead * -- PJW */ /* * Check against overflow volumes */ for (p = get_overflow_list(); *p != NULL; p += DSW_NAMELEN) { if (strncmp(master_volume, p, DSW_NAMELEN) == 0) dsw_error(gettext( "Master volume is already an overflow volume"), NULL); else if (strncmp(shadow_volume, p, DSW_NAMELEN) == 0) dsw_error(gettext( "Shadow volume is already an overflow volume"), NULL); else if (strncmp(bitmap_volume, p, DSW_NAMELEN) == 0) dsw_error(gettext( "Bitmap volume is already an overflow volume"), NULL); } /* * Make sure that the shadow volume is not already configured */ if (find_shadow_config(shadow_volume, NULL, &temp)) dsw_error(gettext( "Shadow volume is already configured"), NULL); if (perform_autosv()) { /* * parse the dsvol entries to see if we need to place * the master or shadow under SV control */ if (nsc_lookup(volhash, master_volume) == NULL) { if (cfg_vol_enable(cfg, master_volume, cfg_cluster_tag, "ii") < 0) { dsw_error( gettext("Cannot enable master volume"), NULL); } mvol_enabled = 1; } else { mvol_enabled = 0; } if (nsc_lookup(volhash, shadow_volume) == NULL) { if (nflg) { cfg_resource(cfg, shd_dg); rc = cfg_vol_enable(cfg, shadow_volume, shd_dg, "ii"); cfg_resource(cfg, cfg_cluster_tag); } else { rc = cfg_vol_enable(cfg, shadow_volume, cfg_cluster_tag, "ii"); } if (rc < 0) { if (mvol_enabled) { if (cfg_vol_disable(cfg, master_volume, cfg_cluster_tag, "ii") < 0) dsw_error(gettext( "SV disable of master failed"), NULL); } dsw_error( gettext("Cannot enable shadow volume"), NULL); } } unload_ii_vols(); cfg_unload_shadows(); cfg_unload_dsvols(); cfg_unload_svols(); reload_vols = 0; } add_cfg_entry(&parms); cfg_unlock(cfg); config_locked = 0; sigset(SIGCHLD, sigchild); switch (child = fork()) { case (pid_t)-1: dsw_error(gettext("Unable to fork"), NULL); break; case 0: rc = do_ioctl(dsw_fd, DSWIOC_ENABLE, &parms); if (rc == -1 && errno != DSW_EABORTED && errno != DSW_EIO) { /* * Failed to enable shadow group, log problem and remove * the shadow group from the config file. */ spcs_log("ii", &parms.status, gettext("Enable failed %s %s %s (%s)"), master_volume, shadow_volume, bitmap_volume, parms.flag & DSW_GOLDEN ? "independent" : "dependent"); if (!ii_lock(cfg, CFG_WRLOCK) || !find_shadow_config(shadow_volume, NULL, &temp)) { dsw_error(gettext( "Enable failed, can't tidy up cfg"), &parms.status); } config_locked = 1; remove_iiset(setnumber, shadow_volume, 0); dsw_error(gettext("Enable failed"), &parms.status); } if (rc == -1) sp_info = &parms.status; else sp_info = NULL; spcs_log("ii", sp_info, gettext("Enabled %s %s %s (%s)"), master_volume, shadow_volume, bitmap_volume, parms.flag & DSW_GOLDEN ? "independent" : "dependent"); spcs_s_ufree(&parms.status); break; default: exit(child_wait(child, Status, shadow_volume)); break; } } int reset(char *volume) { dsw_ioctl_t args; dsw_config_t parms; int rc; int wait_loc; pid_t child = (pid_t)0; enum copy_wait wait_action; spcs_s_info_t *stat; dsw_stat_t prev_stat; int stat_flags; static int unlocked = 0; int do_enable = 0; char key[CFG_MAX_KEY]; char optval[CFG_MAX_BUF]; unsigned int flags; wait_action = WaitForStart; if (unlocked && !ii_lock(cfg, CFG_RDLOCK)) { dsw_error(gettext("Unable to set locking on the configuration"), NULL); } config_locked = 1; if (!find_shadow_config(volume, &parms, &args)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); cfg_unlock(cfg); config_locked = 0; unlocked = 1; spcs_log("ii", NULL, gettext("Start reset %s"), volume); strncpy(prev_stat.shadow_vol, volume, DSW_NAMELEN); prev_stat.shadow_vol[DSW_NAMELEN - 1] = '\0'; prev_stat.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_STAT, &prev_stat) == -1) { /* set is suspended, so we do the enable processing instead */ do_enable = 1; /* first check to see whether the set was offline */ snprintf(key, CFG_MAX_KEY, "ii.set%d.options", setnumber); if (!ii_lock(cfg, CFG_RDLOCK)) { dsw_error(gettext("Unable to set locking on the " "configuration"), NULL); } config_locked = 1; unlocked = 0; if (cfg_get_single_option(cfg, CFG_SEC_CONF, key, NSKERN_II_BMP_OPTION, optval, CFG_MAX_BUF) < 0) { dsw_error(gettext("unable to read config file"), NULL); } cfg_unlock(cfg); config_locked = 0; unlocked = 1; sscanf(optval, "%x", &flags); if ((flags & DSW_OFFLINE) == 0) { /* set wasn't offline - don't reset */ dsw_error(gettext("Set not offline, will not reset"), NULL); } parms.status = spcs_s_ucreate(); stat = &parms.status; stat_flags = DSW_BMPOFFLINE; } else { args.status = spcs_s_ucreate(); stat = &args.status; stat_flags = prev_stat.stat; } spcs_s_ufree(&prev_stat.status); if (wait_action == WaitForStart) sigset(SIGCHLD, sigchild); switch (child = fork()) { case (pid_t)-1: dsw_error(gettext("Unable to fork"), NULL); break; case 0: if (do_enable) { rc = do_ioctl(dsw_fd, DSWIOC_ENABLE, &parms); } else { rc = do_ioctl(dsw_fd, DSWIOC_RESET, &args); } if (rc == -1 && errno != DSW_EABORTED && errno != DSW_EIO) { spcs_log("ii", stat, gettext("Fail reset %s"), volume); dsw_error(gettext("Reset shadow failed"), stat); } /* last_overflow is set during find_shadow_config */ if (strlen(last_overflow) > 0 && (stat_flags & (DSW_SHDOFFLINE | DSW_BMPOFFLINE)) != 0) { /* reattach it */ strncpy(parms.bitmap_vol, last_overflow, DSW_NAMELEN); do_attach(&parms); } spcs_log("ii", stat, gettext("Finish reset %s"), volume); spcs_s_ufree(stat); exit(0); break; default: if (wait_action == WaitForStart) { rc = child_wait(child, CopyStart, args.shadow_vol); } else { /* wait_action == WaitForEnd */ wait_loc = 0; wait(&wait_loc); if (WIFEXITED(wait_loc) && (WEXITSTATUS(wait_loc) == 0)) rc = 0; else rc = -1; } break; } /* if successful, remove flags entry from options field */ if (rc >= 0) { if (!ii_lock(cfg, CFG_WRLOCK)) { dsw_error(gettext("Unable to set locking on the " "configuration"), NULL); } config_locked = 1; if (!find_shadow_config(volume, &parms, &args)) { dsw_error(gettext("Volume is not in a Point-in-Time " "Copy group"), NULL); } snprintf(key, CFG_MAX_KEY, "ii.set%d.options", setnumber); if (cfg_del_option(cfg, CFG_SEC_CONF, key, NSKERN_II_BMP_OPTION) < 0) { dsw_error(gettext("Update of config failed"), NULL); } cfg_commit(cfg); cfg_unlock(cfg); config_locked = 0; } return (rc); } int overflow(char *volume) { dsw_ioctl_t args; int rc; spcs_s_info_t *stat; check_action(gettext("Initialize this overflow volume?")); if (find_matching_cf_line(volume, NULL, &args)) dsw_error(gettext("Volume is part of a Point-in-Time Copy " "group"), NULL); args.status = spcs_s_ucreate(); strncpy(args.shadow_vol, volume, DSW_NAMELEN); rc = do_ioctl(dsw_fd, DSWIOC_OCREAT, &args); if (rc == -1) { spcs_log("ii", &args.status, gettext("Create overflow failed %s"), volume); dsw_error(gettext("Create overflow failed"), &args.status); } if (rc == -1) stat = &args.status; else stat = NULL; spcs_log("ii", stat, gettext("Create overflow succeeded %s"), volume); spcs_s_ufree(&args.status); return (0); } void bitmap_op(char *master_volume, int print_bitmap, int bitmap_percent, int used, int is_compact) { unsigned char *bitmap; char *name; int i, x, y; unsigned j; unsigned long n; unsigned long percent; bitmap = allocate_bitmap(master_volume); if (bitmap == NULL) return; if (bitmap_percent) { /* count the number of bits set in bitmap */ for (i = n = 0; i < bm_size; i++) for (j = (unsigned)bitmap[i]; j; j &= j -1) n++; if (is_compact) (void) printf(gettext("Chunks in map: %d used: %d\n"), used, n); if (bm_actual < 100) { percent = 0; } else { percent = (n * 100) / bm_actual; } (void) printf(gettext("Percent of bitmap set: %u\n"), percent); percent = percent/100; /* distinguish between 0.0000% and 0.n% of bitmap set */ if (percent < 1) (void) printf("\t(%s)\n", n > 0 ? gettext("bitmap dirty") : gettext("bitmap clean")); } if (print_bitmap) { name = strrchr(master_volume, '/'); if (name++ == NULL) name = master_volume; i = bm_size * 8; x = (int)ceil(sqrt((double)i)); x += (8 - (x % 8)); /* round up to nearest multiple of 8 */ y = i / x; if (y * x < i) y++; (void) printf("#define bm%s_width %d\n#define bm%s_height %d\n", name, x, name, y); (void) printf("#define bm%s_x_hot 0\n#define bm%s_y_hot 0\n", name, name); (void) printf("static char bm%s_bits[] = {\n", name); for (i = 0; i < bm_size; i++) { if (i % 12 == 0) (void) printf("\n"); (void) printf("0x%02x, ", bitmap[i]); } y = x * y; for (; i < y; i++) { if (i % 12 == 0) (void) printf("\n"); (void) printf("0x00, "); } (void) printf("\n};\n"); } free_bitmap(bitmap); } static int validate_group_names(char **vol_list, char *group) { ENTRY item, *found; int i, rc, count; dsw_aioctl_t *group_list; char *ptr; if (group == NULL || *group == NULL) { /* no group set, just count volume list */ for (i = 0; *vol_list++ != NULL; i++) ; return (i); } if ((count = do_ioctl(dsw_fd, DSWIOC_LISTLEN, NULL)) < 0) dsw_error("DSWIOC_LISTLEN", NULL); group_list = malloc(sizeof (dsw_aioctl_t) + count * DSW_NAMELEN); if (group_list == NULL) dsw_error(gettext("Failed to allocate memory"), NULL); bzero(group_list, sizeof (dsw_aioctl_t) + count * DSW_NAMELEN); group_list->count = count; group_list->flags = 0; group_list->status = spcs_s_ucreate(); strncpy(group_list->shadow_vol, group, DSW_NAMELEN); rc = do_ioctl(dsw_fd, DSWIOC_GLIST, group_list); if (rc < 0) dsw_error(gettext("Group list access failure"), &group_list->status); group_list->shadow_vol[DSW_NAMELEN * group_list->count] = '\0'; /* create hash and enter all volumes into it */ if (hcreate(group_list->count) == 0) dsw_error(gettext("Failed to allocate memory"), NULL); ptr = group_list->shadow_vol; count = group_list->count; i = 0; while (i < count) { ptr[ DSW_NAMELEN - 1 ] = '\0'; item.key = ptr; item.data = (void *) 0; (void) hsearch(item, ENTER); ++i; ptr += DSW_NAMELEN; } /* now compare the volume list with the hash */ for (i = 0; vol_list[ i ]; i++) { item.key = vol_list[ i ]; found = hsearch(item, FIND); if (!found) dsw_error(gettext("Group config does not match kernel"), NULL); if (found->data != (void *) 0) dsw_error(gettext("Duplicate volume specified"), NULL); found->data = (void *) 1; } if (i != count) dsw_error(gettext("Group config does not match kernel"), NULL); /* everything checks out */ free(group_list); hdestroy(); return (count); } int do_acopy(char **vol_list, enum copy_update update_mode, enum copy_direction direction) { dsw_aioctl_t *acopy_args; dsw_ioctl_t copy_args; dsw_config_t parms; dsw_stat_t stat_s; int i; int rc; int n_vols; char *t; char buf[1024]; char *sp; char *ppid; n_vols = validate_group_names(vol_list, group_name); acopy_args = calloc(sizeof (dsw_aioctl_t) + n_vols * DSW_NAMELEN, 1); if (acopy_args == NULL) dsw_error(gettext("Too many volumes given for update"), NULL); acopy_args->count = n_vols; acopy_args->flags = 0; if (update_mode == Update) acopy_args->flags |= CV_BMP_ONLY; if (direction == ToMaster) acopy_args->flags |= CV_SHD2MST; if (pflg) { acopy_args->flags |= CV_LOCK_PID; #ifdef DEBUG ppid = getenv("IIADM_PPID"); if (ppid) { acopy_args->pid = atoi(ppid); fprintf(stderr, "(using %s for ppid)\n", ppid); } else { acopy_args->pid = getppid(); } #else acopy_args->pid = getppid(); #endif } for (i = 0; i < n_vols; i++) { if (!find_shadow_config(vol_list[i], &parms, ©_args)) dsw_error(gettext("Volume is not in a Point-in-Time " "group"), NULL); if (direction == ToMaster) { t = parms.master_vol; } else { t = parms.shadow_vol; } if (mounted(t)) { errno = EBUSY; dsw_error(gettext("Target of copy/update is mounted, " "unmount it first"), NULL); } strncpy(stat_s.shadow_vol, parms.shadow_vol, DSW_NAMELEN); stat_s.shadow_vol[DSW_NAMELEN-1] = '\0'; stat_s.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_STAT, &stat_s); spcs_s_ufree(&stat_s.status); if (rc == -1) { (void) sprintf(buf, gettext("Shadow group %s is suspended"), vol_list[i]); dsw_error(buf, NULL); } if (stat_s.stat & DSW_COPYINGP) { (void) fprintf(stderr, "%s: %s\n", cmdnam, gettext("Copy already in progress")); exit(1); } } acopy_args->status = spcs_s_ucreate(); for (i = 0; i < n_vols; i++) { spcs_log("ii", NULL, gettext("Atomic %s %s %s"), update_mode == Update ? gettext("update") : gettext("copy"), vol_list[i], direction == ToMaster ? gettext("from shadow") : gettext("to shadow")); } if (group_name == NULL || *group_name == NULL) { sp = acopy_args->shadow_vol; for (i = 0; i < n_vols; i++, sp += DSW_NAMELEN) strncpy(sp, vol_list[i], DSW_NAMELEN); } else { strncpy(acopy_args->shadow_vol, group_name, DSW_NAMELEN); acopy_args->flags |= CV_IS_GROUP; } rc = do_ioctl(dsw_fd, DSWIOC_ACOPY, acopy_args); if (rc == -1) { i = acopy_args->count; if (i < 0 || i >= n_vols) { spcs_log("ii", NULL, gettext("Atomic update failed")); (void) sprintf(buf, gettext("Update failed")); } else { spcs_log("ii", NULL, gettext("Atomic update of %s failed"), vol_list[acopy_args->count]); (void) sprintf(buf, gettext("Update of %s failed"), vol_list[acopy_args->count]); } dsw_error(buf, &(acopy_args->status)); } return (rc); } int do_copy(char **vol_list, enum copy_update update_mode, enum copy_direction direction, enum copy_wait wait_action) { dsw_ioctl_t copy_args; dsw_config_t parms; dsw_stat_t stat_s; int rc; int wait_loc; char *t; char *volume; pid_t child = (pid_t)0; char *ppid; if (vol_list[0] && vol_list[1]) return (do_acopy(vol_list, update_mode, direction)); volume = vol_list[0]; if (!find_shadow_config(volume, &parms, ©_args)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); cfg_unlock(cfg); config_locked = 0; copy_args.flags = 0; if (update_mode == Update) copy_args.flags |= CV_BMP_ONLY; if (direction == ToMaster) { copy_args.flags |= CV_SHD2MST; t = parms.master_vol; } else { t = parms.shadow_vol; } if (pflg) { copy_args.flags |= CV_LOCK_PID; #ifdef DEBUG ppid = getenv("IIADM_PPID"); if (ppid) { copy_args.pid = atoi(ppid); fprintf(stderr, "(using %s for ppid)\n", ppid); } else { copy_args.pid = getppid(); } #else copy_args.pid = getppid(); #endif } if (mounted(t)) { errno = EBUSY; dsw_error(gettext("Target of copy/update is mounted, " "unmount it first"), NULL); } strncpy(stat_s.shadow_vol, copy_args.shadow_vol, DSW_NAMELEN); stat_s.shadow_vol[DSW_NAMELEN-1] = '\0'; stat_s.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_STAT, &stat_s); spcs_s_ufree(&stat_s.status); if (rc == -1) dsw_error(gettext("Shadow group suspended"), NULL); if (stat_s.stat & DSW_COPYINGP) { (void) fprintf(stderr, "%s: %s\n", cmdnam, gettext("Copy already in progress")); exit(1); } copy_args.status = spcs_s_ucreate(); spcs_log("ii", NULL, gettext("Start %s %s %s"), update_mode == Update ? gettext("update") : gettext("copy"), volume, direction == ToMaster ? gettext("from shadow") : gettext("to shadow")); if (wait_action == WaitForStart) sigset(SIGCHLD, sigchild); switch (child = fork()) { case (pid_t)-1: dsw_error(gettext("Unable to fork"), NULL); break; case 0: rc = do_ioctl(dsw_fd, DSWIOC_COPY, ©_args); if (rc == -1) { spcs_log("ii", ©_args.status, gettext("Fail %s %s %s"), update_mode == Update ? gettext("update") : gettext("copy"), volume, direction == ToMaster ? gettext("from shadow") : gettext("to shadow")); dsw_error(gettext("Copy failed"), ©_args.status); } spcs_s_ufree(©_args.status); spcs_log("ii", NULL, gettext("Finish %s %s %s"), update_mode == Update ? gettext("update") : gettext("copy"), volume, direction == ToMaster ? gettext("from shadow") : gettext("to shadow")); exit(0); break; default: if (wait_action == WaitForStart) { rc = child_wait(child, CopyStart, copy_args.shadow_vol); } else { /* wait_action == WaitForEnd */ wait_loc = 0; wait(&wait_loc); if (WIFEXITED(wait_loc) && (WEXITSTATUS(wait_loc) == 0)) rc = 0; else rc = 1; } break; } return (rc); } void print_status(dsw_config_t *conf, int in_config) { dsw_stat_t args; int stat_flags; static int need_sep = 0; time_t tmp_time; if (need_sep++ > 0) (void) printf("--------------------------------------" "----------------------------------------\n"); strncpy(args.shadow_vol, conf->shadow_vol, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN-1] = '\0'; if (in_config) { (void) printf("%s: %s\n", conf->master_vol, gettext("(master volume)")); (void) printf("%s: %s\n", conf->shadow_vol, gettext("(shadow volume)")); (void) printf("%s: %s\n", conf->bitmap_vol, gettext("(bitmap volume)")); } /* * Do special checking on the status of this volume in a Sun Cluster */ if (check_cluster() == II_CLUSTER) { char dgname[CFG_MAX_BUF], *other_node; if (cfg_dgname(conf->bitmap_vol, dgname, sizeof (dgname))) { if (strlen(dgname)) { int rc = cfg_dgname_islocal(dgname, &other_node); if (rc < 0) { (void) printf(gettext( "Suspended on this node, not active elsewhere\n")); return; } else if (rc == 0) { (void) printf(gettext( "Suspended on this node, active on %s\n"), other_node); return; } } } } args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_STAT, &args) == -1) { /* Handle Not found or not in config */ if (errno != DSW_ENOTFOUND || !in_config) dsw_error(gettext("Stat failed"), &args.status); /* Just suspend */ (void) printf(gettext("Suspended.\n")); return; } if (args.overflow_vol[0] != '\0') (void) printf("%s: %s\n", args.overflow_vol, gettext("(overflow volume)")); if (conf->group_name[0] != '\0') (void) printf(gettext("Group name: %s\n"), conf->group_name); if (conf->cluster_tag[0] != '\0') (void) printf(gettext("Cluster tag: %s\n"), conf->cluster_tag); stat_flags = args.stat; spcs_s_ufree(&args.status); if (stat_flags & DSW_GOLDEN) (void) printf(gettext("Independent copy")); else (void) printf(gettext("Dependent copy")); if (stat_flags & DSW_TREEMAP) (void) printf(gettext(", compacted shadow space")); if (stat_flags & DSW_COPYINGP) (void) printf(gettext(", copy in progress")); else if (stat_flags & DSW_COPYING) (void) printf(gettext(", copy not active")); if (stat_flags & DSW_COPYINGM) (void) printf(gettext(", copying master to shadow")); if (stat_flags & DSW_COPYINGS) (void) printf(gettext(", copying shadow to master")); if (stat_flags & DSW_COPYINGX) (void) printf(gettext(", abort of copy requested")); if (stat_flags & DSW_MSTOFFLINE) (void) printf(gettext(", master volume offline")); if (stat_flags & DSW_SHDOFFLINE) (void) printf(gettext(", shadow volume offline")); if (stat_flags & DSW_BMPOFFLINE) (void) printf(gettext(", bitmap volume offline")); if (stat_flags & DSW_OVROFFLINE) (void) printf(gettext(", overflow volume offline")); if (stat_flags & DSW_SHDEXPORT) (void) printf(gettext(", shadow volume exported")); if (stat_flags & DSW_SHDIMPORT) (void) printf(gettext(", shadow volume imported")); if (stat_flags & DSW_OVERFLOW) (void) printf(gettext(", out of space")); if (stat_flags & DSW_VOVERFLOW) (void) printf(gettext(", spilled into overflow volume")); (void) printf("\n"); tmp_time = args.mtime; if (tmp_time != 0) (void) printf("%s %s", gettext("Latest modified time:"), ctime(&tmp_time)); else (void) printf("%s\n", gettext("Latest modified time: unknown")); (void) printf("%s %8llu\n", gettext("Volume size:"), args.size); if (args.shdsize != 0) { (void) printf("%s %lld %s %lld\n", gettext("Shadow chunks total:"), args.shdsize, gettext("Shadow chunks used:"), args.shdused); } bitmap_op(args.shadow_vol, 0, 1, 0, 0); } int abort_copy(char *volume) { dsw_ioctl_t args; if (!find_shadow_config(volume, NULL, &args)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_ABORT, &args) == -1) dsw_error(gettext("Abort failed"), &args.status); spcs_log("ii", NULL, gettext("Abort %s"), args.shadow_vol); spcs_s_ufree(&args.status); return (0); } void iiversion() { dsw_version_t args; args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_VERSION, &args) == -1) dsw_error(gettext("Version failed"), &args.status); spcs_s_ufree(&args.status); #ifdef DEBUG (void) printf(gettext("Point in Time Copy version %d.%d.%d.%d\n"), args.major, args.minor, args.micro, args.baseline); #else if (args.micro) { (void) printf(gettext("Point in Time Copy version %d.%d.%d\n"), args.major, args.minor, args.micro); } else { (void) printf(gettext("Point in Time Copy version %d.%d\n"), args.major, args.minor); } #endif } void list_volumes() { dsw_list_t args; int i, set, found; dsw_config_t *lp; ENTRY item, *ip; dsw_config_t parms; if ((i = do_ioctl(dsw_fd, DSWIOC_LISTLEN, &args)) == -1) dsw_error("DSWIOC_LISTLEN", NULL); args.status = spcs_s_ucreate(); args.list_used = 0; args.list_size = i + 4; lp = args.list = (dsw_config_t *) malloc(args.list_size * sizeof (dsw_config_t)); if (args.list == NULL) dsw_error(gettext("Failed to allocate memory"), NULL); if (do_ioctl(dsw_fd, DSWIOC_LIST, &args) == -1) dsw_error(gettext("List failed"), &args.status); spcs_s_ufree(&args.status); /* make a hashtable */ if (args.list_used > 0) { if (hcreate(args.list_used) == 0) { dsw_error(gettext("Failed to allocate memory"), NULL); /*NOTREACHED*/ } } /* populate the hashtable */ for (i = 0; i < args.list_used; i++, lp++) { item.key = lp->shadow_vol; item.data = (char *)lp; if (hsearch(item, ENTER) == NULL) { dsw_error(gettext("Failed to allocate memory"), NULL); /*NOTREACHED*/ } } /* perform action for each line of the stored config file */ for (set = 1; get_dsw_config(set, &parms) == 0; set++) { /* Are there any II sets configured on this node? */ if (args.list_used > 0) { item.key = parms.shadow_vol; /* Is this volume configured on this node? */ if (ip = hsearch(item, FIND)) { /* Handle Imported Shadows */ /* LINTED alignment of cast ok */ lp = (dsw_config_t *)ip->data; if (strcmp(parms.master_vol, II_IMPORTED_SHADOW)) found = !(lp->flag & DSW_SHDIMPORT); else found = (lp->flag & DSW_SHDIMPORT); } else found = FALSE; } else found = FALSE; if ((cfg_cluster_tag) && strcmp(cfg_cluster_tag, parms.cluster_tag)) continue; if ((group_name) && strcmp(group_name, parms.group_name)) continue; (void) printf("%s %.*s %.*s %.*s%s\n", (parms.flag & DSW_GOLDEN) ? "ind" : "dep", DSW_NAMELEN, parms.master_vol, DSW_NAMELEN, parms.shadow_vol, DSW_NAMELEN, parms.bitmap_vol, found ? "" : gettext(" (suspended)")); } hdestroy(); free(args.list); } int wait_for_copy(char *volume) { dsw_ioctl_t parms; int rc; static int unlocked = 0; char *ppid; if (unlocked && !ii_lock(cfg, CFG_RDLOCK)) { dsw_error(gettext("Unable to set locking on the configuration"), NULL); } config_locked = 1; if (!find_shadow_config(volume, NULL, &parms)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); cfg_unlock(cfg); config_locked = 0; unlocked = 1; parms.status = spcs_s_ucreate(); if (pflg) { #ifdef DEBUG ppid = getenv("IIADM_PPID"); if (ppid) { parms.pid = atoi(ppid); fprintf(stderr, "(using %s for ppid)\n", ppid); } else { parms.pid = (nflg) ? -1 : getppid(); } #else parms.pid = (nflg) ? -1 : getppid(); #endif parms.flags |= CV_LOCK_PID; } rc = do_ioctl(dsw_fd, DSWIOC_WAIT, &parms); if (rc == -1) dsw_error(gettext("Wait failed"), &parms.status); spcs_s_ufree(&parms.status); return (0); } int export(char *volume) { dsw_ioctl_t parms; dsw_config_t conf; char *old_ctag, dgname[DSW_NAMELEN]; int rc; if (!find_shadow_config(volume, &conf, &parms)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); if (mounted(volume)) dsw_error(gettext("Can't export a mounted volume"), NULL); /* If this is an exportable shadow in the cluster, change ctag */ if (strlen(conf.cluster_tag) && (cfg_dgname(volume, dgname, sizeof (dgname)))) { old_ctag = cfg_cluster_tag; cfg_resource(cfg, cfg_cluster_tag = strdup(dgname)); } else old_ctag = NULL; if (cfg_load_dsvols(cfg) < 0 || cfg_load_shadows(cfg) < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } reload_vols = LD_DSVOLS | LD_SHADOWS; conform_name(&volume); spcs_log("ii", NULL, gettext("Export %s"), volume); parms.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_EXPORT, &parms); if (rc == -1) dsw_error(gettext("Export failed"), &parms.status); if (perform_autosv()) { if (cfg_vol_disable(cfg, volume, cfg_cluster_tag, "ii") < 0) { dsw_error(gettext("SV-disable failed"), NULL); } cfg_commit(cfg); } /* restore old cluster tag, if changed */ if (old_ctag != NULL) cfg_resource(cfg, cfg_cluster_tag = old_ctag); spcs_s_ufree(&parms.status); return (0); } int detach(char *volume) { dsw_ioctl_t parms; int rc; if (!find_shadow_config(volume, NULL, &parms)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); parms.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_ODETACH, &parms); if (rc == 0) { /* remove overflow from cfg line */ (void) sprintf(key, "ii.set%d.overflow", setnumber); if (cfg_put_cstring(cfg, key, "-", 1) < 0) { perror("cfg_put_cstring"); } (void) cfg_commit(cfg); } else { spcs_log("ii", NULL, gettext("Detach of overflow %s failed"), parms.shadow_vol); dsw_error(gettext("Failed to detach overflow volume"), &parms.status); } return (rc); } static void can_disable(char *vol) { dsw_stat_t args; if (mounted(vol)) { strncpy(args.shadow_vol, vol, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN - 1] = '\0'; args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_STAT, &args) != -1 && (args.stat & DSW_GOLDEN) == 0) { errno = EBUSY; dsw_error(gettext("Shadow Volume is currently mounted " "and dependent on the master volume"), NULL); } spcs_s_ufree(&args.status); } } static void clean_up_after_failed_disable(dsw_ioctl_t *parms) { char **p; dsw_stat_t args; for (p = group_volumes; *p; p++) { strncpy(args.shadow_vol, *p, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN - 1] = '\0'; args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_STAT, &args) == -1) { /* set was successfully disabled */ if (find_shadow_config(*p, NULL, NULL)) remove_iiset(setnumber, *p, 0); } spcs_s_ufree(&args.status); } dsw_error(gettext("Some sets in the group failed to disable"), &parms->status); } int dsw_group_or_single_disable(int argc, char *argv[]) { int rc = 0; char **p; dsw_ioctl_t parms; int flags = 0; dsw_config_t conf; int shd_exported = 0; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name) { if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); for (p = group_volumes; *p; p++) { can_disable(*p); } strncpy(parms.shadow_vol, group_name, DSW_NAMELEN); if (*group_name) flags = CV_IS_GROUP; } else { if (!find_shadow_config(argv[1], &conf, &parms)) { dsw_error(gettext("Volume is not in a Point-in-Time " "Copy group"), NULL); } can_disable(argv[1]); flags = 0; } if (group_name && !*group_name) { /* user typed iiadm -g "" -d */ for (p = group_volumes; *p; p++) { parms.status = spcs_s_ucreate(); parms.flags = flags; strncpy(parms.shadow_vol, *p, DSW_NAMELEN); rc = do_ioctl(dsw_fd, DSWIOC_DISABLE, &parms); if (rc == -1 && errno != DSW_ENOTFOUND) dsw_error(gettext("Disable failed"), &parms.status); if (!find_shadow_config(*p, NULL, NULL)) dsw_error(gettext("Volume is not in a Point-in" "-Time Copy group"), &parms.status); remove_iiset(setnumber, *p, 0); spcs_s_ufree(&parms.status); spcs_log("ii", NULL, gettext("Disabled %s"), parms.shadow_vol); } } else { if (is_exported(conf.shadow_vol)) { shd_exported = 1; } if ((strcmp(conf.master_vol, II_IMPORTED_SHADOW) == 0) && is_exported(conf.shadow_vol)) { dsw_error(gettext( "Imported shadow not disabled"), NULL); } parms.status = spcs_s_ucreate(); parms.flags = flags; rc = do_ioctl(dsw_fd, DSWIOC_DISABLE, &parms); if (rc == -1 && errno != DSW_ENOTFOUND) { if (errno == DSW_EDISABLE) { /* * one or more sets within the group * couldn't disable */ clean_up_after_failed_disable(&parms); } else { dsw_error(gettext("Disable failed"), &parms.status); } } spcs_log("ii", NULL, gettext("Disabled %s"), parms.shadow_vol); } if (group_name && *group_name) { for (p = group_volumes; *p; p++) { if (!find_shadow_config(*p, NULL, NULL)) { /* argh! */ fprintf(stderr, gettext("Volume '%s' is not " "in a Point-in-Time Copy group"), *p); } else { remove_iiset(setnumber, *p, 0); } } } else if (!group_name) { if (!find_shadow_config(argv[1], NULL, NULL)) { /* argh! */ dsw_error(gettext("Volume is not in a Point-in-Time " "Copy group"), NULL); } remove_iiset(setnumber, argv[1], shd_exported); } return (0); } int dsw_group_or_single_op(int argc, char *argv[], int (*op)(char *)) { int rc = 0; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name) { if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); for (; *group_volumes; group_volumes++) rc |= (*op)(*group_volumes); } else { rc = (*op)(argv[1]); } return (rc); } void dsw_list_clusters(char *cluster) { dsw_aioctl_t *acopy_args; int rc, i, count; char *ptr; if ((count = do_ioctl(dsw_fd, DSWIOC_LISTLEN, NULL)) < 0) dsw_error("DSWIOC_LISTLEN", NULL); acopy_args = malloc(sizeof (dsw_aioctl_t) + count * DSW_NAMELEN); if (acopy_args == NULL) dsw_error(gettext("Can't get memory for list enquiry"), NULL); bzero(acopy_args, sizeof (dsw_aioctl_t) + count * DSW_NAMELEN); acopy_args->count = count; acopy_args->flags = 0; acopy_args->status = spcs_s_ucreate(); if (cluster) strncpy(acopy_args->shadow_vol, cluster, DSW_NAMELEN); rc = do_ioctl(dsw_fd, DSWIOC_CLIST, acopy_args); if (rc == -1) dsw_error(gettext("Cluster list access failure"), &acopy_args->status); acopy_args->shadow_vol[DSW_NAMELEN*acopy_args->count] = NULL; if (cluster) { printf(gettext("Sets in cluster resource group %s:\n"), cluster); } else { printf(gettext("Currently configured resource groups\n")); } for (i = 0, ptr = acopy_args->shadow_vol; *ptr && i < acopy_args->count; i++, ptr += DSW_NAMELEN) { printf(" %-64.64s\n", ptr); } } void dsw_enable(int argc, char *argv[]) { if (argc != 5) usage(gettext("Incorrect number of arguments")); enable(argv[1], argv[2], argv[3], argv[4]); exit(0); } void dsw_disable(int argc, char *argv[]) { (void) dsw_group_or_single_disable(argc, argv); exit(0); } void dsw_copy_to_shadow(int argc, char *argv[]) { char **volume_list; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name == NULL) volume_list = ++argv; else { if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); volume_list = group_volumes; } exit(do_copy(volume_list, Copy, ToShadow, WaitForStart)); } void dsw_update_shadow(int argc, char *argv[]) { char **volume_list; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name == NULL) volume_list = ++argv; else { if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); volume_list = group_volumes; } exit(do_copy(volume_list, Update, ToShadow, WaitForStart)); } void dsw_copy_to_master(int argc, char *argv[]) { char **volume_list; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name == NULL) { volume_list = ++argv; check_action(gettext("Overwrite master with shadow volume?")); } else { check_action(gettext("Overwrite every" " master in this group with its shadow volume?")); if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); volume_list = group_volumes; } exit(do_copy(volume_list, Copy, ToMaster, WaitForStart)); } void dsw_update_master(int argc, char *argv[]) { char **volume_list; if (argc != 2) usage(gettext("Incorrect number of arguments")); if (group_name == NULL) { volume_list = ++argv; check_action(gettext("Overwrite master with shadow volume?")); } else { check_action(gettext("Overwrite every" " master in this group with its shadow volume?")); if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); volume_list = group_volumes; } exit(do_copy(volume_list, Update, ToMaster, WaitForStart)); } void dsw_abort_copy(int argc, char *argv[]) { exit(dsw_group_or_single_op(argc, argv, abort_copy)); } void dsw_display_status(int argc, char *argv[]) { dsw_config_t parms; int in_config; if (argc != 2 && argc != 1) usage(gettext("Incorrect number of arguments")); /* "iiadm -i" and "iiadm -i all" are equivalent */ if (argc == 2 && strcmp("all", argv[1]) != 0) { in_config = find_shadow_config(argv[1], &parms, NULL); if (!in_config) { (void) printf(gettext( "Volume is not in configuration file\n"), NULL); (void) fflush(stdout); strncpy(parms.shadow_vol, argv[1], DSW_NAMELEN); parms.shadow_vol[DSW_NAMELEN] = '\0'; } print_status(&parms, in_config); } else if (group_name) { if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or " "has no members"), NULL); for (; *group_volumes; group_volumes++) { in_config = find_shadow_config(*group_volumes, &parms, NULL); if (in_config) print_status(&parms, in_config); } } else { /* perform action for each line of the stored config file */ for (setnumber = 1; !get_dsw_config(setnumber, &parms); setnumber++) { switch (check_cluster()) { case II_CLUSTER: if ((cfg_cluster_tag) && (strcmp(cfg_cluster_tag, parms.cluster_tag))) continue; break; case II_CLUSTER_LCL: if (strlen(parms.cluster_tag)) continue; break; } print_status(&parms, 1); } } exit(0); } void dsw_display_bitmap(int argc, char *argv[]) { dsw_config_t parms; int in_config; if (argc != 2) usage(gettext("Incorrect number of arguments")); in_config = find_shadow_config(argv[1], &parms, NULL); if (!in_config) { (void) printf(gettext( "Volume is not in configuration file\n"), NULL); (void) fflush(stdout); strncpy(parms.master_vol, argv[1], DSW_NAMELEN); parms.master_vol[DSW_NAMELEN] = '\0'; } bitmap_op(parms.shadow_vol, 1, 0, 0, 0); exit(0); } /*ARGSUSED*/ void dsw_version(int argc, char *argv[]) { iiversion(); exit(0); } void dsw_reset(int argc, char *argv[]) { exit(dsw_group_or_single_op(argc, argv, reset)); } void dsw_overflow(int argc, char *argv[]) { if (argc != 2) usage(gettext("Incorrect number of arguments")); exit(overflow(argv[1])); } void dsw_wait(int argc, char *argv[]) { exit(dsw_group_or_single_op(argc, argv, wait_for_copy)); } /*ARGSUSED*/ void dsw_list_volumes(int argc, char *argv[]) { if (argc != 1) usage(gettext("Incorrect number of arguments")); list_volumes(); exit(0); } void dsw_export(int argc, char *argv[]) { if (argc != 2) usage(gettext("Incorrect number of arguments")); exit(dsw_group_or_single_op(argc, argv, export)); } void dsw_detach(int argc, char *argv[]) { (void) dsw_group_or_single_op(argc, argv, detach); exit(0); } void import(char *shadow_volume, char *bitmap_volume) { dsw_config_t parms = {0}; int rc = 0; char shd_dg[DSW_NAMELEN]; char bmp_dg[DSW_NAMELEN]; /* * If importing a shadow volume and the shadow volume is already * configured, we only support this if we are in a Sun Cluster * and the current user specified a cluster tag of -C local */ if (find_shadow_config(shadow_volume, &parms, NULL)) { dsw_error(gettext("Can't import volume on same node"), NULL); } switch (check_cluster()) { case II_CLUSTER: case II_CLUSTER_LCL: (void) check_resource_group(shadow_volume); if (cfg_cluster_tag) { /* check all volumes are in same dg */ if (cfg_dgname(shadow_volume, shd_dg, DSW_NAMELEN) == NULL) dsw_error(gettext("Shadow volume not in a" " disk group"), NULL); if (cfg_dgname(bitmap_volume, bmp_dg, DSW_NAMELEN) == NULL) dsw_error(gettext("Bitmap volume not in a" " disk group"), NULL); if (strcmp(bmp_dg, shd_dg) != 0) dsw_error(gettext("Bitmap volume not in" " same disk group as shadow set members"), NULL); } break; case II_NOT_CLUSTER: /* do nothing */ break; default: dsw_error(gettext( "Unexpected return from check_cluster()"), NULL); } /* Local configuration volumes */ if (cfg_load_dsvols(cfg) < 0 || cfg_load_shadows(cfg) < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } reload_vols = LD_DSVOLS | LD_SHADOWS; conform_name(&shadow_volume); strcpy(parms.master_vol, II_IMPORTED_SHADOW); strncpy(parms.shadow_vol, shadow_volume, DSW_NAMELEN); parms.shadow_vol[DSW_NAMELEN-1] = '\0'; strncpy(parms.bitmap_vol, bitmap_volume, DSW_NAMELEN); parms.bitmap_vol[DSW_NAMELEN-1] = '\0'; parms.flag = DSW_GOLDEN; spcs_log("ii", NULL, gettext("Import %s %s"), parms.shadow_vol, parms.bitmap_vol); parms.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_IMPORT, &parms); if (rc == -1) { spcs_log("ii", NULL, gettext("Import failed %s %s"), parms.shadow_vol, parms.bitmap_vol); dsw_error(gettext("Import failed"), &parms.status); } if (perform_autosv()) { if (cfg_vol_enable(cfg, shadow_volume, cfg_cluster_tag, "ii") < 0) { dsw_error(gettext("SV-enable failed"), NULL); } /* cfg_commit is called by add_cfg_entry below */ } spcs_s_ufree(&parms.status); add_cfg_entry(&parms); } void dsw_import(int argc, char *argv[]) { if (argc != 3) usage(gettext("Incorrect number of arguments")); import(argv[1], argv[2]); exit(0); } void join(char *shadow_volume, char *bitmap_file) { dsw_ioctl_t shd; dsw_config_t conf; dsw_bitmap_t parms; int rc = 0; int size; FILE *bmpfp; uchar_t *shd_bitmap = 0; ii_header_t header; char dgname[DSW_NAMELEN]; if (!find_shadow_config(shadow_volume, &conf, &shd)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); /* If this is an exportable shadow in the cluster, change ctag */ if (strlen(conf.cluster_tag) && (cfg_dgname(shadow_volume, dgname, sizeof (dgname)))) cfg_resource(cfg, cfg_cluster_tag = strdup(dgname)); if (cfg_load_dsvols(cfg) < 0 || cfg_load_shadows(cfg) < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } reload_vols = LD_DSVOLS | LD_SHADOWS; conform_name(&shadow_volume); if ((bmpfp = fopen(bitmap_file, "r")) == NULL) { perror(bitmap_file); (void) fprintf(stderr, gettext("Can't open imported bitmap volume\n")); exit(1); } if (fread(&header, sizeof (header), 1, bmpfp) != 1) { (void) fprintf(stderr, gettext("Can't read imported bitmap volume\n")); exit(1); } /* See if this is a bitmap header */ switch (header.ii_magic) { case DSW_DIRTY: /* A copy of a enable bitmap volume */ case DSW_CLEAN: check_action(gettext("Use the never imported bitmap?")); break; case DSW_INVALID: /* A valid diskable secondary bitmap */ break; default: (void) fprintf(stderr, gettext("Secondary bitmap is not a valid bitmap volume\n")); exit(1); } size = FBA_SIZE(header.ii_copyfba - header.ii_shdfba); if ((shd_bitmap = malloc(size)) == NULL) { perror("malloc"); exit(1); } if (fseek(bmpfp, FBA_SIZE(header.ii_shdfba), SEEK_SET)) { perror("fseek"); exit(1); } if (fread(shd_bitmap, 1, size, bmpfp) != size) { (void) fprintf(stderr, gettext("Can't read imported bitmap volume\n")); exit(1); } (void) fclose(bmpfp); strncpy(parms.shadow_vol, shadow_volume, DSW_NAMELEN); parms.shadow_vol[DSW_NAMELEN-1] = '\0'; parms.shd_bitmap = shd_bitmap; parms.shd_size = size; parms.copy_bitmap = NULL; parms.copy_size = 0; spcs_log("ii", NULL, gettext("Join %s %s"), parms.shadow_vol, bitmap_file); parms.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_JOIN, &parms); if (rc == -1) { spcs_log("ii", NULL, gettext("Join failed %s %s"), parms.shadow_vol, bitmap_file); dsw_error(gettext("Join failed"), &parms.status); } if (perform_autosv()) { rc = cfg_vol_enable(cfg, shadow_volume, cfg_cluster_tag, "ii"); if (rc < 0) { dsw_error(gettext("SV-enable failed"), NULL); } cfg_commit(cfg); } spcs_s_ufree(&parms.status); } int params(char *shadow_volume) { char *delay = param_delay; char *unit = param_unit; dsw_copyp_t parms; int rc = 0; int get = 0; int new_delay; int new_unit; strncpy(parms.shadow_vol, shadow_volume, DSW_NAMELEN); parms.shadow_vol[DSW_NAMELEN-1] = '\0'; if (delay == NULL || unit == NULL) { get = 1; parms.copy_delay = -1; parms.copy_unit = -1; } else { new_delay = parms.copy_delay = convert_int(delay); new_unit = parms.copy_unit = convert_int(unit); } parms.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_COPYP, &parms); if (rc == -1) { (void) fprintf(stderr, gettext("Parameter ranges are delay(%d - %d), " "units(%d - %d)\n"), MIN_THROTTLE_DELAY, MAX_THROTTLE_DELAY, MIN_THROTTLE_UNIT, MAX_THROTTLE_UNIT); dsw_error(gettext("Set Copy Parameters failed"), &parms.status); } if (!get) spcs_log("ii", NULL, gettext("Changed copy parameters %s from " "%d %d to %d %d"), parms.shadow_vol, parms.copy_delay, parms.copy_unit, new_delay, new_unit); else (void) printf(gettext("volume: %s\ncopy delay: %d\ncopy unit:" " %d\n"), parms.shadow_vol, parms.copy_delay, parms.copy_unit); spcs_s_ufree(&parms.status); return (0); } static void do_attach(dsw_config_t *parms) { dsw_config_t io; int rc; int check = 0; spcs_log("ii", NULL, gettext("Attach %s %s"), parms->shadow_vol, parms->bitmap_vol); parms->status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_OATTACH, parms); if (rc == -1) { check = 1; /* if overflow() fails, it calls dsw_error to exit */ (void) overflow(parms->bitmap_vol); } spcs_s_ufree(&parms->status); if (check == 1) { if (!find_shadow_config(parms->shadow_vol, &io, NULL)) dsw_error( gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); strncpy(io.bitmap_vol, parms->bitmap_vol, DSW_NAMELEN); io.bitmap_vol[DSW_NAMELEN-1] = '\0'; io.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_OATTACH, &io) == -1) { spcs_log("ii", NULL, gettext("Attach failed %s %s"), io.shadow_vol, parms->bitmap_vol); dsw_error(gettext("Attach failed"), &io.status); } spcs_s_ufree(&io.status); } } int attach(char *shadow_volume) { dsw_config_t parms; dsw_stat_t args; char shd_dg[DSW_NAMELEN]; char ovr_dg[DSW_NAMELEN]; switch (check_cluster()) { case II_CLUSTER: case II_CLUSTER_LCL: (void) check_resource_group(shadow_volume); if (cfg_cluster_tag) { /* check all volumes are in same dg */ if (cfg_dgname(shadow_volume, shd_dg, DSW_NAMELEN) == NULL) dsw_error(gettext("Shadow volume not in a" " disk group"), NULL); if (cfg_dgname(overflow_file, ovr_dg, DSW_NAMELEN) == NULL) dsw_error(gettext("Overflow volume not in a" " disk group"), NULL); if (strcmp(ovr_dg, shd_dg) != 0) dsw_error(gettext("Overflow volume not in" " same disk group as shadow set members"), NULL); } break; case II_NOT_CLUSTER: /* do nothing */ break; default: dsw_error(gettext( "Unexpected return from check_cluster()"), NULL); } /* assure that the overflow_file is not an II volume */ if (find_any_cf_line(overflow_file)) dsw_error(gettext( "Overflow volume is already in a Point-in-Time Copy " "group"), NULL); /* use find_shadow_config() to find setnumber */ if (!find_shadow_config(shadow_volume, &parms, NULL)) dsw_error(gettext("Volume is not in a Point-in-Time Copy " "group"), NULL); /* can only attach an overflow volume to dependent, compact shadow */ strncpy(args.shadow_vol, shadow_volume, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN-1] = '\0'; args.status = spcs_s_ucreate(); if ((do_ioctl(dsw_fd, DSWIOC_STAT, &args) == -1) || !(args.stat & DSW_TREEMAP)) dsw_error(gettext("Not a compact dependent shadow"), NULL); /* bitmap_vol is overloaded */ strncpy(parms.bitmap_vol, overflow_file, DSW_NAMELEN); parms.bitmap_vol[DSW_NAMELEN-1] = '\0'; do_attach(&parms); /* add overflow to cfg line */ (void) sprintf(key, "ii.set%d.overflow", setnumber); if (cfg_put_cstring(cfg, key, overflow_file, strlen(overflow_file)) < 0) { perror("cfg_put_cstring"); } (void) cfg_commit(cfg); return (0); } void dsw_join(int argc, char *argv[]) { if (argc != 3) usage(gettext("Incorrect number of arguments")); join(argv[1], argv[2]); exit(0); } void dsw_params(int argc, char *argv[]) { if (argc != 4 && argc != 2 && argc != 0) usage(gettext("Incorrect number of arguments")); if ((argc == 4) || (argc == 2)) { param_delay = argv[1]; param_unit = argv[2]; if (argc == 4) { argv[1] = argv[3]; argv[2] = NULL; } } exit(dsw_group_or_single_op(2, argv, params)); } /*ARGSUSED*/ void dsw_attach(int argc, char *argv[]) { overflow_file = argv[1]; argv[1] = argv[2]; (void) dsw_group_or_single_op(2, argv, attach); exit(0); } /*ARGSUSED*/ void dsw_olist(int argc, char *argv[]) { char *sp, *overflow_list, **vol; int count, i; ENTRY item, *found; char key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; overflow_list = get_overflow_list(); /* count entries */ count = 0; for (sp = overflow_list; *sp; sp += DSW_NAMELEN) { ++count; } /* create hash (adding room for suspended overflow volumes) */ if (hcreate(count + 1024) == 0) { dsw_error(gettext("Out of memory creating lookup table"), NULL); /*NOTREACHED*/ } if (count > 0) { /* create memory to store copy of list */ vol = (char **)calloc(count, sizeof (char *)); if (!vol) { dsw_error( gettext("Out of memory creating lookup table"), NULL); /*NOTREACHED*/ } /* fill hash */ for (i = 0, sp = overflow_list; *sp; sp += DSW_NAMELEN, i++) { /* make copy of string */ vol[ i ] = (char *)malloc(DSW_NAMELEN + 1); strncpy(vol[ i ], sp, DSW_NAMELEN); vol[ i ][ DSW_NAMELEN ] = '\0'; item.key = vol[ i ]; item.data = (char *)0; (void) hsearch(item, ENTER); } } /* loop through config file entries */ i = 0; cfg_rewind(cfg, CFG_SEC_CONF); /*CONSTCOND*/ while (1) { ++i; (void) snprintf(key, CFG_MAX_KEY, "ii.set%d.overflow", i); if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) { break; } /* has this set got an overflow volume? */ if (!*buf) { continue; } /* look up overflow in hash */ item.key = buf; if (count > 0 && (found = hsearch(item, FIND)) != NULL) { if (0 == (int)found->data) { (void) printf("%s\n", buf); found->data = (char *)1; (void) hsearch(*found, ENTER); } } else { /* must be part of a suspended set */ (void) printf("%s (attached to suspended set)\n", buf); item.key = buf; item.data = (char *)1; (void) hsearch(item, ENTER); } } exit(0); } void dsw_ostat(int argc, char *argv[]) { dsw_ostat_t args; int stat_flags; if (argc != 2) usage(gettext("Incorrect number of arguments")); strncpy(args.overflow_vol, argv[1], DSW_NAMELEN); args.overflow_vol[DSW_NAMELEN-1] = '\0'; args.status = spcs_s_ucreate(); if (do_ioctl(dsw_fd, DSWIOC_OSTAT2, &args) == -1) dsw_error(gettext("Stat failed"), &args.status); spcs_s_ufree(&args.status); if ((args.hversion >= 1) && (args.hmagic == II_OMAGIC)) { stat_flags = args.flags; if (stat_flags & IIO_CNTR_INVLD) (void) printf(gettext("Clean shutdown of volume " "sets associated with overflow volume " "did not occur.\n" "Overflow counters will be inconsistent " "until new point-in-time(s) are taken.\n")); } (void) printf(gettext("Total number of attached shadows: %d\n"), args.drefcnt); (void) printf(gettext("Number of currently attached shadows: %d\n"), args.crefcnt); (void) printf(gettext("Total number of chunks: %lld\n"), args.nchunks); (void) printf(gettext("Number of chunks ever allocated: %lld\n"), args.used); (void) printf(gettext("Number of used chunks: %lld\n"), (args.nchunks - args.unused)); (void) printf(gettext("Number of unused chunks: %lld\n"), args.unused); exit(0); } /*ARGSUSED*/ void dsw_move_2_group(int argc, char *argv[]) { dsw_config_t parms; dsw_movegrp_t movegrp; grptag_t *gdata; int waserr = 0; /* handle move to NULL group, or group of all spaces or tabs */ strncpy(movegrp.new_group, group_name, DSW_NAMELEN); if ((strlen(group_name) == 0) || (strcspn(group_name, " \t") == 0)) { group_name = "-"; bzero(movegrp.new_group, DSW_NAMELEN); gdata = NULL; } else { /* get the ctag for this group (if any) */ gdata = (grptag_t *)nsc_lookup(volhash, group_name); } movegrp.status = spcs_s_ucreate(); for (++argv; *argv; argv++) { if (!find_shadow_config(*argv, &parms, NULL)) dsw_error(gettext("Volume is not in a Point-in-Time " "Copy group"), NULL); /* ensure the ctag matches the group */ if (gdata && *gdata->ctag) { if (strncmp(parms.cluster_tag, gdata->ctag, DSW_NAMELEN) != 0) { (void) fprintf(stderr, "%s: %s %s %s\n", cmdnam, gettext("unable to move set"), *argv, gettext("into new group - cluster " "resource mismatch")); waserr = 1; continue; } } /* move the set in the kernel */ strncpy(movegrp.shadow_vol, parms.shadow_vol, DSW_NAMELEN); if (do_ioctl(dsw_fd, DSWIOC_MOVEGRP, &movegrp) < 0) dsw_error(gettext("Failed to move group in kernel"), NULL); /* now update the config */ (void) sprintf(key, "ii.set%d.group", setnumber); if (cfg_put_cstring(cfg, key, group_name, strlen(group_name)) < 0) { perror("cfg_put_cstring"); } (void) cfg_commit(cfg); } spcs_s_ufree(&movegrp.status); cfg_close(cfg); exit(waserr); } void dsw_list_groups() { FILE *pfp; if ((pfp = popen("/usr/bin/sort -u", "w")) == NULL) { dsw_error(gettext("Can't open sort program"), NULL); } (void) fflush(stdout); for (setnumber = 1; /*CSTYLED*/; setnumber++) { (void) snprintf(key, sizeof (key), "ii.set%d.group", setnumber); if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) break; /* skip if shadow set is not in any group */ if (strcmp(buf, "") == 0) continue; (void) fprintf(pfp, "%s\n", buf); } (void) pclose(pfp); } void dsw_list_group_volumes() { FILE *pfp; if (find_group_members(group_name) < 1) dsw_error(gettext("Group does not exist or has no members"), NULL); if ((pfp = popen("/usr/bin/sort -u", "w")) == NULL) { dsw_error(gettext("Can't open sort program"), NULL); } (void) fflush(stdout); for (; *group_volumes; group_volumes++) (void) fprintf(pfp, "%s\n", *group_volumes); (void) pclose(pfp); } static void load_ii_vols(CFGFILE *cfg) { int set, entries; char *mst, *shd, *buf, **entry; char *ctag, *group; mstcount_t *mdata; shdvol_t *sdata; grptag_t *gdata; static int whinged = 0; if (volhash) { return; } volhash = nsc_create_hash(); cfg_rewind(cfg, CFG_SEC_CONF); entries = cfg_get_section(cfg, &entry, "ii"); for (set = 1; set <= entries; set++) { buf = entry[set - 1]; /* grab master volume name */ mst = strtok(buf, " "); if (!mst) { free(buf); break; } /* grab shadow, group & cnode fields */ shd = strtok(NULL, " "); (void) strtok(NULL, " "); /* bitmap */ (void) strtok(NULL, " "); /* mode */ (void) strtok(NULL, " "); /* overflow */ ctag = strtok(NULL, " "); /* cnode */ (void) strtok(NULL, " "); /* options */ group = strtok(NULL, " "); /* group */ /* Fix optional tags */ if (ctag) ctag += strspn(ctag, "-"); if (group) group += strspn(group, "-"); /* If cluster tags don't match, skip record */ if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) || (!cfg_cluster_tag && strlen(ctag))) { free(buf); continue; } /* master volume, may be duplicates */ mdata = (mstcount_t *)nsc_lookup(volhash, mst); if (mdata) { ++mdata->count; } else { mdata = (mstcount_t *)malloc(sizeof (mstcount_t)); mdata->count = 1; (void) nsc_insert_node(volhash, mdata, mst); } /* grab shadow volume name */ sdata = (shdvol_t *)malloc(sizeof (shdvol_t)); strncpy(sdata->master, mst, DSW_NAMELEN); (void) nsc_insert_node(volhash, sdata, shd); /* No need to continue if no groups or ctags */ if (!group || !*group || !ctag || !*ctag) { free(buf); continue; } gdata = (grptag_t *)nsc_lookup(volhash, group); if (gdata) { /* group already exists - check ctag */ if (*ctag && (strncmp(ctag, gdata->ctag, DSW_NAMELEN) != 0)) { if (!whinged) { printf(gettext("Warning: multiple " "cluster resource groups " "defined within a single " "I/O group\n")); whinged = 1; } } } else { gdata = (grptag_t *)malloc(sizeof (grptag_t)); strncpy(gdata->ctag, ctag, DSW_NAMELEN); (void) nsc_insert_node(volhash, gdata, group); } free(buf); } /* free up any leftovers */ while (set < entries) free(entry[set++]); if (entries) free(entry); } static void unload_ii_vols() { nsc_remove_all(volhash, free); volhash = 0; } static int perform_autosv() { static int result; static int calculated = 0; int rc; #ifdef DEBUG if (getenv("II_SET_CLUSTER")) return (1); #endif if (calculated) { return (result); } /* * we only perform auto-sv if we're in a sun cluster or if * we're on a standalone system. I.e. we don't do auto-sv on Harry */ rc = check_cluster(); if (II_NOT_CLUSTER == rc) { result = 1; } else { result = cfg_issuncluster(); } calculated = 1; return (result); } /* * Returns true if set has had the shadow volume exported. * Returns false if shadow volume is not exported, or set is suspended. */ static int is_exported(char *set) { dsw_stat_t args; int rc; strncpy(args.shadow_vol, set, DSW_NAMELEN); args.shadow_vol[DSW_NAMELEN-1] = '\0'; args.status = spcs_s_ucreate(); rc = do_ioctl(dsw_fd, DSWIOC_STAT, &args); spcs_s_ufree(&args.status); if (-1 == rc) { /* set must be suspended, or being disabled */ return (0); } return ((args.stat & DSW_SHDEXPORT) == DSW_SHDEXPORT); } static void conform_name(char **path) { char *cfgname; int rc = cfg_get_canonical_name(cfg, *path, &cfgname); if (rc < 0) { dsw_error(gettext("Unable to parse config file"), NULL); } if (rc) { printf(" '%s'\n%s\n '%s'\n", *path, gettext("is currently configured as"), cfgname); check_action(gettext("Perform operation with indicated volume" " name?")); *path = cfgname; /* * NOTE: *path ought to be deallocated ('free(*path)') after * we're done with it, but since this routine is called just * before we exit, it doesn't really matter */ } } /* * verify_groupname(char *, int); * * 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. The check for a * leading dash can be skipped if the 'testDash' argument is false. This is to * allow for the '-g -L' functionality. * */ static void verify_groupname(char *grp, int testDash) { int i; if (testDash && grp[0] == '-') { errno = EINVAL; dsw_error(gettext("group name cannot start with a '-'"), NULL); } for (i = 0; grp[i] != '\0'; i++) { if (isspace(grp[i])) { errno = EINVAL; dsw_error(gettext("group name cannot contain a space"), NULL); } } } void check_iishadow(char *shadow_vol) { int i; int entries; char **entry; char *shost; char *svol; char *buf; void *librdc; /* * See if librdc is around * If not, we can just return */ if (librdc = dlopen(RDC_LIB, RTLD_LAZY | RTLD_GLOBAL)) self_check = (int (*)(char *)) dlsym(librdc, "self_check"); else { return; } entry = NULL; entries = cfg_get_section(cfg, &entry, "sndr"); for (i = 0; i < entries; i++) { buf = entry[i]; (void) strtok(buf, " "); /* phost */ (void) strtok(NULL, " "); /* primary */ (void) strtok(NULL, " "); /* pbitmap */ shost = strtok(NULL, " "); /* shost */ svol = strtok(NULL, " "); /* secondary */ if (self_check(shost) && (strcmp(shadow_vol, svol) == 0)) { free(buf); if (entries) free(entry); errno = EINVAL; dsw_error(gettext( "shadow volume is in use as SNDR secondary volume"), NULL); } free(buf); } (void) dlclose(librdc); if (entries) free(entry); }