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