xref: /illumos-gate/usr/src/cmd/ldmad/ldma_device.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
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 2009 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_dev_validate_path },
58 	{ LDMA_MSGDEV_VALIDATE_NIC,	ldma_dev_validate_nic }
59 };
60 
61 ldma_agent_info_t ldma_device_info = {
62 	LDMA_NAME_DEVICE,
63 	ldma_versions, LDMA_NVERSIONS,
64 	ldma_handlers, LDMA_NHANDLERS
65 };
66 
67 /*ARGSUSED*/
68 static ldma_request_status_t
69 ldma_dev_validate_path(ds_ver_t *ver, ldma_message_header_t *request,
70     size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
71 {
72 	ldma_message_header_t *reply = NULL;
73 	ldma_request_status_t status;
74 	struct stat st;
75 	char *path = NULL;
76 	uint32_t *path_type, reply_dlen;
77 	uint32_t plen;
78 	int fd;
79 
80 	plen = request->msg_info;
81 	if (plen == 0 || plen > MAXPATHLEN || plen > request_dlen) {
82 		status = LDMA_REQ_INVALID;
83 		goto done;
84 	}
85 
86 	path = malloc(plen + 1);
87 	if (path == NULL) {
88 		status = LDMA_REQ_FAILED;
89 		goto done;
90 	}
91 
92 	(void) strncpy(path, LDMA_HDR2DATA(request), plen);
93 	path[plen] = '\0';
94 
95 	LDMA_DBG("VALIDATE_PATH(%s)", path);
96 
97 	reply_dlen = sizeof (uint32_t);
98 	reply = ldma_alloc_result_msg(request, reply_dlen);
99 	if (reply == NULL) {
100 		status = LDMA_REQ_FAILED;
101 		goto done;
102 	}
103 
104 	/* LINTED E_BAD_PTR_CAST_ALIGN */
105 	path_type = (uint32_t *)(LDMA_HDR2DATA(reply));
106 
107 	reply->msg_info = 0x0;
108 
109 	/* check if path exists */
110 	if (stat(path, &st) != 0) {
111 
112 		LDMA_DBG("VALIDATE_PATH(%s): stat failed with error %d",
113 		    path, errno);
114 
115 		switch (errno) {
116 
117 		case EACCES:
118 		case ELOOP:
119 		case ENOENT:
120 		case ENOLINK:
121 		case ENOTDIR:
122 			/* path is inaccessible, the request is completed */
123 			status = LDMA_REQ_COMPLETED;
124 			break;
125 
126 		case ENAMETOOLONG:
127 			status = LDMA_REQ_INVALID;
128 			break;
129 
130 		default:
131 			/* request has failed */
132 			status = LDMA_REQ_FAILED;
133 			break;
134 		}
135 
136 		goto done;
137 	}
138 
139 	status = LDMA_REQ_COMPLETED;
140 
141 	reply->msg_info |= LDMA_DEVPATH_EXIST;
142 
143 	LDMA_DBG("VALIDATE_PATH(%s): file mode = 0x%lx", path, st.st_mode);
144 
145 	switch (st.st_mode & S_IFMT) {
146 
147 	case S_IFREG:
148 		*path_type = LDMA_DEVPATH_TYPE_FILE;
149 		break;
150 
151 	case S_IFCHR:
152 	case S_IFBLK:
153 		*path_type = LDMA_DEVPATH_TYPE_DEVICE;
154 		break;
155 
156 	default:
157 		/* we don't advertise other types (fifo, directory...) */
158 		*path_type = 0;
159 	}
160 
161 	/* check if path can be opened read/write */
162 	if ((fd = open(path, O_RDWR)) != -1) {
163 		reply->msg_info |= LDMA_DEVPATH_OPENRW | LDMA_DEVPATH_OPENRO;
164 		(void) close(fd);
165 	} else {
166 		LDMA_DBG("VALIDATE_PATH(%s): open RDWR failed with error %d",
167 		    path, errno);
168 
169 		/* check if path can be opened read only */
170 		if ((fd = open(path, O_RDONLY)) != -1) {
171 			reply->msg_info |= LDMA_DEVPATH_OPENRO;
172 			(void) close(fd);
173 		} else {
174 			LDMA_DBG("VALIDATE_PATH(%s): open RDONLY failed "
175 			    "with error %d", path, errno);
176 		}
177 	}
178 
179 done:
180 	if (status != LDMA_REQ_COMPLETED) {
181 		/*
182 		 * We don't provide a reply message if the request has not
183 		 * been completed. The LDoms agent daemon will send an
184 		 * appropriate reply based on the return code of this function.
185 		 */
186 		free(reply);
187 		reply = NULL;
188 		reply_dlen = 0;
189 
190 		LDMA_DBG("VALIDATE_PATH(%s): return error %d",
191 		    (path)? path : "<none>", status);
192 	} else {
193 		LDMA_DBG("VALIDATE_PATH(%s): return status=0x%x type=0x%x",
194 		    path, reply->msg_info, *path_type);
195 	}
196 
197 	free(path);
198 	*replyp = reply;
199 	*reply_dlenp = reply_dlen;
200 
201 	return (status);
202 }
203 
204 /*
205  * We check that the device is a network interface (NIC) using libdladm.
206  */
207 /*ARGSUSED*/
208 static ldma_request_status_t
209 ldma_dev_validate_nic(ds_ver_t *ver, ldma_message_header_t *request,
210     size_t request_dlen, ldma_message_header_t **replyp, size_t *reply_dlenp)
211 {
212 	dladm_handle_t dlhandle;
213 	datalink_id_t linkid;
214 	uint32_t flag, media;
215 	datalink_class_t class;
216 	ldma_message_header_t *reply = NULL;
217 	ldma_request_status_t status;
218 	char *nic = NULL;
219 	uint32_t nlen, reply_dlen;
220 
221 	nlen = request->msg_info;
222 	if (nlen == 0 || nlen > MAXPATHLEN || nlen > request_dlen) {
223 		status = LDMA_REQ_INVALID;
224 		goto done;
225 	}
226 
227 	nic = malloc(nlen + 1);
228 	if (nic == NULL) {
229 		status = LDMA_REQ_FAILED;
230 		goto done;
231 	}
232 
233 	(void) strncpy(nic, LDMA_HDR2DATA(request), nlen);
234 	nic[nlen] = '\0';
235 
236 	LDMA_DBG("VALIDATE_NIC(%s)", nic);
237 
238 	reply_dlen = 0;
239 	reply = ldma_alloc_result_msg(request, reply_dlen);
240 	if (reply == NULL) {
241 		status = LDMA_REQ_FAILED;
242 		goto done;
243 	}
244 
245 	reply->msg_info = 0x0;
246 
247 	if (dladm_open(&dlhandle) != DLADM_STATUS_OK) {
248 		status = LDMA_REQ_FAILED;
249 		goto done;
250 	}
251 
252 	if (dladm_name2info(dlhandle, nic, &linkid, &flag, &class,
253 	    &media) != DLADM_STATUS_OK) {
254 		LDMA_DBG("VALIDATE_NIC(%s): name2info failed", nic);
255 	} else {
256 		LDMA_DBG("VALIDATE_NIC(%s): media=0x%x", nic, media);
257 		reply->msg_info = LDMA_DEVNIC_EXIST;
258 	}
259 
260 	dladm_close(dlhandle);
261 
262 	status = LDMA_REQ_COMPLETED;
263 
264 done:
265 	if (status != LDMA_REQ_COMPLETED) {
266 		/*
267 		 * We don't provide a reply message if the request has not
268 		 * been completed. The LDoms agent daemon will send an
269 		 * appropriate reply based on the return code of this function.
270 		 */
271 		free(reply);
272 		reply = NULL;
273 		reply_dlen = 0;
274 
275 		LDMA_DBG("VALIDATE_NIC(%s): return error %d",
276 		    (nic)? nic : "<none>", status);
277 	} else {
278 		LDMA_DBG("VALIDATE_NIC(%s): return status=0x%x",
279 		    nic, reply->msg_info);
280 	}
281 
282 	free(nic);
283 	*replyp = reply;
284 	*reply_dlenp = reply_dlen;
285 
286 	return (status);
287 }
288