OpenSolaris_b135/lib/libdhcpsvc/private/confopt.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * This module contains the functions implementing the interface to the
 * /etc/inet/dhcpsvc.conf DHCP service configuration file.
 */

#include <thread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <alloca.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dhcp_svc_confkey.h>
#include <dhcp_svc_confopt.h>
#include <dhcp_svc_private.h>

/*
 * Finds the parameter called key, and returns a reference to it.  Returns
 * NULL if not found or an error occurred.
 */
static dhcp_confopt_t *
find_dhcp_confopt(dhcp_confopt_t *ddp, const char *key)
{
	unsigned int	i;

	if (ddp == NULL || key == NULL)
		return (NULL);

	for (i = 0; ddp[i].co_type != DHCP_END; i++) {
		if (ddp[i].co_type == DHCP_KEY &&
		    strcasecmp(ddp[i].co_key, key) == 0)
			return (&ddp[i]);
	}
	return (NULL);
}

/*
 * Adds a dhcp_confopt_t to the ddpp table. If the table is NULL, one is
 * created. The table is terminated by a NULL entry.  The key and value
 * arguments are copied, not referenced directly.  No check is done to see
 * if the parameter already exists.  Returns 0 for success, nonzero
 * otherwise.
 */
int
add_dsvc_conf(dhcp_confopt_t **ddpp, const char *key, const char *value)
{
	dhcp_confopt_t		*ndp, tdp;
	unsigned int		i;

	if (ddpp == NULL || key == NULL || value == NULL) {
		errno = EINVAL;
		return (-1);
	}

	tdp.co_key = strdup(key);
	tdp.co_type = DHCP_KEY;
	tdp.co_value = strdup(value);
	if (tdp.co_key == NULL || tdp.co_value == NULL) {
		free(tdp.co_key);
		free(tdp.co_value);
		errno = ENOMEM;
		return (-1);
	}

	for (i = 0; *ddpp && (*ddpp)[i].co_key != NULL; i++)
		;

	ndp = realloc(*ddpp, (i + 2) * sizeof (dhcp_confopt_t));
	if (ndp == NULL) {
		free(tdp.co_key);
		free(tdp.co_value);
		errno = ENOMEM;
		return (-1);
	}

	ndp[i] = tdp;
	(void) memset(&ndp[i + 1], 0, sizeof (dhcp_confopt_t));
	*ddpp = ndp;

	return (0);
}

/*
 * Reads the contents of the configuration file into a dynamically
 * allocated array of dhcp_confopt_t records.  A zeroed element marks the
 * end of the array.  Blank lines are ignored.  Caller is responsible for
 * freeing ddp.
 */
int
read_dsvc_conf(dhcp_confopt_t **ddpp)
{
	struct stat	sb;
	int		dd;
	int		error;
	unsigned int	entry;
	char		*cp, *dp, *eol, *value;
	dhcp_confopt_t	confopt, *tdp, *ddp = NULL;
	char		conf[MAXPATHLEN];

	if (ddpp == NULL) {
		errno = EINVAL;
		return (-1);
	}

	(void) snprintf(conf, sizeof (conf), "%s" DHCP_CONFOPT_FILE,
	    DHCP_CONFOPT_ROOT);

	if ((dd = open(conf, O_RDONLY)) == -1)
		return (-1);
	if (fstat(dd, &sb) == -1) {
		error = errno;
		(void) close(dd);
		errno = error;
		return (-1);
	}

	dp = alloca(sb.st_size);
	if (read(dd, dp, sb.st_size) != sb.st_size) {
		error = errno;
		(void) close(dd);
		errno = error;
		return (-1);
	}
	(void) close(dd);

	for (entry = 0, cp = dp; cp < &dp[sb.st_size]; cp = eol + 1) {
		eol = strchr(cp, '\n');
		if (eol == NULL)		/* done parsing file */
			break;
		if (eol == cp) 			/* blank line -- skip */
			continue;
		*eol = '\0';

		if (*cp == '#') {
			confopt.co_type = DHCP_COMMENT;
			confopt.co_comment = strdup(cp + 1);
			if (confopt.co_comment == NULL)
				goto nomem;
		} else {
			value = strchr(cp, '=');
			if (value == NULL)
				continue;
			*value = '\0';

			confopt.co_type = DHCP_KEY;
			confopt.co_key = strdup(cp);
			if (confopt.co_key == NULL)
				goto nomem;

			confopt.co_value = strdup(value + 1);
			if (confopt.co_value == NULL) {
				free(confopt.co_key);
				goto nomem;
			}
		}

		/* always allocate a spare slot for the zeroed entry */
		tdp = realloc(ddp, (entry + 2) * sizeof (dhcp_confopt_t));
		if (tdp == NULL)
			goto nomem;

		tdp[entry] = confopt;
		(void) memset(&tdp[entry + 1], 0, sizeof (dhcp_confopt_t));
		ddp = tdp;
		entry++;
	}

	if (ddp == NULL)
		return (-1);

	*ddpp = ddp;
	return (0);

nomem:
	if (ddp != NULL)
		free_dsvc_conf(ddp);

	errno = ENOMEM;
	return (-1);
}

/*
 * If the requested parameter exists, replace its value with the new
 * value. If it doesn't exist, then add the parameter with the new value.
 * Returns 0 for success, -1 otherwise (errno is set).
 */
