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