ldma_device.c revision 11833:3b3fe296598b
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Logical Domains Device Agent
29 */
30
31#include <errno.h>
32#include <fcntl.h>
33#include <libdladm.h>
34#include <libdllink.h>
35#include <libds.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <strings.h>
39#include <unistd.h>
40#include <sys/param.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43
44#include "ldma.h"
45
46#define	LDMA_MODULE	LDMA_NAME_DEVICE
47
48#define	LDMA_NVERSIONS	(sizeof (ldma_versions) / sizeof (ds_ver_t))
49#define	LDMA_NHANDLERS	(sizeof (ldma_handlers) / sizeof (ldma_msg_handler_t))
50
51static ldm_msg_func_t ldma_dev_validate_path;
52static ldm_msg_func_t ldma_dev_validate_nic;
53
54static ds_ver_t ldma_versions[] = { { 1, 0 } };
55
56static ldma_msg_handler_t ldma_handlers[] = {
57	{ LDMA_MSGDEV_VALIDATE_PATH, LDMA_MSGFLG_ACCESS_CONTROL,
58	    ldma_dev_validate_path },
59	{ LDMA_MSGDEV_VALIDATE_NIC,  LDMA_MSGFLG_ACCESS_CONTROL,
60	    ldma_dev_validate_nic }
61};
62
63ldma_agent_info_t ldma_device_info = {
64	LDMA_NAME_DEVICE,
65	ldma_versions, LDMA_NVERSIONS,
66	ldma_handlers, LDMA_NHANDLERS
67};
68
69/*ARGSUSED*/
70static ldma_request_status_t
71ldma_dev_validate_path(ds_ver_t *ver, ldma_message_header_t *request,
72    size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
73{
74	ldma_message_header_t *reply = NULL;
75	ldma_request_status_t status;
76	struct stat st;
77	char *path = NULL;
78	uint32_t *path_type, reply_dlen;
79	uint32_t plen;
80	int fd;
81
82	plen = request->msg_info;
83	if (plen == 0 || plen > MAXPATHLEN || plen > request_dlen) {
84		status = LDMA_REQ_INVALID;
85		goto done;
86	}
87
88	path = malloc(plen + 1);
89	if (path == NULL) {
90		status = LDMA_REQ_FAILED;
91		goto done;
92	}
93
94	(void) strncpy(path, LDMA_HDR2DATA(request), plen);
95	path[plen] = '\0';
96
97	LDMA_DBG("VALIDATE_PATH(%s)", path);
98
99	reply_dlen = sizeof (uint32_t);
100	reply = ldma_alloc_result_msg(request, reply_dlen);
101	if (reply == NULL) {
102		status = LDMA_REQ_FAILED;
103		goto done;
104	}
105
106	/* LINTED E_BAD_PTR_CAST_ALIGN */
107	path_type = (uint32_t *)(LDMA_HDR2DATA(reply));
108
109	reply->msg_info = 0x0;
110
111	/* check if path exists */
112	if (stat(path, &st) != 0) {
113
114		LDMA_DBG("VALIDATE_PATH(%s): stat failed with error %d",
115		    path, errno);
116
117		switch (errno) {
118
119		case EACCES:
120		case ELOOP:
121		case ENOENT:
122		case ENOLINK:
123		case ENOTDIR:
124			/* path is inaccessible, the request is completed */
125			status = LDMA_REQ_COMPLETED;
126			break;
127
128		case ENAMETOOLONG:
129			status = LDMA_REQ_INVALID;
130			break;
131
132		default:
133			/* request has failed */
134			status = LDMA_REQ_FAILED;
135			break;
136		}
137
138		goto done;
139	}
140
141	status = LDMA_REQ_COMPLETED;
142
143	reply->msg_info |= LDMA_DEVPATH_EXIST;
144
145	LDMA_DBG("VALIDATE_PATH(%s): file mode = 0x%lx", path, st.st_mode);
146
147	switch (st.st_mode & S_IFMT) {
148
149	case S_IFREG:
150		*path_type = LDMA_DEVPATH_TYPE_FILE;
151		break;
152
153	case S_IFCHR:
154	case S_IFBLK:
155		*path_type = LDMA_DEVPATH_TYPE_DEVICE;
156		break;
157
158	default:
159		/* we don't advertise other types (fifo, directory...) */
160		*path_type = 0;
161	}
162
163	/* check if path can be opened read/write */
164	if ((fd = open(path, O_RDWR)) != -1) {
165		reply->msg_info |= LDMA_DEVPATH_OPENRW | LDMA_DEVPATH_OPENRO;
166		(void) close(fd);
167	} else {
168		LDMA_DBG("VALIDATE_PATH(%s): open RDWR failed with error %d",
169		    path, errno);
170
171		/* check if path can be opened read only */
172		if ((fd = open(path, O_RDONLY)) != -1) {
173			reply->msg_info |= LDMA_DEVPATH_OPENRO;
174			(void) close(fd);
175		} else {
176			LDMA_DBG("VALIDATE_PATH(%s): open RDONLY failed "
177			    "with error %d", path, errno);
178		}
179	}
180
181done:
182	if (status != LDMA_REQ_COMPLETED) {
183		/*
184		 * We don't provide a reply message if the request has not
185		 * been completed. The LDoms agent daemon will send an
186		 * appropriate reply based on the return code of this function.
187		 */
188		free(reply);
189		reply = NULL;
190		reply_dlen = 0;
191
192		LDMA_DBG("VALIDATE_PATH(%s): return error %d",
193		    (path)? path : "<none>", status);
194	} else {
195		LDMA_DBG("VALIDATE_PATH(%s): return status=0x%x type=0x%x",
196		    path, reply->msg_info, *path_type);
197	}
198
199	free(path);
200	*replyp = reply;
201	*reply_dlenp = reply_dlen;
202
203	return (status);
204}
205
206/*
207 * We check that the device is a network interface (NIC) using libdladm.
208 */
209/*ARGSUSED*/
210static ldma_request_status_t
211ldma_dev_validate_nic(ds_ver_t *ver, ldma_message_header_t *request,
212    size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
213{
214	dladm_handle_t dlhandle;
215	datalink_id_t linkid;
216	uint32_t flag, media;
217	datalink_class_t class;
218	ldma_message_header_t *reply = NULL;
219	ldma_request_status_t status;
220	char *nic = NULL;
221	uint32_t nlen, reply_dlen;
222
223	nlen = request->msg_info;
224	if (nlen == 0 || nlen > MAXPATHLEN || nlen > request_dlen) {
225		status = LDMA_REQ_INVALID;
226		goto done;
227	}
228
229	nic = malloc(nlen + 1);
230	if (nic == NULL) {
231		status = LDMA_REQ_FAILED;
232		goto done;
233	}
234
235	(void) strncpy(nic, LDMA_HDR2DATA(request), nlen);
236	nic[nlen] = '\0';
237
238	LDMA_DBG("VALIDATE_NIC(%s)", nic);
239
240	reply_dlen = 0;
241	reply = ldma_alloc_result_msg(request, reply_dlen);
242	if (reply == NULL) {
243		status = LDMA_REQ_FAILED;
244		goto done;
245	}
246
247	reply->msg_info = 0x0;
248
249	if (dladm_open(&dlhandle) != DLADM_STATUS_OK) {
250		status = LDMA_REQ_FAILED;
251		goto done;
252	}
253
254	if (dladm_name2info(dlhandle, nic, &linkid, &flag, &class,
255	    &media) != DLADM_STATUS_OK) {
256		LDMA_DBG("VALIDATE_NIC(%s): name2info failed", nic);
257	} else {
258		LDMA_DBG("VALIDATE_NIC(%s): media=0x%x", nic, media);
259		reply->msg_info = LDMA_DEVNIC_EXIST;
260	}
261
262	dladm_close(dlhandle);
263
264	status = LDMA_REQ_COMPLETED;
265
266done:
267	if (status != LDMA_REQ_COMPLETED) {
268		/*
269		 * We don't provide a reply message if the request has not
270		 * been completed. The LDoms agent daemon will send an
271		 * appropriate reply based on the return code of this function.
272		 */
273		free(reply);
274		reply = NULL;
275		reply_dlen = 0;
276
277		LDMA_DBG("VALIDATE_NIC(%s): return error %d",
278		    (nic)? nic : "<none>", status);
279	} else {
280		LDMA_DBG("VALIDATE_NIC(%s): return status=0x%x",
281		    nic, reply->msg_info);
282	}
283
284	free(nic);
285	*replyp = reply;
286	*reply_dlenp = reply_dlen;
287
288	return (status);
289}
290