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 2003 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 * Routines to handle getprof* calls in nscd 31 */ 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <memory.h> 36 #include <signal.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <sys/door.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/types.h> 44 #include <sys/wait.h> 45 #include <thread.h> 46 #include <unistd.h> 47 #include <ucred.h> 48 #include <nss_common.h> 49 50 #include <prof_attr.h> 51 52 #include <getxby_door.h> 53 #include "server_door.h" 54 #include "nscd.h" 55 56 extern profstr_t *_getprofnam(const char *, profstr_t *, char *, int, int *); 57 58 static hash_t *nam_hash; 59 static mutex_t db_lock = DEFAULTMUTEX; 60 static waiter_t db_wait; 61 62 static void getprof_namekeepalive(int keep, int interval); 63 static void update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new, 64 int callnumber); 65 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen); 66 static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam); 67 static void do_invalidate(nsc_bucket_t **ptr, int callnumber); 68 static void getprof_invalidate_unlocked(void); 69 70 void 71 getprof_init(void) 72 { 73 nam_hash = make_hash(current_admin.prof.nsc_suggestedsize); 74 } 75 76 static void 77 do_invalidate(nsc_bucket_t ** ptr, int callnumber) 78 { 79 if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) { 80 /* leave pending calls alone */ 81 update_prof_bucket(ptr, NULL, callnumber); 82 } 83 } 84 85 static void 86 do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam) 87 { 88 89 /* 90 * be careful with ptr - it may be -1 or NULL. 91 */ 92 93 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 94 char *tmp = (char *)insertn(table, ptr->nsc_hits, 95 (int)strdup(gnam)); 96 if (tmp != (char *)-1) 97 free(tmp); 98 } 99 } 100 101 void 102 getprof_revalidate(void) 103 { 104 for (;;) { 105 int slp; 106 int interval; 107 int count; 108 109 slp = current_admin.prof.nsc_pos_ttl; 110 111 if (slp < 60) { 112 slp = 60; 113 } 114 115 if ((count = current_admin.prof.nsc_keephot) != 0) { 116 interval = (slp / 2)/count; 117 if (interval == 0) interval = 1; 118 sleep(slp * 2 / 3); 119 getprof_namekeepalive(count, interval); 120 } else { 121 sleep(slp); 122 } 123 } 124 } 125 126 static void 127 getprof_namekeepalive(int keep, int interval) 128 { 129 int *table; 130 union { 131 nsc_data_t ping; 132 char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN]; 133 } u; 134 135 int i; 136 137 if (!keep) 138 return; 139 140 table = maken(keep); 141 mutex_lock(&db_lock); 142 operate_hash(nam_hash, do_findgnams, (char *)table); 143 mutex_unlock(&db_lock); 144 145 for (i = 1; i <= keep; i++) { 146 char *tmp; 147 u.ping.nsc_call.nsc_callnumber = GETPROFNAM; 148 149 if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1) 150 continue; /* unused slot in table */ 151 152 strcpy(u.ping.nsc_call.nsc_u.name, tmp); 153 154 launch_update(&u.ping.nsc_call); 155 sleep(interval); 156 } 157 158 for (i = 1; i <= keep; i++) { 159 char *tmp; 160 if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1) 161 free(tmp); 162 } 163 164 free(table); 165 } 166 167 168 /* 169 * This routine marks all entries as invalid 170 * 171 */ 172 173 void 174 getprof_invalidate(void) 175 { 176 mutex_lock(&db_lock); 177 getprof_invalidate_unlocked(); 178 mutex_unlock(&db_lock); 179 } 180 181 static void 182 getprof_invalidate_unlocked(void) 183 { 184 operate_hash_addr(nam_hash, do_invalidate, (char *)GETPROFNAM); 185 current_admin.prof.nsc_invalidate_count++; 186 } 187 188 void 189 getprof_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now) 190 { 191 int out_of_date; 192 nsc_bucket_t *retb; 193 char **bucket; 194 195 static time_t lastmod; 196 197 int bufferspace = maxsize - sizeof (nsc_return_t); 198 199 if (current_admin.prof.nsc_enabled == 0) { 200 out->nsc_return_code = NOSERVER; 201 out->nsc_bufferbytesused = sizeof (*out); 202 return; 203 } 204 205 mutex_lock(&db_lock); 206 207 if (current_admin.prof.nsc_check_files) { 208 struct stat buf; 209 210 if (stat(PROFATTR_FILENAME, &buf) < 0) { 211 /*EMPTY*/; 212 } else if (lastmod == 0) { 213 lastmod = buf.st_mtime; 214 } else if (lastmod < buf.st_mtime) { 215 getprof_invalidate_unlocked(); 216 lastmod = buf.st_mtime; 217 } 218 } 219 220 if (current_admin.debug_level >= DBG_ALL) { 221 logit("getprof_lookup: looking for name %s\n", 222 in->nsc_u.name); 223 } 224 225 for (;;) { 226 if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) { 227 ucred_t *uc = NULL; 228 229 if (door_ucred(&uc) != 0) { 230 logit("getprof_lookup: Name too long, " 231 "but no user credential: %s\n", 232 strerror(errno)); 233 } else { 234 logit("getprof_lookup: Name too long " 235 "from pid %d uid %d\n", 236 ucred_getpid(uc), 237 ucred_getruid(uc)); 238 ucred_free(uc); 239 } 240 241 out->nsc_errno = NSS_NOTFOUND; 242 out->nsc_return_code = NOTFOUND; 243 out->nsc_bufferbytesused = sizeof (*out); 244 goto getout; 245 } 246 bucket = get_hash(nam_hash, in->nsc_u.name); 247 248 if (*bucket == (char *)-1) { /* pending lookup */ 249 if (get_clearance(in->nsc_callnumber) != 0) { 250 /* no threads available */ 251 out->nsc_return_code = NOSERVER; 252 /* cannot process now */ 253 out->nsc_bufferbytesused = 254 sizeof (*out); 255 current_admin.prof.nsc_throttle_count++; 256 goto getout; 257 } 258 nscd_wait(&db_wait, &db_lock, bucket); 259 release_clearance(in->nsc_callnumber); 260 continue; /* go back and relookup hash bucket */ 261 } 262 break; 263 } 264 265 /* 266 * check for no name_service mode 267 */ 268 269 if (*bucket == NULL && current_admin.avoid_nameservice) { 270 out->nsc_return_code = NOTFOUND; 271 out->nsc_bufferbytesused = sizeof (*out); 272 } else if ((*bucket == NULL) || /* New entry in name service */ 273 (in->nsc_callnumber & UPDATEBIT) || /* needs updating */ 274 (out_of_date = (!current_admin.avoid_nameservice && 275 (current_admin.prof.nsc_old_data_ok == 0) && 276 (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) { 277 /* time has expired */ 278 int saved_errno; 279 int saved_hits = 0; 280 profstr_t *p; 281 282 if (get_clearance(in->nsc_callnumber) != 0) { 283 /* no threads available */ 284 out->nsc_return_code = NOSERVER; 285 /* cannot process now */ 286 out->nsc_bufferbytesused = sizeof (*out); 287 current_admin.prof.nsc_throttle_count++; 288 goto getout; 289 } 290 291 if (*bucket != NULL) { 292 saved_hits = 293 ((nsc_bucket_t *)*bucket)->nsc_hits; 294 } 295 296 /* 297 * block any threads accessing this bucket if data is 298 * non-existent out of date 299 */ 300 301 if (*bucket == NULL || out_of_date) { 302 update_prof_bucket((nsc_bucket_t **)bucket, 303 (nsc_bucket_t *)-1, in->nsc_callnumber); 304 } else { 305 /* 306 * if still not -1 bucket we are doing update... 307 * mark to prevent 308 * pileups of threads if the name service is hanging.... 309 */ 310 ((nsc_bucket_t *)(*bucket))->nsc_status |= 311 ST_UPDATE_PENDING; 312 /* cleared by deletion of old data */ 313 } 314 mutex_unlock(&db_lock); 315 316 /* 317 * Call non-caching version in libnsl. 318 */ 319 p = _getprofnam(in->nsc_u.name, &out->nsc_u.prof, 320 out->nsc_u.buff + sizeof (profstr_t), 321 bufferspace, &errno); 322 saved_errno = errno; 323 324 mutex_lock(&db_lock); 325 326 release_clearance(in->nsc_callnumber); 327 328 if (p == NULL) { /* data not found */ 329 330 if (current_admin.debug_level >= DBG_CANT_FIND) { 331 logit("getprof_lookup: nscd COULDN'T FIND prof_attr name %s\n", 332 in->nsc_u.name); 333 } 334 335 if (!(UPDATEBIT & in->nsc_callnumber)) 336 current_admin.prof.nsc_neg_cache_misses++; 337 338 retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t)); 339 340 retb->nsc_refcount = 1; 341 retb->nsc_data.nsc_bufferbytesused = 342 sizeof (nsc_return_t); 343 retb->nsc_data.nsc_return_code = NOTFOUND; 344 retb->nsc_data.nsc_errno = saved_errno; 345 memcpy(out, &retb->nsc_data, 346 retb->nsc_data.nsc_bufferbytesused); 347 update_prof_bucket((nsc_bucket_t **)bucket, 348 retb, in->nsc_callnumber); 349 goto getout; 350 } else { 351 if (current_admin.debug_level >= DBG_ALL) { 352 logit("getprof_lookup: nscd FOUND prof_attr name %s\n", 353 in->nsc_u.name); 354 } 355 if (!(UPDATEBIT & in->nsc_callnumber)) 356 current_admin.prof.nsc_pos_cache_misses++; 357 358 retb = fixbuffer(out, bufferspace); 359 update_prof_bucket((nsc_bucket_t **)bucket, 360 retb, in->nsc_callnumber); 361 if (saved_hits) 362 retb->nsc_hits = saved_hits; 363 } 364 } else { /* found entry in cache */ 365 retb = (nsc_bucket_t *)*bucket; 366 367 retb->nsc_hits++; 368 369 memcpy(out, &(retb->nsc_data), 370 retb->nsc_data.nsc_bufferbytesused); 371 372 if (out->nsc_return_code == SUCCESS) { 373 if (!(UPDATEBIT & in->nsc_callnumber)) 374 current_admin.prof.nsc_pos_cache_hits++; 375 if (current_admin.debug_level >= DBG_ALL) { 376 logit("getprof_lookup: found name %s in cache\n", 377 in->nsc_u.name); 378 } 379 } else { 380 if (!(UPDATEBIT & in->nsc_callnumber)) 381 current_admin.prof.nsc_neg_cache_hits++; 382 if (current_admin.debug_level >= DBG_ALL) { 383 logit("getprof_lookup: %s marked as NOT FOUND in cache.\n", 384 in->nsc_u.name); 385 } 386 } 387 388 if ((retb->nsc_timestamp < now) && 389 !(in->nsc_callnumber & UPDATEBIT) && 390 !(retb->nsc_status & ST_UPDATE_PENDING)) { 391 logit("launch update since time = %d\n", retb->nsc_timestamp); 392 retb->nsc_status |= ST_UPDATE_PENDING; 393 /* cleared by deletion of old data */ 394 launch_update(in); 395 } 396 } 397 398 getout: 399 mutex_unlock(&db_lock); 400 } 401 402 /*ARGSUSED*/ 403 static void 404 update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber) 405 { 406 if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */ 407 free(*old); 408 current_admin.prof.nsc_entries--; 409 } 410 411 /* 412 * we can do this before reseting *old since we're holding the lock 413 */ 414 415 else if (*old == (nsc_bucket_t *)-1) { 416 nscd_signal(&db_wait, (char **)old); 417 } 418 419 *old = new; 420 421 if ((new != NULL) && (new != (nsc_bucket_t *)-1)) { 422 /* real data, not just update pending or invalidate */ 423 new->nsc_hits = 1; 424 new->nsc_status = 0; 425 new->nsc_refcount = 1; 426 current_admin.prof.nsc_entries++; 427 428 if (new->nsc_data.nsc_return_code == SUCCESS) { 429 new->nsc_timestamp = time(NULL) + 430 current_admin.prof.nsc_pos_ttl; 431 } else { 432 new->nsc_timestamp = time(NULL) + 433 current_admin.prof.nsc_neg_ttl; 434 } 435 } 436 } 437 438 /*ARGSUSED*/ 439 static nsc_bucket_t * 440 fixbuffer(nsc_return_t *in, int maxlen) 441 { 442 nsc_bucket_t *retb; 443 nsc_return_t *out; 444 char *dest; 445 int offset; 446 int strs; 447 448 /* 449 * find out the size of the data block we're going to need 450 */ 451 452 strs = attr_strlen(in->nsc_u.prof.name) + 453 attr_strlen(in->nsc_u.prof.res1) + 454 attr_strlen(in->nsc_u.prof.res2) + 455 attr_strlen(in->nsc_u.prof.desc) + 456 attr_strlen(in->nsc_u.prof.attr) + PROFATTR_DB_NCOL; 457 458 /* 459 * allocate it and copy it in 460 * code doesn't assume packing order in original buffer 461 */ 462 463 if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) { 464 return (NULL); 465 } 466 467 out = &(retb->nsc_data); 468 out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.prof - (int)out) + 469 sizeof (profstr_t); 470 out->nsc_return_code = SUCCESS; 471 out->nsc_errno = 0; 472 473 dest = retb->nsc_data.nsc_u.buff + sizeof (profstr_t); 474 offset = (int)dest; 475 476 attr_strcpy(dest, in->nsc_u.prof.name); 477 strs = 1 + attr_strlen(in->nsc_u.prof.name); 478 out->nsc_u.prof.name = dest - offset; 479 dest += strs; 480 481 attr_strcpy(dest, in->nsc_u.prof.res1); 482 strs = 1 + attr_strlen(in->nsc_u.prof.res1); 483 out->nsc_u.prof.res1 = dest - offset; 484 dest += strs; 485 486 attr_strcpy(dest, in->nsc_u.prof.res2); 487 strs = 1 + attr_strlen(in->nsc_u.prof.res2); 488 out->nsc_u.prof.res2 = dest - offset; 489 dest += strs; 490 491 attr_strcpy(dest, in->nsc_u.prof.desc); 492 strs = 1 + attr_strlen(in->nsc_u.prof.desc); 493 out->nsc_u.prof.desc = dest - offset; 494 dest += strs; 495 496 attr_strcpy(dest, in->nsc_u.prof.attr); 497 out->nsc_u.prof.attr = dest - offset; 498 499 memcpy(in, out, out->nsc_bufferbytesused); 500 501 return (retb); 502 } 503 504 void 505 getprof_reaper(void) 506 { 507 nsc_reaper("getprof", nam_hash, ¤t_admin.prof, &db_lock); 508 } 509