clnt_simple.c revision 1219:f89f56c2d9ac
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29/* All Rights Reserved */
30/*
31 * Portions of this source code were derived from Berkeley
32 * 4.3 BSD under license from the Regents of the University of
33 * California.
34 */
35
36#pragma ident	"%Z%%M%	%I%	%E% SMI"
37
38/*
39 * Simplified front end to client rpc.
40 */
41
42#include "mt.h"
43#include "rpc_mt.h"
44#include <stdio.h>
45#include <errno.h>
46#include <rpc/rpc.h>
47#include <string.h>
48#include <sys/param.h>
49#include <stdlib.h>
50#include <unistd.h>
51
52#ifndef MAXHOSTNAMELEN
53#define	MAXHOSTNAMELEN 64
54#endif
55
56#ifndef NETIDLEN
57#define	NETIDLEN 32
58#endif
59
60struct rpc_call_private {
61	int	valid;			/* Is this entry valid ? */
62	CLIENT	*client;		/* Client handle */
63	pid_t	pid;			/* process-id at moment of creation */
64	rpcprog_t	prognum;	/* Program */
65	rpcvers_t	versnum;	/* version */
66	char	host[MAXHOSTNAMELEN];	/* Servers host */
67	char	nettype[NETIDLEN];	/* Network type */
68};
69
70static void
71rpc_call_destroy(void *vp)
72{
73	struct rpc_call_private *rcp = (struct rpc_call_private *)vp;
74
75	if (rcp) {
76		if (rcp->client)
77			CLNT_DESTROY(rcp->client);
78		free(rcp);
79	}
80}
81
82/*
83 * This is the simplified interface to the client rpc layer.
84 * The client handle is not destroyed here and is reused for
85 * the future calls to same prog, vers, host and nettype combination.
86 *
87 * The total time available is 25 seconds.
88 */
89enum clnt_stat
90rpc_call(const char *host, const rpcprog_t prognum, const rpcvers_t versnum,
91	const rpcproc_t procnum, const xdrproc_t inproc, const char *in,
92	const xdrproc_t outproc, char  *out, const char *netclass)
93{
94	struct rpc_call_private *rcp;
95	enum clnt_stat clnt_stat;
96	struct timeval timeout, tottimeout;
97	static pthread_key_t rpc_call_key;
98	char nettype_array[NETIDLEN];
99	char *nettype = &nettype_array[0];
100
101	if (netclass == NULL)
102		nettype = NULL;
103	else {
104		size_t len = strlen(netclass);
105		if (len >= sizeof (nettype_array)) {
106			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
107			return (rpc_createerr.cf_stat);
108		}
109		(void) strcpy(nettype, netclass);
110	}
111
112	rcp = thr_get_storage(&rpc_call_key, sizeof (*rcp), rpc_call_destroy);
113	if (rcp == NULL) {
114		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
115		rpc_createerr.cf_error.re_errno = errno;
116		return (rpc_createerr.cf_stat);
117	}
118
119	if ((nettype == NULL) || (nettype[0] == NULL))
120		nettype = "netpath";
121	if (!(rcp->valid &&
122	    rcp->pid == getpid() &&
123	    rcp->prognum == prognum &&
124	    rcp->versnum == versnum &&
125	    strcmp(rcp->host, host) == 0 &&
126	    strcmp(rcp->nettype, nettype) == 0)) {
127		int fd;
128
129		rcp->valid = 0;
130		if (rcp->client)
131			CLNT_DESTROY(rcp->client);
132		/*
133		 * Using the first successful transport for that type
134		 */
135		rcp->client = clnt_create(host, prognum, versnum, nettype);
136		rcp->pid = getpid();
137		if (rcp->client == NULL)
138			return (rpc_createerr.cf_stat);
139		/*
140		 * Set time outs for connectionless case.  Do it
141		 * unconditionally.  Faster than doing a t_getinfo()
142		 * and then doing the right thing.
143		 */
144		timeout.tv_usec = 0;
145		timeout.tv_sec = 5;
146		(void) CLNT_CONTROL(rcp->client,
147				CLSET_RETRY_TIMEOUT, (char *)&timeout);
148		if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)&fd))
149			(void) fcntl(fd, F_SETFD, 1);	/* close on exec */
150		rcp->prognum = prognum;
151		rcp->versnum = versnum;
152		if ((strlen(host) < (size_t)MAXHOSTNAMELEN) &&
153		    (strlen(nettype) < (size_t)NETIDLEN)) {
154			(void) strcpy(rcp->host, host);
155			(void) strcpy(rcp->nettype, nettype);
156			rcp->valid = 1;
157		} else {
158			rcp->valid = 0;
159		}
160	} /* else reuse old client */
161	tottimeout.tv_sec = 25;
162	tottimeout.tv_usec = 0;
163	clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *)in,
164				outproc, out, tottimeout);
165	/*
166	 * if call failed, empty cache
167	 */
168	if (clnt_stat != RPC_SUCCESS)
169		rcp->valid = 0;
170	return (clnt_stat);
171}
172