17a286c47SDai Ngo /* 27a286c47SDai Ngo * CDDL HEADER START 37a286c47SDai Ngo * 47a286c47SDai Ngo * The contents of this file are subject to the terms of the 57a286c47SDai Ngo * Common Development and Distribution License (the "License"). 67a286c47SDai Ngo * You may not use this file except in compliance with the License. 77a286c47SDai Ngo * 87a286c47SDai Ngo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97a286c47SDai Ngo * or http://www.opensolaris.org/os/licensing. 107a286c47SDai Ngo * See the License for the specific language governing permissions 117a286c47SDai Ngo * and limitations under the License. 127a286c47SDai Ngo * 137a286c47SDai Ngo * When distributing Covered Code, include this CDDL HEADER in each 147a286c47SDai Ngo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157a286c47SDai Ngo * If applicable, add the following below this CDDL HEADER, with the 167a286c47SDai Ngo * fields enclosed by brackets "[]" replaced with your own identifying 177a286c47SDai Ngo * information: Portions Copyright [yyyy] [name of copyright owner] 187a286c47SDai Ngo * 197a286c47SDai Ngo * CDDL HEADER END 207a286c47SDai Ngo */ 217a286c47SDai Ngo 227a286c47SDai Ngo /* 237a286c47SDai Ngo * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 247a286c47SDai Ngo * Use is subject to license terms. 257a286c47SDai Ngo */ 267a286c47SDai Ngo 277a286c47SDai Ngo #include <stdio.h> 287a286c47SDai Ngo #include <stdlib.h> 297a286c47SDai Ngo #include <unistd.h> 307a286c47SDai Ngo #include <strings.h> 317a286c47SDai Ngo #include <string.h> 327a286c47SDai Ngo #include <dirent.h> 337a286c47SDai Ngo #include <sys/types.h> 347a286c47SDai Ngo #include <sys/stat.h> 357a286c47SDai Ngo #include <sys/param.h> 367a286c47SDai Ngo #include <sys/errno.h> 377a286c47SDai Ngo #include <limits.h> 387a286c47SDai Ngo #include <libnvpair.h> 397a286c47SDai Ngo #include <dlfcn.h> 407a286c47SDai Ngo #include <libintl.h> 417a286c47SDai Ngo #include <sys/systeminfo.h> 427a286c47SDai Ngo #include <sys/fs_reparse.h> 437a286c47SDai Ngo #include "rp_plugin.h" 447a286c47SDai Ngo 457a286c47SDai Ngo #define MAXISALEN 257 /* based on sysinfo(2) man page */ 467a286c47SDai Ngo 477a286c47SDai Ngo static rp_proto_handle_t rp_proto_handle; 487a286c47SDai Ngo static rp_proto_plugin_t *rp_proto_list; 497a286c47SDai Ngo 507a286c47SDai Ngo int rp_plugin_init(void); 517a286c47SDai Ngo static void proto_plugin_fini(void); 527a286c47SDai Ngo static rp_plugin_ops_t *rp_find_protocol(const char *svctype); 537a286c47SDai Ngo 547a286c47SDai Ngo extern int errno; 557a286c47SDai Ngo static int rp_plugin_inited = 0; 567a286c47SDai Ngo 577a286c47SDai Ngo /* 587a286c47SDai Ngo * reparse_create() 597a286c47SDai Ngo * 607a286c47SDai Ngo * Create a symlink at the specified 'path' as a reparse point. 617a286c47SDai Ngo * This function will fail if path refers to an existing file system 627a286c47SDai Ngo * object or an object named string already exists at the given path. 637a286c47SDai Ngo * 647a286c47SDai Ngo * return 0 if ok else return error code. 657a286c47SDai Ngo */ 667a286c47SDai Ngo int 67*6a1af1a6SRichard Lowe reparse_create(const char *path, const char *data) 687a286c47SDai Ngo { 697a286c47SDai Ngo int err; 707a286c47SDai Ngo struct stat sbuf; 717a286c47SDai Ngo 72*6a1af1a6SRichard Lowe if (path == NULL || data == NULL) 737a286c47SDai Ngo return (EINVAL); 747a286c47SDai Ngo 75*6a1af1a6SRichard Lowe if ((err = reparse_validate(data)) != 0) 767a286c47SDai Ngo return (err); 777a286c47SDai Ngo 787a286c47SDai Ngo /* check if object exists */ 797a286c47SDai Ngo if (lstat(path, &sbuf) == 0) 807a286c47SDai Ngo return (EEXIST); 817a286c47SDai Ngo 82*6a1af1a6SRichard Lowe return (symlink(data, path) ? errno : 0); 837a286c47SDai Ngo } 847a286c47SDai Ngo 857a286c47SDai Ngo /* 867a286c47SDai Ngo * reparse_unparse() 877a286c47SDai Ngo * 887a286c47SDai Ngo * Convert an nvlist back to a string format suitable to write 897a286c47SDai Ngo * to the reparse point symlink body. The string returned is in 907a286c47SDai Ngo * allocated memory and must be freed by the caller. 917a286c47SDai Ngo * 927a286c47SDai Ngo * return 0 if ok else return error code. 937a286c47SDai Ngo */ 947a286c47SDai Ngo int 957a286c47SDai Ngo reparse_unparse(nvlist_t *nvl, char **stringp) 967a286c47SDai Ngo { 977a286c47SDai Ngo int err, buflen; 987a286c47SDai Ngo char *buf, *stype, *val; 997a286c47SDai Ngo nvpair_t *curr; 1007a286c47SDai Ngo 1017a286c47SDai Ngo if (nvl == NULL || stringp == NULL || 1027a286c47SDai Ngo ((curr = nvlist_next_nvpair(nvl, NULL)) == NULL)) 1037a286c47SDai Ngo return (EINVAL); 1047a286c47SDai Ngo 1057a286c47SDai Ngo buflen = SYMLINK_MAX; 1067a286c47SDai Ngo if ((buf = malloc(buflen)) == NULL) 1077a286c47SDai Ngo return (ENOMEM); 1087a286c47SDai Ngo 1097a286c47SDai Ngo err = 0; 1107a286c47SDai Ngo (void) snprintf(buf, buflen, "%s", FS_REPARSE_TAG_STR); 1117a286c47SDai Ngo while (curr != NULL) { 1127a286c47SDai Ngo if (!(stype = nvpair_name(curr))) { 1137a286c47SDai Ngo err = EINVAL; 1147a286c47SDai Ngo break; 1157a286c47SDai Ngo } 1167a286c47SDai Ngo if ((strlcat(buf, FS_TOKEN_START_STR, buflen) >= buflen) || 1177a286c47SDai Ngo (strlcat(buf, stype, buflen) >= buflen) || 1187a286c47SDai Ngo (strlcat(buf, ":", buflen) >= buflen) || 1197a286c47SDai Ngo (nvpair_value_string(curr, &val) != 0) || 1207a286c47SDai Ngo (strlcat(buf, val, buflen) >= buflen) || 1217a286c47SDai Ngo (strlcat(buf, FS_TOKEN_END_STR, buflen) >= buflen)) { 1227a286c47SDai Ngo err = E2BIG; 1237a286c47SDai Ngo break; 1247a286c47SDai Ngo } 1257a286c47SDai Ngo curr = nvlist_next_nvpair(nvl, curr); 1267a286c47SDai Ngo } 1277a286c47SDai Ngo if (err != 0) { 1287a286c47SDai Ngo free(buf); 1297a286c47SDai Ngo return (err); 1307a286c47SDai Ngo } 1317a286c47SDai Ngo if (strlcat(buf, FS_REPARSE_TAG_END_STR, buflen) >= buflen) { 1327a286c47SDai Ngo free(buf); 1337a286c47SDai Ngo return (E2BIG); 1347a286c47SDai Ngo } 1357a286c47SDai Ngo 1367a286c47SDai Ngo *stringp = buf; 1377a286c47SDai Ngo return (0); 1387a286c47SDai Ngo } 1397a286c47SDai Ngo 1407a286c47SDai Ngo /* 1417a286c47SDai Ngo * reparse_deref() 1427a286c47SDai Ngo * 1437a286c47SDai Ngo * Accepts the service-specific item from the reparse point and returns 1447a286c47SDai Ngo * the service-specific data requested. The caller specifies the size 1457a286c47SDai Ngo * of the buffer provided via *bufsz. 1467a286c47SDai Ngo * 1477a286c47SDai Ngo * if ok return 0 and *bufsz is updated to contain the actual length of 1487a286c47SDai Ngo * the returned results, else return error code. If the error code is 1497a286c47SDai Ngo * EOVERFLOW; results do not fit in the buffer, *bufsz will be updated 1507a286c47SDai Ngo * to contain the number of bytes needed to hold the results. 1517a286c47SDai Ngo */ 1527a286c47SDai Ngo int 1537a286c47SDai Ngo reparse_deref(const char *svc_type, const char *svc_data, char *buf, 1547a286c47SDai Ngo size_t *bufsz) 1557a286c47SDai Ngo { 1567a286c47SDai Ngo rp_plugin_ops_t *ops; 1577a286c47SDai Ngo 1587a286c47SDai Ngo if ((svc_type == NULL) || (svc_data == NULL) || (buf == NULL) || 1597a286c47SDai Ngo (bufsz == NULL)) 1607a286c47SDai Ngo return (EINVAL); 1617a286c47SDai Ngo 1627a286c47SDai Ngo ops = rp_find_protocol(svc_type); 1637a286c47SDai Ngo if ((ops != NULL) && (ops->rpo_deref != NULL)) 1647a286c47SDai Ngo return (ops->rpo_deref(svc_type, svc_data, buf, bufsz)); 1657a286c47SDai Ngo 1667a286c47SDai Ngo /* no plugin, return error */ 1677a286c47SDai Ngo return (ENOTSUP); 1687a286c47SDai Ngo } 1697a286c47SDai Ngo 1707a286c47SDai Ngo /* 1717a286c47SDai Ngo * reparse_delete() 1727a286c47SDai Ngo * 1737a286c47SDai Ngo * Delete a reparse point at a given pathname. It will fail if 1747a286c47SDai Ngo * a reparse point does not exist at the given path or the pathname 1757a286c47SDai Ngo * is not a symlink. 1767a286c47SDai Ngo * 1777a286c47SDai Ngo * return 0 if ok else return error code. 1787a286c47SDai Ngo */ 1797a286c47SDai Ngo int 1807a286c47SDai Ngo reparse_delete(const char *path) 1817a286c47SDai Ngo { 1827a286c47SDai Ngo struct stat sbuf; 1837a286c47SDai Ngo 1847a286c47SDai Ngo if (path == NULL) 1857a286c47SDai Ngo return (EINVAL); 1867a286c47SDai Ngo 1877a286c47SDai Ngo /* check if object exists */ 1887a286c47SDai Ngo if (lstat(path, &sbuf) != 0) 1897a286c47SDai Ngo return (errno); 1907a286c47SDai Ngo 1917a286c47SDai Ngo if ((sbuf.st_mode & S_IFLNK) != S_IFLNK) 1927a286c47SDai Ngo return (EINVAL); 1937a286c47SDai Ngo 1947a286c47SDai Ngo return (unlink(path) ? errno : 0); 1957a286c47SDai Ngo } 1967a286c47SDai Ngo 1977a286c47SDai Ngo /* 1987a286c47SDai Ngo * reparse_add() 1997a286c47SDai Ngo * 2007a286c47SDai Ngo * Add a service type entry to a nvlist with a copy of svc_data, 2017a286c47SDai Ngo * replacing one of the same type if already present. 2027a286c47SDai Ngo * 2037a286c47SDai Ngo * return 0 if ok else return error code. 2047a286c47SDai Ngo */ 2057a286c47SDai Ngo int 2067a286c47SDai Ngo reparse_add(nvlist_t *nvl, const char *svc_type, const char *svc_data) 2077a286c47SDai Ngo { 2087a286c47SDai Ngo int err; 2097a286c47SDai Ngo char *buf; 2107a286c47SDai Ngo size_t bufsz; 2117a286c47SDai Ngo rp_plugin_ops_t *ops; 2127a286c47SDai Ngo 2137a286c47SDai Ngo if ((nvl == NULL) || (svc_type == NULL) || (svc_data == NULL)) 2147a286c47SDai Ngo return (EINVAL); 2157a286c47SDai Ngo 2167a286c47SDai Ngo bufsz = SYMLINK_MAX; /* no need to mess around */ 2177a286c47SDai Ngo if ((buf = malloc(bufsz)) == NULL) 2187a286c47SDai Ngo return (ENOMEM); 2197a286c47SDai Ngo 2207a286c47SDai Ngo ops = rp_find_protocol(svc_type); 2217a286c47SDai Ngo if ((ops != NULL) && (ops->rpo_form != NULL)) 2227a286c47SDai Ngo err = ops->rpo_form(svc_type, svc_data, buf, &bufsz); 2237a286c47SDai Ngo else 2247a286c47SDai Ngo err = ENOTSUP; /* no plugin */ 2257a286c47SDai Ngo 2267a286c47SDai Ngo if (err != 0) { 2277a286c47SDai Ngo free(buf); 2287a286c47SDai Ngo return (err); 2297a286c47SDai Ngo } 2307a286c47SDai Ngo 2317a286c47SDai Ngo err = nvlist_add_string(nvl, svc_type, buf); 2327a286c47SDai Ngo free(buf); 2337a286c47SDai Ngo return (err); 2347a286c47SDai Ngo } 2357a286c47SDai Ngo 2367a286c47SDai Ngo /* 2377a286c47SDai Ngo * reparse_remove() 2387a286c47SDai Ngo * 2397a286c47SDai Ngo * Remove a service type entry from the nvlist, if present. 2407a286c47SDai Ngo * 2417a286c47SDai Ngo * return 0 if ok else return error code. 2427a286c47SDai Ngo */ 2437a286c47SDai Ngo int 2447a286c47SDai Ngo reparse_remove(nvlist_t *nvl, const char *svc_type) 2457a286c47SDai Ngo { 2467a286c47SDai Ngo if ((nvl == NULL) || (svc_type == NULL)) 2477a286c47SDai Ngo return (EINVAL); 2487a286c47SDai Ngo 2497a286c47SDai Ngo return (nvlist_remove_all(nvl, svc_type)); 2507a286c47SDai Ngo } 2517a286c47SDai Ngo 2527a286c47SDai Ngo /* 2537a286c47SDai Ngo * Returns true if name is "." or "..", otherwise returns false. 2547a286c47SDai Ngo */ 2557a286c47SDai Ngo static boolean_t 2567a286c47SDai Ngo rp_is_dot_or_dotdot(const char *name) 2577a286c47SDai Ngo { 2587a286c47SDai Ngo if (*name != '.') 2597a286c47SDai Ngo return (B_FALSE); 2607a286c47SDai Ngo 2617a286c47SDai Ngo if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) 2627a286c47SDai Ngo return (B_TRUE); 2637a286c47SDai Ngo 2647a286c47SDai Ngo return (B_FALSE); 2657a286c47SDai Ngo } 2667a286c47SDai Ngo 2677a286c47SDai Ngo static void 2687a286c47SDai Ngo proto_plugin_fini() 2697a286c47SDai Ngo { 2707a286c47SDai Ngo rp_proto_plugin_t *p; 2717a286c47SDai Ngo 2727a286c47SDai Ngo /* 2737a286c47SDai Ngo * Protocols may call this framework during _fini 2747a286c47SDai Ngo */ 2757a286c47SDai Ngo for (p = rp_proto_list; p != NULL; p = p->plugin_next) { 2767a286c47SDai Ngo if (p->plugin_ops->rpo_fini) 2777a286c47SDai Ngo p->plugin_ops->rpo_fini(); 2787a286c47SDai Ngo } 2797a286c47SDai Ngo while ((p = rp_proto_list) != NULL) { 2807a286c47SDai Ngo rp_proto_list = p->plugin_next; 2817a286c47SDai Ngo if (p->plugin_handle != NULL) 2827a286c47SDai Ngo (void) dlclose(p->plugin_handle); 2837a286c47SDai Ngo free(p); 2847a286c47SDai Ngo } 2857a286c47SDai Ngo 2867a286c47SDai Ngo if (rp_proto_handle.rp_ops != NULL) { 2877a286c47SDai Ngo free(rp_proto_handle.rp_ops); 2887a286c47SDai Ngo rp_proto_handle.rp_ops = NULL; 2897a286c47SDai Ngo } 2907a286c47SDai Ngo rp_proto_handle.rp_num_proto = 0; 2917a286c47SDai Ngo } 2927a286c47SDai Ngo 2937a286c47SDai Ngo /* 2947a286c47SDai Ngo * rp_plugin_init() 2957a286c47SDai Ngo * 2967a286c47SDai Ngo * Initialize the service type specific plugin modules. 2977a286c47SDai Ngo * For each reparse service type, there should be a plugin library for it. 2987a286c47SDai Ngo * This function walks /usr/lib/reparse directory for plugin libraries. 2997a286c47SDai Ngo * For each plugin library found, initialize it and add it to the internal 3007a286c47SDai Ngo * list of service type plugin. These are used for service type specific 3017a286c47SDai Ngo * operations. 3027a286c47SDai Ngo */ 3037a286c47SDai Ngo int 3047a286c47SDai Ngo rp_plugin_init() 3057a286c47SDai Ngo { 3067a286c47SDai Ngo int err, ret = RP_OK; 3077a286c47SDai Ngo char isa[MAXISALEN], dirpath[MAXPATHLEN], path[MAXPATHLEN]; 3087a286c47SDai Ngo int num_protos = 0; 3097a286c47SDai Ngo rp_proto_handle_t *rp_hdl; 3107a286c47SDai Ngo rp_proto_plugin_t *proto, *tmp; 3117a286c47SDai Ngo rp_plugin_ops_t *plugin_ops; 3127a286c47SDai Ngo struct stat st; 3137a286c47SDai Ngo void *dlhandle; 3147a286c47SDai Ngo DIR *dir; 3157a286c47SDai Ngo struct dirent *dent; 3167a286c47SDai Ngo 3177a286c47SDai Ngo #if defined(_LP64) 3187a286c47SDai Ngo if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1) 3197a286c47SDai Ngo isa[0] = '\0'; 3207a286c47SDai Ngo #else 3217a286c47SDai Ngo isa[0] = '\0'; 3227a286c47SDai Ngo #endif 3237a286c47SDai Ngo 3247a286c47SDai Ngo (void) snprintf(dirpath, MAXPATHLEN, 3257a286c47SDai Ngo "%s/%s", RP_LIB_DIR, isa); 3267a286c47SDai Ngo 3277a286c47SDai Ngo if ((dir = opendir(dirpath)) == NULL) 3287a286c47SDai Ngo return (RP_NO_PLUGIN_DIR); 3297a286c47SDai Ngo 3307a286c47SDai Ngo while ((dent = readdir(dir)) != NULL) { 3317a286c47SDai Ngo if (rp_is_dot_or_dotdot(dent->d_name)) 3327a286c47SDai Ngo continue; 3337a286c47SDai Ngo 3347a286c47SDai Ngo (void) snprintf(path, MAXPATHLEN, 3357a286c47SDai Ngo "%s/%s", dirpath, dent->d_name); 3367a286c47SDai Ngo 3377a286c47SDai Ngo /* 3387a286c47SDai Ngo * If file doesn't exist, don't try to map it 3397a286c47SDai Ngo */ 3407a286c47SDai Ngo if (stat(path, &st) < 0) 3417a286c47SDai Ngo continue; 3427a286c47SDai Ngo if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL) 3437a286c47SDai Ngo continue; 3447a286c47SDai Ngo 3457a286c47SDai Ngo plugin_ops = (rp_plugin_ops_t *) 3467a286c47SDai Ngo dlsym(dlhandle, "rp_plugin_ops"); 3477a286c47SDai Ngo if (plugin_ops == NULL) { 3487a286c47SDai Ngo (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 3497a286c47SDai Ngo "Error in plugin ops for service type %s\n%s\n"), 3507a286c47SDai Ngo dent->d_name, dlerror()); 3517a286c47SDai Ngo (void) dlclose(dlhandle); 3527a286c47SDai Ngo continue; 3537a286c47SDai Ngo } 3547a286c47SDai Ngo proto = (rp_proto_plugin_t *) 3557a286c47SDai Ngo calloc(1, sizeof (rp_proto_plugin_t)); 3567a286c47SDai Ngo if (proto == NULL) { 3577a286c47SDai Ngo (void) dlclose(dlhandle); 3587a286c47SDai Ngo (void) fprintf(stderr, 3597a286c47SDai Ngo dgettext(TEXT_DOMAIN, "No memory for plugin %s\n"), 3607a286c47SDai Ngo dent->d_name); 3617a286c47SDai Ngo ret = RP_NO_MEMORY; 3627a286c47SDai Ngo break; 3637a286c47SDai Ngo } 3647a286c47SDai Ngo 3657a286c47SDai Ngo proto->plugin_ops = plugin_ops; 3667a286c47SDai Ngo proto->plugin_handle = dlhandle; 3677a286c47SDai Ngo num_protos++; 3687a286c47SDai Ngo proto->plugin_next = rp_proto_list; 3697a286c47SDai Ngo rp_proto_list = proto; 3707a286c47SDai Ngo } 3717a286c47SDai Ngo 3727a286c47SDai Ngo (void) closedir(dir); 3737a286c47SDai Ngo 3747a286c47SDai Ngo if ((num_protos == 0) && (ret == 0)) 3757a286c47SDai Ngo ret = RP_NO_PLUGIN; 3767a286c47SDai Ngo /* 3777a286c47SDai Ngo * There was an error, so cleanup prior to return of failure. 3787a286c47SDai Ngo */ 3797a286c47SDai Ngo if (ret != RP_OK) { 3807a286c47SDai Ngo proto_plugin_fini(); 3817a286c47SDai Ngo return (ret); 3827a286c47SDai Ngo } 3837a286c47SDai Ngo 3847a286c47SDai Ngo rp_proto_handle.rp_ops = (rp_plugin_ops_t **)calloc(num_protos, 3857a286c47SDai Ngo sizeof (rp_plugin_ops_t *)); 3867a286c47SDai Ngo if (!rp_proto_handle.rp_ops) { 3877a286c47SDai Ngo proto_plugin_fini(); 3887a286c47SDai Ngo return (RP_NO_MEMORY); 3897a286c47SDai Ngo } 3907a286c47SDai Ngo 3917a286c47SDai Ngo rp_hdl = &rp_proto_handle; 3927a286c47SDai Ngo rp_hdl->rp_num_proto = 0; 3937a286c47SDai Ngo for (tmp = rp_proto_list; rp_hdl->rp_num_proto < num_protos && 3947a286c47SDai Ngo tmp != NULL; tmp = tmp->plugin_next) { 3957a286c47SDai Ngo 3967a286c47SDai Ngo err = RP_OK; 3977a286c47SDai Ngo if (tmp->plugin_ops->rpo_init != NULL) 3987a286c47SDai Ngo err = tmp->plugin_ops->rpo_init(); 3997a286c47SDai Ngo if (err != RP_OK) 4007a286c47SDai Ngo continue; 4017a286c47SDai Ngo rp_hdl->rp_ops[rp_hdl->rp_num_proto++] = tmp->plugin_ops; 4027a286c47SDai Ngo } 4037a286c47SDai Ngo 4047a286c47SDai Ngo return (rp_hdl->rp_num_proto > 0 ? RP_OK : RP_NO_PLUGIN); 4057a286c47SDai Ngo } 4067a286c47SDai Ngo 4077a286c47SDai Ngo 4087a286c47SDai Ngo /* 4097a286c47SDai Ngo * find_protocol() 4107a286c47SDai Ngo * 4117a286c47SDai Ngo * Search the plugin list for the specified protocol and return the 4127a286c47SDai Ngo * ops vector. return NULL if protocol is not defined. 4137a286c47SDai Ngo */ 4147a286c47SDai Ngo static rp_plugin_ops_t * 4157a286c47SDai Ngo rp_find_protocol(const char *svc_type) 4167a286c47SDai Ngo { 4177a286c47SDai Ngo int i; 4187a286c47SDai Ngo rp_plugin_ops_t *ops = NULL; 4197a286c47SDai Ngo 4207a286c47SDai Ngo if (svc_type == NULL) 4217a286c47SDai Ngo return (NULL); 4227a286c47SDai Ngo 4237a286c47SDai Ngo if (rp_plugin_inited == 0) { 4247a286c47SDai Ngo if (rp_plugin_init() == RP_OK) 4257a286c47SDai Ngo rp_plugin_inited = 1; 4267a286c47SDai Ngo else 4277a286c47SDai Ngo return (NULL); 4287a286c47SDai Ngo } 4297a286c47SDai Ngo 4307a286c47SDai Ngo for (i = 0; i < rp_proto_handle.rp_num_proto; i++) { 4317a286c47SDai Ngo ops = rp_proto_handle.rp_ops[i]; 4327a286c47SDai Ngo if (ops->rpo_supports_svc(svc_type)) 4337a286c47SDai Ngo return (ops); 4347a286c47SDai Ngo 4357a286c47SDai Ngo } 4367a286c47SDai Ngo return (NULL); 4377a286c47SDai Ngo } 438