2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * Internal libdhcpsvc public module utility functions: a collection of
2N/A * general-purpose routines that are used by assorted public modules.
2N/A * Someday we should integrate this into the build process a bit more
2N/A * intelligently.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/mman.h>
2N/A#include <sys/isa_defs.h>
2N/A#include <dhcp_svc_public.h>
2N/A#include <assert.h>
2N/A#include <stdlib.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <unistd.h>
2N/A#include <ctype.h>
2N/A
2N/A#include "util.h"
2N/A
2N/A/*
2N/A * Open a file at path `pathname'; depending on the flags passed in through
2N/A * `dsvc_flags', this file may be optionally created or opened read-only.
2N/A * On success, DSVC_SUCCESS is returned and `fdp' points to the opened file
2N/A * descriptor. On failure, a DSVC_* error code is returned.
2N/A */
2N/Aint
2N/Aopen_file(const char *pathname, unsigned int dsvc_flags, int *fdp)
2N/A{
2N/A int open_flags;
2N/A
2N/A /*
2N/A * Note that we always open with read access, independent of
2N/A * dsvc_flags, because an update operation (add, delete, modify)
2N/A * needs to lookup records to detect collisions.
2N/A */
2N/A open_flags = O_RDONLY;
2N/A if (dsvc_flags & DSVC_WRITE)
2N/A open_flags = O_RDWR;
2N/A if (dsvc_flags & DSVC_CREATE)
2N/A open_flags |= O_CREAT|O_EXCL;
2N/A
2N/A *fdp = open(pathname, open_flags, 0644);
2N/A if (*fdp == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Read input a chunk at a time, avoiding as much copying as possible. To
2N/A * this end, we don't read into a temporary buffer, but rather read
2N/A * directly into dynamically reallocated storage (on the assumption that
2N/A * most of the time we will have to return something). Returns NULL either
2N/A * on failure or EOF; use feof(3C) on `fp' to determine which condition
2N/A * occurred.
2N/A */
2N/Achar *
2N/Aread_entry(FILE *fp)
2N/A{
2N/A char *newline, *new_result, *result = NULL;
2N/A unsigned int len = 0, size = 0, chunksize = BUFSIZ;
2N/A
2N/A for (;;) {
2N/A /*
2N/A * See if we need to grow the buffer; we always try to read
2N/A * `chunksize' bytes, so we need at least `chunksize' around;
2N/A * grab a little more just to avoid constant realloc'ing
2N/A */
2N/A if (len + chunksize > size) {
2N/A size = len + (chunksize * 2);
2N/A new_result = realloc(result, size);
2N/A if (new_result == NULL) {
2N/A free(result);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A if (fgets(&new_result[len], chunksize, fp) == NULL) {
2N/A /*
2N/A * Hit EOF; if we never read any data, then free
2N/A * `new_result' and return NULL. If we are
2N/A * returning data, it's in `new_result', not
2N/A * `result'.
2N/A */
2N/A if (result == NULL)
2N/A free(new_result);
2N/A else
2N/A result = new_result;
2N/A break;
2N/A }
2N/A result = new_result;
2N/A
2N/A /*
2N/A * If we read a newline, then see if the preceding
2N/A * character was an escape. If so, remove the escape and
2N/A * continue; otherwise we're done. Note that we need to
2N/A * do the strrchr() on `&result[len]' so that NUL's that
2N/A * may be lurking elsewhere on the line don't confuse us.
2N/A */
2N/A newline = strrchr(&result[len], '\n');
2N/A if (newline != NULL) {
2N/A len = newline - result;
2N/A if (newline == result || newline[-1] != '\\') {
2N/A newline[0] = '\0';
2N/A break;
2N/A }
2N/A newline[-1] = '\0';
2N/A len -= 2;
2N/A } else {
2N/A /*
2N/A * We either `chunksize' worth of data or we hit a
2N/A * NUL somewhere in the data stream. If we hit a
2N/A * NUL, then we can't "see" beyond the NUL; just
2N/A * advance to the NUL itself and continue.
2N/A */
2N/A len += strlen(&result[len]);
2N/A }
2N/A }
2N/A return (result);
2N/A}
2N/A
2N/A/*
2N/A * Given a buffer `buf' of words separated by one or more of the characters
2N/A * in `seps', split it into at most `nfields' fields, by changing the
2N/A * separator character following a field to a NUL character. Set
2N/A * `fields[i]' to point to the beginning of field i in `buf'. Return the
2N/A * number of fields set in `fields[]'. This routine is quite similar to
2N/A * bufsplit(3G), but less general, faster, and also handles multiple
2N/A * multiple whitespace separator characters differently.
2N/A */
2N/Aunsigned int
2N/Afield_split(char *buf, unsigned int nfields, char *fields[], const char *seps)
2N/A{
2N/A unsigned int i = 0;
2N/A char *ch;
2N/A
2N/A for (;;) {
2N/A fields[i] = buf;
2N/A
2N/A /*
2N/A * Look for the field separator, byte-at-a-time; ignore
2N/A * separators that have been escaped. Believe it or not,
2N/A * strchr(3C) will match `seps' if `*buf' is the NUL byte
2N/A * (which indicates we're done).
2N/A */
2N/A for (;;) {
2N/A ch = strchr(seps, *buf);
2N/A if (ch != NULL && *ch == '\0')
2N/A return (i + 1);
2N/A if (ch != NULL && (buf == fields[i] || buf[-1] != '\\'))
2N/A break;
2N/A buf++;
2N/A }
2N/A
2N/A /*
2N/A * If this is the last field, then consider any remaining
2N/A * text on the line part of the last field. This is
2N/A * similar to how `read' in sh(1) works.
2N/A */
2N/A if (++i == nfields)
2N/A return (i);
2N/A
2N/A if (*buf == '\0')
2N/A return (i);
2N/A
2N/A *buf = '\0';
2N/A
2N/A /*
2N/A * If separator is whitespace, then skip all consecutive
2N/A * pieces of whitespace.
2N/A */
2N/A while (ch != NULL && isspace(*ch)) {
2N/A ch = strchr(seps, buf[1]);
2N/A if (ch != NULL && isspace(*ch))
2N/A buf++;
2N/A }
2N/A buf++;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Map a standard errno into a corresponding DSVC_* code. If there
2N/A * is no translation, default to DSVC_INTERNAL.
2N/A */
2N/Aint
2N/Asyserr_to_dsvcerr(int error)
2N/A{
2N/A switch (error) {
2N/A
2N/A case EEXIST:
2N/A return (DSVC_TABLE_EXISTS);
2N/A
2N/A case ENOMEM:
2N/A return (DSVC_NO_MEMORY);
2N/A
2N/A case ENOSPC:
2N/A return (DSVC_NO_RESOURCES);
2N/A
2N/A case EROFS:
2N/A case EPERM:
2N/A case EACCES:
2N/A return (DSVC_ACCESS);
2N/A
2N/A case ENOENT:
2N/A return (DSVC_NO_TABLE);
2N/A
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A return (DSVC_INTERNAL);
2N/A}
2N/A
2N/A/*
2N/A * Convert an object of `len' bytes pointed to by `srcraw' between
2N/A * network-order and host-order and store in `dstraw'. The length `len'
2N/A * must be the actual length of the objects pointed to by `srcraw' and
2N/A * `dstraw' (or zero) or the results are undefined. Note that `srcraw' and
2N/A * `dstraw' may be the same, in which case the object is converted
2N/A * in-place. This routine will convert from host-order to network-order or
2N/A * network-order to host-order, since the conversion is the same.
2N/A */
2N/Avoid
2N/Anhconvert(void *dstraw, const void *srcraw, size_t len)
2N/A{
2N/A#ifdef _LITTLE_ENDIAN
2N/A uint8_t b1, b2;
2N/A uint8_t *dst, *src;
2N/A size_t i;
2N/A
2N/A /*
2N/A * If both `srcraw' and `dstraw' are 32-bit aligned and `len' is 4,
2N/A * then use ntohl() to do the byteswap, since it's hand-tuned.
2N/A */
2N/A if (IS_P2ALIGNED(dstraw, 4) && IS_P2ALIGNED(srcraw, 4) && len == 4) {
2N/A *(uint32_t *)dstraw = ntohl(*(uint32_t *)srcraw);
2N/A return;
2N/A }
2N/A
2N/A dst = (uint8_t *)dstraw;
2N/A src = (uint8_t *)srcraw;
2N/A
2N/A for (i = 0; i < len / 2; i++) {
2N/A b1 = src[i];
2N/A b2 = src[len - i - 1];
2N/A dst[i] = b2;
2N/A dst[len - i - 1] = b1;
2N/A }
2N/A#else
2N/A if (srcraw != dstraw)
2N/A (void) memmove(dstraw, srcraw, len);
2N/A#endif
2N/A}
2N/A
2N/A/*
2N/A * Positioned n-byte read: read `buflen' bytes at offset `off' at open file
2N/A * `fd' into `buffer', or "read" none at all. Returns -1 if all `buflen'
2N/A * bytes cannot be read; otherwise, returns 0.
2N/A */
2N/Aint
2N/Apnread(int fd, void *buffer, size_t buflen, off_t off)
2N/A{
2N/A size_t nread;
2N/A ssize_t nbytes;
2N/A char *buf = buffer;
2N/A
2N/A for (nread = 0; nread < buflen; nread += nbytes) {
2N/A nbytes = pread(fd, &buf[nread], buflen - nread, off + nread);
2N/A if (nbytes == -1)
2N/A return (-1);
2N/A if (nbytes == 0) {
2N/A errno = EIO;
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A assert(nread == buflen);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Positioned n-byte write: write `buflen' bytes from `buffer' to offset
2N/A * `off' in open file `fd'. Tries to write all `buflen' bytes, but does
2N/A * not attempt to "undo" what it has done in the case of failure. Returns
2N/A * -1 if all `buflen' bytes cannot be written, otherwise returns 0.
2N/A */
2N/Aint
2N/Apnwrite(int fd, const void *buffer, size_t buflen, off_t off)
2N/A{
2N/A size_t nwritten;
2N/A ssize_t nbytes;
2N/A const char *buf = buffer;
2N/A
2N/A for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
2N/A nbytes = pwrite(fd, &buf[nwritten], buflen - nwritten,
2N/A off + nwritten);
2N/A if (nbytes == -1)
2N/A return (-1);
2N/A if (nbytes == 0) {
2N/A errno = EIO;
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A assert(nwritten == buflen);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Copy `nbytes' efficiently from offset `srcoff' in `srcfd' to offset
2N/A * `dstoff' in `dstfd'; returns a DSVC_* return code. Note that we make
2N/A * `nbytes' a uint64_t (rather than a size_t) so that we can copy 2^64
2N/A * bits even when compiled ILP32.
2N/A */
2N/Aint
2N/Acopy_range(int srcfd, off_t srcoff, int dstfd, off_t dstoff, uint64_t nbytes)
2N/A{
2N/A const size_t chunksize = 16 * PAGESIZE;
2N/A size_t validsize;
2N/A size_t skip;
2N/A uint64_t nwritten = 0;
2N/A int mflags = MAP_PRIVATE;
2N/A char *buf = NULL;
2N/A int error;
2N/A
2N/A /*
2N/A * Handle trivial copy specially so we don't call munmap() below.
2N/A */
2N/A if (nbytes == 0)
2N/A return (DSVC_SUCCESS);
2N/A
2N/A /*
2N/A * The `off' argument to mmap(2) must be page-aligned, so align it;
2N/A * compute how many bytes we need to skip over in the mmap()'d
2N/A * buffer as a result.
2N/A */
2N/A skip = srcoff % PAGESIZE;
2N/A srcoff -= skip;
2N/A
2N/A while (nwritten < nbytes) {
2N/A buf = mmap(buf, chunksize, PROT_READ, mflags, srcfd, srcoff);
2N/A if (buf == MAP_FAILED)
2N/A return (DSVC_INTERNAL);
2N/A mflags |= MAP_FIXED;
2N/A
2N/A validsize = MIN(chunksize, nbytes - nwritten + skip);
2N/A if (pnwrite(dstfd, &buf[skip], validsize - skip, dstoff)
2N/A == -1) {
2N/A error = errno;
2N/A (void) munmap(buf, chunksize);
2N/A return (syserr_to_dsvcerr(error));
2N/A }
2N/A
2N/A nwritten += validsize - skip;
2N/A dstoff += validsize - skip;
2N/A srcoff += validsize;
2N/A skip = 0;
2N/A }
2N/A (void) munmap(buf, chunksize);
2N/A
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Unescape all instances of `delimiter' in `buffer' and store result in
2N/A * `unescaped', which is `size' bytes. To guarantee that all data is
2N/A * copied, `unescaped' should be at least as long as `buffer'.
2N/A */
2N/Avoid
2N/Aunescape(char delimiter, const char *buffer, char *unescaped, size_t size)
2N/A{
2N/A int i, j;
2N/A
2N/A size--;
2N/A for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
2N/A if (buffer[i] == '\\' && buffer[i + 1] == delimiter)
2N/A i++;
2N/A unescaped[j] = buffer[i];
2N/A }
2N/A unescaped[j] = '\0';
2N/A}
2N/A
2N/A/*
2N/A * Escape all instances of `delimiter' in `buffer' and store result in
2N/A * `escaped', which is `size' bytes. To guarantee that all data is
2N/A * copied, `escaped' should be at least twice as long as `buffer'.
2N/A */
2N/Avoid
2N/Aescape(char delimiter, const char *buffer, char *escaped, size_t size)
2N/A{
2N/A int i, j;
2N/A
2N/A size--;
2N/A for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
2N/A if (buffer[i] == delimiter)
2N/A escaped[j++] = '\\';
2N/A escaped[j] = buffer[i];
2N/A }
2N/A escaped[j] = '\0';
2N/A}
2N/A
2N/A/*
2N/A * Generate a signature for a new record. The signature is conceptually
2N/A * divided into two pieces: a random 16-bit "generation number" and a
2N/A * 48-bit monotonically increasing integer. The generation number protects
2N/A * against stale updates to records that have been deleted and since
2N/A * recreated.
2N/A */
2N/Auint64_t
2N/Agensig(void)
2N/A{
2N/A static int seeded = 0;
2N/A
2N/A if (seeded == 0) {
2N/A srand48((long)gethrtime());
2N/A seeded++;
2N/A }
2N/A
2N/A return ((uint64_t)lrand48() << 48 | 1);
2N/A}