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