#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <except.h>
#include <miptrnam.h>
#include <qusec.h>
#include <qp0lstdi.h>
#include <qp0z1170.h>
#include <qleawi.h>
#include <qsy.h>
#include <qmhrtvm.h>
#include <mih/rinzstat.h>
#include <mih/matactex.h>
#include "libxml/hash.h"
#include "dlfcn.h"
#define MAXPATHLEN 5120
#define MAX_ERR_STR 511
#define offset_by(t, b, o) ((t *) ((char *) (b) + (unsigned int) (o)))
#define INITED 000001
#define THREADS 000002
#define MULTIBUF 000004
typedef struct {
Qle_ABP_Info_Long_t actinfo;
_SYSPTR pointer;
unsigned int actcount;
} dlinfo;
typedef struct {
unsigned int lockcount;
unsigned int iserror;
char str[MAX_ERR_STR + 1];
} dlts_t;
static pthread_mutex_t dlmutex = PTHREAD_MUTEX_INITIALIZER;
static xmlHashTablePtr dldir = (xmlHashTablePtr) NULL;
static unsigned int dlflags = 0;
static pthread_key_t dlkey;
static dlts_t static_buf;
static void
dlthreadterm(void * mem)
{
free(mem);
pthread_setspecific(dlkey, NULL);
}
static void
dlterm(void)
{
void * p;
if (dlflags & MULTIBUF) {
p = pthread_getspecific(dlkey);
if (p)
dlthreadterm(p);
}
if (dlflags & THREADS)
pthread_mutex_lock(&dlmutex);
if (dldir) {
xmlHashFree(dldir, (xmlHashDeallocator) NULL);
dldir = NULL;
}
if (dlflags & MULTIBUF)
pthread_key_delete(dlkey);
dlflags |= ~(INITED | MULTIBUF);
pthread_mutex_unlock(&dlmutex);
pthread_mutex_destroy(&dlmutex);
}
static void
dlinit(void)
{
int locked;
locked = !pthread_mutex_lock(&dlmutex);
if (!(dlflags & INITED)) {
dlflags &= ~THREADS;
if (locked)
dlflags |= THREADS;
Qp0zInitEnv();
dldir = xmlHashCreate(16);
dlflags &= ~MULTIBUF;
if (dlflags & THREADS)
if (!pthread_key_create(&dlkey, dlthreadterm))
dlflags |= MULTIBUF;
atexit(dlterm);
dlflags |= INITED;
}
if (locked)
pthread_mutex_unlock(&dlmutex);
}
static void
dlthreadinit(void)
{
dlts_t * p;
if (!(dlflags & INITED))
dlinit();
if (dlflags & MULTIBUF) {
p = pthread_getspecific(dlkey);
if (!p) {
p = (dlts_t *) malloc(sizeof *p);
if (p) {
p->lockcount = 0;
p->iserror = 0;
if (pthread_setspecific(dlkey, p))
free(p);
}
}
}
}
static void
dllock(void)
{
dlts_t * p;
if (!(dlflags & THREADS))
return;
if (dlflags & MULTIBUF) {
p = pthread_getspecific(dlkey);
if (p && p->lockcount) {
p->lockcount++;
return;
}
}
else
p = (dlts_t *) NULL;
if (pthread_mutex_lock(&dlmutex))
return;
if (p)
p->lockcount++;
}
static void
dlunlock(void)
{
dlts_t * p;
if (!(dlflags & THREADS))
return;
if (dlflags & MULTIBUF) {
p = pthread_getspecific(dlkey);
if (p && p->lockcount > 1) {
p->lockcount--;
return;
}
}
else
p = (dlts_t *) NULL;
if (pthread_mutex_unlock(&dlmutex))
return;
if (p)
p->lockcount--;
}
const char *
dlerror(void)
{
dlts_t * p;
dlthreadinit();
if (!(dlflags & MULTIBUF))
p = &static_buf;
else if (!(p = (dlts_t *) pthread_getspecific(dlkey)))
p = &static_buf;
if (!p->iserror)
return (const char *) NULL;
p->iserror = 0;
return p->str;
}
static void
dlseterror_from_errno(unsigned int err_no)
{
dlts_t * p;
if (!(dlflags & MULTIBUF))
p = &static_buf;
else if (!(p = (dlts_t *) pthread_getspecific(dlkey)))
p = &static_buf;
strcpy(p->str, strerror(err_no));
p->iserror = 1;
}
static void
dlseterror_from_exception(volatile _INTRPT_Hndlr_Parms_T * excp)
{
int i;
Qmh_Rtvm_RTVM0300_t * imp;
char * cp;
_INTRPT_Hndlr_Parms_T * p;
dlts_t * q;
char rtvmbuf[30000];
Qus_EC_t errinfo;
p = (_INTRPT_Hndlr_Parms_T *) excp;
errinfo.Bytes_Provided = 0;
QMHRTVM(rtvmbuf, sizeof rtvmbuf, "RTVM0300", p->Msg_Id,
"QCPFMSG QSYS ", p->Ex_Data, p->Msg_Data_Len,
"*YES ", "*NO ", &errinfo);
imp = offset_by(Qmh_Rtvm_RTVM0300_t, rtvmbuf, 0);
if (!(dlflags & MULTIBUF))
q = &static_buf;
else if (!(q = (dlts_t *) pthread_getspecific(dlkey)))
q = &static_buf;
if (i = imp->Length_Message_Returned)
cp = offset_by(char, imp, imp->Offset_Message_Returned);
else if (i = imp->Length_Help_Returned)
cp = offset_by(char, imp, imp->Offset_Help_Returned);
else {
q->iserror = 0;
return;
}
q->iserror = 1;
if (i > sizeof q->str - 1)
i = sizeof q->str - 1;
memcpy(q->str, cp, i);
q->str[i] = '\0';
}
static int
dlparentpath(const char * path, size_t len)
{
if (len <= 1)
return len;
while (path[--len] != '/')
;
return len? len: 1;
}
static int
dlmakepath(char * path, size_t pathlen, const char * tail, size_t taillen)
{
int i;
if (taillen && tail[0] == '/')
pathlen = 0;
for (;;) {
while (taillen && *tail == '/') {
tail++;
taillen--;
}
if (!taillen)
break;
for (i = 0; i < taillen; i++)
if (tail[i] == '/')
break;
if (*tail == '.')
switch (i) {
case 2:
if (tail[1] != '.')
break;
pathlen = dlparentpath(path, pathlen);
case 1:
tail += i;
taillen -= i;
continue;
}
if (pathlen + i + 1 >= MAXPATHLEN) {
errno = ENAMETOOLONG;
return -1;
}
path[pathlen++] = '/';
memcpy(path + pathlen, tail, i);
pathlen += i;
}
if (!pathlen)
path[pathlen++] = '/';
path[pathlen] = '\0';
return pathlen;
}
static int
dlresolveLink(const char * path, char * buf, size_t bufsiz)
{
int n;
int l1;
int l2;
struct stat sbuf;
char buf1[MAXPATHLEN + 1];
char buf2[MAXPATHLEN + 1];
if (!buf) {
errno = EFAULT;
return -1;
}
if (!path || !*path || !bufsiz) {
errno = EINVAL;
return -1;
}
if (*path != '/') {
if (!getcwd(buf1, sizeof buf1))
return -1;
l1 = strlen(buf1);
}
else
l1 = 0;
l1 = dlmakepath(buf1, l1, path, strlen(path));
n = 0;
for (;;) {
if (l1 < 0)
return -1;
if (n++ >= 256) {
errno = ELOOP;
return -1;
}
if (lstat(buf1, &sbuf)) {
if (errno == ENOENT)
break;
return -1;
}
if (!S_ISLNK(sbuf.st_mode))
break;
if (sbuf.st_size > MAXPATHLEN) {
errno = ENAMETOOLONG;
return -1;
}
l2 = readlink(buf1, buf2, MAXPATHLEN + 1);
if (l2 < 0)
return -1;
if (buf2[0] != '/')
l1 = dlparentpath(buf1, l1);
l1 = dlmakepath(buf1, l1, buf2, l2);
}
if (l1 >= bufsiz) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(buf, buf1, l1 + 1);
return l1;
}
static int
dlGetObjectName(Qp0l_QSYS_Info_t * qsysinfo, const char * dir,
int dirlen, const char * link)
{
int n;
char * namebuf;
Qlg_Path_Name_T * qptp;
char pathbuf[sizeof(Qlg_Path_Name_T) + _QP0L_DIR_NAME_LG + 4];
Qus_EC_t errinfo;
struct stat sbuf;
if (!qsysinfo) {
errno = EFAULT;
return -1;
}
if (!dir && !link) {
errno = EINVAL;
return -1;
}
qptp = (Qlg_Path_Name_T *) pathbuf;
namebuf = pathbuf + sizeof(Qlg_Path_Name_T);
n = 0;
if (dir) {
if (dirlen < 0 || dirlen > _QP0L_DIR_NAME_LG + 4)
dirlen = _QP0L_DIR_NAME_LG + 4;
while (*dir && n < dirlen)
namebuf[n++] = *dir++;
}
if (n && namebuf[n - 1] == '/')
n--;
if (link) {
if (*link && *link != '/' && n < _QP0L_DIR_NAME_LG + 4)
namebuf[n++] = '/';
while (*link && n < _QP0L_DIR_NAME_LG + 4)
namebuf[n++] = *link++;
}
if (!n || n > _QP0L_DIR_NAME_LG) {
errno = ENAMETOOLONG;
return -1;
}
namebuf[n] = '\0';
n = dlresolveLink(namebuf, namebuf, _QP0L_DIR_NAME_LG + 1);
if (n == -1)
return -1;
if (stat(namebuf, &sbuf))
return -1;
memset((char *) qptp, 0, sizeof *qptp);
qptp->Path_Length = n;
qptp->Path_Name_Delimiter[0] = '/';
errinfo.Bytes_Provided = sizeof errinfo;
Qp0lCvtPathToQSYSObjName(qptp, qsysinfo, "QSYS0100", sizeof *qsysinfo,
0, &errinfo);
return errinfo.Bytes_Available? -1: 0;
}
static const char *
getcomponent(char * dst, const char * src)
{
int i;
for (i = 0;; src++) {
if (!*src || *src == ' ' || *src == '/') {
*dst = '\0';
return src;
}
if (i < 10) {
*dst++ = toupper(*src);
i++;
}
}
}
static int
dlpath2QSYS(Qp0l_QSYS_Info_t * qsysinfo, const char * path, const char * dftlib)
{
unsigned int flags;
char * cp;
if (!qsysinfo || !path) {
errno = EFAULT;
return -1;
}
while (*path == ' ')
path++;
if (!*path) {
errno = EINVAL;
return -1;
}
memset((char *) qsysinfo, 0, sizeof *qsysinfo);
if (*path == '/') {
while (*++path == '/')
;
if (!*path || *path == ' ')
strcpy(qsysinfo->Lib_Name, "QSYS");
else
path = getcomponent(qsysinfo->Lib_Name, path);
if (*path == '/') {
while (*++path == '/')
;
if (*path && *path != ' ')
path = getcomponent(qsysinfo->Obj_Name, path);
}
}
else {
path = getcomponent(qsysinfo->Obj_Name, path);
while (*path == '/')
path++;
if (*path && *path != ' ') {
strcpy(qsysinfo->Lib_Name, qsysinfo->Obj_Name);
memset(qsysinfo->Obj_Name, 0,
sizeof qsysinfo->Obj_Name);
path = getcomponent(qsysinfo->Obj_Name, path);
}
else
strcpy(qsysinfo->Lib_Name, dftlib? dftlib: "*LIBL");
}
while (*path == '/')
path++;
if (*path && *path != ' ') {
path = getcomponent(qsysinfo->Mbr_Name, path);
strcpy(qsysinfo->Mbr_Type, "*MBR");
while (*path == '/')
path++;
}
strcpy(qsysinfo->Lib_Type, "*LIB");
if (qsysinfo->Obj_Name[0])
strcpy(qsysinfo->Obj_Type, "*FILE");
qsysinfo->Bytes_Returned = sizeof *qsysinfo;
qsysinfo->Bytes_Available = sizeof *qsysinfo;
while (*path == ' ')
path++;
if (*path) {
errno = EINVAL;
return -1;
}
return 0;
}
static int
dl_ifs_link(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
{
return dlGetObjectName(qsysinfo, (const char *) NULL, 0, pathname);
}
static int
dl_path_link(Qp0l_QSYS_Info_t * qsysinfo, const char * pathvar,
const char * filename, int (* testproc)(const Qp0l_QSYS_Info_t *))
{
const char * p;
const char * q;
unsigned int i;
const char * path;
i = _QP0L_DIR_NAME_LG;
for (p = filename; *p; p++)
if (*p == '/' || !--i)
return -1;
path = getenv(pathvar);
if (!path)
return -1;
q = path;
if (!*q)
return -1;
for (;;) {
for (p = q; *p && *p != ':'; p++)
;
if (p > q)
if (!dlGetObjectName(qsysinfo, q, p - q, filename))
if (!testproc || (*testproc)(qsysinfo))
return 0;
if (!*p)
break;
q = p + 1;
}
errno = ENOENT;
return -1;
}
static int
dl_DB2_path(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
{
if (dlpath2QSYS(qsysinfo, pathname, (const char *) NULL))
return -1;
if (qsysinfo->Mbr_Type[0])
return -1;
if (!qsysinfo->Obj_Type[0])
return -1;
strcpy(qsysinfo->Obj_Type, "*SRVPGM");
return 0;
}
static int
dl_DB2_name(char * dst, const char * name)
{
int i;
for (i = 0; i < 10; i++) {
switch (*name) {
default:
if (!islower(*name))
break;
case '\0':
case '/':
case ' ':
return -1;
}
*dst++ = *name++;
}
if (!i)
return -1;
*dst = '\0';
return 0;
}
static int
dl_qualified_object(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
{
memset((char *) qsysinfo, 0, sizeof *qsysinfo);
if (dl_DB2_name(qsysinfo->Obj_Name, pathname) ||
dl_DB2_name(qsysinfo->Lib_Name, pathname + 10))
return -1;
strcpy(qsysinfo->Lib_Type, "*LIB");
strcpy(qsysinfo->Obj_Type, "*SRVPGM");
return 0;
}
static int
dl_lib_object(Qp0l_QSYS_Info_t * qsysinfo,
const char * libname, const char * pathname)
{
int i;
char * cp;
strcpy(qsysinfo->Lib_Name, libname);
strcpy(qsysinfo->Lib_Type, "*LIB");
strcpy(qsysinfo->Obj_Type, "*SRVPGM");
cp = qsysinfo->Obj_Name;
while (*pathname == ' ')
pathname++;
for (i = 0;; pathname++) {
switch (*pathname) {
case '\0':
case ' ':
break;
case '/':
return -1;
default:
if (i < 10)
*cp++ = toupper(*pathname);
i++;
continue;
}
break;
}
while (*pathname == ' ')
pathname++;
if (!i || *pathname)
return -1;
*cp = '\0';
return 0;
}
static int
dl_is_srvpgm(const Qp0l_QSYS_Info_t * qsysinfo)
{
struct stat sbuf;
char namebuf[100];
if (!qsysinfo->Lib_Name[0] || strcmp(qsysinfo->Lib_Type, "*LIB") ||
!qsysinfo->Obj_Name[0] || strcmp(qsysinfo->Obj_Type, "*SRVPGM") ||
qsysinfo->Mbr_Name[0] || qsysinfo->Mbr_Type[0])
return 0;
sprintf(namebuf, "%s/%s.LIB/%s.SRVPGM",
strcmp(qsysinfo->Lib_Name, "QSYS")? "/QSYS.LIB": "",
qsysinfo->Lib_Name, qsysinfo->Obj_Name);
return stat(namebuf, &sbuf) == 0;
}
static int
dlreinit(dlinfo * dlip)
{
RINZ_TEMPL_T t;
RINZ_TEMPL_T * p;
volatile _INTRPT_Hndlr_Parms_T excbuf;
if (dlip->actinfo.Flags & QLE_ABP_WAS_ACTIVE)
return 0;
#pragma exception_handler(err, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
p = &t;
t.rinz_pgm = dlip->pointer;
t.rinz_agpmk = dlip->actinfo.Act_Grp_Mark;
_RINZSTAT(p);
#pragma disable_handler
return 0;
err:
if (!memcmp((char *) excbuf.Msg_Id, "MCH4421", 7))
return 0;
dlseterror_from_exception(&excbuf);
return -1;
}
void *
dlsym(void * handle, const char * symbol)
{
dlinfo * dlip;
void * p;
int export_type;
Qus_EC_t errinfo;
volatile _INTRPT_Hndlr_Parms_T excbuf;
static int zero = 0;
dlthreadinit();
if (!handle || !symbol) {
dlseterror_from_errno(EFAULT);
return (void *) NULL;
}
dlip = (dlinfo *) handle;
#pragma exception_handler(error, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
errinfo.Bytes_Provided = 0;
QleGetExpLong(&dlip->actinfo.Act_Mark, &zero, &zero,
(char *) symbol, &p, &export_type, &errinfo);
return p;
#pragma disable_handler
error:
dlseterror_from_exception(&excbuf);
return (void *) NULL;
}
int
dlclose(void * handle)
{
dlinfo * dlip;
void (* _fini)(void);
dlthreadinit();
if (!handle) {
dlseterror_from_errno(EFAULT);
return -1;
}
dlip = (dlinfo *) handle;
if (dlip->actcount) {
if (--(dlip->actcount))
return 0;
if (_fini = dlsym(handle, "_fini"))
(*_fini)();
}
return dlreinit(dlip);
}
static void *
dlopenqsys(const Qp0l_QSYS_Info_t * dllinfo)
{
dlinfo * dlip;
dlinfo * dlip2;
void (* _init)(void);
unsigned int i;
_SYSPTR pgmptr;
unsigned long long actmark;
Qus_EC_t errinfo;
char actmarkstr[2 * sizeof actmark + 1];
static int actinfo_size = sizeof dlip->actinfo;
volatile _INTRPT_Hndlr_Parms_T excbuf;
#pragma exception_handler(error1, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
pgmptr = rslvsp(WLI_SRVPGM, (char *) dllinfo->Obj_Name,
(char *) dllinfo->Lib_Name ,_AUTH_NONE);
if (!pgmptr) {
errno = ENOENT;
return (void *) NULL;
}
dlip = (dlinfo *) malloc(sizeof *dlip);
if (!dlip)
return (void *) NULL;
#pragma disable_handler
dllock();
#pragma exception_handler(error2, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
memset((char *) dlip, 0, sizeof *dlip);
dlip->pointer = pgmptr;
errinfo.Bytes_Provided = 0;
QleActBndPgmLong(&pgmptr, &actmark,
&dlip->actinfo, &actinfo_size, &errinfo);
dlip->actinfo.Act_Mark = actmark;
for (i = 0; actmark; actmark >>= 6)
actmarkstr[i++] = 0x40 + (actmark & 0x3F);
actmarkstr[i] = '\0';
dlip2 = (dlinfo *) xmlHashLookup(dldir, actmarkstr);
if (dlip2) {
free((char *) dlip);
dlip = dlip2;
}
else if (xmlHashAddEntry(dldir, (const xmlChar *) actmarkstr, dlip)) {
dlreinit(dlip);
free((char *) dlip);
dlunlock();
return (void *) NULL;
}
#pragma disable_handler
#pragma exception_handler(error2, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
if (!(dlip->actcount++) && (_init = dlsym(dlip, "_init")))
(*_init)();
dlunlock();
return (void *) dlip;
#pragma disable_handler
error2:
free((char *) dlip);
dlunlock();
error1:
dlseterror_from_exception(&excbuf);
return (void *) NULL;
}
void *
dlopen(const char * filename, int flag)
{
void * dlhandle;
int sverrno;
Qp0l_QSYS_Info_t dllinfo;
sverrno = errno;
errno = 0;
dlthreadinit();
if (!filename) {
dlseterror_from_errno(EFAULT);
errno = sverrno;
return NULL;
}
if (!dl_ifs_link(&dllinfo, filename) && dl_is_srvpgm(&dllinfo))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_path_link(&dllinfo,
"LD_LIBRARY_PATH", filename, dl_is_srvpgm))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_path_link(&dllinfo, "PATH", filename, dl_is_srvpgm))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_DB2_path(&dllinfo, filename) && dl_is_srvpgm(&dllinfo))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_qualified_object(&dllinfo, filename) &&
dl_is_srvpgm(&dllinfo))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_lib_object(&dllinfo, "*CURLIB", filename) &&
dl_is_srvpgm(&dllinfo))
dlhandle = dlopenqsys(&dllinfo);
else if (!dl_lib_object(&dllinfo, "*LIBL", filename) &&
dl_is_srvpgm(&dllinfo))
dlhandle = dlopenqsys(&dllinfo);
else
dlhandle = NULL;
if (!dlhandle && errno)
dlseterror_from_errno(errno);
errno = sverrno;
return dlhandle;
}