//roardl.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2015 * * This file is part of libroar a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * libroar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * NOTE for everyone want's to change something and send patches: * read README and HACKING! There a addition information on * the license of this document you need to read before you send * any patches. * * NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc * or libpulse*: * The libs libroaresd, libroararts and libroarpulse link this lib * and are therefore GPL. Because of this it may be illigal to use * them with any software that uses libesd, libartsc or libpulse*. */ #include "libroar.h" #ifdef ROAR_HAVE_H_DIRENT #include #endif #if defined(ROAR_HAVE_H_DLFCN) && !defined(RTLD_NEXT) #define RTLD_NEXT ((void *) -1L) #endif #define RTF_RA_INITED 0x0001 #define RTF_APPSCHED_INITED 0x0002 #define RTF_APPSCHED_FREED 0x0004 struct roar_dl_lhandle { size_t refc; // currently unused. int flags; char * libname; // only used for ROAR_DL_FLAG_STATIC. struct roar_dl_librarypara * para; struct roar_dl_libraryinst * lib; struct { struct roar_error_state error_state; void * global_data; void * global_data_state; struct roar_notify_core * notifycore; struct roar_vio_calls * stdvios[3]; } context; unsigned int runtime_flags; #if defined(ROAR_HAVE_H_DLFCN) void * handle; #elif defined(ROAR_TARGET_WIN32) HMODULE handle; #endif }; // TODO: this should be removed on next SONAME change. static struct roar_dl_lhandle * __currently_inited = NULL; static int __currently_inited_fn = -1; struct roar_dl_librarypara * roar_dl_para_new(const char * args, void * binargv, const char * appname, const char * abiversion) { struct roar_dl_librarypara * ret = roar_mm_malloc(sizeof(struct roar_dl_librarypara)); ssize_t argc; int err; if ( ret == NULL ) return NULL; memset(ret, 0, sizeof(struct roar_dl_librarypara)); ret->version = ROAR_DL_LIBPARA_VERSION; ret->len = sizeof(struct roar_dl_librarypara); ret->refc = 1; ret->argc = 0; ret->argv = NULL; ret->args_store = NULL; ret->binargv = binargv; ret->appname = appname; ret->abiversion = abiversion; if ( args != NULL ) { ret->args_store = roar_mm_strdup(args); if ( ret->args_store == NULL ) { roar_mm_free_noerror(ret); return NULL; } argc = roar_keyval_split(&(ret->argv), ret->args_store, NULL, NULL, 1); if ( argc == -1 ) { err = roar_error; roar_mm_free(ret->args_store); roar_mm_free(ret); roar_error = err; return NULL; } ret->argc = argc; } return ret; } int roar_dl_para_ref (struct roar_dl_librarypara * para) { if ( para == NULL ) { ROAR_DBG("roar_dl_para_ref(para=%p) = -1 // error=FAULT", para); roar_err_set(ROAR_ERROR_FAULT); return -1; } para->refc++; ROAR_DBG("roar_dl_para_ref(para=%p) = 0", para); return 0; } int roar_dl_para_unref (struct roar_dl_librarypara * para) { if ( para == NULL ) { ROAR_DBG("roar_dl_para_unref(para=%p) = -1 // error=FAULT", para); roar_err_set(ROAR_ERROR_FAULT); return -1; } para->refc--; if ( para->refc ) { ROAR_DBG("roar_dl_para_unref(para=%p) = 0", para); return 0; } if ( para->notifycore != NULL ) roar_notify_core_unref(para->notifycore); if ( para->args_store != NULL ) { roar_mm_free(para->args_store); roar_mm_free(para->argv); } roar_mm_free(para); ROAR_DBG("roar_dl_para_unref(para=%p) = 0", para); return 0; } int roar_dl_para_check_version (struct roar_dl_librarypara * para, const char * appname, const char * abiversion) { if ( para == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } // check if both appnames are NULL or non-NULL. if ( (para->appname == NULL && appname != NULL) || (para->appname != NULL && appname == NULL) ) { roar_err_set(ROAR_ERROR_BADHOST); return -1; } // check if the appname matches if given. if ( para->appname != NULL && !!strcmp(para->appname, appname) ) { roar_err_set(ROAR_ERROR_BADHOST); return -1; } // check if both ABI versions are NULL or non-NULL. if ( (para->abiversion == NULL && abiversion != NULL) || (para->abiversion != NULL && abiversion == NULL) ) { roar_err_set(ROAR_ERROR_BADVERSION); return -1; } // check if the ABI versions matches if given. if ( para->abiversion != NULL && !!strcmp(para->abiversion, abiversion) ) { roar_err_set(ROAR_ERROR_BADVERSION); return -1; } return 0; } #if defined(ROAR_HAVE_H_DLFCN) static void * _roardl2ldl (struct roar_dl_lhandle * lhandle) { ROAR_DBG("_roardl2ldl(lhandle=%p) = ?", lhandle); if ( (void*)lhandle < (void*)128 ) { switch ((int)(void*)lhandle) { case (int)(void*)ROAR_DL_HANDLE_DEFAULT: case (int)(void*)ROAR_DL_HANDLE_LIBROAR: case (int)(void*)ROAR_DL_HANDLE_APPLICATION: ROAR_DBG("_roardl2ldl(lhandle=%p) = %p", lhandle, (void*)RTLD_DEFAULT); return RTLD_DEFAULT; break; case (int)(void*)ROAR_DL_HANDLE_NEXT: ROAR_DBG("_roardl2ldl(lhandle=%p) = %p", lhandle, (void*)RTLD_NEXT); return RTLD_NEXT; break; } } ROAR_DBG("_roardl2ldl(lhandle=%p) = %p", lhandle, (void*)(lhandle->handle)); return lhandle->handle; } #elif defined(ROAR_TARGET_WIN32) static HMODULE _roardl2winhandle(struct roar_dl_lhandle * lhandle) { if ( (void*)lhandle < (void*)128 ) { return NULL; } return lhandle->handle; } #endif #ifdef ROAR_HAVE_H_DIRENT // pvn = prefix, host vendor, host name static struct roar_dl_lhandle * _load_from_path_pvn(const char * name, int flags, int ra_init, struct roar_dl_librarypara * para, const char * prefix, const char * hostvendor, const char * hostname) { struct roar_dl_lhandle * ret = NULL; int i, j; char path[1024]; const char * path_format[] = {"%s/%s/%s", "%s/%s/universal", "%s/universal/universal"}; DIR * dir; struct dirent * dirent; char file[1024]; const char * file_format[] = {"%s/%s/%s", "%s/%s/%s" ROAR_SHARED_SUFFIX, "%s/%s/lib%s" ROAR_SHARED_SUFFIX}; // cont->handle[idx] = roar_dl_open(name, ROAR_DL_FLAG_DEFAULTS, 1, para); //#vars: $PREFIX_PLUGINS, $hostvendor, $hostname, $name //#search order: $PREFIX_PLUGINS/{$hostvendor/{$hostname,universal},universal/universal}/*/{,lib}$name.so for (i = 0; i < 3; i++) { snprintf(path, sizeof(path), path_format[i], prefix, hostvendor, hostname); dir = opendir(path); if ( dir == NULL ) continue; while ((dirent = readdir(dir)) != NULL) { if ( !strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..") ) continue; for (j = 0; j < 3; j++) { snprintf(file, sizeof(file), file_format[j], path, dirent->d_name, name); ret = roar_dl_open(file, flags, ra_init, para); if ( ret != NULL ) { closedir(dir); return ret; } } } closedir(dir); } return NULL; } static struct roar_dl_lhandle * _load_from_path(const char * name, int flags, int ra_init, struct roar_dl_librarypara * para) { struct roar_dl_lhandle * ret = NULL; char * host = NULL; char * hostvendor_buffer = NULL; char * hostvendor = NULL; char * hostname = NULL; size_t hostvendor_len; char * c, * d; if ( para != NULL && para->appname != NULL && para->appname[0] != 0 ) { host = roar_mm_strdup(para->appname); // if we are out of memory we will likely not pass the rest, so just return in error. if ( host == NULL ) return NULL; hostname = host; c = strstr(host, "<"); if ( c != NULL ) { while (c[-1] == ' ') c--; *c = 0; c++; while (*c == ' ' || *c == '<') c++; if ( *c ) { d = strstr(c, ">"); if ( d != NULL ) *d = 0; d = strstr(c, "/"); if ( d != NULL ) { *d = '-'; hostvendor = c; } else { hostvendor_len = roar_mm_strlen(c) + 1 /* tailing \0 */ + 6 /* "unreg-" */; hostvendor_buffer = roar_mm_malloc(hostvendor_len); // see above if ( hostvendor_buffer == NULL ) { roar_mm_free(host); return NULL; } roar_mm_strlcpy(hostvendor_buffer, "unreg-", hostvendor_len); roar_mm_strlcat(hostvendor_buffer, c, hostvendor_len); hostvendor = hostvendor_buffer; } } } } if ( hostvendor == NULL ) hostvendor = "universal"; if ( hostname == NULL ) hostname = "universal"; ret = _load_from_path_pvn(name, flags, ra_init, para, ROAR_PREFIX_PLUGINS, hostvendor, hostname); if ( host != NULL ) roar_mm_free(host); if ( hostvendor_buffer != NULL ) roar_mm_free(hostvendor_buffer); return ret; } #endif static struct roar_dl_lhandle * _roar_dl_open_pluginpath(const char * filename, int flags, int ra_init, struct roar_dl_librarypara * para) { flags |= ROAR_DL_FLAG_PLUGINPATH; flags -= ROAR_DL_FLAG_PLUGINPATH; #ifdef ROAR_HAVE_H_DIRENT // do normal open for everything with a path name. if ( strstr(filename, "/") != NULL ) return roar_dl_open(filename, flags, ra_init, para); return _load_from_path(filename, flags, ra_init, para); #else // fall back to system open function. return roar_dl_open(filename, flags, ra_init, para); #endif } struct roar_dl_lhandle * roar_dl_open(const char * filename, int flags, int ra_init, struct roar_dl_librarypara * para) { struct roar_dl_lhandle * ret = NULL; #if defined(ROAR_HAVE_H_DLFCN) #if defined(RTLD_DEEPBIND) && 0 // FIXME: This is currently disabled. See #296. int libdl_flags = RTLD_DEEPBIND; #else int libdl_flags = 0; #endif #endif int err; switch (flags) { case ROAR_DL_FLAG_DEFAULTS: flags = ROAR_DL_FLAG_NONE; break; case ROAR_DL_FLAG_PLUGIN: flags = ROAR_DL_FLAG_PLUGINPATH; break; } if ( flags & ROAR_DL_FLAG_PLUGINPATH ) return _roar_dl_open_pluginpath(filename, flags, ra_init, para); #if defined(ROAR_HAVE_H_DLFCN) if ( flags & ROAR_DL_FLAG_LAZY ) { libdl_flags |= RTLD_LAZY; } else { libdl_flags |= RTLD_NOW; } #endif if ( (ret = roar_mm_malloc(sizeof(struct roar_dl_lhandle))) == NULL ) return NULL; memset(ret, 0, sizeof(struct roar_dl_lhandle)); roar_err_initstore(&(ret->context.error_state)); ret->flags = flags; ret->refc = 1; if ( flags & ROAR_DL_FLAG_STATIC ) { if ( filename == NULL ) { ret->libname = NULL; } else { ret->libname = roar_mm_strdup(filename); } } else { #if defined(ROAR_HAVE_H_DLFCN) ret->handle = dlopen(filename, libdl_flags); if ( ret->handle == NULL ) { ROAR_DBG("roar_dl_open(filename='%s', flags=%i, ra_init=%i, para=%p): Can not load library: %s", filename, flags, ra_init, para, roar_dl_errstr(ret)); roar_mm_free(ret); return NULL; } #elif defined(ROAR_TARGET_WIN32) ret->handle = LoadLibrary(filename); if ( ret->handle == NULL ) { roar_mm_free(ret); return NULL; } #else roar_mm_free(ret); roar_err_set(ROAR_ERROR_NOSYS); return NULL; #endif } ret->para = para; if ( roar_vio_ref(roar_stdin) == 0 ) ret->context.stdvios[0] = roar_stdin; if ( roar_vio_ref(roar_stdout) == 0 ) ret->context.stdvios[1] = roar_stdout; if ( roar_vio_ref(roar_stderr) == 0 ) ret->context.stdvios[2] = roar_stderr; if ( ra_init ) { if ( roar_dl_ra_init(ret, NULL, para) == -1 ) { err = roar_error; ROAR_WARN("roar_dl_open(filename='%s', flags=%i, ra_init=%i, para=%p): Can not init RA lib: %s", filename, flags, ra_init, para, roar_error2str(err)); #if defined(ROAR_HAVE_H_DLFCN) if ( ret->handle != NULL ) dlclose(ret->handle); #elif defined(ROAR_TARGET_WIN32) if ( ret->handle != NULL ) FreeLibrary(ret->handle); #endif roar_vio_unref(ret->context.stdvios[0]); roar_vio_unref(ret->context.stdvios[1]); roar_vio_unref(ret->context.stdvios[2]); roar_mm_free(ret); roar_error = err; return NULL; } } if ( para != NULL ) roar_dl_para_ref(para); return ret; } int roar_dl_ref (struct roar_dl_lhandle * lhandle) { if ( (void*)lhandle < (void*)128 ) { ROAR_DBG("roar_dl_ref(lhandle=%p) = -1 // error=BADFH", lhandle); roar_err_set(ROAR_ERROR_BADFH); return -1; } lhandle->refc++; ROAR_DBG("roar_dl_ref(lhandle=%p) = 0", lhandle); return 0; } int roar_dl_unref (struct roar_dl_lhandle * lhandle) { int ret = -1; if ( (void*)lhandle < (void*)128 ) { ROAR_DBG("roar_dl_unref(lhandle=%p) = -1 // error=BADFH", lhandle); roar_err_set(ROAR_ERROR_BADFH); return -1; } lhandle->refc--; if ( lhandle->refc ) { ROAR_DBG("roar_dl_unref(lhandle=%p) = 0", lhandle); return 0; } roar_dl_unregister_fn(lhandle); if ( lhandle->lib != NULL && lhandle->lib->unload != NULL ) { roar_dl_context_restore(lhandle); lhandle->lib->unload(lhandle->para, lhandle->lib); roar_dl_context_store(lhandle); } #if defined(ROAR_HAVE_H_DLFCN) if ( lhandle->handle == NULL ) { ret = 0; } else { ret = dlclose(_roardl2ldl(lhandle)); } #elif defined(ROAR_TARGET_WIN32) if ( lhandle->handle == NULL ) { ret = 0; } else { if ( FreeLibrary(_roardl2winhandle(lhandle)) ) { ret = 0; } else { ret = -1; } } #else ret = -1; #endif roar_vio_unref(lhandle->context.stdvios[0]); roar_vio_unref(lhandle->context.stdvios[1]); roar_vio_unref(lhandle->context.stdvios[2]); if ( lhandle->context.global_data != NULL ) roar_mm_free(lhandle->context.global_data); if ( lhandle->para != NULL ) roar_dl_para_unref(lhandle->para); if ( lhandle->libname != NULL ) roar_mm_free(lhandle->libname); roar_mm_free(lhandle); ROAR_DBG("roar_dl_unref(lhandle=%p) = %i", lhandle, ret); return ret; } void * roar_dl_getsym(struct roar_dl_lhandle * lhandle, const char * sym, int type) { #if defined(ROAR_HAVE_H_DLFCN) void * ret = dlsym(_roardl2ldl(lhandle), sym); (void)type; ROAR_DBG("roar_dl_getsym(lhandle=%p, sym='%s', type=%i): ret=%p, errno=%s(%i), dlerror()='%s'", lhandle, sym, type, ret, strerror(errno), errno, dlerror()); ROAR_DBG("roar_dl_getsym(lhandle=%p, sym='%s', type=%i) = %p", lhandle, sym, type, ret); return ret; #elif defined(ROAR_TARGET_WIN32) FARPROC ret = GetProcAddress(_roardl2winhandle(lhandle), sym); return (void*)ret; #else ROAR_DBG("roar_dl_getsym(lhandle=%p, sym='%s', type=%i) = NULL // errno=NOSYS", lhandle, sym, type, ret); roar_err_set(ROAR_ERROR_NOSYS); return NULL; #endif } int roar_dl_ra_init(struct roar_dl_lhandle * lhandle, const char * prefix, struct roar_dl_librarypara * para) { struct roar_dl_lhandle * old_init; int old_fn; #define _SUFFIX "_roaraudio_library_init" char name[80] = _SUFFIX; struct roar_dl_libraryinst * (*func)(struct roar_dl_librarypara * para); struct roar_dl_libraryinst * lib; struct roar_dl_lhandle * getsymhandle = lhandle; void * global_data_state = NULL; int i; if ( (void*)lhandle < (void*)128 ) { if ( prefix == NULL ) return -1; } else { if ( lhandle->runtime_flags & RTF_RA_INITED ) return 0; if ( prefix == NULL ) prefix = lhandle->libname; if ( para == NULL ) para = lhandle->para; if ( lhandle->flags & ROAR_DL_FLAG_STATIC ) getsymhandle = ROAR_DL_HANDLE_DEFAULT; } if ( prefix != NULL ) { roar_mm_strscpy(name, "_"); roar_mm_strscat(name, prefix); roar_mm_strscat(name, _SUFFIX); } ROAR_DBG("roar_dl_ra_init(lhandle=%p, prefix='%s'): name='%s'", lhandle, prefix, name); if ( (func = roar_dl_getsym(getsymhandle, name, -1)) == NULL ) { ROAR_DBG("roar_dl_ra_init(lhandle=%p, prefix='%s') = -1", lhandle, prefix); return -1; } ROAR_DBG("roar_dl_ra_init(lhandle=%p, prefix='%s'): func=%p", lhandle, prefix, func); roar_err_set(ROAR_ERROR_BADLIB); // set a default error in case it returns NULL but not set an error code. lib = func(para); if ( lib == NULL ) return -1; if ( lib->version != ROAR_DL_LIBINST_VERSION ) return -1; if ( sizeof(struct roar_dl_libraryinst) > lib->len ) return -1; if ( lib->host_appname != NULL || lib->host_abiversion != NULL ) { // check for correct host. if ( para == NULL ) { roar_err_set(ROAR_ERROR_INVAL); return -1; } if ( roar_dl_para_check_version(para, lib->host_appname, lib->host_abiversion) == -1 ) return -1; } if ( (lib->libdep == NULL && lib->libdep_len) || (lib->libdep != NULL && !lib->libdep_len) ) { roar_err_set(ROAR_ERROR_BADLIB); return -1; } if ( lib->libdep != NULL && lib->libdep_len ) { // dynamic loader infos are currently not supported. roar_err_set(ROAR_ERROR_NOTSUP); return -1; } if ( !((void*)lhandle < (void*)128) ) { lhandle->lib = lib; if ( lib->global_data_len ) { lhandle->context.global_data = roar_mm_malloc(lib->global_data_len); if ( lhandle->context.global_data == NULL ) return -1; if ( lib->global_data_init == NULL ) { memset(lhandle->context.global_data, 0, lib->global_data_len); } else { memcpy(lhandle->context.global_data, lib->global_data_init, lib->global_data_len); } } } if ( lib->global_data_pointer != NULL ) { global_data_state = *(lib->global_data_pointer); if ( (void*)lhandle < (void*)128 ) { *(lib->global_data_pointer) = lib->global_data_init; } else { *(lib->global_data_pointer) = lhandle->context.global_data; } } roar_dl_context_restore(lhandle); old_init = __currently_inited; old_fn = __currently_inited_fn; __currently_inited = lhandle; for (i = 0; i < ROAR_DL_FN_MAX; i++) { __currently_inited_fn = i; if ( lib->func[i] != NULL ) lib->func[i](para, lib); } __currently_inited_fn = old_fn; __currently_inited = old_init; roar_dl_context_store(lhandle); if ( lib->global_data_pointer != NULL ) { if ( !((void*)lhandle < (void*)128) ) { *(lib->global_data_pointer) = global_data_state; } } if ( !((void*)lhandle < (void*)128) ) lhandle->runtime_flags |= RTF_RA_INITED; return 0; } const char * roar_dl_errstr(struct roar_dl_lhandle * lhandle) { #if defined(ROAR_HAVE_H_DLFCN) (void)lhandle; return dlerror(); #elif defined(ROAR_TARGET_WIN32) roar_err_from_errno(); return roar_error2str(roar_error); #else return NULL; #endif } struct roar_dl_librarypara * roar_dl_getpara(struct roar_dl_lhandle * lhandle) { if ( (void*)lhandle < (void*)128 ) { roar_err_set(ROAR_ERROR_NOTSUP); return NULL; } if ( lhandle->para == NULL ) { roar_err_set(ROAR_ERROR_NOENT); return NULL; } if ( roar_dl_para_ref(lhandle->para) == -1 ) return NULL; return lhandle->para; } const struct roar_dl_libraryname * roar_dl_getlibname(struct roar_dl_lhandle * lhandle) { if ( (void*)lhandle < (void*)128 ) { roar_err_set(ROAR_ERROR_NOTSUP); return NULL; } if ( lhandle->lib == NULL ) { roar_err_set(ROAR_ERROR_BADLIB); return NULL; } if ( lhandle->lib->libname == NULL ) { roar_err_set(ROAR_ERROR_NOENT); return NULL; } if ( lhandle->lib->libname->version != ROAR_DL_LIBNAME_VERSION ) { roar_err_set(ROAR_ERROR_BADVERSION); return NULL; } if ( lhandle->lib->libname->len != sizeof(struct roar_dl_libraryname) ) { roar_err_set(ROAR_ERROR_HOLE); return NULL; } return lhandle->lib->libname; } static inline void __swap_stdvios(struct roar_dl_lhandle * lhandle) { struct roar_vio_calls * vio; vio = roar_stdin; roar_stdin = lhandle->context.stdvios[0]; lhandle->context.stdvios[0] = vio; vio = roar_stdout; roar_stdout = lhandle->context.stdvios[1]; lhandle->context.stdvios[1] = vio; vio = roar_stderr; roar_stderr = lhandle->context.stdvios[2]; lhandle->context.stdvios[2] = vio; } int roar_dl_context_restore(struct roar_dl_lhandle * lhandle) { struct roar_error_state error_state; ROAR_DBG("roar_dl_context_restore(lhandle=%p) = ?", lhandle); if ( (void*)lhandle < (void*)128 ) { ROAR_DBG("roar_dl_context_restore(lhandle=%p) = -1 // errno=NOTSUP", lhandle); roar_err_set(ROAR_ERROR_NOTSUP); return -1; } roar_err_store(&error_state); roar_err_restore(&(lhandle->context.error_state)); lhandle->context.error_state = error_state; __swap_stdvios(lhandle); if ( lhandle->lib->global_data_pointer != NULL ) { ROAR_DBG("roar_dl_context_restore(lhandle=%p): gptr(%p): %p -> %p", lhandle, lhandle->lib->global_data_pointer, *(lhandle->lib->global_data_pointer), lhandle->context.global_data); lhandle->context.global_data_state = *(lhandle->lib->global_data_pointer); *(lhandle->lib->global_data_pointer) = lhandle->context.global_data; } if ( lhandle->para != NULL && lhandle->para->notifycore != NULL ) lhandle->context.notifycore = roar_notify_core_swap_global(lhandle->para->notifycore); ROAR_DBG("roar_dl_context_restore(lhandle=%p) = 0", lhandle); return 0; } int roar_dl_context_store(struct roar_dl_lhandle * lhandle) { struct roar_error_state error_state; ROAR_DBG("roar_dl_context_store(lhandle=%p) = ?", lhandle); if ( (void*)lhandle < (void*)128 ) { ROAR_DBG("roar_dl_context_store(lhandle=%p) = -1 // errno=NOTSUP", lhandle); roar_err_set(ROAR_ERROR_NOTSUP); return -1; } if ( lhandle->para != NULL && lhandle->para->notifycore != NULL ) { roar_notify_core_unref(roar_notify_core_swap_global(lhandle->context.notifycore)); roar_notify_core_unref(lhandle->context.notifycore); lhandle->context.notifycore = NULL; } if ( lhandle->lib->global_data_pointer != NULL ) { ROAR_DBG("roar_dl_context_store(lhandle=%p): gptr(%p): %p -> %p", lhandle, lhandle->lib->global_data_pointer, *(lhandle->lib->global_data_pointer), lhandle->context.global_data_state); *(lhandle->lib->global_data_pointer) = lhandle->context.global_data_state; } __swap_stdvios(lhandle); roar_err_store(&error_state); roar_err_restore(&(lhandle->context.error_state)); lhandle->context.error_state = error_state; ROAR_DBG("roar_dl_context_store(lhandle=%p) = 0", lhandle); return 0; } int roar_dl_appsched_trigger__handle_about(struct roar_dl_lhandle * lhandle) { libroar_dl_service_apitype(roar_service_about) api; int ret, err; if ( roar_dl_service_get_api(NULL, ROAR_SERVICE_ABOUT_NAME, ROAR_SERVICE_ABOUT_ABI, &api) == -1 ) return -1; ret = libroar_dl_service_run_func(api, show, int, roar_dl_getlibname(lhandle)); err = roar_error; libroar_dl_service_free_api(api); roar_error = err; return ret; } int roar_dl_appsched_trigger__handle_help(struct roar_dl_lhandle * lhandle) { libroar_dl_service_apitype(roar_service_help) api; int ret, err; if ( roar_dl_service_get_api(NULL, ROAR_SERVICE_HELP_NAME, ROAR_SERVICE_HELP_ABI, &api) == -1 ) return -1; ret = libroar_dl_service_run_func(api, show, int, roar_dl_getlibname(lhandle), NULL); err = roar_error; libroar_dl_service_free_api(api); roar_error = err; return ret; } int roar_dl_appsched_trigger__handle_preferences(struct roar_dl_lhandle * lhandle) { roar_err_set(ROAR_ERROR_NOSYS); return -1; } int roar_dl_appsched_trigger(struct roar_dl_lhandle * lhandle, enum roar_dl_appsched_trigger trigger) { int (*func) (struct roar_dl_librarypara * para) = NULL; int ret; if ( (void*)lhandle < (void*)128 ) { roar_err_set(ROAR_ERROR_NOTSUP); return -1; } if ( lhandle->lib == NULL ) { roar_err_set(ROAR_ERROR_TYPEMM); return -1; } if ( lhandle->lib->appsched == NULL ) { roar_err_set(ROAR_ERROR_NOENT); return -1; } switch (trigger) { #define _trig(lname,uname) \ case ROAR_DL_APPSCHED_ ## uname : \ func = lhandle->lib->appsched->lname; \ break; _trig(init, INIT); _trig(free, FREE); _trig(update, UPDATE); _trig(tick, TICK); _trig(wait, WAIT); // not yet supported by struct roar_dl_appsched. case ROAR_DL_APPSCHED_ABOUT: case ROAR_DL_APPSCHED_HELP: case ROAR_DL_APPSCHED_PREFERENCES: func = NULL; break; // use ifndef here so warnings of unhandled enum values will be shown in DEBUG mode. #ifndef DEBUG default: roar_err_set(ROAR_ERROR_BADRQC); return -1; break; #endif } if ( func == NULL ) { if ( trigger == ROAR_DL_APPSCHED_INIT ) { lhandle->runtime_flags |= RTF_APPSCHED_INITED|RTF_APPSCHED_FREED; lhandle->runtime_flags -= RTF_APPSCHED_FREED; } else if ( trigger == ROAR_DL_APPSCHED_FREE ) { lhandle->runtime_flags |= RTF_APPSCHED_INITED|RTF_APPSCHED_FREED; lhandle->runtime_flags -= RTF_APPSCHED_INITED; } switch (trigger) { case ROAR_DL_APPSCHED_ABOUT: return roar_dl_appsched_trigger__handle_about(lhandle); break; case ROAR_DL_APPSCHED_HELP: return roar_dl_appsched_trigger__handle_help(lhandle); break; case ROAR_DL_APPSCHED_PREFERENCES: return roar_dl_appsched_trigger__handle_preferences(lhandle); break; default: roar_err_set(ROAR_ERROR_NOENT); return -1; break; } } if ( trigger == ROAR_DL_APPSCHED_INIT ) { if ( lhandle->runtime_flags & RTF_APPSCHED_INITED ) { roar_err_set(ROAR_ERROR_BUSY); return -1; } } else { if ( !(lhandle->runtime_flags & RTF_APPSCHED_INITED) ) { roar_err_set(ROAR_ERROR_BUSY); return -1; } } roar_dl_context_restore(lhandle); ret = func(lhandle->para); roar_dl_context_store(lhandle); if ( trigger == ROAR_DL_APPSCHED_INIT ) { lhandle->runtime_flags |= RTF_APPSCHED_INITED|RTF_APPSCHED_FREED; lhandle->runtime_flags -= RTF_APPSCHED_FREED; } else if ( trigger == ROAR_DL_APPSCHED_FREE ) { lhandle->runtime_flags |= RTF_APPSCHED_INITED|RTF_APPSCHED_FREED; lhandle->runtime_flags -= RTF_APPSCHED_INITED; } return ret; } #define __MAX_FNREGS 24 static struct __fnregs { struct roar_dl_lhandle * lhandle; int subtype; const void * object; size_t objectlen; int version; int options; } __fnrefs[ROAR_DL_FN_MAX][__MAX_FNREGS]; static int __fnreg_check_trigger(const struct __fnregs * handle) { if ( handle->subtype != ROAR_DL_FNREG_SUBTYPE ) return -1; if ( handle->version != ROAR_DL_FNREG_VERSION ) return -1; if ( handle->objectlen != ROAR_DL_FNREG_SIZE ) return -1; return 0; } static void __fnreg_trigger_if_match(const struct roar_dl_fnreg * callback, const struct __fnregs * reg, int fn, enum roar_dl_fnreg_action action) { if ( callback->fn != -1 && callback->fn != fn ) return; if ( callback->subtype != -1 && callback->subtype != reg->subtype ) return; if ( callback->version != -1 && callback->version != reg->version ) return; if ( callback->callback == NULL ) return; callback->callback(action, fn, reg->subtype, reg->object, reg->objectlen, reg->version, reg->options, callback->userdata, reg->lhandle); } static void __fnreg_trigger_by_reg(const struct __fnregs * reg, enum roar_dl_fnreg_action action, int fn) { size_t j; for (j = 0; j < __MAX_FNREGS; j++) { if ( __fnrefs[ROAR_DL_FN_REGFN][j].lhandle != NULL ) { if ( __fnreg_check_trigger(&(__fnrefs[ROAR_DL_FN_REGFN][j])) == -1 ) continue; __fnreg_trigger_if_match(__fnrefs[ROAR_DL_FN_REGFN][j].object, reg, fn, action); } } } static void __fnreg_trigger_by_handler(const struct __fnregs * handle, enum roar_dl_fnreg_action action) { size_t i, j; if ( __fnreg_check_trigger(handle) == -1 ) return; for (i = 0; i < ROAR_DL_FN_MAX; i++) { for (j = 0; j < __MAX_FNREGS; j++) { if ( __fnrefs[i][j].lhandle != NULL ) { __fnreg_trigger_if_match(handle->object, &(__fnrefs[i][j]), i, action); } } } } int roar_dl_register_fn(struct roar_dl_lhandle * lhandle, int fn, int subtype, const void * object, size_t objectlen, int version, int options) { struct __fnregs * c = NULL; size_t i; if ( lhandle == NULL ) lhandle = __currently_inited; if ( fn == -1 ) fn = __currently_inited_fn; if ( lhandle == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } if ( fn < 0 || fn >= ROAR_DL_FN_MAX ) { roar_err_set(ROAR_ERROR_RANGE); return -1; } if ( object == NULL || objectlen == 0 ) { roar_err_set(ROAR_ERROR_INVAL); return -1; } for (i = 0; i < __MAX_FNREGS; i++) { if ( __fnrefs[fn][i].lhandle != NULL ) continue; c = &(__fnrefs[fn][i]); break; } if ( c == NULL ) { roar_err_set(ROAR_ERROR_NOSPC); return -1; } c->lhandle = lhandle; c->subtype = subtype; c->object = object; c->objectlen = objectlen; c->version = version; if ( fn == ROAR_DL_FN_REGFN ) { __fnreg_trigger_by_handler(c, ROAR_DL_FNREG); } else { __fnreg_trigger_by_reg(c, ROAR_DL_FNREG, fn); } return 0; } int roar_dl_unregister_fn2(struct roar_dl_lhandle * lhandle, int fn, int subtype, const void * object, size_t objectlen, int version, int options) { struct __fnregs * c = NULL; size_t i, j; if ( lhandle == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } for (i = 0; i < ROAR_DL_FN_MAX; i++) { if ( fn != -1 && fn != i ) continue; for (j = 0; j < __MAX_FNREGS; j++) { c = &(__fnrefs[i][j]); if ( c->lhandle != lhandle ) continue; if ( subtype != -1 && subtype != c->subtype ) continue; if ( object != NULL && object != c->object ) continue; if ( objectlen != (size_t)-1 && objectlen != c->objectlen ) continue; if ( version != -1 && version != c->version ) continue; if ( options != -1 && options != c->options ) continue; if ( i == ROAR_DL_FN_REGFN ) { __fnreg_trigger_by_handler(&(__fnrefs[i][j]), ROAR_DL_FNUNREG); } else { __fnreg_trigger_by_reg(&(__fnrefs[i][j]), ROAR_DL_FNUNREG, i); } memset(&(__fnrefs[i][j]), 0, sizeof(__fnrefs[i][j])); __fnrefs[i][j].lhandle = NULL; } } return 0; } int roar_dl_unregister_fn(struct roar_dl_lhandle * lhandle) { return roar_dl_unregister_fn2(lhandle, -1, -1, NULL, (size_t)-1, -1, -1); } int roar_dl_query_fn(struct roar_dl_lhandle ** lhandle, int fn, int subtype, const void ** object, size_t * objectlen, int version, int * options, ssize_t index) { struct __fnregs * c = NULL; ssize_t found = 0; size_t j; if ( lhandle == NULL || object == NULL || objectlen == NULL || options == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } if ( fn <= 0 || fn >= ROAR_DL_FN_MAX || index < 0 || index >= __MAX_FNREGS ) { roar_err_set(ROAR_ERROR_INVAL); return -1; } for (j = 0; j < __MAX_FNREGS; j++) { c = &(__fnrefs[fn][j]); if ( c->lhandle == NULL ) continue; if ( c->subtype != subtype ) continue; if ( c->version != version ) continue; if ( index == found++ ) { *lhandle = c->lhandle; *object = c->object; *objectlen = c->objectlen; *options = c->options; return 0; } } roar_err_set(ROAR_ERROR_NOENT); return -1; } static inline int __load_plugin_any_get_api(struct roar_dl_librarypara * para, const char * appname, const char * appabi, const char * servicename, const char * serviceabi, int universal, struct roar_dl_service_api * api) { struct roar_dl_lhandle * lhandle = NULL; struct roar_dl_librarypara * mypara = NULL; char name[80]; int err; int ret; mypara = roar_dl_para_new(NULL, NULL, LIBROAR_DL_APPNAME, LIBROAR_DL_ABIVERSION); if ( mypara == NULL ) return -1; if ( serviceabi != NULL ) { snprintf(name, sizeof(name), "service-%s-%s", servicename, serviceabi); lhandle = roar_dl_open(name, ROAR_DL_FLAG_PLUGIN, 1, mypara); } if ( lhandle == NULL ) { snprintf(name, sizeof(name), "service-%s", servicename); lhandle = roar_dl_open(name, ROAR_DL_FLAG_PLUGIN, 1, mypara); } err = roar_error; roar_dl_para_unref(mypara); roar_error = err; if ( lhandle == NULL ) return -1; roar_dl_appsched_trigger(lhandle, ROAR_DL_APPSCHED_INIT); ret = libroar_dl_service_get_api_real(para, appname, appabi, servicename, serviceabi, universal, api, 0); err = roar_error; roar_dl_appsched_trigger(lhandle, ROAR_DL_APPSCHED_FREE); roar_dl_unref(lhandle); roar_error = err; return ret; } int libroar_dl_service_get_api_real(struct roar_dl_librarypara * para, const char * appname, const char * appabi, const char * servicename, const char * serviceabi, int universal, struct roar_dl_service_api * api, int retry) { const struct roar_dl_service * s; struct __fnregs * c = NULL; size_t i; int err; if ( servicename == NULL || serviceabi == NULL || api == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } if ( para != NULL ) { if ( roar_dl_para_ref(para) == -1 ) return -1; } else { para = roar_dl_para_new(NULL, NULL, NULL, NULL); if ( para == NULL ) return -1; } memset(api, 0, sizeof(struct roar_dl_service_api)); for (i = 0; i < __MAX_FNREGS; i++) { c = &(__fnrefs[ROAR_DL_FN_SERVICE][i]); s = c->object; if ( c->lhandle == NULL ) continue; if ( c->subtype != ROAR_DL_SERVICE_SUBTYPE ) continue; if ( c->objectlen != ROAR_DL_SERVICE_SIZE ) continue; if ( c->version != ROAR_DL_SERVICE_VERSION ) continue; if ( !universal && s->appname == NULL ) continue; if ( universal == 2 && s->appname != NULL ) continue; if ( s->appname != NULL && !!strcmp(s->appname, appname) ) continue; if ( (s->appabi == NULL && appabi != NULL) || (s->appabi != NULL && appabi == NULL) || (s->appabi != NULL && appabi != NULL && !!strcmp(s->appabi, appabi) ) ) continue; if ( s->servicename == NULL || !!strcmp(s->servicename, servicename) ) continue; if ( s->serviceabi == NULL || !!strcmp(s->serviceabi, serviceabi) ) continue; if ( s->get_api == NULL ) continue; if ( roar_dl_ref(c->lhandle) != 0 ) continue; api->service = s; api->lhandle = c->lhandle; api->api = s->get_api(s, para); if ( api->api == NULL ) { err = roar_error; roar_dl_unref(c->lhandle); roar_dl_para_unref(para); api->lhandle = NULL; roar_error = err; return -1; } roar_dl_para_unref(para); return 0; } if ( retry ) { if ( __load_plugin_any_get_api(para, appname, appabi, servicename, serviceabi, universal, api) == 0 ) { roar_dl_para_unref(para); return 0; } } roar_dl_para_unref(para); roar_err_set(ROAR_ERROR_NOENT); return -1; } int libroar_dl_service_free_api_real(struct roar_dl_service_api * api) { if ( api == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return -1; } roar_dl_unref(api->lhandle); memset(api, 0, sizeof(struct roar_dl_service_api)); return 0; } //ll