| /* |
| * Copyright (c) 2019, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #ifndef FARF_ERROR |
| #define FARF_ERROR 1 |
| #endif |
| |
| #include <assert.h> |
| #include "verify.h" |
| #include "HAP_farf.h" |
| #include "HAP_pls.h" |
| #include "mutex.h" |
| #include "mod_table.h" |
| #include "platform_libs.h" |
| #include "remote64.h" |
| #include "uthash.h" |
| #include "AEEstd.h" |
| #include "AEEStdErr.h" |
| #include "sbuf_parser.h" |
| |
| #include <dlfcn.h> |
| |
| #define DLOPEN dlopen |
| #define DLCLOSE dlclose |
| #define DLSYM dlsym |
| #define DLERROR dlerror |
| |
| /** |
| * structure for the mod table |
| * |
| * you need to define a rw_mutex type and its read/write lock/unlock api's |
| * which are under the RW_MUTEX namespace. |
| * |
| * this library defines 2 functions for opening modules, open_static and |
| * open_dynamic. Both return a handle that should be closed via close. |
| * |
| * you can also register a const handle, an invoke function for a known handle |
| * value. since handle keys are allocated, you should pick handle values that are |
| * not going to be returned by malloc (0, or odd). |
| */ |
| struct static_mod_table { |
| RW_MUTEX_T mut; |
| struct static_mod* staticModOverrides; |
| struct static_mod* staticMods; |
| struct const_mod* constMods; |
| boolean bInit; |
| }; |
| |
| struct open_mod_table { |
| RW_MUTEX_T mut; |
| struct open_mod* openMods; |
| struct static_mod_table* smt; |
| }; |
| |
| typedef int (*invoke_fn)(uint32, remote_arg*); |
| typedef int (*handle_invoke_fn)(remote_handle64, uint32, remote_arg*); |
| struct static_mod { |
| invoke_fn invoke; |
| handle_invoke_fn handle_invoke; |
| UT_hash_handle hh; |
| char uri[1]; |
| }; |
| |
| struct const_mod { |
| invoke_fn invoke; |
| handle_invoke_fn handle_invoke; |
| uint32 key; |
| remote_handle64 h64; |
| UT_hash_handle hh; |
| char uri[1]; |
| }; |
| |
| struct parsed_uri { |
| const char *file; |
| int filelen; |
| const char *sym; |
| int symlen; |
| const char *ver; |
| int verlen; |
| }; |
| |
| struct open_mod { |
| void* dlhandle; |
| invoke_fn invoke; |
| handle_invoke_fn handle_invoke; |
| uint64 key; |
| UT_hash_handle hh; |
| remote_handle64 h64; |
| int refs; |
| struct parsed_uri vals; |
| char uri[1]; |
| }; |
| |
| static int static_mod_table_ctor(struct static_mod_table* me) { |
| if(me->bInit == 0) { |
| RW_MUTEX_CTOR(me->mut); |
| me->staticMods = 0; |
| me->staticModOverrides = 0; |
| me->bInit = 1; |
| } |
| return 0; |
| } |
| |
| static void static_mod_table_dtor_imp(struct static_mod_table* me) { |
| struct static_mod *sm, *stmp; |
| struct const_mod *dm, *ftmp; |
| if(me->bInit != 0) { |
| if( me->staticMods || me->constMods || me->staticModOverrides) { |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| HASH_ITER(hh, me->staticMods, sm, stmp) { |
| if(me->staticMods) { |
| HASH_DEL(me->staticMods,sm); |
| } |
| free(sm); |
| sm = NULL; |
| } |
| HASH_ITER(hh, me->staticModOverrides, sm, stmp) { |
| if(me->staticModOverrides) { |
| HASH_DEL(me->staticModOverrides,sm); |
| } |
| free(sm); |
| sm = NULL; |
| } |
| HASH_ITER(hh, me->constMods, dm, ftmp) { |
| if(me->constMods) { |
| HASH_DEL(me->constMods,dm); |
| } |
| free(dm); |
| dm = NULL; |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| } |
| RW_MUTEX_DTOR(me->mut); |
| me->staticMods = 0; |
| me->staticModOverrides = 0; |
| me->bInit = 0; |
| } |
| } |
| |
| static int open_mod_table_ctor_imp(void* ctx, void* data) { |
| struct open_mod_table* me = (struct open_mod_table*)data; |
| RW_MUTEX_CTOR(me->mut); |
| me->openMods = 0; |
| me->smt = (struct static_mod_table*) ctx; |
| return 0; |
| } |
| |
| static int open_mod_handle_close(struct open_mod *mod, remote_handle64 h); |
| |
| static void open_mod_table_dtor_imp(void* data) { |
| struct open_mod_table* me = (struct open_mod_table*)data; |
| struct open_mod *dm, *ftmp; |
| if( me->openMods) { |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| HASH_ITER(hh, me->openMods, dm, ftmp) { |
| if(me->openMods) { |
| HASH_DEL(me->openMods,dm); |
| } |
| if(dm->h64) { |
| (void)open_mod_handle_close(dm, dm->h64); |
| } |
| if(dm->dlhandle) { |
| DLCLOSE(dm->dlhandle); |
| } |
| free(dm); |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| } |
| RW_MUTEX_DTOR(me->mut); |
| me->openMods = 0; |
| } |
| static int open_mod_table_open_from_static(struct open_mod_table* me, |
| struct static_mod** tbl, |
| const char* uri, |
| remote_handle* handle); |
| |
| static int open_mod_table_open_static_override(struct open_mod_table* me, const char* uri, remote_handle* handle) { |
| FARF(HIGH, "open_mod_table_open_static_override"); |
| return open_mod_table_open_from_static(me, &me->smt->staticModOverrides, uri, handle); |
| } |
| |
| |
| static int open_mod_table_open_static(struct open_mod_table* me, const char* uri, remote_handle* handle) { |
| FARF(HIGH, "open_mod_table_open_static"); |
| return open_mod_table_open_from_static(me, &me->smt->staticMods, uri, handle); |
| } |
| |
| static int static_mod_add(struct static_mod_table* me, struct static_mod** tbl, const char* uri, |
| int(*invoke)(uint32 sc, remote_arg* pra), |
| int(*handle_invoke)(remote_handle64, uint32 sc, remote_arg* pra)) { |
| int nErr = AEE_SUCCESS; |
| struct static_mod *sm = 0; |
| int len = std_strlen(uri) + 1; |
| VERIFYC(NULL != (sm = ((struct static_mod*)calloc(1, sizeof(struct static_mod) + len))), AEE_ENOMEMORY); |
| std_strlcpy(sm->uri, uri, len); |
| sm->invoke = invoke; |
| sm->handle_invoke = handle_invoke; |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| HASH_ADD_STR(*tbl, uri, sm); |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| bail: |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: static module addition failed\n", nErr); |
| if(sm) { |
| free(sm); |
| sm = NULL; |
| } |
| } |
| return nErr; |
| } |
| |
| static int static_mod_table_register_static_override(struct static_mod_table* me, const char* uri, int(*pfn)(uint32 sc, remote_arg* pra)) { |
| return static_mod_add(me, &me->staticModOverrides, uri, pfn, 0); |
| } |
| static int static_mod_table_register_static_override1(struct static_mod_table* me, const char* uri, int(*pfn)(remote_handle64, uint32 sc, remote_arg* pra)) { |
| return static_mod_add(me, &me->staticModOverrides, uri, 0, pfn); |
| } |
| static int static_mod_table_register_static(struct static_mod_table* me, const char* uri, int(*pfn)(uint32 sc, remote_arg* pra)) { |
| return static_mod_add(me, &me->staticMods, uri, pfn, 0); |
| } |
| static int static_mod_table_register_static1(struct static_mod_table* me, const char* uri, int(*pfn)(remote_handle64,uint32 sc, remote_arg* pra)) { |
| return static_mod_add(me, &me->staticMods, uri, 0, pfn); |
| } |
| |
| |
| static int static_mod_table_register_const_handle(struct static_mod_table* me, remote_handle local, |
| remote_handle64 remote, const char* uri, |
| int(*invoke)(uint32 sc, remote_arg* pra), |
| int(*handle_invoke)(remote_handle64, uint32 sc, remote_arg* pra) |
| ) { |
| int nErr = AEE_SUCCESS; |
| int len = std_strlen(uri) + 1; |
| struct const_mod *dm = 0, *dmOld; |
| VERIFYC(NULL != (dm = ((struct const_mod*)calloc(1, sizeof(struct open_mod) + len))), AEE_ENOMEMORY); |
| dm->key = local; |
| dm->invoke = invoke; |
| dm->handle_invoke = handle_invoke; |
| dm->h64 = remote; |
| std_strlcpy(dm->uri, uri, len); |
| |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| HASH_FIND_INT(me->constMods, &local, dmOld); |
| if(dmOld == 0) { |
| HASH_ADD_INT(me->constMods, key, dm); |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| nErr = dmOld != 0 ? -1 : nErr; |
| bail: |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: failed to register const handle in modtable\n", nErr); |
| if(dm) { |
| free(dm); |
| dm = NULL; |
| } |
| } |
| return nErr; |
| } |
| |
| static int open_mod_handle_open(struct open_mod *mod, const char* name, |
| remote_handle64 *ph) { |
| int nErr = AEE_SUCCESS; |
| remote_arg args[3]; |
| int32_t len = strlen(name) + 1; |
| args[0].buf.pv = &len; |
| args[0].buf.nLen = sizeof(len); |
| args[1].buf.pv = (void*)name; |
| args[1].buf.nLen = len; |
| nErr = mod->handle_invoke(0,REMOTE_SCALARS_MAKEX(0,0,2,0,0,1),args); |
| if(!nErr) { |
| *ph = args[2].h64; |
| } |
| FARF(HIGH, "allocated %x", *ph); |
| return nErr; |
| } |
| |
| static int open_mod_handle_close(struct open_mod *mod, remote_handle64 h) { |
| int nErr; |
| remote_arg args[1]; |
| args[0].h64 = h; |
| FARF(HIGH, "releasing %x", h); |
| nErr = mod->handle_invoke(0,REMOTE_SCALARS_MAKEX(0,1,0,0,1,0),args); |
| return nErr; |
| } |
| |
| static int notqmark(struct sbuf *buf) { |
| return sbuf_notchar(buf, '?'); |
| } |
| static int notandoreq(struct sbuf *buf) { |
| return sbuf_notchars(buf, "&="); |
| } |
| static int notand(struct sbuf *buf) { |
| return sbuf_notchar(buf, '&'); |
| } |
| |
| static int parse_uri(const char *uri, int urilen, struct parsed_uri *out) { |
| // "file:///librhtest_skel.so?rhtest_skel_handle_invoke&_modver=1.0" |
| int nErr = 0; |
| char *name, *value; |
| int nameLen, valueLen; |
| struct sbuf buf; |
| FARF(HIGH, "parse_uri %s %d", uri, urilen); |
| memset(out, 0, sizeof(*out)); |
| //initialize |
| sbuf_parser_init(&buf, uri, urilen); |
| |
| //parse until question mark |
| VERIFYC(sbuf_string(&buf, "file://"), AEE_EINVALIDFORMAT); |
| |
| //ignore the starting / |
| (void)sbuf_string(&buf, "/"); |
| |
| out->file = sbuf_cur(&buf); |
| VERIFY(sbuf_many1(&buf, notqmark)); |
| out->filelen = sbuf_cur(&buf) - out->file; |
| FARF(HIGH, "file:%.*s %d", out->filelen, out->file, out->filelen); |
| VERIFY(sbuf_char(&buf, '?')); |
| out->sym = sbuf_cur(&buf); |
| VERIFY(sbuf_many1(&buf, notand)); |
| out->symlen = sbuf_cur(&buf) - out->sym; |
| assert(out->sym + out->symlen <= uri + urilen); |
| FARF(HIGH, "sym:%.*s %d", out->symlen, out->sym, out->symlen); |
| |
| if(!sbuf_end(&buf) && sbuf_char(&buf, '&')) { |
| //parse each query |
| while(!sbuf_end(&buf)) { |
| //record where the name starts |
| name = sbuf_cur(&buf); |
| |
| //name is valid until '=' or '&' |
| VERIFY(sbuf_many1(&buf, notandoreq)); |
| nameLen = sbuf_cur(&buf) - name; |
| |
| value = 0; |
| valueLen = 0; |
| //if the next char is a '=' then we also get a value |
| if(sbuf_char(&buf, '=')) { |
| value = sbuf_cur(&buf); |
| |
| //value is until the next query that starts with '&' |
| VERIFY(sbuf_many1(&buf, notand)); |
| valueLen = sbuf_cur(&buf) - value; |
| } |
| //expect '&' or end |
| sbuf_char(&buf, '&'); |
| if(!std_strncmp(name, "_modver", nameLen)) { |
| out->ver = value; |
| out->verlen = valueLen; |
| } |
| } |
| } |
| bail: |
| if(out->filelen) { |
| FARF(HIGH, "parse_uri file: %.*s", out->filelen, out->file); |
| } |
| if(out->symlen) { |
| FARF(HIGH, "parse_uri sym: %.*s", out->symlen, out->sym); |
| } |
| if(out->verlen) { |
| FARF(HIGH, "parse_uri version: %.*s", out->verlen, out->ver); |
| } |
| FARF(HIGH, "parse_uri done: %s %d err:%x", uri, urilen, nErr); |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: parseuri failed for uri %s, urilen %d\n", nErr, uri, urilen); |
| } |
| return nErr; |
| } |
| |
| static int open_mod_table_open_dynamic(struct open_mod_table* me, const char* uri, remote_handle* handle, char* dlStr, int dlerrorLen, int* pdlErr) |
| { |
| int nErr = AEE_SUCCESS, dlErr = 0; |
| struct open_mod *dm = 0, *dmOld; |
| int len = strlen(uri); |
| int tmplen = len*2 + sizeof("file:///lib_skel.so?_skel_handle_invoke&_modver=1.0") + 1; |
| char *tmp = 0; |
| FARF(HIGH, "open_mod_table_open_dynamic"); |
| VERIFYC(NULL != (tmp = calloc(1, tmplen)), AEE_ENOMEMORY); |
| VERIFYC(NULL != (dm = ((struct open_mod*)calloc(1, sizeof(struct open_mod) + len + 1))), AEE_ENOMEMORY); |
| std_memmove(dm->uri, uri, len + 1); |
| FARF(HIGH, "calling parse_uri"); |
| (void)parse_uri(dm->uri, len, &dm->vals); |
| FARF(HIGH, "done calling parse_uri"); |
| FARF(HIGH, "vals %d %d %d", dm->vals.filelen, dm->vals.symlen, dm->vals.verlen); |
| if(dm->vals.filelen) { |
| int rv = std_snprintf(tmp, tmplen, "%.*s", dm->vals.filelen, dm->vals.file); |
| VERIFYC(tmplen >= rv, AEE_EBADSIZE); |
| } else { |
| int rv; |
| rv = std_snprintf(tmp, tmplen, "lib%s_skel.so", uri); |
| VERIFYC(tmplen >= rv, AEE_EBADSIZE); |
| } |
| FARF(HIGH, "calling dlopen for %s", tmp); |
| dm->dlhandle = DLOPEN(tmp,RTLD_NOW); |
| FARF(HIGH, "got %p for dlopen %s", dm->dlhandle, tmp); |
| VERIFY(!(nErr = (dlErr = dm->dlhandle == 0 ? -5 : 0))); |
| |
| if(dm->vals.symlen) { |
| int rv = std_snprintf(tmp, tmplen, "%.*s", dm->vals.symlen, dm->vals.sym); |
| VERIFYC(tmplen >= rv, AEE_EBADSIZE); |
| } else { |
| int rv = std_snprintf(tmp, tmplen, "%s_skel_invoke", uri); |
| VERIFYC(tmplen >= rv, AEE_EBADSIZE); |
| } |
| |
| FARF(HIGH, "calling dlsym for %s", tmp); |
| if(dm->vals.verlen && 0 == std_strncmp(dm->vals.ver, "1.0", dm->vals.verlen)) { |
| dm->handle_invoke = (handle_invoke_fn) DLSYM(dm->dlhandle, tmp); |
| } else { |
| dm->invoke = (invoke_fn) DLSYM(dm->dlhandle, tmp); |
| } |
| FARF(HIGH, "dlsym returned %p %p", dm->invoke, dm->handle_invoke); |
| VERIFYC(!(dlErr = dm->invoke || dm->handle_invoke ? 0 : AEE_ENOSUCHSYMBOL), AEE_ENOSUCHSYMBOL); |
| |
| dm->key = (uint32)(uintptr_t)dm; |
| dm->refs = 1; |
| if(dm->handle_invoke) { |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_handle_open(dm, uri, &dm->h64))); |
| } |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| do { |
| HASH_FIND_INT(me->openMods, &dm->key, dmOld); |
| if(dmOld) { |
| dm->key++; |
| } |
| } while(dmOld); |
| RW_MUTEX_LOCK_WRITE(me->smt->mut); |
| HASH_FIND_INT(me->smt->constMods, &dm->key, dmOld); |
| RW_MUTEX_UNLOCK_WRITE(me->smt->mut); |
| if(dmOld == 0) { |
| HASH_ADD_INT(me->openMods, key, dm); |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| nErr = dmOld != 0 ? -1 : nErr; |
| if(nErr == 0) { |
| *handle = dm->key; |
| } |
| bail: |
| if (nErr != AEE_SUCCESS) { |
| if(dlErr) { |
| const char* dlerr = DLERROR(); |
| if(dlerr != 0){ |
| std_strlcpy(dlStr,dlerr,dlerrorLen); |
| } |
| FARF(HIGH, "dlerror:%x:%s", dlErr, dlerr == 0 ? "" : dlerr); |
| nErr = 0; |
| } |
| if(pdlErr) { |
| *pdlErr = dlErr; |
| } |
| if(dm && dm->h64) { |
| (void)open_mod_handle_close(dm, dm->h64); |
| } |
| if(dm && dm->dlhandle) { |
| DLCLOSE(dm->dlhandle); |
| } |
| if(dm) { |
| free(dm); |
| dm = NULL; |
| } |
| VERIFY_EPRINTF("Error %x: open modtable dynamic failed. dlerr %x\n", nErr, dlErr); |
| } |
| FARF(HIGH, "done open_mod_table_open_dynamic for %s rv %x handle: %p %x", uri, nErr, *handle, dlErr); |
| if(tmp) { |
| free(tmp); |
| tmp = NULL; |
| } |
| return nErr; |
| } |
| |
| static int open_mod_table_open_from_static(struct open_mod_table* me, |
| struct static_mod** tbl, |
| const char* uri, |
| remote_handle* handle) |
| { |
| int nErr = AEE_SUCCESS; |
| struct static_mod *sm = 0; |
| struct open_mod *dm = 0, *dmOld = 0; |
| int len = std_strlen(uri); |
| int sz = len*2 + sizeof("file:///lib_skel.so?_skel_handle_invoke&_modver=1.0") + 1; |
| char *tmp = 0; |
| VERIFYC(NULL != (dm = ((struct open_mod*)calloc(1, sizeof(*dm) + sz))), AEE_ENOMEMORY); |
| RW_MUTEX_LOCK_READ(me->mut); |
| HASH_FIND_STR(*tbl, uri, sm); |
| RW_MUTEX_UNLOCK_READ(me->mut); |
| std_memmove(dm->uri, uri, len); |
| if(sm == 0) { |
| VERIFY(AEE_SUCCESS == (nErr = parse_uri(uri, len, &dm->vals))); |
| FARF(HIGH, "file %.*s %d", dm->vals.filelen, dm->vals.file, dm->vals.filelen); |
| FARF(HIGH, "sym %.*s %d", dm->vals.symlen, dm->vals.sym, dm->vals.symlen); |
| FARF(HIGH, "version %.*s %d", dm->vals.verlen, dm->vals.ver, dm->vals.verlen); |
| if(dm->vals.verlen) { |
| int rv = std_snprintf(dm->uri, sz, "file:///%.*s?%.*s&_modver=%.*s", |
| dm->vals.filelen, dm->vals.file, |
| dm->vals.symlen, dm->vals.sym, |
| dm->vals.verlen, dm->vals.ver); |
| VERIFYC(sz >= rv, AEE_EBADSIZE); |
| } else { |
| int rv = std_snprintf(dm->uri, sz, "file://%.*s?%.*s", |
| dm->vals.filelen, dm->vals.file, |
| dm->vals.symlen, dm->vals.sym); |
| VERIFYC(sz >= rv, AEE_EBADSIZE); |
| } |
| FARF(HIGH, "dm->uri:%s", dm->uri); |
| |
| RW_MUTEX_LOCK_READ(me->mut); |
| HASH_FIND_STR(*tbl, dm->uri, sm); |
| RW_MUTEX_UNLOCK_READ(me->mut); |
| } |
| VERIFYC(0 != sm, AEE_ENOTINITIALIZED); |
| assert(sm->handle_invoke || sm->invoke); |
| dm->handle_invoke = sm->handle_invoke; |
| dm->invoke = sm->invoke; |
| dm->key = (uint32)(uintptr_t)dm; |
| dm->refs = 1; |
| if(dm->handle_invoke) { |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_handle_open(dm, uri, &dm->h64))); |
| } |
| |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| do { |
| HASH_FIND_INT(me->openMods, &dm->key, dmOld); |
| if(dmOld) { |
| dm->key++; |
| } |
| } while(dmOld); |
| HASH_ADD_INT(me->openMods, key, dm); |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| |
| *handle = dm->key; |
| bail: |
| if(tmp) { |
| free(tmp); |
| tmp = NULL; |
| } |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: modtable open from static failed.\n", nErr); |
| } |
| if(nErr && dm) { |
| if(dm->h64) { |
| (void)open_mod_handle_close(dm, dm->h64); |
| } |
| free(dm); |
| dm = NULL; |
| } |
| return nErr; |
| } |
| |
| static int open_mod_table_open(struct open_mod_table* me, const char* uri, remote_handle* handle, char* dlerr, int dlerrorLen, int* pdlErr) |
| { |
| int nErr = AEE_SUCCESS, dlErr = 0; |
| if(pdlErr) { |
| *pdlErr = 0; |
| } |
| if(0 != open_mod_table_open_static_override(me, uri, handle)) { |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_table_open_dynamic(me, uri, handle, dlerr, dlerrorLen, &dlErr))); |
| if(dlErr != 0) { |
| FARF(HIGH, "dynammic open failed, trying static"); |
| if(0 != open_mod_table_open_static(me, uri, handle)) { |
| if(pdlErr) { |
| *pdlErr = dlErr; |
| } |
| } |
| } |
| } |
| bail: |
| FARF(HIGH, "done open for %s rv %d handle: %p", uri, nErr, *handle); |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: open modtable failed\n", nErr); |
| } |
| return nErr; |
| } |
| |
| static void open_mod_close(struct open_mod_table *me, struct open_mod* dm) { |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| dm->refs--; |
| if(dm->refs <= 0) { |
| HASH_DEL(me->openMods,dm); |
| } else { |
| dm = 0; |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| if(dm) { |
| if(dm->h64) { |
| (void)open_mod_handle_close(dm, dm->h64); |
| } |
| if(dm->dlhandle) { |
| DLCLOSE(dm->dlhandle); |
| } |
| free(dm); |
| dm = NULL; |
| } |
| } |
| static int open_mod_table_close(struct open_mod_table* me, remote_handle64 handle, char* errStr, int errStrLen, int* pdlErr) |
| { |
| int nErr = AEE_SUCCESS; |
| struct open_mod *dm, *del = 0;; |
| int dlErr = 0; |
| // First ensure that the handle is valid |
| RW_MUTEX_LOCK_WRITE(me->mut); |
| HASH_FIND_INT(me->openMods, &handle, dm); |
| if(dm) { |
| dm->refs--; |
| if(dm->refs <= 0) { |
| del = dm; |
| FARF(HIGH, "deleting %s %p", del->uri, del); |
| HASH_DEL(me->openMods,dm); |
| } else { |
| FARF(HIGH, "leaked %s", dm->uri); |
| dm = 0; |
| } |
| } |
| RW_MUTEX_UNLOCK_WRITE(me->mut); |
| if(del) { |
| if(del->h64) { |
| (void)open_mod_handle_close(dm, dm->h64); |
| } |
| if(del->dlhandle) { |
| dlErr = DLCLOSE(del->dlhandle); |
| } |
| FARF(HIGH, "free %s %p", del->uri, del); |
| free(del); |
| del = NULL; |
| } |
| VERIFY(del); |
| |
| bail: |
| if(dlErr) { |
| const char* error = DLERROR(); |
| nErr = dlErr; |
| if(error != 0){ |
| std_strlcpy(errStr,error,errStrLen); |
| } |
| VERIFY_EPRINTF("Error %x: open modtable close failed. dlerr %s\n", nErr, error); |
| } |
| if(pdlErr) { |
| *pdlErr = dlErr; |
| } |
| return nErr; |
| } |
| |
| static struct open_mod* open_mod_table_get_open(struct open_mod_table* me, remote_handle handle) { |
| struct open_mod* om = 0; |
| RW_MUTEX_LOCK_READ(me->mut); |
| HASH_FIND_INT(me->openMods, &handle, om); |
| if(0 != om) { |
| om->refs++; |
| } |
| RW_MUTEX_UNLOCK_READ(me->mut); |
| return om; |
| } |
| static struct const_mod* open_mod_table_get_const(struct open_mod_table* me, remote_handle handle) { |
| struct const_mod* cm = 0; |
| RW_MUTEX_LOCK_READ(me->smt->mut); |
| HASH_FIND_INT(me->smt->constMods, &handle, cm); |
| RW_MUTEX_UNLOCK_READ(me->smt->mut); |
| return cm; |
| } |
| |
| static int open_mod_table_handle_invoke(struct open_mod_table* me, remote_handle handle, uint32 sc, remote_arg* pra) { |
| int nErr = AEE_SUCCESS; |
| struct open_mod* om = 0; |
| struct const_mod* cm = 0; |
| remote_handle64 h = 0; |
| invoke_fn invoke = 0; |
| handle_invoke_fn handle_invoke = 0; |
| cm = open_mod_table_get_const(me, handle); |
| if(cm) { |
| invoke = cm->invoke; |
| handle_invoke = cm->handle_invoke; |
| h = cm->h64; |
| } else { |
| VERIFYC(0 != (om = open_mod_table_get_open(me, handle)), AEE_ENOSUCHMOD); |
| invoke = om->invoke; |
| handle_invoke = om->handle_invoke; |
| h = om->h64; |
| } |
| if(invoke) { |
| VERIFY(AEE_SUCCESS == (nErr = invoke(sc, pra))); |
| } else { |
| VERIFY(AEE_SUCCESS == (nErr = handle_invoke(h, sc, pra))); |
| } |
| bail: |
| if(om) { |
| open_mod_close(me, om); |
| } |
| FARF(HIGH, "invoke rv %p %x %x", handle, sc, nErr); |
| return nErr; |
| } |
| |
| struct mod_table { |
| struct static_mod_table smt; |
| struct open_mod_table omt; |
| }; |
| |
| // mod_table object |
| static struct static_mod_table static_mod_table_obj; |
| |
| |
| /** |
| * register a static component for invocations |
| * this can be called at any time including from a static constructor |
| * |
| * overrides will be tried first, then dynamic modules, then regular |
| * static modules. |
| * |
| * name, name of the interface to register |
| * pfn, function pointer to the skel invoke function |
| * |
| * for example: |
| * __attribute__((constructor)) static void my_module_ctor(void) { |
| * mod_table_register_static("my_module", my_module_skel_invoke); |
| * } |
| * |
| */ |
| int mod_table_register_static_override(const char* name, int(*pfn)(uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_static_override(&static_mod_table_obj, name, pfn); |
| } |
| return AEE_EUNKNOWN; |
| } |
| |
| int mod_table_register_static_override1(const char* name, int(*pfn)(remote_handle64, uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_static_override1(&static_mod_table_obj, name, pfn); |
| } |
| return AEE_EUNKNOWN; |
| } |
| |
| |
| /** |
| * register a static component for invocations |
| * this can be called at any time including from a static constructor |
| * |
| * name, name of the interface to register |
| * pfn, function pointer to the skel invoke function |
| * |
| * for example: |
| * __attribute__((constructor)) static void my_module_ctor(void) { |
| * mod_table_register_static("my_module", my_module_skel_invoke); |
| * } |
| * |
| */ |
| int mod_table_register_static(const char* name, int(*pfn)(uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_static(&static_mod_table_obj, name, pfn); |
| } |
| return AEE_EUNKNOWN; |
| } |
| |
| int mod_table_register_static1(const char* name, int(*pfn)(remote_handle64, uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_static1(&static_mod_table_obj, name, pfn); |
| } |
| return AEE_EUNKNOWN; |
| } |
| |
| |
| /** |
| * Open a module and get a handle to it |
| * |
| * uri, name of module to open |
| * handle, Output handle |
| * dlerr, Error String (if an error occurs) |
| * dlerrorLen, Length of error String (if an error occurs) |
| * pdlErr, Error identifier |
| */ |
| int mod_table_open(const char* uri, remote_handle* handle, char* dlerr, int dlerrorLen, int* pdlErr) { |
| int nErr = AEE_SUCCESS; |
| struct open_mod_table* pomt = 0; |
| FARF(HIGH, "mod_table_open for %s", uri); |
| VERIFY(AEE_SUCCESS == (nErr = HAP_pls_add_lookup((uintptr_t)open_mod_table_ctor_imp, 0, sizeof(*pomt), open_mod_table_ctor_imp, (void*)&static_mod_table_obj, open_mod_table_dtor_imp, (void**)&pomt))); |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_table_open(pomt,uri,handle,dlerr,dlerrorLen,pdlErr))); |
| bail: |
| FARF(HIGH, "mod_table_open for %s nErr: %x", uri, nErr); |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: modtable open failed\n", nErr); |
| } |
| return nErr; |
| } |
| /** |
| * invoke a handle in the mod table |
| * |
| * handle, handle to invoke |
| * sc, scalars, see remote.h for documentation. |
| * pra, args, see remote.h for documentation. |
| */ |
| int mod_table_invoke(remote_handle handle, uint32 sc, remote_arg* pra) { |
| int nErr = AEE_SUCCESS; |
| struct open_mod_table* pomt = 0; |
| VERIFY(AEE_SUCCESS == (nErr = HAP_pls_add_lookup((uintptr_t)open_mod_table_ctor_imp, 0, sizeof(*pomt), open_mod_table_ctor_imp, (void*)&static_mod_table_obj, open_mod_table_dtor_imp, (void**)&pomt))); |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_table_handle_invoke(pomt, handle, sc, pra))); |
| bail: |
| return nErr; |
| } |
| |
| /** |
| * Closes a handle in the mod table |
| * |
| * handle, handle to close |
| * errStr, Error String (if an error occurs) |
| * errStrLen, Length of error String (if an error occurs) |
| * pdlErr, Error identifier |
| */ |
| int mod_table_close(remote_handle handle, char* errStr, int errStrLen, int* pdlErr) { |
| int nErr = AEE_SUCCESS; |
| struct open_mod_table* pomt = 0; |
| VERIFY(AEE_SUCCESS == (nErr = HAP_pls_lookup((uintptr_t)open_mod_table_ctor_imp, 0, (void**)&pomt))); |
| VERIFY(AEE_SUCCESS == (nErr = open_mod_table_close(pomt, handle, errStr,errStrLen,pdlErr))); |
| bail: |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: modtable close failed\n", nErr); |
| } |
| return nErr; |
| } |
| |
| /** |
| * internal use only |
| */ |
| int mod_table_register_const_handle(remote_handle remote, const char* uri, int(*pfn)(uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_const_handle(&static_mod_table_obj, remote, 0, uri, pfn, 0); |
| } |
| return AEE_EUNKNOWN; |
| } |
| int mod_table_register_const_handle1(remote_handle remote, remote_handle64 local, const char* uri, int(*pfn)(remote_handle64, uint32 sc, remote_arg* pra)) { |
| if(0 == static_mod_table_ctor(&static_mod_table_obj)) { |
| return static_mod_table_register_const_handle(&static_mod_table_obj, remote, local, uri, 0, pfn); |
| } |
| return AEE_EUNKNOWN; |
| } |
| |
| // Constructor and destructor |
| static int mod_table_ctor(void) { |
| return static_mod_table_ctor(&static_mod_table_obj); |
| } |
| static void mod_table_dtor(void) { |
| static_mod_table_dtor_imp(&static_mod_table_obj); |
| return; |
| } |
| |
| PL_DEFINE(mod_table, mod_table_ctor, mod_table_dtor); |