1 1.19 christos /* $NetBSD: services_mkdb.c,v 1.19 2017/01/10 21:04:06 christos Exp $ */ 2 1.1 lukem 3 1.1 lukem /*- 4 1.1 lukem * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 1.1 lukem * All rights reserved. 6 1.1 lukem * 7 1.1 lukem * This code is derived from software contributed to The NetBSD Foundation 8 1.5 christos * by Luke Mewburn and Christos Zoulas. 9 1.1 lukem * 10 1.1 lukem * Redistribution and use in source and binary forms, with or without 11 1.1 lukem * modification, are permitted provided that the following conditions 12 1.1 lukem * are met: 13 1.1 lukem * 1. Redistributions of source code must retain the above copyright 14 1.1 lukem * notice, this list of conditions and the following disclaimer. 15 1.1 lukem * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 lukem * notice, this list of conditions and the following disclaimer in the 17 1.1 lukem * documentation and/or other materials provided with the distribution. 18 1.1 lukem * 19 1.1 lukem * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 lukem * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 lukem * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 lukem * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 lukem * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 lukem * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 lukem * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 lukem * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 lukem * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 lukem * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 lukem * POSSIBILITY OF SUCH DAMAGE. 30 1.1 lukem */ 31 1.1 lukem 32 1.1 lukem #include <sys/cdefs.h> 33 1.1 lukem #ifndef lint 34 1.19 christos __RCSID("$NetBSD: services_mkdb.c,v 1.19 2017/01/10 21:04:06 christos Exp $"); 35 1.1 lukem #endif /* not lint */ 36 1.1 lukem 37 1.1 lukem #include <sys/param.h> 38 1.19 christos #include <sys/stat.h> 39 1.1 lukem 40 1.9 christos #include <assert.h> 41 1.1 lukem #include <err.h> 42 1.1 lukem #include <fcntl.h> 43 1.1 lukem #include <netdb.h> 44 1.1 lukem #include <stdio.h> 45 1.1 lukem #include <stdlib.h> 46 1.1 lukem #include <string.h> 47 1.1 lukem #include <unistd.h> 48 1.1 lukem #include <util.h> 49 1.3 christos #include <ctype.h> 50 1.9 christos #include <errno.h> 51 1.3 christos #include <stringlist.h> 52 1.1 lukem 53 1.15 joerg #include "extern.h" 54 1.15 joerg 55 1.3 christos static char tname[MAXPATHLEN]; 56 1.3 christos 57 1.9 christos #define PMASK 0xffff 58 1.18 christos #define PROTOMAX 6 59 1.9 christos 60 1.9 christos static StringList ***parseservices(const char *, StringList *); 61 1.3 christos static void cleanup(void); 62 1.5 christos static char *getstring(const char *, size_t, char **, const char *); 63 1.9 christos static size_t getprotoindex(StringList *, const char *); 64 1.9 christos static const char *getprotostr(StringList *, size_t); 65 1.12 perry static void usage(void) __dead; 66 1.1 lukem 67 1.1 lukem int 68 1.3 christos main(int argc, char *argv[]) 69 1.1 lukem { 70 1.3 christos int ch; 71 1.3 christos const char *fname = _PATH_SERVICES; 72 1.15 joerg const char *dbname = NULL; 73 1.15 joerg int use_db = 0; 74 1.16 joerg int warndup = 0; 75 1.10 christos int unique = 0; 76 1.11 christos int otherflag = 0; 77 1.9 christos size_t cnt = 0; 78 1.9 christos StringList *sl, ***svc; 79 1.9 christos size_t port, proto; 80 1.15 joerg void (*addfn)(StringList *, size_t, const char *, size_t *, int); 81 1.15 joerg int (*closefn)(void); 82 1.3 christos 83 1.3 christos setprogname(argv[0]); 84 1.1 lukem 85 1.17 wiz while ((ch = getopt(argc, argv, "o:quV:v")) != -1) 86 1.1 lukem switch (ch) { 87 1.17 wiz case 'o': 88 1.17 wiz otherflag = 1; 89 1.17 wiz dbname = optarg; 90 1.17 wiz break; 91 1.6 christos case 'q': 92 1.11 christos otherflag = 1; 93 1.6 christos warndup = 0; 94 1.6 christos break; 95 1.10 christos case 'u': 96 1.10 christos unique++; 97 1.10 christos break; 98 1.15 joerg case 'V': 99 1.15 joerg if (strcmp(optarg, "db") == 0) 100 1.15 joerg use_db = 1; 101 1.15 joerg else if (strcmp(optarg, "cdb") == 0) 102 1.15 joerg use_db = 0; 103 1.15 joerg else 104 1.15 joerg usage(); 105 1.15 joerg break; 106 1.17 wiz case 'v': 107 1.17 wiz otherflag = 1; 108 1.17 wiz warndup = 1; 109 1.17 wiz break; 110 1.1 lukem default: 111 1.1 lukem usage(); 112 1.1 lukem } 113 1.1 lukem 114 1.1 lukem argc -= optind; 115 1.1 lukem argv += optind; 116 1.1 lukem 117 1.11 christos if (argc > 1 || (unique && otherflag)) 118 1.3 christos usage(); 119 1.1 lukem if (argc == 1) 120 1.1 lukem fname = argv[0]; 121 1.3 christos 122 1.10 christos if (unique) 123 1.10 christos uniq(fname); 124 1.10 christos 125 1.15 joerg if (dbname == NULL) 126 1.15 joerg dbname = use_db ? _PATH_SERVICES_DB : _PATH_SERVICES_CDB; 127 1.15 joerg 128 1.9 christos svc = parseservices(fname, sl = sl_init()); 129 1.3 christos 130 1.1 lukem if (atexit(cleanup)) 131 1.1 lukem err(1, "Cannot install exit handler"); 132 1.3 christos 133 1.3 christos (void)snprintf(tname, sizeof(tname), "%s.tmp", dbname); 134 1.1 lukem 135 1.15 joerg if (use_db) { 136 1.15 joerg if (db_open(tname)) 137 1.15 joerg err(1, "Error opening temporary database `%s'", tname); 138 1.15 joerg addfn = db_add; 139 1.15 joerg closefn = db_close; 140 1.15 joerg } else { 141 1.15 joerg if (cdb_open(tname)) 142 1.15 joerg err(1, "Error opening temporary database `%s'", tname); 143 1.15 joerg addfn = cdb_add; 144 1.15 joerg closefn = cdb_close; 145 1.15 joerg } 146 1.9 christos 147 1.9 christos for (port = 0; port < PMASK + 1; port++) { 148 1.9 christos if (svc[port] == NULL) 149 1.9 christos continue; 150 1.9 christos 151 1.9 christos for (proto = 0; proto < PROTOMAX; proto++) { 152 1.9 christos StringList *s; 153 1.9 christos if ((s = svc[port][proto]) == NULL) 154 1.9 christos continue; 155 1.15 joerg (addfn)(s, port, getprotostr(sl, proto), &cnt, warndup); 156 1.9 christos } 157 1.9 christos 158 1.9 christos free(svc[port]); 159 1.9 christos } 160 1.9 christos 161 1.9 christos free(svc); 162 1.9 christos sl_free(sl, 1); 163 1.9 christos 164 1.15 joerg if ((closefn)()) 165 1.15 joerg err(1, "Error writing temporary database `%s'", tname); 166 1.9 christos 167 1.9 christos if (rename(tname, dbname) == -1) 168 1.9 christos err(1, "Cannot rename `%s' to `%s'", tname, dbname); 169 1.9 christos 170 1.9 christos return 0; 171 1.9 christos } 172 1.9 christos 173 1.9 christos static StringList *** 174 1.9 christos parseservices(const char *fname, StringList *sl) 175 1.9 christos { 176 1.9 christos size_t len, line, pindex; 177 1.9 christos FILE *fp; 178 1.9 christos StringList ***svc, *s; 179 1.9 christos char *p, *ep; 180 1.9 christos 181 1.9 christos if ((fp = fopen(fname, "r")) == NULL) 182 1.9 christos err(1, "Cannot open `%s'", fname); 183 1.9 christos 184 1.9 christos line = 0; 185 1.9 christos svc = ecalloc(PMASK + 1, sizeof(StringList **)); 186 1.9 christos 187 1.3 christos /* XXX: change NULL to "\0\0#" when fparseln fixed */ 188 1.1 lukem for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) { 189 1.3 christos char *name, *port, *proto, *aliases, *cp, *alias; 190 1.9 christos unsigned long pnum; 191 1.1 lukem 192 1.1 lukem if (len == 0) 193 1.1 lukem continue; 194 1.9 christos 195 1.8 christos for (cp = p; *cp && isspace((unsigned char)*cp); cp++) 196 1.8 christos continue; 197 1.9 christos 198 1.8 christos if (*cp == '\0' || *cp == '#') 199 1.8 christos continue; 200 1.5 christos 201 1.5 christos if ((name = getstring(fname, line, &cp, "name")) == NULL) 202 1.3 christos continue; 203 1.5 christos 204 1.5 christos if ((port = getstring(fname, line, &cp, "port")) == NULL) 205 1.1 lukem continue; 206 1.5 christos 207 1.9 christos if (cp) { 208 1.9 christos for (aliases = cp; *cp && *cp != '#'; cp++) 209 1.9 christos continue; 210 1.9 christos 211 1.9 christos if (*cp) 212 1.9 christos *cp = '\0'; 213 1.9 christos } else 214 1.9 christos aliases = NULL; 215 1.9 christos 216 1.1 lukem proto = strchr(port, '/'); 217 1.1 lukem if (proto == NULL || proto[1] == '\0') { 218 1.3 christos warnx("%s, %zu: no protocol found", fname, line); 219 1.1 lukem continue; 220 1.1 lukem } 221 1.1 lukem *proto++ = '\0'; 222 1.5 christos 223 1.9 christos errno = 0; 224 1.9 christos pnum = strtoul(port, &ep, 0); 225 1.9 christos if (*port == '\0' || *ep != '\0') { 226 1.9 christos warnx("%s, %zu: invalid port `%s'", fname, line, port); 227 1.9 christos continue; 228 1.9 christos } 229 1.9 christos if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) { 230 1.9 christos warnx("%s, %zu: port too big `%s'", fname, line, port); 231 1.3 christos continue; 232 1.9 christos } 233 1.3 christos 234 1.9 christos if (svc[pnum] == NULL) 235 1.9 christos svc[pnum] = ecalloc(PROTOMAX, sizeof(StringList *)); 236 1.1 lukem 237 1.9 christos pindex = getprotoindex(sl, proto); 238 1.9 christos if (svc[pnum][pindex] == NULL) 239 1.9 christos s = svc[pnum][pindex] = sl_init(); 240 1.9 christos else 241 1.9 christos s = svc[pnum][pindex]; 242 1.15 joerg 243 1.15 joerg if (strlen(name) > 255) { 244 1.15 joerg warnx("%s, %zu: invalid name too long `%s'", fname, 245 1.15 joerg line, name); 246 1.15 joerg continue; 247 1.15 joerg } 248 1.15 joerg 249 1.3 christos /* build list of aliases */ 250 1.9 christos if (sl_find(s, name) == NULL) 251 1.9 christos (void)sl_add(s, estrdup(name)); 252 1.1 lukem 253 1.9 christos if (aliases) { 254 1.9 christos while ((alias = strsep(&aliases, " \t")) != NULL) { 255 1.9 christos if (alias[0] == '\0') 256 1.9 christos continue; 257 1.15 joerg if (strlen(alias) > 255) { 258 1.15 joerg warnx("%s, %zu: alias name too long `%s'", 259 1.15 joerg fname, line, alias); 260 1.15 joerg continue; 261 1.15 joerg } 262 1.9 christos if (sl_find(s, alias) == NULL) 263 1.9 christos (void)sl_add(s, estrdup(alias)); 264 1.9 christos } 265 1.1 lukem } 266 1.1 lukem } 267 1.9 christos (void)fclose(fp); 268 1.9 christos return svc; 269 1.1 lukem } 270 1.1 lukem 271 1.1 lukem /* 272 1.1 lukem * cleanup(): Remove temporary files upon exit 273 1.1 lukem */ 274 1.3 christos static void 275 1.3 christos cleanup(void) 276 1.1 lukem { 277 1.3 christos if (tname[0]) 278 1.3 christos (void)unlink(tname); 279 1.1 lukem } 280 1.1 lukem 281 1.5 christos static char * 282 1.5 christos getstring(const char *fname, size_t line, char **cp, const char *tag) 283 1.5 christos { 284 1.5 christos char *str; 285 1.5 christos 286 1.5 christos while ((str = strsep(cp, " \t")) != NULL && *str == '\0') 287 1.5 christos continue; 288 1.5 christos 289 1.5 christos if (str == NULL) 290 1.5 christos warnx("%s, %zu: no %s found", fname, line, tag); 291 1.5 christos 292 1.5 christos return str; 293 1.5 christos } 294 1.5 christos 295 1.9 christos static size_t 296 1.9 christos getprotoindex(StringList *sl, const char *str) 297 1.9 christos { 298 1.9 christos size_t i; 299 1.9 christos 300 1.9 christos for (i= 0; i < sl->sl_cur; i++) 301 1.9 christos if (strcmp(sl->sl_str[i], str) == 0) 302 1.9 christos return i; 303 1.9 christos 304 1.9 christos if (i == PROTOMAX) 305 1.9 christos errx(1, "Ran out of protocols adding `%s';" 306 1.9 christos " recompile with larger PROTOMAX", str); 307 1.9 christos (void)sl_add(sl, estrdup(str)); 308 1.9 christos return i; 309 1.9 christos } 310 1.9 christos 311 1.9 christos static const char * 312 1.9 christos getprotostr(StringList *sl, size_t i) 313 1.9 christos { 314 1.9 christos assert(i < sl->sl_cur); 315 1.9 christos return sl->sl_str[i]; 316 1.9 christos } 317 1.9 christos 318 1.4 christos static void 319 1.3 christos usage(void) 320 1.1 lukem { 321 1.15 joerg (void)fprintf(stderr, "Usage:\t%s [-q] [-o <db>] [-V cdb|db] [<servicefile>]\n" 322 1.10 christos "\t%s -u [<servicefile>]\n", getprogname(), getprogname()); 323 1.1 lukem exit(1); 324 1.1 lukem } 325