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 2005 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 functions for managing DHCP network
2N/A * containers. For the semantics of these functions, please see the
2N/A * Enterprise DHCP Architecture Document.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <stdlib.h>
2N/A#include <arpa/inet.h>
2N/A#include <sys/stat.h>
2N/A#include <string.h>
2N/A#include <errno.h>
2N/A#include <unistd.h>
2N/A#include <fcntl.h>
2N/A#include <alloca.h>
2N/A#include <dhcp_svc_public.h>
2N/A#include <dirent.h>
2N/A#include <libgen.h>
2N/A#include <libinetutil.h>
2N/A#include <sys/mman.h>
2N/A
2N/A#include "dhcp_network.h"
2N/A#include "util.h"
2N/A
2N/Astatic void net2path(char *, size_t, const char *, ipaddr_t, const char *);
2N/Astatic boolean_t record_match(char *[], dn_rec_t *, const dn_rec_t *, uint_t);
2N/Astatic int write_rec(int, dn_rec_t *, off_t);
2N/A
2N/A/* ARGSUSED */
2N/Aint
2N/Aopen_dn(void **handlep, const char *location, uint_t flags,
2N/A const struct in_addr *netp, const struct in_addr *maskp)
2N/A{
2N/A char connet[INET_ADDRSTRLEN];
2N/A char dnpath[MAXPATHLEN];
2N/A unsigned int conver;
2N/A dn_handle_t *dhp;
2N/A FILE *fp;
2N/A int retval;
2N/A int i, nelems;
2N/A char nl;
2N/A struct in_addr net_nbo;
2N/A int fd;
2N/A
2N/A dhp = malloc(sizeof (dn_handle_t));
2N/A if (dhp == NULL)
2N/A return (DSVC_NO_MEMORY);
2N/A
2N/A dhp->dh_net = netp->s_addr;
2N/A dhp->dh_oflags = flags;
2N/A (void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
2N/A
2N/A net2path(dnpath, MAXPATHLEN, location, netp->s_addr, "");
2N/A retval = open_file(dnpath, flags, &fd);
2N/A if (retval != DSVC_SUCCESS) {
2N/A free(dhp);
2N/A return (retval);
2N/A }
2N/A
2N/A fp = fdopen(fd, flags & DSVC_WRITE ? "r+" : "r");
2N/A if (fp == NULL) {
2N/A (void) close(fd);
2N/A free(dhp);
2N/A return (DSVC_INTERNAL);
2N/A }
2N/A
2N/A if (flags & DSVC_CREATE) {
2N/A /*
2N/A * We just created the per-network container; put the
2N/A * header on for future use...
2N/A */
2N/A net_nbo.s_addr = htonl(netp->s_addr);
2N/A (void) inet_ntop(AF_INET, &net_nbo, connet, INET_ADDRSTRLEN);
2N/A
2N/A for (i = 0; connet[i] != '\0'; i++)
2N/A if (connet[i] == '.')
2N/A connet[i] = '_';
2N/A
2N/A retval = fprintf(fp, "# SUNWfiles%u_%s\n", DSVC_CONVER, connet);
2N/A if (retval < 0 || fflush(fp) == EOF) {
2N/A (void) fclose(fp);
2N/A (void) free(dhp);
2N/A return (DSVC_INTERNAL);
2N/A }
2N/A
2N/A (void) fprintf(fp, "#\n# Do NOT edit this file by hand -- use");
2N/A (void) fprintf(fp, " pntadm(1M) or dhcpmgr(1M) instead\n#\n");
2N/A } else {
2N/A /*
2N/A * Container already exists; sanity check against the
2N/A * header that's on-disk.
2N/A */
2N/A nelems = fscanf(fp, "#%*1[ ]SUNWfiles%u_%15s%c", &conver,
2N/A connet, &nl);
2N/A
2N/A for (i = 0; connet[i] != '\0'; i++)
2N/A if (connet[i] == '_')
2N/A connet[i] = '.';
2N/A
2N/A if (nelems != 3 || inet_addr(connet) != htonl(netp->s_addr) ||
2N/A conver != DSVC_CONVER || nl != '\n') {
2N/A (void) fclose(fp);
2N/A (void) free(dhp);
2N/A return (DSVC_INTERNAL);
2N/A }
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A *handlep = dhp;
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Aclose_dn(void **handlep)
2N/A{
2N/A free(*handlep);
2N/A return (DSVC_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Aremove_dn(const char *dir, const struct in_addr *netp)
2N/A{
2N/A char dnpath[MAXPATHLEN];
2N/A
2N/A net2path(dnpath, MAXPATHLEN, dir, netp->s_addr, "");
2N/A if (unlink(dnpath) == -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 lookup routine used by both lookup_dn() and
2N/A * update_dn(); same semantics as lookup_dn() except that the `partial'
2N/A * argument has been generalized into a `flags' field.
2N/A */
2N/Astatic int
2N/Afind_dn(int fd, uint_t flags, uint_t query, int count, const dn_rec_t *targetp,
2N/A dn_rec_list_t **recordsp, uint_t *nrecordsp)
2N/A{
2N/A int retval = DSVC_SUCCESS;
2N/A char *fields[DNF_FIELDS];
2N/A uint_t nrecords;
2N/A dn_rec_t dn, *recordp;
2N/A dn_rec_list_t *records, *new_records;
2N/A unsigned int nfields;
2N/A struct stat st;
2N/A struct in_addr cip_nbo;
2N/A char *ent0, *ent, *entend;
2N/A char cip[INET_ADDRSTRLEN + 2];
2N/A
2N/A /*
2N/A * Page the whole container into memory via mmap() so we can scan it
2N/A * quickly; map it MAP_PRIVATE so that we can change newlines to
2N/A * NULs without changing the actual container itself.
2N/A */
2N/A if (fstat(fd, &st) == -1 || st.st_size < 1)
2N/A return (DSVC_INTERNAL);
2N/A
2N/A ent0 = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
2N/A if (ent0 == MAP_FAILED)
2N/A return (DSVC_INTERNAL);
2N/A
2N/A /*
2N/A * NUL-terminate the last byte (which should be a newline) so that
2N/A * we can safely use string functions on the mapped container.
2N/A */
2N/A ent0[st.st_size - 1] = '\0';
2N/A
2N/A /*
2N/A * If we're searching by client IP address, then build a target
2N/A * string we can use to find it quickly.
2N/A */
2N/A if (DSVC_QISEQ(query, DN_QCIP)) {
2N/A cip[0] = '\n';
2N/A cip_nbo.s_addr = htonl(targetp->dn_cip.s_addr);
2N/A (void) inet_ntop(AF_INET, &cip_nbo, cip + 1, INET_ADDRSTRLEN);
2N/A (void) strlcat(cip, "|", sizeof (cip));
2N/A }
2N/A
2N/A records = NULL;
2N/A ent = ent0;
2N/A for (nrecords = 0; count < 0 || nrecords < count; ent = entend + 1) {
2N/A /*
2N/A * Bail if we've reached the end of the container.
2N/A */
2N/A if (ent - ent0 >= st.st_size)
2N/A break;
2N/A
2N/A /*
2N/A * If we're searching by client IP address, locate it
2N/A * quickly using strstr(3C); if we can't find it by this
2N/A * technique then it's not in the container.
2N/A */
2N/A if (DSVC_QISEQ(query, DN_QCIP)) {
2N/A /*
2N/A * If we've already found the DN_QCIP record, bail.
2N/A */
2N/A if (nrecords > 0)
2N/A break;
2N/A
2N/A ent = strstr(ent, cip);
2N/A if (ent == NULL)
2N/A break;
2N/A ent++;
2N/A }
2N/A
2N/A /*
2N/A * Find the end of the record and change it a NUL byte so
2N/A * that it is interpreted correctly with field_split() and
2N/A * record_match() below. If we can't find a trailing
2N/A * newline, then it must be the last record (whose newline
2N/A * we already changed to a NUL above).
2N/A */
2N/A entend = strchr(ent, '\n');
2N/A if (entend != NULL)
2N/A *entend = '\0';
2N/A else
2N/A entend = &ent0[st.st_size - 1];
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 (ent[0] == DNF_COMMENT_CHAR)
2N/A continue;
2N/A
2N/A /*
2N/A * Split the buffer up into DNF_FIELDS fields.
2N/A */
2N/A nfields = field_split(ent, DNF_FIELDS, fields, "|");
2N/A if (nfields < DNF_FIELDS)
2N/A continue;
2N/A
2N/A /*
2N/A * See if we've got a match, filling in dnf.dnf_rec as
2N/A * we go. If record_match() succeeds, dnf.dnf_rec will
2N/A * be completely filled in.
2N/A */
2N/A if (!record_match(fields, &dn, targetp, query))
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
2N/A * we need to allocate an extended (dn_recpos_t) record.
2N/A */
2N/A if (flags & FIND_POSITION)
2N/A recordp = malloc(sizeof (dn_recpos_t));
2N/A else
2N/A recordp = malloc(sizeof (dn_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; do a structure copy from our automatic
2N/A * dn. If FIND_POSITION flag is on, pass back additional
2N/A * position information.
2N/A */
2N/A *recordp = dn;
2N/A if (flags & FIND_POSITION) {
2N/A ((dn_recpos_t *)recordp)->dnp_off = ent - ent0;
2N/A ((dn_recpos_t *)recordp)->dnp_size = entend - ent + 1;
2N/A }
2N/A
2N/A /*
2N/A * Chuck the record on the list; up the counter.
2N/A */
2N/A new_records = add_dnrec_to_list(recordp, records);
2N/A if (new_records == 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 records = new_records;
2N/A nrecords++;
2N/A }
2N/A
2N/A (void) munmap(ent0, st.st_size);
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_dnrec_list(records);
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/Aint
2N/Alookup_dn(void *handle, boolean_t partial, uint_t query, int count,
2N/A const dn_rec_t *targetp, dn_rec_list_t **recordsp, uint_t *nrecordsp)
2N/A{
2N/A int retval;
2N/A char dnpath[MAXPATHLEN];
2N/A int fd;
2N/A dn_handle_t *dhp = (dn_handle_t *)handle;
2N/A
2N/A if ((dhp->dh_oflags & DSVC_READ) == 0)
2N/A return (DSVC_ACCESS);
2N/A
2N/A net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
2N/A fd = open(dnpath, O_RDONLY);
2N/A if (fd == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A retval = find_dn(fd, partial ? FIND_PARTIAL : 0, query, count, targetp,
2N/A recordsp, nrecordsp);
2N/A
2N/A (void) close(fd);
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Compares the fields in fields[] agains the fields in target `targetp',
2N/A * using `query' to decide what fields to compare. Returns B_TRUE if `dnp'
2N/A * matches `targetp', B_FALSE if not. On success, `dnp' is completely
2N/A * filled in.
2N/A */
2N/Astatic boolean_t
2N/Arecord_match(char *fields[], dn_rec_t *dnp, const dn_rec_t *targetp,
2N/A uint_t query)
2N/A{
2N/A unsigned int qflags[] = { DN_QFDYNAMIC, DN_QFAUTOMATIC, DN_QFMANUAL,
2N/A DN_QFUNUSABLE, DN_QFBOOTP_ONLY };
2N/A unsigned int flags[] = { DN_FDYNAMIC, DN_FAUTOMATIC, DN_FMANUAL,
2N/A DN_FUNUSABLE, DN_FBOOTP_ONLY };
2N/A unsigned int i;
2N/A uint_t dn_cid_len;
2N/A
2N/A dnp->dn_cip.s_addr = ntohl(inet_addr(fields[DNF_CIP]));
2N/A if (DSVC_QISEQ(query, DN_QCIP) &&
2N/A dnp->dn_cip.s_addr != targetp->dn_cip.s_addr)
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, DN_QCIP) &&
2N/A dnp->dn_cip.s_addr == targetp->dn_cip.s_addr)
2N/A return (B_FALSE);
2N/A
2N/A dnp->dn_lease = atoi(fields[DNF_LEASE]);
2N/A if (DSVC_QISEQ(query, DN_QLEASE) && targetp->dn_lease != dnp->dn_lease)
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, DN_QLEASE) && targetp->dn_lease == dnp->dn_lease)
2N/A return (B_FALSE);
2N/A
2N/A /*
2N/A * We use dn_cid_len since dnp->dn_cid_len is of type uchar_t but
2N/A * hexascii_to_octet() expects an uint_t *
2N/A */
2N/A dn_cid_len = DN_MAX_CID_LEN;
2N/A if (hexascii_to_octet(fields[DNF_CID], strlen(fields[DNF_CID]),
2N/A dnp->dn_cid, &dn_cid_len) != 0)
2N/A return (B_FALSE);
2N/A
2N/A dnp->dn_cid_len = dn_cid_len;
2N/A if (DSVC_QISEQ(query, DN_QCID) &&
2N/A (dnp->dn_cid_len != targetp->dn_cid_len ||
2N/A (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) != 0)))
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, DN_QCID) &&
2N/A (dnp->dn_cid_len == targetp->dn_cid_len &&
2N/A (memcmp(dnp->dn_cid, targetp->dn_cid, dnp->dn_cid_len) == 0)))
2N/A return (B_FALSE);
2N/A
2N/A dnp->dn_sip.s_addr = ntohl(inet_addr(fields[DNF_SIP]));
2N/A if (DSVC_QISEQ(query, DN_QSIP) &&
2N/A dnp->dn_sip.s_addr != targetp->dn_sip.s_addr)
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, DN_QSIP) &&
2N/A dnp->dn_sip.s_addr == targetp->dn_sip.s_addr)
2N/A return (B_FALSE);
2N/A
2N/A unescape('|', fields[DNF_MACRO], dnp->dn_macro, sizeof (dnp->dn_macro));
2N/A if (DSVC_QISEQ(query, DN_QMACRO) &&
2N/A strcmp(targetp->dn_macro, dnp->dn_macro) != 0)
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, DN_QMACRO) &&
2N/A strcmp(targetp->dn_macro, dnp->dn_macro) == 0)
2N/A return (B_FALSE);
2N/A
2N/A dnp->dn_flags = atoi(fields[DNF_FLAGS]);
2N/A for (i = 0; i < sizeof (qflags) / sizeof (unsigned int); i++) {
2N/A if (DSVC_QISEQ(query, qflags[i]) &&
2N/A (dnp->dn_flags & flags[i]) !=
2N/A (targetp->dn_flags & flags[i]))
2N/A return (B_FALSE);
2N/A if (DSVC_QISNEQ(query, qflags[i]) &&
2N/A (dnp->dn_flags & flags[i]) ==
2N/A (targetp->dn_flags & flags[i]))
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A dnp->dn_sig = atoll(fields[DNF_SIG]);
2N/A unescape('|', fields[DNF_COMMENT], dnp->dn_comment,
2N/A sizeof (dnp->dn_comment));
2N/A
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/*
2N/A * Internal dhcp_network record update routine, used to factor out the
2N/A * common code between add_dn(), delete_dn(), and modify_dn(). If
2N/A * `origp' is NULL, then act like add_dn(); if `newp' is NULL, then
2N/A * act like delete_dn(); otherwise act like modify_dn().
2N/A */
2N/Astatic int
2N/Aupdate_dn(const dn_handle_t *dhp, const dn_rec_t *origp, dn_rec_t *newp)
2N/A{
2N/A char dnpath[MAXPATHLEN], newpath[MAXPATHLEN];
2N/A int retval = DSVC_SUCCESS;
2N/A off_t recoff, recnext;
2N/A dn_rec_list_t *reclist;
2N/A int fd, newfd;
2N/A uint_t found;
2N/A int query;
2N/A struct stat st;
2N/A
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 net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
2N/A fd = open(dnpath, O_RDONLY);
2N/A if (fd == -1)
2N/A return (syserr_to_dsvcerr(errno));
2N/A
2N/A net2path(newpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, ".new");
2N/A newfd = open(newpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
2N/A if (newfd == -1) {
2N/A (void) close(fd);
2N/A return (syserr_to_dsvcerr(errno));
2N/A }
2N/A
2N/A DSVC_QINIT(query);
2N/A DSVC_QEQ(query, DN_QCIP);
2N/A
2N/A /*
2N/A * If we're changing the key for this record, make sure the key
2N/A * we're changing to doesn't already exist.
2N/A */
2N/A if (origp != NULL && newp != NULL) {
2N/A if (origp->dn_cip.s_addr != newp->dn_cip.s_addr) {
2N/A retval = find_dn(fd, 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 adding a new record, make sure the record doesn't
2N/A * already exist.
2N/A */
2N/A if (newp != NULL && origp == NULL) {
2N/A retval = find_dn(fd, 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 * If we're deleting or modifying record, make sure the record
2N/A * still exists and that our copy isn't stale. Note that we don't
2N/A * check signatures if we're deleting the record and origp->dn_sig
2N/A * is zero, so that records that weren't looked up can be deleted.
2N/A */
2N/A if (origp != NULL) {
2N/A retval = find_dn(fd, 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 if (reclist->dnl_rec->dn_sig != origp->dn_sig) {
2N/A if (newp != NULL || origp->dn_sig != 0) {
2N/A free_dnrec_list(reclist);
2N/A retval = DSVC_COLLISION;
2N/A goto out;
2N/A }
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 = ((dn_recpos_t *)reclist->dnl_rec)->dnp_off;
2N/A recnext = recoff + ((dn_recpos_t *)reclist->dnl_rec)->dnp_size;
2N/A
2N/A free_dnrec_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(fd, &st) == -1) {
2N/A retval = DSVC_INTERNAL;
2N/A goto out;
2N/A }
2N/A
2N/A retval = copy_range(fd, 0, newfd, 0, recoff);
2N/A if (retval != DSVC_SUCCESS)
2N/A goto out;
2N/A
2N/A retval = copy_range(fd, recnext, newfd, recoff, 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/modified record, append it to the new container.
2N/A */
2N/A if (newp != NULL) {
2N/A if (origp == NULL)
2N/A newp->dn_sig = gensig();
2N/A else
2N/A newp->dn_sig = origp->dn_sig + 1;
2N/A
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 `dnpath' to an alternate
2N/A * name since its vnode would still be active).
2N/A */
2N/A (void) close(fd);
2N/A (void) close(newfd);
2N/A
2N/A if (rename(newpath, dnpath) == -1)
2N/A retval = syserr_to_dsvcerr(errno);
2N/A
2N/A return (retval);
2N/Aout:
2N/A (void) close(fd);
2N/A (void) close(newfd);
2N/A (void) unlink(newpath);
2N/A return (retval);
2N/A}
2N/A
2N/Aint
2N/Aadd_dn(void *handle, dn_rec_t *addp)
2N/A{
2N/A return (update_dn((dn_handle_t *)handle, NULL, addp));
2N/A}
2N/A
2N/Aint
2N/Amodify_dn(void *handle, const dn_rec_t *origp, dn_rec_t *newp)
2N/A{
2N/A return (update_dn((dn_handle_t *)handle, origp, newp));
2N/A}
2N/A
2N/Aint
2N/Adelete_dn(void *handle, const dn_rec_t *delp)
2N/A{
2N/A return (update_dn((dn_handle_t *)handle, delp, NULL));
2N/A}
2N/A
2N/Aint
2N/Alist_dn(const char *location, char ***listppp, uint_t *countp)
2N/A{
2N/A char ipaddr[INET_ADDRSTRLEN];
2N/A struct dirent *result;
2N/A DIR *dirp;
2N/A unsigned int i, count = 0;
2N/A char *re, **new_listpp, **listpp = NULL;
2N/A char conver[4];
2N/A int error;
2N/A
2N/A dirp = opendir(location);
2N/A if (dirp == NULL) {
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 /*
2N/A * Compile a regular expression matching "SUNWfilesX_" (where X is
2N/A * a container version number) followed by an IP address (roughly
2N/A * speaking). Note that the $N constructions allow us to get the
2N/A * container version and IP address when calling regex(3C).
2N/A */
2N/A re = regcmp("^SUNWfiles([0-9]{1,3})$0_"
2N/A "(([0-9]{1,3}_){3}[0-9]{1,3})$1$", (char *)0);
2N/A if (re == NULL)
2N/A return (DSVC_NO_MEMORY);
2N/A
2N/A while ((result = readdir(dirp)) != NULL) {
2N/A if (regex(re, result->d_name, conver, ipaddr) != NULL) {
2N/A if (atoi(conver) != DSVC_CONVER)
2N/A continue;
2N/A
2N/A for (i = 0; ipaddr[i] != '\0'; i++)
2N/A if (ipaddr[i] == '_')
2N/A ipaddr[i] = '.';
2N/A
2N/A new_listpp = realloc(listpp,
2N/A (sizeof (char **)) * (count + 1));
2N/A if (new_listpp == NULL) {
2N/A error = DSVC_NO_MEMORY;
2N/A goto fail;
2N/A }
2N/A listpp = new_listpp;
2N/A listpp[count] = strdup(ipaddr);
2N/A if (listpp[count] == NULL) {
2N/A error = DSVC_NO_MEMORY;
2N/A goto fail;
2N/A }
2N/A count++;
2N/A }
2N/A }
2N/A free(re);
2N/A (void) closedir(dirp);
2N/A
2N/A *countp = count;
2N/A *listppp = listpp;
2N/A return (DSVC_SUCCESS);
2N/A
2N/Afail:
2N/A free(re);
2N/A (void) closedir(dirp);
2N/A
2N/A for (i = 0; i < count; i++)
2N/A free(listpp[i]);
2N/A free(listpp);
2N/A return (error);
2N/A}
2N/A
2N/A/*
2N/A * Given a buffer `path' of `pathlen' bytes, fill it in with a path to the
2N/A * DHCP Network table for IP network `ip' located in directory `dir' with a
2N/A * suffix of `suffix'.
2N/A */
2N/Astatic void
2N/Anet2path(char *path, size_t pathlen, const char *dir, ipaddr_t ip,
2N/A const char *suffix)
2N/A{
2N/A (void) snprintf(path, pathlen, "%s/SUNWfiles%u_%d_%d_%d_%d%s", dir,
2N/A DSVC_CONVER, ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff,
2N/A ip & 0xff, suffix);
2N/A}
2N/A
2N/A/*
2N/A * Write the dn_rec_t `recp' into the open container `fd' at offset
2N/A * `recoff'. Returns DSVC_* error code.
2N/A */
2N/Astatic int
2N/Awrite_rec(int fd, dn_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 dn_filerec_t dnf;
2N/A struct in_addr nip;
2N/A unsigned int cid_len = sizeof (dnf.dnf_cid);
2N/A
2N/A /*
2N/A * Copy data into a dn_filerec_t, since that's what we can
2N/A * actually put on disk.
2N/A */
2N/A if (octet_to_hexascii(recp->dn_cid, recp->dn_cid_len, dnf.dnf_cid,
2N/A &cid_len) != 0)
2N/A return (DSVC_INTERNAL);
2N/A
2N/A nip.s_addr = htonl(recp->dn_cip.s_addr);
2N/A (void) inet_ntop(AF_INET, &nip, dnf.dnf_cip, sizeof (dnf.dnf_cip));
2N/A nip.s_addr = htonl(recp->dn_sip.s_addr);
2N/A (void) inet_ntop(AF_INET, &nip, dnf.dnf_sip, sizeof (dnf.dnf_cip));
2N/A
2N/A dnf.dnf_sig = recp->dn_sig;
2N/A dnf.dnf_flags = recp->dn_flags;
2N/A dnf.dnf_lease = recp->dn_lease;
2N/A
2N/A escape('|', recp->dn_macro, dnf.dnf_macro, sizeof (dnf.dnf_macro));
2N/A escape('|', recp->dn_comment, dnf.dnf_comment,
2N/A sizeof (dnf.dnf_comment));
2N/Aagain:
2N/A entlen = snprintf(ent, entsize, "%s|%s|%02hu|%s|%u|%llu|%s|%s\n",
2N/A dnf.dnf_cip, dnf.dnf_cid, dnf.dnf_flags, dnf.dnf_sip,
2N/A dnf.dnf_lease, dnf.dnf_sig, dnf.dnf_macro, dnf.dnf_comment);
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}