]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/services_mkdb/services_mkdb.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / usr.sbin / services_mkdb / services_mkdb.c
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-NetBSD
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/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/stat.h>
39
40 #include <assert.h>
41 #include <db.h>
42 #include <err.h>
43 #include <fcntl.h>
44 #include <netdb.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <libgen.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <stringlist.h>
53
54 #include "extern.h"
55
56 static char tname[MAXPATHLEN];
57
58 #define PMASK           0xffff
59 #define PROTOMAX        5
60
61 static void     add(DB *, StringList *, size_t, const char *, size_t *, int);
62 static StringList ***parseservices(const char *, StringList *);
63 static void     cleanup(void);
64 static void     store(DB *, DBT *, DBT *, int);
65 static void     killproto(DBT *);
66 static char    *getstring(const char *, size_t, char **, const char *);
67 static size_t   getprotoindex(StringList *, const char *);
68 static const char *getprotostr(StringList *, size_t);
69 static const char *mkaliases(StringList *, char *, size_t);
70 static void     usage(void);
71
72 HASHINFO hinfo = {
73         .bsize = 256,
74         .ffactor = 4,
75         .nelem = 32768,
76         .cachesize = 1024,
77         .hash = NULL,
78         .lorder = 0
79 };
80
81
82 int
83 main(int argc, char *argv[])
84 {
85         DB      *db;
86         int      ch;
87         const char *fname = _PATH_SERVICES;
88         const char *dbname = _PATH_SERVICES_DB;
89         int      warndup = 1;
90         int      unique = 0;
91         int      otherflag = 0;
92         int      byteorder = 0;
93         size_t   cnt = 0;
94         StringList *sl, ***svc;
95         size_t port, proto;
96         char *dbname_dir, *dbname_dirbuf;
97         int dbname_dir_fd = -1;
98
99         setprogname(argv[0]);
100
101         while ((ch = getopt(argc, argv, "blo:qu")) != -1)
102                 switch (ch) {
103                 case 'b':
104                 case 'l':
105                         if (byteorder != 0)
106                                 usage();
107                         byteorder = ch == 'b' ? 4321 : 1234;
108                         break;
109                 case 'q':
110                         otherflag = 1;
111                         warndup = 0;
112                         break;
113                 case 'o':
114                         otherflag = 1;
115                         dbname = optarg;
116                         break;
117                 case 'u':
118                         unique++;
119                         break;
120                 case '?':
121                 default:
122                         usage();
123                 }
124
125         argc -= optind;
126         argv += optind;
127
128         if (argc > 1 || (unique && otherflag))
129                 usage();
130         if (argc == 1)
131                 fname = argv[0];
132
133         /* Set byte order. */
134         hinfo.lorder = byteorder;
135
136         if (unique)
137                 uniq(fname);
138
139         svc = parseservices(fname, sl = sl_init());
140
141         if (atexit(cleanup))
142                 err(1, "Cannot install exit handler");
143
144         (void)snprintf(tname, sizeof(tname), "%s.tmp", dbname);
145         db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL,
146             (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo);
147         if (!db)
148                 err(1, "Error opening temporary database `%s'", tname);
149
150
151         for (port = 0; port < PMASK + 1; port++) {
152                 if (svc[port] == NULL)
153                         continue;
154
155                 for (proto = 0; proto < PROTOMAX; proto++) {
156                         StringList *s;
157                         if ((s = svc[port][proto]) == NULL)
158                                 continue;
159                         add(db, s, port, getprotostr(sl, proto), &cnt, warndup);
160                 }
161
162                 free(svc[port]);
163         }
164
165         free(svc);
166         sl_free(sl, 1);
167
168         if ((db->close)(db))
169                 err(1, "Error closing temporary database `%s'", tname);
170
171         /*
172          * Make sure file is safe on disk. To improve performance we will call
173          * fsync() to the directory where file lies
174          */
175         if (rename(tname, dbname) == -1 ||
176             (dbname_dirbuf = strdup(dbname)) == NULL ||
177             (dbname_dir = dirname(dbname_dirbuf)) == NULL ||
178             (dbname_dir_fd = open(dbname_dir, O_RDONLY|O_DIRECTORY)) == -1 ||
179             fsync(dbname_dir_fd) != 0) {
180                 if (dbname_dir_fd != -1)
181                         close(dbname_dir_fd);
182                 err(1, "Cannot rename `%s' to `%s'", tname, dbname);
183         }
184
185         if (dbname_dir_fd != -1)
186                 close(dbname_dir_fd);
187
188         return 0;
189 }
190
191 static void
192 add(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt,
193     int warndup)
194 {
195         size_t i;
196         char     keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ];
197         DBT      data, key;
198         key.data = keyb;
199         data.data = datab;
200
201 #ifdef DEBUG
202         (void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto);
203         for (i = 1; i < sl->sl_cur; i++)
204             (void)printf("%s ", sl->sl_str[i]);
205         (void)printf("]\n");
206 #endif
207
208         /* key `indirect key', data `full line' */
209         data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1;
210         key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s",
211             sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1;
212         store(db, &data, &key, warndup);
213
214         /* key `\377port/proto', data = `indirect key' */
215         key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s",
216             port, proto) + 1;
217         store(db, &key, &data, warndup);
218
219         /* key `\377port', data = `indirect key' */
220         killproto(&key);
221         store(db, &key, &data, warndup);
222
223         /* add references for service and all aliases */
224         for (i = 0; i < sl->sl_cur; i++) {
225                 /* key `\376service/proto', data = `indirect key' */
226                 key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s",
227                     sl->sl_str[i], proto) + 1;
228                 store(db, &key, &data, warndup);
229
230                 /* key `\376service', data = `indirect key' */
231                 killproto(&key);
232                 store(db, &key, &data, warndup);
233         }
234         sl_free(sl, 1);
235 }
236
237 static StringList ***
238 parseservices(const char *fname, StringList *sl)
239 {
240         ssize_t len;
241         size_t linecap, line, pindex;
242         FILE *fp;
243         StringList ***svc, *s;
244         char *p, *ep;
245
246         if ((fp = fopen(fname, "r")) == NULL)
247                 err(1, "Cannot open `%s'", fname);
248
249         line = linecap = 0;
250         if ((svc = calloc(PMASK + 1, sizeof(StringList **))) == NULL)
251                 err(1, "Cannot allocate %zu bytes", (size_t)(PMASK + 1));
252
253         p = NULL;
254         while ((len = getline(&p, &linecap, fp)) != -1) {
255                 char    *name, *port, *proto, *aliases, *cp, *alias;
256                 unsigned long pnum;
257
258                 line++;
259
260                 if (len == 0)
261                         continue;
262
263                 if (p[len - 1] == '\n')
264                         p[len - 1] = '\0';
265
266                 for (cp = p; *cp && isspace((unsigned char)*cp); cp++)
267                         continue;
268
269                 if (*cp == '\0' || *cp == '#')
270                         continue;
271
272                 if ((name = getstring(fname, line, &cp, "name")) == NULL)
273                         continue;
274
275                 if ((port = getstring(fname, line, &cp, "port")) == NULL)
276                         continue;
277
278                 if (cp) {
279                         for (aliases = cp; *cp && *cp != '#'; cp++)
280                                 continue;
281
282                         if (*cp)
283                                 *cp = '\0';
284                 } else
285                         aliases = NULL;
286
287                 proto = strchr(port, '/');
288                 if (proto == NULL || proto[1] == '\0') {
289                         warnx("%s, %zu: no protocol found", fname, line);
290                         continue;
291                 }
292                 *proto++ = '\0';
293
294                 errno = 0;
295                 pnum = strtoul(port, &ep, 0);
296                 if (*port == '\0' || *ep != '\0') {
297                         warnx("%s, %zu: invalid port `%s'", fname, line, port);
298                         continue;
299                 }
300                 if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) {
301                         warnx("%s, %zu: port too big `%s'", fname, line, port);
302                         continue;
303                 }
304
305                 if (svc[pnum] == NULL) {
306                         svc[pnum] = calloc(PROTOMAX, sizeof(StringList *));
307                         if (svc[pnum] == NULL)
308                                 err(1, "Cannot allocate %zu bytes",
309                                     (size_t)PROTOMAX);
310                 }
311
312                 pindex = getprotoindex(sl, proto);
313                 if (svc[pnum][pindex] == NULL)
314                         s = svc[pnum][pindex] = sl_init();
315                 else
316                         s = svc[pnum][pindex];
317
318                 /* build list of aliases */
319                 if (sl_find(s, name) == NULL) {
320                         char *p2;
321
322                         if ((p2 = strdup(name)) == NULL)
323                                 err(1, "Cannot copy string");
324                         (void)sl_add(s, p2);
325                 }
326
327                 if (aliases) {
328                         while ((alias = strsep(&aliases, " \t")) != NULL) {
329                                 if (alias[0] == '\0')
330                                         continue;
331                                 if (sl_find(s, alias) == NULL) {
332                                         char *p2;
333
334                                         if ((p2 = strdup(alias)) == NULL)
335                                                 err(1, "Cannot copy string");
336                                         (void)sl_add(s, p2);
337                                 }
338                         }
339                 }
340         }
341         (void)fclose(fp);
342         return svc;
343 }
344
345 /*
346  * cleanup(): Remove temporary files upon exit
347  */
348 static void
349 cleanup(void)
350 {
351         if (tname[0])
352                 (void)unlink(tname);
353 }
354
355 static char *
356 getstring(const char *fname, size_t line, char **cp, const char *tag)
357 {
358         char *str;
359
360         while ((str = strsep(cp, " \t")) != NULL && *str == '\0')
361                 continue;
362
363         if (str == NULL)
364                 warnx("%s, %zu: no %s found", fname, line, tag);
365
366         return str;
367 }
368
369 static void
370 killproto(DBT *key)
371 {
372         char *p, *d = key->data;
373
374         if ((p = strchr(d, '/')) == NULL)
375                 abort();
376         *p++ = '\0';
377         key->size = p - d;
378 }
379
380 static void
381 store(DB *db, DBT *key, DBT *data, int warndup)
382 {
383 #ifdef DEBUG
384         int k = key->size - 1;
385         int d = data->size - 1;
386         (void)printf("store [%*.*s] [%*.*s]\n",
387                 k, k, (char *)key->data + 1,
388                 d, d, (char *)data->data + 1);
389 #endif
390         switch ((db->put)(db, key, data, R_NOOVERWRITE)) {
391         case 0:
392                 break;
393         case 1:
394                 if (warndup)
395                         warnx("duplicate service `%s'",
396                             &((char *)key->data)[1]);
397                 break;
398         case -1:
399                 err(1, "put");
400                 break;
401         default:
402                 abort();
403                 break;
404         }
405 }
406
407 static size_t
408 getprotoindex(StringList *sl, const char *str)
409 {
410         size_t i;
411         char *p;
412
413         for (i= 0; i < sl->sl_cur; i++)
414                 if (strcmp(sl->sl_str[i], str) == 0)
415                         return i;
416
417         if (i == PROTOMAX)
418                 errx(1, "Ran out of protocols adding `%s';"
419                     " recompile with larger PROTOMAX", str);
420         if ((p = strdup(str)) == NULL)
421                 err(1, "Cannot copy string");
422         (void)sl_add(sl, p);
423         return i;
424 }
425
426 static const char *
427 getprotostr(StringList *sl, size_t i)
428 {
429         assert(i < sl->sl_cur);
430         return sl->sl_str[i];
431 }
432
433 static const char *
434 mkaliases(StringList *sl, char *buf, size_t len)
435 {
436         size_t nc, i, pos;
437
438         buf[0] = 0;
439         for (i = 1, pos = 0; i < sl->sl_cur; i++) {
440                 nc = strlcpy(buf + pos, sl->sl_str[i], len);
441                 if (nc >= len)
442                         goto out;
443                 pos += nc;
444                 len -= nc;
445                 nc = strlcpy(buf + pos, " ", len);
446                 if (nc >= len)
447                         goto out;
448                 pos += nc;
449                 len -= nc;
450         }
451         return buf;
452 out:
453         warn("aliases for `%s' truncated", sl->sl_str[0]);
454         return buf;
455 }
456
457 static void
458 usage(void)
459 {
460         (void)fprintf(stderr,
461             "Usage:\t%s [-b | -l] [-q] [-o <db>] [<servicefile>]\n"
462             "\t%s -u [<servicefile>]\n", getprogname(), getprogname());
463         exit(1);
464 }