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 *
find_dhcp_confopt(dhcp_confopt_t * ddp,const char * key)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
add_dsvc_conf(dhcp_confopt_t ** ddpp,const char * key,const char * value)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
read_dsvc_conf(dhcp_confopt_t ** ddpp)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
replace_dsvc_conf(dhcp_confopt_t ** ddpp,const char * key,const char * value)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
write_dsvc_conf(dhcp_confopt_t * ddp,mode_t mode)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
free_dsvc_conf(dhcp_confopt_t * ddp)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
delete_dsvc_conf(void)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
query_dsvc_conf(dhcp_confopt_t * ddp,const char * key,char ** value)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
confopt_to_datastore(dhcp_confopt_t * ddp,dsvc_datastore_t * dsp)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 }
416