move 32-bit libs to lib/i386 subdirs & 64-bit libs to lib/
[unleashed.git] / usr / src / lib / scsi / libses / common / ses_plugin.c
blob6ad72d1b7172cf47b9b8323a46a9de706de7fc7e
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
29 #include <scsi/libses.h>
30 #include "ses_impl.h"
32 static boolean_t ses_plugin_dlclose;
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)
39 ses_target_t *tp = snap->ss_target;
40 ses_snap_page_t *pp;
41 ses_pagedesc_t *dp;
43 if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL)
44 return (NULL);
46 if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL)
47 return (NULL);
49 if (np != NULL && 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);
57 int
58 ses_fill_node(ses_node_t *np)
60 ses_target_t *tp = np->sn_snapshot->ss_target;
61 ses_plugin_t *sp;
63 for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
64 if (sp->sp_node_parse == NULL)
65 continue;
67 if (sp->sp_node_parse(sp, np) != 0)
68 return (-1);
71 return (0);
74 int
75 ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg)
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;
83 if (nvlist_dup(arg, &nvl, 0) != 0)
84 return (ses_set_errno(ESES_NOMEM));
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.
92 (void) pthread_mutex_lock(&tp->st_lock);
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.
99 for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
100 if (sp->sp_node_ctl == NULL)
101 continue;
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);
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);
118 nvlist_free(nvl);
120 ret = ses_snap_do_ctl(np->sn_snapshot);
121 (void) pthread_mutex_unlock(&tp->st_lock);
123 return (ret);
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)
131 ses_snap_page_t *pp;
132 ses_target_t *tp = sp->sp_target;
133 ses_pagedesc_t *dp;
135 if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL)
136 return (NULL);
138 if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL)
139 return (NULL);
141 if (np != NULL && 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);
150 ses_pagedesc_t *
151 ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type)
153 ses_plugin_t *sp;
154 ses_pagedesc_t *dp;
156 for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
157 if (sp->sp_pages == NULL)
158 continue;
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;
166 if (dp->spd_pagenum == pagenum)
167 return (dp);
171 (void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum);
172 return (NULL);
176 ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp)
178 if (version != LIBSES_PLUGIN_VERSION)
179 return (ses_set_errno(ESES_VERSION));
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;
185 return (0);
188 void
189 ses_plugin_setspecific(ses_plugin_t *sp, void *data)
191 sp->sp_data = data;
194 void *
195 ses_plugin_getspecific(ses_plugin_t *sp)
197 return (sp->sp_data);
200 static void
201 ses_plugin_cleanstr(char *s)
203 while (*s != '\0') {
204 if (*s == ' ' || *s == '/')
205 *s = '-';
206 s++;
210 static void
211 ses_plugin_destroy(ses_plugin_t *sp)
213 if (sp->sp_initialized && sp->sp_fini != NULL)
214 sp->sp_fini(sp);
216 if (ses_plugin_dlclose)
217 (void) dlclose(sp->sp_object);
219 ses_free(sp);
222 static int
223 ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass)
225 ses_plugin_t *sp, **loc;
226 void *obj;
227 int (*ses_priority)(void);
229 if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
230 return (0);
232 if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) {
233 (void) dlclose(obj);
234 return (-1);
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;
242 if (sp->sp_init == NULL) {
243 ses_plugin_destroy(sp);
244 return (0);
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.
252 if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL)
253 sp->sp_priority = ses_priority();
255 sp->sp_priority |= (uint64_t)pass << 32;
257 for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
258 if ((*loc)->sp_priority > sp->sp_priority)
259 break;
262 if (*loc != NULL)
263 (*loc)->sp_prev = sp;
264 else
265 tp->st_plugin_last = sp;
267 sp->sp_next = *loc;
268 *loc = sp;
270 if (sp->sp_init(sp) != 0)
271 return (-1);
272 sp->sp_initialized = B_TRUE;
274 return (0);
277 static int
278 ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot)
280 char path[PATH_MAX];
281 DIR *dirp;
282 struct dirent *dp;
283 char *vendor, *product, *revision;
284 char isa[257];
286 (void) snprintf(path, sizeof (path), "%s/%s",
287 pluginroot, LIBSES_PLUGIN_FRAMEWORK);
289 #if defined(_LP64)
290 isa[0] = '\0';
291 #else
292 strcpy(isa, "i386");
293 #endif
295 if ((dirp = opendir(path)) != NULL) {
296 while ((dp = readdir(dirp)) != NULL) {
297 if (strcmp(dp->d_name, ".") == 0 ||
298 strcmp(dp->d_name, "..") == 0)
299 continue;
301 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
302 pluginroot, LIBSES_PLUGIN_FRAMEWORK,
303 isa, dp->d_name);
305 if (ses_plugin_loadone(tp, path, 0) != 0) {
306 (void) closedir(dirp);
307 return (-1);
311 (void) closedir(dirp);
315 * Create a local copy of the vendor/product/revision, strip out any
316 * questionable characters, and then attempt to load each plugin.
318 vendor = strdupa(libscsi_vendor(tp->st_target));
319 product = strdupa(libscsi_product(tp->st_target));
320 revision = strdupa(libscsi_revision(tp->st_target));
322 ses_plugin_cleanstr(vendor);
323 ses_plugin_cleanstr(product);
324 ses_plugin_cleanstr(revision);
326 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
327 LIBSES_PLUGIN_VENDOR, isa, vendor,
328 LIBSES_PLUGIN_EXT);
329 if (ses_plugin_loadone(tp, path, 1) != 0)
330 return (-1);
332 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
333 LIBSES_PLUGIN_VENDOR, isa, vendor, product,
334 LIBSES_PLUGIN_EXT);
335 if (ses_plugin_loadone(tp, path, 2) != 0)
336 return (-1);
338 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
339 LIBSES_PLUGIN_VENDOR, isa, vendor, product,
340 revision, LIBSES_PLUGIN_EXT);
341 if (ses_plugin_loadone(tp, path, 3) != 0)
342 return (-1);
344 return (0);
348 ses_plugin_load(ses_target_t *tp)
350 char pluginroot[PATH_MAX];
351 const char *pluginpath, *p, *q;
353 if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL)
354 pluginpath = LIBSES_DEFAULT_PLUGINDIR;
355 ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL);
357 for (p = pluginpath; p != NULL; p = q) {
358 if ((q = strchr(p, ':')) != NULL) {
359 ptrdiff_t len = q - p;
360 (void) strncpy(pluginroot, p, len);
361 pluginroot[len] = '\0';
362 while (*q == ':')
363 ++q;
364 if (*q == '\0')
365 q = NULL;
366 if (len == 0)
367 continue;
368 } else {
369 (void) strcpy(pluginroot, p);
372 if (pluginroot[0] != '/')
373 continue;
375 if (ses_plugin_load_dir(tp, pluginroot) != 0)
376 return (-1);
379 if (tp->st_plugin_first == NULL)
380 return (ses_error(ESES_PLUGIN, "no plugins found"));
382 return (0);
385 void
386 ses_plugin_unload(ses_target_t *tp)
388 ses_plugin_t *sp;
390 while ((sp = tp->st_plugin_first) != NULL) {
391 tp->st_plugin_first = sp->sp_next;
392 ses_plugin_destroy(sp);