summaryrefslogtreecommitdiff
path: root/usr/src/lib/libidmap/common/utils.c
diff options
context:
space:
mode:
authorjoyce mcintosh <Joyce.McIntosh@Sun.COM>2010-07-26 15:02:13 -0700
committerjoyce mcintosh <Joyce.McIntosh@Sun.COM>2010-07-26 15:02:13 -0700
commit1fdeec650620e8498c06f832ea4bd2292f7e9632 (patch)
tree93e66a90f7f7260ca1086e7701a6c8dc1e46c5fb /usr/src/lib/libidmap/common/utils.c
parent1a024a4828552f36f41749085bad547c64a0f0a6 (diff)
downloadillumos-joyent-1fdeec650620e8498c06f832ea4bd2292f7e9632.tar.gz
6779186 need domain controller hot failover
6970986 Level II oplocks - smb_oplock_grant_t shouldn't be dynamically allocated 6971031 Unable add ACL on the share which has only the default owner tab by ZFS 6971899 OpenSSL not MT-safe and takes down smbd 6936762 libidmap should transparently handle interruption in connection to idmapd 6954902 mapping to unknown type does not use directory-based mapping information --HG-- rename : usr/src/lib/libidmap/common/idmap_priv.h => usr/src/cmd/idmap/idmap/namemaps.h
Diffstat (limited to 'usr/src/lib/libidmap/common/utils.c')
-rw-r--r--usr/src/lib/libidmap/common/utils.c237
1 files changed, 213 insertions, 24 deletions
diff --git a/usr/src/lib/libidmap/common/utils.c b/usr/src/lib/libidmap/common/utils.c
index 1c073b75d6..1068e0aa2c 100644
--- a/usr/src/lib/libidmap/common/utils.c
+++ b/usr/src/lib/libidmap/common/utils.c
@@ -19,12 +19,9 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Utility routines
*/
@@ -33,6 +30,9 @@
#include <stdlib.h>
#include <errno.h>
#include <libintl.h>
+#include <assert.h>
+#include <ucontext.h>
+#include <pthread.h>
#include "idmap_impl.h"
#define _UDT_SIZE_INCR 1
@@ -41,6 +41,21 @@
static struct timeval TIMEOUT = { 25, 0 };
+struct idmap_handle {
+ CLIENT *client;
+ boolean_t failed;
+ rwlock_t lock;
+};
+
+static struct idmap_handle idmap_handle = {
+ NULL, /* client */
+ B_TRUE, /* failed */
+ DEFAULTRWLOCK, /* lock */
+};
+
+static idmap_stat _idmap_clnt_connect(void);
+static void _idmap_clnt_disconnect(void);
+
idmap_retcode
_udt_extend_batch(idmap_udt_handle_t *udthandle)
{
@@ -103,13 +118,10 @@ _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)
{
-
- CLIENT *clnt;
- enum clnt_stat clntstat;
+ idmap_stat rc;
iter->next = 0;
iter->retlist = NULL;
- _IDMAP_GET_CLIENT_HANDLE(iter->ih, clnt);
/* init the result */
if (*list) {
@@ -122,20 +134,25 @@ _iter_get_next_list(int type, idmap_iter_t *iter,
}
(void) memset(*list, 0, valsize);
- clntstat = clnt_call(clnt, type,
+ rc = _idmap_clnt_call(type,
xdr_arg_proc, (caddr_t)arg,
xdr_res_proc, (caddr_t)*list,
TIMEOUT);
- if (clntstat != RPC_SUCCESS) {
+ if (rc != IDMAP_SUCCESS) {
free(*list);
- return (_idmap_rpc2stat(clnt));
+ return (rc);
}
iter->retlist = *list;
return (IDMAP_SUCCESS);
}
+/*
+ * Convert the return values from an RPC request into an idmap return code.
+ * Set errno on error.
+ */
+static
idmap_stat
-_idmap_rpc2stat(CLIENT *clnt)
+_idmap_rpc2stat(enum clnt_stat clntstat, CLIENT *clnt)
{
/*
* We only deal with door_call(3C) errors here. We look at
@@ -144,19 +161,191 @@ _idmap_rpc2stat(CLIENT *clnt)
* and others.
*/
struct rpc_err r_err;
- if (clnt) {
- clnt_geterr(clnt, &r_err);
- errno = r_err.re_errno;
- switch (r_err.re_errno) {
- case ENOMEM:
- return (IDMAP_ERR_MEMORY);
- case EBADF:
- return (IDMAP_ERR_RPC_HANDLE);
- default:
- return (IDMAP_ERR_RPC);
+
+ if (clntstat == RPC_SUCCESS)
+ return (IDMAP_SUCCESS);
+
+ clnt_geterr(clnt, &r_err);
+ errno = r_err.re_errno;
+ switch (r_err.re_errno) {
+ case ENOMEM:
+ return (IDMAP_ERR_MEMORY);
+ case EBADF:
+ return (IDMAP_ERR_RPC_HANDLE);
+ default:
+ return (IDMAP_ERR_RPC);
+ }
+}
+
+/*
+ * Management of the connection to idmapd.
+ *
+ * The intent is that connections to idmapd are automatically maintained,
+ * reconnecting if necessary. No attempt is made to retry connnection
+ * attempts; a failure to connect yields an immediate error return.
+ *
+ * State of the connection is maintained through the "client" and "failed"
+ * elements of the handle structure:
+ *
+ * client failed
+ * NULL true Failed on a previous request and was not recovered.
+ * NULL false Should never happen.
+ * nonNULL true Structure exists, but an error has occurred. Waiting
+ * for a chance to attempt to reconnect.
+ * nonNULL false Connection is good.
+ *
+ * Note that the initial state is NULL/true, so that the first request
+ * will establish the initial connection.
+ *
+ * Concurrency is managed through the rw lock "lock". Only the writer is
+ * allowed to connect or disconnect, and thus only the writer can set
+ * "failed" to "false". Readers are allowed to use the "client" pointer,
+ * and to set "failed" to "true", indicating that they have encountered a
+ * failure. The "client" pointer is only valid while one holds a reader
+ * lock. Once "failed" has been set to "true", all requests (including
+ * the retry of the failing request) will attempt to gain the writer lock.
+ * When they succeed, indicating that there are no requests in flight and
+ * thus no outstanding references to the CLIENT structure, they check
+ * again to see if the connection is still failed (since another thread
+ * might have fixed it), and then if it is still failed they disconnect
+ * and reconnect.
+ */
+
+/*
+ * Make an RPC call. Automatically reconnect if the connection to idmapd
+ * fails. Convert RPC results to idmap return codes.
+ */
+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)
+{
+ enum clnt_stat clntstat;
+ idmap_stat rc;
+
+ (void) rw_rdlock(&idmap_handle.lock);
+ for (;;) {
+ if (idmap_handle.failed) {
+ /* No connection. Bid to see if we should fix it. */
+ (void) rw_unlock(&idmap_handle.lock);
+ /* Somebody else might fix it here. */
+ (void) rw_wrlock(&idmap_handle.lock);
+ /*
+ * At this point, everybody else is asleep waiting
+ * for us. Check to see if somebody else has already
+ * fixed the problem.
+ */
+ if (idmap_handle.failed) {
+ /* It's our job to fix. */
+ _idmap_clnt_disconnect();
+ rc = _idmap_clnt_connect();
+ if (rc != IDMAP_SUCCESS) {
+ /* We couldn't fix it. */
+ assert(idmap_handle.failed);
+ assert(idmap_handle.client == NULL);
+ break;
+ }
+ /* We fixed it. */
+ idmap_handle.failed = B_FALSE;
+ }
+
+ /* It's fixed now. */
+ (void) rw_unlock(&idmap_handle.lock);
+ /*
+ * Starting here, somebody might declare it failed
+ * again.
+ */
+ (void) rw_rdlock(&idmap_handle.lock);
+ continue;
+ }
+
+ clntstat = clnt_call(idmap_handle.client, procnum, inproc, in,
+ outproc, out, tout);
+ rc = _idmap_rpc2stat(clntstat, idmap_handle.client);
+ if (rc == IDMAP_ERR_RPC_HANDLE) {
+ /* Failed. Needs to be reconnected. */
+ idmap_handle.failed = B_TRUE;
+ continue;
+ }
+
+ /* Success or unrecoverable failure. */
+ break;
+ }
+ (void) rw_unlock(&idmap_handle.lock);
+ return (rc);
+}
+
+#define MIN_STACK_NEEDS 65536
+
+/*
+ * Connect to idmapd.
+ * Must be single-threaded through rw_wrlock(&idmap_handle.lock).
+ */
+static
+idmap_stat
+_idmap_clnt_connect(void)
+{
+ uint_t sendsz = 0;
+ stack_t st;
+
+ /*
+ * clnt_door_call() alloca()s sendsz bytes (twice too, once for
+ * the call args buffer and once for the call result buffer), so
+ * we want to pick a sendsz that will be large enough, but not
+ * too large.
+ */
+ if (stack_getbounds(&st) == 0) {
+ /*
+ * Estimate how much stack space is left;
+ * st.ss_sp is the top of stack.
+ */
+ if ((char *)&sendsz < (char *)st.ss_sp)
+ /* stack grows up */
+ sendsz = ((char *)st.ss_sp - (char *)&sendsz);
+ else
+ /* stack grows down */
+ sendsz = ((char *)&sendsz - (char *)st.ss_sp);
+
+ if (sendsz <= MIN_STACK_NEEDS) {
+ sendsz = 0; /* RPC call may fail */
+ } else {
+ /* Leave 64Kb (just a guess) for our needs */
+ sendsz -= MIN_STACK_NEEDS;
+
+ /* Divide the stack space left by two */
+ sendsz = RNDUP(sendsz / 2);
+
+ /* Limit sendsz to 256KB */
+ if (sendsz > IDMAP_MAX_DOOR_RPC)
+ sendsz = IDMAP_MAX_DOOR_RPC;
}
}
- /* null handle */
- return (IDMAP_ERR_RPC_HANDLE);
+ idmap_handle.client = clnt_door_create(IDMAP_PROG, IDMAP_V1, sendsz);
+ if (idmap_handle.client == NULL)
+ return (IDMAP_ERR_RPC);
+
+ return (IDMAP_SUCCESS);
+}
+
+/*
+ * Disconnect from idmapd, if we're connected.
+ */
+static
+void
+_idmap_clnt_disconnect(void)
+{
+ CLIENT *clnt;
+
+ clnt = idmap_handle.client;
+ if (clnt != NULL) {
+ if (clnt->cl_auth)
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ idmap_handle.client = NULL;
+ }
}