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
53 static char tname[MAXPATHLEN];
54
55 #define PMASK 0xffff
56 #define PROTOMAX 6
57
58 static void add(DB *, StringList *, size_t, const char *, size_t *, int);
59 static StringList ***parseservices(const char *, StringList *);
60 static void cleanup(void);
61 static void store(DB *, DBT *, DBT *, int);
62 static void killproto(DBT *);
63 static char *getstring(const char *, size_t, char **, const char *);
64 static size_t getprotoindex(StringList *, const char *);
65 static const char *getprotostr(StringList *, size_t);
66 static const char *mkaliases(StringList *, char *, size_t);
67 static void usage(void);
68
69 HASHINFO hinfo = {
70 .bsize = 256,
71 .ffactor = 4,
72 .nelem = 32768,
73 .cachesize = 1024,
74 .hash = NULL,
75 .lorder = 0
76 };
77
78
79 int
main(int argc,char * argv[])80 main(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
188 static void
add(DB * db,StringList * sl,size_t port,const char * proto,size_t * cnt,int warndup)189 add(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
234 static StringList ***
parseservices(const char * fname,StringList * sl)235 parseservices(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 */
345 static void
cleanup(void)346 cleanup(void)
347 {
348 if (tname[0])
349 (void)unlink(tname);
350 }
351
352 static char *
getstring(const char * fname,size_t line,char ** cp,const char * tag)353 getstring(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
366 static void
killproto(DBT * key)367 killproto(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
377 static void
store(DB * db,DBT * key,DBT * data,int warndup)378 store(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
404 static size_t
getprotoindex(StringList * sl,const char * str)405 getprotoindex(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
423 static const char *
getprotostr(StringList * sl,size_t i)424 getprotostr(StringList *sl, size_t i)
425 {
426 assert(i < sl->sl_cur);
427 return sl->sl_str[i];
428 }
429
430 static const char *
mkaliases(StringList * sl,char * buf,size_t len)431 mkaliases(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;
449 out:
450 warn("aliases for `%s' truncated", sl->sl_str[0]);
451 return buf;
452 }
453
454 static void
usage(void)455 usage(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