blob: 7c92d8468885b123dfb86f2e8648ddb515b1e441 [file] [log] [blame]
/*
* 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);