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