1 /*-
2  * Copyright (c) 2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
3  * Copyright (c) 2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4  * Copyright (c) 2012-2014 Baptiste Daroussin <bapt@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <dlfcn.h>
36 #include <stdbool.h>
37 #include <string.h>
38 #include <assert.h>
39 #include <unistd.h>
40 #include <ucl.h>
41 
42 #include "pkg.h"
43 #include "private/pkg.h"
44 #include "private/event.h"
45 
46 #define PLUGIN_NUMFIELDS 4
47 
48 struct plugin_hook {
49 	pkg_plugin_hook_t hook;				/* plugin hook type */
50 	pkg_plugin_callback callback;			/* plugin callback function */
51 	UT_hash_handle hh;
52 };
53 
54 struct pkg_plugin {
55 	xstring *fields[PLUGIN_NUMFIELDS];
56 	void *lh;						/* library handle */
57 	bool parsed;
58 	struct plugin_hook *hooks;
59 	pkg_object *conf;
60 	struct pkg_plugin *next;
61 };
62 
63 static struct pkg_plugin *plugins = NULL;
64 
65 static int pkg_plugin_free(void);
66 static int pkg_plugin_hook_free(struct pkg_plugin *p);
67 static int pkg_plugin_hook_exec(struct pkg_plugin *p, pkg_plugin_hook_t hook, void *data, struct pkgdb *db);
68 
69 void *
pkg_plugin_func(struct pkg_plugin * p,const char * func)70 pkg_plugin_func(struct pkg_plugin *p, const char *func)
71 {
72 	return (dlsym(p->lh, func));
73 }
74 
75 static int
pkg_plugin_hook_free(struct pkg_plugin * p)76 pkg_plugin_hook_free(struct pkg_plugin *p)
77 {
78 	assert(p != NULL);
79 
80 	HASH_FREE(p->hooks, free);
81 
82 	return (EPKG_OK);
83 }
84 
85 static void
plug_free(struct pkg_plugin * p)86 plug_free(struct pkg_plugin *p)
87 {
88 	unsigned int i;
89 
90 	for (i = 0; i < PLUGIN_NUMFIELDS; i++)
91 		xstring_free(p->fields[i]);
92 
93 	ucl_object_unref(p->conf);
94 	pkg_plugin_hook_free(p);
95 	free(p);
96 }
97 
98 static int
pkg_plugin_free(void)99 pkg_plugin_free(void)
100 {
101 	LL_FREE(plugins, plug_free);
102 
103 	return (EPKG_OK);
104 }
105 
106 int
pkg_plugin_hook_register(struct pkg_plugin * p,pkg_plugin_hook_t hook,pkg_plugin_callback callback)107 pkg_plugin_hook_register(struct pkg_plugin *p, pkg_plugin_hook_t hook, pkg_plugin_callback callback)
108 {
109 	struct plugin_hook *new = NULL;
110 
111 	assert(p != NULL);
112 	assert(callback != NULL);
113 
114 	new = xcalloc(1, sizeof(struct plugin_hook));
115 	new->hook = hook;
116 	new->callback = callback;
117 
118 	HASH_ADD_INT(p->hooks, hook, new);
119 
120 	return (EPKG_OK);
121 }
122 
123 static int
pkg_plugin_hook_exec(struct pkg_plugin * p,pkg_plugin_hook_t hook,void * data,struct pkgdb * db)124 pkg_plugin_hook_exec(struct pkg_plugin *p, pkg_plugin_hook_t hook, void *data, struct pkgdb *db)
125 {
126 	struct plugin_hook *h = NULL;
127 
128 	assert(p != NULL);
129 
130 	HASH_FIND_INT(p->hooks, &hook, h);
131 	if (h != NULL)
132 		h->callback(data, db);
133 
134 	return (EPKG_OK);
135 }
136 
137 int
pkg_plugins_hook_run(pkg_plugin_hook_t hook,void * data,struct pkgdb * db)138 pkg_plugins_hook_run(pkg_plugin_hook_t hook, void *data, struct pkgdb *db)
139 {
140 	struct pkg_plugin *p = NULL;
141 
142 	while (pkg_plugins(&p) != EPKG_END)
143 		pkg_plugin_hook_exec(p, hook, data, db);
144 
145 	return (EPKG_OK);
146 }
147 
148 int
pkg_plugin_set(struct pkg_plugin * p,pkg_plugin_key key,const char * str)149 pkg_plugin_set(struct pkg_plugin *p, pkg_plugin_key key, const char *str)
150 {
151 	assert(p != NULL);
152 
153 	xstring_renew(p->fields[key]);
154 	fputs(str, p->fields[key]->fp);
155 	fflush(p->fields[key]->fp);
156 	return (EPKG_OK);
157 }
158 
159 const char *
pkg_plugin_get(struct pkg_plugin * p,pkg_plugin_key key)160 pkg_plugin_get(struct pkg_plugin *p, pkg_plugin_key key)
161 {
162 	assert(p != NULL);
163 
164 	if (p->fields[key] == NULL)
165 		return (NULL);
166 
167 	return (p->fields[key]->buf);
168 }
169 
170 int
pkg_plugin_conf_add(struct pkg_plugin * p,pkg_object_t type,const char * key,const char * def)171 pkg_plugin_conf_add(struct pkg_plugin *p, pkg_object_t type, const char *key,
172     const char *def)
173 {
174 	ucl_object_t *o = NULL;
175 	const char *walk, *buf, *value, *k;
176 	k = NULL;
177 
178 	switch (type) {
179 	case PKG_STRING:
180 		o = ucl_object_fromstring_common(def, 0, UCL_STRING_TRIM);
181 		break;
182 	case PKG_BOOL:
183 		o = ucl_object_fromstring_common(def, 0, UCL_STRING_PARSE_BOOLEAN);
184 		if (o->type != UCL_BOOLEAN) {
185 			ucl_object_unref(o);
186 			return (EPKG_FATAL);
187 		}
188 		break;
189 	case PKG_INT:
190 		o = ucl_object_fromstring_common(def, 0, UCL_STRING_PARSE_INT);
191 		if (o->type != UCL_INT) {
192 			ucl_object_unref(o);
193 			return (EPKG_FATAL);
194 		}
195 		break;
196 	case PKG_OBJECT:
197 		walk = buf = def;
198 		while ((buf = strchr(buf, ',')) != NULL) {
199 			k = walk;
200 			value = walk;
201 			while (*value != ',') {
202 				if (*value == '=')
203 					break;
204 				value++;
205 			}
206 			if (o == NULL)
207 				o = ucl_object_typed_new(UCL_OBJECT);
208 			ucl_object_insert_key(o,
209 			    ucl_object_fromstring_common(value + 1, buf - value - 1, UCL_STRING_TRIM),
210 			    k, value - k, false);
211 			buf++;
212 			walk = buf;
213 		}
214 		k = walk;
215 		value = walk;
216 		while (*value != '\0') {
217 			if (*value == '=')
218 				break;
219 			value++;
220 		}
221 		if (o == NULL)
222 			o = ucl_object_typed_new(UCL_OBJECT);
223 		ucl_object_insert_key(o,
224 		    ucl_object_fromstring_common(value + 1, strlen(value + 1), UCL_STRING_TRIM),
225 		    k, value - k, false);
226 		break;
227 	case PKG_ARRAY:
228 		walk = buf = def;
229 		while ((buf = strchr(buf, ',')) != NULL) {
230 			if (o == NULL)
231 				o = ucl_object_typed_new(UCL_ARRAY);
232 			ucl_array_append(o,
233 					ucl_object_fromstring_common(walk, buf - walk, UCL_STRING_TRIM));
234 			buf++;
235 			walk = buf;
236 		}
237 		if (o == NULL)
238 			o = ucl_object_typed_new(UCL_ARRAY);
239 		ucl_array_append(o,
240 				ucl_object_fromstring_common(walk, strlen(walk), UCL_STRING_TRIM));
241 		break;
242 	default:
243 		break;
244 	}
245 
246 	if (o != NULL)
247 		ucl_object_replace_key(p->conf, o, key, strlen(key), false);
248 
249 	return (EPKG_OK);
250 }
251 
252 int
pkg_plugins(struct pkg_plugin ** plugin)253 pkg_plugins(struct pkg_plugin **plugin)
254 {
255 	if ((*plugin) == NULL)
256 		(*plugin) = plugins;
257 	else
258 		(*plugin) = (*plugin)->next;
259 
260 	if ((*plugin) == NULL)
261 		return (EPKG_END);
262 	else
263 		return (EPKG_OK);
264 }
265 
266 int
pkg_plugins_init(void)267 pkg_plugins_init(void)
268 {
269 	struct pkg_plugin *p = NULL;
270 	char pluginfile[MAXPATHLEN];
271 	const ucl_object_t *obj, *cur;
272 	ucl_object_iter_t it = NULL;
273 	const char *plugdir;
274 	bool plug_enabled = false;
275 	int (*init_func)(struct pkg_plugin *);
276 
277 	plug_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
278 	if (!plug_enabled)
279 		return (EPKG_OK);
280 	/*
281 	 * Discover available plugins
282 	 */
283 	plugdir = pkg_object_string(pkg_config_get("PKG_PLUGINS_DIR"));
284 
285 	obj = pkg_config_get("PLUGINS");
286 	while ((cur = ucl_iterate_object(obj, &it, true))) {
287 		/*
288 		 * Load the plugin
289 		 */
290 		if (cur->type != UCL_STRING)
291 			continue;
292 
293 		snprintf(pluginfile, sizeof(pluginfile), "%s/%s.so", plugdir,
294 		    pkg_object_string(cur));
295 		p = xcalloc(1, sizeof(struct pkg_plugin));
296 		if ((p->lh = dlopen(pluginfile, RTLD_LAZY)) == NULL) {
297 			pkg_emit_error("Loading of plugin '%s' failed: %s",
298 			    pkg_object_string(cur), dlerror());
299 			free(p);
300 			return (EPKG_FATAL);
301 		}
302 		if ((init_func = dlsym(p->lh, "pkg_plugin_init")) == NULL) {
303 			pkg_emit_error("Cannot load init function for plugin '%s'",
304 			     pkg_object_string(cur));
305 			pkg_emit_error("Plugin '%s' will not be loaded: %s",
306 			      pkg_object_string(cur), dlerror());
307 			dlclose(p->lh);
308 			free(p);
309 			return (EPKG_FATAL);
310 		}
311 		p->conf = ucl_object_typed_new(UCL_OBJECT);
312 		pkg_plugin_set(p, PKG_PLUGIN_PLUGINFILE, pluginfile);
313 		if (init_func(p) == EPKG_OK) {
314 			LL_APPEND(plugins, p);
315 		} else {
316 			ucl_object_unref(p->conf);
317 			dlclose(p->lh);
318 			free(p);
319 		}
320 	}
321 
322 	return (EPKG_OK);
323 }
324 
325 int
pkg_plugin_parse(struct pkg_plugin * p)326 pkg_plugin_parse(struct pkg_plugin *p)
327 {
328 	char confpath[MAXPATHLEN];
329 	const char *path;
330 	const char *plugname;
331 	struct ucl_parser *pr;
332 	const ucl_object_t *cur, *o;
333 	ucl_object_t *obj;
334 	ucl_object_iter_t it = NULL;
335 	const char *key;
336 
337 	pr = ucl_parser_new(0);
338 
339 	path = pkg_object_string(pkg_config_get("PLUGINS_CONF_DIR"));
340 	plugname = pkg_plugin_get(p, PKG_PLUGIN_NAME);
341 
342 	snprintf(confpath, sizeof(confpath), "%s/%s.conf", path, plugname);
343 
344 	if (!ucl_parser_add_file(pr, confpath)) {
345 		if (errno == ENOENT) {
346 			ucl_parser_free(pr);
347 			p->parsed = true;
348 			return (EPKG_OK);
349 		}
350 		pkg_emit_error("%s\n", ucl_parser_get_error(pr));
351 		ucl_parser_free(pr);
352 
353 		return (EPKG_FATAL);
354 	}
355 
356 	obj = ucl_parser_get_object(pr);
357 
358 	while ((cur = ucl_iterate_object(obj, &it, true))) {
359 		key = ucl_object_key(cur);
360 		o = ucl_object_find_key(p->conf, key);
361 		if (o == NULL)
362 			continue;
363 
364 		if (o->type != cur->type) {
365 			pkg_emit_error("Malformed key %s, ignoring", key);
366 			continue;
367 		}
368 
369 		ucl_object_delete_key(p->conf, key);
370 		ucl_object_insert_key(p->conf, ucl_object_ref(cur), key, strlen(key), false);
371 	}
372 
373 	p->parsed = true;
374 	ucl_object_unref(obj);
375 	ucl_parser_free(pr);
376 
377 	return (EPKG_OK);
378 }
379 
380 void
pkg_plugins_shutdown(void)381 pkg_plugins_shutdown(void)
382 {
383 	struct pkg_plugin *p = NULL;
384 	int (*shutdown_func)(struct pkg_plugin *p);
385 
386 	/*
387 	 * Unload any previously loaded plugins
388 	 */
389 	while (pkg_plugins(&p) != EPKG_END) {
390 		if ((shutdown_func = dlsym(p->lh, "pkg_plugin_shutdown")) != NULL) {
391 			shutdown_func(p);
392 		}
393 		dlclose(p->lh);
394 	}
395 
396 	/*
397 	 * Deallocate memory used by the plugins
398 	 */
399 	pkg_plugin_free();
400 
401 	return;
402 }
403 
404 const pkg_object *
pkg_plugin_conf(struct pkg_plugin * p)405 pkg_plugin_conf(struct pkg_plugin *p)
406 {
407 	return (p->conf);
408 }
409