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>
47293929Sgarga#include <libgen.h>
48206156Sume#include <libutil.h>
49206156Sume#include <ctype.h>
50206156Sume#include <errno.h>
51206156Sume#include <stringlist.h>
52206156Sume
53241777Sed#include "extern.h"
54241777Sed
55206156Sumestatic char tname[MAXPATHLEN];
56206156Sume
57206156Sume#define	PMASK		0xffff
58206156Sume#define PROTOMAX	5
59206156Sume
60206156Sumestatic void	add(DB *, StringList *, size_t, const char *, size_t *, int);
61206156Sumestatic StringList ***parseservices(const char *, StringList *);
62206156Sumestatic void	cleanup(void);
63206156Sumestatic void	store(DB *, DBT *, DBT *, int);
64206156Sumestatic void	killproto(DBT *);
65206156Sumestatic char    *getstring(const char *, size_t, char **, const char *);
66206156Sumestatic size_t	getprotoindex(StringList *, const char *);
67206156Sumestatic const char *getprotostr(StringList *, size_t);
68206156Sumestatic const char *mkaliases(StringList *, char *, size_t);
69206156Sumestatic void	usage(void);
70206156Sume
71263028SjhbHASHINFO hinfo = {
72206156Sume	.bsize = 256,
73206156Sume	.ffactor = 4,
74206156Sume	.nelem = 32768,
75206156Sume	.cachesize = 1024,
76206156Sume	.hash = NULL,
77206156Sume	.lorder = 0
78206156Sume};
79206156Sume
80206156Sume
81206156Sumeint
82206156Sumemain(int argc, char *argv[])
83206156Sume{
84206156Sume	DB	*db;
85206156Sume	int	 ch;
86206156Sume	const char *fname = _PATH_SERVICES;
87206156Sume	const char *dbname = _PATH_SERVICES_DB;
88206156Sume	int	 warndup = 1;
89206156Sume	int	 unique = 0;
90206156Sume	int	 otherflag = 0;
91263028Sjhb	int	 byteorder = 0;
92206156Sume	size_t	 cnt = 0;
93206156Sume	StringList *sl, ***svc;
94206156Sume	size_t port, proto;
95293929Sgarga	char *dbname_dir;
96293929Sgarga	int dbname_dir_fd = -1;
97206156Sume
98206156Sume	setprogname(argv[0]);
99206156Sume
100263028Sjhb	while ((ch = getopt(argc, argv, "blo:qu")) != -1)
101206156Sume		switch (ch) {
102263028Sjhb		case 'b':
103263028Sjhb		case 'l':
104263028Sjhb			if (byteorder != 0)
105263028Sjhb				usage();
106263028Sjhb			byteorder = ch == 'b' ? 4321 : 1234;
107263028Sjhb			break;
108206156Sume		case 'q':
109206156Sume			otherflag = 1;
110206156Sume			warndup = 0;
111206156Sume			break;
112206156Sume		case 'o':
113206156Sume			otherflag = 1;
114206156Sume			dbname = optarg;
115206156Sume			break;
116206156Sume		case 'u':
117206156Sume			unique++;
118206156Sume			break;
119206156Sume		case '?':
120206156Sume		default:
121206156Sume			usage();
122206156Sume		}
123206156Sume
124206156Sume	argc -= optind;
125206156Sume	argv += optind;
126206156Sume
127206156Sume	if (argc > 1 || (unique && otherflag))
128206156Sume		usage();
129206156Sume	if (argc == 1)
130206156Sume		fname = argv[0];
131206156Sume
132263028Sjhb	/* Set byte order. */
133263028Sjhb	hinfo.lorder = byteorder;
134263028Sjhb
135206156Sume	if (unique)
136206156Sume		uniq(fname);
137206156Sume
138206156Sume	svc = parseservices(fname, sl = sl_init());
139206156Sume
140206156Sume	if (atexit(cleanup))
141206156Sume		err(1, "Cannot install exit handler");
142206156Sume
143206156Sume	(void)snprintf(tname, sizeof(tname), "%s.tmp", dbname);
144296424Sdwmalone	db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL,
145206156Sume	    (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo);
146206156Sume	if (!db)
147206156Sume		err(1, "Error opening temporary database `%s'", tname);
148206156Sume
149206156Sume
150206156Sume	for (port = 0; port < PMASK + 1; port++) {
151206156Sume		if (svc[port] == NULL)
152206156Sume			continue;
153206156Sume
154206156Sume		for (proto = 0; proto < PROTOMAX; proto++) {
155206156Sume			StringList *s;
156206156Sume			if ((s = svc[port][proto]) == NULL)
157206156Sume				continue;
158206156Sume			add(db, s, port, getprotostr(sl, proto), &cnt, warndup);
159206156Sume		}
160206156Sume
161206156Sume		free(svc[port]);
162206156Sume	}
163206156Sume
164206156Sume	free(svc);
165206156Sume	sl_free(sl, 1);
166206156Sume
167206156Sume	if ((db->close)(db))
168206156Sume		err(1, "Error closing temporary database `%s'", tname);
169206156Sume
170293929Sgarga	/*
171293929Sgarga	 * Make sure file is safe on disk. To improve performance we will call
172293929Sgarga	 * fsync() to the directory where file lies
173293929Sgarga	 */
174293929Sgarga	if (rename(tname, dbname) == -1 ||
175293929Sgarga	    (dbname_dir = dirname(dbname)) == NULL ||
176293929Sgarga	    (dbname_dir_fd = open(dbname_dir, O_RDONLY|O_DIRECTORY)) == -1 ||
177293929Sgarga	    fsync(dbname_dir_fd) != 0) {
178293929Sgarga		if (dbname_dir_fd != -1)
179293929Sgarga			close(dbname_dir_fd);
180206156Sume		err(1, "Cannot rename `%s' to `%s'", tname, dbname);
181293929Sgarga	}
182206156Sume
183293929Sgarga	if (dbname_dir_fd != -1)
184293929Sgarga		close(dbname_dir_fd);
185293929Sgarga
186206156Sume	return 0;
187206156Sume}
188206156Sume
189206156Sumestatic void
190206156Sumeadd(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt,
191206156Sume    int warndup)
192206156Sume{
193206156Sume	size_t i;
194206156Sume	char	 keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ];
195206156Sume	DBT	 data, key;
196206156Sume	key.data = keyb;
197206156Sume	data.data = datab;
198206156Sume
199206156Sume#ifdef DEBUG
200206156Sume	(void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto);
201206156Sume	for (i = 1; i < sl->sl_cur; i++)
202206156Sume	    (void)printf("%s ", sl->sl_str[i]);
203206156Sume	(void)printf("]\n");
204206156Sume#endif
205206156Sume
206206156Sume	/* key `indirect key', data `full line' */
207206156Sume	data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1;
208206156Sume	key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s",
209206156Sume	    sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1;
210206156Sume	store(db, &data, &key, warndup);
211206156Sume
212206156Sume	/* key `\377port/proto', data = `indirect key' */
213206156Sume	key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s",
214206156Sume	    port, proto) + 1;
215206156Sume	store(db, &key, &data, warndup);
216206156Sume
217206156Sume	/* key `\377port', data = `indirect key' */
218206156Sume	killproto(&key);
219206156Sume	store(db, &key, &data, warndup);
220206156Sume
221206156Sume	/* add references for service and all aliases */
222206156Sume	for (i = 0; i < sl->sl_cur; i++) {
223206156Sume		/* key `\376service/proto', data = `indirect key' */
224206156Sume		key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s",
225206156Sume		    sl->sl_str[i], proto) + 1;
226206156Sume		store(db, &key, &data, warndup);
227206156Sume
228206156Sume		/* key `\376service', data = `indirect key' */
229206156Sume		killproto(&key);
230206156Sume		store(db, &key, &data, warndup);
231206156Sume	}
232206156Sume	sl_free(sl, 1);
233206156Sume}
234206156Sume
235206156Sumestatic StringList ***
236206156Sumeparseservices(const char *fname, StringList *sl)
237206156Sume{
238206156Sume	size_t len, line, pindex;
239206156Sume	FILE *fp;
240206156Sume	StringList ***svc, *s;
241206156Sume	char *p, *ep;
242206156Sume
243206156Sume	if ((fp = fopen(fname, "r")) == NULL)
244206156Sume		err(1, "Cannot open `%s'", fname);
245206156Sume
246206156Sume	line = 0;
247206156Sume	if ((svc = calloc(PMASK + 1, sizeof(StringList **))) == NULL)
248206163Sume		err(1, "Cannot allocate %zu bytes", (size_t)(PMASK + 1));
249206156Sume
250206156Sume	/* XXX: change NULL to "\0\0#" when fparseln fixed */
251206156Sume	for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) {
252206156Sume		char	*name, *port, *proto, *aliases, *cp, *alias;
253206156Sume		unsigned long pnum;
254206156Sume
255206156Sume		if (len == 0)
256206156Sume			continue;
257206156Sume
258206156Sume		for (cp = p; *cp && isspace((unsigned char)*cp); cp++)
259206156Sume			continue;
260206156Sume
261206156Sume		if (*cp == '\0' || *cp == '#')
262206156Sume			continue;
263206156Sume
264206156Sume		if ((name = getstring(fname, line, &cp, "name")) == NULL)
265206156Sume			continue;
266206156Sume
267206156Sume		if ((port = getstring(fname, line, &cp, "port")) == NULL)
268206156Sume			continue;
269206156Sume
270206156Sume		if (cp) {
271206156Sume			for (aliases = cp; *cp && *cp != '#'; cp++)
272206156Sume				continue;
273206156Sume
274206156Sume			if (*cp)
275206156Sume				*cp = '\0';
276206156Sume		} else
277206156Sume			aliases = NULL;
278206156Sume
279206156Sume		proto = strchr(port, '/');
280206156Sume		if (proto == NULL || proto[1] == '\0') {
281206156Sume			warnx("%s, %zu: no protocol found", fname, line);
282206156Sume			continue;
283206156Sume		}
284206156Sume		*proto++ = '\0';
285206156Sume
286206156Sume		errno = 0;
287206156Sume		pnum = strtoul(port, &ep, 0);
288206156Sume		if (*port == '\0' || *ep != '\0') {
289206156Sume			warnx("%s, %zu: invalid port `%s'", fname, line, port);
290206156Sume			continue;
291206156Sume		}
292206156Sume		if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) {
293206156Sume			warnx("%s, %zu: port too big `%s'", fname, line, port);
294206156Sume			continue;
295206156Sume		}
296206156Sume
297206156Sume		if (svc[pnum] == NULL) {
298206156Sume			svc[pnum] = calloc(PROTOMAX, sizeof(StringList *));
299206156Sume			if (svc[pnum] == NULL)
300206163Sume				err(1, "Cannot allocate %zu bytes",
301206163Sume				    (size_t)PROTOMAX);
302206156Sume		}
303206156Sume
304206156Sume		pindex = getprotoindex(sl, proto);
305206156Sume		if (svc[pnum][pindex] == NULL)
306206156Sume			s = svc[pnum][pindex] = sl_init();
307206156Sume		else
308206156Sume			s = svc[pnum][pindex];
309206156Sume
310206156Sume		/* build list of aliases */
311206156Sume		if (sl_find(s, name) == NULL) {
312206159Sume			char *p2;
313206156Sume
314206159Sume			if ((p2 = strdup(name)) == NULL)
315206156Sume				err(1, "Cannot copy string");
316206159Sume			(void)sl_add(s, p2);
317206156Sume		}
318206156Sume
319206156Sume		if (aliases) {
320206156Sume			while ((alias = strsep(&aliases, " \t")) != NULL) {
321206156Sume				if (alias[0] == '\0')
322206156Sume					continue;
323206156Sume				if (sl_find(s, alias) == NULL) {
324206159Sume					char *p2;
325206156Sume
326206159Sume					if ((p2 = strdup(alias)) == NULL)
327206156Sume						err(1, "Cannot copy string");
328206159Sume					(void)sl_add(s, p2);
329206156Sume				}
330206156Sume			}
331206156Sume		}
332206156Sume	}
333206156Sume	(void)fclose(fp);
334206156Sume	return svc;
335206156Sume}
336206156Sume
337206156Sume/*
338206156Sume * cleanup(): Remove temporary files upon exit
339206156Sume */
340206156Sumestatic void
341206156Sumecleanup(void)
342206156Sume{
343206156Sume	if (tname[0])
344206156Sume		(void)unlink(tname);
345206156Sume}
346206156Sume
347206156Sumestatic char *
348206156Sumegetstring(const char *fname, size_t line, char **cp, const char *tag)
349206156Sume{
350206156Sume	char *str;
351206156Sume
352206156Sume	while ((str = strsep(cp, " \t")) != NULL && *str == '\0')
353206156Sume		continue;
354206156Sume
355206156Sume	if (str == NULL)
356206156Sume		warnx("%s, %zu: no %s found", fname, line, tag);
357206156Sume
358206156Sume	return str;
359206156Sume}
360206156Sume
361206156Sumestatic void
362206156Sumekillproto(DBT *key)
363206156Sume{
364206156Sume	char *p, *d = key->data;
365206156Sume
366206156Sume	if ((p = strchr(d, '/')) == NULL)
367206156Sume		abort();
368206156Sume	*p++ = '\0';
369206156Sume	key->size = p - d;
370206156Sume}
371206156Sume
372206156Sumestatic void
373206156Sumestore(DB *db, DBT *key, DBT *data, int warndup)
374206156Sume{
375206156Sume#ifdef DEBUG
376206156Sume	int k = key->size - 1;
377206156Sume	int d = data->size - 1;
378206156Sume	(void)printf("store [%*.*s] [%*.*s]\n",
379206156Sume		k, k, (char *)key->data + 1,
380206156Sume		d, d, (char *)data->data + 1);
381206156Sume#endif
382206156Sume	switch ((db->put)(db, key, data, R_NOOVERWRITE)) {
383206156Sume	case 0:
384206156Sume		break;
385206156Sume	case 1:
386206156Sume		if (warndup)
387206156Sume			warnx("duplicate service `%s'",
388206156Sume			    &((char *)key->data)[1]);
389206156Sume		break;
390206156Sume	case -1:
391206156Sume		err(1, "put");
392206156Sume		break;
393206156Sume	default:
394206156Sume		abort();
395206156Sume		break;
396206156Sume	}
397206156Sume}
398206156Sume
399206156Sumestatic size_t
400206156Sumegetprotoindex(StringList *sl, const char *str)
401206156Sume{
402206156Sume	size_t i;
403206156Sume	char *p;
404206156Sume
405206156Sume	for (i= 0; i < sl->sl_cur; i++)
406206156Sume		if (strcmp(sl->sl_str[i], str) == 0)
407206156Sume			return i;
408206156Sume
409206156Sume	if (i == PROTOMAX)
410206156Sume		errx(1, "Ran out of protocols adding `%s';"
411206156Sume		    " recompile with larger PROTOMAX", str);
412206156Sume	if ((p = strdup(str)) == NULL)
413206156Sume		err(1, "Cannot copy string");
414206156Sume	(void)sl_add(sl, p);
415206156Sume	return i;
416206156Sume}
417206156Sume
418206156Sumestatic const char *
419206156Sumegetprotostr(StringList *sl, size_t i)
420206156Sume{
421206156Sume	assert(i < sl->sl_cur);
422206156Sume	return sl->sl_str[i];
423206156Sume}
424206156Sume
425206156Sumestatic const char *
426206156Sumemkaliases(StringList *sl, char *buf, size_t len)
427206156Sume{
428206156Sume	size_t nc, i, pos;
429206156Sume
430206156Sume	buf[0] = 0;
431206156Sume	for (i = 1, pos = 0; i < sl->sl_cur; i++) {
432206156Sume		nc = strlcpy(buf + pos, sl->sl_str[i], len);
433206156Sume		if (nc >= len)
434206156Sume			goto out;
435206156Sume		pos += nc;
436206156Sume		len -= nc;
437206156Sume		nc = strlcpy(buf + pos, " ", len);
438206156Sume		if (nc >= len)
439206156Sume			goto out;
440206156Sume		pos += nc;
441206156Sume		len -= nc;
442206156Sume	}
443206156Sume	return buf;
444206156Sumeout:
445206156Sume	warn("aliases for `%s' truncated", sl->sl_str[0]);
446206156Sume	return buf;
447206156Sume}
448206156Sume
449206156Sumestatic void
450206156Sumeusage(void)
451206156Sume{
452263028Sjhb	(void)fprintf(stderr,
453263028Sjhb	    "Usage:\t%s [-b | -l] [-q] [-o <db>] [<servicefile>]\n"
454206156Sume	    "\t%s -u [<servicefile>]\n", getprogname(), getprogname());
455206156Sume	exit(1);
456206156Sume}
457