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