1206156Sume/*	$NetBSD: services_mkdb.c,v 1.14 2008/04/28 20:24:17 martin Exp $	*/
2206156Sume
3206156Sume/*-
4206156Sume * Copyright (c) 1999 The NetBSD Foundation, Inc.
5206156Sume * All rights reserved.
6206156Sume *
7206156Sume * This code is derived from software contributed to The NetBSD Foundation
8206156Sume * by Luke Mewburn and Christos Zoulas.
9206156Sume *
10206156Sume * Redistribution and use in source and binary forms, with or without
11206156Sume * modification, are permitted provided that the following conditions
12206156Sume * are met:
13206156Sume * 1. Redistributions of source code must retain the above copyright
14206156Sume *    notice, this list of conditions and the following disclaimer.
15206156Sume * 2. Redistributions in binary form must reproduce the above copyright
16206156Sume *    notice, this list of conditions and the following disclaimer in the
17206156Sume *    documentation and/or other materials provided with the distribution.
18206156Sume *
19206156Sume * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20206156Sume * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21206156Sume * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22206156Sume * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23206156Sume * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24206156Sume * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25206156Sume * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26206156Sume * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27206156Sume * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28206156Sume * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29206156Sume * POSSIBILITY OF SUCH DAMAGE.
30206156Sume */
31206156Sume
32206156Sume#include <sys/cdefs.h>
33206156Sume__FBSDID("$FreeBSD$");
34206156Sume
35206156Sume#include <sys/param.h>
36206156Sume#include <sys/stat.h>
37206156Sume
38206156Sume#include <assert.h>
39206156Sume#include <db.h>
40206156Sume#include <err.h>
41206156Sume#include <fcntl.h>
42206156Sume#include <netdb.h>
43206156Sume#include <stdio.h>
44206156Sume#include <stdlib.h>
45206156Sume#include <string.h>
46206156Sume#include <unistd.h>
47206156Sume#include <libutil.h>
48206156Sume#include <ctype.h>
49206156Sume#include <errno.h>
50206156Sume#include <stringlist.h>
51206156Sume
52206156Sumestatic char tname[MAXPATHLEN];
53206156Sume
54206156Sume#define	PMASK		0xffff
55206156Sume#define PROTOMAX	5
56206156Sume
57206156Sumeextern void	uniq(const char *);
58206156Sume
59206156Sumestatic void	add(DB *, StringList *, size_t, const char *, size_t *, int);
60206156Sumestatic StringList ***parseservices(const char *, StringList *);
61206156Sumestatic void	cleanup(void);
62206156Sumestatic void	store(DB *, DBT *, DBT *, int);
63206156Sumestatic void	killproto(DBT *);
64206156Sumestatic char    *getstring(const char *, size_t, char **, const char *);
65206156Sumestatic size_t	getprotoindex(StringList *, const char *);
66206156Sumestatic const char *getprotostr(StringList *, size_t);
67206156Sumestatic const char *mkaliases(StringList *, char *, size_t);
68206156Sumestatic void	usage(void);
69206156Sume
70263028SjhbHASHINFO hinfo = {
71206156Sume	.bsize = 256,
72206156Sume	.ffactor = 4,
73206156Sume	.nelem = 32768,
74206156Sume	.cachesize = 1024,
75206156Sume	.hash = NULL,
76206156Sume	.lorder = 0
77206156Sume};
78206156Sume
79206156Sume
80206156Sumeint
81206156Sumemain(int argc, char *argv[])
82206156Sume{
83206156Sume	DB	*db;
84206156Sume	int	 ch;
85206156Sume	const char *fname = _PATH_SERVICES;
86206156Sume	const char *dbname = _PATH_SERVICES_DB;
87206156Sume	int	 warndup = 1;
88206156Sume	int	 unique = 0;
89206156Sume	int	 otherflag = 0;
90263028Sjhb	int	 byteorder = 0;
91206156Sume	size_t	 cnt = 0;
92206156Sume	StringList *sl, ***svc;
93206156Sume	size_t port, proto;
94206156Sume
95206156Sume	setprogname(argv[0]);
96206156Sume
97263028Sjhb	while ((ch = getopt(argc, argv, "blo:qu")) != -1)
98206156Sume		switch (ch) {
99263028Sjhb		case 'b':
100263028Sjhb		case 'l':
101263028Sjhb			if (byteorder != 0)
102263028Sjhb				usage();
103263028Sjhb			byteorder = ch == 'b' ? 4321 : 1234;
104263028Sjhb			break;
105206156Sume		case 'q':
106206156Sume			otherflag = 1;
107206156Sume			warndup = 0;
108206156Sume			break;
109206156Sume		case 'o':
110206156Sume			otherflag = 1;
111206156Sume			dbname = optarg;
112206156Sume			break;
113206156Sume		case 'u':
114206156Sume			unique++;
115206156Sume			break;
116206156Sume		case '?':
117206156Sume		default:
118206156Sume			usage();
119206156Sume		}
120206156Sume
121206156Sume	argc -= optind;
122206156Sume	argv += optind;
123206156Sume
124206156Sume	if (argc > 1 || (unique && otherflag))
125206156Sume		usage();
126206156Sume	if (argc == 1)
127206156Sume		fname = argv[0];
128206156Sume
129263028Sjhb	/* Set byte order. */
130263028Sjhb	hinfo.lorder = byteorder;
131263028Sjhb
132206156Sume	if (unique)
133206156Sume		uniq(fname);
134206156Sume
135206156Sume	svc = parseservices(fname, sl = sl_init());
136206156Sume
137206156Sume	if (atexit(cleanup))
138206156Sume		err(1, "Cannot install exit handler");
139206156Sume
140206156Sume	(void)snprintf(tname, sizeof(tname), "%s.tmp", dbname);
141206156Sume	db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL,
142206156Sume	    (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo);
143206156Sume	if (!db)
144206156Sume		err(1, "Error opening temporary database `%s'", tname);
145206156Sume
146206156Sume
147206156Sume	for (port = 0; port < PMASK + 1; port++) {
148206156Sume		if (svc[port] == NULL)
149206156Sume			continue;
150206156Sume
151206156Sume		for (proto = 0; proto < PROTOMAX; proto++) {
152206156Sume			StringList *s;
153206156Sume			if ((s = svc[port][proto]) == NULL)
154206156Sume				continue;
155206156Sume			add(db, s, port, getprotostr(sl, proto), &cnt, warndup);
156206156Sume		}
157206156Sume
158206156Sume		free(svc[port]);
159206156Sume	}
160206156Sume
161206156Sume	free(svc);
162206156Sume	sl_free(sl, 1);
163206156Sume
164206156Sume	if ((db->close)(db))
165206156Sume		err(1, "Error closing temporary database `%s'", tname);
166206156Sume
167206156Sume	if (rename(tname, dbname) == -1)
168206156Sume		err(1, "Cannot rename `%s' to `%s'", tname, dbname);
169206156Sume
170206156Sume	return 0;
171206156Sume}
172206156Sume
173206156Sumestatic void
174206156Sumeadd(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt,
175206156Sume    int warndup)
176206156Sume{
177206156Sume	size_t i;
178206156Sume	char	 keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ];
179206156Sume	DBT	 data, key;
180206156Sume	key.data = keyb;
181206156Sume	data.data = datab;
182206156Sume
183206156Sume#ifdef DEBUG
184206156Sume	(void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto);
185206156Sume	for (i = 1; i < sl->sl_cur; i++)
186206156Sume	    (void)printf("%s ", sl->sl_str[i]);
187206156Sume	(void)printf("]\n");
188206156Sume#endif
189206156Sume
190206156Sume	/* key `indirect key', data `full line' */
191206156Sume	data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1;
192206156Sume	key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s",
193206156Sume	    sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1;
194206156Sume	store(db, &data, &key, warndup);
195206156Sume
196206156Sume	/* key `\377port/proto', data = `indirect key' */
197206156Sume	key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s",
198206156Sume	    port, proto) + 1;
199206156Sume	store(db, &key, &data, warndup);
200206156Sume
201206156Sume	/* key `\377port', data = `indirect key' */
202206156Sume	killproto(&key);
203206156Sume	store(db, &key, &data, warndup);
204206156Sume
205206156Sume	/* add references for service and all aliases */
206206156Sume	for (i = 0; i < sl->sl_cur; i++) {
207206156Sume		/* key `\376service/proto', data = `indirect key' */
208206156Sume		key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s",
209206156Sume		    sl->sl_str[i], proto) + 1;
210206156Sume		store(db, &key, &data, warndup);
211206156Sume
212206156Sume		/* key `\376service', data = `indirect key' */
213206156Sume		killproto(&key);
214206156Sume		store(db, &key, &data, warndup);
215206156Sume	}
216206156Sume	sl_free(sl, 1);
217206156Sume}
218206156Sume
219206156Sumestatic StringList ***
220206156Sumeparseservices(const char *fname, StringList *sl)
221206156Sume{
222206156Sume	size_t len, line, pindex;
223206156Sume	FILE *fp;
224206156Sume	StringList ***svc, *s;
225206156Sume	char *p, *ep;
226206156Sume
227206156Sume	if ((fp = fopen(fname, "r")) == NULL)
228206156Sume		err(1, "Cannot open `%s'", fname);
229206156Sume
230206156Sume	line = 0;
231206156Sume	if ((svc = calloc(PMASK + 1, sizeof(StringList **))) == NULL)
232206163Sume		err(1, "Cannot allocate %zu bytes", (size_t)(PMASK + 1));
233206156Sume
234206156Sume	/* XXX: change NULL to "\0\0#" when fparseln fixed */
235206156Sume	for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) {
236206156Sume		char	*name, *port, *proto, *aliases, *cp, *alias;
237206156Sume		unsigned long pnum;
238206156Sume
239206156Sume		if (len == 0)
240206156Sume			continue;
241206156Sume
242206156Sume		for (cp = p; *cp && isspace((unsigned char)*cp); cp++)
243206156Sume			continue;
244206156Sume
245206156Sume		if (*cp == '\0' || *cp == '#')
246206156Sume			continue;
247206156Sume
248206156Sume		if ((name = getstring(fname, line, &cp, "name")) == NULL)
249206156Sume			continue;
250206156Sume
251206156Sume		if ((port = getstring(fname, line, &cp, "port")) == NULL)
252206156Sume			continue;
253206156Sume
254206156Sume		if (cp) {
255206156Sume			for (aliases = cp; *cp && *cp != '#'; cp++)
256206156Sume				continue;
257206156Sume
258206156Sume			if (*cp)
259206156Sume				*cp = '\0';
260206156Sume		} else
261206156Sume			aliases = NULL;
262206156Sume
263206156Sume		proto = strchr(port, '/');
264206156Sume		if (proto == NULL || proto[1] == '\0') {
265206156Sume			warnx("%s, %zu: no protocol found", fname, line);
266206156Sume			continue;
267206156Sume		}
268206156Sume		*proto++ = '\0';
269206156Sume
270206156Sume		errno = 0;
271206156Sume		pnum = strtoul(port, &ep, 0);
272206156Sume		if (*port == '\0' || *ep != '\0') {
273206156Sume			warnx("%s, %zu: invalid port `%s'", fname, line, port);
274206156Sume			continue;
275206156Sume		}
276206156Sume		if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) {
277206156Sume			warnx("%s, %zu: port too big `%s'", fname, line, port);
278206156Sume			continue;
279206156Sume		}
280206156Sume
281206156Sume		if (svc[pnum] == NULL) {
282206156Sume			svc[pnum] = calloc(PROTOMAX, sizeof(StringList *));
283206156Sume			if (svc[pnum] == NULL)
284206163Sume				err(1, "Cannot allocate %zu bytes",
285206163Sume				    (size_t)PROTOMAX);
286206156Sume		}
287206156Sume
288206156Sume		pindex = getprotoindex(sl, proto);
289206156Sume		if (svc[pnum][pindex] == NULL)
290206156Sume			s = svc[pnum][pindex] = sl_init();
291206156Sume		else
292206156Sume			s = svc[pnum][pindex];
293206156Sume
294206156Sume		/* build list of aliases */
295206156Sume		if (sl_find(s, name) == NULL) {
296206159Sume			char *p2;
297206156Sume
298206159Sume			if ((p2 = strdup(name)) == NULL)
299206156Sume				err(1, "Cannot copy string");
300206159Sume			(void)sl_add(s, p2);
301206156Sume		}
302206156Sume
303206156Sume		if (aliases) {
304206156Sume			while ((alias = strsep(&aliases, " \t")) != NULL) {
305206156Sume				if (alias[0] == '\0')
306206156Sume					continue;
307206156Sume				if (sl_find(s, alias) == NULL) {
308206159Sume					char *p2;
309206156Sume
310206159Sume					if ((p2 = strdup(alias)) == NULL)
311206156Sume						err(1, "Cannot copy string");
312206159Sume					(void)sl_add(s, p2);
313206156Sume				}
314206156Sume			}
315206156Sume		}
316206156Sume	}
317206156Sume	(void)fclose(fp);
318206156Sume	return svc;
319206156Sume}
320206156Sume
321206156Sume/*
322206156Sume * cleanup(): Remove temporary files upon exit
323206156Sume */
324206156Sumestatic void
325206156Sumecleanup(void)
326206156Sume{
327206156Sume	if (tname[0])
328206156Sume		(void)unlink(tname);
329206156Sume}
330206156Sume
331206156Sumestatic char *
332206156Sumegetstring(const char *fname, size_t line, char **cp, const char *tag)
333206156Sume{
334206156Sume	char *str;
335206156Sume
336206156Sume	while ((str = strsep(cp, " \t")) != NULL && *str == '\0')
337206156Sume		continue;
338206156Sume
339206156Sume	if (str == NULL)
340206156Sume		warnx("%s, %zu: no %s found", fname, line, tag);
341206156Sume
342206156Sume	return str;
343206156Sume}
344206156Sume
345206156Sumestatic void
346206156Sumekillproto(DBT *key)
347206156Sume{
348206156Sume	char *p, *d = key->data;
349206156Sume
350206156Sume	if ((p = strchr(d, '/')) == NULL)
351206156Sume		abort();
352206156Sume	*p++ = '\0';
353206156Sume	key->size = p - d;
354206156Sume}
355206156Sume
356206156Sumestatic void
357206156Sumestore(DB *db, DBT *key, DBT *data, int warndup)
358206156Sume{
359206156Sume#ifdef DEBUG
360206156Sume	int k = key->size - 1;
361206156Sume	int d = data->size - 1;
362206156Sume	(void)printf("store [%*.*s] [%*.*s]\n",
363206156Sume		k, k, (char *)key->data + 1,
364206156Sume		d, d, (char *)data->data + 1);
365206156Sume#endif
366206156Sume	switch ((db->put)(db, key, data, R_NOOVERWRITE)) {
367206156Sume	case 0:
368206156Sume		break;
369206156Sume	case 1:
370206156Sume		if (warndup)
371206156Sume			warnx("duplicate service `%s'",
372206156Sume			    &((char *)key->data)[1]);
373206156Sume		break;
374206156Sume	case -1:
375206156Sume		err(1, "put");
376206156Sume		break;
377206156Sume	default:
378206156Sume		abort();
379206156Sume		break;
380206156Sume	}
381206156Sume}
382206156Sume
383206156Sumestatic size_t
384206156Sumegetprotoindex(StringList *sl, const char *str)
385206156Sume{
386206156Sume	size_t i;
387206156Sume	char *p;
388206156Sume
389206156Sume	for (i= 0; i < sl->sl_cur; i++)
390206156Sume		if (strcmp(sl->sl_str[i], str) == 0)
391206156Sume			return i;
392206156Sume
393206156Sume	if (i == PROTOMAX)
394206156Sume		errx(1, "Ran out of protocols adding `%s';"
395206156Sume		    " recompile with larger PROTOMAX", str);
396206156Sume	if ((p = strdup(str)) == NULL)
397206156Sume		err(1, "Cannot copy string");
398206156Sume	(void)sl_add(sl, p);
399206156Sume	return i;
400206156Sume}
401206156Sume
402206156Sumestatic const char *
403206156Sumegetprotostr(StringList *sl, size_t i)
404206156Sume{
405206156Sume	assert(i < sl->sl_cur);
406206156Sume	return sl->sl_str[i];
407206156Sume}
408206156Sume
409206156Sumestatic const char *
410206156Sumemkaliases(StringList *sl, char *buf, size_t len)
411206156Sume{
412206156Sume	size_t nc, i, pos;
413206156Sume
414206156Sume	buf[0] = 0;
415206156Sume	for (i = 1, pos = 0; i < sl->sl_cur; i++) {
416206156Sume		nc = strlcpy(buf + pos, sl->sl_str[i], len);
417206156Sume		if (nc >= len)
418206156Sume			goto out;
419206156Sume		pos += nc;
420206156Sume		len -= nc;
421206156Sume		nc = strlcpy(buf + pos, " ", len);
422206156Sume		if (nc >= len)
423206156Sume			goto out;
424206156Sume		pos += nc;
425206156Sume		len -= nc;
426206156Sume	}
427206156Sume	return buf;
428206156Sumeout:
429206156Sume	warn("aliases for `%s' truncated", sl->sl_str[0]);
430206156Sume	return buf;
431206156Sume}
432206156Sume
433206156Sumestatic void
434206156Sumeusage(void)
435206156Sume{
436263028Sjhb	(void)fprintf(stderr,
437263028Sjhb	    "Usage:\t%s [-b | -l] [-q] [-o <db>] [<servicefile>]\n"
438206156Sume	    "\t%s -u [<servicefile>]\n", getprogname(), getprogname());
439206156Sume	exit(1);
440206156Sume}
441