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 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/fm/protocol.h>
30#include <sys/types.h>
31#include <sys/systeminfo.h>
32#include <fm/fmd_snmp.h>
33#include <fm/libtopo.h>
34#include <net-snmp/net-snmp-config.h>
35#include <net-snmp/net-snmp-includes.h>
36#include <net-snmp/agent/net-snmp-agent-includes.h>
37#include <libnvpair.h>
38#include <limits.h>
39#include <strings.h>
40#include <stddef.h>
41#include <unistd.h>
42#include <dlfcn.h>
43#include <errno.h>
44
45#define	SCHEMEDIR_BASE	"/usr/lib/fm/fmd/schemes"
46
47#if defined(__sparcv9)
48#define	DEFAULTSCHEMEDIR	SCHEMEDIR_BASE "/sparcv9"
49#elif defined(__amd64)
50#define	DEFAULTSCHEMEDIR	SCHEMEDIR_BASE "/amd64"
51#else
52#define	DEFAULTSCHEMEDIR	SCHEMEDIR_BASE
53#endif
54
55typedef struct fmd_scheme_ops {
56	int (*sop_init)(void);
57	void (*sop_fini)(void);
58	ssize_t (*sop_nvl2str)(nvlist_t *, char *, size_t);
59} fmd_scheme_ops_t;
60
61typedef struct fmd_scheme_opd {
62	const char *opd_name;		/* symbol name of scheme function */
63	size_t opd_off;			/* offset within fmd_scheme_ops_t */
64} fmd_scheme_opd_t;
65
66typedef struct fmd_scheme {
67	struct fmd_scheme *sch_next;    /* next scheme on list of schemes */
68	char *sch_name;			/* name of this scheme (fmri prefix) */
69	void *sch_dlp;			/* libdl(3DL) shared library handle */
70	int sch_err;			/* if negative entry, errno to return */
71	fmd_scheme_ops_t sch_ops;	/* scheme function pointers */
72} fmd_scheme_t;
73
74static fmd_scheme_t *sch_list;		/* list of cached schemes */
75static char *g_root;			/* fmd root dir */
76static struct topo_hdl *g_thp;
77
78static long
79fmd_scheme_notsup(void)
80{
81	errno = ENOTSUP;
82	return (-1);
83}
84
85static int
86fmd_scheme_nop(void)
87{
88	return (0);
89}
90
91/*
92 * Default values for the scheme ops.  If a scheme function is not defined in
93 * the module, then this operation is implemented using the default function.
94 */
95static const fmd_scheme_ops_t _fmd_scheme_default_ops = {
96	(int (*)())fmd_scheme_nop,		/* sop_init */
97	(void (*)())fmd_scheme_nop,		/* sop_fini */
98	(ssize_t (*)())fmd_scheme_notsup,	/* sop_nvl2str */
99};
100
101/*
102 * Scheme ops descriptions.  These names and offsets are used by the function
103 * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t.
104 */
105static const fmd_scheme_opd_t _fmd_scheme_ops[] = {
106	{ "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) },
107	{ "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) },
108	{ "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) },
109	{ NULL, 0 }
110};
111
112static fmd_scheme_t *
113fmd_scheme_create(const char *name)
114{
115	fmd_scheme_t *sp;
116
117	if ((sp = malloc(sizeof (fmd_scheme_t))) == NULL ||
118	    (sp->sch_name = strdup(name)) == NULL) {
119		free(sp);
120		return (NULL);
121	}
122
123	sp->sch_next = sch_list;
124	sp->sch_dlp = NULL;
125	sp->sch_err = 0;
126	sp->sch_ops = _fmd_scheme_default_ops;
127
128	sch_list = sp;
129	return (sp);
130}
131
132static int
133fmd_scheme_rtld_init(fmd_scheme_t *sp)
134{
135	const fmd_scheme_opd_t *opd;
136	void *p;
137
138	for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) {
139		if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL)
140			*(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p;
141	}
142
143	return (sp->sch_ops.sop_init());
144}
145
146static fmd_scheme_t *
147fmd_scheme_lookup(const char *dir, const char *name)
148{
149	fmd_scheme_t *sp;
150	char path[PATH_MAX];
151
152	for (sp = sch_list; sp != NULL; sp = sp->sch_next) {
153		if (strcmp(name, sp->sch_name) == 0)
154			return (sp);
155	}
156
157	if ((sp = fmd_scheme_create(name)) == NULL)
158		return (NULL); /* errno is set for us */
159
160	(void) snprintf(path, sizeof (path), "%s%s/%s.so",
161	    g_root ? g_root : "", dir, name);
162
163	if (access(path, F_OK) != 0) {
164		sp->sch_err = errno;
165		return (sp);
166	}
167
168	if ((sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_PARENT)) ==
169	    NULL) {
170		sp->sch_err = ELIBACC;
171		return (sp);
172	}
173
174	if (fmd_scheme_rtld_init(sp) != 0) {
175		sp->sch_err = errno;
176		(void) dlclose(sp->sch_dlp);
177		sp->sch_dlp = NULL;
178	}
179
180	return (sp);
181}
182
183char *
184sunFm_nvl2str(nvlist_t *nvl)
185{
186	fmd_scheme_t *sp;
187	char c, *name, *s = NULL;
188	ssize_t len;
189
190	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) {
191		DEBUGMSGTL((MODNAME_STR, "fmri does not contain required "
192		    "'%s' nvpair\n", FM_FMRI_SCHEME));
193		return (NULL);
194	}
195
196	if ((sp = fmd_scheme_lookup(DEFAULTSCHEMEDIR, name)) == NULL ||
197	    sp->sch_dlp == NULL || sp->sch_err != 0) {
198		const char *msg =
199		    sp->sch_err == ELIBACC ? dlerror() : strerror(sp->sch_err);
200		DEBUGMSGTL((MODNAME_STR, "cannot init '%s' scheme library to "
201		    "format fmri: %s\n", name, msg ? msg : "unknown error"));
202		return (NULL);
203	}
204
205	if ((len = sp->sch_ops.sop_nvl2str(nvl, &c, sizeof (c))) == -1 ||
206	    (s = malloc(len + 1)) == NULL ||
207	    sp->sch_ops.sop_nvl2str(nvl, s, len + 1) == -1) {
208		DEBUGMSGTL((MODNAME_STR, "cannot format fmri using scheme '%s'",
209		    name));
210		free(s);
211		return (NULL);
212	}
213
214	return (s);
215}
216
217void *
218fmd_fmri_alloc(size_t size)
219{
220	return (malloc(size));
221}
222
223void *
224fmd_fmri_zalloc(size_t size)
225{
226	void *data;
227
228	if ((data = malloc(size)) != NULL)
229		bzero(data, size);
230
231	return (data);
232}
233
234/*ARGSUSED*/
235void
236fmd_fmri_free(void *data, size_t size)
237{
238	free(data);
239}
240
241int
242fmd_fmri_error(int err)
243{
244	errno = err;
245	return (-1);
246}
247
248char *
249fmd_fmri_strescape(const char *s)
250{
251	return (strdup(s));
252}
253
254char *
255fmd_fmri_strdup(const char *s)
256{
257	return (strdup(s));
258}
259
260void
261fmd_fmri_strfree(char *s)
262{
263	free(s);
264}
265
266const char *
267fmd_fmri_get_rootdir(void)
268{
269	return (g_root ? g_root : "");
270}
271
272const char *
273fmd_fmri_get_platform(void)
274{
275	static char platform[MAXNAMELEN];
276
277	if (platform[0] == '\0')
278		(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
279
280	return (platform);
281}
282
283uint64_t
284fmd_fmri_get_drgen(void)
285{
286	return (0);
287}
288
289int
290fmd_fmri_set_errno(int err)
291{
292	errno = err;
293	return (-1);
294}
295
296/*ARGSUSED*/
297void
298fmd_fmri_warn(const char *format, ...)
299{
300}
301
302struct topo_hdl *
303fmd_fmri_topo_hold(int version)
304{
305	int err;
306
307	if (version != TOPO_VERSION)
308		return (NULL);
309
310	if (g_thp == NULL) {
311		if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) {
312			DEBUGMSGTL((MODNAME_STR, "topo_open failed: %s\n",
313			    topo_strerror(err)));
314			return (NULL);
315		}
316	}
317
318	return (g_thp);
319}
320
321/*ARGSUSED*/
322void
323fmd_fmri_topo_rele(struct topo_hdl *thp)
324{
325	/* nothing to do */
326}
327