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