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