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