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 }