/ libpkg / plugins.c
plugins.c
  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  };
 52  
 53  struct pkg_plugin {
 54  	xstring *fields[PLUGIN_NUMFIELDS];
 55  	void *lh;						/* library handle */
 56  	bool parsed;
 57  	struct plugin_hook *hooks[PKG_PLUGIN_HOOK_LAST];
 58  	pkg_object *conf;
 59  	struct pkg_plugin *next;
 60  };
 61  
 62  static struct pkg_plugin *plugins = NULL;
 63  
 64  static int pkg_plugin_free(void);
 65  static void pkg_plugin_hook_free(struct pkg_plugin *p);
 66  static int pkg_plugin_hook_exec(struct pkg_plugin *p, pkg_plugin_hook_t hook, void *data, struct pkgdb *db);
 67  
 68  void *
 69  pkg_plugin_func(struct pkg_plugin *p, const char *func)
 70  {
 71  	return (dlsym(p->lh, func));
 72  }
 73  
 74  static void
 75  pkg_plugin_hook_free(struct pkg_plugin *p)
 76  {
 77  	if (p == NULL)
 78  		return;
 79  
 80  	for (int i = 1; i < PKG_PLUGIN_HOOK_LAST; i++) {
 81  		if (p->hooks[i] != NULL)
 82  			free(p->hooks[i]);
 83  	}
 84  }
 85  
 86  static void
 87  plug_free(struct pkg_plugin *p)
 88  {
 89  	unsigned int i;
 90  
 91  	for (i = 0; i < PLUGIN_NUMFIELDS; i++)
 92  		xstring_free(p->fields[i]);
 93  
 94  	ucl_object_unref(p->conf);
 95  	pkg_plugin_hook_free(p);
 96  	free(p);
 97  }
 98  
 99  static int
