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
libvarpd_plugin_comparator(const void * lp,const void * rp)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 *
libvarpd_plugin_alloc(uint_t version,int * errp)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
libvarpd_plugin_free(varpd_plugin_register_t * vprp)83 libvarpd_plugin_free(varpd_plugin_register_t *vprp)
84 {
85 umem_free(vprp, sizeof (varpd_plugin_register_t));
86 }
87
88 int
libvarpd_plugin_register(varpd_plugin_register_t * vprp)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 *
libvarpd_plugin_lookup(varpd_impl_t * vip,const char * name)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
libvarpd_plugin_load_cb(varpd_impl_t * vip,const char * path,void * unused)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
libvarpd_plugin_load(varpd_handle_t * vph,const char * path)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
libvarpd_plugin_walk(varpd_handle_t * vph,libvarpd_plugin_walk_f func,void * arg)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
libvarpd_plugin_init(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
libvarpd_plugin_fini(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
libvarpd_plugin_prefork(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
libvarpd_plugin_postfork(void)252 libvarpd_plugin_postfork(void)
253 {
254 (void) cond_signal(&varpd_load_cv);
255 mutex_exit(&varpd_load_lock);
256 }
257