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