netgroup_mkdb.c revision 1.15 1 /* $NetBSD: netgroup_mkdb.c,v 1.15 2006/08/26 18:15:37 christos Exp $ */
2
3 /*
4 * Copyright (c) 1994 Christos Zoulas
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Christos Zoulas.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: netgroup_mkdb.c,v 1.15 2006/08/26 18:15:37 christos Exp $");
36 #endif
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <stdlib.h>
42 #include <stddef.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <db.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <stringlist.h>
51 #define _NETGROUP_PRIVATE
52 #include <netgroup.h>
53 #include <assert.h>
54
55 #include "str.h"
56 #include "util.h"
57
58 #define DEBUG_NG
59
60 #define NEW(a) (a *) emalloc(sizeof(a))
61
62 struct nentry {
63 int n_type;
64 size_t n_size; /* Buffer size required for printing */
65 union {
66 char *_name;
67 struct netgroup *_group;
68 } _n;
69 #define n_name _n._name
70 #define n_group _n._group
71 struct nentry *n_next;
72 };
73
74
75 static void cleanup(void);
76 static DB *ng_insert(DB *, const char *);
77 static void ng_reventry(DB *, DB *, struct nentry *, char *,
78 size_t, StringList *);
79 static void ng_print(struct nentry *, struct string *);
80 static void ng_rprint(DB *, struct string *);
81 static DB *ng_reverse(DB *, size_t);
82 static DB *ng_load(const char *);
83 static void ng_write(DB *, DB *, int);
84 static void ng_rwrite(DB *, DB *, int);
85 static void usage(void) __attribute__((__noreturn__));
86
87 #ifdef DEBUG_NG
88 static int debug = 0;
89 static void ng_dump(DB *);
90 static void ng_rdump(DB *);
91 #endif /* DEBUG_NG */
92 static int dups = 0;
93
94
95 static const char ng_empty[] = "";
96 #define NG_EMPTY(a) ((a) ? (a) : ng_empty)
97
98 static char *dbname = _PATH_NETGROUP_DB;
99
100 int
101 main(int argc, char **argv)
102 {
103 DB *db, *ndb, *hdb, *udb;
104 int ch;
105 char buf[MAXPATHLEN];
106 char *fname = _PATH_NETGROUP;
107
108
109 while ((ch = getopt(argc, argv, "dDo:")) != -1)
110 switch (ch) {
111 #ifdef DEBUG_NG
112 case 'd':
113 debug++;
114 break;
115 #endif
116 case 'o':
117 dbname = optarg;
118 break;
119
120 case 'D':
121 dups++;
122 break;
123
124 case '?':
125 default:
126 usage();
127 }
128
129 argc -= optind;
130 argv += optind;
131
132 if (argc == 1)
133 fname = *argv;
134 else if (argc > 1)
135 usage();
136
137 if (atexit(cleanup))
138 err(1, "Cannot install exit handler");
139
140 /* Read and parse the netgroup file */
141 ndb = ng_load(fname);
142 #ifdef DEBUG_NG
143 if (debug) {
144 (void)fprintf(stderr, "#### Database\n");
145 ng_dump(ndb);
146 }
147 #endif
148
149 /* Reverse the database by host */
150 hdb = ng_reverse(ndb, offsetof(struct netgroup, ng_host));
151 #ifdef DEBUG_NG
152 if (debug) {
153 (void)fprintf(stderr, "#### Reverse by host\n");
154 ng_rdump(hdb);
155 }
156 #endif
157
158 /* Reverse the database by user */
159 udb = ng_reverse(ndb, offsetof(struct netgroup, ng_user));
160 #ifdef DEBUG_NG
161 if (debug) {
162 (void)fprintf(stderr, "#### Reverse by user\n");
163 ng_rdump(udb);
164 }
165 #endif
166
167 (void)snprintf(buf, sizeof(buf), "%s.tmp", dbname);
168
169 db = dbopen(buf, O_RDWR | O_CREAT | O_EXCL,
170 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, NULL);
171 if (!db)
172 err(1, "%s", buf);
173
174 ng_write(db, ndb, _NG_KEYBYNAME);
175 ng_rwrite(db, udb, _NG_KEYBYUSER);
176 ng_rwrite(db, hdb, _NG_KEYBYHOST);
177
178 if ((db->close)(db))
179 err(1, "Error closing database");
180
181 if (rename(buf, dbname) == -1)
182 err(1, "Cannot rename `%s' to `%s'", buf, dbname);
183
184 return 0;
185 }
186
187
188 /*
189 * cleanup(): Remove temporary files upon exit
190 */
191 static void
192 cleanup(void)
193 {
194 char buf[MAXPATHLEN];
195 (void)snprintf(buf, sizeof(buf), "%s.tmp", dbname);
196 (void)unlink(buf);
197 }
198
199
200
201 /*
202 * ng_load(): Load the netgroup database from a file
203 */
204 static DB *
205 ng_load(const char *fname)
206 {
207 FILE *fp;
208 DB *db;
209 char *buf;
210 size_t size;
211 struct nentry *tail, *head, *e;
212 char *p, *name;
213 struct netgroup *ng;
214 DBT data, key;
215
216 /* Open the netgroup file */
217 if ((fp = fopen(fname, "r")) == NULL)
218 err(1, "%s", fname);
219
220 db = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL);
221
222 if (db == NULL)
223 err(1, "dbopen");
224
225 while ((buf = fparseln(fp, &size, NULL, NULL, 0)) != NULL) {
226 tail = head = NULL;
227 p = buf;
228
229 while (p != NULL) {
230 switch (_ng_parse(&p, &name, &ng)) {
231 case _NG_NONE:
232 /* done with this one */
233 p = NULL;
234 free(buf);
235 if (head == NULL)
236 break;
237
238 key.data = (u_char *)head->n_name;
239 key.size = strlen(head->n_name) + 1;
240 data.data = (u_char *)&head;
241 data.size = sizeof(head);
242 switch ((db->put)(db, &key, &data,
243 R_NOOVERWRITE)) {
244 case 0:
245 break;
246
247 case 1:
248 warnx("Duplicate entry netgroup `%s'",
249 head->n_name);
250 break;
251
252 case -1:
253 err(1, "put");
254 break;
255
256 default:
257 abort();
258 break;
259 }
260 break;
261
262 case _NG_NAME:
263 e = NEW(struct nentry);
264 e->n_type = _NG_NAME;
265 e->n_name = name;
266 e->n_next = NULL;
267 e->n_size = size;
268 if (tail == NULL)
269 head = tail = e;
270 else {
271 tail->n_next = e;
272 tail = e;
273 }
274 break;
275
276 case _NG_GROUP:
277 if (tail == NULL) {
278 char fmt[BUFSIZ];
279 _ng_print(fmt, sizeof(fmt), ng);
280 errx(1, "no netgroup key for %s", fmt);
281 }
282 else {
283 e = NEW(struct nentry);
284 e->n_type = _NG_GROUP;
285 e->n_group = ng;
286 e->n_next = NULL;
287 e->n_size = size;
288 tail->n_next = e;
289 tail = e;
290 }
291 break;
292
293 case _NG_ERROR:
294 errx(1, "Fatal error at `%s'", p);
295 break;
296
297 default:
298 abort();
299 break;
300 }
301 }
302 }
303 (void)fclose(fp);
304 return db;
305 }
306
307
308 /*
309 * ng_insert(): Insert named key into the database, and return its associated
310 * string database
311 */
312 static DB *
313 ng_insert(DB *db, const char *name)
314 {
315 DB *xdb = NULL;
316 DBT key, data;
317
318 key.data = (u_char *)name;
319 key.size = strlen(name) + 1;
320
321 switch ((db->get)(db, &key, &data, 0)) {
322 case 0:
323 (void)memcpy(&xdb, data.data, sizeof(xdb));
324 break;
325
326 case 1:
327 xdb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL);
328 if (xdb == NULL)
329 err(1, "dbopen");
330
331 data.data = (u_char *)&xdb;
332 data.size = sizeof(xdb);
333 switch ((db->put)(db, &key, &data, R_NOOVERWRITE)) {
334 case 0:
335 break;
336
337 case -1:
338 err(1, "db put `%s'", name);
339 break;
340
341 case 1:
342 default:
343 abort();
344 }
345 break;
346
347 case -1:
348 err(1, "db get `%s'", name);
349 break;
350
351 default:
352 abort();
353 break;
354 }
355
356 return xdb;
357 }
358
359
360 /*
361 * ng_reventry(): Recursively add all the netgroups to the group entry.
362 */
363 static void
364 ng_reventry(DB *db, DB *udb, struct nentry *fe, char *name, size_t s,
365 StringList *ss)
366 {
367 DBT key, data;
368 struct nentry *e;
369 struct netgroup *ng;
370 char *p;
371 DB *xdb;
372
373 if (sl_find(ss, fe->n_name) != NULL) {
374 _ng_cycle(name, ss);
375 return;
376 }
377 sl_add(ss, fe->n_name);
378
379 for (e = fe->n_next; e != NULL; e = e->n_next)
380 switch (e->n_type) {
381 case _NG_GROUP:
382 if (!dups)
383 sl_delete(ss, fe->n_name, 0);
384 ng = e->n_group;
385 p = _ng_makekey(*((char **)(((char *) ng) + s)),
386 ng->ng_domain, e->n_size);
387 xdb = ng_insert(udb, p);
388 key.data = (u_char *)name;
389 key.size = strlen(name) + 1;
390 data.data = NULL;
391 data.size = 0;
392 switch ((xdb->put)(xdb, &key, &data, R_NOOVERWRITE)) {
393 case 0:
394 case 1:
395 break;
396
397 case -1:
398 err(1, "db put `%s'", name);
399 return;
400
401 default:
402 abort();
403 break;
404 }
405 free(p);
406 break;
407
408 case _NG_NAME:
409 key.data = (u_char *) e->n_name;
410 key.size = strlen(e->n_name) + 1;
411 switch ((db->get)(db, &key, &data, 0)) {
412 struct nentry *rfe;
413 case 0:
414 (void)memcpy(&rfe, data.data, sizeof(rfe));
415 ng_reventry(db, udb, rfe, name, s, ss);
416 break;
417
418 case 1:
419 break;
420
421 case -1:
422 err(1, "db get `%s'", e->n_name);
423 return;
424
425 default:
426 abort();
427 return;
428 }
429 break;
430
431 default:
432 abort();
433 break;
434 }
435 }
436
437
438 /*
439 * ng_reverse(): Reverse the database
440 */
441 static DB *
442 ng_reverse(DB *db, size_t s)
443 {
444 int pos;
445 StringList *sl;
446 DBT key, data;
447 struct nentry *fe;
448 DB *udb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0,
449 DB_HASH, NULL);
450
451 if (udb == NULL)
452 err(1, "dbopen");
453
454 for (pos = R_FIRST;; pos = R_NEXT)
455 switch ((db->seq)(db, &key, &data, pos)) {
456 case 0:
457 sl = sl_init();
458 (void)memcpy(&fe, data.data, sizeof(fe));
459 ng_reventry(db, udb, fe, (char *) key.data, s, sl);
460 sl_free(sl, 0);
461 break;
462
463 case 1:
464 return udb;
465
466 case -1:
467 err(1, "seq");
468 return udb;
469 }
470
471 return udb;
472 }
473
474
475 /*
476 * ng_print(): Pretty print a netgroup entry
477 */
478 static void
479 ng_print(struct nentry *e, struct string *str)
480 {
481 char *ptr;
482
483 if (e->n_next == NULL) {
484 str_append(str, "", ' ');
485 return;
486 }
487
488 ptr = emalloc(e->n_size);
489
490 for (e = e->n_next; e != NULL; e = e->n_next) {
491 switch (e->n_type) {
492 case _NG_NAME:
493 (void)snprintf(ptr, e->n_size, "%s", e->n_name);
494 break;
495
496 case _NG_GROUP:
497 (void)snprintf(ptr, e->n_size, "(%s,%s,%s)",
498 NG_EMPTY(e->n_group->ng_host),
499 NG_EMPTY(e->n_group->ng_user),
500 NG_EMPTY(e->n_group->ng_domain));
501 break;
502
503 default:
504 errx(1, "Internal error: Bad netgroup type");
505 break;
506 }
507 str_append(str, ptr, ' ');
508 }
509 free(ptr);
510 }
511
512
513 /*
514 * ng_rprint(): Pretty print all reverse netgroup mappings in the given entry
515 */
516 static void
517 ng_rprint(DB *db, struct string *str)
518 {
519 int pos;
520 DBT key, data;
521
522 for (pos = R_FIRST;; pos = R_NEXT)
523 switch ((db->seq)(db, &key, &data, pos)) {
524 case 0:
525 str_append(str, (char *)key.data, ',');
526 break;
527
528 case 1:
529 return;
530
531 default:
532 err(1, "seq");
533 break;
534 }
535 }
536
537
538 #ifdef DEBUG_NG
539 /*
540 * ng_dump(): Pretty print all netgroups in the given database
541 */
542 static void
543 ng_dump(DB *db)
544 {
545 int pos;
546 DBT key, data;
547 struct nentry *e;
548 struct string buf;
549
550 for (pos = R_FIRST;; pos = R_NEXT)
551 switch ((db->seq)(db, &key, &data, pos)) {
552 case 0:
553 (void)memcpy(&e, data.data, sizeof(e));
554 str_init(&buf);
555 assert(e->n_type == _NG_NAME);
556
557 ng_print(e, &buf);
558 (void)fprintf(stderr, "%s\t%s\n", e->n_name,
559 buf.s_str ? buf.s_str : "");
560 str_free(&buf);
561 break;
562
563 case 1:
564 return;
565
566 default:
567 err(1, "seq");
568 return;
569 }
570 }
571
572
573 /*
574 * ng_rdump(): Pretty print all reverse mappings in the given database
575 */
576 static void
577 ng_rdump(DB *db)
578 {
579 int pos;
580 DBT key, data;
581 DB *xdb;
582 struct string buf;
583
584 for (pos = R_FIRST;; pos = R_NEXT)
585 switch ((db->seq)(db, &key, &data, pos)) {
586 case 0:
587 (void)memcpy(&xdb, data.data, sizeof(xdb));
588 str_init(&buf);
589 ng_rprint(xdb, &buf);
590 (void)fprintf(stderr, "%s\t%s\n",
591 (char *)key.data, buf.s_str ? buf.s_str : "");
592 str_free(&buf);
593 break;
594
595 case 1:
596 return;
597
598 default:
599 err(1, "seq");
600 return;
601 }
602 }
603 #endif /* DEBUG_NG */
604
605
606 /*
607 * ng_write(): Dump the database into a file.
608 */
609 static void
610 ng_write(DB *odb, DB *idb, int k)
611 {
612 int pos;
613 DBT key, data;
614 struct nentry *e;
615 struct string skey, sdata;
616
617 for (pos = R_FIRST;; pos = R_NEXT)
618 switch ((idb->seq)(idb, &key, &data, pos)) {
619 case 0:
620 memcpy(&e, data.data, sizeof(e));
621 str_init(&skey);
622 str_init(&sdata);
623 assert(e->n_type == _NG_NAME);
624
625 str_prepend(&skey, e->n_name, k);
626 ng_print(e, &sdata);
627 key.data = (u_char *) skey.s_str;
628 key.size = skey.s_len + 1;
629 data.data = (u_char *) sdata.s_str;
630 data.size = sdata.s_len + 1;
631
632 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) {
633 case 0:
634 break;
635
636 case -1:
637 err(1, "put");
638 break;
639
640 case 1:
641 default:
642 abort();
643 break;
644 }
645
646 str_free(&skey);
647 str_free(&sdata);
648 break;
649
650 case 1:
651 return;
652
653 default:
654 err(1, "seq");
655 return;
656 }
657 }
658
659
660 /*
661 * ng_rwrite(): Write the database
662 */
663 static void
664 ng_rwrite(DB *odb, DB *idb, int k)
665 {
666 int pos;
667 DBT key, data;
668 DB *xdb;
669 struct string skey, sdata;
670
671 for (pos = R_FIRST;; pos = R_NEXT)
672 switch ((idb->seq)(idb, &key, &data, pos)) {
673 case 0:
674 memcpy(&xdb, data.data, sizeof(xdb));
675 str_init(&skey);
676 str_init(&sdata);
677
678 str_prepend(&skey, (char *) key.data, k);
679 ng_rprint(xdb, &sdata);
680 key.data = (u_char *) skey.s_str;
681 key.size = skey.s_len + 1;
682 data.data = (u_char *) sdata.s_str;
683 data.size = sdata.s_len + 1;
684
685 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) {
686 case 0:
687 break;
688
689 case -1:
690 err(1, "put");
691 break;
692
693 case 1:
694 default:
695 abort();
696 break;
697 }
698
699 str_free(&skey);
700 str_free(&sdata);
701 break;
702
703 case 1:
704 return;
705
706 default:
707 err(1, "seq");
708 return;
709 }
710 }
711
712
713 /*
714 * usage(): Print usage message and exit
715 */
716 static void
717 usage(void)
718 {
719
720 (void)fprintf(stderr, "Usage: %s [-D] [-o db] [<file>]\n",
721 getprogname());
722 exit(1);
723 }
724