Update.
[glibc.git] / nscd / pwdcache.c
blobe2bf73afc131ae0f84028e87af550b8bb657baf6
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. */
20 #include <errno.h>
21 #include <malloc.h>
22 #include <pthread.h>
23 #include <pwd.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <rpcsvc/nis.h>
27 #include <sys/types.h>
29 #include "dbg_log.h"
30 #include "nscd.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;
41 struct pwdhash
43 time_t create;
44 struct pwdhash *next;
45 struct passwd *pwd;
47 typedef struct pwdhash pwdhash;
49 struct uidhash
51 struct uidhash *next;
52 struct passwd *pwptr;
54 typedef struct uidhash uidhash;
56 struct neghash
58 time_t create;
59 struct neghash *next;
60 char *key;
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 *);
74 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;
86 void
87 set_pwd_modulo (unsigned long int mod)
89 modulo = mod;
92 void
93 set_pos_pwd_ttl (unsigned long int ttl)
95 postimeout = ttl;
98 void
99 set_neg_pwd_ttl (unsigned long int ttl)
101 negtimeout = ttl;
105 cache_pwdinit ()
107 pthread_attr_t attr;
108 pthread_t thread;
110 pwdtbl = calloc (modulo, sizeof (pwdhash));
111 if (pwdtbl == NULL)
112 return -1;
113 uidtbl = calloc (modulo, sizeof (pwdhash));
114 if (uidtbl == NULL)
115 return -1;
116 negtbl = calloc (modulo, sizeof (neghash));
117 if (negtbl == NULL)
118 return -1;
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);
128 return 0;
131 static struct passwd *
132 save_pwd (struct passwd *src)
134 struct passwd *dest;
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);
145 return dest;
148 static void
149 free_pwd (struct passwd *src)
151 free (src->pw_name);
152 free (src->pw_passwd);
153 free (src->pw_gecos);
154 free (src->pw_dir);
155 free (src->pw_shell);
156 free (src);
159 static int
160 add_cache (struct passwd *pwd)
162 pwdhash *work;
163 uidhash *uidwork;
164 unsigned long int hash = __nis_hash (pwd->pw_name,
165 strlen (pwd->pw_name)) % modulo;
167 if (debug_flag)
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);
174 else
176 while (work->next != NULL)
177 work = work->next;
179 work->next = calloc (1, sizeof (pwdhash));
180 work->next->pwd = save_pwd (pwd);
181 work = work->next;
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;
188 else
190 while (uidwork->next != NULL)
191 uidwork = uidwork->next;
193 uidwork->next = calloc (1, sizeof (uidhash));
194 uidwork->next->pwptr = work->pwd;
196 return 0;
199 static struct passwd *
200 cache_search_name (const char *name)
202 pwdhash *work;
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)
210 return work->pwd;
211 if (work->next != NULL)
212 work = work->next;
213 else
214 return NULL;
216 return NULL;
219 static struct passwd *
220 cache_search_uid (uid_t uid)
222 uidhash *work;
224 work = &uidtbl[uid % modulo];
226 while (work->pwptr != NULL)
228 if (work->pwptr->pw_uid == uid)
229 return work->pwptr;
230 if (work->next != NULL)
231 work = work->next;
232 else
233 return NULL;
235 return NULL;
238 static int
239 add_negcache (char *key)
241 neghash *work;
242 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
244 if (debug_flag)
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;
254 else
256 while (work->next != NULL)
257 work = work->next;
259 work->next = calloc (1, sizeof (neghash));
260 work->next->key = strdup (key);
261 work = work->next;
264 time (&work->create);
266 return 0;
269 static int
270 cache_search_neg (const char *key)
272 neghash *work;
273 unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
275 if (debug_flag)
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)
283 return 1;
284 if (work->next != NULL)
285 work = work->next;
286 else
287 return 0;
289 return 0;
292 void *
293 cache_getpwnam (void *v_param)
295 struct passwd *pwd;
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. */
304 if (pwd != NULL)
306 if (debug_flag)
307 dbg_log (_("Found \"%s\" in cache !"), param->key);
309 ++poshit;
310 pw_send_answer (param->conn, pwd);
311 close_socket (param->conn);
313 pthread_rwlock_unlock (&pwdlock);
315 else
317 int status;
318 int buflen = 1024;
319 char *buffer = calloc (1, buflen);
320 struct passwd resultbuf;
322 if (debug_flag)
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);
331 if (status == 0)
333 while (buffer != NULL
334 && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
335 != 0)
336 && errno == ERANGE)
338 errno = 0;
339 buflen += 1024;
340 buffer = realloc (buffer, buflen);
343 if (buffer != NULL && pwd != NULL)
345 struct passwd *tmp;
347 ++posmiss;
348 pthread_rwlock_wrlock (&pwdlock);
349 /* While we are waiting on the lock, somebody else could
350 add this entry. */
351 tmp = cache_search_name (param->key);
352 if (tmp == NULL)
353 add_cache (pwd);
354 pthread_rwlock_unlock (&pwdlock);
356 else
358 ++negmiss;
359 pthread_rwlock_wrlock (&neglock);
360 add_negcache (param->key);
361 pthread_rwlock_unlock (&neglock);
364 else
365 ++neghit;
366 pw_send_answer (param->conn, pwd);
367 close_socket (param->conn);
368 if (buffer != NULL)
369 free (buffer);
371 free (param->key);
372 free (param);
373 return NULL;
376 void *
377 cache_pw_disabled (void *v_param)
379 param_t *param = (param_t *)v_param;
381 if (debug_flag)
382 dbg_log (_("\tpasswd cache is disabled\n"));
384 pw_send_disabled (param->conn);
385 return NULL;
388 void *
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. */
401 if (pwd != NULL)
403 if (debug_flag)
404 dbg_log (_("Found \"%d\" in cache !"), uid);
406 ++poshit;
407 pw_send_answer (param->conn, pwd);
408 close_socket (param->conn);
410 pthread_rwlock_unlock (&pwdlock);
412 else
414 int buflen = 1024;
415 char *buffer = malloc (buflen);
416 int status;
418 if (debug_flag)
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);
427 if (status == 0)
429 while (buffer != NULL
430 && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
431 && errno == ERANGE)
433 errno = 0;
434 buflen += 1024;
435 buffer = realloc (buffer, buflen);
438 if (buffer != NULL && pwd != NULL)
440 struct passwd *tmp;
442 ++posmiss;
443 pthread_rwlock_wrlock (&pwdlock);
444 /* While we are waiting on the lock, somebody else could
445 add this entry. */
446 tmp = cache_search_uid (uid);
447 if (tmp == NULL)
448 add_cache (pwd);
449 pthread_rwlock_unlock (&pwdlock);
451 else
453 ++negmiss;
454 pthread_rwlock_wrlock (&neglock);
455 add_negcache (param->key);
456 pthread_rwlock_unlock (&neglock);
459 else
460 ++neghit;
462 pw_send_answer (param->conn, pwd);
463 close_socket (param->conn);
464 if (buffer != NULL)
465 free (buffer);
467 free (param->key);
468 free (param);
469 return NULL;
472 static void *
473 pwdtable_update (void *v)
475 time_t now;
476 int i;
478 sleep (20);
480 while (!do_shutdown)
482 if (debug_flag > 2)
483 dbg_log (_("(pwdtable_update) Wait for write lock!"));
485 pthread_rwlock_wrlock (&pwdlock);
487 if (debug_flag > 2)
488 dbg_log (_("(pwdtable_update) Have write lock"));
490 time (&now);
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];
501 if (debug_flag)
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)
508 if (debug_flag)
509 dbg_log (_("Give uid for \"%s\" free"),
510 work->pwd->pw_name);
511 if (uh->next != NULL)
513 uidhash *tmp = uh->next;
514 uh->pwptr = tmp->pwptr;
515 uh->next = tmp->next;
516 free (tmp);
518 else
519 uh->pwptr = NULL;
521 uh = uh->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;
531 free (tmp);
533 else
534 work->pwd = NULL;
536 work = work->next;
539 if (debug_flag > 2)
540 dbg_log (_("(pwdtable_update) Release wait lock"));
541 pthread_rwlock_unlock (&pwdlock);
542 sleep (20);
544 return NULL;
547 static void *
548 negtable_update (void *v)
550 time_t now;
551 int i;
553 sleep (30);
555 while (!do_shutdown)
557 if (debug_flag > 2)
558 dbg_log (_("(negpwdtable_update) Wait for write lock!"));
560 pthread_rwlock_wrlock (&neglock);
562 if (debug_flag > 2)
563 dbg_log (_("(negpwdtable_update) Have write lock"));
565 time (&now);
566 for (i = 0; i < modulo; ++i)
568 neghash *work = &negtbl[i];
570 while (work && work->key)
572 if ((now - work->create) >= negtimeout)
574 if (debug_flag)
575 dbg_log (_("Give \"%s\" free"), work->key);
577 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;
585 free (tmp);
587 else
588 work->key = NULL;
590 work = work->next;
593 if (debug_flag > 2)
594 dbg_log (_("(negpwdtable_update) Release wait lock"));
596 pthread_rwlock_unlock (&neglock);
597 sleep (10);
599 return NULL;