OpenSolaris_b135/cmd/ldmad/ldma_device.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.
 */

/*
 * Logical Domains Device Agent
 */

#include <errno.h>
#include <fcntl.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libds.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "ldma.h"

#define	LDMA_MODULE	LDMA_NAME_DEVICE

#define	LDMA_NVERSIONS	(sizeof (ldma_versions) / sizeof (ds_ver_t))
#define	LDMA_NHANDLERS	(sizeof (ldma_handlers) / sizeof (ldma_msg_handler_t))

static ldm_msg_func_t ldma_dev_validate_path;
static ldm_msg_func_t ldma_dev_validate_nic;

static ds_ver_t ldma_versions[] = { { 1, 0 } };

static ldma_msg_handler_t ldma_handlers[] = {
	{ LDMA_MSGDEV_VALIDATE_PATH, LDMA_MSGFLG_ACCESS_CONTROL,
	    ldma_dev_validate_path },
	{ LDMA_MSGDEV_VALIDATE_NIC,  LDMA_MSGFLG_ACCESS_CONTROL,
	    ldma_dev_validate_nic }
};

ldma_agent_info_t ldma_device_info = {
	LDMA_NAME_DEVICE,
	ldma_versions, LDMA_NVERSIONS,
	ldma_handlers, LDMA_NHANDLERS
};

