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 <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <libshare.h>
33 #include "libshare_impl.h"
34 #include <dlfcn.h>
35 #include <link.h>
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 #include <dirent.h>
40 #include <libintl.h>
41 #include <sys/systeminfo.h>
42 #include <thread.h>
43 #include <synch.h>
44 
45 #define	MAXISALEN	257	/* based on sysinfo(2) man page */
46 
47 /*
48  * protocol plugin interface
49  *
50  * finds plugins and makes them accessible. This is only "used" by
51  * libshare.so.
52  */
53 
54 struct sa_proto_plugin *sap_proto_list;
55 
56 static struct sa_proto_handle sa_proto_handle;
57 
58 void proto_plugin_fini();
59 
60 /*
61  * proto_plugin_init()
62  *
63  * Initialize the protocol specific plugin modules.
64  *
65  * Walk /usr/lib/fs/\* for libshare_*.so modules. That is,
66  * /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory
67  * would have a modules with name libshare_<proto>.so. If one is
68  * found, initialize it and add to the internal list of
69  * protocols. These are used for protocol specific operations.
70  */
71 
72 int
73 proto_plugin_init()
74 {
75 	struct sa_proto_plugin *proto;
76 	int num_protos = 0;
77 	struct sa_plugin_ops *plugin_ops;
78 	void *dlhandle;
79 	DIR *dir;
80 	struct dirent *dent;
81 	int ret = SA_OK;
82 	struct stat st;
83 
84 	/*
85 	 * Should walk "/usr/lib/fs/" for files of the form:
86 	 * libshare_*.so
87 	 */
88 	dir = opendir(SA_LIB_DIR);
89 	if (dir != NULL) {
90 		while (ret == SA_OK && (dent = readdir(dir)) != NULL) {
91 			char path[MAXPATHLEN];
92 			char isa[MAXISALEN];
93 
94 #if defined(_LP64)
95 			if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
96 				isa[0] = '\0';
97 #else
98 			isa[0] = '\0';
99 #endif
100 			(void) snprintf(path, MAXPATHLEN,
101 			    "%s/%s/%s/libshare_%s.so.1", SA_LIB_DIR,
102 			    dent->d_name, isa, dent->d_name);
103 			/*
104 			 * If file doesn't exist, don't try to map it
105 			 */
106 			if (stat(path, &st) < 0)
107 				continue;
108 
109 			dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY);
110 			if (dlhandle != NULL) {
111 				plugin_ops = (struct sa_plugin_ops *)
112 				    dlsym(dlhandle, "sa_plugin_ops");
113 				proto = (struct sa_proto_plugin *)
114 				    calloc(1, sizeof (struct sa_proto_plugin));
115 				if (proto != NULL) {
116 					proto->plugin_ops = plugin_ops;
117 					proto->plugin_handle = dlhandle;
118 					num_protos++;
119 					proto->plugin_next = sap_proto_list;
120 					sap_proto_list = proto;
121 				} else {
122 					ret = SA_NO_MEMORY;
123 					/* Don't leak a dlhandle */
124 					(void) dlclose(dlhandle);
125 					break;
126 				}
127 			} else {
128 				(void) fprintf(stderr,
129 				    dgettext(TEXT_DOMAIN,
130 				    "Error in plugin for protocol %s: %s\n"),
131 				    dent->d_name, dlerror());
132 			}
133 		}
134 		(void) closedir(dir);
135 	}
136 	if (ret == SA_OK) {
137 		sa_proto_handle.sa_proto =
138 		    (char **)calloc(num_protos, sizeof (char *));
139 		sa_proto_handle.sa_ops =
140 		    (struct sa_plugin_ops **)calloc(num_protos,
141 		    sizeof (struct sa_plugin_ops *));
142 		if (sa_proto_handle.sa_proto != NULL &&
143 		    sa_proto_handle.sa_ops != NULL) {
144 			int i;
145 			struct sa_proto_plugin *tmp;
146 
147 			for (i = 0, tmp = sap_proto_list;
148 			    i < num_protos && tmp != NULL;
149 			    tmp = tmp->plugin_next) {
150 				int err;
151 				err = SA_OK;
152 				if (tmp->plugin_ops->sa_init != NULL)
153 					err = tmp->plugin_ops->sa_init();
154 				if (err == SA_OK) {
155 					/*
156 					 * Only include if the init
157 					 * succeeded or was NULL
158 					 */
159 					sa_proto_handle.sa_num_proto++;
160 					sa_proto_handle.sa_ops[i] =
161 					    tmp->plugin_ops;
162 					sa_proto_handle.sa_proto[i] =
163 					    tmp->plugin_ops->sa_protocol;
164 					i++;
165 				}
166 			}
167 		} else {
168 			ret = SA_NO_MEMORY;
169 		}
170 	}
171 
172 	/*
173 	 * There was an error, so cleanup prior to return of failure.
174 	 */
175 	if (ret != SA_OK)
176 		proto_plugin_fini();
177 
178 	return (ret);
179 }
180 
181 /*
182  * proto_plugin_fini()
183  *
184  * Uninitialize all the plugin modules.
185  */
186 
187 void
188 proto_plugin_fini()
189 {
190 	struct sa_proto_plugin *p;
191 
192 	/*
193 	 * Protocols may call this framework during _fini
194 	 * (the smbfs plugin is known to do this) so do
195 	 * two passes: 1st call _fini; 2nd free, dlclose.
196 	 */
197 	for (p = sap_proto_list; p != NULL; p = p->plugin_next)
198 		p->plugin_ops->sa_fini();
199 
200 	while ((p = sap_proto_list) != NULL) {
201 		sap_proto_list = p->plugin_next;
202 
203 		if (p->plugin_handle != NULL)
204 			(void) dlclose(p->plugin_handle);
205 		free(p);
206 	}
207 	if (sa_proto_handle.sa_ops != NULL) {
208 		free(sa_proto_handle.sa_ops);
209 		sa_proto_handle.sa_ops = NULL;
210 	}
211 	if (sa_proto_handle.sa_proto != NULL) {
212 		free(sa_proto_handle.sa_proto);
213 		sa_proto_handle.sa_proto = NULL;
214 	}
215 	sa_proto_handle.sa_num_proto = 0;
216 }
217 
218 /*
219  * find_protocol(proto)
220  *
221  * Search the plugin list for the specified protocol and return the
222  * ops vector.  NULL if protocol is not defined.
223  */
224 
225 static struct sa_plugin_ops *
226 find_protocol(char *proto)
227 {
228 	int i;
229 	struct sa_plugin_ops *ops = NULL;
230 	extern mutex_t sa_global_lock;
231 
232 	(void) mutex_lock(&sa_global_lock);
233 	if (proto != NULL) {
234 		for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
235 			if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) {
236 				ops = sa_proto_handle.sa_ops[i];
237 				break;
238 			}
239 		}
240 	}
241 	(void) mutex_unlock(&sa_global_lock);
242 	return (ops);
243 }
244 
245 /*
246  * sa_proto_share(proto, share)
247  *
248  * Activate a share for the specified protocol.
249  */
250 
251 int
252 sa_proto_share(char *proto, sa_share_t share)
253 {
254 	struct sa_plugin_ops *ops = find_protocol(proto);
255 	int ret = SA_INVALID_PROTOCOL;
256 
257 	if (ops != NULL && ops->sa_share != NULL)
258 		ret = ops->sa_share(share);
259 	return (ret);
260 }
261 
262 /*
263  * sa_proto_unshare(proto, share)
264  *
265  * Deactivate (unshare) the share for this protocol.
266  */
267 
268 int
269 sa_proto_unshare(sa_share_t share, char *proto, char *path)
270 {
271 	struct sa_plugin_ops *ops = find_protocol(proto);
272 	int ret = SA_INVALID_PROTOCOL;
273 
274 	if (ops != NULL && ops->sa_unshare != NULL)
275 		ret = ops->sa_unshare(share, path);
276 	return (ret);
277 }
278 
279 /*
280  * sa_proto_share_resource(char *proto, sa_resource_t resource)
281  *
282  * For protocols that actually enable at the resource level, do the
283  * protocol specific resource enable. If it doesn't, return an error.
284  * Note that the resource functions are optional so can return
285  * SA_NOT_SUPPORTED.
286  */
287 
288 int
289 sa_proto_share_resource(char *proto, sa_resource_t resource)
290 {
291 	struct sa_plugin_ops *ops = find_protocol(proto);
292 	int ret = SA_INVALID_PROTOCOL;
293 
294 	if (ops != NULL) {
295 		if (ops->sa_enable_resource != NULL)
296 			ret = ops->sa_enable_resource(resource);
297 		else
298 			ret = SA_NOT_SUPPORTED;
299 	}
300 	return (ret);
301 }
302 
303 /*
304  * sa_proto_unshare_resource(char *proto, sa_resource_t resource)
305  *
306  * For protocols that actually disable at the resource level, do the
307  * protocol specific resource disable. If it doesn't, return an error.
308  */
309 
310 int
311 sa_proto_unshare_resource(char *proto, sa_resource_t resource)
312 {
313 	struct sa_plugin_ops *ops = find_protocol(proto);
314 	int ret = SA_INVALID_PROTOCOL;
315 
316 	if (ops != NULL) {
317 		if (ops->sa_disable_resource != NULL)
318 			ret = ops->sa_disable_resource(resource);
319 		else
320 			ret = SA_NOT_SUPPORTED;
321 	}
322 	return (ret);
323 }
324 
325 /*
326  * sa_proto_valid_prop(handle, proto, prop, opt)
327  *
328  * Check to see if the specified prop is valid for this protocol.
329  */
330 
331 int
332 sa_proto_valid_prop(sa_handle_t handle, char *proto, sa_property_t prop,
333     sa_optionset_t opt)
334 {
335 	struct sa_plugin_ops *ops = find_protocol(proto);
336 	int ret = 0;
337 
338 	if (ops != NULL && ops->sa_valid_prop != NULL)
339 		ret = ops->sa_valid_prop(handle, prop, opt);
340 	return (ret);
341 }
342 
343 /*
344  * sa_proto_valid_space(proto, space)
345  *
346  * Check if space is valid optionspace for proto.
347  * Protocols that don't implement this don't support spaces.
348  */
349 int
350 sa_proto_valid_space(char *proto, char *token)
351 {
352 	struct sa_plugin_ops *ops = find_protocol(proto);
353 	int ret = 0;
354 
355 	if (ops != NULL && ops->sa_valid_space != NULL)
356 		ret = ops->sa_valid_space(token);
357 	return (ret);
358 }
359 
360 /*
361  * sa_proto_space_alias(proto, space)
362  *
363  * If the name for space is an alias, return its proper name.  This is
364  * used to translate "default" values into proper form.
365  */
366 char *
367 sa_proto_space_alias(char *proto, char *space)
368 {
369 	struct sa_plugin_ops *ops = find_protocol(proto);
370 	char *ret = space;
371 
372 	if (ops != NULL && ops->sa_space_alias != NULL)
373 		ret = ops->sa_space_alias(space);
374 	return (ret);
375 }
376 
377 /*
378  * sa_proto_security_prop(proto, token)
379  *
380  * Check to see if the property name in token is a valid named
381  * optionset property.
382  */
383 
384 int
385 sa_proto_security_prop(char *proto, char *token)
386 {
387 	struct sa_plugin_ops *ops = find_protocol(proto);
388 	int ret = 0;
389 
390 	if (ops != NULL && ops->sa_security_prop != NULL)
391 		ret = ops->sa_security_prop(token);
392 	return (ret);
393 }
394 
395 /*
396  * sa_proto_legacy_opts(proto, grouup, options)
397  *
398  * Have the protocol specific parser parse the options string and add
399  * an appropriate optionset to group.
400  */
401 
402 int
403 sa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
404 {
405 	struct sa_plugin_ops *ops = find_protocol(proto);
406 	int ret = SA_INVALID_PROTOCOL;
407 
408 	if (ops != NULL && ops->sa_legacy_opts != NULL)
409 		ret = ops->sa_legacy_opts(group, options);
410 	return (ret);
411 }
412 
413 /*
414  * sa_proto_legacy_format(proto, group, hier)
415  *
416  * Return a legacy format string representing either the group's
417  * properties or the groups hierarchical properties.
418  */
419 
420 char *
421 sa_proto_legacy_format(char *proto, sa_group_t group, int hier)
422 {
423 	struct sa_plugin_ops *ops = find_protocol(proto);
424 	char *ret = NULL;
425 
426 	if (ops != NULL && ops->sa_legacy_format != NULL)
427 		ret = ops->sa_legacy_format(group, hier);
428 	return (ret);
429 }
430 
431 void
432 sa_format_free(char *str)
433 {
434 	free(str);
435 }
436 
437 /*
438  * sharectl related API functions
439  */
440 
441 /*
442  * sa_proto_get_properties(proto)
443  *
444  * Return the set of properties that are specific to the
445  * protocol. These are usually in /etc/dfs/<proto> and related files,
446  * but only the protocol module knows which ones for sure.
447  */
448 
449 sa_protocol_properties_t
450 sa_proto_get_properties(char *proto)
451 {
452 	struct sa_plugin_ops *ops = find_protocol(proto);
453 	sa_protocol_properties_t props = NULL;
454 
455 	if (ops != NULL && ops->sa_get_proto_set != NULL)
456 		props = ops->sa_get_proto_set();
457 	return (props);
458 }
459 
460 /*
461  * sa_proto_set_property(proto, prop)
462  *
463  * Update the protocol specific property.
464  */
465 
466 int
467 sa_proto_set_property(char *proto, sa_property_t prop)
468 {
469 	struct sa_plugin_ops *ops = find_protocol(proto);
470 	int ret = SA_OK;
471 
472 	if (ops != NULL && ops->sa_set_proto_prop != NULL)
473 		ret = ops->sa_set_proto_prop(prop);
474 	return (ret);
475 }
476 
477 /*
478  * sa_valid_protocol(proto)
479  *
480  * Check to see if the protocol specified is defined by a
481  * plugin. Returns true (1) or false (0)
482  */
483 
484 int
485 sa_valid_protocol(char *proto)
486 {
487 	struct sa_plugin_ops *ops = find_protocol(proto);
488 	return (ops != NULL);
489 }
490 
491 /*
492  * Return the current operational status of the protocol
493  */
494 
495 char *
496 sa_get_protocol_status(char *proto)
497 {
498 	struct sa_plugin_ops *ops = find_protocol(proto);
499 	char *ret = NULL;
500 	if (ops != NULL && ops->sa_get_proto_status != NULL)
501 		ret = ops->sa_get_proto_status(proto);
502 	return (ret);
503 }
504 
505 /*
506  * sa_proto_update_legacy(proto, share)
507  *
508  * Update the protocol specific legacy files if necessary for the
509  * specified share.
510  */
511 
512 int
513 sa_proto_update_legacy(char *proto, sa_share_t share)
514 {
515 	struct sa_plugin_ops *ops = find_protocol(proto);
516 	int ret = SA_NOT_IMPLEMENTED;
517 
518 	if (ops != NULL) {
519 		if (ops->sa_update_legacy != NULL)
520 			ret = ops->sa_update_legacy(share);
521 	}
522 	return (ret);
523 }
524 
525 /*
526  * sa_delete_legacy(proto, share)
527  *
528  * Remove the specified share from the protocol specific legacy files.
529  */
530 
531 int
532 sa_proto_delete_legacy(char *proto, sa_share_t share)
533 {
534 	struct sa_plugin_ops *ops = find_protocol(proto);
535 	int ret = SA_NOT_IMPLEMENTED;
536 
537 	if (ops != NULL) {
538 		if (ops->sa_delete_legacy != NULL)
539 			ret = ops->sa_delete_legacy(share);
540 	} else {
541 		if (proto != NULL)
542 			ret = SA_NOT_IMPLEMENTED;
543 		else
544 			ret = SA_INVALID_PROTOCOL;
545 	}
546 	return (ret);
547 }
548 
549 /*
550  * sa_proto_delete_section(proto, section)
551  *
552  * Remove the specified section from the protocol specific legacy files,
553  * if supported.
554  */
555 
556 int
557 sa_proto_delete_section(char *proto, char *section)
558 {
559 	struct sa_plugin_ops *ops = find_protocol(proto);
560 	int ret = SA_OK;
561 
562 	if (ops != NULL) {
563 		if (ops->sa_delete_proto_section != NULL)
564 			ret = ops->sa_delete_proto_section(section);
565 	} else {
566 		if (proto != NULL)
567 			ret = SA_NOT_IMPLEMENTED;
568 		else
569 			ret = SA_INVALID_PROTOCOL;
570 	}
571 	return (ret);
572 }
573 
574 /*
575  * sa_proto_change_notify(share, char *protocol)
576  *
577  * Notify the protocol that a change has been made to the share
578  */
579 
580 int
581 sa_proto_change_notify(sa_share_t share, char *proto)
582 {
583 	struct sa_plugin_ops *ops = find_protocol(proto);
584 	int ret = SA_NOT_IMPLEMENTED;
585 
586 	if (ops != NULL) {
587 		if (ops->sa_change_notify != NULL)
588 			ret = ops->sa_change_notify(share);
589 	} else	if (proto == NULL) {
590 
591 			ret = SA_INVALID_PROTOCOL;
592 	}
593 	return (ret);
594 }
595 
596 /*
597  * sa_proto_notify_resource(resource, char *protocol)
598  *
599  * Notify the protocol that a change has been made to the share
600  */
601 
602 int
603 sa_proto_notify_resource(sa_resource_t resource, char *proto)
604 {
605 	struct sa_plugin_ops *ops = find_protocol(proto);
606 	int ret = SA_NOT_IMPLEMENTED;
607 
608 	if (ops != NULL) {
609 		if (ops->sa_notify_resource != NULL)
610 			ret = ops->sa_notify_resource(resource);
611 	} else if (proto == NULL) {
612 			ret = SA_INVALID_PROTOCOL;
613 	}
614 	return (ret);
615 }
616 
617 /*
618  * sa_proto_get_featureset(protocol)
619  *
620  * Get bitmask of defined features of the protocol. These are
621  * primarily things like SA_FEATURE_RESOURCE (shares are by resource
622  * name rather than path) and other operational features that affect
623  * behavior.
624  */
625 
626 uint64_t
627 sa_proto_get_featureset(char *proto)
628 {
629 	struct sa_plugin_ops *ops = find_protocol(proto);
630 	uint64_t ret = 0;
631 
632 	if (ops != NULL) {
633 		if (ops->sa_features != NULL)
634 			ret = ops->sa_features();
635 	}
636 	/* if not implemented, zero is valid */
637 	return (ret);
638 }
639 
640 /*
641  * sa_proto_get_transients(sa_handle_t)
642  *
643  * Called to get any protocol specific transient shares.  NFS doesn't
644  * use this since the info is in sharetab which is processed as a
645  * common transient store.
646  *
647  * The protocol plugin should verify that the share isn't in the
648  * repository and then add it as a transient.
649  *
650  * Not having an entry is not a problem. It returns 0 in that case.
651  */
652 
653 int
654 sa_proto_get_transients(sa_handle_t handle, char *proto)
655 {
656 	struct sa_plugin_ops *ops = find_protocol(proto);
657 	int ret = 0;
658 
659 	if (ops != NULL) {
660 		if (ops->sa_get_transient_shares != NULL)
661 			ret = ops->sa_get_transient_shares(handle);
662 	}
663 	return (ret);
664 }
665 
666 /*
667  * sa_proto_rename_resource(sa_handle_t, proto, sa_resource_t, newname)
668  *
669  * Protocols may need to know when a resource has changed names in
670  * order to notify clients. This must be done "before" the name in the
671  * resource has been changed. Not being implemented is not a problem.
672  */
673 
674 int
675 sa_proto_rename_resource(sa_handle_t handle, char *proto,
676     sa_resource_t resource, char *newname)
677 {
678 	struct sa_plugin_ops *ops = find_protocol(proto);
679 	int ret = SA_OK;
680 
681 	if (ops != NULL) {
682 		if (ops->sa_rename_resource != NULL)
683 			ret = ops->sa_rename_resource(handle, resource,
684 			    newname);
685 	}
686 	return (ret);
687 }
688