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