OpenSolaris_b135/lib/libbsm/common/getdment.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 (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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <bsm/devices.h>
#include <bsm/devalloc.h>

char *strtok_r(char *, const char *, char **);

/* externs from getdaent.c */
extern char *trim_white(char *);
extern int pack_white(char *);
extern char *getdadmfield(char *, char *);
extern int getdadmline(char *, int, FILE *);

static struct _dmapbuff {
	FILE		*_dmapf;	/* for /etc/security/device_maps */
	devmap_t	_interpdevmap;
	char		_interpdmline[DA_BUFSIZE + 1];
	char		*_DEVMAP;
} *__dmapbuff;

#define	dmapf	(_dmap->_dmapf)
#define	interpdevmap	(_dmap->_interpdevmap)
#define	interpdmline	(_dmap->_interpdmline)
#define	DEVMAPS_FILE	(_dmap->_DEVMAP)

devmap_t	*dmap_interpret(char *, devmap_t *);
static devmap_t	*dmap_interpretf(char *, devmap_t *);
static devmap_t *dmap_dlexpand(devmap_t *);

int	dmap_matchdev(devmap_t *, char *);
int	dmap_matchname(devmap_t *, char *);


/*
 * _dmapalloc -
 *	allocates common buffers and structures.
 *	returns pointer to the new structure, else returns NULL on error.
 */
static struct _dmapbuff *
_dmapalloc(void)
{
	struct _dmapbuff *_dmap = __dmapbuff;

	if (_dmap == NULL) {
		_dmap = (struct _dmapbuff *)calloc((unsigned)1,
		    (unsigned)sizeof (*__dmapbuff));
		if (_dmap == NULL)
			return (NULL);
		DEVMAPS_FILE = "/etc/security/device_maps";
		dmapf = NULL;
		__dmapbuff = _dmap;
	}

	return (_dmap);
}

/*
 * setdmapent -
 *	rewinds the device_maps file to the beginning.
 */
void
setdmapent(void)
{
	struct _dmapbuff *_dmap = _dmapalloc();

	if (_dmap == NULL)
		return;
	if (dmapf == NULL)
		dmapf = fopen(DEVMAPS_FILE, "rF");
	else
		rewind(dmapf);
}

/*
 * enddmapent -
 *	closes device_maps file.
 */
void
enddmapent(void)
{
	struct _dmapbuff *_dmap = _dmapalloc();

	if (_dmap == NULL)
		return;
	if (dmapf != NULL) {
		(void) fclose(dmapf);
		dmapf = NULL;
	}
}

void
freedmapent(devmap_t *dmap)
{
	char	**darp;

	if ((darp = dmap->dmap_devarray) != NULL) {
		while (*darp != NULL)
			free(*darp++);
		free(dmap->dmap_devarray);
		dmap->dmap_devarray = NULL;
	}
}

/*
 * setdmapfile -
 *	changes the default device_maps file to the one specified.
 *	It does not close the previous file. If this is desired, enddmapent
 *	should be called prior to setdampfile.
 */
void
setdmapfile(char *file)
{
	struct _dmapbuff *_dmap = _dmapalloc();

	if (_dmap == NULL)
		return;
	if (dmapf != NULL) {
		(void) fclose(dmapf);
		dmapf = NULL;
	}
	DEVMAPS_FILE = file;
}

/*
 * getdmapent -
 * 	When first called, returns a pointer to the first devmap_t structure
 * 	in device_maps; thereafter, it returns a pointer to the next devmap_t
 *	structure in the file. Thus successive calls can be used to read the
 *	entire file.
 *	call to getdmapent should be bracketed by setdmapent and enddmapent.
 * 	returns pointer to devmap_t found, else returns NULL if no entry found
 * 	or on error.
 */
devmap_t *
getdmapent(void)
{
	devmap_t		*dmap;
	struct _dmapbuff 	*_dmap = _dmapalloc();

	if ((_dmap == 0) || (dmapf == NULL))
		return (NULL);

	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
	    dmapf) != 0) {
		if ((dmap = dmap_interpret(interpdmline,
		    &interpdevmap)) == NULL)
			continue;
		return (dmap);
	}

	return (NULL);
}

/*
 * getdmapnam -
 *	searches from the beginning of device_maps for the device specified by
 *	its name.
 *	call to getdmapnam should be bracketed by setdmapent and enddmapent.
 * 	returns pointer to devmapt_t for the device if it is found, else
 * 	returns NULL if device not found or in case of error.
 */
