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
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)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
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)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