diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/nscd | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/nscd')
| -rw-r--r-- | usr/src/cmd/nscd/Makefile | 110 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/attrstr.c | 41 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getexec.c | 533 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getgr.c | 616 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/gethost.c | 683 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getnode.c | 803 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getprof.c | 508 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getpw.c | 673 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/getuser.c | 510 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/hash.c | 346 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/name-service-cache.xml | 105 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/nscd.h | 126 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/nscd_biggest.c | 115 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/nscd_nischeck.c | 94 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/nscd_parse.c | 402 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/nscd_wait.c | 152 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/server.c | 1882 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/server_door.h | 85 | ||||
| -rw-r--r-- | usr/src/cmd/nscd/svc-nscd | 43 |
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, ¤t_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, ¤t_admin.group, &group_lock); +} + +void +getgr_nam_reaper() +{ + nsc_reaper("gr_nam", nam_hash, ¤t_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, ¤t_admin.host, &host_lock); +} + +void +gethost_addr_reaper() +{ + nsc_reaper("gethost_addr", addr_hash, ¤t_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, ¤t_admin.node, &node_lock); +} + +void +getnode_addr_reaper() +{ + nsc_reaper("getnode_addr", addr_hash, ¤t_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, ¤t_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, ¤t_admin.passwd, &passwd_lock); +} + +void +getpw_nam_reaper() +{ + nsc_reaper("getpw_nam", nam_hash, ¤t_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, ¤t_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(¤t_admin, fields[1]) < 0) + errflg++; + + break; + + case 'd': + if (strcmp("debug-level", fields[0])) { + errflg++; + break; + } + + if (nscd_set_dl(¤t_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 (¤t_admin.passwd); + + if (strncmp(caches[1], s, strlen(caches[1])) == 0) + return (¤t_admin.group); + + if (strncmp(caches[2], s, strlen(caches[2])) == 0) + return (¤t_admin.host); + + if (strncmp(caches[3], s, strlen(caches[3])) == 0) + return (¤t_admin.node); + + if (strncmp(caches[4], s, strlen(caches[4])) == 0) + return (¤t_admin.exec); + + if (strncmp(caches[5], s, strlen(caches[5])) == 0) + return (¤t_admin.prof); + + if (strncmp(caches[6], s, strlen(caches[6])) == 0) + return (¤t_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(¤t_admin) != 0)) { + (void) fprintf(stderr, + "%s doesn't appear to be running.\n", argv[0]); + exit(1); + } + client_showstats(¤t_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(¤t_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(¤t_admin); + } + + if (doset) { + if (client_setadmin(¤t_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(¤t_admin, + current_admin.logfile); + } else { + if (strlen(current_admin.logfile) == 0) + (void) strcpy(current_admin.logfile, "/dev/null"); + (void) nscd_set_lf(¤t_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, ¤t_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 = ¤t_admin.exec; + new = &new_admin->exec; + invalidate_func = getexec_invalidate; + break; + case 2: + dbname = NSS_DBNAM_PROFATTR; + cache = ¤t_admin.prof; + new = &new_admin->prof; + invalidate_func = getprof_invalidate; + break; + case 3: + dbname = NSS_DBNAM_USERATTR; + cache = ¤t_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(¤t_admin, new->logfile) < 0) || + nscd_set_dl(¤t_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(¤t_admin.passwd, + "passwd", + new->passwd.nsc_pos_ttl) < 0 || + nscd_set_ttl_negative(¤t_admin.passwd, + "passwd", + new->passwd.nsc_neg_ttl) < 0 || + nscd_set_khc(¤t_admin.passwd, + "passwd", + new->passwd.nsc_keephot) < 0 || + nscd_set_odo(¤t_admin.passwd, + "passwd", + new->passwd.nsc_old_data_ok) < 0 || + nscd_set_ec(¤t_admin.passwd, + "passwd", + new->passwd.nsc_enabled) < 0 || + nscd_set_ss(¤t_admin.passwd, + "passwd", + new->passwd.nsc_suggestedsize) < 0 || + + nscd_set_ttl_positive(¤t_admin.group, + "group", + new->group.nsc_pos_ttl) < 0 || + nscd_set_ttl_negative(¤t_admin.group, + "group", + new->group.nsc_neg_ttl) < 0 || + nscd_set_khc(¤t_admin.group, + "group", + new->group.nsc_keephot) < 0 || + nscd_set_odo(¤t_admin.group, + "group", + new->group.nsc_old_data_ok) < 0 || + nscd_set_ec(¤t_admin.group, + "group", + new->group.nsc_enabled) < 0 || + nscd_set_ss(¤t_admin.group, + "group", + new->group.nsc_suggestedsize) < 0 || + + nscd_set_ttl_positive(¤t_admin.node, + "ipnodes", + new->node.nsc_pos_ttl) < 0 || + nscd_set_ttl_negative(¤t_admin.node, + "ipnodes", + new->node.nsc_neg_ttl) < 0 || + nscd_set_khc(¤t_admin.node, + "ipnodes", + new->node.nsc_keephot) < 0 || + nscd_set_odo(¤t_admin.node, + "ipnodes", + new->node.nsc_old_data_ok) < 0 || + nscd_set_ec(¤t_admin.node, + "ipnodes", + new->node.nsc_enabled) < 0 || + nscd_set_ss(¤t_admin.node, + "ipnodes", + new->node.nsc_suggestedsize) < 0 || + + nscd_set_ttl_positive(¤t_admin.host, + "hosts", + new->host.nsc_pos_ttl) < 0 || + nscd_set_ttl_negative(¤t_admin.host, + "hosts", + new->host.nsc_neg_ttl) < 0 || + nscd_set_khc(¤t_admin.host, + "hosts", + new->host.nsc_keephot) < 0 || + nscd_set_odo(¤t_admin.host, + "hosts", + new->host.nsc_old_data_ok) < 0 || + nscd_set_ec(¤t_admin.host, + "hosts", + new->host.nsc_enabled) < 0 || + nscd_set_ss(¤t_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 |
