/* * 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 /* * Mediator functions */ #include #include #include #include /* * There are too many external factors that affect the timing of the * operations, so we set the timeout to a very large value, in this * case 1 day, which should handle HW timeouts, large configurations, * and other potential delays. */ #define CL_LONG_TMO 86400L /* 1 day */ #define CL_MEDIUM_TMO 3600L /* 1 hour */ #define CL_SHORT_TMO 600L /* 10 minutes */ #define CL_DEF_TMO 10L /* 10 seconds */ static md_timeval32_t def_rpcb_timeout = { MD_CLNT_CREATE_TOUT, 0 }; /* * RPC handle */ typedef struct { char *hostname; CLIENT *clntp; } med_handle_t; /* * Data to be sent from med_clnt_create_timed to med_create_helper via * meta_client_create_retry. */ typedef struct { rpcprog_t mcd_program; /* RPC program designation */ rpcvers_t mcd_version; /* RPC version */ char *mcd_nettype; /* Type of network to use for RPC */ } med_create_data_t; /* * Perform the work of actually doing the clnt_create for * meta_client_create_retry. */ static CLIENT * med_create_helper(char *hostname, void *private, struct timeval *time_out) { med_create_data_t *cd = (med_create_data_t *)private; return (clnt_create_timed(hostname, cd->mcd_program, cd->mcd_version, cd->mcd_nettype, time_out)); } static CLIENT *med_clnt_create_timed( char *hostname, const ulong_t prog, const ulong_t vers, char *nettype, const md_timeval32_t *tp ) { med_create_data_t cd; /* Create data. */ cd.mcd_program = prog; cd.mcd_version = vers; cd.mcd_nettype = nettype; return (meta_client_create_retry(hostname, med_create_helper, (void *)&cd, (time_t)tp->tv_sec, NULL)); } /* * Set the timeout value for this client handle. */ static int cl_sto_medd( CLIENT *clntp, char *hostname, long time_out, md_error_t *ep ) { md_timeval32_t nto; (void) memset(&nto, '\0', sizeof (nto)); nto.tv_sec = time_out; if (clnt_control(clntp, CLSET_TIMEOUT, (char *)&nto) != TRUE) return (mdrpcerror(ep, clntp, hostname, dgettext(TEXT_DOMAIN, "metad client set timeout"))); return (0); } /* * close RPC connection */ static void close_medd( med_handle_t *hp ) { assert(hp != NULL); if (hp->hostname != NULL) { Free(hp->hostname); } if (hp->clntp != NULL) { auth_destroy(hp->clntp->cl_auth); clnt_destroy(hp->clntp); } Free(hp); } /* * open RPC connection to rpc.medd */ static med_handle_t * open_medd( char *hostname, long time_out, md_error_t *ep ) { CLIENT *clntp; med_handle_t *hp; /* default to local host */ if ((hostname == NULL) || (*hostname == '\0')) hostname = mynode(); /* open RPC connection */ assert(hostname != NULL); if ((clntp = med_clnt_create_timed(hostname, MED_PROG, MED_VERS, "tcp", &def_rpcb_timeout)) == NULL) { if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) clnt_pcreateerror(hostname); (void) mdrpccreateerror(ep, hostname, "medd med_clnt_create_timed"); return (NULL); } else { auth_destroy(clntp->cl_auth); clntp->cl_auth = authsys_create_default(); assert(clntp->cl_auth != NULL); } if (cl_sto_medd(clntp, hostname, time_out, ep) != 0) return (NULL); /* return connection */ hp = Zalloc(sizeof (*hp)); hp->hostname = Strdup(hostname); hp->clntp = clntp; return (hp); } /* * steal and convert med_err_t */ int meddstealerror( md_error_t *ep, med_err_t *medep ) { char buf[BUFSIZ]; char *p = buf; size_t psize = BUFSIZ; char *emsg; int rval = -1; /* no error */ if (medep->med_errno == 0) { /* assert(medep->name == NULL); */ rval = 0; goto out; } /* steal error */ if ((medep->med_node != NULL) && (medep->med_node[0] != '\0')) { (void) snprintf(p, psize, "%s: ", medep->med_node); p = &buf[strlen(buf)]; psize = buf + BUFSIZ - p; } if ((medep->med_misc != NULL) && (medep->med_misc[0] != '\0')) { (void) snprintf(p, psize, "%s: ", medep->med_misc); p = &buf[strlen(buf)]; psize = buf + BUFSIZ - p; } if (medep->med_errno < 0) { if ((emsg = med_errnum_to_str(medep->med_errno)) != NULL) (void) snprintf(p, psize, "%s", emsg); else (void) snprintf(p, psize, dgettext(TEXT_DOMAIN, "unknown mediator errno %d\n"), medep->med_errno); } else { if ((emsg = strerror(medep->med_errno)) != NULL) (void) snprintf(p, psize, "%s", emsg); else (void) snprintf(p, psize, dgettext(TEXT_DOMAIN, "errno %d out of range"), medep->med_errno); } (void) mderror(ep, MDE_MED_ERROR, buf); /* cleanup, return success */ out: if (medep->med_node != NULL) Free(medep->med_node); if (medep->med_misc != NULL) Free(medep->med_misc); (void) memset(medep, 0, sizeof (*medep)); return (rval); } static med_handle_t * open_medd_wrap( md_h_t *mdhp, long time_out, md_error_t *ep ) { med_handle_t *hp = NULL; int i; char *hnm; assert(mdhp && mdhp->a_cnt > 0); /* Loop through the hosts listed */ i = min(mdhp->a_cnt, MAX_HOST_ADDRS) - 1; for (; i >= 0; i--) { hnm = mdhp->a_nm[i]; if ((hp = open_medd(hnm, time_out, ep)) == NULL) { if (mdanyrpcerror(ep) && i != 0) { mdclrerror(ep); continue; } } return (hp); } rpc_createerr.cf_stat = RPC_CANTSEND; rpc_createerr.cf_error.re_status = 0; (void) mdrpccreateerror(ep, mdhp->a_nm[0], dgettext(TEXT_DOMAIN, "medd open wrap")); return (NULL); } static int setup_med_transtab(md_error_t *ep) { mddb_med_t_parm_t *tp = NULL; struct stat statb; int i; size_t alloc_size = 0; int err = 0; if ((tp = Zalloc(sizeof (mddb_med_t_parm_t))) == NULL) return (mdsyserror(ep, ENOMEM, "setup_med_transtab")); if (metaioctl(MD_MED_GET_TLEN, tp, &tp->med_tp_mde, NULL) != 0) { err = mdstealerror(ep, &tp->med_tp_mde); goto out; } if (tp->med_tp_setup == 1) goto out; alloc_size = (sizeof (mddb_med_t_parm_t) - sizeof (mddb_med_t_ent_t)) + (sizeof (mddb_med_t_ent_t) * tp->med_tp_nents); if ((tp = Realloc(tp, alloc_size)) == NULL) { err = mdsyserror(ep, ENOMEM, "setup_med_transtab"); goto out; } if (metaioctl(MD_MED_GET_T, tp, &tp->med_tp_mde, NULL) != 0) { err = mdstealerror(ep, &tp->med_tp_mde); goto out; } for (i = 0; i < tp->med_tp_nents; i++) { if (meta_stat(tp->med_tp_ents[i].med_te_nm, &statb) == -1) { md_perror("setup_med_transtab(): stat():"); tp->med_tp_ents[i].med_te_dev = NODEV64; } else { tp->med_tp_ents[i].med_te_dev = meta_expldev(statb.st_rdev); } } if (metaioctl(MD_MED_SET_T, tp, &tp->med_tp_mde, NULL) != 0) err = mdstealerror(ep, &tp->med_tp_mde); out: Free(tp); return (err); } /* * Externals */ /* * NULLPROC - just returns a response */ int clnt_med_null( char *hostname, md_error_t *ep ) { med_handle_t *hp; med_err_t res; /* initialize */ mdclrerror(ep); /* do it */ if ((hp = open_medd(hostname, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_null_1(NULL, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hostname, dgettext(TEXT_DOMAIN, "medd nullproc")); close_medd(hp); xdr_free(xdr_med_err_t, (char *)&res); if (! mdisok(ep)) return (-1); return (0); } /* * Update the mediator information on the mediator. * *** This is not normally called from user code, the kernel does this! *** */ int clnt_med_upd_data( md_h_t *mdhp, mdsetname_t *sp, med_data_t *meddp, md_error_t *ep ) { med_handle_t *hp; med_upd_data_args_t args; med_err_t res; md_set_desc *sd; /* initialize */ mdclrerror(ep); (void) memset(&args, 0, sizeof (args)); (void) memset(&res, 0, sizeof (res)); /* build args */ if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) /* * In the MN diskset, use a generic nodename, multiowner, as * the node initiating the RPC request. This allows * any node to access mediator information. * * MN diskset reconfig cycle forces consistent * view of set/node/drive/mediator information across all nodes * in the MN diskset. This allows the relaxation of * node name checking in rpc.metamedd for MN disksets. * * In the traditional diskset, only a calling node that is * in the mediator record's diskset nodelist can access * mediator data. */ args.med.med_caller = Strdup(MED_MN_CALLER); else args.med.med_caller = Strdup(mynode()); args.med.med_setname = Strdup(sp->setname); args.med.med_setno = sp->setno; args.med_data = *meddp; /* do it */ if ((hp = open_medd_wrap(mdhp, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_upd_data_1(&args, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hp->hostname, dgettext(TEXT_DOMAIN, "medd update data")); else (void) meddstealerror(ep, &res); close_medd(hp); xdr_free(xdr_med_upd_data_args_t, (char *)&args); xdr_free(xdr_med_err_t, (char *)&res); if (! mdisok(ep)) return (-1); return (0); } /* * Get the mediator data for this client from the mediator */ int clnt_med_get_data( md_h_t *mdhp, mdsetname_t *sp, med_data_t *meddp, md_error_t *ep ) { med_handle_t *hp; med_args_t args; med_get_data_res_t res; int rval = -1; md_set_desc *sd; /* initialize */ mdclrerror(ep); (void) memset(&args, 0, sizeof (args)); (void) memset(&res, 0, sizeof (res)); /* build args */ if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) /* * In the MN diskset, use a generic nodename, multiowner, as * the node initiating the RPC request. This allows * any node to access mediator information. * * MN diskset reconfig cycle forces consistent * view of set/node/drive/mediator information across all nodes * in the MN diskset. This allows the relaxation of * node name checking in rpc.metamedd for MN disksets. * * In the traditional diskset, only a calling node that is * in the mediator record's diskset nodelist can access * mediator data. */ args.med.med_caller = Strdup(MED_MN_CALLER); else args.med.med_caller = Strdup(mynode()); args.med.med_setname = Strdup(sp->setname); args.med.med_setno = sp->setno; /* do it */ if ((hp = open_medd_wrap(mdhp, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_get_data_1(&args, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hp->hostname, dgettext(TEXT_DOMAIN, "medd get data")); else (void) meddstealerror(ep, &res.med_status); close_medd(hp); if (mdisok(ep)) { /* do something with the results */ (void) memmove(meddp, &res.med_data, sizeof (med_data_t)); rval = 0; } xdr_free(xdr_med_args_t, (char *)&args); xdr_free(xdr_med_get_data_res_t, (char *)&res); return (rval); } /* * Update the mediator record on the mediator. */ int clnt_med_upd_rec( md_h_t *mdhp, mdsetname_t *sp, med_rec_t *medrp, md_error_t *ep ) { med_handle_t *hp; med_upd_rec_args_t args; med_err_t res; md_set_desc *sd; /* initialize */ mdclrerror(ep); (void) memset(&args, 0, sizeof (args)); (void) memset(&res, 0, sizeof (res)); /* build args */ if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) /* * In the MN diskset, use a generic nodename, multiowner, as * the node initiating the RPC request. This allows * any node to access mediator information. * * MN diskset reconfig cycle forces consistent * view of set/node/drive/mediator information across all nodes * in the MN diskset. This allows the relaxation of * node name checking in rpc.metamedd for MN disksets. * * In the traditional diskset, only a calling node that is * in the mediator record's diskset nodelist can access * mediator data. */ args.med.med_caller = Strdup(MED_MN_CALLER); else args.med.med_caller = Strdup(mynode()); args.med.med_setname = Strdup(sp->setname); args.med.med_setno = sp->setno; args.med_flags = 0; args.med_rec = *medrp; /* structure assignment */ /* do it */ if ((hp = open_medd_wrap(mdhp, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_upd_rec_1(&args, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hp->hostname, dgettext(TEXT_DOMAIN, "medd update record")); else (void) meddstealerror(ep, &res); close_medd(hp); xdr_free(xdr_med_upd_rec_args_t, (char *)&args); xdr_free(xdr_med_err_t, (char *)&res); if (! mdisok(ep)) return (-1); return (0); } /* * Get the mediator record for this client from the mediator */ int clnt_med_get_rec( md_h_t *mdhp, mdsetname_t *sp, med_rec_t *medrp, md_error_t *ep ) { med_handle_t *hp; med_args_t args; med_get_rec_res_t res; int rval = -1; md_set_desc *sd; /* initialize */ mdclrerror(ep); (void) memset(&args, 0, sizeof (args)); (void) memset(&res, 0, sizeof (res)); /* build args */ if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) /* * In the MN diskset, use a generic nodename, multiowner, as * the node initiating the RPC request. This allows * any node to access mediator information. * * MN diskset reconfig cycle forces consistent * view of set/node/drive/mediator information across all nodes * in the MN diskset. This allows the relaxation of * node name checking in rpc.metamedd for MN disksets. * * In the traditional diskset, only a calling node that is * in the mediator record's diskset nodelist can access * mediator data. */ args.med.med_caller = Strdup(MED_MN_CALLER); else args.med.med_caller = Strdup(mynode()); args.med.med_setname = Strdup(sp->setname); args.med.med_setno = sp->setno; /* do it */ if ((hp = open_medd_wrap(mdhp, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_get_rec_1(&args, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hp->hostname, dgettext(TEXT_DOMAIN, "medd get record")); else (void) meddstealerror(ep, &res.med_status); close_medd(hp); if (mdisok(ep)) { /* do something with the results */ (void) memmove(medrp, &res.med_rec, sizeof (med_rec_t)); rval = 0; } xdr_free(xdr_med_args_t, (char *)&args); xdr_free(xdr_med_get_rec_res_t, (char *)&res); return (rval); } /* * Get the name of the host from the mediator daemon. */ int clnt_med_hostname( char *hostname, char **ret_hostname, md_error_t *ep ) { med_handle_t *hp; med_hnm_res_t res; int rval = -1; /* initialize */ mdclrerror(ep); (void) memset(&res, 0, sizeof (res)); /* No args */ /* do it */ if ((hp = open_medd(hostname, CL_DEF_TMO, ep)) == NULL) return (-1); if (med_hostname_1(NULL, &res, hp->clntp) != RPC_SUCCESS) (void) mdrpcerror(ep, hp->clntp, hostname, dgettext(TEXT_DOMAIN, "medd hostname")); else (void) meddstealerror(ep, &res.med_status); close_medd(hp); if (mdisok(ep)) { /* do something with the results */ rval = 0; if (ret_hostname != NULL) *ret_hostname = Strdup(res.med_hnm); } xdr_free(xdr_med_hnm_res_t, (char *)&res); return (rval); } int meta_med_hnm2ip(md_hi_arr_t *mp, md_error_t *ep) { int i, j; int max_meds; if ((max_meds = get_max_meds(ep)) == 0) return (-1); for (i = 0; i < max_meds; i++) { mp->n_lst[i].a_flg = 0; /* See if this is the local host */ if (mp->n_lst[i].a_cnt > 0 && strcmp(mp->n_lst[i].a_nm[0], mynode()) == NULL) mp->n_lst[i].a_flg |= NMIP_F_LOCAL; for (j = 0; j < mp->n_lst[i].a_cnt; j++) { struct hostent *hp; char *hnm = mp->n_lst[i].a_nm[j]; /* * Cluster nodename support * * See if the clustering code can give us an IP addr * for the stored name. If not, find it the old way * which will use the public interface. */ if (sdssc_get_priv_ipaddr(mp->n_lst[i].a_nm[j], (struct in_addr *)&mp->n_lst[i].a_ip[j]) != SDSSC_OKAY) { if ((hp = gethostbyname(hnm)) == NULL) return (mdsyserror(ep, EADDRNOTAVAIL, hnm)); /* We only do INET addresses */ if (hp->h_addrtype != AF_INET) return (mdsyserror(ep, EPFNOSUPPORT, hnm)); /* We take the first address only */ if (*hp->h_addr_list) { (void) memmove(&mp->n_lst[i].a_ip[j], *hp->h_addr_list, sizeof (struct in_addr)); } else return (mdsyserror(ep, EADDRNOTAVAIL, hnm)); } } } return (0); } int meta_h2hi(md_h_arr_t *mdhp, md_hi_arr_t *mdhip, md_error_t *ep) { int i, j; int max_meds; if ((max_meds = get_max_meds(ep)) == 0) return (-1); mdhip->n_cnt = mdhp->n_cnt; for (i = 0; i < max_meds; i++) { mdhip->n_lst[i].a_flg = 0; mdhip->n_lst[i].a_cnt = mdhp->n_lst[i].a_cnt; if (mdhp->n_lst[i].a_cnt == 0) continue; for (j = 0; j < mdhp->n_lst[i].a_cnt; j++) (void) strcpy(mdhip->n_lst[i].a_nm[j], mdhp->n_lst[i].a_nm[j]); } return (0); } int meta_hi2h(md_hi_arr_t *mdhip, md_h_arr_t *mdhp, md_error_t *ep) { int i, j; int max_meds; if ((max_meds = get_max_meds(ep)) == 0) return (-1); mdhp->n_cnt = mdhip->n_cnt; for (i = 0; i < max_meds; i++) { mdhp->n_lst[i].a_cnt = mdhip->n_lst[i].a_cnt; if (mdhip->n_lst[i].a_cnt == 0) continue; for (j = 0; j < mdhip->n_lst[i].a_cnt; j++) (void) strcpy(mdhp->n_lst[i].a_nm[j], mdhip->n_lst[i].a_nm[j]); } return (0); } int setup_med_cfg( mdsetname_t *sp, mddb_config_t *cp, int force, md_error_t *ep ) { md_set_desc *sd; int i; int max_meds; if (metaislocalset(sp)) return (0); if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (setup_med_transtab(ep)) return (-1); if (meta_h2hi(&sd->sd_med, &cp->c_med, ep)) return (-1); /* Make sure the ip addresses are current */ if (meta_med_hnm2ip(&cp->c_med, ep)) return (-1); if (force) return (0); if ((max_meds = get_max_meds(ep)) == 0) return (-1); /* Make sure metamedd still running on host - only chk nodename */ for (i = 0; i < max_meds; i++) { char *hostname; char *hnm; if (sd->sd_med.n_lst[i].a_cnt == 0) continue; hnm = sd->sd_med.n_lst[i].a_nm[0]; if (clnt_med_hostname(hnm, &hostname, ep)) return (mddserror(ep, MDE_DS_NOMEDONHOST, sp->setno, hnm, NULL, sp->setname)); Free(hostname); } return (0); }