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 2004 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * This file contains public API functions for managing the legacy dhcptab
2N/A * container format. For the semantics of these functions, please see the
2N/A * Enterprise DHCP Architecture Document.
2N/A */
2N/A
2N/A#include <alloca.h>
2N/A#include <dhcp_svc_public.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <netinet/in.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/types.h>
2N/A#include <unistd.h>
2N/A
2N/A#include "dhcptab.h"
2N/A#include "util.h"
2N/A
2N/Astatic void dt2path(char *, size_t, const char *, const char *);
2N/Astatic int write_rec(int, dt_rec_t *, off_t);
2N/A
2N/Aint
2N/Aopen_dt(void **handlep, const char *location, uint_t flags)
2N/A{
2N/A dt_handle_t *dhp;
2N/A int retval;
2N/A int fd;
2N/A char dtpath[MAXPATHLEN];
2N/A
2N/A dhp = malloc(sizeof (dt_handle_t));
2N/A if (dhp == NULL)
2N/A return (DSVC_NO_MEMORY);
2N/A
2N/A dhp->dh_oflags = flags;
2N/A (void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
2N/A
2N/A /*
2N/A * This is a legacy format which has no header, so we neither write
2N/A * nor verify a header (we just create the file or make sure it
2N/A * exists, depending on the value of `flags').
2N/A */
2N/A dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
2N/A retval = open_file(dtpath, flags, &fd);
2N/A if (retval != DSVC_SUCCESS) {
2N/A free(dhp);
2N/A return (retval);
2N/A }
2N/A (void) close(fd);
2N/A
2N/A *handlep = dhp;
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Aclose_dt(void **handlep)
2N/A{
2N/A free(*handlep);
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Aremove_dt(const char *location)
2N/A{
2N/A char dtpath[MAXPATHLEN];
2N/A
2N/A dt2path(dtpath, MAXPATHLEN, location, "");
2N/A if (unlink(dtpath) == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Internal version of lookup_dt() used by both lookup_dt() and
2N/A * update_dt(); same semantics as lookup_dt() except that the `partial'
2N/A * argument has been generalized into a `flags' field and the handle has
2N/A * been turned into a FILE pointer.
2N/A */
2N/Astatic int
2N/Afind_dt(FILE *fp, uint_t flags, uint_t query, int count,
2N/A const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
2N/A{
2N/A int retval = DSVC_SUCCESS;
2N/A char *buf = NULL, *fields[DTF_MAX_FIELDS];
2N/A uint_t nrecords;
2N/A dt_rec_t *recordp;
2N/A dt_rec_list_t *records, *new_records;
2N/A unsigned int nfields;
2N/A off_t recoff;
2N/A
2N/A if (fseek(fp, 0, SEEK_SET) == -1)
2N/A return (DSVC_INTERNAL);
2N/A
2N/A records = NULL;
2N/A for (nrecords = 0; count < 0 || nrecords < count; ) {
2N/A free(buf);
2N/A
2N/A if (flags & FIND_POSITION)
2N/A recoff = ftello(fp);
2N/A
2N/A buf = read_entry(fp);
2N/A if (buf == NULL) {
2N/A if (!feof(fp))
2N/A retval = DSVC_NO_MEMORY;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * Skip pure comment lines; for now this just skips the
2N/A * header information at the top of the container.
2N/A */
2N/A if (buf[0] == DTF_COMMENT_CHAR)
2N/A continue;
2N/A
2N/A /*
2N/A * Parse out the entry into the dt_rec_t
2N/A */
2N/A nfields = field_split(buf, DTF_MAX_FIELDS, fields, " \t");
2N/A if (nfields < DTF_MAX_FIELDS)
2N/A continue;
2N/A
2N/A /*
2N/A * See if we've got a match. If so, allocate the new
2N/A * record, fill it in, and continue.
2N/A */
2N/A if (DSVC_QISEQ(query, DT_QTYPE) &&
2N/A targetp->dt_type != fields[DTF_TYPE][0])
2N/A continue;
2N/A else if (DSVC_QISNEQ(query, DT_QTYPE) &&
2N/A targetp->dt_type == fields[DTF_TYPE][0])
2N/A continue;
2N/A
2N/A if (DSVC_QISEQ(query, DT_QKEY) &&
2N/A strcmp(targetp->dt_key, fields[DTF_KEY]) != 0)
2N/A continue;
2N/A else if (DSVC_QISNEQ(query, DT_QKEY) &&
2N/A strcmp(targetp->dt_key, fields[DTF_KEY]) == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * Caller just wants a count of the number of matching
2N/A * records, not the records themselves; continue.
2N/A */
2N/A if (recordsp == NULL) {
2N/A nrecords++;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Allocate record; if FIND_POSITION flag is set, then we
2N/A * need to allocate an extended (dt_recpos_t) record.
2N/A */
2N/A if (flags & FIND_POSITION)
2N/A recordp = malloc(sizeof (dt_recpos_t));
2N/A else
2N/A recordp = malloc(sizeof (dt_rec_t));
2N/A
2N/A if (recordp == NULL) {
2N/A if ((flags & FIND_PARTIAL) == 0)
2N/A retval = DSVC_NO_MEMORY;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * Fill in record; if FIND_POSITION flag is set, then pass
2N/A * back additional location information.
2N/A */
2N/A (void) strlcpy(recordp->dt_key, fields[DTF_KEY],
2N/A sizeof (recordp->dt_key));
2N/A recordp->dt_sig = 1;
2N/A recordp->dt_type = fields[DTF_TYPE][0];
2N/A recordp->dt_value = strdup(fields[DTF_VALUE]);
2N/A if (recordp->dt_value == NULL) {
2N/A free(recordp);
2N/A if ((flags & FIND_PARTIAL) == 0)
2N/A retval = DSVC_NO_MEMORY;
2N/A break;
2N/A }
2N/A
2N/A if (flags & FIND_POSITION) {
2N/A ((dt_recpos_t *)recordp)->dtp_off = recoff;
2N/A ((dt_recpos_t *)recordp)->dtp_size = ftello(fp) -
2N/A recoff;
2N/A }
2N/A
2N/A /*
2N/A * Chuck the record on the list; up the counter.
2N/A */
2N/A new_records = add_dtrec_to_list(recordp, records);
2N/A if (new_records == NULL) {
2N/A free_dtrec(recordp);
2N/A if ((flags & FIND_PARTIAL) == 0)
2N/A retval = DSVC_NO_MEMORY;
2N/A break;
2N/A }
2N/A records = new_records;
2N/A nrecords++;
2N/A }
2N/A
2N/A free(buf);
2N/A
2N/A if (retval == DSVC_SUCCESS) {
2N/A *nrecordsp = nrecords;
2N/A if (recordsp != NULL)
2N/A *recordsp = records;
2N/A return (DSVC_SUCCESS);
2N/A }
2N/A
2N/A if (records != NULL)
2N/A free_dtrec_list(records);
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/Aint
2N/Alookup_dt(void *handle, boolean_t partial, uint_t query, int count,
2N/A const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
2N/A{
2N/A int retval;
2N/A char dtpath[MAXPATHLEN];
2N/A FILE *fp;
2N/A dt_handle_t *dhp = (dt_handle_t *)handle;
2N/A
2N/A if ((dhp->dh_oflags & DSVC_READ) == 0)
2N/A return (DSVC_ACCESS);
2N/A
2N/A dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
2N/A fp = fopen(dtpath, "r");
2N/A if (fp == NULL)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A retval = find_dt(fp, partial ? FIND_PARTIAL : 0, query, count, targetp,
2N/A recordsp, nrecordsp);
2N/A
2N/A (void) fclose(fp);
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Internal dhcptab record update routine, used to factor out the
2N/A * common code between add_dt(), delete_dt(), and modify_dt(). If
2N/A * `origp' is NULL, then act like add_dt(); if `newp' is NULL, then
2N/A * act like delete_dt(); otherwise act like modify_dt().
2N/A */
2N/Astatic int
2N/Aupdate_dt(const dt_handle_t *dhp, const dt_rec_t *origp, dt_rec_t *newp)
2N/A{
2N/A char dtpath[MAXPATHLEN], newpath[MAXPATHLEN];
2N/A int retval = DSVC_SUCCESS;
2N/A off_t recoff, recnext;
2N/A dt_rec_list_t *reclist;
2N/A FILE *fp;
2N/A int newfd;
2N/A uint_t found;
2N/A int query;
2N/A struct stat st;
2N/A
2N/A if ((dhp->dh_oflags & DSVC_WRITE) == 0)
2N/A return (DSVC_ACCESS);
2N/A
2N/A /*
2N/A * Open the container to update and a new container file which we
2N/A * will store the updated version of the container in. When the
2N/A * update is done, rename the new file to be the real container.
2N/A */
2N/A dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
2N/A fp = fopen(dtpath, "r");
2N/A if (fp == NULL)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new");
2N/A (void) unlink(newpath);
2N/A newfd = open(newpath, O_CREAT|O_EXCL|O_WRONLY, 0644);
2N/A if (newfd == -1) {
2N/A (void) fclose(fp);
2N/A return (syserr_to_dsvcerr(errno));
2N/A }
2N/A
2N/A DSVC_QINIT(query);
2N/A DSVC_QEQ(query, DT_QKEY|DT_QTYPE);
2N/A
2N/A /*
2N/A * If we're adding a new record or changing a key for an existing
2N/A * record, bail if the record we want to add already exists.
2N/A */
2N/A if (newp != NULL) {
2N/A if (origp == NULL || origp->dt_type != newp->dt_type ||
2N/A strcmp(origp->dt_key, newp->dt_key) != 0) {
2N/A retval = find_dt(fp, 0, query, 1, newp, NULL, &found);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A if (found != 0) {
2N/A retval = DSVC_EXISTS;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If we're deleting or modifying record, make sure the record
2N/A * still exists. Note that we don't check signatures because this
2N/A * is a legacy format that has no signatures.
2N/A */
2N/A if (origp != NULL) {
2N/A retval = find_dt(fp, FIND_POSITION, query, 1, origp, &reclist,
2N/A &found);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A if (found == 0) {
2N/A retval = DSVC_NOENT;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Note the offset of the record we're modifying or deleting
2N/A * for use down below.
2N/A */
2N/A recoff = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off;
2N/A recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size;
2N/A
2N/A free_dtrec_list(reclist);
2N/A } else {
2N/A /*
2N/A * No record to modify or delete, so set `recoff' and
2N/A * `recnext' appropriately.
2N/A */
2N/A recoff = 0;
2N/A recnext = 0;
2N/A }
2N/A
2N/A /*
2N/A * Make a new copy of the container. If we're deleting or
2N/A * modifying a record, don't copy that record to the new container.
2N/A */
2N/A if (fstat(fileno(fp), &st) == -1) {
2N/A retval = DSVC_INTERNAL;
2N/A goto out;
2N/A }
2N/A
2N/A retval = copy_range(fileno(fp), 0, newfd, 0, recoff);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A
2N/A retval = copy_range(fileno(fp), recnext, newfd, recoff,
2N/A st.st_size - recnext);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A
2N/A /*
2N/A * If there's a new record, append it to the new container.
2N/A */
2N/A if (newp != NULL) {
2N/A retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Note: we close these descriptors before the rename(2) (rather
2N/A * than just having the `out:' label clean them up) to save NFS
2N/A * some work (otherwise, NFS has to save `dtpath' to an alternate
2N/A * name since its vnode would still be active).
2N/A */
2N/A (void) fclose(fp);
2N/A (void) close(newfd);
2N/A
2N/A if (rename(newpath, dtpath) == -1)
2N/A retval = syserr_to_dsvcerr(errno);
2N/A
2N/A return (retval);
2N/Aout:
2N/A (void) fclose(fp);
2N/A (void) close(newfd);
2N/A (void) unlink(newpath);
2N/A return (retval);
2N/A}
2N/A
2N/Aint
2N/Adelete_dt(void *handle, const dt_rec_t *delp)
2N/A{
2N/A return (update_dt((dt_handle_t *)handle, delp, NULL));
2N/A}
2N/A
2N/Aint
2N/Aadd_dt(void *handle, dt_rec_t *addp)
2N/A{
2N/A return (update_dt((dt_handle_t *)handle, NULL, addp));
2N/A}
2N/A
2N/Aint
2N/Amodify_dt(void *handle, const dt_rec_t *origp, dt_rec_t *newp)
2N/A{
2N/A return (update_dt((dt_handle_t *)handle, origp, newp));
2N/A}
2N/A
2N/Aint
2N/Alist_dt(const char *location, char ***listppp, uint_t *countp)
2N/A{
2N/A char dtpath[MAXPATHLEN];
2N/A char **listpp;
2N/A
2N/A if (access(location, F_OK|R_OK) == -1) {
2N/A switch (errno) {
2N/A case EACCES:
2N/A case EPERM:
2N/A return (DSVC_ACCESS);
2N/A case ENOENT:
2N/A return (DSVC_NO_LOCATION);
2N/A default:
2N/A break;
2N/A }
2N/A return (DSVC_INTERNAL);
2N/A }
2N/A
2N/A dt2path(dtpath, MAXPATHLEN, location, "");
2N/A if (access(dtpath, F_OK|R_OK) == -1) {
2N/A *countp = 0;
2N/A *listppp = NULL;
2N/A return (DSVC_SUCCESS);
2N/A }
2N/A
2N/A listpp = malloc(sizeof (char **));
2N/A if (listpp == NULL)
2N/A return (DSVC_NO_MEMORY);
2N/A listpp[0] = strdup(DT_DHCPTAB);
2N/A if (listpp[0] == NULL) {
2N/A free(listpp);
2N/A return (DSVC_NO_MEMORY);
2N/A }
2N/A
2N/A *listppp = listpp;
2N/A *countp = 1;
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Given a buffer `path' of `pathlen' bytes, fill it in with a path to
2N/A * the dhcptab in directory `dir' with a suffix of `suffix'.
2N/A */
2N/Astatic void
2N/Adt2path(char *path, size_t pathlen, const char *dir, const char *suffix)
2N/A{
2N/A (void) snprintf(path, pathlen, "%s/%s%s", dir, DT_DHCPTAB, suffix);
2N/A}
2N/A
2N/A/*
2N/A * Write the dt_rec_t pointed to by `recp' into the open container `fd' at
2N/A * offset `recoff'. Returns DSVC_* error code.
2N/A */
2N/Astatic int
2N/Awrite_rec(int fd, dt_rec_t *recp, off_t recoff)
2N/A{
2N/A char entbuf[1024], *ent = entbuf;
2N/A size_t entsize = sizeof (entbuf);
2N/A int entlen;
2N/A
2N/Aagain:
2N/A entlen = snprintf(ent, entsize, "%s\t%c\t%s\n", recp->dt_key,
2N/A recp->dt_type, recp->dt_value);
2N/A if (entlen == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A if (entlen > entsize) {
2N/A entsize = entlen;
2N/A ent = alloca(entlen);
2N/A goto again;
2N/A }
2N/A
2N/A if (pnwrite(fd, ent, entlen, recoff) == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A return (DSVC_SUCCESS);
2N/A}