metahs.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* hotspare maintenance
*/
#include <meta.h>
#include <sdssc.h>
/*
* possible actions
*/
enum metahs_op {
NONE,
ADD_A_HS,
DELETE_A_HS,
ENABLE_A_HS,
REPLACE_A_HS,
STATUS_A_HSP
};
/*
* report status of a hotspare pool
*/
static int
status_hsp(
mdsetname_t *sp,
mdhspname_t *hspnp,
md_error_t *ep
)
{
mdprtopts_t options = (PRINT_HEADER | PRINT_SUBDEVS | PRINT_DEVID);
mdnamelist_t *nlp = NULL;
/* must have set */
assert(sp != NULL);
assert(sp->setno == HSP_SET(hspnp->hsp));
/* print status */
if (meta_hsp_print(sp, hspnp, &nlp, NULL, stdout, options, ep) != 0)
return (-1);
/* return success */
return (0);
}
/*
* print usage message
*/
static void
usage(
mdsetname_t *sp,
int eval
)
{
(void) fprintf(stderr, gettext("\
usage: %s [-s setname] -a hot_spare_pool [component...]\n\
%s [-s setname] -a \"all\" component...\n\
%s [-s setname] -d hot_spare_pool [component...]\n\
%s [-s setname] -d \"all\" component...\n\
%s [-s setname] -e component...\n\
%s [-s setname] -r hot_spare_pool component_old component_new\n\
%s [-s setname] -r \"all\" component_old component_new\n\
%s [-s setname] -i [hot_spare_pool...]\n"),
myname, myname, myname, myname, myname, myname, myname, myname);
md_exit(sp, eval);
}
/*
* check for "all"
*/
static int
is_all(char *s)
{
if ((strcoll(s, gettext("all")) == 0) ||
(strcoll(s, gettext("ALL")) == 0))
return (1);
return (0);
}
/*
* parse args and add hotspares
*/
static int
add_hotspares(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
mdhspnamelist_t *hspnlp = NULL;
mdnamelist_t *nlp = NULL;
int cnt;
mdhspnamelist_t *p;
int rval = -1;
/* get hotspare pool name(s) */
if (argc < 1)
usage(*spp, 1);
if ((argc > 1) && is_all(argv[0])) {
if ((*spp == NULL) &&
((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) {
return (-1);
}
/* check for ownership */
assert(*spp != NULL);
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
return (-1);
} else if (cnt == 0) {
return (mderror(ep, MDE_NO_HSPS, NULL));
}
} else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
ep)) < 0) {
return (-1);
}
assert(cnt > 0);
--argc, ++argv;
assert(*spp != NULL);
/* grab set lock */
if (meta_lock(*spp, TRUE, ep))
return (-1);
/* check for ownership */
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
/* get hotspares */
if (metanamelist(spp, &nlp, argc, argv, ep) < 0) {
goto out;
}
/* add hotspares */
for (p = hspnlp; (p != NULL); p = p->next) {
mdhspname_t *hspnp = p->hspnamep;
if (meta_hs_add(*spp, hspnp, nlp, options, ep) != 0)
goto out;
}
rval = 0;
/* cleanup, return success */
out:
if (hspnlp != NULL)
metafreehspnamelist(hspnlp);
if (nlp != NULL)
metafreenamelist(nlp);
return (rval);
}
/*
* parse args and delete hotspares
*/
static int
delete_hotspares(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
mdhspnamelist_t *hspnlp = NULL;
mdnamelist_t *nlp = NULL;
int cnt;
mdhspnamelist_t *p;
int rval = -1;
/* get hotspare pool name(s) */
if (argc < 1)
usage(*spp, 1);
if ((argc > 1) && is_all(argv[0])) {
if ((*spp == NULL) &&
((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) {
return (-1);
}
/* check for ownership */
assert(*spp != NULL);
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
return (-1);
} else if (cnt == 0) {
return (mderror(ep, MDE_NO_HSPS, NULL));
}
} else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
ep)) < 0) {
return (-1);
}
assert(cnt > 0);
--argc, ++argv;
assert(*spp != NULL);
/* grab set lock */
if (meta_lock(*spp, TRUE, ep))
return (-1);
/* check for ownership */
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
/* get hotspares */
if (metanamelist(spp, &nlp, argc, argv, ep) < 0) {
goto out;
}
/* delete hotspares */
cnt = 0;
for (p = hspnlp; (p != NULL); p = p->next) {
mdhspname_t *hspnp = p->hspnamep;
if (meta_hs_delete(*spp, hspnp, nlp, options, ep) != 0) {
if (mdisdeverror(ep, MDE_INVAL_HS))
mdclrerror(ep);
else
goto out;
} else {
++cnt;
}
}
/* make sure we got some */
if ((nlp != NULL) && (cnt == 0)) {
(void) mddeverror(ep, MDE_INVAL_HS, nlp->namep->dev,
nlp->namep->cname);
goto out;
}
/* success */
rval = 0;
/* cleanup, return success */
out:
if (hspnlp != NULL)
metafreehspnamelist(hspnlp);
if (nlp != NULL)
metafreenamelist(nlp);
return (rval);
}
/*
* parse args and enable hotspares
*/
static int
enable_hotspares(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
mdnamelist_t *nlp = NULL;
int rval = -1;
/* enable hotspares */
if (argc < 1)
usage(*spp, 1);
/* get list of hotspares */
if (metanamelist(spp, &nlp, argc, argv, ep) < 0)
goto out;
assert(nlp != NULL);
assert(*spp != NULL);
/* grab set lock */
if (meta_lock(*spp, TRUE, ep))
return (-1);
/* check for ownership */
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
/* enable hotspares */
rval = meta_hs_enable(*spp, nlp, options, ep);
/* cleanup, return success */
out:
metafreenamelist(nlp);
return (rval);
}
/*
* parse args and replace hotspares
*/
static int
replace_hotspares(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
mdhspnamelist_t *hspnlp = NULL;
int cnt;
mdname_t *oldnp;
mdname_t *newnp;
mdhspnamelist_t *p;
int rval = -1;
/* get hotspare pool name(s) */
if (argc != 3)
usage(*spp, 1);
if (is_all(argv[0])) {
if ((*spp == NULL) &&
((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) {
return (-1);
}
/* check for ownership */
assert(*spp != NULL);
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
return (-1);
} else if (cnt == 0) {
return (mderror(ep, MDE_NO_HSPS, NULL));
}
} else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
ep)) < 0) {
return (-1);
}
assert(cnt > 0);
assert(*spp != NULL);
/* grab set lock */
if (meta_lock(*spp, TRUE, ep))
return (-1);
/* check for ownership */
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
/* get old component */
if ((oldnp = metaname(spp, argv[1], ep)) == NULL)
goto out;
/* get new component */
if ((newnp = metaname(spp, argv[2], ep)) == NULL)
goto out;
/* replace hotspares */
cnt = 0;
for (p = hspnlp; (p != NULL); p = p->next) {
mdhspname_t *hspnp = p->hspnamep;
if (meta_hs_replace(*spp, hspnp, oldnp, newnp, options, ep)
!= 0) {
if (mdisdeverror(ep, MDE_INVAL_HS))
mdclrerror(ep);
else
goto out;
} else {
++cnt;
}
}
/* make sure we got some */
if (cnt == 0) {
(void) mddeverror(ep, MDE_INVAL_HS, oldnp->dev, oldnp->cname);
goto out;
}
/* success */
rval = 0;
/* cleanup, return success */
out:
if (hspnlp != NULL)
metafreehspnamelist(hspnlp);
return (rval);
}
/*
* print_hsp_devid will collect the information for each underlying
* physical device for all the hotspare pools and print out the
* device relocation information
* INPUT:
* mdsetname_t *sp set the hsp is in
* mdhspnamelist_t *hspnlp list of hsp
* FILE *fp where to print to
* md_error_t *ep errors
* RETURN:
* 0 SUCCESS
* -1 ERROR
*/
static int
print_hsp_devid(
mdsetname_t *sp,
mdhspnamelist_t *hspnlp,
FILE *fp,
md_error_t *ep
)
{
mddevid_t *ldevidp = NULL;
int retval = 0;
mdhspnamelist_t *p;
mddevid_t *nextp;
/* for all hotspare pools */
for (p = hspnlp; (p != NULL); p = p->next) {
mdhspname_t *hspnp = p->hspnamep;
uint_t hsi;
/* for all hotspares within a pool */
for (hsi = 0;
hsi < hspnp->unitp->hotspares.hotspares_len; hsi++) {
mdname_t *hsname;
hsname =
hspnp->unitp->hotspares.hotspares_val[hsi].hsnamep;
meta_create_non_dup_list(hsname, &ldevidp);
}
}
retval = meta_print_devid(sp, fp, ldevidp, ep);
/* cleanup */
for (nextp = ldevidp; nextp != NULL; ldevidp = nextp) {
Free(ldevidp->ctdname);
nextp = ldevidp->next;
Free(ldevidp);
}
return (retval);
}
/*
* parse args and status hotspares
*/
static int
status_hotspares(
mdsetname_t **spp,
int argc,
char *argv[],
md_error_t *ep
)
{
mdhspnamelist_t *hspnlp = NULL;
int cnt;
mdhspnamelist_t *p;
int rval = -1;
/* get hotspare pool name(s) */
if (argc == 0) {
if ((*spp == NULL) &&
((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)) {
return (-1);
}
/* check for ownership */
assert(*spp != NULL);
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
return (-1);
} else if (cnt == 0) {
return (mderror(ep, MDE_NO_HSPS, NULL));
}
} else if ((cnt = metahspnamelist(spp, &hspnlp, argc, argv, ep)) < 0) {
return (-1);
}
assert(cnt > 0);
/* check for ownership */
assert(*spp != NULL);
if (meta_check_ownership(*spp, ep) != 0)
return (-1);
/* status hotspare pools */
for (p = hspnlp; (p != NULL); p = p->next) {
mdhspname_t *hspnp = p->hspnamep;
if (status_hsp(*spp, hspnp, ep) != 0)
goto out;
}
if (print_hsp_devid(*spp, hspnlp, stdout, ep) == 0) {
rval = 0;
}
/* cleanup, return success */
out:
if (hspnlp != NULL)
metafreehspnamelist(hspnlp);
return (rval);
}
/*
* parse args and doit
*/
int
main(
int argc,
char **argv
)
{
char *sname = MD_LOCAL_NAME;
mdsetname_t *sp = NULL;
enum metahs_op which_op = NONE;
mdcmdopts_t options = (MDCMD_PRINT | MDCMD_DOIT);
int c;
md_error_t status = mdnullerror;
md_error_t *ep = &status;
int error;
bool_t called_thru_rpc = FALSE;
char *cp;
/*
* 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 ((cp = strstr(argv[0], ".rpc_call")) == NULL) {
if (sdssc_bind_library() == SDSSC_OKAY)
if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
&error) == SDSSC_PROXY_DONE)
exit(error);
} else {
*cp = '\0'; /* cut off ".rpc_call" */
called_thru_rpc = TRUE;
}
/* 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, "hs:aderin?")) != -1) {
switch (c) {
case 'h':
usage(sp, 0);
break;
case 's':
sname = optarg;
break;
case 'a':
if (which_op != NONE)
usage(sp, 1);
which_op = ADD_A_HS;
break;
case 'd':
if (which_op != NONE)
usage(sp, 1);
which_op = DELETE_A_HS;
break;
case 'e':
if (which_op != NONE)
usage(sp, 1);
which_op = ENABLE_A_HS;
break;
case 'r':
if (which_op != NONE)
usage(sp, 1);
which_op = REPLACE_A_HS;
break;
case 'i':
if (which_op != NONE)
usage(sp, 1);
which_op = STATUS_A_HSP;
break;
case 'n':
if (called_thru_rpc == TRUE) {
options &= ~MDCMD_DOIT;
} else {
usage(sp, 1);
}
break;
case '?':
if (optopt == '?')
usage(sp, 0);
/*FALLTHROUGH*/
default:
usage(sp, 1);
break;
}
}
/* get set context */
if ((sp = metasetname(sname, ep)) == NULL) {
mde_perror(ep, "");
md_exit(sp, 1);
}
/*
* Send the command to all nodes if the -s argument refers to a MN
* set or the next argument refers to MN set hotspare name ( argc
* greater than optind if there is a next argument)
*/
if ((called_thru_rpc == FALSE) &&
(meta_is_mn_set(sp, ep) || ((argc > optind) &&
meta_is_mn_name(&sp, argv[optind], ep)))) {
int i;
int newargc;
int result;
char **newargv;
/*
* If we are dealing with a MN set and we were not
* called thru an rpc call, we are just to send this
* command string to the master of the set and let it
* deal with it.
* First we send out a dryrun version of this command.
* If that returns success, we know it succeeded on all
* nodes and it is safe to do the real command now.
*/
newargv = calloc(argc+1, sizeof (char *));
newargv[0] = "metahs";
newargv[1] = "-n"; /* always do "-n" first */
newargc = 2;
for (i = 1; i < argc; i++, newargc++)
newargv[newargc] = argv[i];
result = meta_mn_send_command(sp, newargc, newargv,
MD_DISP_STDERR | MD_DRYRUN, NO_CONTEXT_STRING, ep);
/* If we found a problem don't do it for real */
if (result != 0) {
md_exit(sp, result);
}
/*
* Do it for real now. Remove "-n" from the arguments and
* MD_DRYRUN from the flags. If this fails the master must panic
* as the mddbs may be inconsistent.
*/
newargv[1] = ""; /* this was "-n" before */
result = meta_mn_send_command(sp, newargc, newargv,
MD_DISP_STDERR | MD_RETRY_BUSY | MD_PANIC_WHEN_INCONSISTENT,
NO_CONTEXT_STRING, ep);
free(newargv);
/* No further action required */
md_exit(sp, result);
}
argc -= optind;
argv += optind;
if (which_op == NONE)
usage(sp, 1);
if (which_op == STATUS_A_HSP) {
if (status_hotspares(&sp, argc, argv, 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);
}
/* dispatch */
switch (which_op) {
case ADD_A_HS:
if (add_hotspares(&sp, argc, argv, options, ep) != 0) {
mde_perror(ep, "");
md_exit(sp, 1);
}
break;
case DELETE_A_HS:
if (delete_hotspares(&sp, argc, argv, options, ep) != 0) {
mde_perror(ep, "");
md_exit(sp, 1);
}
break;
case ENABLE_A_HS:
if (enable_hotspares(&sp, argc, argv, options, ep) != 0) {
mde_perror(ep, "");
md_exit(sp, 1);
}
break;
case REPLACE_A_HS:
if (replace_hotspares(&sp, argc, argv, options, ep) != 0) {
mde_perror(ep, "");
md_exit(sp, 1);
}
break;
default:
assert(0);
break;
}
/* update md.cf */
out:
if (meta_update_md_cf(sp, ep) != 0) {
mde_perror(ep, "");
md_exit(sp, 1);
}
md_exit(sp, 0);
/*NOTREACHED*/
return (0);
}