xref: /onnv-gate/usr/src/lib/libdhcpsvc/modules/files/dhcptab.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 file contains public API functions for managing the dhcptab
31  * container.  For the semantics of these functions, please see the
32  * Enterprise DHCP Architecture Document.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <unistd.h>
38 #include <netinet/in.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <alloca.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <dhcp_svc_public.h>
47 
48 #include "dhcptab.h"
49 #include "util.h"
50 
51 static void dt2path(char *, size_t, const char *, const char *);
52 static int write_rec(int, dt_rec_t *, off_t);
53 
54 int
open_dt(void ** handlep,const char * location,uint_t flags)55 open_dt(void **handlep, const char *location, uint_t flags)
56 {
57 	dt_handle_t	*dhp;
58 	unsigned int	conver;
59 	int		nelems;
60 	int		retval;
61 	char		nl;
62 	int		fd;
63 	char		dtpath[MAXPATHLEN];
64 	FILE		*fp;
65 
66 	dhp = malloc(sizeof (dt_handle_t));
67 	if (dhp == NULL)
68 		return (DSVC_NO_MEMORY);
69 
70 	dhp->dh_oflags = flags;
71 	(void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
72 
73 	dt2path(dtpath, MAXPATHLEN, location, "");
74 	retval = open_file(dtpath, flags, &fd);
75 	if (retval != DSVC_SUCCESS) {
76 		free(dhp);
77 		return (retval);
78 	}
79 
80 	fp = fdopen(fd, flags & DSVC_WRITE ? "r+" : "r");
81 	if (fp == NULL) {
82 		(void) close(fd);
83 		free(dhp);
84 		return (DSVC_INTERNAL);
85 	}
86 
87 	if (flags & DSVC_CREATE) {
88 		/*
89 		 * We just created the per-network container; put the
90 		 * header on for future use...
91 		 */
92 		retval = fprintf(fp, "# SUNWfiles%u_dhcptab\n", DSVC_CONVER);
93 		if (retval < 0 || fflush(fp) == EOF) {
94 			(void) fclose(fp);
95 			(void) free(dhp);
96 			return (DSVC_INTERNAL);
97 		}
98 
99 		(void) fprintf(fp, "#\n# Do NOT edit this file by hand -- use");
100 		(void) fprintf(fp, " dhtadm(1M) or dhcpmgr(1M) instead\n#\n");
101 	} else {
102 		/*
103 		 * Container already exists; sanity check against the
104 		 * header that's on-disk.
105 		 */
106 		nelems = fscanf(fp, "#%*1[ ]SUNWfiles%u_dhcptab%c", &conver,
107 		    &nl);
108 		if (nelems != 2 || conver != DSVC_CONVER || nl != '\n') {
109 			(void) fclose(fp);
110 			free(dhp);
111 			return (DSVC_INTERNAL);
112 		}
113 	}
114 
115 	(void) fclose(fp);
116 	*handlep = dhp;
117 	return (DSVC_SUCCESS);
118 }
119 
120 int
close_dt(void ** handlep)121 close_dt(void **handlep)
122 {
123 	free(*handlep);
124 	return (DSVC_SUCCESS);
125 }
126 
127 int
remove_dt(const char * location)128 remove_dt(const char *location)
129 {
130 	char dtpath[MAXPATHLEN];
131 
132 	dt2path(dtpath, MAXPATHLEN, location, "");
133 	if (unlink(dtpath) == -1)
134 		return (syserr_to_dsvcerr(errno));
135 
136 	return (DSVC_SUCCESS);
137 }
138 
139 /*
140  * Internal version of lookup_dt() used by both lookup_dt() and
141  * update_dt(); same semantics as lookup_dt() except that the `partial'
142  * argument has been generalized into a `flags' field and the handle has
143  * been turned into a FILE pointer.
144  */
145 static int
find_dt(FILE * fp,uint_t flags,uint_t query,int count,const dt_rec_t * targetp,dt_rec_list_t ** recordsp,uint_t * nrecordsp)146 find_dt(FILE *fp, uint_t flags, uint_t query, int count,
147     const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
148 {
149 	int		retval = DSVC_SUCCESS;
150 	char 		*buf = NULL, *fields[DTF_FIELDS];
151 	uint_t		nrecords;
152 	dt_rec_t	dt, *recordp;
153 	dt_rec_list_t	*records, *new_records;
154 	off_t		recoff;
155 	unsigned int	nfields;
156 
157 	if (fseek(fp, 0, SEEK_SET) == -1)
158 		return (DSVC_INTERNAL);
159 
160 	records = NULL;
161 	for (nrecords = 0; count < 0 || nrecords < count; ) {
162 		free(buf);
163 
164 		if (flags & FIND_POSITION)
165 			recoff = ftello(fp);
166 
167 		buf = read_entry(fp);
168 		if (buf == NULL) {
169 			if (!feof(fp))
170 				retval = DSVC_NO_MEMORY;
171 			break;
172 		}
173 
174 		/*
175 		 * Skip pure comment lines; for now this just skips the
176 		 * header information at the top of the container.
177 		 */
178 		if (buf[0] == DTF_COMMENT_CHAR)
179 			continue;
180 
181 		/*
182 		 * Split the buffer up into DTF_FIELDS fields.
183 		 */
184 		nfields = field_split(buf, DTF_FIELDS, fields, "|");
185 		if (nfields < DTF_FIELDS)
186 			continue;
187 
188 		/*
189 		 * See if we've got a match.  If so, allocate the new
190 		 * record, fill it in, and continue.
191 		 */
192 		dt.dt_type = fields[DTF_TYPE][0];
193 		if (DSVC_QISEQ(query, DT_QTYPE) &&
194 		    targetp->dt_type != dt.dt_type)
195 			continue;
196 		else if (DSVC_QISNEQ(query, DT_QTYPE) &&
197 		    targetp->dt_type == dt.dt_type)
198 			continue;
199 
200 		unescape('|', fields[DTF_KEY], dt.dt_key, sizeof (dt.dt_key));
201 		if (DSVC_QISEQ(query, DT_QKEY) &&
202 		    strcmp(targetp->dt_key, dt.dt_key) != 0)
203 			continue;
204 		else if (DSVC_QISNEQ(query, DT_QKEY) &&
205 		    strcmp(targetp->dt_key, dt.dt_key) == 0)
206 			continue;
207 
208 		/*
209 		 * Caller just wants a count of the number of matching
210 		 * records, not the records themselves; continue.
211 		 */
212 		if (recordsp == NULL) {
213 			nrecords++;
214 			continue;
215 		}
216 
217 		dt.dt_sig = atoll(fields[DTF_SIG]);
218 		dt.dt_value = strdup(fields[DTF_VALUE]);
219 		if (dt.dt_value == NULL) {
220 			if ((flags & FIND_PARTIAL) == 0)
221 				retval = DSVC_NO_MEMORY;
222 			break;
223 		}
224 
225 		/*
226 		 * Allocate record; if FIND_POSITION flag is set, then
227 		 * we need to allocate an extended (dt_recpos_t) record.
228 		 */
229 		if (flags & FIND_POSITION)
230 			recordp = malloc(sizeof (dt_recpos_t));
231 		else
232 			recordp = malloc(sizeof (dt_rec_t));
233 
234 		if (recordp == NULL) {
235 			free(dt.dt_value);
236 			if ((flags & FIND_PARTIAL) == 0)
237 				retval = DSVC_NO_MEMORY;
238 			break;
239 		}
240 
241 		/*
242 		 * Fill in record; do a structure copy from our automatic
243 		 * dt.  If FIND_POSITION flag is on, pass back additional
244 		 * location information.
245 		 */
246 		*recordp = dt;
247 		if (flags & FIND_POSITION) {
248 			((dt_recpos_t *)recordp)->dtp_off = recoff;
249 			((dt_recpos_t *)recordp)->dtp_size = ftello(fp) -
250 			    recoff;
251 		}
252 
253 		/*
254 		 * Chuck the record on the list; up the counter.
255 		 */
256 		new_records = add_dtrec_to_list(recordp, records);
257 		if (new_records == NULL) {
258 			free_dtrec(recordp);
259 			if ((flags & FIND_PARTIAL) == 0)
260 				retval = DSVC_NO_MEMORY;
261 			break;
262 		}
263 		records = new_records;
264 		nrecords++;
265 	}
266 
267 	free(buf);
268 
269 	if (retval == DSVC_SUCCESS) {
270 		*nrecordsp = nrecords;
271 		if (recordsp != NULL)
272 			*recordsp = records;
273 		return (DSVC_SUCCESS);
274 	}
275 
276 	if (records != NULL)
277 		free_dtrec_list(records);
278 
279 	return (retval);
280 }
281 
282 int
lookup_dt(void * handle,boolean_t partial,uint_t query,int count,const dt_rec_t * targetp,dt_rec_list_t ** recordsp,uint_t * nrecordsp)283 lookup_dt(void *handle, boolean_t partial, uint_t query, int count,
284     const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
285 {
286 	int		retval;
287 	char		dtpath[MAXPATHLEN];
288 	FILE		*fp;
289 	dt_handle_t	*dhp = (dt_handle_t *)handle;
290 
291 	if ((dhp->dh_oflags & DSVC_READ) == 0)
292 		return (DSVC_ACCESS);
293 
294 	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
295 	fp = fopen(dtpath, "r");
296 	if (fp == NULL)
297 		return (syserr_to_dsvcerr(errno));
298 
299 	retval = find_dt(fp, partial ? FIND_PARTIAL : 0, query, count, targetp,
300 	    recordsp, nrecordsp);
301 
302 	(void) fclose(fp);
303 	return (retval);
304 }
305 
306 /*
307  * Internal dhcptab record update routine, used to factor out the
308  * common code between add_dt(), delete_dt(), and modify_dt().  If
309  * `origp' is NULL, then act like add_dt(); if `newp' is NULL, then
310  * act like delete_dt(); otherwise act like modify_dt().
311  */
312 static int
update_dt(const dt_handle_t * dhp,const dt_rec_t * origp,dt_rec_t * newp)313 update_dt(const dt_handle_t *dhp, const dt_rec_t *origp, dt_rec_t *newp)
314 {
315 	char		dtpath[MAXPATHLEN], newpath[MAXPATHLEN];
316 	int		retval = DSVC_SUCCESS;
317 	off_t		recoff, recnext;
318 	dt_rec_list_t	*reclist;
319 	FILE		*fp;
320 	int		newfd;
321 	uint_t		found;
322 	int		query;
323 	struct stat	st;
324 
325 	if ((dhp->dh_oflags & DSVC_WRITE) == 0)
326 		return (DSVC_ACCESS);
327 
328 	/*
329 	 * Open the container to update and a new container file which we
330 	 * will store the updated version of the container in.  When the
331 	 * update is done, rename the new file to be the real container.
332 	 */
333 	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
334 	fp = fopen(dtpath, "r");
335 	if (fp == NULL)
336 		return (syserr_to_dsvcerr(errno));
337 
338 	dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new");
339 	(void) unlink(newpath);
340 	newfd = open(newpath, O_CREAT|O_EXCL|O_WRONLY, 0644);
341 	if (newfd == -1) {
342 		(void) fclose(fp);
343 		return (syserr_to_dsvcerr(errno));
344 	}
345 
346 	DSVC_QINIT(query);
347 	DSVC_QEQ(query, DT_QKEY|DT_QTYPE);
348 
349 	/*
350 	 * If we're changing the key for this record, make sure the key
351 	 * we're changing to doesn't already exist.
352 	 */
353 	if (origp != NULL && newp != NULL) {
354 		if ((origp->dt_type != newp->dt_type ||
355 		    strcmp(origp->dt_key, newp->dt_key) != 0)) {
356 			retval = find_dt(fp, 0, query, 1, newp, NULL, &found);
357 			if (retval != DSVC_SUCCESS)
358 				goto out;
359 			if (found != 0) {
360 				retval = DSVC_EXISTS;
361 				goto out;
362 			}
363 		}
364 	}
365 
366 	/*
367 	 * If we're adding a new record, make sure the record doesn't
368 	 * already exist.
369 	 */
370 	if (newp != NULL && origp == NULL) {
371 		retval = find_dt(fp, 0, query, 1, newp, NULL, &found);
372 		if (retval != DSVC_SUCCESS)
373 			goto out;
374 		if (found != 0) {
375 			retval = DSVC_EXISTS;
376 			goto out;
377 		}
378 	}
379 
380 	/*
381 	 * If we're deleting or modifying record, make sure the record
382 	 * still exists and that our copy isn't stale.  Note that we don't
383 	 * check signatures if we're deleting the record and origp->dt_sig
384 	 * is zero, so that records can be deleted that weren't looked up
385 	 * first.
386 	 */
387 	if (origp != NULL) {
388 		retval = find_dt(fp, FIND_POSITION, query, 1, origp, &reclist,
389 		    &found);
390 		if (retval != DSVC_SUCCESS)
391 			goto out;
392 		if (found == 0) {
393 			retval = DSVC_NOENT;
394 			goto out;
395 		}
396 
397 		if (reclist->dtl_rec->dt_sig != origp->dt_sig) {
398 			if (newp != NULL || origp->dt_sig != 0) {
399 				free_dtrec_list(reclist);
400 				retval = DSVC_COLLISION;
401 				goto out;
402 			}
403 		}
404 
405 		/*
406 		 * Note the offset of the record we're modifying or deleting
407 		 * for use down below.
408 		 */
409 		recoff  = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off;
410 		recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size;
411 
412 		free_dtrec_list(reclist);
413 	} else {
414 		/*
415 		 * No record to modify or delete, so set `recoff' and
416 		 * `recnext' appropriately.
417 		 */
418 		recoff = 0;
419 		recnext = 0;
420 	}
421 
422 	/*
423 	 * Make a new copy of the container.  If we're deleting or
424 	 * modifying a record, don't copy that record to the new container.
425 	 */
426 	if (fstat(fileno(fp), &st) == -1) {
427 		retval = DSVC_INTERNAL;
428 		goto out;
429 	}
430 
431 	retval = copy_range(fileno(fp), 0, newfd, 0, recoff);
432 	if (retval != DSVC_SUCCESS)
433 		goto out;
434 
435 	retval = copy_range(fileno(fp), recnext, newfd, recoff,
436 	    st.st_size - recnext);
437 	if (retval != DSVC_SUCCESS)
438 		goto out;
439 
440 	/*
441 	 * If there's a new record, append it to the new container.
442 	 */
443 	if (newp != NULL) {
444 		if (origp == NULL)
445 			newp->dt_sig = gensig();
446 		else
447 			newp->dt_sig = origp->dt_sig + 1;
448 
449 		if (fstat(newfd, &st) == -1) {
450 			retval = DSVC_INTERNAL;
451 			goto out;
452 		}
453 
454 		retval = write_rec(newfd, newp, st.st_size);
455 		if (retval != DSVC_SUCCESS)
456 			goto out;
457 	}
458 
459 	/*
460 	 * Note: we close these descriptors before the rename(2) (rather
461 	 * than just having the `out:' label clean them up) to save NFS
462 	 * some work (otherwise, NFS has to save `dnpath' to an alternate
463 	 * name since its vnode would still be active).
464 	 */
465 	(void) fclose(fp);
466 	(void) close(newfd);
467 
468 	if (rename(newpath, dtpath) == -1)
469 		retval = syserr_to_dsvcerr(errno);
470 
471 	return (retval);
472 out:
473 	(void) fclose(fp);
474 	(void) close(newfd);
475 	(void) unlink(newpath);
476 	return (retval);
477 }
478 
479 int
delete_dt(void * handle,const dt_rec_t * delp)480 delete_dt(void *handle, const dt_rec_t *delp)
481 {
482 	return (update_dt((dt_handle_t *)handle, delp, NULL));
483 }
484 
485 int
add_dt(void * handle,dt_rec_t * addp)486 add_dt(void *handle, dt_rec_t *addp)
487 {
488 	return (update_dt((dt_handle_t *)handle, NULL, addp));
489 }
490 
491 int
modify_dt(void * handle,const dt_rec_t * origp,dt_rec_t * newp)492 modify_dt(void *handle, const dt_rec_t *origp, dt_rec_t *newp)
493 {
494 	return (update_dt((dt_handle_t *)handle, origp, newp));
495 }
496 
497 int
list_dt(const char * location,char *** listppp,uint_t * countp)498 list_dt(const char *location, char ***listppp, uint_t *countp)
499 {
500 	char	dtpath[MAXPATHLEN];
501 	char	**listpp;
502 
503 	if (access(location, F_OK|R_OK) == -1) {
504 		switch (errno) {
505 		case EACCES:
506 		case EPERM:
507 			return (DSVC_ACCESS);
508 		case ENOENT:
509 			return (DSVC_NO_LOCATION);
510 		default:
511 			break;
512 		}
513 		return (DSVC_INTERNAL);
514 	}
515 
516 	dt2path(dtpath, MAXPATHLEN, location, "");
517 	if (access(dtpath, F_OK|R_OK) == -1) {
518 		*countp = 0;
519 		*listppp = NULL;
520 		return (DSVC_SUCCESS);
521 	}
522 
523 	listpp = malloc(sizeof (char **));
524 	if (listpp == NULL)
525 		return (DSVC_NO_MEMORY);
526 	listpp[0] = strdup(DT_DHCPTAB);
527 	if (listpp[0] == NULL) {
528 		free(listpp);
529 		return (DSVC_NO_MEMORY);
530 	}
531 
532 	*listppp = listpp;
533 	*countp = 1;
534 	return (DSVC_SUCCESS);
535 }
536 
537 /*
538  * Given a buffer `path' of `pathlen' bytes, fill it in with a path to the
539  * dhcptab in directory `dir' with a suffix of `suffix'.
540  */
541 static void
dt2path(char * path,size_t pathlen,const char * dir,const char * suffix)542 dt2path(char *path, size_t pathlen, const char *dir, const char *suffix)
543 {
544 	(void) snprintf(path, pathlen, "%s/SUNWfiles%u_%s%s", dir,
545 	    DSVC_CONVER, DT_DHCPTAB, suffix);
546 }
547 
548 /*
549  * Write the dt_rec_t pointed to by `recp' into the open container `fd' at
550  * offset `recoff'.  Returns DSVC_* error code.
551  */
552 static int
write_rec(int fd,dt_rec_t * recp,off_t recoff)553 write_rec(int fd, dt_rec_t *recp, off_t recoff)
554 {
555 	char	escaped_key[DSVC_MAX_MACSYM_LEN * 2 + 1];
556 	char	entbuf[1024], *ent = entbuf;
557 	size_t	entsize = sizeof (entbuf);
558 	int	entlen;
559 
560 	escape('|', recp->dt_key, escaped_key, sizeof (escaped_key));
561 again:
562 	entlen = snprintf(ent, entsize, "%s|%c|%llu|%s\n", escaped_key,
563 	    recp->dt_type, recp->dt_sig, recp->dt_value);
564 	if (entlen == -1)
565 		return (syserr_to_dsvcerr(errno));
566 
567 	if (entlen > entsize) {
568 		entsize = entlen;
569 		ent = alloca(entlen);
570 		goto again;
571 	}
572 
573 	if (pnwrite(fd, ent, entlen, recoff) == -1)
574 		return (syserr_to_dsvcerr(errno));
575 
576 	return (DSVC_SUCCESS);
577 }
578