/*
* 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);
}