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