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