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