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