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#include "HBAPort.h"
29#include "Exceptions.h"
30#include "Trace.h"
31#include <iostream>
32#include <iomanip>
33#include <cerrno>
34#include <cstring>
35#include <sys/types.h>
36#include <sys/mkdev.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <stropts.h>
41#include <dirent.h>
42#include <libdevinfo.h>
43
44using namespace std;
45
46/**
47 * Standard definition for general topology lookup (See T11 FC-FS)
48 */
49const int HBAPort::RNID_GENERAL_TOPOLOGY_DATA_FORMAT = 0xDF;
50const uint8_t HBAPort::HBA_NPIV_PORT_MAX = UCHAR_MAX;
51
52/**
53 * @memo	    Construct a new deafult HBA Port
54 */
55HBAPort::HBAPort() {
56}
57
58/**
59 * @memo	    Compare two HBA ports for equality
60 * @return	    TRUE if both ports are the same
61 * @return	    FALSE if the ports are different
62 *
63 * @doc		    Comparison is based on Node WWN, Port WWN and path
64 */
65bool HBAPort::operator==(HBAPort &comp) {
66	return (this->getPortWWN() == comp.getPortWWN() &&
67		this->getNodeWWN() == comp.getNodeWWN() &&
68		this->getPath() == comp.getPath());
69}
70
71/**
72 * @memo	    Validate that the port is still present in the system
73 * @exception	    UnavailableException if the port is not present
74 *
75 * @doc		    If the port is still present on the system, the routine
76 *		    will return normally.  If the port is not present
77 *		    an exception will be thrown.
78 */
79void HBAPort::validatePresent() {
80	Trace log("HBAPort::validatePresent");
81	string path = getPath();
82	struct stat sbuf;
83	if (stat(path.c_str(), &sbuf) == -1) {
84	    if (errno == ENOENT) {
85		throw UnavailableException();
86	    } else {
87		log.debug("Unable to stat %s: %s", path.c_str(),
88			strerror(errno));
89		throw InternalError();
90	    }
91	}
92}
93
94
95/*
96 * structure for di_devlink_walk
97 */
98typedef struct walk_devlink {
99	char *path;
100	size_t len;
101	char **linkpp;
102} walk_devlink_t;
103
104/**
105 * @memo	    callback funtion for di_devlink_walk
106 * @postcondition   Find matching /dev link for the given path argument.
107 * @param	    devlink element and callback function argument.
108 *
109 * @doc		    The input path is expected to not have "/devices".
110 */
111extern "C" int
112get_devlink(di_devlink_t devlink, void *arg) {
113	Trace log("get_devlink");
114	walk_devlink_t *warg = (walk_devlink_t *)arg;
115
116	/*
117	 * When path is specified, it doesn't have minor
118	 * name. Therefore, the ../.. prefixes needs to be stripped.
119	 */
120	if (warg->path) {
121		// di_devlink_content contains /devices
122		char *content = (char *)di_devlink_content(devlink);
123		char *start = strstr(content, "/devices");
124
125		if (start == NULL ||
126		    strncmp(start, warg->path, warg->len) != 0 ||
127		    // make it sure the device path has minor name
128		    start[warg->len] != ':')
129			return (DI_WALK_CONTINUE);
130	}
131
132	*(warg->linkpp) = strdup(di_devlink_path(devlink));
133	return (DI_WALK_TERMINATE);
134}
135
136/**
137 * @memo	    Convert /devices paths to /dev sym-link paths.
138 * @postcondition   The mapping buffer OSDeviceName paths will be
139 *		    converted to short names.
140 * @param	    mappings The target mappings data to convert to
141 *		    short names
142 *
143 * @doc		    If no link
144 * is found, the long path is left as is.
145 * Note: The NumberOfEntries field MUST not be greater than the size
146 * of the array passed in.
147 */
148void HBAPort::convertToShortNames(PHBA_FCPTARGETMAPPINGV2 mappings) {
149	Trace log("HBAPort::convertToShortNames");
150	di_devlink_handle_t hdl;
151	walk_devlink_t warg;
152	char *minor_path, *devlinkp;
153
154	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
155	    log.internalError("di_devlink_init failed. Errno:%d", errno);
156	    // no need to check further, just return here.
157	    return;
158	}
159
160	for (int j = 0; j < mappings->NumberOfEntries; j++) {
161	    if (strchr(mappings->entry[j].ScsiId.OSDeviceName, ':')) {
162		// search link for minor node
163		minor_path = mappings->entry[j].ScsiId.OSDeviceName;
164		if (strstr(minor_path, "/devices") != NULL) {
165		    minor_path = mappings->entry[j].ScsiId.OSDeviceName +
166			strlen("/devices");
167		} else {
168		    minor_path = mappings->entry[j].ScsiId.OSDeviceName;
169		}
170		warg.path = NULL;
171	    } else {
172		minor_path = NULL;
173		if (strstr(mappings->entry[j].ScsiId.OSDeviceName,
174		    "/devices") != NULL) {
175		    warg.len = strlen (mappings->entry[j].ScsiId.OSDeviceName) -
176			    strlen ("/devices");
177		    warg.path = mappings->entry[j].ScsiId.OSDeviceName +
178			    strlen ("/devices");
179		} else {
180		    warg.len = strlen(mappings->entry[j].ScsiId.OSDeviceName);
181		    warg.path = mappings->entry[j].ScsiId.OSDeviceName;
182		}
183	    }
184
185	    devlinkp = NULL;
186	    warg.linkpp = &devlinkp;
187	    (void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK,
188		(void *)&warg, get_devlink);
189
190	    if (devlinkp != NULL) {
191		snprintf(mappings->entry[j].ScsiId.OSDeviceName,
192		    sizeof (mappings->entry[j].ScsiId.OSDeviceName),
193		    "%s", devlinkp);
194		free(devlinkp);
195	    } // else leave OSDeviceName alone.
196
197	}
198
199	di_devlink_fini(&hdl);
200
201}
202
203/*
204 * Finds controller path for a give device path.
205 *
206 * Return vale: controller path.
207 */
208string HBAPort::lookupControllerPath(string path) {
209	Trace log("lookupControllerPath");
210	DIR	    *dp;
211	char    buf[MAXPATHLEN];
212	char    node[MAXPATHLEN];
213	struct dirent **dirpp, *dirp;
214	const char    dir[] = "/dev/cfg";
215	ssize_t	    count;
216	uchar_t *dir_buf = new uchar_t[sizeof (struct dirent) + MAXPATHLEN];
217
218	if ((dp = opendir(dir)) == NULL) {
219	    string tmp = "Unable to open ";
220	    tmp += dir;
221	    tmp += "to find controller number.";
222	    delete (dir_buf);
223	    throw IOError(tmp);
224	}
225
226	dirp = (struct dirent *) dir_buf;
227	dirpp = &dirp;
228	while ((readdir_r(dp, dirp, dirpp)) == 0  && dirp != NULL) {
229	    if (strcmp(dirp->d_name, ".") == 0 ||
230		    strcmp(dirp->d_name, "..") == 0) {
231		continue;
232	    }
233	    sprintf(node, "%s/%s", dir, dirp->d_name);
234	    if ((count = readlink(node,buf,sizeof(buf)))) {
235		buf[count] = '\0';
236		if (strstr(buf, path.c_str())) {
237		    string cfg_path = dir;
238		    cfg_path += "/";
239		    cfg_path += dirp->d_name;
240		    closedir(dp);
241		    delete (dir_buf);
242		    return (cfg_path);
243		}
244	    }
245	}
246
247	closedir(dp);
248	delete (dir_buf);
249	throw InternalError("Unable to find controller path");
250}
251
252void HBAPort::addPort(HBANPIVPort *port) {
253	Trace log("HBAPort::addPort");
254	lock();
255	// support hba with up to UCHAR_MAX number of ports.
256	if (npivportsByIndex.size() + 1 > HBA_NPIV_PORT_MAX) {
257		unlock();
258		throw InternalError("HBA NPIV Port count exceeds max number of ports");
259	}
260
261	try {
262		npivportsByWWN[port->getPortWWN()] = port;
263		npivportsByIndex.insert(npivportsByIndex.end(), port);
264		unlock();
265	} catch (...) {
266		unlock();
267		throw;
268	}
269}
270
271HBANPIVPort* HBAPort::getPort(uint64_t wwn) {
272	Trace log("HBAPort::getPort");
273	HBANPIVPort *port = NULL;
274
275	lock();
276	try {
277		if (npivportsByWWN.find(wwn) == npivportsByWWN.end()) {
278			throw IllegalWWNException();
279		}
280		port = npivportsByWWN[wwn];
281		unlock();
282		return (port);
283	} catch (...) {
284		unlock();
285		throw;
286	}
287}
288
289HBANPIVPort* HBAPort::getPortByIndex(int index) {
290	Trace log("HBAPort::getPortByIndex");
291	lock();
292	try {
293		if (index >= npivportsByIndex.size() || index < 0) {
294			throw IllegalIndexException();
295		}
296		HBANPIVPort *tmp = npivportsByIndex[index];
297		unlock();
298		return (tmp);
299	} catch (...) {
300		unlock();
301		throw;
302	}
303}
304
305