dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / clnt_door.c
blob7cdec983266edc74ddf226330c7ff030a58693e1
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * clnt_doors.c, Client side for doors IPC based RPC.
31 #include "mt.h"
32 #include "rpc_mt.h"
33 #include <rpc/rpc.h>
34 #include <errno.h>
35 #include <sys/poll.h>
36 #include <syslog.h>
37 #include <sys/types.h>
38 #include <sys/kstat.h>
39 #include <sys/time.h>
40 #include <door.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <alloca.h>
45 #include <rpc/svc_mt.h>
46 #include <sys/mman.h>
47 #include <atomic.h>
50 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
52 static struct clnt_ops *clnt_door_ops();
54 extern int __rpc_default_door_buf_size;
55 extern int __rpc_min_door_buf_size;
58 * Private data kept per client handle
60 struct cu_data {
61 int cu_fd; /* door fd */
62 bool_t cu_closeit; /* close it on destroy */
63 struct rpc_err cu_error;
64 uint_t cu_xdrpos;
65 uint_t cu_sendsz; /* send size */
66 char cu_header[32]; /* precreated header */
70 * Door IPC based client creation routine.
72 * NB: The rpch->cl_auth is initialized to null authentication.
73 * Caller may wish to set this something more useful.
75 * sendsz is the maximum allowable packet size that can be sent.
76 * 0 will cause default to be used.
78 CLIENT *
79 clnt_door_create(const rpcprog_t program, const rpcvers_t version,
80 const uint_t sendsz)
82 CLIENT *cl = NULL; /* client handle */
83 struct cu_data *cu = NULL; /* private data */
84 struct rpc_msg call_msg;
85 char rendezvous[64];
86 int did;
87 struct door_info info;
88 XDR xdrs;
89 struct timeval now;
90 uint_t ssz;
92 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)program,
93 (int)version);
94 if ((did = open(rendezvous, O_RDONLY, 0)) < 0) {
95 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
96 rpc_createerr.cf_error.re_errno = errno;
97 rpc_createerr.cf_error.re_terrno = 0;
98 return (NULL);
101 if (door_info(did, &info) < 0 || (info.di_attributes & DOOR_REVOKED)) {
102 (void) close(did);
103 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
104 rpc_createerr.cf_error.re_errno = errno;
105 rpc_createerr.cf_error.re_terrno = 0;
106 return (NULL);
110 * Determine send size
112 if (sendsz < __rpc_min_door_buf_size)
113 ssz = __rpc_default_door_buf_size;
114 else
115 ssz = RNDUP(sendsz);
117 if ((cl = malloc(sizeof (CLIENT))) == NULL ||
118 (cu = malloc(sizeof (*cu))) == NULL) {
119 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
120 rpc_createerr.cf_error.re_errno = errno;
121 goto err;
125 * Precreate RPC header for performance reasons.
127 (void) gettimeofday(&now, NULL);
128 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
129 call_msg.rm_call.cb_prog = program;
130 call_msg.rm_call.cb_vers = version;
131 xdrmem_create(&xdrs, cu->cu_header, sizeof (cu->cu_header), XDR_ENCODE);
132 if (!xdr_callhdr(&xdrs, &call_msg)) {
133 rpc_createerr.cf_stat = RPC_CANTENCODEARGS;
134 rpc_createerr.cf_error.re_errno = 0;
135 goto err;
137 cu->cu_xdrpos = XDR_GETPOS(&xdrs);
139 cu->cu_sendsz = ssz;
140 cu->cu_fd = did;
141 cu->cu_closeit = TRUE;
142 cl->cl_ops = clnt_door_ops();
143 cl->cl_private = (caddr_t)cu;
144 cl->cl_auth = authnone_create();
145 cl->cl_tp = strdup(rendezvous);
146 if (cl->cl_tp == NULL) {
147 syslog(LOG_ERR, "clnt_door_create: strdup failed");
148 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
149 rpc_createerr.cf_error.re_errno = errno;
150 goto err;
152 cl->cl_netid = strdup("door");
153 if (cl->cl_netid == NULL) {
154 syslog(LOG_ERR, "clnt_door_create: strdup failed");
155 free(cl->cl_tp);
156 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
157 rpc_createerr.cf_error.re_errno = errno;
158 goto err;
160 return (cl);
161 err:
162 rpc_createerr.cf_error.re_terrno = 0;
163 if (cl) {
164 free(cl);
165 free(cu);
167 (void) close(did);
168 return (NULL);
171 /* ARGSUSED */
172 static enum clnt_stat
173 clnt_door_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp,
174 xdrproc_t xresults, caddr_t resultsp, struct timeval utimeout)
176 /* LINTED pointer alignment */
177 struct cu_data *cu = (struct cu_data *)cl->cl_private;
178 XDR xdrs;
179 door_arg_t params;
180 char *outbuf_ref;
181 struct rpc_msg reply_msg;
182 bool_t need_to_unmap;
183 uint32_t xid;
184 int nrefreshes = 2; /* number of times to refresh cred */
186 rpc_callerr.re_errno = 0;
187 rpc_callerr.re_terrno = 0;
189 if ((params.rbuf = alloca(cu->cu_sendsz)) == NULL) {
190 rpc_callerr.re_terrno = 0;
191 rpc_callerr.re_errno = errno;
192 return (rpc_callerr.re_status = RPC_SYSTEMERROR);
194 outbuf_ref = params.rbuf;
195 params.rsize = cu->cu_sendsz;
196 if ((params.data_ptr = alloca(cu->cu_sendsz)) == NULL) {
197 rpc_callerr.re_terrno = 0;
198 rpc_callerr.re_errno = errno;
199 return (rpc_callerr.re_status = RPC_SYSTEMERROR);
202 call_again:
203 xdrmem_create(&xdrs, params.data_ptr, cu->cu_sendsz, XDR_ENCODE);
204 /* Increment XID (not really needed for RPC over doors...) */
205 /* LINTED pointer alignment */
206 xid = atomic_inc_uint_nv((uint32_t *)cu->cu_header);
207 (void) memcpy(params.data_ptr, cu->cu_header, cu->cu_xdrpos);
208 /* LINTED pointer alignment */
209 *(uint32_t *)params.data_ptr = xid;
210 XDR_SETPOS(&xdrs, cu->cu_xdrpos);
212 if ((!XDR_PUTINT32(&xdrs, (int32_t *)&proc)) ||
213 (!AUTH_MARSHALL(cl->cl_auth, &xdrs)) ||
214 (!(*xargs)(&xdrs, argsp))) {
215 return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
217 params.data_size = (int)XDR_GETPOS(&xdrs);
219 params.desc_ptr = NULL;
220 params.desc_num = 0;
221 if (door_call(cu->cu_fd, &params) < 0) {
222 rpc_callerr.re_errno = errno;
223 return (rpc_callerr.re_status = RPC_CANTSEND);
226 if (params.rbuf == NULL || params.rsize == 0) {
227 return (rpc_callerr.re_status = RPC_FAILED);
229 need_to_unmap = (params.rbuf != outbuf_ref);
231 /* LINTED pointer alignment */
232 if (*(uint32_t *)params.rbuf != xid) {
233 rpc_callerr.re_status = RPC_CANTDECODERES;
234 goto done;
237 xdrmem_create(&xdrs, params.rbuf, params.rsize, XDR_DECODE);
238 reply_msg.acpted_rply.ar_verf = _null_auth;
239 reply_msg.acpted_rply.ar_results.where = resultsp;
240 reply_msg.acpted_rply.ar_results.proc = xresults;
242 if (xdr_replymsg(&xdrs, &reply_msg)) {
243 if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED &&
244 reply_msg.acpted_rply.ar_stat == SUCCESS)
245 rpc_callerr.re_status = RPC_SUCCESS;
246 else
247 __seterr_reply(&reply_msg, &rpc_callerr);
249 if (rpc_callerr.re_status == RPC_SUCCESS) {
250 if (!AUTH_VALIDATE(cl->cl_auth,
251 &reply_msg.acpted_rply.ar_verf)) {
252 rpc_callerr.re_status = RPC_AUTHERROR;
253 rpc_callerr.re_why = AUTH_INVALIDRESP;
255 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
256 xdrs.x_op = XDR_FREE;
257 (void) xdr_opaque_auth(&xdrs,
258 &(reply_msg.acpted_rply.ar_verf));
262 * If unsuccesful AND error is an authentication error
263 * then refresh credentials and try again, else break
265 else if (rpc_callerr.re_status == RPC_AUTHERROR) {
267 * maybe our credentials need to be refreshed ...
269 if (nrefreshes-- &&
270 AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
271 if (need_to_unmap)
272 (void) munmap(params.rbuf,
273 params.rsize);
274 goto call_again;
275 } else
277 * We are setting rpc_callerr here given that
278 * libnsl is not reentrant thereby
279 * reinitializing the TSD. If not set here then
280 * success could be returned even though refresh
281 * failed.
283 rpc_callerr.re_status = RPC_AUTHERROR;
285 } else
286 rpc_callerr.re_status = RPC_CANTDECODERES;
288 done:
289 if (need_to_unmap)
290 (void) munmap(params.rbuf, params.rsize);
291 return (rpc_callerr.re_status);
294 /* ARGSUSED */
295 static enum clnt_stat
296 clnt_door_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp)
298 /* send() call not supported on doors */
300 rpc_callerr.re_errno = ENOTSUP;
301 rpc_callerr.re_terrno = 0;
303 return (rpc_callerr.re_status = RPC_FAILED);
306 static void
307 clnt_door_geterr(CLIENT *cl, struct rpc_err *errp)
309 /* LINTED pointer alignment */
310 struct cu_data *cu = (struct cu_data *)cl->cl_private;
312 *errp = rpc_callerr;
315 /* ARGSUSED */
316 static bool_t
317 clnt_door_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
319 XDR xdrs;
321 (void) memset(&xdrs, 0, sizeof (xdrs));
322 xdrs.x_op = XDR_FREE;
323 return ((*xdr_res)(&xdrs, res_ptr));
326 static void
327 clnt_door_abort(CLIENT *cl)
329 cl = cl;
332 static bool_t
333 clnt_door_control(CLIENT *cl, int request, char *info)
335 /* LINTED pointer alignment */
336 struct cu_data *cu = (struct cu_data *)cl->cl_private;
338 switch (request) {
339 case CLSET_FD_CLOSE:
340 cu->cu_closeit = TRUE;
341 return (TRUE);
343 case CLSET_FD_NCLOSE:
344 cu->cu_closeit = FALSE;
345 return (TRUE);
348 /* for other requests which use info */
349 if (info == NULL)
350 return (FALSE);
352 switch (request) {
353 case CLGET_FD:
354 /* LINTED pointer alignment */
355 *(int *)info = cu->cu_fd;
356 break;
358 case CLGET_XID:
360 * use the knowledge that xid is the
361 * first element in the call structure *.
362 * This will get the xid of the PREVIOUS call
364 /* LINTED pointer alignment */
365 *(uint32_t *)info = ntohl(*(uint32_t *)cu->cu_header);
366 break;
368 case CLSET_XID:
369 /* This will set the xid of the NEXT call */
370 /* LINTED pointer alignment */
371 *(uint32_t *)cu->cu_header = htonl(*(uint32_t *)info - 1);
372 /* decrement by 1 as clnt_door_call() increments once */
373 break;
375 case CLGET_VERS:
377 * This RELIES on the information that, in the call body,
378 * the version number field is the fifth field from the
379 * begining of the RPC header. MUST be changed if the
380 * call_struct is changed
382 /* LINTED pointer alignment */
383 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header +
384 4 * BYTES_PER_XDR_UNIT));
385 break;
387 case CLSET_VERS:
388 /* LINTED pointer alignment */
389 *(uint32_t *)(cu->cu_header + 4 * BYTES_PER_XDR_UNIT) =
390 /* LINTED pointer alignment */
391 htonl(*(uint32_t *)info);
392 break;
394 case CLGET_PROG:
396 * This RELIES on the information that, in the call body,
397 * the program number field is the fourth field from the
398 * begining of the RPC header. MUST be changed if the
399 * call_struct is changed
401 /* LINTED pointer alignment */
402 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header +
403 3 * BYTES_PER_XDR_UNIT));
404 break;
406 case CLSET_PROG:
407 /* LINTED pointer alignment */
408 *(uint32_t *)(cu->cu_header + 3 * BYTES_PER_XDR_UNIT) =
409 /* LINTED pointer alignment */
410 htonl(*(uint32_t *)info);
411 break;
413 default:
414 return (FALSE);
416 return (TRUE);
419 static void
420 clnt_door_destroy(CLIENT *cl)
422 /* LINTED pointer alignment */
423 struct cu_data *cu = (struct cu_data *)cl->cl_private;
424 int cu_fd = cu->cu_fd;
426 if (cu->cu_closeit)
427 (void) close(cu_fd);
428 free(cu);
429 if (cl->cl_netid && cl->cl_netid[0])
430 free(cl->cl_netid);
431 if (cl->cl_tp && cl->cl_tp[0])
432 free(cl->cl_tp);
433 free(cl);
436 static struct clnt_ops *
437 clnt_door_ops(void)
439 static struct clnt_ops ops;
440 extern mutex_t ops_lock;
442 sig_mutex_lock(&ops_lock);
443 if (ops.cl_call == NULL) {
444 ops.cl_call = clnt_door_call;
445 ops.cl_send = clnt_door_send;
446 ops.cl_abort = clnt_door_abort;
447 ops.cl_geterr = clnt_door_geterr;
448 ops.cl_freeres = clnt_door_freeres;
449 ops.cl_destroy = clnt_door_destroy;
450 ops.cl_control = clnt_door_control;
452 sig_mutex_unlock(&ops_lock);
453 return (&ops);
457 _update_did(CLIENT *cl, int vers)
459 /* LINTED pointer alignment */
460 struct cu_data *cu = (struct cu_data *)cl->cl_private;
461 rpcprog_t prog;
462 char rendezvous[64];
464 if (cu->cu_fd >= 0)
465 (void) close(cu->cu_fd);
466 /* Make sure that the right door id is used in door_call. */
467 clnt_control(cl, CLGET_PROG, (void *)&prog);
468 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)prog, vers);
469 if ((cu->cu_fd = open(rendezvous, O_RDONLY, 0)) < 0) {
470 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
471 rpc_createerr.cf_error.re_errno = errno;
472 rpc_createerr.cf_error.re_terrno = 0;
473 return (0);
475 free(cl->cl_tp);
476 cl->cl_tp = strdup(rendezvous);
477 if (cl->cl_tp == NULL) {
478 syslog(LOG_ERR, "_update_did: strdup failed");
479 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
480 rpc_createerr.cf_error.re_errno = errno;
481 rpc_createerr.cf_error.re_terrno = 0;
482 return (0);
484 return (1);