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%x", 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