/* * 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" /* * 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 /* * MH ioctl functions */ #include #include #include #include "meta_runtime.h" #define DEFAULTDEV "/dev/rdsk" /* * default timeout values */ mhd_mhiargs_t defmhiargs = { 1000, /* failfast */ { 6000, 6000, 30000 } /* take ownership */ }; /* RPC timeouts */ static md_timeval32_t tk_own_timeout = { 24 * 60 * 60, 0 }; /* 1 day */ static md_timeval32_t rel_own_timeout = { 24 * 60 * 60, 0 }; /* 1 day */ /* * RPC handle */ typedef struct { char *hostname; CLIENT *clientp; } mhd_handle_t; /* * close RPC connection */ static void close_metamhd( mhd_handle_t *hp ) { assert(hp != NULL); if (hp->hostname != NULL) { Free(hp->hostname); } if (hp->clientp != NULL) { auth_destroy(hp->clientp->cl_auth); clnt_destroy(hp->clientp); } Free(hp); } /* * open RPC connection to rpc.metamhd */ static mhd_handle_t * open_metamhd( char *hostname, md_error_t *ep ) { CLIENT *clientp; mhd_handle_t *hp; /* default to local host */ if ((hostname == NULL) || (*hostname == '\0')) hostname = mynode(); /* open RPC connection */ assert(hostname != NULL); if ((clientp = meta_client_create(hostname, METAMHD, METAMHD_VERSION, "tcp")) == NULL) { clnt_pcreateerror(hostname); (void) mdrpccreateerror(ep, hostname, "metamhd clnt_create"); return (NULL); } else { auth_destroy(clientp->cl_auth); clientp->cl_auth = authsys_create_default(); assert(clientp->cl_auth != NULL); } /* return connection */ hp = Zalloc(sizeof (*hp)); hp->hostname = Strdup(hostname); hp->clientp = clientp; return (hp); } /* * steal and convert mherror_t */ int mhstealerror( mhd_error_t *mhep, md_error_t *ep ) { int rval = -1; /* no error */ if (mhep->errnum == 0) { /* assert(mhep->name == NULL); */ rval = 0; goto out; } /* steal error */ switch (mhep->errnum) { case MHD_E_MAJORITY: (void) mderror(ep, MDE_TAKE_OWN, mhep->name); break; case MHD_E_RESERVED: (void) mderror(ep, MDE_RESERVED, mhep->name); break; default: (void) mdsyserror(ep, mhep->errnum, mhep->name); break; } /* cleanup, return success */ out: if (mhep->name != NULL) Free(mhep->name); (void) memset(mhep, 0, sizeof (*mhep)); return (rval); } /* * should we do MHIOCTLs ? */ static int do_mhioctl() { if (getenv("MD_NOMHIOCTL") != NULL) { (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "NOT doing MH ioctls\n")); (void) fflush(stderr); return (0); } return (1); } /* * take ownership of drives */ int meta_take_own( char *sname, mddrivenamelist_t *dnlp, mhd_mhiargs_t *mhiargsp, int partial_set, md_error_t *ep ) { mddrivenamelist_t *p; uint_t ndev = 0; mhd_tkown_args_t args; mhd_error_t mherror; mhd_set_t *mhsp = &args.set; uint_t i; char *e; mhd_handle_t *hp = NULL; int rval = -1; /* * RFE 4126509. Check the runtime parameters to see if * they're set to disable MHIOCTKOWN ioctl() operations * on the disks. If so, return immediately without * performing the operations. */ if (do_owner_ioctls() == B_FALSE) { return (0); } /* count drives, get set */ for (p = dnlp; (p != NULL); p = p->next) ++ndev; if (ndev == 0) return (0); /* initialize */ (void) memset(&args, 0, sizeof (args)); (void) memset(&mherror, 0, sizeof (mherror)); /* build arguments */ mhsp->setname = Strdup(sname); mhsp->drives.drives_len = ndev; mhsp->drives.drives_val = Calloc(ndev, sizeof (*mhsp->drives.drives_val)); for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) { mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname); } args.timeouts = *mhiargsp; args.ff_mode = MHD_FF_DRIVER; if (((e = getenv("MD_DEBUG")) != NULL) && ((e = strstr(e, "FAILFAST=")) != NULL) && ((e = strchr(e, '=')) != NULL)) { ++e; if (strcmp(e, "NONE") == 0) args.ff_mode = MHD_FF_NONE; else if (strcmp(e, "DRIVER") == 0) args.ff_mode = MHD_FF_DRIVER; else if (strcmp(e, "DEBUG") == 0) args.ff_mode = MHD_FF_DEBUG; else if (strcmp(e, "HALT") == 0) args.ff_mode = MHD_FF_HALT; else if (strcmp(e, "PANIC") == 0) args.ff_mode = MHD_FF_PANIC; } if (partial_set) args.options |= MHD_PARTIAL_SET; if (((e = getenv("MD_DEBUG")) != NULL) && (strstr(e, "NOTHREAD") != NULL)) { args.options |= MHD_SERIAL; } /* open connection */ if ((hp = open_metamhd(NULL, ep)) == NULL) return (-1); clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout); /* take ownership */ if (mhd_tkown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) { (void) mdrpcerror(ep, hp->clientp, hp->hostname, "metamhd tkown"); } else if (mhstealerror(&mherror, ep) == 0) { rval = 0; /* success */ } /* cleanup, return success */ out: xdr_free(xdr_mhd_tkown_args_t, (char *)&args); xdr_free(xdr_mhd_error_t, (char *)&mherror); if (hp != NULL) close_metamhd(hp); return (rval); } /* * take ownership of drives */ int tk_own_bydd( mdsetname_t *sp, md_drive_desc *ddlp, mhd_mhiargs_t *mhiargsp, int partial_set, md_error_t *ep ) { mddrivenamelist_t *dnlp = NULL; mddrivenamelist_t **tailpp = &dnlp; md_drive_desc *p; int rval; /* * Add the drivename struct to the end of the * drivenamelist but keep a pointer to the last * element so that we don't incur the overhead * of traversing the list each time */ for (p = ddlp; (p != NULL); p = p->dd_next) tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp); /* take ownership */ rval = meta_take_own(sp->setname, dnlp, mhiargsp, partial_set, ep); /* cleanup, return success */ metafreedrivenamelist(dnlp); return (rval); } /* * release ownership of drives */ int meta_rel_own( char *sname, mddrivenamelist_t *dnlp, int partial_set, md_error_t *ep ) { mddrivenamelist_t *p; uint_t ndev = 0; mhd_relown_args_t args; mhd_error_t mherror; mhd_set_t *mhsp = &args.set; uint_t i; char *e; mhd_handle_t *hp = NULL; int rval = -1; /* * RFE 4126509. Check the runtime parameters to see if * they're set to disable MHIOCRELEASE and MHIOCENFAILFAST * ioctl() operations on the disks. If so, return * immediately without performing the operations. */ if (do_owner_ioctls() == B_FALSE) { return (0); } /* * if not doing ioctls (HK 98/10/28: the following code tests * an environment variable, and was apparently inserted to * make testing easier.) */ if (! do_mhioctl()) return (0); /* count drives, get set */ for (p = dnlp; (p != NULL); p = p->next) ++ndev; if (ndev == 0) return (0); /* initialize */ (void) memset(&args, 0, sizeof (args)); (void) memset(&mherror, 0, sizeof (mherror)); /* build arguments */ mhsp->setname = Strdup(sname); mhsp->drives.drives_len = ndev; mhsp->drives.drives_val = Calloc(ndev, sizeof (*mhsp->drives.drives_val)); for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) { mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname); } if (partial_set) args.options |= MHD_PARTIAL_SET; if (((e = getenv("MD_DEBUG")) != NULL) && (strstr(e, "NOTHREAD") != NULL)) { args.options |= MHD_SERIAL; } /* open connection */ if ((hp = open_metamhd(NULL, ep)) == NULL) return (-1); clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&rel_own_timeout); /* take ownership */ if (mhd_relown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) { (void) mdrpcerror(ep, hp->clientp, hp->hostname, "metamhd relown"); } else if (mhstealerror(&mherror, ep) == 0) { rval = 0; /* success */ } /* cleanup, return success */ out: xdr_free(xdr_mhd_relown_args_t, (char *)&args); xdr_free(xdr_mhd_error_t, (char *)&mherror); if (hp != NULL) close_metamhd(hp); return (rval); } /* * release ownership of drives */ int rel_own_bydd( mdsetname_t *sp, md_drive_desc *ddlp, int partial_set, md_error_t *ep ) { mddrivenamelist_t *dnlp = NULL; mddrivenamelist_t **tailpp = &dnlp; md_drive_desc *p; int rval; /* * Add the drivename struct to the end of the * drivenamelist but keep a pointer to the last * element so that we don't incur the overhead * of traversing the list each time */ for (p = ddlp; (p != NULL); p = p->dd_next) tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp); /* release ownership */ rval = meta_rel_own(sp->setname, dnlp, partial_set, ep); /* cleanup, return success */ metafreedrivenamelist(dnlp); return (rval); } /* * get status of drives */ int meta_status_own( char *sname, md_disk_status_list_t *dslp, int partial_set, md_error_t *ep ) { md_disk_status_list_t *p; uint_t ndev = 0; mhd_status_args_t args; mhd_status_res_t results; mhd_error_t *mhep = &results.status; mhd_set_t *mhsp = &args.set; uint_t i; char *e; mhd_handle_t *hp = NULL; int rval = -1; /* if not doing ioctls */ if (! do_mhioctl()) return (0); /* count drives, get set */ for (p = dslp; (p != NULL); p = p->next) ++ndev; if (ndev == 0) return (0); /* initialize */ (void) memset(&args, 0, sizeof (args)); (void) memset(&results, 0, sizeof (results)); /* build arguments */ mhsp->setname = Strdup(sname); mhsp->drives.drives_len = ndev; mhsp->drives.drives_val = Calloc(ndev, sizeof (*mhsp->drives.drives_val)); for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) { mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname); } if (partial_set) args.options |= MHD_PARTIAL_SET; if (((e = getenv("MD_DEBUG")) != NULL) && (strstr(e, "NOTHREAD") != NULL)) { args.options |= MHD_SERIAL; } /* open connection */ if ((hp = open_metamhd(NULL, ep)) == NULL) return (-1); clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout); /* get status */ if (mhd_status_1(&args, &results, hp->clientp) != RPC_SUCCESS) { (void) mdrpcerror(ep, hp->clientp, hp->hostname, dgettext(TEXT_DOMAIN, "metamhd status")); goto out; } else if (mhstealerror(mhep, ep) != 0) { goto out; } /* do something with it */ assert(results.results.results_len == ndev); for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) { mhd_drive_status_t *resp = &results.results.results_val[i]; mddrivename_t *dp = p->drivenamep; mhd_error_t mherror; /* make sure we have the right drive */ assert(strcmp(dp->rname, resp->drive) == 0); /* copy status */ if (resp->errnum != 0) { (void) memset(&mherror, 0, sizeof (mherror)); mherror.errnum = resp->errnum; mherror.name = Strdup(resp->drive); (void) mhstealerror(&mherror, &p->status); } } rval = 0; /* success */ /* cleanup, return success */ out: xdr_free(xdr_mhd_status_args_t, (char *)&args); xdr_free(xdr_mhd_status_res_t, (char *)&results); if (hp != NULL) close_metamhd(hp); return (rval); } /* * build disk status list from drivename list */ md_disk_status_list_t * meta_drive_to_disk_status_list( mddrivenamelist_t *dnlp ) { md_disk_status_list_t *head = NULL; md_disk_status_list_t **tailp = &head; mddrivenamelist_t *p; /* copy list */ for (p = dnlp; (p != NULL); p = p->next) { md_disk_status_list_t *dsp; dsp = *tailp = Zalloc(sizeof (*dsp)); tailp = &dsp->next; dsp->drivenamep = p->drivenamep; } /* return list */ return (head); } /* * free disk status list */ void meta_free_disk_status_list( md_disk_status_list_t *dslp ) { md_disk_status_list_t *next = NULL; for (/* void */; (dslp != NULL); dslp = next) { next = dslp->next; mdclrerror(&dslp->status); Free(dslp); } } /* * free drive info list */ void meta_free_drive_info_list( mhd_drive_info_list_t *listp ) { xdr_free(xdr_mhd_drive_info_list_t, (char *)listp); (void) memset(listp, 0, sizeof (*listp)); } /* * sort drive info list */ static int compare_drives( const void *p1, const void *p2 ) { const mhd_drive_info_t *di1 = p1; const mhd_drive_info_t *di2 = p2; const char *n1 = di1->dif_name; const char *n2 = di2->dif_name; uint_t c1 = 0, t1 = 0, d1 = 0, s1 = 0; uint_t c2 = 0, t2 = 0, d2 = 0, s2 = 0; uint_t l, cl; if (n1 == NULL) n1 = ""; if (n2 == NULL) n2 = ""; /* attempt to sort correctly for c0t1d0s0 .vs. c0t18d0s0 */ if ((n1 = strrchr(n1, '/')) == NULL) goto u; n1 += (n1[1] != 'c') ? 2 : 1; cl = strlen(n1); if ((sscanf(n1, "c%ut%ud%us%u%n", &c1, &t1, &d1, &s1, &l) != 4 && sscanf(n1, "c%ud%us%u%n", &c1, &d1, &s1, &l) != 3 && sscanf(n1, "c%ut%ud%u%n", &c1, &t1, &d1, &l) != 3 && sscanf(n1, "c%ud%u%n", &c1, &d1, &l) != 2) || (l != cl)) goto u; if ((n2 = strrchr(n2, '/')) == NULL) goto u; n2 += (n2[1] != 'c') ? 2 : 1; cl = strlen(n2); if ((sscanf(n2, "c%ut%ud%us%u%n", &c2, &t2, &d2, &s2, &l) != 4 && sscanf(n2, "c%ud%us%u%n", &c2, &d2, &s2, &l) != 3 && sscanf(n2, "c%ut%ud%u%n", &c2, &t2, &d2, &l) != 3 && sscanf(n2, "c%ud%u%n", &c2, &d2, &l) != 2) || (l != cl)) goto u; if (c1 != c2) return ((c1 > c2) ? 1 : -1); if (t1 != t2) return ((t1 > t2) ? 1 : -1); if (d1 != d2) return ((d1 > d2) ? 1 : -1); if (s1 != s2) return ((s1 > s2) ? 1 : -1); return (0); u: return (strcmp(di1->dif_name, di2->dif_name)); } static void sort_drives( mhd_drive_info_list_t *listp ) { qsort(listp->mhd_drive_info_list_t_val, listp->mhd_drive_info_list_t_len, sizeof (*listp->mhd_drive_info_list_t_val), compare_drives); } /* * return list of all drives */ int meta_list_drives( char *hostname, char *path, mhd_did_flags_t flags, mhd_drive_info_list_t *listp, md_error_t *ep ) { mhd_list_args_t args; mhd_list_res_t results; mhd_error_t *mhep = &results.status; mhd_handle_t *hp = NULL; int rval = -1; /* if not doing ioctls */ if (! do_mhioctl()) return (0); /* initialize */ (void) memset(&args, 0, sizeof (args)); (void) memset(&results, 0, sizeof (results)); /* build arguments */ if (path == NULL) path = getenv("MD_DRIVE_ROOT"); if ((path != NULL) && (*path != '\0')) args.path = Strdup(path); args.flags = flags; /* open connection */ if ((hp = open_metamhd(hostname, ep)) == NULL) return (-1); clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout); /* get list */ if (mhd_list_1(&args, &results, hp->clientp) != RPC_SUCCESS) { (void) mdrpcerror(ep, hp->clientp, hp->hostname, dgettext(TEXT_DOMAIN, "metamhd list")); goto out; } else if (mhstealerror(mhep, ep) != 0) { goto out; } /* sort list */ sort_drives(&results.results); /* steal list */ *listp = results.results; results.results.mhd_drive_info_list_t_len = 0; results.results.mhd_drive_info_list_t_val = NULL; rval = listp->mhd_drive_info_list_t_len; /* success */ /* cleanup, return success */ out: xdr_free(xdr_mhd_list_args_t, (char *)&args); xdr_free(xdr_mhd_list_res_t, (char *)&results); if (hp != NULL) close_metamhd(hp); return (rval); } static void load_paths_to_metamhd() { FILE *cfp; /* config file pointer */ char buf[BUFSIZ], *p, *x; mhd_drive_info_list_t list; md_error_t ep; mhd_did_flags_t flags = MHD_DID_SERIAL; if ((cfp = fopen(METADEVPATH, "r")) != NULL) { /* * Read each line from the file. Lines will be either * comments or path names to pass to rpc.metamhd. If * path names check to see if their a colon seperate * list of names which must be processed one at a time. */ while (fgets(buf, BUFSIZ, cfp) != NULL) { if (buf[0] == '#') { /* * Ignore comment lines */ continue; } else if (strchr(buf, ':') != NULL) { p = buf; while ((x = strchr(p, ':')) != NULL) { *x = '\0'; (void) memset(&ep, '\0', sizeof (ep)); (void) meta_list_drives(NULL, p, 0, &list, &ep); meta_free_drive_info_list(&list); p = x + 1; } /* * We won't pick up the last path name * because the line ends with a newline * not a ':'. So p will still point to * a valid path in this case. Copy the * data that p points to to the beginning * of the buf and let the default case * handle this buffer. * NOTE: * If the file does end with a ":\n", p at * will point to the newline. The default * cause would then set the newline to a * NULL which is okay because meta_list_drives * interprets a null string as /dev/rdsk. */ (void) memcpy(buf, p, strlen(p)); } /* * Remove any newlines in the buffer. */ if ((p = strchr(buf, '\n')) != NULL) *p = '\0'; (void) memset(&ep, '\0', sizeof (ep)); (void) memset(&list, '\0', sizeof (list)); (void) meta_list_drives(NULL, buf, flags, &list, &ep); meta_free_drive_info_list(&list); } (void) fclose(cfp); } } /* * build list of all drives in set */ /*ARGSUSED*/ int meta_get_drive_names( mdsetname_t *sp, mddrivenamelist_t **dnlpp, int options, md_error_t *ep ) { mhd_did_flags_t flags = MHD_DID_SERIAL; mhd_drive_info_list_t list; mhd_drive_info_t *mp; uint_t i; unsigned cnt = 0; int rval = -1; mddrivenamelist_t **tailpp = dnlpp; /* must have a set */ assert(sp != NULL); load_paths_to_metamhd(); (void) memset(&list, 0, sizeof (list)); if ((meta_list_drives(NULL, NULL, flags, &list, ep)) < 0) return (-1); /* find drives in set */ for (i = 0; (i < list.mhd_drive_info_list_t_len); ++i) { mddrivename_t *dnp; mdname_t *np; mp = &list.mhd_drive_info_list_t_val[i]; if (mp->dif_id.did_flags & MHD_DID_DUPLICATE) continue; /* quietly skip drives which don't conform */ if ((dnp = metadrivename(&sp, mp->dif_name, ep)) == NULL) { mdclrerror(ep); continue; } /* check in set */ if ((np = metaslicename(dnp, MD_SLICE0, ep)) == NULL) goto out; if (meta_check_inset(sp, np, ep) != 0) { mdclrerror(ep); continue; } /* * Add the drivename struct to the end of the * drivenamelist but keep a pointer to the last * element so that we don't incur the overhead * of traversing the list each time */ tailpp = meta_drivenamelist_append_wrapper(tailpp, dnp); ++cnt; } rval = cnt; /* cleanup, return error */ out: meta_free_drive_info_list(&list); return (rval); }