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