xref: /titanic_41/usr/src/lib/lvm/libmeta/common/metarpcopen.c (revision ae5b046d8f8cec187d40041c4b74b43f561d5ac7)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Just in case we're not in a build environment, make sure that
30  * TEXT_DOMAIN gets set to something.
31  */
32 #if !defined(TEXT_DOMAIN)
33 #define	TEXT_DOMAIN "SYS_TEST"
34 #endif
35 
36 #include <meta.h>
37 #include <metad.h>
38 #include <sdssc.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 
44 #define	CC_TTL_MAX	20
45 
46 typedef struct {
47 	char		*cc_node;
48 	struct timeval	cc_ttl;
49 	CLIENT		*cc_clp;
50 } client_cache_t;
51 
52 typedef struct client_header {
53 	client_cache_t	**ch_cache;	/* array of clients. */
54 	mutex_t		ch_mutex;	/* lock access to ch_cache */
55 } client_header_t;
56 
57 /*
58  * This structure is used to pass data from meta_client_create to
59  * client_create_helper via meta_client_create_retry.
60  */
61 typedef struct clnt_data {
62 	rpcprog_t	cd_prognum;	/* RPC program number */
63 	rpcvers_t	cd_version;	/* Desired interface version */
64 	char		*cd_nettype;	/* Type of network to use */
65 } clnt_data_t;
66 
67 #define	MALLOC_BLK_SIZE	10
68 static client_header_t	client_header = {(client_cache_t **)NULL, DEFAULTMUTEX};
69 
70 static void
71 cc_add(
72 	client_header_t *header,
73 	char *node,
74 	CLIENT *clntp,
75 	md_error_t *ep
76 )
77 {
78 	client_cache_t ***cachep = &header->ch_cache;
79 	struct timeval	now;
80 	int		i;
81 	int		j = 0;
82 
83 	if (gettimeofday(&now, NULL) == -1) {
84 		(void) mdsyserror(ep, errno, "gettimeofday()");
85 		return;
86 	}
87 
88 	(void) mutex_lock(&header->ch_mutex);
89 	if (*cachep) {
90 		for (i = 0; (*cachep)[i] != NULL; i++)
91 			if (strcmp((*cachep)[i]->cc_node, node) == 0 &&
92 			    (*cachep)[i]->cc_clp == NULL) {
93 				(*cachep)[i]->cc_clp = clntp;
94 				(*cachep)[i]->cc_ttl = now;
95 				(void) mutex_unlock(&header->ch_mutex);
96 				return;
97 			}
98 	} else {
99 		*cachep = Calloc(MALLOC_BLK_SIZE, sizeof (**cachep));
100 		i = 0;
101 	}
102 
103 	(*cachep)[i] = Zalloc(sizeof (***cachep));
104 	(*cachep)[i]->cc_node = Strdup(node);
105 	(*cachep)[i]->cc_clp = clntp;
106 	(*cachep)[i]->cc_ttl = now;
107 
108 	if ((++i % MALLOC_BLK_SIZE) == 0) {
109 		*cachep = Realloc(*cachep,
110 		    (i + MALLOC_BLK_SIZE) * sizeof (**cachep));
111 		for (j = i; j < (i + MALLOC_BLK_SIZE); j++)
112 			(*cachep)[j] = NULL;
113 	}
114 	(void) mutex_unlock(&header->ch_mutex);
115 }
116 
117 static void
118 rel_clntp(client_cache_t *cachep)
119 {
120 	CLIENT		*clntp = cachep->cc_clp;
121 
122 	if (clntp != NULL) {
123 		auth_destroy(clntp->cl_auth);
124 		clnt_destroy(clntp);
125 	}
126 	cachep->cc_clp = NULL;
127 }
128 
129 static void
130 cc_destroy(client_header_t *header)
131 {
132 	client_cache_t ***cachep = &header->ch_cache;
133 	int	i;
134 
135 	(void) mutex_lock(&header->ch_mutex);
136 	if (*cachep) {
137 		for (i = 0; ((*cachep)[i] != NULL); i++) {
138 			client_cache_t	*p = (*cachep)[i];
139 
140 			Free(p->cc_node);
141 			rel_clntp(p);
142 			Free(p);
143 		}
144 		Free(*cachep);
145 		*cachep = NULL;
146 	}
147 	(void) mutex_unlock(&header->ch_mutex);
148 }
149 
150 /*
151  * Set the timeout value for this client handle.
152  */
153 int
154 cl_sto(
155 	CLIENT		*clntp,
156 	char		*hostname,
157 	long		time_out,
158 	md_error_t	*ep
159 )
160 {
161 	struct timeval	nto;
162 
163 	(void) memset(&nto, '\0', sizeof (nto));
164 
165 	nto.tv_sec = time_out;
166 
167 	if (clnt_control(clntp, CLSET_TIMEOUT, (char *)&nto) != TRUE)
168 		return (mdrpcerror(ep, clntp, hostname,
169 		    dgettext(TEXT_DOMAIN, "metad client set timeout")));
170 
171 	return (0);
172 }
173 
174 /*
175  * client_create_vers_retry is the helper function to be passed to
176  * meta_client_create_retry to do the actual work of creating the client
177  * when version selection is necessary.
178  */
179 
180 /* ARGSUSED */
181 static CLIENT *
182 client_create_vers_retry(char *hostname,
183 	void *ignore,
184 	struct timeval *tout
185 )
186 {
187 	rpcvers_t	vers;		/* Version # not needed. */
188 
189 	return (clnt_create_vers_timed(hostname, METAD, &vers,
190 	    METAD_VERSION, METAD_VERSION_DEVID, "tcp", tout));
191 }
192 
193 /*
194  * client_create_helper is the helper function to be passed to
195  * meta_client_create_retry when plain vanilla client create is desired.
196  */
197 static CLIENT *
198 client_create_helper(char *hostname, void *private, struct timeval *time_out)
199 {
200 	clnt_data_t	*cd = (clnt_data_t *)private;
201 
202 	return (clnt_create_timed(hostname, cd->cd_prognum, cd->cd_version,
203 	    cd->cd_nettype, time_out));
204 }
205 
206 /*
207  * meta_client_create_retry is a general function to assist in creating RPC
208  * clients.  This function handles retrying if the attempt to create a
209  * client fails.  meta_client_create_retry itself does not actually create
210  * the client.  Instead it calls the helper function, func, to do that job.
211  *
212  * With the help of func, meta_client_create_retry will create an RPC
213  * connection allowing up to tout seconds to complete the task.  If the
214  * connection creation fails for RPC_RPCBFAILURE, RPC_CANTRECV or
215  * RPC_PROGNOTREGISTERED and tout seconds have not passed,
216  * meta_client_create_retry will try again.  The reason retries are
217  * important is that when the inet daemon is being refreshed, it can take
218  * 15-20 seconds for it to start responding again.
219  *
220  * Arguments:
221  *
222  *	hostname	- Name of remote host
223  *
224  *	func		- Pointer to the helper function, that will
225  *			  actually try to create the client.
226  *
227  *	data		- Private data to be passed on to func.
228  *			  meta_client_create_retry treats this as an opaque
229  *			  pointer.
230  *
231  *	tout		- Number of seconds to allow for the connection
232  *			  attempt.
233  *
234  *	ep		- Standard SVM error pointer.  May be NULL.
235  */
236 CLIENT *
237 meta_client_create_retry(
238 	char			*hostname,
239 	clnt_create_func_t	func,
240 	void			*data,
241 	time_t			tout,
242 	md_error_t		*ep
243 )
244 {
245 	static int		debug;		/* print debugging info */
246 	static int		debug_set = 0;
247 
248 	CLIENT			*clnt = (CLIENT *) NULL;
249 	struct timeval		curtime;
250 	char			*d;
251 	struct timeval		start;
252 	struct timeval		timeout;
253 
254 	if (debug_set == 0) {
255 		d = getenv("MD_DEBUG");
256 		if (d == NULL) {
257 			debug = 0;
258 		} else {
259 			debug = (strstr(d, "RPC") == NULL) ? 0 : 1;
260 		}
261 		debug_set = 1;
262 	}
263 	timeout.tv_usec = 0;
264 	if (gettimeofday(&start, NULL) == -1) {
265 		if (ep != (md_error_t *)NULL) {
266 			(void) mdsyserror(ep, errno, "gettimeofday()");
267 		}
268 		return (clnt);
269 	}
270 	curtime = start;
271 	while ((curtime.tv_sec - start.tv_sec) < tout) {
272 		/* Use remaining time as the timeout value. */
273 		timeout.tv_sec = tout - (curtime.tv_sec - start.tv_sec);
274 		clnt = (*func)(hostname, data, &timeout);
275 		if (clnt != (CLIENT *) NULL)
276 			break;
277 		if ((rpc_createerr.cf_stat == RPC_RPCBFAILURE) ||
278 		    (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) ||
279 		    (rpc_createerr.cf_stat == RPC_CANTRECV)) {
280 			if (debug) {
281 				clnt_pcreateerror("meta_client_create_retry");
282 			}
283 			/* If error might be fixed in time, sleep & try again */
284 			(void) sleep(2);
285 			if (gettimeofday(&curtime, NULL) == -1) {
286 				if (ep != (md_error_t *)NULL) {
287 					(void) mdsyserror(ep, errno,
288 					    "gettimeofday()");
289 				}
290 				return (clnt);
291 			}
292 		} else {
293 			/* Not a recoverable error. */
294 			break;
295 		}
296 	}
297 	if ((clnt == (CLIENT *) NULL) && (ep != (md_error_t *)NULL)) {
298 		(void) mdrpccreateerror(ep, hostname,
299 		    "meta_client_create_retry");
300 	}
301 	return (clnt);
302 }
303 
304 /*
305  * meta_client_create is intended to be used within SVM as a replacement
306  * for calls to clnt_create.  meta_client_create invokes the retry
307  * mechanism of meta_client_create_retry.
308  */
309 CLIENT *
310 meta_client_create(char *host, rpcprog_t prognum, rpcvers_t version,
311 	char *nettype)
312 {
313 	clnt_data_t		cd;
314 
315 	cd.cd_prognum = prognum;
316 	cd.cd_version = version;
317 	cd.cd_nettype = nettype;
318 	return (meta_client_create_retry(host, client_create_helper,
319 	    (void *)&cd, MD_CLNT_CREATE_TOUT, (md_error_t *)NULL));
320 }
321 
322 /*
323  * create and return RPC connection
324  */
325 CLIENT *
326 metarpcopen(
327 	char		*hostname,
328 	long		time_out,
329 	md_error_t	*ep
330 )
331 {
332 	CLIENT		*clntp = NULL;
333 	client_cache_t	***cachep = &client_header.ch_cache;
334 	int		i;
335 	long		delta;
336 	struct timeval	now;
337 	struct in_addr	p_ip;
338 	char		*host_sc = NULL;
339 	char		host_priv[18];
340 
341 	/*
342 	 * If we are in cluster mode, lets use the private interconnect
343 	 * hostnames to establish the rpc connections.
344 	 */
345 	if (sdssc_bind_library() != SDSSC_NOT_BOUND)
346 		if (sdssc_get_priv_ipaddr(hostname, &p_ip) == SDSSC_OKAY) {
347 			/*
348 			 * inet_ntoa() returns pointer to a string in the
349 			 * base 256 notation d.d.d.d (IPv4) and so
350 			 * host_priv[18] should be sufficient enough to
351 			 * hold it.
352 			 */
353 			host_sc = inet_ntoa(p_ip);
354 			if (host_sc != NULL) {
355 				int size = sizeof (host_priv);
356 				if (strlcpy(host_priv, host_sc, size) < size)
357 					hostname = host_priv;
358 			}
359 		}
360 
361 	if (gettimeofday(&now, NULL) == -1) {
362 		(void) mdsyserror(ep, errno, "gettimeofday()");
363 		return (NULL);
364 	}
365 
366 	/*
367 	 * Before trying to create the client, make sure that the core SVM
368 	 * services are enabled by the Service Management Facility.  We
369 	 * don't want to suffer the 60 second timeout if the services are
370 	 * not even enabled.  This call actually only verifies that they
371 	 * are enabled on this host no matter which host the caller wants
372 	 * to connect to.  Nonetheless, if the services are not enabled on
373 	 * the local host, our RPC stuff is not going to work as expected.
374 	 */
375 	if (meta_smf_isonline(META_SMF_CORE, ep) == 0) {
376 		return (NULL);
377 	}
378 
379 	(void) mutex_lock(&client_header.ch_mutex);
380 	if (client_header.ch_cache) {
381 		for (i = 0; (*cachep)[i] != NULL; i++) {
382 			if (strcmp((*cachep)[i]->cc_node, hostname) == 0) {
383 				clntp = (*cachep)[i]->cc_clp;
384 				if (clntp == NULL)
385 					continue;
386 				delta = now.tv_sec -
387 				    (*cachep)[i]->cc_ttl.tv_sec;
388 				if (delta > CC_TTL_MAX) {
389 					rel_clntp((*cachep)[i]);
390 					continue;
391 				}
392 				if (cl_sto(clntp, hostname, time_out,
393 				    ep) != 0) {
394 					(void) mutex_unlock(
395 					    &client_header.ch_mutex);
396 					return (NULL);
397 				}
398 				(void) mutex_unlock(&client_header.ch_mutex);
399 				return (clntp);
400 			}
401 		}
402 	}
403 	(void) mutex_unlock(&client_header.ch_mutex);
404 
405 	/*
406 	 * Try to create a version 2 client handle by default.
407 	 * If this fails (i.e. client is version 1), try to
408 	 * create a version 1 client handle.
409 	 */
410 	clntp = meta_client_create_retry(hostname, client_create_vers_retry,
411 	    (void *)NULL, MD_CLNT_CREATE_TOUT, ep);
412 
413 	/* open connection */
414 	if (clntp == NULL) {
415 		(void) mdrpccreateerror(ep, hostname,
416 		    dgettext(TEXT_DOMAIN, "metad client create"));
417 		cc_add(&client_header, hostname, NULL, ep);
418 		return (NULL);
419 	} else {
420 		auth_destroy(clntp->cl_auth);
421 		clntp->cl_auth = authsys_create_default();
422 		assert(clntp->cl_auth != NULL);
423 	}
424 
425 	cc_add(&client_header, hostname, clntp, ep);
426 
427 	if (cl_sto(clntp, hostname, time_out, ep) != 0)
428 		return (NULL);
429 
430 	return (clntp);
431 }
432 
433 /*
434  * metarpcclose - is a place holder so that when using
435  *		  metarpcopen, it does not appear that
436  *		  we have dangling opens.  We can at some
437  *		  later decrement open counts here too, if needed.
438  */
439 /*ARGSUSED*/
440 void
441 metarpcclose(CLIENT *clntp)
442 {
443 }
444 
445 void
446 metarpccloseall(void)
447 {
448 	cc_destroy(&client_header);
449 }
450