1 /* Copyright (c) 1998 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
26 #include <rpcsvc/nis.h>
27 #include <sys/types.h>
32 static unsigned long int modulo = 211;
33 static unsigned long int postimeout = 600;
34 static unsigned long int negtimeout = 20;
36 static unsigned long int poshit = 0;
37 static unsigned long int posmiss = 0;
38 static unsigned long int neghit = 0;
39 static unsigned long int negmiss = 0;
47 typedef struct pwdhash pwdhash;
54 typedef struct uidhash uidhash;
62 typedef struct neghash neghash;
64 static pwdhash *pwdtbl;
65 static uidhash *uidtbl;
66 static neghash *negtbl;
68 static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
69 static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
71 static void *pwdtable_update (void *);
72 static void *negtable_update (void *);
75 get_pw_stat (stat_response_header *stat)
77 stat->pw_poshit = poshit;
78 stat->pw_posmiss = posmiss;
79 stat->pw_neghit = neghit;
80 stat->pw_negmiss = negmiss;
81 stat->pw_size = modulo;
82 stat->pw_posttl = postimeout;
83 stat->pw_negttl = negtimeout;
87 set_pwd_modulo (unsigned long int mod)
93 set_pos_pwd_ttl (unsigned long int ttl)
99 set_neg_pwd_ttl (unsigned long int ttl)
110 pwdtbl = calloc (modulo, sizeof (pwdhash));
113 uidtbl = calloc (modulo, sizeof (pwdhash));
116 negtbl = calloc (modulo, sizeof (neghash));
120 pthread_attr_init (&attr);
121 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
123 pthread_create (&thread, NULL, pwdtable_update, &attr);
124 pthread_create (&thread, NULL, negtable_update, &attr);
126 pthread_attr_destroy (&attr);
131 static struct passwd *
132 save_pwd (struct passwd *src)
136 dest = calloc (1, sizeof (struct passwd));
137 dest->pw_name = strdup (src->pw_name);
138 dest->pw_passwd = strdup (src->pw_passwd);
139 dest->pw_uid = src->pw_uid;
140 dest->pw_gid = src->pw_gid;
141 dest->pw_gecos = strdup (src->pw_gecos);
142 dest->pw_dir = strdup (src->pw_dir);
143 dest->pw_shell = strdup (src->pw_shell);
149 free_pwd (struct passwd *src)
152 free (src->pw_passwd);
153 free (src->pw_gecos);
155 free (src->pw_shell);
160 add_cache (struct passwd *pwd)
164 unsigned long int hash = __nis_hash (pwd->pw_name,
165 strlen (pwd->pw_name)) % modulo;
168 dbg_log (_("pwd_add_cache (%s)"), pwd->pw_name);
170 work = &pwdtbl[hash];
172 if (pwdtbl[hash].pwd == NULL)
173 pwdtbl[hash].pwd = save_pwd (pwd);
176 while (work->next != NULL)
179 work->next = calloc (1, sizeof (pwdhash));
180 work->next->pwd = save_pwd (pwd);
183 /* Set a pointer from the pwuid hash table to the pwname hash table */
184 time (&work->create);
185 uidwork = &uidtbl[pwd->pw_uid % modulo];
186 if (uidwork->pwptr == NULL)
187 uidwork->pwptr = work->pwd;
190 while (uidwork->next != NULL)
191 uidwork = uidwork->next;
193 uidwork->next = calloc (1, sizeof (uidhash));
194 uidwork->next->pwptr = work->pwd;
199 static struct passwd *
200 cache_search_name (const char *name)
203 unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
205 work = &pwdtbl[hash];
207 while (work->pwd != NULL)
209 if (strcmp (work->pwd->pw_name, name) == 0)
211 if (work->next != NULL)
219 static struct passwd *
220 cache_search_uid (uid_t uid)
224 work = &uidtbl[uid % modulo];
226 while (work->pwptr != NULL)
228 if (work->pwptr->pw_uid == uid)
230 if (work->next != NULL)
239 add_negcache (char *key)
242 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
245 dbg_log (_("pwd_add_netgache (%s|%ld)"), key, hash);
247 work = &negtbl[hash];
249 if (negtbl[hash].key == NULL)
251 negtbl[hash].key = strdup (key);
252 negtbl[hash].next = NULL;
256 while (work->next != NULL)
259 work->next = calloc (1, sizeof (neghash));
260 work->next->key = strdup (key);
264 time (&work->create);
270 cache_search_neg (const char *key)
273 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
276 dbg_log (_("pwd_cache_search_neg (%s|%ld)"), key, hash);
278 work = &negtbl[hash];
280 while (work->key != NULL)
282 if (strcmp (work->key, key) == 0)
284 if (work->next != NULL)
293 cache_getpwnam (void *v_param)
296 param_t *param = (param_t *)v_param;
298 pthread_rwlock_rdlock (&pwdlock);
299 pwd = cache_search_name (param->key);
301 /* I don't like it to hold the read only lock longer, but it is
302 necessary to avoid to much malloc/free/strcpy. */
307 dbg_log (_("Found \"%s\" in cache !"), param->key);
310 pw_send_answer (param->conn, pwd);
311 close_socket (param->conn);
313 pthread_rwlock_unlock (&pwdlock);
319 char *buffer = calloc (1, buflen);
320 struct passwd resultbuf;
323 dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
325 pthread_rwlock_unlock (&pwdlock);
327 pthread_rwlock_rdlock (&neglock);
328 status = cache_search_neg (param->key);
329 pthread_rwlock_unlock (&neglock);
333 while (buffer != NULL
334 && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
340 buffer = realloc (buffer, buflen);
343 if (buffer != NULL && pwd != NULL)
348 pthread_rwlock_wrlock (&pwdlock);
349 /* While we are waiting on the lock, somebody else could
351 tmp = cache_search_name (param->key);
354 pthread_rwlock_unlock (&pwdlock);
359 pthread_rwlock_wrlock (&neglock);
360 add_negcache (param->key);
361 pthread_rwlock_unlock (&neglock);
366 pw_send_answer (param->conn, pwd);
367 close_socket (param->conn);
377 cache_pw_disabled (void *v_param)
379 param_t *param = (param_t *)v_param;
382 dbg_log (_("\tpasswd cache is disabled\n"));
384 pw_send_disabled (param->conn);
389 cache_getpwuid (void *v_param)
391 param_t *param = (param_t *)v_param;
392 struct passwd *pwd, resultbuf;
393 uid_t uid = strtol (param->key, NULL, 10);
395 pthread_rwlock_rdlock (&pwdlock);
396 pwd = cache_search_uid (uid);
398 /* I don't like it to hold the read only lock longer, but it is
399 necessary to avoid to much malloc/free/strcpy. */
404 dbg_log (_("Found \"%d\" in cache !"), uid);
407 pw_send_answer (param->conn, pwd);
408 close_socket (param->conn);
410 pthread_rwlock_unlock (&pwdlock);
415 char *buffer = malloc (buflen);
419 dbg_log (_("Doesn't found \"%d\" in cache !"), uid);
421 pthread_rwlock_unlock (&pwdlock);
423 pthread_rwlock_rdlock (&neglock);
424 status = cache_search_neg (param->key);
425 pthread_rwlock_unlock (&neglock);
429 while (buffer != NULL
430 && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
435 buffer = realloc (buffer, buflen);
438 if (buffer != NULL && pwd != NULL)
443 pthread_rwlock_wrlock (&pwdlock);
444 /* While we are waiting on the lock, somebody else could
446 tmp = cache_search_uid (uid);
449 pthread_rwlock_unlock (&pwdlock);
454 pthread_rwlock_wrlock (&neglock);
455 add_negcache (param->key);
456 pthread_rwlock_unlock (&neglock);
462 pw_send_answer (param->conn, pwd);
463 close_socket (param->conn);
473 pwdtable_update (void *v)
483 dbg_log (_("(pwdtable_update) Wait for write lock!"));
485 pthread_rwlock_wrlock (&pwdlock);
488 dbg_log (_("(pwdtable_update) Have write lock"));
491 for (i = 0; i < modulo; ++i)
493 pwdhash *work = &pwdtbl[i];
495 while (work && work->pwd)
497 if ((now - work->create) >= postimeout)
499 uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo];
502 dbg_log (_("Give \"%s\" free"), work->pwd->pw_name);
504 while (uh != NULL && uh->pwptr)
506 if (uh->pwptr->pw_uid == work->pwd->pw_uid)
509 dbg_log (_("Give uid for \"%s\" free"),
511 if (uh->next != NULL)
513 uidhash *tmp = uh->next;
514 uh->pwptr = tmp->pwptr;
515 uh->next = tmp->next;
524 free_pwd (work->pwd);
525 if (work->next != NULL)
527 pwdhash *tmp = work->next;
528 work->create = tmp->create;
529 work->next = tmp->next;
530 work->pwd = tmp->pwd;
540 dbg_log (_("(pwdtable_update) Release wait lock"));
541 pthread_rwlock_unlock (&pwdlock);
548 negtable_update (void *v)
558 dbg_log (_("(negpwdtable_update) Wait for write lock!"));
560 pthread_rwlock_wrlock (&neglock);
563 dbg_log (_("(negpwdtable_update) Have write lock"));
566 for (i = 0; i < modulo; ++i)
568 neghash *work = &negtbl[i];
570 while (work && work->key)
572 if ((now - work->create) >= negtimeout)
575 dbg_log (_("Give \"%s\" free"), work->key);
579 if (work->next != NULL)
581 neghash *tmp = work->next;
582 work->create = tmp->create;
583 work->next = tmp->next;
584 work->key = tmp->key;
594 dbg_log (_("(negpwdtable_update) Release wait lock"));
596 pthread_rwlock_unlock (&neglock);