Name Switch Cach Daemon implementation.
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
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.
4
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.
9
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.
14
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. */
19
20 #include <errno.h>
21 #include <malloc.h>
22 #include <pthread.h>
23 #include <pwd.h>
24 #include <string.h>
25 #include <rpcsvc/nis.h>
26 #include <sys/types.h>
27
28 #include "dbg_log.h"
29 #include "nscd.h"
30
31 static unsigned long int modulo = 211;
32 static unsigned long int postimeout = 600;
33 static unsigned long int negtimeout = 20;
34
35 static unsigned long int poshit = 0;
36 static unsigned long int posmiss = 0;
37 static unsigned long int neghit = 0;
38 static unsigned long int negmiss = 0;
39
40 struct pwdhash
41 {
42   time_t create;
43   struct pwdhash *next;
44   struct passwd *pwd;
45 };
46 typedef struct pwdhash pwdhash;
47
48 struct uidhash
49 {
50   struct uidhash *next;
51   struct pwdhash *pwptr;
52 };
53 typedef struct uidhash uidhash;
54
55 struct neghash
56 {
57   time_t create;
58   struct neghash *next;
59   char *key;
60 };
61 typedef struct neghash neghash;
62
63 static pwdhash *pwdtbl;
64 static uidhash *uidtbl;
65 static neghash *negtbl;
66
67 static pthread_rwlock_t pwdlock = PTHREAD_RWLOCK_INITIALIZER;
68 static pthread_rwlock_t neglock = PTHREAD_RWLOCK_INITIALIZER;
69
70 static void *pwdtable_update (void *);
71 static void *negtable_update (void *);
72
73 void
74 get_pw_stat (stat_response_header *stat)
75 {
76   stat->pw_poshit = poshit;
77   stat->pw_posmiss = posmiss;
78   stat->pw_neghit = neghit;
79   stat->pw_negmiss = negmiss;
80   stat->pw_size = modulo;
81   stat->pw_posttl = postimeout;
82   stat->pw_negttl = negtimeout;
83 }
84
85 void
86 set_pwd_modulo (unsigned long int mod)
87 {
88   modulo = mod;
89 }
90
91 void
92 set_pos_pwd_ttl (unsigned long int ttl)
93 {
94   postimeout = ttl;
95 }
96
97 void
98 set_neg_pwd_ttl (unsigned long int ttl)
99 {
100   negtimeout = ttl;
101 }
102
103 int
104 cache_pwdinit ()
105 {
106   pthread_t thread;
107
108   pwdtbl = calloc (modulo, sizeof (pwdhash));
109   if (pwdtbl == NULL)
110     return -1;
111   uidtbl = calloc (modulo, sizeof (pwdhash));
112   if (uidtbl == NULL)
113     return -1;
114   negtbl = calloc (modulo, sizeof (neghash));
115   if (negtbl == NULL)
116     return -1;
117
118   pthread_create (&thread, NULL, pwdtable_update, (void *)NULL);
119   pthread_detach (thread);
120   pthread_create (&thread, NULL, negtable_update, (void *)NULL);
121   pthread_detach (thread);
122   return 0;
123 }
124
125 static struct passwd *
126 save_pwd (struct passwd *src)
127 {
128   struct passwd *dest;
129
130   dest = calloc (1, sizeof (struct passwd));
131   dest->pw_name = strdup (src->pw_name);
132   dest->pw_passwd = strdup (src->pw_passwd);
133   dest->pw_uid = src->pw_uid;
134   dest->pw_gid = src->pw_gid;
135   dest->pw_gecos = strdup (src->pw_gecos);
136   dest->pw_dir = strdup (src->pw_dir);
137   dest->pw_shell = strdup (src->pw_shell);
138
139   return dest;
140 }
141
142 static void
143 free_pwd (struct passwd *src)
144 {
145   free (src->pw_name);
146   free (src->pw_passwd);
147   free (src->pw_gecos);
148   free (src->pw_dir);
149   free (src->pw_shell);
150   free (src);
151 }
152
153 static int
154 add_cache (struct passwd *pwd)
155 {
156   pwdhash *work;
157   unsigned long int hash = __nis_hash (pwd->pw_name,
158                                        strlen (pwd->pw_name)) % modulo;
159
160   if (debug_flag)
161     dbg_log (_("add_cache (%s)"), pwd->pw_name);
162
163   work = &pwdtbl[hash];
164
165   if (pwdtbl[hash].pwd == NULL)
166     pwdtbl[hash].pwd = save_pwd (pwd);
167   else
168     {
169       while (work->next != NULL)
170         work = work->next;
171
172       work->next = calloc (1, sizeof (pwdhash));
173       work->next->pwd = save_pwd (pwd);
174       work = work->next;
175     }
176   /* Set a pointer from the pwuid hash table to the pwname hash table */
177   time (&work->create);
178   uidtbl[pwd->pw_uid % modulo].pwptr = work;
179
180   return 0;
181 }
182
183 static struct passwd *
184 cache_search_name (const char *name)
185 {
186   pwdhash *work;
187   unsigned long int hash = __nis_hash (name, strlen (name)) % modulo;
188
189   work = &pwdtbl[hash];
190
191   while (work->pwd != NULL)
192     {
193       if (strcmp (work->pwd->pw_name, name) == 0)
194         return work->pwd;
195       if (work->next != NULL)
196         work = work->next;
197       else
198         return NULL;
199     }
200   return NULL;
201 }
202
203 static struct passwd *
204 cache_search_uid (uid_t uid)
205 {
206   uidhash *work;
207
208   work = &uidtbl[uid % modulo];
209
210   while (work->pwptr != NULL)
211     {
212       if (work->pwptr->pwd->pw_uid == uid)
213         return work->pwptr->pwd;
214       if (work->next != NULL)
215         work = work->next;
216       else
217         return NULL;
218     }
219   return NULL;
220 }
221
222 static int
223 add_negcache (char *key)
224 {
225   neghash *work;
226   unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
227
228   if (debug_flag)
229     dbg_log (_("add_netgache (%s|%ld)"), key, hash);
230
231   work = &negtbl[hash];
232
233   if (negtbl[hash].key == NULL)
234     {
235       negtbl[hash].key = strdup (key);
236       negtbl[hash].next = NULL;
237     }
238   else
239     {
240       while (work->next != NULL)
241         work = work->next;
242
243       work->next = calloc (1, sizeof (neghash));
244       work->next->key = strdup (key);
245       work = work->next;
246     }
247   /* Set a pointer from the pwuid hash table to the pwname hash table */
248   time (&work->create);
249
250   return 0;
251 }
252
253 static int
254 cache_search_neg (const char *key)
255 {
256   neghash *work;
257   unsigned long int hash = __nis_hash (key, strlen (key)) % modulo;
258
259   work = &negtbl[hash];
260
261   if (debug_flag)
262     dbg_log (_("cache_search_neg (%s|%ld)"), key, hash);
263
264   while (work->key != NULL)
265     {
266       if (strcmp (work->key, key) == 0)
267         return 1;
268       if (work->next != NULL)
269         work = work->next;
270       else
271         return 0;
272     }
273   return 0;
274 }
275
276 void *
277 cache_getpwnam (void *v_param)
278 {
279   param_t *param = (param_t *)v_param;
280   struct passwd *pwd, resultbuf;
281
282   pthread_rwlock_rdlock (&pwdlock);
283   pwd = cache_search_name (param->key);
284
285   /* I don't like it to hold the read only lock longer, but it is
286      necessary to avoid to much malloc/free/strcpy.  */
287
288   if (pwd != NULL)
289     {
290       if (debug_flag)
291         dbg_log (_("Found \"%s\" in cache !"), param->key);
292
293       ++poshit;
294       pw_send_answer (param->conn, pwd);
295       close_socket (param->conn);
296
297       pthread_rwlock_unlock (&pwdlock);
298       pwd = &resultbuf;
299     }
300   else
301     {
302       int status;
303       int buflen = 1024;
304       char *buffer = malloc (buflen);
305
306       if (debug_flag)
307         dbg_log (_("Doesn't found \"%s\" in cache !"), param->key);
308
309       pthread_rwlock_unlock (&pwdlock);
310
311       pthread_rwlock_rdlock (&neglock);
312       status = cache_search_neg (param->key);
313       pthread_rwlock_unlock (&neglock);
314
315       if (status == 0)
316         {
317           while (buffer != NULL
318                  && (getpwnam_r (param->key, &resultbuf, buffer, buflen, &pwd)
319                      != 0)
320                  && errno == ERANGE)
321             {
322               errno = 0;
323               buflen += 1024;
324               buffer = realloc (buffer, buflen);
325             }
326
327           if (buffer != NULL && pwd != NULL)
328             {
329               struct passwd *tmp;
330
331               ++posmiss;
332               pthread_rwlock_wrlock (&pwdlock);
333               /* While we are waiting on the lock, somebody else could
334                  add this entry.  */
335               tmp = cache_search_name (param->key);
336               if (tmp == NULL)
337                 add_cache (pwd);
338               pthread_rwlock_unlock (&pwdlock);
339             }
340           else
341             {
342               ++negmiss;
343               pthread_rwlock_wrlock (&neglock);
344               add_negcache (param->key);
345               pthread_rwlock_unlock (&neglock);
346             }
347         }
348       else
349         ++neghit;
350       pw_send_answer (param->conn, pwd);
351       close_socket (param->conn);
352       if (buffer != NULL)
353         free (buffer);
354     }
355   free (param->key);
356   free (param);
357   return NULL;
358 }
359
360 void *
361 cache_pw_disabled (void *v_param)
362 {
363   param_t *param = (param_t *)v_param;
364
365   pw_send_disabled (param->conn);
366   return NULL;
367 }
368
369 void *
370 cache_getpwuid (void *v_param)
371 {
372   param_t *param = (param_t *)v_param;
373   struct passwd *pwd, resultbuf;
374   uid_t uid = strtol (param->key, NULL, 10);
375
376   pthread_rwlock_rdlock (&pwdlock);
377   pwd = cache_search_uid (uid);
378
379   /* I don't like it to hold the read only lock longer, but it is
380      necessary to avoid to much malloc/free/strcpy.  */
381
382   if (pwd != NULL)
383     {
384       if (debug_flag)
385         dbg_log (_("Found \"%d\" in cache !"), uid);
386
387       ++poshit;
388       pw_send_answer (param->conn, pwd);
389       close_socket (param->conn);
390
391       pthread_rwlock_unlock (&pwdlock);
392     }
393   else
394     {
395       int buflen = 1024;
396       char *buffer = malloc (buflen);
397       int status;
398
399       if (debug_flag)
400         dbg_log (_("Doesn't found \"%d\" in cache !"), uid);
401
402       pthread_rwlock_unlock (&pwdlock);
403
404       pthread_rwlock_rdlock (&neglock);
405       status = cache_search_neg (param->key);
406       pthread_rwlock_unlock (&neglock);
407
408       if (status == 0)
409         {
410           while (buffer != NULL
411                  && (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0)
412                  && errno == ERANGE)
413             {
414               errno = 0;
415               buflen += 1024;
416               buffer = realloc (buffer, buflen);
417             }
418
419           if (buffer != NULL && pwd != NULL)
420             {
421               struct passwd *tmp;
422
423               ++posmiss;
424               pthread_rwlock_wrlock (&pwdlock);
425               /* While we are waiting on the lock, somebody else could
426                  add this entry.  */
427               tmp = cache_search_uid (uid);
428               if (tmp == NULL)
429                 add_cache (pwd);
430               pthread_rwlock_unlock (&pwdlock);
431             }
432           else
433             {
434               ++negmiss;
435               pthread_rwlock_wrlock (&neglock);
436               add_negcache (param->key);
437               pthread_rwlock_unlock (&neglock);
438             }
439         }
440       else
441         ++neghit;
442
443       pw_send_answer (param->conn, pwd);
444       close_socket (param->conn);
445       if (buffer != NULL)
446         free (buffer);
447     }
448   free (param->key);
449   free (param);
450   return NULL;
451 }
452
453 void *
454 pwdtable_update (void *v)
455 {
456   time_t now;
457   int i;
458
459   sleep (20);
460
461   while (!do_shutdown)
462     {
463       if (debug_flag > 2)
464         dbg_log (_("(pwdtable_update) Wait for write lock!"));
465
466       pthread_rwlock_wrlock (&pwdlock);
467
468       if (debug_flag > 2)
469         dbg_log (_("(pwdtable_update) Have write lock"));
470
471       time (&now);
472       for (i = 0; i < modulo; ++i)
473         {
474           pwdhash *work = &pwdtbl[i];
475
476           while (work && work->pwd)
477             {
478               if ((now - work->create) >= postimeout)
479                 {
480                   uidhash *uh = &uidtbl[work->pwd->pw_uid % modulo];
481
482                   if (debug_flag)
483                     dbg_log (_("Give \"%s\" free"), work->pwd->pw_name);
484
485                   while (uh != NULL && uh->pwptr)
486                     {
487                       if (uh->pwptr->pwd->pw_uid == work->pwd->pw_uid)
488                         {
489                           if (debug_flag)
490                             dbg_log (_("Give uid for \"%s\" free"),
491                                      work->pwd->pw_name);
492                           if (uh->next != NULL)
493                             {
494                               uidhash *tmp = uh->next;
495                               uh->pwptr = tmp->pwptr;
496                               uh->next = tmp->next;
497                               free (tmp);
498                             }
499                           else
500                             uh->pwptr = NULL;
501                         }
502                       uh = uh->next;
503                     }
504
505                   free_pwd (work->pwd);
506                   if (work->next != NULL)
507                     {
508                       pwdhash *tmp = work->next;
509                       work->create = tmp->create;
510                       work->next = tmp->next;
511                       work->pwd = tmp->pwd;
512                       free (tmp);
513                     }
514                   else
515                     work->pwd = NULL;
516                 }
517               work = work->next;
518             }
519         }
520       if (debug_flag > 2)
521         dbg_log (_("(pwdtable_update) Release wait lock"));
522       pthread_rwlock_unlock (&pwdlock);
523       sleep (20);
524     }
525   return NULL;
526 }
527
528 void *
529 negtable_update (void *v)
530 {
531   time_t now;
532   int i;
533
534   sleep (30);
535
536   while (!do_shutdown)
537     {
538       if (debug_flag > 2)
539         dbg_log (_("(negtable_update) Wait for write lock!"));
540
541       pthread_rwlock_wrlock (&neglock);
542
543       if (debug_flag)
544         dbg_log (_("(negtable_update) Have write lock"));
545
546       time (&now);
547       for (i = 0; i < modulo; ++i)
548         {
549           neghash *work = &negtbl[i];
550
551           while (work && work->key)
552             {
553               if ((now - work->create) >= negtimeout)
554                 {
555                   if (debug_flag)
556                     dbg_log (_("Give \"%s\" free"), work->key);
557
558                   free (work->key);
559
560                   if (work->next != NULL)
561                     {
562                       neghash *tmp = work->next;
563                       work->create = tmp->create;
564                       work->next = tmp->next;
565                       work->key = tmp->key;
566                       free (tmp);
567                     }
568                   else
569                     work->key = NULL;
570                 }
571               work = work->next;
572             }
573         }
574       if (debug_flag)
575         dbg_log (_("(negtable_update) Release wait lock"));
576
577       pthread_rwlock_unlock (&neglock);
578       sleep (10);
579     }
580   return NULL;
581 }