/*ARGSUSED*/
static ldma_request_status_t
ldma_dev_validate_path(ds_ver_t *ver, ldma_message_header_t *request,
    size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
{
	ldma_message_header_t *reply = NULL;
	ldma_request_status_t status;
	struct stat st;
	char *path = NULL;
	uint32_t *path_type, reply_dlen;
	uint32_t plen;
	int fd;

	plen = request->msg_info;
	if (plen == 0 || plen > MAXPATHLEN || plen > request_dlen) {
		status = LDMA_REQ_INVALID;
		goto done;
	}

	path = malloc(plen + 1);
	if (path == NULL) {
		status = LDMA_REQ_FAILED;
		goto done;
	}

	(void) strncpy(path, LDMA_HDR2DATA(request), plen);
	path[plen] = '\0';

	LDMA_DBG("VALIDATE_PATH(%s)", path);

	reply_dlen = sizeof (uint32_t);
	reply = ldma_alloc_result_msg(request, reply_dlen);
	if (reply == NULL) {
		status = LDMA_REQ_FAILED;
		goto done;
	}

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	path_type = (uint32_t *)(LDMA_HDR2DATA(reply));

	reply->msg_info = 0x0;

	/* check if path exists */
	if (stat(path, &st) != 0) {

		LDMA_DBG("VALIDATE_PATH(%s): stat failed with error %d",
		    path, errno);

		switch (errno) {

		case EACCES:
		case ELOOP:
		case ENOENT:
		case ENOLINK:
		case ENOTDIR:
			/* path is inaccessible, the request is completed */
			status = LDMA_REQ_COMPLETED;
			break;

		case ENAMETOOLONG:
			status = LDMA_REQ_INVALID;
			break;

		default:
			/* request has failed */
			status = LDMA_REQ_FAILED;
			break;
		}

		goto done;
	}

	status = LDMA_REQ_COMPLETED;

	reply->msg_info |= LDMA_DEVPATH_EXIST;

	LDMA_DBG("VALIDATE_PATH(%s): file mode = 0x%lx", path, st.st_mode);

	switch (st.st_mode & S_IFMT) {

	case S_IFREG:
		*path_type = LDMA_DEVPATH_TYPE_FILE;
		break;

	case S_IFCHR:
	case S_IFBLK:
		*path_type = LDMA_DEVPATH_TYPE_DEVICE;
		break;

	default:
		/* we don't advertise other types (fifo, directory...) */
		*path_type = 0;
	}

	/* check if path can be opened read/write */
	if ((fd = open(path, O_RDWR)) != -1) {
		reply->msg_info |= LDMA_DEVPATH_OPENRW | LDMA_DEVPATH_OPENRO;
		(void) close(fd);
	} else {
		LDMA_DBG("VALIDATE_PATH(%s): open RDWR failed with error %d",
		    path, errno);

		/* check if path can be opened read only */
		if ((fd = open(path, O_RDONLY)) != -1) {
			reply->msg_info |= LDMA_DEVPATH_OPENRO;
			(void) close(fd);
		} else {
			LDMA_DBG("VALIDATE_PATH(%s): open RDONLY failed "
			    "with error %d", path, errno);
		}
	}

done:
	if (status != LDMA_REQ_COMPLETED) {
		/*
		 * We don't provide a reply message if the request has not
		 * been completed. The LDoms agent daemon will send an
		 * appropriate reply based on the return code of this function.
		 */
		free(reply);
		reply = NULL;
		reply_dlen = 0;

		LDMA_DBG("VALIDATE_PATH(%s): return error %d",
		    (path)? path : "<none>", status);
	} else {
		LDMA_DBG("VALIDATE_PATH(%s): return status=0x%x type=0x%x",
		    path, reply->msg_info, *path_type);
	}

	free(path);
	*replyp = reply;
	*reply_dlenp = reply_dlen;

	return (status);
}

/*
 * We check that the device is a network interface (NIC) using libdladm.
 */
/*ARGSUSED*/
static ldma_request_status_t
ldma_dev_validate_nic(ds_ver_t *ver, ldma_message_header_t *request,
    size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
{
	dladm_handle_t dlhandle;
	datalink_id_t linkid;
	uint32_t flag, media;
	datalink_class_t class;
	ldma_message_header_t *reply = NULL;
	ldma_request_status_t status;
	char *nic = NULL;
	uint32_t nlen, reply_dlen;

	nlen = request->msg_info;
	if (nlen == 0 || nlen > MAXPATHLEN || nlen > request_dlen) {
		status = LDMA_REQ_INVALID;
		goto done;
	}

	nic = malloc(nlen + 1);
	if (nic == NULL) {
		status = LDMA_REQ_FAILED;
		goto done;
	}

	(void) strncpy(nic, LDMA_HDR2DATA(request), nlen);
	nic[nlen] = '\0';

	LDMA_DBG("VALIDATE_NIC(%s)", nic);

	reply_dlen = 0;
	reply = ldma_alloc_result_msg(request, reply_dlen);
	if (reply == NULL) {
		status = LDMA_REQ_FAILED;
		goto done;
	}

	reply->msg_info = 0x0;

	if (dladm_open(&dlhandle) != DLADM_STATUS_OK) {
		status = LDMA_REQ_FAILED;
		goto done;
	}

	if (dladm_name2info(dlhandle, nic, &linkid, &flag, &class,
	    &media) != DLADM_STATUS_OK) {
		LDMA_DBG("VALIDATE_NIC(%s): name2info failed", nic);
	} else {
		LDMA_DBG("VALIDATE_NIC(%s): media=0x%x", nic, media);
		reply->msg_info = LDMA_DEVNIC_EXIST;
	}

	dladm_close(dlhandle);

	status = LDMA_REQ_COMPLETED;

done:
	if (status != LDMA_REQ_COMPLETED) {
		/*
		 * We don't provide a reply message if the request has not
		 * been completed. The LDoms agent daemon will send an
		 * appropriate reply based on the return code of this function.
		 */
		free(reply);
		reply = NULL;
		reply_dlen = 0;

		LDMA_DBG("VALIDATE_NIC(%s): return error %d",
		    (nic)? nic : "<none>", status);
	} else {
		LDMA_DBG("VALIDATE_NIC(%s): return status=0x%x",
		    nic, reply->msg_info);
	}

	free(nic);
	*replyp = reply;
	*reply_dlenp = reply_dlen;

	return (status);
}