xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/sw.c (revision d0698e0d179f97729cacdbc2f13446a6b0a3f22a)
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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <libnvpair.h>
27 #include <fm/topo_mod.h>
28 
29 #include <sys/fm/protocol.h>
30 #include <sys/types.h>
31 
32 #include <topo_method.h>
33 #include <topo_subr.h>
34 #include <sw.h>
35 
36 static int sw_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
37     nvlist_t *, nvlist_t **);
38 static int sw_fmri_create(topo_mod_t *, tnode_t *, topo_version_t,
39     nvlist_t *, nvlist_t **);
40 
41 static const topo_method_t sw_methods[] = {
42 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
43 	    TOPO_STABILITY_INTERNAL, sw_fmri_nvl2str },
44 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
45 	    TOPO_STABILITY_INTERNAL, sw_fmri_create },
46 	{ NULL }
47 };
48 
49 static int sw_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
50     topo_instance_t, void *, void *);
51 static void sw_release(topo_mod_t *, tnode_t *);
52 
53 static const topo_modops_t sw_ops =
54 	{ sw_enum, sw_release };
55 
56 static const topo_modinfo_t sw_info =
57 	{ "sw", FM_FMRI_SCHEME_SW, SW_VERSION, &sw_ops };
58 
59 int
60 sw_init(topo_mod_t *mod, topo_version_t version)
61 {
62 	if (getenv("TOPOSWDEBUG"))
63 		topo_mod_setdebug(mod);
64 	topo_mod_dprintf(mod, "initializing sw builtin\n");
65 
66 	if (version != SW_VERSION)
67 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
68 
69 	if (topo_mod_register(mod, &sw_info, TOPO_VERSION) != 0) {
70 		topo_mod_dprintf(mod, "failed to register sw_info: "
71 		    "%s\n", topo_mod_errmsg(mod));
72 		return (-1);
73 	}
74 
75 	return (0);
76 }
77 
78 void
79 sw_fini(topo_mod_t *mod)
80 {
81 	topo_mod_unregister(mod);
82 }
83 
84 static int
85 sw_get_optl_string(nvlist_t *nvl, char *name, char **dest)
86 {
87 	if (nvlist_lookup_string(nvl, name, dest) == 0) {
88 		return (0);
89 	} else {
90 		*dest = NULL;
91 		return (errno == ENOENT ? 0 : 1);
92 	}
93 }
94 
95 static int
96 sw_get_optl_int64(nvlist_t *nvl, char *name, int64_t *dest)
97 {
98 	if (nvlist_lookup_int64(nvl, name, dest) == 0) {
99 		return (0);
100 	} else {
101 		*dest = -1;
102 		return (errno == ENOENT ? 0 : 1);
103 	}
104 }
105 
106 static int
107 sw_get_optl_nvlist(nvlist_t *nvl, char *name, nvlist_t **dest)
108 {
109 	if (nvlist_lookup_nvlist(nvl, name, dest) == 0) {
110 		return (0);
111 	} else {
112 		*dest = NULL;
113 		return (errno == ENOENT ? 0 : 1);
114 	}
115 }
116 
117 static int
118 sw_add_optl_string(nvlist_t *nvl, char *name, char *val)
119 {
120 	if (val)
121 		return (nvlist_add_string(nvl, name, val) != 0);
122 	else
123 		return (0);
124 }
125 
126 /*ARGSUSED*/
127 static int
128 sw_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version,
129     nvlist_t *in, nvlist_t **out)
130 {
131 	nvlist_t *args, *fmri = NULL, *obj = NULL, *site = NULL, *ctxt = NULL;
132 	topo_mod_errno_t moderr;
133 	int err = 0;
134 
135 	char *obj_path, *obj_root;
136 	nvlist_t *obj_pkg;
137 
138 	char *site_token, *site_module, *site_file, *site_func;
139 	int64_t site_line;
140 
141 	char *ctxt_origin, *ctxt_execname, *ctxt_zone;
142 	int64_t ctxt_pid, ctxt_ctid;
143 	char **ctxt_stack;
144 	uint_t ctxt_stackdepth;
145 
146 
147 	if (version > TOPO_METH_FMRI_VERSION)
148 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
149 
150 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0)
151 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
152 
153 	if (nvlist_lookup_string(args, "obj_path", &obj_path) != 0)
154 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
155 	err |= sw_get_optl_string(args, "obj_root", &obj_root);
156 	err |= sw_get_optl_nvlist(args, "obj-pkg", &obj_pkg);
157 
158 	err |= sw_get_optl_string(args, "site_token", &site_token);
159 	err |= sw_get_optl_string(args, "site_module", &site_module);
160 	err |= sw_get_optl_string(args, "site_file", &site_file);
161 	err |= sw_get_optl_string(args, "site_func", &site_func);
162 	err |= sw_get_optl_int64(args, "site_line", &site_line);
163 
164 	err |= sw_get_optl_string(args, "ctxt_origin", &ctxt_origin);
165 	err |= sw_get_optl_string(args, "ctxt_execname", &ctxt_execname);
166 	err |= sw_get_optl_string(args, "ctxt_zone", &ctxt_zone);
167 	err |= sw_get_optl_int64(args, "ctxt_pid", &ctxt_pid);
168 	err |= sw_get_optl_int64(args, "ctxt_ctid", &ctxt_ctid);
169 
170 	if (nvlist_lookup_string_array(args, "stack", &ctxt_stack,
171 	    &ctxt_stackdepth) != 0) {
172 		if (errno == ENOENT)
173 			ctxt_stack = NULL;
174 		else
175 			err++;
176 	}
177 
178 	if (err)
179 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
180 
181 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0 ||
182 	    topo_mod_nvalloc(mod, &obj, NV_UNIQUE_NAME) != 0) {
183 		moderr = EMOD_NOMEM;
184 		goto out;
185 	}
186 
187 	/*
188 	 * Add standard FMRI members 'version' and 'scheme'.
189 	 */
190 	err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION);
191 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
192 
193 	/*
194 	 * Build up the 'object' nvlist.
195 	 */
196 	err |= nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, obj_path);
197 	err |= sw_add_optl_string(obj, FM_FMRI_SW_OBJ_ROOT, obj_root);
198 	if (obj_pkg)
199 		err |= nvlist_add_nvlist(obj, FM_FMRI_SW_OBJ_PKG, obj_pkg);
200 
201 	/*
202 	 * Add 'object' to the fmri.
203 	 */
204 	if (err == 0)
205 		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, obj);
206 
207 	if (err) {
208 		moderr = EMOD_NOMEM;
209 		goto out;
210 	}
211 
212 	/*
213 	 * Do we have anything for a 'site' nvlist?
214 	 */
215 	if (site_token == NULL && site_module == NULL && site_file == NULL &&
216 	    site_func == NULL && site_line == -1)
217 		goto context;
218 
219 	/*
220 	 * Allocate and build 'site' nvlist.
221 	 */
222 	if (topo_mod_nvalloc(mod, &site, NV_UNIQUE_NAME) != 0) {
223 		moderr = EMOD_NOMEM;
224 		goto out;
225 	}
226 
227 	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_TOKEN, site_token);
228 	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_MODULE, site_module);
229 	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FILE, site_file);
230 	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FUNC, site_func);
231 	if ((site_token || site_module || site_file || site_func) &&
232 	    site_line != -1)
233 		err |= nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, site_line);
234 
235 	/*
236 	 * Add 'site' to the fmri.
237 	 */
238 	if (err == 0)
239 		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_SITE, site);
240 
241 	if (err) {
242 		moderr = EMOD_NOMEM;
243 		goto out;
244 	}
245 
246 context:
247 	/*
248 	 * Do we have anything for a 'context' nvlist?
249 	 */
250 	if (ctxt_origin || ctxt_execname || ctxt_zone ||
251 	    ctxt_pid != -1 || ctxt_ctid != -1 || ctxt_stack != NULL)
252 		goto out;
253 
254 	/*
255 	 * Allocate and build 'context' nvlist.
256 	 */
257 	if (topo_mod_nvalloc(mod, &ctxt, NV_UNIQUE_NAME) != 0) {
258 		moderr = EMOD_NOMEM;
259 		goto out;
260 	}
261 
262 	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN, ctxt_origin);
263 	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME,
264 	    ctxt_execname);
265 	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ZONE, ctxt_zone);
266 	if (ctxt_pid != -1)
267 		err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_PID, ctxt_pid);
268 	if (ctxt_ctid != -1)
269 		err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_CTID, ctxt_ctid);
270 	if (ctxt_stack != NULL)
271 		err |= nvlist_add_string_array(ctxt, FM_FMRI_SW_CTXT_STACK,
272 		    ctxt_stack, ctxt_stackdepth);
273 
274 	/*
275 	 * Add 'context' to the fmri.
276 	 */
277 	if (err == 0)
278 		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_CTXT, ctxt);
279 
280 	moderr = err ? EMOD_NOMEM : 0;
281 out:
282 	if (moderr == 0)
283 		*out = fmri;
284 
285 	if (moderr != 0 && fmri)
286 		nvlist_free(fmri);
287 
288 	if (obj)
289 		nvlist_free(obj);
290 
291 	if (site)
292 		nvlist_free(site);
293 
294 	if (ctxt)
295 		nvlist_free(ctxt);
296 
297 	return (moderr == 0 ? 0 : topo_mod_seterrno(mod, moderr));
298 }
299 
300 
301 /*ARGSUSED*/
302 static int
303 sw_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
304     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
305 {
306 	(void) topo_method_register(mod, pnode, sw_methods);
307 	return (0);
308 }
309 
310 static void
311 sw_release(topo_mod_t *mod, tnode_t *node)
312 {
313 	topo_method_unregister_all(mod, node);
314 }
315 
316 /*
317  * Lookup a string in an nvlist.  Possible return values:
318  *	if 'required' is B_TRUE:
319  *		1 = found
320  *		0 = not found
321  *	if 'required' is B_FALSE:
322  *		1 = found
323  *		0 = not found, but some error other than ENOENT encountered
324  *		-1 = not found, with ENOENT
325  *
326  *	So 0 is an error condition in both cases.
327  *
328  *	In all "not found" cases, *valp is NULLed.
329  */
330 static int
331 lookup_string(nvlist_t *nvl, char *name, char **valp, boolean_t required)
332 {
333 	int err;
334 
335 	err = nvlist_lookup_string(nvl, name, valp);
336 
337 	/*
338 	 * A return value of 1 always means "found"
339 	 */
340 	if (err == 0)
341 		return (1);
342 
343 	/*
344 	 * Failure to lookup for whatever reason NULLs valp
345 	 */
346 	*valp = NULL;
347 
348 	/*
349 	 * Return 0 if not found but required, or optional but some error
350 	 * other than ENOENT was returned.
351 	 */
352 	if (required == B_TRUE || err != ENOENT)
353 		return (0);
354 
355 	/*
356 	 * Return -1 if not found but was optional (and got ENOENT).
357 	 */
358 	return (-1);
359 }
360 
361 /*ARGSUSED*/
362 static int
363 sw_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
364     nvlist_t *nvl, nvlist_t **out)
365 {
366 	nvlist_t *object, *site = NULL, *anvl = NULL;
367 	char *file, *func, *token;
368 	uint8_t scheme_version;
369 	char *path, *root;
370 	nvlist_t *fmristr;
371 	size_t buflen = 0;
372 	int linevalid = 0;
373 	char *buf = NULL;
374 	ssize_t size = 0;
375 	char linebuf[32];
376 	int64_t line;
377 	int pass;
378 	int err;
379 
380 	if (version > TOPO_METH_NVL2STR_VERSION)
381 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
382 
383 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 ||
384 	    scheme_version > FM_SW_SCHEME_VERSION)
385 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
386 
387 	/* Get authority, if present */
388 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
389 	if (err != 0 && err != ENOENT)
390 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
391 
392 	/*
393 	 * The 'object' nvlist is required. It must include the path,
394 	 * but the root is optional.
395 	 */
396 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_SW_OBJ, &object) != 0 ||
397 	    !lookup_string(object, FM_FMRI_SW_OBJ_PATH, &path, B_TRUE) ||
398 	    !lookup_string(object, FM_FMRI_SW_OBJ_ROOT, &root, B_FALSE))
399 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
400 
401 	/* The 'site' nvlist is optional */
402 	file = func = token = NULL;
403 	linevalid = 0;
404 	if ((err = nvlist_lookup_nvlist(nvl, FM_FMRI_SW_SITE, &site)) == 0) {
405 		/*
406 		 * Prefer 'token' to file/func/line
407 		 */
408 		if (lookup_string(site, FM_FMRI_SW_SITE_TOKEN, &token,
409 		    B_FALSE) <= 0) {
410 			/*
411 			 * If no token then try file, func, line - but
412 			 * func and line are meaningless without file.
413 			 */
414 			if (lookup_string(site, FM_FMRI_SW_SITE_FILE,
415 			    &file, B_FALSE) == 1) {
416 				(void) lookup_string(site, FM_FMRI_SW_SITE_FUNC,
417 				    &func, B_FALSE);
418 				if (nvlist_lookup_int64(site,
419 				    FM_FMRI_SW_SITE_LINE, &line) == 0)
420 					linevalid = 1;
421 			}
422 		}
423 	} else if (err != ENOENT) {
424 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
425 	}
426 
427 	/* On the first pass buf is NULL and size and buflen are 0 */
428 	pass = 1;
429 again:
430 	/*
431 	 * sw://[<authority>]/
432 	 *	[:root=<object.root]
433 	 *	:path=<object.path>
434 	 *	[#<fragment-identifier>]
435 	 *
436 	 *	<fragment-identifier> is one of
437 	 *
438 	 *		:token=<site.token>
439 	 *	or
440 	 *		:file=<site.file>[:func=<site.func>][:line=<site.line>]
441 	 */
442 
443 	/* sw:// */
444 	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SW,
445 	    NULL, "://");
446 
447 	/* authority, if any */
448 	if (anvl != NULL) {
449 		nvpair_t *apair;
450 		char *aname, *aval;
451 
452 		for (apair = nvlist_next_nvpair(anvl, NULL);
453 		    apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) {
454 			if (nvpair_type(apair) != DATA_TYPE_STRING ||
455 			    nvpair_value_string(apair, &aval) != 0)
456 				continue;
457 			aname = nvpair_name(apair);
458 			topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL);
459 			topo_fmristr_build(&size, buf, buflen, "=",
460 			    aname, aval);
461 		}
462 	}
463 
464 	/* separating slash */
465 	topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL);
466 
467 	/* :root=... */
468 	if (root) {
469 		topo_fmristr_build(&size, buf, buflen, root,
470 		    ":" FM_FMRI_SW_OBJ_ROOT "=", NULL);
471 	}
472 
473 	/* :path=... */
474 	topo_fmristr_build(&size, buf, buflen, path,
475 	    ":" FM_FMRI_SW_OBJ_PATH "=", NULL);
476 
477 	if (token) {
478 		/* #:token=... */
479 		topo_fmristr_build(&size, buf, buflen, token,
480 		    "#:" FM_FMRI_SW_SITE_TOKEN "=", NULL);
481 	} else if (file) {
482 		/* #:file=... */
483 		topo_fmristr_build(&size, buf, buflen, file,
484 		    "#:" FM_FMRI_SW_SITE_FILE "=", NULL);
485 
486 		/* :func=... */
487 		if (func) {
488 			topo_fmristr_build(&size, buf, buflen, func,
489 			    ":" FM_FMRI_SW_SITE_FUNC "=", NULL);
490 		}
491 
492 		/* :line=... */
493 		if (linevalid) {
494 			if (pass == 1)
495 				(void) snprintf(linebuf, sizeof (linebuf),
496 				    "%lld", line);
497 
498 			topo_fmristr_build(&size, buf, buflen, linebuf,
499 			    ":" FM_FMRI_SW_SITE_LINE "=", NULL);
500 		}
501 	}
502 
503 	if (buf == NULL) {
504 		if ((buf = topo_mod_alloc(mod, size + 1)) == NULL)
505 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
506 
507 		buflen = size + 1;
508 		size = 0;
509 		pass = 2;
510 		goto again;
511 	}
512 
513 	/*
514 	 * Construct the nvlist to return as the result.
515 	 */
516 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
517 		topo_mod_strfree(mod, buf);
518 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
519 	}
520 
521 	if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) {
522 		topo_mod_strfree(mod, buf);
523 		nvlist_free(fmristr);
524 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
525 	}
526 	topo_mod_strfree(mod, buf);
527 	*out = fmristr;
528 
529 	return (0);
530 }
531