2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <string.h>
2N/A#include <pthread.h>
2N/A#include <syslog.h>
2N/A#include <rpcsvc/nis.h>
2N/A
2N/A#include "nis_hashitem.h"
2N/A
2N/A/* We're the magician, so undefine the define-magic */
2N/A#undef NIS_HASH_ITEM
2N/A#undef NIS_HASH_TABLE
2N/A#undef nis_insert_item
2N/A#undef nis_find_item
2N/A#undef nis_pop_item
2N/A#undef nis_remove_item
2N/A
2N/A#define set_thread_status(msg, state)
2N/A
2N/A/*
2N/A * The hash table routines below implement nested (or recursive)
2N/A * one-writer-or-many-readers locking. The following restrictions
2N/A * exist:
2N/A *
2N/A * Unless an item destructor has been established, an item
2N/A * MUST NOT be removed from a list (__nis_pop_item_mt() or
2N/A * (__nis_remove_item_mt()) when the thread performing
2N/A * the deletion is holding a read-only lock on the item.
2N/A * Doing so will result in the thread blocking in
2N/A * pthread_cond_wait() waiting for itself to signal on
2N/A * the condition variable. Deletion when the invoking
2N/A * thread is holding a write lock (any level of nesting),
2N/A * or no lock, is OK.
2N/A */
2N/A
2N/Avoid
2N/A__nis_init_hash_table(__nis_hash_table_mt *table,
2N/A void (*itemDestructor)(void *)) {
2N/A
2N/A int errorcode;
2N/A
2N/A if (table != 0) {
2N/A errorcode = pthread_mutex_init(&table->lock, 0);
2N/A if (errorcode != 0) {
2N/A syslog(LOG_WARNING, "__nis_init_hash_table: "
2N/A "(table->lock) pthread_mutex_init returned %d (%s)",
2N/A errorcode, strerror(errorcode));
2N/A }
2N/A
2N/A errorcode = pthread_cond_init(&table->cond, 0);
2N/A if (errorcode != 0) {
2N/A syslog(LOG_WARNING, "__nis_init_hash_table: "
2N/A "(table->cond) pthread_cond_init returned %d (%s)",
2N/A errorcode, strerror(errorcode));
2N/A }
2N/A
2N/A errorcode = pthread_mutex_init(&table->traverser_id_lock, 0);
2N/A if (errorcode != 0) {
2N/A syslog(LOG_WARNING, "__nis_init_hash_table: "
2N/A "(table->traverser_id_lock) "
2N/A "pthread_mutex_init returned %d (%s)",
2N/A errorcode, strerror(errorcode));
2N/A }
2N/A
2N/A table->traversed = 0;
2N/A table->locked_items = 0;
2N/A (void) memset(table->keys, 0, sizeof (table->keys));
2N/A table->first = 0;
2N/A table->destroyItem = itemDestructor;
2N/A }
2N/A}
2N/A
2N/Aint
2N/A__nis_lock_hash_table(__nis_hash_table_mt *table, int traverse, char *msg) {
2N/A
2N/A pthread_t myself = pthread_self();
2N/A
2N/A if (table != 0) {
2N/A if (traverse) {
2N/A /*
2N/A * We want exclusive access to everything in the
2N/A * table (list). Wait until there are no threads
2N/A * either traversing the list, or with exclusive
2N/A * access to an item.
2N/A */
2N/A set_thread_status(msg, "table WL");
2N/A (void) pthread_mutex_lock(&table->lock);
2N/A set_thread_status(msg, "table L");
2N/A while ((table->traversed != 0 &&
2N/A table->traverser_id != myself) ||
2N/A table->locked_items != 0) {
2N/A set_thread_status(msg, "traverse cond_wait");
2N/A MT_LOG(1, (LOG_NOTICE,
2N/A "%d: lh table 0x%x trav cond wait",
2N/A myself, table));
2N/A (void) pthread_cond_wait(&table->cond,
2N/A &table->lock);
2N/A }
2N/A set_thread_status(msg, "traverser_id WL");
2N/A (void) pthread_mutex_lock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id L");
2N/A table->traversed = 1;
2N/A table->traverser_id = myself;
2N/A (void) pthread_mutex_unlock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id U");
2N/A } else {
2N/A /*
2N/A * Called from the nis_*_item() functions. If no one's
2N/A * locked the table, lock it. If the table already is
2N/A * being traversed by us, do nothing. Otherwise, wait
2N/A * for the lock.
2N/A */
2N/A set_thread_status(msg, "non-traverse TL");
2N/A if (pthread_mutex_trylock(&table->lock) == EBUSY) {
2N/A int dolock = 1;
2N/A /* Already locked; find out if it's us */
2N/A set_thread_status(msg, "traverser_id L");
2N/A (void) pthread_mutex_lock(
2N/A &table->traverser_id_lock);
2N/A if (table->traversed != 0 &&
2N/A table->traverser_id == myself) {
2N/A /* It's us. No action. */
2N/A dolock = 0;
2N/A }
2N/A (void) pthread_mutex_unlock(
2N/A &table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id U");
2N/A /* Not us. Wait for the lock */
2N/A if (dolock) {
2N/A MT_LOG(1, (LOG_NOTICE,
2N/A "%d: lh table 0x%x cond wait",
2N/A myself, table));
2N/A set_thread_status(msg, "table WL");
2N/A (void) pthread_mutex_lock(&table->lock);
2N/A set_thread_status(msg, "table L");
2N/A }
2N/A }
2N/A }
2N/A MT_LOG(1, (LOG_NOTICE, "%d: lh table %s lock acquired 0x%x",
2N/A myself, traverse?"traverse":"non-traverse", table));
2N/A return (1);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Aint
2N/A__nis_ulock_hash_table(__nis_hash_table_mt *table, int traverse, char *msg) {
2N/A
2N/A int dounlock = 0;
2N/A
2N/A if (table != 0) {
2N/A if (traverse) {
2N/A /*
2N/A * Since we're keeping track of who's traversing the
2N/A * table in order to avoid recursive locking in the
2N/A * nis_*item() functions, we might as well sanity check
2N/A * here.
2N/A */
2N/A set_thread_status(msg, "traverser_id WL");
2N/A (void) pthread_mutex_lock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id L");
2N/A if (table->traversed != 0 &&
2N/A table->traverser_id == pthread_self()) {
2N/A table->traversed = 0;
2N/A /*
2N/A * Leave traverser_id as it is, so that it
2N/A * possible to see which thread last held
2N/A * the traverser lock.
2N/A */
2N/A dounlock = 1;
2N/A /* Wake up other traversers-to-be */
2N/A set_thread_status(msg, "table cond_signal");
2N/A (void) pthread_cond_signal(&table->cond);
2N/A }
2N/A (void) pthread_mutex_unlock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id U");
2N/A } else {
2N/A /*
2N/A * Called from the nis_*_item() functions. If we're
2N/A * traversing the table, leave it locked.
2N/A */
2N/A set_thread_status(msg, "traverser_id WL");
2N/A (void) pthread_mutex_lock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id L");
2N/A if (table->traversed == 0) {
2N/A dounlock = 1;
2N/A }
2N/A (void) pthread_mutex_unlock(&table->traverser_id_lock);
2N/A set_thread_status(msg, "traverser_id U");
2N/A }
2N/A if (dounlock) {
2N/A (void) pthread_mutex_unlock(&table->lock);
2N/A set_thread_status(msg, "table U");
2N/A }
2N/A MT_LOG(1, (LOG_NOTICE, "%d: lh table %s release 0x%x (%s)",
2N/A pthread_self(), traverse?"traverse":"non-traverse", table,
2N/A dounlock?"unlocked":"still held"));
2N/A return (1);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Astatic __nis_hash_item_mt **
2N/A__find_item_mt(nis_name name, __nis_hash_table_mt *table, int *keyp) {
2N/A
2N/A int key = 0;
2N/A unsigned char *s;
2N/A __nis_hash_item_mt *it, **pp;
2N/A
2N/A /* Assume table!=0, table lock held */
2N/A
2N/A for (s = (unsigned char *)name; *s != 0; s++) {
2N/A key += *s;
2N/A }
2N/A key %= (sizeof (table->keys) / sizeof (table->keys[0]));
2N/A
2N/A if (keyp != 0) {
2N/A *keyp = key;
2N/A }
2N/A for (pp = &table->keys[key]; (it = *pp) != 0; pp = &it->next) {
2N/A if (strcmp(name, it->name) == 0) {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A return (pp);
2N/A}
2N/A
2N/A/*
2N/A * The 'readwrite' argument is interpreted as follows on a successful
2N/A * return:
2N/A *
2N/A * < 0 Exclusive access to item
2N/A * 0 Item must not be used or referenced in any way
2N/A * > 0 Non-exclusive access (read-only) to item
2N/A *
2N/A * Except when 'readwrite' == 0, the caller must explicitly release the
2N/A * item (__nis_release_item()).
2N/A */
2N/Aint
2N/A__nis_insert_item_mt(void *arg, __nis_hash_table_mt *table, int readwrite) {
2N/A
2N/A __nis_hash_item_mt *item = arg;
2N/A int key;
2N/A __nis_hash_item_mt **pp;
2N/A
2N/A if (item == 0 || __nis_lock_hash_table(table, 0, "nitmt") == 0)
2N/A return (0);
2N/A
2N/A if ((*(pp = __find_item_mt(item->name, table, &key))) != 0) {
2N/A (void) __nis_ulock_hash_table(table, 0, "nitmt");
2N/A return (0);
2N/A }
2N/A
2N/A (void) pthread_cond_init(&item->lock, 0);
2N/A item->readers = item->writer = 0;
2N/A item->last_reader_id = item->writer_id = INV_PTHREAD_ID;
2N/A if (readwrite < 0) {
2N/A item->writer = 1;
2N/A item->writer_id = pthread_self();
2N/A table->locked_items++;
2N/A } else if (readwrite > 0) {
2N/A item->readers = 1;
2N/A item->last_reader_id = pthread_self();
2N/A table->locked_items++;
2N/A }
2N/A item->next = *pp;
2N/A *pp = item;
2N/A item->keychain = key;
2N/A
2N/A if (table->first)
2N/A table->first->prv_item = item;
2N/A
2N/A item->nxt_item = table->first;
2N/A item->prv_item = NULL;
2N/A table->first = item;
2N/A
2N/A (void) __nis_ulock_hash_table(table, 0, "nitmt");
2N/A
2N/A return (1);
2N/A}
2N/A
2N/Avoid
2N/A__nis_insert_name_mt(nis_name name, __nis_hash_table_mt *table) {
2N/A
2N/A __nis_hash_item_mt *item;
2N/A
2N/A if (name == 0 || table == 0)
2N/A return;
2N/A
2N/A if ((item = malloc(sizeof (*item))) == 0) {
2N/A syslog(LOG_WARNING, "__nis_insert_name_mt: malloc failed\n");
2N/A return;
2N/A }
2N/A
2N/A if ((item->name = strdup(name)) == 0) {
2N/A syslog(LOG_WARNING, "__nis_insert_name_mt: strdup failed\n");
2N/A free(item);
2N/A return;
2N/A }
2N/A
2N/A if (! __nis_insert_item_mt(item, table, 0)) {
2N/A free(item->name);
2N/A free(item);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * readwrite: < 0 Exclusive access
2N/A * 0 No access; must not use returned item in any way,
2N/A * other than to confirm existence indicated by a non-NULL
2N/A * return value.
2N/A * > 0 Non-exclusive (read-only) access
2N/A *
2N/A * If trylock != 0 and *trylock != 0 and the item exists but the requested
2N/A * lock type cannot be acquired, set *trylock = -1 and return 0.
2N/A */
2N/Avoid *
2N/A__nis_find_item_mt(nis_name name, __nis_hash_table_mt *table, int readwrite,
2N/A int *trylock) {
2N/A
2N/A __nis_hash_item_mt *item;
2N/A pthread_t me = pthread_self();
2N/A
2N/A if (name == 0 || __nis_lock_hash_table(table, 0, "nfimt") == 0)
2N/A return (0);
2N/A
2N/A /*
2N/A * Block waiting for more favorable conditions unless:
2N/A *
2N/A * The item doesn't exist anymore
2N/A *
2N/A * 'readwrite' == 0 (verify existence only)
2N/A *
2N/A * There's a writer, but it's us
2N/A *
2N/A * There are no writers, and we're satisfied by RO access
2N/A *
2N/A * A trylock was requested
2N/A */
2N/A while ((item = *__find_item_mt(name, table, 0)) != 0) {
2N/A if (readwrite == 0 ||
2N/A (item->writer == 0 && item->readers == 0))
2N/A break;
2N/A if (item->writer == 0 && readwrite > 0)
2N/A break;
2N/A if ((item->writer != 0 && item->writer_id == me))
2N/A break;
2N/A if (trylock != 0 && *trylock != 0) {
2N/A *trylock = -1;
2N/A (void) __nis_ulock_hash_table(table, 0, "nfimt");
2N/A return (0);
2N/A }
2N/A (void) pthread_cond_wait(&item->lock, &table->lock);
2N/A }
2N/A
2N/A if (item != 0) {
2N/A if (readwrite < 0) {
2N/A if (item->writer == 0) {
2N/A item->writer_id = me;
2N/A table->locked_items++;
2N/A }
2N/A item->writer++;
2N/A } else if (readwrite > 0) {
2N/A if (item->readers == 0) {
2N/A table->locked_items++;
2N/A }
2N/A item->last_reader_id = me;
2N/A item->readers++;
2N/A }
2N/A }
2N/A
2N/A (void) __nis_ulock_hash_table(table, 0, "nfimt");
2N/A
2N/A return (item);
2N/A}
2N/A
2N/Avoid *
2N/A__nis_pop_item_mt(__nis_hash_table_mt *table) {
2N/A
2N/A __nis_hash_item_mt *item, *cur, *prev;
2N/A pthread_t mtid;
2N/A
2N/A if (__nis_lock_hash_table(table, 0, "npimt") == 0)
2N/A return (0);
2N/A
2N/A /* Wait until the first item isn't in use by another thread */
2N/A mtid = pthread_self();
2N/A while ((item = table->first) != 0) {
2N/A if (table->destroyItem != 0)
2N/A break;
2N/A if (item->readers == 0 && item->writer == 0)
2N/A break;
2N/A if (item->writer != 0 && item->writer_id == mtid)
2N/A break;
2N/A (void) pthread_cond_wait(&item->lock, &table->lock);
2N/A }
2N/A
2N/A /* List might be empty now */
2N/A if (item == 0) {
2N/A __nis_ulock_hash_table(table, 0, "npimt");
2N/A return (0);
2N/A }
2N/A
2N/A prev = 0;
2N/A for (cur = table->keys[item->keychain]; cur;
2N/A prev = cur, cur = cur->next) {
2N/A if (cur == item) {
2N/A if (prev)
2N/A prev->next = cur->next;
2N/A else
2N/A table->keys[cur->keychain] = cur->next;
2N/A if (cur->prv_item)
2N/A cur->prv_item->nxt_item = cur->nxt_item;
2N/A else
2N/A table->first = cur->nxt_item;
2N/A if (cur->nxt_item)
2N/A cur->nxt_item->prv_item = cur->prv_item;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * We use keychain < 0 to indicate that the item isn't linked
2N/A * into the table anymore.
2N/A */
2N/A item->keychain = -1;
2N/A
2N/A /* Adjust the count of locked items in the table */
2N/A if (table->locked_items != 0 &&
2N/A (item->writer > 0 || item->readers > 0)) {
2N/A table->locked_items--;
2N/A if (table->locked_items == 0) {
2N/A /* Wake up traversers-to-be */
2N/A (void) pthread_cond_signal(&table->cond);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Wake up any threads that were waiting for this item. Obviously,
2N/A * such threads must start over scanning the list.
2N/A */
2N/A (void) pthread_cond_signal(&item->lock);
2N/A (void) pthread_cond_destroy(&item->lock);
2N/A
2N/A /*
2N/A * If the item isn't locked, and an item destructor has been
2N/A * established, invoke the destructor.
2N/A */
2N/A if (item->readers == 0 && item->writer == 0 &&
2N/A table->destroyItem != 0) {
2N/A (*table->destroyItem)(item);
2N/A item = 0;
2N/A } else {
2N/A item->next = 0;
2N/A item->prv_item = 0;
2N/A item->nxt_item = 0;
2N/A }
2N/A
2N/A (void) __nis_ulock_hash_table(table, 0, "npimt");
2N/A
2N/A /*
2N/A * If we get here, and the 'item' is NULL, we've popped the
2N/A * item, but also destroyed it. Returning NULL would make
2N/A * our caller believe the list is empty, so instead, we invoke
2N/A * ourselves to pop the next item.
2N/A */
2N/A return ((item != 0) ? item : __nis_pop_item_mt(table));
2N/A}
2N/A
2N/Avoid *
2N/A__nis_remove_item_mt(nis_name name, __nis_hash_table_mt *table) {
2N/A
2N/A __nis_hash_item_mt *nl, **pp;
2N/A pthread_t mtid;
2N/A
2N/A if (__nis_lock_hash_table(table, 0, "nrimt") == 0)
2N/A return (0);
2N/A
2N/A /* Find the item, and make sure it's not in use */
2N/A mtid = pthread_self();
2N/A while ((nl = *(pp = __find_item_mt(name, table, (int *)0))) != 0) {
2N/A if (table->destroyItem != 0)
2N/A break;
2N/A if (nl->readers == 0 && nl->writer == 0)
2N/A break;
2N/A if (nl->writer != 0 && nl->writer_id == mtid)
2N/A break;
2N/A (void) pthread_cond_wait(&nl->lock, &table->lock);
2N/A }
2N/A
2N/A if (nl == 0) {
2N/A (void) __nis_ulock_hash_table(table, 0, "nrimt");
2N/A return (0);
2N/A }
2N/A
2N/A /* Remove nl from the hash chain */
2N/A *pp = nl->next;
2N/A nl->next = 0;
2N/A
2N/A /* Remove nl from the linked list of all names */
2N/A if (nl->prv_item)
2N/A nl->prv_item->nxt_item = nl->nxt_item;
2N/A else
2N/A table->first = nl->nxt_item;
2N/A
2N/A if (nl->nxt_item)
2N/A nl->nxt_item->prv_item = nl->prv_item;
2N/A nl->prv_item = 0;
2N/A nl->nxt_item = 0;
2N/A
2N/A /* keychain < 0 means not in table anymore */
2N/A nl->keychain = -1;
2N/A
2N/A /*
2N/A * If this item was locked, we can now decrement the count of
2N/A * locked items for the table.
2N/A */
2N/A if (table->locked_items != 0 &&
2N/A (nl->writer > 0 || nl->readers > 0)) {
2N/A table->locked_items--;
2N/A if (table->locked_items == 0) {
2N/A /* Wake up traversers-to-be */
2N/A (void) pthread_cond_signal(&table->cond);
2N/A }
2N/A }
2N/A (void) pthread_cond_signal(&nl->lock);
2N/A (void) pthread_cond_destroy(&nl->lock);
2N/A
2N/A /*
2N/A * If the item isn't locked, and an item destructor has been
2N/A * established, invoke the destructor. In that case, we return
2N/A * NULL, so that our caller doesn't try to reference the
2N/A * deleted item.
2N/A */
2N/A if (nl->readers == 0 && nl->writer == 0 && table->destroyItem != 0) {
2N/A (*table->destroyItem)(nl);
2N/A nl = 0;
2N/A }
2N/A
2N/A (void) __nis_ulock_hash_table(table, 0, "nrimt");
2N/A
2N/A return (nl);
2N/A}
2N/A
2N/A/*
2N/A * Release an item that had been acquired exclusively or non-exclusively.
2N/A * Note that 'readwrite' can assume any integer value, and thus can be
2N/A * used to release any level of recursive locking. It's the responsibility
2N/A * of the caller to make sure that proper nesting is maintained.
2N/A */
2N/Aint
2N/A__nis_release_item(void *arg, __nis_hash_table_mt *table, int readwrite) {
2N/A
2N/A __nis_hash_item_mt *item = arg;
2N/A int wakeup = 0;
2N/A
2N/A if (item == 0 || __nis_lock_hash_table(table, 0, "nreli") == 0)
2N/A return (0);
2N/A
2N/A if ((readwrite < 0 && abs(readwrite) > item->writer) ||
2N/A (readwrite < 0 && item->writer > 0 &&
2N/A item->writer_id != pthread_self()) ||
2N/A (readwrite > 0 && readwrite > item->readers)) {
2N/A /* Caller confused; ignore */
2N/A (void) __nis_ulock_hash_table(table, 0, "nreli");
2N/A return (0);
2N/A }
2N/A
2N/A if (readwrite < 0) {
2N/A item->writer += readwrite;
2N/A if (item->writer == 0 && item->keychain >= 0) {
2N/A if (table->locked_items != 0)
2N/A table->locked_items--;
2N/A wakeup = 1;
2N/A }
2N/A } else if (readwrite > 0) {
2N/A item->readers -= readwrite;
2N/A item->last_reader_id = INV_PTHREAD_ID;
2N/A if (item->readers == 0 && item->keychain >= 0) {
2N/A if (table->locked_items != 0)
2N/A table->locked_items--;
2N/A wakeup = 1;
2N/A }
2N/A }
2N/A
2N/A if (table->locked_items == 0) {
2N/A /* Wake up traversers-to-be */
2N/A (void) pthread_cond_signal(&table->cond);
2N/A }
2N/A if (wakeup) {
2N/A /* Wake up anyone else who wants this item */
2N/A (void) pthread_cond_signal(&item->lock);
2N/A }
2N/A
2N/A /*
2N/A * Delete if no references, not linked into list, and destructor
2N/A * established.
2N/A */
2N/A if (item->keychain < 0 &&
2N/A item->readers == 0 && item->writer == 0 &&
2N/A item->next == 0 &&
2N/A item->prv_item == 0 && item->nxt_item == 0 &&
2N/A table->destroyItem != 0)
2N/A (*table->destroyItem)(item);
2N/A
2N/A (void) __nis_ulock_hash_table(table, 0, "nreli");
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Return -1 if item checked out for both reading and writing, 1 if
2N/A * readonly, and 0 otherwise.
2N/A */
2N/Aint
2N/A__nis_item_access(void *arg) {
2N/A
2N/A __nis_hash_item_mt *item = arg;
2N/A
2N/A if (item != 0) {
2N/A if (item->writer > 0) {
2N/A if (item->writer_id != pthread_self())
2N/A abort();
2N/A return (-1);
2N/A } else if (item->readers > 0) {
2N/A return (1);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * __nis_scan_table_mt()
2N/A *
2N/A * Iterate over all items in a __nis_hash_table_mt. We ignore
2N/A * first/prv_item/nxt_item and scan in hash-chain order. The iterator
2N/A * function should *not* insert or delete items. If the iterator
2N/A * function returns TRUE the scan terminates. For compatibility with
2N/A * the existing non-MT nis_scan_table() this function has no return
2N/A * value.
2N/A */
2N/Avoid
2N/A__nis_scan_table_mt(
2N/A __nis_hash_table_mt *table,
2N/A bool_t (*func)(__nis_hash_item_mt *, void *),
2N/A void *funcarg)
2N/A{
2N/A int slot;
2N/A
2N/A if (table == 0) {
2N/A return;
2N/A }
2N/A
2N/A if (__nis_lock_hash_table(table, 1, "nstmt") == 0) {
2N/A syslog(LOG_DEBUG, "__nis_scan_table_mt: mutex lock failed ");
2N/A return;
2N/A }
2N/A
2N/A for (slot = 0;
2N/A slot < sizeof (table->keys) / sizeof (table->keys[0]);
2N/A slot++) {
2N/A __nis_hash_item_mt *it;
2N/A
2N/A for (it = table->keys[slot]; it != 0; it = it->next) {
2N/A if (TRUE == (*func)(it, funcarg)) {
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A if (__nis_ulock_hash_table(table, 1, "nstmt") == 0)
2N/A syslog(LOG_DEBUG, "__nis_scan_table_mt: mutex unlock failed ");
2N/A}