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