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