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