summaryrefslogtreecommitdiff
path: root/usr/src/cmd/nscd
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/nscd
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/nscd')
-rw-r--r--usr/src/cmd/nscd/Makefile110
-rw-r--r--usr/src/cmd/nscd/attrstr.c41
-rw-r--r--usr/src/cmd/nscd/getexec.c533
-rw-r--r--usr/src/cmd/nscd/getgr.c616
-rw-r--r--usr/src/cmd/nscd/gethost.c683
-rw-r--r--usr/src/cmd/nscd/getnode.c803
-rw-r--r--usr/src/cmd/nscd/getprof.c508
-rw-r--r--usr/src/cmd/nscd/getpw.c673
-rw-r--r--usr/src/cmd/nscd/getuser.c510
-rw-r--r--usr/src/cmd/nscd/hash.c346
-rw-r--r--usr/src/cmd/nscd/name-service-cache.xml105
-rw-r--r--usr/src/cmd/nscd/nscd.h126
-rw-r--r--usr/src/cmd/nscd/nscd_biggest.c115
-rw-r--r--usr/src/cmd/nscd/nscd_nischeck.c94
-rw-r--r--usr/src/cmd/nscd/nscd_parse.c402
-rw-r--r--usr/src/cmd/nscd/nscd_wait.c152
-rw-r--r--usr/src/cmd/nscd/server.c1882
-rw-r--r--usr/src/cmd/nscd/server_door.h85
-rw-r--r--usr/src/cmd/nscd/svc-nscd43
19 files changed, 7827 insertions, 0 deletions
diff --git a/usr/src/cmd/nscd/Makefile b/usr/src/cmd/nscd/Makefile
new file mode 100644
index 0000000000..4932374ed6
--- /dev/null
+++ b/usr/src/cmd/nscd/Makefile
@@ -0,0 +1,110 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= nscd
+NISPROG= nscd_nischeck
+MANIFEST= name-service-cache.xml
+SVCMETHOD= svc-nscd
+
+include ../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+$(ROOTMANIFESTDIR)/%: %
+ $(INS.file)
+
+OBJS= server.o getpw.o getgr.o gethost.o getnode.o hash.o \
+ nscd_biggest.o nscd_parse.o nscd_wait.o \
+ getexec.o getprof.o getuser.o attrstr.o
+
+NISOBJS= nscd_nischeck.o
+
+CLOBBERFILES= nscd nscd_nischeck
+
+SRCS= ${OBJS:%.o=%.c}
+
+NISSRC= ${NISOBJS:%.o=%.c}
+
+CPPFLAGS += -D_REENTRANT -DSUN_THREADS
+
+# TCOV_FLAG= -ql
+# GPROF_FLAG= -xpg
+# DEBUG_FLAG= -g
+
+$(PROG) := LDLIBS += -lresolv -lnsl -lsocket -lumem -lscf
+$(NISPROG) := LDLIBS += -lnsl
+
+# install macros and rule
+#
+GROUP= bin
+ROOTPROG= ${ROOTUSRSBIN}/nscd
+ROOTNISPROG= ${ROOTLIB}/nscd_nischeck
+
+.KEEP_STATE:
+
+all: $(PROG) $(NISPROG)
+
+${PROG}: ${OBJS}
+ ${LINK.c} ${OPT} -o $@ ${OBJS} ${LDLIBS}
+ ${POST_PROCESS}
+
+${NISPROG}: ${NISOBJS}
+ ${LINK.c} ${OPT} -o $@ ${NISOBJS} ${LDLIBS}
+ ${POST_PROCESS}
+
+lint := LINTFLAGS=-x -b -u
+
+lint:
+ $(LINT.c) ${SRCS}
+
+lint2:
+ $(LINT.c) ${NISSRC}
+
+cstyle:
+ ${CSTYLE} ${SRCS} ${NISSRC}
+
+install: all $(ROOTPROG) $(ROOTNISPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+check: $(CHKMANIFEST)
+
+clean:
+ ${RM} ${OBJS} ${NISOBJS}
+
+${ROOTUSRSBIN}/%: %
+ ${INS.file}
+
+${ROOTUSRLIB}/%: %
+ ${INS.file}
+
+
+
+include ../Makefile.targ
+
+
+
diff --git a/usr/src/cmd/nscd/attrstr.c b/usr/src/cmd/nscd/attrstr.c
new file mode 100644
index 0000000000..1d4f162458
--- /dev/null
+++ b/usr/src/cmd/nscd/attrstr.c
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <strings.h>
+
+int
+attr_strlen(char *s)
+{
+ return (s ? strlen(s) : 0);
+}
+
+char *
+attr_strcpy(char *dest, char *src)
+{
+ return (strcpy(dest, src ? src : ""));
+}
diff --git a/usr/src/cmd/nscd/getexec.c b/usr/src/cmd/nscd/getexec.c
new file mode 100644
index 0000000000..d69f994352
--- /dev/null
+++ b/usr/src/cmd/nscd/getexec.c
@@ -0,0 +1,533 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getexec* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+
+#include <exec_attr.h>
+
+#include <getxby_door.h>
+#include "server_door.h"
+#include "nscd.h"
+
+extern execstr_t *_getexecprof(char *, char *, char *, int, execstr_t *,
+ char *, int, int *);
+
+static hash_t *nam_hash;
+static mutex_t db_lock = DEFAULTMUTEX;
+static waiter_t db_wait;
+
+static getexec_namekeepalive(int keep, int interval);
+static int update_exec_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+static void getexec_invalidate_unlocked(void);
+
+
+void
+getexec_init(void)
+{
+ nam_hash = make_hash(current_admin.exec.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_exec_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(gnam));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+void
+getexec_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.exec.nsc_pos_ttl;
+
+ if (slp < 60) {
+ slp = 60;
+ }
+ if ((count = current_admin.exec.nsc_keephot) != 0) {
+ interval = (slp / 2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp * 2 / 3);
+ getexec_namekeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static
+getexec_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return (0);
+
+ table = maken(keep);
+ mutex_lock(&db_lock);
+ operate_hash(nam_hash, do_findgnams, (char *)table);
+ mutex_unlock(&db_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+
+ u.ping.nsc_call.nsc_callnumber = GETEXECID;
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+ return (0);
+}
+
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+
+void
+getexec_invalidate(void)
+{
+ mutex_lock(&db_lock);
+ getexec_invalidate_unlocked();
+ mutex_unlock(&db_lock);
+}
+
+static void
+getexec_invalidate_unlocked(void)
+{
+ operate_hash_addr(nam_hash, do_invalidate, (char *)GETEXECID);
+ current_admin.exec.nsc_invalidate_count++;
+}
+
+void
+getexec_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+ char *p_name;
+ char *p_type;
+ char *p_id;
+ char *unique;
+ char *lasts;
+ char *sep = ":";
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.exec.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&db_lock);
+
+ if (current_admin.exec.nsc_check_files) {
+ struct stat buf;
+
+ if (stat(EXECATTR_FILENAME, &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getexec_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getexec_lookup: looking for name %s\n",
+ in->nsc_u.name);
+ }
+
+ for (;;) {
+ if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getexec_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("getexec_lookup: Name too long from pid "
+ "%d uid %d\n", ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(nam_hash, in->nsc_u.name);
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused =
+ sizeof (*out);
+ current_admin.exec.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&db_wait, &db_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.exec.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ execstr_t *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.exec.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits =
+ ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_exec_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1,
+ in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update...
+ * mark to prevent
+ * pileups of threads if the name service is hanging....
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&db_lock);
+
+ /*
+ * Call non-caching version in libnsl.
+ */
+ unique = strdup(in->nsc_u.name);
+ p_name = strtok_r(unique, sep, &lasts);
+ p_type = strtok_r(NULL, sep, &lasts);
+ p_id = strtok_r(NULL, sep, &lasts);
+ p = _getexecprof(p_name,
+ p_type,
+ p_id,
+ GET_ONE,
+ &out->nsc_u.exec,
+ out->nsc_u.buff + sizeof (execstr_t),
+ bufferspace,
+ &saved_errno);
+
+ free(unique);
+ mutex_lock(&db_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ logit("getexec_lookup: nscd COULDN'T FIND exec_attr name %s\n",
+ in->nsc_u.name);
+ }
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.exec.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &retb->nsc_data,
+ retb->nsc_data.nsc_bufferbytesused);
+ update_exec_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getexec_lookup: nscd FOUND exec_attr name %s\n",
+ in->nsc_u.name);
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.exec.nsc_pos_cache_misses++;
+ retb = fixbuffer(out, bufferspace);
+ update_exec_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+ retb->nsc_hits++;
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.exec.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getexec_lookup: found name %s in cache\n",
+ in->nsc_u.name);
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.exec.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getexec_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n", retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+ mutex_unlock(&db_lock);
+}
+
+/*ARGSUSED*/
+static int
+update_exec_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
+ free(*old);
+ current_admin.exec.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&db_wait, (char **)old);
+ }
+
+ *old = new;
+
+ if ((new != NULL) && (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.exec.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.exec.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.exec.nsc_neg_ttl;
+ }
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_bucket_t *retb;
+ nsc_return_t *out;
+ char *dest;
+ int offset;
+ int strs;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = attr_strlen(in->nsc_u.exec.name) +
+ attr_strlen(in->nsc_u.exec.policy) +
+ attr_strlen(in->nsc_u.exec.type) +
+ attr_strlen(in->nsc_u.exec.res1) +
+ attr_strlen(in->nsc_u.exec.res2) +
+ attr_strlen(in->nsc_u.exec.id) +
+ attr_strlen(in->nsc_u.exec.attr) + EXECATTR_DB_NCOL;
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.exec - (int)out) +
+ sizeof (execstr_t);
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (execstr_t);
+ offset = (int)dest;
+
+ attr_strcpy(dest, in->nsc_u.exec.name);
+ strs = 1 + attr_strlen(in->nsc_u.exec.name);
+ out->nsc_u.exec.name = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.policy);
+ strs = 1 + attr_strlen(in->nsc_u.exec.policy);
+ out->nsc_u.exec.policy = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.type);
+ strs = 1 + attr_strlen(in->nsc_u.exec.type);
+ out->nsc_u.exec.type = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.res1);
+ strs = 1 + attr_strlen(in->nsc_u.exec.res1);
+ out->nsc_u.exec.res1 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.res2);
+ strs = 1 + attr_strlen(in->nsc_u.exec.res2);
+ out->nsc_u.exec.res2 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.id);
+ strs = 1 + attr_strlen(in->nsc_u.exec.id);
+ out->nsc_u.exec.id = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.exec.attr);
+ out->nsc_u.exec.attr = dest - offset;
+
+ memcpy(in, out, out->nsc_bufferbytesused);
+
+ return (retb);
+}
+
+void
+getexec_reaper(void)
+{
+ nsc_reaper("getexec", nam_hash, &current_admin.exec, &db_lock);
+}
diff --git a/usr/src/cmd/nscd/getgr.c b/usr/src/cmd/nscd/getgr.c
new file mode 100644
index 0000000000..5bf10680b4
--- /dev/null
+++ b/usr/src/cmd/nscd/getgr.c
@@ -0,0 +1,616 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getgr* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+
+#include <getxby_door.h>
+#include "server_door.h"
+#include "nscd.h"
+
+static hash_t *uid_hash;
+static hash_t *nam_hash;
+static mutex_t group_lock = DEFAULTMUTEX;
+static waiter_t group_wait;
+
+static void getgr_gidkeepalive(int keep, int interval);
+static void getgr_namekeepalive(int keep, int interval);
+static int update_gr_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findgids(nsc_bucket_t *ptr, int *table, int gid);
+static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+static void getgr_invalidate_unlocked(void);
+
+
+void
+getgr_init(void)
+{
+ uid_hash = make_ihash(current_admin.group.nsc_suggestedsize);
+ nam_hash = make_hash(current_admin.group.nsc_suggestedsize);
+
+}
+
+static void
+do_invalidate(nsc_bucket_t **ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_gr_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findgids(nsc_bucket_t *ptr, int *table, int gid)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ insertn(table, ptr->nsc_hits, gid);
+ }
+}
+
+static void
+do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(gnam));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+void
+getgr_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.group.nsc_pos_ttl;
+
+ if (slp < 60) {
+ slp = 60;
+ }
+
+ if ((count = current_admin.group.nsc_keephot) != 0) {
+ interval = (slp / 2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp * 2 / 3);
+ getgr_gidkeepalive(count, interval);
+ getgr_namekeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static void
+getgr_gidkeepalive(int keep, int interval)
+{
+ int *table;
+ nsc_data_t ping;
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&group_lock);
+ operate_hash(uid_hash, do_findgids, (char *)table);
+ mutex_unlock(&group_lock);
+
+ for (i = 1; i <= keep; i++) {
+ ping.nsc_call.nsc_callnumber = GETGRGID;
+ if ((ping.nsc_call.nsc_u.gid = table[keep + 1 + i]) == -1)
+ continue; /* unused slot in table */
+ launch_update(&ping.nsc_call);
+ sleep(interval);
+ }
+ free(table);
+}
+
+static void
+getgr_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&group_lock);
+ operate_hash(nam_hash, do_findgnams, (char *)table);
+ mutex_unlock(&group_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETGRNAM;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+
+void
+getgr_invalidate(void)
+{
+ mutex_lock(&group_lock);
+ getgr_invalidate_unlocked();
+ mutex_unlock(&group_lock);
+}
+
+static void
+getgr_invalidate_unlocked(void)
+{
+ operate_hash_addr(nam_hash, do_invalidate, (char *)GETGRNAM);
+ operate_hash_addr(uid_hash, do_invalidate, (char *)GETGRGID);
+ current_admin.group.nsc_invalidate_count++;
+}
+
+void
+getgr_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.group.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&group_lock);
+
+ if (current_admin.group.nsc_check_files) {
+ struct stat buf;
+
+ if (stat("/etc/group", &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getgr_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
+ logit("getgr_lookup: looking for gid %d\n",
+ in->nsc_u.gid);
+ } else {
+ logit("getgr_lookup: looking for name %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+ for (;;) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
+ bucket = get_hash(uid_hash, (char *)in->nsc_u.gid);
+ } else {
+ if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getgr_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("getgr_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(nam_hash, in->nsc_u.name);
+ }
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /*
+ * no threads available
+ * cannot process now
+ */
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.group.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&group_wait, &group_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.group.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ struct group *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.group.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_gr_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1,
+ in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update...
+ * mark to prevent pileups of threads if the name
+ * service is hanging....
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&group_lock);
+
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETGRGID) {
+ p = _uncached_getgrgid_r(in->nsc_u.gid, &out->nsc_u.grp,
+ out->nsc_u.buff + sizeof (struct group),
+ bufferspace);
+ saved_errno = errno;
+ } else {
+ p = _uncached_getgrnam_r(in->nsc_u.name,
+ &out->nsc_u.grp,
+ out->nsc_u.buff + sizeof (struct group),
+ bufferspace);
+ saved_errno = errno;
+ }
+
+ mutex_lock(&group_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETGRGID) {
+ logit("getgr_lookup: nscd COULDN'T FIND gid %d\n",
+ in->nsc_u.gid);
+ } else {
+ logit("getgr_lookup: nscd COULDN'T FIND group name %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.group.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &retb->nsc_data,
+ retb->nsc_data.nsc_bufferbytesused);
+ update_gr_bucket((nsc_bucket_t **)bucket,
+ retb,
+ in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETGRGID) {
+ logit("getgr_lookup: nscd FOUND gid %d\n",
+ in->nsc_u.gid);
+ } else {
+ logit("getgr_lookup: nscd FOUND group name %s\n",
+ in->nsc_u.name);
+ }
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.group.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+ update_gr_bucket((nsc_bucket_t **)bucket,
+ retb,
+ in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.group.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETGRGID) {
+ logit("getgr_lookup: found gid %d in cache\n",
+ in->nsc_u.gid);
+ } else {
+ logit("getgr_lookup: found name %s in cache\n",
+ in->nsc_u.name);
+ }
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.group.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETGRGID) {
+ logit("getgr_lookup: %d marked as NOT FOUND in cache.\n",
+ in->nsc_u.gid);
+ } else {
+ logit("getgr_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n", retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+
+ mutex_unlock(&group_lock);
+}
+
+/*ARGSUSED*/
+static int
+update_gr_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
+ free(*old);
+ current_admin.group.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&group_wait, (char **)old);
+ }
+
+
+ *old = new;
+
+ if ((new != NULL) && (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.group.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.group.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.group.nsc_neg_ttl;
+ }
+ }
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ int group_members;
+ int i;
+ nsc_bucket_t *retb;
+ nsc_return_t *out;
+ char *dest;
+ int offset;
+ int strs;
+ char **members;
+ int pwlen;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = 0;
+ strs += 1 + strlen(in->nsc_u.grp.gr_name);
+ pwlen = strlen(in->nsc_u.grp.gr_passwd);
+ if (pwlen < 4)
+ pwlen = 4;
+ strs += 1 + pwlen;
+
+ group_members = 0;
+ while (in->nsc_u.grp.gr_mem[group_members]) {
+ strs += 1 + strlen(in->nsc_u.grp.gr_mem[group_members]);
+ group_members++;
+ }
+
+ strs += (group_members+1) * sizeof (char *);
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.grp - (int)out) +
+ sizeof (struct group);
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+
+ out->nsc_u.grp.gr_gid = in->nsc_u.grp.gr_gid;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (struct group);
+ offset = (int)dest;
+
+ members = (char **)dest;
+ out->nsc_u.grp.gr_mem = (char **)(dest - offset);
+ dest += (group_members+1) * sizeof (char *);
+
+
+ strcpy(dest, in->nsc_u.grp.gr_name);
+ strs = 1 + strlen(in->nsc_u.grp.gr_name);
+ out->nsc_u.grp.gr_name = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.grp.gr_passwd);
+ strs = 1 + pwlen;
+ out->nsc_u.grp.gr_passwd = dest - offset;
+ dest += strs;
+
+ for (i = 0; i < group_members; i++) {
+ members[i] = dest - offset;
+ strcpy(dest, in->nsc_u.grp.gr_mem[i]);
+ strs = 1 + strlen(in->nsc_u.grp.gr_mem[i]);
+ dest += strs;
+ }
+ members[i] = NULL; /* null terminate list */
+ memcpy(in, out, out->nsc_bufferbytesused);
+
+ return (retb);
+}
+
+void
+getgr_uid_reaper()
+{
+ nsc_reaper("gr_uid", uid_hash, &current_admin.group, &group_lock);
+}
+
+void
+getgr_nam_reaper()
+{
+ nsc_reaper("gr_nam", nam_hash, &current_admin.group, &group_lock);
+}
diff --git a/usr/src/cmd/nscd/gethost.c b/usr/src/cmd/nscd/gethost.c
new file mode 100644
index 0000000000..c38b7f0b2b
--- /dev/null
+++ b/usr/src/cmd/nscd/gethost.c
@@ -0,0 +1,683 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle gethost* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+
+#include "getxby_door.h"
+#include "server_door.h"
+#include "nscd.h"
+
+static hash_t *addr_hash;
+static hash_t *hnam_hash;
+static mutex_t host_lock = DEFAULTMUTEX;
+static waiter_t host_wait;
+
+static void gethost_addrkeepalive(int keep, int interval);
+static void gethost_invalidate_unlocked(void);
+static void gethost_namekeepalive(int keep, int interval);
+static int addr_to_int(char *addr);
+static int int_to_addr(int h);
+static void update_host_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findhaddrs(nsc_bucket_t *ptr, int *table, int intaddr);
+static void do_findhnams(nsc_bucket_t *ptr, int *table, char *name);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+
+static int
+addr_to_int(char *addr)
+{
+ union {
+ char data[4];
+ int hashval;
+ } u;
+
+/*
+ * following code is byte order dependant, but since all we use this for
+ * is hashing this works out just fine.
+ */
+ u.data[0] = *addr++;
+ u.data[1] = *addr++;
+ u.data[2] = *addr++;
+ u.data[3] = *addr++;
+
+ return (u.hashval);
+}
+
+static int
+int_to_addr(int h)
+{
+ union {
+ char data[4];
+ int hashval;
+ } u;
+
+/*
+ * following code is byte order dependant, but since all we use this for
+ * is hashing this works out just fine.
+ */
+ u.hashval = h;
+ return (* ((int *)u.data));
+}
+
+void
+gethost_init(void)
+{
+ addr_hash = make_ihash(current_admin.host.nsc_suggestedsize);
+ hnam_hash = make_hash(current_admin.host.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_host_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findhnams(nsc_bucket_t *ptr, int *table, char *name)
+{
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(name));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+static void
+do_findhaddrs(nsc_bucket_t *ptr, int *table, int intaddr)
+{
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ insertn(table, ptr->nsc_hits, int_to_addr(intaddr));
+ }
+}
+
+void
+gethost_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.host.nsc_pos_ttl;
+
+ if (slp < 60)
+ slp = 60;
+ count = current_admin.host.nsc_keephot;
+ if (count != 0) {
+ interval = (slp/2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp*2/3);
+ gethost_namekeepalive(count, interval);
+ gethost_addrkeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static void
+gethost_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&host_lock);
+ operate_hash(hnam_hash, do_findhnams, (char *)table);
+ mutex_unlock(&host_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETHOSTBYNAME;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+ if (current_admin.debug_level >= DBG_ALL)
+ logit("keepalive: reviving host %s\n", tmp);
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+static void
+gethost_addrkeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + 80];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&host_lock);
+ operate_hash(addr_hash, do_findhaddrs, (char *)table);
+ mutex_unlock(&host_lock);
+
+ for (i = 1; i <= keep; i++) {
+ int tmp;
+ u.ping.nsc_call.nsc_callnumber = GETHOSTBYADDR;
+
+ if ((tmp = table[keep + 1 + i]) == -1)
+ continue; /* unused slot in table */
+ u.ping.nsc_call.nsc_u.addr.a_type = AF_INET;
+ u.ping.nsc_call.nsc_u.addr.a_length = sizeof (int);
+ memcpy(u.ping.nsc_call.nsc_u.addr.a_data, &tmp, sizeof (int));
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ free(table);
+}
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+void
+gethost_invalidate(void)
+{
+ mutex_lock(&host_lock);
+ gethost_invalidate_unlocked();
+ mutex_unlock(&host_lock);
+}
+
+static void
+gethost_invalidate_unlocked(void)
+{
+ operate_hash_addr(hnam_hash, do_invalidate, (char *)GETHOSTBYNAME);
+ operate_hash_addr(addr_hash, do_invalidate, (char *)GETHOSTBYADDR);
+ current_admin.host.nsc_invalidate_count++;
+}
+
+void
+gethost_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.host.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&host_lock);
+
+ if (current_admin.host.nsc_check_files) {
+ struct stat buf;
+
+ if (stat("/etc/hosts", &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ gethost_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) {
+ logit("gethost_lookup: looking for address %s\n",
+ inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data)));
+ } else {
+ logit("gethost_lookup: looking for hostname %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+ for (;;) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) {
+ bucket = get_hash(addr_hash,
+ (char *)addr_to_int(in->nsc_u.addr.a_data));
+ } else { /* bounce excessively long requests */
+ if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("gethost_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("gethost_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(hnam_hash, in->nsc_u.name);
+ }
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.host.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&host_wait, &host_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.host.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ struct hostent *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.host.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent or out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_host_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1,
+ in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update... mark
+ * to prevent pileups of threads if the name service
+ * is hanging....
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&host_lock);
+
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) {
+ p = _uncached_gethostbyaddr_r(in->nsc_u.addr.a_data,
+ in->nsc_u.addr.a_length,
+ in->nsc_u.addr.a_type,
+ &out->nsc_u.hst,
+ out->nsc_u.buff+sizeof (struct hostent),
+ bufferspace,
+ &saved_errno);
+ } else {
+ p = _uncached_gethostbyname_r(in->nsc_u.name,
+ &out->nsc_u.hst,
+ out->nsc_u.buff+sizeof (struct hostent),
+ bufferspace,
+ &saved_errno);
+ }
+
+ mutex_lock(&host_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETHOSTBYADDR) {
+ logit("gethost_lookup: nscd COULDN'T FIND address %s\n",
+ inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data)));
+ } else {
+ logit("gethost_lookup: nscd COULDN'T FIND host name %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.host.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+ update_host_bucket((nsc_bucket_t **)bucket, retb,
+ in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETHOSTBYADDR) {
+ logit("gethost_lookup: nscd FOUND addr %s\n",
+ inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data)));
+ } else {
+ logit("gethost_lookup: nscd FOUND host name %s\n",
+ in->nsc_u.name);
+ }
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.host.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+
+ update_host_bucket((nsc_bucket_t **)bucket, retb,
+ in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.host.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETHOSTBYADDR) {
+ logit("gethost_lookup: found address %s in cache\n",
+ inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data)));
+ } else {
+ logit("gethost_lookup: found host name %s in cache\n",
+ in->nsc_u.name);
+ }
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.host.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETHOSTBYADDR) {
+ logit("gethost_lookup: %s marked as NOT FOUND in cache.\n",
+ inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data)));
+ } else {
+ logit("gethost_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n", retb->nsc_timestamp);
+ /* cleared by deletion of old data */
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ launch_update(in);
+ }
+ }
+
+getout:
+
+ mutex_unlock(&host_lock);
+}
+
+/*ARGSUSED*/
+static void
+update_host_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) {
+ /* old data exists */
+ free(*old);
+ current_admin.host.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&host_wait, (char **)old);
+ }
+
+
+
+ *old = new;
+
+ if ((new != NULL) &&
+ (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.host.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.host.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.host.nsc_neg_ttl;
+ }
+ }
+}
+
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_return_t *out;
+ nsc_bucket_t *retb;
+ char *dest;
+ char **aliaseslist;
+ char **addrlist;
+ int offset;
+ int strs;
+ int i;
+ int numaliases;
+ int numaddrs;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = 1 + strlen(in->nsc_u.hst.h_name);
+ for (numaliases = 0; in->nsc_u.hst.h_aliases[numaliases]; numaliases++)
+ strs += 1 + strlen(in->nsc_u.hst.h_aliases[numaliases]);
+ strs += sizeof (char *) * (numaliases+1);
+ for (numaddrs = 0; in->nsc_u.hst.h_addr_list[numaddrs]; numaddrs++)
+ strs += in->nsc_u.hst.h_length;
+ strs += sizeof (char *) * (numaddrs+1+3);
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = sizeof (*in) + strs;
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (struct hostent);
+
+ offset = (int)dest;
+
+ /*
+ * allocat the h_aliases list and the h_addr_list first to align 'em.
+ */
+ aliaseslist = (char **)dest;
+
+ dest += sizeof (char *) * (numaliases+1);
+
+ addrlist = (char **)dest;
+
+ dest += sizeof (char *) * (numaddrs+1);
+
+ strcpy(dest, in->nsc_u.hst.h_name);
+ strs = 1 + strlen(in->nsc_u.hst.h_name);
+ out->nsc_u.hst.h_name = dest - offset;
+ dest += strs;
+
+
+ /*
+ * fill out the h_aliases list
+ */
+ for (i = 0; i < numaliases; i++) {
+ strcpy(dest, in->nsc_u.hst.h_aliases[i]);
+ strs = 1 + strlen(in->nsc_u.hst.h_aliases[i]);
+ aliaseslist[i] = dest - offset;
+ dest += strs;
+ }
+ aliaseslist[i] = 0; /* null term ptr chain */
+
+ out->nsc_u.hst.h_aliases = (char **)((int)aliaseslist-offset);
+
+ /*
+ * fill out the h_addr list
+ */
+
+ dest = (char *)(((int)dest + 3) & ~3);
+
+ for (i = 0; i < numaddrs; i++) {
+ memcpy(dest, in->nsc_u.hst.h_addr_list[i],
+ in->nsc_u.hst.h_length);
+ strs = in->nsc_u.hst.h_length;
+ addrlist[i] = dest - offset;
+ dest += strs;
+ dest = (char *)(((int)dest + 3) & ~3);
+ }
+
+ addrlist[i] = 0; /* null term ptr chain */
+
+ out->nsc_u.hst.h_addr_list = (char **)((int)addrlist-offset);
+
+ out->nsc_u.hst.h_length = in->nsc_u.hst.h_length;
+ out->nsc_u.hst.h_addrtype = in->nsc_u.hst.h_addrtype;
+
+ memcpy(in, &(retb->nsc_data), retb->nsc_data.nsc_bufferbytesused);
+
+ return (retb);
+
+}
+
+void
+gethost_nam_reaper()
+{
+ nsc_reaper("gethost_nam", hnam_hash, &current_admin.host, &host_lock);
+}
+
+void
+gethost_addr_reaper()
+{
+ nsc_reaper("gethost_addr", addr_hash, &current_admin.host, &host_lock);
+}
diff --git a/usr/src/cmd/nscd/getnode.c b/usr/src/cmd/nscd/getnode.c
new file mode 100644
index 0000000000..a744841197
--- /dev/null
+++ b/usr/src/cmd/nscd/getnode.c
@@ -0,0 +1,803 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getipnode* calls in nscd. Note that the
+ * getnodeby* APIs were renamed getipnodeby*. The interfaces
+ * related to them in the nscd will remain as getnode*.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+#include <inet/ip6.h>
+
+#include "getxby_door.h"
+#include "server_door.h"
+#include "nscd.h"
+
+static hash_t *addr_hash; /* node address hash */
+static hash_t *nnam_hash; /* node name hash */
+static mutex_t node_lock = DEFAULTMUTEX;
+static waiter_t node_wait;
+
+static void getnode_addrkeepalive(int keep, int interval);
+static void getnode_invalidate_unlocked(void);
+static void getnode_namekeepalive(int keep, int interval);
+static void update_node_bucket(nsc_bucket_t ** old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findnaddrs(nsc_bucket_t *ptr, int *table, char *addr);
+static void do_findnnams(nsc_bucket_t *ptr, int *table, char *name);
+static void do_invalidate(nsc_bucket_t ** ptr, int callnumber);
+
+/*
+ * Create a combined key
+ * combined key =
+ * name + ":" + af_family(in %d format) + ":" + flags(in %d format)
+ * name = foo, af_family = 26, flags = 3, addrconfig = 1
+ * key = "foo:26:3"
+ * The string is allocated and caller has to free it.
+ *
+ * Insert ":" in between to ensure the uniqueness of the key.
+ *
+ * af_family: [2 | 26] ([AF_INET | AF_INET6])
+ * flags: 0 - strict
+ * 1 - AI_V4MAPPED
+ * 3 - (AI_V4MAPPED | AI_ALL)
+ * 4 - AI_ADDRCONFIG
+ * 5 - AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG)
+ * 7 - (AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG)
+ */
+static char *
+get_ipnode_combined_key(nsc_call_t *in) {
+ int len;
+ char *key;
+ char af_str[8]; /* in %d format */
+ char flags_str[8]; /* in %d format */
+
+ snprintf(af_str, 8, "%d", in->nsc_u.ipnode.af_family);
+ snprintf(flags_str, 8, "%d", in->nsc_u.ipnode.flags);
+ /* name + ":" + af_str + ":" + flags_str + 1 */
+ len = strlen(in->nsc_u.ipnode.name) + strlen(af_str) +
+ strlen(flags_str) + 3;
+
+ if ((key = (char *)malloc(len)) == NULL)
+ return (NULL);
+
+ snprintf(key, len, "%s:%s:%s", in->nsc_u.ipnode.name, af_str,
+ flags_str);
+ return (key);
+}
+/*
+ * Parse key and copy the values to out
+ */
+static void
+copy_ipnode_combined_key(char *key, nsc_call_t *out) {
+ char *token, *lasts;
+
+ if ((token = strtok_r(key, ":", &lasts)) != NULL) {
+ /* copy name */
+ (void) strncpy(out->nsc_u.ipnode.name, token,
+ NSCDMAXNAMELEN);
+ if ((token = strtok_r(NULL, ":", &lasts)) != NULL) {
+ /* Copy af_family */
+ out->nsc_u.ipnode.af_family = atoi(token);
+
+ if ((token = strtok_r(NULL, ":", &lasts)) != NULL) {
+ /* Copy flags */
+ out->nsc_u.ipnode.flags = atoi(token);
+ }
+ }
+ }
+}
+
+static void
+safe_inet_ntop(int af, const void *addr, char *cp, size_t size)
+{
+ if (inet_ntop(af, addr, cp, size) != cp) {
+ /*
+ * Called only for logging purposes...
+ * this should never happen in the nscd... but
+ * just in case we'll make sure we have a
+ * plausible error message.
+ */
+ (void) snprintf(cp, size, "<inet_ntop returned %s>",
+ strerror(errno));
+ }
+}
+
+void
+getnode_init(void)
+{
+ addr_hash = make_hash(current_admin.node.nsc_suggestedsize);
+ nnam_hash = make_hash(current_admin.node.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_node_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findnnams(nsc_bucket_t *ptr, int *table, char *name)
+{
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(name));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+static void
+do_findnaddrs(nsc_bucket_t *ptr, int *table, char *addr)
+{
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+
+ /* leave pending calls alone */
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(addr));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+void
+getnode_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.node.nsc_pos_ttl;
+
+ if (slp < 60)
+ slp = 60;
+
+ if ((count = current_admin.node.nsc_keephot) != 0) {
+ interval = (slp/2)/count;
+ if (interval == 0) interval = 1;
+ (void) sleep(slp*2/3);
+ getnode_namekeepalive(count, interval);
+ getnode_addrkeepalive(count, interval);
+ } else {
+ (void) sleep(slp);
+ }
+ }
+}
+
+static void
+getnode_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ (void) mutex_lock(&node_lock);
+ operate_hash(nnam_hash, do_findnnams, (char *)table);
+ (void) mutex_unlock(&node_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETIPNODEBYNAME;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+ if (current_admin.debug_level >= DBG_ALL)
+ logit("keepalive: reviving node %s\n", tmp);
+ copy_ipnode_combined_key(tmp, &u.ping.nsc_call);
+
+ launch_update(&u.ping.nsc_call);
+ (void) sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+static void
+getnode_addrkeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + 80];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ (void) mutex_lock(&node_lock);
+ operate_hash(addr_hash, do_findnaddrs, (char *)table);
+ (void) mutex_unlock(&node_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ char addr[IPV6_ADDR_LEN];
+
+ u.ping.nsc_call.nsc_callnumber = GETIPNODEBYADDR;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* enused slot in table */
+ u.ping.nsc_call.nsc_u.addr.a_type = AF_INET6;
+ u.ping.nsc_call.nsc_u.addr.a_length = IPV6_ADDR_LEN;
+ if (inet_pton(AF_INET6, (const char *) tmp, (void *) addr) !=
+ 1)
+ continue; /* illegal address - skip it */
+ else {
+ (void) memcpy(u.ping.nsc_call.nsc_u.addr.a_data,
+ addr, IPV6_ADDR_LEN);
+ if (current_admin.debug_level >= DBG_ALL)
+ logit("keepalive: reviving address %s\n", tmp);
+ launch_update(&u.ping.nsc_call);
+ }
+ (void) sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+
+void
+getnode_invalidate(void)
+{
+ (void) mutex_lock(&node_lock);
+ getnode_invalidate_unlocked();
+ (void) mutex_unlock(&node_lock);
+}
+
+static void
+getnode_invalidate_unlocked(void)
+{
+ operate_hash_addr(nnam_hash, do_invalidate, (char *)GETIPNODEBYNAME);
+ operate_hash_addr(addr_hash, do_invalidate, (char *)GETIPNODEBYADDR);
+ current_admin.node.nsc_invalidate_count++;
+}
+
+/*
+ * Mark only the ipnodebyname cache entries as invalid
+ */
+
+void
+getnode_name_invalidate(void)
+{
+ (void) mutex_lock(&node_lock);
+ operate_hash_addr(nnam_hash, do_invalidate, (char *)GETIPNODEBYNAME);
+ current_admin.node.nsc_invalidate_count++;
+ (void) mutex_unlock(&node_lock);
+}
+
+void
+getnode_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket, *key;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.node.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ (void) mutex_lock(&node_lock);
+
+ if (current_admin.node.nsc_check_files) {
+ struct stat buf;
+
+ if (stat("/etc/inet/ipnodes", &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getnode_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ safe_inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data, addr,
+ sizeof (addr));
+ logit("getnode_lookup: looking for address %s\n", addr);
+ } else {
+ logit("getnode_lookup: looking for nodename %s:%d:%d\n",
+ in->nsc_u.ipnode.name,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags);
+ }
+ }
+
+ for (;;) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data,
+ addr, sizeof (addr)) == NULL) {
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(addr_hash, addr);
+ } else { /* bounce excessively long requests */
+ if (strlen(in->nsc_u.ipnode.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getnode_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("getnode_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ if ((key = get_ipnode_combined_key(in)) == NULL) {
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+
+ bucket = get_hash(nnam_hash, key);
+ free(key);
+ }
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.node.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&node_wait, &node_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.node.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ struct hostent *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (* out);
+ current_admin.node.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent or out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_node_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1,
+ in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update... mark to
+ * prevent pileups of threads if the name service is hanging...
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ (void) mutex_unlock(&node_lock);
+
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETIPNODEBYADDR) {
+ p = _uncached_getipnodebyaddr(in->nsc_u.addr.a_data,
+ in->nsc_u.addr.a_length,
+ in->nsc_u.addr.a_type,
+ &out->nsc_u.hst,
+ out->nsc_u.buff+sizeof (struct hostent),
+ bufferspace,
+ &saved_errno);
+ } else {
+ p = _uncached_getipnodebyname(in->nsc_u.ipnode.name,
+ &out->nsc_u.hst,
+ out->nsc_u.buff+sizeof (struct hostent),
+ bufferspace,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags,
+ &saved_errno);
+ }
+
+ (void) mutex_lock(&node_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ safe_inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data,
+ addr, sizeof (addr));
+ logit("getnode_lookup: nscd COULDN'T "\
+ "FIND address %s\n", addr);
+ } else {
+ logit("getnode_lookup: nscd COULDN'T "\
+ "FIND node name %s:%d:%d\n",
+ in->nsc_u.ipnode.name,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags);
+ }
+ }
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.node.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_errno = saved_errno;
+ (void) memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+ update_node_bucket((nsc_bucket_t **)bucket, retb,
+ in->nsc_callnumber);
+ goto getout;
+ }
+
+ else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ safe_inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data,
+ addr, sizeof (addr));
+ logit("getnode_lookup: nscd FOUND "\
+ "addr %s\n", addr);
+ } else {
+ logit("getnode_lookup: nscd FOUND "\
+ "node name %s:%d:%d\n",
+ in->nsc_u.ipnode.name,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags);
+ }
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.node.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+
+ update_node_bucket((nsc_bucket_t **)bucket, retb,
+ in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ (void) memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.node.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ safe_inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data,
+ addr, sizeof (addr));
+ logit("getnode_lookup: found address "\
+ "%s in cache\n", addr);
+ } else {
+ logit("getnode_lookup: found node "\
+ "name %s:%d:%d in cache\n",
+ in->nsc_u.ipnode.name,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags);
+ }
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.node.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETIPNODEBYADDR) {
+ char addr[INET6_ADDRSTRLEN];
+ safe_inet_ntop(AF_INET6,
+ (const void *)in->nsc_u.addr.a_data,
+ addr, sizeof (addr));
+ logit("getnode_lookup: %s marked as "\
+ "NOT FOUND in cache.\n", addr);
+ } else {
+ logit("getnode_lookup: %s:%d:%d marked"\
+ " as NOT FOUND in cache.\n",
+ in->nsc_u.ipnode.name,
+ in->nsc_u.ipnode.af_family,
+ in->nsc_u.ipnode.flags);
+ }
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n",
+ retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+
+ (void) mutex_unlock(&node_lock);
+}
+
+
+/*ARGSUSED*/
+static void
+update_node_bucket(nsc_bucket_t ** old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
+ free(*old);
+ current_admin.node.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&node_wait, (char **)old);
+ }
+
+ *old = new;
+
+ if ((new != NULL) &&
+ (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.node.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.node.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.node.nsc_neg_ttl;
+ }
+ }
+}
+
+/* Allocate a bucket to fit the data(nsc_return_t *in size) */
+/* copy the data into the bucket and return the bucket */
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_return_t *out;
+ nsc_bucket_t *retb;
+ char *dest;
+ char ** aliaseslist;
+ char ** addrlist;
+ int offset;
+ int strs;
+ int i;
+ int numaliases;
+ int numaddrs;
+
+ /* find out the size of the data block we're going to need */
+
+ strs = 1 + strlen(in->nsc_u.hst.h_name);
+ for (numaliases = 0; in->nsc_u.hst.h_aliases[numaliases]; numaliases++)
+ strs += 1 + strlen(in->nsc_u.hst.h_aliases[numaliases]);
+ strs += sizeof (char *) * (numaliases+1);
+ for (numaddrs = 0; in->nsc_u.hst.h_addr_list[numaddrs]; numaddrs++)
+ strs += in->nsc_u.hst.h_length;
+ strs += sizeof (char *) * (numaddrs+1+3);
+
+ /* allocate it and copy it in code doesn't assume packing */
+ /* order in original buffer */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = sizeof (*in) + strs;
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (struct hostent);
+ offset = (int)dest;
+
+ /* allocat the h_aliases list and the h_addr_list first to align 'em. */
+ aliaseslist = (char **)dest;
+
+ dest += sizeof (char *) * (numaliases+1);
+
+ addrlist = (char **)dest;
+
+ dest += sizeof (char *) * (numaddrs+1);
+
+ (void) strcpy(dest, in->nsc_u.hst.h_name);
+ strs = 1 + strlen(in->nsc_u.hst.h_name);
+ out->nsc_u.hst.h_name = dest - offset;
+ dest += strs;
+
+
+ /* fill out the h_aliases list */
+
+ for (i = 0; i < numaliases; i++) {
+ (void) strcpy(dest, in->nsc_u.hst.h_aliases[i]);
+ strs = 1 + strlen(in->nsc_u.hst.h_aliases[i]);
+ aliaseslist[i] = dest - offset;
+ dest += strs;
+ }
+ aliaseslist[i] = 0; /* null term ptr chain */
+
+ out->nsc_u.hst.h_aliases = (char **)((int)aliaseslist-offset);
+
+ /* fill out the h_addr list */
+
+ dest = (char *)(((int)dest + 3) & ~3);
+
+ for (i = 0; i < numaddrs; i++) {
+ (void) memcpy(dest, in->nsc_u.hst.h_addr_list[i],
+ in->nsc_u.hst.h_length);
+ strs = in->nsc_u.hst.h_length;
+ addrlist[i] = dest - offset;
+ dest += strs;
+ dest = (char *)(((int)dest + 3) & ~3);
+ }
+
+ addrlist[i] = 0; /* null term ptr chain */
+
+ out->nsc_u.hst.h_addr_list = (char **)((int)addrlist-offset);
+
+ out->nsc_u.hst.h_length = in->nsc_u.hst.h_length;
+ out->nsc_u.hst.h_addrtype = in->nsc_u.hst.h_addrtype;
+
+ (void) memcpy(in, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ return (retb);
+
+}
+
+void
+getnode_nam_reaper()
+{
+ nsc_reaper("getnode_nam", nnam_hash, &current_admin.node, &node_lock);
+}
+
+void
+getnode_addr_reaper()
+{
+ nsc_reaper("getnode_addr", addr_hash, &current_admin.node, &node_lock);
+}
diff --git a/usr/src/cmd/nscd/getprof.c b/usr/src/cmd/nscd/getprof.c
new file mode 100644
index 0000000000..ad171d027c
--- /dev/null
+++ b/usr/src/cmd/nscd/getprof.c
@@ -0,0 +1,508 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getprof* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+
+#include <prof_attr.h>
+
+#include <getxby_door.h>
+#include "server_door.h"
+#include "nscd.h"
+
+extern profstr_t *_getprofnam(const char *, profstr_t *, char *, int, int *);
+
+static hash_t *nam_hash;
+static mutex_t db_lock = DEFAULTMUTEX;
+static waiter_t db_wait;
+
+static void getprof_namekeepalive(int keep, int interval);
+static void update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+static void getprof_invalidate_unlocked(void);
+
+void
+getprof_init(void)
+{
+ nam_hash = make_hash(current_admin.prof.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_prof_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(gnam));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+void
+getprof_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.prof.nsc_pos_ttl;
+
+ if (slp < 60) {
+ slp = 60;
+ }
+
+ if ((count = current_admin.prof.nsc_keephot) != 0) {
+ interval = (slp / 2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp * 2 / 3);
+ getprof_namekeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static void
+getprof_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&db_lock);
+ operate_hash(nam_hash, do_findgnams, (char *)table);
+ mutex_unlock(&db_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETPROFNAM;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+
+void
+getprof_invalidate(void)
+{
+ mutex_lock(&db_lock);
+ getprof_invalidate_unlocked();
+ mutex_unlock(&db_lock);
+}
+
+static void
+getprof_invalidate_unlocked(void)
+{
+ operate_hash_addr(nam_hash, do_invalidate, (char *)GETPROFNAM);
+ current_admin.prof.nsc_invalidate_count++;
+}
+
+void
+getprof_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.prof.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&db_lock);
+
+ if (current_admin.prof.nsc_check_files) {
+ struct stat buf;
+
+ if (stat(PROFATTR_FILENAME, &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getprof_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getprof_lookup: looking for name %s\n",
+ in->nsc_u.name);
+ }
+
+ for (;;) {
+ if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getprof_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("getprof_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(nam_hash, in->nsc_u.name);
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused =
+ sizeof (*out);
+ current_admin.prof.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&db_wait, &db_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.prof.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ profstr_t *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.prof.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits =
+ ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_prof_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1, in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update...
+ * mark to prevent
+ * pileups of threads if the name service is hanging....
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&db_lock);
+
+ /*
+ * Call non-caching version in libnsl.
+ */
+ p = _getprofnam(in->nsc_u.name, &out->nsc_u.prof,
+ out->nsc_u.buff + sizeof (profstr_t),
+ bufferspace, &errno);
+ saved_errno = errno;
+
+ mutex_lock(&db_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ logit("getprof_lookup: nscd COULDN'T FIND prof_attr name %s\n",
+ in->nsc_u.name);
+ }
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.prof.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &retb->nsc_data,
+ retb->nsc_data.nsc_bufferbytesused);
+ update_prof_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getprof_lookup: nscd FOUND prof_attr name %s\n",
+ in->nsc_u.name);
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.prof.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+ update_prof_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.prof.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getprof_lookup: found name %s in cache\n",
+ in->nsc_u.name);
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.prof.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getprof_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n", retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+ mutex_unlock(&db_lock);
+}
+
+/*ARGSUSED*/
+static void
+update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
+ free(*old);
+ current_admin.prof.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&db_wait, (char **)old);
+ }
+
+ *old = new;
+
+ if ((new != NULL) && (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.prof.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.prof.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.prof.nsc_neg_ttl;
+ }
+ }
+}
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_bucket_t *retb;
+ nsc_return_t *out;
+ char *dest;
+ int offset;
+ int strs;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = attr_strlen(in->nsc_u.prof.name) +
+ attr_strlen(in->nsc_u.prof.res1) +
+ attr_strlen(in->nsc_u.prof.res2) +
+ attr_strlen(in->nsc_u.prof.desc) +
+ attr_strlen(in->nsc_u.prof.attr) + PROFATTR_DB_NCOL;
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.prof - (int)out) +
+ sizeof (profstr_t);
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (profstr_t);
+ offset = (int)dest;
+
+ attr_strcpy(dest, in->nsc_u.prof.name);
+ strs = 1 + attr_strlen(in->nsc_u.prof.name);
+ out->nsc_u.prof.name = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.prof.res1);
+ strs = 1 + attr_strlen(in->nsc_u.prof.res1);
+ out->nsc_u.prof.res1 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.prof.res2);
+ strs = 1 + attr_strlen(in->nsc_u.prof.res2);
+ out->nsc_u.prof.res2 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.prof.desc);
+ strs = 1 + attr_strlen(in->nsc_u.prof.desc);
+ out->nsc_u.prof.desc = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.prof.attr);
+ out->nsc_u.prof.attr = dest - offset;
+
+ memcpy(in, out, out->nsc_bufferbytesused);
+
+ return (retb);
+}
+
+void
+getprof_reaper(void)
+{
+ nsc_reaper("getprof", nam_hash, &current_admin.prof, &db_lock);
+}
diff --git a/usr/src/cmd/nscd/getpw.c b/usr/src/cmd/nscd/getpw.c
new file mode 100644
index 0000000000..cd38871554
--- /dev/null
+++ b/usr/src/cmd/nscd/getpw.c
@@ -0,0 +1,673 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getpw* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <nss_common.h>
+#include <ucred.h>
+
+#include "getxby_door.h"
+#include "server_door.h"
+
+#include "nscd.h"
+
+static hash_t *uid_hash;
+static hash_t *nam_hash;
+static mutex_t passwd_lock = DEFAULTMUTEX;
+static waiter_t passwd_wait;
+
+static void getpw_invalidate_unlocked(void);
+static void getpw_namekeepalive(int keep, int interval);
+static void getpw_uidkeepalive(int keep, int interval);
+static void update_pw_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findnams(nsc_bucket_t *ptr, int *table, char *name);
+static void do_finduids(nsc_bucket_t *ptr, int *table, int uid);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+
+void
+getpw_init(void)
+{
+ uid_hash = make_ihash(current_admin.passwd.nsc_suggestedsize);
+ nam_hash = make_hash(current_admin.passwd.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_pw_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_finduids(nsc_bucket_t *ptr, int *table, int uid)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ insertn(table, ptr->nsc_hits, uid);
+ }
+}
+
+static void
+do_findnams(nsc_bucket_t *ptr, int *table, char *name)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(name));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+
+
+void
+getpw_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.passwd.nsc_pos_ttl;
+
+ if (slp < 60) {
+ slp = 60;
+ }
+
+ if ((count = current_admin.passwd.nsc_keephot) != 0) {
+ interval = (slp / 2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp * 2 / 3);
+ getpw_uidkeepalive(count, interval);
+ getpw_namekeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static void
+getpw_uidkeepalive(int keep, int interval)
+{
+ int *table;
+ nsc_data_t ping;
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&passwd_lock);
+ operate_hash(uid_hash, do_finduids, (char *)table);
+ mutex_unlock(&passwd_lock);
+
+ for (i = 1; i <= keep; i++) {
+ ping.nsc_call.nsc_callnumber = GETPWUID;
+ if ((ping.nsc_call.nsc_u.uid = table[keep + 1 + i]) == -1)
+ continue; /* unused slot in table */
+ launch_update(&ping.nsc_call);
+ sleep(interval);
+ }
+ free(table);
+}
+
+
+static void
+getpw_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&passwd_lock);
+ operate_hash(nam_hash, do_findnams, (char *)table);
+ mutex_unlock(&passwd_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETPWNAM;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+
+
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+void
+getpw_invalidate(void)
+{
+ mutex_lock(&passwd_lock);
+ getpw_invalidate_unlocked();
+ mutex_unlock(&passwd_lock);
+}
+
+static void
+getpw_invalidate_unlocked(void)
+{
+ operate_hash_addr(nam_hash, do_invalidate, (char *)GETPWNAM);
+ operate_hash_addr(uid_hash, do_invalidate, (char *)GETPWUID);
+ current_admin.passwd.nsc_invalidate_count++;
+}
+
+void
+getpw_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.passwd.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&passwd_lock);
+
+ if (current_admin.passwd.nsc_check_files) {
+ struct stat buf;
+
+ if (stat("/etc/passwd", &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getpw_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) {
+ logit("getpw_lookup: looking for uid %d\n",
+ in->nsc_u.uid);
+ } else {
+ logit("getpw_lookup: looking for name %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+ for (;;) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) {
+ bucket = get_hash(uid_hash, (char *)in->nsc_u.uid);
+ } else { /* make reasonableness check here */
+ if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getpw_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+
+ logit("getpw_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(nam_hash, in->nsc_u.name);
+ }
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.passwd.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&passwd_wait, &passwd_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if (*bucket == NULL ||
+ (in->nsc_callnumber & UPDATEBIT) ||
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.passwd.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /*
+ * time has expired
+ */
+ int saved_errno;
+ int saved_hits = 0;
+ struct passwd *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.passwd.nsc_throttle_count++;
+ goto getout;
+ }
+ if (*bucket != NULL) {
+ saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data
+ * is non-existent or out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_pw_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1,
+ in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing
+ * update... mark to prevent pileups of threads if
+ * the name service is hanging..
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&passwd_lock);
+
+ if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) {
+ p = _uncached_getpwuid_r(in->nsc_u.uid, &out->nsc_u.pwd,
+ out->nsc_u.buff+sizeof (struct passwd),
+ bufferspace);
+ saved_errno = errno;
+ } else {
+ p = _uncached_getpwnam_r(in->nsc_u.name,
+ &out->nsc_u.pwd,
+ out->nsc_u.buff+sizeof (struct passwd),
+ bufferspace);
+ saved_errno = errno;
+ }
+
+ mutex_lock(&passwd_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETPWUID) {
+ logit("getpw_lookup: nscd COULDN'T FIND uid %d\n",
+ in->nsc_u.uid);
+ } else {
+ logit("getpw_lookup: nscd COULDN'T FIND passwd name %s\n",
+ in->nsc_u.name);
+ }
+ }
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.passwd.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &retb->nsc_data,
+ retb->nsc_data.nsc_bufferbytesused);
+ update_pw_bucket((nsc_bucket_t **)bucket, retb,
+ in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETPWUID) {
+ logit("getpw_lookup: nscd FOUND uid %d\n",
+ in->nsc_u.uid);
+ } else {
+ logit("getpw_lookup: nscd FOUND passwd name %s\n",
+ in->nsc_u.name);
+ }
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.passwd.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+ update_pw_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.passwd.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETPWUID) {
+ logit("getpw_lookup: found uid %d in cache\n",
+ in->nsc_u.uid);
+ } else {
+ logit("getpw_lookup: found name %s in cache\n",
+ in->nsc_u.name);
+ }
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.passwd.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ if (MASKUPDATEBIT(in->nsc_callnumber) ==
+ GETPWUID) {
+ logit("getpw_lookup: %d marked as NOT FOUND in cache.\n",
+ in->nsc_u.uid);
+ } else {
+ logit("getpw_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n",
+ retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+
+ mutex_unlock(&passwd_lock);
+
+ /*
+ * secure mode check - blank out passwd if call sucessfull
+ * and caller != effective id
+ */
+ if ((current_admin.passwd.nsc_secure_mode != 0) &&
+ (out->nsc_return_code == SUCCESS) &&
+ !(UPDATEBIT & in->nsc_callnumber)) {
+
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ perror("door_ucred");
+ } else {
+ if (ucred_geteuid(uc) != out->nsc_u.pwd.pw_uid) {
+ /*
+ * write *NP* into passwd field if
+ * not already that way... we fixed
+ * the buffer code so there's always room.
+ */
+ int len;
+
+ char *foo = out->nsc_u.buff
+ + sizeof (struct passwd)
+ + (int)out->nsc_u.pwd.pw_passwd;
+
+ len = strlen(foo);
+ if (len > 0 &&
+ strcmp(foo, "*NP*") != 0 &&
+ strcmp(foo, "x") != 0) {
+ if (len < 5)
+ len = 5;
+ strncpy(foo, "*NP*", len);
+ /*
+ * strncpy will
+ * blank all
+ */
+ }
+ }
+ ucred_free(uc);
+ }
+ }
+}
+
+/*ARGSUSED*/
+static void
+update_pw_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) {
+ /* old data exists */
+ free(*old);
+ current_admin.passwd.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&passwd_wait, (char **)old);
+ }
+
+
+
+ *old = new;
+
+ if ((new != NULL) &&
+ (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.passwd.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.passwd.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.passwd.nsc_neg_ttl;
+ }
+ }
+}
+
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_bucket_t *retb;
+ char *dest;
+
+ nsc_return_t *out;
+ int offset;
+ int strs;
+ int pwlen;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = 0;
+ strs += 1 + strlen(in->nsc_u.pwd.pw_name);
+ pwlen = strlen(in->nsc_u.pwd.pw_passwd);
+ if (pwlen < 4)
+ pwlen = 4;
+ strs += 1 + pwlen;
+ strs += 1 + strlen(in->nsc_u.pwd.pw_age);
+ strs += 1 + strlen(in->nsc_u.pwd.pw_comment);
+ strs += 1 + strlen(in->nsc_u.pwd.pw_gecos);
+ strs += 1 + strlen(in->nsc_u.pwd.pw_dir);
+ strs += 1 + strlen(in->nsc_u.pwd.pw_shell);
+
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+
+
+
+ out->nsc_bufferbytesused = sizeof (*in) + strs;
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+ out->nsc_u.pwd.pw_uid = in->nsc_u.pwd.pw_uid;
+ out->nsc_u.pwd.pw_gid = in->nsc_u.pwd.pw_gid;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (struct passwd);
+
+ offset = (int)dest;
+
+ strcpy(dest, in->nsc_u.pwd.pw_name);
+ strs = 1 + strlen(in->nsc_u.pwd.pw_name);
+ out->nsc_u.pwd.pw_name = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_passwd);
+ strs = 1 + pwlen;
+ out->nsc_u.pwd.pw_passwd = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_age);
+ strs = 1 + strlen(in->nsc_u.pwd.pw_age);
+ out->nsc_u.pwd.pw_age = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_comment);
+ strs = 1 + strlen(in->nsc_u.pwd.pw_comment);
+ out->nsc_u.pwd.pw_comment = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_gecos);
+ strs = 1 + strlen(in->nsc_u.pwd.pw_gecos);
+ out->nsc_u.pwd.pw_gecos = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_dir);
+ strs = 1 + strlen(in->nsc_u.pwd.pw_dir);
+ out->nsc_u.pwd.pw_dir = dest - offset;
+ dest += strs;
+
+ strcpy(dest, in->nsc_u.pwd.pw_shell);
+ out->nsc_u.pwd.pw_shell = dest - offset;
+
+ memcpy(in, out, retb->nsc_data.nsc_bufferbytesused);
+
+
+ return (retb);
+
+}
+
+void
+getpw_uid_reaper()
+{
+ nsc_reaper("getpw_uid", uid_hash, &current_admin.passwd, &passwd_lock);
+}
+
+void
+getpw_nam_reaper()
+{
+ nsc_reaper("getpw_nam", nam_hash, &current_admin.passwd, &passwd_lock);
+}
diff --git a/usr/src/cmd/nscd/getuser.c b/usr/src/cmd/nscd/getuser.c
new file mode 100644
index 0000000000..fe654ef070
--- /dev/null
+++ b/usr/src/cmd/nscd/getuser.c
@@ -0,0 +1,510 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to handle getuser* calls in nscd
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/door.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <thread.h>
+#include <unistd.h>
+#include <ucred.h>
+#include <nss_common.h>
+
+#include <user_attr.h>
+
+#include <getxby_door.h>
+#include "server_door.h"
+#include "nscd.h"
+
+extern userstr_t *_getusernam(const char *, userstr_t *, char *, int, int *);
+
+static hash_t *nam_hash;
+static mutex_t db_lock = DEFAULTMUTEX;
+static waiter_t db_wait;
+
+static void getuser_namekeepalive(int keep, int interval);
+static void update_user_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
+ int callnumber);
+static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
+static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
+static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
+static void getuser_invalidate_unlocked(void);
+
+void
+getuser_init(void)
+{
+ nam_hash = make_hash(current_admin.user.nsc_suggestedsize);
+}
+
+static void
+do_invalidate(nsc_bucket_t ** ptr, int callnumber)
+{
+ if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
+ /* leave pending calls alone */
+ update_user_bucket(ptr, NULL, callnumber);
+ }
+}
+
+static void
+do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
+{
+
+ /*
+ * be careful with ptr - it may be -1 or NULL.
+ */
+
+ if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
+ char *tmp = (char *)insertn(table, ptr->nsc_hits,
+ (int)strdup(gnam));
+ if (tmp != (char *)-1)
+ free(tmp);
+ }
+}
+
+void
+getuser_revalidate(void)
+{
+ for (;;) {
+ int slp;
+ int interval;
+ int count;
+
+ slp = current_admin.user.nsc_pos_ttl;
+
+ if (slp < 60) {
+ slp = 60;
+ }
+
+ if ((count = current_admin.user.nsc_keephot) != 0) {
+ interval = (slp / 2)/count;
+ if (interval == 0) interval = 1;
+ sleep(slp * 2 / 3);
+ getuser_namekeepalive(count, interval);
+ } else {
+ sleep(slp);
+ }
+ }
+}
+
+static void
+getuser_namekeepalive(int keep, int interval)
+{
+ int *table;
+ union {
+ nsc_data_t ping;
+ char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
+ } u;
+
+ int i;
+
+ if (!keep)
+ return;
+
+ table = maken(keep);
+ mutex_lock(&db_lock);
+ operate_hash(nam_hash, do_findgnams, (char *)table);
+ mutex_unlock(&db_lock);
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ u.ping.nsc_call.nsc_callnumber = GETUSERNAM;
+
+ if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
+ continue; /* unused slot in table */
+
+ strcpy(u.ping.nsc_call.nsc_u.name, tmp);
+
+ launch_update(&u.ping.nsc_call);
+ sleep(interval);
+ }
+
+ for (i = 1; i <= keep; i++) {
+ char *tmp;
+ if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
+ free(tmp);
+ }
+
+ free(table);
+}
+
+
+/*
+ * This routine marks all entries as invalid
+ *
+ */
+
+void
+getuser_invalidate()
+{
+ mutex_lock(&db_lock);
+ getuser_invalidate_unlocked();
+ mutex_unlock(&db_lock);
+}
+
+static void
+getuser_invalidate_unlocked()
+{
+ operate_hash_addr(nam_hash, do_invalidate, (char *)GETUSERNAM);
+ current_admin.user.nsc_invalidate_count++;
+}
+
+void
+getuser_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
+{
+ int out_of_date;
+ nsc_bucket_t *retb;
+ char **bucket;
+
+ static time_t lastmod;
+
+ int bufferspace = maxsize - sizeof (nsc_return_t);
+
+ if (current_admin.user.nsc_enabled == 0) {
+ out->nsc_return_code = NOSERVER;
+ out->nsc_bufferbytesused = sizeof (*out);
+ return;
+ }
+
+ mutex_lock(&db_lock);
+
+ if (current_admin.user.nsc_check_files) {
+ struct stat buf;
+
+ if (stat(USERATTR_FILENAME, &buf) < 0) {
+ /*EMPTY*/;
+ } else if (lastmod == 0) {
+ lastmod = buf.st_mtime;
+ } else if (lastmod < buf.st_mtime) {
+ getuser_invalidate_unlocked();
+ lastmod = buf.st_mtime;
+ }
+ }
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getuser_lookup: looking for name %s\n",
+ in->nsc_u.name);
+ }
+
+ for (;;) {
+ if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
+ ucred_t *uc = NULL;
+
+ if (door_ucred(&uc) != 0) {
+ logit("getuser_lookup: Name too long, "
+ "but no user credential: %s\n",
+ strerror(errno));
+ } else {
+ logit("getuser_lookup: Name too long "
+ "from pid %d uid %d\n",
+ ucred_getpid(uc),
+ ucred_getruid(uc));
+ ucred_free(uc);
+ }
+
+ out->nsc_errno = NSS_NOTFOUND;
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ goto getout;
+ }
+ bucket = get_hash(nam_hash, in->nsc_u.name);
+
+ if (*bucket == (char *)-1) { /* pending lookup */
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused =
+ sizeof (*out);
+ current_admin.user.nsc_throttle_count++;
+ goto getout;
+ }
+ nscd_wait(&db_wait, &db_lock, bucket);
+ release_clearance(in->nsc_callnumber);
+ continue; /* go back and relookup hash bucket */
+ }
+ break;
+ }
+
+ /*
+ * check for no name_service mode
+ */
+
+ if (*bucket == NULL && current_admin.avoid_nameservice) {
+ out->nsc_return_code = NOTFOUND;
+ out->nsc_bufferbytesused = sizeof (*out);
+ } else if ((*bucket == NULL) || /* New entry in name service */
+ (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
+ (out_of_date = (!current_admin.avoid_nameservice &&
+ (current_admin.user.nsc_old_data_ok == 0) &&
+ (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
+ /* time has expired */
+ int saved_errno;
+ int saved_hits = 0;
+ userstr_t *p;
+
+ if (get_clearance(in->nsc_callnumber) != 0) {
+ /* no threads available */
+ out->nsc_return_code = NOSERVER;
+ /* cannot process now */
+ out->nsc_bufferbytesused = sizeof (*out);
+ current_admin.user.nsc_throttle_count++;
+ goto getout;
+ }
+
+ if (*bucket != NULL) {
+ saved_hits =
+ ((nsc_bucket_t *)*bucket)->nsc_hits;
+ }
+
+ /*
+ * block any threads accessing this bucket if data is
+ * non-existent out of date
+ */
+
+ if (*bucket == NULL || out_of_date) {
+ update_user_bucket((nsc_bucket_t **)bucket,
+ (nsc_bucket_t *)-1, in->nsc_callnumber);
+ } else {
+ /*
+ * if still not -1 bucket we are doing update...
+ * mark to prevent
+ * pileups of threads if the name service is hanging....
+ */
+ ((nsc_bucket_t *)(*bucket))->nsc_status |=
+ ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ }
+ mutex_unlock(&db_lock);
+
+ /*
+ * Call non-caching version in libnsl.
+ */
+ p = _getusernam(in->nsc_u.name, &out->nsc_u.user,
+ out->nsc_u.buff + sizeof (userstr_t),
+ bufferspace, &errno);
+ saved_errno = errno;
+
+ mutex_lock(&db_lock);
+
+ release_clearance(in->nsc_callnumber);
+
+ if (p == NULL) { /* data not found */
+
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ logit("getuser_lookup: nscd COULDN'T FIND user_attr name %s\n",
+ in->nsc_u.name);
+ }
+
+
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.user.nsc_neg_cache_misses++;
+
+ retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
+
+ retb->nsc_refcount = 1;
+ retb->nsc_data.nsc_bufferbytesused =
+ sizeof (nsc_return_t);
+ retb->nsc_data.nsc_return_code = NOTFOUND;
+ retb->nsc_data.nsc_errno = saved_errno;
+ memcpy(out, &retb->nsc_data,
+ retb->nsc_data.nsc_bufferbytesused);
+ update_user_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ goto getout;
+ } else {
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getuser_lookup: nscd FOUND user_attr name %s\n",
+ in->nsc_u.name);
+ }
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.user.nsc_pos_cache_misses++;
+
+ retb = fixbuffer(out, bufferspace);
+ update_user_bucket((nsc_bucket_t **)bucket,
+ retb, in->nsc_callnumber);
+ if (saved_hits)
+ retb->nsc_hits = saved_hits;
+ }
+ } else { /* found entry in cache */
+ retb = (nsc_bucket_t *)*bucket;
+
+ retb->nsc_hits++;
+
+ memcpy(out, &(retb->nsc_data),
+ retb->nsc_data.nsc_bufferbytesused);
+
+ if (out->nsc_return_code == SUCCESS) {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.user.nsc_pos_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getuser_lookup: found name %s in cache\n",
+ in->nsc_u.name);
+ }
+ } else {
+ if (!(UPDATEBIT & in->nsc_callnumber))
+ current_admin.user.nsc_neg_cache_hits++;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("getuser_lookup: %s marked as NOT FOUND in cache.\n",
+ in->nsc_u.name);
+ }
+ }
+
+ if ((retb->nsc_timestamp < now) &&
+ !(in->nsc_callnumber & UPDATEBIT) &&
+ !(retb->nsc_status & ST_UPDATE_PENDING)) {
+ logit("launch update since time = %d\n", retb->nsc_timestamp);
+ retb->nsc_status |= ST_UPDATE_PENDING;
+ /* cleared by deletion of old data */
+ launch_update(in);
+ }
+ }
+
+getout:
+ mutex_unlock(&db_lock);
+}
+
+/*ARGSUSED*/
+static void
+update_user_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
+{
+ if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
+ free(*old);
+ current_admin.user.nsc_entries--;
+ }
+
+ /*
+ * we can do this before reseting *old since we're holding the lock
+ */
+
+ else if (*old == (nsc_bucket_t *)-1) {
+ nscd_signal(&db_wait, (char **)old);
+ }
+
+ *old = new;
+
+ if ((new != NULL) &&
+ (new != (nsc_bucket_t *)-1)) {
+ /* real data, not just update pending or invalidate */
+ new->nsc_hits = 1;
+ new->nsc_status = 0;
+ new->nsc_refcount = 1;
+ current_admin.user.nsc_entries++;
+
+ if (new->nsc_data.nsc_return_code == SUCCESS) {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.user.nsc_pos_ttl;
+ } else {
+ new->nsc_timestamp = time(NULL) +
+ current_admin.user.nsc_neg_ttl;
+ }
+ }
+}
+
+/*ARGSUSED*/
+static nsc_bucket_t *
+fixbuffer(nsc_return_t *in, int maxlen)
+{
+ nsc_bucket_t *retb;
+ nsc_return_t *out;
+ char *dest;
+ int offset;
+ int strs;
+
+ /*
+ * find out the size of the data block we're going to need
+ */
+
+ strs = attr_strlen(in->nsc_u.user.name) +
+ attr_strlen(in->nsc_u.user.qualifier) +
+ attr_strlen(in->nsc_u.user.res1) +
+ attr_strlen(in->nsc_u.user.res2) +
+ attr_strlen(in->nsc_u.user.attr) + USERATTR_DB_NCOL;
+
+ /*
+ * allocate it and copy it in
+ * code doesn't assume packing order in original buffer
+ */
+
+ if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
+ return (NULL);
+ }
+
+ out = &(retb->nsc_data);
+ out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.user - (int)out) +
+ sizeof (userstr_t);
+ out->nsc_return_code = SUCCESS;
+ out->nsc_errno = 0;
+
+ dest = retb->nsc_data.nsc_u.buff + sizeof (userstr_t);
+ offset = (int)dest;
+
+ attr_strcpy(dest, in->nsc_u.user.name);
+ strs = 1 + attr_strlen(in->nsc_u.user.name);
+ out->nsc_u.user.name = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.user.qualifier);
+ strs = 1 + attr_strlen(in->nsc_u.user.qualifier);
+ out->nsc_u.user.qualifier = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.user.res1);
+ strs = 1 + attr_strlen(in->nsc_u.user.res1);
+ out->nsc_u.user.res1 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.user.res2);
+ strs = 1 + attr_strlen(in->nsc_u.user.res2);
+ out->nsc_u.user.res2 = dest - offset;
+ dest += strs;
+
+ attr_strcpy(dest, in->nsc_u.user.attr);
+ out->nsc_u.user.attr = dest - offset;
+
+ memcpy(in, out, out->nsc_bufferbytesused);
+
+ return (retb);
+}
+
+void
+getuser_reaper(void)
+{
+ nsc_reaper("getuser", nam_hash, &current_admin.user, &db_lock);
+}
diff --git a/usr/src/cmd/nscd/hash.c b/usr/src/cmd/nscd/hash.c
new file mode 100644
index 0000000000..91a3018cbb
--- /dev/null
+++ b/usr/src/cmd/nscd/hash.c
@@ -0,0 +1,346 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <synch.h>
+#include <memory.h>
+#include <getxby_door.h>
+
+static int hash_string();
+
+hash_t *
+make_hash(size)
+int size;
+{
+ hash_t *ptr;
+
+ ptr = (hash_t *)malloc(sizeof (*ptr));
+ ptr->size = size;
+ ptr->table = (hash_entry_t **)
+ malloc((unsigned) (sizeof (hash_entry_t *) * size));
+ (void) memset((char *)ptr->table, (char)0,
+ sizeof (hash_entry_t *)*size);
+ ptr->start = NULL;
+ ptr->hash_type = String_Key;
+ return (ptr);
+}
+
+hash_t *
+make_ihash(size)
+int size;
+{
+ hash_t *ptr;
+
+ ptr = (hash_t *)malloc(sizeof (*ptr));
+ ptr->size = size;
+ ptr->table = (hash_entry_t **)malloc((unsigned)
+ (sizeof (hash_entry_t *) * size));
+ (void) memset((char *)ptr->table, (char)0,
+ sizeof (hash_entry_t *)*size);
+ ptr->start = NULL;
+ ptr->hash_type = Integer_Key;
+ return (ptr);
+}
+
+
+char **
+get_hash(hash_t *tbl, char *key)
+{
+
+ int bucket;
+ hash_entry_t *tmp;
+ hash_entry_t *new;
+
+ if (tbl->hash_type == String_Key) {
+ tmp = tbl->table[bucket = hash_string(key, tbl->size)];
+ } else {
+ tmp = tbl->table[bucket = abs((int)key) % tbl->size];
+ }
+
+ if (tbl->hash_type == String_Key) {
+ while (tmp != NULL) {
+ if (strcmp(tmp->key, key) == 0) {
+ return (&tmp->data);
+ }
+ tmp = tmp->next_entry;
+ }
+ } else {
+ while (tmp != NULL) {
+ if (tmp->key == key) {
+ return (&tmp->data);
+ }
+ tmp = tmp->next_entry;
+ }
+ }
+
+ /*
+ * not found....
+ * insert new entry into bucket...
+ */
+
+ new = (hash_entry_t *)malloc(sizeof (*new));
+ new->key = ((tbl->hash_type == String_Key)?strdup(key):key);
+ /*
+ * hook into chain from tbl...
+ */
+ new->right_entry = NULL;
+ new->left_entry = tbl->start;
+ tbl->start = new;
+ if (new->left_entry != NULL)
+ new->left_entry->right_entry = new;
+ /*
+ * hook into bucket chain
+ */
+ new->next_entry = tbl->table[bucket];
+ tbl->table[bucket] = new;
+ new->data = NULL; /* so we know that it is new */
+ return (&new->data);
+}
+
+char **
+find_hash(hash_t *tbl, char *key)
+{
+ hash_entry_t *tmp;
+
+ if (tbl->hash_type == String_Key) {
+ tmp = tbl->table[hash_string(key, tbl->size)];
+ for (; tmp != NULL; tmp = tmp->next_entry) {
+ if (strcmp(tmp->key, key) == 0) {
+ return (&tmp->data);
+ }
+ }
+ } else {
+ tmp = tbl->table[abs((int)key) % tbl->size];
+ for (; tmp != NULL; tmp = tmp->next_entry) {
+ if (tmp->key == key) {
+ return (&tmp->data);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+char *
+del_hash(hash_t *tbl, hash_entry_t *del_this, hash_entry_t *prev, int bucket)
+{
+ /*
+ * del_this points to entry marked for deletion, prev to
+ * item preceeding in bucket chain or NULL if del_this is first.
+ * remove from bucket chain first....
+ */
+ if (tbl->hash_type == String_Key) {
+ free(del_this->key);
+ }
+ if (prev != NULL) {
+ prev->next_entry = del_this->next_entry;
+ } else {
+ tbl->table[bucket] = del_this->next_entry;
+ }
+ /*
+ * now remove from tbl chain....
+ */
+ if (del_this->right_entry != NULL) { /* not first in chain.... */
+ del_this->right_entry->left_entry = del_this->left_entry;
+ } else {
+ tbl->start = del_this->left_entry;
+ }
+ if (del_this->left_entry != NULL) { /* not last in chain.... */
+ del_this->left_entry->right_entry = del_this->right_entry;
+ }
+ return (del_this->data);
+}
+
+int
+operate_hash(hash_t *tbl, void (*ptr)(), char *usr_arg)
+{
+ hash_entry_t *tmp = tbl->start;
+ int c = 0;
+
+ while (tmp) {
+ (*ptr)(tmp->data, usr_arg, tmp->key);
+ tmp = tmp->left_entry;
+ c++;
+ }
+ return (c);
+}
+
+int
+operate_hash_addr(hash_t *tbl, void (*ptr)(), char *usr_arg)
+{
+ hash_entry_t *tmp = tbl->start;
+ int c = 0;
+
+ while (tmp) {
+ (*ptr)(&(tmp->data), usr_arg, tmp->key);
+ tmp = tmp->left_entry;
+ c++;
+ }
+ return (c);
+}
+
+void
+destroy_hash(hash_t *tbl, int (*ptr)(), char *usr_arg)
+{
+ hash_entry_t *tmp = tbl->start, *prev;
+
+ while (tmp) {
+ if (ptr) {
+ (*ptr)(tmp->data, usr_arg, tmp->key);
+ }
+
+ if (tbl->hash_type == String_Key) {
+ free(tmp->key);
+ }
+ prev = tmp;
+ tmp = tmp->left_entry;
+ free((char *)prev);
+ }
+ free((char *)tbl->table);
+ free(tbl);
+}
+
+static int
+hash_string(char *s, int modulo)
+{
+ unsigned result = 0;
+ int i = 1;
+
+ while (*s != 0) {
+ result += (*s++ << i++);
+ }
+
+ return (result % modulo);
+}
+
+int
+reap_hash(hash_t *tbl, nsc_stat_t *admin_ptr, mutex_t *hash_lock,
+ int howlong)
+{
+
+ hash_entry_t *tmp, *next, *prev;
+ uint_t count = 0;
+ uint_t bucket;
+ uint_t extra_sleep = 1;
+ uint_t buckets_per_interval, seconds_per_interval, buckets_togo;
+ uint_t total_buckets;
+ time_t now;
+
+ /*
+ * We don't want to spend too much time reaping nor too little.
+ * We cap the TTL at 2^28 to prevent overflow. This is 8.5 years,
+ * so we aren't really going to reap anything anyway.
+ * Also, we want the total time to be one second more than the
+ * time to expire the entries.
+ */
+ howlong++;
+ if (howlong < 32) howlong = 32;
+ if (howlong > (1<<28)) howlong = 1<<28;
+
+ /* Total_buckets can range from 37 to 2^30 */
+ total_buckets = admin_ptr->nsc_suggestedsize;
+
+ if (total_buckets >= howlong && total_buckets > (howlong>>2)) {
+ /*
+ * In the realm of buckets_per_second. total_buckets might
+ * be near 2^30, so we divide first
+ */
+ buckets_per_interval = total_buckets/(howlong>>2);
+ seconds_per_interval = 4;
+ } else if (total_buckets >= howlong) {
+ /* Still buckets per second, but it is safe to multiply first */
+ buckets_per_interval = (total_buckets<<2)/howlong;
+ seconds_per_interval = 4;
+ } else if (total_buckets <= (howlong>>2)) {
+ /*
+ * Now in the secs/buck realm. Howlong is at least 4 times
+ * total_buckets, so we are safe to use this as the interval.
+ * Figure out the rounding error and sleep it at the end.
+ */
+ seconds_per_interval = howlong/total_buckets;
+ buckets_per_interval = 1;
+ extra_sleep = 1 + howlong -
+ (total_buckets*seconds_per_interval);
+ } else {
+ /*
+ * Still in secs/buck realm, but seconds_per_interval
+ * is too short. Use 8 as the minimum, then adjust the extra
+ * at the end. We need 8 because of rounding error.
+ */
+ seconds_per_interval = (howlong/(total_buckets>>3));
+ buckets_per_interval = 8;
+ extra_sleep = 1 + howlong -
+ ((total_buckets>>3)*seconds_per_interval);
+ }
+
+ /*
+ * bucket keeps track of which bucket in the whole table we are on.
+ * buckets_togo is which bucket in this interval we are on.
+ */
+
+ for (bucket = buckets_togo = 0;
+ bucket < admin_ptr->nsc_suggestedsize;
+ bucket++) {
+ if (buckets_togo <= 0) {
+ sleep(seconds_per_interval);
+ buckets_togo = buckets_per_interval;
+ now = time(NULL);
+ }
+ mutex_lock(hash_lock);
+ tmp = tbl->table[bucket];
+ prev = NULL;
+ while (tmp != NULL) {
+ next = tmp->next_entry;
+ if (tmp->data == (char *)NULL) {
+ del_hash(tbl, tmp, prev, bucket);
+ free(tmp);
+ count++;
+ } else if ((tmp->data != (char *)-1) &&
+ ((((nsc_bucket_t *)(tmp->data))->nsc_status &
+ ST_UPDATE_PENDING) == 0) &&
+ (((nsc_bucket_t *)(tmp->data))->nsc_timestamp
+ < now)) {
+ del_hash(tbl, tmp, prev, bucket);
+ free(tmp->data);
+ free(tmp);
+ count++;
+ admin_ptr->nsc_entries--;
+ } else {
+ prev = tmp;
+ }
+ tmp = next;
+ }
+ mutex_unlock(hash_lock);
+ buckets_togo--;
+
+ }
+ sleep(extra_sleep);
+ return (count);
+}
diff --git a/usr/src/cmd/nscd/name-service-cache.xml b/usr/src/cmd/nscd/name-service-cache.xml
new file mode 100644
index 0000000000..14cc588eff
--- /dev/null
+++ b/usr/src/cmd/nscd/name-service-cache.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:nscd'>
+
+<service
+ name='system/name-service-cache'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance />
+
+ <dependency
+ name='config_data'
+ grouping='require_all'
+ restart_on='restart'
+ type='path'>
+ <service_fmri value='file://localhost/etc/nscd.conf' />
+ <service_fmri value='file://localhost/etc/nsswitch.conf' />
+ </dependency>
+
+ <!-- nscd needs to create /var/run/name_service_door -->
+ <dependency
+ name='filesystem'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/minimal' />
+ </dependency>
+
+ <dependent
+ name='name-service-cache_multi-user'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/multi-user' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/svc-nscd'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <property_group name='general' type='framework'>
+ <!-- to start stop nscd -->
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.name-service-cache' />
+ </property_group>
+
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ name service cache
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='nscd' section='1M' manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/nscd/nscd.h b/usr/src/cmd/nscd/nscd.h
new file mode 100644
index 0000000000..83f85a6b16
--- /dev/null
+++ b/usr/src/cmd/nscd/nscd.h
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NSCD_H
+#define _NSCD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern admin_t current_admin;
+
+extern int attr_strlen(char *);
+extern char *attr_strcpy(char *, char *);
+
+extern nsc_stat_t *getcacheptr(char *s);
+extern int nscd_set_lf(admin_t *ptr, char *s);
+extern void logit(char *format, ...);
+extern int launch_update(nsc_call_t *in);
+extern int load_admin_defaults(admin_t *ptr, int will_become_server);
+extern void getpw_init(void);
+extern void getpw_revalidate(void);
+extern void getpw_uid_reaper(void);
+extern void getpw_nam_reaper(void);
+extern void getpw_invalidate(void);
+extern void getpw_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern void getgr_init(void);
+extern void getgr_revalidate(void);
+extern void getgr_uid_reaper(void);
+extern void getgr_nam_reaper(void);
+extern void getgr_invalidate(void);
+extern void getgr_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern void gethost_init(void);
+extern void gethost_revalidate(void);
+extern void gethost_nam_reaper(void);
+extern void gethost_addr_reaper(void);
+extern void gethost_invalidate(void);
+extern void gethost_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern void getnode_init(void);
+extern void getnode_revalidate(void);
+extern void getnode_nam_reaper(void);
+extern void getnode_addr_reaper(void);
+extern void getnode_invalidate(void);
+extern void getnode_name_invalidate(void);
+extern void getnode_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern hash_t *make_hash(int size);
+extern hash_t *make_ihash(int size);
+extern char **get_hash(hash_t *tbl, char *key);
+extern char **find_hash(hash_t *tbl, char *key);
+extern char *del_hash(hash_t *tbl, hash_entry_t *del_this, hash_entry_t *prev,
+ int bucket);
+extern int operate_hash(hash_t *tbl, void (*ptr)(), char *usr_arg);
+extern int operate_hash_addr(hash_t *tbl, void (*ptr)(), char *usr_arg);
+extern void nsc_reaper(char *tbl_name, hash_t *tbl,
+ nsc_stat_t *admin_ptr, mutex_t *hash_lock);
+extern int reap_hash(hash_t *tbl, nsc_stat_t *admin_ptr,
+ mutex_t *hash_lock, int howlong);
+extern void destroy_hash(hash_t *tbl, int (*ptr)(), char *usr_arg);
+extern int *maken(int n);
+extern int insertn(int *table, int n, int data);
+extern int nscd_parse(char *progname, char *filename);
+extern int nscd_set_dl(admin_t *ptr, int value);
+extern int nscd_set_ec(nsc_stat_t *cache, char *name, int value);
+extern int nscd_set_khc(nsc_stat_t *cache, char *name, int value);
+extern int nscd_set_odo(nsc_stat_t *cache, char *name, int value);
+extern int nscd_set_ss(nsc_stat_t *cache, char *name, int value);
+extern int nscd_set_ttl_positive(nsc_stat_t *cache, char *name, int value);
+extern int nscd_set_ttl_negative(nsc_stat_t *cache, char *name, int value);
+extern int nscd_wait(waiter_t *wchan, mutex_t *lock, char **key);
+extern int nscd_signal(waiter_t *wchan, char **key);
+extern int get_clearance(int callnumber);
+extern int release_clearance(int callnumber);
+
+extern void getexec_init(void);
+extern void getexec_revalidate(void);
+extern void getexec_reaper(void);
+extern void getexec_invalidate(void);
+extern void getexec_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern void getprof_init(void);
+extern void getprof_revalidate(void);
+extern void getprof_reaper(void);
+extern void getprof_invalidate(void);
+extern void getprof_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+extern void getuser_init(void);
+extern void getuser_revalidate(void);
+extern void getuser_reaper(void);
+extern void getuser_invalidate(void);
+extern void getuser_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in,
+ time_t now);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NSCD_H */
diff --git a/usr/src/cmd/nscd/nscd_biggest.c b/usr/src/cmd/nscd/nscd_biggest.c
new file mode 100644
index 0000000000..199471d061
--- /dev/null
+++ b/usr/src/cmd/nscd/nscd_biggest.c
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1994 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * routines to find largest n numbers, carrying 4 bytes of data
+ */
+
+int *
+maken(int n)
+{
+ int * ret;
+
+ n++;
+
+ ret = (int *) memset(malloc(2 * n *sizeof (int)),
+ -1, 2 * n * sizeof (int));
+ ret[0] = n - 1;
+ return (ret);
+}
+
+insertn(int * table, int n, int data)
+{
+ int size = *table;
+ int guess, base, last;
+ int olddata;
+
+ if (table[1] > n)
+ return (data);
+
+ if (table[size] < n) /* biggest so far */
+ guess = size;
+ else {
+ base = 1;
+ last = size;
+ while (last >= base) {
+ guess = (last+base)/2;
+ if (table[guess] == n)
+ goto doit;
+ if (table[guess] > n)
+ last = guess -1;
+ else
+ base = guess + 1;
+ }
+ guess = last;
+ }
+ doit:
+ olddata = table[2 + size];
+ memmove(table + 1, table+2, sizeof (int) * (guess-1));
+ memmove(table + 2 + size, table + 3 + size, sizeof (int) * (guess-1));
+ table[guess + size + 1] = data;
+ table[guess] = n;
+ return (olddata);
+}
+
+/*
+ * test code
+ */
+#if 0
+int
+main(int argc, char * argv[])
+{
+ int * t;
+ char buffer[100];
+ int i, n;
+ char * tmp;
+
+ t = maken(100);
+
+ for (i = 0; i < 1100; i++) {
+ n = random();
+ sprintf(buffer, "trial %d: %d", i, n);
+ tmp = (char *)insertn(t, n, (int)strdup(buffer));
+ if (tmp != -1) {
+ printf("freeing %s\n", tmp);
+ free(tmp);
+ }
+ }
+
+ for (i = 1; i <= 100; i++) {
+ printf("%d: %s\n", i, t[100 + 1 + i]);
+ free((char *)t[100 + 1 + i]);
+ }
+
+ free(t);
+}
+#endif
diff --git a/usr/src/cmd/nscd/nscd_nischeck.c b/usr/src/cmd/nscd/nscd_nischeck.c
new file mode 100644
index 0000000000..f6b43de468
--- /dev/null
+++ b/usr/src/cmd/nscd/nscd_nischeck.c
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1994 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Check permissions on NIS+ tables for security
+ *
+ * Usage: /usr/lib/nscd_nischeck <table>
+ *
+ * Emit 1 if table isn't readable by "nobody" eg everybody.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nis.h>
+#include <unistd.h>
+extern int optind;
+extern char *optarg;
+
+int
+check_col(struct nis_object *table, int col)
+{
+ struct table_col *c;
+ c = table->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val + col;
+ return (NIS_NOBODY(c->tc_rights, NIS_READ_ACC));
+}
+
+int
+main(int argc, char **argv)
+{
+ nis_result *tab;
+ nis_object *obj;
+ char namebuf[64];
+
+ if (argc != 2) {
+ (void)fprintf(stderr, "usage: %s cache_name\n", argv[0]);
+ leave(1);
+ }
+
+ sprintf(namebuf, "%s.org_dir", argv[1]);
+ tab = nis_lookup(namebuf, EXPAND_NAME);
+ if (tab->status != NIS_SUCCESS) {
+ nis_perror(tab->status, namebuf);
+ leave(2);
+ }
+
+ obj = tab->objects.objects_val;
+ if (NIS_NOBODY(obj->zo_access, NIS_READ_ACC))
+ leave(0);
+
+ /*
+ * Currently only makes sense for passwd
+ */
+
+ if (strcmp(argv[1], "passwd") == 0) {
+ leave(1);
+ }
+
+ leave(0);
+}
+
+leave(int n)
+{
+ if(getenv("NSCD_DEBUG"))
+ fprintf(stderr, "nscd_nischeck: exit(%d)\n", n);
+ exit(n);
+}
diff --git a/usr/src/cmd/nscd/nscd_parse.c b/usr/src/cmd/nscd/nscd_parse.c
new file mode 100644
index 0000000000..7555bb10b1
--- /dev/null
+++ b/usr/src/cmd/nscd/nscd_parse.c
@@ -0,0 +1,402 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * routine to parse configuration file
+ *
+ * returns -1 on error, 0 on sucess. Error messages to log.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <synch.h>
+#include <sys/door.h>
+#include <unistd.h>
+
+#include "getxby_door.h"
+#include "server_door.h"
+#include "nscd.h"
+
+static int strbreak(char **, char *, char *);
+static int nscd_yesno(char *);
+static int nscd_set_integer(int *, char *, char *, int, int, int);
+static int nscd_set_short(short *, char *, char *, int, int, int);
+static int nscd_setyesno(int *, char *, char *, int);
+static int nscd_setyesno_sh(short *, char *, char *, int);
+static int nscd_set_cf(nsc_stat_t *, char *, int);
+
+int
+nscd_parse(char *progname, char *filename)
+{
+ FILE *in;
+ char buffer[255];
+ char *fields [128];
+ int errflg;
+ int linecnt;
+ int fieldcnt;
+
+ if ((in = fopen(filename, "r")) == NULL) {
+ logit("%s: open of configuration file %s failed: %s\n",
+ progname, filename, strerror(errno));
+ return (-1);
+ }
+
+ errflg = 0;
+ linecnt = 0;
+ while (fgets(buffer, sizeof (buffer), in) != NULL && !errflg) {
+ nsc_stat_t *cache;
+
+ linecnt++;
+ if ((fieldcnt = strbreak(fields, buffer, " \t\n")) == 0) {
+ /* blank */
+ continue;
+ }
+
+ switch (*fields[0]) {
+ case '#': /* comment ignore it */
+ break;
+ case 'p':
+
+ if ((strcmp("positive-time-to-live", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_ttl_positive(cache, fields[1],
+ atoi(fields[2])) < 0)
+ errflg++;
+
+ break;
+
+ case 'n':
+
+ if ((strcmp("negative-time-to-live", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_ttl_negative(cache, fields[1],
+ atoi(fields[2])) < 0)
+ errflg++;
+
+ break;
+
+ case 's':
+
+ if ((strcmp("suggested-size", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_ss(cache, fields[1], atoi(fields[2])) < 0)
+ errflg++;
+
+ break;
+
+ case 'k':
+
+ if ((strcmp("keep-hot-count", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_khc(cache, fields[1], atoi(fields[2])) < 0)
+ errflg++;
+
+ break;
+
+ case 'o':
+
+ if ((strcmp("old-data-ok", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_odo(cache, fields[1],
+ nscd_yesno(fields[2])) < 0) {
+ errflg++;
+ }
+
+ break;
+
+ case 'e':
+ if ((strcmp("enable-cache", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_ec(cache, fields[1],
+ nscd_yesno(fields[2])) < 0)
+ errflg++;
+ break;
+
+ case 'c':
+
+ if ((strcmp("check-files", fields[0]) != 0) ||
+ (fieldcnt != 3) ||
+ !(cache = getcacheptr(fields[1]))) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_cf(cache, fields[1],
+ nscd_yesno(fields[2])) < 0)
+ errflg++;
+ break;
+
+
+ case 'l':
+
+ if (strcmp("logfile", fields[0])) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_lf(&current_admin, fields[1]) < 0)
+ errflg++;
+
+ break;
+
+ case 'd':
+ if (strcmp("debug-level", fields[0])) {
+ errflg++;
+ break;
+ }
+
+ if (nscd_set_dl(&current_admin, atoi(fields[1])) < 0)
+ errflg++;
+ break;
+
+
+
+ default:
+ errflg++;
+ break;
+ }
+
+ if (errflg) {
+ logit("Syntax error line %d of configuration file %s\n",
+ linecnt, filename);
+ return (-1);
+ }
+
+ }
+
+ fclose(in);
+ return (errflg?-1:0);
+}
+
+static int
+strbreak(char *field[], char *s, char *sep)
+{
+ register int i;
+ char *lasts;
+
+ for (i = 0; field[i] = strtok_r((i?(char *)NULL:s), sep, &lasts); i++)
+ ;
+ return (i);
+}
+
+static int
+nscd_yesno(char *s)
+{
+ if (strcmp(s, "yes") == 0)
+ return (1);
+
+ if (strcmp(s, "no") == 0)
+ return (0);
+ return (-1);
+}
+
+static int
+nscd_set_integer(int *addr, char *facility, char *cachename, int value,
+ int min, int max)
+{
+ if (value < min || value > max) {
+ logit("attempted to set value of %s for %s to %d, which is not"
+ "%d <= x <= %d\n", facility, cachename, value, min, max);
+ return (-1);
+ }
+
+ if (*addr != value) {
+ if (current_admin.debug_level)
+ logit("Setting %s for %s to %d\n",
+ facility, cachename, value);
+ *addr = value;
+ return (1);
+ }
+ return (0);
+}
+
+static int
+nscd_set_short(short *addr, char *facility, char *cachename, int value,
+ int min, int max)
+{
+ if (value < min || value > max) {
+ logit("attempted to set value of %s for %s to %d, which is not "
+ "%d <= x <= %d\n", facility, cachename, value, min, max);
+ return (-1);
+ }
+
+ if (*addr != value) {
+ if (current_admin.debug_level) {
+ logit("Setting %s for %s to %d\n",
+ facility, cachename, value);
+ }
+ *addr = value;
+ return (1);
+ }
+ return (0);
+}
+
+static int
+nscd_setyesno(int *addr, char *facility, char *cachename, int value)
+{
+ int yn;
+
+ switch (yn = value) {
+ case 1:
+ case 0:
+ if (*addr != yn) {
+ if (current_admin.debug_level)
+ logit("%s now %s for %s\n", facility,
+ (yn?"enabled":"disabled"), cachename);
+ *addr = yn;
+ return (1);
+ }
+ else
+ return (0);
+ }
+ return (-1);
+}
+
+static int
+nscd_setyesno_sh(short *addr, char *facility, char *cachename, int value)
+{
+ int yn;
+
+ switch (yn = value) {
+ case 1:
+ case 0:
+ if (*addr != yn) {
+ if (current_admin.debug_level) {
+ logit("%s now %s for %s\n",
+ facility, (yn?"enabled":"disabled"),
+ cachename);
+ }
+ *addr = yn;
+ return (1);
+ }
+ else
+ return (0);
+ }
+ return (-1);
+}
+
+int
+nscd_set_dl(admin_t *ptr, int value)
+{
+ return (nscd_set_integer(&(ptr->debug_level), "Debug level", "nscd",
+ value, 0, 10));
+}
+
+int
+nscd_set_ec(nsc_stat_t *cache, char *name, int value)
+{
+ return (nscd_setyesno(&(cache->nsc_enabled), "Caching", name, value));
+}
+
+static int
+nscd_set_cf(nsc_stat_t *cache, char *name, int value)
+{
+ return (nscd_setyesno_sh(&(cache->nsc_check_files), "Checking files",
+ name, value));
+}
+
+int
+nscd_set_khc(nsc_stat_t *cache, char *name, int value)
+{
+ if (cache->nsc_pos_ttl < 600 && cache->nsc_keephot) {
+ logit("ttl less than 600 seconds - disabling keep warm for %s "
+ "cache\n", name);
+ return (0);
+ } else {
+ return (nscd_set_short(&(cache->nsc_keephot),
+ "Number of entries to keep hot", name, value, 0, 200));
+ }
+}
+
+int
+nscd_set_odo(nsc_stat_t *cache, char *name, int value)
+{
+ return (nscd_setyesno_sh(&(cache->nsc_old_data_ok),
+ "Allowing return of old data", name, value));
+}
+
+int
+nscd_set_ss(nsc_stat_t *cache, char *name, int value)
+{
+ return (nscd_set_integer(&(cache->nsc_suggestedsize),
+ "Suggested size", name, value, 37, 1<<30));
+}
+
+int
+nscd_set_ttl_positive(nsc_stat_t *cache, char *name, int value)
+{
+ int result = nscd_set_integer(&(cache->nsc_pos_ttl),
+ "Time to live for positive cache entries",
+ name, value, 0, 1<<30);
+ if (cache->nsc_pos_ttl < 600 && cache->nsc_keephot) {
+ cache->nsc_keephot = 0;
+ logit("Disabling keephot for cache %s since ttl is less than "
+ "600 seconds\n", name);
+ }
+ return (result);
+}
+
+int
+nscd_set_ttl_negative(nsc_stat_t *cache, char *name, int value)
+{
+ int result = nscd_set_integer(&(cache->nsc_neg_ttl),
+ "Time to live for negative cache entries",
+ name, value, 0, 1 << 30);
+ return (result);
+}
diff --git a/usr/src/cmd/nscd/nscd_wait.c b/usr/src/cmd/nscd/nscd_wait.c
new file mode 100644
index 0000000000..b73aaf05c9
--- /dev/null
+++ b/usr/src/cmd/nscd/nscd_wait.c
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1994 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * routines to wait and wake up a client waiting on a list for a
+ * name service request
+ */
+#include <thread.h>
+#include <synch.h>
+#include "getxby_door.h"
+
+int
+nscd_wait(waiter_t * wchan, mutex_t * lock, char ** key)
+{
+ waiter_t mywait;
+ cond_init(&(mywait.w_waitcv), USYNC_THREAD, 0);
+ mywait.w_key = key;
+ mywait.w_next = wchan->w_next;
+ mywait.w_prev = wchan;
+ if(mywait.w_next)
+ mywait.w_next->w_prev = &mywait;
+ wchan->w_next = &mywait;
+
+ while( *key == (char *) -1)
+ cond_wait(&(mywait.w_waitcv), lock);
+ if(mywait.w_prev)
+ mywait.w_prev->w_next = mywait.w_next;
+ if(mywait.w_next)
+ mywait.w_next->w_prev = mywait.w_prev;
+ return(0);
+}
+
+int
+nscd_signal(waiter_t * wchan, char ** key)
+{
+ int c = 0;
+ waiter_t * tmp = wchan->w_next;
+
+ while(tmp) {
+ if(tmp->w_key == key) {
+ cond_signal(&(tmp->w_waitcv));
+ c++;
+ }
+ tmp = tmp->w_next;
+ }
+
+ return(c);
+}
+
+#ifdef TESTPROG
+
+static waiter_t w;
+static mutex_t l;
+static char ** blocks;
+
+static int num_threads;
+
+main(int argc, char * argv[])
+{
+ int i;
+ void * go();
+ if(argc != 2) {
+ printf("usage: %s numthreads\n", argv[0]);
+ exit(1);
+ }
+
+ num_threads = atoi(argv[1]);
+
+ blocks = (char **) malloc(sizeof(char **) * num_threads);
+
+ memset(blocks, -1, sizeof(char**) * num_threads);
+
+ mutex_lock(&l);
+
+ for(i=0;i<num_threads;i++)
+ if(thr_create(NULL, NULL, go, (void*)i, THR_NEW_LWP, NULL) != 0) {
+ printf("thread_create failed\n");
+ exit(2);
+ }
+
+
+
+ mutex_unlock(&l);
+
+ sleep(5);
+
+ printf("going\n");
+ mutex_lock(&l);
+
+ memset(blocks, 0, sizeof(char**) * num_threads);
+
+ for(i=0;i<num_threads;i++)
+ nscd_signal(&w, blocks+i);
+
+ mutex_unlock(&l);
+
+ while(num_threads--) {
+ if(thr_join(NULL, NULL, NULL) < 0) {
+ printf("error in join\n");
+ exit(2);
+ }
+ }
+
+ printf("all done\n");
+ exit(0);
+}
+
+void * go(int index)
+{
+ printf("thread %d locking\n", index);
+
+ mutex_lock(&l);
+
+ printf("thread %d waiting\n", index);
+
+ nscd_wait(&w,&l, blocks+index);
+
+ printf("thread %d unlocking\n", index);
+
+ mutex_unlock(&l);
+
+ thr_exit(NULL);
+}
+
+
+#endif TESTPROG
+
diff --git a/usr/src/cmd/nscd/server.c b/usr/src/cmd/nscd/server.c
new file mode 100644
index 0000000000..3adfd88dc5
--- /dev/null
+++ b/usr/src/cmd/nscd/server.c
@@ -0,0 +1,1882 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Simple doors name server cache daemon
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/door.h>
+#include <sys/types.h>
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/zone.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <thread.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <door.h>
+#include "getxby_door.h"
+#include "server_door.h"
+#include "nscd.h"
+/* Includes for filenames of databases */
+#include <shadow.h>
+#include <userdefs.h>
+#include <netdb.h>
+#include <nss_dbdefs.h>
+#include <exec_attr.h>
+#include <prof_attr.h>
+#include <user_attr.h>
+#include <ucred.h>
+#include <priv.h>
+#include <libscf.h>
+
+extern int optind;
+extern int opterr;
+extern int optopt;
+extern char *optarg;
+
+static void switcher(void *, char *, size_t, door_desc_t *, uint_t);
+static void rts_mon(void);
+static void usage(char *);
+static int nsc_calllen(nsc_call_t *);
+static int client_getadmin(admin_t *);
+static void getadmin(nsc_return_t *, int, nsc_call_t *);
+static int setadmin(nsc_return_t *, int, nsc_call_t *);
+static void client_killserver(void);
+static int client_setadmin(admin_t *);
+static void client_showstats(admin_t *);
+static void detachfromtty(void);
+
+
+admin_t current_admin;
+static int will_become_server;
+
+void
+nsc_reaper(char *tbl_name, hash_t *tbl, nsc_stat_t *admin_ptr,
+ mutex_t *hash_lock)
+{
+ uint_t count;
+ uint_t interval;
+
+ while (1) {
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("reaper_%s: %d entries in cache\n",
+ tbl_name, admin_ptr->nsc_entries);
+ }
+ if (admin_ptr->nsc_entries > 0) {
+ count = reap_hash(tbl, admin_ptr, hash_lock,
+ admin_ptr->nsc_pos_ttl);
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("reaper_%s: reaped %d entries\n",
+ tbl_name, count);
+ }
+ } else {
+ /*
+ * We set a minimum wait of 60 before checking again;
+ * we don't want to sleep for no time at all.
+ * We don't clamp it for the reaping itself, that is
+ * done in reap_hash, and with a different minimum.
+ */
+ interval = admin_ptr->nsc_pos_ttl;
+ if (interval < 60) interval = 60;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit(
+ "reaper_%s: Nothing to reap, sleep %d\n",
+ tbl_name, interval);
+ }
+ sleep(interval);
+ }
+ }
+}
+
+nsc_stat_t *
+getcacheptr(char *s)
+{
+ static const char *caches[7] = {"passwd", "group", "hosts", "ipnodes",
+ "exec_attr", "prof_attr", "user_attr" };
+
+ if (strncmp(caches[0], s, strlen(caches[0])) == 0)
+ return (&current_admin.passwd);
+
+ if (strncmp(caches[1], s, strlen(caches[1])) == 0)
+ return (&current_admin.group);
+
+ if (strncmp(caches[2], s, strlen(caches[2])) == 0)
+ return (&current_admin.host);
+
+ if (strncmp(caches[3], s, strlen(caches[3])) == 0)
+ return (&current_admin.node);
+
+ if (strncmp(caches[4], s, strlen(caches[4])) == 0)
+ return (&current_admin.exec);
+
+ if (strncmp(caches[5], s, strlen(caches[5])) == 0)
+ return (&current_admin.prof);
+
+ if (strncmp(caches[6], s, strlen(caches[6])) == 0)
+ return (&current_admin.user);
+
+ return (NULL);
+}
+
+static char *
+getcacheopt(char *s)
+{
+ while (*s && *s != ',')
+ s++;
+ return ((*s == ',') ? (s + 1) : NULL);
+}
+
+/*
+ * routine to check if server is already running
+ */
+
+static int
+nsc_ping(void)
+{
+ nsc_data_t data;
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ data.nsc_call.nsc_callnumber = NULLCALL;
+ ndata = sizeof (data);
+ adata = sizeof (data);
+ dptr = &data;
+ return (_nsc_trydoorcall(&dptr, &ndata, &adata));
+}
+
+static void
+dozip(void)
+{
+ /* not much here */
+}
+
+static void
+keep_open_dns_socket(void)
+{
+ _res.options |= RES_STAYOPEN; /* just keep this udp socket open */
+}
+
+/*
+ * declaring this causes the files backend to use hashing
+ * this is of course an utter hack, but provides a nice
+ * quiet back door to enable this feature for only the nscd.
+ */
+void
+__nss_use_files_hash(void)
+{
+
+}
+/*
+ *
+ * The allocation of resources for cache lookups is an interesting
+ * problem, and one that has caused several bugs in the beta release
+ * of 2.5. In particular, the introduction of a thottle to prevent
+ * the creation of excessive numbers of LWPs in the case of a failed
+ * name service has led to a denial of service problem when the
+ * name service request rate exceeds the name service's ability
+ * to respond. As a result, I'm implementing the following
+ * algorithm:
+ *
+ * 1) We cap the number of total threads.
+ * 2) We save CACHE_THREADS of those for cache lookups only.
+ * 3) We use a common pool of 2/3 of the remain threads that are used first
+ * 4) We save the remainder and allocate 1/3 of it for table specific lookups
+ *
+ * The intent is to prevent the failure of a single name service from
+ * causing denial of service, and to always have threads available for
+ * cached lookups. If a request comes in and the answer isn't in the
+ * cache and we cannot get a thread, we simply return NOSERVER, forcing
+ * the client to lookup the
+ * data itself. This will prevent the types of starvation seen
+ * at UNC due to a single threaded DNS backend, and allows the cache
+ * to eventually become filled.
+ *
+ */
+
+/* 7 tables: passwd, group, hosts, ipnodes, exec_attr, prof_attr, user_attr */
+#define NSCD_TABLES 7
+#define TABLE_THREADS 10
+#define COMMON_THREADS 20
+#define CACHE_MISS_THREADS (COMMON_THREADS + NSCD_TABLES * TABLE_THREADS)
+#define CACHE_HIT_THREADS 20
+#define MAX_SERVER_THREADS (CACHE_HIT_THREADS + CACHE_MISS_THREADS)
+
+static sema_t common_sema;
+static sema_t passwd_sema;
+static sema_t hosts_sema;
+static sema_t nodes_sema;
+static sema_t group_sema;
+static sema_t exec_sema;
+static sema_t prof_sema;
+static sema_t user_sema;
+static thread_key_t lookup_state_key;
+
+static void
+initialize_lookup_clearance(void)
+{
+ thr_keycreate(&lookup_state_key, NULL);
+ (void) sema_init(&common_sema, COMMON_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&passwd_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&hosts_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&nodes_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&group_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&exec_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&prof_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&user_sema, TABLE_THREADS, USYNC_THREAD, 0);
+}
+
+int
+get_clearance(int callnumber)
+{
+ sema_t *table_sema = NULL;
+ char *tab;
+
+ if (sema_trywait(&common_sema) == 0) {
+ thr_setspecific(lookup_state_key, NULL);
+ return (0);
+ }
+
+ switch (MASKUPDATEBIT(callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ tab = "passwd";
+ table_sema = &passwd_sema;
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ tab = "group";
+ table_sema = &group_sema;
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ tab = "hosts";
+ table_sema = &hosts_sema;
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ tab = "ipnodes";
+ table_sema = &nodes_sema;
+ break;
+ case GETEXECID:
+ tab = "exec_attr";
+ table_sema = &exec_sema;
+ break;
+
+ case GETPROFNAM:
+ tab = "prof_attr";
+ table_sema = &prof_sema;
+ break;
+
+ case GETUSERNAM:
+ tab = "user_attr";
+ table_sema = &user_sema;
+ break;
+
+ }
+
+ if (sema_trywait(table_sema) == 0) {
+ thr_setspecific(lookup_state_key, (void*)1);
+ return (0);
+ }
+
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ logit("get_clearance: throttling load for %s table\n", tab);
+ }
+ return (-1);
+}
+
+int
+release_clearance(int callnumber)
+{
+ int which;
+
+ sema_t *table_sema = NULL;
+
+ thr_getspecific(lookup_state_key, (void**)&which);
+
+ if (which == 0) /* from common pool */ {
+ (void) sema_post(&common_sema);
+ return (0);
+ }
+
+ switch (MASKUPDATEBIT(callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ table_sema = &passwd_sema;
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ table_sema = &group_sema;
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ table_sema = &hosts_sema;
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ table_sema = &nodes_sema;
+ break;
+
+ case GETEXECID:
+ table_sema = &exec_sema;
+ break;
+
+ case GETPROFNAM:
+ table_sema = &prof_sema;
+ break;
+
+ case GETUSERNAM:
+ table_sema = &user_sema;
+ break;
+ }
+
+ (void) sema_post(table_sema);
+ return (0);
+}
+
+
+static mutex_t create_lock;
+static int nscd_max_servers = MAX_SERVER_THREADS;
+static int num_servers = 0;
+static thread_key_t server_key;
+
+/*
+ * Bind a TSD value to a server thread. This enables the destructor to
+ * be called if/when this thread exits. This would be a programming error,
+ * but better safe than sorry.
+ */
+/*ARGSUSED*/
+static void *
+server_tsd_bind(void *arg)
+{
+ static void *value = 0;
+
+ /* disable cancellation to avoid hangs if server threads disappear */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ thr_setspecific(server_key, value);
+ door_return(NULL, 0, NULL, 0);
+
+ /* make lint happy */
+ return (NULL);
+}
+
+/*
+ * Server threads are created here.
+ */
+/*ARGSUSED*/
+static void
+server_create(door_info_t *dip)
+{
+ (void) mutex_lock(&create_lock);
+ if (++num_servers > nscd_max_servers) {
+ num_servers--;
+ (void) mutex_unlock(&create_lock);
+ return;
+ }
+ (void) mutex_unlock(&create_lock);
+ thr_create(NULL, 0, server_tsd_bind, NULL, THR_BOUND|THR_DETACHED,
+ NULL);
+}
+
+/*
+ * Server thread are destroyed here
+ */
+/*ARGSUSED*/
+static void
+server_destroy(void *arg)
+{
+ (void) mutex_lock(&create_lock);
+ num_servers--;
+ (void) mutex_unlock(&create_lock);
+}
+
+static char **saved_argv;
+static char saved_execname[MAXPATHLEN];
+
+static void
+save_execname()
+{
+ const char *name = getexecname();
+
+ saved_execname[0] = 0;
+
+ if (name[0] != '/') { /* started w/ relative path */
+ (void) getcwd(saved_execname, MAXPATHLEN);
+ strlcat(saved_execname, "/", MAXPATHLEN);
+ }
+ strlcat(saved_execname, name, MAXPATHLEN);
+}
+
+void
+main(int argc, char ** argv)
+{
+ int did;
+ int opt;
+ int errflg = 0;
+ int showstats = 0;
+ int doset = 0;
+ int loaded_config_file = 0;
+ struct stat buf;
+ sigset_t myset;
+ struct sigaction action;
+
+ /*
+ * Special case non-root user here - he can just print stats
+ */
+
+ if (geteuid()) {
+ if (argc != 2 || strcmp(argv[1], "-g")) {
+ (void) fprintf(stderr,
+ "Must be root to use any option other than "\
+ "-g.\n\n");
+ usage(argv[0]);
+ }
+
+ if ((nsc_ping() != SUCCESS) ||
+ (client_getadmin(&current_admin) != 0)) {
+ (void) fprintf(stderr,
+ "%s doesn't appear to be running.\n", argv[0]);
+ exit(1);
+ }
+ client_showstats(&current_admin);
+ exit(0);
+ }
+
+
+
+ /*
+ * Determine if there is already a daemon running
+ */
+
+ will_become_server = (nsc_ping() != SUCCESS);
+
+ /*
+ * process usual options
+ */
+
+ /*
+ * load normal config file
+ */
+
+ if (will_become_server) {
+ static const nsc_stat_t defaults = {
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 211, /* suggested size */
+ 1, /* enabled */
+ 0, /* invalidate cmd */
+ 600, /* positive ttl */
+ 10, /* netative ttl */
+ 20, /* keep hot */
+ 0, /* old data not ok */
+ 1 }; /* check files */
+
+ current_admin.passwd = defaults;
+ current_admin.group = defaults;
+ current_admin.host = defaults;
+ current_admin.node = defaults;
+ current_admin.exec = defaults;
+ current_admin.prof = defaults;
+ current_admin.user = defaults;
+
+ current_admin.logfile[0] = '\0';
+
+ if (access("/etc/nscd.conf", R_OK) == 0) {
+ if (nscd_parse(argv[0], "/etc/nscd.conf") < 0) {
+ exit(1);
+ }
+ loaded_config_file++;
+ }
+ }
+
+ else {
+ if (client_getadmin(&current_admin)) {
+ (void) fprintf(stderr,
+ "Cannot contact nscd properly(?)\n");
+ exit(1);
+ }
+
+ current_admin.logfile[0] = '\0';
+ }
+
+ while ((opt = getopt(argc, argv,
+ "S:Kf:c:ge:p:n:i:l:d:s:h:o:")) != EOF) {
+ nsc_stat_t *cache;
+ char *cacheopt;
+
+ switch (opt) {
+
+ case 'S': /* undocumented feature */
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_secure_mode = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_secure_mode = 0;
+ else
+ errflg++;
+ break;
+
+ case 'K': /* undocumented feature */
+ client_killserver();
+ exit(0);
+ break;
+
+ case 'f':
+ doset++;
+ loaded_config_file++;
+ if (nscd_parse(argv[0], optarg) < 0) {
+ exit(1);
+ }
+ break;
+
+ case 'g':
+ showstats++;
+ break;
+
+ case 'p':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_pos_ttl = atoi(cacheopt);
+ break;
+
+ case 'n':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_neg_ttl = atoi(cacheopt);
+ break;
+
+ case 'c':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_check_files = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_check_files = 0;
+ else
+ errflg++;
+ break;
+
+
+ case 'i':
+ doset++;
+ cache = getcacheptr(optarg);
+ if (!cache) {
+ errflg++;
+ break;
+ }
+ cache->nsc_invalidate = 1;
+ break;
+
+ case 'l':
+ doset++;
+ (void) strlcpy(current_admin.logfile, optarg, 128);
+ break;
+
+ case 'd':
+
+ doset++;
+ current_admin.debug_level = atoi(optarg);
+ break;
+
+ case 's':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+
+ cache->nsc_suggestedsize = atoi(cacheopt);
+
+ break;
+
+ case 'h':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_keephot = atoi(cacheopt);
+ break;
+
+ case 'o':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_old_data_ok = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_old_data_ok = 0;
+ else
+ errflg++;
+ break;
+
+ case 'e':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_enabled = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_enabled = 0;
+ else
+ errflg++;
+ break;
+
+ default:
+ errflg++;
+ break;
+ }
+
+ }
+
+ if (errflg)
+ usage(argv[0]);
+
+ if (!will_become_server) {
+
+ if (showstats) {
+ client_showstats(&current_admin);
+ }
+
+ if (doset) {
+ if (client_setadmin(&current_admin) < 0) {
+ (void) fprintf(stderr,
+ "Error during admin call\n");
+ exit(1);
+ }
+ }
+ if (!showstats && !doset) {
+ (void) fprintf(stderr,
+ "%s already running.... no admin specified\n",
+ argv[0]);
+ }
+ exit(0);
+ }
+
+ /*
+ * daemon from here ou
+ */
+
+ if (!loaded_config_file) {
+ (void) fprintf(stderr,
+ "No configuration file specifed and /etc/nscd.conf" \
+ "not present\n");
+ exit(1);
+ }
+
+ saved_argv = argv;
+ save_execname();
+
+ if (current_admin.debug_level) {
+ /* we're debugging... */
+ if (strlen(current_admin.logfile) == 0)
+ /* no specified log file */
+ (void) strcpy(current_admin.logfile, "stderr");
+ else
+ (void) nscd_set_lf(&current_admin,
+ current_admin.logfile);
+ } else {
+ if (strlen(current_admin.logfile) == 0)
+ (void) strcpy(current_admin.logfile, "/dev/null");
+ (void) nscd_set_lf(&current_admin, current_admin.logfile);
+ detachfromtty();
+ }
+
+ /* perform some initialization */
+ initialize_lookup_clearance();
+ keep_open_dns_socket();
+ getpw_init();
+ getgr_init();
+ gethost_init();
+ getnode_init();
+ getexec_init();
+ getprof_init();
+ getuser_init();
+
+ /* Establish our own server thread pool */
+
+ door_server_create(server_create);
+ if (thr_keycreate(&server_key, server_destroy) != 0) {
+ perror("thr_keycreate");
+ exit(-1);
+ }
+
+ /* Create a door */
+
+ if ((did = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
+ DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
+ perror("door_create");
+ exit(-1);
+ }
+
+ /* bind to file system */
+
+ if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
+ int newfd;
+ if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
+ logit("Cannot create %s:%s\n",
+ NAME_SERVICE_DOOR,
+ strerror(errno));
+ exit(1);
+ }
+ (void) close(newfd);
+ }
+
+ if (fattach(did, NAME_SERVICE_DOOR) < 0) {
+ if ((errno != EBUSY) ||
+ (fdetach(NAME_SERVICE_DOOR) < 0) ||
+ (fattach(did, NAME_SERVICE_DOOR) < 0)) {
+ perror("door_attach");
+ exit(2);
+ }
+ }
+
+ action.sa_handler = dozip;
+ action.sa_flags = 0;
+ (void) sigemptyset(&action.sa_mask);
+ (void) sigemptyset(&myset);
+ (void) sigaddset(&myset, SIGHUP);
+
+ if (sigaction(SIGHUP, &action, NULL) < 0) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ if (thr_sigsetmask(SIG_BLOCK, &myset, NULL) < 0) {
+ perror("thr_sigsetmask");
+ exit(1);
+ }
+
+
+ /*
+ * kick off revalidate threads
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getnode_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getgr_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getexec_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getprof_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getuser_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ /*
+ * kick off reaper threads
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_uid_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getgr_uid_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getgr_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_addr_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getnode_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getnode_addr_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getexec_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getprof_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getuser_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ /*
+ * kick off routing socket monitor thread
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_sigsetmask(SIG_UNBLOCK, &myset, NULL) < 0) {
+ perror("thr_sigsetmask");
+ exit(1);
+ }
+
+ for (;;) {
+ (void) pause();
+ logit("Reloading /etc/nscd.conf\n");
+ nscd_parse(argv[0], "/etc/nscd.conf");
+ }
+}
+
+
+/*ARGSUSED*/
+static void
+switcher(void *cookie, char *argp, size_t arg_size,
+ door_desc_t *dp, uint_t n_desc)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ time_t now;
+
+ static time_t last_nsswitch_check;
+ static time_t last_nsswitch_modified;
+ static time_t last_resolv_modified;
+
+ static mutex_t nsswitch_lock;
+
+ nsc_call_t *ptr = (nsc_call_t *)argp;
+
+ if (argp == DOOR_UNREF_DATA) {
+ (void) printf("Door Slam... exiting\n");
+ exit(0);
+ }
+
+ if (ptr == NULL) { /* empty door call */
+ (void) door_return(NULL, 0, 0, 0); /* return the favor */
+ }
+
+ now = time(NULL);
+
+ /*
+ * just in case check
+ */
+
+ (void) mutex_lock(&nsswitch_lock);
+
+ if (now - last_nsswitch_check > 10) {
+ struct stat nss_buf;
+ struct stat res_buf;
+
+ last_nsswitch_check = now;
+
+ (void) mutex_unlock(&nsswitch_lock); /* let others continue */
+
+ /*
+ * This code keeps us from statting resolv.conf
+ * if it doesn't exist, yet prevents us from ignoring
+ * it if it happens to disappear later on for a bit.
+ */
+
+ if (last_resolv_modified >= 0) {
+ if (stat("/etc/resolv.conf", &res_buf) < 0) {
+ if (last_resolv_modified == 0)
+ last_resolv_modified = -1;
+ else
+ res_buf.st_mtime = last_resolv_modified;
+ } else if (last_resolv_modified == 0) {
+ last_resolv_modified = res_buf.st_mtime;
+ }
+ }
+
+ if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
+
+ /*EMPTY*/;
+
+ } else if (last_nsswitch_modified == 0) {
+
+ last_nsswitch_modified = nss_buf.st_mtime;
+
+ } else if ((last_nsswitch_modified < nss_buf.st_mtime) ||
+ ((last_resolv_modified > 0) &&
+ (last_resolv_modified < res_buf.st_mtime))) {
+ static mutex_t exit_lock;
+ char *fmri;
+ /*
+ * time for restart
+ */
+ logit("nscd restart due to /etc/nsswitch.conf or "\
+ "resolv.conf change\n");
+ /*
+ * try to restart under smf
+ */
+ if ((fmri = getenv("SMF_FMRI")) == NULL) {
+ /* not running under smf - reexec */
+ execv(saved_execname, saved_argv);
+ exit(1); /* just in case */
+ }
+
+ mutex_lock(&exit_lock); /* prevent multiple restarts */
+ if (smf_restart_instance(fmri) == 0)
+ sleep(10); /* wait a bit */
+ exit(1); /* give up waiting for resurrection */
+ }
+
+ } else
+ (void) mutex_unlock(&nsswitch_lock);
+
+ switch (ptr->nsc_callnumber) {
+
+ case NULLCALL:
+ u.data.nsc_ret.nsc_return_code = SUCCESS;
+ u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
+ break;
+
+
+ case GETPWNAM:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETPWUID:
+ getpw_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETGRNAM:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETGRGID:
+ getgr_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETHOSTBYNAME:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETHOSTBYADDR:
+ gethost_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETIPNODEBYNAME:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETIPNODEBYADDR:
+ getnode_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETEXECID:
+ *(argp + arg_size - 1) = 0;
+ getexec_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETPROFNAM:
+ *(argp + arg_size - 1) = 0;
+ getprof_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETUSERNAM:
+ *(argp + arg_size - 1) = 0;
+ getuser_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETADMIN:
+ getadmin(&u.data.nsc_ret, sizeof (u), ptr);
+ break;
+
+ case SETADMIN:
+ case KILLSERVER: {
+
+ ucred_t *uc = NULL;
+ const priv_set_t *eset;
+ zoneid_t zoneid;
+
+ if (door_ucred(&uc) != 0) {
+ perror("door_ucred");
+ u.data.nsc_ret.nsc_return_code = NOTFOUND;
+ break;
+ }
+
+ eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
+ zoneid = ucred_getzoneid(uc);
+
+ if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
+ eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
+ ucred_geteuid(uc) != 0) {
+ logit("SETADMIN call failed(cred): caller pid %d, "
+ "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
+ ucred_getruid(uc), ucred_geteuid(uc), zoneid);
+ u.data.nsc_ret.nsc_return_code = NOTFOUND;
+ ucred_free(uc);
+ break;
+ }
+
+ if (ptr->nsc_callnumber == KILLSERVER) {
+ logit("Nscd received KILLSERVER cmd from pid %d, "
+ "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
+ ucred_getruid(uc), ucred_geteuid(uc), zoneid);
+ exit(0);
+ } else {
+ if (setadmin(&u.data.nsc_ret, sizeof (u), ptr) != 0)
+ logit("SETADMIN call failed\n");
+ }
+ ucred_free(uc);
+ break;
+ }
+
+ default:
+ logit("Unknown name service door call op %d\n",
+ ptr->nsc_callnumber);
+ u.data.nsc_ret.nsc_return_code = -1;
+ u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
+ break;
+
+ }
+ door_return((char *)&u.data, u.data.nsc_ret.nsc_bufferbytesused,
+ NULL, 0);
+}
+
+/*
+ * Monitor the routing socket. Address lists stored in the ipnodes
+ * cache are sorted based on destination address selection rules,
+ * so when things change that could affect that sorting (interfaces
+ * go up or down, flags change, etc.), we clear that cache so the
+ * list will be re-ordered the next time the hostname is resolved.
+ */
+static void
+rts_mon(void)
+{
+ int rt_sock, rdlen;
+ union {
+ struct {
+ struct rt_msghdr rtm;
+ struct sockaddr_storage addrs[RTA_NUMBITS];
+ } r;
+ struct if_msghdr ifm;
+ struct ifa_msghdr ifam;
+ } mbuf;
+ struct ifa_msghdr *ifam = &mbuf.ifam;
+
+ rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (rt_sock < 0) {
+ logit("Failed to open routing socket: %s\n", strerror(errno));
+ thr_exit(0);
+ }
+
+ for (;;) {
+ rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
+ if (rdlen <= 0) {
+ if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
+ logit("routing socket read: %s\n",
+ strerror(errno));
+ thr_exit(0);
+ }
+ continue;
+ }
+ if (ifam->ifam_version != RTM_VERSION) {
+ logit("rx unknown version (%d) on routing socket.\n",
+ ifam->ifam_version);
+ continue;
+ }
+ switch (ifam->ifam_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ getnode_name_invalidate();
+ break;
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ case RTM_GET:
+ case RTM_LOSING:
+ case RTM_REDIRECT:
+ case RTM_MISS:
+ case RTM_LOCK:
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ case RTM_RESOLVE:
+ case RTM_IFINFO:
+ break;
+ default:
+ logit("rx unknown msg type (%d) on routing socket.\n",
+ ifam->ifam_type);
+ break;
+ }
+ }
+}
+
+static void
+usage(char *s)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-d debug_level] [-l logfilename]\n", s);
+ (void) fprintf(stderr,
+ " [-p cachename,positive_time_to_live]\n");
+ (void) fprintf(stderr,
+ " [-n cachename,negative_time_to_live]\n");
+ (void) fprintf(stderr,
+ " [-i cachename] [-s cachename,suggestedsize]\n");
+
+ (void) fprintf(stderr,
+ " [-h cachename,keep_hot_count] "\
+ "[-o cachename,\"yes\"|\"no\"]\n");
+
+ (void) fprintf(stderr,
+ " [-e cachename,\"yes\"|\"no\"] [-g] " \
+ "[-c cachename,\"yes\"|\"no\"]\n");
+
+ (void) fprintf(stderr,
+ " [-f configfilename] \n");
+
+ (void) fprintf(stderr,
+ "\n Supported caches: passwd, group, hosts, ipnodes\n");
+
+ (void) fprintf(stderr,
+ " exec_attr, prof_attr, and user_attr.\n");
+
+ exit(1);
+
+}
+
+
+static int logfd = 2;
+
+int
+nscd_set_lf(admin_t *ptr, char *s)
+{
+ int newlogfd;
+
+ /*
+ * we don't really want to try and open the log file
+ * /dev/null since that will fail w/ our security fixes
+ */
+
+ if (*s == 0) {
+ /* ignore empty log file specs */
+ /*EMPTY*/;
+ } else if (s == NULL || strcmp(s, "/dev/null") == 0) {
+ (void) strcpy(current_admin.logfile, "/dev/null");
+ (void) close(logfd);
+ logfd = -1;
+ } else {
+ /*
+ * In order to open this file securely, we'll try a few tricks
+ */
+
+ if ((newlogfd = open(s, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
+ /*
+ * File already exists... now we need to get cute
+ * since opening a file in a world-writeable directory
+ * safely is hard = it could be a hard link or a
+ * symbolic link to a system file.
+ */
+ struct stat before;
+
+ if (lstat(s, &before) < 0) {
+ logit("Cannot open new logfile \"%s\": %sn",
+ s, strerror(errno));
+ return (-1);
+ }
+
+ if (S_ISREG(before.st_mode) && /* no symbolic links */
+ (before.st_nlink == 1) && /* no hard links */
+ (before.st_uid == 0)) { /* owned by root */
+ if ((newlogfd =
+ open(s, O_APPEND|O_WRONLY, 0644)) < 0) {
+ logit("Cannot open new "\
+ "logfile \"%s\": %s\n", s,
+ strerror(errno));
+ return (-1);
+ }
+ } else {
+ logit("Cannot use specified logfile \"%s\": "\
+ "file is/has links or isn't owned by "\
+ "root\n", s);
+ return (-1);
+ }
+ }
+
+ (void) strlcpy(ptr->logfile, s, 128);
+ (void) close(logfd);
+ logfd = newlogfd;
+ logit("Start of new logfile %s\n", s);
+ }
+ return (0);
+}
+
+void
+logit(char *format, ...)
+{
+ static mutex_t loglock;
+ struct timeval tv;
+
+#define LOGBUFLEN 1024
+ char buffer[LOGBUFLEN];
+
+ va_list ap;
+ va_start(ap, format);
+
+ if (logfd >= 0) {
+ int safechars, offset;
+ if (gettimeofday(&tv, NULL) != 0 ||
+ ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) {
+ (void) snprintf(buffer, LOGBUFLEN,
+ "<time conversion failed>\t");
+ } else {
+ /*
+ * ctime_r() includes some stuff we don't want;
+ * adjust length to overwrite " YYYY\n".
+ */
+ offset = strlen(buffer) - 6;
+ safechars = LOGBUFLEN - (offset - 1);
+ (void) snprintf(buffer + offset, safechars, ".%.4ld\t",
+ tv.tv_usec/100);
+ }
+ offset = strlen(buffer);
+ safechars = LOGBUFLEN - (offset - 1);
+ if (vsnprintf(buffer + offset, safechars, format, ap) >
+ safechars) {
+ (void) strncat(buffer, "...\n", LOGBUFLEN);
+ }
+
+ (void) mutex_lock(&loglock);
+ (void) write(logfd, buffer, strlen(buffer));
+ (void) mutex_unlock(&loglock);
+ }
+
+ va_end(ap);
+#undef LOGBUFLEN
+}
+
+static void
+do_update(nsc_call_t *in)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ time_t now = time(NULL);
+
+ switch (MASKUPDATEBIT(in->nsc_callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ getpw_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ getgr_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ gethost_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ getnode_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETEXECID:
+ getexec_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETPROFNAM:
+ getprof_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETUSERNAM:
+ getuser_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ free(in);
+}
+
+int
+launch_update(nsc_call_t *in)
+{
+ nsc_call_t *c;
+
+ int l = nsc_calllen(in);
+
+ in->nsc_callnumber |= UPDATEBIT;
+
+ if ((c = malloc(l)) == NULL) {
+ logit("thread create failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ (void) memcpy(c, in, l);
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("launching update\n");
+ }
+
+ if (thr_create(NULL,
+ NULL,
+ (void *(*)(void*))do_update,
+ c,
+ 0|THR_DETACHED, NULL) != 0) {
+ logit("thread create failed\n");
+ exit(1);
+ }
+
+ return (0);
+}
+
+static int
+nsc_calllen(nsc_call_t *in)
+{
+ switch (MASKUPDATEBIT(in->nsc_callnumber)) {
+
+ case GETPWUID:
+ case GETGRGID:
+ case NULLCALL:
+ return (sizeof (*in));
+
+ case GETPWNAM:
+ case GETGRNAM:
+ case GETHOSTBYNAME:
+ return (sizeof (*in) + strlen(in->nsc_u.name));
+ case GETIPNODEBYNAME:
+ return (sizeof (*in) + strlen(in->nsc_u.ipnode.name));
+
+ case GETHOSTBYADDR:
+ case GETIPNODEBYADDR:
+ return (sizeof (*in) + in->nsc_u.addr.a_length);
+
+ case GETEXECID:
+ case GETPROFNAM:
+ case GETUSERNAM:
+
+ return (sizeof (*in) + strlen(in->nsc_u.name));
+ }
+
+ return (0);
+}
+
+static int
+client_getadmin(admin_t *ptr)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = GETADMIN;
+ ndata = sizeof (u);
+ adata = sizeof (u.data);
+ dptr = &u.data;
+
+ if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
+ return (-1);
+ }
+
+ (void) memcpy(ptr, dptr->nsc_ret.nsc_u.buff, sizeof (*ptr));
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+getadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
+{
+ out->nsc_return_code = SUCCESS;
+ out->nsc_bufferbytesused = sizeof (current_admin);
+ (void) memcpy(out->nsc_u.buff, &current_admin, sizeof (current_admin));
+}
+
+
+static int
+nscd_set_rbac(admin_t *new_admin, int invalidate)
+{
+ int i;
+ char *dbname = NULL;
+ nsc_stat_t *cache = NULL;
+ nsc_stat_t *new = NULL;
+ void (*invalidate_func)(void);
+
+
+ for (i = 1; i <= 3; i++) {
+ /*
+ * Three of the RBAC databases are cached.
+ */
+ switch (i) {
+ case 1:
+ dbname = NSS_DBNAM_EXECATTR;
+ cache = &current_admin.exec;
+ new = &new_admin->exec;
+ invalidate_func = getexec_invalidate;
+ break;
+ case 2:
+ dbname = NSS_DBNAM_PROFATTR;
+ cache = &current_admin.prof;
+ new = &new_admin->prof;
+ invalidate_func = getprof_invalidate;
+ break;
+ case 3:
+ dbname = NSS_DBNAM_USERATTR;
+ cache = &current_admin.user;
+ new = &new_admin->user;
+ invalidate_func = getuser_invalidate;
+ break;
+ default:
+ break;
+ }
+
+ if (invalidate) {
+ if (new->nsc_invalidate) {
+ logit("Invalidating %s cache\n", dbname);
+ (*invalidate_func)();
+ }
+ } else {
+ if (nscd_set_ttl_positive(cache, dbname,
+ new->nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(cache, dbname,
+ new->nsc_neg_ttl) < 0 ||
+ nscd_set_khc(cache, dbname, new->nsc_keephot) < 0 ||
+ nscd_set_odo(cache, dbname,
+ new->nsc_old_data_ok) < 0 ||
+ nscd_set_ec(cache, dbname, new->nsc_enabled) < 0 ||
+ nscd_set_ss(cache, dbname,
+ new->nsc_suggestedsize) < 0)
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+setadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
+{
+ admin_t *new;
+
+ out->nsc_return_code = SUCCESS;
+ out->nsc_bufferbytesused = sizeof (nsc_return_t);
+
+ new = (admin_t *)ptr->nsc_u.name;
+
+
+ /*
+ * global admin stuff
+ */
+
+ if ((nscd_set_lf(&current_admin, new->logfile) < 0) ||
+ nscd_set_dl(&current_admin, new->debug_level) < 0) {
+ out->nsc_return_code = NOTFOUND;
+ return (-1);
+ }
+
+ /*
+ * per cache items
+ */
+
+ if (new->passwd.nsc_invalidate) {
+ logit("Invalidating passwd cache\n");
+ getpw_invalidate();
+ }
+
+ if (new->group.nsc_invalidate) {
+ logit("Invalidating group cache\n");
+ getgr_invalidate();
+ }
+
+ if (new->host.nsc_invalidate) {
+ logit("Invalidating host cache\n");
+ gethost_invalidate();
+ }
+
+ if (new->node.nsc_invalidate) {
+ logit("Invalidating ipnodes cache\n");
+ getnode_invalidate();
+ }
+
+ (void) nscd_set_rbac(new, 1); /* invalidate rbac cache */
+
+ if (nscd_set_ttl_positive(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.group,
+ "group",
+ new->group.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.group,
+ "group",
+ new->group.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.group,
+ "group",
+ new->group.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.group,
+ "group",
+ new->group.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.group,
+ "group",
+ new->group.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.group,
+ "group",
+ new->group.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.host,
+ "hosts",
+ new->host.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.host,
+ "hosts",
+ new->host.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.host,
+ "hosts",
+ new->host.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.host,
+ "hosts",
+ new->host.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.host,
+ "hosts",
+ new->host.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.host,
+ "hosts",
+ new->host.nsc_suggestedsize) < 0 ||
+ nscd_set_rbac(new, 0) < 0) {
+ out->nsc_return_code = NOTFOUND;
+ return (-1);
+ }
+ out->nsc_return_code = SUCCESS;
+ return (0);
+}
+
+void
+client_killserver(void)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = KILLSERVER;
+
+ ndata = sizeof (u);
+ adata = sizeof (nsc_call_t);
+
+ dptr = &u.data;
+
+ _nsc_trydoorcall(&dptr, &ndata, &adata);
+}
+
+
+static int
+client_setadmin(admin_t *ptr)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = SETADMIN;
+
+ (void) memcpy(u.data.nsc_call.nsc_u.name, ptr, sizeof (*ptr));
+
+ ndata = sizeof (u);
+ adata = sizeof (*ptr);
+
+ dptr = &u.data;
+
+ if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+dump_stat(nsc_stat_t *ptr)
+{
+ double hitrate;
+ (void) printf("%10s cache is enabled\n",
+ (ptr->nsc_enabled?"Yes":"No"));
+ (void) printf("%10d cache hits on positive entries\n",
+ ptr->nsc_pos_cache_hits);
+ (void) printf("%10d cache hits on negative entries\n",
+ ptr->nsc_neg_cache_hits);
+ (void) printf("%10d cache misses on positive entries\n",
+ ptr->nsc_pos_cache_misses);
+ (void) printf("%10d cache misses on negative entries\n",
+ ptr->nsc_neg_cache_misses);
+ hitrate = ptr->nsc_pos_cache_misses + ptr->nsc_neg_cache_misses +
+ ptr->nsc_pos_cache_hits + ptr->nsc_neg_cache_hits;
+
+ if (hitrate > 0.0)
+ hitrate = (100.0 * ((double)ptr->nsc_pos_cache_hits +
+ (double)ptr->nsc_neg_cache_hits))/hitrate;
+
+ (void) printf("%10.1f%% cache hit rate\n", hitrate);
+ (void) printf("%10d queries deferred\n", ptr->nsc_throttle_count);
+ (void) printf("%10d total entries\n", ptr->nsc_entries);
+ (void) printf("%10d complete cache invalidations\n",
+ ptr->nsc_invalidate_count);
+ (void) printf("%10d suggested size\n", ptr->nsc_suggestedsize);
+ (void) printf("%10d seconds time to live for positive entries\n",
+ ptr->nsc_pos_ttl);
+ (void) printf("%10d seconds time to live for negative entries\n",
+ ptr->nsc_neg_ttl);
+ (void) printf("%10d most active entries to be kept valid\n",
+ ptr->nsc_keephot);
+ (void) printf("%10s check /etc/{passwd, group, hosts, inet/ipnodes} "
+ "file for changes\n",
+ (ptr->nsc_check_files?"Yes":"No"));
+
+ (void) printf("%10s use possibly stale data rather than waiting for "
+ "refresh\n",
+ (ptr->nsc_old_data_ok?"Yes":"No"));
+}
+
+static void
+client_showstats(admin_t *ptr)
+{
+
+ (void) printf("nscd configuration:\n\n");
+ (void) printf("%10d server debug level\n", ptr->debug_level);
+ (void) printf("\"%s\" is server log file\n", ptr->logfile);
+
+ (void) printf("\npasswd cache:\n\n");
+ dump_stat(&(ptr->passwd));
+ (void) printf("\ngroup cache:\n\n");
+ dump_stat(&(ptr->group));
+ (void) printf("\nhosts cache:\n\n");
+ dump_stat(&(ptr->host));
+ (void) printf("\nipnodes cache:\n\n");
+ dump_stat(&(ptr->node));
+ (void) printf("\nexec_attr cache:\n\n");
+ dump_stat(&(ptr->exec));
+ (void) printf("\nprof_attr cache:\n\n");
+ dump_stat(&(ptr->prof));
+ (void) printf("\nuser_attr cache:\n\n");
+ dump_stat(&(ptr->user));
+}
+
+
+
+/*
+ * detach from tty
+ */
+static void
+detachfromtty(void)
+{
+ if (logfd > 0) {
+ int i;
+ for (i = 0; i < logfd; i++)
+ (void) close(i);
+ closefrom(logfd+1);
+ } else
+ closefrom(0);
+
+ (void) chdir("/");
+
+ switch (fork1()) {
+ case (pid_t)-1:
+ exit(1);
+ break;
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+ (void) setsid();
+ (void) open("/dev/null", O_RDWR, 0);
+ (void) dup(0);
+ (void) dup(0);
+}
diff --git a/usr/src/cmd/nscd/server_door.h b/usr/src/cmd/nscd/server_door.h
new file mode 100644
index 0000000000..ed40ce4e4d
--- /dev/null
+++ b/usr/src/cmd/nscd/server_door.h
@@ -0,0 +1,85 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SERVER_DOOR_H
+#define _SERVER_DOOR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Definitions for server side of doors-based name service caching
+ */
+
+
+typedef struct admin {
+ nsc_stat_t passwd;
+ nsc_stat_t group;
+ nsc_stat_t host;
+ nsc_stat_t node;
+ nsc_stat_t exec;
+ nsc_stat_t prof;
+ nsc_stat_t user;
+ int debug_level;
+ int avoid_nameservice;
+ /* set to true for disconnected op */
+ int ret_stats; /* return status of admin calls */
+ char logfile[128]; /* debug file for logging */
+} admin_t;
+
+
+extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int);
+
+extern struct group *_uncached_getgrnam_r(const char *, struct group *,
+ char *, int);
+
+extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int);
+
+extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *,
+ char *, int);
+
+extern struct hostent *_uncached_gethostbyname_r(const char *,
+ struct hostent *, char *, int, int *h_errnop);
+
+extern struct hostent *_uncached_gethostbyaddr_r(const char *, int, int,
+ struct hostent *, char *, int, int *h_errnop);
+
+extern struct hostent *_uncached_getipnodebyname(const char *,
+ struct hostent *, char *, int, int, int, int *h_errnop);
+
+extern struct hostent *_uncached_getipnodebyaddr(const char *, int, int,
+ struct hostent *, char *, int, int *h_errnop);
+
+extern int _nsc_trydoorcall(nsc_data_t **dptr, int *ndata, int *adata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SERVER_DOOR_H */
diff --git a/usr/src/cmd/nscd/svc-nscd b/usr/src/cmd/nscd/svc-nscd
new file mode 100644
index 0000000000..67af46a86e
--- /dev/null
+++ b/usr/src/cmd/nscd/svc-nscd
@@ -0,0 +1,43 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+. /lib/svc/share/smf_include.sh
+
+if [ -f /etc/nscd.conf -a -f /usr/sbin/nscd ]; then
+ secure=""
+
+ if egrep -s "^(passwd|passwd_compat):.*nisplus" /etc/nsswitch.conf
+ then
+ /usr/lib/nscd_nischeck passwd || secure=" -S passwd,yes"
+ fi
+
+ /usr/sbin/nscd$secure < /dev/null > /dev/msglog 2>&1 &
+else
+ echo "No /etc/nscd.conf or no /usr/sbin/nscd"
+ exit $SMF_EXIT_ERR_CONFIG
+fi