100  pkg_plugin_free(void)
101  {
102  	LL_FREE(plugins, plug_free);
103  
104  	return (EPKG_OK);
105  }
106  
107  int
108  pkg_plugin_hook_register(struct pkg_plugin *p, pkg_plugin_hook_t hook, pkg_plugin_callback callback)
109  {
110  	struct plugin_hook *new = NULL;
111  	
112  	assert(p != NULL);
113  	assert(callback != NULL);
114  
115  	if (hook < 1 || hook >= PKG_PLUGIN_HOOK_LAST) {
116  		pkg_emit_error("Invalid hook");
117  		return (EPKG_FATAL);
118  	}
119  	if (p->hooks[hook] != NULL) {
120  		pkg_emit_error("Hook already installed");
121  		return (EPKG_FATAL);
122  	}
123  	new = xcalloc(1, sizeof(struct plugin_hook));
124  	new->hook = hook;
125  	new->callback = callback;
126  
127  	p->hooks[hook] = new;
128  
129  	return (EPKG_OK);
130  }
131  
132  static int
133  pkg_plugin_hook_exec(struct pkg_plugin *p, pkg_plugin_hook_t hook, void *data, struct pkgdb *db)
134  {
135  	assert(p != NULL);
136  
137  	if (p->hooks[hook] != NULL)
138  		p->hooks[hook]->callback(data, db);
139  
140  	return (EPKG_OK);
141  }
142  
143  int
144  pkg_plugins_hook_run(pkg_plugin_hook_t hook, void *data, struct pkgdb *db)
145  {
146  	struct pkg_plugin *p = NULL;
147  
148  	while (pkg_plugins(&p) != EPKG_END)
149  		pkg_plugin_hook_exec(p, hook, data, db);
150  
151  	return (EPKG_OK);
152  }
153  
154  int
155  pkg_plugin_set(struct pkg_plugin *p, pkg_plugin_key key, const char *str)
156  {
157  	assert(p != NULL);
158  
159  	xstring_renew(p->fields[key]);
160  	fputs(str, p->fields[key]->fp);
161  	fflush(p->fields[key]->fp);
162  	return (EPKG_OK);
163  }
164  
165  const char *
166  pkg_plugin_get(struct pkg_plugin *p, pkg_plugin_key key)
167  {
168  	assert(p != NULL);
169  
170  	if (p->fields[key] == NULL)
171  		return (NULL);
172  
173  	return (p->fields[key]->buf);
174  }
175  
176  int
177  pkg_plugin_conf_add(struct pkg_plugin *p, pkg_object_t type, const char *key,
178      const char *def)
179  {
180  	ucl_object_t *o = NULL;
181  	const char *walk, *buf, *value, *k;
182  	k = NULL;
183  
184  	switch (type) {
185  	case PKG_STRING:
186  		o = ucl_object_fromstring_common(def, 0, UCL_STRING_TRIM);
187  		break;
188  	case PKG_BOOL:
189  		o = ucl_object_fromstring_common(def, 0, UCL_STRING_PARSE_BOOLEAN);
190  		if (o->type != UCL_BOOLEAN) {
191  			ucl_object_unref(o);
192  			return (EPKG_FATAL);
193  		}
194  		break;
195  	case PKG_INT:
196  		o = ucl_object_fromstring_common(def, 0, UCL_STRING_PARSE_INT);
197  		if (o->type != UCL_INT) {
198  			ucl_object_unref(o);
199  			return (EPKG_FATAL);
200  		}
201  		break;
202  	case PKG_OBJECT:
203  		walk = buf = def;
204  		while ((buf = strchr(buf, ',')) != NULL) {
205  			k = walk;
206  			value = walk;
207  			while (*value != ',') {
208  				if (*value == '=')
209  					break;
210  				value++;
211  			}
212  			if (o == NULL)
213  				o = ucl_object_typed_new(UCL_OBJECT);
214  			ucl_object_insert_key(o,
215  			    ucl_object_fromstring_common(value + 1, buf - value - 1, UCL_STRING_TRIM),
216  			    k, value - k, false);
217  			buf++;
218  			walk = buf;
219  		}
220  		k = walk;
221  		value = walk;
222  		while (*value != '\0') {
223  			if (*value == '=')
224  				break;
225  			value++;
226  		}
227  		if (o == NULL)
228  			o = ucl_object_typed_new(UCL_OBJECT);
229  		ucl_object_insert_key(o,
230  		    ucl_object_fromstring_common(value + 1, strlen(value + 1), UCL_STRING_TRIM),
231  		    k, value - k, false);
232  		break;
233  	case PKG_ARRAY:
234  		walk = buf = def;
235  		while ((buf = strchr(buf, ',')) != NULL) {
236  			if (o == NULL)
237  				o = ucl_object_typed_new(UCL_ARRAY);
238  			ucl_array_append(o,
239  					ucl_object_fromstring_common(walk, buf - walk, UCL_STRING_TRIM));
240  			buf++;
241  			walk = buf;
242  		}
243  		if (o == NULL)
244  			o = ucl_object_typed_new(UCL_ARRAY);
245  		ucl_array_append(o,
246  				ucl_object_fromstring_common(walk, strlen(walk), UCL_STRING_TRIM));
247  		break;
248  	default:
249  		break;
250  	}
251  
252  	if (o != NULL)
253  		ucl_object_replace_key(p->conf, o, key, strlen(key), false);
254  
255  	return (EPKG_OK);
256  }
257  
258  int
259  pkg_plugins(struct pkg_plugin **plugin)
260  {
261  	if ((*plugin) == NULL)
262  		(*plugin) = plugins;
263  	else
264  		(*plugin) = (*plugin)->next;
265  
266  	if ((*plugin) == NULL)
267  		return (EPKG_END);
268  	else
269  		return (EPKG_OK);
270  }
271  
272  int
273  pkg_plugins_init(void)
274  {
275  	struct pkg_plugin *p = NULL;
276  	char pluginfile[MAXPATHLEN];
277  	const ucl_object_t *obj, *cur;
278  	ucl_object_iter_t it = NULL;
279  	const char *plugdir;
280  	bool plug_enabled = false;
281  	int (*init_func)(struct pkg_plugin *);
282  
283  	plug_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
284  	if (!plug_enabled)
285  		return (EPKG_OK);
286  	/*
287  	 * Discover available plugins
288  	 */
289  	plugdir = pkg_object_string(pkg_config_get("PKG_PLUGINS_DIR"));
290  
291  	obj = pkg_config_get("PLUGINS");
292  	while ((cur = ucl_iterate_object(obj, &it, true))) {
293  		/*
294  		 * Load the plugin
295  		 */
296  		if (cur->type != UCL_STRING)
297  			continue;
298  
299  		snprintf(pluginfile, sizeof(pluginfile), "%s/%s.so", plugdir,
300  		    pkg_object_string(cur));
301  		p = xcalloc(1, sizeof(struct pkg_plugin));
302  		if ((p->lh = dlopen(pluginfile, RTLD_LAZY)) == NULL) {
303  			pkg_emit_error("Loading of plugin '%s' failed: %s",
304  			    pkg_object_string(cur), dlerror());
305  			free(p);
306  			return (EPKG_FATAL);
307  		}
308  		if ((init_func = dlsym(p->lh, "pkg_plugin_init")) == NULL) {
309  			pkg_emit_error("Cannot load init function for plugin '%s'",
310  			     pkg_object_string(cur));
311  			pkg_emit_error("Plugin '%s' will not be loaded: %s",
312  			      pkg_object_string(cur), dlerror());
313  			dlclose(p->lh);
314  			free(p);
315  			return (EPKG_FATAL);
316  		}
317  		p->conf = ucl_object_typed_new(UCL_OBJECT);
318  		pkg_plugin_set(p, PKG_PLUGIN_PLUGINFILE, pluginfile);
319  		if (init_func(p) == EPKG_OK) {
320  			LL_APPEND(plugins, p);
321  		} else {
322  			ucl_object_unref(p->conf);
323  			dlclose(p->lh);
324  			free(p);
325  		}
326  	}
327  
328  	return (EPKG_OK);
329  }
330  
331  int
332  pkg_plugin_parse(struct pkg_plugin *p)
333  {
334  	char confpath[MAXPATHLEN];
335  	const char *path;
336  	const char *plugname;
337  	struct ucl_parser *pr;
338  	const ucl_object_t *cur, *o;
339  	ucl_object_t *obj;
340  	ucl_object_iter_t it = NULL;
341  	const char *key;
342  
343  	pr = ucl_parser_new(0);
344  
345  	path = pkg_object_string(pkg_config_get("PLUGINS_CONF_DIR"));
346  	plugname = pkg_plugin_get(p, PKG_PLUGIN_NAME);
347  
348  	snprintf(confpath, sizeof(confpath), "%s/%s.conf", path, plugname);
349  
350  	if (!ucl_parser_add_file(pr, confpath)) {
351  		if (errno == ENOENT) {
352  			ucl_parser_free(pr);
353  			p->parsed = true;
354  			return (EPKG_OK);
355  		}
356  		pkg_emit_error("%s\n", ucl_parser_get_error(pr));
357  		ucl_parser_free(pr);
358  
359  		return (EPKG_FATAL);
360  	}
361  
362  	obj = ucl_parser_get_object(pr);
363  
364  	while ((cur = ucl_iterate_object(obj, &it, true))) {
365  		key = ucl_object_key(cur);
366  		o = ucl_object_find_key(p->conf, key);
367  		if (o == NULL)
368  			continue;
369  
370  		if (o->type != cur->type) {
371  			pkg_emit_error("Malformed key %s, ignoring", key);
372  			continue;
373  		}
374  
375  		ucl_object_delete_key(p->conf, key);
376  		ucl_object_insert_key(p->conf, ucl_object_ref(cur), key, strlen(key), false);
377  	}
378  
379  	p->parsed = true;
380  	ucl_object_unref(obj);
381  	ucl_parser_free(pr);
382  
383  	return (EPKG_OK);
384  }
385  
386  void
387  pkg_plugins_shutdown(void)
388  {
389  	struct pkg_plugin *p = NULL;
390  	int (*shutdown_func)(struct pkg_plugin *p);
391  
392  	/*
393  	 * Unload any previously loaded plugins
394  	 */
395  	while (pkg_plugins(&p) != EPKG_END) {
396  		if ((shutdown_func = dlsym(p->lh, "pkg_plugin_shutdown")) != NULL) {
397  			shutdown_func(p);
398  		}
399  		dlclose(p->lh);
400  	}
401  
402  	/*
403  	 * Deallocate memory used by the plugins
404  	 */
405  	pkg_plugin_free();
406  
407  	return;
408  }
409  
410  const pkg_object *
411  pkg_plugin_conf(struct pkg_plugin *p)
412  {
413  	return (p->conf);
414  }