services_mkdb.c revision 1.11 1 /* $NetBSD: services_mkdb.c,v 1.11 2007/06/23 17:02:03 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.11 2007/06/23 17:02:03 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 int otherflag = 0;
98 size_t cnt = 0;
99 StringList *sl, ***svc;
100 size_t port, proto;
101
102 setprogname(argv[0]);
103
104 while ((ch = getopt(argc, argv, "qo:u")) != -1)
105 switch (ch) {
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 if (unique)
131 uniq(fname);
132
133 svc = parseservices(fname, sl = sl_init());
134
135 if (atexit(cleanup))
136 err(1, "Cannot install exit handler");
137
138 (void)snprintf(tname, sizeof(tname), "%s.tmp", dbname);
139 db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL,
140 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo);
141 if (!db)
142 err(1, "Error opening temporary database `%s'", tname);
143
144
145 for (port = 0; port < PMASK + 1; port++) {
146 if (svc[port] == NULL)
147 continue;
148
149 for (proto = 0; proto < PROTOMAX; proto++) {
150 StringList *s;
151 if ((s = svc[port][proto]) == NULL)
152 continue;
153 add(db, s, port, getprotostr(sl, proto), &cnt, warndup);
154 }
155
156 free(svc[port]);
157 }
158
159 free(svc);
160 sl_free(sl, 1);
161
162 if ((db->close)(db))
163 err(1, "Error closing temporary database `%s'", tname);
164
165 if (rename(tname, dbname) == -1)
166 err(1, "Cannot rename `%s' to `%s'", tname, dbname);
167
168 return 0;
169 }
170
171 static void
172 add(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt,
173 int warndup)
174 {
175 size_t i;
176 char keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ];
177 DBT data, key;
178 key.data = keyb;
179 data.data = datab;
180
181 #ifdef DEBUG
182 (void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto);
183 for (i = 1; i < sl->sl_cur; i++)
184 (void)printf("%s ", sl->sl_str[i]);
185 (void)printf("]\n");
186 #endif
187
188 /* key `indirect key', data `full line' */
189 data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1;
190 key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s",
191 sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1;
192 store(db, &data, &key, warndup);
193
194 /* key `\377port/proto', data = `indirect key' */
195 key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s",
196 port, proto) + 1;
197 store(db, &key, &data, warndup);
198
199 /* key `\377port', data = `indirect key' */
200 killproto(&key);
201 store(db, &key, &data, warndup);
202
203 /* add references for service and all aliases */
204 for (i = 0; i < sl->sl_cur; i++) {
205 /* key `\376service/proto', data = `indirect key' */
206 key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s",
207 sl->sl_str[i], proto) + 1;
208 store(db, &key, &data, warndup);
209
210 /* key `\376service', data = `indirect key' */
211 killproto(&key);
212 store(db, &key, &data, warndup);
213 }
214 sl_free(sl, 1);
215 }
216
217 static StringList ***
218 parseservices(const char *fname, StringList *sl)
219 {
220 size_t len, line, pindex;
221 FILE *fp;
222 StringList ***svc, *s;
223 char *p, *ep;
224
225 if ((fp = fopen(fname, "r")) == NULL)
226 err(1, "Cannot open `%s'", fname);
227
228 line = 0;
229 svc = ecalloc(PMASK + 1, sizeof(StringList **));
230
231 /* XXX: change NULL to "\0\0#" when fparseln fixed */
232 for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) {
233 char *name, *port, *proto, *aliases, *cp, *alias;
234 unsigned long pnum;
235
236 if (len == 0)
237 continue;
238
239 for (cp = p; *cp && isspace((unsigned char)*cp); cp++)
240 continue;
241
242 if (*cp == '\0' || *cp == '#')
243 continue;
244
245 if ((name = getstring(fname, line, &cp, "name")) == NULL)
246 continue;
247
248 if ((port = getstring(fname, line, &cp, "port")) == NULL)
249 continue;
250
251 if (cp) {
252 for (aliases = cp; *cp && *cp != '#'; cp++)
253 continue;
254
255 if (*cp)
256 *cp = '\0';
257 } else
258 aliases = NULL;
259
260 proto = strchr(port, '/');
261 if (proto == NULL || proto[1] == '\0') {
262 warnx("%s, %zu: no protocol found", fname, line);
263 continue;
264 }
265 *proto++ = '\0';
266
267 errno = 0;
268 pnum = strtoul(port, &ep, 0);
269 if (*port == '\0' || *ep != '\0') {
270 warnx("%s, %zu: invalid port `%s'", fname, line, port);
271 continue;
272 }
273 if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) {
274 warnx("%s, %zu: port too big `%s'", fname, line, port);
275 continue;
276 }
277
278 if (svc[pnum] == NULL)
279 svc[pnum] = ecalloc(PROTOMAX, sizeof(StringList *));
280
281 pindex = getprotoindex(sl, proto);
282 if (svc[pnum][pindex] == NULL)
283 s = svc[pnum][pindex] = sl_init();
284 else
285 s = svc[pnum][pindex];
286
287 /* build list of aliases */
288 if (sl_find(s, name) == NULL)
289 (void)sl_add(s, estrdup(name));
290
291 if (aliases) {
292 while ((alias = strsep(&aliases, " \t")) != NULL) {
293 if (alias[0] == '\0')
294 continue;
295 if (sl_find(s, alias) == NULL)
296 (void)sl_add(s, estrdup(alias));
297 }
298 }
299 }
300 (void)fclose(fp);
301 return svc;
302 }
303
304 /*
305 * cleanup(): Remove temporary files upon exit
306 */
307 static void
308 cleanup(void)
309 {
310 if (tname[0])
311 (void)unlink(tname);
312 }
313
314 static char *
315 getstring(const char *fname, size_t line, char **cp, const char *tag)
316 {
317 char *str;
318
319 while ((str = strsep(cp, " \t")) != NULL && *str == '\0')
320 continue;
321
322 if (str == NULL)
323 warnx("%s, %zu: no %s found", fname, line, tag);
324
325 return str;
326 }
327
328 static void
329 killproto(DBT *key)
330 {
331 char *p, *d = key->data;
332
333 if ((p = strchr(d, '/')) == NULL)
334 abort();
335 *p++ = '\0';
336 key->size = p - d;
337 }
338
339 static void
340 store(DB *db, DBT *key, DBT *data, int warndup)
341 {
342 #ifdef DEBUG
343 int k = key->size - 1;
344 int d = data->size - 1;
345 (void)printf("store [%*.*s] [%*.*s]\n",
346 k, k, (char *)key->data + 1,
347 d, d, (char *)data->data + 1);
348 #endif
349 switch ((db->put)(db, key, data, R_NOOVERWRITE)) {
350 case 0:
351 break;
352 case 1:
353 if (warndup)
354 warnx("duplicate service `%s'",
355 &((char *)key->data)[1]);
356 break;
357 case -1:
358 err(1, "put");
359 break;
360 default:
361 abort();
362 break;
363 }
364 }
365
366 static size_t
367 getprotoindex(StringList *sl, const char *str)
368 {
369 size_t i;
370
371 for (i= 0; i < sl->sl_cur; i++)
372 if (strcmp(sl->sl_str[i], str) == 0)
373 return i;
374
375 if (i == PROTOMAX)
376 errx(1, "Ran out of protocols adding `%s';"
377 " recompile with larger PROTOMAX", str);
378 (void)sl_add(sl, estrdup(str));
379 return i;
380 }
381
382 static const char *
383 getprotostr(StringList *sl, size_t i)
384 {
385 assert(i < sl->sl_cur);
386 return sl->sl_str[i];
387 }
388
389 static const char *
390 mkaliases(StringList *sl, char *buf, size_t len)
391 {
392 size_t nc, i, pos;
393
394 for (i = 1, pos = 0; i < sl->sl_cur; i++) {
395 nc = strlcpy(buf + pos, sl->sl_str[i], len);
396 if (nc >= len)
397 goto out;
398 pos += nc;
399 len -= nc;
400 nc = strlcpy(buf + pos, " ", len);
401 if (nc >= len)
402 goto out;
403 pos += nc;
404 len -= nc;
405 }
406 return buf;
407 out:
408 warn("aliases for `%s' truncated", sl->sl_str[0]);
409 return buf;
410 }
411
412 static void
413 usage(void)
414 {
415 (void)fprintf(stderr, "Usage:\t%s [-q] [-o <db>] [<servicefile>]\n"
416 "\t%s -u [<servicefile>]\n", getprogname(), getprogname());
417 exit(1);
418 }
419