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