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