xref: /illumos-gate/usr/src/lib/scsi/libses/common/ses_plugin.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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 2008 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 <scsi/libses.h>
30 #include "ses_impl.h"
31 
32 static boolean_t ses_plugin_dlclose;
33 
34 /*ARGSUSED*/
35 void *
36 ses_plugin_ctlpage_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
37     size_t len, ses_node_t *np, boolean_t unique)
38 {
39 	ses_target_t *tp = snap->ss_target;
40 	ses_snap_page_t *pp;
41 	ses_pagedesc_t *dp;
42 
43 	if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL)
44 		return (NULL);
45 
46 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL)
47 		return (NULL);
48 
49 	if (dp->spd_ctl_fill != NULL) {
50 		return (dp->spd_ctl_fill(sp, pp->ssp_page,
51 		    pp->ssp_len, np));
52 	} else {
53 		return (pp->ssp_page);
54 	}
55 }
56 
57 int
58 ses_fill_node(ses_node_t *np)
59 {
60 	ses_target_t *tp = np->sn_snapshot->ss_target;
61 	ses_plugin_t *sp;
62 
63 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
64 		if (sp->sp_node_parse == NULL)
65 			continue;
66 
67 		if (sp->sp_node_parse(sp, np) != 0)
68 			return (-1);
69 	}
70 
71 	return (0);
72 }
73 
74 int
75 ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg)
76 {
77 	ses_target_t *tp = np->sn_snapshot->ss_target;
78 	ses_plugin_t *sp;
79 	nvlist_t *nvl;
80 	nvpair_t *nvp;
81 	int ret;
82 
83 	if (nvlist_dup(arg, &nvl, 0) != 0)
84 		return (ses_set_errno(ESES_NOMEM));
85 
86 	/*
87 	 * Technically we could get away with a per-snapshot lock while we fill
88 	 * the control page contents, but this doesn't take much time and we
89 	 * want actual control operations to be protected per-target, so we just
90 	 * take the target lock.
91 	 */
92 	(void) pthread_mutex_lock(&tp->st_lock);
93 
94 	/*
95 	 * We walk the list of plugins backwards, so that a product-specific
96 	 * plugin can rewrite the nvlist to control operations in terms of the
97 	 * standard mechanisms, if desired.
98 	 */
99 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
100 		if (sp->sp_node_ctl == NULL)
101 			continue;
102 
103 		if (sp->sp_node_ctl(sp, np, op, nvl) != 0) {
104 			nvlist_free(nvl);
105 			(void) pthread_mutex_unlock(&tp->st_lock);
106 			return (-1);
107 		}
108 	}
109 
110 	if ((nvp = nvlist_next_nvpair(nvl, NULL)) != NULL) {
111 		(void) ses_error(ESES_NOTSUP, "property '%s' invalid for "
112 		    "this node", nvpair_name(nvp));
113 		nvlist_free(nvl);
114 		(void) pthread_mutex_unlock(&tp->st_lock);
115 		return (-1);
116 	}
117 
118 	nvlist_free(nvl);
119 
120 	ret = ses_snap_do_ctl(np->sn_snapshot);
121 	(void) pthread_mutex_unlock(&tp->st_lock);
122 
123 	return (ret);
124 }
125 
126 /*ARGSUSED*/
127 void *
128 ses_plugin_page_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
129     ses_node_t *np, size_t *lenp)
130 {
131 	ses_snap_page_t *pp;
132 	ses_target_t *tp = sp->sp_target;
133 	ses_pagedesc_t *dp;
134 
135 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL)
136 		return (NULL);
137 
138 	if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL)
139 		return (NULL);
140 
141 	if (dp->spd_index != NULL) {
142 		return (dp->spd_index(sp, np, pp->ssp_page, pp->ssp_len,
143 		    lenp));
144 	} else {
145 		*lenp = pp->ssp_len;
146 		return (pp->ssp_page);
147 	}
148 }
149 
150 ses_pagedesc_t *
151 ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type)
152 {
153 	ses_plugin_t *sp;
154 	ses_pagedesc_t *dp;
155 
156 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
157 		if (sp->sp_pages == NULL)
158 			continue;
159 
160 		for (dp = &sp->sp_pages[0]; dp->spd_pagenum != -1;
161 		    dp++) {
162 			if ((type == SES_PAGE_CTL && dp->spd_ctl_len == NULL) ||
163 			    (type == SES_PAGE_DIAG && dp->spd_ctl_len != NULL))
164 				continue;
165 
166 			if (dp->spd_pagenum == pagenum)
167 				return (dp);
168 		}
169 	}
170 
171 	(void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum);
172 	return (NULL);
173 }
174 
175 int
176 ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp)
177 {
178 	if (version != LIBSES_PLUGIN_VERSION)
179 		return (ses_set_errno(ESES_VERSION));
180 
181 	sp->sp_pages = scp->spc_pages;
182 	sp->sp_node_parse = scp->spc_node_parse;
183 	sp->sp_node_ctl = scp->spc_node_ctl;
184 
185 	return (0);
186 }
187 
188 void
189 ses_plugin_setspecific(ses_plugin_t *sp, void *data)
190 {
191 	sp->sp_data = data;
192 }
193 
194 void *
195 ses_plugin_getspecific(ses_plugin_t *sp)
196 {
197 	return (sp->sp_data);
198 }
199 
200 static void
201 ses_plugin_cleanstr(char *s)
202 {
203 	while (*s != '\0') {
204 		if (*s == ' ' || *s == '/')
205 			*s = '-';
206 		s++;
207 	}
208 }
209 
210 static void
211 ses_plugin_destroy(ses_plugin_t *sp)
212 {
213 	if (sp->sp_initialized && sp->sp_fini != NULL)
214 		sp->sp_fini(sp);
215 
216 	if (ses_plugin_dlclose)
217 		(void) dlclose(sp->sp_object);
218 
219 	ses_free(sp);
220 }
221 
222 static int
223 ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass)
224 {
225 	ses_plugin_t *sp, **loc;
226 	void *obj;
227 	int (*ses_priority)(void);
228 
229 	if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
230 		return (0);
231 
232 	if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) {
233 		(void) dlclose(obj);
234 		return (-1);
235 	}
236 
237 	sp->sp_object = obj;
238 	sp->sp_init = (int (*)())dlsym(obj, "_ses_init");
239 	sp->sp_fini = (void (*)())dlsym(obj, "_ses_fini");
240 	sp->sp_target = tp;
241 
242 	if (sp->sp_init == NULL) {
243 		ses_plugin_destroy(sp);
244 		return (0);
245 	}
246 
247 	/*
248 	 * Framework modules can establish an explicit prioritying by declaring
249 	 * the '_ses_priority' symbol, which returns an integer used to create
250 	 * an explicit ordering between plugins.
251 	 */
252 	if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL)
253 		sp->sp_priority = ses_priority();
254 
255 	sp->sp_priority |= (uint64_t)pass << 32;
256 
257 	for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
258 		if ((*loc)->sp_priority > sp->sp_priority)
259 			break;
260 	}
261 
262 	if (*loc != NULL)
263 		(*loc)->sp_prev = sp;
264 	else
265 		tp->st_plugin_last = sp;
266 
267 	sp->sp_next = *loc;
268 	*loc = sp;
269 
270 	if (sp->sp_init(sp) != 0)
271 		return (-1);
272 	sp->sp_initialized = B_TRUE;
273 
274 	return (0);
275 }
276 
277 static int
278 ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot)
279 {
280 	char path[PATH_MAX];
281 	DIR *dirp;
282 	struct dirent64 *dp;
283 	char *vendor, *product, *revision;
284 	char isa[257];
285 
286 	(void) snprintf(path, sizeof (path), "%s/%s",
287 	    pluginroot, LIBSES_PLUGIN_FRAMEWORK);
288 
289 #if defined(_LP64)
290 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
291 		isa[0] = '\0';
292 #else
293 	isa[0] = '\0';
294 #endif
295 
296 	if ((dirp = opendir(path)) != NULL) {
297 		while ((dp = readdir64(dirp)) != NULL) {
298 			if (strcmp(dp->d_name, ".") == 0 ||
299 			    strcmp(dp->d_name, "..") == 0)
300 				continue;
301 
302 			(void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
303 			    pluginroot, LIBSES_PLUGIN_FRAMEWORK,
304 			    isa, dp->d_name);
305 
306 			if (ses_plugin_loadone(tp, path, 0) != 0) {
307 				(void) closedir(dirp);
308 				return (-1);
309 			}
310 		}
311 
312 		(void) closedir(dirp);
313 	}
314 
315 	/*
316 	 * Create a local copy of the vendor/product/revision, strip out any
317 	 * questionable characters, and then attempt to load each plugin.
318 	 */
319 	vendor = alloca(strlen(libscsi_vendor(tp->st_target)) + 1);
320 	product = alloca(strlen(libscsi_product(tp->st_target)) + 1);
321 	revision = alloca(strlen(libscsi_revision(tp->st_target)) + 1);
322 	(void) strcpy(vendor, libscsi_vendor(tp->st_target));
323 	(void) strcpy(product, libscsi_product(tp->st_target));
324 	(void) strcpy(revision, libscsi_revision(tp->st_target));
325 
326 	ses_plugin_cleanstr(vendor);
327 	ses_plugin_cleanstr(product);
328 	ses_plugin_cleanstr(revision);
329 
330 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
331 	    LIBSES_PLUGIN_VENDOR, isa, vendor,
332 	    LIBSES_PLUGIN_EXT);
333 	if (ses_plugin_loadone(tp, path, 1) != 0)
334 		return (-1);
335 
336 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
337 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
338 	    LIBSES_PLUGIN_EXT);
339 	if (ses_plugin_loadone(tp, path, 2) != 0)
340 		return (-1);
341 
342 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
343 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
344 	    revision, LIBSES_PLUGIN_EXT);
345 	if (ses_plugin_loadone(tp, path, 3) != 0)
346 		return (-1);
347 
348 	return (0);
349 }
350 
351 int
352 ses_plugin_load(ses_target_t *tp)
353 {
354 	char pluginroot[PATH_MAX];
355 	const char *pluginpath, *p, *q;
356 
357 	if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL)
358 		pluginpath = LIBSES_DEFAULT_PLUGINDIR;
359 	ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL);
360 
361 	for (p = pluginpath, q = strchr(p, ':'); p != NULL; p = q) {
362 		if (q != NULL) {
363 			ptrdiff_t len = q - p;
364 			(void) strncpy(pluginroot, p, len);
365 			pluginroot[len] = '\0';
366 			while (*q == ':')
367 				++q;
368 			if (*q == '\0')
369 				q = NULL;
370 			if (len == 0)
371 				continue;
372 		} else {
373 			(void) strcpy(pluginroot, p);
374 		}
375 
376 		if (pluginroot[0] != '/')
377 			continue;
378 
379 		if (ses_plugin_load_dir(tp, pluginpath) != 0)
380 			return (-1);
381 	}
382 
383 	if (tp->st_plugin_first == NULL)
384 		return (ses_error(ESES_PLUGIN, "no plugins found"));
385 
386 	return (0);
387 }
388 
389 void
390 ses_plugin_unload(ses_target_t *tp)
391 {
392 	ses_plugin_t *sp;
393 
394 	while ((sp = tp->st_plugin_first) != NULL) {
395 		tp->st_plugin_first = sp->sp_next;
396 		ses_plugin_destroy(sp);
397 	}
398 }
399