xref: /illumos-gate/usr/src/cmd/fm/fmdump/common/scheme.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 #include <sys/types.h>
27 #include <sys/systeminfo.h>
28 
29 #include <limits.h>
30 #include <strings.h>
31 #include <stddef.h>
32 #include <unistd.h>
33 #include <dlfcn.h>
34 #include <errno.h>
35 
36 #include <fmdump.h>
37 
38 /*
39  * fmdump loadable scheme support
40  *
41  * This file provides a pared-down implementation of fmd's fmd_fmri.c and
42  * fmd_scheme.c and must be kept in sync with the set of service routines
43  * required by scheme plug-ins.  At some point if other utilities want to
44  * use this we can refactor it into a more general library.  (Note: fmd
45  * cannot use such a library because it has its own internal locking, etc.)
46  * As schemes are needed, we dlopen() them and cache a list of them which we
47  * can search later.  We also use the list as a negative cache: if we fail to
48  * load a scheme, we add an entry with sch_dlp = NULL and sch_err recording
49  * the errno to be returned to the caller.
50  */
51 
52 typedef struct fmd_scheme_ops {
53 	int (*sop_init)(void);
54 	void (*sop_fini)(void);
55 	ssize_t (*sop_nvl2str)(nvlist_t *, char *, size_t);
56 } fmd_scheme_ops_t;
57 
58 typedef struct fmd_scheme_opd {
59 	const char *opd_name;		/* symbol name of scheme function */
60 	size_t opd_off;			/* offset within fmd_scheme_ops_t */
61 } fmd_scheme_opd_t;
62 
63 typedef struct fmd_scheme {
64 	struct fmd_scheme *sch_next;    /* next scheme on list of schemes */
65 	char *sch_name;			/* name of this scheme (fmri prefix) */
66 	void *sch_dlp;			/* libdl(3DL) shared library handle */
67 	int sch_err;			/* if negative entry, errno to return */
68 	fmd_scheme_ops_t sch_ops;	/* scheme function pointers */
69 } fmd_scheme_t;
70 
71 static fmd_scheme_t *sch_list;		/* list of cached schemes */
72 
73 static long
74 fmd_scheme_notsup(void)
75 {
76 	errno = ENOTSUP;
77 	return (-1);
78 }
79 
80 static void
81 fmd_scheme_vnop(void)
82 {
83 }
84 
85 static int
86 fmd_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  */
95 static const fmd_scheme_ops_t _fmd_scheme_default_ops = {
96 	(int (*)())fmd_scheme_nop,		/* sop_init */
97 	(void (*)())fmd_scheme_vnop,		/* 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  */
105 static 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 
112 static fmd_scheme_t *
113 fmd_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 
132 static int
133 fmd_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 
146 static fmd_scheme_t *
147 fmd_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)) == NULL) {
169 		sp->sch_err = ELIBACC;
170 		return (sp);
171 	}
172 
173 	if (fmd_scheme_rtld_init(sp) != 0) {
174 		sp->sch_err = errno;
175 		(void) dlclose(sp->sch_dlp);
176 		sp->sch_dlp = NULL;
177 	}
178 
179 	return (sp);
180 }
181 
182 char *
183 fmdump_nvl2str(nvlist_t *nvl)
184 {
185 	fmd_scheme_t *sp;
186 	char c, *name, *s = NULL;
187 	ssize_t len;
188 
189 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) {
190 		fmdump_warn("fmri does not contain required '%s' nvpair\n",
191 		    FM_FMRI_SCHEME);
192 		return (NULL);
193 	}
194 
195 	if ((sp = fmd_scheme_lookup("/usr/lib/fm/fmd/schemes", name)) == NULL ||
196 	    sp->sch_dlp == NULL || sp->sch_err != 0) {
197 		const char *msg =
198 		    sp->sch_err == ELIBACC ? dlerror() : strerror(sp->sch_err);
199 
200 		fmdump_warn("cannot init '%s' scheme library to "
201 		    "format fmri: %s\n", name, msg ? msg : "unknown error");
202 
203 		return (NULL);
204 	}
205 
206 	if ((len = sp->sch_ops.sop_nvl2str(nvl, &c, sizeof (c))) == -1 ||
207 	    (s = malloc(len + 1)) == NULL ||
208 	    sp->sch_ops.sop_nvl2str(nvl, s, len + 1) == -1) {
209 		fmdump_warn("cannot format fmri using scheme '%s'", name);
210 		free(s);
211 		return (NULL);
212 	}
213 
214 	return (s);
215 }
216 
217 
218 void *
219 fmd_fmri_alloc(size_t size)
220 {
221 	return (malloc(size));
222 }
223 
224 void *
225 fmd_fmri_zalloc(size_t size)
226 {
227 	void *data;
228 
229 	if ((data = malloc(size)) != NULL)
230 		bzero(data, size);
231 
232 	return (data);
233 }
234 
235 /*ARGSUSED*/
236 void
237 fmd_fmri_free(void *data, size_t size)
238 {
239 	free(data);
240 }
241 
242 int
243 fmd_fmri_error(int err)
244 {
245 	errno = err;
246 	return (-1);
247 }
248 
249 char *
250 fmd_fmri_strescape(const char *s)
251 {
252 	return (strdup(s));
253 }
254 
255 char *
256 fmd_fmri_strdup(const char *s)
257 {
258 	return (strdup(s));
259 }
260 
261 void
262 fmd_fmri_strfree(char *s)
263 {
264 	free(s);
265 }
266 
267 const char *
268 fmd_fmri_get_rootdir(void)
269 {
270 	return (g_root ? g_root : "");
271 }
272 
273 const char *
274 fmd_fmri_get_platform(void)
275 {
276 	static char platform[MAXNAMELEN];
277 
278 	if (platform[0] == '\0')
279 		(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
280 
281 	return (platform);
282 }
283 
284 uint64_t
285 fmd_fmri_get_drgen(void)
286 {
287 	return (0);
288 }
289 
290 int
291 fmd_fmri_set_errno(int err)
292 {
293 	errno = err;
294 	return (-1);
295 }
296 
297 void
298 fmd_fmri_warn(const char *format, ...)
299 {
300 	va_list ap;
301 
302 	va_start(ap, format);
303 	fmdump_vwarn(format, ap);
304 	va_end(ap);
305 }
306 
307 struct topo_hdl *
308 fmd_fmri_topo_hold(int version)
309 {
310 	int err;
311 
312 	if (version != TOPO_VERSION)
313 		return (NULL);
314 
315 	if (g_thp == NULL) {
316 		if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) {
317 			(void) fprintf(stderr, "topo_open failed: %s\n",
318 			    topo_strerror(err));
319 			exit(1);
320 		}
321 	}
322 
323 	return (g_thp);
324 }
325 
326 /*ARGSUSED*/
327 void
328 fmd_fmri_topo_rele(struct topo_hdl *thp)
329 {
330 	/* nothing to do */
331 }
332