xref: /onnv-gate/usr/src/lib/libdhcpsvc/modules/util/util.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 (c) 2000-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Internal libdhcpsvc public module utility functions: a collection of
31  * general-purpose routines that are used by assorted public modules.
32  * Someday we should integrate this into the build process a bit more
33  * intelligently.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/mman.h>
38 #include <sys/isa_defs.h>
39 #include <dhcp_svc_public.h>
40 #include <assert.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <sys/sysmacros.h>
46 #include <unistd.h>
47 #include <ctype.h>
48 
49 #include "util.h"
50 
51 /*
52  * Open a file at path `pathname'; depending on the flags passed in through
53  * `dsvc_flags', this file may be optionally created or opened read-only.
54  * On success, DSVC_SUCCESS is returned and `fdp' points to the opened file
55  * descriptor.  On failure, a DSVC_* error code is returned.
56  */
57 int
open_file(const char * pathname,unsigned int dsvc_flags,int * fdp)58 open_file(const char *pathname, unsigned int dsvc_flags, int *fdp)
59 {
60 	int open_flags;
61 
62 	/*
63 	 * Note that we always open with read access, independent of
64 	 * dsvc_flags, because an update operation (add, delete, modify)
65 	 * needs to lookup records to detect collisions.
66 	 */
67 	open_flags = O_RDONLY;
68 	if (dsvc_flags & DSVC_WRITE)
69 		open_flags = O_RDWR;
70 	if (dsvc_flags & DSVC_CREATE)
71 		open_flags |= O_CREAT|O_EXCL;
72 
73 	*fdp = open(pathname, open_flags, 0644);
74 	if (*fdp == -1)
75 		return (syserr_to_dsvcerr(errno));
76 
77 	return (DSVC_SUCCESS);
78 }
79 
80 /*
81  * Read input a chunk at a time, avoiding as much copying as possible.  To
82  * this end, we don't read into a temporary buffer, but rather read
83  * directly into dynamically reallocated storage (on the assumption that
84  * most of the time we will have to return something).  Returns NULL either
85  * on failure or EOF; use feof(3C) on `fp' to determine which condition
86  * occurred.
87  */
88 char *
read_entry(FILE * fp)89 read_entry(FILE *fp)
90 {
91 	char		*newline, *new_result, *result = NULL;
92 	unsigned int	len = 0, size = 0, chunksize = BUFSIZ;
93 
94 	for (;;) {
95 		/*
96 		 * See if we need to grow the buffer; we always try to read
97 		 * `chunksize' bytes, so we need at least `chunksize' around;
98 		 * grab a little more just to avoid constant realloc'ing
99 		 */
100 		if (len + chunksize > size) {
101 			size = len + (chunksize * 2);
102 			new_result = realloc(result, size);
103 			if (new_result == NULL) {
104 				free(result);
105 				return (NULL);
106 			}
107 		}
108 
109 		if (fgets(&new_result[len], chunksize, fp) == NULL) {
110 			/*
111 			 * Hit EOF; if we never read any data, then free
112 			 * `new_result' and return NULL.  If we are
113 			 * returning data, it's in `new_result', not
114 			 * `result'.
115 			 */
116 			if (result == NULL)
117 				free(new_result);
118 			else
119 				result = new_result;
120 			break;
121 		}
122 		result = new_result;
123 
124 		/*
125 		 * If we read a newline, then see if the preceding
126 		 * character was an escape.  If so, remove the escape and
127 		 * continue; otherwise we're done.  Note that we need to
128 		 * do the strrchr() on `&result[len]' so that NUL's that
129 		 * may be lurking elsewhere on the line don't confuse us.
130 		 */
131 		newline = strrchr(&result[len], '\n');
132 		if (newline != NULL) {
133 			len = newline - result;
134 			if (newline == result || newline[-1] != '\\') {
135 				newline[0] = '\0';
136 				break;
137 			}
138 			newline[-1] = '\0';
139 			len -= 2;
140 		} else {
141 			/*
142 			 * We either `chunksize' worth of data or we hit a
143 			 * NUL somewhere in the data stream.  If we hit a
144 			 * NUL, then we can't "see" beyond the NUL; just
145 			 * advance to the NUL itself and continue.
146 			 */
147 			len += strlen(&result[len]);
148 		}
149 	}
150 	return (result);
151 }
152 
153 /*
154  * Given a buffer `buf' of words separated by one or more of the characters
155  * in `seps', split it into at most `nfields' fields, by changing the
156  * separator character following a field to a NUL character.  Set
157  * `fields[i]' to point to the beginning of field i in `buf'.  Return the
158  * number of fields set in `fields[]'.  This routine is quite similar to
159  * bufsplit(3G), but less general, faster, and also handles multiple
160  * multiple whitespace separator characters differently.
161  */
162 unsigned int
field_split(char * buf,unsigned int nfields,char * fields[],const char * seps)163 field_split(char *buf, unsigned int nfields, char *fields[], const char *seps)
164 {
165 	unsigned int	i = 0;
166 	char		*ch;
167 
168 	for (;;) {
169 		fields[i] = buf;
170 
171 		/*
172 		 * Look for the field separator, byte-at-a-time; ignore
173 		 * separators that have been escaped.  Believe it or not,
174 		 * strchr(3C) will match `seps' if `*buf' is the NUL byte
175 		 * (which indicates we're done).
176 		 */
177 		for (;;) {
178 			ch = strchr(seps, *buf);
179 			if (ch != NULL && *ch == '\0')
180 				return (i + 1);
181 			if (ch != NULL && (buf == fields[i] || buf[-1] != '\\'))
182 				break;
183 			buf++;
184 		}
185 
186 		/*
187 		 * If this is the last field, then consider any remaining
188 		 * text on the line part of the last field.  This is
189 		 * similar to how `read' in sh(1) works.
190 		 */
191 		if (++i == nfields)
192 			return (i);
193 
194 		if (*buf == '\0')
195 			return (i);
196 
197 		*buf = '\0';
198 
199 		/*
200 		 * If separator is whitespace, then skip all consecutive
201 		 * pieces of whitespace.
202 		 */
203 		while (ch != NULL && isspace(*ch)) {
204 			ch = strchr(seps, buf[1]);
205 			if (ch != NULL && isspace(*ch))
206 				buf++;
207 		}
208 		buf++;
209 	}
210 }
211 
212 /*
213  * Map a standard errno into a corresponding DSVC_* code.  If there
214  * is no translation, default to DSVC_INTERNAL.
215  */
216 int
syserr_to_dsvcerr(int error)217 syserr_to_dsvcerr(int error)
218 {
219 	switch (error) {
220 
221 	case EEXIST:
222 		return (DSVC_TABLE_EXISTS);
223 
224 	case ENOMEM:
225 		return (DSVC_NO_MEMORY);
226 
227 	case ENOSPC:
228 		return (DSVC_NO_RESOURCES);
229 
230 	case EROFS:
231 	case EPERM:
232 	case EACCES:
233 		return (DSVC_ACCESS);
234 
235 	case ENOENT:
236 		return (DSVC_NO_TABLE);
237 
238 	default:
239 		break;
240 	}
241 
242 	return (DSVC_INTERNAL);
243 }
244 
245 /*
246  * Convert an object of `len' bytes pointed to by `srcraw' between
247  * network-order and host-order and store in `dstraw'.  The length `len'
248  * must be the actual length of the objects pointed to by `srcraw' and
249  * `dstraw' (or zero) or the results are undefined.  Note that `srcraw' and
250  * `dstraw' may be the same, in which case the object is converted
251  * in-place.  This routine will convert from host-order to network-order or
252  * network-order to host-order, since the conversion is the same.
253  */
254 void
nhconvert(void * dstraw,const void * srcraw,size_t len)255 nhconvert(void *dstraw, const void *srcraw, size_t len)
256 {
257 #ifdef	_LITTLE_ENDIAN
258 	uint8_t	b1, b2;
259 	uint8_t *dst, *src;
260 	size_t i;
261 
262 	/*
263 	 * If both `srcraw' and `dstraw' are 32-bit aligned and `len' is 4,
264 	 * then use ntohl() to do the byteswap, since it's hand-tuned.
265 	 */
266 	if (IS_P2ALIGNED(dstraw, 4) && IS_P2ALIGNED(srcraw, 4) && len == 4) {
267 		*(uint32_t *)dstraw = ntohl(*(uint32_t *)srcraw);
268 		return;
269 	}
270 
271 	dst = (uint8_t *)dstraw;
272 	src = (uint8_t *)srcraw;
273 
274 	for (i = 0; i < len / 2; i++) {
275 		b1 = src[i];
276 		b2 = src[len - i - 1];
277 		dst[i] = b2;
278 		dst[len - i - 1] = b1;
279 	}
280 #else
281 	if (srcraw != dstraw)
282 		(void) memmove(dstraw, srcraw, len);
283 #endif
284 }
285 
286 /*
287  * Positioned n-byte read: read `buflen' bytes at offset `off' at open file
288  * `fd' into `buffer', or "read" none at all.  Returns -1 if all `buflen'
289  * bytes cannot be read; otherwise, returns 0.
290  */
291 int
pnread(int fd,void * buffer,size_t buflen,off_t off)292 pnread(int fd, void *buffer, size_t buflen, off_t off)
293 {
294 	size_t	nread;
295 	ssize_t	nbytes;
296 	char	*buf = buffer;
297 
298 	for (nread = 0; nread < buflen; nread += nbytes) {
299 		nbytes = pread(fd, &buf[nread], buflen - nread, off + nread);
300 		if (nbytes == -1)
301 			return (-1);
302 		if (nbytes == 0) {
303 			errno = EIO;
304 			return (-1);
305 		}
306 	}
307 
308 	assert(nread == buflen);
309 	return (0);
310 }
311 
312 /*
313  * Positioned n-byte write: write `buflen' bytes from `buffer' to offset
314  * `off' in open file `fd'.  Tries to write all `buflen' bytes, but does
315  * not attempt to "undo" what it has done in the case of failure.  Returns
316  * -1 if all `buflen' bytes cannot be written, otherwise returns 0.
317  */
318 int
pnwrite(int fd,const void * buffer,size_t buflen,off_t off)319 pnwrite(int fd, const void *buffer, size_t buflen, off_t off)
320 {
321 	size_t		nwritten;
322 	ssize_t		nbytes;
323 	const char	*buf = buffer;
324 
325 	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
326 		nbytes = pwrite(fd, &buf[nwritten], buflen - nwritten,
327 		    off + nwritten);
328 		if (nbytes == -1)
329 			return (-1);
330 		if (nbytes == 0) {
331 			errno = EIO;
332 			return (-1);
333 		}
334 	}
335 
336 	assert(nwritten == buflen);
337 	return (0);
338 }
339 
340 /*
341  * Copy `nbytes' efficiently from offset `srcoff' in `srcfd' to offset
342  * `dstoff' in `dstfd'; returns a DSVC_* return code.  Note that we make
343  * `nbytes' a uint64_t (rather than a size_t) so that we can copy 2^64
344  * bits even when compiled ILP32.
345  */
346 int
copy_range(int srcfd,off_t srcoff,int dstfd,off_t dstoff,uint64_t nbytes)347 copy_range(int srcfd, off_t srcoff, int dstfd, off_t dstoff, uint64_t nbytes)
348 {
349 	const size_t	chunksize = 16 * PAGESIZE;
350 	size_t		validsize;
351 	size_t		skip;
352 	uint64_t	nwritten = 0;
353 	int		mflags = MAP_PRIVATE;
354 	char		*buf = NULL;
355 	int		error;
356 
357 	/*
358 	 * Handle trivial copy specially so we don't call munmap() below.
359 	 */
360 	if (nbytes == 0)
361 		return (DSVC_SUCCESS);
362 
363 	/*
364 	 * The `off' argument to mmap(2) must be page-aligned, so align it;
365 	 * compute how many bytes we need to skip over in the mmap()'d
366 	 * buffer as a result.
367 	 */
368 	skip = srcoff % PAGESIZE;
369 	srcoff -= skip;
370 
371 	while (nwritten < nbytes) {
372 		buf = mmap(buf, chunksize, PROT_READ, mflags, srcfd, srcoff);
373 		if (buf == MAP_FAILED)
374 			return (DSVC_INTERNAL);
375 		mflags |= MAP_FIXED;
376 
377 		validsize = MIN(chunksize, nbytes - nwritten + skip);
378 		if (pnwrite(dstfd, &buf[skip], validsize - skip, dstoff)
379 		    == -1) {
380 			error = errno;
381 			(void) munmap(buf, chunksize);
382 			return (syserr_to_dsvcerr(error));
383 		}
384 
385 		nwritten += validsize - skip;
386 		dstoff += validsize - skip;
387 		srcoff += validsize;
388 		skip = 0;
389 	}
390 	(void) munmap(buf, chunksize);
391 
392 	return (DSVC_SUCCESS);
393 }
394 
395 /*
396  * Unescape all instances of `delimiter' in `buffer' and store result in
397  * `unescaped', which is `size' bytes.  To guarantee that all data is
398  * copied, `unescaped' should be at least as long as `buffer'.
399  */
400 void
unescape(char delimiter,const char * buffer,char * unescaped,size_t size)401 unescape(char delimiter, const char *buffer, char *unescaped, size_t size)
402 {
403 	int i, j;
404 
405 	size--;
406 	for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
407 		if (buffer[i] == '\\' && buffer[i + 1] == delimiter)
408 			i++;
409 		unescaped[j] = buffer[i];
410 	}
411 	unescaped[j] = '\0';
412 }
413 
414 /*
415  * Escape all instances of `delimiter' in `buffer' and store result in
416  * `escaped', which is `size' bytes.  To guarantee that all data is
417  * copied, `escaped' should be at least twice as long as `buffer'.
418  */
419 void
escape(char delimiter,const char * buffer,char * escaped,size_t size)420 escape(char delimiter, const char *buffer, char *escaped, size_t size)
421 {
422 	int i, j;
423 
424 	size--;
425 	for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
426 		if (buffer[i] == delimiter)
427 			escaped[j++] = '\\';
428 		escaped[j] = buffer[i];
429 	}
430 	escaped[j] = '\0';
431 }
432 
433 /*
434  * Generate a signature for a new record.  The signature is conceptually
435  * divided into two pieces: a random 16-bit "generation number" and a
436  * 48-bit monotonically increasing integer.  The generation number protects
437  * against stale updates to records that have been deleted and since
438  * recreated.
439  */
440 uint64_t
gensig(void)441 gensig(void)
442 {
443 	static int seeded = 0;
444 
445 	if (seeded == 0) {
446 		srand48((long)gethrtime());
447 		seeded++;
448 	}
449 
450 	return ((uint64_t)lrand48() << 48 | 1);
451 }
452