#include <string.h>
#include <pthread.h>
#include <syslog.h>
#include <rpcsvc/nis.h>
#include "nis_hashitem.h"
#undef NIS_HASH_ITEM
#undef NIS_HASH_TABLE
#undef nis_insert_item
#undef nis_find_item
#undef nis_pop_item
#undef nis_remove_item
#define set_thread_status(msg, state)
void
__nis_init_hash_table(__nis_hash_table_mt *table,
void (*itemDestructor)(void *)) {
int errorcode;
if (table != 0) {
errorcode = pthread_mutex_init(&table->lock, 0);
if (errorcode != 0) {
syslog(LOG_WARNING, "__nis_init_hash_table: "
"(table->lock) pthread_mutex_init returned %d (%s)",
errorcode, strerror(errorcode));
}
errorcode = pthread_cond_init(&table->cond, 0);
if (errorcode != 0) {
syslog(LOG_WARNING, "__nis_init_hash_table: "
"(table->cond) pthread_cond_init returned %d (%s)",
errorcode, strerror(errorcode));
}
errorcode = pthread_mutex_init(&table->traverser_id_lock, 0);
if (errorcode != 0) {
syslog(LOG_WARNING, "__nis_init_hash_table: "
"(table->traverser_id_lock) "
"pthread_mutex_init returned %d (%s)",
errorcode, strerror(errorcode));
}
table->traversed = 0;
table->locked_items = 0;
(void) memset(table->keys, 0, sizeof (table->keys));
table->first = 0;
table->destroyItem = itemDestructor;
}
}
int
__nis_lock_hash_table(__nis_hash_table_mt *table, int traverse, char *msg) {
pthread_t myself = pthread_self();
if (table != 0) {
if (traverse) {
set_thread_status(msg, "table WL");
(void) pthread_mutex_lock(&table->lock);
set_thread_status(msg, "table L");
while ((table->traversed != 0 &&
table->traverser_id != myself) ||
table->locked_items != 0) {
set_thread_status(msg, "traverse cond_wait");
MT_LOG(1, (LOG_NOTICE,
"%d: lh table 0x%x trav cond wait",
myself, table));
(void) pthread_cond_wait(&table->cond,
&table->lock);
}
set_thread_status(msg, "traverser_id WL");
(void) pthread_mutex_lock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id L");
table->traversed = 1;
table->traverser_id = myself;
(void) pthread_mutex_unlock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id U");
} else {
set_thread_status(msg, "non-traverse TL");
if (pthread_mutex_trylock(&table->lock) == EBUSY) {
int dolock = 1;
set_thread_status(msg, "traverser_id L");
(void) pthread_mutex_lock(
&table->traverser_id_lock);
if (table->traversed != 0 &&
table->traverser_id == myself) {
dolock = 0;
}
(void) pthread_mutex_unlock(
&table->traverser_id_lock);
set_thread_status(msg, "traverser_id U");
if (dolock) {
MT_LOG(1, (LOG_NOTICE,
"%d: lh table 0x%x cond wait",
myself, table));
set_thread_status(msg, "table WL");
(void) pthread_mutex_lock(&table->lock);
set_thread_status(msg, "table L");
}
}
}
MT_LOG(1, (LOG_NOTICE, "%d: lh table %s lock acquired 0x%x",
myself, traverse?"traverse":"non-traverse", table));
return (1);
} else {
return (0);
}
}
int
__nis_ulock_hash_table(__nis_hash_table_mt *table, int traverse, char *msg) {
int dounlock = 0;
if (table != 0) {
if (traverse) {
set_thread_status(msg, "traverser_id WL");
(void) pthread_mutex_lock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id L");
if (table->traversed != 0 &&
table->traverser_id == pthread_self()) {
table->traversed = 0;
dounlock = 1;
set_thread_status(msg, "table cond_signal");
(void) pthread_cond_signal(&table->cond);
}
(void) pthread_mutex_unlock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id U");
} else {
set_thread_status(msg, "traverser_id WL");
(void) pthread_mutex_lock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id L");
if (table->traversed == 0) {
dounlock = 1;
}
(void) pthread_mutex_unlock(&table->traverser_id_lock);
set_thread_status(msg, "traverser_id U");
}
if (dounlock) {
(void) pthread_mutex_unlock(&table->lock);
set_thread_status(msg, "table U");
}
MT_LOG(1, (LOG_NOTICE, "%d: lh table %s release 0x%x (%s)",
pthread_self(), traverse?"traverse":"non-traverse", table,
dounlock?"unlocked":"still held"));
return (1);
} else {
return (0);
}
}
static __nis_hash_item_mt **
__find_item_mt(nis_name name, __nis_hash_table_mt *table, int *keyp) {
int key = 0;
unsigned char *s;
__nis_hash_item_mt *it, **pp;
for (s = (unsigned char *)name; *s != 0; s++) {
key += *s;
}
key %= (sizeof (table->keys) / sizeof (table->keys[0]));
if (keyp != 0) {
*keyp = key;
}
for (pp = &table->keys[key]; (it = *pp) != 0; pp = &it->next) {
if (strcmp(name, it->name) == 0) {
break;
}
}
return (pp);
}
int
__nis_insert_item_mt(void *arg, __nis_hash_table_mt *table, int readwrite) {
__nis_hash_item_mt *item = arg;
int key;
__nis_hash_item_mt **pp;
if (item == 0 || __nis_lock_hash_table(table, 0, "nitmt") == 0)
return (0);
if ((*(pp = __find_item_mt(item->name, table, &key))) != 0) {
(void) __nis_ulock_hash_table(table, 0, "nitmt");
return (0);
}
(void) pthread_cond_init(&item->lock, 0);
item->readers = item->writer = 0;
item->last_reader_id = item->writer_id = INV_PTHREAD_ID;
if (readwrite < 0) {
item->writer = 1;
item->writer_id = pthread_self();
table->locked_items++;
} else if (readwrite > 0) {
item->readers = 1;
item->last_reader_id = pthread_self();
table->locked_items++;
}
item->next = *pp;
*pp = item;
item->keychain = key;
if (table->first)
table->first->prv_item = item;
item->nxt_item = table->first;
item->prv_item = NULL;
table->first = item;
(void) __nis_ulock_hash_table(table, 0, "nitmt");
return (1);
}
void
__nis_insert_name_mt(nis_name name, __nis_hash_table_mt *table) {
__nis_hash_item_mt *item;
if (name == 0 || table == 0)
return;
if ((item = malloc(sizeof (*item))) == 0) {
syslog(LOG_WARNING, "__nis_insert_name_mt: malloc failed\n");
return;
}
if ((item->name = strdup(name)) == 0) {
syslog(LOG_WARNING, "__nis_insert_name_mt: strdup failed\n");
free(item);
return;
}
if (! __nis_insert_item_mt(item, table, 0)) {
free(item->name);
free(item);
}
}
void *
__nis_find_item_mt(nis_name name, __nis_hash_table_mt *table, int readwrite,
int *trylock) {
__nis_hash_item_mt *item;
pthread_t me = pthread_self();
if (name == 0 || __nis_lock_hash_table(table, 0, "nfimt") == 0)
return (0);
while ((item = *__find_item_mt(name, table, 0)) != 0) {
if (readwrite == 0 ||
(item->writer == 0 && item->readers == 0))
break;
if (item->writer == 0 && readwrite > 0)
break;
if ((item->writer != 0 && item->writer_id == me))
break;
if (trylock != 0 && *trylock != 0) {
*trylock = -1;
(void) __nis_ulock_hash_table(table, 0, "nfimt");
return (0);
}
(void) pthread_cond_wait(&item->lock, &table->lock);
}
if (item != 0) {
if (readwrite < 0) {
if (item->writer == 0) {
item->writer_id = me;
table->locked_items++;
}
item->writer++;
} else if (readwrite > 0) {
if (item->readers == 0) {
table->locked_items++;
}
item->last_reader_id = me;
item->readers++;
}
}
(void) __nis_ulock_hash_table(table, 0, "nfimt");
return (item);
}
void *
__nis_pop_item_mt(__nis_hash_table_mt *table) {
__nis_hash_item_mt *item, *cur, *prev;
pthread_t mtid;
if (__nis_lock_hash_table(table, 0, "npimt") == 0)
return (0);
mtid = pthread_self();
while ((item = table->first) != 0) {
if (table->destroyItem != 0)
break;
if (item->readers == 0 && item->writer == 0)
break;
if (item->writer != 0 && item->writer_id == mtid)
break;
(void) pthread_cond_wait(&item->lock, &table->lock);
}
if (item == 0) {
__nis_ulock_hash_table(table, 0, "npimt");
return (0);
}
prev = 0;
for (cur = table->keys[item->keychain]; cur;
prev = cur, cur = cur->next) {
if (cur == item) {
if (prev)
prev->next = cur->next;
else
table->keys[cur->keychain] = cur->next;
if (cur->prv_item)
cur->prv_item->nxt_item = cur->nxt_item;
else
table->first = cur->nxt_item;
if (cur->nxt_item)
cur->nxt_item->prv_item = cur->prv_item;
break;
}
}
item->keychain = -1;
if (table->locked_items != 0 &&
(item->writer > 0 || item->readers > 0)) {
table->locked_items--;
if (table->locked_items == 0) {
(void) pthread_cond_signal(&table->cond);
}
}
(void) pthread_cond_signal(&item->lock);
(void) pthread_cond_destroy(&item->lock);
if (item->readers == 0 && item->writer == 0 &&
table->destroyItem != 0) {
(*table->destroyItem)(item);
item = 0;
} else {
item->next = 0;
item->prv_item = 0;
item->nxt_item = 0;
}
(void) __nis_ulock_hash_table(table, 0, "npimt");
return ((item != 0) ? item : __nis_pop_item_mt(table));
}
void *
__nis_remove_item_mt(nis_name name, __nis_hash_table_mt *table) {
__nis_hash_item_mt *nl, **pp;
pthread_t mtid;
if (__nis_lock_hash_table(table, 0, "nrimt") == 0)
return (0);
mtid = pthread_self();
while ((nl = *(pp = __find_item_mt(name, table, (int *)0))) != 0) {
if (table->destroyItem != 0)
break;
if (nl->readers == 0 && nl->writer == 0)
break;
if (nl->writer != 0 && nl->writer_id == mtid)
break;
(void) pthread_cond_wait(&nl->lock, &table->lock);
}
if (nl == 0) {
(void) __nis_ulock_hash_table(table, 0, "nrimt");
return (0);
}
*pp = nl->next;
nl->next = 0;
if (nl->prv_item)
nl->prv_item->nxt_item = nl->nxt_item;
else
table->first = nl->nxt_item;
if (nl->nxt_item)
nl->nxt_item->prv_item = nl->prv_item;
nl->prv_item = 0;
nl->nxt_item = 0;
nl->keychain = -1;
if (table->locked_items != 0 &&
(nl->writer > 0 || nl->readers > 0)) {
table->locked_items--;
if (table->locked_items == 0) {
(void) pthread_cond_signal(&table->cond);
}
}
(void) pthread_cond_signal(&nl->lock);
(void) pthread_cond_destroy(&nl->lock);
if (nl->readers == 0 && nl->writer == 0 && table->destroyItem != 0) {
(*table->destroyItem)(nl);
nl = 0;
}
(void) __nis_ulock_hash_table(table, 0, "nrimt");
return (nl);
}
int
__nis_release_item(void *arg, __nis_hash_table_mt *table, int readwrite) {
__nis_hash_item_mt *item = arg;
int wakeup = 0;
if (item == 0 || __nis_lock_hash_table(table, 0, "nreli") == 0)
return (0);
if ((readwrite < 0 && abs(readwrite) > item->writer) ||
(readwrite < 0 && item->writer > 0 &&
item->writer_id != pthread_self()) ||
(readwrite > 0 && readwrite > item->readers)) {
(void) __nis_ulock_hash_table(table, 0, "nreli");
return (0);
}
if (readwrite < 0) {
item->writer += readwrite;
if (item->writer == 0 && item->keychain >= 0) {
if (table->locked_items != 0)
table->locked_items--;
wakeup = 1;
}
} else if (readwrite > 0) {
item->readers -= readwrite;
item->last_reader_id = INV_PTHREAD_ID;
if (item->readers == 0 && item->keychain >= 0) {
if (table->locked_items != 0)
table->locked_items--;
wakeup = 1;
}
}
if (table->locked_items == 0) {
(void) pthread_cond_signal(&table->cond);
}
if (wakeup) {
(void) pthread_cond_signal(&item->lock);
}
if (item->keychain < 0 &&
item->readers == 0 && item->writer == 0 &&
item->next == 0 &&
item->prv_item == 0 && item->nxt_item == 0 &&
table->destroyItem != 0)
(*table->destroyItem)(item);
(void) __nis_ulock_hash_table(table, 0, "nreli");
return (1);
}
int
__nis_item_access(void *arg) {
__nis_hash_item_mt *item = arg;
if (item != 0) {
if (item->writer > 0) {
if (item->writer_id != pthread_self())
abort();
return (-1);
} else if (item->readers > 0) {
return (1);
}
}
return (0);
}
void
__nis_scan_table_mt(
__nis_hash_table_mt *table,
bool_t (*func)(__nis_hash_item_mt *, void *),
void *funcarg)
{
int slot;
if (table == 0) {
return;
}
if (__nis_lock_hash_table(table, 1, "nstmt") == 0) {
syslog(LOG_DEBUG, "__nis_scan_table_mt: mutex lock failed ");
return;
}
for (slot = 0;
slot < sizeof (table->keys) / sizeof (table->keys[0]);
slot++) {
__nis_hash_item_mt *it;
for (it = table->keys[slot]; it != 0; it = it->next) {
if (TRUE == (*func)(it, funcarg)) {
break;
}
}
}
if (__nis_ulock_hash_table(table, 1, "nstmt") == 0)
syslog(LOG_DEBUG, "__nis_scan_table_mt: mutex unlock failed ");
}