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
cc_add(client_header_t * header,char * node,CLIENT * clntp,md_error_t * ep)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
rel_clntp(client_cache_t * cachep)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
cc_destroy(client_header_t * header)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
cl_sto(CLIENT * clntp,char * hostname,long time_out,md_error_t * ep)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 *
client_create_vers_retry(char * hostname,void * ignore,struct timeval * tout)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 *
client_create_helper(char * hostname,void * private,struct timeval * time_out)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 *
meta_client_create_retry(char * hostname,clnt_create_func_t func,void * data,time_t tout,md_error_t * ep)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 *
meta_client_create(char * host,rpcprog_t prognum,rpcvers_t version,char * nettype)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 *
metarpcopen(char * hostname,long time_out,md_error_t * ep)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
metarpcclose(CLIENT * clntp)441 metarpcclose(CLIENT *clntp)
442 {
443 }
444
445 void
metarpccloseall(void)446 metarpccloseall(void)
447 {
448 cc_destroy(&client_header);
449 }
450