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