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#include <stdio.h>
286185db8dougm#include <stdlib.h>
296185db8dougm#include <string.h>
306185db8dougm#include <libshare.h>
316185db8dougm#include "libshare_impl.h"
326185db8dougm#include <dlfcn.h>
336185db8dougm#include <link.h>
346185db8dougm#include <sys/types.h>
356185db8dougm#include <sys/param.h>
366185db8dougm#include <sys/stat.h>
376185db8dougm#include <dirent.h>
386185db8dougm#include <libintl.h>
39549ec3fdougm#include <sys/systeminfo.h>
40c5f5847dougm#include <thread.h>
41c5f5847dougm#include <synch.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/*
598d7e416jose borrego * Returns true if name is "." or "..", otherwise returns false.
608d7e416jose borrego */
618d7e416jose borregostatic boolean_t
628d7e416jose borregoproto_is_dot_or_dotdot(const char *name)
638d7e416jose borrego{
648d7e416jose borrego	if (*name != '.')
658d7e416jose borrego		return (B_FALSE);
668d7e416jose borrego
678d7e416jose borrego	if ((name[1] == '\0') || (name[1] == '.' && name[2] == '\0'))
688d7e416jose borrego		return (B_TRUE);
698d7e416jose borrego
708d7e416jose borrego	return (B_FALSE);
718d7e416jose borrego}
728d7e416jose borrego
738d7e416jose borrego/*
746185db8dougm * proto_plugin_init()
756185db8dougm *
766185db8dougm * Initialize the protocol specific plugin modules.
776185db8dougm *
788d7e416jose borrego * Walk /usr/lib/fs/\* for libshare_*.so modules, for example,
798d7e416jose borrego * /usr/lib/fs/nfs/libshare_nfs.so. A protocol specific directory
808d7e416jose borrego * would have modules with names of the form libshare_<proto>.so.
818d7e416jose borrego * For each protocol found, initialize it and add it to the internal
828d7e416jose borrego * list of protocols. These are used for protocol specific operations.
836185db8dougm */
846185db8dougm
856185db8dougmint
866185db8dougmproto_plugin_init()
876185db8dougm{
886185db8dougm	struct sa_proto_plugin *proto;
896185db8dougm	int num_protos = 0;
906185db8dougm	struct sa_plugin_ops *plugin_ops;
916185db8dougm	void *dlhandle;
926185db8dougm	DIR *dir;
936185db8dougm	struct dirent *dent;
946185db8dougm	int ret = SA_OK;
956185db8dougm	struct stat st;
968d7e416jose borrego	char isa[MAXISALEN];
97549ec3fdougm
98549ec3fdougm#if defined(_LP64)
998d7e416jose borrego	if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
1008d7e416jose borrego		isa[0] = '\0';
101549ec3fdougm#else
1028d7e416jose borrego	isa[0] = '\0';
103549ec3fdougm#endif
1048d7e416jose borrego
1058d7e416jose borrego	if ((dir = opendir(SA_LIB_DIR)) == NULL)
1068d7e416jose borrego		return (SA_OK);
1078d7e416jose borrego
1088d7e416jose borrego	while ((dent = readdir(dir)) != NULL) {
1098d7e416jose borrego		char path[MAXPATHLEN];
1108d7e416jose borrego
1118d7e416jose borrego		if (proto_is_dot_or_dotdot(dent->d_name))
1128d7e416jose borrego			continue;
1138d7e416jose borrego
1148d7e416jose borrego		(void) snprintf(path, MAXPATHLEN,
1158d7e416jose borrego		    "%s/%s/%s/libshare_%s.so.1", SA_LIB_DIR,
1168d7e416jose borrego		    dent->d_name, isa, dent->d_name);
1178d7e416jose borrego
1188d7e416jose borrego		/*
1198d7e416jose borrego		 * If file doesn't exist, don't try to map it
1208d7e416jose borrego		 */
1218d7e416jose borrego		if (stat(path, &st) < 0)
1228d7e416jose borrego			continue;
1238d7e416jose borrego
1248d7e416jose borrego		if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL) {
1258d7e416jose borrego			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1268d7e416jose borrego			    "Error in plugin for protocol %s: %s\n"),
1278d7e416jose borrego			    dent->d_name, dlerror());
1288d7e416jose borrego			continue;
1298d7e416jose borrego		}
1308d7e416jose borrego
1318d7e416jose borrego		plugin_ops = (struct sa_plugin_ops *)
1328d7e416jose borrego		    dlsym(dlhandle, "sa_plugin_ops");
1338d7e416jose borrego		if (plugin_ops == NULL) {
1348d7e416jose borrego			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1358d7e416jose borrego			    "Error in plugin ops for protocol %s: %s\n"),
1368d7e416jose borrego			    dent->d_name, dlerror());
1378d7e416jose borrego			(void) dlclose(dlhandle);
1388d7e416jose borrego			continue;
1396185db8dougm		}
1408d7e416jose borrego
1418d7e416jose borrego		proto = (struct sa_proto_plugin *)
1428d7e416jose borrego		    calloc(1, sizeof (struct sa_proto_plugin));
1438d7e416jose borrego		if (proto == NULL) {
1448d7e416jose borrego			(void) dlclose(dlhandle);
1458d7e416jose borrego			ret = SA_NO_MEMORY;
1468d7e416jose borrego			continue;
1478d7e416jose borrego		}
1488d7e416jose borrego
1498d7e416jose borrego		proto->plugin_ops = plugin_ops;
1508d7e416jose borrego		proto->plugin_handle = dlhandle;
1518d7e416jose borrego		num_protos++;
1528d7e416jose borrego		proto->plugin_next = sap_proto_list;
1538d7e416jose borrego		sap_proto_list = proto;
1546185db8dougm	}
1558d7e416jose borrego
1568d7e416jose borrego	(void) closedir(dir);
1578d7e416jose borrego
1588d7e416jose borrego	if (num_protos != 0) {
15925a6847dougm		sa_proto_handle.sa_proto =
16025a6847dougm		    (char **)calloc(num_protos, sizeof (char *));
16125a6847dougm		sa_proto_handle.sa_ops =
16225a6847dougm		    (struct sa_plugin_ops **)calloc(num_protos,
16325a6847dougm		    sizeof (struct sa_plugin_ops *));
16425a6847dougm		if (sa_proto_handle.sa_proto != NULL &&
16525a6847dougm		    sa_proto_handle.sa_ops != NULL) {
16625a6847dougm			int i;
16725a6847dougm			struct sa_proto_plugin *tmp;
16825a6847dougm
16925a6847dougm			for (i = 0, tmp = sap_proto_list;
170917c27cdougm			    i < num_protos && tmp != NULL;
17125a6847dougm			    tmp = tmp->plugin_next) {
172c5f5847dougm				int err;
173c5f5847dougm				err = SA_OK;
17425a6847dougm				if (tmp->plugin_ops->sa_init != NULL)
17525a6847dougm					err = tmp->plugin_ops->sa_init();
17625a6847dougm				if (err == SA_OK) {
17725a6847dougm					/*
17825a6847dougm					 * Only include if the init
17925a6847dougm					 * succeeded or was NULL
18025a6847dougm					 */
18125a6847dougm					sa_proto_handle.sa_num_proto++;
18225a6847dougm					sa_proto_handle.sa_ops[i] =
18325a6847dougm					    tmp->plugin_ops;
18425a6847dougm					sa_proto_handle.sa_proto[i] =
18525a6847dougm					    tmp->plugin_ops->sa_protocol;
18625a6847dougm					i++;
18725a6847dougm				}
18825a6847dougm			}
189c5f5847dougm		} else {
190c5f5847dougm			ret = SA_NO_MEMORY;
1916185db8dougm		}
1926185db8dougm	}
193c5f5847dougm
194c5f5847dougm	/*
195c5f5847dougm	 * There was an error, so cleanup prior to return of failure.
196c5f5847dougm	 */
197c5f5847dougm	if (ret != SA_OK)
198c5f5847dougm		proto_plugin_fini();
199c5f5847dougm
2006185db8dougm	return (ret);
2016185db8dougm}
2026185db8dougm
2036185db8dougm/*
2046185db8dougm * proto_plugin_fini()
2056185db8dougm *
20625a6847dougm * Uninitialize all the plugin modules.
2076185db8dougm */
2086185db8dougm
2096185db8dougmvoid
2106185db8dougmproto_plugin_fini()
2116185db8dougm{
2120fd7766gwr	struct sa_proto_plugin *p;
2130fd7766gwr
2146185db8dougm	/*
2150fd7766gwr	 * Protocols may call this framework during _fini
2160fd7766gwr	 * (the smbfs plugin is known to do this) so do
2170fd7766gwr	 * two passes: 1st call _fini; 2nd free, dlclose.
2186185db8dougm	 */
2190fd7766gwr	for (p = sap_proto_list; p != NULL; p = p->plugin_next)
2200fd7766gwr		p->plugin_ops->sa_fini();
2210fd7766gwr
2220fd7766gwr	while ((p = sap_proto_list) != NULL) {
2230fd7766gwr		sap_proto_list = p->plugin_next;
2240fd7766gwr
2250fd7766gwr		if (p->plugin_handle != NULL)
2260fd7766gwr			(void) dlclose(p->plugin_handle);
2270fd7766gwr		free(p);
2286185db8dougm	}
2296185db8dougm	if (sa_proto_handle.sa_ops != NULL) {
23025a6847dougm		free(sa_proto_handle.sa_ops);
23125a6847dougm		sa_proto_handle.sa_ops = NULL;
2326185db8dougm	}
2336185db8dougm	if (sa_proto_handle.sa_proto != NULL) {
23425a6847dougm		free(sa_proto_handle.sa_proto);
23525a6847dougm		sa_proto_handle.sa_proto = NULL;
2366185db8dougm	}
2376185db8dougm	sa_proto_handle.sa_num_proto = 0;
2386185db8dougm}
2396185db8dougm
2406185db8dougm/*
2416185db8dougm * find_protocol(proto)
2426185db8dougm *
2436185db8dougm * Search the plugin list for the specified protocol and return the
2446185db8dougm * ops vector.  NULL if protocol is not defined.
2456185db8dougm */
2466185db8dougm
2476185db8dougmstatic struct sa_plugin_ops *
2486185db8dougmfind_protocol(char *proto)
2496185db8dougm{
2506185db8dougm	int i;
251c5f5847dougm	struct sa_plugin_ops *ops = NULL;
252c5f5847dougm	extern mutex_t sa_global_lock;
2536185db8dougm
254c5f5847dougm	(void) mutex_lock(&sa_global_lock);
2556185db8dougm	if (proto != NULL) {
25625a6847dougm		for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
257c5f5847dougm			if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) {
258c5f5847dougm				ops = sa_proto_handle.sa_ops[i];
259c5f5847dougm				break;
260c5f5847dougm			}
26125a6847dougm		}
2626185db8dougm	}
263c5f5847dougm	(void) mutex_unlock(&sa_global_lock);
264c5f5847dougm	return (ops);
2656185db8dougm}
2666185db8dougm
2676185db8dougm/*
2686185db8dougm * sa_proto_share(proto, share)
2696185db8dougm *
2706185db8dougm * Activate a share for the specified protocol.
2716185db8dougm */
2726185db8dougm
2736185db8dougmint
2746185db8dougmsa_proto_share(char *proto, sa_share_t share)
2756185db8dougm{
2766185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
2776185db8dougm	int ret = SA_INVALID_PROTOCOL;
2786185db8dougm
2796185db8dougm	if (ops != NULL && ops->sa_share != NULL)
28025a6847dougm		ret = ops->sa_share(share);
2816185db8dougm	return (ret);
2826185db8dougm}
2836185db8dougm
2846185db8dougm/*
285da6c28aamw * sa_proto_unshare(proto, share)
2866185db8dougm *
287da6c28aamw * Deactivate (unshare) the share for this protocol.
2886185db8dougm */
2896185db8dougm
2906185db8dougmint
291ecd6cf8markssa_proto_unshare(sa_share_t share, char *proto, char *path)
2926185db8dougm{
2936185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
2946185db8dougm	int ret = SA_INVALID_PROTOCOL;
2956185db8dougm
2966185db8dougm	if (ops != NULL && ops->sa_unshare != NULL)
29725a6847dougm		ret = ops->sa_unshare(share, path);
2986185db8dougm	return (ret);
2996185db8dougm}
3006185db8dougm
3016185db8dougm/*
302da6c28aamw * sa_proto_share_resource(char *proto, sa_resource_t resource)
303da6c28aamw *
304da6c28aamw * For protocols that actually enable at the resource level, do the
305da6c28aamw * protocol specific resource enable. If it doesn't, return an error.
306da6c28aamw * Note that the resource functions are optional so can return
307da6c28aamw * SA_NOT_SUPPORTED.
308da6c28aamw */
309da6c28aamw
310da6c28aamwint
311da6c28aamwsa_proto_share_resource(char *proto, sa_resource_t resource)
312da6c28aamw{
313da6c28aamw	struct sa_plugin_ops *ops = find_protocol(proto);
314da6c28aamw	int ret = SA_INVALID_PROTOCOL;
315da6c28aamw
316da6c28aamw	if (ops != NULL) {
317da6c28aamw		if (ops->sa_enable_resource != NULL)
318da6c28aamw			ret = ops->sa_enable_resource(resource);
319da6c28aamw		else
320da6c28aamw			ret = SA_NOT_SUPPORTED;
321da6c28aamw	}
322da6c28aamw	return (ret);
323da6c28aamw}
324da6c28aamw
325da6c28aamw/*
326da6c28aamw * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
327da6c28aamw *
328da6c28aamw * For protocols that actually disable at the resource level, do the
329da6c28aamw * protocol specific resource disable. If it doesn't, return an error.
330da6c28aamw */
331da6c28aamw
332da6c28aamwint
333da6c28aamwsa_proto_unshare_resource(char *proto, sa_resource_t resource)
334da6c28aamw{
335da6c28aamw	struct sa_plugin_ops *ops = find_protocol(proto);
336da6c28aamw	int ret = SA_INVALID_PROTOCOL;
337da6c28aamw
338da6c28aamw	if (ops != NULL) {
339da6c28aamw		if (ops->sa_disable_resource != NULL)
340da6c28aamw			ret = ops->sa_disable_resource(resource);
341da6c28aamw		else
342da6c28aamw			ret = SA_NOT_SUPPORTED;
343da6c28aamw	}
344da6c28aamw	return (ret);
345da6c28aamw}
346da6c28aamw
347da6c28aamw/*
348687915edougm * sa_proto_valid_prop(handle, proto, prop, opt)
3496185db8dougm *
35025a6847dougm * Check to see if the specified prop is valid for this protocol.
3516185db8dougm */
3526185db8dougm
3536185db8dougmint
354687915edougmsa_proto_valid_prop(sa_handle_t handle, char *proto, sa_property_t prop,
355687915edougm    sa_optionset_t opt)
3566185db8dougm{
3576185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3586185db8dougm	int ret = 0;
3596185db8dougm
3606185db8dougm	if (ops != NULL && ops->sa_valid_prop != NULL)
361687915edougm		ret = ops->sa_valid_prop(handle, prop, opt);
3626185db8dougm	return (ret);
3636185db8dougm}
3646185db8dougm
3656185db8dougm/*
3666185db8dougm * sa_proto_valid_space(proto, space)
3676185db8dougm *
36825a6847dougm * Check if space is valid optionspace for proto.
3696185db8dougm * Protocols that don't implement this don't support spaces.
3706185db8dougm */
3716185db8dougmint
3726185db8dougmsa_proto_valid_space(char *proto, char *token)
3736185db8dougm{
3746185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3756185db8dougm	int ret = 0;
3766185db8dougm
3776185db8dougm	if (ops != NULL && ops->sa_valid_space != NULL)
37825a6847dougm		ret = ops->sa_valid_space(token);
3796185db8dougm	return (ret);
3806185db8dougm}
3816185db8dougm
3826185db8dougm/*
3836185db8dougm * sa_proto_space_alias(proto, space)
3846185db8dougm *
38525a6847dougm * If the name for space is an alias, return its proper name.  This is
3866185db8dougm * used to translate "default" values into proper form.
3876185db8dougm */
3886185db8dougmchar *
3896185db8dougmsa_proto_space_alias(char *proto, char *space)
3906185db8dougm{
3916185db8dougm	struct sa_plugin_ops *ops = find_protocol(proto);
3926185db8dougm	char *ret = space;
3936185db8dougm
3946185db8dougm	if (ops != NULL && ops->sa_space_alias != NULL)
39525a6847dougm		ret = ops->sa_space_alias(space);
3966185db8dougm	return (ret);
3976185db8dougm