devmap_t *
getdmapnam(char *name)
{
	devmap_t		*dmap;
	struct _dmapbuff	*_dmap = _dmapalloc();

	if ((name == NULL) || (_dmap == 0) || (dmapf == NULL))
		return (NULL);

	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
	    dmapf) != 0) {
		if (strstr(interpdmline, name) == NULL)
			continue;
		if ((dmap = dmap_interpretf(interpdmline,
		    &interpdevmap)) == NULL)
			continue;
		if (dmap_matchname(dmap, name)) {
			if ((dmap = dmap_dlexpand(dmap)) == NULL)
				continue;
			enddmapent();
			return (dmap);
		}
		freedmapent(dmap);
	}

	return (NULL);
}

/*
 * getdmapdev -
 *	searches from the beginning of device_maps for the device specified by
 *	its logical name.
 *	call to getdmapdev should be bracketed by setdmapent and enddmapent.
 * 	returns  pointer to the devmap_t for the device if device is found,
 *	else returns NULL if device not found or on error.
 */
devmap_t *
getdmapdev(char *dev)
{
	devmap_t		*dmap;
	struct _dmapbuff	*_dmap = _dmapalloc();

	if ((dev == NULL) || (_dmap == 0) || (dmapf == NULL))
		return (NULL);

	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
	    dmapf) != 0) {
		if ((dmap = dmap_interpret(interpdmline,
		    &interpdevmap)) == NULL)
			continue;
		if (dmap_matchdev(dmap, dev)) {
			enddmapent();
			return (dmap);
		}
		freedmapent(dmap);
	}

	return (NULL);
}

/*
 * getdmaptype -
 *	searches from the beginning of device_maps for the device specified by
 *	its type.
 *	call to getdmaptype should be bracketed by setdmapent and enddmapent.
 * 	returns pointer to devmap_t found, else returns NULL if no entry found
 * 	or on error.
 */
devmap_t *
getdmaptype(char *type)
{
	devmap_t		*dmap;
	struct _dmapbuff	*_dmap = _dmapalloc();

	if ((type == NULL) || (_dmap == 0) || (dmapf == NULL))
		return (NULL);

	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
	    dmapf) != 0) {
		if ((dmap = dmap_interpretf(interpdmline,
		    &interpdevmap)) == NULL)
			continue;
		if (dmap->dmap_devtype != NULL &&
		    strcmp(type, dmap->dmap_devtype) == 0) {
			if ((dmap = dmap_dlexpand(dmap)) == NULL)
				continue;
			return (dmap);
		}
		freedmapent(dmap);
	}

	return (NULL);
}

/*
 * dmap_match_one_dev -
 *    Checks if the specified devmap_t contains strings
 *    for the same logical link as the device specified.
 *    This guarantees that the beginnings of a devlist build
 *    match a more-complete devlist for the same device.
 *
 *    Returns 1 for a match, else returns 0.
 */
static int
dmap_match_one_dev(devmap_t *dmap, char *dev)
{
	char **dva;
	char *dv;

	if (dmap->dmap_devarray == NULL)
		return (0);

	for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva++) {
		if (strstr(dev, dv) != NULL)
			return (1);
	}
	return (0);
}

/*
 * dmap_matchdev -
 * 	checks if the specified devmap_t is for the device specified.
 *	returns 1 if it is, else returns 0.
 */
int
dmap_matchdev(devmap_t *dmap, char *dev)
{
	char **dva;
	char *dv;

	if (dmap->dmap_devarray == NULL)
		return (0);
	for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva ++) {
		if (strcmp(dv, dev) == 0)
			return (1);
	}

	return (0);
}

/*
 * Requires a match of the /dev/?dsk links, not just the logical devname
 * Returns 1 for match found, 0 for match not found, 2 for invalid arguments.
 */
int
dmap_exact_dev(devmap_t *dmap, char *dev, int *num)
{
	char *dv;

	if ((dev == NULL) || (dmap->dmap_devname == NULL))
		return (2);
	dv = dmap->dmap_devname;
	dv +=  strcspn(dmap->dmap_devname, "0123456789");
	if (sscanf(dv, "%d", num) != 1)
		return (2);
	/* during some add processes, dev can be shorter than dmap */
	return (dmap_match_one_dev(dmap, dev));
}

/*
 * dmap_matchtype -
 *	checks if the specified devmap_t is for the device specified.
 *	returns 1 if it is, else returns 0.
 */
