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