1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This module contains the functions implementing the interface to the 31 * /etc/inet/dhcpsvc.conf DHCP service configuration file. 32 */ 33 34 #include <thread.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <sys/types.h> 39 #include <alloca.h> 40 #include <string.h> 41 #include <sys/stat.h> 42 #include <fcntl.h> 43 #include <errno.h> 44 #include <dhcp_svc_confkey.h> 45 #include <dhcp_svc_confopt.h> 46 #include <dhcp_svc_private.h> 47 48 /* 49 * Finds the parameter called key, and returns a reference to it. Returns 50 * NULL if not found or an error occurred. 51 */ 52 static dhcp_confopt_t * 53 find_dhcp_confopt(dhcp_confopt_t *ddp, const char *key) 54 { 55 unsigned int i; 56 57 if (ddp == NULL || key == NULL) 58 return (NULL); 59 60 for (i = 0; ddp[i].co_type != DHCP_END; i++) { 61 if (ddp[i].co_type == DHCP_KEY && 62 strcasecmp(ddp[i].co_key, key) == 0) 63 return (&ddp[i]); 64 } 65 return (NULL); 66 } 67 68 /* 69 * Adds a dhcp_confopt_t to the ddpp table. If the table is NULL, one is 70 * created. The table is terminated by a NULL entry. The key and value 71 * arguments are copied, not referenced directly. No check is done to see 72 * if the parameter already exists. Returns 0 for success, nonzero 73 * otherwise. 74 */ 75 int 76 add_dsvc_conf(dhcp_confopt_t **ddpp, const char *key, const char *value) 77 { 78 dhcp_confopt_t *ndp, tdp; 79 unsigned int i; 80 81 if (ddpp == NULL || key == NULL || value == NULL) { 82 errno = EINVAL; 83 return (-1); 84 } 85 86 tdp.co_key = strdup(key); 87 tdp.co_type = DHCP_KEY; 88 tdp.co_value = strdup(value); 89 if (tdp.co_key == NULL || tdp.co_value == NULL) { 90 free(tdp.co_key); 91 free(tdp.co_value); 92 errno = ENOMEM; 93 return (-1); 94 } 95 96 for (i = 0; *ddpp && (*ddpp)[i].co_key != NULL; i++) 97 ; 98 99 ndp = realloc(*ddpp, (i + 2) * sizeof (dhcp_confopt_t)); 100 if (ndp == NULL) { 101 free(tdp.co_key); 102 free(tdp.co_value); 103 errno = ENOMEM; 104 return (-1); 105 } 106 107 ndp[i] = tdp; 108 (void) memset(&ndp[i + 1], 0, sizeof (dhcp_confopt_t)); 109 *ddpp = ndp; 110 111 return (0); 112 } 113 114 /* 115 * Reads the contents of the configuration file into a dynamically 116 * allocated array of dhcp_confopt_t records. A zeroed element marks the 117 * end of the array. Blank lines are ignored. Caller is responsible for 118 * freeing ddp. 119 */ 120 int 121 read_dsvc_conf(dhcp_confopt_t **ddpp) 122 { 123 struct stat sb; 124 int dd; 125 int error; 126 unsigned int entry; 127 char *cp, *dp, *eol, *value; 128 dhcp_confopt_t confopt, *tdp, *ddp = NULL; 129 char conf[MAXPATHLEN]; 130 131 if (ddpp == NULL) { 132 errno = EINVAL; 133 return (-1); 134 } 135 136 (void) snprintf(conf, sizeof (conf), "%s" DHCP_CONFOPT_FILE, 137 DHCP_CONFOPT_ROOT); 138 139 if ((dd = open(conf, O_RDONLY)) == -1) 140 return (-1); 141 if (fstat(dd, &sb) == -1) { 142 error = errno; 143 (void) close(dd); 144 errno = error; 145 return (-1); 146 } 147 148 dp = alloca(sb.st_size); 149 if (read(dd, dp, sb.st_size) != sb.st_size) { 150 error = errno; 151 (void) close(dd); 152 errno = error; 153 return (-1); 154 } 155 (void) close(dd); 156 157 for (entry = 0, cp = dp; cp < &dp[sb.st_size]; cp = eol + 1) { 158 eol = strchr(cp, '\n'); 159 if (eol == NULL) /* done parsing file */ 160 break; 161 if (eol == cp) /* blank line -- skip */ 162 continue; 163 *eol = '\0'; 164 165 if (*cp == '#') { 166 confopt.co_type = DHCP_COMMENT; 167 confopt.co_comment = strdup(cp + 1); 168 if (confopt.co_comment == NULL) 169 goto nomem; 170 } else { 171 value = strchr(cp, '='); 172 if (value == NULL) 173 continue; 174 *value = '\0'; 175 176 confopt.co_type = DHCP_KEY; 177 confopt.co_key = strdup(cp); 178 if (confopt.co_key == NULL) 179 goto nomem; 180 181 confopt.co_value = strdup(value + 1); 182 if (confopt.co_value == NULL) { 183 free(confopt.co_key); 184 goto nomem; 185 } 186 } 187 188 /* always allocate a spare slot for the zeroed entry */ 189 tdp = realloc(ddp, (entry + 2) * sizeof (dhcp_confopt_t)); 190 if (tdp == NULL) 191 goto nomem; 192 193 tdp[entry] = confopt; 194 (void) memset(&tdp[entry + 1], 0, sizeof (dhcp_confopt_t)); 195 ddp = tdp; 196 entry++; 197 } 198 199 if (ddp == NULL) 200 return (-1); 201 202 *ddpp = ddp; 203 return (0); 204 205 nomem: 206 if (ddp != NULL) 207 free_dsvc_conf(ddp); 208 209 errno = ENOMEM; 210 return (-1); 211 } 212 213 /* 214 * If the requested parameter exists, replace its value with the new 215 * value. If it doesn't exist, then add the parameter with the new value. 216 * Returns 0 for success, -1 otherwise (errno is set). 217 */ 218 int 219 replace_dsvc_conf(dhcp_confopt_t **ddpp, const char *key, const char *value) 220 { 221 dhcp_confopt_t *tdp; 222 int err; 223 224 if (ddpp == NULL || key == NULL || value == NULL) { 225 errno = EINVAL; 226 return (-1); 227 } 228 if ((tdp = find_dhcp_confopt(*ddpp, key)) != NULL) { 229 char *valp; 230 231 if ((valp = strdup(value)) == NULL) 232 return (-1); /* NOMEM */ 233 234 if (tdp->co_value != NULL) 235 free(tdp->co_value); 236 237 tdp->co_value = valp; 238 239 errno = 0; 240 err = 0; 241 } else 242 err = (add_dsvc_conf(ddpp, key, value) == 0) ? 0 : -1; 243 244 return (err); 245 } 246 247 /* 248 * Writes ddp array to the configuration file. If the configuration file 249 * already exists, its contents are replaced with the contents of the ddp 250 * array. If the configuration file does not exist, it is created using 251 * the identity of the caller (euid/egid) with the permission bits 252 * specified by the mode argument (and modified by the umask). Caller is 253 * responsible for freeing the array. 254 */ 255 int 256 write_dsvc_conf(dhcp_confopt_t *ddp, mode_t mode) 257 { 258 int tdd; 259 ssize_t bytes; 260 size_t i, size; 261 char *tmpbuf; 262 char tmpconf[MAXPATHLEN], conf[MAXPATHLEN]; 263 264 if (ddp == NULL) { 265 errno = EINVAL; 266 return (-1); 267 } 268 269 /* guess at final file size */ 270 for (i = 0, size = 0; ddp[i].co_type != DHCP_END; i++) { 271 if (ddp[i].co_type == DHCP_KEY) { 272 size += strlen(ddp[i].co_key) + 1; /* include = */ 273 size += strlen(ddp[i].co_value) + 1; /* include \n */ 274 } else 275 size += strlen(ddp[i].co_comment) + 2; /* inc # + \n */ 276 } 277 278 if (size == 0) { 279 errno = EINVAL; 280 return (-1); 281 } 282 283 (void) snprintf(conf, sizeof (conf), "%s" DHCP_CONFOPT_FILE, 284 DHCP_CONFOPT_ROOT); 285 (void) snprintf(tmpconf, sizeof (tmpconf), 286 "%s" DHCP_CONFOPT_FILE ".%ld.%u", DHCP_CONFOPT_ROOT, getpid(), 287 thr_self()); 288 289 if ((tdd = open(tmpconf, O_CREAT | O_EXCL | O_WRONLY, mode)) < 0) 290 return (-1); 291 292 tmpbuf = alloca(size); 293 for (i = 0; ddp[i].co_type != DHCP_END; i++) { 294 if (ddp[i].co_type == DHCP_KEY) 295 (void) snprintf(tmpbuf, size, "%s=%s\n", ddp[i].co_key, 296 ddp[i].co_value); 297 else 298 (void) snprintf(tmpbuf, size, "#%s\n", 299 ddp[i].co_comment); 300 301 bytes = write(tdd, tmpbuf, strlen(tmpbuf)); 302 303 /* Nuke the file if we can't successfully update it */ 304 if (bytes != strlen(tmpbuf)) { 305 (void) close(tdd); 306 (void) unlink(tmpconf); 307 return (-1); 308 } 309 } 310 (void) close(tdd); 311 312 /* Move new file into place */ 313 if (rename(tmpconf, conf) < 0) { 314 (void) unlink(tmpconf); 315 return (-1); 316 } 317 318 return (0); 319 } 320 321 /* 322 * Frees the memory associated with the ddp array. 323 */ 324 void 325 free_dsvc_conf(dhcp_confopt_t *ddp) 326 { 327 unsigned int i; 328 329 if (ddp == NULL) 330 return; 331 332 for (i = 0; ddp[i].co_type != DHCP_END; i++) { 333 if (ddp[i].co_type == DHCP_KEY) { 334 free(ddp[i].co_key); 335 free(ddp[i].co_value); 336 } else 337 free(ddp[i].co_comment); 338 } 339 free(ddp); 340 } 341 342 /* 343 * Deletes the configuration file. 344 */ 345 int 346 delete_dsvc_conf(void) 347 { 348 char confpath[MAXPATHLEN]; 349 350 (void) snprintf(confpath, sizeof (confpath), "%s" DHCP_CONFOPT_FILE, 351 DHCP_CONFOPT_ROOT); 352 return (unlink(confpath)); 353 } 354 355 /* 356 * Return a copy of the value portion of the named key. Caller is 357 * responsible for freeing value when they're finished using it. Returns 0 358 * for success, -1 otherwise (errno is set). 359 */ 360 int 361 query_dsvc_conf(dhcp_confopt_t *ddp, const char *key, char **value) 362 { 363 dhcp_confopt_t *tdp; 364 365 if (key == NULL || value == NULL) { 366 errno = EINVAL; 367 return (-1); 368 } 369 if ((tdp = find_dhcp_confopt(ddp, key)) != NULL) { 370 *value = strdup(tdp->co_value); 371 if (*value == NULL) { 372 errno = ENOMEM; 373 return (-1); 374 } 375 errno = 0; 376 return (0); 377 } 378 errno = ENOENT; 379 *value = NULL; 380 return (-1); 381 } 382 383 /* 384 * Given a dhcp_confopt_t structure, fill in a dsvc_datastore_t. 385 * Data is copied from dhcp_confopt_t structure. 386 */ 387 int 388 confopt_to_datastore(dhcp_confopt_t *ddp, dsvc_datastore_t *dsp) 389 { 390 dhcp_confopt_t *tdp; 391 392 if (ddp == NULL || dsp == NULL) 393 return (DSVC_INVAL); 394 395 tdp = find_dhcp_confopt(ddp, DSVC_CK_CONVER); 396 if (tdp == NULL || tdp->co_value == NULL) 397 return (DSVC_BAD_CONVER); 398 dsp->d_conver = atoi(tdp->co_value); 399 400 if (query_dsvc_conf(ddp, DSVC_CK_RESOURCE, &dsp->d_resource) == -1) 401 return (DSVC_BAD_RESOURCE); 402 403 if (query_dsvc_conf(ddp, DSVC_CK_PATH, &dsp->d_location) == -1) { 404 free(dsp->d_resource); 405 return (DSVC_BAD_PATH); 406 } 407 408 /* 409 * RESOURCE_CONFIG is optional - underlying service will complain 410 * if it isn't right. 411 */ 412 (void) query_dsvc_conf(ddp, DSVC_CK_RESOURCE_CONFIG, &dsp->d_config); 413 414 return (DSVC_SUCCESS); 415 }