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
36static int sw_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
37    nvlist_t *, nvlist_t **);
38static int sw_fmri_create(topo_mod_t *, tnode_t *, topo_version_t,
39    nvlist_t *, nvlist_t **);
40
41static 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
49static int sw_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
50    topo_instance_t, void *, void *);
51static void sw_release(topo_mod_t *, tnode_t *);
52
53static const topo_modops_t sw_ops =
54	{ sw_enum, sw_release };
55
56static const topo_modinfo_t sw_info =
57	{ "sw", FM_FMRI_SCHEME_SW, SW_VERSION, &sw_ops };
58
59int
60sw_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
78void
79sw_fini(topo_mod_t *mod)
80{
81	topo_mod_unregister(mod);
82}
83
84static int
85sw_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
95static int
96sw_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
106static int
107sw_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
117static int
118sw_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*/
127static int
128sw_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
246context:
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;
281out:
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*/
302static int
303sw_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
310static void
311sw_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 */
330static int
331lookup_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*/
362static int
363sw_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;
429again:
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