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 2008 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 #include <strings.h> 29 #include <stdlib.h> 30 #include <syslog.h> 31 #include <errno.h> 32 #include <libintl.h> 33 #include <door.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <fcntl.h> 37 #include <procfs.h> 38 #include "cachemgr.h" 39 40 extern admin_t current_admin; 41 42 #define CLEANUP_WAIT_TIME 60 43 44 typedef enum cleanup_type { 45 CLEANUP_ALL = 1, 46 CLEANUP_BY_PID = 2 47 } cleanup_type_t; 48 49 typedef struct cleanup_op { 50 pid_t pid; 51 cleanup_type_t type; 52 } cleanup_op_t; 53 54 typedef struct main_nscd_struct { 55 pid_t pid; /* main nscd pid */ 56 thread_t tid; /* main nscd tid */ 57 int in_progress; /* A main nscd thread is */ 58 /* waiting for change or */ 59 /* copying data */ 60 int is_waiting_cleanup; /* A main nscd thread is */ 61 /* waiting for another main */ 62 /* nscd thread to be cleaned */ 63 /* up */ 64 } main_nscd_t; 65 66 static chg_info_t chg = { DEFAULTMUTEX, DEFAULTCV, 0, NULL, NULL, NULL, 0 }; 67 68 static main_nscd_t chg_main_nscd = {0, 0, 0, 0}; 69 static mutex_t chg_main_nscd_lock = DEFAULTMUTEX; 70 static cond_t chg_main_nscd_cv = DEFAULTCV; 71 72 /* 73 * The cookie of the configuration and its mutex 74 */ 75 static ldap_get_chg_cookie_t config_cookie = {0, 0}; 76 static mutex_t config_cookie_lock = DEFAULTMUTEX; 77 78 static void cleanup_thread_by_pid(pid_t pid); 79 80 ldap_get_chg_cookie_t 81 chg_config_cookie_get(void) 82 { 83 ldap_get_chg_cookie_t cookie; 84 (void) mutex_lock(&config_cookie_lock); 85 cookie = config_cookie; 86 (void) mutex_unlock(&config_cookie_lock); 87 return (cookie); 88 } 89 90 static void 91 chg_config_cookie_increment_seq_num(void) 92 { 93 (void) mutex_lock(&config_cookie_lock); 94 config_cookie.seq_num++; 95 (void) mutex_unlock(&config_cookie_lock); 96 } 97 98 void 99 chg_config_cookie_set(ldap_get_chg_cookie_t *cookie) 100 { 101 (void) mutex_lock(&config_cookie_lock); 102 config_cookie.mgr_pid = cookie->mgr_pid; 103 config_cookie.seq_num = cookie->seq_num; 104 (void) mutex_unlock(&config_cookie_lock); 105 } 106 static boolean_t 107 chg_cookie_equal(ldap_get_chg_cookie_t *c1, ldap_get_chg_cookie_t *c2) 108 { 109 if (c1->mgr_pid == c2->mgr_pid && c1->seq_num == c2->seq_num) 110 return (B_TRUE); 111 else 112 return (B_FALSE); 113 } 114 /* 115 * Create a node in the list and output the node. The caller can NOT free it. 116 */ 117 static int 118 waiting_list_add(chg_info_t *info, pid_t pid, thread_t tid, 119 waiting_list_t **wlp) 120 { 121 122 waiting_list_t *wl; 123 124 *wlp = NULL; 125 126 if ((wl = (waiting_list_t *)calloc(1, sizeof (waiting_list_t))) 127 == NULL) { 128 logit("waiting_list_add: No memory. pid %ld tid %d\n", 129 pid, tid); 130 return (CHG_NO_MEMORY); 131 } 132 133 wl->pid = pid; 134 wl->tid = tid; 135 136 if (info->chg_w_first == NULL) { 137 info->chg_w_first = wl; 138 info->chg_w_last = wl; 139 } else { 140 info->chg_w_last->next = wl; 141 wl->prev = info->chg_w_last; 142 info->chg_w_last = wl; 143 } 144 *wlp = wl; 145 return (CHG_SUCCESS); 146 } 147 148 /* 149 * Find a node with matching tid in the list and remove it from the list. 150 */ 151 static int 152 waiting_list_delete(chg_info_t *info, thread_t tid) 153 { 154 waiting_list_t *wl; 155 156 for (wl = info->chg_w_first; wl != NULL; wl = wl->next) { 157 if (wl->tid == tid) { 158 if (wl->next == NULL) { 159 if (wl->prev == NULL) { 160 info->chg_w_first = NULL; 161 info->chg_w_last = NULL; 162 } else { 163 wl->prev->next = NULL; 164 info->chg_w_last = wl->prev; 165 } 166 } else { 167 if (wl->prev == NULL) { 168 wl->next->prev = NULL; 169 info->chg_w_first = wl->next; 170 } else { 171 wl->prev->next = wl->next; 172 wl->next->prev = wl->prev; 173 } 174 } 175 free(wl); 176 return (CHG_SUCCESS); 177 } 178 } 179 return (CHG_NOT_FOUND_IN_WAITING_LIST); 180 } 181 182 /* 183 * Delete the thread from the waiting list and remove data when the list 184 * is empty. 185 */ 186 static void 187 waiting_list_cleanup(chg_info_t *chg, thread_t tid) 188 { 189 int rc; 190 191 rc = waiting_list_delete(chg, tid); 192 193 if (rc == CHG_SUCCESS && chg->chg_w_first == NULL) { 194 free(chg->chg_data); 195 chg->chg_data = NULL; 196 chg->chg_wakeup = 0; 197 } 198 } 199 200 /* 201 * Set flag by pid so it can be cleaned up. 202 */ 203 static void 204 waiting_list_set_cleanup(chg_info_t *info, pid_t pid) 205 { 206 waiting_list_t *wl; 207 208 for (wl = info->chg_w_first; wl != NULL; wl = wl->next) { 209 if (wl->pid == pid) { 210 wl->cleanup = 1; 211 break; 212 } 213 } 214 } 215 216 /* 217 * Return: 1 - door client is dead, 0 - door client is alive 218 */ 219 static int 220 door_client_dead(void) 221 { 222 ucred_t *uc = NULL; 223 int rc; 224 225 if (door_ucred(&uc) == -1 && errno == EINVAL) { 226 rc = 1; 227 } else { 228 rc = 0; 229 } 230 if (uc) 231 ucred_free(uc); 232 233 return (rc); 234 } 235 236 /* 237 * This function handles GETSTATUSCHANGE call from main nscd. 238 * The call can be a START op or STOP op. A cookie is sent from main nscd too. 239 * The static global variable main_nscd keeps record of pid, tid and some flags. 240 * If the thread is door_return(), main_nscd.pid, main_nscd.tid are set to 0. 241 * When the call is START op, it checks if main_nscd.pid is 0. If it is, it 242 * proceeds to wait for the change notification. If it's not, which means 243 * another main nscd handling thread is still around. It sends broadcast to 244 * clean up that thread and wait until the cleanup is done then proceeds to 245 * wait for the change notification. If same main nscd sends START op 246 * repeatedly, it'll be rejected. 247 * It also checks the cookie from main nscd. If it's not the same as 248 * ldap_cachemgr's cookie, door returns config change. 249 * If the door call is STOP op, it creates a thread to clean up main nscd START 250 * thread so it won't be blocking. 251 * In waiting for the change notification phase, the thread is waken up by 252 * the notification threads or by the cleanup threads. 253 * If it's a notification, it copies data to the stack then door return. 254 * If it's a cleanup, door_client_dead() is called to verify it then 255 * door return. 256 */ 257 int 258 chg_get_statusChange(LineBuf *info, ldap_call_t *in, pid_t nscd_pid) 259 { 260 int rc = CHG_SUCCESS, another_main_nscd_thread_alive = 0; 261 int len, return_now; 262 thread_t this_tid = thr_self(); 263 waiting_list_t *wl = NULL; 264 ldap_get_change_out_t *cout; 265 ldap_get_chg_cookie_t cookie; 266 267 info->str = NULL; 268 info->len = 0; 269 270 if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_START) { 271 272 (void) mutex_lock(&chg_main_nscd_lock); 273 if (chg_main_nscd.pid != 0) { 274 if (nscd_pid != chg_main_nscd.pid) { 275 /* 276 * This is the case that nscd doesn't shut down 277 * properly(e.g. core) and STOP op is not sent, 278 * the thread handling it is still around and 279 * not cleaned up yet. 280 * Test if the thread is still alive. 281 * If it is, clean it up. 282 * For thr_kill, if sig is 0, a validity check 283 * is done for the existence of the target 284 * thread; no signal is sent. 285 */ 286 if (thr_kill(chg_main_nscd.tid, 0) == 0) { 287 another_main_nscd_thread_alive = 1; 288 cleanup_thread_by_pid( 289 chg_main_nscd.pid); 290 } 291 } else if (chg_main_nscd.in_progress || 292 chg_main_nscd.is_waiting_cleanup) { 293 /* 294 * Same nscd pid can only send door call 295 * one at a time and wait for ldap_cachemgr to 296 * return change data. If it's the same pid 297 * again, it's an nscd error. 298 */ 299 (void) mutex_unlock(&chg_main_nscd_lock); 300 return (CHG_NSCD_REPEATED_CALL); 301 } 302 } 303 /* 304 * Wait for another thread to be cleaned up if it's alive. 305 * After that this cond var is waken up. 306 */ 307 if (another_main_nscd_thread_alive) { 308 while (chg_main_nscd.in_progress) { 309 chg_main_nscd.is_waiting_cleanup = 1; 310 (void) cond_wait(&chg_main_nscd_cv, 311 &chg_main_nscd_lock); 312 } 313 } 314 315 /* 316 * Replace pid and tid and set the flag. 317 */ 318 chg_main_nscd.is_waiting_cleanup = 0; 319 chg_main_nscd.pid = nscd_pid; 320 chg_main_nscd.tid = this_tid; 321 chg_main_nscd.in_progress = 1; 322 (void) mutex_unlock(&chg_main_nscd_lock); 323 324 cookie = chg_config_cookie_get(); 325 326 if (!chg_cookie_equal(&cookie, &in->ldap_u.get_change.cookie)) { 327 /* 328 * different cookie, set new cookie and 329 * return door call right away 330 */ 331 len = sizeof (ldap_get_change_out_t); 332 if ((cout = calloc(1, len)) == NULL) { 333 rc = CHG_NO_MEMORY; 334 } else { 335 cout->type = NS_STATUS_CHANGE_TYPE_CONFIG; 336 cout->cookie = cookie; 337 info->str = (char *)cout; 338 info->len = len; 339 } 340 341 } else { 342 (void) mutex_lock(&chg.chg_lock); 343 344 /* wait for the change notification */ 345 rc = waiting_list_add(&chg, nscd_pid, this_tid, &wl); 346 if (rc == CHG_SUCCESS) { 347 return_now = 0; 348 while (!chg.chg_wakeup) { 349 if (wl->cleanup || 350 door_client_dead()) { 351 return_now = 1; 352 break; 353 } 354 (void) cond_wait(&chg.chg_cv, 355 &chg.chg_lock); 356 } 357 /* Check if door client is still alive again */ 358 if (!return_now && !wl->cleanup && 359 !door_client_dead()) { 360 /* copy data to buffer */ 361 if ((info->str = malloc( 362 chg.chg_data_size)) == NULL) { 363 rc = CHG_NO_MEMORY; 364 } else { 365 (void) memcpy(info->str, 366 chg.chg_data, 367 chg.chg_data_size); 368 info->len = chg.chg_data_size; 369 } 370 } 371 waiting_list_cleanup(&chg, this_tid); 372 } 373 (void) mutex_unlock(&chg.chg_lock); 374 } 375 376 377 /* 378 * Reset pid, tid and flag, send wakeup signal. 379 */ 380 (void) mutex_lock(&chg_main_nscd_lock); 381 chg_main_nscd.pid = 0; 382 chg_main_nscd.tid = 0; 383 chg_main_nscd.in_progress = 0; 384 if (chg_main_nscd.is_waiting_cleanup) 385 (void) cond_broadcast(&chg_main_nscd_cv); 386 387 (void) mutex_unlock(&chg_main_nscd_lock); 388 389 } else if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_STOP) { 390 391 cleanup_thread_by_pid(nscd_pid); 392 rc = CHG_SUCCESS; 393 394 } else { 395 rc = CHG_INVALID_PARAM; 396 } 397 if (rc == CHG_EXCEED_MAX_THREADS) 398 cleanup_thread_by_pid(0); 399 400 return (rc); 401 } 402 403 /* 404 * This function copies the header and data stream to the buffer 405 * then send broadcast to wake up the chg_get_statusChange() threads. 406 */ 407 int 408 chg_notify_statusChange(char *str) 409 { 410 ldap_get_change_out_t *cout = (ldap_get_change_out_t *)str; 411 412 cout->cookie = chg_config_cookie_get(); 413 414 (void) mutex_lock(&chg.chg_lock); 415 if (chg.chg_w_first != NULL && chg.chg_wakeup == 0) { 416 417 if (chg.chg_data) { 418 free(chg.chg_data); 419 chg.chg_data = NULL; 420 } 421 422 chg.chg_data = str; 423 424 if (cout->type == NS_STATUS_CHANGE_TYPE_CONFIG) 425 chg.chg_data_size = sizeof (ldap_get_change_out_t); 426 else 427 /* NS_STATUS_CHANGE_TYPE_SERVER */ 428 chg.chg_data_size = sizeof (ldap_get_change_out_t) - 429 sizeof (int) + cout->data_size; 430 431 chg.chg_wakeup = 1; 432 (void) cond_broadcast(&chg.chg_cv); 433 } 434 (void) mutex_unlock(&chg.chg_lock); 435 436 return (CHG_SUCCESS); 437 } 438 439 /* 440 * This is called when the configuration is refreshed. 441 * The new configuration is different from the current one, a notification 442 * is sent tochg_get_statusChange() threads. 443 */ 444 void 445 chg_test_config_change(ns_config_t *new, int *change_status) 446 { 447 int changed = 0; 448 LineBuf new_cfg, cur_cfg; 449 ns_ldap_error_t *errp = NULL; 450 ldap_config_out_t *new_out, *cur_out; 451 ldap_get_change_out_t *cout; 452 453 (void) memset(&new_cfg, 0, sizeof (LineBuf)); 454 (void) memset(&cur_cfg, 0, sizeof (LineBuf)); 455 /* 456 * Flatten the config data of the newly downloaded config and 457 * current default config and compare both. 458 */ 459 if ((errp = __ns_ldap_LoadDoorInfo(&new_cfg, NULL, new)) != NULL) { 460 __ns_ldap_freeError(&errp); 461 /* error, assume the config is changed */ 462 changed = 1; 463 } else if ((errp = __ns_ldap_LoadDoorInfo(&cur_cfg, NULL, NULL)) 464 != NULL) { 465 __ns_ldap_freeError(&errp); 466 /* error, assume the config is changed */ 467 changed = 1; 468 } 469 if (changed == 0) { 470 new_out = (ldap_config_out_t *)new_cfg.str; 471 cur_out = (ldap_config_out_t *)cur_cfg.str; 472 if (strcmp(new_out->config_str, cur_out->config_str) != 0) { 473 changed = 1; 474 if (current_admin.debug_level >= DBG_PROFILE_REFRESH) { 475 logit("config changed.\n"); 476 } 477 } 478 } 479 if (cur_cfg.str) 480 free(cur_cfg.str); 481 if (new_cfg.str) 482 free(new_cfg.str); 483 484 if (changed) { 485 486 if ((cout = calloc(1, sizeof (ldap_get_change_out_t))) 487 == NULL) { 488 logit("chg_test_config_change: No Memory\n"); 489 } else { 490 /* 491 * Replace the currentdefault config with the new 492 * config 493 */ 494 __s_api_init_config(new); 495 chg_config_cookie_increment_seq_num(); 496 cout->type = NS_STATUS_CHANGE_TYPE_CONFIG; 497 /* 498 * cout->cookie is set by 499 * chg_notify_statusChange 500 */ 501 (void) chg_notify_statusChange((char *)cout); 502 } 503 } else { 504 __s_api_destroy_config(new); 505 } 506 507 *change_status = changed; 508 } 509 510 /* 511 * Wake up chg_get_statusChange() threads to clean up the threads 512 * that main nscd doesn't exist on the other of door anymore or 513 * the thread is marked as cleanup. 514 */ 515 static void 516 cleanup_threads(chg_info_t *chg, pid_t pid, cleanup_type_t type) 517 { 518 (void) mutex_lock(&chg->chg_lock); 519 if (type == CLEANUP_BY_PID) 520 waiting_list_set_cleanup(chg, pid); 521 /* 522 * wake up threads without setting chg.chg_wakeup. 523 * It's for cleanup purpose, not for notifying changes. 524 */ 525 (void) cond_broadcast(&chg->chg_cv); 526 (void) mutex_unlock(&chg->chg_lock); 527 } 528 /* 529 * If arg is NULL, it loops forever, 530 * else it calls cleanup_threads once and exits. 531 */ 532 void * 533 chg_cleanup_waiting_threads(void *arg) 534 { 535 cleanup_op_t *op = (cleanup_op_t *)arg; 536 cleanup_type_t type = 0; 537 pid_t pid; 538 int always = 1, waiting; 539 540 if (op == NULL) { 541 waiting = 1; 542 type = CLEANUP_ALL; 543 pid = 0; 544 } else { 545 waiting = 0; 546 type = op->type; 547 pid = op->pid; 548 } 549 550 while (always) { 551 if (waiting) 552 (void) sleep(CLEANUP_WAIT_TIME); 553 cleanup_threads(&chg, pid, type); 554 if (!waiting) 555 break; 556 } 557 558 if (op) 559 free(op); 560 561 thr_exit(NULL); 562 return (NULL); 563 } 564 /* 565 * The door server thead which has the door client pid will be marked 566 * as to be clean up. If pid is 0, no marking and just clean up all. 567 */ 568 static void 569 cleanup_thread_by_pid(pid_t pid) 570 { 571 cleanup_op_t *op; 572 573 if ((op = malloc(sizeof (cleanup_op_t))) == NULL) 574 return; 575 576 op->pid = pid; 577 /* clean up all if pid is 0 */ 578 if (pid == 0) 579 op->type = CLEANUP_ALL; 580 else 581 op->type = CLEANUP_BY_PID; 582 583 if (thr_create(NULL, 0, chg_cleanup_waiting_threads, 584 (void *)op, THR_BOUND|THR_DETACHED, NULL) != 0) { 585 free(op); 586 logit("thr_create failed for cleanup_thread_by_pid(%ld)\n", 587 pid); 588 } 589 } 590 591 /* 592 * Output a psinfo of an nscd process with process id pid 593 * Return: 0 - Can't find the process or it's not nscd 594 * 1 - psinfo found 595 * Note: If info is NULL, returns 0 or 1 only and no output from info. 596 */ 597 static int 598 get_nscd_psinfo(pid_t pid, psinfo_t *info) 599 { 600 psinfo_t pinfo; 601 char fname[MAXPATHLEN]; 602 ssize_t ret; 603 int fd; 604 605 if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) { 606 if ((fd = open(fname, O_RDONLY)) >= 0) { 607 ret = read(fd, &pinfo, sizeof (psinfo_t)); 608 (void) close(fd); 609 if ((ret == sizeof (psinfo_t)) && 610 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 611 if (info) 612 *info = pinfo; 613 return (1); 614 } 615 } 616 } 617 return (0); 618 } 619 /* 620 * If the parent process is nscd and euid is 0, it's a peruser nscd. 621 */ 622 static int 623 is_peruser_nscd(pid_t pid) 624 { 625 pid_t ppid; 626 psinfo_t pinfo; 627 628 if (get_nscd_psinfo(pid, &pinfo)) { 629 ppid = pinfo.pr_ppid; 630 if (get_nscd_psinfo(ppid, &pinfo) && pinfo.pr_euid == 0) 631 /* 632 * get psinfo of parent forker nscd 633 */ 634 return (1); 635 else 636 return (0); 637 } else { 638 return (0); 639 } 640 } 641 /* 642 * Check if the door client making door call is a nscd or peruser nscd and 643 * output door client's pid. 644 */ 645 int 646 chg_is_called_from_nscd_or_peruser_nscd(char *dc_str, pid_t *pidp) 647 { 648 int rc; 649 uid_t euid; 650 pid_t pid; 651 ucred_t *uc = NULL; 652 653 if (door_ucred(&uc) != 0) { 654 rc = errno; 655 logit("door_ucred() call failed %s\n", strerror(rc)); 656 return (0); 657 } 658 euid = ucred_geteuid(uc); 659 pid = *pidp = ucred_getpid(uc); 660 661 if ((euid == 0 && is_called_from_nscd(pid)) || 662 is_peruser_nscd(pid)) { 663 if (current_admin.debug_level >= DBG_ALL) 664 logit("ldap_cachemgr received %s call from pid %ld, " 665 "uid %u, euid %u\n", dc_str, pid, 666 ucred_getruid(uc), euid); 667 rc = 1; 668 } else { 669 if (current_admin.debug_level >= DBG_CANT_FIND) 670 logit("%s call failed(cred): caller pid %ld, uid %u, " 671 "euid %u\n", dc_str, pid, 672 ucred_getruid(uc), euid); 673 674 rc = 0; 675 } 676 677 ucred_free(uc); 678 679 return (rc); 680 } 681