int
dmap_matchtype(devmap_t *dmap, char *type)
{
	if ((dmap->dmap_devtype == NULL) || (type == NULL))
		return (0);

	return ((strcmp(dmap->dmap_devtype, type) == 0));
}

/*
 * dmap_matchname -
 * 	checks if the specified devmap_t is for the device specified.
 * 	returns 1 if it is, else returns 0.
 */
int
dmap_matchname(devmap_t *dmap, char *name)
{
	if (dmap->dmap_devname == NULL)
		return (0);

	return ((strcmp(dmap->dmap_devname, name) == 0));
}

/*
 * Temporarily duplicated directly from libdevinfo's is_minor_node(),
 * and renamed, because we cannot link this from libdevinfo.
 *
 * To be resolved in a couple of builds.
 *
 * The real fix is to move device allocation out of libbsm.
 *
 * returns 1 if contents is a minor node in /devices.
 * If mn_root is not NULL, mn_root is set to:
 *	if contents is a /dev node, mn_root = contents
 *			OR
 *	if contents is a /devices node, mn_root set to the '/'
 *	following /devices.
 */
static int
dmap_minor_root(const char *contents, const char **mn_root)
{
	char *ptr, *prefix;

	prefix = "../devices/";

	if ((ptr = strstr(contents, prefix)) != NULL) {

		/* mn_root should point to the / following /devices */
		if (mn_root != NULL) {
			*mn_root = ptr += strlen(prefix) - 1;
		}
		return (1);
	}

	prefix = "/devices/";

	if (strncmp(contents, prefix, strlen(prefix)) == 0) {

		/* mn_root should point to the / following /devices/ */
		if (mn_root != NULL) {
			*mn_root = contents + strlen(prefix) - 1;
		}
		return (1);
	}

	if (mn_root != NULL) {
		*mn_root = contents;
	}
	return (0);
}

/*
 * Temporarily duplicated directly from libdevinfo's devfs_resolve_link(),
 * and renamed, because we cannot link this from libdevinfo now, and will
 * create a sensible solution in the near future.
 *
 * returns 0 if resolved, -1 otherwise.
 * devpath: Absolute path to /dev link
 * devfs_path: Returns malloced string: /devices path w/out "/devices"
 */
static int
dmap_resolve_link(char *devpath, char **devfs_path)
{
	char contents[PATH_MAX + 1];
	char stage_link[PATH_MAX + 1];
	char *ptr;
	int linksize;
	char *slashdev = "/dev/";

	if (devfs_path) {
		*devfs_path = NULL;
	}

	linksize = readlink(devpath, contents, PATH_MAX);

	if (linksize <= 0) {
		return (-1);
	} else {
		contents[linksize] = '\0';
	}

	/*
	 * if the link contents is not a minor node assume
	 * that link contents is really a pointer to another
	 * link, and if so recurse and read its link contents.
	 */
	if (dmap_minor_root((const char *)contents, (const char **)&ptr) !=
	    1) {
		if (strncmp(contents, slashdev, strlen(slashdev)) == 0)  {
			/* absolute path, starting with /dev */
			(void) strcpy(stage_link, contents);
		} else {
			/* relative path, prefix devpath */
			if ((ptr = strrchr(devpath, '/')) == NULL) {
				/* invalid link */
				return (-1);
			}
			*ptr = '\0';
			(void) strcpy(stage_link, devpath);
			*ptr = '/';
			(void) strcat(stage_link, "/");
			(void) strcat(stage_link, contents);

		}
		return (dmap_resolve_link(stage_link, devfs_path));
	}

	if (devfs_path) {
		*devfs_path = strdup(ptr);
		if (*devfs_path == NULL) {
			return (-1);
		}
	}

	return (0);
}

/*
 * dmap_physname: path to /devices device
 * Returns:
 *	strdup'd (i.e. malloc'd) real device file if successful
 *      NULL on error
 */
char *
dmap_physname(devmap_t *dmap)
{
	char *oldlink;
	char stage_link[PATH_MAX + 1];

	if ((dmap == NULL) || (dmap->dmap_devarray == NULL) ||
	    (dmap->dmap_devarray[0] == NULL))
		return (NULL);

	(void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link));

	if (dmap_resolve_link(stage_link, &oldlink) == 0)
		return (oldlink);
	return (NULL);
}

/*
 * dm_match -
 *	calls dmap_matchname or dmap_matchtype as appropriate.
 */
