summaryrefslogtreecommitdiff
path: root/usr/src/cmd/nscd/server.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/nscd/server.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/nscd/server.c')
-rw-r--r--usr/src/cmd/nscd/server.c1882
1 files changed, 1882 insertions, 0 deletions
diff --git a/usr/src/cmd/nscd/server.c b/usr/src/cmd/nscd/server.c
new file mode 100644
index 0000000000..3adfd88dc5
--- /dev/null
+++ b/usr/src/cmd/nscd/server.c
@@ -0,0 +1,1882 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Simple doors name server cache daemon
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/door.h>
+#include <sys/types.h>
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/zone.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <thread.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <door.h>
+#include "getxby_door.h"
+#include "server_door.h"
+#include "nscd.h"
+/* Includes for filenames of databases */
+#include <shadow.h>
+#include <userdefs.h>
+#include <netdb.h>
+#include <nss_dbdefs.h>
+#include <exec_attr.h>
+#include <prof_attr.h>
+#include <user_attr.h>
+#include <ucred.h>
+#include <priv.h>
+#include <libscf.h>
+
+extern int optind;
+extern int opterr;
+extern int optopt;
+extern char *optarg;
+
+static void switcher(void *, char *, size_t, door_desc_t *, uint_t);
+static void rts_mon(void);
+static void usage(char *);
+static int nsc_calllen(nsc_call_t *);
+static int client_getadmin(admin_t *);
+static void getadmin(nsc_return_t *, int, nsc_call_t *);
+static int setadmin(nsc_return_t *, int, nsc_call_t *);
+static void client_killserver(void);
+static int client_setadmin(admin_t *);
+static void client_showstats(admin_t *);
+static void detachfromtty(void);
+
+
+admin_t current_admin;
+static int will_become_server;
+
+void
+nsc_reaper(char *tbl_name, hash_t *tbl, nsc_stat_t *admin_ptr,
+ mutex_t *hash_lock)
+{
+ uint_t count;
+ uint_t interval;
+
+ while (1) {
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("reaper_%s: %d entries in cache\n",
+ tbl_name, admin_ptr->nsc_entries);
+ }
+ if (admin_ptr->nsc_entries > 0) {
+ count = reap_hash(tbl, admin_ptr, hash_lock,
+ admin_ptr->nsc_pos_ttl);
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("reaper_%s: reaped %d entries\n",
+ tbl_name, count);
+ }
+ } else {
+ /*
+ * We set a minimum wait of 60 before checking again;
+ * we don't want to sleep for no time at all.
+ * We don't clamp it for the reaping itself, that is
+ * done in reap_hash, and with a different minimum.
+ */
+ interval = admin_ptr->nsc_pos_ttl;
+ if (interval < 60) interval = 60;
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit(
+ "reaper_%s: Nothing to reap, sleep %d\n",
+ tbl_name, interval);
+ }
+ sleep(interval);
+ }
+ }
+}
+
+nsc_stat_t *
+getcacheptr(char *s)
+{
+ static const char *caches[7] = {"passwd", "group", "hosts", "ipnodes",
+ "exec_attr", "prof_attr", "user_attr" };
+
+ if (strncmp(caches[0], s, strlen(caches[0])) == 0)
+ return (&current_admin.passwd);
+
+ if (strncmp(caches[1], s, strlen(caches[1])) == 0)
+ return (&current_admin.group);
+
+ if (strncmp(caches[2], s, strlen(caches[2])) == 0)
+ return (&current_admin.host);
+
+ if (strncmp(caches[3], s, strlen(caches[3])) == 0)
+ return (&current_admin.node);
+
+ if (strncmp(caches[4], s, strlen(caches[4])) == 0)
+ return (&current_admin.exec);
+
+ if (strncmp(caches[5], s, strlen(caches[5])) == 0)
+ return (&current_admin.prof);
+
+ if (strncmp(caches[6], s, strlen(caches[6])) == 0)
+ return (&current_admin.user);
+
+ return (NULL);
+}
+
+static char *
+getcacheopt(char *s)
+{
+ while (*s && *s != ',')
+ s++;
+ return ((*s == ',') ? (s + 1) : NULL);
+}
+
+/*
+ * routine to check if server is already running
+ */
+
+static int
+nsc_ping(void)
+{
+ nsc_data_t data;
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ data.nsc_call.nsc_callnumber = NULLCALL;
+ ndata = sizeof (data);
+ adata = sizeof (data);
+ dptr = &data;
+ return (_nsc_trydoorcall(&dptr, &ndata, &adata));
+}
+
+static void
+dozip(void)
+{
+ /* not much here */
+}
+
+static void
+keep_open_dns_socket(void)
+{
+ _res.options |= RES_STAYOPEN; /* just keep this udp socket open */
+}
+
+/*
+ * declaring this causes the files backend to use hashing
+ * this is of course an utter hack, but provides a nice
+ * quiet back door to enable this feature for only the nscd.
+ */
+void
+__nss_use_files_hash(void)
+{
+
+}
+/*
+ *
+ * The allocation of resources for cache lookups is an interesting
+ * problem, and one that has caused several bugs in the beta release
+ * of 2.5. In particular, the introduction of a thottle to prevent
+ * the creation of excessive numbers of LWPs in the case of a failed
+ * name service has led to a denial of service problem when the
+ * name service request rate exceeds the name service's ability
+ * to respond. As a result, I'm implementing the following
+ * algorithm:
+ *
+ * 1) We cap the number of total threads.
+ * 2) We save CACHE_THREADS of those for cache lookups only.
+ * 3) We use a common pool of 2/3 of the remain threads that are used first
+ * 4) We save the remainder and allocate 1/3 of it for table specific lookups
+ *
+ * The intent is to prevent the failure of a single name service from
+ * causing denial of service, and to always have threads available for
+ * cached lookups. If a request comes in and the answer isn't in the
+ * cache and we cannot get a thread, we simply return NOSERVER, forcing
+ * the client to lookup the
+ * data itself. This will prevent the types of starvation seen
+ * at UNC due to a single threaded DNS backend, and allows the cache
+ * to eventually become filled.
+ *
+ */
+
+/* 7 tables: passwd, group, hosts, ipnodes, exec_attr, prof_attr, user_attr */
+#define NSCD_TABLES 7
+#define TABLE_THREADS 10
+#define COMMON_THREADS 20
+#define CACHE_MISS_THREADS (COMMON_THREADS + NSCD_TABLES * TABLE_THREADS)
+#define CACHE_HIT_THREADS 20
+#define MAX_SERVER_THREADS (CACHE_HIT_THREADS + CACHE_MISS_THREADS)
+
+static sema_t common_sema;
+static sema_t passwd_sema;
+static sema_t hosts_sema;
+static sema_t nodes_sema;
+static sema_t group_sema;
+static sema_t exec_sema;
+static sema_t prof_sema;
+static sema_t user_sema;
+static thread_key_t lookup_state_key;
+
+static void
+initialize_lookup_clearance(void)
+{
+ thr_keycreate(&lookup_state_key, NULL);
+ (void) sema_init(&common_sema, COMMON_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&passwd_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&hosts_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&nodes_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&group_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&exec_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&prof_sema, TABLE_THREADS, USYNC_THREAD, 0);
+ (void) sema_init(&user_sema, TABLE_THREADS, USYNC_THREAD, 0);
+}
+
+int
+get_clearance(int callnumber)
+{
+ sema_t *table_sema = NULL;
+ char *tab;
+
+ if (sema_trywait(&common_sema) == 0) {
+ thr_setspecific(lookup_state_key, NULL);
+ return (0);
+ }
+
+ switch (MASKUPDATEBIT(callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ tab = "passwd";
+ table_sema = &passwd_sema;
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ tab = "group";
+ table_sema = &group_sema;
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ tab = "hosts";
+ table_sema = &hosts_sema;
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ tab = "ipnodes";
+ table_sema = &nodes_sema;
+ break;
+ case GETEXECID:
+ tab = "exec_attr";
+ table_sema = &exec_sema;
+ break;
+
+ case GETPROFNAM:
+ tab = "prof_attr";
+ table_sema = &prof_sema;
+ break;
+
+ case GETUSERNAM:
+ tab = "user_attr";
+ table_sema = &user_sema;
+ break;
+
+ }
+
+ if (sema_trywait(table_sema) == 0) {
+ thr_setspecific(lookup_state_key, (void*)1);
+ return (0);
+ }
+
+ if (current_admin.debug_level >= DBG_CANT_FIND) {
+ logit("get_clearance: throttling load for %s table\n", tab);
+ }
+ return (-1);
+}
+
+int
+release_clearance(int callnumber)
+{
+ int which;
+
+ sema_t *table_sema = NULL;
+
+ thr_getspecific(lookup_state_key, (void**)&which);
+
+ if (which == 0) /* from common pool */ {
+ (void) sema_post(&common_sema);
+ return (0);
+ }
+
+ switch (MASKUPDATEBIT(callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ table_sema = &passwd_sema;
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ table_sema = &group_sema;
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ table_sema = &hosts_sema;
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ table_sema = &nodes_sema;
+ break;
+
+ case GETEXECID:
+ table_sema = &exec_sema;
+ break;
+
+ case GETPROFNAM:
+ table_sema = &prof_sema;
+ break;
+
+ case GETUSERNAM:
+ table_sema = &user_sema;
+ break;
+ }
+
+ (void) sema_post(table_sema);
+ return (0);
+}
+
+
+static mutex_t create_lock;
+static int nscd_max_servers = MAX_SERVER_THREADS;
+static int num_servers = 0;
+static thread_key_t server_key;
+
+/*
+ * Bind a TSD value to a server thread. This enables the destructor to
+ * be called if/when this thread exits. This would be a programming error,
+ * but better safe than sorry.
+ */
+/*ARGSUSED*/
+static void *
+server_tsd_bind(void *arg)
+{
+ static void *value = 0;
+
+ /* disable cancellation to avoid hangs if server threads disappear */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ thr_setspecific(server_key, value);
+ door_return(NULL, 0, NULL, 0);
+
+ /* make lint happy */
+ return (NULL);
+}
+
+/*
+ * Server threads are created here.
+ */
+/*ARGSUSED*/
+static void
+server_create(door_info_t *dip)
+{
+ (void) mutex_lock(&create_lock);
+ if (++num_servers > nscd_max_servers) {
+ num_servers--;
+ (void) mutex_unlock(&create_lock);
+ return;
+ }
+ (void) mutex_unlock(&create_lock);
+ thr_create(NULL, 0, server_tsd_bind, NULL, THR_BOUND|THR_DETACHED,
+ NULL);
+}
+
+/*
+ * Server thread are destroyed here
+ */
+/*ARGSUSED*/
+static void
+server_destroy(void *arg)
+{
+ (void) mutex_lock(&create_lock);
+ num_servers--;
+ (void) mutex_unlock(&create_lock);
+}
+
+static char **saved_argv;
+static char saved_execname[MAXPATHLEN];
+
+static void
+save_execname()
+{
+ const char *name = getexecname();
+
+ saved_execname[0] = 0;
+
+ if (name[0] != '/') { /* started w/ relative path */
+ (void) getcwd(saved_execname, MAXPATHLEN);
+ strlcat(saved_execname, "/", MAXPATHLEN);
+ }
+ strlcat(saved_execname, name, MAXPATHLEN);
+}
+
+void
+main(int argc, char ** argv)
+{
+ int did;
+ int opt;
+ int errflg = 0;
+ int showstats = 0;
+ int doset = 0;
+ int loaded_config_file = 0;
+ struct stat buf;
+ sigset_t myset;
+ struct sigaction action;
+
+ /*
+ * Special case non-root user here - he can just print stats
+ */
+
+ if (geteuid()) {
+ if (argc != 2 || strcmp(argv[1], "-g")) {
+ (void) fprintf(stderr,
+ "Must be root to use any option other than "\
+ "-g.\n\n");
+ usage(argv[0]);
+ }
+
+ if ((nsc_ping() != SUCCESS) ||
+ (client_getadmin(&current_admin) != 0)) {
+ (void) fprintf(stderr,
+ "%s doesn't appear to be running.\n", argv[0]);
+ exit(1);
+ }
+ client_showstats(&current_admin);
+ exit(0);
+ }
+
+
+
+ /*
+ * Determine if there is already a daemon running
+ */
+
+ will_become_server = (nsc_ping() != SUCCESS);
+
+ /*
+ * process usual options
+ */
+
+ /*
+ * load normal config file
+ */
+
+ if (will_become_server) {
+ static const nsc_stat_t defaults = {
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 0, /* stats */
+ 211, /* suggested size */
+ 1, /* enabled */
+ 0, /* invalidate cmd */
+ 600, /* positive ttl */
+ 10, /* netative ttl */
+ 20, /* keep hot */
+ 0, /* old data not ok */
+ 1 }; /* check files */
+
+ current_admin.passwd = defaults;
+ current_admin.group = defaults;
+ current_admin.host = defaults;
+ current_admin.node = defaults;
+ current_admin.exec = defaults;
+ current_admin.prof = defaults;
+ current_admin.user = defaults;
+
+ current_admin.logfile[0] = '\0';
+
+ if (access("/etc/nscd.conf", R_OK) == 0) {
+ if (nscd_parse(argv[0], "/etc/nscd.conf") < 0) {
+ exit(1);
+ }
+ loaded_config_file++;
+ }
+ }
+
+ else {
+ if (client_getadmin(&current_admin)) {
+ (void) fprintf(stderr,
+ "Cannot contact nscd properly(?)\n");
+ exit(1);
+ }
+
+ current_admin.logfile[0] = '\0';
+ }
+
+ while ((opt = getopt(argc, argv,
+ "S:Kf:c:ge:p:n:i:l:d:s:h:o:")) != EOF) {
+ nsc_stat_t *cache;
+ char *cacheopt;
+
+ switch (opt) {
+
+ case 'S': /* undocumented feature */
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_secure_mode = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_secure_mode = 0;
+ else
+ errflg++;
+ break;
+
+ case 'K': /* undocumented feature */
+ client_killserver();
+ exit(0);
+ break;
+
+ case 'f':
+ doset++;
+ loaded_config_file++;
+ if (nscd_parse(argv[0], optarg) < 0) {
+ exit(1);
+ }
+ break;
+
+ case 'g':
+ showstats++;
+ break;
+
+ case 'p':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_pos_ttl = atoi(cacheopt);
+ break;
+
+ case 'n':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_neg_ttl = atoi(cacheopt);
+ break;
+
+ case 'c':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_check_files = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_check_files = 0;
+ else
+ errflg++;
+ break;
+
+
+ case 'i':
+ doset++;
+ cache = getcacheptr(optarg);
+ if (!cache) {
+ errflg++;
+ break;
+ }
+ cache->nsc_invalidate = 1;
+ break;
+
+ case 'l':
+ doset++;
+ (void) strlcpy(current_admin.logfile, optarg, 128);
+ break;
+
+ case 'd':
+
+ doset++;
+ current_admin.debug_level = atoi(optarg);
+ break;
+
+ case 's':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+
+ cache->nsc_suggestedsize = atoi(cacheopt);
+
+ break;
+
+ case 'h':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ cache->nsc_keephot = atoi(cacheopt);
+ break;
+
+ case 'o':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_old_data_ok = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_old_data_ok = 0;
+ else
+ errflg++;
+ break;
+
+ case 'e':
+ doset++;
+ cache = getcacheptr(optarg);
+ cacheopt = getcacheopt(optarg);
+ if (!cache || !cacheopt) {
+ errflg++;
+ break;
+ }
+ if (strcmp(cacheopt, "yes") == 0)
+ cache->nsc_enabled = 1;
+ else if (strcmp(cacheopt, "no") == 0)
+ cache->nsc_enabled = 0;
+ else
+ errflg++;
+ break;
+
+ default:
+ errflg++;
+ break;
+ }
+
+ }
+
+ if (errflg)
+ usage(argv[0]);
+
+ if (!will_become_server) {
+
+ if (showstats) {
+ client_showstats(&current_admin);
+ }
+
+ if (doset) {
+ if (client_setadmin(&current_admin) < 0) {
+ (void) fprintf(stderr,
+ "Error during admin call\n");
+ exit(1);
+ }
+ }
+ if (!showstats && !doset) {
+ (void) fprintf(stderr,
+ "%s already running.... no admin specified\n",
+ argv[0]);
+ }
+ exit(0);
+ }
+
+ /*
+ * daemon from here ou
+ */
+
+ if (!loaded_config_file) {
+ (void) fprintf(stderr,
+ "No configuration file specifed and /etc/nscd.conf" \
+ "not present\n");
+ exit(1);
+ }
+
+ saved_argv = argv;
+ save_execname();
+
+ if (current_admin.debug_level) {
+ /* we're debugging... */
+ if (strlen(current_admin.logfile) == 0)
+ /* no specified log file */
+ (void) strcpy(current_admin.logfile, "stderr");
+ else
+ (void) nscd_set_lf(&current_admin,
+ current_admin.logfile);
+ } else {
+ if (strlen(current_admin.logfile) == 0)
+ (void) strcpy(current_admin.logfile, "/dev/null");
+ (void) nscd_set_lf(&current_admin, current_admin.logfile);
+ detachfromtty();
+ }
+
+ /* perform some initialization */
+ initialize_lookup_clearance();
+ keep_open_dns_socket();
+ getpw_init();
+ getgr_init();
+ gethost_init();
+ getnode_init();
+ getexec_init();
+ getprof_init();
+ getuser_init();
+
+ /* Establish our own server thread pool */
+
+ door_server_create(server_create);
+ if (thr_keycreate(&server_key, server_destroy) != 0) {
+ perror("thr_keycreate");
+ exit(-1);
+ }
+
+ /* Create a door */
+
+ if ((did = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
+ DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
+ perror("door_create");
+ exit(-1);
+ }
+
+ /* bind to file system */
+
+ if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
+ int newfd;
+ if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
+ logit("Cannot create %s:%s\n",
+ NAME_SERVICE_DOOR,
+ strerror(errno));
+ exit(1);
+ }
+ (void) close(newfd);
+ }
+
+ if (fattach(did, NAME_SERVICE_DOOR) < 0) {
+ if ((errno != EBUSY) ||
+ (fdetach(NAME_SERVICE_DOOR) < 0) ||
+ (fattach(did, NAME_SERVICE_DOOR) < 0)) {
+ perror("door_attach");
+ exit(2);
+ }
+ }
+
+ action.sa_handler = dozip;
+ action.sa_flags = 0;
+ (void) sigemptyset(&action.sa_mask);
+ (void) sigemptyset(&myset);
+ (void) sigaddset(&myset, SIGHUP);
+
+ if (sigaction(SIGHUP, &action, NULL) < 0) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ if (thr_sigsetmask(SIG_BLOCK, &myset, NULL) < 0) {
+ perror("thr_sigsetmask");
+ exit(1);
+ }
+
+
+ /*
+ * kick off revalidate threads
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getnode_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getgr_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getexec_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getprof_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void*))getuser_revalidate, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ /*
+ * kick off reaper threads
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_uid_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getpw_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getgr_uid_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getgr_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))gethost_addr_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getnode_nam_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getnode_addr_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getexec_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getprof_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))getuser_reaper, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ /*
+ * kick off routing socket monitor thread
+ */
+
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
+ perror("thr_create");
+ exit(1);
+ }
+
+ if (thr_sigsetmask(SIG_UNBLOCK, &myset, NULL) < 0) {
+ perror("thr_sigsetmask");
+ exit(1);
+ }
+
+ for (;;) {
+ (void) pause();
+ logit("Reloading /etc/nscd.conf\n");
+ nscd_parse(argv[0], "/etc/nscd.conf");
+ }
+}
+
+
+/*ARGSUSED*/
+static void
+switcher(void *cookie, char *argp, size_t arg_size,
+ door_desc_t *dp, uint_t n_desc)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ time_t now;
+
+ static time_t last_nsswitch_check;
+ static time_t last_nsswitch_modified;
+ static time_t last_resolv_modified;
+
+ static mutex_t nsswitch_lock;
+
+ nsc_call_t *ptr = (nsc_call_t *)argp;
+
+ if (argp == DOOR_UNREF_DATA) {
+ (void) printf("Door Slam... exiting\n");
+ exit(0);
+ }
+
+ if (ptr == NULL) { /* empty door call */
+ (void) door_return(NULL, 0, 0, 0); /* return the favor */
+ }
+
+ now = time(NULL);
+
+ /*
+ * just in case check
+ */
+
+ (void) mutex_lock(&nsswitch_lock);
+
+ if (now - last_nsswitch_check > 10) {
+ struct stat nss_buf;
+ struct stat res_buf;
+
+ last_nsswitch_check = now;
+
+ (void) mutex_unlock(&nsswitch_lock); /* let others continue */
+
+ /*
+ * This code keeps us from statting resolv.conf
+ * if it doesn't exist, yet prevents us from ignoring
+ * it if it happens to disappear later on for a bit.
+ */
+
+ if (last_resolv_modified >= 0) {
+ if (stat("/etc/resolv.conf", &res_buf) < 0) {
+ if (last_resolv_modified == 0)
+ last_resolv_modified = -1;
+ else
+ res_buf.st_mtime = last_resolv_modified;
+ } else if (last_resolv_modified == 0) {
+ last_resolv_modified = res_buf.st_mtime;
+ }
+ }
+
+ if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
+
+ /*EMPTY*/;
+
+ } else if (last_nsswitch_modified == 0) {
+
+ last_nsswitch_modified = nss_buf.st_mtime;
+
+ } else if ((last_nsswitch_modified < nss_buf.st_mtime) ||
+ ((last_resolv_modified > 0) &&
+ (last_resolv_modified < res_buf.st_mtime))) {
+ static mutex_t exit_lock;
+ char *fmri;
+ /*
+ * time for restart
+ */
+ logit("nscd restart due to /etc/nsswitch.conf or "\
+ "resolv.conf change\n");
+ /*
+ * try to restart under smf
+ */
+ if ((fmri = getenv("SMF_FMRI")) == NULL) {
+ /* not running under smf - reexec */
+ execv(saved_execname, saved_argv);
+ exit(1); /* just in case */
+ }
+
+ mutex_lock(&exit_lock); /* prevent multiple restarts */
+ if (smf_restart_instance(fmri) == 0)
+ sleep(10); /* wait a bit */
+ exit(1); /* give up waiting for resurrection */
+ }
+
+ } else
+ (void) mutex_unlock(&nsswitch_lock);
+
+ switch (ptr->nsc_callnumber) {
+
+ case NULLCALL:
+ u.data.nsc_ret.nsc_return_code = SUCCESS;
+ u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
+ break;
+
+
+ case GETPWNAM:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETPWUID:
+ getpw_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETGRNAM:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETGRGID:
+ getgr_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETHOSTBYNAME:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETHOSTBYADDR:
+ gethost_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETIPNODEBYNAME:
+ *(argp + arg_size - 1) = 0; /* FALLTHROUGH */
+ case GETIPNODEBYADDR:
+ getnode_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETEXECID:
+ *(argp + arg_size - 1) = 0;
+ getexec_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETPROFNAM:
+ *(argp + arg_size - 1) = 0;
+ getprof_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETUSERNAM:
+ *(argp + arg_size - 1) = 0;
+ getuser_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
+ break;
+
+ case GETADMIN:
+ getadmin(&u.data.nsc_ret, sizeof (u), ptr);
+ break;
+
+ case SETADMIN:
+ case KILLSERVER: {
+
+ ucred_t *uc = NULL;
+ const priv_set_t *eset;
+ zoneid_t zoneid;
+
+ if (door_ucred(&uc) != 0) {
+ perror("door_ucred");
+ u.data.nsc_ret.nsc_return_code = NOTFOUND;
+ break;
+ }
+
+ eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
+ zoneid = ucred_getzoneid(uc);
+
+ if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
+ eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
+ ucred_geteuid(uc) != 0) {
+ logit("SETADMIN call failed(cred): caller pid %d, "
+ "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
+ ucred_getruid(uc), ucred_geteuid(uc), zoneid);
+ u.data.nsc_ret.nsc_return_code = NOTFOUND;
+ ucred_free(uc);
+ break;
+ }
+
+ if (ptr->nsc_callnumber == KILLSERVER) {
+ logit("Nscd received KILLSERVER cmd from pid %d, "
+ "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
+ ucred_getruid(uc), ucred_geteuid(uc), zoneid);
+ exit(0);
+ } else {
+ if (setadmin(&u.data.nsc_ret, sizeof (u), ptr) != 0)
+ logit("SETADMIN call failed\n");
+ }
+ ucred_free(uc);
+ break;
+ }
+
+ default:
+ logit("Unknown name service door call op %d\n",
+ ptr->nsc_callnumber);
+ u.data.nsc_ret.nsc_return_code = -1;
+ u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
+ break;
+
+ }
+ door_return((char *)&u.data, u.data.nsc_ret.nsc_bufferbytesused,
+ NULL, 0);
+}
+
+/*
+ * Monitor the routing socket. Address lists stored in the ipnodes
+ * cache are sorted based on destination address selection rules,
+ * so when things change that could affect that sorting (interfaces
+ * go up or down, flags change, etc.), we clear that cache so the
+ * list will be re-ordered the next time the hostname is resolved.
+ */
+static void
+rts_mon(void)
+{
+ int rt_sock, rdlen;
+ union {
+ struct {
+ struct rt_msghdr rtm;
+ struct sockaddr_storage addrs[RTA_NUMBITS];
+ } r;
+ struct if_msghdr ifm;
+ struct ifa_msghdr ifam;
+ } mbuf;
+ struct ifa_msghdr *ifam = &mbuf.ifam;
+
+ rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (rt_sock < 0) {
+ logit("Failed to open routing socket: %s\n", strerror(errno));
+ thr_exit(0);
+ }
+
+ for (;;) {
+ rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
+ if (rdlen <= 0) {
+ if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
+ logit("routing socket read: %s\n",
+ strerror(errno));
+ thr_exit(0);
+ }
+ continue;
+ }
+ if (ifam->ifam_version != RTM_VERSION) {
+ logit("rx unknown version (%d) on routing socket.\n",
+ ifam->ifam_version);
+ continue;
+ }
+ switch (ifam->ifam_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ getnode_name_invalidate();
+ break;
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ case RTM_GET:
+ case RTM_LOSING:
+ case RTM_REDIRECT:
+ case RTM_MISS:
+ case RTM_LOCK:
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ case RTM_RESOLVE:
+ case RTM_IFINFO:
+ break;
+ default:
+ logit("rx unknown msg type (%d) on routing socket.\n",
+ ifam->ifam_type);
+ break;
+ }
+ }
+}
+
+static void
+usage(char *s)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-d debug_level] [-l logfilename]\n", s);
+ (void) fprintf(stderr,
+ " [-p cachename,positive_time_to_live]\n");
+ (void) fprintf(stderr,
+ " [-n cachename,negative_time_to_live]\n");
+ (void) fprintf(stderr,
+ " [-i cachename] [-s cachename,suggestedsize]\n");
+
+ (void) fprintf(stderr,
+ " [-h cachename,keep_hot_count] "\
+ "[-o cachename,\"yes\"|\"no\"]\n");
+
+ (void) fprintf(stderr,
+ " [-e cachename,\"yes\"|\"no\"] [-g] " \
+ "[-c cachename,\"yes\"|\"no\"]\n");
+
+ (void) fprintf(stderr,
+ " [-f configfilename] \n");
+
+ (void) fprintf(stderr,
+ "\n Supported caches: passwd, group, hosts, ipnodes\n");
+
+ (void) fprintf(stderr,
+ " exec_attr, prof_attr, and user_attr.\n");
+
+ exit(1);
+
+}
+
+
+static int logfd = 2;
+
+int
+nscd_set_lf(admin_t *ptr, char *s)
+{
+ int newlogfd;
+
+ /*
+ * we don't really want to try and open the log file
+ * /dev/null since that will fail w/ our security fixes
+ */
+
+ if (*s == 0) {
+ /* ignore empty log file specs */
+ /*EMPTY*/;
+ } else if (s == NULL || strcmp(s, "/dev/null") == 0) {
+ (void) strcpy(current_admin.logfile, "/dev/null");
+ (void) close(logfd);
+ logfd = -1;
+ } else {
+ /*
+ * In order to open this file securely, we'll try a few tricks
+ */
+
+ if ((newlogfd = open(s, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
+ /*
+ * File already exists... now we need to get cute
+ * since opening a file in a world-writeable directory
+ * safely is hard = it could be a hard link or a
+ * symbolic link to a system file.
+ */
+ struct stat before;
+
+ if (lstat(s, &before) < 0) {
+ logit("Cannot open new logfile \"%s\": %sn",
+ s, strerror(errno));
+ return (-1);
+ }
+
+ if (S_ISREG(before.st_mode) && /* no symbolic links */
+ (before.st_nlink == 1) && /* no hard links */
+ (before.st_uid == 0)) { /* owned by root */
+ if ((newlogfd =
+ open(s, O_APPEND|O_WRONLY, 0644)) < 0) {
+ logit("Cannot open new "\
+ "logfile \"%s\": %s\n", s,
+ strerror(errno));
+ return (-1);
+ }
+ } else {
+ logit("Cannot use specified logfile \"%s\": "\
+ "file is/has links or isn't owned by "\
+ "root\n", s);
+ return (-1);
+ }
+ }
+
+ (void) strlcpy(ptr->logfile, s, 128);
+ (void) close(logfd);
+ logfd = newlogfd;
+ logit("Start of new logfile %s\n", s);
+ }
+ return (0);
+}
+
+void
+logit(char *format, ...)
+{
+ static mutex_t loglock;
+ struct timeval tv;
+
+#define LOGBUFLEN 1024
+ char buffer[LOGBUFLEN];
+
+ va_list ap;
+ va_start(ap, format);
+
+ if (logfd >= 0) {
+ int safechars, offset;
+ if (gettimeofday(&tv, NULL) != 0 ||
+ ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) {
+ (void) snprintf(buffer, LOGBUFLEN,
+ "<time conversion failed>\t");
+ } else {
+ /*
+ * ctime_r() includes some stuff we don't want;
+ * adjust length to overwrite " YYYY\n".
+ */
+ offset = strlen(buffer) - 6;
+ safechars = LOGBUFLEN - (offset - 1);
+ (void) snprintf(buffer + offset, safechars, ".%.4ld\t",
+ tv.tv_usec/100);
+ }
+ offset = strlen(buffer);
+ safechars = LOGBUFLEN - (offset - 1);
+ if (vsnprintf(buffer + offset, safechars, format, ap) >
+ safechars) {
+ (void) strncat(buffer, "...\n", LOGBUFLEN);
+ }
+
+ (void) mutex_lock(&loglock);
+ (void) write(logfd, buffer, strlen(buffer));
+ (void) mutex_unlock(&loglock);
+ }
+
+ va_end(ap);
+#undef LOGBUFLEN
+}
+
+static void
+do_update(nsc_call_t *in)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ time_t now = time(NULL);
+
+ switch (MASKUPDATEBIT(in->nsc_callnumber)) {
+
+ case GETPWUID:
+ case GETPWNAM:
+ getpw_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETGRNAM:
+ case GETGRGID:
+ getgr_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETHOSTBYNAME:
+ case GETHOSTBYADDR:
+ gethost_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETIPNODEBYNAME:
+ case GETIPNODEBYADDR:
+ getnode_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETEXECID:
+ getexec_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETPROFNAM:
+ getprof_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ case GETUSERNAM:
+ getuser_lookup(&u.data.nsc_ret, sizeof (u), in, now);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ free(in);
+}
+
+int
+launch_update(nsc_call_t *in)
+{
+ nsc_call_t *c;
+
+ int l = nsc_calllen(in);
+
+ in->nsc_callnumber |= UPDATEBIT;
+
+ if ((c = malloc(l)) == NULL) {
+ logit("thread create failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ (void) memcpy(c, in, l);
+
+ if (current_admin.debug_level >= DBG_ALL) {
+ logit("launching update\n");
+ }
+
+ if (thr_create(NULL,
+ NULL,
+ (void *(*)(void*))do_update,
+ c,
+ 0|THR_DETACHED, NULL) != 0) {
+ logit("thread create failed\n");
+ exit(1);
+ }
+
+ return (0);
+}
+
+static int
+nsc_calllen(nsc_call_t *in)
+{
+ switch (MASKUPDATEBIT(in->nsc_callnumber)) {
+
+ case GETPWUID:
+ case GETGRGID:
+ case NULLCALL:
+ return (sizeof (*in));
+
+ case GETPWNAM:
+ case GETGRNAM:
+ case GETHOSTBYNAME:
+ return (sizeof (*in) + strlen(in->nsc_u.name));
+ case GETIPNODEBYNAME:
+ return (sizeof (*in) + strlen(in->nsc_u.ipnode.name));
+
+ case GETHOSTBYADDR:
+ case GETIPNODEBYADDR:
+ return (sizeof (*in) + in->nsc_u.addr.a_length);
+
+ case GETEXECID:
+ case GETPROFNAM:
+ case GETUSERNAM:
+
+ return (sizeof (*in) + strlen(in->nsc_u.name));
+ }
+
+ return (0);
+}
+
+static int
+client_getadmin(admin_t *ptr)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = GETADMIN;
+ ndata = sizeof (u);
+ adata = sizeof (u.data);
+ dptr = &u.data;
+
+ if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
+ return (-1);
+ }
+
+ (void) memcpy(ptr, dptr->nsc_ret.nsc_u.buff, sizeof (*ptr));
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+getadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
+{
+ out->nsc_return_code = SUCCESS;
+ out->nsc_bufferbytesused = sizeof (current_admin);
+ (void) memcpy(out->nsc_u.buff, &current_admin, sizeof (current_admin));
+}
+
+
+static int
+nscd_set_rbac(admin_t *new_admin, int invalidate)
+{
+ int i;
+ char *dbname = NULL;
+ nsc_stat_t *cache = NULL;
+ nsc_stat_t *new = NULL;
+ void (*invalidate_func)(void);
+
+
+ for (i = 1; i <= 3; i++) {
+ /*
+ * Three of the RBAC databases are cached.
+ */
+ switch (i) {
+ case 1:
+ dbname = NSS_DBNAM_EXECATTR;
+ cache = &current_admin.exec;
+ new = &new_admin->exec;
+ invalidate_func = getexec_invalidate;
+ break;
+ case 2:
+ dbname = NSS_DBNAM_PROFATTR;
+ cache = &current_admin.prof;
+ new = &new_admin->prof;
+ invalidate_func = getprof_invalidate;
+ break;
+ case 3:
+ dbname = NSS_DBNAM_USERATTR;
+ cache = &current_admin.user;
+ new = &new_admin->user;
+ invalidate_func = getuser_invalidate;
+ break;
+ default:
+ break;
+ }
+
+ if (invalidate) {
+ if (new->nsc_invalidate) {
+ logit("Invalidating %s cache\n", dbname);
+ (*invalidate_func)();
+ }
+ } else {
+ if (nscd_set_ttl_positive(cache, dbname,
+ new->nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(cache, dbname,
+ new->nsc_neg_ttl) < 0 ||
+ nscd_set_khc(cache, dbname, new->nsc_keephot) < 0 ||
+ nscd_set_odo(cache, dbname,
+ new->nsc_old_data_ok) < 0 ||
+ nscd_set_ec(cache, dbname, new->nsc_enabled) < 0 ||
+ nscd_set_ss(cache, dbname,
+ new->nsc_suggestedsize) < 0)
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+setadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
+{
+ admin_t *new;
+
+ out->nsc_return_code = SUCCESS;
+ out->nsc_bufferbytesused = sizeof (nsc_return_t);
+
+ new = (admin_t *)ptr->nsc_u.name;
+
+
+ /*
+ * global admin stuff
+ */
+
+ if ((nscd_set_lf(&current_admin, new->logfile) < 0) ||
+ nscd_set_dl(&current_admin, new->debug_level) < 0) {
+ out->nsc_return_code = NOTFOUND;
+ return (-1);
+ }
+
+ /*
+ * per cache items
+ */
+
+ if (new->passwd.nsc_invalidate) {
+ logit("Invalidating passwd cache\n");
+ getpw_invalidate();
+ }
+
+ if (new->group.nsc_invalidate) {
+ logit("Invalidating group cache\n");
+ getgr_invalidate();
+ }
+
+ if (new->host.nsc_invalidate) {
+ logit("Invalidating host cache\n");
+ gethost_invalidate();
+ }
+
+ if (new->node.nsc_invalidate) {
+ logit("Invalidating ipnodes cache\n");
+ getnode_invalidate();
+ }
+
+ (void) nscd_set_rbac(new, 1); /* invalidate rbac cache */
+
+ if (nscd_set_ttl_positive(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.passwd,
+ "passwd",
+ new->passwd.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.group,
+ "group",
+ new->group.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.group,
+ "group",
+ new->group.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.group,
+ "group",
+ new->group.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.group,
+ "group",
+ new->group.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.group,
+ "group",
+ new->group.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.group,
+ "group",
+ new->group.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.node,
+ "ipnodes",
+ new->node.nsc_suggestedsize) < 0 ||
+
+ nscd_set_ttl_positive(&current_admin.host,
+ "hosts",
+ new->host.nsc_pos_ttl) < 0 ||
+ nscd_set_ttl_negative(&current_admin.host,
+ "hosts",
+ new->host.nsc_neg_ttl) < 0 ||
+ nscd_set_khc(&current_admin.host,
+ "hosts",
+ new->host.nsc_keephot) < 0 ||
+ nscd_set_odo(&current_admin.host,
+ "hosts",
+ new->host.nsc_old_data_ok) < 0 ||
+ nscd_set_ec(&current_admin.host,
+ "hosts",
+ new->host.nsc_enabled) < 0 ||
+ nscd_set_ss(&current_admin.host,
+ "hosts",
+ new->host.nsc_suggestedsize) < 0 ||
+ nscd_set_rbac(new, 0) < 0) {
+ out->nsc_return_code = NOTFOUND;
+ return (-1);
+ }
+ out->nsc_return_code = SUCCESS;
+ return (0);
+}
+
+void
+client_killserver(void)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = KILLSERVER;
+
+ ndata = sizeof (u);
+ adata = sizeof (nsc_call_t);
+
+ dptr = &u.data;
+
+ _nsc_trydoorcall(&dptr, &ndata, &adata);
+}
+
+
+static int
+client_setadmin(admin_t *ptr)
+{
+ union {
+ nsc_data_t data;
+ char space[8192];
+ } u;
+
+ nsc_data_t *dptr;
+ int ndata;
+ int adata;
+
+ u.data.nsc_call.nsc_callnumber = SETADMIN;
+
+ (void) memcpy(u.data.nsc_call.nsc_u.name, ptr, sizeof (*ptr));
+
+ ndata = sizeof (u);
+ adata = sizeof (*ptr);
+
+ dptr = &u.data;
+
+ if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+dump_stat(nsc_stat_t *ptr)
+{
+ double hitrate;
+ (void) printf("%10s cache is enabled\n",
+ (ptr->nsc_enabled?"Yes":"No"));
+ (void) printf("%10d cache hits on positive entries\n",
+ ptr->nsc_pos_cache_hits);
+ (void) printf("%10d cache hits on negative entries\n",
+ ptr->nsc_neg_cache_hits);
+ (void) printf("%10d cache misses on positive entries\n",
+ ptr->nsc_pos_cache_misses);
+ (void) printf("%10d cache misses on negative entries\n",
+ ptr->nsc_neg_cache_misses);
+ hitrate = ptr->nsc_pos_cache_misses + ptr->nsc_neg_cache_misses +
+ ptr->nsc_pos_cache_hits + ptr->nsc_neg_cache_hits;
+
+ if (hitrate > 0.0)
+ hitrate = (100.0 * ((double)ptr->nsc_pos_cache_hits +
+ (double)ptr->nsc_neg_cache_hits))/hitrate;
+
+ (void) printf("%10.1f%% cache hit rate\n", hitrate);
+ (void) printf("%10d queries deferred\n", ptr->nsc_throttle_count);
+ (void) printf("%10d total entries\n", ptr->nsc_entries);
+ (void) printf("%10d complete cache invalidations\n",
+ ptr->nsc_invalidate_count);
+ (void) printf("%10d suggested size\n", ptr->nsc_suggestedsize);
+ (void) printf("%10d seconds time to live for positive entries\n",
+ ptr->nsc_pos_ttl);
+ (void) printf("%10d seconds time to live for negative entries\n",
+ ptr->nsc_neg_ttl);
+ (void) printf("%10d most active entries to be kept valid\n",
+ ptr->nsc_keephot);
+ (void) printf("%10s check /etc/{passwd, group, hosts, inet/ipnodes} "
+ "file for changes\n",
+ (ptr->nsc_check_files?"Yes":"No"));
+
+ (void) printf("%10s use possibly stale data rather than waiting for "
+ "refresh\n",
+ (ptr->nsc_old_data_ok?"Yes":"No"));
+}
+
+static void
+client_showstats(admin_t *ptr)
+{
+
+ (void) printf("nscd configuration:\n\n");
+ (void) printf("%10d server debug level\n", ptr->debug_level);
+ (void) printf("\"%s\" is server log file\n", ptr->logfile);
+
+ (void) printf("\npasswd cache:\n\n");
+ dump_stat(&(ptr->passwd));
+ (void) printf("\ngroup cache:\n\n");
+ dump_stat(&(ptr->group));
+ (void) printf("\nhosts cache:\n\n");
+ dump_stat(&(ptr->host));
+ (void) printf("\nipnodes cache:\n\n");
+ dump_stat(&(ptr->node));
+ (void) printf("\nexec_attr cache:\n\n");
+ dump_stat(&(ptr->exec));
+ (void) printf("\nprof_attr cache:\n\n");
+ dump_stat(&(ptr->prof));
+ (void) printf("\nuser_attr cache:\n\n");
+ dump_stat(&(ptr->user));
+}
+
+
+
+/*
+ * detach from tty
+ */
+static void
+detachfromtty(void)
+{
+ if (logfd > 0) {
+ int i;
+ for (i = 0; i < logfd; i++)
+ (void) close(i);
+ closefrom(logfd+1);
+ } else
+ closefrom(0);
+
+ (void) chdir("/");
+
+ switch (fork1()) {
+ case (pid_t)-1:
+ exit(1);
+ break;
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+ (void) setsid();
+ (void) open("/dev/null", O_RDWR, 0);
+ (void) dup(0);
+ (void) dup(0);
+}