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 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 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 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 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 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 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 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