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 * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 27 */ 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 (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); 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 (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); 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 = strdupa(libscsi_vendor(tp->st_target)); 320 product = strdupa(libscsi_product(tp->st_target)); 321 revision = strdupa(libscsi_revision(tp->st_target)); 322 323 ses_plugin_cleanstr(vendor); 324 ses_plugin_cleanstr(product); 325 ses_plugin_cleanstr(revision); 326 327 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot, 328 LIBSES_PLUGIN_VENDOR, isa, vendor, 329 LIBSES_PLUGIN_EXT); 330 if (ses_plugin_loadone(tp, path, 1) != 0) 331 return (-1); 332 333 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot, 334 LIBSES_PLUGIN_VENDOR, isa, vendor, product, 335 LIBSES_PLUGIN_EXT); 336 if (ses_plugin_loadone(tp, path, 2) != 0) 337 return (-1); 338 339 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot, 340 LIBSES_PLUGIN_VENDOR, isa, vendor, product, 341 revision, LIBSES_PLUGIN_EXT); 342 if (ses_plugin_loadone(tp, path, 3) != 0) 343 return (-1); 344 345 return (0); 346 } 347 348 int 349 ses_plugin_load(ses_target_t *tp) 350 { 351 char pluginroot[PATH_MAX]; 352 const char *pluginpath, *p, *q; 353 354 if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL) 355 pluginpath = LIBSES_DEFAULT_PLUGINDIR; 356 ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL); 357 358 for (p = pluginpath; p != NULL; p = q) { 359 if ((q = strchr(p, ':')) != NULL) { 360 ptrdiff_t len = q - p; 361 (void) strncpy(pluginroot, p, len); 362 pluginroot[len] = '\0'; 363 while (*q == ':') 364 ++q; 365 if (*q == '\0') 366 q = NULL; 367 if (len == 0) 368 continue; 369 } else { 370 (void) strcpy(pluginroot, p); 371 } 372 373 if (pluginroot[0] != '/') 374 continue; 375 376 if (ses_plugin_load_dir(tp, pluginroot) != 0) 377 return (-1); 378 } 379 380 if (tp->st_plugin_first == NULL) 381 return (ses_error(ESES_PLUGIN, "no plugins found")); 382 383 return (0); 384 } 385 386 void 387 ses_plugin_unload(ses_target_t *tp) 388 { 389 ses_plugin_t *sp; 390 391 while ((sp = tp->st_plugin_first) != NULL) { 392 tp->st_plugin_first = sp->sp_next; 393 ses_plugin_destroy(sp); 394 } 395 } 396