/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Metadevice database utility. */ #include <meta.h> #define MDDB #include <sys/lvm/md_mddb.h> #include <sdssc.h> enum mddb_cmd {none, attach, detach, patch, infolong, infoshort}; extern int procsigs(int block, sigset_t *oldsigs, md_error_t *ep); static void usage( mdsetname_t *sp, char *string ) { if ((string != NULL) && (*string != '\0')) md_eprintf("%s\n", string); (void) fprintf(stderr, gettext( "usage: %s [-s setname] -a [options] mddbnnn\n" " %s [-s setname] -a [options] device ...\n" " %s [-s setname] -d [options] mddbnnn\n" " %s [-s setname] -d [options] device ...\n" " %s [-s setname] -i \n" " %s -p [options] [ mddb.cf-file ]\n" "options:\n" "-c count number of replicas (for use with -a only)\n" "-f force adding or deleting of replicas\n" "-k filename alternate /etc/system file\n" "-l length specify size of replica (for use with -a only)\n"), myname, myname, myname, myname, myname, myname); md_exit(sp, (string == NULL) ? 0 : 1); } static mdname_t * make_dbname( mdsetname_t *sp, mdnamelist_t **nlp, char *name, md_error_t *ep ) { mdname_t *np; if ((np = metaname(&sp, name, LOGICAL_DEVICE, ep)) == NULL) return (NULL); return (metanamelist_append(nlp, np)); } static mdnamelist_t * get_dbnames_fromfile( mdsetname_t *sp, mdnamelist_t **nlp, char *tabname, int *dbsize, int *dbcnt, int *default_size, md_error_t *ep ) { md_tab_t *tabp = NULL; md_tab_line_t *linep = NULL; int argc; char **argv; char *context; int save = optind; int c; /* look in md.tab */ if ((tabp = meta_tab_parse(NULL, ep)) == NULL) { if (! mdissyserror(ep, ENOENT)) mde_perror(ep, ""); mdclrerror(ep); return (NULL); } if ((linep = meta_tab_find(sp, tabp, tabname, TAB_MDDB)) == NULL) { (void) mdsyserror(ep, ENOENT, tabname); goto out; } argc = linep->argc; argv = linep->argv; context = linep->context; /* parse up entry */ optind = 1; opterr = 1; while ((c = getopt(argc, argv, "c:l:")) != -1) { switch (c) { case 'c': if (sscanf(optarg, "%d", dbcnt) != 1) { md_eprintf("%s: %s\n", context, gettext("bad format")); usage(sp, ""); } break; case 'l': if (sscanf(optarg, "%d", dbsize) != 1) { md_eprintf("%s: %s\n", context, gettext("bad format")); usage(sp, ""); } *default_size = FALSE; break; default: usage(sp, ""); } } argc -= optind; argv += optind; for (; (argc > 0); --argc, ++argv) { char *token = argv[0]; if (make_dbname(sp, nlp, token, ep) == NULL) { metafreenamelist(*nlp); *nlp = NULL; goto out; } } /* cleanup, return list */ out: if (tabp != NULL) meta_tab_free(tabp); optind = save; return (*nlp); } /* * built list of all devices which are to be detached */ static mdnamelist_t * build_a_namelist( mdsetname_t *sp, int argc, char **argv, md_error_t *ep ) { int i; int dbsize, dbcnt, default_size; mdnamelist_t *dbnlp = NULL; for (i = 0; i < argc; i++) { if (strncmp(argv[i], "mddb", 4) == 0) { if (get_dbnames_fromfile(sp, &dbnlp, argv[i], &dbsize, &dbcnt, &default_size, ep) == NULL) { /* don't freelist here - already been done */ return (NULL); } continue; } if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) { metafreenamelist(dbnlp); return (NULL); } } return (dbnlp); } /* * built the next list of devices which are to be attached * that have the same size and count of replicas. */ static mdnamelist_t * build_next_namelist( mdsetname_t *sp, int argc, char **argv, int *arg_index, int *dbsize, int *dbcnt, int *default_size, md_error_t *ep ) { int i; mdnamelist_t *dbnlp = NULL; for (i = *arg_index; i < argc; i++) { if (strncmp(argv[i], "mddb", 4) == 0) { /* * If we have stuff in the namelist * return it before processing the mddb entry. */ if (dbnlp) { *arg_index = i; return (dbnlp); } if (get_dbnames_fromfile(sp, &dbnlp, argv[i], dbsize, dbcnt, default_size, ep) == NULL) { /* don't freelist here - already been done */ return (NULL); } *arg_index = i + 1; return (dbnlp); } if (make_dbname(sp, &dbnlp, argv[i], ep) == NULL) { metafreenamelist(dbnlp); return (NULL); } } *arg_index = argc; return (dbnlp); } static int chngdb( mdsetname_t *sp, enum mddb_cmd cmd, int argc, char *argv[], uint_t options, md_error_t *ep ) { int c; int i; md_error_t xep = mdnullerror; mdnamelist_t *dbnlp = NULL; int dbsize = MD_DBSIZE; int maxblks = MDDB_MAXBLKS; int minblks = MDDB_MINBLKS; int dbcnt = 1; mdforceopts_t force = MDFORCE_NONE; int rval = 0; char *sysfilename = NULL; int default_size = TRUE; md_set_desc *sd; md_setkey_t *cl_sk; md_mnnode_desc *nd; int suspend1_flag = 0; /* reset and parse args */ optind = 1; opterr = 1; while ((c = getopt(argc, argv, "ac:dfk:pl:s:")) != -1) { switch (c) { case 'a': break; case 'c': if (sscanf(optarg, "%d", &dbcnt) != 1) { md_eprintf("%s: %s\n", optarg, gettext("bad format")); usage(sp, ""); } break; case 'd': break; case 'f': force = MDFORCE_LOCAL; break; case 'k': sysfilename = optarg; break; case 'l': if (sscanf(optarg, "%d", &dbsize) != 1) { md_eprintf("%s: %s\n", optarg, gettext("bad format")); usage(sp, ""); } default_size = FALSE; break; case 'p': break; case 's': break; default: usage(sp, ""); } } /* * If it is a multinode diskset, use appropriate metadb size. */ if (! metaislocalset(sp)) { if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) { maxblks = MDDB_MN_MAXBLKS; minblks = MDDB_MN_MINBLKS; if (default_size) dbsize = MD_MN_DBSIZE; } } if (dbsize > maxblks) usage(sp, gettext("size (-l) is too big")); if (dbsize < minblks) usage(sp, gettext("size (-l) is too small")); if (dbcnt < 1) usage(sp, gettext( "count (-c) must be 1 or more")); argc -= optind; argv += optind; if (argc <= 0) { usage(sp, gettext( "no devices specified to attach or detach")); } if (! metaislocalset(sp)) { if (MD_MNSET_DESC(sd)) { md_error_t xep = mdnullerror; sigset_t sigs; /* Make sure we are blocking all signals */ if (procsigs(TRUE, &sigs, &xep) < 0) mdclrerror(&xep); /* * Lock out other metaset or metadb commands * across the diskset. */ nd = sd->sd_nodelist; while (nd) { if ((force & MDFORCE_LOCAL) && strcmp(nd->nd_nodename, mynode()) != 0) { nd = nd->nd_next; continue; } if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { nd = nd->nd_next; continue; } if (clnt_lock_set(nd->nd_nodename, sp, ep)) { rval = -1; goto done; } nd = nd->nd_next; } /* * Lock out other meta* commands by suspending * class 1 messages across the diskset. */ nd = sd->sd_nodelist; while (nd) { if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { nd = nd->nd_next; continue; } if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1, MD_MSCF_NO_FLAGS, ep)) { rval = -1; goto done; } suspend1_flag = 1; nd = nd->nd_next; } } else { /* Lock the set on current set members */ for (i = 0; i < MD_MAXSIDES; i++) { /* Skip empty slots */ if (sd->sd_nodes[i][0] == '\0') continue; if ((force & MDFORCE_LOCAL) && strcmp(sd->sd_nodes[i], mynode()) != 0) continue; if (clnt_lock_set(sd->sd_nodes[i], sp, ep)) { rval = -1; goto done; } } } force |= MDFORCE_SET_LOCKED; options |= MDCHK_SET_LOCKED; } if (cmd == detach) { if ((dbnlp = build_a_namelist(sp, argc, argv, ep)) == NULL) { rval = -1; goto done; } rval = meta_db_detach(sp, dbnlp, force, sysfilename, ep); metafreenamelist(dbnlp); } if (cmd == attach) { daddr_t nblks = 0; int arg_index = 0; int saved_dbsize = dbsize; int saved_dbcnt = dbcnt; int saved_default_size = default_size; if (force & MDFORCE_LOCAL) options |= MDCHK_SET_FORCE; if (default_size) if ((nblks = meta_db_minreplica(sp, ep)) < 0) mdclrerror(ep); /* * Loop through build a new namelist * for each "mddb" entry or the devices list * on the command line. This allows each "mddb" * entry to have unique dbsize and dbcnt. */ while (arg_index < argc) { dbnlp = build_next_namelist(sp, argc, argv, &arg_index, &dbsize, &dbcnt, &default_size, ep); if (dbnlp == NULL) { rval = -1; goto done; } /* * If using the default size, * then let's adjust the default to the minimum * size currently in use. */ if (default_size && (nblks > 0)) dbsize = nblks; /* adjust replica size */ if (dbsize > maxblks) usage(sp, gettext("size (-l) is too big")); rval = meta_db_attach(sp, dbnlp, options, NULL, dbcnt, dbsize, sysfilename, ep); if (rval) { metafreenamelist(dbnlp); break; } dbsize = saved_dbsize; dbcnt = saved_dbcnt; default_size = saved_default_size; metafreenamelist(dbnlp); } } done: if (! metaislocalset(sp)) { cl_sk = cl_get_setkey(sp->setno, sp->setname); if (MD_MNSET_DESC(sd)) { /* * Unlock diskset by resuming * class 1 messages across the diskset. */ if (suspend1_flag) { nd = sd->sd_nodelist; while (nd) { if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { nd = nd->nd_next; continue; } if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME, sp, MD_MSG_CLASS1, MD_MSCF_NO_FLAGS, &xep)) { mde_perror(&xep, ""); mdclrerror(&xep); } nd = nd->nd_next; } } nd = sd->sd_nodelist; while (nd) { if ((force & MDFORCE_LOCAL) && strcmp(nd->nd_nodename, mynode()) != 0) { nd = nd->nd_next; continue; } if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) { nd = nd->nd_next; continue; } if (clnt_unlock_set(nd->nd_nodename, cl_sk, &xep)) mdclrerror(&xep); nd = nd->nd_next; } } else { for (i = 0; i < MD_MAXSIDES; i++) { /* Skip empty slots */ if (sd->sd_nodes[i][0] == '\0') continue; if ((force & MDFORCE_LOCAL) && strcmp(sd->sd_nodes[i], mynode()) != 0) continue; if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, &xep)) mdclrerror(&xep); } } cl_set_setkey(NULL); } return (rval); } static int info( mdsetname_t *sp, enum mddb_cmd cmd, int print_headers, int print_footers, md_error_t *ep ) { md_replicalist_t *rlp = NULL; md_replicalist_t *rl; md_replica_t *r; int i; char *unk_str = NULL; /* get list of replicas, quit if none */ if (metareplicalist(sp, (MD_BASICNAME_OK | PRINT_FAST), &rlp, ep) < 0) return (-1); else if (rlp == NULL) return (0); if (print_headers) { (void) printf("\t%5.5s\t\t%9.9s\t%11.11s\n", gettext("flags"), gettext("first blk"), gettext("block count")); } unk_str = gettext("unknown"); for (rl = rlp; rl != NULL; rl = rl->rl_next) { r = rl->rl_repp; for (i = 0; i < MDDB_FLAGS_LEN; i++) { if (r->r_flags & (1 << i)) (void) putchar(MDDB_FLAGS_STRING[i]); else (void) putchar(' '); } if ((r->r_blkno == -1) && (r->r_nblk == -1)) { (void) printf("\t%7.7s\t\t%7.7s\t", unk_str, unk_str); } else if (r->r_nblk == -1) { (void) printf("\t%ld\t\t%7.7s\t", r->r_blkno, unk_str); } else { (void) printf("\t%ld\t\t%ld\t", r->r_blkno, r->r_nblk); } (void) printf("\t%s\n", r->r_namep->bname); } metafreereplicalist(rlp); if (cmd == infoshort) return (0); if (!print_footers) return (0); (void) printf(gettext( " r - replica does not have device relocation information\n" " o - replica active prior to last mddb configuration change\n" " u - replica is up to date\n" " l - locator for this replica was read successfully\n" " c - replica's location was in %s\n" " p - replica's location was patched in kernel\n" " m - replica is master, this is replica selected as input\n" " t - tagged data is associated with the replica\n" " W - replica has device write errors\n" " a - replica is active, commits are occurring to this replica\n" " M - replica had problem with master blocks\n" " D - replica had problem with data blocks\n" " F - replica had format problems\n" " S - replica is too small to hold current data base\n" " R - replica had device read errors\n" " B - tagged data associated with the replica is not valid\n"), META_DBCONF); return (0); } int main(int argc, char **argv) { mdsetname_t *sp = NULL; int c; enum mddb_cmd cmd = none; char *sname = MD_LOCAL_NAME; char *cffilename = NULL; char *sysfilename = NULL; int forceflg = FALSE; mdchkopts_t options = 0; md_error_t status = mdnullerror; md_error_t *ep = &status; int error; md_set_desc *sd; int multi_node = 0; /* * Get the locale set up before calling any other routines * with messages to ouput. Just in case we're not in a build * environment, make sure that TEXT_DOMAIN gets set to * something. */ #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (sdssc_bind_library() == SDSSC_OKAY) if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY, &error) == SDSSC_PROXY_DONE) exit(error); /* parse args */ optind = 1; opterr = 1; /* initialize */ if (md_init(argc, argv, 0, 1, ep) != 0) { mde_perror(ep, ""); md_exit(sp, 1); } /* parse args */ optind = 1; opterr = 1; while ((c = getopt(argc, argv, "ac:dfhik:l:ps:?")) != -1) { switch (c) { case 'a': cmd = attach; break; case 'c': break; case 'd': cmd = detach; break; case 'f': forceflg = TRUE; break; case 'h': usage(sp, (char *)0); break; case 'i': cmd = infolong; break; case 'k': sysfilename = optarg; break; case 'l': break; case 'p': cmd = patch; break; case 's': sname = optarg; break; case '?': if (optopt == '?') usage(sp, NULL); /*FALLTHROUGH*/ default: usage(sp, gettext("unknown command")); } } if (cmd == none) cmd = infoshort; /* get set context */ if ((sp = metasetname(sname, ep)) == NULL) { mde_perror(ep, ""); md_exit(sp, 1); } /* print status */ if (cmd == infoshort || cmd == infolong) { if (optind != argc) usage(sp, gettext( "too many arguments")); if (info(sp, cmd, 1, 1, ep)) { mde_perror(ep, ""); md_exit(sp, 1); } if (meta_smf_isonline(meta_smf_getmask(), ep) == 0) { mde_perror(ep, ""); md_exit(sp, 1); } md_exit(sp, 0); } if (meta_check_root(ep) != 0) { mde_perror(ep, ""); md_exit(sp, 1); } if (! metaislocalset(sp)) { if ((sd = metaget_setdesc(sp, ep)) == NULL) { mde_perror(ep, ""); md_exit(sp, 1); } if (MD_MNSET_DESC(sd)) { multi_node = 1; } } /* * Adjust lock for traditional and local diskset. * * A MN diskset does not use the set meta_lock but instead * uses the clnt_lock of rpc.metad and the suspend/resume * feature of the rpc.mdcommd. Can't use set meta_lock since * class 1 messages are grabbing this lock and if this thread * is holding the set meta_lock then no rpc.mdcommd suspend * can occur. */ if ((!multi_node) && (meta_lock(sp, TRUE, ep) != 0)) { mde_perror(ep, ""); md_exit(sp, 1); } /* check for ownership */ if (meta_check_ownership(sp, ep) != 0) { mde_perror(ep, ""); md_exit(sp, 1); } /* snarf MDDB locations */ if (cmd != patch) { if (meta_setup_db_locations(ep) != 0) { if (! mdismddberror(ep, MDE_DB_STALE)) { if (forceflg == FALSE) { mde_perror(ep, ""); md_exit(sp, 1); } options = MDCHK_ALLOW_NODBS; } mdclrerror(ep); } } /* patch MDDB locations */ if (cmd == patch) { if (optind < (argc - 1)) { usage(sp, gettext( "too many arguments to -p")); } if (optind == (argc - 1)) cffilename = argv[optind]; if (metaislocalset(sp)) { if (meta_db_patch(sysfilename, cffilename, 1, ep)) { mde_perror(ep, ""); md_exit(sp, 1); } } } /* add/delete replicas */ if (cmd == attach || cmd == detach) { if (chngdb(sp, cmd, argc, argv, options, ep)) { mde_perror(ep, ""); md_exit(sp, 1); } } md_exit(sp, 0); /*NOTREACHED*/ return (0); }