1/*	$NetBSD: services_mkdb.c,v 1.14 2008/04/28 20:24:17 martin Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Luke Mewburn and Christos Zoulas.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/param.h>
35#include <sys/stat.h>
36
37#include <assert.h>
38#include <db.h>
39#include <err.h>
40#include <fcntl.h>
41#include <netdb.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include <libgen.h>
47#include <ctype.h>
48#include <errno.h>
49#include <stringlist.h>
50
51#include "extern.h"
52
53static char tname[MAXPATHLEN];
54
55#define	PMASK		0xffff
56#define	PROTOMAX	6
57
58static void	add(DB *, StringList *, size_t, const char *, size_t *, int);
59static StringList ***parseservices(const char *, StringList *);
60static void	cleanup(void);
61static void	store(DB *, DBT *, DBT *, int);
62static void	killproto(DBT *);
63static char    *getstring(const char *, size_t, char **, const char *);
64static size_t	getprotoindex(StringList *, const char *);
65static const char *getprotostr(StringList *, size_t);
66static const char *mkaliases(StringList *, char *, size_t);
67static void	usage(void);
68
69HASHINFO hinfo = {
70	.bsize = 256,
71	.ffactor = 4,
72	.nelem = 32768,
73	.cachesize = 1024,
74	.hash = NULL,
75	.lorder = 0
76};
77
78
79int
80main(int argc, char *argv[])
81{
82	DB	*db;
83	int	 ch;
84	const char *fname = _PATH_SERVICES;
85	const char *dbname = _PATH_SERVICES_DB;
86	int	 warndup = 1;
87	int	 unique = 0;
88	int	 otherflag = 0;
89	int	 byteorder = 0;
90	size_t	 cnt = 0;
91	StringList *sl, ***svc;
92	size_t port, proto;
93	char *dbname_dir, *dbname_dirbuf;
94	int dbname_dir_fd = -1;
95
96	setprogname(argv[0]);
97
98	while ((ch = getopt(argc, argv, "blo:qu")) != -1)
99		switch (ch) {
100		case 'b':
101		case 'l':
102			if (byteorder != 0)
103				usage();
104			byteorder = ch == 'b' ? 4321 : 1234;
105			break;
106		case 'q':
107			otherflag = 1;
108			warndup = 0;
109			break;
110		case 'o':
111			otherflag = 1;
112			dbname = optarg;
113			break;
114		case 'u':
115			unique++;
116			break;
117		case '?':
118		default:
119			usage();
120		}
121
122	argc -= optind;
123	argv += optind;
124
125	if (argc > 1 || (unique && otherflag))
126		usage();
127	if (argc == 1)
128		fname = argv[0];
129
130	/* Set byte order. */
131	hinfo.lorder = byteorder;
132
133	if (unique)
134		uniq(fname);
135
136	svc = parseservices(fname, sl = sl_init());
137
138	if (atexit(cleanup))
139		err(1, "Cannot install exit handler");
140
141	(void)snprintf(tname, sizeof(tname), "%s.tmp", dbname);
142	db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL,
143	    (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo);
144	if (!db)
145		err(1, "Error opening temporary database `%s'", tname);
146
147
148	for (port = 0; port < PMASK + 1; port++) {
149		if (svc[port] == NULL)
150			continue;
151
152		for (proto = 0; proto < PROTOMAX; proto++) {
153			StringList *s;
154			if ((s = svc[port][proto]) == NULL)
155				continue;
156			add(db, s, port, getprotostr(sl, proto), &cnt, warndup);
157		}
158
159		free(svc[port]);
160	}
161
162	free(svc);
163	sl_free(sl, 1);
164
165	if ((db->close)(db))
166		err(1, "Error closing temporary database `%s'", tname);
167
168	/*
169	 * Make sure file is safe on disk. To improve performance we will call
170	 * fsync() to the directory where file lies
171	 */
172	if (rename(tname, dbname) == -1 ||
173	    (dbname_dirbuf = strdup(dbname)) == NULL ||
174	    (dbname_dir = dirname(dbname_dirbuf)) == NULL ||
175	    (dbname_dir_fd = open(dbname_dir, O_RDONLY|O_DIRECTORY)) == -1 ||
176	    fsync(dbname_dir_fd) != 0) {
177		if (dbname_dir_fd != -1)
178			close(dbname_dir_fd);
179		err(1, "Cannot rename `%s' to `%s'", tname, dbname);
180	}
181
182	if (dbname_dir_fd != -1)
183		close(dbname_dir_fd);
184
185	return 0;
186}
187
188static void
189add(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt,
190    int warndup)
191{
192	size_t i;
193	char	 keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ];
194	DBT	 data, key;
195	key.data = keyb;
196	data.data = datab;
197
198#ifdef DEBUG
199	(void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto);
200	for (i = 1; i < sl->sl_cur; i++)
201	    (void)printf("%s ", sl->sl_str[i]);
202	(void)printf("]\n");
203#endif
204
205	/* key `indirect key', data `full line' */
206	data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1;
207	key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s",
208	    sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1;
209	store(db, &data, &key, warndup);
210
211	/* key `\377port/proto', data = `indirect key' */
212	key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s",
213	    port, proto) + 1;
214	store(db, &key, &data, warndup);
215
216	/* key `\377port', data = `indirect key' */
217	killproto(&key);
218	store(db, &key, &data, warndup);
219
220	/* add references for service and all aliases */
221	for (i = 0; i < sl->sl_cur; i++) {
222		/* key `\376service/proto', data = `indirect key' */
223		key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s",
224		    sl->sl_str[i], proto) + 1;
225		store(db, &key, &data, warndup);
226
227		/* key `\376service', data = `indirect key' */
228		killproto(&key);
229		store(db, &key, &data, warndup);
230	}
231	sl_free(sl, 1);
232}
233
234static StringList ***
235parseservices(const char *fname, StringList *sl)
236{
237	ssize_t len;
238	size_t linecap, line, pindex;
239	FILE *fp;
240	StringList ***svc, *s;
241	char *p, *ep;
242
243	if ((fp = fopen(fname, "r")) == NULL)
244		err(1, "Cannot open `%s'", fname);
245
246	line = linecap = 0;
247	if ((svc = calloc(PMASK + 1, sizeof(StringList **))) == NULL)
248		err(1, "Cannot allocate %zu bytes", (size_t)(PMASK + 1));
249
250	p = NULL;
251	while ((len = getline(&p, &linecap, fp)) != -1) {
252		char	*name, *port, *proto, *aliases, *cp, *alias;
253		unsigned long pnum;
254
255		line++;
256
257		if (len == 0)
258			continue;
259
260		if (p[len - 1] == '\n')
261			p[len - 1] = '\0';
262
263		for (cp = p; *cp && isspace((unsigned char)*cp); cp++)
264			continue;
265
266		if (*cp == '\0' || *cp == '#')
267			continue;
268
269		if ((name = getstring(fname, line, &cp, "name")) == NULL)
270			continue;
271
272		if ((port = getstring(fname, line, &cp, "port")) == NULL)
273			continue;
274
275		if (cp) {
276			for (aliases = cp; *cp && *cp != '#'; cp++)
277				continue;
278
279			if (*cp)
280				*cp = '\0';
281		} else
282			aliases = NULL;
283
284		proto = strchr(port, '/');
285		if (proto == NULL || proto[1] == '\0') {
286			warnx("%s, %zu: no protocol found", fname, line);
287			continue;
288		}
289		*proto++ = '\0';
290
291		errno = 0;
292		pnum = strtoul(port, &ep, 0);
293		if (*port == '\0' || *ep != '\0') {
294			warnx("%s, %zu: invalid port `%s'", fname, line, port);
295			continue;
296		}
297		if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) {
298			warnx("%s, %zu: port too big `%s'", fname, line, port);
299			continue;
300		}
301
302		if (svc[pnum] == NULL) {
303			svc[pnum] = calloc(PROTOMAX, sizeof(StringList *));
304			if (svc[pnum] == NULL)
305				err(1, "Cannot allocate %zu bytes",
306				    (size_t)PROTOMAX);
307		}
308
309		pindex = getprotoindex(sl, proto);
310		if (svc[pnum][pindex] == NULL)
311			s = svc[pnum][pindex] = sl_init();
312		else
313			s = svc[pnum][pindex];
314
315		/* build list of aliases */
316		if (sl_find(s, name) == NULL) {
317			char *p2;
318
319			if ((p2 = strdup(name)) == NULL)
320				err(1, "Cannot copy string");
321			(void)sl_add(s, p2);
322		}
323
324		if (aliases) {
325			while ((alias = strsep(&aliases, " \t")) != NULL) {
326				if (alias[0] == '\0')
327					continue;
328				if (sl_find(s, alias) == NULL) {
329					char *p2;
330
331					if ((p2 = strdup(alias)) == NULL)
332						err(1, "Cannot copy string");
333					(void)sl_add(s, p2);
334				}
335			}
336		}
337	}
338	(void)fclose(fp);
339	return svc;
340}
341
342/*
343 * cleanup(): Remove temporary files upon exit
344 */
345static void
346cleanup(void)
347{
348	if (tname[0])
349		(void)unlink(tname);
350}
351
352static char *
353getstring(const char *fname, size_t line, char **cp, const char *tag)
354{
355	char *str;
356
357	while ((str = strsep(cp, " \t")) != NULL && *str == '\0')
358		continue;
359
360	if (str == NULL)
361		warnx("%s, %zu: no %s found", fname, line, tag);
362
363	return str;
364}
365
366static void
367killproto(DBT *key)
368{
369	char *p, *d = key->data;
370
371	if ((p = strchr(d, '/')) == NULL)
372		abort();
373	*p++ = '\0';
374	key->size = p - d;
375}
376
377static void
378store(DB *db, DBT *key, DBT *data, int warndup)
379{
380#ifdef DEBUG
381	int k = key->size - 1;
382	int d = data->size - 1;
383	(void)printf("store [%*.*s] [%*.*s]\n",
384		k, k, (char *)key->data + 1,
385		d, d, (char *)data->data + 1);
386#endif
387	switch ((db->put)(db, key, data, R_NOOVERWRITE)) {
388	case 0:
389		break;
390	case 1:
391		if (warndup)
392			warnx("duplicate service `%s'",
393			    &((char *)key->data)[1]);
394		break;
395	case -1:
396		err(1, "put");
397		break;
398	default:
399		abort();
400		break;
401	}
402}
403
404static size_t
405getprotoindex(StringList *sl, const char *str)
406{
407	size_t i;
408	char *p;
409
410	for (i= 0; i < sl->sl_cur; i++)
411		if (strcmp(sl->sl_str[i], str) == 0)
412			return i;
413
414	if (i == PROTOMAX)
415		errx(1, "Ran out of protocols adding `%s';"
416		    " recompile with larger PROTOMAX", str);
417	if ((p = strdup(str)) == NULL)
418		err(1, "Cannot copy string");
419	(void)sl_add(sl, p);
420	return i;
421}
422
423static const char *
424getprotostr(StringList *sl, size_t i)
425{
426	assert(i < sl->sl_cur);
427	return sl->sl_str[i];
428}
429
430static const char *
431mkaliases(StringList *sl, char *buf, size_t len)
432{
433	size_t nc, i, pos;
434
435	buf[0] = 0;
436	for (i = 1, pos = 0; i < sl->sl_cur; i++) {
437		nc = strlcpy(buf + pos, sl->sl_str[i], len);
438		if (nc >= len)
439			goto out;
440		pos += nc;
441		len -= nc;
442		nc = strlcpy(buf + pos, " ", len);
443		if (nc >= len)
444			goto out;
445		pos += nc;
446		len -= nc;
447	}
448	return buf;
449out:
450	warn("aliases for `%s' truncated", sl->sl_str[0]);
451	return buf;
452}
453
454static void
455usage(void)
456{
457	(void)fprintf(stderr,
458	    "Usage:\t%s [-b | -l] [-q] [-o <db>] [<servicefile>]\n"
459	    "\t%s -u [<servicefile>]\n", getprogname(), getprogname());
460	exit(1);
461}
462