plugin.c revision 917c27c8913183a8fd19de808ddac321484cba3a
16185db8dougm/*
26185db8dougm * CDDL HEADER START
36185db8dougm *
46185db8dougm * The contents of this file are subject to the terms of the
56185db8dougm * Common Development and Distribution License (the "License").
66185db8dougm * You may not use this file except in compliance with the License.
76185db8dougm *
86185db8dougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96185db8dougm * or http://www.opensolaris.org/os/licensing.
106185db8dougm * See the License for the specific language governing permissions
116185db8dougm * and limitations under the License.
126185db8dougm *
136185db8dougm * When distributing Covered Code, include this CDDL HEADER in each
146185db8dougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156185db8dougm * If applicable, add the following below this CDDL HEADER, with the
166185db8dougm * fields enclosed by brackets "[]" replaced with your own identifying
176185db8dougm * information: Portions Copyright [yyyy] [name of copyright owner]
186185db8dougm *
196185db8dougm * CDDL HEADER END
206185db8dougm */
216185db8dougm
226185db8dougm/*
234bff34ethurlow * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
246185db8dougm * Use is subject to license terms.
256185db8dougm */
266185db8dougm
276185db8dougm#pragma ident	"%Z%%M%	%I%	%E% SMI"
286185db8dougm
296185db8dougm#include <stdio.h>
306185db8dougm#include <stdlib.h>
316185db8dougm#include <string.h>
326185db8dougm#include <libshare.h>
336185db8dougm#include "libshare_impl.h"
346185db8dougm#include <dlfcn.h>
356185db8dougm#include <link.h>
366185db8dougm#include <sys/types.h>
376185db8dougm#include <sys/param.h>
386185db8dougm#include <sys/stat.h>
396185db8dougm#include <dirent.h>
406185db8dougm#include <libintl.h>
41549ec3fdougm#include <sys/systeminfo.h>
42549ec3fdougm
43549ec3fdougm#define	MAXISALEN	257	/* based on sysinfo(2) man page */
446185db8dougm
456185db8dougm/*
466185db8dougm * protocol plugin interface
476185db8dougm *
486185db8dougm * finds plugins and makes them accessible. This is only "used" by
496185db8dougm * libshare.so.
506185db8dougm */
516185db8dougm
526185db8dougmstruct sa_proto_plugin *sap_proto_list;
536185db8dougm
546185db8dougmstatic struct sa_proto_handle sa_proto_handle;
556185db8dougm
566185db8dougmvoid proto_plugin_fini();
576185db8dougm
586185db8dougm/*
596185db8dougm * proto_plugin_init()
606185db8dougm *
616185db8dougm * Initialize the protocol specific plugin modules.
626185db8dougm *
636185db8dougm * Walk /usr/lib/fs/\* for libshare_*.so modules. That is,
646185db8dougm * /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory
656185db8dougm * would have a modules with name libshare_<proto>.so. If one is
666185db8dougm * found, initialize it and add to the internal list of
67da6c28aamw * protocols. These are used for protocol specific operations.
686185db8dougm */
696185db8dougm
706185db8dougmint
716185db8dougmproto_plugin_init()
726185db8dougm{
736185db8dougm	struct sa_proto_plugin *proto;
746185db8dougm	int num_protos = 0;
756185db8dougm	int err;
766185db8dougm	struct sa_plugin_ops *plugin_ops;
776185db8dougm	void *dlhandle;
786185db8dougm	DIR *dir;
796185db8dougm	struct dirent *dent;
806185db8dougm	int ret = SA_OK;
816185db8dougm	struct stat st;
826185db8dougm
836185db8dougm	/*
8425a6847dougm	 * Should walk "/usr/lib/fs/" for files of the form:
856185db8dougm	 * libshare_*.so
866185db8dougm	 */
876185db8dougm	dir = opendir(SA_LIB_DIR);
886185db8dougm	if (dir != NULL) {
8925a6847dougm		while (ret == SA_OK && (dent = readdir(dir)) != NULL) {
9025a6847dougm			char path[MAXPATHLEN];
9125a6847dougm			char isa[MAXISALEN];
92549ec3fdougm
93549ec3fdougm#if defined(_LP64)
9425a6847dougm			if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
9525a6847dougm				isa[0] = '\0';
96549ec3fdougm#else
9725a6847dougm			isa[0] = '\0';
98549ec3fdougm#endif
9925a6847dougm			(void) snprintf(path, MAXPATHLEN,
10025a6847dougm			    "%s/%s/%s/libshare_%s.so.1", SA_LIB_DIR,
10125a6847dougm			    dent->d_name, isa, dent->d_name);
10225a6847dougm			/*
10325a6847dougm			 * If file doesn't exist, don't try to map it
10425a6847dougm			 */
10525a6847dougm			if (stat(path, &st) < 0)
10625a6847dougm				continue;
10725a6847dougm
10825a6847dougm			dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY);
10925a6847dougm			if (dlhandle != NULL) {
11025a6847dougm				plugin_ops = (struct sa_plugin_ops *)
11125a6847dougm				    dlsym(dlhandle, "sa_plugin_ops");
11225a6847dougm				proto = (struct sa_proto_plugin *)
11325a6847dougm				    calloc(1, sizeof (struct sa_proto_plugin));
11425a6847dougm				if (proto != NULL) {
11525a6847dougm					proto->plugin_ops = plugin_ops;
11625a6847dougm					proto->plugin_handle = dlhandle;
11725a6847dougm					num_protos++;
11825a6847dougm					proto->plugin_next = sap_proto_list;
11925a6847dougm					sap_proto_list = proto;
12025a6847dougm				} else {
12125a6847dougm					ret = SA_NO_MEMORY;
12225a6847dougm				}
12325a6847dougm			} else {
12425a6847dougm				(void) fprintf(stderr,
12525a6847dougm				    dgettext(TEXT_DOMAIN,
12624424a3dougm				    "Error in plugin for protocol %s: %s\n"),
12725a6847dougm				    dent->d_name, dlerror());
12825a6847dougm			}
1296185db8dougm		}
13025a6847dougm		(void) closedir(dir);
1316185db8dougm	}
1326185db8dougm	if (ret == SA_OK) {
13325a6847dougm		sa_proto_handle.sa_proto =
13425a6847dougm		    (char **)calloc(num_protos, sizeof (char *));
13525a6847dougm		sa_proto_handle.sa_ops =
13625a6847dougm		    (struct sa_plugin_ops **)calloc(num_protos,
13725a6847dougm		    sizeof (struct sa_plugin_ops *));
13825a6847dougm		if (sa_proto_handle.sa_proto != NULL &&
13925a6847dougm		    sa_proto_handle.sa_ops != NULL) {
14025a6847dougm			int i;
14125a6847dougm			struct sa_proto_plugin *tmp;
14225a6847dougm
14325a6847dougm			for (i = 0, tmp = sap_proto_list;
144917c27cdougm			    i < num_protos && tmp != NULL;
14525a6847dougm			    tmp = tmp->plugin_next) {
14625a6847dougm				err = 0;
14725a6847dougm				if (tmp->plugin_ops->sa_init != NULL)
14825a6847dougm					err = tmp->plugin_ops->sa_init();
14925a6847dougm				if (err == SA_OK) {
15025a6847dougm					/*
15125a6847dougm					 * Only include if the init
15225a6847dougm					 * succeeded or was NULL
15325a6847dougm					 */
15425a6847dougm					sa_proto_handle.sa_num_proto++;
15525a6847dougm					sa_proto_handle.sa_ops[i] =
15625a6847dougm					    tmp->plugin_ops;
15725a6847dougm					sa_proto_handle.sa_proto[i] =
15825a6847dougm					    tmp->plugin_ops->sa_protocol;
15925a6847dougm					i++;
16025a6847dougm				}
16125a6847dougm			}
1626185db8dougm		}
1636185db8dougm	} else {
16425a6847dougm		/*
16525a6847dougm		 * There was an error, so cleanup prior to return of failure.
16625a6847dougm		 */
16725a6847dougm		proto_plugin_fini();
1686185db8dougm	}
1696185db8dougm	return (ret);
1706185db8dougm}
1716185db8dougm
1726185db8dougm/*
1736185db8dougm * proto_plugin_fini()
1746185db8dougm *
17525a6847dougm * Uninitialize all the plugin modules.
1766185db8dougm */
1776185db8dougm
1786185db8dougmvoid
1796185db8dougmproto_plugin_fini()
1806185db8dougm{
1816185db8dougm	/*
18225a6847dougm	 * Free up all the protocols, calling their fini, if there is
1836185db8dougm	 * one.
1846185db8dougm	 */
1856185db8dougm	while (sap_proto_list != NULL) {
18625a6847dougm		struct sa_proto_plugin *next;
18725a6847dougm
18825a6847dougm		next = sap_proto_list->plugin_next;
18925a6847dougm		sap_proto_list->plugin_ops->sa_fini();
19025a6847dougm		if (sap_proto_list->plugin_handle != NULL)
19125a6847dougm			(void) dlclose(sap_proto_list->plugin_handle);
19225a6847dougm		free(sap_proto_list);
19325a6847dougm		sap_proto_list = next;
1946185db8dougm	}
1956185db8dougm	if (sa_proto_handle.sa_ops != NULL) {
19625a6847dougm		free(sa_proto_handle.sa_ops);
19725a6847dougm		sa_proto_handle.sa_ops = NULL;
1986185db8dougm	}
1996185db8dougm	if (sa_proto_handle.sa_proto != NULL) {
20025a6847dougm		free(sa_proto_handle.sa_proto);
20125a6847dougm		sa_proto_handle.sa_proto = NULL;
2026185db8dougm	}
2036185db8dougm	sa_proto_handle.sa_num_proto = 0;
2046185db8dougm}
2056185db8dougm
2066185db8dougm/*
2076185db8dougm * find_protocol(proto)
2086185db8dougm *
2096185db8dougm * Search the plugin list for the specified protocol and return the
2106185db8dougm * ops vector.  NULL if protocol is not defined.
2116185db8dougm */
2126185db8dougm
2136185db8dougmstatic struct sa_plugin_ops *
2146185db8dougmfind_protocol(char *proto)
2156185db8dougm{
2166185db8dougm	int i;
2176185db8dougm
2186185db8dougm	if (proto != NULL) {
21925a6847dougm		for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
22025a6847dougm			if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0)
22125a6847dougm				return (sa_proto_handle.sa_ops[i]);
22225a6847dougm		}
2236185db8dougm	}
2246185db8dougm	return (NULL);
2256185db8dougm}
2266185db8dougm
2276185db8dougm/*
2286185db8dougm * sa_proto_share(proto, share)
2296185db8dougm *
2306185db8dougm * Activate a share for the specified protocol.
2316185db8dougm */
2326185db8dougm
2336185db8dougmint
2346185db8dougmsa_proto_share(char *proto, sa_share_t share)
2356185db8dougm{
2366185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
2376185db8dougm	int ret = SA_INVALID_PROTOCOL;
2386185db8dougm
2396185db8dougm	if (ops != NULL && ops->sa_share != NULL)
24025a6847dougm		ret = ops->sa_share(share);
2416185db8dougm	return (ret);
2426185db8dougm}
2436185db8dougm
2446185db8dougm/*
245da6c28aamw * sa_proto_unshare(proto, share)
2466185db8dougm *
247da6c28aamw * Deactivate (unshare) the share for this protocol.
2486185db8dougm */
2496185db8dougm
2506185db8dougmint
251ecd6cf8markssa_proto_unshare(sa_share_t share, char *proto, char *path)
2526185db8dougm{
2536185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
2546185db8dougm	int ret = SA_INVALID_PROTOCOL;
2556185db8dougm
2566185db8dougm	if (ops != NULL && ops->sa_unshare != NULL)
25725a6847dougm		ret = ops->sa_unshare(share, path);
2586185db8dougm	return (ret);
2596185db8dougm}
2606185db8dougm
2616185db8dougm/*
262da6c28aamw * sa_proto_share_resource(char *proto, sa_resource_t resource)
263da6c28aamw *
264da6c28aamw * For protocols that actually enable at the resource level, do the
265da6c28aamw * protocol specific resource enable. If it doesn't, return an error.
266da6c28aamw * Note that the resource functions are optional so can return
267da6c28aamw * SA_NOT_SUPPORTED.
268da6c28aamw */
269da6c28aamw
270da6c28aamwint
271da6c28aamwsa_proto_share_resource(char *proto, sa_resource_t resource)
272da6c28aamw{
273da6c28aamw	struct sa_plugin_ops *ops = find_protocol(proto);
274da6c28aamw	int ret = SA_INVALID_PROTOCOL;
275da6c28aamw
276da6c28aamw	if (ops != NULL) {
277da6c28aamw		if (ops->sa_enable_resource != NULL)
278da6c28aamw			ret = ops->sa_enable_resource(resource);
279da6c28aamw		else
280da6c28aamw			ret = SA_NOT_SUPPORTED;
281da6c28aamw	}
282da6c28aamw	return (ret);
283da6c28aamw}
284da6c28aamw
285da6c28aamw/*
286da6c28aamw * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
287da6c28aamw *
288da6c28aamw * For protocols that actually disable at the resource level, do the
289da6c28aamw * protocol specific resource disable. If it doesn't, return an error.
290da6c28aamw */
291da6c28aamw
292da6c28aamwint
293da6c28aamwsa_proto_unshare_resource(char *proto, sa_resource_t resource)
294da6c28aamw{
295da6c28aamw	struct sa_plugin_ops *ops = find_protocol(proto);
296da6c28aamw	int ret = SA_INVALID_PROTOCOL;
297da6c28aamw
298da6c28aamw	if (ops != NULL) {
299da6c28aamw		if (ops->sa_disable_resource != NULL)
300da6c28aamw			ret = ops->sa_disable_resource(resource);
301da6c28aamw		else
302da6c28aamw			ret = SA_NOT_SUPPORTED;
303da6c28aamw	}
304da6c28aamw	return (ret);
305da6c28aamw}
306da6c28aamw
307da6c28aamw/*
3086185db8dougm * sa_proto_valid_prop(proto, prop, opt)
3096185db8dougm *
31025a6847dougm * Check to see if the specified prop is valid for this protocol.
3116185db8dougm */
3126185db8dougm
3136185db8dougmint
3146185db8dougmsa_proto_valid_prop(char *proto, sa_property_t prop, sa_optionset_t opt)
3156185db8dougm{
3166185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3176185db8dougm	int ret = 0;
3186185db8dougm
3196185db8dougm	if (ops != NULL && ops->sa_valid_prop != NULL)
32025a6847dougm		ret = ops->sa_valid_prop(prop, opt);
3216185db8dougm	return (ret);
3226185db8dougm}
3236185db8dougm
3246185db8dougm/*
3256185db8dougm * sa_proto_valid_space(proto, space)
3266185db8dougm *
32725a6847dougm * Check if space is valid optionspace for proto.
3286185db8dougm * Protocols that don't implement this don't support spaces.
3296185db8dougm */
3306185db8dougmint
3316185db8dougmsa_proto_valid_space(char *proto, char *token)
3326185db8dougm{
3336185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3346185db8dougm	int ret = 0;
3356185db8dougm
3366185db8dougm	if (ops != NULL && ops->sa_valid_space != NULL)
33725a6847dougm		ret = ops->sa_valid_space(token);
3386185db8dougm	return (ret);
3396185db8dougm}
3406185db8dougm
3416185db8dougm/*
3426185db8dougm * sa_proto_space_alias(proto, space)
3436185db8dougm *
34425a6847dougm * If the name for space is an alias, return its proper name.  This is
3456185db8dougm * used to translate "default" values into proper form.
3466185db8dougm */
3476185db8dougmchar *
3486185db8dougmsa_proto_space_alias(char *proto, char *space)
3496185db8dougm{
3506185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3516185db8dougm	char *ret = space;
3526185db8dougm
3536185db8dougm	if (ops != NULL && ops->sa_space_alias != NULL)
35425a6847dougm		ret = ops->sa_space_alias(space);
3556185db8dougm	return (ret);
3566185db8dougm}
3576185db8dougm
3586185db8dougm/*
3596185db8dougm * sa_proto_security_prop(proto, token)
3606185db8dougm *
3616185db8dougm * Check to see if the property name in token is a valid named
3626185db8dougm * optionset property.
3636185db8dougm */
3646185db8dougm
3656185db8dougmint
3666185db8dougmsa_proto_security_prop(char *proto, char *token)
3676185db8dougm{
3686185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3696185db8dougm	int ret = 0;
3706185db8dougm
3716185db8dougm	if (ops != NULL && ops->sa_security_prop != NULL)
37225a6847dougm		ret = ops->sa_security_prop(token);
3736185db8dougm	return (ret);
3746185db8dougm}
3756185db8dougm
3766185db8dougm/*
3776185db8dougm * sa_proto_legacy_opts(proto, grouup, options)
3786185db8dougm *
3796185db8dougm * Have the protocol specific parser parse the options string and add
3806185db8dougm * an appropriate optionset to group.
3816185db8dougm */
3826185db8dougm
3836185db8dougmint
3846185db8dougmsa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
3856185db8dougm{
3866185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3876185db8dougm	int ret = SA_INVALID_PROTOCOL;
3886185db8dougm
3896185db8dougm	if (ops != NULL && ops->sa_legacy_opts != NULL)
39025a6847dougm		ret = ops->sa_legacy_opts(group, options);
3916185db8dougm	return (ret);
3926185db8dougm}
3936185db8dougm
3946185db8dougm/*
3956185db8dougm * sa_proto_legacy_format(proto, group, hier)
3966185db8dougm *
3976185db8dougm * Return a legacy format string representing either the group's
3986185db8dougm * properties or the groups hierarchical properties.
3996185db8dougm */
4006185db8dougm
4016185db8dougmchar *
4026185db8dougmsa_proto_legacy_format(char *proto, sa_group_t group, int hier)
4036185db8dougm{
4046185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4056185db8dougm	char *ret = NULL;
4066185db8dougm
4076185db8dougm	if (ops != NULL && ops->sa_legacy_format != NULL)
40825a6847dougm		ret = ops->sa_legacy_format(group, hier);
4096185db8dougm	return (ret);
4106185db8dougm}
4116185db8dougm
4126185db8dougmvoid
4136185db8dougmsa_format_free(char *str)
4146185db8dougm{
4156185db8dougm	free(str);
4166185db8dougm}
4176185db8dougm
4186185db8dougm/*
4196185db8dougm * sharectl related API functions
4206185db8dougm */
4216185db8dougm
4226185db8dougm/*
4236185db8dougm * sa_proto_get_properties(proto)
4246185db8dougm *
4256185db8dougm * Return the set of properties that are specific to the
4266185db8dougm * protocol. These are usually in /etc/dfs/<proto> and related files,
4276185db8dougm * but only the protocol module knows which ones for sure.
4286185db8dougm */
4296185db8dougm
4306185db8dougmsa_protocol_properties_t
4316185db8dougmsa_proto_get_properties(char *proto)
4326185db8dougm{
4336185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4346185db8dougm	sa_protocol_properties_t props = NULL;
4356185db8dougm
4366185db8dougm	if (ops != NULL && ops->sa_get_proto_set != NULL)
43725a6847dougm		props = ops->sa_get_proto_set();
4386185db8dougm	return (props);
4396185db8dougm}
4406185db8dougm
4416185db8dougm/*
4426185db8dougm * sa_proto_set_property(proto, prop)
4436185db8dougm *
444da6c28aamw * Update the protocol specific property.
4456185db8dougm */
4466185db8dougm
4476185db8dougmint
4486185db8dougmsa_proto_set_property(char *proto, sa_property_t prop)
4496185db8dougm{
4506185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4516185db8dougm	int ret = SA_OK;
45225a6847dougm
4536185db8dougm	if (ops != NULL && ops->sa_set_proto_prop != NULL)
45425a6847dougm		ret = ops->sa_set_proto_prop(prop);
4556185db8dougm	return (ret);
4566185db8dougm}
4576185db8dougm
4586185db8dougm/*
4596185db8dougm * sa_valid_protocol(proto)
4606185db8dougm *
46125a6847dougm * Check to see if the protocol specified is defined by a
4626185db8dougm * plugin. Returns true (1) or false (0)
4636185db8dougm */
4646185db8dougm
4656185db8dougmint
4666185db8dougmsa_valid_protocol(char *proto)
4676185db8dougm{
4686185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4696185db8dougm	return (ops != NULL);
4706185db8dougm}
4716185db8dougm
4726185db8dougm/*
4736185db8dougm * Return the current operational status of the protocol
4746185db8dougm */
4756185db8dougm
4766185db8dougmchar *
4776185db8dougmsa_get_protocol_status(char *proto)
4786185db8dougm{
4796185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4806185db8dougm	char *ret = NULL;
4816185db8dougm	if (ops != NULL && ops->sa_get_proto_status != NULL)
48225a6847dougm		ret = ops->sa_get_proto_status(proto);
4836185db8dougm	return (ret);
4846185db8dougm}
4856185db8dougm
4866185db8dougm/*
4876185db8dougm * sa_proto_update_legacy(proto, share)
4886185db8dougm *
4896185db8dougm * Update the protocol specific legacy files if necessary for the
4906185db8dougm * specified share.
4916185db8dougm */
4926185db8dougm
4936185db8dougmint
4946185db8dougmsa_proto_update_legacy(char *proto, sa_share_t share)
4956185db8dougm{
4966185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
4976185db8dougm	int ret = SA_NOT_IMPLEMENTED;
4986185db8dougm
4996185db8dougm	if (ops != NULL) {
50025a6847dougm		if (ops->sa_update_legacy != NULL)
50125a6847dougm			ret = ops->sa_update_legacy(share);
5026185db8dougm	}
5036185db8dougm	return (ret);
5046185db8dougm}
5056185db8dougm
5066185db8dougm/*
5076185db8dougm * sa_delete_legacy(proto, share)
5086185db8dougm *
50925a6847dougm * Remove the specified share from the protocol specific legacy files.
5106185db8dougm */
5116185db8dougm
5126185db8dougmint
5136185db8dougmsa_proto_delete_legacy(char *proto, sa_share_t share)
5146185db8dougm{
5156185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
516da6c28aamw	int ret = SA_NOT_IMPLEMENTED;
5176185db8dougm
5186185db8dougm	if (ops != NULL) {
51925a6847dougm		if (ops->sa_delete_legacy != NULL)
52025a6847dougm			ret = ops->sa_delete_legacy(share);
5214bff34ethurlow	} else {
5224bff34ethurlow		if (proto != NULL)
5234bff34ethurlow			ret = SA_NOT_IMPLEMENTED;
5244bff34ethurlow		else
5254bff34ethurlow			ret = SA_INVALID_PROTOCOL;
5264bff34ethurlow	}
5274bff34ethurlow	return (ret);
5284bff34ethurlow}
5294bff34ethurlow
5304bff34ethurlow/*
5314bff34ethurlow * sa_proto_delete_section(proto, section)
5324bff34ethurlow *
5334bff34ethurlow * Remove the specified section from the protocol specific legacy files,
5344bff34ethurlow * if supported.
5354bff34ethurlow */
5364bff34ethurlow
5374bff34ethurlowint
5384bff34ethurlowsa_proto_delete_section(char *proto, char *section)
5394bff34ethurlow{
5404bff34ethurlow	struct sa_plugin_ops *ops = find_protocol(proto);
5414bff34ethurlow	int ret = SA_OK;
5424bff34ethurlow
5434bff34ethurlow	if (ops != NULL) {
5444bff34e</