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