xref: /onnv-gate/usr/src/lib/libdhcpsvc/private/confopt.c (revision 0:68f95e015346)
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