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