chiark / gitweb /
Fix value of clNode_binop, required for bcquery.
[become] / src / userdb.c
1 /* -*-c-*-
2  *
3  * $Id: userdb.c,v 1.6 1998/01/12 16:46:33 mdw Exp $
4  *
5  * User database management
6  *
7  * (c) 1998 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: userdb.c,v $
32  * Revision 1.6  1998/01/12 16:46:33  mdw
33  * Fix copyright date.
34  *
35  * Revision 1.5  1997/09/17  10:24:08  mdw
36  * Use `uid_t' instead of `int' for uids and gids.  Not quite sure why I
37  * didn't do this before.
38  *
39  * Revision 1.4  1997/08/20  16:24:58  mdw
40  * Patch memory leak.  Rename `userdb_reinit' to `userdb_end' for more
41  * sensible restart.
42  *
43  * Revision 1.3  1997/08/07 09:44:29  mdw
44  * Read NIS-based passwords from the YP server directly, rather than using
45  * `popen(ypcat)', which is probably both slower and less secure.
46  *
47  * Revision 1.2  1997/08/04 10:24:26  mdw
48  * Sources placed under CVS control.
49  *
50  * Revision 1.1  1997/07/21  13:47:43  mdw
51  * Initial revision
52  *
53  */
54
55 /*----- Header files ------------------------------------------------------*/
56
57 /* --- ANSI headers --- */
58
59 #include <ctype.h>
60 #include <errno.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64
65 /* --- Unix headers --- */
66
67 #include "config.h"
68
69 #include <sys/types.h>
70
71 #ifdef HAVE_YP
72 #  include <rpc/rpc.h>
73 #  include <rpcsvc/ypclnt.h>
74 #  include <rpcsvc/yp_prot.h>
75 #endif
76
77 #include <grp.h>
78 #include <pwd.h>
79 #include <unistd.h>
80
81 /* --- Local headers --- */
82
83 #include "become.h"
84 #include "sym.h"
85 #include "userdb.h"
86 #include "utils.h"
87
88 /*----- Type definitions --------------------------------------------------*/
89
90 /* --- A map link --- */
91
92 typedef struct userdb__node {
93   struct userdb__node *next;
94   void *rec;
95 } userdb__node;
96
97 /* --- A reference to a real record --- */
98
99 typedef struct userdb__sym {
100   sym_base _base;
101   void *rec;
102 } userdb__sym;
103
104 /* --- A name- and number-mapping --- */
105
106 typedef struct userdb__map {
107   sym_table nmap;
108   sym_table idmap;
109   userdb__node *list;
110 } userdb__map;
111
112 /*----- Static variables --------------------------------------------------*/
113
114 static userdb__map userdb__users;       /* Map of user info blocks */
115 static sym_iter userdb__useri;          /* Iterator for users */
116 static userdb__map userdb__groups;      /* Map of group info blocks */
117 static sym_iter userdb__groupi;         /* Iterator for groups */
118
119 /*----- Map management functions ------------------------------------------*/
120
121 /* --- @userdb__createMap@ --- *
122  *
123  * Arguments:   @userdb__map *m@ = pointer to a map block
124  *
125  * Returns:     ---
126  *
127  * Use:         Initialises a map table.
128  */
129
130 static void userdb__createMap(userdb__map *m)
131 {
132   sym_createTable(&m->nmap);
133   sym_createTable(&m->idmap);
134   m->list = 0;
135 }
136
137 /* --- @userdb__addToMap@ --- *
138  *
139  * Arguments:   @userdb__map *m@ = pointer to the map block
140  *              @const char *name@ = pointer to the item's name
141  *              @uid_t id@ = the item's id number
142  *              @void *rec@ = pointer to the actual record
143  *
144  * Returns:     ---
145  *
146  * Use:         Adds an item to the given map.
147  */
148
149 static void userdb__addToMap(userdb__map *m,
150                              const char *name,
151                              uid_t id, void *rec)
152 {
153   unsigned f;
154   userdb__sym *s;
155   userdb__node *n;
156
157   s = sym_find(&m->nmap, name, -1, sizeof(*s), &f);
158   if (!f)
159     s->rec = rec;
160
161   s = sym_find(&m->idmap, (char *)&id, sizeof(id), sizeof(*s), &f);
162   if (!f)
163     s->rec = rec;
164
165   n = xmalloc(sizeof(*n));
166   n->rec = rec;
167   n->next = m->list;
168   m->list = n;
169 }
170
171 /* --- @userdb__byName@ --- *
172  *
173  * Arguments:   @userdb__map *m@ = pointer to a map block
174  *              @const char *name@ = name to look up
175  *
176  * Returns:     A pointer to the appropriate block, or zero if not found.
177  *
178  * Use:         Looks up a name in a mapping and returns the result.
179  */
180
181 static void *userdb__byName(userdb__map *m, const char *name)
182 {
183   userdb__sym *s = sym_find(&m->nmap, name, -1, 0, 0);
184   return (s ? s->rec : 0);
185 }
186
187 /* --- @userdb__byId@ --- *
188  *
189  * Arguments:   @userdb__map *m@ = pointer to a map block
190  *              @uid_t id@ = id number to find
191  *
192  * Returns:     A pointer to the appropriate block, or zero if not found.
193  *
194  * Use:         Looks up an ID in a mapping, and returns the result.
195  */
196
197 static void *userdb__byId(userdb__map *m, uid_t id)
198 {
199   userdb__sym *s = sym_find(&m->idmap, (char *)&id, sizeof(id), 0, 0);
200   return (s ? s->rec : 0);
201 }
202
203 /* --- @userdb__clearMap@ --- *
204  *
205  * Arguments:   @userdb__map *m@ = pointer to a map block
206  *              @void (*freerec)(void *rec)@ = pointer to a free-record proc
207  *
208  * Returns:     ---
209  *
210  * Use:         Clears a map, emptying it and releasing the memory it
211  *              occupied.
212  */
213
214 static void userdb__clearMap(userdb__map *m, void (*freerec)(void *rec))
215 {
216   userdb__node *n, *t;
217
218   sym_destroyTable(&m->nmap);
219   sym_destroyTable(&m->idmap);
220
221   for (n = m->list; n; n = t) {
222     t = n->next;
223     freerec(n->rec);
224     free(n);
225   }
226 }
227
228 /*----- User and group block management -----------------------------------*/
229
230 /* --- @userdb__dumpUser@ --- *
231  *
232  * Arguments:   @const struct passwd *pw@ = pointer to a user block
233  *
234  * Returns:     ---
235  *
236  * Use:         Writes a user's informationt to a stream.
237  */
238
239 #ifdef TRACING
240
241 static void userdb__dumpUser(const struct passwd *pw)
242 {
243   trace(TRACE_DEBUG,
244         "debug: name `%s' passwd `%s' uid %i gid %i",
245         pw->pw_name, pw->pw_passwd, (int)pw->pw_uid, (int)pw->pw_gid);
246   trace(TRACE_DEBUG,
247         "debug: ... gecos `%s' home `%s' shell `%s'",
248         pw->pw_gecos, pw->pw_dir, pw->pw_shell);
249 }
250
251 #endif
252
253 /* --- @userdb_copyUser@ --- *
254  *
255  * Arguments:   @struct passwd *pw@ = pointer to block to copy
256  *
257  * Returns:     Pointer to the copy.
258  *
259  * Use:         Copies a user block.  The copy is `deep' so all the strings
260  *              are copied too.  Free the copy with @userdb_freeUser@ when
261  *              you don't want it any more.
262  */
263
264 struct passwd *userdb_copyUser(struct passwd *pw)
265 {
266   struct passwd *npw;
267
268   if (!pw)
269     return (0);
270
271   npw = xmalloc(sizeof(*npw));
272
273   npw->pw_name = xstrdup(pw->pw_name);
274   npw->pw_passwd = xstrdup(pw->pw_passwd);
275   npw->pw_uid = pw->pw_uid;
276   npw->pw_gid = pw->pw_gid;
277   npw->pw_gecos = xstrdup(pw->pw_gecos);
278   npw->pw_dir = xstrdup(pw->pw_dir);
279   npw->pw_shell = xstrdup(pw->pw_shell);
280
281   return (npw);
282 }
283
284 /* --- @userdb__buildUser@ --- *
285  *
286  * Arguments:   @char *s@ = pointer to user string
287  *
288  * Returns:     Pointer to a user block.
289  *
290  * Use:         Converts a line from a user file into a password entry.
291  *              Note that the string is corrupted by @strtok@ while it gets
292  *              parsed.
293  */
294
295 static struct passwd *userdb__buildUser(char *s)
296 {
297   struct passwd *pw = xmalloc(sizeof(*pw));
298
299   s = strtok(s, ":"); if (!s) goto tidy_0; pw->pw_name = xstrdup(s);
300   s = strtok(0, ":"); if (!s) goto tidy_1; pw->pw_passwd = xstrdup(s);
301   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_uid = (uid_t)atol(s);
302   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gid = (gid_t)atol(s);
303   s = strtok(0, ":"); if (!s) goto tidy_2; pw->pw_gecos = xstrdup(s);
304   s = strtok(0, ":"); if (!s) goto tidy_3; pw->pw_dir = xstrdup(s);
305   s = strtok(0, ":"); if (!s) goto tidy_4; pw->pw_shell = xstrdup(s);
306   return (pw);
307
308   /* --- Error handling --- */
309
310 tidy_4:
311   free(pw->pw_dir);
312 tidy_3:
313   free(pw->pw_gecos);
314 tidy_2:
315   free(pw->pw_passwd);
316 tidy_1:
317   free(pw->pw_name);
318 tidy_0:
319   free(pw);
320
321   return (0);
322 }
323
324 /* --- @userdb_freeUser@ --- *
325  *
326  * Arguments:   @void *rec@ = pointer to a user record
327  *
328  * Returns:     ---
329  *
330  * Use:         Frees a user record.
331  */
332
333 void userdb_freeUser(void *rec)
334 {
335   struct passwd *pw;
336
337   if (!rec)
338     return;
339
340   pw = rec;
341   free(pw->pw_name);
342   free(pw->pw_passwd);
343   free(pw->pw_gecos);
344   free(pw->pw_dir);
345   free(pw->pw_shell);
346   free(pw);
347
348
349 /* --- @userdb__dumpGroup@ --- *
350  *
351  * Arguments:   @const struct group *gr@ = pointer to a group block
352  *              @FILE *fp@ = pointer to stream to write on
353  *
354  * Returns:     ---
355  *
356  * Use:         Writes a group's information to a stream.
357  */
358
359 #ifdef TRACING
360
361 static void userdb__dumpGroup(const struct group *gr)
362 {
363   char *const *p;
364
365   trace(TRACE_DEBUG,
366          "debug: name `%s' passwd `%s' gid %i",
367          gr->gr_name, gr->gr_passwd, (int)gr->gr_gid);
368   for (p = gr->gr_mem; *p; p++)
369     trace(TRACE_DEBUG,"debug: ... `%s'", *p);
370 }
371
372 #endif
373
374 /* --- @userdb_copyGroup@ --- *
375  *
376  * Arguments:   @struct group *gr@ = pointer to group block
377  *
378  * Returns:     Pointer to copied block
379  *
380  * Use:         Copies a group block.  The copy is `deep' so all the strings
381  *              are copied too.  Free the copy with @userdb_freeGroup@ when
382  *              you don't want it any more.
383  */
384
385 struct group *userdb_copyGroup(struct group *gr)
386 {
387   struct group *ngr;
388   int i, max;
389
390   if (!gr)
391     return (0);
392
393   ngr = xmalloc(sizeof(*ngr));
394
395   ngr->gr_name = xstrdup(gr->gr_name);
396   ngr->gr_passwd = xstrdup(gr->gr_passwd);
397   ngr->gr_gid = gr->gr_gid;
398
399   for (max = 0; gr->gr_mem[max]; max++)
400     ;
401   ngr->gr_mem = xmalloc((max + 1) * sizeof(char *));
402   for (i = 0; i < max; i++)
403     ngr->gr_mem[i] = xstrdup(gr->gr_mem[i]);
404   ngr->gr_mem[max] = 0;
405
406   return (ngr);
407 }
408
409 /* --- @userdb__buildGroup@ --- *
410  *
411  * Arguments:   @char *s@ = pointer to group line string
412  *
413  * Returns:     Pointer to a group block
414  *
415  * Use:         Parses an entry in the groups file.  The string is garbled
416  *              by @strtok@ as we go.
417  */
418
419 static struct group *userdb__buildGroup(char *s)
420 {
421   struct group *gr = xmalloc(sizeof(*gr));
422   char *p;
423   int i;
424
425   /* --- Do the easy bits --- */
426
427   s = strtok(s, ":"); if (!s) goto tidy_0; gr->gr_name = xstrdup(s);
428   s = strtok(0, ":"); if (!s) goto tidy_1; gr->gr_passwd = xstrdup(s);
429   s = strtok(0, ":"); if (!s) goto tidy_2; gr->gr_gid = (gid_t)atol(s);
430
431   /* --- Find the start of the member list --- */
432
433   s = strtok(0, "");
434   if (!s)
435     goto tidy_2;
436
437   /* --- Count the number of members --- */
438
439   p = s;
440   i = 0;
441   for (;;) {
442     i++;
443     if ((p = strpbrk(p, ",")) == 0)
444       break;
445     p++;
446   }
447
448   /* --- Allocate the block and fill it --- */
449
450   gr->gr_mem = xmalloc((i + 1) * sizeof(char *));
451   i = 0;
452   s = strtok(s, ",");
453   do {
454     gr->gr_mem[i++] = xstrdup(s);
455     s = strtok(0, ",");
456   } while (s);
457   gr->gr_mem[i] = 0;
458
459   return (gr);
460
461   /* --- Various tidying-up things --- */
462
463 tidy_2:
464   free(gr->gr_passwd);
465 tidy_1:
466   free(gr->gr_name);
467 tidy_0:
468   free(gr);
469
470   return (0);
471 }
472
473 /* --- @userdb_freeGroup@ --- *
474  *
475  * Arguments:   @void *rec@ = pointer to a group record
476  *
477  * Returns:     ---
478  *
479  * Use:         Frees a group record.
480  */
481
482 void userdb_freeGroup(void *rec)
483 {
484   struct group *gr;
485   char **p;
486
487   if (!rec)
488     return;
489
490   gr = rec;
491   free(gr->gr_name);
492   free(gr->gr_passwd);
493   for (p = gr->gr_mem; *p; p++)
494     free(*p);
495   free(gr->gr_mem);
496   free(gr);
497
498
499 /*----- Answering queries -------------------------------------------------*/
500
501 /* --- @userdb_userByName@, @userdb_userById@ --- *
502  *
503  * Arguments:   @const char *name@ = pointer to user's name
504  *              @uid_t id@ = user id to find
505  *
506  * Returns:     Pointer to user block, or zero if not found.
507  *
508  * Use:         Looks up a user by name or id.
509  */
510
511 struct passwd *userdb_userByName(const char *name)
512 { return (userdb__byName(&userdb__users, name)); }
513
514 struct passwd *userdb_userById(uid_t id)
515 { return (userdb__byId(&userdb__users, id)); }
516
517 /* --- @userdb_iterateUsers@, @userdb_iterateUsers_r@ --- *
518  *
519  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
520  *
521  * Returns:     ---
522  *
523  * Use:         Initialises an iteration for the user database.
524  */
525
526 void userdb_iterateUsers(void)
527 { userdb_iterateUsers_r(&userdb__useri); }
528
529 void userdb_iterateUsers_r(userdb_iter *i)
530 { sym_createIter(i, &userdb__users.nmap); }
531
532 /* --- @userdb_nextUser@, @userdb_nextUser_r@ --- *
533  *
534  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
535  *
536  * Returns:     Pointer to the next user block, or null.
537  *
538  * Use:         Returns another user block.
539  */
540
541 struct passwd *userdb_nextUser(void)
542 { return (userdb_nextUser_r(&userdb__useri)); }
543
544 struct passwd *userdb_nextUser_r(userdb_iter *i)
545 {
546   userdb__sym *s = sym_next(i);
547   return (s ? s->rec : 0);
548 }
549
550 /* --- @userdb_groupByName@, @userdb_groupById@ --- *
551  *
552  * Arguments:   @const char *name@ = pointer to group's name
553  *              @gid_t id@ = group id to find
554  *
555  * Returns:     Pointer to group block, or zero if not found.
556  *
557  * Use:         Looks up a group by name or id.
558  */
559
560 struct group *userdb_groupByName(const char *name)
561 { return (userdb__byName(&userdb__groups, name)); }
562
563 struct group *userdb_groupById(gid_t id)
564 { return (userdb__byId(&userdb__groups, id)); }
565
566 /* --- @userdb_iterateGroups@, @userdb_iterateGroups_r@ --- *
567  *
568  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator object
569  *
570  * Returns:     ---
571  *
572  * Use:         Initialises an iteration for the group database.
573  */
574
575 void userdb_iterateGroups(void)
576 { userdb_iterateGroups_r(&userdb__groupi); }
577
578 void userdb_iterateGroups_r(userdb_iter *i)
579 { sym_createIter(i, &userdb__groups.nmap); }
580
581 /* --- @userdb_nextGroup@, @userdb_nextGroup_r@ --- *
582  *
583  * Arguments:   @userdb_iter *i@ = pointer to a symbol table iterator oject
584  *
585  * Returns:     Pointer to the next group block, or null.
586  *
587  * Use:         Returns another group block.
588  */
589
590 struct group *userdb_nextGroup(void)
591 { return (userdb_nextGroup_r(&userdb__groupi)); }
592
593 struct group *userdb_nextGroup_r(userdb_iter *i)
594 {
595   userdb__sym *s = sym_next(i);
596   return (s ? s->rec : 0);
597 }
598
599 /*----- Yellow pages support ----------------------------------------------*/
600
601 #ifdef HAVE_YP
602
603 /* --- @userdb__foreachUser@ --- *
604  *
605  * Arguments:   @int st@ = YP protocol-level status code
606  *              @char *k@ = address of the key for this record
607  *              @int ksz@ = size of the key
608  *              @char *v@ = address of the value for this record
609  *              @int vsz@ = size of the value
610  *              @char *data@ = pointer to some data passed to me
611  *
612  * Returns:     Zero to be called again, nonzero to end the enumeration.
613  *
614  * Use:         Handles an incoming user record.
615  */
616
617 static int userdb__foreachUser(int st, char *k, int ksz,
618                                char *v, int vsz, char *data)
619 {
620   char *cv;
621   struct passwd *pw;
622
623   if (st != YP_TRUE)
624     return (-1);
625   cv = xmalloc(vsz + 1);
626   memcpy(cv, v, vsz);
627   cv[vsz] = 0;
628   T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
629   pw = userdb__buildUser(cv);
630   if (pw && !userdb__byName(&userdb__users, pw->pw_name)) {
631     IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
632     userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid, pw);
633   } else
634     userdb_freeUser(pw);
635   free(cv);
636   return (0);
637 }
638
639 /* --- @userdb__foreachGroup@ --- *
640  *
641  * Arguments:   @int st@ = YP protocol-level status code
642  *              @char *k@ = address of the key for this record
643  *              @int ksz@ = size of the key
644  *              @char *v@ = address of the value for this record
645  *              @int vsz@ = size of the value
646  *              @char *data@ = pointer to some data passed to me
647  *
648  * Returns:     Zero to be called again, nonzero to end the enumeration.
649  *
650  * Use:         Handles an incoming user record.
651  */
652
653 static int userdb__foreachGroup(int st, char *k, int ksz,
654                                 char *v, int vsz, char *data)
655 {
656   char *cv;
657   struct group *gr;
658
659   if (st != YP_TRUE)
660     return (-1);
661   cv = xmalloc(vsz + 1);
662   memcpy(cv, v, vsz);
663   cv[vsz] = 0;
664   T( trace(TRACE_DEBUG, "debug: nis string: `%s'", cv); )
665   gr = userdb__buildGroup(cv);
666   if (gr && !userdb__byName(&userdb__groups, gr->gr_name)) {
667     IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
668     userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid, gr);
669   } else
670     userdb_freeGroup(gr);
671   free(cv);
672   return (0);
673 }
674
675 /* --- @userdb_yp@ --- *
676  *
677  * Arguments:   ---
678  *
679  * Returns:     ---
680  *
681  * Use:         Fetches the YP database of users.
682  */
683
684 void userdb_yp(void)
685 {
686   char *ypdom;
687
688   /* --- Bind to a server --- */
689
690   if (yp_get_default_domain(&ypdom) ||
691       yp_bind(ypdom))
692     return;
693
694   T( trace(TRACE_DEBUG, "debug: adding NIS users"); )
695
696   /* --- Fetch the users map --- */
697
698   {
699     static struct ypall_callback ucb = { userdb__foreachUser, 0 };
700     yp_all(ypdom, "passwd.byuid", &ucb);
701   }
702
703   /* --- Fetch the groups map --- */
704
705   {
706     static struct ypall_callback gcb = { userdb__foreachGroup, 0 };
707     yp_all(ypdom, "group.bygid", &gcb);
708   }
709
710   yp_unbind(ypdom);
711 }
712
713 #else
714
715 void userdb_yp(void) { ; }
716
717 #endif
718
719 /*----- Building the databases --------------------------------------------*/
720
721 /* --- @userdb_local@ --- *
722  *
723  * Arguments:   ---
724  *
725  * Returns:     ---
726  *
727  * Use:         Reads the local list of users into the maps.
728  */
729
730 void userdb_local(void)
731 {
732   T( trace(TRACE_DEBUG, "debug: adding local users"); )
733
734   /* --- Fetch users first --- */
735
736   {
737     struct passwd *pw;
738
739     setpwent();
740     while ((pw = getpwent()) != 0) {
741       IF_TRACING(TRACE_DEBUG, userdb__dumpUser(pw); )
742       if (!userdb__byName(&userdb__users, pw->pw_name))
743         userdb__addToMap(&userdb__users, pw->pw_name, pw->pw_uid,
744                          userdb_copyUser(pw));
745     }
746     endpwent();
747   }
748
749   /* --- Then fetch groups --- */
750
751   {
752     struct group *gr;
753
754     setgrent();
755     while ((gr = getgrent()) != 0) {
756       IF_TRACING(TRACE_DEBUG, userdb__dumpGroup(gr); )
757       if (!userdb__byName(&userdb__groups, gr->gr_name))
758         userdb__addToMap(&userdb__groups, gr->gr_name, gr->gr_gid,
759                          userdb_copyGroup(gr));
760     }
761     endgrent();
762   }
763 }
764
765 /* --- @userdb_init@ --- *
766  *
767  * Arguments:   ---
768  *
769  * Returns:     ---
770  *
771  * Use:         Initialises the user database.
772  */
773
774 void userdb_init(void)
775 {
776   userdb__createMap(&userdb__users);
777   userdb__createMap(&userdb__groups);
778 }
779
780 /* --- @userdb_end@ --- *
781  *
782  * Arguments:   ---
783  *
784  * Returns:     ---
785  *
786  * Use:         Closes down the user database.
787  */
788
789 void userdb_end(void)
790 {
791   userdb__clearMap(&userdb__users, userdb_freeUser);
792   userdb__clearMap(&userdb__groups, userdb_freeGroup);
793 }
794
795 /*----- Test rig ----------------------------------------------------------*/
796
797 #ifdef TEST_RIG
798
799 void dumpit(const char *msg)
800 {
801   trace(TRACE_DEBUG, "debug: %s", msg);
802
803   {
804     struct passwd *pw;
805     for (userdb_iterateUsers(); (pw = userdb_nextUser()) != 0; )
806       userdb__dumpUser(pw);
807   }
808
809   {
810     struct group *gr;
811     for (userdb_iterateGroups(); (gr = userdb_nextGroup()) != 0; )
812       userdb__dumpGroup(gr);
813   }
814 }
815
816 int main(void)
817 {
818   ego("userdb-test");
819 /*   traceon(stdout, TRACE_ALL); */
820   userdb_init();
821   userdb_local();
822   userdb_yp();
823   printf("loaded (%lu)\n", track_memused());
824   getchar();
825   for (;;) {
826     userdb_end();
827     printf("cleared (%lu)\n", track_memused());
828 /*    track_memlist(); */
829     userdb_init();
830     userdb_local();
831     userdb_yp();
832     printf("reloaded (%lu)\n", track_memused());
833     getchar();
834   }
835   return (0);
836 }
837
838 #endif
839
840 /*----- That's all, folks -------------------------------------------------*/