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