1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Joyent, Inc. 14 */ 15 16 /* 17 * varpd plugin management 18 */ 19 20 #include <libvarpd_impl.h> 21 #include <errno.h> 22 #include <umem.h> 23 #include <assert.h> 24 #include <strings.h> 25 #include <dlfcn.h> 26 #include <link.h> 27 #include <stdio.h> 28 29 static varpd_impl_t *varpd_load_handle; 30 static const char *varpd_load_path; 31 static mutex_t varpd_load_lock; 32 static cond_t varpd_load_cv; 33 34 int 35 libvarpd_plugin_comparator(const void *lp, const void *rp) 36 { 37 int ret; 38 const varpd_plugin_t *lpp, *rpp; 39 40 lpp = lp; 41 rpp = rp; 42 43 ret = strcmp(lpp->vpp_name, rpp->vpp_name); 44 if (ret > 0) 45 return (1); 46 if (ret < 0) 47 return (-1); 48 return (0); 49 } 50 51 varpd_plugin_register_t * 52 libvarpd_plugin_alloc(uint_t version, int *errp) 53 { 54 int err; 55 varpd_plugin_register_t *vprp; 56 57 if (errp == NULL) 58 errp = &err; 59 60 if (version != VARPD_VERSION_ONE) { 61 (void) fprintf(stderr, 62 "unsupported registration version %u - %s\n", 63 version, varpd_load_path); 64 *errp = EINVAL; 65 return (NULL); 66 } 67 68 vprp = umem_alloc(sizeof (varpd_plugin_register_t), UMEM_DEFAULT); 69 if (vprp == NULL) { 70 (void) fprintf(stderr, 71 "failed to allocate registration handle - %s\n", 72 varpd_load_path); 73 *errp = ENOMEM; 74 return (NULL); 75 } 76 77 vprp->vpr_version = VARPD_VERSION_ONE; 78 79 return (vprp); 80 } 81 82 void 83 libvarpd_plugin_free(varpd_plugin_register_t *vprp) 84 { 85 umem_free(vprp, sizeof (varpd_plugin_register_t)); 86 } 87 88 int 89 libvarpd_plugin_register(varpd_plugin_register_t *vprp) 90 { 91 varpd_plugin_t *vpp; 92 varpd_plugin_t lookup; 93 94 vpp = umem_alloc(sizeof (varpd_plugin_t), UMEM_DEFAULT); 95 if (vpp == NULL) { 96 (void) fprintf(stderr, 97 "failed to allocate memory for the varpd_plugin_t - %s\n", 98 varpd_load_path); 99 return (ENOMEM); 100 } 101 102 /* Watch out for an evil plugin */ 103 if (vprp->vpr_version != VARPD_VERSION_ONE) { 104 (void) fprintf(stderr, 105 "unsupported registration version %u - %s\n", 106 vprp->vpr_version, varpd_load_path); 107 return (EINVAL); 108 } 109 110 mutex_enter(&varpd_load_lock); 111 if (varpd_load_handle == NULL) 112 libvarpd_panic("varpd_load_handle was unexpectedly null"); 113 114 mutex_enter(&varpd_load_handle->vdi_lock); 115 lookup.vpp_name = vprp->vpr_name; 116 if (avl_find(&varpd_load_handle->vdi_plugins, &lookup, NULL) != NULL) { 117 (void) fprintf(stderr, 118 "module already exists with requested name '%s' - %s\n", 119 vprp->vpr_name, varpd_load_path); 120 mutex_exit(&varpd_load_handle->vdi_lock); 121 mutex_exit(&varpd_load_lock); 122 umem_free(vpp, sizeof (varpd_plugin_t)); 123 return (EEXIST); 124 } 125 vpp->vpp_name = strdup(vprp->vpr_name); 126 if (vpp->vpp_name == NULL) { 127 (void) fprintf(stderr, 128 "failed to allocate memory to duplicate name - %s\n", 129 varpd_load_path); 130 mutex_exit(&varpd_load_handle->vdi_lock); 131 mutex_exit(&varpd_load_lock); 132 umem_free(vpp, sizeof (varpd_plugin_t)); 133 return (ENOMEM); 134 } 135 136 vpp->vpp_mode = vprp->vpr_mode; 137 vpp->vpp_ops = vprp->vpr_ops; 138 if (mutex_init(&vpp->vpp_lock, USYNC_THREAD | LOCK_ERRORCHECK, 139 NULL) != 0) 140 libvarpd_panic("failed to create plugin's vpp_lock"); 141 vpp->vpp_active = 0; 142 avl_add(&varpd_load_handle->vdi_plugins, vpp); 143 mutex_exit(&varpd_load_handle->vdi_lock); 144 mutex_exit(&varpd_load_lock); 145 146 return (0); 147 } 148 149 varpd_plugin_t * 150 libvarpd_plugin_lookup(varpd_impl_t *vip, const char *name) 151 { 152 varpd_plugin_t lookup, *ret; 153 154 lookup.vpp_name = name; 155 mutex_enter(&vip->vdi_lock); 156 ret = avl_find(&vip->vdi_plugins, &lookup, NULL); 157 mutex_exit(&vip->vdi_lock); 158 159 return (ret); 160 } 161 162 /* ARGSUSED */ 163 static int 164 libvarpd_plugin_load_cb(varpd_impl_t *vip, const char *path, void *unused) 165 { 166 void *dlp; 167 168 varpd_load_path = path; 169 dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW); 170 if (dlp == NULL) 171 (void) fprintf(stderr, "dlopen failed - %s\n", path); 172 path = NULL; 173 174 return (0); 175 } 176 177 int 178 libvarpd_plugin_load(varpd_handle_t *vph, const char *path) 179 { 180 int ret = 0; 181 varpd_impl_t *vip = (varpd_impl_t *)vph; 182 183 if (vip == NULL || path == NULL) 184 return (EINVAL); 185 mutex_enter(&varpd_load_lock); 186 while (varpd_load_handle != NULL) 187 (void) cond_wait(&varpd_load_cv, &varpd_load_lock); 188 varpd_load_handle = vip; 189 mutex_exit(&varpd_load_lock); 190 191 ret = libvarpd_dirwalk(vip, path, ".so", libvarpd_plugin_load_cb, NULL); 192 193 mutex_enter(&varpd_load_lock); 194 varpd_load_handle = NULL; 195 (void) cond_signal(&varpd_load_cv); 196 mutex_exit(&varpd_load_lock); 197 198 return (ret); 199 } 200 201 int 202 libvarpd_plugin_walk(varpd_handle_t *vph, libvarpd_plugin_walk_f func, 203 void *arg) 204 { 205 varpd_impl_t *vip = (varpd_impl_t *)vph; 206 varpd_plugin_t *vpp; 207 208 mutex_enter(&vip->vdi_lock); 209 for (vpp = avl_first(&vip->vdi_plugins); vpp != NULL; 210 vpp = AVL_NEXT(&vip->vdi_plugins, vpp)) { 211 if (func(vph, vpp->vpp_name, arg) != 0) { 212 mutex_exit(&vip->vdi_lock); 213 return (1); 214 } 215 } 216 mutex_exit(&vip->vdi_lock); 217 return (0); 218 } 219 220 void 221 libvarpd_plugin_init(void) 222 { 223 if (mutex_init(&varpd_load_lock, USYNC_THREAD | LOCK_RECURSIVE | 224 LOCK_ERRORCHECK, NULL) != 0) 225 libvarpd_panic("failed to create varpd_load_lock"); 226 227 if (cond_init(&varpd_load_cv, USYNC_THREAD, NULL) != 0) 228 libvarpd_panic("failed to create varpd_load_cv"); 229 230 varpd_load_handle = NULL; 231 } 232 233 void 234 libvarpd_plugin_fini(void) 235 { 236 assert(varpd_load_handle == NULL); 237 if (mutex_destroy(&varpd_load_lock) != 0) 238 libvarpd_panic("failed to destroy varpd_load_lock"); 239 if (cond_destroy(&varpd_load_cv) != 0) 240 libvarpd_panic("failed to destroy varpd_load_cv"); 241 } 242 243 void 244 libvarpd_plugin_prefork(void) 245 { 246 mutex_enter(&varpd_load_lock); 247 while (varpd_load_handle != NULL) 248 (void) cond_wait(&varpd_load_cv, &varpd_load_lock); 249 } 250 251 void 252 libvarpd_plugin_postfork(void) 253 { 254 (void) cond_signal(&varpd_load_cv); 255 mutex_exit(&varpd_load_lock); 256 } 257