int
dm_match(devmap_t *dmap, da_args *dargs)
{
	if (dargs->devinfo->devname)
		return (dmap_matchname(dmap, dargs->devinfo->devname));
	else if (dargs->devinfo->devtype)
		return (dmap_matchtype(dmap, dargs->devinfo->devtype));

	return (0);
}

/*
 * dmap_interpret -
 *	calls dmap_interpretf and dmap_dlexpand to parse devmap_t line.
 *	returns  pointer to parsed devmapt_t entry, else returns NULL on error.
 */
devmap_t  *
dmap_interpret(char *val, devmap_t *dm)
{
	if (dmap_interpretf(val, dm) == NULL)
		return (NULL);

	return (dmap_dlexpand(dm));
}

/*
 * dmap_interpretf -
 * 	parses string "val" and initializes pointers in the given devmap_t to
 * 	fields in "val".
 * 	returns pointer to updated devmap_t.
 */
static devmap_t  *
dmap_interpretf(char *val, devmap_t *dm)
{
	dm->dmap_devname = getdadmfield(val, KV_TOKEN_DELIMIT);
	dm->dmap_devtype = getdadmfield(NULL, KV_TOKEN_DELIMIT);
	dm->dmap_devlist = getdadmfield(NULL, KV_TOKEN_DELIMIT);
	dm->dmap_devarray = NULL;
	if (dm->dmap_devname == NULL ||
	    dm->dmap_devtype == NULL ||
	    dm->dmap_devlist == NULL)
		return (NULL);

	return (dm);
}

/*
 * dmap_dlexpand -
 * 	expands dmap_devlist of the form `devlist_generate`
 *	returns unexpanded form if there is no '\`' or in case of error.
 */
static devmap_t *
dmap_dlexpand(devmap_t *dmp)
{
	char	tmplist[DA_BUFSIZE + 1];
	char	*cp, *cpl, **darp;
	int	count;
	FILE	*expansion;

	dmp->dmap_devarray = NULL;
	if (dmp->dmap_devlist == NULL)
		return (NULL);
	if (*(dmp->dmap_devlist) != '`') {
		(void) strcpy(tmplist, dmp->dmap_devlist);
	} else {
		(void) strcpy(tmplist, dmp->dmap_devlist + 1);
		if ((cp = strchr(tmplist, '`')) != NULL)
			*cp = '\0';
		if ((expansion = popen(tmplist, "rF")) == NULL)
			return (NULL);
		count = fread(tmplist, 1, sizeof (tmplist) - 1, expansion);
		(void) pclose(expansion);
		tmplist[count] = '\0';
	}

	/* cleanup the list */
	count = pack_white(tmplist);
	dmp->dmap_devarray = darp =
	    (char **)malloc((count + 2) * sizeof (char *));
	if (darp == NULL)
		return (NULL);
	cp = tmplist;
	while ((cp = strtok_r(cp, " ", &cpl)) != NULL) {
		*darp = strdup(cp);
		if (*darp == NULL) {
			freedmapent(dmp);
			return (NULL);
		}
		darp++;
		cp = NULL;
	}
	*darp = NULL;

	return (dmp);
}

/*
 * dmapskip -
 * 	scans input string to find next colon or end of line.
 *	returns pointer to next char.
 */
static char *
dmapskip(char *p)
{
	while (*p && *p != ':' && *p != '\n')
		++p;
	if (*p == '\n')
		*p = '\0';
	else if (*p != '\0')
		*p++ = '\0';

	return (p);
}

/*
 * dmapdskip -
 * 	scans input string to find next space or end of line.
 *	returns pointer to next char.
 */
static char *
dmapdskip(p)
	register char *p;
{
	while (*p && *p != ' ' && *p != '\n')
		++p;
	if (*p != '\0')
		*p++ = '\0';

	return (p);
}

char *
getdmapfield(char *ptr)
{
	static	char	*tptr;

	if (ptr == NULL)
		ptr = tptr;
	if (ptr == NULL)
		return (NULL);
	tptr = dmapskip(ptr);
	ptr = trim_white(ptr);
	if (ptr == NULL)
		return (NULL);
	if (*ptr == NULL)
		return (NULL);

	return (ptr);
}

char *
getdmapdfield(char *ptr)
{
	static	char	*tptr;
	if (ptr != NULL) {
		ptr = trim_white(ptr);
	} else {
		ptr = tptr;
	}
	if (ptr == NULL)
		return (NULL);
	tptr = dmapdskip(ptr);
	if (ptr == NULL)
		return (NULL);
	if (*ptr == NULL)
		return (NULL);

	return (ptr);
}