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