metarpcopen.c revision 2
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Just in case we're not in a build environment, make sure that
2N/A * TEXT_DOMAIN gets set to something.
2N/A */
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/A#include <meta.h>
2N/A#include <metad.h>
2N/A#include <sdssc.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A
2N/A#define CC_TTL_MAX 20
2N/A
2N/Atypedef struct {
2N/A char *cc_node;
2N/A char *cc_priv_ip;
2N/A struct timeval cc_ttl;
2N/A CLIENT *cc_clp;
2N/A} client_cache_t;
2N/A
2N/Atypedef struct client_header {
2N/A client_cache_t **ch_cache; /* array of clients. */
2N/A mutex_t ch_mutex; /* lock access to ch_cache */
2N/A} client_header_t;
2N/A
2N/A/*
2N/A * This structure is used to pass data from meta_client_create to
2N/A * client_create_helper via meta_client_create_retry.
2N/A */
2N/Atypedef struct clnt_data {
2N/A rpcprog_t cd_prognum; /* RPC program number */
2N/A rpcvers_t cd_version; /* Desired interface version */
2N/A char *cd_nettype; /* Type of network to use */
2N/A} clnt_data_t;
2N/A
2N/A#define MALLOC_BLK_SIZE 10
2N/Astatic client_header_t client_header = {(client_cache_t **)NULL, DEFAULTMUTEX};
2N/A
2N/Astatic void
2N/Acc_add(
2N/A client_header_t *header,
2N/A char *node,
2N/A char *priv_ip,
2N/A CLIENT *clntp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A client_cache_t ***cachep = &header->ch_cache;
2N/A struct timeval now;
2N/A int i;
2N/A int j = 0;
2N/A
2N/A if (gettimeofday(&now, NULL) == -1) {
2N/A (void) mdsyserror(ep, errno, "gettimeofday()");
2N/A return;
2N/A }
2N/A
2N/A (void) mutex_lock(&header->ch_mutex);
2N/A if (*cachep) {
2N/A for (i = 0; (*cachep)[i] != NULL; i++)
2N/A if (strcmp((*cachep)[i]->cc_node, node) == 0 &&
2N/A (*cachep)[i]->cc_clp == NULL) {
2N/A (*cachep)[i]->cc_clp = clntp;
2N/A (*cachep)[i]->cc_ttl = now;
2N/A (void) mutex_unlock(&header->ch_mutex);
2N/A return;
2N/A }
2N/A } else {
2N/A *cachep = Calloc(MALLOC_BLK_SIZE, sizeof (**cachep));
2N/A i = 0;
2N/A }
2N/A
2N/A (*cachep)[i] = Zalloc(sizeof (***cachep));
2N/A (*cachep)[i]->cc_node = Strdup(node);
2N/A (*cachep)[i]->cc_priv_ip = Strdup(priv_ip);
2N/A (*cachep)[i]->cc_clp = clntp;
2N/A (*cachep)[i]->cc_ttl = now;
2N/A
2N/A if ((++i % MALLOC_BLK_SIZE) == 0) {
2N/A *cachep = Realloc(*cachep,
2N/A (i + MALLOC_BLK_SIZE) * sizeof (**cachep));
2N/A for (j = i; j < (i + MALLOC_BLK_SIZE); j++)
2N/A (*cachep)[j] = NULL;
2N/A }
2N/A (void) mutex_unlock(&header->ch_mutex);
2N/A}
2N/A
2N/Astatic void
2N/Arel_clntp(client_cache_t *cachep)
2N/A{
2N/A CLIENT *clntp = cachep->cc_clp;
2N/A
2N/A if (clntp != NULL) {
2N/A auth_destroy(clntp->cl_auth);
2N/A clnt_destroy(clntp);
2N/A }
2N/A cachep->cc_clp = NULL;
2N/A}
2N/A
2N/Astatic void
2N/Acc_destroy(client_header_t *header)
2N/A{
2N/A client_cache_t ***cachep = &header->ch_cache;
2N/A int i;
2N/A
2N/A (void) mutex_lock(&header->ch_mutex);
2N/A if (*cachep) {
2N/A for (i = 0; ((*cachep)[i] != NULL); i++) {
2N/A client_cache_t *p = (*cachep)[i];
2N/A
2N/A Free(p->cc_node);
2N/A Free(p->cc_priv_ip);
2N/A rel_clntp(p);
2N/A Free(p);
2N/A }
2N/A Free(*cachep);
2N/A *cachep = NULL;
2N/A }
2N/A (void) mutex_unlock(&header->ch_mutex);
2N/A}
2N/A
2N/A/*
2N/A * Set the timeout value for this client handle.
2N/A */
2N/Aint
2N/Acl_sto(
2N/A CLIENT *clntp,
2N/A char *hostname,
2N/A long time_out,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A struct timeval nto;
2N/A
2N/A (void) memset(&nto, '\0', sizeof (nto));
2N/A
2N/A nto.tv_sec = time_out;
2N/A
2N/A if (clnt_control(clntp, CLSET_TIMEOUT, (char *)&nto) != TRUE)
2N/A return (mdrpcerror(ep, clntp, hostname,
2N/A dgettext(TEXT_DOMAIN, "metad client set timeout")));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * client_create_vers_retry is the helper function to be passed to
2N/A * meta_client_create_retry to do the actual work of creating the client
2N/A * when version selection is necessary.
2N/A */
2N/A
2N/A/* ARGSUSED */
2N/Astatic CLIENT *
2N/Aclient_create_vers_retry(char *hostname,
2N/A void *ignore,
2N/A struct timeval *tout
2N/A)
2N/A{
2N/A rpcvers_t vers; /* Version # not needed. */
2N/A
2N/A return (clnt_create_vers_timed(hostname, METAD, &vers,
2N/A METAD_VERSION, METAD_VERSION_DEVID, "tcp", tout));
2N/A}
2N/A
2N/A/*
2N/A * client_create_helper is the helper function to be passed to
2N/A * meta_client_create_retry when plain vanilla client create is desired.
2N/A */
2N/Astatic CLIENT *
2N/Aclient_create_helper(char *hostname, void *private, struct timeval *time_out)
2N/A{
2N/A clnt_data_t *cd = (clnt_data_t *)private;
2N/A
2N/A return (clnt_create_timed(hostname, cd->cd_prognum, cd->cd_version,
2N/A cd->cd_nettype, time_out));
2N/A}
2N/A
2N/A/*
2N/A * meta_client_create_retry is a general function to assist in creating RPC
2N/A * clients. This function handles retrying if the attempt to create a
2N/A * client fails. meta_client_create_retry itself does not actually create
2N/A * the client. Instead it calls the helper function, func, to do that job.
2N/A *
2N/A * With the help of func, meta_client_create_retry will create an RPC
2N/A * connection allowing up to tout seconds to complete the task. If the
2N/A * connection creation fails for RPC_RPCBFAILURE, RPC_CANTRECV or
2N/A * RPC_PROGNOTREGISTERED and tout seconds have not passed,
2N/A * meta_client_create_retry will try again. The reason retries are
2N/A * important is that when the inet daemon is being refreshed, it can take
2N/A * 15-20 seconds for it to start responding again.
2N/A *
2N/A * Arguments:
2N/A *
2N/A * hostname - Name of remote host
2N/A *
2N/A * func - Pointer to the helper function, that will
2N/A * actually try to create the client.
2N/A *
2N/A * data - Private data to be passed on to func.
2N/A * meta_client_create_retry treats this as an opaque
2N/A * pointer.
2N/A *
2N/A * tout - Number of seconds to allow for the connection
2N/A * attempt.
2N/A *
2N/A * ep - Standard SVM error pointer. May be NULL.
2N/A */
2N/ACLIENT *
2N/Ameta_client_create_retry(
2N/A char *hostname,
2N/A clnt_create_func_t func,
2N/A void *data,
2N/A time_t tout,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A static int debug; /* print debugging info */
2N/A static int debug_set = 0;
2N/A
2N/A CLIENT *clnt = (CLIENT *) NULL;
2N/A struct timeval curtime;
2N/A char *d;
2N/A struct timeval start;
2N/A struct timeval timeout;
2N/A
2N/A if (debug_set == 0) {
2N/A d = getenv("MD_DEBUG");
2N/A if (d == NULL) {
2N/A debug = 0;
2N/A } else {
2N/A debug = (strstr(d, "RPC") == NULL) ? 0 : 1;
2N/A }
2N/A debug_set = 1;
2N/A }
2N/A timeout.tv_usec = 0;
2N/A if (gettimeofday(&start, NULL) == -1) {
2N/A if (ep != (md_error_t *)NULL) {
2N/A (void) mdsyserror(ep, errno, "gettimeofday()");
2N/A }
2N/A return (clnt);
2N/A }
2N/A curtime = start;
2N/A while ((curtime.tv_sec - start.tv_sec) < tout) {
2N/A /* Use remaining time as the timeout value. */
2N/A timeout.tv_sec = tout - (curtime.tv_sec - start.tv_sec);
2N/A clnt = (*func)(hostname, data, &timeout);
2N/A if (clnt != (CLIENT *) NULL)
2N/A break;
2N/A if ((rpc_createerr.cf_stat == RPC_RPCBFAILURE) ||
2N/A (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) ||
2N/A (rpc_createerr.cf_stat == RPC_CANTRECV)) {
2N/A if (debug) {
2N/A clnt_pcreateerror("meta_client_create_retry");
2N/A }
2N/A /* If error might be fixed in time, sleep & try again */
2N/A (void) sleep(2);
2N/A if (gettimeofday(&curtime, NULL) == -1) {
2N/A if (ep != (md_error_t *)NULL) {
2N/A (void) mdsyserror(ep, errno,
2N/A "gettimeofday()");
2N/A }
2N/A return (clnt);
2N/A }
2N/A } else {
2N/A /* Not a recoverable error. */
2N/A break;
2N/A }
2N/A }
2N/A if ((clnt == (CLIENT *) NULL) && (ep != (md_error_t *)NULL)) {
2N/A (void) mdrpccreateerror(ep, hostname,
2N/A "meta_client_create_retry");
2N/A }
2N/A return (clnt);
2N/A}
2N/A
2N/A/*
2N/A * meta_client_create is intended to be used within SVM as a replacement
2N/A * for calls to clnt_create. meta_client_create invokes the retry
2N/A * mechanism of meta_client_create_retry.
2N/A */
2N/ACLIENT *
2N/Ameta_client_create(char *host, rpcprog_t prognum, rpcvers_t version,
2N/A char *nettype)
2N/A{
2N/A clnt_data_t cd;
2N/A
2N/A cd.cd_prognum = prognum;
2N/A cd.cd_version = version;
2N/A cd.cd_nettype = nettype;
2N/A return (meta_client_create_retry(host, client_create_helper,
2N/A (void *)&cd, MD_CLNT_CREATE_TOUT, (md_error_t *)NULL));
2N/A}
2N/A
2N/A/*
2N/A * create and return RPC connection
2N/A */
2N/ACLIENT *
2N/Ametarpcopen(
2N/A char *hostname,
2N/A long time_out,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A CLIENT *clntp = NULL;
2N/A client_cache_t ***cachep = &client_header.ch_cache;
2N/A int i;
2N/A long delta;
2N/A struct timeval now;
2N/A struct in_addr p_ip;
2N/A char *host_sc = NULL;
2N/A char host_priv[MD_MAX_MNNODENAME_PLUS_1];
2N/A
2N/A if (gettimeofday(&now, NULL) == -1) {
2N/A (void) mdsyserror(ep, errno, "gettimeofday()");
2N/A return (NULL);
2N/A }
2N/A
2N/A (void) mutex_lock(&client_header.ch_mutex);
2N/A if (client_header.ch_cache) {
2N/A for (i = 0; (*cachep)[i] != NULL; i++) {
2N/A if (strcmp((*cachep)[i]->cc_node, hostname) == 0) {
2N/A clntp = (*cachep)[i]->cc_clp;
2N/A if (clntp == NULL)
2N/A continue;
2N/A delta = now.tv_sec -
2N/A (*cachep)[i]->cc_ttl.tv_sec;
2N/A if (delta > CC_TTL_MAX) {
2N/A rel_clntp((*cachep)[i]);
2N/A continue;
2N/A }
2N/A if (cl_sto(clntp, hostname, time_out,
2N/A ep) != 0) {
2N/A (void) mutex_unlock(
2N/A &client_header.ch_mutex);
2N/A return (NULL);
2N/A }
2N/A (void) mutex_unlock(&client_header.ch_mutex);
2N/A return (clntp);
2N/A }
2N/A }
2N/A }
2N/A (void) mutex_unlock(&client_header.ch_mutex);
2N/A
2N/A /*
2N/A * Before trying to create the client, make sure that the core SVM
2N/A * services are enabled by the Service Management Facility. We
2N/A * don't want to suffer the 60 second timeout if the services are
2N/A * not even enabled. This call actually only verifies that they
2N/A * are enabled on this host no matter which host the caller wants
2N/A * to connect to. Nonetheless, if the services are not enabled on
2N/A * the local host, our RPC stuff is not going to work as expected.
2N/A */
2N/A if (meta_smf_isonline(META_SMF_CORE, ep) == 0) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Copy at most MD_MAX_MNNODENAME octets from the passed hostname.
2N/A * This is limited by RFC1035. We truncate at this amount.
2N/A */
2N/A (void) strlcpy(host_priv, hostname, sizeof (host_priv));
2N/A
2N/A /*
2N/A * If we are in cluster mode, lets use the private interconnect
2N/A * hostnames to establish the rpc connections.
2N/A */
2N/A if (sdssc_bind_library() != SDSSC_NOT_BOUND) {
2N/A if (sdssc_get_priv_ipaddr(hostname, &p_ip) == SDSSC_OKAY) {
2N/A /*
2N/A * inet_ntoa() returns pointer to a string in the
2N/A * base 256 notation d.d.d.d (IPv4) and so
2N/A * host_priv[18] should be sufficient enough to
2N/A * hold it.
2N/A */
2N/A host_sc = inet_ntoa(p_ip);
2N/A if (host_sc != NULL) {
2N/A int size = sizeof (host_priv);
2N/A (void) strlcpy(host_priv, host_sc, size);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Try to create a version 2 client handle by default.
2N/A * If this fails (i.e. client is version 1), try to
2N/A * create a version 1 client handle.
2N/A */
2N/A clntp = meta_client_create_retry(host_priv, client_create_vers_retry,
2N/A (void *)NULL, MD_CLNT_CREATE_TOUT, ep);
2N/A
2N/A /* open connection */
2N/A if (clntp == NULL) {
2N/A (void) mdrpccreateerror(ep, hostname,
2N/A dgettext(TEXT_DOMAIN, "metad client create"));
2N/A cc_add(&client_header, hostname, host_priv, NULL, ep);
2N/A return (NULL);
2N/A } else {
2N/A auth_destroy(clntp->cl_auth);
2N/A clntp->cl_auth = authsys_create_default();
2N/A assert(clntp->cl_auth != NULL);
2N/A }
2N/A
2N/A cc_add(&client_header, hostname, host_priv, clntp, ep);
2N/A
2N/A if (cl_sto(clntp, hostname, time_out, ep) != 0)
2N/A return (NULL);
2N/A
2N/A return (clntp);
2N/A}
2N/A
2N/A/*
2N/A * metarpcclose - is a place holder so that when using
2N/A * metarpcopen, it does not appear that
2N/A * we have dangling opens. We can at some
2N/A * later decrement open counts here too, if needed.
2N/A */
2N/A/*ARGSUSED*/
2N/Avoid
2N/Ametarpcclose(CLIENT *clntp)
2N/A{
2N/A}
2N/A
2N/Avoid
2N/Ametarpccloseall(void)
2N/A{
2N/A cc_destroy(&client_header);
2N/A}