xref: /illumos-gate/usr/src/lib/libpcp/common/pcp_utils.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Implements auxiliary routines declared in pcp_utils.h to facilitate
28  * finding the appropriate communication transport & device path for a
29  * given service.  This supports the transition from the legacy service channel
30  * transport (glvc) to the logical domain channel (vldc) transport native
31  * to platforms running Logical Domains (LDoms).
32  */
33 
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <strings.h>
38 #include <stdlib.h>
39 #include <libgen.h>
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <libdevinfo.h>
43 
44 #include "pcp_utils.h"
45 
46 typedef enum { false = 0, true = 1 } bool_t;
47 
48 #define	SERVICE_PREFIX	"SUNW,sun4v-"
49 #define	DEVICES_DIR "/devices"
50 #define	GLVC	":glvc"
51 #define	VCHAN	"virtual-channel@"
52 #define	VCHAN_C	"virtual-channel-client@"
53 
54 /*
55  * The mechanism to relate a service to a device path is different for
56  * vldc and glvc, due to the way the device pathnames are encoded:
57  * Sample service: sunvts
58  * Service Name: SUNW,sun4v-sunvts
59  * GLVC device path:
60  * "/devices/virtual-devices@100/sunvts@a:glvc"
61  * VLDC device path:
62  * "/devices/virtual-devices@100/channel-devices@200/virtual-channel@3:sunvts"
63  *
64  * As VLDC is the communication mechanism used in an LDoms environment, it is
65  * the preferred channel, and its existence is checked for first.
66  */
67 
68 /*
69  * Extract from dev_path the "service name" portion.
70  * For vldc, the service corresponds to the minor name of the device path
71  * (e.g. virtual-channel@3:sunvts for sunvts).  If service is non-NULL, it must
72  * match the extracted service name for the function to succeed.
73  * The service name is returned in match (if non-NULL), and the function
74  * itself returns true on success; false on failure.
75  */
76 static bool_t
77 get_vldc_svc_name(char *dev_path, char *service, char **match)
78 {
79 	bool_t ret = false;
80 	char *pathname = strdup(dev_path);
81 	char *devname, *s;
82 
83 	if (NULL == pathname)
84 		return (false);
85 
86 	devname = basename(pathname);
87 	s = strrchr(devname, ':');
88 
89 	if (s++ == NULL) {
90 		goto end;
91 	}
92 
93 	if ((strncmp(devname, VCHAN, strlen(VCHAN)) == 0) ||
94 	    (strncmp(devname, VCHAN_C, strlen(VCHAN_C)) == 0)) {
95 		/*
96 		 * If in addition, a service string is specified to
97 		 * be matched, do a comparison
98 		 */
99 		if (service != NULL) {
100 			if (strcmp(s, service) == 0) {
101 				if (match)
102 					*match = strdup(s);
103 				ret = true;
104 				goto end;
105 			} else {
106 				ret = false;
107 				goto end;
108 			}
109 		} else if (match) {
110 			*match = strdup(s);
111 		}
112 
113 		ret = true;
114 		goto end;
115 	}
116 end:
117 
118 	free(pathname);
119 	return (ret);
120 }
121 
122 /*
123  * Extract from dev_path the "service name" portion.
124  * For glvc, the service corresponds to the node name of the device path
125  * (e.g. sunvts@a:glvc for sunvts).  If service is non-NULL, it must
126  * match the extracted service name for the function to succeed.
127  * The service name is returned in match (if non-NULL), and the function
128  * itself returns true on success; false on failure.
129  */
130 static bool_t
131 get_glvc_svc_name(char *dev_path, char *service, char **match)
132 {
133 	bool_t ret = true;
134 	char *pathname = strdup(dev_path);
135 	char *devname, *substr, *t;
136 	int len;
137 
138 	if (NULL == pathname)
139 		return (false);
140 
141 	devname = basename(pathname);
142 	substr = strstr(devname, GLVC);
143 
144 	if (!((substr != NULL) && (strcmp(substr, GLVC) == 0))) {
145 		ret = false;
146 		goto end;
147 	}
148 
149 	if ((t = strrchr(devname, '@')) == NULL) {
150 		ret = false;
151 		goto end;
152 	}
153 
154 	len = t - devname;
155 
156 	/*
157 	 * If a service string is specified, check if there
158 	 * is a match
159 	 */
160 	if ((service != NULL) && (strncmp(devname, service, len) != 0))
161 		ret = false;
162 
163 	if ((ret == true) && (match != NULL)) {
164 		*match = calloc(len + 1, 1);
165 		if (*match)
166 			(void) strncpy(*match, devname, len);
167 	}
168 
169 end:
170 	free(pathname);
171 	return (ret);
172 }
173 
174 /*
175  * This routine accepts either a prefixed service name or a legacy full
176  * pathname (which might not even exist in the filesystem), and in either case
177  * returns a canonical service name.  If the parameter is neither a service
178  * name (i.e. with a "SUNW,sun4v-" prefix), nor a path to a legacy glvc or
179  * vldc device, NULL is returned.
180  */
181 char *
182 platsvc_extract_svc_name(char *devname)
183 {
184 	char *sname = NULL;
185 	char *vldc_path, *glvc_path;
186 
187 	/*
188 	 * First check whether a service name
189 	 */
190 	if (strncmp(devname, SERVICE_PREFIX, strlen(SERVICE_PREFIX)) == 0) {
191 		sname = strdup(devname + strlen(SERVICE_PREFIX));
192 		return (sname);
193 	}
194 
195 	/*
196 	 * Not a service name, check if it's a valid pathname
197 	 */
198 	if (!(devname[0] == '/' || devname[0] == '.')) {
199 		return (NULL);
200 	}
201 
202 	/*
203 	 * Ideally, we should only check for a valid glvc pathname,
204 	 * requiring all vldc access to be only via service names.  But
205 	 * to prevent a flag day with code that's already passing in
206 	 * vldc full pathnames (e.g. sunMC), we allow them here.
207 	 */
208 	if (get_vldc_svc_name(devname, NULL, &vldc_path) == true) {
209 		return (vldc_path);
210 	} else if (get_glvc_svc_name(devname, NULL, &glvc_path) == true) {
211 		return (glvc_path);
212 	}
213 
214 	return (NULL);
215 }
216 
217 /*
218  * Walk all "service" device nodes to find the one with the
219  * matching glvc minor name
220  */
221 static char *
222 svc_name_to_glvc_dev_path(char *service)
223 {
224 	di_node_t root_node, service_node;
225 	char *glvc_path;
226 	char *minor_name;
227 	di_minor_t minor;
228 	char *dev_path = NULL;
229 
230 	if (service == NULL)
231 		return (NULL);
232 
233 	/* Ensure that the 'glvc' driver is loaded */
234 	root_node = di_init_driver("glvc", DINFOCPYALL);
235 	if (root_node == DI_NODE_NIL) {
236 		return (dev_path);
237 	}
238 
239 	service_node = di_drv_first_node("glvc", root_node);
240 
241 	while (service_node != DI_NODE_NIL) {
242 		/* Make sure node name matches service name */
243 		if (strcmp(service, di_node_name(service_node)) == 0) {
244 			/* Walk minor nodes */
245 			minor = di_minor_next(service_node, DI_NODE_NIL);
246 
247 			while (minor != DI_NODE_NIL) {
248 				glvc_path = di_devfs_minor_path(minor);
249 				minor_name = di_minor_name(minor);
250 
251 				if (strcmp(minor_name, "glvc") == 0) {
252 					dev_path = malloc(strlen(glvc_path) +
253 					    strlen(DEVICES_DIR) + 1);
254 					(void) strcpy(dev_path, DEVICES_DIR);
255 					(void) strcat(dev_path, glvc_path);
256 					di_devfs_path_free(glvc_path);
257 					break;
258 				}
259 
260 				di_devfs_path_free(glvc_path);
261 				minor = di_minor_next(service_node, minor);
262 			}
263 		}
264 		if (dev_path != NULL)
265 			break;
266 
267 		service_node = di_drv_next_node(service_node);
268 	}
269 
270 	di_fini(root_node);
271 	return (dev_path);
272 }
273 
274 /*
275  * Walk all vldc device nodes to find the one with the
276  * matching minor name
277  */
278 static char *
279 svc_name_to_vldc_dev_path(char *service)
280 {
281 	di_node_t root_node, vldc_node;
282 	char *vldc_path;
283 	char *minor_name;
284 	di_minor_t minor;
285 	char *dev_path = NULL;
286 
287 	/* Ensure that the 'vldc' driver is loaded */
288 	root_node = di_init_driver("vldc", DINFOCPYALL);
289 	if (root_node == DI_NODE_NIL) {
290 		return (dev_path);
291 	}
292 
293 	vldc_node = di_drv_first_node("vldc", root_node);
294 
295 	while (vldc_node != DI_NODE_NIL) {
296 		/* Walk minor nodes */
297 		minor = di_minor_next(vldc_node, DI_NODE_NIL);
298 
299 		while (minor != DI_NODE_NIL) {
300 			vldc_path = di_devfs_minor_path(minor);
301 			minor_name = di_minor_name(minor);
302 
303 			if (strcmp(minor_name, service) == 0) {
304 				dev_path = malloc(strlen(vldc_path) +
305 				    strlen(DEVICES_DIR) + 1);
306 				(void) strcpy(dev_path, DEVICES_DIR);
307 				(void) strcat(dev_path, vldc_path);
308 				di_devfs_path_free(vldc_path);
309 				break;
310 			}
311 
312 			di_devfs_path_free(vldc_path);
313 			minor = di_minor_next(vldc_node, minor);
314 		}
315 		if (dev_path != NULL)
316 			break;
317 
318 		vldc_node = di_drv_next_node(vldc_node);
319 	}
320 
321 	di_fini(root_node);
322 	return (dev_path);
323 }
324 
325 /*
326  * Given a service name or a full legacy pathname, return
327  * the full pathname to the appropriate vldc or glvc device.
328  */
329 char *
330 platsvc_name_to_path(char *svc_or_path, pcp_xport_t *type)
331 {
332 	char		*pathn_p;
333 	char		*service;
334 
335 	if ((service = platsvc_extract_svc_name(svc_or_path)) == NULL)
336 		return (NULL);
337 
338 	/*
339 	 * First lookup vldc nodes
340 	 */
341 	pathn_p = svc_name_to_vldc_dev_path(service);
342 	if (pathn_p != NULL) {
343 		*type = VLDC_STREAMING;
344 	} else {
345 		/*
346 		 * If no vldc, try to find a glvc node
347 		 */
348 		pathn_p = svc_name_to_glvc_dev_path(service);
349 		if (pathn_p != NULL) {
350 			*type = GLVC_NON_STREAM;
351 		}
352 	}
353 
354 	free(service);
355 	return (pathn_p);
356 }
357