xref: /illumos-gate/usr/src/cmd/ldmad/ldma_device.c (revision f2ba9e96867935dc624ece52573c174612f72825)
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 
51 static ldm_msg_func_t ldma_dev_validate_path;
52 static ldm_msg_func_t ldma_dev_validate_nic;
53 
54 static ds_ver_t ldma_versions[] = { { 1, 0 } };
55 
56 static 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 
63 ldma_agent_info_t ldma_device_info = {
64 	LDMA_NAME_DEVICE,
65 	ldma_versions, LDMA_NVERSIONS,
66 	ldma_handlers, LDMA_NHANDLERS
67 };
68 
69 /*ARGSUSED*/
70 static ldma_request_status_t
71 ldma_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 
181 done:
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*/
210 static ldma_request_status_t
211 ldma_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 
266 done:
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