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