xref: /illumos-gate/usr/src/cmd/luxadm/fabric_conf.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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