1275c9da8Seschrock /*
2275c9da8Seschrock  * CDDL HEADER START
3275c9da8Seschrock  *
4275c9da8Seschrock  * The contents of this file are subject to the terms of the
5275c9da8Seschrock  * Common Development and Distribution License (the "License").
6275c9da8Seschrock  * You may not use this file except in compliance with the License.
7275c9da8Seschrock  *
8275c9da8Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock  * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock  * See the License for the specific language governing permissions
11275c9da8Seschrock  * and limitations under the License.
12275c9da8Seschrock  *
13275c9da8Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock  *
19275c9da8Seschrock  * CDDL HEADER END
20275c9da8Seschrock  */
21275c9da8Seschrock 
22275c9da8Seschrock /*
23ac88567aSHyon Kim  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24275c9da8Seschrock  */
25*a8be79faSAlexander Stetsenko /*
26*a8be79faSAlexander Stetsenko  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
27*a8be79faSAlexander Stetsenko  */
28275c9da8Seschrock 
29275c9da8Seschrock #include <scsi/libses.h>
30275c9da8Seschrock #include "ses_impl.h"
31275c9da8Seschrock 
32275c9da8Seschrock static boolean_t ses_plugin_dlclose;
33275c9da8Seschrock 
34275c9da8Seschrock /*ARGSUSED*/
35275c9da8Seschrock 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)36275c9da8Seschrock ses_plugin_ctlpage_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
37275c9da8Seschrock     size_t len, ses_node_t *np, boolean_t unique)
38275c9da8Seschrock {
39275c9da8Seschrock 	ses_target_t *tp = snap->ss_target;
40275c9da8Seschrock 	ses_snap_page_t *pp;
41275c9da8Seschrock 	ses_pagedesc_t *dp;
42275c9da8Seschrock 
43275c9da8Seschrock 	if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL)
44275c9da8Seschrock 		return (NULL);
45275c9da8Seschrock 
46275c9da8Seschrock 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL)
47275c9da8Seschrock 		return (NULL);
48275c9da8Seschrock 
49*a8be79faSAlexander Stetsenko 	if (np != NULL && dp->spd_ctl_fill != NULL) {
50275c9da8Seschrock 		return (dp->spd_ctl_fill(sp, pp->ssp_page,
51275c9da8Seschrock 		    pp->ssp_len, np));
52275c9da8Seschrock 	} else {
53275c9da8Seschrock 		return (pp->ssp_page);
54275c9da8Seschrock 	}
55275c9da8Seschrock }
56275c9da8Seschrock 
57275c9da8Seschrock int
ses_fill_node(ses_node_t * np)58275c9da8Seschrock ses_fill_node(ses_node_t *np)
59275c9da8Seschrock {
60275c9da8Seschrock 	ses_target_t *tp = np->sn_snapshot->ss_target;
61275c9da8Seschrock 	ses_plugin_t *sp;
62275c9da8Seschrock 
63275c9da8Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
64275c9da8Seschrock 		if (sp->sp_node_parse == NULL)
65275c9da8Seschrock 			continue;
66275c9da8Seschrock 
67275c9da8Seschrock 		if (sp->sp_node_parse(sp, np) != 0)
68275c9da8Seschrock 			return (-1);
69275c9da8Seschrock 	}
70275c9da8Seschrock 
71275c9da8Seschrock 	return (0);
72275c9da8Seschrock }
73275c9da8Seschrock 
74275c9da8Seschrock int
ses_node_ctl(ses_node_t * np,const char * op,nvlist_t * arg)75275c9da8Seschrock ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg)
76275c9da8Seschrock {
77275c9da8Seschrock 	ses_target_t *tp = np->sn_snapshot->ss_target;
78275c9da8Seschrock 	ses_plugin_t *sp;
79275c9da8Seschrock 	nvlist_t *nvl;
80275c9da8Seschrock 	nvpair_t *nvp;
81275c9da8Seschrock 	int ret;
82275c9da8Seschrock 
83275c9da8Seschrock 	if (nvlist_dup(arg, &nvl, 0) != 0)
84275c9da8Seschrock 		return (ses_set_errno(ESES_NOMEM));
85275c9da8Seschrock 
86275c9da8Seschrock 	/*
87275c9da8Seschrock 	 * Technically we could get away with a per-snapshot lock while we fill
88275c9da8Seschrock 	 * the control page contents, but this doesn't take much time and we
89275c9da8Seschrock 	 * want actual control operations to be protected per-target, so we just
90275c9da8Seschrock 	 * take the target lock.
91275c9da8Seschrock 	 */
92275c9da8Seschrock 	(void) pthread_mutex_lock(&tp->st_lock);
93275c9da8Seschrock 
94275c9da8Seschrock 	/*
95275c9da8Seschrock 	 * We walk the list of plugins backwards, so that a product-specific
96275c9da8Seschrock 	 * plugin can rewrite the nvlist to control operations in terms of the
97275c9da8Seschrock 	 * standard mechanisms, if desired.
98275c9da8Seschrock 	 */
99275c9da8Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
100275c9da8Seschrock 		if (sp->sp_node_ctl == NULL)
101275c9da8Seschrock 			continue;
102275c9da8Seschrock 
103275c9da8Seschrock 		if (sp->sp_node_ctl(sp, np, op, nvl) != 0) {
104275c9da8Seschrock 			nvlist_free(nvl);
105275c9da8Seschrock 			(void) pthread_mutex_unlock(&tp->st_lock);
106275c9da8Seschrock 			return (-1);
107275c9da8Seschrock 		}
108275c9da8Seschrock 	}
109275c9da8Seschrock 
110275c9da8Seschrock 	if ((nvp = nvlist_next_nvpair(nvl, NULL)) != NULL) {
111275c9da8Seschrock 		(void) ses_error(ESES_NOTSUP, "property '%s' invalid for "
112275c9da8Seschrock 		    "this node", nvpair_name(nvp));
113275c9da8Seschrock 		nvlist_free(nvl);
114275c9da8Seschrock 		(void) pthread_mutex_unlock(&tp->st_lock);
115275c9da8Seschrock 		return (-1);
116275c9da8Seschrock 	}
117275c9da8Seschrock 
118275c9da8Seschrock 	nvlist_free(nvl);
119275c9da8Seschrock 
120275c9da8Seschrock 	ret = ses_snap_do_ctl(np->sn_snapshot);
121275c9da8Seschrock 	(void) pthread_mutex_unlock(&tp->st_lock);
122275c9da8Seschrock 
123275c9da8Seschrock 	return (ret);
124275c9da8Seschrock }
125275c9da8Seschrock 
126275c9da8Seschrock /*ARGSUSED*/
127275c9da8Seschrock void *
ses_plugin_page_lookup(ses_plugin_t * sp,ses_snap_t * snap,int pagenum,ses_node_t * np,size_t * lenp)128275c9da8Seschrock ses_plugin_page_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
129275c9da8Seschrock     ses_node_t *np, size_t *lenp)
130275c9da8Seschrock {
131275c9da8Seschrock 	ses_snap_page_t *pp;
132275c9da8Seschrock 	ses_target_t *tp = sp->sp_target;
133275c9da8Seschrock 	ses_pagedesc_t *dp;
134275c9da8Seschrock 
135275c9da8Seschrock 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL)
136275c9da8Seschrock 		return (NULL);
137275c9da8Seschrock 
138275c9da8Seschrock 	if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL)
139275c9da8Seschrock 		return (NULL);
140275c9da8Seschrock 
141*a8be79faSAlexander Stetsenko 	if (np != NULL && dp->spd_index != NULL) {
142275c9da8Seschrock 		return (dp->spd_index(sp, np, pp->ssp_page, pp->ssp_len,
143275c9da8Seschrock 		    lenp));
144275c9da8Seschrock 	} else {
145275c9da8Seschrock 		*lenp = pp->ssp_len;
146275c9da8Seschrock 		return (pp->ssp_page);
147275c9da8Seschrock 	}
148275c9da8Seschrock }
149275c9da8Seschrock 
150275c9da8Seschrock ses_pagedesc_t *
ses_get_pagedesc(ses_target_t * tp,int pagenum,ses_pagetype_t type)151275c9da8Seschrock ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type)
152275c9da8Seschrock {
153275c9da8Seschrock 	ses_plugin_t *sp;
154275c9da8Seschrock 	ses_pagedesc_t *dp;
155275c9da8Seschrock 
156275c9da8Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
157275c9da8Seschrock 		if (sp->sp_pages == NULL)
158275c9da8Seschrock 			continue;
159275c9da8Seschrock 
160275c9da8Seschrock 		for (dp = &sp->sp_pages[0]; dp->spd_pagenum != -1;
161275c9da8Seschrock 		    dp++) {
162275c9da8Seschrock 			if ((type == SES_PAGE_CTL && dp->spd_ctl_len == NULL) ||
163275c9da8Seschrock 			    (type == SES_PAGE_DIAG && dp->spd_ctl_len != NULL))
164275c9da8Seschrock 				continue;
165275c9da8Seschrock 
166275c9da8Seschrock 			if (dp->spd_pagenum == pagenum)
167275c9da8Seschrock 				return (dp);
168275c9da8Seschrock 		}
169275c9da8Seschrock 	}
170275c9da8Seschrock 
171275c9da8Seschrock 	(void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum);
172275c9da8Seschrock 	return (NULL);
173275c9da8Seschrock }
174275c9da8Seschrock 
175275c9da8Seschrock int
ses_plugin_register(ses_plugin_t * sp,int version,ses_plugin_config_t * scp)176275c9da8Seschrock ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp)
177275c9da8Seschrock {
178275c9da8Seschrock 	if (version != LIBSES_PLUGIN_VERSION)
179275c9da8Seschrock 		return (ses_set_errno(ESES_VERSION));
180275c9da8Seschrock 
181275c9da8Seschrock 	sp->sp_pages = scp->spc_pages;
182275c9da8Seschrock 	sp->sp_node_parse = scp->spc_node_parse;
183275c9da8Seschrock 	sp->sp_node_ctl = scp->spc_node_ctl;
184275c9da8Seschrock 
185275c9da8Seschrock 	return (0);
186275c9da8Seschrock }
187275c9da8Seschrock 
188275c9da8Seschrock void
ses_plugin_setspecific(ses_plugin_t * sp,void * data)189275c9da8Seschrock ses_plugin_setspecific(ses_plugin_t *sp, void *data)
190275c9da8Seschrock {
191275c9da8Seschrock 	sp->sp_data = data;
192275c9da8Seschrock }
193275c9da8Seschrock 
194275c9da8Seschrock void *
ses_plugin_getspecific(ses_plugin_t * sp)195275c9da8Seschrock ses_plugin_getspecific(ses_plugin_t *sp)
196275c9da8Seschrock {
197275c9da8Seschrock 	return (sp->sp_data);
198275c9da8Seschrock }
199275c9da8Seschrock 
200275c9da8Seschrock static void
ses_plugin_cleanstr(char * s)201275c9da8Seschrock ses_plugin_cleanstr(char *s)
202275c9da8Seschrock {
203275c9da8Seschrock 	while (*s != '\0') {
204275c9da8Seschrock 		if (*s == ' ' || *s == '/')
205275c9da8Seschrock 			*s = '-';
206275c9da8Seschrock 		s++;
207275c9da8Seschrock 	}
208275c9da8Seschrock }
209275c9da8Seschrock 
210275c9da8Seschrock static void
ses_plugin_destroy(ses_plugin_t * sp)211275c9da8Seschrock ses_plugin_destroy(ses_plugin_t *sp)
212275c9da8Seschrock {
213275c9da8Seschrock 	if (sp->sp_initialized && sp->sp_fini != NULL)
214275c9da8Seschrock 		sp->sp_fini(sp);
215275c9da8Seschrock 
216c5904d13Seschrock 	if (ses_plugin_dlclose)
217275c9da8Seschrock 		(void) dlclose(sp->sp_object);
218275c9da8Seschrock 
219275c9da8Seschrock 	ses_free(sp);
220275c9da8Seschrock }
221275c9da8Seschrock 
222275c9da8Seschrock static int
ses_plugin_loadone(ses_target_t * tp,const char * path,uint32_t pass)223275c9da8Seschrock ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass)
224275c9da8Seschrock {
225275c9da8Seschrock 	ses_plugin_t *sp, **loc;
226275c9da8Seschrock 	void *obj;
227275c9da8Seschrock 	int (*ses_priority)(void);
228275c9da8Seschrock 
229275c9da8Seschrock 	if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
230275c9da8Seschrock 		return (0);
231275c9da8Seschrock 
232275c9da8Seschrock 	if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) {
233275c9da8Seschrock 		(void) dlclose(obj);
234275c9da8Seschrock 		return (-1);
235275c9da8Seschrock 	}
236275c9da8Seschrock 
237275c9da8Seschrock 	sp->sp_object = obj;
238275c9da8Seschrock 	sp->sp_init = (int (*)())dlsym(obj, "_ses_init");
239275c9da8Seschrock 	sp->sp_fini = (void (*)())dlsym(obj, "_ses_fini");
240275c9da8Seschrock 	sp->sp_target = tp;
241275c9da8Seschrock 
242275c9da8Seschrock 	if (sp->sp_init == NULL) {
243275c9da8Seschrock 		ses_plugin_destroy(sp);
244275c9da8Seschrock 		return (0);
245275c9da8Seschrock 	}
246275c9da8Seschrock 
247275c9da8Seschrock 	/*
248275c9da8Seschrock 	 * Framework modules can establish an explicit prioritying by declaring
249275c9da8Seschrock 	 * the '_ses_priority' symbol, which returns an integer used to create
250275c9da8Seschrock 	 * an explicit ordering between plugins.
251275c9da8Seschrock 	 */
252275c9da8Seschrock 	if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL)
253275c9da8Seschrock 		sp->sp_priority = ses_priority();
254275c9da8Seschrock 
255275c9da8Seschrock 	sp->sp_priority |= (uint64_t)pass << 32;
256275c9da8Seschrock 
257275c9da8Seschrock 	for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
258275c9da8Seschrock 		if ((*loc)->sp_priority > sp->sp_priority)
259275c9da8Seschrock 			break;
260275c9da8Seschrock 	}
261275c9da8Seschrock 
262275c9da8Seschrock 	if (*loc != NULL)
263275c9da8Seschrock 		(*loc)->sp_prev = sp;
264275c9da8Seschrock 	else
265275c9da8Seschrock 		tp->st_plugin_last = sp;
266275c9da8Seschrock 
267275c9da8Seschrock 	sp->sp_next = *loc;
268275c9da8Seschrock 	*loc = sp;
269275c9da8Seschrock 
270275c9da8Seschrock 	if (sp->sp_init(sp) != 0)
271275c9da8Seschrock 		return (-1);
272275c9da8Seschrock 	sp->sp_initialized = B_TRUE;
273275c9da8Seschrock 
274275c9da8Seschrock 	return (0);
275275c9da8Seschrock }
276275c9da8Seschrock 
277275c9da8Seschrock static int
ses_plugin_load_dir(ses_target_t * tp,const char * pluginroot)278275c9da8Seschrock ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot)
279275c9da8Seschrock {
280275c9da8Seschrock 	char path[PATH_MAX];
281275c9da8Seschrock 	DIR *dirp;
282275c9da8Seschrock 	struct dirent64 *dp;
283275c9da8Seschrock 	char *vendor, *product, *revision;
284275c9da8Seschrock 	char isa[257];
285275c9da8Seschrock 
286275c9da8Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s",
287275c9da8Seschrock 	    pluginroot, LIBSES_PLUGIN_FRAMEWORK);
288275c9da8Seschrock 
289275c9da8Seschrock #if defined(_LP64)
290275c9da8Seschrock 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
291275c9da8Seschrock 		isa[0] = '\0';
292275c9da8Seschrock #else
293275c9da8Seschrock 	isa[0] = '\0';
294275c9da8Seschrock #endif
295275c9da8Seschrock 
296275c9da8Seschrock 	if ((dirp = opendir(path)) != NULL) {
297275c9da8Seschrock 		while ((dp = readdir64(dirp)) != NULL) {
298275c9da8Seschrock 			if (strcmp(dp->d_name, ".") == 0 ||
299275c9da8Seschrock 			    strcmp(dp->d_name, "..") == 0)
300275c9da8Seschrock 				continue;
301275c9da8Seschrock 
302275c9da8Seschrock 			(void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
303275c9da8Seschrock 			    pluginroot, LIBSES_PLUGIN_FRAMEWORK,
304275c9da8Seschrock 			    isa, dp->d_name);
305275c9da8Seschrock 
306275c9da8Seschrock 			if (ses_plugin_loadone(tp, path, 0) != 0) {
307275c9da8Seschrock 				(void) closedir(dirp);
308275c9da8Seschrock 				return (-1);
309275c9da8Seschrock 			}
310275c9da8Seschrock 		}
311275c9da8Seschrock 
312275c9da8Seschrock 		(void) closedir(dirp);
313275c9da8Seschrock 	}
314275c9da8Seschrock 
315275c9da8Seschrock 	/*
316275c9da8Seschrock 	 * Create a local copy of the vendor/product/revision, strip out any
317275c9da8Seschrock 	 * questionable characters, and then attempt to load each plugin.
318275c9da8Seschrock 	 */
31923a1cceaSRoger A. Faulkner 	vendor = strdupa(libscsi_vendor(tp->st_target));
32023a1cceaSRoger A. Faulkner 	product = strdupa(libscsi_product(tp->st_target));
32123a1cceaSRoger A. Faulkner 	revision = strdupa(libscsi_revision(tp->st_target));
322275c9da8Seschrock 
323275c9da8Seschrock 	ses_plugin_cleanstr(vendor);
324275c9da8Seschrock 	ses_plugin_cleanstr(product);
325275c9da8Seschrock 	ses_plugin_cleanstr(revision);
326275c9da8Seschrock 
327275c9da8Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
328275c9da8Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor,
329275c9da8Seschrock 	    LIBSES_PLUGIN_EXT);
330275c9da8Seschrock 	if (ses_plugin_loadone(tp, path, 1) != 0)
331275c9da8Seschrock 		return (-1);
332275c9da8Seschrock 
333275c9da8Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
334275c9da8Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
335275c9da8Seschrock 	    LIBSES_PLUGIN_EXT);
336275c9da8Seschrock 	if (ses_plugin_loadone(tp, path, 2) != 0)
337275c9da8Seschrock 		return (-1);
338275c9da8Seschrock 
339275c9da8Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
340275c9da8Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
341275c9da8Seschrock 	    revision, LIBSES_PLUGIN_EXT);
342275c9da8Seschrock 	if (ses_plugin_loadone(tp, path, 3) != 0)
343275c9da8Seschrock 		return (-1);
344275c9da8Seschrock 
345275c9da8Seschrock 	return (0);
346275c9da8Seschrock }
347275c9da8Seschrock 
348275c9da8Seschrock int
ses_plugin_load(ses_target_t * tp)349275c9da8Seschrock ses_plugin_load(ses_target_t *tp)
350275c9da8Seschrock {
351275c9da8Seschrock 	char pluginroot[PATH_MAX];
352275c9da8Seschrock 	const char *pluginpath, *p, *q;
353275c9da8Seschrock 
354275c9da8Seschrock 	if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL)
355275c9da8Seschrock 		pluginpath = LIBSES_DEFAULT_PLUGINDIR;
356275c9da8Seschrock 	ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL);
357275c9da8Seschrock 
358ac88567aSHyon Kim 	for (p = pluginpath; p != NULL; p = q) {
359ac88567aSHyon Kim 		if ((q = strchr(p, ':')) != NULL) {
360275c9da8Seschrock 			ptrdiff_t len = q - p;
361275c9da8Seschrock 			(void) strncpy(pluginroot, p, len);
362275c9da8Seschrock 			pluginroot[len] = '\0';
363275c9da8Seschrock 			while (*q == ':')
364275c9da8Seschrock 				++q;
365275c9da8Seschrock 			if (*q == '\0')
366275c9da8Seschrock 				q = NULL;
367275c9da8Seschrock 			if (len == 0)
368275c9da8Seschrock 				continue;
369275c9da8Seschrock 		} else {
370275c9da8Seschrock 			(void) strcpy(pluginroot, p);
371275c9da8Seschrock 		}
372275c9da8Seschrock 
373275c9da8Seschrock 		if (pluginroot[0] != '/')
374275c9da8Seschrock 			continue;
375275c9da8Seschrock 
376ac88567aSHyon Kim 		if (ses_plugin_load_dir(tp, pluginroot) != 0)
377275c9da8Seschrock 			return (-1);
378275c9da8Seschrock 	}
379275c9da8Seschrock 
380275c9da8Seschrock 	if (tp->st_plugin_first == NULL)
381275c9da8Seschrock 		return (ses_error(ESES_PLUGIN, "no plugins found"));
382275c9da8Seschrock 
383275c9da8Seschrock 	return (0);
384275c9da8Seschrock }
385275c9da8Seschrock 
386275c9da8Seschrock void
ses_plugin_unload(ses_target_t * tp)387275c9da8Seschrock ses_plugin_unload(ses_target_t *tp)
388275c9da8Seschrock {
389275c9da8Seschrock 	ses_plugin_t *sp;
390275c9da8Seschrock 
391275c9da8Seschrock 	while ((sp = tp->st_plugin_first) != NULL) {
392275c9da8Seschrock 		tp->st_plugin_first = sp->sp_next;
393275c9da8Seschrock 		ses_plugin_destroy(sp);
394275c9da8Seschrock 	}
395275c9da8Seschrock }
396