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 *
ses_plugin_ctlpage_lookup(ses_plugin_t * sp,ses_snap_t * snap,int pagenum,size_t len,ses_node_t * np,boolean_t unique)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
ses_fill_node(ses_node_t * np)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
ses_node_ctl(ses_node_t * np,const char * op,nvlist_t * arg)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 *
ses_plugin_page_lookup(ses_plugin_t * sp,ses_snap_t * snap,int pagenum,ses_node_t * np,size_t * lenp)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 *
ses_get_pagedesc(ses_target_t * tp,int pagenum,ses_pagetype_t type)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
ses_plugin_register(ses_plugin_t * sp,int version,ses_plugin_config_t * scp)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
ses_plugin_setspecific(ses_plugin_t * sp,void * data)186 ses_plugin_setspecific(ses_plugin_t *sp, void *data)
187 {
188 sp->sp_data = data;
189 }
190
191 void *
ses_plugin_getspecific(ses_plugin_t * sp)192 ses_plugin_getspecific(ses_plugin_t *sp)
193 {
194 return (sp->sp_data);
195 }
196
197 static void
ses_plugin_cleanstr(char * s)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
ses_plugin_destroy(ses_plugin_t * sp)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
ses_plugin_loadone(ses_target_t * tp,const char * path,uint32_t pass)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
ses_plugin_load_dir(ses_target_t * tp,const char * pluginroot)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
ses_plugin_load(ses_target_t * tp)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
ses_plugin_unload(ses_target_t * tp)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