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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Utility routines
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <libintl.h>
33 #include <assert.h>
34 #include <ucontext.h>
35 #include <pthread.h>
36 #include "idmap_impl.h"
37
38 #define _UDT_SIZE_INCR 1
39
40 #define _GET_IDS_SIZE_INCR 1
41
42 static struct timeval TIMEOUT = { 25, 0 };
43
44 struct idmap_handle {
45 CLIENT *client;
46 boolean_t failed;
47 rwlock_t lock;
48 };
49
50 static struct idmap_handle idmap_handle = {
51 NULL, /* client */
52 B_TRUE, /* failed */
53 DEFAULTRWLOCK, /* lock */
54 };
55
56 static idmap_stat _idmap_clnt_connect(void);
57 static void _idmap_clnt_disconnect(void);
58
59 idmap_retcode
_udt_extend_batch(idmap_udt_handle_t * udthandle)60 _udt_extend_batch(idmap_udt_handle_t *udthandle)
61 {
62 idmap_update_op *tmplist;
63 size_t nsize;
64
65 if (udthandle->next >= udthandle->batch.idmap_update_batch_len) {
66 nsize = (udthandle->batch.idmap_update_batch_len +
67 _UDT_SIZE_INCR) * sizeof (*tmplist);
68 tmplist = realloc(
69 udthandle->batch.idmap_update_batch_val, nsize);
70 if (tmplist == NULL)
71 return (IDMAP_ERR_MEMORY);
72 (void) memset((uchar_t *)tmplist +
73 (udthandle->batch.idmap_update_batch_len *
74 sizeof (*tmplist)), 0,
75 _UDT_SIZE_INCR * sizeof (*tmplist));
76 udthandle->batch.idmap_update_batch_val = tmplist;
77 udthandle->batch.idmap_update_batch_len += _UDT_SIZE_INCR;
78 }
79 udthandle->batch.idmap_update_batch_val[udthandle->next].opnum =
80 OP_NONE;
81 return (IDMAP_SUCCESS);
82 }
83
84 idmap_retcode
_get_ids_extend_batch(idmap_get_handle_t * gh)85 _get_ids_extend_batch(idmap_get_handle_t *gh)
86 {
87 idmap_mapping *t1;
88 idmap_get_res_t *t2;
89 size_t nsize, len;
90
91 len = gh->batch.idmap_mapping_batch_len;
92 if (gh->next >= len) {
93 /* extend the request array */
94 nsize = (len + _GET_IDS_SIZE_INCR) * sizeof (*t1);
95 t1 = realloc(gh->batch.idmap_mapping_batch_val, nsize);
96 if (t1 == NULL)
97 return (IDMAP_ERR_MEMORY);
98 (void) memset((uchar_t *)t1 + (len * sizeof (*t1)), 0,
99 _GET_IDS_SIZE_INCR * sizeof (*t1));
100 gh->batch.idmap_mapping_batch_val = t1;
101
102 /* extend the return list */
103 nsize = (len + _GET_IDS_SIZE_INCR) * sizeof (*t2);
104 t2 = realloc(gh->retlist, nsize);
105 if (t2 == NULL)
106 return (IDMAP_ERR_MEMORY);
107 (void) memset((uchar_t *)t2 + (len * sizeof (*t2)), 0,
108 _GET_IDS_SIZE_INCR * sizeof (*t2));
109 gh->retlist = t2;
110
111 gh->batch.idmap_mapping_batch_len += _GET_IDS_SIZE_INCR;
112 }
113 return (IDMAP_SUCCESS);
114 }
115
116 idmap_stat
_iter_get_next_list(int type,idmap_iter_t * iter,void * arg,uchar_t ** list,size_t valsize,xdrproc_t xdr_arg_proc,xdrproc_t xdr_res_proc)117 _iter_get_next_list(int type, idmap_iter_t *iter,
118 void *arg, uchar_t **list, size_t valsize,
119 xdrproc_t xdr_arg_proc, xdrproc_t xdr_res_proc)
120 {
121 idmap_stat rc;
122
123 iter->next = 0;
124 iter->retlist = NULL;
125
126 /* init the result */
127 if (*list) {
128 xdr_free(xdr_res_proc, (caddr_t)*list);
129 } else {
130 if ((*list = malloc(valsize)) == NULL) {
131 errno = ENOMEM;
132 return (IDMAP_ERR_MEMORY);
133 }
134 }
135 (void) memset(*list, 0, valsize);
136
137 rc = _idmap_clnt_call(type,
138 xdr_arg_proc, (caddr_t)arg,
139 xdr_res_proc, (caddr_t)*list,
140 TIMEOUT);
141 if (rc != IDMAP_SUCCESS) {
142 free(*list);
143 return (rc);
144 }
145 iter->retlist = *list;
146 return (IDMAP_SUCCESS);
147 }
148
149 /*
150 * Convert the return values from an RPC request into an idmap return code.
151 * Set errno on error.
152 */
153 static
154 idmap_stat
_idmap_rpc2stat(enum clnt_stat clntstat,CLIENT * clnt)155 _idmap_rpc2stat(enum clnt_stat clntstat, CLIENT *clnt)
156 {
157 /*
158 * We only deal with door_call(3C) errors here. We look at
159 * r_err.re_errno instead of r_err.re_status because we need
160 * to differentiate between RPC failures caused by bad door fd
161 * and others.
162 */
163 struct rpc_err r_err;
164
165 if (clntstat == RPC_SUCCESS)
166 return (IDMAP_SUCCESS);
167
168 clnt_geterr(clnt, &r_err);
169 errno = r_err.re_errno;
170 switch (r_err.re_errno) {
171 case ENOMEM:
172 return (IDMAP_ERR_MEMORY);
173 case EBADF:
174 return (IDMAP_ERR_RPC_HANDLE);
175 default:
176 return (IDMAP_ERR_RPC);
177 }
178 }
179
180 /*
181 * Management of the connection to idmapd.
182 *
183 * The intent is that connections to idmapd are automatically maintained,
184 * reconnecting if necessary. No attempt is made to retry connnection
185 * attempts; a failure to connect yields an immediate error return.
186 *
187 * State of the connection is maintained through the "client" and "failed"
188 * elements of the handle structure:
189 *
190 * client failed
191 * NULL true Failed on a previous request and was not recovered.
192 * NULL false Should never happen.
193 * nonNULL true Structure exists, but an error has occurred. Waiting
194 * for a chance to attempt to reconnect.
195 * nonNULL false Connection is good.
196 *
197 * Note that the initial state is NULL/true, so that the first request
198 * will establish the initial connection.
199 *
200 * Concurrency is managed through the rw lock "lock". Only the writer is
201 * allowed to connect or disconnect, and thus only the writer can set
202 * "failed" to "false". Readers are allowed to use the "client" pointer,
203 * and to set "failed" to "true", indicating that they have encountered a
204 * failure. The "client" pointer is only valid while one holds a reader
205 * lock. Once "failed" has been set to "true", all requests (including
206 * the retry of the failing request) will attempt to gain the writer lock.
207 * When they succeed, indicating that there are no requests in flight and
208 * thus no outstanding references to the CLIENT structure, they check
209 * again to see if the connection is still failed (since another thread
210 * might have fixed it), and then if it is still failed they disconnect
211 * and reconnect.
212 */
213
214 /*
215 * Make an RPC call. Automatically reconnect if the connection to idmapd
216 * fails. Convert RPC results to idmap return codes.
217 */
218 idmap_stat
_idmap_clnt_call(const rpcproc_t procnum,const xdrproc_t inproc,const caddr_t in,const xdrproc_t outproc,caddr_t out,const struct timeval tout)219 _idmap_clnt_call(
220 const rpcproc_t procnum,
221 const xdrproc_t inproc,
222 const caddr_t in,
223 const xdrproc_t outproc,
224 caddr_t out,
225 const struct timeval tout)
226 {
227 enum clnt_stat clntstat;
228 idmap_stat rc;
229
230 (void) rw_rdlock(&idmap_handle.lock);
231 for (;;) {
232 if (idmap_handle.failed) {
233 /* No connection. Bid to see if we should fix it. */
234 (void) rw_unlock(&idmap_handle.lock);
235 /* Somebody else might fix it here. */
236 (void) rw_wrlock(&idmap_handle.lock);
237 /*
238 * At this point, everybody else is asleep waiting
239 * for us. Check to see if somebody else has already
240 * fixed the problem.
241 */
242 if (idmap_handle.failed) {
243 /* It's our job to fix. */
244 _idmap_clnt_disconnect();
245 rc = _idmap_clnt_connect();
246 if (rc != IDMAP_SUCCESS) {
247 /* We couldn't fix it. */
248 assert(idmap_handle.failed);
249 assert(idmap_handle.client == NULL);
250 break;
251 }
252 /* We fixed it. */
253 idmap_handle.failed = B_FALSE;
254 }
255
256 /* It's fixed now. */
257 (void) rw_unlock(&idmap_handle.lock);
258 /*
259 * Starting here, somebody might declare it failed
260 * again.
261 */
262 (void) rw_rdlock(&idmap_handle.lock);
263 continue;
264 }
265
266 clntstat = clnt_call(idmap_handle.client, procnum, inproc, in,
267 outproc, out, tout);
268 rc = _idmap_rpc2stat(clntstat, idmap_handle.client);
269 if (rc == IDMAP_ERR_RPC_HANDLE) {
270 /* Failed. Needs to be reconnected. */
271 idmap_handle.failed = B_TRUE;
272 continue;
273 }
274
275 /* Success or unrecoverable failure. */
276 break;
277 }
278 (void) rw_unlock(&idmap_handle.lock);
279 return (rc);
280 }
281
282 #define MIN_STACK_NEEDS 65536
283
284 /*
285 * Connect to idmapd.
286 * Must be single-threaded through rw_wrlock(&idmap_handle.lock).
287 */
288 static
289 idmap_stat
_idmap_clnt_connect(void)290 _idmap_clnt_connect(void)
291 {
292 uint_t sendsz = 0;
293 stack_t st;
294
295 /*
296 * clnt_door_call() alloca()s sendsz bytes (twice too, once for
297 * the call args buffer and once for the call result buffer), so
298 * we want to pick a sendsz that will be large enough, but not
299 * too large.
300 */
301 if (stack_getbounds(&st) == 0) {
302 /*
303 * Estimate how much stack space is left;
304 * st.ss_sp is the top of stack.
305 */
306 if ((char *)&sendsz < (char *)st.ss_sp)
307 /* stack grows up */
308 sendsz = ((char *)st.ss_sp - (char *)&sendsz);
309 else
310 /* stack grows down */
311 sendsz = ((char *)&sendsz - (char *)st.ss_sp);
312
313 if (sendsz <= MIN_STACK_NEEDS) {
314 sendsz = 0; /* RPC call may fail */
315 } else {
316 /* Leave 64Kb (just a guess) for our needs */
317 sendsz -= MIN_STACK_NEEDS;
318
319 /* Divide the stack space left by two */
320 sendsz = RNDUP(sendsz / 2);
321
322 /* Limit sendsz to 256KB */
323 if (sendsz > IDMAP_MAX_DOOR_RPC)
324 sendsz = IDMAP_MAX_DOOR_RPC;
325 }
326 }
327
328 idmap_handle.client = clnt_door_create(IDMAP_PROG, IDMAP_V1, sendsz);
329 if (idmap_handle.client == NULL)
330 return (IDMAP_ERR_RPC);
331
332 return (IDMAP_SUCCESS);
333 }
334
335 /*
336 * Disconnect from idmapd, if we're connected.
337 */
338 static
339 void
_idmap_clnt_disconnect(void)340 _idmap_clnt_disconnect(void)
341 {
342 CLIENT *clnt;
343
344 clnt = idmap_handle.client;
345 if (clnt != NULL) {
346 if (clnt->cl_auth)
347 auth_destroy(clnt->cl_auth);
348 clnt_destroy(clnt);
349 idmap_handle.client = NULL;
350 }
351 }
352