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