1 /* $NetBSD: services_mkdb.c,v 1.18 2010/10/07 01:28:50 christos 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 #ifndef lint 34 __RCSID("$NetBSD: services_mkdb.c,v 1.18 2010/10/07 01:28:50 christos Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/param.h> 38 39 #include <assert.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 <util.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 6 58 59 static StringList ***parseservices(const char *, StringList *); 60 static void cleanup(void); 61 static char *getstring(const char *, size_t, char **, const char *); 62 static size_t getprotoindex(StringList *, const char *); 63 static const char *getprotostr(StringList *, size_t); 64 static void usage(void) __dead; 65 66 int 67 main(int argc, char *argv[]) 68 { 69 int ch; 70 const char *fname = _PATH_SERVICES; 71 const char *dbname = NULL; 72 int use_db = 0; 73 int warndup = 0; 74 int unique = 0; 75 int otherflag = 0; 76 size_t cnt = 0; 77 StringList *sl, ***svc; 78 size_t port, proto; 79 void (*addfn)(StringList *, size_t, const char *, size_t *, int); 80 int (*closefn)(void); 81 82 setprogname(argv[0]); 83 84 while ((ch = getopt(argc, argv, "o:quV:v")) != -1) 85 switch (ch) { 86 case 'o': 87 otherflag = 1; 88 dbname = optarg; 89 break; 90 case 'q': 91 otherflag = 1; 92 warndup = 0; 93 break; 94 case 'u': 95 unique++; 96 break; 97 case 'V': 98 if (strcmp(optarg, "db") == 0) 99 use_db = 1; 100 else if (strcmp(optarg, "cdb") == 0) 101 use_db = 0; 102 else 103 usage(); 104 break; 105 case 'v': 106 otherflag = 1; 107 warndup = 1; 108 break; 109 default: 110 usage(); 111 } 112 113 argc -= optind; 114 argv += optind; 115 116 if (argc > 1 || (unique && otherflag)) 117 usage(); 118 if (argc == 1) 119 fname = argv[0]; 120 121 if (unique) 122 uniq(fname); 123 124 if (dbname == NULL) 125 dbname = use_db ? _PATH_SERVICES_DB : _PATH_SERVICES_CDB; 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 134 if (use_db) { 135 if (db_open(tname)) 136 err(1, "Error opening temporary database `%s'", tname); 137 addfn = db_add; 138 closefn = db_close; 139 } else { 140 if (cdb_open(tname)) 141 err(1, "Error opening temporary database `%s'", tname); 142 addfn = cdb_add; 143 closefn = cdb_close; 144 } 145 146 for (port = 0; port < PMASK + 1; port++) { 147 if (svc[port] == NULL) 148 continue; 149 150 for (proto = 0; proto < PROTOMAX; proto++) { 151 StringList *s; 152 if ((s = svc[port][proto]) == NULL) 153 continue; 154 (addfn)(s, port, getprotostr(sl, proto), &cnt, warndup); 155 } 156 157 free(svc[port]); 158 } 159 160 free(svc); 161 sl_free(sl, 1); 162 163 if ((closefn)()) 164 err(1, "Error writing temporary database `%s'", tname); 165 166 if (rename(tname, dbname) == -1) 167 err(1, "Cannot rename `%s' to `%s'", tname, dbname); 168 169 return 0; 170 } 171 172 static StringList *** 173 parseservices(const char *fname, StringList *sl) 174 { 175 size_t len, line, pindex; 176 FILE *fp; 177 StringList ***svc, *s; 178 char *p, *ep; 179 180 if ((fp = fopen(fname, "r")) == NULL) 181 err(1, "Cannot open `%s'", fname); 182 183 line = 0; 184 svc = ecalloc(PMASK + 1, sizeof(StringList **)); 185 186 /* XXX: change NULL to "\0\0#" when fparseln fixed */ 187 for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) { 188 char *name, *port, *proto, *aliases, *cp, *alias; 189 unsigned long pnum; 190 191 if (len == 0) 192 continue; 193 194 for (cp = p; *cp && isspace((unsigned char)*cp); cp++) 195 continue; 196 197 if (*cp == '\0' || *cp == '#') 198 continue; 199 200 if ((name = getstring(fname, line, &cp, "name")) == NULL) 201 continue; 202 203 if ((port = getstring(fname, line, &cp, "port")) == NULL) 204 continue; 205 206 if (cp) { 207 for (aliases = cp; *cp && *cp != '#'; cp++) 208 continue; 209 210 if (*cp) 211 *cp = '\0'; 212 } else 213 aliases = NULL; 214 215 proto = strchr(port, '/'); 216 if (proto == NULL || proto[1] == '\0') { 217 warnx("%s, %zu: no protocol found", fname, line); 218 continue; 219 } 220 *proto++ = '\0'; 221 222 errno = 0; 223 pnum = strtoul(port, &ep, 0); 224 if (*port == '\0' || *ep != '\0') { 225 warnx("%s, %zu: invalid port `%s'", fname, line, port); 226 continue; 227 } 228 if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) { 229 warnx("%s, %zu: port too big `%s'", fname, line, port); 230 continue; 231 } 232 233 if (svc[pnum] == NULL) 234 svc[pnum] = ecalloc(PROTOMAX, sizeof(StringList *)); 235 236 pindex = getprotoindex(sl, proto); 237 if (svc[pnum][pindex] == NULL) 238 s = svc[pnum][pindex] = sl_init(); 239 else 240 s = svc[pnum][pindex]; 241 242 if (strlen(name) > 255) { 243 warnx("%s, %zu: invalid name too long `%s'", fname, 244 line, name); 245 continue; 246 } 247 248 /* build list of aliases */ 249 if (sl_find(s, name) == NULL) 250 (void)sl_add(s, estrdup(name)); 251 252 if (aliases) { 253 while ((alias = strsep(&aliases, " \t")) != NULL) { 254 if (alias[0] == '\0') 255 continue; 256 if (strlen(alias) > 255) { 257 warnx("%s, %zu: alias name too long `%s'", 258 fname, line, alias); 259 continue; 260 } 261 if (sl_find(s, alias) == NULL) 262 (void)sl_add(s, estrdup(alias)); 263 } 264 } 265 } 266 (void)fclose(fp); 267 return svc; 268 } 269 270 /* 271 * cleanup(): Remove temporary files upon exit 272 */ 273 static void 274 cleanup(void) 275 { 276 if (tname[0]) 277 (void)unlink(tname); 278 } 279 280 static char * 281 getstring(const char *fname, size_t line, char **cp, const char *tag) 282 { 283 char *str; 284 285 while ((str = strsep(cp, " \t")) != NULL && *str == '\0') 286 continue; 287 288 if (str == NULL) 289 warnx("%s, %zu: no %s found", fname, line, tag); 290 291 return str; 292 } 293 294 static size_t 295 getprotoindex(StringList *sl, const char *str) 296 { 297 size_t i; 298 299 for (i= 0; i < sl->sl_cur; i++) 300 if (strcmp(sl->sl_str[i], str) == 0) 301 return i; 302 303 if (i == PROTOMAX) 304 errx(1, "Ran out of protocols adding `%s';" 305 " recompile with larger PROTOMAX", str); 306 (void)sl_add(sl, estrdup(str)); 307 return i; 308 } 309 310 static const char * 311 getprotostr(StringList *sl, size_t i) 312 { 313 assert(i < sl->sl_cur); 314 return sl->sl_str[i]; 315 } 316 317 static void 318 usage(void) 319 { 320 (void)fprintf(stderr, "Usage:\t%s [-q] [-o <db>] [-V cdb|db] [<servicefile>]\n" 321 "\t%s -u [<servicefile>]\n", getprogname(), getprogname()); 322 exit(1); 323 } 324