int
replace_dsvc_conf(dhcp_confopt_t **ddpp, const char *key, const char *value)
{
	dhcp_confopt_t	*tdp;
	int		err;

	if (ddpp == NULL || key == NULL || value == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if ((tdp = find_dhcp_confopt(*ddpp, key)) != NULL) {
		char	*valp;

		if ((valp = strdup(value)) == NULL)
			return (-1); /* NOMEM */

		if (tdp->co_value != NULL)
			free(tdp->co_value);

		tdp->co_value = valp;

		errno = 0;
		err = 0;
	} else
		err = (add_dsvc_conf(ddpp, key, value) == 0) ? 0 : -1;

	return (err);
}

/*
 * Writes ddp array to the configuration file.  If the configuration file
 * already exists, its contents are replaced with the contents of the ddp
 * array.  If the configuration file does not exist, it is created using
 * the identity of the caller (euid/egid) with the permission bits
 * specified by the mode argument (and modified by the umask).  Caller is
 * responsible for freeing the array.
 */
int
write_dsvc_conf(dhcp_confopt_t *ddp, mode_t mode)
{
	int		tdd;
	ssize_t		bytes;
	size_t		i, size;
	char		*tmpbuf;
	char		tmpconf[MAXPATHLEN], conf[MAXPATHLEN];

	if (ddp == NULL) {
		errno = EINVAL;
		return (-1);
	}

	/* guess at final file size */
	for (i = 0, size = 0; ddp[i].co_type != DHCP_END; i++) {
		if (ddp[i].co_type == DHCP_KEY) {
			size += strlen(ddp[i].co_key) + 1; /* include = */
			size += strlen(ddp[i].co_value) + 1; /* include \n */
		} else
			size += strlen(ddp[i].co_comment) + 2; /* inc # + \n */
	}

	if (size == 0) {
		errno = EINVAL;
		return (-1);
	}

	(void) snprintf(conf, sizeof (conf), "%s" DHCP_CONFOPT_FILE,
	    DHCP_CONFOPT_ROOT);
	(void) snprintf(tmpconf, sizeof (tmpconf),
	    "%s" DHCP_CONFOPT_FILE ".%ld.%u", DHCP_CONFOPT_ROOT, getpid(),
	    thr_self());

	if ((tdd = open(tmpconf, O_CREAT | O_EXCL | O_WRONLY, mode)) < 0)
		return (-1);

	tmpbuf = alloca(size);
	for (i = 0; ddp[i].co_type != DHCP_END; i++) {
		if (ddp[i].co_type == DHCP_KEY)
			(void) snprintf(tmpbuf, size, "%s=%s\n", ddp[i].co_key,
			    ddp[i].co_value);
		else
			(void) snprintf(tmpbuf, size, "#%s\n",
			    ddp[i].co_comment);

		bytes = write(tdd, tmpbuf, strlen(tmpbuf));

		/* Nuke the file if we can't successfully update it */
		if (bytes != strlen(tmpbuf)) {
			(void) close(tdd);
			(void) unlink(tmpconf);
			return (-1);
		}
	}
	(void) close(tdd);

	/* Move new file into place */
	if (rename(tmpconf, conf) < 0) {
		(void) unlink(tmpconf);
		return (-1);
	}

	return (0);
}

/*
 * Frees the memory associated with the ddp array.
 */
void
free_dsvc_conf(dhcp_confopt_t *ddp)
{
	unsigned int	i;

	if (ddp == NULL)
		return;

	for (i = 0; ddp[i].co_type != DHCP_END; i++) {
		if (ddp[i].co_type == DHCP_KEY) {
			free(ddp[i].co_key);
			free(ddp[i].co_value);
		} else
			free(ddp[i].co_comment);
	}
	free(ddp);
}

/*
 * Deletes the configuration file.
 */
int
delete_dsvc_conf(void)
{
	char confpath[MAXPATHLEN];

	(void) snprintf(confpath, sizeof (confpath), "%s" DHCP_CONFOPT_FILE,
	    DHCP_CONFOPT_ROOT);
	return (unlink(confpath));
}

/*
 * Return a copy of the value portion of the named key.  Caller is
 * responsible for freeing value when they're finished using it.  Returns 0
 * for success, -1 otherwise (errno is set).
 */
int
query_dsvc_conf(dhcp_confopt_t *ddp, const char *key, char **value)
{
	dhcp_confopt_t	*tdp;

	if (key == NULL || value == NULL) {
		errno = EINVAL;
		return (-1);
	}
	if ((tdp = find_dhcp_confopt(ddp, key)) != NULL) {
		*value = strdup(tdp->co_value);
		if (*value == NULL) {
			errno = ENOMEM;
			return (-1);
		}
		errno = 0;
		return (0);
	}
	errno = ENOENT;
	*value = NULL;
	return (-1);
}

/*
 * Given a dhcp_confopt_t structure, fill in a dsvc_datastore_t.
 * Data is copied from dhcp_confopt_t structure.
 */
int
confopt_to_datastore(dhcp_confopt_t *ddp, dsvc_datastore_t *dsp)
{
	dhcp_confopt_t	*tdp;

	if (ddp == NULL || dsp == NULL)
		return (DSVC_INVAL);

	tdp = find_dhcp_confopt(ddp, DSVC_CK_CONVER);
	if (tdp == NULL || tdp->co_value == NULL)
		return (DSVC_BAD_CONVER);
	dsp->d_conver = atoi(tdp->co_value);

	if (query_dsvc_conf(ddp, DSVC_CK_RESOURCE, &dsp->d_resource) == -1)
		return (DSVC_BAD_RESOURCE);

	if (query_dsvc_conf(ddp, DSVC_CK_PATH, &dsp->d_location) == -1) {
		free(dsp->d_resource);
		return (DSVC_BAD_PATH);
	}

	/*
	 * RESOURCE_CONFIG is optional - underlying service will complain
	 * if it isn't right.
	 */
	(void) query_dsvc_conf(ddp, DSVC_CK_RESOURCE_CONFIG, &dsp->d_config);

	return (DSVC_SUCCESS);
}