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