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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <unistd.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <stdarg.h> 38 #include <syslog.h> 39 #include <libdevice.h> 40 #include <sys/fibre-channel/fcio.h> 41 #include "common.h" 42 43 static int parse_line(char *line, char *path, char *wwn, char *filename); 44 static int create_ap_instance(char *ap_id, char *wwn_string, 45 char *filename, char *line); 46 static void log_error(char *msg_id, char *input_tmplt, ...); 47 static char ctoi(char c); 48 49 /* 50 * Simple wrapper for syslog error messages. 51 * Allows easy addition of syserr output if desired. 52 */ 53 static void 54 log_error(char *msg_id, char *input_tmplt, ...) 55 { 56 va_list ap; 57 char input_merged_msg[200]; 58 char *msg_template = "ID[luxadm.create_fabric_device.%s] %s"; 59 /* 60 * First %s for msg_id in merged msg. 61 * Second %s is for input merged_msg 62 */ 63 char *merged_msg; 64 65 va_start(ap, input_tmplt); 66 /* insert caller's args */ 67 (void) vsprintf(input_merged_msg, input_tmplt, ap); 68 va_end(ap); 69 70 merged_msg = (char *)malloc(strlen(msg_template) + 71 strlen(input_merged_msg) + 72 strlen(msg_id) + 1); 73 if (merged_msg == NULL) { 74 syslog(LOG_ERR, 75 "ID[luxadm.create_fabric_device.2317] " 76 "malloc failure, %s", strerror(errno)); 77 } else { 78 sprintf(merged_msg, msg_template, msg_id, input_merged_msg); 79 /* first insert msg_id */ 80 syslog(LOG_ERR, merged_msg, ""); 81 (void) puts(merged_msg); /* also print message */ 82 free(merged_msg); 83 } 84 } 85 86 /* 87 * Routines for reading tapestry repository file 88 */ 89 90 #define COMMENT_CHAR '#' 91 int 92 read_repos_file(char *repos_filename) 93 { 94 int fd; 95 char *line; 96 char *tmp_ptr, *mmap_ptr; 97 char path[MAXPATHLEN]; 98 int ret; 99 char wwn[FC_WWN_SIZE*2+1]; 100 struct stat stbuf; 101 unsigned int filesize; 102 unsigned int bytes_read; 103 104 if (repos_filename == NULL || *repos_filename == NULL) { 105 log_error("2310", 106 "filename missing for -f option of " 107 "luxadm -e create_fabric_device"); 108 return (-1); 109 } 110 111 fd = open(repos_filename, O_RDONLY); 112 113 if (fd == -1) { 114 log_error("2311", 115 "fopen failed: cannot open repository file %s. %d", 116 repos_filename, strerror(errno)); 117 return (-1); 118 } 119 120 if (fstat(fd, &stbuf) == -1) { 121 close(fd); 122 log_error("2312", 123 "stat failed on file %s. %s", 124 repos_filename, strerror(errno)); 125 return (-1); 126 } 127 filesize = stbuf.st_size; 128 tmp_ptr = mmap_ptr = mmap((caddr_t)0, filesize, 129 (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0); 130 131 if (mmap_ptr == MAP_FAILED) { 132 log_error("2315", 133 "Failed to mmap file %s. %s", 134 repos_filename, strerror(errno)); 135 return (-1); 136 } 137 138 bytes_read = 0; 139 while (bytes_read < filesize) { 140 line = tmp_ptr; 141 while (bytes_read < filesize && *tmp_ptr != '\n') { 142 bytes_read++; 143 tmp_ptr++; 144 } 145 if (*tmp_ptr == '\n') { 146 *tmp_ptr = NULL; 147 tmp_ptr++; 148 bytes_read++; 149 } 150 151 /* If the line is a comment, read another line */ 152 if (*line == COMMENT_CHAR) { 153 continue; 154 } 155 ret = parse_line(line, path, wwn, repos_filename); 156 if (ret == 0) { 157 ret = create_ap_instance(path, 158 wwn, repos_filename, line); 159 } 160 } 161 162 ret = close(fd); 163 ret = munmap(mmap_ptr, filesize); 164 return (ret); 165 } 166 167 /* 168 * Input is paramater 1 - a line from repository 169 * Output is other parameters, the path to the attachment point, 170 * and the port wwn are parsed from the repository 171 * Format is 172 * "/devices/pci..../fp@1,0:fc::wwn" 173 * If controller name is missing, that's okay. Other fields 174 * must be present 175 * 176 * Return 0 on success or -1 on failure; all failures logged to syslog. 177 */ 178 #define WWN_DELIM "::" 179 static int 180 parse_line(char *line, char *path, char *wwn, char *filename) 181 { 182 char *p_path, *p_wwn, *p_delim; 183 char *line_copy; 184 185 line_copy = strdup(line); 186 if (line_copy == NULL) { 187 log_error("2317", 188 "malloc failure, %s", strerror(errno)); 189 } 190 p_path = line_copy; 191 p_delim = strstr(p_path, WWN_DELIM); 192 if (p_delim == NULL) { 193 log_error("2313", 194 "Invalid line (%s) in file %s.", line, filename); 195 free(line_copy); 196 return (-1); 197 } 198 *p_delim = NULL; /* NULL terminate path */ 199 200 if (strlcpy(path, p_path, MAXPATHLEN) >= MAXPATHLEN) { 201 log_error("2318", 202 "Path too long (%s) in file %s.", p_path, filename); 203 free(line_copy); 204 return (-1); 205 } 206 207 p_wwn = p_delim + strlen(WWN_DELIM); 208 /* 209 * Now look for the blank delimiter before the controller 210 * 211 * This is just the case when there may be a controller # 212 * after the attachment point and WWN. For example - 213 * /devices/pci@b,2000/pci@2/SUNW,qlc@4/fp@0,0:fc::220000203707f4f1 c4 214 */ 215 p_delim = strchr(p_wwn, ' '); 216 if (p_delim != NULL) { 217 /* now p_delim points to blank */ 218 *p_delim = NULL; /* terminate wwn at delim */ 219 } else { 220 char *p_last_char; 221 p_last_char = p_wwn+strlen(p_wwn)-1; 222 if (*p_last_char == '\n') { 223 *p_last_char = NULL; 224 } 225 } 226 strcpy(wwn, p_wwn); 227 free(line_copy); 228 return (0); 229 } 230 231 static char 232 ctoi(char c) 233 { 234 if ((c >= '0') && (c <= '9')) 235 c -= '0'; 236 else if ((c >= 'A') && (c <= 'F')) 237 c = c - 'A' + 10; 238 else if ((c >= 'a') && (c <= 'f')) 239 c = c - 'a' + 10; 240 else 241 c = -1; 242 return (c); 243 } 244 245 /* 246 * "string" is Input and "port_wwn" has the output 247 * 248 * This function converts a string to WWN. 249 * For example a string like 250 * "220000203707F4F1" gets converted to 0x220000203707F4F1 ... 251 * where 252 * port_wwn[0] = 0x22, 253 * port_wwn[1] = 0x00, 254 * port_wwn[2] = 0x00, 255 * port_wwn[3] = 0x20, 256 * port_wwn[4] = 0x37, 257 * port_wwn[5] = 0x07, 258 * port_wwn[6] = 0xF4, and 259 * port_wwn[7] = 0xF1 260 */ 261 static int 262 string_to_wwn(const uchar_t *string, uchar_t *port_wwn) 263 { 264 int i; 265 char c, c1; 266 uchar_t *wwnp; 267 268 wwnp = port_wwn; 269 for (i = 0; i < WWN_SIZE; i++, wwnp++) { 270 271 c = ctoi(*string++); 272 c1 = ctoi(*string++); 273 if (c == -1 || c1 == -1) 274 return (-1); 275 *wwnp = ((c << 4) + c1); 276 } 277 278 return (0); 279 } 280 281 static int 282 create_ap_instance(char *ap_id, char *wwn_string, 283 char *filename, char *line) 284 { 285 devctl_hdl_t bus_handle, dev_handle; 286 devctl_ddef_t ddef_handle; 287 int ret; 288 uchar_t wwn_array[FC_WWN_SIZE]; 289 290 ddef_handle = devctl_ddef_alloc("dummy", 0); 291 if (ddef_handle == NULL) { 292 log_error("2314", 293 "Internal error to process line (%s) " 294 "in file: %s. %s", 295 line, filename, strerror(errno)); 296 return (-1); 297 } 298 /* 299 * g_string_to_wwn() has not been used here because it 300 * prepends 2 NULLs. 301 */ 302 if (string_to_wwn((uchar_t *)wwn_string, wwn_array) != 0) { 303 log_error("2314", 304 "Internal error to process line (%s) " 305 "in file: %s. %s", 306 line, filename, strerror(errno)); 307 devctl_ddef_free(ddef_handle); 308 return (-1); 309 } 310 (void) devctl_ddef_byte_array(ddef_handle, 311 "port-wwn", FC_WWN_SIZE, wwn_array); 312 313 if ((bus_handle = devctl_bus_acquire(ap_id, 0)) == NULL) { 314 devctl_ddef_free(ddef_handle); 315 log_error("2314", 316 "Internal error to process line (%s) " 317 "in file: %s. %s", 318 line, filename, strerror(errno)); 319 return (-1); 320 } 321 if (ret = 322 devctl_bus_dev_create(bus_handle, ddef_handle, 0, &dev_handle)) { 323 devctl_ddef_free(ddef_handle); 324 devctl_release(bus_handle); 325 log_error("2316", 326 "configuration failed for line (%s) " 327 "in file: %s. %s", 328 line, filename, strerror(errno)); 329 return (-1); 330 } 331 devctl_release(dev_handle); 332 devctl_ddef_free(ddef_handle); 333 devctl_release(bus_handle); 334 return (ret); 335 } 336