xref: /illumos-gate/usr/src/lib/libnsl/rpc/svc_simple.c (revision f808c858)
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 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * svc_simple.c
39  * Simplified front end to rpc.
40  */
41 
42 /*
43  * This interface creates a virtual listener for all the services
44  * started thru rpc_reg(). It listens on the same endpoint for
45  * all the services and then executes the corresponding service
46  * for the given prognum and procnum.
47  */
48 
49 #include "mt.h"
50 #include "rpc_mt.h"
51 #include <errno.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <rpc/rpc.h>
56 #include <sys/types.h>
57 #include <syslog.h>
58 #include <rpc/nettype.h>
59 
60 static struct proglst {
61 	char *(*p_progname)();
62 	rpcprog_t p_prognum;
63 	rpcvers_t p_versnum;
64 	rpcproc_t p_procnum;
65 	SVCXPRT *p_transp;
66 	char *p_netid;
67 	char *p_xdrbuf;
68 	int p_recvsz;
69 	xdrproc_t p_inproc, p_outproc;
70 	struct proglst *p_nxt;
71 } *proglst;
72 
73 static void universal();
74 
75 static const char rpc_reg_err[] = "%s: %s";
76 static const char rpc_reg_msg[] = "rpc_reg: ";
77 static const char __reg_err1[] = "can't find appropriate transport";
78 static const char __reg_err3[] = "unsupported transport size";
79 static const char __no_mem_str[] = "out of memory";
80 /*
81  * For simplified, easy to use kind of rpc interfaces.
82  * nettype indicates the type of transport on which the service will be
83  * listening. Used for conservation of the system resource. Only one
84  * handle is created for all the services (actually one of each netid)
85  * and same xdrbuf is used for same netid. The size of the arguments
86  * is also limited by the recvsize for that transport, even if it is
87  * a COTS transport. This may be wrong, but for cases like these, they
88  * should not use the simplified interfaces like this.
89  */
90 
91 int
92 rpc_reg(const rpcprog_t prognum, const rpcvers_t versnum,
93 	const rpcproc_t procnum, char *(*progname)(), const xdrproc_t inproc,
94 	const xdrproc_t outproc, const char *nettype)
95 {
96 	struct netconfig *nconf;
97 	int done = FALSE;
98 	void *handle;
99 	extern mutex_t proglst_lock;
100 
101 	if (procnum == NULLPROC) {
102 		(void) syslog(LOG_ERR, (const char *) "%s: %s %d",
103 			rpc_reg_msg,
104 			(const char *) "can't reassign procedure number %d",
105 			NULLPROC);
106 		return (-1);
107 	}
108 
109 	if (nettype == NULL)
110 		nettype = "netpath";		/* The default behavior */
111 	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
112 		(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg, __reg_err1);
113 		return (-1);
114 	}
115 /* VARIABLES PROTECTED BY proglst_lock: proglst */
116 	(void) mutex_lock(&proglst_lock);
117 	while (nconf = __rpc_getconf(handle)) {
118 		struct proglst *pl;
119 		SVCXPRT *svcxprt;
120 		int madenow;
121 		uint_t recvsz;
122 		char *xdrbuf;
123 		char *netid;
124 
125 		madenow = FALSE;
126 		svcxprt = NULL;
127 		for (pl = proglst; pl; pl = pl->p_nxt)
128 			if (strcmp(pl->p_netid, nconf->nc_netid) == 0) {
129 				svcxprt = pl->p_transp;
130 				xdrbuf = pl->p_xdrbuf;
131 				recvsz = pl->p_recvsz;
132 				netid = pl->p_netid;
133 				break;
134 			}
135 
136 		if (svcxprt == NULL) {
137 			struct t_info tinfo;
138 
139 			svcxprt = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
140 			if (svcxprt == NULL)
141 				continue;
142 			if (t_getinfo(svcxprt->xp_fd, &tinfo) == -1) {
143 				char errorstr[100];
144 
145 				__tli_sys_strerror(errorstr, sizeof (errorstr),
146 						t_errno, errno);
147 				(void) syslog(LOG_ERR, "%s : %s : %s",
148 					rpc_reg_msg, "t_getinfo failed",
149 					errorstr);
150 				SVC_DESTROY(svcxprt);
151 				continue;
152 			}
153 			if ((recvsz = __rpc_get_t_size(0, tinfo.tsdu)) == 0) {
154 				(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
155 					__reg_err3);
156 				SVC_DESTROY(svcxprt);
157 				continue;
158 			}
159 			if (((xdrbuf = malloc((unsigned)recvsz)) == NULL) ||
160 				((netid = strdup(nconf->nc_netid)) == NULL)) {
161 				(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
162 					__no_mem_str);
163 				SVC_DESTROY(svcxprt);
164 				break;
165 			}
166 			madenow = TRUE;
167 		}
168 		/*
169 		 * Check if this (program, version, netid) had already been
170 		 * registered.  The check may save a few RPC calls to rpcbind
171 		 */
172 		for (pl = proglst; pl; pl = pl->p_nxt)
173 			if ((pl->p_prognum == prognum) &&
174 				(pl->p_versnum == versnum) &&
175 				(strcmp(pl->p_netid, netid) == 0))
176 				break;
177 		if (pl == NULL) { /* Not yet */
178 			(void) rpcb_unset(prognum, versnum, nconf);
179 		} else {
180 			/* so that svc_reg does not call rpcb_set() */
181 			nconf = NULL;
182 		}
183 
184 		if (!svc_reg(svcxprt, prognum, versnum, universal, nconf)) {
185 			(void) syslog(LOG_ERR,
186 				"%s couldn't register prog %d vers %d for %s",
187 				rpc_reg_msg, prognum, versnum, netid);
188 			if (madenow) {
189 				SVC_DESTROY(svcxprt);
190 				free(xdrbuf);
191 				free(netid);
192 			}
193 			continue;
194 		}
195 
196 		pl = malloc(sizeof (struct proglst));
197 		if (pl == NULL) {
198 			(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
199 					__no_mem_str);
200 			if (madenow) {
201 				SVC_DESTROY(svcxprt);
202 				free(xdrbuf);
203 				free(netid);
204 			}
205 			break;
206 		}
207 		pl->p_progname = progname;
208 		pl->p_prognum = prognum;
209 		pl->p_versnum = versnum;
210 		pl->p_procnum = procnum;
211 		pl->p_inproc = inproc;
212 		pl->p_outproc = outproc;
213 		pl->p_transp = svcxprt;
214 		pl->p_xdrbuf = xdrbuf;
215 		pl->p_recvsz = recvsz;
216 		pl->p_netid = netid;
217 		pl->p_nxt = proglst;
218 		proglst = pl;
219 		done = TRUE;
220 	}
221 	__rpc_endconf(handle);
222 	(void) mutex_unlock(&proglst_lock);
223 
224 	if (done == FALSE) {
225 		(void) syslog(LOG_ERR,
226 			(const char *) "%s cant find suitable transport for %s",
227 			rpc_reg_msg, nettype);
228 		return (-1);
229 	}
230 	return (0);
231 }
232 
233 /*
234  * The universal handler for the services registered using registerrpc.
235  * It handles both the connectionless and the connection oriented cases.
236  */
237 
238 static void
239 universal(struct svc_req *rqstp, SVCXPRT *transp)
240 {
241 	rpcprog_t prog;
242 	rpcvers_t vers;
243 	rpcproc_t proc;
244 	char *outdata;
245 	char *xdrbuf;
246 	struct proglst *pl;
247 	extern mutex_t proglst_lock;
248 
249 	/*
250 	 * enforce "procnum 0 is echo" convention
251 	 */
252 	if (rqstp->rq_proc == NULLPROC) {
253 		if (svc_sendreply(transp, (xdrproc_t)xdr_void, NULL) == FALSE) {
254 			(void) syslog(LOG_ERR,
255 				(const char *) "svc_sendreply failed");
256 		}
257 		return;
258 	}
259 	prog = rqstp->rq_prog;
260 	vers = rqstp->rq_vers;
261 	proc = rqstp->rq_proc;
262 	(void) mutex_lock(&proglst_lock);
263 	for (pl = proglst; pl; pl = pl->p_nxt) {
264 		if (pl->p_prognum == prog && pl->p_procnum == proc &&
265 			pl->p_versnum == vers &&
266 			(strcmp(pl->p_netid, transp->xp_netid) == 0)) {
267 			/* decode arguments into a CLEAN buffer */
268 			xdrbuf = pl->p_xdrbuf;
269 			/* Zero the arguments: reqd ! */
270 			(void) memset(xdrbuf, 0, pl->p_recvsz);
271 			/*
272 			 * Assuming that sizeof (xdrbuf) would be enough
273 			 * for the arguments; if not then the program
274 			 * may bomb. BEWARE!
275 			 */
276 			if (!svc_getargs(transp, pl->p_inproc, xdrbuf)) {
277 				svcerr_decode(transp);
278 				(void) mutex_unlock(&proglst_lock);
279 				return;
280 			}
281 			outdata = (*(pl->p_progname))(xdrbuf);
282 			if (outdata == NULL &&
283 				pl->p_outproc != (xdrproc_t)xdr_void) {
284 				/* there was an error */
285 				(void) mutex_unlock(&proglst_lock);
286 				return;
287 			}
288 			if (!svc_sendreply(transp, pl->p_outproc, outdata)) {
289 				(void) syslog(LOG_ERR, (const char *)
290 			"rpc: rpc_reg trouble replying to prog %d vers %d",
291 				prog, vers);
292 				(void) mutex_unlock(&proglst_lock);
293 				return;
294 			}
295 			/* free the decoded arguments */
296 			(void) svc_freeargs(transp, pl->p_inproc, xdrbuf);
297 			(void) mutex_unlock(&proglst_lock);
298 			return;
299 		}
300 	}
301 	(void) mutex_unlock(&proglst_lock);
302 	/* This should never happen */
303 	(void) syslog(LOG_ERR, (const char *)
304 		"rpc: rpc_reg: never registered prog %d vers %d",
305 		prog, vers);
306 }
307