# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2018, Joyent, Inc. # # Makefile for name service cache daemon # PROG= nscd MANIFEST= name-service-cache.xml SVCMETHOD= svc-nscd include ../Makefile.cmd ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) OBJS= server.o getpw.o getgr.o gethost.o getnode.o \ getether.o getrpc.o getproto.o getnet.o \ getbootp.o getauth.o getserv.o \ getnetmasks.o getprinter.o getproject.o \ getexec.o getprof.o getuser.o cache.o \ nscd_biggest.o nscd_wait.o \ nscd_init.o nscd_access.o nscd_cfgfile.o nscd_config.o \ nscd_dbimpl.o nscd_getentctx.o nscd_intaddr.o \ nscd_log.o nscd_nswconfig.o nscd_nswstate.o nscd_nswcfgst.o \ nscd_seqnum.o nscd_smfmonitor.o \ nscd_switch.o nscd_nswparse.o nscd_initf.o nscd_selfcred.o \ nscd_frontend.o nscd_admin.o nscd_door.o \ gettnrhtp.o gettnrhdb.o CLOBBERFILES= nscd SRCS= ${OBJS:%.o=%.c} CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_REENTRANT -DSUN_THREADS \ -I../../lib/libc/port/gen -I../../lib/libc/inc \ -I../../lib/libsldap/common CERRWARN += -Wno-switch CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses CERRWARN += -Wno-type-limits # not linted SMATCH=off # nscd interposes on many symbols, and must export others for its own dlsym() # use, and dlsym() calls from libc. Itemizing the interfaces within a mapfile # is error-prone, so establish the whole object as an interposer. LDFLAGS += $(ZINTERPOSE) # TCOV_FLAG= -ql PROGLIBS= $(LDLIBS) -lresolv -lnsl -lsocket -lumem -lscf -lavl # install macros and rule # ROOTPROG= ${ROOTUSRSBIN}/nscd .KEEP_STATE: all: $(PROG) $(NISPROG) ${PROG}: ${OBJS} ${LINK.c} ${OPT} -o $@ ${OBJS} ${PROGLIBS} ${POST_PROCESS} cstyle: ${CSTYLE} ${SRCS} install: all $(ROOTPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) check: $(CHKMANIFEST) clean: ${RM} ${OBJS} ${ROOTUSRSBIN}/%: % ${INS.file} ${ROOTUSRLIB}/%: % ${INS.file} include ../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. * Copyright 2018 Joyent, Inc. */ /* * Cache routines for nscd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cache.h" #include "nscd_door.h" #include "nscd_log.h" #include "nscd_config.h" #include "nscd_frontend.h" #include "nscd_switch.h" #define SUCCESS 0 #define NOTFOUND -1 #define SERVERERROR -2 #define NOSERVER -3 #define CONTINUE -4 static nsc_db_t *nsc_get_db(nsc_ctx_t *, int); static nscd_rc_t lookup_cache(nsc_lookup_args_t *, nscd_cfg_cache_t *, nss_XbyY_args_t *, char *, nsc_entry_t **); static uint_t reap_cache(nsc_ctx_t *, uint_t, uint_t); static void delete_entry(nsc_db_t *, nsc_ctx_t *, nsc_entry_t *); static void print_stats(nscd_cfg_stat_cache_t *); static void print_cfg(nscd_cfg_cache_t *); static int lookup_int(nsc_lookup_args_t *, int); #ifdef NSCD_DEBUG static void print_entry(nsc_db_t *, time_t, nsc_entry_t *); static void avl_dump(nsc_db_t *, time_t); static void hash_dump(nsc_db_t *, time_t); #endif /* NSCD_DEBUG */ static nsc_entry_t *hash_find(nsc_db_t *, nsc_entry_t *, uint_t *, nscd_bool_t); static void queue_adjust(nsc_db_t *, nsc_entry_t *); static void queue_remove(nsc_db_t *, nsc_entry_t *); #ifdef NSCD_DEBUG static void queue_dump(nsc_db_t *, time_t); #endif /* NSCD_DEBUG */ static int launch_update(nsc_lookup_args_t *); static void *do_update(void *); static void getxy_keepalive(nsc_ctx_t *, nsc_db_t *, int, int); static void ctx_info(nsc_ctx_t *); static void ctx_info_nolock(nsc_ctx_t *); static void ctx_invalidate(nsc_ctx_t *); static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int nsc_db_cis_key_compar(const void *, const void *); static int nsc_db_ces_key_compar(const void *, const void *); static int nsc_db_int_key_compar(const void *, const void *); static uint_t nsc_db_cis_key_gethash(nss_XbyY_key_t *, int); static uint_t nsc_db_ces_key_gethash(nss_XbyY_key_t *, int); static uint_t nsc_db_int_key_gethash(nss_XbyY_key_t *, int); static umem_cache_t *nsc_entry_cache; static nsc_ctx_t *init_cache_ctx(int); static void *reaper(void *); static void *revalidate(void *); static nss_status_t dup_packed_buffer(void *src, void *dst) { nsc_lookup_args_t *s = (nsc_lookup_args_t *)src; nsc_entry_t *d = (nsc_entry_t *)dst; nss_pheader_t *sphdr = (nss_pheader_t *)s->buffer; nss_pheader_t *dphdr = (nss_pheader_t *)d->buffer; int slen, new_pbufsiz = 0; if (NSCD_GET_STATUS(sphdr) != NSS_SUCCESS) { /* no result, copy header only (status, errno, etc) */ slen = sphdr->data_off; } else { /* * lookup result returned, data to copy is the packed * header plus result (add 1 for the terminating NULL * just in case) */ slen = sphdr->data_off + sphdr->data_len + 1; } /* allocate cache packed buffer */ if (dphdr != NULL && d->bufsize <= slen && d->bufsize != 0) { /* old buffer too small, free it */ free(dphdr); d->buffer = NULL; d->bufsize = 0; dphdr = NULL; } if (dphdr == NULL) { /* get new buffer */ dphdr = calloc(1, slen + 1); if (dphdr == NULL) return (NSS_ERROR); d->buffer = dphdr; d->bufsize = slen + 1; new_pbufsiz = slen + 1; } (void) memcpy(dphdr, sphdr, slen); if (new_pbufsiz != 0) dphdr->pbufsiz = new_pbufsiz; return (NSS_SUCCESS); } char *cache_name[CACHE_CTX_COUNT] = { NSS_DBNAM_PASSWD, NSS_DBNAM_GROUP, NSS_DBNAM_HOSTS, NSS_DBNAM_IPNODES, NSS_DBNAM_EXECATTR, NSS_DBNAM_PROFATTR, NSS_DBNAM_USERATTR, NSS_DBNAM_ETHERS, NSS_DBNAM_RPC, NSS_DBNAM_PROTOCOLS, NSS_DBNAM_NETWORKS, NSS_DBNAM_BOOTPARAMS, NSS_DBNAM_AUTHATTR, NSS_DBNAM_SERVICES, NSS_DBNAM_NETMASKS, NSS_DBNAM_PRINTERS, NSS_DBNAM_PROJECT, NSS_DBNAM_TSOL_TP, NSS_DBNAM_TSOL_RH }; typedef void (*cache_init_ctx_t)(nsc_ctx_t *); static cache_init_ctx_t cache_init_ctx[CACHE_CTX_COUNT] = { passwd_init_ctx, group_init_ctx, host_init_ctx, ipnode_init_ctx, exec_init_ctx, prof_init_ctx, user_init_ctx, ether_init_ctx, rpc_init_ctx, proto_init_ctx, net_init_ctx, bootp_init_ctx, auth_init_ctx, serv_init_ctx, netmask_init_ctx, printer_init_ctx, project_init_ctx, tnrhtp_init_ctx, tnrhdb_init_ctx }; nsc_ctx_t *cache_ctx_p[CACHE_CTX_COUNT] = { 0 }; static nscd_cfg_stat_cache_t null_stats = { 0 }; static nscd_cfg_global_cache_t global_cfg; /* * Given database name 'dbname' find cache index */ int get_cache_idx(char *dbname) { int i; char *nsc_name; for (i = 0; i < CACHE_CTX_COUNT; i++) { nsc_name = cache_name[i]; if (strcmp(nsc_name, dbname) == 0) return (i); } return (-1); } /* * Given database name 'dbname' retrieve cache context, * if not created yet, allocate and initialize it. */ static nscd_rc_t get_cache_ctx(char *dbname, nsc_ctx_t **ctx) { int i; *ctx = NULL; i = get_cache_idx(dbname); if (i == -1) return (NSCD_INVALID_ARGUMENT); if ((*ctx = cache_ctx_p[i]) == NULL) { *ctx = init_cache_ctx(i); if (*ctx == NULL) return (NSCD_NO_MEMORY); } return (NSCD_SUCCESS); } /* * Generate a log string to identify backend operation in debug logs */ static void nsc_db_str_key_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%s]", name, argp->key.name); } static void nsc_db_int_key_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.number); } /*ARGSUSED*/ static void nsc_db_any_key_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s", name); } /* * Returns cache based on dbop */ static nsc_db_t * nsc_get_db(nsc_ctx_t *ctx, int dbop) { int i; for (i = 0; i < ctx->db_count; i++) { if (ctx->nsc_db[i] && dbop == ctx->nsc_db[i]->dbop) return (ctx->nsc_db[i]); } return (NULL); } /* * integer compare routine for _NSC_DB_INT_KEY */ static int nsc_db_int_key_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; return (_NSC_INT_KEY_CMP(e1->key.number, e2->key.number)); } /* * case sensitive name compare routine for _NSC_DB_CES_KEY */ static int nsc_db_ces_key_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; l1 = strlen(e1->key.name); l2 = strlen(e2->key.name); res = strncmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2); return (_NSC_INT_KEY_CMP(res, 0)); } /* * case insensitive name compare routine _NSC_DB_CIS_KEY */ static int nsc_db_cis_key_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; l1 = strlen(e1->key.name); l2 = strlen(e2->key.name); res = strncasecmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2); return (_NSC_INT_KEY_CMP(res, 0)); } /* * macro used to generate elf hashes for strings */ #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \ hval = 0; \ while (*str) { \ uint_t g; \ hval = (hval << 4) + func(*str++); \ if ((g = (hval & 0xf0000000)) != 0) \ hval ^= g >> 24; \ hval &= ~g; \ } \ hval %= htsize; /* * cis hash function */ uint_t cis_gethash(const char *key, int htsize) { uint_t hval; if (key == NULL) return (0); _NSC_ELF_STR_GETHASH(tolower, key, htsize, hval); return (hval); } /* * ces hash function */ uint_t ces_gethash(const char *key, int htsize) { uint_t hval; if (key == NULL) return (0); _NSC_ELF_STR_GETHASH(, key, htsize, hval); return (hval); } /* * one-at-a-time hash function */ uint_t db_gethash(const void *key, int len, int htsize) { uint_t hval, i; const char *str = key; if (str == NULL) return (0); for (hval = 0, i = 0; i < len; i++) { hval += str[i]; hval += (hval << 10); hval ^= (hval >> 6); } hval += (hval << 3); hval ^= (hval >> 11); hval += (hval << 15); return (hval % htsize); } /* * case insensitive name gethash routine _NSC_DB_CIS_KEY */ static uint_t nsc_db_cis_key_gethash(nss_XbyY_key_t *key, int htsize) { return (cis_gethash(key->name, htsize)); } /* * case sensitive name gethash routine _NSC_DB_CES_KEY */ static uint_t nsc_db_ces_key_gethash(nss_XbyY_key_t *key, int htsize) { return (ces_gethash(key->name, htsize)); } /* * integer gethash routine _NSC_DB_INT_KEY */ static uint_t nsc_db_int_key_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(&key->number, sizeof (key->number), htsize)); } /* * Find entry in the hash table * if cmp == nscd_true) * return entry only if the keys match * else * return entry in the hash location without checking the keys * */ static nsc_entry_t * hash_find(nsc_db_t *nscdb, nsc_entry_t *entry, uint_t *hash, nscd_bool_t cmp) { nsc_entry_t *hashentry; if (nscdb->gethash) *hash = nscdb->gethash(&entry->key, nscdb->htsize); else return (NULL); hashentry = nscdb->htable[*hash]; if (cmp == nscd_false || hashentry == NULL) return (hashentry); if (nscdb->compar) { if (nscdb->compar(entry, hashentry) == 0) return (hashentry); } return (NULL); } #define HASH_REMOVE(nscdb, entry, hash, cmp) \ if (nscdb->htable) { \ if (entry == hash_find(nscdb, entry, &hash, cmp)) \ nscdb->htable[hash] = NULL; \ } #define HASH_INSERT(nscdb, entry, hash, cmp) \ if (nscdb->htable) { \ (void) hash_find(nscdb, entry, &hash, cmp); \ nscdb->htable[hash] = entry; \ } #ifdef NSCD_DEBUG static void print_entry(nsc_db_t *nscdb, time_t now, nsc_entry_t *entry) { nss_XbyY_args_t args; char whoami[512]; switch (entry->stats.status) { case ST_NEW_ENTRY: (void) fprintf(stdout, gettext("\t status: new entry\n")); return; case ST_UPDATE_PENDING: (void) fprintf(stdout, gettext("\t status: update pending\n")); return; case ST_LOOKUP_PENDING: (void) fprintf(stdout, gettext("\t status: lookup pending\n")); return; case ST_DISCARD: (void) fprintf(stdout, gettext("\t status: discarded entry\n")); return; default: if (entry->stats.timestamp < now) (void) fprintf(stdout, gettext("\t status: expired (%d seconds ago)\n"), now - entry->stats.timestamp); else (void) fprintf(stdout, gettext( "\t status: valid (expiry in %d seconds)\n"), entry->stats.timestamp - now); break; } (void) fprintf(stdout, gettext("\t hits: %u\n"), entry->stats.hits); args.key = entry->key; (void) nscdb->getlogstr(nscdb->name, whoami, sizeof (whoami), &args); (void) fprintf(stdout, "\t %s\n", whoami); } #endif /* NSCD_DEBUG */ static void print_stats(nscd_cfg_stat_cache_t *statsp) { (void) fprintf(stdout, gettext("\n\t STATISTICS:\n")); (void) fprintf(stdout, gettext("\t positive hits: %lu\n"), statsp->pos_hits); (void) fprintf(stdout, gettext("\t negative hits: %lu\n"), statsp->neg_hits); (void) fprintf(stdout, gettext("\t positive misses: %lu\n"), statsp->pos_misses); (void) fprintf(stdout, gettext("\t negative misses: %lu\n"), statsp->neg_misses); (void) fprintf(stdout, gettext("\t total entries: %lu\n"), statsp->entries); (void) fprintf(stdout, gettext("\t queries queued: %lu\n"), statsp->wait_count); (void) fprintf(stdout, gettext("\t queries dropped: %lu\n"), statsp->drop_count); (void) fprintf(stdout, gettext("\t cache invalidations: %lu\n"), statsp->invalidate_count); _NSC_GET_HITRATE(statsp); (void) fprintf(stdout, gettext("\t cache hit rate: %.1f\n"), statsp->hitrate); } static void print_cfg(nscd_cfg_cache_t *cfgp) { (void) fprintf(stdout, gettext("\n\t CONFIG:\n")); (void) fprintf(stdout, gettext("\t enabled: %s\n"), yes_no(cfgp->enable)); (void) fprintf(stdout, gettext("\t per user cache: %s\n"), yes_no(cfgp->per_user)); (void) fprintf(stdout, gettext("\t avoid name service: %s\n"), yes_no(cfgp->avoid_ns)); (void) fprintf(stdout, gettext("\t check file: %s\n"), yes_no(cfgp->check_files)); (void) fprintf(stdout, gettext("\t check file interval: %d\n"), cfgp->check_interval); (void) fprintf(stdout, gettext("\t positive ttl: %d\n"), cfgp->pos_ttl); (void) fprintf(stdout, gettext("\t negative ttl: %d\n"), cfgp->neg_ttl); (void) fprintf(stdout, gettext("\t keep hot count: %d\n"), cfgp->keephot); (void) fprintf(stdout, gettext("\t hint size: %d\n"), cfgp->hint_size); (void) fprintf(stdout, gettext("\t max entries: %lu%s"), cfgp->maxentries, cfgp->maxentries?"\n":" (unlimited)\n"); } #ifdef NSCD_DEBUG static void hash_dump(nsc_db_t *nscdb, time_t now) { nsc_entry_t *entry; int i; (void) fprintf(stdout, gettext("\n\nHASH TABLE:\n")); for (i = 0; i < nscdb->htsize; i++) { if ((entry = nscdb->htable[i]) != NULL) { (void) fprintf(stdout, "hash[%d]:\n", i); print_entry(nscdb, now, entry); } } } #endif /* NSCD_DEBUG */ #ifdef NSCD_DEBUG static void avl_dump(nsc_db_t *nscdb, time_t now) { nsc_entry_t *entry; int i; (void) fprintf(stdout, gettext("\n\nAVL TREE:\n")); for (entry = avl_first(&nscdb->tree), i = 0; entry != NULL; entry = avl_walk(&nscdb->tree, entry, AVL_AFTER)) { (void) fprintf(stdout, "avl node[%d]:\n", i++); print_entry(nscdb, now, entry); } } #endif /* NSCD_DEBUG */ #ifdef NSCD_DEBUG static void queue_dump(nsc_db_t *nscdb, time_t now) { nsc_entry_t *entry; int i; (void) fprintf(stdout, gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"), nscdb->name, avl_numnodes(&nscdb->tree)); (void) fprintf(stdout, gettext("Starting with the most recently accessed:\n")); for (entry = nscdb->qtail, i = 0; entry; entry = entry->qnext) { (void) fprintf(stdout, "entry[%d]:\n", i++); print_entry(nscdb, now, entry); } } #endif /* NSCD_DEBUG */ static void queue_remove(nsc_db_t *nscdb, nsc_entry_t *entry) { if (nscdb->qtail == entry) nscdb->qtail = entry->qnext; else entry->qprev->qnext = entry->qnext; if (nscdb->qhead == entry) nscdb->qhead = entry->qprev; else entry->qnext->qprev = entry->qprev; if (nscdb->reap_node == entry) nscdb->reap_node = entry->qnext; entry->qnext = entry->qprev = NULL; } static void queue_adjust(nsc_db_t *nscdb, nsc_entry_t *entry) { #ifdef NSCD_DEBUG assert(nscdb->qtail || entry->qnext == NULL && entry->qprev == NULL); assert(nscdb->qtail && nscdb->qhead || nscdb->qtail == NULL && nscdb->qhead == NULL); assert(entry->qprev || entry->qnext == NULL || nscdb->qtail == entry); #endif /* NSCD_DEBUG */ /* already in the desired position */ if (nscdb->qtail == entry) return; /* new queue */ if (nscdb->qtail == NULL) { nscdb->qhead = nscdb->qtail = entry; return; } /* new entry (prev == NULL AND tail != entry) */ if (entry->qprev == NULL) { nscdb->qtail->qprev = entry; entry->qnext = nscdb->qtail; nscdb->qtail = entry; return; } /* existing entry */ if (nscdb->reap_node == entry) nscdb->reap_node = entry->qnext; if (nscdb->qhead == entry) nscdb->qhead = entry->qprev; else entry->qnext->qprev = entry->qprev; entry->qprev->qnext = entry->qnext; entry->qprev = NULL; entry->qnext = nscdb->qtail; nscdb->qtail->qprev = entry; nscdb->qtail = entry; } /* * Init cache */ nscd_rc_t init_cache(int debug_level) { int cflags; cflags = (debug_level > 0)?0:UMC_NODEBUG; nsc_entry_cache = umem_cache_create("nsc_entry_cache", sizeof (nsc_entry_t), 0, NULL, NULL, NULL, NULL, NULL, cflags); if (nsc_entry_cache == NULL) return (NSCD_NO_MEMORY); return (NSCD_SUCCESS); } /* * Create cache */ nsc_db_t * make_cache(enum db_type dbtype, int dbop, char *name, int (*compar) (const void *, const void *), void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *), uint_t (*gethash)(nss_XbyY_key_t *, int), enum hash_type httype, int htsize) { nsc_db_t *nscdb; char *me = "make_cache"; nscdb = (nsc_db_t *)malloc(sizeof (*nscdb)); if (nscdb == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "%s: memory allocation failure\n", name); goto out; } (void) memset(nscdb, 0, sizeof (*nscdb)); nscdb->dbop = dbop; nscdb->name = name; nscdb->db_type = dbtype; /* Assign compare routine */ if (compar == NULL) { if (_NSC_DB_CES_KEY(nscdb)) nscdb->compar = nsc_db_ces_key_compar; else if (_NSC_DB_CIS_KEY(nscdb)) nscdb->compar = nsc_db_cis_key_compar; else if (_NSC_DB_INT_KEY(nscdb)) nscdb->compar = nsc_db_int_key_compar; else assert(0); } else { nscdb->compar = compar; } /* The cache is an AVL tree */ avl_create(&nscdb->tree, nscdb->compar, sizeof (nsc_entry_t), offsetof(nsc_entry_t, avl_link)); /* Assign log routine */ if (getlogstr == NULL) { if (_NSC_DB_STR_KEY(nscdb)) nscdb->getlogstr = nsc_db_str_key_getlogstr; else if (_NSC_DB_INT_KEY(nscdb)) nscdb->getlogstr = nsc_db_int_key_getlogstr; else nscdb->getlogstr = nsc_db_any_key_getlogstr; } else { nscdb->getlogstr = getlogstr; } /* The AVL tree based cache uses a hash table for quick access */ if (htsize != 0) { /* Determine hash table size based on type */ nscdb->hash_type = httype; if (htsize < 0) { switch (httype) { case nsc_ht_power2: htsize = _NSC_INIT_HTSIZE_POWER2; break; case nsc_ht_prime: case nsc_ht_default: default: htsize = _NSC_INIT_HTSIZE_PRIME; } } nscdb->htsize = htsize; /* Create the hash table */ nscdb->htable = calloc(htsize, sizeof (*(nscdb->htable))); if (nscdb->htable == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "%s: memory allocation failure\n", name); goto out; } /* Assign gethash routine */ if (gethash == NULL) { if (_NSC_DB_CES_KEY(nscdb)) nscdb->gethash = nsc_db_ces_key_gethash; else if (_NSC_DB_CIS_KEY(nscdb)) nscdb->gethash = nsc_db_cis_key_gethash; else if (_NSC_DB_INT_KEY(nscdb)) nscdb->gethash = nsc_db_int_key_gethash; else assert(0); } else { nscdb->gethash = gethash; } } (void) mutex_init(&nscdb->db_mutex, USYNC_THREAD, NULL); return (nscdb); out: if (nscdb->htable) free(nscdb->htable); if (nscdb) free(nscdb); return (NULL); } /* * verify */ /* ARGSUSED */ nscd_rc_t _nscd_cfg_cache_verify( void *data, struct nscd_cfg_param_desc *pdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t dflag, nscd_cfg_error_t **errorp, void **cookie) { return (NSCD_SUCCESS); } /* * notify */ /* ARGSUSED */ nscd_rc_t _nscd_cfg_cache_notify( void *data, struct nscd_cfg_param_desc *pdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t dflag, nscd_cfg_error_t **errorp, void **cookie) { nsc_ctx_t *ctx; void *dp; int i; /* group data */ if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) { /* global config */ global_cfg = *(nscd_cfg_global_cache_t *)data; } else if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_SET_ALL_DB)) { /* non-global config for all dbs */ for (i = 0; i < CACHE_CTX_COUNT; i++) { ctx = cache_ctx_p[i]; if (ctx == NULL) return (NSCD_CTX_NOT_FOUND); (void) rw_wrlock(&ctx->cfg_rwlp); ctx->cfg = *(nscd_cfg_cache_t *)data; ctx->cfg_mtime = time(NULL); (void) rw_unlock(&ctx->cfg_rwlp); } } else { /* non-global config for a specific db */ /* ignore non-caching databases */ if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS) return (NSCD_SUCCESS); (void) rw_wrlock(&ctx->cfg_rwlp); ctx->cfg = *(nscd_cfg_cache_t *)data; ctx->cfg_mtime = time(NULL); (void) rw_unlock(&ctx->cfg_rwlp); } return (NSCD_SUCCESS); } /* individual data */ if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) { /* global config */ dp = (char *)&global_cfg + pdesc->p_offset; (void) memcpy(dp, data, pdesc->p_size); } else if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_SET_ALL_DB)) { /* non-global config for all dbs */ for (i = 0; i < CACHE_CTX_COUNT; i++) { ctx = cache_ctx_p[i]; if (ctx == NULL) return (NSCD_CTX_NOT_FOUND); dp = (char *)&ctx->cfg + pdesc->p_offset; (void) rw_wrlock(&ctx->cfg_rwlp); (void) memcpy(dp, data, pdesc->p_size); ctx->cfg_mtime = time(NULL); (void) rw_unlock(&ctx->cfg_rwlp); } } else { /* non-global config for a specific db */ /* ignore non-caching databases */ if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS) return (NSCD_SUCCESS); dp = (char *)&ctx->cfg + pdesc->p_offset; (void) rw_wrlock(&ctx->cfg_rwlp); (void) memcpy(dp, data, pdesc->p_size); ctx->cfg_mtime = time(NULL); (void) rw_unlock(&ctx->cfg_rwlp); } return (NSCD_SUCCESS); } /* * get stat */ /* ARGSUSED */ nscd_rc_t _nscd_cfg_cache_get_stat( void **stat, struct nscd_cfg_stat_desc *sdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t *dflag, void (**free_stat)(void *stat), nscd_cfg_error_t **errorp) { nscd_cfg_stat_cache_t *statsp, stats; nsc_ctx_t *ctx; int i; nscd_rc_t rc; statsp = calloc(1, sizeof (*statsp)); if (statsp == NULL) return (NSCD_NO_MEMORY); if (_nscd_cfg_flag_is_set(sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL)) { for (i = 0; i < CACHE_CTX_COUNT; i++) { if (cache_ctx_p[i] == NULL) stats = null_stats; else { (void) mutex_lock(&cache_ctx_p[i]->stats_mutex); stats = cache_ctx_p[i]->stats; (void) mutex_unlock( &cache_ctx_p[i]->stats_mutex); } statsp->pos_hits += stats.pos_hits; statsp->neg_hits += stats.neg_hits; statsp->pos_misses += stats.pos_misses; statsp->neg_misses += stats.neg_misses; statsp->entries += stats.entries; statsp->drop_count += stats.drop_count; statsp->wait_count += stats.wait_count; statsp->invalidate_count += stats.invalidate_count; } } else { if ((rc = get_cache_ctx(nswdb->name, &ctx)) != NSCD_SUCCESS) { free(statsp); return (rc); } (void) mutex_lock(&ctx->stats_mutex); *statsp = ctx->stats; (void) mutex_unlock(&ctx->stats_mutex); } _NSC_GET_HITRATE(statsp); *stat = statsp; return (NSCD_SUCCESS); } /* * This function should only be called when nscd is * not a daemon. */ void nsc_info(nsc_ctx_t *ctx, char *dbname, nscd_cfg_cache_t cfg[], nscd_cfg_stat_cache_t stats[]) { int i; char *me = "nsc_info"; nsc_ctx_t *ctx1; nsc_ctx_t ctx2; nscd_rc_t rc; if (ctx) { ctx_info(ctx); return; } if (dbname) { rc = get_cache_ctx(dbname, &ctx1); if (rc == NSCD_INVALID_ARGUMENT) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: no cache context found\n", dbname); return; } else if (rc == NSCD_NO_MEMORY) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: unable to create cache context - no memory\n", dbname); return; } ctx_info(ctx1); return; } if (cfg == NULL || stats == NULL) return; for (i = 0; i < CACHE_CTX_COUNT; i++) { ctx2.dbname = cache_name[i]; ctx2.cfg = cfg[i]; ctx2.stats = stats[i]; ctx_info_nolock(&ctx2); } } static void ctx_info_nolock(nsc_ctx_t *ctx) { nscd_cfg_cache_t cfg; nscd_cfg_stat_cache_t stats; cfg = ctx->cfg; (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname); (void) print_cfg(&cfg); if (cfg.enable == nscd_false) return; stats = ctx->stats; (void) print_stats(&stats); } static void ctx_info(nsc_ctx_t *ctx) { nscd_cfg_cache_t cfg; nscd_cfg_stat_cache_t stats; (void) rw_rdlock(&ctx->cfg_rwlp); cfg = ctx->cfg; (void) rw_unlock(&ctx->cfg_rwlp); (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname); (void) print_cfg(&cfg); if (cfg.enable == nscd_false) return; (void) mutex_lock(&ctx->stats_mutex); stats = ctx->stats; (void) mutex_unlock(&ctx->stats_mutex); (void) print_stats(&stats); } #ifdef NSCD_DEBUG /* * This function should only be called when nscd is * not a daemon. */ int nsc_dump(char *dbname, int dbop) { nsc_ctx_t *ctx; nsc_db_t *nscdb; nscd_bool_t enabled; time_t now; char *me = "nsc_dump"; int i; if ((i = get_cache_idx(dbname)) == -1) { (void) fprintf(stdout, gettext("invalid cache name\n")); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: invalid cache name\n", dbname); return (NSCD_CACHE_INVALID_CACHE_NAME); } if ((ctx = cache_ctx_p[i]) == NULL) { (void) fprintf(stdout, gettext("no cache context\n")); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: no cache context\n", dbname); return (NSCD_CACHE_NO_CACHE_CTX); } now = time(NULL); (void) rw_rdlock(&ctx->cfg_rwlp); enabled = ctx->cfg.enable; (void) rw_unlock(&ctx->cfg_rwlp); if (enabled == nscd_false) return (NSCD_CACHE_DISABLED); nscdb = nsc_get_db(ctx, dbop); if (nscdb == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s:%d: no cache found\n", dbname, dbop); return (NSCD_CACHE_NO_CACHE_FOUND); } (void) mutex_lock(&nscdb->db_mutex); (void) queue_dump(nscdb, now); (void) hash_dump(nscdb, now); (void) avl_dump(nscdb, now); (void) mutex_unlock(&nscdb->db_mutex); return (NSCD_SUCCESS); } #endif /* NSCD_DEBUG */ /* * These macros are for exclusive use of nsc_lookup */ #define NSC_LOOKUP_LOG(loglevel, fmt) \ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \ (me, fmt, whoami); static int nsc_lookup_no_cache(nsc_lookup_args_t *largs, const char *str) { char *me = "nsc_lookup_no_cache"; nss_status_t status; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: name service lookup (bypassing cache)\n", str); nss_psearch(largs->buffer, largs->bufsize); status = NSCD_GET_STATUS(largs->buffer); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: name service lookup status = %d\n", str, status); if (status == NSS_SUCCESS) { return (SUCCESS); } else if (status == NSS_NOTFOUND) { return (NOTFOUND); } else { return (SERVERERROR); } } /* * This function starts the revalidation and reaper threads * for a cache */ static void start_threads(nsc_ctx_t *ctx) { int errnum; char *me = "start_threads"; /* * kick off the revalidate thread (if necessary) */ if (ctx->revalidate_on != nscd_true) { if (thr_create(NULL, 0, revalidate, ctx, 0, NULL) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "thr_create (revalidate thread for %s): %s\n", ctx->dbname, strerror(errnum)); exit(1); } ctx->revalidate_on = nscd_true; } /* * kick off the reaper thread (if necessary) */ if (ctx->reaper_on != nscd_true) { if (thr_create(NULL, 0, reaper, ctx, 0, NULL) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "thr_create (reaper thread for %s): %s\n", ctx->dbname, strerror(errnum)); exit(1); } ctx->reaper_on = nscd_true; } } /* * Examine the packed buffer, see if the front-end parameters * indicate that the caller specified nsswitch config should be * used for the lookup. Return 1 if yes, otherwise 0. */ static int nsw_config_in_phdr(void *buf) { nss_pheader_t *pbuf = (nss_pheader_t *)buf; nssuint_t off; nss_dbd_t *pdbd; char *me = "nsw_config_in_phdr"; off = pbuf->dbd_off; if (off == 0) return (0); pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off)); if (pdbd->o_default_config == 0) return (0); if ((enum nss_dbp_flags)pdbd->flags & NSS_USE_DEFAULT_CONFIG) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "use caller specified nsswitch config\n"); return (1); } else return (0); } static nss_status_t copy_result(void *rbuf, void *cbuf) { nss_pheader_t *rphdr = (nss_pheader_t *)rbuf; nss_pheader_t *cphdr = (nss_pheader_t *)cbuf; char *me = "copy_result"; /* return NSS_ERROR if not enough room to copy result */ if (cphdr->data_len + 1 > rphdr->data_len) { NSCD_SET_STATUS(rphdr, NSS_ERROR, ERANGE); return (NSS_ERROR); } else { char *dst; if (cphdr->data_len == 0) return (NSS_SUCCESS); dst = (char *)rphdr + rphdr->data_off; (void) memcpy(dst, (char *)cphdr + cphdr->data_off, cphdr->data_len); rphdr->data_len = cphdr->data_len; /* some frontend code expects a terminating NULL char */ *(dst + rphdr->data_len) = '\0'; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "cache data (len = %lld): >>%s<<\n", cphdr->data_len, (char *)cphdr + cphdr->data_off); return (NSS_SUCCESS); } } static int get_dns_ttl(void *pbuf, char *dbname) { nss_pheader_t *phdr = (nss_pheader_t *)pbuf; int ttl; char *me = "get_dns_ttl"; /* if returned, dns ttl is stored in the extended data area */ if (phdr->ext_off == 0) return (-1); if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 && strcmp(dbname, NSS_DBNAM_IPNODES) != 0) return (-1); ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off)); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "dns ttl is %d seconds\n", ttl); return (ttl); } static int check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, char *whoami, int flag) { nsc_db_t *nscdb; nsc_ctx_t *ctx; char *me = "check_config"; ctx = largs->ctx; nscdb = largs->nscdb; /* see if the cached config needs update */ if (nscdb->cfg_mtime != ctx->cfg_mtime) { (void) rw_rdlock(&ctx->cfg_rwlp); nscdb->cfg = ctx->cfg; nscdb->cfg_mtime = ctx->cfg_mtime; (void) rw_unlock(&ctx->cfg_rwlp); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "config for context %s, database %s updated\n", ctx->dbname, nscdb->name); } *cfgp = nscdb->cfg; if (cfgp->enable == nscd_false) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: cache disabled\n", ctx->dbname); if (UPDATEBIT & flag) return (NOTFOUND); else return (nsc_lookup_no_cache(largs, whoami)); } /* * if caller requests lookup using its * own nsswitch config, bypass cache */ if (nsw_config_in_phdr(largs->buffer)) return (nsc_lookup_no_cache(largs, whoami)); /* no need of cache if we are dealing with 0 ttls */ if (cfgp->pos_ttl <= 0 && cfgp->neg_ttl <= 0) { if (flag & UPDATEBIT) return (NOTFOUND); else if (cfgp->avoid_ns == nscd_true) return (SERVERERROR); return (nsc_lookup_no_cache(largs, whoami)); } return (CONTINUE); } /* * Invalidate cache if database file has been modified. * See check_files config param for details. */ static void check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg, char *whoami, time_t now) { struct stat buf; nscd_bool_t file_modified = nscd_false; char *me = "check_db_file"; if (cfg.check_interval != 0 && (now - ctx->file_chktime) < cfg.check_interval) return; ctx->file_chktime = now; if (stat(ctx->file_name, &buf) == 0) { if (ctx->file_mtime == 0) { (void) mutex_lock(&ctx->file_mutex); if (ctx->file_mtime == 0) { ctx->file_mtime = buf.st_mtime; ctx->file_size = buf.st_size; ctx->file_ino = buf.st_ino; } (void) mutex_unlock(&ctx->file_mutex); } else if (ctx->file_mtime < buf.st_mtime || ctx->file_size != buf.st_size || ctx->file_ino != buf.st_ino) { (void) mutex_lock(&ctx->file_mutex); if (ctx->file_mtime < buf.st_mtime || ctx->file_size != buf.st_size || ctx->file_ino != buf.st_ino) { file_modified = nscd_true; ctx->file_mtime = buf.st_mtime; ctx->file_size = buf.st_size; ctx->file_ino = buf.st_ino; } (void) mutex_unlock(&ctx->file_mutex); } } if (file_modified == nscd_true) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: file %s has been modified - invalidating cache\n", whoami, ctx->file_name); ctx_invalidate(ctx); } } static int lookup_int(nsc_lookup_args_t *largs, int flag) { nsc_ctx_t *ctx; nsc_db_t *nscdb; nscd_cfg_cache_t cfg; nsc_entry_t *this_entry; nsc_entry_stat_t *this_stats; nsc_action_t next_action; nss_status_t status; nscd_bool_t delete; nscd_rc_t rc; char *dbname; int dbop, errnum; int cfg_rc; nss_XbyY_args_t args; char whoami[128]; time_t now = time(NULL); /* current time */ char *me = "lookup_int"; /* extract dbop, dbname, key and cred */ status = nss_packed_getkey(largs->buffer, largs->bufsize, &dbname, &dbop, &args); if (status != NSS_SUCCESS) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "nss_packed_getkey failure (%d)\n", status); return (SERVERERROR); } /* get the cache context */ if (largs->ctx == NULL) { if (get_cache_ctx(dbname, &largs->ctx) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: no cache context found\n", dbname); if (UPDATEBIT & flag) return (NOTFOUND); else return (nsc_lookup_no_cache(largs, dbname)); } } ctx = largs->ctx; if (largs->nscdb == NULL) { if ((largs->nscdb = nsc_get_db(ctx, dbop)) == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s:%d: no cache found\n", dbname, dbop); if (UPDATEBIT & flag) return (NOTFOUND); else return (nsc_lookup_no_cache(largs, dbname)); } } nscdb = largs->nscdb; _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) { (void) nscdb->getlogstr(nscdb->name, whoami, sizeof (whoami), &args); } if (UPDATEBIT & flag) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: refresh start\n", whoami); } else { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: lookup start\n", whoami); } cfg_rc = check_config(largs, &cfg, whoami, flag); if (cfg_rc != CONTINUE) return (cfg_rc); /* * Invalidate cache if file has been modified. */ if (cfg.check_files == nscd_true) check_db_file(ctx, cfg, whoami, now); (void) mutex_lock(&nscdb->db_mutex); /* Lookup the cache table */ for (;;) { delete = nscd_false; rc = lookup_cache(largs, &cfg, &args, whoami, &this_entry); if (rc != NSCD_SUCCESS) { (void) mutex_unlock(&nscdb->db_mutex); /* Either no entry and avoid name service */ if (rc == NSCD_DB_ENTRY_NOT_FOUND || rc == NSCD_INVALID_ARGUMENT) return (NOTFOUND); /* OR memory error */ return (SERVERERROR); } /* get the stats from the entry */ this_stats = &this_entry->stats; /* * What should we do next ? */ switch (this_stats->status) { case ST_NEW_ENTRY: delete = nscd_true; next_action = _NSC_NSLOOKUP; break; case ST_UPDATE_PENDING: if (flag & UPDATEBIT) { (void) mutex_unlock(&nscdb->db_mutex); return (NOTFOUND); } else if (this_stats->timestamp < now) next_action = _NSC_WAIT; else next_action = _NSC_USECACHED; break; case ST_LOOKUP_PENDING: if (flag & UPDATEBIT) { (void) mutex_unlock(&nscdb->db_mutex); return (NOTFOUND); } next_action = _NSC_WAIT; break; case ST_DISCARD: if (cfg.avoid_ns == nscd_true) { (void) mutex_unlock(&nscdb->db_mutex); return (NOTFOUND); } /* otherwise reuse the entry */ (void) memset(this_stats, 0, sizeof (*this_stats)); next_action = _NSC_NSLOOKUP; break; default: if (cfg.avoid_ns == nscd_true) next_action = _NSC_USECACHED; else if ((flag & UPDATEBIT) || (this_stats->timestamp < now)) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: cached entry needs to be updated\n", whoami); next_action = _NSC_NSLOOKUP; } else next_action = _NSC_USECACHED; break; } if (next_action == _NSC_WAIT) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: need to wait\n", whoami); /* do we have clearance ? */ if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { /* nope. quit */ (void) mutex_lock(&ctx->stats_mutex); ctx->stats.drop_count++; (void) mutex_unlock(&ctx->stats_mutex); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) (me, "%s: throttling load\n", whoami); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(WARNING, "%s: no clearance to wait\n"); return (NOSERVER); } /* yes can wait */ (void) nscd_wait(ctx, nscdb, this_entry); (void) _nscd_release_clearance(&ctx->throttle_sema); continue; } break; } if (!(UPDATEBIT & flag)) this_stats->hits++; /* update hit count */ if (next_action == _NSC_NSLOOKUP) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: name service lookup required\n", whoami); if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { if (delete == nscd_true) delete_entry(nscdb, ctx, this_entry); else this_stats->status = ST_DISCARD; (void) mutex_lock(&ctx->stats_mutex); ctx->stats.drop_count++; (void) mutex_unlock(&ctx->stats_mutex); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(WARNING, "%s: no clearance for lookup\n"); return (NOSERVER); } /* block any threads accessing this entry */ this_stats->status = (flag & UPDATEBIT) ? ST_UPDATE_PENDING : ST_LOOKUP_PENDING; /* release lock and do name service lookup */ (void) mutex_unlock(&nscdb->db_mutex); nss_psearch(largs->buffer, largs->bufsize); status = NSCD_GET_STATUS(largs->buffer); (void) mutex_lock(&nscdb->db_mutex); this_stats->status = 0; (void) _nscd_release_clearance(&ctx->throttle_sema); /* signal waiting threads */ (void) nscd_signal(ctx, nscdb, this_entry); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: name service lookup status = %d\n", whoami, status); if (status == NSS_SUCCESS) { int ttl; /* * data found in name service * update cache */ status = dup_packed_buffer(largs, this_entry); if (status != NSS_SUCCESS) { delete_entry(nscdb, ctx, this_entry); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: failed to update cache\n"); return (SERVERERROR); } /* * store unpacked key in cache */ status = nss_packed_getkey(this_entry->buffer, this_entry->bufsize, &dbname, &dbop, &args); if (status != NSS_SUCCESS) { delete_entry(nscdb, ctx, this_entry); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: failed to extract key\n"); return (SERVERERROR); } this_entry->key = args.key; /* struct copy */ /* update +ve miss count */ if (!(UPDATEBIT & flag)) { (void) mutex_lock(&ctx->stats_mutex); ctx->stats.pos_misses++; (void) mutex_unlock(&ctx->stats_mutex); } /* update +ve ttl */ ttl = get_dns_ttl(largs->buffer, dbname); /* honor the dns ttl less than postive ttl */ if (ttl < 0 || ttl > cfg.pos_ttl) ttl = cfg.pos_ttl; this_stats->timestamp = time(NULL) + ttl; /* * start the revalidation and reaper threads * if not already started */ start_threads(ctx); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: cache updated with positive entry\n"); return (SUCCESS); } else if (status == NSS_NOTFOUND) { /* * data not found in name service * update cache */ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) (me, "%s: name service lookup failed\n", whoami); if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) { delete_entry(nscdb, ctx, this_entry); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: ERANGE, cache not updated " "with negative entry\n"); return (NOTFOUND); } status = dup_packed_buffer(largs, this_entry); if (status != NSS_SUCCESS) { delete_entry(nscdb, ctx, this_entry); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: failed to update cache\n"); return (SERVERERROR); } /* store unpacked key in cache */ status = nss_packed_getkey(this_entry->buffer, this_entry->bufsize, &dbname, &dbop, &args); if (status != NSS_SUCCESS) { delete_entry(nscdb, ctx, this_entry); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: failed to extract key\n"); return (SERVERERROR); } this_entry->key = args.key; /* struct copy */ /* update -ve ttl */ this_stats->timestamp = time(NULL) + cfg.neg_ttl; /* update -ve miss count */ if (!(UPDATEBIT & flag)) { (void) mutex_lock(&ctx->stats_mutex); ctx->stats.neg_misses++; (void) mutex_unlock(&ctx->stats_mutex); } /* * start the revalidation and reaper threads * if not already started */ start_threads(ctx); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: cache updated with negative entry\n"); return (NOTFOUND); } else { /* * name service lookup failed */ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) (me, "%s: name service lookup failed\n", whoami); errnum = NSCD_GET_ERRNO(largs->buffer); if (delete == nscd_true) delete_entry(nscdb, ctx, this_entry); else this_stats->status = ST_DISCARD; (void) mutex_unlock(&nscdb->db_mutex); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: name service lookup failed " "(status=%d, errno=%d)\n", whoami, status, errnum); return (SERVERERROR); } } else if (next_action == _NSC_USECACHED) { /* * found entry in cache */ if (UPDATEBIT & flag) { (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: no need to update\n"); return (SUCCESS); } if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) == NSS_SUCCESS) { /* positive hit */ (void) mutex_lock(&ctx->stats_mutex); ctx->stats.pos_hits++; (void) mutex_unlock(&ctx->stats_mutex); /* update response buffer */ if (copy_result(largs->buffer, this_entry->buffer) != NSS_SUCCESS) { (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: response buffer insufficient\n"); return (SERVERERROR); } (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: positive entry in cache\n"); return (SUCCESS); } else { /* negative hit */ (void) mutex_lock(&ctx->stats_mutex); ctx->stats.neg_hits++; (void) mutex_unlock(&ctx->stats_mutex); NSCD_SET_STATUS((nss_pheader_t *)largs->buffer, NSCD_GET_STATUS(this_entry->buffer), NSCD_GET_ERRNO(this_entry->buffer)); NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer, NSCD_GET_HERRNO(this_entry->buffer)); (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(DEBUG, "%s: negative entry in cache\n"); return (NOTFOUND); } } (void) mutex_unlock(&nscdb->db_mutex); NSC_LOOKUP_LOG(ERROR, "%s: cache backend failure\n"); return (SERVERERROR); } /* * NSCD cache backend lookup function */ /*ARGSUSED*/ void nsc_lookup(nsc_lookup_args_t *largs, int flag) { nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer; int rc; rc = lookup_int(largs, 0); if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL) return; switch (rc) { case SUCCESS: NSCD_SET_STATUS(phdr, NSS_SUCCESS, 0); break; case NOTFOUND: NSCD_SET_STATUS(phdr, NSS_NOTFOUND, -1); break; case SERVERERROR: /* * status and errno should have been set in the phdr, * if not, set status to NSS_ERROR */ if (NSCD_STATUS_IS_OK(phdr)) { NSCD_SET_STATUS(phdr, NSS_ERROR, 0); } break; case NOSERVER: NSCD_SET_STATUS(phdr, NSS_TRYLOCAL, -1); break; } } static nsc_ctx_t * init_cache_ctx(int i) { nsc_ctx_t *ctx; ctx = calloc(1, sizeof (nsc_ctx_t)); if (ctx == NULL) return (NULL); /* init locks and semaphores */ (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL); (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL); (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL); (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]); cache_init_ctx[i](ctx); cache_ctx_p[i] = ctx; return (ctx); } static void * revalidate(void *arg) { nsc_ctx_t *ctx = arg; (void) thr_setname(thr_self(), "revalidate"); for (;;) { int i, slp, interval, count; (void) rw_rdlock(&ctx->cfg_rwlp); slp = ctx->cfg.pos_ttl; count = ctx->cfg.keephot; (void) rw_unlock(&ctx->cfg_rwlp); if (slp < 60) slp = 60; if (count != 0) { interval = (slp/2)/count; if (interval == 0) interval = 1; (void) sleep(slp*2/3); for (i = 0; i < ctx->db_count; i++) { getxy_keepalive(ctx, ctx->nsc_db[i], count, interval); } } else { (void) sleep(slp); } } return (NULL); } static void getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval) { nsc_keephot_t *table; nsc_entry_t *entry, *ptr; int i; nsc_lookup_args_t *largs; nss_pheader_t *phdr; int bufsiz; char *me = "getxy_keepalive"; /* we won't be here if keep == 0 so need to check that */ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: keep alive\n", nscdb->name); if ((table = maken(keep)) == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "memory allocation failure\n"); exit(1); } (void) mutex_lock(&nscdb->db_mutex); entry = nscdb->qtail; while (entry != NULL) { /* leave pending calls alone */ if (!(entry->stats.status & ST_PENDING)) { /* do_revalidate */ (void) insertn(table, entry->stats.hits, entry); } entry = entry->qnext; } for (i = 1; i <= keep; i++) { if (table[i].ptr == NULL) continue; ptr = (nsc_entry_t *)table[i].ptr; phdr = (nss_pheader_t *)ptr->buffer; if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS) /* * for positive cache, in addition to the packed * header size, allocate twice the size of the * existing result (in case the result grows * larger) plus 2K (for the file/compat backend to * process a possible large entry in the /etc files) */ bufsiz = phdr->data_off + 2 * phdr->data_len + 2048; else /* * for negative cache, allocate 8K buffer to * hold result in case the next lookup may * return something (in addition to the * packed header size) */ bufsiz = phdr->data_off + 8096; table[i].ptr = malloc(bufsiz); if (table[i].ptr == NULL) { (void) mutex_unlock(&nscdb->db_mutex); _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "memory allocation failure\n"); exit(1); } (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize); ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz; table[i].num = bufsiz; } (void) mutex_unlock(&nscdb->db_mutex); /* launch update thread for each keep hot entry */ for (i = keep; i > 0; i--) { if (table[i].ptr == NULL) continue; /* unused slot in table */ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: launching update\n", nscdb->name); largs = (nsc_lookup_args_t *)malloc(sizeof (*largs)); if (largs == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "memory allocation failure\n"); exit(1); } largs->buffer = table[i].ptr; largs->bufsize = table[i].num; largs->ctx = ctx; largs->nscdb = nscdb; if (launch_update(largs) < 0) exit(1); (void) sleep(interval); } /* * The update thread will handle freeing of buffer and largs. * Free the table here. */ free(table); } static int launch_update(nsc_lookup_args_t *in) { char *me = "launch_update"; int errnum; errnum = thr_create(NULL, 0, do_update, in, 0|THR_DETACHED, NULL); if (errnum != 0) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "%s: thread creation failure (%d)\n", in->nscdb->name, errnum); return (-1); } return (0); } static void * do_update(void *arg) { nsc_lookup_args_t *in = arg; nss_pheader_t *phdr = (nss_pheader_t *)in->buffer; (void) thr_setname(thr_self(), "do_update"); /* update the length of the data buffer */ phdr->data_len = phdr->pbufsiz - phdr->data_off; (void) lookup_int(in, UPDATEBIT); if (in->buffer) free(in->buffer); free(in); return (NULL); } /* * Invalidate cache */ void nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs) { int i; char *me = "nsc_invalidate"; if (ctx) { ctx_invalidate(ctx); return; } if (dbname) { if ((i = get_cache_idx(dbname)) == -1) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: invalid cache name\n", dbname); return; } if ((ctx = cache_ctx_p[i]) == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) (me, "%s: no cache context found\n", dbname); return; } ctx_invalidate(ctx); return; } if (ctxs == NULL) ctxs = cache_ctx_p; for (i = 0; i < CACHE_CTX_COUNT; i++) { if (ctxs[i] != NULL) ctx_invalidate(ctxs[i]); } } /* * Invalidate cache by context */ static void ctx_invalidate(nsc_ctx_t *ctx) { int i; nsc_entry_t *entry; char *me = "ctx_invalidate"; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: invalidate cache\n", ctx->dbname); for (i = 0; i < ctx->db_count; i++) { if (ctx->nsc_db[i] == NULL) continue; (void) mutex_lock(&ctx->nsc_db[i]->db_mutex); entry = ctx->nsc_db[i]->qtail; while (entry != NULL) { /* leave pending calls alone */ if (!(entry->stats.status & ST_PENDING)) entry->stats.status = ST_DISCARD; entry = entry->qnext; } (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex); } (void) mutex_lock(&ctx->stats_mutex); ctx->stats.invalidate_count++; (void) mutex_unlock(&ctx->stats_mutex); } /* * Free nsc_entry_t * * Pre-reqs: * nscdb->db_mutex lock must be held before calling this function */ static void delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) { uint_t hash; avl_remove(&nscdb->tree, entry); HASH_REMOVE(nscdb, entry, hash, nscd_false); queue_remove(nscdb, entry); if (entry->buffer != NULL) { free(entry->buffer); entry->buffer = NULL; } umem_cache_free(nsc_entry_cache, entry); (void) mutex_lock(&ctx->stats_mutex); ctx->stats.entries--; (void) mutex_unlock(&ctx->stats_mutex); } static nscd_rc_t lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry) { nsc_db_t *nscdb; nsc_ctx_t *ctx; uint_t hash; avl_index_t pos; ulong_t nentries; nsc_entry_t find_entry, *node; char *me = "lookup_cache"; ctx = largs->ctx; nscdb = largs->nscdb; /* set the search key */ find_entry.key = argp->key; /* struct copy (not deep) */ /* lookup the hash table ==> O(1) */ if (nscdb->htable) { *entry = hash_find(nscdb, &find_entry, &hash, nscd_true); if (*entry != NULL) { (void) queue_adjust(nscdb, *entry); return (NSCD_SUCCESS); } } /* if not found, lookup the AVL tree ==> O(log n) */ *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos); if (*entry != NULL) { (void) queue_adjust(nscdb, *entry); /* move it to the hash table */ if (nscdb->htable) { if (nscdb->htable[hash] == NULL || (*entry)->stats.hits >= nscdb->htable[hash]->stats.hits) { nscdb->htable[hash] = *entry; } } return (NSCD_SUCCESS); } /* entry not found in the cache */ _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: cache miss\n", whoami); if (cfgp->avoid_ns == nscd_true) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: avoid name service\n", whoami); return (NSCD_DB_ENTRY_NOT_FOUND); } /* allocate memory for new entry (stub) */ *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache, UMEM_DEFAULT); if (*entry == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "%s: memory allocation failure\n", whoami); return (NSCD_NO_MEMORY); } (void) memset(*entry, 0, sizeof (**entry)); /* * Note that the actual data for the key is stored within * the largs->buffer (input buffer to nsc_lookup). * find_entry.key only contains pointers to this data. * * If largs->buffer will be re-allocated by nss_psearch * then (*entry)->key will have dangling pointers. * In such case, the following assignment needs to be * replaced by code that duplicates the key. */ (*entry)->key = find_entry.key; /* * Add the entry to the cache. */ avl_insert(&nscdb->tree, *entry, pos); /* O(log n) */ (void) queue_adjust(nscdb, *entry); /* constant */ if (nscdb->htable) /* constant */ nscdb->htable[hash] = *entry; (*entry)->stats.status = ST_NEW_ENTRY; (void) mutex_lock(&ctx->stats_mutex); nentries = ++(ctx->stats.entries); (void) mutex_unlock(&ctx->stats_mutex); /* Have we exceeded max entries ? */ if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: maximum entries exceeded -- " "deleting least recently used entry\n", whoami); node = nscdb->qhead; while (node != NULL && node != *entry) { if (node->stats.status == ST_DISCARD || !(node->stats.status & ST_PENDING)) { delete_entry(nscdb, ctx, node); break; } node = node->qprev; } /* * It's okay if we were not able to find one to delete. * The reaper (when invoked) will return the cache to a * safe level. */ } return (NSCD_SUCCESS); } static void * reaper(void *arg) { nsc_ctx_t *ctx = arg; uint_t ttl, extra_sleep, total_sleep, intervals; uint_t nodes_per_interval, seconds_per_interval; ulong_t nsc_entries; char *me = "reaper"; (void) thr_setname(thr_self(), me); for (;;) { (void) mutex_lock(&ctx->stats_mutex); nsc_entries = ctx->stats.entries; (void) mutex_unlock(&ctx->stats_mutex); (void) rw_rdlock(&ctx->cfg_rwlp); ttl = ctx->cfg.pos_ttl; (void) rw_unlock(&ctx->cfg_rwlp); if (nsc_entries == 0) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: nothing to reap\n", ctx->dbname); /* sleep for atleast 60 seconds */ if (ttl < 60) ttl = 60; _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: sleep %d\n", ctx->dbname, ttl); (void) sleep(ttl); continue; } if (ttl < 32) ttl = 32; if (ttl > (1<<28)) ttl = 1<<28; /* * minimum nodes_per_interval = 256 or 1<<8 * maximum nodes_per_interval = nsc_entries * minimum seconds_per_interval = 32 or 1<<5 * maximum_seconds_per_interval = ttl */ if (nsc_entries <= ttl) { intervals = (nsc_entries >> 8) + 1; seconds_per_interval = ttl / intervals; nodes_per_interval = 256; } else { intervals = (ttl >> 5) + 1; seconds_per_interval = 32; nodes_per_interval = nsc_entries / intervals; if (nodes_per_interval < 256) nodes_per_interval = 256; } _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: total entries = %d, " "seconds per interval = %d, " "nodes per interval = %d\n", ctx->dbname, nsc_entries, seconds_per_interval, nodes_per_interval); total_sleep = reap_cache(ctx, nodes_per_interval, seconds_per_interval); extra_sleep = 1 + ttl - total_sleep; if (extra_sleep > 0) (void) sleep(extra_sleep); } return (NULL); } static uint_t reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval, uint_t seconds_per_interval) { uint_t nodes_togo, total_sleep; time_t now; nsc_entry_t *node, *next_node; nsc_db_t *nscdb; uint_t primes[] = {_NSC_HTSIZE_PRIMES}; ulong_t count, nentries, maxentries; int i, slot, value, newhtsize; char *me = "reap_cache"; count = 0; total_sleep = 0; nodes_togo = nodes_per_interval; now = time(NULL); for (i = 0; i < ctx->db_count; i++) { nscdb = ctx->nsc_db[i]; (void) mutex_lock(&nscdb->db_mutex); nscdb->reap_node = nscdb->qtail; while (nscdb->reap_node != NULL) { if (nodes_togo == 0) { (void) mutex_unlock(&nscdb->db_mutex); (void) sleep(seconds_per_interval); total_sleep += seconds_per_interval; nodes_togo = nodes_per_interval; now = time(NULL); (void) mutex_lock(&nscdb->db_mutex); } /* delete ST_DISCARD and expired nodes */ if ((node = nscdb->reap_node) == NULL) break; if (node->stats.status == ST_DISCARD || (!(node->stats.status & ST_PENDING) && node->stats.timestamp < now)) { /* * Delete entry if its discard flag is * set OR if it has expired. Entries * with pending updates are not * deleted. * nscdb->reap_node will be adjusted * by delete_entry() */ delete_entry(nscdb, ctx, node); count++; } else { nscdb->reap_node = node->qnext; } nodes_togo--; } if (nscdb->htsize == 0) { (void) mutex_unlock(&nscdb->db_mutex); continue; } /* * Dynamic adjustment of hash table size. * * Hash table size is roughly 1/8th of the * total entries. However the size is changed * only when the number of entries double or * reduced by half */ nentries = avl_numnodes(&nscdb->tree); for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE; slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value; value = (value << 1) + 1, slot++) ; if (nscdb->hash_type == nsc_ht_power2) newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot; else newhtsize = primes[slot]; /* Recommended size is same as the current size. Done */ if (nscdb->htsize == newhtsize) { (void) mutex_unlock(&nscdb->db_mutex); continue; } _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: resizing hash table from %d to %d\n", nscdb->name, nscdb->htsize, newhtsize); /* * Dump old hashes because it would be time * consuming to rehash them. */ (void) free(nscdb->htable); nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable))); if (nscdb->htable == NULL) { _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) (me, "%s: memory allocation failure\n", nscdb->name); /* -1 to try later */ nscdb->htsize = -1; } else { nscdb->htsize = newhtsize; } (void) mutex_unlock(&nscdb->db_mutex); } _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: reaped %lu entries\n", ctx->dbname, count); /* * if cache is almost full then reduce it to a safe level by * evicting LRU entries */ (void) rw_rdlock(&ctx->cfg_rwlp); maxentries = ctx->cfg.maxentries; (void) rw_unlock(&ctx->cfg_rwlp); /* No limit on number of entries. Done */ if (maxentries == 0) goto out; (void) mutex_lock(&ctx->stats_mutex); nentries = ctx->stats.entries; (void) mutex_unlock(&ctx->stats_mutex); /* what is the percentage of cache used ? */ value = (nentries * 100) / maxentries; if (value < _NSC_EVICTION_START_LEVEL) goto out; /* * cache needs to be reduced to a safe level */ value -= _NSC_EVICTION_SAFE_LEVEL; for (i = 0, count = 0; i < ctx->db_count; i++) { /* * Reduce each subcache by 'value' percent */ nscdb = ctx->nsc_db[i]; (void) mutex_lock(&nscdb->db_mutex); nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100; /* Start from LRU entry i.e queue head */ next_node = nscdb->qhead; while (nodes_togo > 0 && next_node != NULL) { node = next_node; next_node = next_node->qprev; if (node->stats.status == ST_DISCARD || !(node->stats.status & ST_PENDING)) { /* Leave nodes with pending updates alone */ delete_entry(nscdb, ctx, node); count++; nodes_togo--; } } (void) mutex_unlock(&nscdb->db_mutex); } _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count); out: return (total_sleep); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _NSCD_H #define _NSCD_H /* * This is a private header file. Applications should not directly include * this file. */ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "getxby_door.h" #include "nscd_common.h" #include "nscd_config.h" /* * OR'D in by server to call self for updates */ #define UPDATEBIT (1<<30) #define MASKUPDATEBIT(a) ((~UPDATEBIT)&(a)) /* * debug levels */ #define DBG_OFF 0 #define DBG_CANT_FIND 2 #define DBG_NETLOOKUPS 4 #define DBG_ALL 6 /* * Max size name we allow to be passed to avoid * buffer overflow problems */ #define NSCDMAXNAMELEN 255 /* * cached entry status */ #define ST_UPDATE_PENDING 0x1 #define ST_LOOKUP_PENDING 0x2 #define ST_PENDING (ST_LOOKUP_PENDING | ST_UPDATE_PENDING) #define ST_NEW_ENTRY 0x4 #define ST_DISCARD 0x8 /* * Cache eviction start and stop levels */ #define _NSC_EVICTION_START_LEVEL 90 #define _NSC_EVICTION_SAFE_LEVEL 80 /* * other internal constants */ #define _NSC_MAX_DB 3 #define _NSC_PUBLIC_ACCESS -1 #define _NSC_FILE_CHECK_TIME 0 /* check always for backwards compat */ /* * Macros used for logging purposes */ #define yes_no(flag) (flag == nscd_true)?"yes":"no" #define check_null(str) (str)?str:"" /* * Macros used by compare routines */ #define _NSC_INT_KEY_CMP(n1, n2) \ (n1 > n2)?1:((n1 == n2)?0:-1) #define _NSC_GET_HITRATE(sp) \ sp->hitrate = sp->pos_misses + sp->neg_misses + \ sp->pos_hits + sp->neg_hits; \ if (sp->hitrate > 0.0) \ sp->hitrate = (100.0 * \ ((double)sp->pos_hits + \ (double)sp->neg_hits)) / sp->hitrate; /* * nsc_lookup action */ typedef enum { _NSC_NSLOOKUP = 0, _NSC_WAIT, _NSC_USECACHED } nsc_action_t; /* * What each entry in the nameserver cache looks like. */ typedef struct nsc_entry_stat { uint_t hits; /* number of hits */ uint8_t status; /* activity status */ time_t timestamp; /* expiry time */ int refcount; /* reference count */ } nsc_entry_stat_t; typedef struct nsc_entry { avl_node_t avl_link; /* libavl requirement */ struct nsc_entry *qnext; /* next on pqueue */ struct nsc_entry *qprev; /* prev on pqueue */ nsc_entry_stat_t stats; /* entry's statistics */ nss_XbyY_key_t key; /* entry's key */ void *buffer; /* data buffer */ size_t bufsize; /* data buffer length */ } nsc_entry_t; typedef struct nsc_keephot { void *ptr; uint_t num; } nsc_keephot_t; /* * Structure to handle waiting for pending name service requests */ typedef struct waiter { cond_t w_waitcv; uint8_t w_signaled; nsc_entry_t *w_key; struct waiter *w_next, *w_prev; } waiter_t; /* * Macros used by hash table * * _NSC_HTSIZE_PRIMES are prime numbers that are used as hash table * sizes when hash table type is nsc_ht_prime. For hash tables of * type nsc_ht_power2, the size is automatically calculated. * Number of primes listed below is _NSC_HTSIZE_NUM_SLOTS + 1. * Each number (except the first) is a prime closest to a * power of 2 in increasing order. Ex: 509 is the closest prime to * 512 (2**9), 1021 is closest to 1024 (2**10), and so on. * The first prime is chosen as 211 for historical reasons. */ #define _NSC_INIT_HTSIZE_PRIME 211 #define _NSC_INIT_HTSIZE_POWER2 256 #define _NSC_INIT_HTSIZE_SLOT_VALUE 2896 #define _NSC_HTSIZE_NUM_SLOTS 10 #define _NSC_HTSIZE_PRIMES 211, 509, 1021, 2053, 4099, 8191, \ 16381, 32771, 65537, 131071, 262147 #define _NSC_DB_CES_KEY(ptr) \ ((ptr)->db_type == nsc_key_ces) #define _NSC_DB_CIS_KEY(ptr) \ ((ptr)->db_type == nsc_key_cis) #define _NSC_DB_STR_KEY(ptr) \ _NSC_DB_CES_KEY(ptr) || _NSC_DB_CIS_KEY(ptr) #define _NSC_DB_INT_KEY(ptr) \ ((ptr)->db_type == nsc_key_int) /* * cache backend param group (global) */ #define NSCD_CFG_GROUP_INFO_GLOBAL_CACHE {1, 0x0001} typedef struct nscd_cfg_global_cache { nscd_cfg_group_info_t gi; /* config requirement */ nscd_bool_t enable; } nscd_cfg_global_cache_t; #define NSCD_CFG_GLOBAL_CACHE_DEFAULTS \ { NSCD_CFG_GROUP_INFO_GLOBAL_CACHE, nscd_true } /* * cache backend param group (per database) */ #define NSCD_CFG_GROUP_INFO_CACHE {12, 0x0fff} typedef struct nscd_cfg_cache { nscd_cfg_group_info_t gi; /* config requirement */ nscd_bool_t enable; /* if false return NOSERVER */ nscd_bool_t per_user; /* if true per user access */ nscd_bool_t avoid_ns; /* if true avoid name service */ nscd_bool_t check_files; /* if true check file */ int check_interval; /* check interval */ int pos_ttl; /* time to live for +ve entries */ int neg_ttl; /* time to live for -ve entries */ int keephot; /* keep hot count */ int hint_size; /* size to return for a GETHINTS */ ulong_t maxentries; /* maximum entries allowed */ int suggestedsize; /* obsolete */ nscd_bool_t old_data_ok; /* obsolete */ } nscd_cfg_cache_t; #define NSCD_CFG_CACHE_DEFAULTS \ { \ NSCD_CFG_GROUP_INFO_CACHE, \ nscd_true, nscd_false, nscd_false, nscd_true, \ _NSC_FILE_CHECK_TIME, 600, 10, 0, 1 << 11, 0, \ 0, nscd_false \ } /* * cache backend stat group (per database) */ #define NSCD_CFG_STAT_GROUP_INFO_CACHE {9, 0x01ff} typedef struct nscd_cfg_stat_cache { nscd_cfg_group_info_t gi; /* config requirement */ ulong_t pos_hits; /* hits on +ve entries */ ulong_t neg_hits; /* hits on -ve entries */ ulong_t pos_misses; /* misses on +ve entries */ ulong_t neg_misses; /* misses on -ve entries */ ulong_t entries; /* count of cache entries */ ulong_t drop_count; /* cache queries dropped */ ulong_t wait_count; /* cache queries queued */ ulong_t invalidate_count; /* count for cache invalidation */ double hitrate; /* computed from other fields */ } nscd_cfg_stat_cache_t; typedef struct nsc_db { /* * Data */ avl_tree_t tree; nsc_entry_t **htable; nsc_entry_t *qhead; nsc_entry_t *qtail; nsc_entry_t *reap_node; int callnumber; int dbop; char *name; mutex_t db_mutex; waiter_t db_wait; /* lookup wait CV */ int htsize; enum hash_type { nsc_ht_default = 0, nsc_ht_prime = 1, nsc_ht_power2 = 2 } hash_type; enum db_type { nsc_key_ces = 0, nsc_key_cis = 1, nsc_key_int = 2, nsc_key_other = 3 } db_type; /* * Methods */ uint_t (*gethash)(nss_XbyY_key_t *, int); int (*compar)(const void *, const void *); void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *); /* * Config */ nscd_cfg_cache_t cfg; time_t cfg_mtime; } nsc_db_t; typedef struct nsc_ctx { char *dbname; /* cache name */ nscd_cfg_stat_cache_t stats; /* statistics */ nscd_cfg_cache_t cfg; /* configs */ time_t cfg_mtime; /* config last modified time */ rwlock_t cfg_rwlp; /* config rwlock */ mutex_t stats_mutex; /* stats mutex */ mutex_t file_mutex; /* file mutex */ time_t file_mtime; /* file last modified time */ time_t file_chktime; /* file last checked time */ off_t file_size; /* file size at last check */ ino_t file_ino; /* file inode at last check */ const char *file_name; /* filename for check_files */ int db_count; /* number of caches, max _NSC_MAX_DB */ nsc_db_t *nsc_db[_NSC_MAX_DB]; /* caches */ sema_t throttle_sema; /* throttle lookups */ sema_t revalidate_sema; /* revalidation threads */ nscd_bool_t revalidate_on; /* reval. thread started */ nscd_bool_t reaper_on; /* reaper thread started */ } nsc_ctx_t; typedef struct nsc_lookup_args { nsc_ctx_t *ctx; nsc_db_t *nscdb; void *buffer; size_t bufsize; } nsc_lookup_args_t; #define CACHE_CTX_COUNT 19 /* Context initialization */ extern void passwd_init_ctx(nsc_ctx_t *); extern void group_init_ctx(nsc_ctx_t *); extern void host_init_ctx(nsc_ctx_t *); extern void ipnode_init_ctx(nsc_ctx_t *); extern void exec_init_ctx(nsc_ctx_t *); extern void prof_init_ctx(nsc_ctx_t *); extern void user_init_ctx(nsc_ctx_t *); extern void ether_init_ctx(nsc_ctx_t *); extern void rpc_init_ctx(nsc_ctx_t *); extern void proto_init_ctx(nsc_ctx_t *); extern void net_init_ctx(nsc_ctx_t *); extern void bootp_init_ctx(nsc_ctx_t *); extern void auth_init_ctx(nsc_ctx_t *); extern void serv_init_ctx(nsc_ctx_t *); extern void netmask_init_ctx(nsc_ctx_t *); extern void printer_init_ctx(nsc_ctx_t *); extern void project_init_ctx(nsc_ctx_t *); extern void tnrhtp_init_ctx(nsc_ctx_t *); extern void tnrhdb_init_ctx(nsc_ctx_t *); /* Functions used to throttle threads */ extern int nscd_wait(nsc_ctx_t *, nsc_db_t *, nsc_entry_t *); extern int nscd_signal(nsc_ctx_t *, nsc_db_t *, nsc_entry_t *); /* Cache creation and initialization */ extern nscd_rc_t init_cache(); extern nsc_db_t *make_cache(enum db_type, int, char *, int (*compar) (const void *, const void *), void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *), uint_t (*gethash)(nss_XbyY_key_t *, int), enum hash_type, int); /* Cache backend lookup */ extern void nsc_lookup(nsc_lookup_args_t *, int); /* Cache backend info */ extern void nsc_info(nsc_ctx_t *, char *, nscd_cfg_cache_t cfg[], nscd_cfg_stat_cache_t stats[]); #ifdef NSCD_DEBUG extern int nsc_dump(char *, int); #endif /* NSCD_DEBUG */ /* Cache invalidate */ extern void nsc_invalidate(nsc_ctx_t *, char *, nsc_ctx_t **); /* Keep hot functions */ extern nsc_keephot_t *maken(int); extern void *insertn(nsc_keephot_t *, uint_t, void *); /* hash related routines */ extern uint_t cis_gethash(const char *, int); extern uint_t ces_gethash(const char *, int); extern uint_t db_gethash(const void *, int, int); extern void leave(int n); extern int get_cache_idx(char *); #ifdef __cplusplus } #endif #endif /* _NSCD_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getauth* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define NSC_NAME_AUTHATTR_BYNAME "getauthnam" void auth_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_AUTHATTR; ctx->file_name = "/etc/security/auth_attr"; ctx->db_count = 1; nam_db = make_cache(nsc_key_ces, NSS_DBOP_AUTHATTR_BYNAME, NSC_NAME_AUTHATTR_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle bootparams* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define NSC_NAME_BOOTPARAMS_BYNAME "bootparams" void bootp_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_BOOTPARAMS; ctx->file_name = "/etc/bootparams"; ctx->db_count = 1; nam_db = make_cache(nsc_key_cis, NSS_DBOP_BOOTPARAMS_BYNAME, NSC_NAME_BOOTPARAMS_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle ether_*to* calls in nscd */ #include #include #include #include #include #include "cache.h" #define host_db ctx->nsc_db[0] #define addr_db ctx->nsc_db[1] #define NSC_NAME_ETHERS_HOSTTON "ether_hostton" #define NSC_NAME_ETHERS_NTOHOST "ether_ntohost" static void ether_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int ether_compar(const void *, const void *); static uint_t ether_gethash(nss_XbyY_key_t *, int); void ether_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_ETHERS; ctx->file_name = "/etc/ethers"; ctx->db_count = 2; host_db = make_cache(nsc_key_cis, NSS_DBOP_ETHERS_HOSTTON, NSC_NAME_ETHERS_HOSTTON, NULL, NULL, NULL, nsc_ht_default, -1); addr_db = make_cache(nsc_key_other, NSS_DBOP_ETHERS_NTOHOST, NSC_NAME_ETHERS_NTOHOST, ether_compar, ether_getlogstr, ether_gethash, nsc_ht_default, -1); } static int ether_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; if (ether_cmp(e1->key.ether, e2->key.ether) != 0) { res = memcmp(e1->key.ether, e2->key.ether, sizeof (struct ether_addr)); return (_NSC_INT_KEY_CMP(res, 0)); } return (0); } static uint_t ether_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(key->ether, sizeof (struct ether_addr), htsize)); } static void ether_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { struct ether_addr *e; e = (struct ether_addr *)argp->key.ether; (void) snprintf(whoami, len, "%s [key=%x:%x:%x:%x:%x:%x]", name, e->ether_addr_octet[0], e->ether_addr_octet[1], e->ether_addr_octet[2], e->ether_addr_octet[3], e->ether_addr_octet[4], e->ether_addr_octet[5]); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getexec* calls in nscd */ #include #include #include "cache.h" static int execattr_compar(const void *, const void *); static uint_t execattr_gethash(nss_XbyY_key_t *, int); static void execattr_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); #define nam_db ctx->nsc_db[0] #define id_db ctx->nsc_db[1] #define nam_id_db ctx->nsc_db[2] #define NSC_NAME_EXECATTR_BYNAME "execattr_byname" #define NSC_NAME_EXECATTR_BYID "execattr_byid" #define NSC_NAME_EXECATTR_BYNAMEID "execattr_bynameid" void exec_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_EXECATTR; ctx->file_name = "/etc/security/exec_attr"; ctx->db_count = 3; nam_db = make_cache(nsc_key_other, NSS_DBOP_EXECATTR_BYNAME, NSC_NAME_EXECATTR_BYNAME, execattr_compar, execattr_getlogstr, execattr_gethash, nsc_ht_default, -1); id_db = make_cache(nsc_key_other, NSS_DBOP_EXECATTR_BYID, NSC_NAME_EXECATTR_BYID, execattr_compar, execattr_getlogstr, execattr_gethash, nsc_ht_default, -1); nam_id_db = make_cache(nsc_key_other, NSS_DBOP_EXECATTR_BYNAMEID, NSC_NAME_EXECATTR_BYNAMEID, execattr_compar, execattr_getlogstr, execattr_gethash, nsc_ht_default, -1); } #define EXEC_STR_CMP(s1, s2) \ if ((a = s1) == NULL) \ a = z; \ if ((b = s2) == NULL) \ b = z; \ res = strcmp(a, b); \ if (res != 0) \ return (res > 0 ? 1 : -1); static int execattr_compar(const void *n1, const void *n2) { nsc_entry_t *e1 = (nsc_entry_t *)n1; nsc_entry_t *e2 = (nsc_entry_t *)n2; _priv_execattr *ep1 = (_priv_execattr *)e1->key.attrp; _priv_execattr *ep2 = (_priv_execattr *)e2->key.attrp; int res; const char *a, *b, *z = ""; /* compare name */ EXEC_STR_CMP(ep1->name, ep2->name); /* compare policy */ EXEC_STR_CMP(ep1->policy, ep2->policy); /* compare type */ EXEC_STR_CMP(ep1->type, ep2->type); /* compare id */ EXEC_STR_CMP(ep1->id, ep2->id); /* compare search flag */ return (_NSC_INT_KEY_CMP(ep1->search_flag, ep2->search_flag)); } static uint_t execattr_gethash(nss_XbyY_key_t *key, int htsize) { _priv_execattr *ep = key->attrp; char keys[1024]; int len; len = snprintf(keys, sizeof (keys), "%s:%s:%s:%s:%d", ep->name ? ep->name : "", ep->type ? ep->type : "", ep->id ? ep->id : "", ep->policy ? ep->policy : "", ep->search_flag); return (db_gethash(keys, len, htsize)); } static void execattr_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { _priv_execattr *ep = argp->key.attrp; (void) snprintf(whoami, len, "%s [name=%s:type=%s:id=%s:policy=%s:flags=%d]", name, check_null(ep->name), check_null(ep->type), check_null(ep->id), check_null(ep->policy), ep->search_flag); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getgr* calls in nscd */ #include #include "cache.h" #define nam_db ctx->nsc_db[0] #define gid_db ctx->nsc_db[1] #define NSC_NAME_GROUP_BYNAME "getgrnam" #define NSC_NAME_GROUP_BYGID "getgrgid" static void grgid_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int grgid_compar(const void *, const void *); static uint_t grgid_gethash(nss_XbyY_key_t *, int); void group_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_GROUP; ctx->file_name = "/etc/group"; ctx->db_count = 2; nam_db = make_cache(nsc_key_ces, NSS_DBOP_GROUP_BYNAME, NSC_NAME_GROUP_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); gid_db = make_cache(nsc_key_other, NSS_DBOP_GROUP_BYGID, NSC_NAME_GROUP_BYGID, grgid_compar, grgid_getlogstr, grgid_gethash, nsc_ht_default, -1); } static void grgid_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.gid); } static int grgid_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; return (_NSC_INT_KEY_CMP(e1->key.gid, e2->key.gid)); } static uint_t grgid_gethash(nss_XbyY_key_t *key, int htsize) { return ((uint_t)key->gid % htsize); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle gethost* calls in nscd */ #include #include #include #include #include #include #include "cache.h" #define hnam_db ctx->nsc_db[0] #define addr_db ctx->nsc_db[1] #define NSC_NAME_HOSTS_BYNAME "gethostbyname" #define NSC_NAME_HOSTS_BYADDR "gethostbyaddr" static int hostaddr_compar(const void *, const void *); static uint_t hostaddr_gethash(nss_XbyY_key_t *, int); static void hostaddr_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); void host_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_HOSTS; ctx->db_count = 2; ctx->file_name = "/etc/inet/hosts"; hnam_db = make_cache(nsc_key_cis, NSS_DBOP_HOSTS_BYNAME, NSC_NAME_HOSTS_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); addr_db = make_cache(nsc_key_other, NSS_DBOP_HOSTS_BYADDR, NSC_NAME_HOSTS_BYADDR, hostaddr_compar, hostaddr_getlogstr, hostaddr_gethash, nsc_ht_default, -1); } static void hostaddr_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { char addr[INET6_ADDRSTRLEN]; if (inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr, addr, sizeof (addr)) == NULL) { (void) snprintf(whoami, len, "%s", name); } else { (void) snprintf(whoami, len, "%s [key=%s, addrtype=%d]", name, addr, argp->key.hostaddr.type); } } static int hostaddr_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; l1 = e1->key.hostaddr.len; l2 = e2->key.hostaddr.len; res = memcmp(e1->key.hostaddr.addr, e2->key.hostaddr.addr, (l2 > l1)?l1:l2); return ((res) ? _NSC_INT_KEY_CMP(res, 0) : _NSC_INT_KEY_CMP(l1, l2)); } static uint_t hostaddr_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(key->hostaddr.addr, key->hostaddr.len, htsize)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getnet* calls in nscd */ #include #include #include #include #include #include #include "cache.h" #define nam_db ctx->nsc_db[0] #define addr_db ctx->nsc_db[1] #define NSC_NAME_NETWORKS_BYNAME "getnetbyname" #define NSC_NAME_NETWORKS_BYADDR "getnetbyaddr" static int netaddr_compar(const void *, const void *); static uint_t netaddr_gethash(nss_XbyY_key_t *, int); static void netaddr_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); void net_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_NETWORKS; ctx->db_count = 2; ctx->file_name = "/etc/inet/networks"; nam_db = make_cache(nsc_key_ces, NSS_DBOP_NETWORKS_BYNAME, NSC_NAME_NETWORKS_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); addr_db = make_cache(nsc_key_other, NSS_DBOP_NETWORKS_BYADDR, NSC_NAME_NETWORKS_BYADDR, netaddr_compar, netaddr_getlogstr, netaddr_gethash, nsc_ht_default, -1); } static void netaddr_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { if (argp->key.netaddr.type == AF_INET) { uint32_t net; uchar_t *up; net = htonl(argp->key.netaddr.net); up = (uchar_t *)&net; if (up[0]) (void) snprintf(whoami, len, "%s [key=%d.%d.%d.%d]", name, up[0], up[1], up[2], up[3]); else if (up[1]) (void) snprintf(whoami, len, "%s [key=%d.%d.%d]", name, up[1], up[2], up[3]); else if (up[2]) (void) snprintf(whoami, len, "%s [key=%d.%d]", name, up[2], up[3]); else (void) snprintf(whoami, len, "%s [key=%d]", name, up[3]); } else { (void) snprintf(whoami, len, "%s [key=%d, %d]", name, argp->key.netaddr.net, argp->key.netaddr.type); } } static int netaddr_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; if (e1->key.netaddr.type > e2->key.netaddr.type) return (1); else if (e1->key.netaddr.type < e2->key.netaddr.type) return (-1); return (_NSC_INT_KEY_CMP(e1->key.netaddr.net, e2->key.netaddr.net)); } static uint_t netaddr_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(&key->netaddr.net, sizeof (key->netaddr.net), htsize)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getnetmask* calls in nscd */ #include "cache.h" #define key_db ctx->nsc_db[0] #define NSC_NAME_NETMASKS_BYNET "getnetmaskbykey" void netmask_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_NETMASKS; ctx->db_count = 1; ctx->file_name = "/etc/inet/netmasks"; key_db = make_cache(nsc_key_ces, NSS_DBOP_NETMASKS_BYNET, NSC_NAME_NETMASKS_BYNET, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * 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 #include #include #include #include #include #include #include #include "cache.h" static int ipaddr_compar(const void *, const void *); static uint_t ipaddr_gethash(nss_XbyY_key_t *, int); static void ipaddr_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int ipname_compar(const void *, const void *); static uint_t ipname_gethash(nss_XbyY_key_t *, int); static void ipname_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); #define nnam_db ctx->nsc_db[0] #define addr_db ctx->nsc_db[1] #define NSC_NAME_IPNODES_BYNAME "getipnodebyname" #define NSC_NAME_IPNODES_BYADDR "getipnodebyaddr" void ipnode_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_IPNODES; ctx->file_name = "/etc/inet/ipnodes"; ctx->db_count = 2; nnam_db = make_cache(nsc_key_other, NSS_DBOP_IPNODES_BYNAME, NSC_NAME_IPNODES_BYNAME, ipname_compar, ipname_getlogstr, ipname_gethash, nsc_ht_default, -1); addr_db = make_cache(nsc_key_other, NSS_DBOP_IPNODES_BYADDR, NSC_NAME_IPNODES_BYADDR, ipaddr_compar, ipaddr_getlogstr, ipaddr_gethash, nsc_ht_default, -1); } static int ipaddr_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; if (e1->key.hostaddr.type > e2->key.hostaddr.type) return (1); else if (e1->key.hostaddr.type < e2->key.hostaddr.type) return (-1); l1 = e1->key.hostaddr.len; l2 = e2->key.hostaddr.len; res = memcmp(e1->key.hostaddr.addr, e2->key.hostaddr.addr, (l2 > l1)?l1:l2); return ((res) ? _NSC_INT_KEY_CMP(res, 0) : _NSC_INT_KEY_CMP(l1, l2)); } static uint_t ipaddr_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(key->hostaddr.addr, key->hostaddr.len, htsize)); } static void ipaddr_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { char addr[INET6_ADDRSTRLEN]; if (inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr, addr, sizeof (addr)) == NULL) { (void) snprintf(whoami, len, "%s", name); } else { (void) snprintf(whoami, len, "%s [key=%s addrtype=%d]", name, addr, argp->key.hostaddr.type); } } static int ipname_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; if (e1->key.ipnode.af_family > e2->key.ipnode.af_family) return (1); else if (e1->key.ipnode.af_family < e2->key.ipnode.af_family) return (-1); l1 = strlen(e1->key.ipnode.name); l2 = strlen(e2->key.ipnode.name); res = strncasecmp(e1->key.ipnode.name, e2->key.ipnode.name, (l1 > l2)?l1:l2); return (_NSC_INT_KEY_CMP(res, 0)); } static uint_t ipname_gethash(nss_XbyY_key_t *key, int htsize) { return (cis_gethash(key->ipnode.name, htsize)); } static void ipname_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%s:af=%d:flags=%d]", name, argp->key.ipnode.name, argp->key.ipnode.af_family, argp->key.ipnode.flags); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getprinter* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define NSC_NAME_PRINTERS_BYNAME "getprinterbyname" void printer_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_PRINTERS; ctx->file_name = "/etc/printers.conf"; ctx->db_count = 1; nam_db = make_cache(nsc_key_ces, NSS_DBOP_PRINTERS_BYNAME, NSC_NAME_PRINTERS_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getprof* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define NSC_NAME_PROFATTR_BYNAME "getprofnam" void prof_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_PROFATTR; ctx->file_name = "/etc/security/prof_attr"; ctx->db_count = 1; nam_db = make_cache(nsc_key_ces, NSS_DBOP_PROFATTR_BYNAME, NSC_NAME_PROFATTR_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getproj* calls in nscd */ #include #include "cache.h" #define nam_db ctx->nsc_db[0] #define pid_db ctx->nsc_db[1] #define NSC_NAME_PROJECT_BYNAME "getprojbyname" #define NSC_NAME_PROJECT_BYID "getprojbyid" static void projid_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int projid_compar(const void *, const void *); static uint_t projid_gethash(nss_XbyY_key_t *, int); void project_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_PROJECT; ctx->file_name = "/etc/project"; ctx->db_count = 2; nam_db = make_cache(nsc_key_ces, NSS_DBOP_PROJECT_BYNAME, NSC_NAME_PROJECT_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); pid_db = make_cache(nsc_key_other, NSS_DBOP_PROJECT_BYID, NSC_NAME_PROJECT_BYID, projid_compar, projid_getlogstr, projid_gethash, nsc_ht_default, -1); } static int projid_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; return (_NSC_INT_KEY_CMP(e1->key.projid, e2->key.projid)); } static uint_t projid_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(&key->projid, sizeof (key->projid), htsize)); } static void projid_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.projid); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getprotoby* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define num_db ctx->nsc_db[1] #define NSC_NAME_PROTOCOLS_BYNAME "getprotobyname" #define NSC_NAME_PROTOCOLS_BYNUMBER "getprotobynumber" void proto_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_PROTOCOLS; ctx->file_name = "/etc/inet/protocols"; ctx->db_count = 2; nam_db = make_cache(nsc_key_ces, NSS_DBOP_PROTOCOLS_BYNAME, NSC_NAME_PROTOCOLS_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); num_db = make_cache(nsc_key_int, NSS_DBOP_PROTOCOLS_BYNUMBER, NSC_NAME_PROTOCOLS_BYNUMBER, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getpw* calls in nscd */ #include #include "cache.h" #define nam_db ctx->nsc_db[0] #define uid_db ctx->nsc_db[1] #define NSC_NAME_PASSWD_BYNAME "getpwnam" #define NSC_NAME_PASSWD_BYUID "getpwuid" static void pwuid_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int pwuid_compar(const void *, const void *); static uint_t pwuid_gethash(nss_XbyY_key_t *, int); void passwd_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_PASSWD; ctx->file_name = "/etc/passwd"; ctx->db_count = 2; ctx->cfg.pos_ttl = 600; nam_db = make_cache(nsc_key_ces, NSS_DBOP_PASSWD_BYNAME, NSC_NAME_PASSWD_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); uid_db = make_cache(nsc_key_other, NSS_DBOP_PASSWD_BYUID, NSC_NAME_PASSWD_BYUID, pwuid_compar, pwuid_getlogstr, pwuid_gethash, nsc_ht_default, -1); } static int pwuid_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; return (_NSC_INT_KEY_CMP(e1->key.uid, e2->key.uid)); } static uint_t pwuid_gethash(nss_XbyY_key_t *key, int htsize) { return ((uint_t)key->uid % htsize); } static void pwuid_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.uid); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getrpcby* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define num_db ctx->nsc_db[1] #define NSC_NAME_RPC_BYNAME "getrpcbyname" #define NSC_NAME_RPC_BYNUMBER "getrpcbynumber" void rpc_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_RPC; ctx->file_name = "/etc/rpc"; ctx->db_count = 2; nam_db = make_cache(nsc_key_ces, NSS_DBOP_RPC_BYNAME, NSC_NAME_RPC_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); num_db = make_cache(nsc_key_int, NSS_DBOP_RPC_BYNUMBER, NSC_NAME_RPC_BYNUMBER, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getserv* calls in nscd */ #include #include "cache.h" #define name_db ctx->nsc_db[0] #define port_db ctx->nsc_db[1] #define NSC_NAME_SERVICES_BYNAME "getservbyname" #define NSC_NAME_SERVICES_BYPORT "getservbyport" static void servname_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int servname_compar(const void *, const void *); static uint_t servname_gethash(nss_XbyY_key_t *, int); static void servport_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); static int servport_compar(const void *, const void *); static uint_t servport_gethash(nss_XbyY_key_t *, int); void serv_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_SERVICES; ctx->file_name = "/etc/services"; ctx->db_count = 2; name_db = make_cache(nsc_key_other, NSS_DBOP_SERVICES_BYNAME, NSC_NAME_SERVICES_BYNAME, servname_compar, servname_getlogstr, servname_gethash, nsc_ht_default, -1); port_db = make_cache(nsc_key_other, NSS_DBOP_SERVICES_BYPORT, NSC_NAME_SERVICES_BYPORT, servport_compar, servport_getlogstr, servport_gethash, nsc_ht_default, -1); } static int servname_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; /* compare protocol */ if (e1->key.serv.proto == NULL && e2->key.serv.proto) return (-1); if (e1->key.serv.proto && e2->key.serv.proto == NULL) return (1); if (e1->key.serv.proto) { l1 = strlen(e1->key.serv.proto); l2 = strlen(e2->key.serv.proto); res = strncmp(e1->key.serv.proto, e2->key.serv.proto, (l1 > l2)?l1:l2); if (res > 0) return (1); if (res < 0) return (-1); } /* compare service name */ l1 = strlen(e1->key.serv.serv.name); l2 = strlen(e2->key.serv.serv.name); res = strncmp(e1->key.serv.serv.name, e2->key.serv.serv.name, (l1 > l2)?l1:l2); return (_NSC_INT_KEY_CMP(res, 0)); } static uint_t servname_gethash(nss_XbyY_key_t *key, int htsize) { return (ces_gethash(key->serv.serv.name, htsize)); } static void servname_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%s, %s]", name, argp->key.serv.serv.name, check_null(argp->key.serv.proto)); } static int servport_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; /* compare protocol */ if (e1->key.serv.proto == NULL && e2->key.serv.proto) return (-1); if (e1->key.serv.proto && e2->key.serv.proto == NULL) return (1); if (e1->key.serv.proto) { l1 = strlen(e1->key.serv.proto); l2 = strlen(e2->key.serv.proto); res = strncmp(e1->key.serv.proto, e2->key.serv.proto, (l1 > l2)?l1:l2); if (res > 0) return (1); if (res < 0) return (-1); } /* compare port */ return (_NSC_INT_KEY_CMP(e1->key.serv.serv.port, e2->key.serv.serv.port)); } static uint_t servport_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(&key->serv.serv.port, sizeof (key->serv.serv.port), htsize)); } static void servport_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%d, %s]", name, argp->key.serv.serv.port, check_null(argp->key.serv.proto)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle tsol_getrhbyaddr calls in nscd */ #include #include #include #include #include "cache.h" #define tsol_rh_db ctx->nsc_db[0] #define NSC_NAME_TSOL_RH_BYADDR "tsol_getrhbyaddr" static int tsol_rh_compar(const void *, const void *); static uint_t tsol_rh_gethash(nss_XbyY_key_t *, int); static void tsol_rh_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); void tnrhdb_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_TSOL_RH; ctx->db_count = 1; ctx->file_name = TNRHDB_PATH; tsol_rh_db = make_cache(nsc_key_other, NSS_DBOP_TSOL_RH_BYADDR, NSC_NAME_TSOL_RH_BYADDR, tsol_rh_compar, tsol_rh_getlogstr, tsol_rh_gethash, nsc_ht_default, -1); } static void tsol_rh_getlogstr(char *name, char *whoami, size_t len, nss_XbyY_args_t *argp) { (void) snprintf(whoami, len, "%s [key=%s, len=%d, addrtype=%d]", name, argp->key.hostaddr.addr, argp->key.hostaddr.len, argp->key.hostaddr.type); } static int tsol_rh_compar(const void *n1, const void *n2) { nsc_entry_t *e1, *e2; int res, l1, l2; e1 = (nsc_entry_t *)n1; e2 = (nsc_entry_t *)n2; if (e1->key.hostaddr.type > e2->key.hostaddr.type) return (1); else if (e1->key.hostaddr.type < e2->key.hostaddr.type) return (-1); l1 = strlen(e1->key.hostaddr.addr); l2 = strlen(e2->key.hostaddr.addr); res = strncasecmp(e1->key.hostaddr.addr, e2->key.hostaddr.addr, (l1 > l2)?l1:l2); return (_NSC_INT_KEY_CMP(res, 0)); } static uint_t tsol_rh_gethash(nss_XbyY_key_t *key, int htsize) { return (db_gethash(key->hostaddr.addr, strlen(key->hostaddr.addr), htsize)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle tsol_gettpbyname call in nscd */ #include #include #include #include #include "cache.h" #define tsol_tp_db ctx->nsc_db[0] #define NSC_NAME_TSOL_TP_BYNAME "tsol_gettpbyname" void tnrhtp_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = TNRHTP_PATH; ctx->db_count = 1; ctx->file_name = TNRHTP_PATH; tsol_tp_db = make_cache(nsc_key_cis, NSS_DBOP_TSOL_TP_BYNAME, NSC_NAME_TSOL_TP_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines to handle getuser* calls in nscd */ #include "cache.h" #define nam_db ctx->nsc_db[0] #define NSC_NAME_USERATTR_BYNAME "getusernam" void user_init_ctx(nsc_ctx_t *ctx) { ctx->dbname = NSS_DBNAM_USERATTR; ctx->file_name = "/etc/user_attr"; ctx->db_count = 1; nam_db = make_cache(nsc_key_ces, NSS_DBOP_USERATTR_BYNAME, NSC_NAME_USERATTR_BYNAME, NULL, NULL, NULL, nsc_ht_default, -1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include "nscd_db.h" #include "nscd_log.h" /* * Access control structure for a piece of nscd data. This structure * is always tagged before the nscd data. nscd_alloc, which should * be used to allocate memory that requires access control or usage * count control, will initialize this access control structure at the * start of the memory returned to the caller. */ struct nscd_access_s { void *data; /* addr of real data */ void (*free_func)(nscd_acc_data_t *data); /* destructor */ mutex_t mutex; /* protect this structure */ mutex_t *data_mutex; rwlock_t *data_rwlock; cond_t *data_cond; int nUse; /* usage count */ int type; int delete; /* no longer available */ nscd_seq_num_t seq_num; /* sequence number */ }; /* size should be in multiple of 8 */ static int sizeof_access = roundup(sizeof (nscd_access_t)); #define ABORT_DUE_TO_NO_VALID_NSCD_ACCESS_DATA 0 #define ASSERT_ACCESS_DATA \ if (access->data != data) \ assert(ABORT_DUE_TO_NO_VALID_NSCD_ACCESS_DATA) #define SET_ACCESS_PTR \ access = (nscd_access_t *) \ ((void *)((char *)data - sizeof_access)) static void _nscd_free(nscd_acc_data_t *data); /* * FUNCTION: _nscd_release * * Decrements the usage count maintained in the access data * tagged before 'data'. Delete the nscd data item if the delete * flag is set and the usage count reaches 0. */ void _nscd_release( nscd_acc_data_t *data) { nscd_access_t *access; char *me = "_nscd_release"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p, " "seq = %lld, nUse = %d\n", data, access->data, access->seq_num, access->nUse); ASSERT_ACCESS_DATA; (void) mutex_lock(&access->mutex); access->nUse--; if (access->nUse < 0) { #define ACCESS_NUSE_LESS_THAN_ZERO 0 assert(ACCESS_NUSE_LESS_THAN_ZERO); } if (access->nUse <= 0 && access->delete == 1) { _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "deleting data %p\n", access->data); (access->free_func)(access->data); /* * if we get here, no other thread could be * holding the access->mutex lock, It is safe * to free the memory containing the mutex * structure. No mutex_unlock is necessary. */ _nscd_free(data); } else (void) mutex_unlock(&access->mutex); } /* * FUNCTION: _nscd_destroy * * Marks the nscd data item as to-be-deleted and then releases * (If the usage count happens to be zero, then _nscd_release() * will destroy the data.) * * Note that _nscd_destroy should only be called if the * caller has created the nscd data with _nscd_alloc * (with the exception of _nscd_set). That nscd data * item should be private to the caller. */ static void _nscd_destroy( nscd_acc_data_t *data) { nscd_access_t *access; char *me = "_nscd_destroy"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; (void) mutex_lock(&access->mutex); access->delete = 1; (void) mutex_unlock(&access->mutex); _nscd_release(data); } /* * FUNCTION: _nscd_get * * Increment the usage count by one if 'data' can * be found in the internal address database. */ nscd_acc_data_t * _nscd_get( nscd_acc_data_t *data) { nscd_access_t *access; void *ret = data; rwlock_t *addr_rwlock; char *me = "_nscd_get"; if (data == NULL) return (NULL); SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p, seq#= %lld, nUse = %d\n", data, access->data, access->seq_num, access->nUse); ASSERT_ACCESS_DATA; /* * see if this addr is still valid, * if so, _nscd_is_int_addr will * do a read lock on the returned * multiple readers/single writer lock * to prevent the access data from being * deleted while it is being accessed. */ if ((addr_rwlock = _nscd_is_int_addr(data, access->seq_num)) == NULL) { _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "internal address %p not found\n", data); assert(addr_rwlock != NULL); return (NULL); } (void) mutex_lock(&access->mutex); if (access->delete == 1) ret = NULL; else access->nUse++; (void) mutex_unlock(&access->mutex); /* * done with the multiple readers/single writer lock */ (void) rw_unlock(addr_rwlock); return (ret); } /* * FUNCTION: _nscd_set * * _nscd_set sets the address of a nscd data item * to 'new' and delete the old nscd data (old). * The pointer 'new' is returned. */ nscd_acc_data_t * _nscd_set( nscd_acc_data_t *old, nscd_acc_data_t *new) { nscd_acc_data_t *old_data, *new_data; char *me = "_nscd_set"; if (new == old) return (old); _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "new = %p, old = %p\n", new, old); old_data = _nscd_get(old); new_data = _nscd_get(new); if (old_data != new_data) { _nscd_destroy(old_data); _nscd_release(new_data); return (new_data); } /* if old_data == new_data, both must be NULL */ return (NULL); } /* * FUNCTION: _nscd_rdlock * * Lock (rw_rdlock) a nscd data item for reading. The caller * needs to call _nscd_rw_unlock() to unlock the data item * when done using the data. */ nscd_acc_data_t * _nscd_rdlock( nscd_acc_data_t *data) { nscd_access_t *access; void *ret; char *me = "_nscd_rdlock"; ret = _nscd_get(data); if (ret == NULL) return (NULL); SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_rwlock != NULL); (void) rw_rdlock(access->data_rwlock); return (ret); } /* * FUNCTION: _nscd_wrlock * * Lock (rw_wrlock) a nscd data item for writing. The caller * needs to call _nscd_rw_unlock() to unlock the data item * when done using the data. */ nscd_acc_data_t * _nscd_wrlock( nscd_acc_data_t *data) { nscd_access_t *access; void *ret; char *me = "_nscd_wrlock"; ret = _nscd_get(data); if (ret == NULL) return (NULL); SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_rwlock != NULL); (void) rw_wrlock(access->data_rwlock); return (ret); } /* * FUNCTION: _nscd_rw_unlock * * Unlock (rw_unlock) a locked nscd data item. */ void _nscd_rw_unlock( nscd_acc_data_t *data) { nscd_access_t *access; char *me = "_nscd_rw_unlock"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_rwlock != NULL); (void) rw_unlock(access->data_rwlock); _nscd_release(data); } /* * FUNCTION: _nscd_rw_unlock_no_release * * Unlock (rw_unlock) a locked nscd data item but without release * it, i.e., without decrement the usage count to indicate that * the data item is still being referenced. */ void _nscd_rw_unlock_no_release( nscd_acc_data_t *data) { nscd_access_t *access; if (data == NULL) return; SET_ACCESS_PTR; ASSERT_ACCESS_DATA; assert(access->data_rwlock != NULL); (void) rw_unlock(access->data_rwlock); } /* * FUNCTION: _nscd_mutex_lock * * Lock (mutex_lock) a nscd data item. The caller needs * to call _nscd_mutex_unlock() to unlock the data item * when done using the data. */ nscd_acc_data_t * _nscd_mutex_lock( nscd_acc_data_t *data) { nscd_access_t *access; void *ret; char *me = "_nscd_mutex_lock"; ret = _nscd_get(data); if (ret == NULL) return (NULL); SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_mutex != NULL); (void) mutex_lock(access->data_mutex); return (ret); } /* * FUNCTION: _nscd_mutex_unlock * * Unlock a locked nscd data item (that were locked by _nscd_mutex_lock).. */ void _nscd_mutex_unlock( nscd_acc_data_t *data) { nscd_access_t *access; char *me = "_nscd_mutex_unlock"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_mutex != NULL); (void) mutex_unlock(access->data_mutex); _nscd_release(data); } /* * FUNCTION: _nscd_cond_wait * * Perform a condition wait with the cond_t and mutex_t associated * with data. */ void _nscd_cond_wait( nscd_acc_data_t *data, cond_t *cond) { nscd_access_t *access; char *me = "_nscd_cond_wait"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_cond != NULL && access->data_mutex != NULL); if (cond == NULL) (void) cond_wait(access->data_cond, access->data_mutex); else (void) cond_wait(cond, access->data_mutex); } /* * FUNCTION: _nscd_cond_signal * * Perform a condition signal with the cond_t associated with 'data'. */ void _nscd_cond_signal( nscd_acc_data_t *data) { nscd_access_t *access; char *me = "_nscd_cond_signal"; if (data == NULL) return; SET_ACCESS_PTR; _NSCD_LOG(NSCD_LOG_ACCESS_INFO, NSCD_LOG_LEVEL_DEBUG) (me, "data = %p, access->data = %p\n", data, access->data); ASSERT_ACCESS_DATA; assert(access->data_cond != NULL); (void) cond_signal(access->data_cond); } /* * FUNCTION: _nscd_alloc * * Allocate a piece of nscd memory. 'data_free' * is the function to invoke to free the data * stored in this memory, i.e., the desctrctor. * 'option' indicate whether a mutex or a * readers/writer (or both, or none) should also * be allocated. */ nscd_acc_data_t * _nscd_alloc( int type, size_t size, void (*data_free)(nscd_acc_data_t *data), int option) { nscd_access_t *access; nscd_acc_data_t *ptr; nscd_seq_num_t seq_num; rwlock_t *rwlock = NULL; mutex_t *mutex = NULL; cond_t *cond = NULL; if ((ptr = (nscd_acc_data_t *)calloc(1, size + sizeof_access)) == NULL) return (NULL); if (option & NSCD_ALLOC_MUTEX) { if ((mutex = (mutex_t *)calloc(1, sizeof (mutex_t))) == NULL) { free(ptr); return (NULL); } else (void) mutex_init(mutex, USYNC_THREAD, NULL); } if (option & NSCD_ALLOC_RWLOCK) { if ((rwlock = (rwlock_t *)calloc(1, sizeof (rwlock_t))) == NULL) { free(ptr); free(mutex); return (NULL); } else (void) rwlock_init(rwlock, USYNC_THREAD, NULL); } if (option & NSCD_ALLOC_COND) { if ((cond = (cond_t *)calloc(1, sizeof (cond_t))) == NULL) { free(ptr); free(mutex); free(rwlock); return (NULL); } else (void) cond_init(cond, USYNC_THREAD, NULL); } /* get current sequence number */ seq_num = _nscd_get_seq_num(); access = (nscd_access_t *)ptr; access->data = (char *)ptr + sizeof_access; access->data_mutex = mutex; access->data_rwlock = rwlock; access->data_cond = cond; access->nUse = 0; access->delete = 0; access->type = type; access->free_func = data_free; access->seq_num = seq_num; /* add the address to the internal address database */ if (_nscd_add_int_addr(access->data, type, seq_num) != NSCD_SUCCESS) { free(ptr); return (NULL); } return (access->data); } /* * FUNCTION: _nscd_free * * Free a piece of nscd memory. */ static void _nscd_free( nscd_acc_data_t *data) { nscd_access_t *access; if (data == NULL) return; SET_ACCESS_PTR; ASSERT_ACCESS_DATA; /* remove the address from the internal address database */ _nscd_del_int_addr(access->data, access->seq_num); if (access->data_mutex) free(access->data_mutex); if (access->data_rwlock) free(access->data_rwlock); if (access->data_cond) free(access->data_cond); (void) memset(access, 0, sizeof (*access)); free(access); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include "cache.h" #include "nscd_door.h" #include "nscd_log.h" #include "nscd_admin.h" extern nsc_ctx_t *cache_ctx_p[]; extern char *cache_name[]; static nscd_admin_t admin_c = { 0 }; static nscd_admin_mod_t admin_mod = { 0 }; static mutex_t mod_lock = DEFAULTMUTEX; /*ARGSUSED*/ int _nscd_door_getadmin(void *outbuf) { int i; int data_size = NSCD_N2N_DOOR_BUF_SIZE(admin_c); nss_pheader_t *phdr = (nss_pheader_t *)outbuf; nscd_cfg_cache_t cfg_default = NSCD_CFG_CACHE_DEFAULTS; /* * if size of buffer is not big enough, tell the caller to * increase it to the size returned */ if (phdr->pbufsiz < data_size) return (sizeof (admin_c)); NSCD_SET_STATUS_SUCCESS(phdr); phdr->data_off = sizeof (nss_pheader_t); phdr->data_len = sizeof (admin_c); for (i = 0; i < CACHE_CTX_COUNT; i++) { if (cache_ctx_p[i] != NULL) { (void) rw_rdlock(&cache_ctx_p[i]->cfg_rwlp); admin_c.cache_cfg[i] = cache_ctx_p[i]->cfg; (void) rw_unlock(&cache_ctx_p[i]->cfg_rwlp); (void) mutex_lock(&cache_ctx_p[i]->stats_mutex); admin_c.cache_stats[i] = cache_ctx_p[i]->stats; (void) mutex_unlock(&cache_ctx_p[i]->stats_mutex); } else { admin_c.cache_cfg[i] = cfg_default; (void) memset(&admin_c.cache_stats[i], 0, sizeof (admin_c.cache_stats[0])); } } (void) memcpy(((char *)outbuf) + phdr->data_off, &admin_c, sizeof (admin_c)); return (0); } void _nscd_client_showstats() { (void) printf("nscd configuration:\n\n"); (void) printf("%10d server debug level\n", admin_c.debug_level); (void) printf("\"%s\" is server log file\n", admin_c.logfile); (void) nsc_info(NULL, NULL, admin_c.cache_cfg, admin_c.cache_stats); } /*ARGSUSED*/ nscd_rc_t _nscd_server_setadmin(nscd_admin_mod_t *set) { nscd_rc_t rc = NSCD_ADMIN_FAIL_TO_SET; nscd_cfg_handle_t *h; int i, j; char *group = "param-group-cache"; char *dbname; nscd_cfg_error_t *err = NULL; char *me = "_nscd_server_setadmin"; if (set == NULL) set = &admin_mod; /* one setadmin at a time */ (void) mutex_lock(&mod_lock); _NSCD_LOG_IF(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_DEBUG) { _nscd_logit(me, "total_size = %d\n", set->total_size); _nscd_logit(me, "debug_level_set = %d, debug_level = %d\n", set->debug_level_set, set->debug_level); _nscd_logit(me, "logfile_set = %d, logfile = %s\n", set->logfile_set, *set->logfile == '\0' ? "" : set->logfile); _nscd_logit(me, "cache_cfg_num = %d\n", set->cache_cfg_num); _nscd_logit(me, "cache_flush_num = %d\n", set->cache_flush_num); } /* * global admin stuff */ if (set->debug_level_set == nscd_true) { if (_nscd_set_debug_level(set->debug_level) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to set debug level %d\n", set->debug_level); goto err_exit; } admin_c.debug_level = set->debug_level; } if (set->logfile_set == nscd_true) { if (_nscd_set_log_file(set->logfile) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to set log file %s\n", set->logfile); goto err_exit; } (void) strlcpy(admin_c.logfile, set->logfile, NSCD_LOGFILE_LEN); } /* * For caches to be changed */ if (set->cache_cfg_num > CACHE_CTX_COUNT) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "number of caches (%d) to change out of bound %s\n", set->cache_cfg_num); goto err_exit; } for (i = 0; i < set->cache_cfg_num; i++) { nscd_cfg_cache_t *new_cfg; j = set->cache_cfg_set[i]; new_cfg = &set->cache_cfg[i]; dbname = cache_name[j]; if (cache_ctx_p[j] == NULL) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to find cache context for %s\n", dbname); } rc = _nscd_cfg_get_handle(group, dbname, &h, NULL); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to get handle for < %s : %s >\n", dbname, group); goto err_exit; } rc = _nscd_cfg_set(h, new_cfg, &err); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to set admin data for < %s : %s >\n", dbname, group); _nscd_cfg_free_handle(h); goto err_exit; } _nscd_cfg_free_handle(h); } /* * For caches to be flushed */ if (set->cache_flush_num > CACHE_CTX_COUNT) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "number of caches (%d) to flush out of bound %s\n", set->cache_flush_num); goto err_exit; } for (i = 0; i < set->cache_flush_num; i++) { int j; j = set->cache_flush_set[i]; if (cache_ctx_p[j] == NULL) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "unable to find cache context for %s\n", dbname); } nsc_invalidate(cache_ctx_p[j], NULL, NULL); } rc = NSCD_SUCCESS; err_exit: (void) mutex_unlock(&mod_lock); return (rc); } /*ARGSUSED*/ void _nscd_door_setadmin(void *buf) { nscd_rc_t rc; nss_pheader_t *phdr = (nss_pheader_t *)buf; char *me = "_nscd_door_setadmin"; rc = _nscd_server_setadmin(NSCD_N2N_DOOR_DATA(nscd_admin_mod_t, buf)); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR) (me, "SETADMIN call failed\n"); NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, rc); } else { NSCD_SET_STATUS_SUCCESS(phdr); } } /* * for a database 'dbname', add config value 'val' of option 'opt' * to the global admin_mod structure */ int _nscd_add_admin_mod(char *dbname, char opt, char *val, char *msg, int msglen) { int i, j; nscd_cfg_cache_t *cfg; nscd_cfg_group_info_t gi = NSCD_CFG_GROUP_INFO_CACHE; char dbn[64], *cp; /* set initial admin_mod size; assume no cache config to set */ if (admin_mod.total_size == 0) admin_mod.total_size = sizeof (admin_mod) - sizeof (admin_mod.cache_cfg); /* global admin stuff */ if (opt == 'l' || opt == 'd') { if (opt == 'l') { (void) strlcpy(admin_mod.logfile, val, NSCD_LOGFILE_LEN); admin_mod.logfile_set = nscd_true; } else { admin_mod.debug_level = atoi(val); admin_mod.debug_level_set = nscd_true; } return (0); } /* options to be processed next requires cache name */ (void) strlcpy(dbn, dbname, sizeof (dbn)); if ((cp = strchr(dbn, ',')) != NULL) *cp = '\0'; i = get_cache_idx(dbn); if (i == -1) { (void) snprintf(msg, msglen, gettext("invalid cache name \"%s\""), dbn); return (-1); } /* flush cache ? */ if (opt == 'i') { admin_mod.cache_flush_set[admin_mod.cache_flush_num++] = i; return (0); } /* options to be processed next requires a param value */ if (val == NULL) { (void) snprintf(msg, msglen, gettext("value missing after \"%s\""), dbn); return (-1); } /* try to use an existing cache_cfg in admin_mod */ for (j = 0; j < admin_mod.cache_cfg_num; j++) { if (admin_mod.cache_cfg_set[j] == i) break; } /* no existing one, set up another one */ if (j == admin_mod.cache_cfg_num) { admin_mod.cache_cfg_set[j] = i; admin_mod.cache_cfg_num++; admin_mod.total_size += sizeof (admin_mod.cache_cfg[0]); } cfg = &admin_mod.cache_cfg[j]; cfg->gi.num_param = gi.num_param; switch (opt) { case 'e': /* enable cache */ _nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 0); if (strcmp(val, "yes") == 0) cfg->enable = nscd_true; else if (strcmp(val, "no") == 0) cfg->enable = nscd_false; else { (void) snprintf(msg, msglen, gettext("\"yes\" or \"no\" not specified after \"%s\""), dbn); return (-1); } break; case 'c': /* check files */ _nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 3); if (strcmp(val, "yes") == 0) cfg->check_files = nscd_true; else if (strcmp(val, "no") == 0) cfg->check_files = nscd_false; else { (void) snprintf(msg, msglen, gettext("\"yes\" or \"no\" not specified after \"%s\""), dbn); return (-1); } break; case 'p': /* positive time to live */ _nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 5); cfg->pos_ttl = atoi(val); break; case 'n': /* negative time to live */ _nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 6); cfg->neg_ttl = atoi(val); break; case 'h': /* keep hot count */ _nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 7); cfg->keephot = atoi(val); break; } return (0); } int _nscd_client_getadmin(char opt) { int callnum; nss_pheader_t phdr; if (opt == 'G') callnum = NSCD_GETPUADMIN; else callnum = NSCD_GETADMIN; (void) _nscd_doorcall_data(callnum, NULL, sizeof (admin_c), &admin_c, sizeof (admin_c), &phdr); if (NSCD_STATUS_IS_NOT_OK(&phdr)) { return (1); } return (0); } int _nscd_client_setadmin() { return (_nscd_doorcall_data(NSCD_SETADMIN, &admin_mod, sizeof (admin_mod), NULL, 0, NULL)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NSCD_ADMIN_H #define _NSCD_ADMIN_H #ifdef __cplusplus extern "C" { #endif #include "cache.h" #include "nscd_common.h" #include "nscd_config.h" #define NSCD_LOGFILE_LEN 128 /* * structure used for door call NSCD_GETADMIN and NSCD_GETPUADMIN */ typedef struct nscd_admin { int debug_level; char logfile[NSCD_LOGFILE_LEN]; nscd_cfg_cache_t cache_cfg[CACHE_CTX_COUNT]; nscd_cfg_stat_cache_t cache_stats[CACHE_CTX_COUNT]; } nscd_admin_t; /* * structure used for door call NSCD_SETADMIN and NSCD_SETPUADMIN */ typedef struct nscd_admin_mod { int total_size; nscd_bool_t debug_level_set; nscd_bool_t logfile_set; uint8_t cache_cfg_set[CACHE_CTX_COUNT]; uint8_t cache_flush_set[CACHE_CTX_COUNT]; nscd_bool_t global_cfg_set; uint8_t cache_cfg_num; uint8_t cache_flush_num; int debug_level; char logfile[NSCD_LOGFILE_LEN]; nscd_cfg_cache_t cache_cfg[CACHE_CTX_COUNT]; } nscd_admin_mod_t; #ifdef __cplusplus } #endif int _nscd_client_getadmin(char opt); int _nscd_client_setadmin(); void _nscd_client_showstats(); nscd_rc_t _nscd_server_setadmin(nscd_admin_mod_t *set); int _nscd_door_getadmin(void *outbuf); void _nscd_door_setadmin(void *buf); int _nscd_add_admin_mod(char *dbname, char opt, char *val, char *msg, int msglen); #endif /* _NSCD_ADMIN_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include "cache.h" nsc_keephot_t * maken(int n) { nsc_keephot_t *ret; ++n; ret = (nsc_keephot_t *)calloc(n, sizeof (nsc_keephot_t)); if (ret == NULL) return (NULL); ret[0].num = n - 1; return (ret); } void * insertn(nsc_keephot_t *table, uint_t n, void *data) { void *olddata; int size, guess, base, last; if (n < 1 || table[1].num > n) { return (data); } size = table[0].num; if (table[size].num < n) /* biggest so far */ guess = size; else { base = 1; last = size; while (last >= base) { guess = (last+base)/2; if (table[guess].num == n) goto doit; if (table[guess].num > n) last = guess - 1; else base = guess + 1; } guess = last; } doit: olddata = table[1].ptr; (void) memmove(table + 1, table + 2, sizeof (nsc_keephot_t) * (guess-1)); table[guess].ptr = data; table[guess].num = n; return (olddata); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _NSCD_CFGDEF_H #define _NSCD_CFGDEF_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "nscd_config.h" #include "nscd_log.h" #include "cache.h" /* * structure used for preliminary checking of an integer * configuration value */ typedef struct { int min; int max; } nscd_cfg_int_check_t; /* * structure used for preliminary checking of a bitmap * configuration value */ typedef struct { nscd_cfg_bitmap_t valid_bits; } nscd_cfg_bitmap_check_t; /* * structure used for preliminary checking of a string * configuration value */ typedef struct { nscd_bool_t must_not_null; int maxlen; } nscd_cfg_str_check_t; /* * Per nsswitch database config data */ typedef struct { nscd_cfg_frontend_t fe; nscd_cfg_switch_t sw; nscd_cfg_cache_t cache; } nscd_cfg_nsw_db_data_t; /* * Per nsswitch database statistics data */ typedef struct { nscd_cfg_stat_switch_t sw; nscd_cfg_stat_cache_t cache; } nscd_cfg_stat_nsw_db_data_t; /* * global statistics data */ typedef struct { nscd_cfg_stat_global_log_t log; nscd_cfg_stat_global_switch_t sw; nscd_cfg_stat_cache_t cache; } nscd_cfg_stat_global_data_t; /* * global config data */ typedef struct { nscd_cfg_global_log_t log; nscd_cfg_global_frontend_t fe; nscd_cfg_global_selfcred_t sc; nscd_cfg_global_switch_t sw; nscd_cfg_global_cache_t cache; } nscd_cfg_global_data_t; /* * structure for handling the switch database specific group * or parameter default */ typedef struct nscd_cfg_nsw_spc_default { char *db; int group_off; int param_off; void *data; /* pointer or link to data */ int data_len; } nscd_cfg_nsw_spc_default_t; /* * name service switch source (repository) table */ nscd_cfg_id_t _nscd_cfg_nsw_src[] = { { 0, "files" }, { 1, "ldap" }, { 2, "nis" }, { 3, "mdns" }, { 4, "dns" }, { 5, "compat" }, { 6, "user" }, { 7, "ad" }, { -1, NULL } }; /* * name service related smf service table * (the order of the services should match the order of the source * listed above, 0: files, 1: ldap, 2: nis, 3: mdns. * dns is not needed) */ nscd_cfg_id_t _nscd_cfg_smf_services[] = { { 0, "svc:/system/name-service-cache:default"}, { 1, "svc:/network/ldap/client:default" }, { 2, "svc:/network/nis/client:default" }, { 3, "svc:/network/dns/multicast:default" }, { -1, NULL } }; /* * default version for supported sources/backends */ void *_nscd_be_version = NULL; /* * name service database table */ nscd_cfg_id_t _nscd_cfg_nsw_db[] = { { 0, NSS_DBNAM_PASSWD }, { 1, NSS_DBNAM_GROUP }, { 2, NSS_DBNAM_HOSTS }, { 3, NSS_DBNAM_IPNODES }, { 4, NSS_DBNAM_EXECATTR }, { 5, NSS_DBNAM_PROFATTR }, { 6, NSS_DBNAM_USERATTR }, { 7, NSS_DBNAM_NETWORKS }, { 8, NSS_DBNAM_PROTOCOLS }, { 9, NSS_DBNAM_RPC }, { 10, NSS_DBNAM_ETHERS }, { 11, NSS_DBNAM_NETMASKS }, { 12, NSS_DBNAM_BOOTPARAMS }, { 13, NSS_DBNAM_PUBLICKEY }, { 14, NSS_DBNAM_NETGROUP }, { 15, NSS_DBNAM_SERVICES }, { 16, NSS_DBNAM_PRINTERS }, { 17, NSS_DBNAM_AUTHATTR }, { 18, NSS_DBNAM_PROJECT }, { 19, NSS_DBNAM_SHADOW }, { 20, NSS_DBNAM_AUDITUSER }, { 21, NSS_DBNAM_TSOL_TP }, { 22, NSS_DBNAM_TSOL_RH }, /* pseudo-databases for the compat backend */ { 23, NSS_DBNAM_PASSWD_COMPAT }, { 24, NSS_DBNAM_GROUP_COMPAT }, #define NSS_DBNAM_COMPAT_NUM_DB 5 /* * pseudo-databases that use the switch policy that is * configured for NSS_DBNAM_PASSWD_COMPAT */ { 25, NSS_DBNAM_PASSWD }, { 26, NSS_DBNAM_SHADOW }, { 27, NSS_DBNAM_AUDITUSER }, { 28, NSS_DBNAM_USERATTR }, /* * pseudo-database that uses the switch policy that is * configured for NSS_DBNAM_GROUP_COMPAT */ { 29, NSS_DBNAM_GROUP }, { -1, NULL } }; /* * A special way to indicate all switch databases */ static nscd_cfg_id_t _nscd_cfg_nsw_alldb = { NSCD_CFG_NSW_ALLDB_INDEX, NSCD_CFG_NSW_ALLDB }; /* * data for preliminary checking of the log configuration */ static nscd_cfg_str_check_t NSCD_CFG_LOGFILE_PCHECK = {nscd_false, PATH_MAX}; static nscd_cfg_bitmap_check_t NSCD_CFG_LOGCOMP_PCHECK = {NSCD_LOG_ALL}; static nscd_cfg_bitmap_check_t NSCD_CFG_LOGLEVEL_PCHECK = {NSCD_LOG_LEVEL_ALL}; /* data for preliminary checking of the switch configuration */ static nscd_cfg_str_check_t NSCD_CFG_NSWCFGSTR_PCHECK = {nscd_true, 128}; /* * macros for defining the static param table */ #define NSCD_CFG_PGROUP_DESC(pn, type, pflag, gf, g_in_t, pcheck_p,\ nfunc_name, vfunc_name) \ { \ {-1, pn}, type, (NSCD_CFG_PFLAG_GROUP | pflag), \ 0, 0, 0,\ NSCD_SIZEOF(g_in_t, gf), offsetof(g_in_t, gf), -1, \ pcheck_p, nfunc_name, vfunc_name \ } #define NSCD_CFG_PARAM_DESC(pn, type, pflag, pf, p_in_t, \ gf, g_in_t, pcheck_p, nfunc_name, vfunc_name) \ { \ {-1, pn}, type, pflag, \ NSCD_SIZEOF(p_in_t, pf), offsetof(p_in_t, pf), -1, \ NSCD_SIZEOF(g_in_t, gf), offsetof(g_in_t, gf), -1, \ pcheck_p, nfunc_name, vfunc_name \ } #define NSCD_CFG_PGROUP_DESC_NULL \ { \ {-1, NULL}, -1, NSCD_CFG_PFLAG_GROUP, \ 0, 0, 0, \ 0, 0, 0, \ NULL, NULL, NULL \ } /* nscd internal cfg_*_notify() cfg_*_verify() and cfg_*_get_stat() */ extern nscd_rc_t _nscd_cfg_log_notify(); extern nscd_rc_t _nscd_cfg_log_verify(); extern nscd_rc_t _nscd_cfg_frontend_notify(); extern nscd_rc_t _nscd_cfg_frontend_verify(); extern nscd_rc_t _nscd_cfg_selfcred_notify(); extern nscd_rc_t _nscd_cfg_selfcred_verify(); extern nscd_rc_t _nscd_cfg_switch_notify(); extern nscd_rc_t _nscd_cfg_switch_verify(); extern nscd_rc_t _nscd_cfg_cache_notify(); extern nscd_rc_t _nscd_cfg_cache_verify(); extern nscd_rc_t _nscd_cfg_log_get_stat(); extern nscd_rc_t _nscd_cfg_switch_get_stat(); extern nscd_rc_t _nscd_cfg_cache_get_stat(); /* * the following macros are used to indicate a parameter's * notify/verify/get_stat functions are the same as those * of the group */ #define NSCD_CFG_FUNC_NOTIFY_AS_GROUP ((nscd_cfg_func_notify_t)-1) #define NSCD_CFG_FUNC_VERIFY_AS_GROUP ((nscd_cfg_func_verify_t)-1) #define NSCD_CFG_FUNC_GET_STAT_AS_GROUP ((nscd_cfg_func_get_stat_t)-1) /* * the static config parameter description table */ static nscd_cfg_param_desc_t _nscd_cfg_param_desc[] = { NSCD_CFG_PGROUP_DESC( "param-group-global-log", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_GLOBAL, log, nscd_cfg_global_data_t, NULL, _nscd_cfg_log_notify, _nscd_cfg_log_verify), NSCD_CFG_PARAM_DESC( "logfile", NSCD_CFG_DATA_STRING, NSCD_CFG_PFLAG_GLOBAL | NSCD_CFG_PFLAG_VLEN_DATA, logfile, nscd_cfg_global_log_t, log, nscd_cfg_global_data_t, &NSCD_CFG_LOGFILE_PCHECK, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "debug-level", NSCD_CFG_DATA_BITMAP, NSCD_CFG_PFLAG_GLOBAL, debug_level, nscd_cfg_global_log_t, log, nscd_cfg_global_data_t, &NSCD_CFG_LOGLEVEL_PCHECK, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "debug-components", NSCD_CFG_DATA_BITMAP, NSCD_CFG_PFLAG_GLOBAL, debug_comp, nscd_cfg_global_log_t, log, nscd_cfg_global_data_t, &NSCD_CFG_LOGCOMP_PCHECK, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-global-frontend", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_GLOBAL, fe, nscd_cfg_global_data_t, NULL, _nscd_cfg_frontend_notify, _nscd_cfg_frontend_verify), NSCD_CFG_PARAM_DESC( "common-worker-threads", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_SEND_BIT_SELECTED | NSCD_CFG_PFLAG_GLOBAL, common_worker_threads, nscd_cfg_global_frontend_t, fe, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "cache-hit-threads", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_SEND_BIT_SELECTED | NSCD_CFG_PFLAG_GLOBAL, cache_hit_threads, nscd_cfg_global_frontend_t, fe, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-global-selfcred", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_GLOBAL, sc, nscd_cfg_global_data_t, NULL, _nscd_cfg_selfcred_notify, _nscd_cfg_selfcred_verify), NSCD_CFG_PARAM_DESC( "enable-selfcred", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_GLOBAL, enable_selfcred, nscd_cfg_global_selfcred_t, sc, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "per-user-nscd-ttl", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_GLOBAL, per_user_nscd_ttl, nscd_cfg_global_selfcred_t, sc, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-global-switch", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_GLOBAL, sw, nscd_cfg_global_data_t, NULL, _nscd_cfg_switch_notify, _nscd_cfg_switch_verify), NSCD_CFG_PARAM_DESC( "global-enable-lookup", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_GLOBAL, enable_lookup_g, nscd_cfg_global_switch_t, sw, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "global-enable-loopback-checking", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_GLOBAL, enable_loopback_checking_g, nscd_cfg_global_switch_t, sw, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "global-check-smf-state-interval", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_GLOBAL, check_smf_state_interval_g, nscd_cfg_global_switch_t, sw, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-global-cache", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_INIT_SET_ALL_DB | NSCD_CFG_PFLAG_GLOBAL, cache, nscd_cfg_global_data_t, NULL, _nscd_cfg_cache_notify, _nscd_cfg_cache_verify), NSCD_CFG_PARAM_DESC( "global-enable-cache", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_GLOBAL, enable, nscd_cfg_global_cache_t, cache, nscd_cfg_global_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), /* non-global config param from this point on */ NSCD_CFG_PGROUP_DESC( "param-group-frontend", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP, fe, nscd_cfg_nsw_db_data_t, NULL, _nscd_cfg_frontend_notify, _nscd_cfg_frontend_verify), NSCD_CFG_PARAM_DESC( "worker-thread-per-nsw-db", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, worker_thread_per_nsw_db, nscd_cfg_frontend_t, fe, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-switch", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP | NSCD_CFG_PFLAG_NONE, sw, nscd_cfg_nsw_db_data_t, NULL, _nscd_cfg_switch_notify, _nscd_cfg_switch_verify), NSCD_CFG_PARAM_DESC( "nsw-config-string", NSCD_CFG_DATA_STRING, NSCD_CFG_PFLAG_VLEN_DATA | NSCD_CFG_PFLAG_LINKED, nsw_config_string, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, &NSCD_CFG_NSWCFGSTR_PCHECK, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "nsw-config-database", NSCD_CFG_DATA_STRING, NSCD_CFG_PFLAG_VLEN_DATA | NSCD_CFG_PFLAG_HIDDEN, nsw_config_db, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, &NSCD_CFG_NSWCFGSTR_PCHECK, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "enable-lookup", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, enable_lookup, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "enable-loopback-checking", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, enable_loopback_checking, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "max-nsw-state-per-db", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, max_nsw_state_per_db, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "max-nsw-state-per-thread", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, max_nsw_state_per_thread, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "max-getent-ctx-per-db", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, max_getent_ctx_per_db, nscd_cfg_switch_t, sw, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC( "param-group-cache", NSCD_CFG_DATA_NONE, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP, cache, nscd_cfg_nsw_db_data_t, NULL, _nscd_cfg_cache_notify, _nscd_cfg_cache_verify), NSCD_CFG_PARAM_DESC( "enable-cache", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, enable, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "enable-per-user-cache", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, per_user, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "avoid-nameservice", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, avoid_ns, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "check-files", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_NONE, check_files, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "check-file-interval", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, check_interval, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "positive-time-to-live", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, pos_ttl, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "negative-time-to-live", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, neg_ttl, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "keep-hot-count", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, keephot, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "hint-size", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, hint_size, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "maximum-entries-allowed", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_NONE, maxentries, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "suggested-size", NSCD_CFG_DATA_INTEGER, NSCD_CFG_PFLAG_OBSOLETE, suggestedsize, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PARAM_DESC( "old-data-ok", NSCD_CFG_DATA_BOOLEAN, NSCD_CFG_PFLAG_OBSOLETE, old_data_ok, nscd_cfg_cache_t, cache, nscd_cfg_nsw_db_data_t, NULL, NSCD_CFG_FUNC_NOTIFY_AS_GROUP, NSCD_CFG_FUNC_VERIFY_AS_GROUP), NSCD_CFG_PGROUP_DESC_NULL }; /* * defaults for the global configuration */ static nscd_cfg_global_data_t nscd_cfg_global_default = { /* * nscd_cfg_global_log_t */ { NSCD_CFG_GROUP_INFO_GLOBAL_LOG, NULL, NSCD_LOG_LEVEL_NONE, /* debug_level */ NSCD_LOG_CACHE, /* debug_comp */ }, /* * nscd_cfg_global_frontend_t */ { NSCD_CFG_GROUP_INFO_GLOBAL_FRONTEND, 100, /* common_worker_threads */ 100, /* cache_hit_threads */ }, /* * nscd_cfg_global_selfcred_t */ { NSCD_CFG_GROUP_INFO_GLOBAL_SELFCRED, nscd_true, /* enable_selfcred */ 120, /* per_user_nscd_ttl: 120 seconds */ }, /* * nscd_cfg_global_switch_t */ { NSCD_CFG_GROUP_INFO_GLOBAL_SWITCH, nscd_true, /* enable_lookup_g */ nscd_false, /* enable_loopback_checking_g */ 120, /* check_smf_state_interval_g */ }, /* * nscd_cfg_global_cache_t */ NSCD_CFG_GLOBAL_CACHE_DEFAULTS }; /* * defaults for the per switch database configuration */ static nscd_cfg_nsw_db_data_t nscd_cfg_nsw_db_data_default = { /* * nscd_cfg_frontend_t */ { NSCD_CFG_GROUP_INFO_FRONTEND, 50, /* worker_thread_per_nsw_db */ }, /* * nscd_cfg_switch_t */ { NSCD_CFG_GROUP_INFO_SWITCH, "nis files", /* nsw_config_string */ NULL, /* nsw_config_db */ nscd_true, /* enable_lookup */ nscd_false, /* enable_loopback_checking */ 288, /* max_nsw_state_per_db */ 32, /* max_nsw_state_per_thread */ 256, /* max_getent_ctx_per_db */ }, /* * nscd_cfg_cache_t */ NSCD_CFG_CACHE_DEFAULTS }; /* * macros for defining the database specific defaults */ #define NSCD_CFG_DB_DEFAULT_PARAM(db, gf, pf, gt, defaddr, deflen) \ { \ db, offsetof(nscd_cfg_nsw_db_data_t, gf), \ offsetof(gt, pf), defaddr, deflen \ } #define NSCD_CFG_DB_DEFAULT_GROUP(db, gf, defaddr, deflen) \ { \ db, offsetof(nscd_cfg_nsw_db_data_t, gf), \ 0, defaddr, deflen \ } #define NSCD_CFG_DB_DEFAULT_NULL \ { \ NULL, 0, 0, NULL, 0 \ } /* * shadow, and user_attr use the same switch policy * as that of passwd. exec_attr use that of prof_attr. */ static char *nscd_cfg_shadow_cfg_db = NSS_DBNAM_PASSWD; static char *nscd_cfg_userattr_cfg_db = NSS_DBNAM_PASSWD; static char *nscd_cfg_execattr_cfg_db = NSS_DBNAM_PROFATTR; /* * default switch policy for pseudo-databases passwd_compat and * and group_compa is "nis" */ static char *nscd_cfg_def_passwd_compat = NSS_DEFCONF_PASSWD_COMPAT; static char *nscd_cfg_def_group_compat = NSS_DEFCONF_GROUP_COMPAT; static nscd_cfg_nsw_spc_default_t nscd_cfg_passwd_cfg_link = NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_PASSWD, sw, nsw_config_string, nscd_cfg_switch_t, NULL, NSCD_SIZEOF(nscd_cfg_switch_t, nsw_config_string)); static nscd_cfg_nsw_spc_default_t nscd_cfg_profattr_cfg_link = NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_PROFATTR, sw, nsw_config_string, nscd_cfg_switch_t, NULL, NSCD_SIZEOF(nscd_cfg_switch_t, nsw_config_string)); /* * switch database specific defaults */ nscd_cfg_nsw_spc_default_t _nscd_cfg_nsw_spc_default[] = { NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_SHADOW, sw, nsw_config_db, nscd_cfg_switch_t, &nscd_cfg_shadow_cfg_db, sizeof (nscd_cfg_shadow_cfg_db)), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_USERATTR, sw, nsw_config_db, nscd_cfg_switch_t, &nscd_cfg_userattr_cfg_db, sizeof (nscd_cfg_userattr_cfg_db)), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_EXECATTR, sw, nsw_config_db, nscd_cfg_switch_t, &nscd_cfg_execattr_cfg_db, sizeof (nscd_cfg_execattr_cfg_db)), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_PASSWD_COMPAT, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_def_passwd_compat, sizeof (nscd_cfg_def_passwd_compat)), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_GROUP_COMPAT, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_def_group_compat, sizeof (nscd_cfg_def_group_compat)), NSCD_CFG_DB_DEFAULT_NULL }; /* * switch database specific defaults that are linked to * those of other databases */ nscd_cfg_nsw_spc_default_t _nscd_cfg_nsw_link_default[] = { NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_SHADOW, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_passwd_cfg_link, 0), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_USERATTR, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_passwd_cfg_link, 0), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_AUDITUSER, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_passwd_cfg_link, 0), NSCD_CFG_DB_DEFAULT_PARAM( NSS_DBNAM_EXECATTR, sw, nsw_config_string, nscd_cfg_switch_t, &nscd_cfg_profattr_cfg_link, 0), NSCD_CFG_DB_DEFAULT_NULL }; /* * macros for defining the static stats table */ #define NSCD_CFG_SGROUP_DESC(sn, type, sflag, gi, \ gf, g_in_t, gsfunc_name) \ { \ {-1, sn}, type, NSCD_CFG_SFLAG_GROUP | sflag, gi, \ 0, 0, 0,\ NSCD_SIZEOF(g_in_t, gf), offsetof(g_in_t, gf), -1, \ gsfunc_name \ } #define NSCD_CFG_STAT_DESC(sn, type, sflag, sf, s_in_t, \ gf, g_in_t, gsfunc_name) \ { \ {-1, sn}, type, sflag, NSCD_CFG_GROUP_INFO_NULL, \ NSCD_SIZEOF(s_in_t, sf), offsetof(s_in_t, sf), -1, \ NSCD_SIZEOF(g_in_t, gf), offsetof(g_in_t, gf), -1, \ gsfunc_name \ } #define NSCD_CFG_SGROUP_DESC_NULL \ { \ {-1, NULL}, -1, NSCD_CFG_SFLAG_GROUP, 0, \ 0, 0, 0, \ 0, 0, 0, \ 0 \ } /* * the static statistics description table */ static nscd_cfg_stat_desc_t _nscd_cfg_stat_desc[] = { NSCD_CFG_SGROUP_DESC( "stat-group-global-log", NSCD_CFG_DATA_NONE, NSCD_CFG_SFLAG_GLOBAL, NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, log, nscd_cfg_stat_global_data_t, _nscd_cfg_log_get_stat), NSCD_CFG_STAT_DESC( "entries-logged", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, entries_logged, nscd_cfg_stat_global_log_t, log, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_SGROUP_DESC( "stat-group-global-switch", NSCD_CFG_DATA_NONE, NSCD_CFG_SFLAG_GLOBAL, NSCD_CFG_STAT_GROUP_INFO_GLOBAL_SWITCH, sw, nscd_cfg_stat_global_data_t, _nscd_cfg_switch_get_stat), NSCD_CFG_STAT_DESC( "global-lookup-request-received", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, lookup_request_received_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-lookup-request-queued", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, lookup_request_queued_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-lookup-request-in-progress", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, lookup_request_in_progress_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-lookup-request-succeeded", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, lookup_request_succeeded_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-lookup-request-failed", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, lookup_request_failed_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-loopback-nsw-db-skipped", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, loopback_nsw_db_skipped_g, nscd_cfg_stat_global_switch_t, sw, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_SGROUP_DESC( "stat-group-global-cache", NSCD_CFG_DATA_NONE, NSCD_CFG_SFLAG_GLOBAL, NSCD_CFG_STAT_GROUP_INFO_CACHE, cache, nscd_cfg_stat_global_data_t, _nscd_cfg_cache_get_stat), NSCD_CFG_STAT_DESC( "global-cache-hits-on-positive", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, pos_hits, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-hits-on-negative", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, neg_hits, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-misses-on-positive", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, pos_misses, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-misses-on-negative", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, neg_misses, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-queries-queued", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, wait_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-total-cache-entries", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, entries, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-complete-cache-invalidations", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, invalidate_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-queries-dropped", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_GLOBAL, drop_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "global-cache-hit-rate", NSCD_CFG_DATA_PERCENT, NSCD_CFG_SFLAG_GLOBAL, hitrate, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_global_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), /* non-global stat from this point on */ NSCD_CFG_SGROUP_DESC( "stat-group-switch", NSCD_CFG_DATA_NONE, NSCD_CFG_SFLAG_GROUP, NSCD_CFG_STAT_GROUP_INFO_SWITCH, cache, nscd_cfg_stat_nsw_db_data_t, _nscd_cfg_switch_get_stat), NSCD_CFG_STAT_DESC( "lookup-request-received", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, lookup_request_received, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "lookup-request-queued", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, lookup_request_queued, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "lookup-request-in-progress", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, lookup_request_in_progress, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "lookup-request-succeeded", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, lookup_request_succeeded, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "lookup-request-failed", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, lookup_request_failed, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "loopback-nsw-db-skipped", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, loopback_nsw_db_skipped, nscd_cfg_stat_switch_t, sw, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_SGROUP_DESC( "stat-group-cache", NSCD_CFG_DATA_NONE, NSCD_CFG_SFLAG_GROUP, NSCD_CFG_STAT_GROUP_INFO_CACHE, cache, nscd_cfg_stat_nsw_db_data_t, _nscd_cfg_cache_get_stat), NSCD_CFG_STAT_DESC( "cache-hits-on-positive", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, pos_hits, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-hits-on-negative", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, neg_hits, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-misses-on-positive", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, pos_misses, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-misses-on-negative", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, neg_misses, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-queries-queued", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, wait_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "total-cache-entries", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, entries, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "complete-cache-invalidations", NSCD_CFG_DATA_INTEGER, NSCD_CFG_SFLAG_NONE, invalidate_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-hit-rate", NSCD_CFG_DATA_PERCENT, NSCD_CFG_SFLAG_NONE, hitrate, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_STAT_DESC( "cache-queries-dropped", NSCD_CFG_DATA_PERCENT, NSCD_CFG_SFLAG_NONE, drop_count, nscd_cfg_stat_cache_t, cache, nscd_cfg_stat_nsw_db_data_t, NSCD_CFG_FUNC_GET_STAT_AS_GROUP), NSCD_CFG_SGROUP_DESC_NULL }; /* number of entries in the static tables */ int _nscd_cfg_num_nsw_src = (sizeof (_nscd_cfg_nsw_src) / sizeof (_nscd_cfg_nsw_src[0]) - 1); int _nscd_cfg_num_smf_services = (sizeof (_nscd_cfg_smf_services) / sizeof (_nscd_cfg_smf_services[0]) - 1); /* number of supported nsw databases (including pseudo ones) */ int _nscd_cfg_num_nsw_db_all = (sizeof (_nscd_cfg_nsw_db) / sizeof (_nscd_cfg_nsw_db[0]) - 1); /* number of supported nsw databases (not including pseudo ones) */ int _nscd_cfg_num_nsw_db = (sizeof (_nscd_cfg_nsw_db) / sizeof (_nscd_cfg_nsw_db[0]) - 1) - NSS_DBNAM_COMPAT_NUM_DB; static int _nscd_cfg_num_param = (sizeof (_nscd_cfg_param_desc) / sizeof (_nscd_cfg_param_desc[0]) - 1); static int _nscd_cfg_num_stat = (sizeof (_nscd_cfg_stat_desc) / sizeof (_nscd_cfg_stat_desc[0]) - 1); int _nscd_cfg_num_nsw_default = (sizeof (_nscd_cfg_nsw_spc_default) / sizeof (_nscd_cfg_nsw_spc_default[0]) - 1); int _nscd_cfg_num_link_default = (sizeof (_nscd_cfg_nsw_link_default) / sizeof (_nscd_cfg_nsw_link_default[0]) - 1); #ifdef __cplusplus } #endif #endif /* _NSCD_CFGDEF_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * routine to read configuration file * */ #include "nscd_config.h" #include "nscd_log.h" #include #include #include #include static int strbreak(char *field[], int array_size, char *s, char *sep) { int i; char *lasts, *qp; int inquote; qp = strchr(s, '"'); for (i = 0; i < array_size && (field[i] = strtok_r((i?(char *)NULL:s), sep, &lasts)); i++) { /* empty */ } if (qp == NULL) return (i); inquote = 1; while (++qp < lasts) { switch (*qp) { case '"': inquote = (inquote == 0); break; case '\\': /* escape " */ if (inquote == 1 && *(qp + 1) == '"') qp++; break; case '\0': if (inquote == 1) { *qp = ' '; i--; } break; } } return (i); } nscd_rc_t _nscd_cfg_read_file( char *filename, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_read_file"; FILE *in; char buffer[255]; char *fields [128]; int linecnt; int fieldcnt; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_handle_t *h = NULL; nscd_cfg_param_desc_t *pdesc; char *dbname, *str; void *data_p; int i; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; union { int i; char data[256]; } u; if ((in = fopen(filename, "r")) == NULL) { (void) snprintf(msg, sizeof (msg), gettext("open of configuration file \"%s\" failed: %s"), filename, strerror(errno)); if (errorp != NULL) *errorp = _nscd_cfg_make_error( NSCD_CFG_FILE_OPEN_ERROR, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); return (NSCD_CFG_FILE_OPEN_ERROR); } linecnt = 0; msg[0] = '\0'; while (fgets(buffer, sizeof (buffer), in) != NULL) { linecnt++; if ((fieldcnt = strbreak(fields, 128, buffer, " \t\n")) == 0 || *fields[0] == '#') { /* skip blank or comment lines */ continue; } switch (fieldcnt) { case 2: dbname = NULL; str = fields[1]; break; case 3: dbname = fields[1]; str = fields[2]; break; default: (void) strlcpy(u.data, fields[0], sizeof (u.data)); for (i = 1; i < fieldcnt; i++) { (void) strlcat(u.data, " ", sizeof (u.data)); (void) strlcat(u.data, fields[i], sizeof (u.data)); } (void) snprintf(msg, sizeof (msg), gettext("Syntax error: line %d of configuration " "file: %s : \"%s\""), linecnt, filename, u.data); if (errorp != NULL) *errorp = _nscd_cfg_make_error( NSCD_CFG_SYNTAX_ERROR, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); rc = NSCD_CFG_SYNTAX_ERROR; break; } if (rc != NSCD_SUCCESS) break; rc = _nscd_cfg_get_handle(fields[0], dbname, &h, errorp); if (rc != NSCD_SUCCESS) break; pdesc = _nscd_cfg_get_desc(h); /* convert string to data */ rc = _nscd_cfg_str_to_data(pdesc, str, &u.data, &data_p, errorp); if (rc != NSCD_SUCCESS) break; /* do preliminary check based on data type */ rc = _nscd_cfg_prelim_check(pdesc, data_p, errorp); if (rc != NSCD_SUCCESS) break; rc = _nscd_cfg_set_linked(h, data_p, errorp); _nscd_cfg_free_handle(h); h = NULL; if (rc != NSCD_CFG_READ_ONLY && rc != NSCD_SUCCESS) break; else { _nscd_cfg_free_error(*errorp); *errorp = NULL; } } /* NSCD_CFG_READ_ONLY is not fatal */ if (rc == NSCD_CFG_READ_ONLY) rc = NSCD_SUCCESS; if (h != NULL) _nscd_cfg_free_handle(h); (void) fclose(in); if (msg[0] == '\0' && rc != NSCD_SUCCESS) { if (errorp != NULL) _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", NSCD_ERR2MSG(*errorp)); } return (rc); } nscd_rc_t _nscd_cfg_read_nsswitch_file( char *filename, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_read_nsswitch_file"; char *pname = "nsw-config-string"; FILE *in; char buffer[255]; char *cc, *ce, *ce1, *c1, *c2; char *db, *dbe; char *nsscfg; int syntax_err; int linecnt; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_handle_t *h = NULL; nscd_cfg_param_desc_t *pdesc; void *data_p; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; union { int i; char data[256]; } u; if ((in = fopen(filename, "r")) == NULL) { (void) snprintf(msg, sizeof (msg), gettext("open of configuration file \"%s\" failed: %s"), filename, strerror(errno)); if (errorp != NULL) *errorp = _nscd_cfg_make_error( NSCD_CFG_FILE_OPEN_ERROR, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); return (NSCD_CFG_FILE_OPEN_ERROR); } linecnt = 0; msg[0] = '\0'; while (fgets(buffer, sizeof (buffer), in) != NULL) { linecnt++; syntax_err = 0; /* skip blank or comment lines */ if (buffer[0] == '#' || buffer[0] == '\n') continue; /* skip end of line comment */ if ((ce = strchr(buffer, '\n')) != NULL) *ce = '\0'; else ce = &buffer[255]; if ((ce1 = strchr(buffer, '#')) != NULL) { ce = ce1; *ce = '\0'; } if ((cc = strchr(buffer, ':')) == NULL) { c1 = buffer; while (isalpha(*c1) && c1 < ce) c1++; if (c1 > ce) syntax_err = 1; else /* blank line */ continue; } else { /* * data name goes before ':', * skip spaces on both ends */ c2 = cc - 1; while (buffer <= c2 && isspace(*c2)) c2--; c1 = buffer; while (c1 <= cc && isspace(*c1)) c1++; if (c1 > c2) syntax_err = 1; else { db = c1; dbe = c2 + 1; /* * nss config goes after ':', * skip spaces on both ends */ c1 = cc + 1; while (c1 <= ce && isspace(*c1)) c1++; c2 = ce - 1; while (cc <= c2 && isspace(*c2)) c2--; if (c1 > c2) { /* no source specified, it's OK */ continue; } else { *dbe = '\0'; nsscfg = c1; *(c2 + 1) = '\0'; } } } if (syntax_err == 1) { (void) snprintf(msg, sizeof (msg), gettext("Syntax error: line %d of configuration " "file: %s : \"%s\""), linecnt, filename, buffer); if (errorp != NULL) *errorp = _nscd_cfg_make_error( NSCD_CFG_SYNTAX_ERROR, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); rc = NSCD_CFG_SYNTAX_ERROR; return (rc); } rc = _nscd_cfg_get_handle(pname, db, &h, errorp); if (rc != NSCD_SUCCESS) { /* ignore unsupported switch database */ if (rc == NSCD_CFG_UNSUPPORTED_SWITCH_DB) { _nscd_cfg_free_error(*errorp); *errorp = NULL; rc = NSCD_SUCCESS; continue; } break; } pdesc = _nscd_cfg_get_desc(h); /* convert string to data */ rc = _nscd_cfg_str_to_data(pdesc, nsscfg, &u.data, &data_p, errorp); if (rc != NSCD_SUCCESS) break; /* do preliminary check based on data type */ rc = _nscd_cfg_prelim_check(pdesc, data_p, errorp); if (rc != NSCD_SUCCESS) break; rc = _nscd_cfg_set_linked(h, data_p, errorp); _nscd_cfg_free_handle(h); h = NULL; if (rc != NSCD_CFG_READ_ONLY && rc != NSCD_SUCCESS) break; else { _nscd_cfg_free_error(*errorp); *errorp = NULL; } } /* NSCD_CFG_READ_ONLY is not fatal */ if (rc == NSCD_CFG_READ_ONLY) rc = NSCD_SUCCESS; if (h != NULL) _nscd_cfg_free_handle(h); (void) fclose(in); if (msg[0] == '\0' && rc != NSCD_SUCCESS) { if (errorp != NULL) _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", NSCD_ERR2MSG(*errorp)); } return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NSCD_COMMON_H #define _NSCD_COMMON_H #ifdef __cplusplus extern "C" { #endif #include #include /* * nscd internal return/error codes */ typedef enum { NSCD_SUCCESS = 0, NSCD_INITIALIZATION_FAILED, NSCD_CTX_NOT_FOUND, NSCD_DB_ENTRY_FOUND, NSCD_DB_ENTRY_NOT_FOUND, NSCD_INVALID_ARGUMENT, NSCD_NO_MEMORY, NSCD_THREAD_CREATE_ERROR, NSCD_SMF_ERROR, NSCD_CFG_UNSUPPORTED_SWITCH_DB, NSCD_CFG_UNSUPPORTED_SWITCH_SRC, NSCD_CFG_DLOPEN_ERROR, NSCD_CFG_DLSYM_ERROR, NSCD_CFG_SET_PARAM_FAILED, NSCD_CFG_PARAM_DESC_ERROR, NSCD_CFG_STAT_DESC_ERROR, NSCD_CFG_INVALID_HANDLE, NSCD_CFG_PARSE_ERROR, NSCD_CFG_FILE_OPEN_ERROR, NSCD_CFG_FILE_ACCESS_ERROR, NSCD_CFG_SYNTAX_ERROR, NSCD_CFG_PRELIM_CHECK_FAILED, NSCD_CFG_DATA_CONVERSION_FAILED, NSCD_CFG_WRITE_ERROR, NSCD_CFG_READ_ONLY, NSCD_CFG_CHANGE_NOT_ALLOWED, NSCD_CREATE_NSW_STATE_FAILED, NSCD_CREATE_GETENT_CTX_FAILED, NSCD_NSS_BACKEND_NOT_FOUND, NSCD_DOOR_UCRED_ERROR, NSCD_DOOR_BUFFER_CHECK_FAILED, NSCD_SELF_CRED_NOT_CONFIGURED, NSCD_SELF_CRED_NO_FORKER, NSCD_SELF_CRED_WRONG_NSCD, NSCD_SELF_CRED_MAIN_IMPOSTER, NSCD_SELF_CRED_FORKER_IMPOSTER, NSCD_SELF_CRED_CHILD_IMPOSTER, NSCD_SELF_CRED_NO_DOOR, NSCD_SELF_CRED_NO_CHILD_SLOT, NSCD_SELF_CRED_INVALID_SLOT_NUMBER, NSCD_SELF_CRED_INVALID_SLOT_STATE, NSCD_ADMIN_FAIL_TO_SET, NSCD_CACHE_INVALID_CACHE_NAME, NSCD_CACHE_NO_CACHE_CTX, NSCD_CACHE_DISABLED, NSCD_CACHE_NO_CACHE_FOUND, NSCD_CACHE_AVOID_NS } nscd_rc_t; /* nscd data type: boolean */ typedef uint8_t nscd_bool_t; #define nscd_true 1 #define nscd_false 0 /* common functions */ void _nscd_set_start_time(int reset); time_t _nscd_get_start_time(); nscd_rc_t _nscd_init(char *cfgfile); nscd_rc_t _nscd_refresh(); #ifdef __cplusplus } #endif #endif /* _NSCD_COMMON_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include /* gettext */ #include #include #include #include #include "nscd_db.h" #include "nscd_config.h" #include "nscd_cfgdef.h" #include "nscd_log.h" typedef struct { rwlock_t *global; rwlock_t *alldb; rwlock_t *nswdb; } nscd_cfg_lock_t; static rwlock_t cfg_paramDB_rwlock = DEFAULTRWLOCK; static nscd_db_t *cfg_paramDB = NULL; static nscd_cfg_global_data_t *nscd_cfg_global_current; static nscd_cfg_nsw_db_data_t *nscd_cfg_nsw_db_data_current; static nscd_cfg_nsw_db_data_t *nscd_cfg_nsw_alldb_current; static rwlock_t *nscd_cfg_global_rwlock; static rwlock_t *nscd_cfg_nsw_db_data_rwlock; static rwlock_t *nscd_cfg_nsw_alldb_rwlock; extern int _nscd_cfg_num_nsw_src_all; extern nscd_cfg_id_t *_nscd_cfg_nsw_src_all; nscd_cfg_error_t * _nscd_cfg_make_error( nscd_rc_t rc, char *msg) { nscd_cfg_error_t *ret; int size, msglen; msglen = (msg != NULL ? strlen(msg) + 1 : 0); size = sizeof (nscd_cfg_error_t) + msglen; ret = calloc(1, size); if (ret == NULL) return (NULL); ret->rc = rc; if (msg != NULL) { ret->msg = (char *)ret + sizeof (nscd_cfg_error_t); (void) memcpy(ret->msg, msg, msglen); } return (ret); } static nscd_rc_t _nscd_cfg_get_list( nscd_cfg_list_t **list, nscd_cfg_list_type_t type) { char *me = "_nscd_cfg_get_list"; int i, num, size; nscd_cfg_id_t *l; nscd_cfg_list_t *ret; nscd_cfg_param_desc_t *pl; nscd_cfg_stat_desc_t *sl; void *p; if (list == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "invalid argument: list = %p\n", list); return (NSCD_INVALID_ARGUMENT); } *list = NULL; switch (type) { case NSCD_CFG_LIST_NSW_DB: num = _nscd_cfg_num_nsw_db; l = &_nscd_cfg_nsw_db[0]; break; case NSCD_CFG_LIST_NSW_SRC: num = _nscd_cfg_num_nsw_src_all; l = _nscd_cfg_nsw_src_all; break; case NSCD_CFG_LIST_PARAM: num = _nscd_cfg_num_param; pl = &_nscd_cfg_param_desc[0]; break; case NSCD_CFG_LIST_STAT: num = _nscd_cfg_num_stat; sl = &_nscd_cfg_stat_desc[0]; break; default: _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "invalid argument: type (%d)\n", type); return (NSCD_INVALID_ARGUMENT); } size = sizeof (nscd_cfg_list_t) + sizeof (nscd_cfg_id_t *) * (num + 1); ret = calloc(1, size); if (ret == NULL) return (NSCD_NO_MEMORY); ret->num = num; p = (char *)ret + sizeof (nscd_cfg_list_t); ret->list = (nscd_cfg_id_t **)p; if (type == NSCD_CFG_LIST_PARAM) { for (i = 0; i <= num; i++) ret->list[i] = (nscd_cfg_id_t *)&pl[i]; } else if (type == NSCD_CFG_LIST_STAT) { for (i = 0; i <= num; i++) ret->list[i] = (nscd_cfg_id_t *)&sl[i]; } else { for (i = 0; i <= num; i++) ret->list[i] = &l[i]; } *list = ret; return (NSCD_SUCCESS); } nscd_rc_t _nscd_cfg_get_param_desc_list( nscd_cfg_param_desc_list_t **list) { return (_nscd_cfg_get_list((nscd_cfg_list_t **)list, NSCD_CFG_LIST_PARAM)); } /* * FUNCTION: _nscd_cfg_create_paramDB * * Create the internal config parameter database */ static nscd_db_t * _nscd_cfg_create_paramDB() { nscd_db_t *ret; (void) rw_wrlock(&cfg_paramDB_rwlock); ret = _nscd_alloc_db(NSCD_DB_SIZE_MEDIUM); if (ret != NULL) cfg_paramDB = ret; (void) rw_unlock(&cfg_paramDB_rwlock); return (ret); } /* * FUNCTION: _nscd_cfg_add_index_entry * * Add a config index entry (a name to index mapping) * to the internal configuration database. */ static nscd_rc_t _nscd_cfg_add_index_entry( char *name, int index, nscd_cfg_list_type_t type) { int *idx; int size; int dbe_type; nscd_db_entry_t *db_entry; if (name == NULL) return (NSCD_INVALID_ARGUMENT); if (type == NSCD_CFG_LIST_NSW_DB) dbe_type = NSCD_DATA_CFG_NSW_DB_INDEX; else if (type == NSCD_CFG_LIST_NSW_SRC) dbe_type = NSCD_DATA_CFG_NSW_SRC_INDEX; else if (type == NSCD_CFG_LIST_PARAM) dbe_type = NSCD_DATA_CFG_PARAM_INDEX; else if (type == NSCD_CFG_LIST_STAT) dbe_type = NSCD_DATA_CFG_STAT_INDEX; size = sizeof (int); db_entry = _nscd_alloc_db_entry(dbe_type, (const char *)name, size, 1, 1); if (db_entry == NULL) return (NSCD_NO_MEMORY); idx = (int *)*(db_entry->data_array); *idx = index; (void) rw_wrlock(&cfg_paramDB_rwlock); (void) _nscd_add_db_entry(cfg_paramDB, name, db_entry, NSCD_ADD_DB_ENTRY_FIRST); (void) rw_unlock(&cfg_paramDB_rwlock); return (NSCD_SUCCESS); } /* * FUNCTION: _nscd_cfg_get_index * * Get the index of a config data item by searching the internal config * database. Do not free the returned data. */ static int _nscd_cfg_get_index( char *name, nscd_cfg_list_type_t type) { int index = -1, dbe_type; const nscd_db_entry_t *db_entry; if (name == NULL) return (-1); if (type == NSCD_CFG_LIST_NSW_DB) dbe_type = NSCD_DATA_CFG_NSW_DB_INDEX; else if (type == NSCD_CFG_LIST_NSW_SRC) dbe_type = NSCD_DATA_CFG_NSW_SRC_INDEX; else if (type == NSCD_CFG_LIST_PARAM) dbe_type = NSCD_DATA_CFG_PARAM_INDEX; else if (type == NSCD_CFG_LIST_STAT) dbe_type = NSCD_DATA_CFG_STAT_INDEX; else return (-1); db_entry = _nscd_get_db_entry(cfg_paramDB, dbe_type, (const char *)name, NSCD_GET_FIRST_DB_ENTRY, 0); if (db_entry != NULL) index = *(int *)*(db_entry->data_array); return (index); } static nscd_rc_t _nscd_cfg_verify_group_info( nscd_cfg_group_info_t *g_info, nscd_cfg_param_desc_t *gdesc) { char *me = "_nscd_cfg_verify_group_info"; void *vp; nscd_cfg_group_info_t *gi; if (_nscd_cfg_flag_is_set(gdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) { vp = (char *)&nscd_cfg_global_default + gdesc->g_offset; gi = (nscd_cfg_group_info_t *)vp; } else { vp = (char *)&nscd_cfg_nsw_db_data_default + gdesc->g_offset; gi = (nscd_cfg_group_info_t *)vp; } if (g_info->num_param == gi->num_param && _nscd_cfg_bitmap_is_equal(g_info->bitmap, gi->bitmap)) return (NSCD_SUCCESS); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: group (%s) info mismatched: group info " "(%d, %#6.4x) not equal to that of default configuration data " "(%d, %#6.4x)\n", gdesc->id.name, g_info->num_param, _nscd_cfg_bitmap_value(g_info->bitmap), gi->num_param, _nscd_cfg_bitmap_value(gi->bitmap)); return (NSCD_CFG_PARAM_DESC_ERROR); } static nscd_rc_t _nscd_cfg_init_nsw() { char *me = "_nscd_cfg_init_nsw"; int i, j, idx, rc, num; nscd_cfg_id_t *id; nscd_cfg_list_type_t type[2] = { NSCD_CFG_LIST_NSW_DB, NSCD_CFG_LIST_NSW_SRC }; nscd_cfg_id_t *list[2] = { _nscd_cfg_nsw_db, NULL}; list[1] = _nscd_cfg_nsw_src_all; for (j = 0; j < 2; j++) { if (j == 0) num = _nscd_cfg_num_nsw_db + 1; else num = _nscd_cfg_num_nsw_src_all; for (i = 0; i < num; i++) { /* * _nscd_cfg_nsw_alldb is the id for the * special ALLDB (defaults for all db) */ if (j == 0 && i == _nscd_cfg_num_nsw_db) { id = &_nscd_cfg_nsw_alldb; idx = NSCD_CFG_NSW_ALLDB_INDEX; } else { id = &(list[j])[i]; id->index = idx = i; } if (id->name == NULL) continue; if ((rc = _nscd_cfg_add_index_entry(id->name, idx, type[j])) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to add index entry for " "nsswitch entry %s\n", id->name); _nscd_free_db(cfg_paramDB); return (rc); } } } return (NSCD_SUCCESS); } static nscd_rc_t _nscd_cfg_init_param() { char *me = "_nscd_cfg_init_param"; int i, gi, fn = 0; nscd_cfg_id_t *id; nscd_cfg_param_desc_t *desc, *gdesc = NULL; nscd_cfg_group_info_t g_info; nscd_cfg_list_type_t type = NSCD_CFG_LIST_PARAM; nscd_rc_t rc; void *nfunc, *vfunc; if (_nscd_cfg_create_paramDB() == NULL) return (NSCD_NO_MEMORY); desc = &_nscd_cfg_param_desc[0]; /* * need to loop to the last (+1) param description * which is a fake group and which marks the end * of list. It is used to signal the end of the * previous group so that the proper data will be * set for that group */ for (i = 0; i < _nscd_cfg_num_param + 1; i++, desc++) { id = (nscd_cfg_id_t *)desc; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) { if (gdesc != NULL) { g_info.num_param = fn; gdesc->p_fn = fn; if ((rc = _nscd_cfg_verify_group_info( &g_info, gdesc)) != NSCD_SUCCESS) return (rc); } gi = i; fn = 0; gdesc = desc; g_info.bitmap = NSCD_CFG_BITMAP_ZERO; /* * set the notify/verify functions */ nfunc = (void *)gdesc->notify; vfunc = (void *)gdesc->verify; } else { if (i == 0) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: first parameter " "description is not for a group\n"); return (NSCD_CFG_PARAM_DESC_ERROR); } /* * set bitmap: the rightmost bit represents * the first member (index = 0) in the group, * the next bit is for the second member * (index = 1), and so on */ _nscd_cfg_bitmap_set_nth(g_info.bitmap, fn); desc->p_fn = fn++; /* * set the notify/verify functions */ if (desc->notify == NSCD_CFG_FUNC_NOTIFY_AS_GROUP) { (void) memcpy(&desc->notify, &nfunc, sizeof (void *)); } if (desc->verify == NSCD_CFG_FUNC_VERIFY_AS_GROUP) { (void) memcpy(&desc->verify, &vfunc, sizeof (void *)); } } /* if end of list reached, we are done */ if (i == _nscd_cfg_num_param) break; desc->g_index = gi; id->index = i; if ((rc = _nscd_cfg_add_index_entry(id->name, i, type)) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to add index entry for parameter " "%s\n", id->name); _nscd_free_db(cfg_paramDB); return (rc); } else { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "index entry for parameter " "%s added\n", id->name); } } return (_nscd_cfg_init_nsw()); } static nscd_rc_t _nscd_cfg_init_stat() { char *me = "_nscd_cfg_init_stat"; int i, gi, fn = 0; nscd_cfg_id_t *id; nscd_cfg_stat_desc_t *desc, *gdesc = NULL; nscd_cfg_group_info_t g_info; nscd_cfg_list_type_t type = NSCD_CFG_LIST_STAT; nscd_rc_t rc; void *gsfunc; desc = &_nscd_cfg_stat_desc[0]; /* * need to loop to the last (+1) stat description * which is a fake group and which marks the end * of list. It is used to signal the end of the * previous group so that the proper data will be * set for that group */ for (i = 0; i < _nscd_cfg_num_stat + 1; i++, desc++) { id = (nscd_cfg_id_t *)desc; if (_nscd_cfg_flag_is_set(desc->sflag, NSCD_CFG_SFLAG_GROUP)) { if (gdesc != NULL) { g_info.num_param = fn; gdesc->s_fn = fn; if (g_info.num_param != gdesc->gi.num_param || !_nscd_cfg_bitmap_is_equal( g_info.bitmap, gdesc->gi.bitmap)) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: group (%s) " "info mismatched: " "group info (%d, %#6.4x) not " "equal to the predefined one " "(%d, %#6.4x)\n", gdesc->id.name, g_info.num_param, _nscd_cfg_bitmap_value( g_info.bitmap), gdesc->gi.num_param, _nscd_cfg_bitmap_value( gdesc->gi.bitmap)); exit(1); return (NSCD_CFG_STAT_DESC_ERROR); } } gi = i; fn = 0; gdesc = desc; g_info.bitmap = NSCD_CFG_BITMAP_ZERO; /* * set the get_stat function */ gsfunc = (void *)gdesc->get_stat; } else { if (i == 0) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: first stat " "description is not for a group\n"); return (NSCD_CFG_STAT_DESC_ERROR); } /* * set bitmap: the rightmost bit represents * the first member (index = 0) in the group, * the next bit is for the second member * (index = 1), and so on */ _nscd_cfg_bitmap_set_nth(g_info.bitmap, fn); desc->s_fn = fn++; /* * set the get_stat function */ if (desc->get_stat == NSCD_CFG_FUNC_GET_STAT_AS_GROUP) { (void) memcpy(&desc->get_stat, &gsfunc, sizeof (void *)); } } /* if end of list reached, we are done */ if (i == _nscd_cfg_num_stat) break; desc->g_index = gi; id->index = i; if ((rc = _nscd_cfg_add_index_entry(id->name, i, type)) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to add index entry for stat " "description %s\n", id->name); _nscd_free_db(cfg_paramDB); return (rc); } else { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "index entry for stat description " "%s added\n", id->name); } } return (NSCD_SUCCESS); } static nscd_rc_t _nscd_cfg_copy_vlen_data( void *data, void **new_data_p, nscd_cfg_param_desc_t *desc, int *data_len, nscd_bool_t in) { int len, dlen; nscd_cfg_vlen_data_t *v = NULL; *new_data_p = NULL; *data_len = 0; /* it is OK if there is nothing to copy */ if (data == NULL) return (NSCD_SUCCESS); /* * if copy to the config store we need to allocate space * for the extra vlen header */ if (desc->type == NSCD_CFG_DATA_STRING) { len = dlen = strlen((char *)data) + 1; if (in == nscd_true) len += sizeof (nscd_cfg_vlen_data_t); } else { /* * should not be here, since for now * only string variable length data * is supported */ *new_data_p = NULL; return (NSCD_CFG_PARAM_DESC_ERROR); } v = calloc(1, len); if (v == NULL) { *new_data_p = NULL; return (NSCD_NO_MEMORY); } /* * if copy to the config store, set up * the extra vlen header in which the * pointer to, and length of, the real * data are kept. The pointer to the real * data, not the vlen header, is returned. */ if (in == nscd_true) { v->ptr = (char *)v + sizeof (nscd_cfg_vlen_data_t); v->len = dlen; (void) memcpy(v->ptr, data, dlen); *new_data_p = v->ptr; } else { (void) memcpy(v, data, dlen); *new_data_p = v; } *data_len = dlen; return (NSCD_SUCCESS); } static void _nscd_cfg_free_vlen_data_int( void *data) { nscd_cfg_vlen_data_t *v = NULL; void *p; if (data == NULL) return; p = (char *)data - sizeof (nscd_cfg_vlen_data_t); v = (nscd_cfg_vlen_data_t *)p; if (v->ptr == data) free(v); } static nscd_rc_t _nscd_cfg_set_vlen_data_int( void *src, void *dest, nscd_bool_t global) { int i, offset, dlen = 0; void *s, *d, *new; void *cptr; nscd_rc_t rc; nscd_cfg_param_desc_t *desc; desc = &_nscd_cfg_param_desc[0]; for (i = 0; i < _nscd_cfg_num_param; i++, desc++) { if (global == nscd_true && _nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_GLOBAL)) continue; else if (global != nscd_true && _nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GLOBAL)) continue; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) { offset = desc->g_offset + desc->p_offset; s = (char *)src + offset; cptr = *(char **)s; rc = _nscd_cfg_copy_vlen_data(cptr, &new, desc, &dlen, nscd_true); if (rc != NSCD_SUCCESS) return (rc); d = (char *)dest + offset; /* free the old vlen data */ if (*(char **)d == NULL) _nscd_cfg_free_vlen_data_int(*(char **)d); *(char **)d = new; } } return (NSCD_SUCCESS); } static void * _nscd_cfg_locate_vlen_data( void *cfg_data, int *len) { void *ptr, *ret; ptr = *(char **)cfg_data; ret = ptr; if (ret == NULL) { *len = 0; return (NULL); } ptr = (char *)ptr - sizeof (nscd_cfg_vlen_data_t); *len = ((nscd_cfg_vlen_data_t *)ptr)->len; return (ret); } static void _nscd_cfg_lock( nscd_bool_t is_read, nscd_cfg_lock_t *cfglock) { int (*lockfunc)(rwlock_t *); if (cfglock == NULL) return; if (is_read == nscd_true) lockfunc = rw_rdlock; else lockfunc = rw_wrlock; if (cfglock->global != NULL) { (lockfunc)(cfglock->global); return; } if (cfglock->alldb != NULL) (lockfunc)(cfglock->alldb); if (cfglock->nswdb != NULL) (lockfunc)(cfglock->nswdb); } static void _nscd_cfg_unlock( nscd_cfg_lock_t *cfglock) { if (cfglock == NULL) return; if (cfglock->global != NULL) { (void) rw_unlock(cfglock->global); free(cfglock); return; } if (cfglock->nswdb != NULL) (void) rw_unlock(cfglock->nswdb); if (cfglock->alldb != NULL) (void) rw_unlock(cfglock->alldb); free(cfglock); } /* * If vlen_data_addr is given, it will be set to the * address of the pointer pointing to the vlen data. * 'cfglock' will be set to point to the reader/writer * lock(s) protecting the (group) configuration data. */ static nscd_rc_t _nscd_cfg_locate_cfg_data( void **cfg_data, nscd_bool_t is_read, nscd_cfg_param_desc_t *desc, nscd_cfg_id_t *nswdb, nscd_bool_t get_group, void **vlen_data_addr, int *len, nscd_cfg_lock_t **cfglock) { int offset; *cfg_data = NULL; if (len != NULL) *len = 0; if (vlen_data_addr != NULL) *vlen_data_addr = NULL; if (cfglock != NULL) { *cfglock = calloc(1, sizeof (nscd_cfg_lock_t)); if (*cfglock == NULL) return (NSCD_NO_MEMORY); } /* assume if nswdb is NULL, the param is a global one */ if (nswdb == NULL) { offset = desc->g_offset; if (get_group != nscd_true) offset += desc->p_offset; *cfg_data = (char *)nscd_cfg_global_current + offset; if (cfglock != NULL) (*cfglock)->global = nscd_cfg_global_rwlock; } else if (nswdb->index == NSCD_CFG_NSW_ALLDB_INDEX) { offset = desc->g_offset; if (get_group != nscd_true) offset += desc->p_offset; *cfg_data = (char *)nscd_cfg_nsw_alldb_current + offset; if (cfglock != NULL) (*cfglock)->alldb = nscd_cfg_nsw_alldb_rwlock; } else { offset = nswdb->index * (sizeof (nscd_cfg_nsw_db_data_t)) + desc->g_offset; if (get_group != nscd_true) offset += desc->p_offset; *cfg_data = (char *)nscd_cfg_nsw_db_data_current + offset; if (cfglock != NULL) { (*cfglock)->nswdb = &nscd_cfg_nsw_db_data_rwlock[nswdb->index]; (*cfglock)->alldb = nscd_cfg_nsw_alldb_rwlock; } } /* lock the config data */ if (cfglock != NULL) _nscd_cfg_lock(is_read, *cfglock); if (get_group != nscd_true && _nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_GROUP) && (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA))) { if (vlen_data_addr != NULL) *vlen_data_addr = *cfg_data; *cfg_data = _nscd_cfg_locate_vlen_data(*cfg_data, len); return (NSCD_SUCCESS); } if (len != NULL) { if (get_group == nscd_true) *len = desc->g_size; else *len = desc->p_size; } return (NSCD_SUCCESS); } /* * perform the preliminary (range) check on 'data' based on the * datatype (desc->datatype) of the config parameter */ nscd_rc_t _nscd_cfg_prelim_check( nscd_cfg_param_desc_t *desc, void *data, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_prelim_check"; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; nscd_cfg_str_check_t *sc; nscd_cfg_int_check_t *ic; nscd_cfg_bitmap_check_t *bmc; nscd_rc_t rc = NSCD_CFG_PRELIM_CHECK_FAILED; if ((nscd_cfg_str_check_t *)desc->p_check == NULL) return (NSCD_SUCCESS); switch (desc->type) { case NSCD_CFG_DATA_STRING: sc = (nscd_cfg_str_check_t *)desc->p_check; if (sc->must_not_null == nscd_true && data == NULL) { if (errorp == NULL) break; (void) snprintf(msg, sizeof (msg), gettext("data must be specified for %s"), desc->id.name); break; } if (data == NULL) { rc = NSCD_SUCCESS; break; } if (sc->maxlen != 0 && strlen((char *)data) > sc->maxlen) { if (errorp == NULL) break; (void) snprintf(msg, sizeof (msg), gettext("length of data (%s) for %s larger " "than %d"), (char *)data, desc->id.name, sc->maxlen); break; } rc = NSCD_SUCCESS; break; case NSCD_CFG_DATA_INTEGER: ic = (nscd_cfg_int_check_t *)desc->p_check; if (*(int *)data > ic->max || *(int *)data < ic->min) { if (errorp == NULL) break; (void) snprintf(msg, sizeof (msg), gettext("data (%d) for %s out of range " "(%d - %d)"), *(int *)data, desc->id.name, ic->min, ic->max); break; } rc = NSCD_SUCCESS; break; case NSCD_CFG_DATA_BITMAP: bmc = (nscd_cfg_bitmap_check_t *)desc->p_check; if (_nscd_cfg_bitmap_value(*(nscd_cfg_bitmap_t *)data) & ~(bmc->valid_bits)) { if (errorp == NULL) break; (void) snprintf(msg, sizeof (msg), gettext("data (%#6.4x) for %s contain bit " "not in 0x%x"), _nscd_cfg_bitmap_value( *(nscd_cfg_bitmap_t *)data), desc->id.name, _nscd_cfg_bitmap_value(bmc->valid_bits)); break; } rc = NSCD_SUCCESS; break; } if (rc != NSCD_SUCCESS && errorp != NULL) { *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "invalid argument: %s\n", (*errorp)->msg); } return (rc); } static nscd_rc_t _nscd_cfg_notify_i( nscd_cfg_param_desc_t *desc, nscd_cfg_id_t *nswdb, int *skip, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_notify_i"; int i, num, skip_bk; void *cfg_data, *cdata; void *cookie = NULL; nscd_rc_t rc; nscd_cfg_flag_t dflag, dflag1; nscd_cfg_bitmap_t bitmap_c, bitmap_s, *bitmap_addr; nscd_cfg_group_info_t *gi; if (errorp != NULL) *errorp = NULL; if (skip == NULL) skip = &skip_bk; *skip = 0; if (_nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: expect parameter description for group, " "but receive parameter description is for %s\n", desc->id.name); return (NSCD_CFG_PARAM_DESC_ERROR); } /* * Set data flag going with data to be sent to the * verify/notify routines. Allowing the config flag * be exipandable, set the bits one by one. */ dflag = NSCD_CFG_FLAG_ZERO; dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_STATIC_DATA); dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_INIT); dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP); if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_INIT_SET_ALL_DB)) dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_SET_ALL_DB); /* get to the group data in the config store */ rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_true, desc, nswdb, nscd_true, NULL, NULL, NULL); if (rc != NSCD_SUCCESS) goto error; /* * the static bitmap associated with the group * may be replaced before sending to the components, * so save the bitmap for later use */ gi = _nscd_cfg_get_gi(cfg_data); bitmap_c = gi->bitmap; bitmap_addr = &(gi->bitmap); /* * the elements in this group will all be handled * so the caller can skip them */ *skip = desc->p_fn; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP)) /* send the entire group just once */ num = 1; else { /* send individual members one by one */ num = desc->p_fn; /* * skip the first desc which is for the group * and get to the desc for the first member */ desc++; dflag = _nscd_cfg_flag_unset(dflag, NSCD_CFG_DFLAG_GROUP); } dflag1 = dflag; for (i = 0; i < num; i++, desc++) { dflag = dflag1; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_SEND_BIT_SELECTED)) { /* set the bitmap to select just this member */ bitmap_s = NSCD_CFG_BITMAP_ZERO; _nscd_cfg_bitmap_set_nth(bitmap_s, i); /* replace the bitmap in the cfg data */ _nscd_cfg_bitmap_set(bitmap_addr, bitmap_s); /* * send the whole group but with only one * member selected */ cdata = cfg_data; dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP); dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_BIT_SELECTED); } else { /* * send param data or group data: * param data - non-xero desc->p_offset * group data - zero desc->p_offset */ cdata = (char *)cfg_data + desc->p_offset; /* * if variable length data, need to send pointer * to the data (not the address of the pointer) */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) cdata = *(char **)cdata; } if (desc->verify != NULL) { dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_VERIFY); rc = desc->verify(cdata, desc, nswdb, dflag, errorp, &cookie); if (rc != NSCD_SUCCESS) goto error; } if (desc->notify != NULL) { dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_NOTIFY); rc = desc->notify(cfg_data, desc, nswdb, dflag, errorp, cookie); if (rc != NSCD_SUCCESS) goto error; } } rc = NSCD_SUCCESS; /* restore the bitmap in the cfg data */ _nscd_cfg_bitmap_set(bitmap_addr, bitmap_c); error: return (rc); } static nscd_rc_t _nscd_cfg_notify_init( nscd_cfg_error_t **errorp) { int i, j, skip; nscd_rc_t rc; nscd_cfg_id_t *nswdb = NULL; nscd_cfg_param_desc_t *desc; if (errorp != NULL) *errorp = NULL; for (i = 0; i < _nscd_cfg_num_param; i++) { desc = &_nscd_cfg_param_desc[i]; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GLOBAL)) { /* global cfg data */ rc = _nscd_cfg_notify_i(desc, NULL, &skip, errorp); } else { /* * if use defaults for all nsswitch database, * send the config data to verify/notify once */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_INIT_SET_ALL_DB)) { nswdb = &_nscd_cfg_nsw_alldb; rc = _nscd_cfg_notify_i(desc, nswdb, &skip, errorp); } else { /* send data once for each nsw db */ for (j = 0; j < _nscd_cfg_num_nsw_db; j++) { nswdb = &_nscd_cfg_nsw_db[j]; rc = _nscd_cfg_notify_i(desc, nswdb, &skip, errorp); if (rc != NSCD_SUCCESS) break; } } } if (rc != NSCD_SUCCESS) return (rc); i += skip; } return (NSCD_SUCCESS); } nscd_rc_t _nscd_cfg_init( nscd_cfg_error_t **errorp) { int i, j, datalen; int dbi = 0, dbj = 0; char *dest, *src; char *dbni = NULL, *dbnj = NULL; nscd_rc_t rc; nscd_cfg_nsw_spc_default_t *spc; if (errorp != NULL) *errorp = NULL; rc = _nscd_cfg_init_param(); if (rc != NSCD_SUCCESS) return (rc); rc = _nscd_cfg_init_stat(); if (rc != NSCD_SUCCESS) return (rc); nscd_cfg_global_current = calloc(1, sizeof (nscd_cfg_global_data_t)); if (nscd_cfg_global_current == NULL) return (NSCD_NO_MEMORY); nscd_cfg_nsw_alldb_current = calloc(1, sizeof (nscd_cfg_nsw_db_data_t)); if (nscd_cfg_nsw_alldb_current == NULL) return (NSCD_NO_MEMORY); nscd_cfg_nsw_db_data_current = calloc(_nscd_cfg_num_nsw_db, sizeof (nscd_cfg_nsw_db_data_t)); if (nscd_cfg_nsw_db_data_current == NULL) return (NSCD_NO_MEMORY); nscd_cfg_global_rwlock = calloc(1, sizeof (rwlock_t)); if (nscd_cfg_global_rwlock == NULL) return (NSCD_NO_MEMORY); (void) rwlock_init(nscd_cfg_global_rwlock, USYNC_THREAD, NULL); *nscd_cfg_global_current = nscd_cfg_global_default; rc = _nscd_cfg_set_vlen_data_int(&nscd_cfg_global_default, nscd_cfg_global_current, nscd_true); if (rc != NSCD_SUCCESS) return (rc); nscd_cfg_nsw_db_data_rwlock = calloc(_nscd_cfg_num_nsw_db, sizeof (rwlock_t)); if (nscd_cfg_nsw_db_data_rwlock == NULL) return (NSCD_NO_MEMORY); /* set per switch db config to the default for all db's */ for (i = 0; i < _nscd_cfg_num_nsw_db; i++) { nscd_cfg_nsw_db_data_current[i] = nscd_cfg_nsw_db_data_default; (void) rwlock_init(&nscd_cfg_nsw_db_data_rwlock[i], 0, NULL); } /* add db specific defaults */ for (i = 0; i < _nscd_cfg_num_nsw_default; i++) { if (_nscd_cfg_nsw_spc_default[i].data == NULL) continue; if (_nscd_cfg_nsw_spc_default[i].db != dbni) { for (j = 0; j < _nscd_cfg_num_nsw_db; j++) { if (strcmp(_nscd_cfg_nsw_db[j].name, _nscd_cfg_nsw_spc_default[i].db) != 0) continue; dbi = _nscd_cfg_nsw_db[j].index; dbni = _nscd_cfg_nsw_db[j].name; break; } } dest = (char *)&nscd_cfg_nsw_db_data_current[dbi] + _nscd_cfg_nsw_spc_default[i].group_off + _nscd_cfg_nsw_spc_default[i].param_off; src = _nscd_cfg_nsw_spc_default[i].data; datalen = _nscd_cfg_nsw_spc_default[i].data_len; (void) memcpy(dest, src, datalen); } /* add db specific defaults via links */ for (i = 0; i < _nscd_cfg_num_link_default; i++) { if (_nscd_cfg_nsw_link_default[i].data == NULL) continue; spc = _nscd_cfg_nsw_link_default[i].data; if (_nscd_cfg_nsw_link_default[i].db != dbni) { for (j = 0; j < _nscd_cfg_num_nsw_db; j++) { if (strcmp(_nscd_cfg_nsw_db[j].name, _nscd_cfg_nsw_link_default[i].db) != 0) continue; dbi = _nscd_cfg_nsw_db[j].index; dbni = _nscd_cfg_nsw_db[j].name; break; } } dest = (char *)&nscd_cfg_nsw_db_data_current[dbi] + _nscd_cfg_nsw_link_default[i].group_off + _nscd_cfg_nsw_link_default[i].param_off; if (_nscd_cfg_nsw_db[j].name != dbnj) { for (j = 0; j < _nscd_cfg_num_nsw_db; j++) { if (strcmp(spc->db, _nscd_cfg_nsw_db[j].name) != 0) continue; dbnj = _nscd_cfg_nsw_db[j].name; dbj = _nscd_cfg_nsw_db[j].index; break; } } src = (char *)&nscd_cfg_nsw_db_data_current[dbj] + spc->group_off + spc->param_off; datalen = spc->data_len; (void) memcpy(dest, src, datalen); } /* fix up variable length fields */ for (i = 0; i < _nscd_cfg_num_nsw_db; i++) { rc = _nscd_cfg_set_vlen_data_int( &nscd_cfg_nsw_db_data_current[i], &nscd_cfg_nsw_db_data_current[i], nscd_false); if (rc != NSCD_SUCCESS) return (rc); } nscd_cfg_nsw_alldb_rwlock = calloc(1, sizeof (rwlock_t)); if (nscd_cfg_nsw_alldb_rwlock == NULL) return (NSCD_NO_MEMORY); (void) rwlock_init(nscd_cfg_nsw_alldb_rwlock, 0, NULL); rc = _nscd_cfg_set_vlen_data_int( &nscd_cfg_nsw_db_data_default, nscd_cfg_nsw_alldb_current, nscd_false); if (rc != NSCD_SUCCESS) return (rc); /* * notify and send the configuration data to * the nscd components */ rc = _nscd_cfg_notify_init(errorp); if (rc != NSCD_SUCCESS) return (rc); return (NSCD_SUCCESS); } static nscd_rc_t _nscd_cfg_get_handle_common( nscd_cfg_list_type_t type, char *name, char *nswdb_name, nscd_cfg_handle_t **handle, nscd_cfg_error_t **errorp) { int i, is_global; char *desc_str; nscd_cfg_handle_t *h; nscd_cfg_param_desc_t *pdesc; nscd_cfg_stat_desc_t *sdesc; char *me = "_nscd_cfg_get_handle_common"; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; nscd_rc_t rc = NSCD_INVALID_ARGUMENT; if (handle == NULL) { (void) snprintf(msg, sizeof (msg), gettext("address of handle not specified")); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "invalid argument: %s\n", msg); return (rc); } *handle = NULL; if (name == NULL) { (void) snprintf(msg, sizeof (msg), gettext("name not specified")); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "invalid argument: %s\n"); return (rc); } h = calloc(1, sizeof (nscd_cfg_handle_t)); if (h == NULL) return (NSCD_NO_MEMORY); h->type = type; if (type == NSCD_CFG_LIST_PARAM) desc_str = gettext("configuration parameter"); else desc_str = gettext("statistics"); /* get param or stat descriptor */ i = _nscd_cfg_get_index(name, type); if (i != -1) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "%s: index of %s is %d\n", desc_str, name, i); if (type == NSCD_CFG_LIST_PARAM) { pdesc = &_nscd_cfg_param_desc[i]; (void) memcpy(&h->desc, &pdesc, sizeof (pdesc)); is_global = _nscd_cfg_flag_is_set( pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL); /* hidden params are not exposed */ if (_nscd_cfg_flag_is_set( pdesc->pflag, NSCD_CFG_PFLAG_HIDDEN)) i = -1; if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_OBSOLETE)) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_WARNING) (me, gettext("%s: %s is obsolete and " "will be ignored\n"), desc_str, name); } } else { sdesc = &_nscd_cfg_stat_desc[i]; (void) memcpy(&h->desc, &sdesc, sizeof (sdesc)); is_global = _nscd_cfg_flag_is_set( sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL); } } if (i == -1) { (void) snprintf(msg, sizeof (msg), gettext("%s: unknown name \"%s\""), desc_str, name); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); free(h); return (rc); } /* * if the param/stat is not a global one, we need to * know which nsswitch database we are dealing with */ if (is_global == 0) { if (nswdb_name == NULL) { (void) snprintf(msg, sizeof (msg), gettext("%s: switch database name not specified"), desc_str); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s for non-global param or stat %s\n", msg, name); free(h); return (rc); } } else { if (nswdb_name != NULL) { (void) snprintf(msg, sizeof (msg), gettext("%s: switch database specified for " "global data"), desc_str); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s %s\n", msg, name); free(h); return (rc); } *handle = h; return (NSCD_SUCCESS); } /* get nsw DB id */ i = _nscd_cfg_get_index(nswdb_name, NSCD_CFG_LIST_NSW_DB); if (i != -1) { if (i == NSCD_CFG_NSW_ALLDB_INDEX) h->nswdb = &_nscd_cfg_nsw_alldb; else h->nswdb = &_nscd_cfg_nsw_db[i]; _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "%s: index of %s is %d\n", desc_str, nswdb_name, i); } else { (void) snprintf(msg, sizeof (msg), gettext("%s: unknown switch database name \"%s\""), desc_str, nswdb_name); if (errorp) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", msg); free(h); return (NSCD_CFG_UNSUPPORTED_SWITCH_DB); } *handle = h; return (NSCD_SUCCESS); } nscd_rc_t _nscd_cfg_get_handle( char *param_name, char *nswdb_name, nscd_cfg_handle_t **handle, nscd_cfg_error_t **errorp) { return (_nscd_cfg_get_handle_common(NSCD_CFG_LIST_PARAM, param_name, nswdb_name, handle, errorp)); } nscd_rc_t _nscd_cfg_get_stat_handle( char *stat_name, char *nswdb_name, nscd_cfg_handle_t **handle, nscd_cfg_error_t **errorp) { return (_nscd_cfg_get_handle_common(NSCD_CFG_LIST_STAT, stat_name, nswdb_name, handle, errorp)); } void _nscd_cfg_free_handle( nscd_cfg_handle_t *handle) { free(handle); } static void _nscd_cfg_free_vlen_data_group( nscd_cfg_param_desc_t *gdesc, void *group_data, nscd_bool_t in) { int num; void *dest, *ptr; nscd_cfg_param_desc_t *desc; desc = gdesc; num = ((nscd_cfg_group_info_t *)group_data)->num_param; while (num-- > 0) { desc++; /* skip fixed length data */ if (_nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) continue; dest = (char *)group_data + desc->p_offset; ptr = *(char **)dest; if (ptr == NULL) continue; if (in == nscd_true) _nscd_cfg_free_vlen_data_int(ptr); else free(ptr); } } void _nscd_cfg_free_param_data( void *data) { if (data == NULL) return; free(data); } void _nscd_cfg_free_group_data( nscd_cfg_handle_t *handle, void *data) { nscd_cfg_param_desc_t *desc; nscd_cfg_group_info_t *gi; if (handle == NULL || data == NULL) return; desc = _nscd_cfg_get_desc(handle); gi = (nscd_cfg_group_info_t *)data; if (desc->p_fn != gi->num_param) return; _nscd_cfg_free_vlen_data_group(desc, data, nscd_false); free(data); } void _nscd_cfg_free_error( nscd_cfg_error_t *error) { if (error == NULL) return; free(error); } static nscd_rc_t _nscd_cfg_copy_param_data( nscd_cfg_param_desc_t *desc, void *dest, void *pdata, nscd_bool_t in, nscd_bool_t set_addr) { char *me = "_nscd_cfg_copy_param_data"; void *tmp; int dlen; nscd_rc_t rc = NSCD_SUCCESS; if (desc == NULL || dest == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "input desc == %p, dest == %p\n", desc, dest); return (NSCD_INVALID_ARGUMENT); } /* fixed length data */ if (_nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) { (void) memcpy(dest, pdata, desc->p_size); goto done; } /* variable length data from this point on */ /* make a copy of the variable length data */ rc = _nscd_cfg_copy_vlen_data(pdata, &tmp, desc, &dlen, in); if (rc != NSCD_SUCCESS) goto done; if (in == nscd_true) { /* data to internal */ /* free the variable length data in the config store */ if (*(char **)dest != NULL) _nscd_cfg_free_vlen_data_int(*(char **)dest); } if (set_addr == nscd_true) { /* * set the addr of the vlen data */ *(char **)dest = tmp; } else { /* * copy the data content (not address) */ (void) memcpy(dest, tmp, dlen); } done: return (rc); } static nscd_rc_t _nscd_cfg_copy_group_data_in( nscd_cfg_param_desc_t *gdesc, nscd_cfg_group_info_t *gi, void *group_dest, void *group_src) { int i, num; nscd_cfg_param_desc_t *desc; void *src, *dest; i = 0; num = gi->num_param; desc = gdesc; while (num-- > 0) { desc++; /* if member not selected by bitmap, skip */ if (_nscd_cfg_bitmap_is_not_set(gi->bitmap, i++)) continue; src = (char *)group_src + desc->p_offset; dest = (char *)group_dest + desc->p_offset; /* * if variable length data, free and replace the old * with the new */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) { _nscd_cfg_free_vlen_data_int(*(char **)dest); *(char **)dest = *(char **)src; *(char **)src = NULL; } else { /* * fixed length data, just copy it */ (void) memcpy(dest, src, desc->p_size); } } return (NSCD_SUCCESS); } static nscd_rc_t _nscd_cfg_copy_group_data_out( nscd_cfg_param_desc_t *gdesc, void *group_dest, void *group_src) { char *me = "_nscd_cfg_copy_group_data_out"; void *src, *dest; int dlen; int num; nscd_cfg_group_info_t *gi; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_param_desc_t *desc; if (group_dest == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "input group_dest = NULL\n"); return (NSCD_INVALID_ARGUMENT); } gi = _nscd_cfg_get_gi(group_src); num = gi->num_param; desc = gdesc; while (num-- > 0) { desc++; dest = (char *)group_dest + desc->p_offset; src = (char *)group_src + desc->p_offset; /* * if variable length data, get the real * address and length of the data */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) { src = _nscd_cfg_locate_vlen_data(src, &dlen); if (dlen == 0) continue; } /* * The nscd_true asks _nscd_cfg_copy_param_data * to set addr of the vlen data in 'dest' rather * than copying the data content */ rc = _nscd_cfg_copy_param_data(desc, dest, src, nscd_false, nscd_true); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to copy param data for %s\n", desc->id.name); _nscd_cfg_free_vlen_data_group(gdesc, group_dest, nscd_false); free(group_dest); return (rc); } } /* * set group bitmap */ (void) memcpy(group_dest, group_src, sizeof (nscd_cfg_group_info_t)); return (rc); } /* * group_cfg is needed always; group_src may be NULL if * param_index not zero and pdata not NULL; group_cfg and * pdata should not be both non-NULL */ static nscd_rc_t _nscd_cfg_copy_group_data_merge( nscd_cfg_param_desc_t *gdesc, void **group_dest, void *group_src, void *group_cfg, int param_index, void *pdata) { char *me = "_nscd_cfg_copy_group_data_merge"; void *src, *dest, *tmp_dest = NULL; int num, i = 0; nscd_cfg_group_info_t *gi; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_param_desc_t *desc; nscd_cfg_bitmap_t bitmap; if (group_dest == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "input **group_dest == NULL\n"); return (NSCD_INVALID_ARGUMENT); } if (group_cfg == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "input **group_cfg == NULL\n"); return (NSCD_INVALID_ARGUMENT); } if (param_index != 0 && pdata == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "param_index != NULL but pdata == %p\n", pdata); return (NSCD_INVALID_ARGUMENT); } tmp_dest = calloc(1, gdesc->g_size); if (tmp_dest == NULL) return (NSCD_NO_MEMORY); if (group_src != NULL) gi = _nscd_cfg_get_gi(group_src); else { gi = _nscd_cfg_get_gi(group_cfg); bitmap = NSCD_CFG_BITMAP_ZERO; } num = gi->num_param; desc = gdesc; while (num-- > 0) { desc++; dest = (char *)tmp_dest + desc->p_offset; /* * if member not selected by bitmap in group_src, * get the member data in group_cfg */ if (_nscd_cfg_bitmap_is_not_set(gi->bitmap, i++) || group_src == NULL) { src = (char *)group_cfg + desc->p_offset; } else src = (char *)group_src + desc->p_offset; if (desc->id.index == param_index) { /* use the param data in pdata if provided */ src = pdata; _nscd_cfg_bitmap_set_nth(bitmap, i); } /* * if variable length data, get to the data * instead of pointer to the data */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) src = *(char **)src; /* * nscd_true asks _nscd_cfg_copy_param_data to * set addr of the vlen data in 'dest' rather * than copying the data content */ rc = _nscd_cfg_copy_param_data(desc, dest, src, nscd_true, nscd_true); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to copy param data for %s\n", desc->id.name); _nscd_cfg_free_vlen_data_group(gdesc, tmp_dest, nscd_true); free(tmp_dest); return (rc); } } *group_dest = tmp_dest; /* * set bitmap: if input is group data, use the one * given; if input is param data, use the one computed * above */ if (group_src != NULL) (void) memcpy(*group_dest, group_src, sizeof (nscd_cfg_group_info_t)); else { gi = _nscd_cfg_get_gi(*group_dest); _nscd_cfg_bitmap_set(&gi->bitmap, bitmap); } return (rc); } /* ARGSUSED */ nscd_rc_t _nscd_cfg_get( nscd_cfg_handle_t *handle, void **data, int *data_len, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_get"; int dlen; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_id_t *nswdb; nscd_cfg_param_desc_t *desc; void *cfg_data, *ptr = NULL; nscd_bool_t get_group = nscd_false; nscd_bool_t out = nscd_false; nscd_cfg_lock_t *lock = NULL; if (data_len != NULL) *data_len = 0; if (data == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "input data = %p\n", data); return (NSCD_INVALID_ARGUMENT); } *data = NULL; if (handle == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "handle is NULL\n"); return (NSCD_INVALID_ARGUMENT); } nswdb = handle->nswdb; desc = (nscd_cfg_param_desc_t *)handle->desc; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) get_group = nscd_true; /* * locate the current value of the param or group * and lock the config data for reading */ rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_true, desc, nswdb, get_group, NULL, &dlen, &lock); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to locate config data\n"); return (rc); } else if (cfg_data == NULL) /* NULL vlen data */ goto done; ptr = calloc(1, dlen); if (ptr == NULL) { rc = NSCD_NO_MEMORY; goto error_exit; } if (get_group == nscd_true) { rc = _nscd_cfg_copy_group_data_out(desc, ptr, cfg_data); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to copy group data %p: " "error = %d\n", cfg_data, rc); goto error_exit; } } else { /* * nscd_false asks _nscd_cfg_copy_param_data to * copy the data content rather than just setting * the addr of the vlen data in 'ptr' */ rc = _nscd_cfg_copy_param_data(desc, ptr, cfg_data, out, nscd_false); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to copy param data %p: " "error = %d\n", cfg_data, rc); goto error_exit; } } *data = ptr; done: if (data_len != NULL) *data_len = dlen; _nscd_cfg_unlock(lock); return (NSCD_SUCCESS); error_exit: _nscd_cfg_unlock(lock); if (ptr != NULL) free(ptr); return (rc); } /* * three type of data: * 1 - single param * desc is that of the param * 2 - single param to be sent in a group * a single bit is set in the bitmap, * desc is that of the group * 3 - group data * one of more bits are set in the bitmap, * desc is that of the group */ static nscd_rc_t _nscd_cfg_notify_s( nscd_cfg_param_desc_t *desc, nscd_cfg_id_t *nswdb, void *data, nscd_cfg_error_t **errorp) { int i, num, is_group = 0; void *cookie = NULL; void *cdata; nscd_rc_t rc; nscd_cfg_flag_t dflag, dflag1; nscd_cfg_bitmap_t bitmap_s, bitmap_in, *bitmap_addr = NULL; nscd_cfg_group_info_t *gi; if (errorp != NULL) *errorp = NULL; /* * Set data flag going with data to be sent to the * verify/notify routines. To allow the config flag * be exipandable, set the bits one by one. */ dflag = NSCD_CFG_FLAG_ZERO; dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_STATIC_DATA); if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) { dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP); is_group = 1; } if (nswdb != NULL && strcmp(NSCD_CFG_NSW_ALLDB, nswdb->name) == 0) dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_SET_ALL_DB); /* * the bitmap in the input data may be replaced before * sending to the components, so save the bitmap for * later use */ if (is_group == 1) { gi = _nscd_cfg_get_gi(data); bitmap_in = gi->bitmap; bitmap_addr = &(gi->bitmap); if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP)) /* send the entire group just once */ num = 1; else { /* send individual members one by one */ num = desc->p_fn; /* * skip the first desc which is for the group * and get to the desc for the first member */ desc++; dflag = _nscd_cfg_flag_unset(dflag, NSCD_CFG_DFLAG_GROUP); } } else { /* not group data, send the member once */ num = 1; } dflag1 = dflag; for (i = 0; i < num; i++, desc++) { dflag = dflag1; if (is_group == 0) { cdata = data; goto verify_data; } if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_SEND_BIT_SELECTED)) { /* set the bitmap to select just this member */ bitmap_s = NSCD_CFG_BITMAP_ZERO; _nscd_cfg_bitmap_set_nth(bitmap_s, i); /* replace the bitmap in the input data */ _nscd_cfg_bitmap_set(bitmap_addr, bitmap_s); /* * send the whole group but with only one * member selected */ cdata = data; dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_GROUP); dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_BIT_SELECTED); } else { /* * send param data or group data: * param data - non-xero desc->p_offset * group data - zero desc->p_offset */ cdata = (char *)data + desc->p_offset; /* * if variable length data, need to send pointer * to the data (not the address of the pointer) */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) cdata = *(char **)cdata; } verify_data: if (desc->verify != NULL) { dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_VERIFY); rc = desc->verify(cdata, desc, nswdb, dflag, errorp, &cookie); if (rc != NSCD_SUCCESS) goto error_exit; } if (desc->notify != NULL) { dflag = _nscd_cfg_flag_set(dflag, NSCD_CFG_DFLAG_NOTIFY); rc = desc->notify(data, desc, nswdb, dflag, errorp, cookie); if (rc != NSCD_SUCCESS) goto error_exit; } } rc = NSCD_SUCCESS; error_exit: /* restore the bitmap in the input data */ if (bitmap_addr != NULL) _nscd_cfg_bitmap_set(bitmap_addr, bitmap_in); return (rc); } /* * Convert string 'str' to data based on the data type in 'desc'. * 'data' points to the buffer in which the converted data * is placed. '*data_p' points to the buffer, or in the case * of a string data type, points to the untoched string (i.e., * 'str'). */ nscd_rc_t _nscd_cfg_str_to_data( nscd_cfg_param_desc_t *desc, char *str, void *data, void **data_p, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_str_to_data"; char *c; nscd_cfg_bitmap_t bitmap; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; nscd_rc_t rc = NSCD_CFG_DATA_CONVERSION_FAILED; if (desc == NULL || str == NULL || data == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: one of the following is NULL " "desc = %p, str = %p, data = %p, data_p = %p\n", desc, str, data, data_p); return (NSCD_INVALID_ARGUMENT); } *data_p = data; /* if description is that of a group, return error */ if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) { (void) snprintf(msg, sizeof (msg), gettext("single data specified for group %s"), desc->id.name); if (errorp != NULL) *errorp = _nscd_cfg_make_error(NSCD_INVALID_ARGUMENT, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s)\n", msg); return (NSCD_INVALID_ARGUMENT); } if (desc->type == NSCD_CFG_DATA_STRING) { if (strcmp(str, NSCD_NULL) == 0) *(char **)data_p = NULL; else { /* remove the " char if quoted string */ if (str[0] == '"') { c = str + strlen(str) - 1; if (*c == '"') *c = '\0'; *(char **)data_p = str + 1; } else *(char **)data_p = str; } return (NSCD_SUCCESS); } if (str == NULL) { (void) snprintf(msg, sizeof (msg), gettext("data must be specified for %s"), desc->id.name); if (errorp != NULL) *errorp = _nscd_cfg_make_error(NSCD_INVALID_ARGUMENT, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s\n", msg); return (NSCD_INVALID_ARGUMENT); } switch (desc->type) { case NSCD_CFG_DATA_BOOLEAN: if (strcasecmp(str, "yes") == 0) *(nscd_bool_t *)data = nscd_true; else if (strcasecmp(str, "no") == 0) *(nscd_bool_t *)data = nscd_false; else { (void) snprintf(msg, sizeof (msg), gettext("data (%s) must be 'yes' or 'no' for %s"), str, desc->id.name); if (errorp != NULL) *errorp = _nscd_cfg_make_error( NSCD_INVALID_ARGUMENT, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s\n", msg); return (NSCD_INVALID_ARGUMENT); } break; case NSCD_CFG_DATA_INTEGER: errno = 0; *(int *)data = (int)strtol(str, NULL, 10); if (errno != 0) { (void) snprintf(msg, sizeof (msg), gettext("unable to convert data (%s) for %s"), str, desc->id.name); if (errorp != NULL) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s\n", msg); return (rc); } break; case NSCD_CFG_DATA_BITMAP: errno = 0; bitmap = (nscd_cfg_bitmap_t)strtol(str, NULL, 10); if (errno != 0) { (void) snprintf(msg, sizeof (msg), gettext("unable to convert data (%s) for %s"), str, desc->id.name); if (errorp != NULL) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s\n", msg); return (rc); } _nscd_cfg_bitmap_set(data, bitmap); break; } return (NSCD_SUCCESS); } nscd_rc_t _nscd_cfg_set( nscd_cfg_handle_t *handle, void *data, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_set"; int dlen; nscd_cfg_id_t *nswdb; nscd_cfg_param_desc_t *desc, *gdesc; nscd_cfg_group_info_t *gi; char *nswdb_name, *param_name; void *pdata = NULL; void *cfg_data, *vdata_addr = NULL; nscd_bool_t get_group = 0; nscd_bool_t in = nscd_true; nscd_cfg_lock_t *lock = NULL; nscd_rc_t rc = NSCD_SUCCESS; if (handle == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "handle is NULL\n"); return (NSCD_INVALID_ARGUMENT); } nswdb = handle->nswdb; desc = (nscd_cfg_param_desc_t *)handle->desc; if (nswdb == NULL) nswdb_name = "global"; else nswdb_name = nswdb->name; param_name = desc->id.name; if (data == NULL && _nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_VLEN_DATA)) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "data == NULL\n"); return (NSCD_INVALID_ARGUMENT); } if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_UPDATE_SEND_WHOLE_GROUP) || _nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) get_group = nscd_true; /* * locate the current value of the param or group * and lock the config data for writing */ rc = _nscd_cfg_locate_cfg_data(&cfg_data, nscd_false, desc, nswdb, get_group, &vdata_addr, &dlen, &lock); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to locate config data (rc = %d)\n", rc); return (rc); } if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP) && ((nscd_cfg_group_info_t *)cfg_data)->num_param != ((nscd_cfg_group_info_t *)data)->num_param) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "number of parameters in group <%s : %s> not equal: " "%d in input data, should be %d\n", NSCD_STR_OR_GLOBAL(nswdb_name), NSCD_STR_OR_NULL(param_name), ((nscd_cfg_group_info_t *)data)->num_param, ((nscd_cfg_group_info_t *)cfg_data)->num_param); rc = NSCD_INVALID_ARGUMENT; goto error_exit; } /* * if variable length data, we want the address * of the pointer pointing to the data */ if (vdata_addr != NULL) cfg_data = vdata_addr; /* * just copy in the specified data, if no need * to verify the data or notify the associated * component */ if (get_group == nscd_true) { gdesc = &_nscd_cfg_param_desc[desc->g_index]; rc = _nscd_cfg_copy_group_data_merge( gdesc, &pdata, data, cfg_data, desc->id.index, data); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to copy group data <%s : %s>\n", NSCD_STR_OR_GLOBAL(nswdb_name), NSCD_STR_OR_NULL(param_name)); goto error_exit; } rc = _nscd_cfg_notify_s(gdesc, nswdb, pdata, errorp); } else rc = _nscd_cfg_notify_s(desc, nswdb, data, errorp); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "verifying/notifying of new configuration " "parameter <%s : %s> failed. %s\n", NSCD_STR_OR_GLOBAL(nswdb_name), param_name, (*errorp && (*errorp)->msg) ? (*errorp)->msg : ""); goto error_exit; } /* * Move the new config into the config store */ rc = NSCD_CFG_SET_PARAM_FAILED; if (_nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP)) { gi = _nscd_cfg_get_gi(pdata); rc = _nscd_cfg_copy_group_data_in(gdesc, gi, cfg_data, pdata); } else { /* * nscd_true asks _nscd_cfg_copy_param_data to * set addr of the vlen data in 'cfg_data' rather * than copying the data content */ if (pdata != NULL) _nscd_cfg_free_vlen_data_group(gdesc, pdata, in); rc = _nscd_cfg_copy_param_data(desc, cfg_data, data, in, nscd_true); } if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "unable to make new param data <%s : %s> current\n", NSCD_STR_OR_GLOBAL(nswdb_name), NSCD_STR_OR_NULL(param_name)); } error_exit: _nscd_cfg_unlock(lock); return (rc); } nscd_rc_t _nscd_cfg_set_linked( nscd_cfg_handle_t *handle, void *data, nscd_cfg_error_t **errorp) { char *me = "_nscd_cfg_set_linked"; nscd_cfg_id_t *nswdb; nscd_cfg_handle_t *hl; nscd_cfg_param_desc_t *desc; char *nswdb_name, *param_name, *dbl; nscd_rc_t rc = NSCD_SUCCESS; nscd_cfg_nsw_spc_default_t *spc; int i; char msg[NSCD_CFG_MAX_ERR_MSG_LEN]; if (handle == NULL) { _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "handle is NULL\n"); return (NSCD_INVALID_ARGUMENT); } nswdb = handle->nswdb; desc = (nscd_cfg_param_desc_t *)handle->desc; /* * no need to do the special linking thing, * if a global param, or a group, or not a linked param */ if (nswdb == NULL || _nscd_cfg_flag_is_set(desc->pflag, NSCD_CFG_PFLAG_GROUP) || _nscd_cfg_flag_is_not_set(desc->pflag, NSCD_CFG_PFLAG_LINKED)) return (_nscd_cfg_set(handle, data, errorp)); else nswdb_name = nswdb->name; param_name = desc->id.name; /* * if a param is linked to another, it can not be * changed directly */ for (i = 0; i < _nscd_cfg_num_link_default; i++) { if (_nscd_cfg_nsw_link_default[i].data == NULL) continue; if (strcmp(_nscd_cfg_nsw_link_default[i].db, nswdb_name) == 0 && _nscd_cfg_nsw_link_default[i].group_off == desc->g_offset && _nscd_cfg_nsw_link_default[i].param_off == desc->p_offset) { rc = NSCD_CFG_READ_ONLY; (void) snprintf(msg, sizeof (msg), gettext("value of \'%s\' not changeable, " "change that of \'%s\' instead"), nswdb->name, "passwd"); if (errorp != NULL) *errorp = _nscd_cfg_make_error(rc, msg); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "ERROR: %s\n", msg); return (rc); } } /* * if a param is linked from another, it should be verify * and changed first */ for (i = 0; i < _nscd_cfg_num_link_default; i++) { if (_nscd_cfg_nsw_link_default[i].data == NULL) continue; spc = _nscd_cfg_nsw_link_default[i].data; if (strcmp(spc->db, nswdb_name) == 0 && spc->group_off == desc->g_offset && spc->param_off == desc->p_offset) { rc = _nscd_cfg_set(handle, data, errorp); if (rc != NSCD_SUCCESS) return (rc); break; } } /* * then change all those linked to the one that has been changed */ for (i = 0; i < _nscd_cfg_num_link_default; i++) { if (_nscd_cfg_nsw_link_default[i].data == NULL) continue; spc = _nscd_cfg_nsw_link_default[i].data; if (strcmp(spc->db, nswdb_name) == 0 && spc->group_off == desc->g_offset && spc->param_off == desc->p_offset && _nscd_cfg_nsw_link_default[i].group_off == desc->g_offset && _nscd_cfg_nsw_link_default[i].param_off == desc->p_offset) { dbl = _nscd_cfg_nsw_link_default[i].db; rc = _nscd_cfg_get_handle(param_name, dbl, &hl, errorp); if (rc != NSCD_SUCCESS) return (rc); rc = _nscd_cfg_set(hl, data, errorp); _nscd_cfg_free_handle(hl); if (rc != NSCD_SUCCESS) return (rc); } } return (_nscd_cfg_set(handle, data, errorp)); } /* * Return a list of comma-separated database names that * have at least one of the input sources (the srcs array) * appears in their configured nsswitch policy string. * That is, if srcs contains "ldap" and "passwd: files ldap" * "group: files ldap" are in /etc/nsswitch.conf, then * "passwd,group" will be returned. The return string * should be freed by the caller. * * For compat nsswitch configuration, "group" and/or * "passwd,user_attr,shadow,audit_user" (not "group_compat" * or "passwd_compat") will be returned. Note that the * user_attr, shadow, and audit_user databases share the * same policy with the passwd database. * * For example, if srcs has "ldap" and in /etc/nsswitch.conf, * there are: * passwd: compat * passwd_compat: ldap * group: compat * group_compat: ldap * netgroup: ldap * then "netgroup,passwd,group,user_attr,shadow,audit_user" * will be returned. */ char * _nscd_srcs_in_db_nsw_policy( int num_src, char **srcs) { uint8_t i, j, n = 0, nc = 0; uint8_t compat_grp = 0, compat_pwd = 0; uint8_t *db; uint8_t *db_compat; int dlen = 0; nscd_cfg_nsw_db_data_t *dbcfg; nscd_cfg_switch_t *sw; char *outstr = NULL; char *dbname; db = (uint8_t *)calloc(_nscd_cfg_num_nsw_db, sizeof (uint8_t)); if (db == NULL) return (NULL); db_compat = (uint8_t *)calloc(_nscd_cfg_num_nsw_db, sizeof (uint8_t)); if (db_compat == NULL) { free(db); return (NULL); } for (i = 0; i < _nscd_cfg_num_nsw_db; i++) { (void) rw_rdlock(&nscd_cfg_nsw_db_data_rwlock[i]); dbcfg = &nscd_cfg_nsw_db_data_current[i]; sw = &dbcfg->sw; if (sw->nsw_config_string == NULL) { (void) rw_unlock(&nscd_cfg_nsw_db_data_rwlock[i]); continue; } dbname = _nscd_cfg_nsw_db[i].name; for (j = 0; j < num_src; j++) { if (strstr(sw->nsw_config_string, srcs[j]) != NULL) { db[n++] = i; dlen += strlen(dbname) + 1; } else if (strcmp(sw->nsw_config_string, "compat") == 0) { if (strcmp(dbname, "passwd") == 0) { compat_pwd = 1; dlen += 7; } else if (strcmp(dbname, "group") == 0) { compat_grp = 1; dlen += 6; } else { db_compat[nc++] = i; dlen += strlen(dbname) + 1; } } } (void) rw_unlock(&nscd_cfg_nsw_db_data_rwlock[i]); } if (dlen != 0) outstr = (char *)calloc(1, dlen); if (outstr == NULL) { free(db_compat); free(db); return (NULL); } for (j = 0; j < n; j++) { dbname = _nscd_cfg_nsw_db[db[j]].name; if (strstr(dbname, "group_compat") != NULL) { if (compat_grp == 1) dbname = "group"; else continue; } else if (strstr(dbname, "passwd_compat") != NULL) { if (compat_pwd == 1) dbname = "passwd"; else continue; } (void) strlcat(outstr, dbname, dlen); (void) strlcat(outstr, ",", dlen); } for (j = 0; j < nc; j++) { dbname = _nscd_cfg_nsw_db[db_compat[j]].name; if (compat_pwd == 1) { (void) strlcat(outstr, dbname, dlen); (void) strlcat(outstr, ",", dlen); } } /* remove the last comma */ i = strlen(outstr) - 1; if (outstr[i] == ',') outstr[i] = '\0'; free(db); free(db_compat); return (outstr); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NSCD_CONFIG_H #define _NSCD_CONFIG_H #ifdef __cplusplus extern "C" { #endif #include #include "nscd_common.h" /* * nscd_cfg_id_t is used to identify a config/stat * object. 'index' provides a way to quickly locate * the object in the associated configuration list. * 'name' can be looked up in the config info database * to obtain the index. */ typedef struct { int index; char *name; } nscd_cfg_id_t; /* * forward declaration of nscd_cfg_param_desc_t */ struct nscd_cfg_param_desc; /* * for operations that apply to configuration data * in all the nsswitch databases */ #define NSCD_CFG_NSW_ALLDB "ALLDB" #define NSCD_CFG_NSW_ALLDB_INDEX 9999 /* * configuration lists includes switch databases (eg. hosts, passwd), * switch sources (eg. files, ldap), config parameter descriptions * (defined below), and status/statistic counter descriptions (defined * below) */ typedef struct { int num; nscd_cfg_id_t **list; } nscd_cfg_list_t; /* * type of configuration list */ typedef enum { NSCD_CFG_LIST_NSW_DB = 0, NSCD_CFG_LIST_NSW_SRC = 1, NSCD_CFG_LIST_PARAM = 2, NSCD_CFG_LIST_STAT = 3 } nscd_cfg_list_type_t; /* * A config handle identifies config or stat data, * which if is nsswitch database specific, 'nswdb' * indicates the id of the database; if global, * 'nswdb' should be null. 'desc' is the config * param or stat description assocaited with the * data. */ typedef struct { nscd_cfg_id_t *nswdb; void *desc; nscd_cfg_list_type_t type; } nscd_cfg_handle_t; /* * type of configuration/statistics data */ typedef enum { NSCD_CFG_DATA_NONE = 0, NSCD_CFG_DATA_INTEGER = 1, NSCD_CFG_DATA_BOOLEAN = 2, NSCD_CFG_DATA_STRING = 3, NSCD_CFG_DATA_BITMAP = 4, NSCD_CFG_DATA_PERCENT = 5 } nscd_cfg_data_type_t; #define NSCD_CFG_NUM_DATA_TYPE 5 /* * data flag is attached to config/stat data passed between * function to specify the nature/type of action to perform */ #define NSCD_CFG_DFLAG_NONE 0x0000 /* * data should not be freed by receiver; * otherwise it should be freed */ #define NSCD_CFG_DFLAG_STATIC_DATA 0x0001 /* * data is sent/received due to nscd initialization; * otherwise due to modification of the config data * requested by users */ #define NSCD_CFG_DFLAG_INIT 0x0002 /* * the entire group of data is, or should be, sent; * otherwise only a single parameter/stat value */ #define NSCD_CFG_DFLAG_GROUP 0x0004 /* * the data sent/received is to be verified by the * 'verify' function defined in the parameter * description */ #define NSCD_CFG_DFLAG_VERIFY 0x0008 /* * the data sent/received is to be processed by the * 'notify' function defined in the parameter * description */ #define NSCD_CFG_DFLAG_NOTIFY 0x0010 /* * the data sent/received is to be applied to all * nsswitch databases */ #define NSCD_CFG_DFLAG_SET_ALL_DB 0x0020 /* * the entire group of data is sent/received; * however, only those parameters selected by * the bitmap in the group info should be * processed */ #define NSCD_CFG_DFLAG_BIT_SELECTED 0x0040 /* * param flag is defined in the parameter description. * It specifies what operation should be applied to, or * the nature of, the config parameters. */ #define NSCD_CFG_PFLAG_NONE 0x0000 /* * At init/refresh time, send the parameter value * with the data of the entire group; otherwise * send the parameter value only */ #define NSCD_CFG_PFLAG_INIT_SEND_WHOLE_GROUP 0x0001 /* * At user requested update time, send the parameter * value with the data of the entire group; otherwise * send the parameter value only */ #define NSCD_CFG_PFLAG_UPDATE_SEND_WHOLE_GROUP 0x0002 /* * At init/refresh time, send the config data * once for each nsswitch database */ #define NSCD_CFG_PFLAG_INIT_SET_ALL_DB 0x0004 /* * At user requested update time, send the per nsswitch * database (non-global) data just one time (not once * for each nsswitch database) */ #define NSCD_CFG_PFLAG_UPDATE_SEND_NON_GLOBAL_ONCE 0x0008 /* * send entire group data, but use bitmap to indicate * the one config parameter being processed. This flag * can only be sepcified for a group description */ #define NSCD_CFG_PFLAG_SEND_BIT_SELECTED 0x0010 /* * data is global, not per nsswitch database */ #define NSCD_CFG_PFLAG_GLOBAL 0x0020 /* * data is group data, not individual parameter value */ #define NSCD_CFG_PFLAG_GROUP 0x0040 /* * data is of variable length */ #define NSCD_CFG_PFLAG_VLEN_DATA 0x0080 /* * data is hidden, for internal use only, get/set not allowed */ #define NSCD_CFG_PFLAG_HIDDEN 0x0100 /* * data is linked, using the value of a different database */ #define NSCD_CFG_PFLAG_LINKED 0x0200 /* * data is obsolete, ignored with warning, should not be displayed */ #define NSCD_CFG_PFLAG_OBSOLETE 0x0400 /* * structure for error reporting */ typedef struct { nscd_rc_t rc; char *msg; } nscd_cfg_error_t; /* * typedef for flag, bitmap, and boolean */ typedef int nscd_cfg_flag_t; typedef int nscd_cfg_bitmap_t; /* * struct nscd_cfg_param_desc is used to describe each and * every one of the nscd config parameters so that they can * be processed generically by the configuration management * component. During init or update time, config data needs * to be pushed to other nscd components (frontend, switch * engine, cache backend, and so on) for further processing. * The 'verify' and 'notify' functions are the hooks provided * by these other components to validate and store the new * config data. The 'p_check' field, if specified, points * to a set of data used for preliminary check of a parameter * value (range, length, null checking etc). */ typedef nscd_rc_t (*nscd_cfg_func_notify_t)(void *, struct nscd_cfg_param_desc *, nscd_cfg_id_t *, nscd_cfg_flag_t, nscd_cfg_error_t **, void *); typedef nscd_rc_t (*nscd_cfg_func_verify_t)(void *, struct nscd_cfg_param_desc *, nscd_cfg_id_t *, nscd_cfg_flag_t, nscd_cfg_error_t **, void **); typedef struct nscd_cfg_param_desc { nscd_cfg_id_t id; nscd_cfg_data_type_t type; nscd_cfg_flag_t pflag; int p_size; size_t p_offset; int p_fn; int g_size; size_t g_offset; int g_index; void *p_check; nscd_cfg_func_notify_t notify; nscd_cfg_func_verify_t verify; } nscd_cfg_param_desc_t; /* * the _nscd_cfg_get_param_desc_list function returns * the list of nscd config param descriptions at * run time */ typedef struct { int num; nscd_cfg_param_desc_t **list; } nscd_cfg_param_desc_list_t; /* this describes data of variable length */ typedef struct { void *ptr; int len; } nscd_cfg_vlen_data_t; /* * The following defines the various global and nsswitch * database specific data structures for all the groups of * configuration parameters. Before each one, there lists * the associated group info which contains the number of * parameters and the corresponding bitmap. */ typedef struct { int num_param; nscd_cfg_bitmap_t bitmap; } nscd_cfg_group_info_t; #define NSCD_CFG_GROUP_INFO_NULL {-1, 0x0000} /* * frontend param group (Per nsswitch database) */ #define NSCD_CFG_GROUP_INFO_FRONTEND {1, 0x0001} typedef struct { nscd_cfg_group_info_t gi; int worker_thread_per_nsw_db; } nscd_cfg_frontend_t; /* * switch engine param group (Per nsswitch database) */ #define NSCD_CFG_GROUP_INFO_SWITCH {7, 0x07f} typedef struct { nscd_cfg_group_info_t gi; char *nsw_config_string; char *nsw_config_db; nscd_bool_t enable_lookup; nscd_bool_t enable_loopback_checking; int max_nsw_state_per_db; int max_nsw_state_per_thread; int max_getent_ctx_per_db; } nscd_cfg_switch_t; /* * log/debug param group (global) */ #define NSCD_CFG_GROUP_INFO_GLOBAL_LOG {3, 0x0007} typedef struct { nscd_cfg_group_info_t gi; char *logfile; int debug_level; int debug_comp; } nscd_cfg_global_log_t; /* * frontend param group (global) */ #define NSCD_CFG_GROUP_INFO_GLOBAL_FRONTEND {2, 0x0003} typedef struct { nscd_cfg_group_info_t gi; int common_worker_threads; int cache_hit_threads; } nscd_cfg_global_frontend_t; /* * self credential param group (global) */ #define NSCD_CFG_GROUP_INFO_GLOBAL_SELFCRED {2, 0x0003} typedef struct { nscd_cfg_group_info_t gi; nscd_bool_t enable_selfcred; int per_user_nscd_ttl; } nscd_cfg_global_selfcred_t; /* * switch engine param group (global) */ #define NSCD_CFG_GROUP_INFO_GLOBAL_SWITCH {3, 0x0007} typedef struct { nscd_cfg_group_info_t gi; nscd_bool_t enable_lookup_g; nscd_bool_t enable_loopback_checking_g; int check_smf_state_interval_g; } nscd_cfg_global_switch_t; /* * nscd_cfg_param_desc_t should always have nscd_cfg_id_t * as its first field. _nscd_cfg_get_desc below provides * an way to get to the nscd_cfg_param_desc_t from a * pointer to the static nscd_cfg_id_t returned by the * various_nscd_cfg_* functions */ #define _nscd_cfg_get_desc_i(id) ((nscd_cfg_param_desc_t *)(id)) #define _nscd_cfg_get_desc(h) ((h)->desc) /* * The various param group structure should always have * nscd_cfg_group_info_t as its first field. * _nscd_cfg_get_gi below provides a generic way to * get to the nscd_cfg_group_info_t from a void pointer * to the various param group structure returned by the * _nscd_cfg_* functions */ #define _nscd_cfg_get_gi(voidp) ((nscd_cfg_group_info_t *)(voidp)) /* * It is possible in the future, we will need more bits * than those in nscd_cfg_flag_t and nscd_cfg_bitmap_t. To * make it easier to extend, the following macro should be * used to deal with flags and bitmaps. * m, m1, m2, ma: mask, n: nth bit (0 based) * f: flag, v: value */ #define NSCD_CFG_BITMAP_ZERO 0 #define _nscd_cfg_bitmap_is_set(m, n) (((m) >> n) & 1) #define _nscd_cfg_bitmap_is_not_set(m, n) (!(((m) >> n) & 1)) #define _nscd_cfg_bitmap_is_equal(m1, m2) ((m1) == (m2)) #define _nscd_cfg_bitmap_value(m) (m) #define _nscd_cfg_bitmap_set_nth(m, n) ((m) |= (1 << n)) #define _nscd_cfg_bitmap_set(ma, m) (*(nscd_cfg_bitmap_t *) \ (ma) = (m)) #define _nscd_cfg_bitmap_valid(m1, m2) (((m1) & ~(m2)) == 0) #define NSCD_CFG_FLAG_ZERO 0 #define _nscd_cfg_flag_is_set(f, v) ((f) & (v)) #define _nscd_cfg_flag_is_not_set(f, v) (!((f) & (v))) #define _nscd_cfg_flag_value(f) (f) #define _nscd_cfg_flag_set(f, v) ((f) | (v)) #define _nscd_cfg_flag_unset(f, v) ((f) & ~(v)) /* * handy macros */ #define NSCD_NULL "NULL" #define NSCD_CFG_MAX_ERR_MSG_LEN 1024 #define NSCD_STR_OR_NULL(s) ((s) == NULL ? "NULL" : (s)) #define NSCD_STR_OR_GLOBAL(s) ((s) == NULL ? "GLOBAL" : (s)) #define NSCD_Y_OR_N(s) (*(nscd_bool_t *)s == nscd_true ? \ "yes" : "no") #define NSCD_ERR2MSG(e) (((e) && (e)->msg) ? (e)->msg : "") /* * This macro is based on offsetof defined in stddef_iso.h, * it gives the size of 'm' in structure 's' without needing * the declaration of a 's' variable (as macro sizeof does) */ #define NSCD_SIZEOF(s, m) (sizeof (((s *)0)->m)) /* * struct nscd_cfg_stat_desc is used to describe each and every * one of the nscd statistics counters so that they can be * processed generically by the configuration management * component. The component does not keep a separate copy of * all counters, which should be maintained by other nscd * components. The 'get_stat' functions defined in the * stat description are supplied by those components and used * by the config management component to request and print * counters on behave of the users. The free_stat function * returned by those components will also be used to free * the stat data if the NSCD_CFG_DFLAG_STATIC_DATA bit is * not set in dflag. */ struct nscd_cfg_stat_desc; typedef nscd_rc_t (*nscd_cfg_func_get_stat_t)(void **, struct nscd_cfg_stat_desc *, nscd_cfg_id_t *, nscd_cfg_flag_t *, void (**) (void *), nscd_cfg_error_t **); typedef struct nscd_cfg_stat_desc { nscd_cfg_id_t id; nscd_cfg_data_type_t type; nscd_cfg_flag_t sflag; nscd_cfg_group_info_t gi; int s_size; size_t s_offset; int s_fn; int g_size; size_t g_offset; int g_index; nscd_cfg_func_get_stat_t get_stat; } nscd_cfg_stat_desc_t; /* * stat flag is defined in the stat description. It * specifies the nature of the statistics counters. */ #define NSCD_CFG_SFLAG_NONE 0x0000 /* * statistics counter is global, not per nsswitch database */ #define NSCD_CFG_SFLAG_GLOBAL 0x0001 /* * description is for counter group, not individual counter */ #define NSCD_CFG_SFLAG_GROUP 0x0002 /* * The following defines the various global and nsswitch * database specific data structures for all the groups of * statistics counters. Before each one, there lists * the associated group info which contains the number of * counters and the corresponding bitmap. */ /* * switch engine stat group (Per nsswitch database) */ #define NSCD_CFG_STAT_GROUP_INFO_SWITCH {6, 0x003f} typedef struct { nscd_cfg_group_info_t gi; int lookup_request_received; int lookup_request_queued; int lookup_request_in_progress; int lookup_request_succeeded; int lookup_request_failed; int loopback_nsw_db_skipped; } nscd_cfg_stat_switch_t; /* * log/debug stat group (global) */ #define NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG {1, 0x0001} typedef struct { nscd_cfg_group_info_t gi; int entries_logged; } nscd_cfg_stat_global_log_t; /* * switch engine stat group (global) */ #define NSCD_CFG_STAT_GROUP_INFO_GLOBAL_SWITCH {6, 0x003f} typedef struct { nscd_cfg_group_info_t gi; int lookup_request_received_g; int lookup_request_queued_g; int lookup_request_in_progress_g; int lookup_request_succeeded_g; int lookup_request_failed_g; int loopback_nsw_db_skipped_g; } nscd_cfg_stat_global_switch_t; /* * control structure for appending string data to a buffer */ typedef struct { char *buf; char *next; int size; int used; int left; int real; } nscd_cfg_buf_t; /* * internal configuration management related functions */ nscd_rc_t _nscd_cfg_init(); nscd_rc_t _nscd_cfg_get_param_desc_list( nscd_cfg_param_desc_list_t **list); nscd_rc_t _nscd_cfg_get_handle( char *param_name, char *nswdb_name, nscd_cfg_handle_t **handle, nscd_cfg_error_t **errorp); nscd_cfg_error_t * _nscd_cfg_make_error( nscd_rc_t rc, char *msg); void _nscd_cfg_free_handle( nscd_cfg_handle_t *handle); void _nscd_cfg_free_group_data( nscd_cfg_handle_t *handle, void *data); void _nscd_cfg_free_param_data( void *data); void _nscd_cfg_free_error( nscd_cfg_error_t *error); nscd_rc_t _nscd_cfg_get( nscd_cfg_handle_t *handle, void **data, int *data_len, nscd_cfg_error_t **errorp); nscd_rc_t _nscd_cfg_set( nscd_cfg_handle_t *handle, void *data, nscd_cfg_error_t **errorp); nscd_rc_t _nscd_cfg_str_to_data( nscd_cfg_param_desc_t *desc, char *str, void *data, void **data_p, nscd_cfg_error_t **errorp); nscd_rc_t _nscd_cfg_prelim_check( nscd_cfg_param_desc_t *desc, void *data, nscd_cfg_error_t **errorp); nscd_rc_t _nscd_cfg_read_file( char *filename, nscd_cfg_error_t **errorp); nscd_rc_t _nscd_cfg_set_linked( nscd_cfg_handle_t *handle, void *data, nscd_cfg_error_t **errorp); char * _nscd_srcs_in_db_nsw_policy( int num_src, char **srcs); nscd_rc_t _nscd_cfg_read_nsswitch_file( char *filename, nscd_cfg_error_t **errorp); #ifdef __cplusplus } #endif #endif /* _NSCD_CONFIG_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NSCD_DB_H #define _NSCD_DB_H #ifdef __cplusplus extern "C" { #endif #include /* nssuint_t */ #include "nscd_common.h" /* make a pointer 8-byte aligned, or an integer a multiple of 8 */ #define roundup(x) (((unsigned long)(x)+7) & ~7) /* * type of nscd data */ #define NSCD_DATA_UNKNOWN 0 #define NSCD_DATA_NSW_CONFIG 1 #define NSCD_DATA_NSW_STATE_BASE 2 #define NSCD_DATA_GETENT_CTX_BASE 3 #define NSCD_DATA_BACKEND_INFO 4 #define NSCD_DATA_BACKEND_INFO_DB 5 #define NSCD_DATA_CFG_NSW_DB_INDEX 6 #define NSCD_DATA_CFG_NSW_SRC_INDEX 7 #define NSCD_DATA_CFG_PARAM_INDEX 8 #define NSCD_DATA_CFG_STAT_INDEX 9 #define NSCD_DATA_ADDR 127 #define NSCD_DATA_CTX_ADDR 128 /* * database operation options */ typedef enum { NSCD_GET_FIRST_DB_ENTRY = 1, NSCD_GET_NEXT_DB_ENTRY = 2, NSCD_GET_EXACT_DB_ENTRY = 3, NSCD_ADD_DB_ENTRY_FIRST = 4, NSCD_ADD_DB_ENTRY_LAST = 5, NSCD_ADD_DB_ENTRY_REPLACE = 6, NSCD_ADD_DB_ENTRY_IF_NONE = 7, NSCD_DEL_FIRST_DB_ENTRY = 8, NSCD_DEL_ALL_DB_ENTRY = 9, NSCD_DEL_EXACT_DB_ENTRY = 10 } nscd_db_option_t; /* * This structure defines an instance of the * nscd database entry. */ typedef struct nscd_db_entry { int type; int id_num; int num_data; int num_array; char *name; void **data_array; } nscd_db_entry_t; /* * sequence number attached to nscd data */ typedef nssuint_t nscd_seq_num_t; typedef nssuint_t nscd_cookie_num_t; /* * The nscd_access_s datatype represents a nscd * access data structure. It is an opaque structure. */ struct nscd_access_s; typedef struct nscd_access_s nscd_access_t; struct nscd_acc_data_s; typedef struct nscd_acc_data_s nscd_acc_data_t; /* * The nscd_db_t datatype represents a nscd * database. It is also an opaque structure. */ struct nscd_db_s; typedef struct nscd_db_s nscd_db_t; /* * four sizes for a nscd database: * large, medium, small, tiny */ #define NSCD_DB_SIZE_LARGE 1 #define NSCD_DB_SIZE_MEDIUM 2 #define NSCD_DB_SIZE_SMALL 3 #define NSCD_DB_SIZE_TINY 4 /* * options for _nscd_alloc() */ #define NSCD_ALLOC_MUTEX 0x0001 #define NSCD_ALLOC_RWLOCK 0x0002 #define NSCD_ALLOC_COND 0x0004 /* * prototypes */ nscd_seq_num_t _nscd_get_seq_num(); nscd_cookie_num_t _nscd_get_cookie_num(); nscd_acc_data_t * _nscd_get( nscd_acc_data_t *data); nscd_acc_data_t *_nscd_set( nscd_acc_data_t *old, nscd_acc_data_t *new); void _nscd_release( nscd_acc_data_t *data); nscd_acc_data_t *_nscd_rdlock( nscd_acc_data_t *data); nscd_acc_data_t *_nscd_wrlock( nscd_acc_data_t *data); void _nscd_rw_unlock( nscd_acc_data_t *data); void _nscd_rw_unlock_no_release( nscd_acc_data_t *data); nscd_acc_data_t *_nscd_mutex_lock( nscd_acc_data_t *data); void _nscd_mutex_unlock( nscd_acc_data_t *data); void _nscd_cond_signal( nscd_acc_data_t *data); void _nscd_cond_wait( nscd_acc_data_t *data, cond_t *cond); nscd_acc_data_t * _nscd_alloc( int type, size_t size, void (*data_free)( nscd_acc_data_t *data), int option); nscd_rc_t _nscd_add_int_addr( void *ptr, int type, nscd_seq_num_t seq_num); rwlock_t * _nscd_is_int_addr( void *ptr, nscd_seq_num_t seq_num); void _nscd_del_int_addr( void *ptr, nscd_seq_num_t seq_num); nscd_db_t * _nscd_alloc_db( int size); void _nscd_free_db( nscd_db_t *db); nscd_db_entry_t * _nscd_alloc_db_entry( int type, const char *name, int dataSize, int num_data, int num_array); const nscd_db_entry_t * _nscd_get_db_entry( const nscd_db_t *db, int type, const char *str, nscd_db_option_t option, int id_num); nscd_rc_t _nscd_add_db_entry( nscd_db_t *db, const char *str, nscd_db_entry_t *entry, nscd_db_option_t option); nscd_rc_t _nscd_delete_db_entry( nscd_db_t *db, int type, const char *str, nscd_db_option_t option, int id_num); void * _nscd_create_int_addrDB(); void _nscd_destroy_int_addrDB(); #ifdef __cplusplus } #endif #endif /* _NSCD_DB_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "nscd_db.h" /* * This file implements the database functionality used by the * switch and configuration components. The implementation is * based on hash and has table. If the need arises in the future, * the code in this file can be changed to use other algorithms. * The following lists the functions implemented: * * _nscd_add_db_entry * _nscd_delete_db_entry * _nscd_get_db_entry * _nscd_alloc_db * _nscd_free_db * _nscd_walk_db * _nscd_delete_db_entry_cookie */ /* * This structure defines an instance of the hash entry * which implements the nscd database entry. The * db_entry field should always be the first one in * the structure. */ typedef struct nscd_hash { nscd_db_entry_t db_entry; struct nscd_hash *next_p; struct nscd_hash *prev_p; } nscd_hash_t; /* * This structure defines a nscd database which * is implemented as an array of nscd_hash_t. */ struct nscd_db_s { int array_size; /* number of elements in hash_tbl_p */ nscd_hash_t **hash_tbl_p; }; /* * This cookie structure is used to iterate through the * database entries contained in a nscd database. */ struct cookie { int idx; /* the current bucket */ nscd_hash_t *hash; /* the current hash entry */ nscd_db_t *db; /* the database */ }; /* * FUNCTION: calc_hash * * Calculate a hash for a string based on the elf_hash * algorithm, hash is case insensitive. Uses tolower * instead of _tolower because of I18N. */ static unsigned long calc_hash( const char *str) { unsigned int hval = 0; char ch; while (*str != '\0') { unsigned int g; ch = (char)*str++; if (isupper(ch)) ch = _tolower(ch); hval = (hval << 4) + ch; if ((g = (hval & 0xf0000000)) != 0) hval ^= g >> 24; hval &= ~g; } return ((unsigned long)hval); } /* * FUNCTION: scan_hash * * Scan a hash table for a matching hash entry. Assume 'str' is * not NULL. If option is NSCD_GET_NEXT_DB_ENTRY and id_num * is less than zero, then treats the option as NSCD_GET_FIRST_DB_ENTRY. */ static const nscd_hash_t * scan_hash( int type, const char *str, const nscd_hash_t *idx_p, nscd_db_option_t option, int id_num) { int id_matched = 0; nscd_db_entry_t *db_entry; while (idx_p != NULL) { db_entry = &((nscd_hash_t *)idx_p)->db_entry; if (db_entry->type == type) { if (strcasecmp(str, db_entry->name) == 0) { switch (option) { case NSCD_GET_FIRST_DB_ENTRY: return (idx_p); case NSCD_GET_EXACT_DB_ENTRY: if (id_num == db_entry->id_num) return (idx_p); break; case NSCD_GET_NEXT_DB_ENTRY: if (id_num < 0) return (idx_p); if (id_matched == 1) return (idx_p); if (id_num == db_entry->id_num) id_matched = 1; break; } } } idx_p = idx_p->next_p; } return (NULL); } /* * FUNCTION: _nscd_get_db_entry * * Find a nscd database entry from a nscd database. */ const nscd_db_entry_t * _nscd_get_db_entry( const nscd_db_t *db, int type, const char *str, nscd_db_option_t option, int id_num) { unsigned long hash; const nscd_hash_t *idx_p, *hash_p; if (db == NULL || str == NULL) return (NULL); hash = calc_hash(str); idx_p = db->hash_tbl_p[hash % db->array_size]; hash_p = scan_hash(type, str, idx_p, option, id_num); return (&hash_p->db_entry); } /* * FUNCTION: _nscd_add_db_entry * * Add a nscd database entry to a nscd database. This function * is not MT safe. The caller should lock the database to * prevent concurrent updates done by other threads. */ nscd_rc_t _nscd_add_db_entry( nscd_db_t *db, const char *str, nscd_db_entry_t *entry, nscd_db_option_t option) { int i; unsigned long hash; nscd_hash_t *next_p = NULL, *prev_p = NULL; nscd_hash_t *idx_p, *hash_entry; nscd_db_entry_t *db_entry; /* find the bucket */ hash = calc_hash(str); i = hash % db->array_size; idx_p = db->hash_tbl_p[i]; /* can not replace nothing */ if (idx_p == NULL) if (option == NSCD_ADD_DB_ENTRY_REPLACE) return (NSCD_DB_ENTRY_NOT_FOUND); while (idx_p != NULL) { db_entry = &idx_p->db_entry; switch (option) { case NSCD_ADD_DB_ENTRY_FIRST: next_p = idx_p; goto add_entry; case NSCD_ADD_DB_ENTRY_REPLACE: if (db_entry->type != entry->type) goto cont; if (strcasecmp(db_entry->name, str) != 0) goto cont; if (db_entry->id_num == entry->id_num) { prev_p = idx_p->prev_p; next_p = idx_p->next_p; free(idx_p); goto add_entry; } goto cont; case NSCD_ADD_DB_ENTRY_IF_NONE: if (db_entry->type != entry->type) break; if (strcasecmp(db_entry->name, str) != 0) break; return (NSCD_DB_ENTRY_FOUND); } if (idx_p->next_p == NULL) { if (option == NSCD_ADD_DB_ENTRY_LAST || option == NSCD_ADD_DB_ENTRY_IF_NONE) { prev_p = idx_p; goto add_entry; } } cont: idx_p = idx_p->next_p; } add_entry: /* * the nscd_entry_t field should be the first field * in a nscd_hash_t */ hash_entry = (nscd_hash_t *)entry; /* update the prev link list */ hash_entry->prev_p = prev_p; if (prev_p == NULL) db->hash_tbl_p[i] = hash_entry; else prev_p->next_p = hash_entry; /* update the next link list */ hash_entry->next_p = next_p; if (next_p != NULL) next_p->prev_p = hash_entry; return (NSCD_SUCCESS); } /* * FUNCTION: _nscd_delete_db_entry * * Delete a nscd database entry from a nscd database. This * function is not MT safe. The caller should lock the * database to prevent concurrent updates done by other * threads. */ nscd_rc_t _nscd_delete_db_entry( nscd_db_t *db, int type, const char *str, nscd_db_option_t option, int id_num) { int i; int del_more = 0; unsigned long hash; nscd_hash_t *idx_p, *next_p = NULL, *prev_p = NULL; nscd_db_entry_t *db_entry; /* find the bucket */ hash = calc_hash(str); i = hash % db->array_size; idx_p = db->hash_tbl_p[i]; /* delete nothing always works */ if (idx_p == NULL) return (NSCD_SUCCESS); while (idx_p != NULL) { db_entry = &idx_p->db_entry; if (db_entry->type != type) goto cont; if (strcasecmp(db_entry->name, str) != 0) goto cont; switch (option) { case NSCD_DEL_FIRST_DB_ENTRY: prev_p = idx_p->prev_p; next_p = idx_p->next_p; del_more = 0; break; case NSCD_DEL_EXACT_DB_ENTRY: if (db_entry->id_num == id_num) { prev_p = idx_p->prev_p; next_p = idx_p->next_p; del_more = 0; } else goto cont; break; case NSCD_DEL_ALL_DB_ENTRY: prev_p = idx_p->prev_p; next_p = idx_p->next_p; break; } if (prev_p == NULL) db->hash_tbl_p[i] = next_p; else prev_p->next_p = next_p; if (next_p != NULL) next_p->prev_p = prev_p; free(idx_p); if (del_more == 0) break; /* * only when option == NSCD_DEL_ALL_DB_ENTRY * will we get here. next_p should be set to * idx_p->next_p beforehand */ idx_p = next_p; continue; cont: idx_p = idx_p->next_p; } return (NSCD_SUCCESS); } /* * FUNCTION: _nscd_alloc_db_entry * * Allocate and return the memory for a database entry * so the caller can insert data and then add it to the * database. */ nscd_db_entry_t * _nscd_alloc_db_entry( int type, const char *name, int dataSize, int num_data, int num_array) { int size; int array_o, data_o; nscd_hash_t *hash; void *p; /* first part: hash data structure and name string */ size = sizeof (*hash) + strlen(name) + 1; array_o = size = roundup(size); /* second part: pointer array to data */ size += (num_data + num_array) * sizeof (void **); size = roundup(size); /* third part: actual data */ data_o = size; size += dataSize; /* allocate the memory */ hash = (nscd_hash_t *)calloc(1, size); if (hash == NULL) return (NULL); /* init the entry based on caller's input */ hash->db_entry.num_data = num_data; hash->db_entry.num_array = num_array; hash->db_entry.type = type; hash->db_entry.name = (char *)hash + sizeof (*hash); p = (char *)hash + array_o; hash->db_entry.data_array = (void **)p; *(hash->db_entry.data_array) = (char *)hash + data_o; (void) strcpy(hash->db_entry.name, name); return (&hash->db_entry); } /* * FUNCTION: _nscd_delete_db_entry_cookie * * Delete a database entry using the information from * the input 'cookie'. */ void _nscd_delete_db_entry_cookie( nscd_db_t *db, void **cookie) { nscd_hash_t *hp; struct cookie *c; /* snaity check */ if (cookie == NULL || *cookie == NULL || db == NULL) return; c = *cookie; /* more snaity check */ if (db != c->db || c->hash == NULL || c->idx < 0 || c->idx >= db->array_size) return; /* retrieve the hash entry from the cookie */ hp = c->hash; /* * Update the next/previous link list. * Need to update c->hash as well, in case * the cookie is also used in a walk-db * loop. This is to make sure that the * next _nscd_walk_db() call will * find the (updated) next hash entry in line. */ if (hp->prev_p == NULL) { /* * make sure the current bucket will be * walked again if _nscd_walk_db is * called next */ c->hash = NULL; db->hash_tbl_p[c->idx] = hp->next_p; c->idx--; } else { c->hash = hp->prev_p; hp->prev_p->next_p = hp->next_p; } if (hp->next_p != NULL) hp->next_p->prev_p = hp->prev_p; /* delete the entry */ free(hp); } /* * FUNCTION: _nscd_alloc_db * * Allocate the space for a nscd database. * * The input argument, size, indicates the size of the database. * NSCD_DB_SIZE_LARGE specifies an bucket array of size 67, * NSCD_DB_SIZE_MEDIUM specifies an bucket array of size 37, * NSCD_DB_SIZE_SMALL specifies an bucket array of size 13, * NSCD_DB_SIZE_TINY specifies an bucket array of size 3. */ nscd_db_t * _nscd_alloc_db( int size) { int sz; nscd_db_t *db; /* allocate the database */ db = (nscd_db_t *)calloc(1, sizeof (nscd_db_t)); if (db == NULL) return (NULL); /* allocate the bucket array */ if (size == NSCD_DB_SIZE_LARGE) sz = 67; else if (size == NSCD_DB_SIZE_MEDIUM) sz = 37; else if (size == NSCD_DB_SIZE_SMALL) sz = 13; else if (size == NSCD_DB_SIZE_TINY) sz = 3; db->hash_tbl_p = (nscd_hash_t **)calloc(sz + 1, sizeof (nscd_hash_t *)); if (db->hash_tbl_p == NULL) { free(db); return (NULL); } db->array_size = sz; return (db); } /* * FUNCTION: _nscd_free_db * * Delete a nscd database. */ void _nscd_free_db( nscd_db_t *db) { int i; nscd_hash_t *hp, *next_p; /* * find non-empty buckets and for each one of them, * delete all the database entries in it's link list */ for (i = 0; i < db->array_size; i++) { hp = db->hash_tbl_p[i]; while (hp != NULL) { next_p = hp->next_p; free(hp); hp = next_p; } } /* free the bucket array */ free(db->hash_tbl_p); free(db); } /* * FUNCTION: _nscd_walk_db * * Iterate through the database entries contained in * a nscd database and return one entry at a time. * The cookie structure is used to indicate the * location of the returned entry for the next call * to this function. For the very first call, *cookie * should be set to NULL. For subsequent calls, the one * returned by the previous call sould be used. */ nscd_db_entry_t * _nscd_walk_db( nscd_db_t *db, void **cookie) { struct cookie *c; /* sanity check */ if (cookie == NULL || db == NULL) return (NULL); if (*cookie != NULL) { c = *cookie; /* * More sanity check. _nscd_delete_db_entry_cookie() * could change c->idx to -1. */ if (db != c->db || c->idx < -1 || c->idx >= db->array_size) return (NULL); /* is there a next entry ? */ if (c->hash != NULL) c->hash = c->hash->next_p; /* yes, return it */ if (c->hash != NULL) { return (&c->hash->db_entry); } } else { c = (struct cookie *)calloc(1, sizeof (*c)); if (c == NULL) return (NULL); c->idx = -1; c->hash = NULL; c->db = db; } /* find the first non-empty bucket */ for (c->idx++; c->idx < db->array_size; c->idx++) { c->hash = db->hash_tbl_p[c->idx]; if (c->hash != NULL) break; } /* no (more) non-empty bucket, we are done */ if (c->hash == NULL) { free(c); *cookie = NULL; return (NULL); } *cookie = c; return (&c->hash->db_entry); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include "nscd_door.h" #include "nscd_log.h" #include #include #include #include static void initdoor(void *buf, int *doorfd) { nss_pheader_t *phdr = (nss_pheader_t *)buf; door_info_t doori; char *me = "initdoor"; *doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0); _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door is %s (fd is %d)\n", NAME_SERVICE_DOOR, *doorfd); if (*doorfd == -1) { NSCD_SET_STATUS(phdr, NSS_ERROR, errno); return; } if (door_info(*doorfd, &doori) < 0 || (doori.di_attributes & DOOR_REVOKED) || doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) { /* * we should close doorfd because we just opened it */ (void) close(*doorfd); _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door %d not valid\n", *doorfd); NSCD_SET_STATUS(phdr, NSS_ERROR, ECONNREFUSED); return; } NSCD_SET_STATUS_SUCCESS(phdr); } /* general door call functions used by nscd */ static nss_status_t copy_output(void *outdata, int outdlen, nss_pheader_t *phdr, nss_pheader_t *outphdr) { void *dp; nss_status_t ret = NSS_SUCCESS; char *me = "copy_output"; if (outdata != NULL && phdr->data_off > 0 && phdr->data_len > 0) { if (phdr->data_len <= outdlen) { dp = (char *)phdr + phdr->data_off; (void) memmove(outdata, dp, phdr->data_len); } else { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "output buffer not large enough " " should be > %d but is %d\n", phdr->data_len, outdlen); if (outphdr != NULL) { NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, NSCD_INVALID_ARGUMENT); NSCD_COPY_STATUS(outphdr, phdr); } ret = NSS_NSCD_PRIV; } } return (ret); } nss_status_t _nscd_doorcall(int callnum) { size_t buflen; nss_pheader_t *phdr; void *dptr; size_t ndata; size_t adata; int ret; char *me = "_nscd_doorcall"; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "processing door call %d ...\n", callnum); /* allocate door buffer from the stack */ NSCD_ALLOC_DOORBUF(callnum, 0, dptr, buflen); ndata = buflen; adata = buflen; ret = _nsc_trydoorcall(&dptr, &ndata, &adata); if (ret != NSS_SUCCESS) { phdr = (nss_pheader_t *)dptr; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (%d) failed (status = %d, error = %s)\n", callnum, ret, strerror(NSCD_GET_ERRNO(phdr))); } return (ret); } nss_status_t _nscd_doorcall_data(int callnum, void *indata, int indlen, void *outdata, int outdlen, nss_pheader_t *phdr) { void *uptr; size_t buflen; void *dptr; void *datap; size_t ndata; size_t adata; nss_pheader_t *phdr_d; int ret; char *me = "_nscd_doorcall_data"; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "processing door call %d ...\n", callnum); /* allocate door buffer from the stack */ NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen); dptr = uptr; ndata = buflen; adata = buflen; datap = NSCD_N2N_DOOR_DATA(void, dptr); if (indata != NULL) (void) memmove(datap, indata, indlen); ret = _nsc_trydoorcall(&dptr, &ndata, &adata); phdr_d = (nss_pheader_t *)dptr; if (ret != NSS_SUCCESS) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (%d) failed (status = %d, error = %s)\n", callnum, ret, strerror(NSCD_GET_ERRNO(phdr_d))); } else { if (phdr != NULL) { NSCD_COPY_STATUS(phdr, phdr_d); } ret = copy_output(outdata, outdlen, phdr_d, phdr); } /* if new buffer allocated for this door call, free it */ if (dptr != uptr) (void) munmap(dptr, ndata); return (ret); } nss_status_t _nscd_doorcall_fd(int fd, int callnum, void *indata, int indlen, void *outdata, int outdlen, nss_pheader_t *phdr) { void *uptr; void *dptr; void *datap; size_t ndata; size_t adata; size_t buflen; door_arg_t param; int ret, errnum; nss_pheader_t *phdr_d; char *me = "_nscd_doorcall_fd"; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "processing door call %d (fd = %d)...\n", callnum, fd); /* allocate door buffer from the stack */ NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen); dptr = uptr; ndata = buflen; adata = buflen; datap = NSCD_N2N_DOOR_DATA(void, dptr); if (indata != NULL) (void) memmove(datap, indata, indlen); param.rbuf = (char *)dptr; param.rsize = ndata; param.data_ptr = (char *)dptr; param.data_size = adata; param.desc_ptr = NULL; param.desc_num = 0; ret = door_call(fd, ¶m); if (ret < 0) { errnum = errno; /* * door call did not get through, return errno * if requested */ if (phdr != NULL) { NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); } _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (%d to %d) did not get through (%s)\n", callnum, fd, strerror(errnum)); return (NSS_ERROR); } ndata = param.rsize; dptr = (void *)param.data_ptr; /* * door call got through, check if operation failed. * if so, return error info if requested */ phdr_d = (nss_pheader_t *)dptr; ret = NSCD_GET_STATUS(phdr_d); if (ret != NSS_SUCCESS) { if (phdr != NULL) { NSCD_COPY_STATUS(phdr, phdr_d); } _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (%d to %d) failed: p_status = %d, " "p_errno = %s, nscd status = %d\n", callnum, fd, ret, strerror(NSCD_GET_ERRNO(phdr_d)), NSCD_GET_NSCD_STATUS(phdr_d)); } else ret = copy_output(outdata, outdlen, phdr_d, phdr); /* if new buffer allocated for this door call, free it */ if (dptr != uptr) (void) munmap(dptr, param.rsize); return (ret); } static void send_doorfd(void **dptr, size_t *ndata, size_t *adata, door_desc_t *pdesc) { nss_pheader_t *phdr = (nss_pheader_t *)*dptr; door_arg_t param; int ret; int doorfd; int errnum; char *me = "send_doorfd"; initdoor(*dptr, &doorfd); if (NSCD_STATUS_IS_NOT_OK(phdr)) return; param.rbuf = (char *)*dptr; param.rsize = *ndata; param.data_ptr = (char *)*dptr; param.data_size = *adata; param.desc_ptr = pdesc; param.desc_num = 1; ret = door_call(doorfd, ¶m); if (ret < 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (to fd %d) failed (%s)\n", doorfd, strerror(errnum)); (void) close(doorfd); NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); return; } *adata = param.data_size; *ndata = param.rsize; *dptr = (void *)param.data_ptr; if (*adata == 0 || *dptr == NULL) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "no data\n"); NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTCONN); } (void) close(doorfd); } nss_status_t _nscd_doorcall_sendfd(int fd, int callnum, void *indata, int indlen, nss_pheader_t *phdr) { void *uptr; void *dptr; void *datap; size_t ndata; size_t adata; size_t buflen; nss_pheader_t *phdr_d; door_desc_t desc; char *me = "_nscd_doorcall_sendfd"; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "processing door call %d (fd = %d)...\n", callnum, fd); /* allocate door buffer from the stack */ NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen); dptr = uptr; ndata = buflen; adata = buflen; datap = NSCD_N2N_DOOR_DATA(void, dptr); if (indata != NULL) (void) memmove(datap, indata, indlen); desc.d_attributes = DOOR_DESCRIPTOR; desc.d_data.d_desc.d_descriptor = fd; send_doorfd(&dptr, &ndata, &adata, &desc); phdr_d = (nss_pheader_t *)dptr; if (NSCD_STATUS_IS_NOT_OK(phdr_d)) { if (phdr != NULL) *phdr = *phdr_d; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door call (%d) failed (status = %d, error = %s)\n", callnum, NSCD_GET_STATUS(phdr_d), strerror(NSCD_GET_ERRNO(phdr_d))); } return (NSCD_GET_STATUS(phdr_d)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #ifndef _NSCD_DOOR_H #define _NSCD_DOOR_H /* * Definitions for nscd to nscd door interfaces */ #ifdef __cplusplus extern "C" { #endif #include #include /* door for Trusted Extensions */ #define TSOL_NAME_SERVICE_DOOR "/var/tsol/doors/name_service_door" /* TX per label nscd indication file */ #define TSOL_NSCD_PER_LABEL_FILE "/var/tsol/doors/nscd_per_label" /* nscd v2 nscd -> nscd call numbers */ #define NSCD_PING (NSCD_CALLCAT_N2N|0x01) #define NSCD_GETADMIN (NSCD_CALLCAT_N2N|0x02) #define NSCD_SETADMIN (NSCD_CALLCAT_N2N|0x03) #define NSCD_GETPUADMIN (NSCD_CALLCAT_N2N|0x04) #define NSCD_SETPUADMIN (NSCD_CALLCAT_N2N|0x05) #define NSCD_KILLSERVER (NSCD_CALLCAT_N2N|0x06) #define NSCD_IMHERE (NSCD_CALLCAT_N2N|0x10) /* IMHERE+WHOAMI */ #define NSCD_FORK (NSCD_CALLCAT_N2N|0x20) #define NSCD_SETUID (NSCD_CALLCAT_N2N|0x30) #define NSCD_KILL (NSCD_CALLCAT_N2N|0x40) #define NSCD_PULSE (NSCD_CALLCAT_N2N|0x50) #define NSCD_REFRESH (NSCD_CALLCAT_N2N|0x60) /* nscd v2 nscd identities */ #define NSCD_MAIN 0x00000001 #define NSCD_FORKER 0x00000002 #define NSCD_CHILD 0x00000004 #define NSCD_WHOAMI 0x0000000F #define NSCD_ALLOC_DOORBUF(cn, dsz, uptr, usz) \ usz = (sizeof (nss_pheader_t) + (dsz)); \ uptr = alloca(usz); \ (void) memset(uptr, 0, usz); \ ((nss_pheader_t *)uptr)->nsc_callnumber = (cn); \ ((nss_pheader_t *)uptr)->p_version = NSCD_HEADER_REV; \ ((nss_pheader_t *)uptr)->pbufsiz = usz; \ ((nss_pheader_t *)uptr)->data_off = sizeof (nss_pheader_t); \ ((nss_pheader_t *)uptr)->key_off = sizeof (nss_pheader_t); \ ((nss_pheader_t *)uptr)->dbd_off = sizeof (nss_pheader_t); \ ((nss_pheader_t *)uptr)->data_len = dsz; #define NSCD_N2N_DOOR_DATA(type, buf) \ (type *)((void *)(((char *)(buf)) + sizeof (nss_pheader_t))) #define NSCD_N2N_DOOR_BUF_SIZE(struct) \ sizeof (nss_pheader_t) + sizeof (struct) #define NSCD_SET_STATUS(ph, st, errno) \ { \ int e = errno; \ (ph)->p_status = st; \ if (e != -1) \ (ph)->p_errno = e; \ } #define NSCD_SET_HERRNO(ph, herrno) \ (ph)->p_herrno = herrno; #define NSCD_SET_STATUS_SUCCESS(ph) \ (ph)->p_status = NSS_SUCCESS; \ (ph)->p_errno = 0; #define NSCD_SET_N2N_STATUS(ph, st, errno, n2nst) \ { \ int e = errno; \ (ph)->p_status = st; \ if (e != -1) \ (ph)->p_errno = e; \ (ph)->nscdpriv = n2nst; \ } #define NSCD_STATUS_IS_OK(ph) \ (((ph)->p_status) == NSS_SUCCESS) #define NSCD_STATUS_IS_NOT_OK(ph) \ (((ph)->p_status) != NSS_SUCCESS) #define NSCD_GET_STATUS(ph) \ (((nss_pheader_t *)(ph))->p_status) #define NSCD_GET_ERRNO(ph) \ (((nss_pheader_t *)(ph))->p_errno) #define NSCD_GET_HERRNO(ph) \ (((nss_pheader_t *)(ph))->p_herrno) #define NSCD_GET_NSCD_STATUS(ph) \ (((nss_pheader_t *)(ph))->nscdpriv) #define NSCD_CLEAR_STATUS(ph) \ (ph)->p_status = 0; \ (ph)->p_errno = 0; \ (ph)->nscdpriv = 0; #define NSCD_COPY_STATUS(ph, ph1) \ (ph)->p_status = (ph1)->p_status; \ (ph)->p_errno = (ph1)->p_errno; \ (ph)->nscdpriv = (ph1)->nscdpriv; nss_status_t _nscd_doorcall(int callnum); nss_status_t _nscd_doorcall_data(int callnum, void *indata, int indlen, void *outdata, int outdlen, nss_pheader_t *phdr); nss_status_t _nscd_doorcall_fd(int fd, int callnum, void *indata, int indlen, void *outdata, int outdlen, nss_pheader_t *phdr); nss_status_t _nscd_doorcall_sendfd(int fd, int callnum, void *indata, int indlen, nss_pheader_t *phdr); #ifdef __cplusplus } #endif #endif /* _NSCD_DOOR_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright 2018 Joyent, Inc. * Copyright 2019 Nexenta Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nscd_common.h" #include "nscd_door.h" #include "nscd_config.h" #include "nscd_switch.h" #include "nscd_log.h" #include "nscd_selfcred.h" #include "nscd_frontend.h" #include "nscd_admin.h" static void rts_mon(void); static void keep_open_dns_socket(void); extern nsc_ctx_t *cache_ctx_p[]; /* * Current active Configuration data for the frontend component */ static nscd_cfg_global_frontend_t frontend_cfg_g; static nscd_cfg_frontend_t *frontend_cfg; static int max_servers = 0; static int max_servers_set = 0; static int per_user_is_on = 1; static char *main_execname; static char **main_argv; extern int _whoami; extern long activity; extern mutex_t activity_lock; static sema_t common_sema; static thread_key_t lookup_state_key; static mutex_t create_lock = DEFAULTMUTEX; 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 = "NON-NULL TSD"; (void) thr_setname(thr_self(), "server_tsd_bind"); /* disable cancellation to avoid hangs if server threads disappear */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); (void) thr_setspecific(server_key, value); (void) 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 > max_servers) { num_servers--; (void) mutex_unlock(&create_lock); return; } (void) mutex_unlock(&create_lock); (void) 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); (void) thr_setspecific(server_key, NULL); } /* * get clearance */ int _nscd_get_clearance(sema_t *sema) { if (sema_trywait(&common_sema) == 0) { (void) thr_setspecific(lookup_state_key, NULL); return (0); } if (sema_trywait(sema) == 0) { (void) thr_setspecific(lookup_state_key, (void*)1); return (0); } return (1); } /* * release clearance */ int _nscd_release_clearance(sema_t *sema) { void *which; (void) thr_getspecific(lookup_state_key, &which); if (which == NULL) /* from common pool */ { (void) sema_post(&common_sema); return (0); } (void) sema_post(sema); return (1); } static void dozip(int signal __unused) { /* not much here */ } /* * _nscd_restart_if_cfgfile_changed() * Restart if modification times of nsswitch.conf or resolv.conf have changed. * * If nsswitch.conf has changed then it is possible that sources for * various backends have changed and therefore the current cached * data may not be consistent with the new data sources. By * restarting the cache will be cleared and the new configuration will * be used. * * The check for resolv.conf is made as only the first call to * res_gethostbyname() or res_getaddrbyname() causes a call to * res_ninit() to occur which in turn parses resolv.conf. Therefore * to benefit from changes to resolv.conf nscd must be restarted when * resolv.conf is updated, removed or created. If res_getXbyY calls * are removed from NSS then this check could be removed. * */ void _nscd_restart_if_cfgfile_changed() { static mutex_t nsswitch_lock = DEFAULTMUTEX; static timestruc_t last_nsswitch_check = { 0 }; static timestruc_t last_nsswitch_modified = { 0 }; static timestruc_t last_resolv_modified = { -1, 0 }; static mutex_t restarting_lock = DEFAULTMUTEX; static int restarting = 0; int restart = 0; time_t now = time(NULL); char *me = "_nscd_restart_if_cfgfile_changed"; #define FLAG_RESTART_REQUIRED if (restarting == 0) {\ (void) mutex_lock(&restarting_lock);\ if (restarting == 0) {\ restarting = 1;\ restart = 1;\ }\ (void) mutex_unlock(&restarting_lock);\ } if (restarting == 1) return; if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME) return; (void) mutex_lock(&nsswitch_lock); if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) { struct stat nss_buf; struct stat res_buf; last_nsswitch_check.tv_sec = now; last_nsswitch_check.tv_nsec = 0; (void) mutex_unlock(&nsswitch_lock); /* let others continue */ if (stat("/etc/nsswitch.conf", &nss_buf) < 0) { return; } else if (last_nsswitch_modified.tv_sec == 0) { last_nsswitch_modified = nss_buf.st_mtim; } if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec || (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec && last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) { FLAG_RESTART_REQUIRED; } if (restart == 0) { if (stat("/etc/resolv.conf", &res_buf) < 0) { /* Unable to stat file, were we previously? */ if (last_resolv_modified.tv_sec > 0) { /* Yes, it must have been removed. */ FLAG_RESTART_REQUIRED; } else if (last_resolv_modified.tv_sec == -1) { /* No, then we've never seen it. */ last_resolv_modified.tv_sec = 0; } } else if (last_resolv_modified.tv_sec == -1) { /* We've just started and file is present. */ last_resolv_modified = res_buf.st_mtim; } else if (last_resolv_modified.tv_sec == 0) { /* Wasn't there at start-up. */ FLAG_RESTART_REQUIRED; } else if (last_resolv_modified.tv_sec < res_buf.st_mtim.tv_sec || (last_resolv_modified.tv_sec == res_buf.st_mtim.tv_sec && last_resolv_modified.tv_nsec < res_buf.st_mtim.tv_nsec)) { FLAG_RESTART_REQUIRED; } } if (restart == 1) { char *fmri; /* * if in self cred mode, kill the forker and * child nscds */ if (_nscd_is_self_cred_on(0, NULL)) { _nscd_kill_forker(); _nscd_kill_all_children(); } /* * time for restart */ _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO) (me, "nscd restart due to %s or %s change\n", "/etc/nsswitch.conf", "resolv.conf"); /* * try to restart under smf */ if ((fmri = getenv("SMF_FMRI")) == NULL) { /* not running under smf - reexec */ (void) execv(main_execname, main_argv); exit(1); /* just in case */ } if (smf_restart_instance(fmri) == 0) (void) sleep(10); /* wait a bit */ exit(1); /* give up waiting for resurrection */ } } else (void) mutex_unlock(&nsswitch_lock); } uid_t _nscd_get_client_euid() { ucred_t *uc = NULL; uid_t id; char *me = "get_client_euid"; if (door_ucred(&uc) != 0) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door_ucred: %s\n", strerror(errno)); return ((uid_t)-1); } id = ucred_geteuid(uc); ucred_free(uc); return (id); } /* * Check to see if the door client's euid is 0 or if it has required_priv * privilege. Return 0 if yes, -1 otherwise. * Supported values for required_priv are: * - NSCD_ALL_PRIV: for all zones privileges * - NSCD_READ_PRIV: for PRIV_FILE_DAC_READ privilege */ int _nscd_check_client_priv(int required_priv) { int rc = 0; ucred_t *uc = NULL; const priv_set_t *eset; char *me = "_nscd_check_client_read_priv"; priv_set_t *zs; /* zone */ if (door_ucred(&uc) != 0) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "door_ucred: %s\n", strerror(errno)); return (-1); } if (ucred_geteuid(uc) == 0) { ucred_free(uc); return (0); } eset = ucred_getprivset(uc, PRIV_EFFECTIVE); switch (required_priv) { case NSCD_ALL_PRIV: zs = priv_str_to_set("zone", ",", NULL); if (!priv_isequalset(eset, zs)) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "missing all zones privileges\n"); rc = -1; } priv_freeset(zs); break; case NSCD_READ_PRIV: if (!priv_ismember(eset, PRIV_FILE_DAC_READ)) rc = -1; break; default: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "unknown required_priv: %d\n", required_priv); rc = -1; break; } ucred_free(uc); return (rc); } static void N2N_check_priv( void *buf, char *dc_str) { nss_pheader_t *phdr = (nss_pheader_t *)buf; ucred_t *uc = NULL; const priv_set_t *eset; zoneid_t zoneid; int errnum; char *me = "N2N_check_priv"; if (door_ucred(&uc) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door_ucred: %s\n", strerror(errno)); NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); return; } 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) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) (me, "%s call failed(cred): caller pid %d, uid %d, " "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc), ucred_getruid(uc), ucred_geteuid(uc), zoneid); ucred_free(uc); NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); return; } _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "nscd received %s cmd from pid %d, uid %d, " "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc), ucred_getruid(uc), ucred_geteuid(uc), zoneid); ucred_free(uc); NSCD_SET_STATUS_SUCCESS(phdr); } void _nscd_APP_check_cred( void *buf, pid_t *pidp, char *dc_str, int log_comp, int log_level) { nss_pheader_t *phdr = (nss_pheader_t *)buf; ucred_t *uc = NULL; uid_t ruid; uid_t euid; pid_t pid; int errnum; char *me = "_nscd_APP_check_cred"; if (door_ucred(&uc) != 0) { errnum = errno; _NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR) (me, "door_ucred: %s\n", strerror(errno)); NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); return; } NSCD_SET_STATUS_SUCCESS(phdr); pid = ucred_getpid(uc); if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc), euid = ucred_geteuid(uc))) { if (pidp != NULL) { if (*pidp == (pid_t)-1) *pidp = pid; else if (*pidp != pid) { NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); } } } else { NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); } ucred_free(uc); if (NSCD_STATUS_IS_NOT_OK(phdr)) { _NSCD_LOG(log_comp, log_level) (me, "%s call failed: caller pid %d (input pid = %d), ruid %d, " "euid %d, header ruid %d, header euid %d\n", dc_str, pid, (pidp != NULL) ? *pidp : -1, ruid, euid, ((nss_pheader_t *)(buf))->p_ruid, ((nss_pheader_t *)(buf))->p_euid); } } /* log error and return -1 when an invalid packed buffer header is found */ static int pheader_error(nss_pheader_t *phdr, uint32_t call_number) { char *call_num_str; switch (call_number) { case NSCD_SEARCH: call_num_str = "NSCD_SEARCH"; break; case NSCD_SETENT: call_num_str = "NSCD_SETENT"; break; case NSCD_GETENT: call_num_str = "NSCD_GETENT"; break; case NSCD_ENDENT: call_num_str = "NSCD_ENDENT"; break; case NSCD_PUT: call_num_str = "NSCD_PUT"; break; case NSCD_GETHINTS: call_num_str = "NSCD_GETHINTS"; break; default: call_num_str = "UNKNOWN"; break; } _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) ("pheader_error", "call number %s: invalid packed buffer header\n", call_num_str); NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); return (-1); } /* * Validate the header of a getXbyY or setent/getent/endent request. * Return 0 if good, -1 otherwise. * * A valid header looks like the following (size is arg_size, does * not include the output area): * +----------------------------------+ -- * | nss_pheader_t (header fixed part)| ^ * | | | * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t) * | data_off .... | | * | | v * +----------------------------------+ <----- dbd_off * | dbd (database description) | ^ * | nss_dbd_t + up to 3 strings | | * | length = sizeof(nss_dbd_t) + | len = key_off - dbd_off * | length of 3 strings + | | * | length of padding | | * | (total length in multiple of 4) | v * +----------------------------------+ <----- key_off * | lookup key | ^ * | nss_XbyY_key_t, content varies, | | * | based on database and lookup op | len = data_off - key_off * | length = data_off - key_off | | * | including padding, multiple of 4 | v * +----------------------------------+ <----- data_off (= arg_size) * | | ^ * | area to hold results | | * | | len = data_len (= pbufsiz - * | | | data_off) * | | v * +----------------------------------+ <----- pbufsiz */ static int validate_pheader( void *argp, size_t arg_size, uint32_t call_number) { nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; nssuint_t l1, l2; /* * current version is NSCD_HEADER_REV, length of the fixed part * of the header must match the size of nss_pheader_t */ if (phdr->p_version != NSCD_HEADER_REV || phdr->dbd_off != sizeof (nss_pheader_t)) return (pheader_error(phdr, call_number)); /* * buffer size and offsets must be in multiple of 4 */ if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) || (phdr->data_off & 3)) return (pheader_error(phdr, call_number)); /* * the input arg_size is the length of the request header * and should be less than NSCD_PHDR_MAXLEN */ if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN) return (pheader_error(phdr, call_number)); /* get length of the dbd area */ l1 = phdr->key_off - phdr-> dbd_off; /* * dbd area may contain padding, so length of dbd should * not be less than the length of the actual data */ if (l1 < phdr->dbd_len) return (pheader_error(phdr, call_number)); /* get length of the key area */ l2 = phdr->data_off - phdr->key_off; /* * key area may contain padding, so length of key area should * not be less than the length of the actual data */ if (l2 < phdr->key_len) return (pheader_error(phdr, call_number)); /* * length of fixed part + lengths of dbd and key area = length of * the request header */ if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off) return (pheader_error(phdr, call_number)); /* header length + data length = buffer length */ if (phdr->data_off + phdr->data_len != phdr->pbufsiz) return (pheader_error(phdr, call_number)); return (0); } /* log error and return -1 when an invalid nscd to nscd buffer is found */ static int N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number) { char *call_num_str; switch (call_number) { case NSCD_PING: call_num_str = "NSCD_PING"; break; case NSCD_IMHERE: call_num_str = "NSCD_IMHERE"; break; case NSCD_PULSE: call_num_str = "NSCD_PULSE"; break; case NSCD_FORK: call_num_str = "NSCD_FORK"; break; case NSCD_KILL: call_num_str = "NSCD_KILL"; break; case NSCD_REFRESH: call_num_str = "NSCD_REFRESH"; break; case NSCD_GETPUADMIN: call_num_str = "NSCD_GETPUADMIN"; break; case NSCD_GETADMIN: call_num_str = "NSCD_GETADMIN"; break; case NSCD_SETADMIN: call_num_str = "NSCD_SETADMIN"; break; case NSCD_KILLSERVER: call_num_str = "NSCD_KILLSERVER"; break; default: call_num_str = "UNKNOWN"; break; } _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) ("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str); NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, NSCD_DOOR_BUFFER_CHECK_FAILED); return (-1); } /* * Validate the buffer of an nscd to nscd request. * Return 0 if good, -1 otherwise. * * A valid buffer looks like the following (size is arg_size): * +----------------------------------+ -- * | nss_pheader_t (header fixed part)| ^ * | | | * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t) * | data_off .... | | * | | v * +----------------------------------+ <---dbd_off = key_off = data_off * | | ^ * | input data/output data | | * | OR no data | len = data_len (= pbufsiz - * | | | data_off) * | | | len could be zero * | | v * +----------------------------------+ <--- pbufsiz */ static int validate_N2Nbuf( void *argp, size_t arg_size, uint32_t call_number) { nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; /* * current version is NSCD_HEADER_REV, length of the fixed part * of the header must match the size of nss_pheader_t */ if (phdr->p_version != NSCD_HEADER_REV || phdr->dbd_off != sizeof (nss_pheader_t)) return (N2Nbuf_error(phdr, call_number)); /* * There are no dbd and key data, so the dbd, key, data * offsets should be equal */ if (phdr->dbd_off != phdr->key_off || phdr->dbd_off != phdr->data_off) return (N2Nbuf_error(phdr, call_number)); /* * the input arg_size is the buffer length and should * be less or equal than NSCD_N2NBUF_MAXLEN */ if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN) return (N2Nbuf_error(phdr, call_number)); /* header length + data length = buffer length */ if (phdr->data_off + phdr->data_len != phdr->pbufsiz) return (N2Nbuf_error(phdr, call_number)); return (0); } static void lookup(char *argp, size_t arg_size) { nsc_lookup_args_t largs; char space[NSCD_LOOKUP_BUFSIZE]; nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space)); /* * make sure the first couple bytes of the data area is null, * so that bad strings in the packed header stop here */ (void) memset((char *)phdr + phdr->data_off, 0, 16); (void) memset(&largs, 0, sizeof (largs)); largs.buffer = argp; largs.bufsize = arg_size; nsc_lookup(&largs, 0); /* * only the PUN needs to keep track of the * activity count to determine when to * terminate itself */ if (_whoami == NSCD_CHILD) { (void) mutex_lock(&activity_lock); ++activity; (void) mutex_unlock(&activity_lock); } NSCD_SET_RETURN_ARG(phdr, arg_size); (void) door_return(argp, arg_size, NULL, 0); } static void getent(char *argp, size_t arg_size) { char space[NSCD_LOOKUP_BUFSIZE]; nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space)); nss_pgetent(argp, arg_size); NSCD_SET_RETURN_ARG(phdr, arg_size); (void) door_return(argp, arg_size, NULL, 0); } static int is_db_per_user(void *buf, char *dblist) { nss_pheader_t *phdr = (nss_pheader_t *)buf; nss_dbd_t *pdbd; char *dbname, *dbn; int len; /* copy db name into a temp buffer */ pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off)); dbname = (char *)pdbd + pdbd->o_name; len = strlen(dbname); dbn = alloca(len + 2); (void) memcpy(dbn, dbname, len); /* check if + ',' can be found in the dblist string */ dbn[len] = ','; dbn[len + 1] = '\0'; if (strstr(dblist, dbn) != NULL) return (1); /* * check if can be found in the last part * of the dblist string */ dbn[len] = '\0'; if (strstr(dblist, dbn) != NULL) return (1); return (0); } /* * Check to see if all conditions are met for processing per-user * requests. Returns 1 if yes, -1 if backend is not configured, * 0 otherwise. */ static int need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist) { nss_pheader_t *phdr = (nss_pheader_t *)buf; NSCD_SET_STATUS_SUCCESS(phdr); /* if already a per-user nscd, no need to get per-user door */ if (whoami == NSCD_CHILD) return (0); /* forker shouldn't be asked */ if (whoami == NSCD_FORKER) { NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); return (0); } /* if door client is root, no need for a per-user door */ if (uid == 0) return (0); /* * if per-user lookup is not configured, no per-user * door available */ if (_nscd_is_self_cred_on(0, dblist) == 0) return (-1); /* * if per-user lookup is not configured for the db, * don't bother */ if (is_db_per_user(phdr, *dblist) == 0) return (0); return (1); } static void if_selfcred_return_per_user_door(char *argp, size_t arg_size, door_desc_t *dp, int whoami) { nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp); char *dblist; int door = -1; int rc = 0; door_desc_t desc; char *space; int len; /* * check to see if self-cred is configured and * need to return an alternate PUN door */ if (per_user_is_on == 1) { rc = need_per_user_door(argp, whoami, _nscd_get_client_euid(), &dblist); if (rc == -1) per_user_is_on = 0; } if (rc <= 0) { /* * self-cred not configured, and no error detected, * return to continue the door call processing */ if (NSCD_STATUS_IS_OK(phdr)) return; else /* * configured but error detected, * stop the door call processing */ (void) door_return(argp, phdr->data_off, NULL, 0); } /* get the alternate PUN door */ _nscd_proc_alt_get(argp, &door); if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) { (void) door_return(argp, phdr->data_off, NULL, 0); } /* return the alternate door descriptor */ len = strlen(dblist) + 1; space = alloca(arg_size + len); phdr->data_len = len; (void) memcpy(space, phdr, arg_size); (void) strncpy((char *)space + arg_size, dblist, len); dp = &desc; dp->d_attributes = DOOR_DESCRIPTOR; dp->d_data.d_desc.d_descriptor = door; arg_size += len; (void) door_return(space, arg_size, dp, 1); } /*ARGSUSED*/ static void switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { int iam; pid_t ent_pid = -1; nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp); void *uptr; int len; size_t buflen; int callnum; char *me = "switcher"; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "switcher ...\n"); if (argp == DOOR_UNREF_DATA) { (void) printf("Door Slam... exiting\n"); exit(0); } if (argp == NULL) { /* empty door call */ (void) door_return(NULL, 0, 0, 0); /* return the favor */ } /* * need to restart if main nscd and config file(s) changed */ if (_whoami == NSCD_MAIN) _nscd_restart_if_cfgfile_changed(); if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) { /* make sure the packed buffer header is good */ if (validate_pheader(argp, arg_size, phdr->nsc_callnumber) == -1) (void) door_return(argp, arg_size, NULL, 0); switch (phdr->nsc_callnumber) { case NSCD_SEARCH: /* if a fallback to main nscd, skip per-user setup */ if (phdr->p_status != NSS_ALTRETRY) if_selfcred_return_per_user_door(argp, arg_size, dp, _whoami); lookup(argp, arg_size); break; case NSCD_SETENT: _nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT", NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT); if (NSCD_STATUS_IS_OK(phdr)) { if_selfcred_return_per_user_door(argp, arg_size, dp, _whoami); nss_psetent(argp, arg_size, ent_pid); } break; case NSCD_GETENT: getent(argp, arg_size); break; case NSCD_ENDENT: nss_pendent(argp, arg_size); break; case NSCD_PUT: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "door call NSCD_PUT not supported yet\n"); NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); break; case NSCD_GETHINTS: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "door call NSCD_GETHINTS not supported yet\n"); NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); break; default: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "Unknown name service door call op %x\n", phdr->nsc_callnumber); NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); break; } (void) door_return(argp, arg_size, NULL, 0); } iam = NSCD_MAIN; callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI; if (callnum == NSCD_IMHERE || callnum == NSCD_PULSE || callnum == NSCD_FORK) iam = phdr->nsc_callnumber & NSCD_WHOAMI; else callnum = phdr->nsc_callnumber; /* nscd -> nscd v2 calls */ /* make sure the buffer is good */ if (validate_N2Nbuf(argp, arg_size, callnum) == -1) (void) door_return(argp, arg_size, NULL, 0); switch (callnum) { case NSCD_PING: NSCD_SET_STATUS_SUCCESS(phdr); break; case NSCD_IMHERE: _nscd_proc_iamhere(argp, dp, n_desc, iam); break; case NSCD_PULSE: N2N_check_priv(argp, "NSCD_PULSE"); if (NSCD_STATUS_IS_OK(phdr)) _nscd_proc_pulse(argp, iam); break; case NSCD_FORK: N2N_check_priv(argp, "NSCD_FORK"); if (NSCD_STATUS_IS_OK(phdr)) _nscd_proc_fork(argp, iam); break; case NSCD_KILL: N2N_check_priv(argp, "NSCD_KILL"); if (NSCD_STATUS_IS_OK(phdr)) exit(0); break; case NSCD_REFRESH: N2N_check_priv(argp, "NSCD_REFRESH"); if (NSCD_STATUS_IS_OK(phdr)) { if (_nscd_refresh() != NSCD_SUCCESS) exit(1); NSCD_SET_STATUS_SUCCESS(phdr); } break; case NSCD_GETPUADMIN: if (_nscd_is_self_cred_on(0, NULL)) { _nscd_peruser_getadmin(argp, sizeof (nscd_admin_t)); } else { NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, NSCD_SELF_CRED_NOT_CONFIGURED); } break; case NSCD_GETADMIN: len = _nscd_door_getadmin((void *)argp); if (len == 0) break; /* size of door buffer not big enough, allocate one */ NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen); /* copy packed header */ *(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp); /* set new buffer size */ ((nss_pheader_t *)uptr)->pbufsiz = buflen; /* try one more time */ (void) _nscd_door_getadmin((void *)uptr); (void) door_return(uptr, buflen, NULL, 0); break; case NSCD_SETADMIN: N2N_check_priv(argp, "NSCD_SETADMIN"); if (NSCD_STATUS_IS_OK(phdr)) _nscd_door_setadmin(argp); break; case NSCD_KILLSERVER: N2N_check_priv(argp, "NSCD_KILLSERVER"); if (NSCD_STATUS_IS_OK(phdr)) { /* also kill the forker nscd if one is running */ _nscd_kill_forker(); exit(0); } break; default: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "Unknown name service door call op %d\n", phdr->nsc_callnumber); NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); (void) door_return(argp, arg_size, NULL, 0); break; } (void) door_return(argp, arg_size, NULL, 0); } int _nscd_setup_server(char *execname, char **argv) { int fd; int errnum; int bind_failed = 0; mode_t old_mask; struct stat buf; sigset_t myset; struct sigaction action; char *me = "_nscd_setup_server"; main_execname = execname; main_argv = argv; /* Any nscd process is to ignore SIGPIPE */ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "signal (SIGPIPE): %s\n", strerror(errnum)); return (-1); } keep_open_dns_socket(); /* * the max number of server threads should be fixed now, so * set flag to indicate that no in-flight change is allowed */ max_servers_set = 1; (void) thr_keycreate(&lookup_state_key, NULL); (void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads, USYNC_THREAD, 0); /* Establish server thread pool */ (void) door_server_create(server_create); if (thr_keycreate(&server_key, server_destroy) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "thr_keycreate (server thread): %s\n", strerror(errnum)); return (-1); } /* Create a door */ if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE, DOOR_UNREF | DOOR_NO_CANCEL)) < 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "door_create: %s\n", strerror(errnum)); return (-1); } /* if not main nscd, no more setup to do */ if (_whoami != NSCD_MAIN) return (fd); /* bind to file system */ if (is_system_labeled() && (getzoneid() == GLOBAL_ZONEID)) { if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) { int newfd; /* make sure the door will be readable by all */ old_mask = umask(0); if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "Cannot create %s: %s\n", TSOL_NAME_SERVICE_DOOR, strerror(errnum)); bind_failed = 1; } /* rstore the old file mode creation mask */ (void) umask(old_mask); (void) close(newfd); } if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) { if (errno != EEXIST) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "Cannot symlink %s: %s\n", NAME_SERVICE_DOOR, strerror(errnum)); bind_failed = 1; } } } else if (stat(NAME_SERVICE_DOOR, &buf) < 0) { int newfd; /* make sure the door will be readable by all */ old_mask = umask(0); if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR, strerror(errnum)); bind_failed = 1; } /* rstore the old file mode creation mask */ (void) umask(old_mask); (void) close(newfd); } if (bind_failed == 1) { (void) door_revoke(fd); return (-1); } if (fattach(fd, NAME_SERVICE_DOOR) < 0) { if ((errno != EBUSY) || (fdetach(NAME_SERVICE_DOOR) < 0) || (fattach(fd, NAME_SERVICE_DOOR) < 0)) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "fattach: %s\n", strerror(errnum)); (void) door_revoke(fd); return (-1); } } /* * kick off routing socket monitor thread */ if (thr_create(NULL, 0, (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "thr_create (routing socket monitor): %s\n", strerror(errnum)); (void) door_revoke(fd); return (-1); } /* * set up signal handler for SIGHUP */ 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) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "sigaction (SIGHUP): %s\n", strerror(errnum)); (void) door_revoke(fd); return (-1); } return (fd); } int _nscd_setup_child_server(int did) { int errnum; int fd; nscd_rc_t rc; char *me = "_nscd_setup_child_server"; /* Re-establish our own server thread pool */ (void) door_server_create(server_create); if (thr_keycreate(&server_key, server_destroy) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "thr_keycreate failed: %s", strerror(errnum)); return (-1); } /* * Create a new door. * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork) */ (void) close(did); if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE, DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) (me, "door_create failed: %s", strerror(errnum)); return (-1); } /* * kick off routing socket monitor thread */ if (thr_create(NULL, 0, (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "thr_create (routing socket monitor): %s\n", strerror(errnum)); (void) door_revoke(fd); return (-1); } /* * start monitoring the states of the name service clients */ rc = _nscd_init_smf_monitor(); if (rc != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "unable to start the SMF monitor (rc = %d)\n", rc); (void) door_revoke(fd); return (-1); } return (fd); } nscd_rc_t _nscd_alloc_frontend_cfg() { frontend_cfg = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t)); if (frontend_cfg == NULL) return (NSCD_NO_MEMORY); return (NSCD_SUCCESS); } /* ARGSUSED */ nscd_rc_t _nscd_cfg_frontend_notify( void *data, struct nscd_cfg_param_desc *pdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t dflag, nscd_cfg_error_t **errorp, void *cookie) { void *dp; /* * At init time, the whole group of config params are received. * At update time, group or individual parameter value could * be received. */ if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) || _nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { /* * group data is received, copy in the * entire strcture */ if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data; else frontend_cfg[nswdb->index] = *(nscd_cfg_frontend_t *)data; } else { /* * individual paramater is received: copy in the * parameter value. */ if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) dp = (char *)&frontend_cfg_g + pdesc->p_offset; else dp = (char *)&frontend_cfg[nswdb->index] + pdesc->p_offset; (void) memcpy(dp, data, pdesc->p_size); } return (NSCD_SUCCESS); } /* ARGSUSED */ nscd_rc_t _nscd_cfg_frontend_verify( void *data, struct nscd_cfg_param_desc *pdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t dflag, nscd_cfg_error_t **errorp, void **cookie) { char *me = "_nscd_cfg_frontend_verify"; /* * if max. number of server threads is set and in effect, * don't allow changing of the frontend configuration */ if (max_servers_set) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO) (me, "changing of the frontend configuration not allowed now"); return (NSCD_CFG_CHANGE_NOT_ALLOWED); } return (NSCD_SUCCESS); } /* ARGSUSED */ nscd_rc_t _nscd_cfg_frontend_get_stat( void **stat, struct nscd_cfg_stat_desc *sdesc, nscd_cfg_id_t *nswdb, nscd_cfg_flag_t *dflag, void (**free_stat)(void *stat), nscd_cfg_error_t **errorp) { return (NSCD_SUCCESS); } void _nscd_init_cache_sema(sema_t *sema, char *cache_name) { int i, j; char *dbn; if (max_servers == 0) max_servers = frontend_cfg_g.common_worker_threads + frontend_cfg_g.cache_hit_threads; for (i = 0; i < NSCD_NUM_DB; i++) { dbn = NSCD_NSW_DB_NAME(i); if (strcasecmp(dbn, cache_name) == 0) { j = frontend_cfg[i].worker_thread_per_nsw_db; (void) sema_init(sema, j, USYNC_THREAD, 0); max_servers += j; break; } } } /* * 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, idx; 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; char *me = "rts_mon"; (void) thr_setname(thr_self(), me); rt_sock = socket(PF_ROUTE, SOCK_RAW, 0); if (rt_sock < 0) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "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)) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "routing socket read: %s\n", strerror(errno)); thr_exit(0); } continue; } if (ifam->ifam_version != RTM_VERSION) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "rx unknown version (%d) on " "routing socket.\n", ifam->ifam_version); continue; } switch (ifam->ifam_type) { case RTM_NEWADDR: case RTM_DELADDR: /* if no ipnodes cache, then nothing to do */ idx = get_cache_idx("ipnodes"); if (cache_ctx_p[idx] == NULL || cache_ctx_p[idx]->reaper_on != nscd_true) break; nsc_invalidate(cache_ctx_p[idx], NULL, NULL); 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: case RTM_CHGADDR: case RTM_FREEADDR: break; default: _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "rx unknown msg type (%d) on routing socket.\n", ifam->ifam_type); break; } } } static void keep_open_dns_socket(void) { _res.options |= RES_STAYOPEN; /* just keep this udp socket open */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NSCD_FRONTEND_H #define _NSCD_FRONTEND_H #ifdef __cplusplus extern "C" { #endif #include "cache.h" #define NSCD_N2NBUF_MAXLEN 1024 * 8 #define NSCD_PHDR_MAXLEN 1024 * 8 #define NSCD_LOOKUP_BUFSIZE 1024 * 16 #define NSCD_DOORBUF_MAXLEN 1024 * 512 #define NSCD_PHDR_LEN(hdrp) ((hdrp)->data_off) #define NSCD_DATA_LEN(hdrp) ((hdrp)->data_len) /* used to check clients required privileges */ #define NSCD_ALL_PRIV 0 #define NSCD_READ_PRIV 1 #define NSCD_ALLOC_LOOKUP_BUFFER(bufp, bufsiz, hdrp, space, spsiz) \ if ((hdrp)->pbufsiz <= spsiz) { \ (void) memcpy(space, (hdrp), NSCD_PHDR_LEN((hdrp))); \ bufp = space; \ bufsiz = spsiz; \ hdrp = (nss_pheader_t *)(void *)space; \ (hdrp)->pbufsiz = bufsiz; \ (hdrp)->data_len = bufsiz - (hdrp)->data_off; \ } else { \ (bufp) = NULL; \ bufsiz = (hdrp)->pbufsiz; \ if (bufsiz > spsiz) \ bufsiz = NSCD_DOORBUF_MAXLEN; \ (bufp) = alloca(bufsiz); \ if ((bufp) != NULL) { \ (void) memcpy((bufp), (hdrp), NSCD_PHDR_LEN(hdrp)); \ (hdrp) = (nss_pheader_t *)(void *)(bufp); \ (hdrp)->pbufsiz = bufsiz; \ (hdrp)->data_len = bufsiz - (hdrp)->data_off; \ } else { \ NSCD_SET_STATUS((hdrp), NSS_ERROR, ENOMEM); \ (void) door_return((char *)(hdrp), \ NSCD_PHDR_LEN(hdrp), NULL, 0); \ } \ } #define NSCD_SET_RETURN_ARG(hdrp, arg_size) \ if (NSCD_STATUS_IS_OK((nss_pheader_t *)(hdrp))) \ arg_size = NSCD_PHDR_LEN(hdrp) + (NSCD_DATA_LEN(hdrp) > 0 ? \ NSCD_DATA_LEN(hdrp) + 1 : 0); \ else \ arg_size = NSCD_PHDR_LEN(hdrp); /* prototypes */ uid_t _nscd_get_client_euid(); int _nscd_check_client_priv(int); int _nscd_setup_server(char *execname, char **argv); int _nscd_setup_child_server(int did); int _nscd_get_clearance(sema_t *sema); int _nscd_release_clearance(sema_t *sema); void _nscd_init_cache_sema(sema_t *sema, char *cache_name); nscd_rc_t _nscd_alloc_frontend_cfg(); void _nscd_APP_check_cred(void *buf, pid_t *pidp, char *dc_str, int log_comp, int log_level); void _nscd_restart_if_cfgfile_changed(); #ifdef __cplusplus } #endif #endif /* _NSCD_FRONTEND_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Joyent, Inc. */ #include #include #include #include #include #include #include "nscd_db.h" #include "nscd_log.h" #include "nscd_switch.h" #include "nscd_door.h" extern int _whoami; static mutex_t getent_monitor_mutex = DEFAULTMUTEX; static int getent_monitor_started = 0; static rwlock_t getent_ctxDB_rwlock = DEFAULTRWLOCK; static nscd_db_t *getent_ctxDB = NULL; /* * internal structure representing a nscd getent context */ typedef struct nscd_getent_ctx { int to_delete; /* this ctx no longer valid */ nscd_getent_context_t *ptr; nscd_cookie_num_t cookie_num; } nscd_getent_ctx_t; /* * nscd_getent_context_t list for each nss database. Protected * by the readers/writer lock nscd_getent_ctx_lock. */ nscd_getent_ctx_base_t **nscd_getent_ctx_base; static rwlock_t nscd_getent_ctx_base_lock = DEFAULTRWLOCK; extern nscd_db_entry_t *_nscd_walk_db(nscd_db_t *db, void **cookie); static nscd_rc_t _nscd_init_getent_ctx_monitor(); /* * FUNCTION: _nscd_create_getent_ctxDB * * Create the internal getent context database to keep track of the * getent contexts currently being used. */ nscd_db_t * _nscd_create_getent_ctxDB() { nscd_db_t *ret; (void) rw_wrlock(&getent_ctxDB_rwlock); if (getent_ctxDB != NULL) { (void) rw_unlock(&getent_ctxDB_rwlock); return (getent_ctxDB); } ret = _nscd_alloc_db(NSCD_DB_SIZE_LARGE); if (ret != NULL) getent_ctxDB = ret; (void) rw_unlock(&getent_ctxDB_rwlock); return (ret); } /* * FUNCTION: _nscd_add_getent_ctx * * Add a getent context to the internal context database. */ static nscd_rc_t _nscd_add_getent_ctx( nscd_getent_context_t *ptr, nscd_cookie_num_t cookie_num) { int size; char buf[32]; nscd_db_entry_t *db_entry; nscd_getent_ctx_t *gnctx; if (ptr == NULL) return (NSCD_INVALID_ARGUMENT); (void) snprintf(buf, sizeof (buf), "%lld", cookie_num); size = sizeof (*gnctx); db_entry = _nscd_alloc_db_entry(NSCD_DATA_CTX_ADDR, (const char *)buf, size, 1, 1); if (db_entry == NULL) return (NSCD_NO_MEMORY); gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); gnctx->ptr = ptr; gnctx->cookie_num = cookie_num; (void) rw_wrlock(&getent_ctxDB_rwlock); (void) _nscd_add_db_entry(getent_ctxDB, buf, db_entry, NSCD_ADD_DB_ENTRY_FIRST); (void) rw_unlock(&getent_ctxDB_rwlock); return (NSCD_SUCCESS); } /* * FUNCTION: _nscd_is_getent_ctx * * Check to see if a getent context can be found in the internal * getent context database. */ nscd_getent_context_t * _nscd_is_getent_ctx( nscd_cookie_num_t cookie_num) { char ptrstr[32]; const nscd_db_entry_t *db_entry; nscd_getent_context_t *ret = NULL; char *me = "_nscd_is_getent_ctx"; (void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie_num); (void) rw_rdlock(&getent_ctxDB_rwlock); db_entry = _nscd_get_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR, (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0); if (db_entry != NULL) { nscd_getent_ctx_t *gnctx; gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "getent context %p, cookie# %lld, to_delete %d\n", gnctx->ptr, gnctx->cookie_num, gnctx->to_delete); /* * If the ctx is not to be deleted and the cookie numbers * match, return the ctx if not aborted and not in use. * Otherwise return NULL. */ if (gnctx->to_delete == 0 && gnctx->cookie_num == cookie_num) { ret = gnctx->ptr; (void) mutex_lock(&gnctx->ptr->getent_mutex); if (ret->aborted == 1 || ret->in_use == 1) ret = NULL; else ret->in_use = 1; (void) mutex_unlock(&gnctx->ptr->getent_mutex); } } (void) rw_unlock(&getent_ctxDB_rwlock); return (ret); } int _nscd_is_getent_ctx_in_use( nscd_getent_context_t *ctx) { int in_use; char *me = "_nscd_getent_ctx_in_use"; (void) mutex_lock(&ctx->getent_mutex); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "in_use = %d, ctx->thr_id = %d, thread id = %d\n", ctx->in_use, ctx->thr_id, thr_self()); in_use = ctx->in_use; if (in_use == 1 && ctx->thr_id == thr_self()) in_use = 0; (void) mutex_unlock(&ctx->getent_mutex); return (in_use); } /* * FUNCTION: _nscd_free_ctx_if_aborted * * Check to see if the getent session associated with a getent context had * been aborted. If so, return the getent context back to the pool. */ void _nscd_free_ctx_if_aborted( nscd_getent_context_t *ctx) { int aborted; char *me = "_nscd_free_ctx_if_aborted"; (void) mutex_lock(&ctx->getent_mutex); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "in_use = %d, aborted = %d\n", ctx->in_use, ctx->aborted); if (ctx->in_use != 1) { (void) mutex_unlock(&ctx->getent_mutex); return; } aborted = ctx->aborted; ctx->in_use = 0; (void) mutex_unlock(&ctx->getent_mutex); if (aborted == 1) { _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "getent session aborted, return the getent context\n"); _nscd_put_getent_ctx(ctx); } } /* * FUNCTION: _nscd_del_getent_ctx * * Delete a getent context from the internal getent context database. */ static void _nscd_del_getent_ctx( nscd_getent_context_t *ptr, nscd_cookie_num_t cookie_num) { char ptrstr[32]; nscd_getent_ctx_t *gnctx; const nscd_db_entry_t *db_entry; if (ptr == NULL) return; (void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie_num); (void) rw_rdlock(&getent_ctxDB_rwlock); /* * first find the db entry and make sure the * sequence number matched, then delete it from * the database. */ db_entry = _nscd_get_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR, (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0); if (db_entry != NULL) { gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); if (gnctx->ptr == ptr && gnctx->cookie_num == cookie_num) { (void) rw_unlock(&getent_ctxDB_rwlock); (void) rw_wrlock(&getent_ctxDB_rwlock); (void) _nscd_delete_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR, (const char *)ptrstr, NSCD_DEL_FIRST_DB_ENTRY, 0); } } (void) rw_unlock(&getent_ctxDB_rwlock); } static void _nscd_free_getent_ctx( nscd_getent_context_t *gnctx) { char *me = "_nscd_free_getent_ctx"; _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "getent context %p\n", gnctx); _nscd_put_nsw_state(gnctx->nsw_state); if (gnctx->base != NULL) { /* remove reference to the getent context base */ _nscd_release((nscd_acc_data_t *)gnctx->base); gnctx->base = NULL; } _nscd_del_getent_ctx(gnctx, gnctx->cookie_num); free(gnctx); } static void _nscd_free_getent_ctx_base( nscd_acc_data_t *data) { nscd_getent_ctx_base_t *base = (nscd_getent_ctx_base_t *)data; nscd_getent_context_t *c, *tc; char *me = "_nscd_free_getent_ctx_base"; _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "getent context base %p\n", base); if (base == NULL) return; c = base->first; while (c != NULL) { tc = c->next; _nscd_free_getent_ctx(c); c = tc; } } void _nscd_free_all_getent_ctx_base() { nscd_getent_ctx_base_t *base; int i; char *me = "_nscd_free_all_getent_ctx_base"; _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "entering ..\n"); (void) rw_wrlock(&nscd_getent_ctx_base_lock); for (i = 0; i < NSCD_NUM_DB; i++) { base = nscd_getent_ctx_base[i]; if (base == NULL) continue; nscd_getent_ctx_base[i] = (nscd_getent_ctx_base_t *) _nscd_set((nscd_acc_data_t *)base, NULL); } (void) rw_unlock(&nscd_getent_ctx_base_lock); } static nscd_getent_context_t * _nscd_create_getent_ctx( nscd_nsw_params_t *params) { nscd_getent_context_t *gnctx; nss_db_root_t db_root; char *me = "_nscd_create_getent_ctx"; gnctx = calloc(1, sizeof (nscd_getent_context_t)); if (gnctx == NULL) return (NULL); else { _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "getent context allocated %p\n", gnctx); } gnctx->dbi = params->dbi; gnctx->cookie_num = _nscd_get_cookie_num(); gnctx->pid = -1; (void) mutex_init(&gnctx->getent_mutex, USYNC_THREAD, NULL); if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) { free(gnctx); return (NULL); } gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s; /* this is a nsw_state used for getent processing */ gnctx->nsw_state->getent = 1; _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "got nsw_state %p\n", gnctx->nsw_state); return (gnctx); } nscd_rc_t _nscd_get_getent_ctx( nss_getent_t *contextpp, nscd_nsw_params_t *params) { nscd_getent_context_t *c; nscd_getent_ctx_base_t *base, *tmp; nscd_rc_t rc; char *me = "_nscd_get_getent_ctx"; _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "entering ...\n"); (void) rw_rdlock(&nscd_getent_ctx_base_lock); base = nscd_getent_ctx_base[params->dbi]; (void) rw_unlock(&nscd_getent_ctx_base_lock); assert(base != NULL); /* * If the context list is not empty, return the first one * on the list. Otherwise, create and return a new one if * limit is not reached. If limit is reached return an error * so that the client can perform the enumeration. */ tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock( (nscd_acc_data_t *)base); assert(base == tmp); if (base->first == NULL) { if (base->num_getent_ctx >= base->max_getent_ctx) { /* run out of contexts */ _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "run out of getent ctxs\n"); _nscd_mutex_unlock((nscd_acc_data_t *)base); return (NSCD_CREATE_GETENT_CTX_FAILED); } else { base->first = _nscd_create_getent_ctx(params); if (base->first != NULL) base->num_getent_ctx++; else { /* not able to create a getent ctx */ _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR) (me, "create getent ctx failed\n"); _nscd_mutex_unlock((nscd_acc_data_t *)base); return (NSCD_CREATE_GETENT_CTX_FAILED); } _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "got a new getent ctx %p\n", base->first); } } assert(base->first != NULL); c = base->first; base->first = c->next; c->next = NULL; c->seq_num = 1; c->cookie_num = _nscd_get_cookie_num(); c->in_use = 1; c->thr_id = thr_self(); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "got a getent ctx %p\n", c); /* * reference count the getent context base bfore handing out * the getent context */ c->base = (nscd_getent_ctx_base_t *) _nscd_get((nscd_acc_data_t *)base); _nscd_mutex_unlock((nscd_acc_data_t *)base); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "adding new ctx %p, cookie # = %lld\n", c, c->cookie_num); if ((rc = _nscd_add_getent_ctx(c, c->cookie_num)) != NSCD_SUCCESS) { _nscd_put_getent_ctx(c); return (rc); } contextpp->ctx = (struct nss_getent_context *)c; /* start monitor and reclaim orphan getent context */ if (getent_monitor_started == 0) { (void) mutex_lock(&getent_monitor_mutex); if (getent_monitor_started == 0) { getent_monitor_started = 1; (void) _nscd_init_getent_ctx_monitor(); } (void) mutex_unlock(&getent_monitor_mutex); } return (NSCD_SUCCESS); } void _nscd_put_getent_ctx( nscd_getent_context_t *gnctx) { nscd_getent_ctx_base_t *base; char *me = "_nscd_put_getent_ctx"; base = gnctx->base; /* if context base is gone or no longer current, free this context */ if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) { _nscd_free_getent_ctx(gnctx); return; } if (base->first != NULL) { gnctx->next = base->first; base->first = gnctx; } else base->first = gnctx; /* put back the db state */ _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "putting back nsw state %p\n", gnctx->nsw_state); /* this nsw_state is no longer used for getent processing */ if (gnctx->nsw_state != NULL) { gnctx->nsw_state->getent = 0; _nscd_put_nsw_state(gnctx->nsw_state); gnctx->nsw_state = NULL; } gnctx->aborted = 0; gnctx->in_use = 0; gnctx->thr_id = (thread_t)-1; _nscd_del_getent_ctx(gnctx, gnctx->cookie_num); _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "ctx (%p, cookie # = %lld) removed from getent ctx DB\n", gnctx, gnctx->cookie_num); gnctx->seq_num = 0; gnctx->cookie_num = 0; gnctx->pid = -1; gnctx->thr_id = (thread_t)-1; gnctx->n_src = 0; gnctx->be = NULL; /* remove reference to the getent context base */ _nscd_release((nscd_acc_data_t *)base); gnctx->base = NULL; _nscd_mutex_unlock((nscd_acc_data_t *)base); } nscd_rc_t _nscd_init_getent_ctx_base( int dbi, int lock) { nscd_getent_ctx_base_t *base = NULL; char *me = "_nscd_init_getent_ctx_base"; if (lock) (void) rw_rdlock(&nscd_getent_ctx_base_lock); base = (nscd_getent_ctx_base_t *)_nscd_alloc( NSCD_DATA_GETENT_CTX_BASE, sizeof (nscd_getent_ctx_base_t), _nscd_free_getent_ctx_base, NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND); if (base == NULL) { if (lock) (void) rw_unlock(&nscd_getent_ctx_base_lock); return (NSCD_NO_MEMORY); } _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "base %p allocated\n", base); /* * initialize and activate the new getent_ctx base */ base->dbi = dbi; base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db; nscd_getent_ctx_base[dbi] = (nscd_getent_ctx_base_t *)_nscd_set( (nscd_acc_data_t *)nscd_getent_ctx_base[dbi], (nscd_acc_data_t *)base); if (lock) (void) rw_unlock(&nscd_getent_ctx_base_lock); return (NSCD_SUCCESS); } nscd_rc_t _nscd_init_all_getent_ctx_base() { int i; nscd_rc_t rc; char *me = "_nscd_init_all_getent_ctx_base"; (void) rw_wrlock(&nscd_getent_ctx_base_lock); for (i = 0; i < NSCD_NUM_DB; i++) { rc = _nscd_init_getent_ctx_base(i, 0); if (rc != NSCD_SUCCESS) { (void) rw_unlock(&nscd_getent_ctx_base_lock); return (rc); } } _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "all getent context base initialized\n"); (void) rw_unlock(&nscd_getent_ctx_base_lock); return (NSCD_SUCCESS); } nscd_rc_t _nscd_alloc_getent_ctx_base() { (void) rw_wrlock(&nscd_getent_ctx_base_lock); nscd_getent_ctx_base = calloc(NSCD_NUM_DB, sizeof (nscd_getent_ctx_base_t *)); if (nscd_getent_ctx_base == NULL) { (void) rw_unlock(&nscd_getent_ctx_base_lock); return (NSCD_NO_MEMORY); } (void) rw_unlock(&nscd_getent_ctx_base_lock); return (NSCD_SUCCESS); } static int process_exited(pid_t pid) { char pname[PATH_MAX]; int fd; (void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid); if ((fd = open(pname, O_RDONLY)) == -1) return (1); else { (void) close(fd); return (0); } } /* * FUNCTION: reclaim_getent_ctx */ /*ARGSUSED*/ static void * __NORETURN reclaim_getent_ctx(void *arg) { void *cookie = NULL; nscd_db_entry_t *ep; nscd_getent_ctx_t *ctx; nscd_getent_context_t *gctx, *c; nscd_getent_context_t *first = NULL, *last = NULL; nss_getent_t nssctx = { 0 }; char *me = "reclaim_getent_ctx"; (void) thr_setname(thr_self(), me); /*CONSTCOND*/ while (1) { (void) sleep(60); (void) rw_rdlock(&getent_ctxDB_rwlock); for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL; ep = _nscd_walk_db(getent_ctxDB, &cookie)) { ctx = (nscd_getent_ctx_t *)*(ep->data_array); gctx = ctx->ptr; /* * if the client process, which did the setent, * exited, add the context to the orphan list */ if (gctx->pid != -1 && process_exited(gctx->pid)) { _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "process %d exited, " "getent context = %p, " "db index = %d, cookie # = %lld, " "sequence # = %lld\n", gctx->pid, gctx, gctx->dbi, gctx->cookie_num, gctx->seq_num); if (first != NULL) { /* add to list if not in already */ for (c = first; c != NULL; c = c->next_to_reclaim) { if (gctx == c) break; } if (c == NULL) { last->next_to_reclaim = gctx; last = gctx; } } else { first = gctx; last = gctx; } } } (void) rw_unlock(&getent_ctxDB_rwlock); /* * return all the orphan getent contexts to the pool if not * in use */ for (gctx = first; gctx; ) { int in_use, num_reclaim_check; c = gctx->next_to_reclaim; gctx->next_to_reclaim = NULL; gctx->aborted = 1; (void) mutex_lock(&gctx->getent_mutex); num_reclaim_check = gctx->num_reclaim_check++; if (num_reclaim_check > 1) gctx->in_use = 0; in_use = gctx->in_use; (void) mutex_unlock(&gctx->getent_mutex); if (in_use == 0) { _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "process %d exited, " "freeing getent context = %p\n", gctx->pid, gctx); nssctx.ctx = (struct nss_getent_context *)gctx; nss_endent(NULL, NULL, &nssctx); } gctx = c; } first = last = NULL; } /*NOTREACHED*/ /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/ } static nscd_rc_t _nscd_init_getent_ctx_monitor() { int errnum; char *me = "_nscd_init_getent_ctx_monitor"; _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) (me, "initializing the getent context monitor\n"); /* * the forker nscd does not process getent requests * so no need to monitor orphan getent contexts */ if (_whoami == NSCD_FORKER) return (NSCD_SUCCESS); /* * start a thread to reclaim unused getent contexts */ if (thr_create(NULL, 0, reclaim_getent_ctx, NULL, THR_DETACHED, NULL) != 0) { errnum = errno; _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR) (me, "thr_create: %s\n", strerror(errnum)); return (NSCD_THREAD_CREATE_ERROR); } return (NSCD_SUCCESS); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "nscd_common.h" #include "nscd_config.h" #include "nscd_log.h" #include "nscd_switch.h" #include "nscd_frontend.h" static char *cfgfile_save = NULL; static mutex_t time_mutex = DEFAULTMUTEX; static time_t start_time = 0; void _nscd_set_start_time(int reset) { (void) mutex_lock(&time_mutex); if (start_time == 0 || reset == 1) start_time = time(NULL); (void) mutex_unlock(&time_mutex); } time_t _nscd_get_start_time() { return (start_time); } nscd_rc_t _nscd_init( char *cfgfile) { char *me = "nscd_init"; nscd_rc_t rc; nscd_cfg_error_t *err; /* * remember when main or forker nscd starts. */ _nscd_set_start_time(0); /* * allocate the space for tables */ if ((rc = _nscd_alloc_nsw_config()) != NSCD_SUCCESS || (rc = _nscd_alloc_service_state_table()) != NSCD_SUCCESS || (rc = _nscd_alloc_nsw_state_base()) != NSCD_SUCCESS || (rc = _nscd_alloc_nsw_be_info_db()) != NSCD_SUCCESS || (rc = _nscd_alloc_getent_ctx_base()) != NSCD_SUCCESS) return (rc); /* * allocate the space for local configuration * and statistics */ if ((rc = _nscd_alloc_switch_cfg()) != NSCD_SUCCESS || (rc = _nscd_alloc_frontend_cfg()) != NSCD_SUCCESS || (rc = _nscd_alloc_switch_stats()) != NSCD_SUCCESS) return (rc); /* * Create and init the internal address database to keep * track of the memory allocated by _nscd_alloc */ if (_nscd_create_int_addrDB() == NULL) { _NSCD_LOG(NSCD_LOG_INT_ADDR, NSCD_LOG_LEVEL_ERROR) (me, "_nscd_create_int_addrDB failed\n"); return (NSCD_NO_MEMORY); } /* * Create and init the internal context database to keep * track of the getent context currently being used */ if (_nscd_create_getent_ctxDB() == NULL) { _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR) (me, "_nscd_create_getent_ctx_addrDB failed\n"); return (NSCD_NO_MEMORY); } /* * Create the backend info database for each possible source */ if ((rc = _nscd_init_all_nsw_be_info_db()) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_NSW_STATE, NSCD_LOG_LEVEL_ERROR) (me, "_nscd_init_all_nsw_be_info_db failed (rc = %d)\n", rc); return (rc); } /* * Create the nscd_nsw_config_t for each possible nss database */ if ((rc = _nscd_init_all_nsw_config()) != NSCD_SUCCESS) { _NSCD_LOG(NSCD_LOG_NSW_STATE, NSCD_LOG_LEVEL_ERROR) (me, "_nscd_init_all_nsw_config failed (rc = %d)\n", rc); return (rc); } /* * initialize config/stats management */ rc = _nscd_cfg_init(&err); if (rc != NSCD_SUCCESS) { if (err != NULL) _nscd_cfg_free_error(err); return (rc); } /* * read in the nsswitch configuration */ rc = _nscd_cfg_read_nsswitch_file("/etc/nsswitch.conf", &err); if (rc != NSCD_SUCCESS) { (void) printf( gettext("reading config file %s failed with rc = %d, %s\n"), "/etc/nsswitch.conf", rc, NSCD_ERR2MSG(err)); if (err != NULL) _nscd_cfg_free_error(err); _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "unable to read /etc/nsswitch.conf (rc = %d)\n", rc); return (rc); } /* * remember which version of /etc/nsswitch.conf that was read */ _nscd_restart_if_cfgfile_changed(); /* * read in the nscd configuration */ if (cfgfile == NULL) { cfgfile = "/etc/nscd.conf"; if (access(cfgfile, R_OK) != 0) { _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "unable to read /etc/nscd.conf (rc = %d)\n", rc); return (NSCD_CFG_FILE_ACCESS_ERROR); } } rc = _nscd_cfg_read_file(cfgfile, &err); if (rc != NSCD_SUCCESS) { (void) printf( gettext("reading config file %s failed with rc = %d, %s\n"), cfgfile, rc, NSCD_ERR2MSG(err)); if (err != NULL) _nscd_cfg_free_error(err); _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) (me, "unable to read configuration from %s (rc = %d)\n", cfgfile, rc); return (rc); } /* * remember the name of the config file * in case refresh is requested later */ if (cfgfile != NULL) { cfgfile_save = strdup(cfgfile); if (cfgfile_save == NULL) return (NSCD_NO_MEMORY); } return (NSCD_SUCCESS); } nscd_rc_t _nscd_refresh() { char *me = "nscd_refresh"; char *cfgfile; nscd_rc_t rc; nscd_cfg_error_t *err; char errmsg[1024]; /* * re-read the nscd configuration */ if (cfgfile_save == NULL) cfgfile = "/etc/nscd.conf"; else cfgfile = cfgfile_save; if (access(cfgfile, R_OK) != 0) { (void) snprintf(errmsg, sizeof (errmsg), "unable to read the config file %s (rc = %d), %s\n", cfgfile, NSCD_CFG_FILE_ACCESS_ERROR, strerror(errno)); goto error_exit; } rc = _nscd_cfg_read_file(cfgfile, &err); if (rc != NSCD_SUCCESS) { (void) snprintf(errmsg, sizeof (errmsg), "unable to parse the config file %s (rc = %d), %s\n", cfgfile, rc, NSCD_ERR2MSG(err)); goto error_exit; } _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ALL) (me, "nscd configuration refreshed successfully\n"); return (NSCD_SUCCESS); error_exit: if (err != NULL) _nscd_cfg_free_error(err); _NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR) (me, "%s\n", errmsg); return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include "nscd_common.h" #include "nscd_switch.h" void _nss_initf_passwd(nss_db_params_t *p) { p->name = NSS_DBNAM_PASSWD; p->default_config = NSS_DEFCONF_PASSWD; } void _nss_initf_hosts(nss_db_params_t *p) { p->name = NSS_DBNAM_HOSTS; p->default_config = NSS_DEFCONF_HOSTS; } void _nss_initf_group(nss_db_params_t *p) { p->name = NSS_DBNAM_GROUP; p->default_config = NSS_DEFCONF_GROUP; } void _nss_initf_ipnodes(nss_db_params_t *p) { p->name = NSS_DBNAM_IPNODES; p->default_config = NSS_DEFCONF_IPNODES; } void _nss_initf_net(nss_db_params_t *p) { p->name = NSS_DBNAM_NETWORKS; p->default_config = NSS_DEFCONF_NETWORKS; } void _nss_initf_proto(nss_db_params_t *p) { p->name = NSS_DBNAM_PROTOCOLS; p->default_config = NSS_DEFCONF_PROTOCOLS; } void _nss_initf_rpc(p) nss_db_params_t *p; { p->name = NSS_DBNAM_RPC; p->default_config = NSS_DEFCONF_RPC; } void _nss_initf_ethers(nss_db_params_t *p) { p->name = NSS_DBNAM_ETHERS; p->default_config = NSS_DEFCONF_ETHERS; } void _nss_initf_netmasks(nss_db_params_t *p) { p->name = NSS_DBNAM_NETMASKS; p->default_config = NSS_DEFCONF_NETMASKS; } void _nss_initf_bootparams(nss_db_params_t *p) { p->name = NSS_DBNAM_BOOTPARAMS; p->default_config = NSS_DEFCONF_BOOTPARAMS; } void _nss_initf_publickey(nss_db_params_t *p) { p->name = NSS_DBNAM_PUBLICKEY; p->default_config = NSS_DEFCONF_PUBLICKEY; } void _nss_initf_netgroup(nss_db_params_t *p) { p->name = NSS_DBNAM_NETGROUP; p->default_config = NSS_DEFCONF_NETGROUP; } void _nss_initf_services(nss_db_params_t *p) { p->name = NSS_DBNAM_SERVICES; p->default_config = NSS_DEFCONF_SERVICES; } void _nss_initf_printers(nss_db_params_t *p) { p->name = NSS_DBNAM_PRINTERS; p->default_config = NSS_DEFCONF_PRINTERS; } void _nss_initf_authattr(nss_db_params_t *p) { p->name = NSS_DBNAM_AUTHATTR; p->default_config = NSS_DEFCONF_AUTHATTR; } void _nss_initf_profattr(nss_db_params_t *p) { p->name = NSS_DBNAM_PROFATTR; p->default_config = NSS_DEFCONF_PROFATTR; } void _nss_initf_execattr(nss_db_params_t *p) { p->name = NSS_DBNAM_EXECATTR; p->default_config = NSS_DEFCONF_PROFATTR; p->config_name = NSS_DBNAM_PROFATTR; /* use config for "prof_attr" */ } void _nss_initf_userattr(nss_db_params_t *p) { p->name = NSS_DBNAM_USERATTR; p->config_name = NSS_DBNAM_PASSWD; p->default_config = NSS_DEFCONF_USERATTR; } void _nss_initf_project(nss_db_params_t *p) { p->name = NSS_DBNAM_PROJECT; p->default_config = NSS_DEFCONF_PROJECT; } void _nss_initf_auuser(nss_db_params_t *p) { p->name = NSS_DBNAM_AUDITUSER; p->config_name = NSS_DBNAM_PASSWD; p->default_config = NSS_DEFCONF_AUDITUSER; } void _nss_initf_shadow(nss_db_params_t *p) { p->name = NSS_DBNAM_SHADOW; p->config_name = NSS_DBNAM_PASSWD; p->default_config = NSS_DEFCONF_PASSWD; } void _nss_initf_passwd_compat(nss_db_params_t *p) { p->name = NSS_DBNAM_PASSWD; p->config_name = NSS_DBNAM_PASSWD_COMPAT; p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } void _nss_initf_group_compat(nss_db_params_t *p) { p->name = NSS_DBNAM_GROUP; p->config_name = NSS_DBNAM_GROUP_COMPAT; p->default_config = NSS_DEFCONF_GROUP_COMPAT; } void _nss_initf_tsol_rh(nss_db_params_t *p) { p->name = NSS_DBNAM_TSOL_RH; p->default_config = NSS_DEFCONF_TSOL_RH; } void _nss_initf_tsol_tp(nss_db_params_t *p) { p->name = NSS_DBNAM_TSOL_TP; p->default_config = NSS_DEFCONF_TSOL_TP; } nss_db_initf_t nscd_nss_db_initf[] = { _nss_initf_passwd, _nss_initf_hosts, _nss_initf_group, _nss_initf_ipnodes, _nss_initf_net, _nss_initf_proto, _nss_initf_rpc, _nss_initf_ethers, _nss_initf_netmasks, _nss_initf_bootparams, _nss_initf_publickey, _nss_initf_netgroup, _nss_initf_services, _nss_initf_printers, _nss_initf_authattr, _nss_initf_profattr, _nss_initf_execattr, _nss_initf_userattr, _nss_initf_project, _nss_initf_shadow, _nss_initf_auuser, _nss_initf_tsol_rh, _nss_initf_tsol_tp, _nss_initf_passwd_compat, _nss_initf_group_compat, /* * no initf() for pseudo-databases: passwd, shadow, audit_user, * user_attr, and group (when called from the compat backend) */ NULL, NULL, NULL, NULL, NULL}; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "nscd_db.h" #include "nscd_log.h" static rwlock_t addrDB_rwlock = DEFAULTRWLOCK; static nscd_db_t *addrDB = NULL; /* * internal structure representing a nscd internal address */ typedef struct nscd_int_addr { int to_delete; /* no longer valid */ int type; void *ptr; nscd_seq_num_t seq_num; rwlock_t rwlock; /* used to serialize get and destroy */ } nscd_int_addr_t; /* * FUNCTION: _nscd_create_int_addrDB * * Create the internal address database to keep track of the * memory allocated by _nscd_alloc. */ void * _nscd_create_int_addrDB() { nscd_db_t *ret; char *me = "_nscd_create_int_addrDB"; _NSCD_LOG(NSCD_LOG_INT_ADDR | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) (me, "initializing the internal address database\n"); (void) rw_wrlock(&addrDB_rwlock); if (addrDB != NULL) { (void) rw_unlock(&addrDB_rwlock); return (addrDB); } ret = _nscd_alloc_db(NSCD_DB_SIZE_LARGE); if (ret != NULL) addrDB = ret; (void) rw_unlock(&addrDB_rwlock); return (ret); } /* * FUNCTION: _nscd_add_int_addr * * Add an address of 'type' to the internal address database. */ nscd_rc_t _nscd_add_int_addr( void *ptr, int type, nscd_seq_num_t seq_num) { int size; char buf[2 * sizeof (ptr) + 1]; nscd_db_entry_t *db_entry; nscd_int_addr_t *int_addr; if (ptr == NULL) return (NSCD_INVALID_ARGUMENT); (void) snprintf(buf, sizeof (buf), "%p", ptr); size = sizeof (*int_addr); db_entry = _nscd_alloc_db_entry(NSCD_DATA_ADDR, (const char *)buf, size, 1, 1); if (db_entry == NULL) return (NSCD_NO_MEMORY); int_addr = (nscd_int_addr_t *)*(db_entry->data_array); int_addr->ptr = ptr; int_addr->type = type; int_addr->seq_num = seq_num; (void) rwlock_init(&int_addr->rwlock, USYNC_THREAD, NULL); (void) rw_wrlock(&addrDB_rwlock); (void) _nscd_add_db_entry(addrDB, buf, db_entry, NSCD_ADD_DB_ENTRY_FIRST); (void) rw_unlock(&addrDB_rwlock); return (NSCD_SUCCESS); } /* * FUNCTION: _nscd_is_int_addr * * Check to see if an address can be found in the internal * address database, if so, obtain a reader lock on the * associated rw_lock. The caller needs to unlock it when * done using the data. */ rwlock_t * _nscd_is_int_addr( void *ptr, nscd_seq_num_t seq_num) { char *me = "_nscd_is_int_addr"; char ptrstr[1 + 2 * sizeof (ptr)]; rwlock_t *addr_rwlock; const nscd_db_entry_t *db_entry; if (ptr == NULL) return (NULL); (void) snprintf(ptrstr, sizeof (ptrstr), "%p", ptr); (void) rw_rdlock(&addrDB_rwlock); db_entry = _nscd_get_db_entry(addrDB, NSCD_DATA_ADDR, (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0); if (db_entry != NULL) { nscd_int_addr_t *int_addr; int_addr = (nscd_int_addr_t *)*(db_entry->data_array); addr_rwlock = &int_addr->rwlock; (void) rw_rdlock(addr_rwlock); /* * If the data is marked as to be deleted * or the sequence number does not match, * return NULL. */ if (int_addr->to_delete == 1 || int_addr->seq_num != seq_num) { (void) rw_unlock(addr_rwlock); addr_rwlock = NULL; } _NSCD_LOG(NSCD_LOG_INT_ADDR, NSCD_LOG_LEVEL_DEBUG) (me, "found %p, seq# = %lld\n", ptr, int_addr->seq_num); } else addr_rwlock = NULL; (void) rw_unlock(&addrDB_rwlock); return (addr_rwlock); } /* * FUNCTION: _nscd_del_int_addr * * Delete an address from the internal address database. */ void _nscd_del_int_addr( void *ptr, nscd_seq_num_t seq_num) { char *me = "_nscd_del_int_addr"; char ptrstr[1 + 2 * sizeof (ptr)]; rwlock_t *addr_rwlock; nscd_int_addr_t *int_addr; const nscd_db_entry_t *db_entry; if (ptr == NULL) return; _NSCD_LOG(NSCD_LOG_INT_ADDR, NSCD_LOG_LEVEL_DEBUG) (me, "deleting int addr %p (%d)\n", ptr, seq_num); (void) snprintf(ptrstr, sizeof (ptrstr), "%p", ptr); (void) rw_rdlock(&addrDB_rwlock); /* * first find the db entry and make sure that * no one is currently locking it. i.e., * no one is waiting to use the same address. * If it is locked, rw_wrlock() will not return * until it is unlocked. */ db_entry = _nscd_get_db_entry(addrDB, NSCD_DATA_ADDR, (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0); if (db_entry != NULL) { int_addr = (nscd_int_addr_t *)*(db_entry->data_array); addr_rwlock = &int_addr->rwlock; (void) rw_wrlock(addr_rwlock); } else { (void) rw_unlock(&addrDB_rwlock); return; } (void) rw_unlock(&addrDB_rwlock); /* * delete the db entry if the sequence numbers match */ if (int_addr->seq_num == seq_num) { (void) rw_wrlock(&addrDB_rwlock); (void) _nscd_delete_db_entry(addrDB, NSCD_DATA_ADDR, (const char *)ptrstr, NSCD_DEL_FIRST_DB_ENTRY, 0); (void) rw_unlock(&addrDB_rwlock); } } /* * FUNCTION: _nscd_destroy_int_addrDB * * Destroy the internal address database. */ void _nscd_destroy_int_addrDB() { (void) rw_wrlock(&addrDB_rwlock); _nscd_free_db(addrDB); addrDB = NULL; (void) rw_unlock(&addrDB_rwlock); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include "nscd_log.h" #include "nscd_config.h" #include "nscd_switch.h" #include "cache.h" /* * old nscd debug levels */ #define DBG_OFF 0 #define DBG_CANT_FIND 2 #define DBG_NETLOOKUPS 4 #define DBG_ALL 6 /* max. chars in a nscd log entry */ #define LOGBUFLEN 1024 /* configuration for the nscd log component */ int _nscd_log_comp = 0x0; int _nscd_log_level = 0x0; static char _nscd_logfile[PATH_MAX] = { 0 }; #define NSCD_DEBUG_NONE '0' #define NSCD_DEBUG_OPEN '1' #define NSCD_DEBUG_CLOSE '2' static char _nscd_debug = NSCD_DEBUG_NONE; static char _nscd_logfile_d[PATH_MAX] = { 0 }; static char _nscd_logfile_s[PATH_MAX] = { 0 }; /* statistics data */ static nscd_cfg_stat_global_log_t logstats = { NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, 0 }; /* if no log file specified, log entry goes to stderr */ int _logfd = 2; /* close old log file and open a new one */ static nscd_rc_t _nscd_set_lf( char *lf) { int newlogfd; char *me = "_nscd_set_lf"; /* * don't try and open the log file /dev/null */ if (lf == NULL || *lf == 0) { /* ignore empty log file specs */ return (NSCD_SUCCESS); } else if (strcmp(lf, "/dev/null") == 0) { (void) strlcpy(_nscd_logfile, lf, PATH_MAX); if (_logfd >= 0) (void) close(_logfd); _logfd = -1; return (NSCD_SUCCESS); } else if (strcmp(lf, "stderr") == 0) { (void) strlcpy(_nscd_logfile, lf, PATH_MAX); if (_logfd != -1 && _logfd != 2) (void) close(_logfd); _logfd = 2; return (NSCD_SUCCESS); } else { /* * In order to open this file securely, we'll try a few tricks */ if ((newlogfd = open(lf, 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(lf, &before) < 0) { if (_nscd_debug == NSCD_DEBUG_NONE) _nscd_logit(me, "Cannot open new " "logfile \"%s\": %sn", lf, strerror(errno)); return (NSCD_CFG_FILE_OPEN_ERROR); } 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(lf, O_APPEND|O_WRONLY, 0644)) < 0) { if (_nscd_debug == NSCD_DEBUG_NONE) _nscd_logit(me, "Cannot open new "\ "logfile \"%s\": %s\n", lf, strerror(errno)); return (NSCD_CFG_FILE_OPEN_ERROR); } } else { if (_nscd_debug == NSCD_DEBUG_NONE) _nscd_logit(me, "Cannot use specified " "logfile \"%s\": "\ "file is/has links or isn't " "owned by root\n", lf); return (NSCD_CFG_FILE_OPEN_ERROR); } } (void) close(_logfd); (void) strlcpy(_nscd_logfile, lf, PATH_MAX); _logfd = newlogfd; if (_nscd_debug == NSCD_DEBUG_NONE) _nscd_logit(me, "Start of new logfile %s\n", lf); } return (NSCD_SUCCESS); } /* log an entry to the configured nscd log file */ void _nscd_logit( char *funcname, char *format, ...) { static mutex_t loglock = DEFAULTMUTEX; struct timeval tv; char tid_buf[32]; char pid_buf[32]; char buffer[LOGBUFLEN]; int safechars, offset; va_list ap; if (_logfd < 0) return; if (_nscd_debug == NSCD_DEBUG_OPEN) { (void) mutex_lock(&loglock); if (_nscd_debug == NSCD_DEBUG_OPEN && *_nscd_logfile_d != '\0' && (strcmp(_nscd_logfile, "/dev/null") == 0 || strcmp(_nscd_logfile, "stderr") == 0)) { (void) strlcpy(_nscd_logfile_s, _nscd_logfile, PATH_MAX); (void) _nscd_set_lf(_nscd_logfile_d); } _nscd_debug = NSCD_DEBUG_NONE; (void) mutex_unlock(&loglock); } else if (_nscd_debug == NSCD_DEBUG_CLOSE) { (void) mutex_lock(&loglock); if (_nscd_debug == NSCD_DEBUG_CLOSE) (void) _nscd_set_lf(_nscd_logfile_s); _nscd_debug = NSCD_DEBUG_NONE; (void) mutex_unlock(&loglock); } va_start(ap, format); if (gettimeofday(&tv, NULL) != 0 || ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) { (void) snprintf(buffer, LOGBUFLEN, "