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