/**
*** dlopen(), dlclose() dlsym(), dlerror() emulation for OS/400.
***
*** See Copyright for the status of this software.
***
*** Author: Patrick Monnerat <pm@datasphere.ch>, DATASPHERE S.A.
**/
#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> /* AS400 exceptions. */
#include <miptrnam.h> /* MI pointers support. */
#include <qusec.h> /* Error structures. */
#include <qp0lstdi.h> /* Path to QSYS object name. */
#include <qp0z1170.h> /* For Qp0zInitEnv(). */
#include <qleawi.h> /* For QleActBndPgmLong() definitions. */
#include <qsy.h> /* Qualified name structure. */
#include <qmhrtvm.h> /* Retrieve message from message file. */
#include <mih/rinzstat.h>
#include <mih/matactex.h>
#include "libxml/hash.h"
#include "dlfcn.h"
/**
*** Maximum internal path length.
**/
#define MAXPATHLEN 5120
/**
*** Maximum error string length.
**/
#define MAX_ERR_STR 511
/**
*** Field address macro.
**/
#define offset_by(t, b, o) ((t *) ((char *) (b) + (unsigned int) (o)))
/**
*** Global flags.
**/
#define INITED 000001 /* Package has been initialized. */
#define THREADS 000002 /* Multithreaded job. */
#define MULTIBUF 000004 /* One error buffer per thread. */
/**
*** DLL handle private structure.
**/
typedef struct {
Qle_ABP_Info_Long_t actinfo; /* Activation information. */
_SYSPTR pointer; /* Pointer to DLL object. */
unsigned int actcount; /* Activation count. */
} dlinfo;
/**
*** Per-thread structure.
**/
typedef struct {
unsigned int lockcount; /* Mutex lock count. */
unsigned int iserror; /* Flag error present. */
char str[MAX_ERR_STR + 1]; /* Error string buffer. */
} dlts_t;
static pthread_mutex_t dlmutex = PTHREAD_MUTEX_INITIALIZER;
static xmlHashTablePtr dldir = (xmlHashTablePtr) NULL; /* DLL directory. */
static unsigned int dlflags = 0; /* Package flags. */
static pthread_key_t dlkey;
static dlts_t static_buf; /* Static error buffer. */
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;
/**
*** Initialize the package.
*** Should be call once per process.
**/
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; /* Exception on error. */
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];
/**
*** Resolve symbolic link to IFS object name.
**/
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;
/**
*** Get QSYS object library/name/member and type corresponding to
*** the symbolic `link' in directory `dir'.
**/
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;
/**
*** Build full path.
**/
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;
/**
*** Get a path component of at most 10 characters and
*** map it to upper case.
*** Return the address of the next delimiter in source.
**/
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;
/**
*** Convert the given path to a QSYS object name.
*** Syntax rules for paths are:
***
*** '/'+ [ <library> [ '/'+ <file> [ '/'+ <member> ] ] '/'* ]
*** <library> '/'+ <file> [ '/'+ <member> ] '/'*
*** <file> '/'*
***
*** If default library is not given, *LIBL is assumed.
*** Components may no contain spaces. They are translated to
*** uppercase. Only the first 10 characters are significant.
*** There is no check for the validity of the given components and
*** for the object existence.
*** Component types are not in the path, but generated internally.
*** CCSID is not processed.
***
*** Return 0 upon success, else -1.
**/
if (!qsysinfo || !path) {
errno = EFAULT;
return -1;
}
/**
*** Strip leading spaces.
**/
while (*path == ' ')
path++;
/**
*** Check for null path.
**/
if (!*path) {
errno = EINVAL;
return -1;
}
/**
*** Preset the result structure.
**/
memset((char *) qsysinfo, 0, sizeof *qsysinfo);
/**
*** Determine the format.
**/
if (*path == '/') {
/**
*** Library component present.
**/
while (*++path == '/')
;
if (!*path || *path == ' ')
strcpy(qsysinfo->Lib_Name, "QSYS");
else
path = getcomponent(qsysinfo->Lib_Name, path);
/**
*** Check for file component and get it.
**/
if (*path == '/') {
while (*++path == '/')
;
if (*path && *path != ' ')
path = getcomponent(qsysinfo->Obj_Name, path);
}
}
else {
/**
*** The mandatory component is the <file>.
**/
path = getcomponent(qsysinfo->Obj_Name, path);
while (*path == '/')
path++;
/**
*** If there is a second component, move the first to
*** the library name and parse the file name.
**/
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");
}
/**
*** Check and set-up member.
**/
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;
/**
*** Strip trailing spaces.
**/
while (*path == ' ')
path++;
if (*path) {
errno = EINVAL;
return -1;
}
return 0;
}
static int
dl_ifs_link(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
{
/**
*** If `pathname' is a link found in IFS, set `qsysinfo' to its
*** DB2 name.
*** Return 0 if OK, else -1.
**/
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;
/**
*** If `filename' is not a path and is a link found in one of the
*** colon-separated paths in environment variable `pathvar',
*** set `qsysinfo' to its DB2 name.
*** Return 0 if OK, else -1.
**/
i = _QP0L_DIR_NAME_LG;
for (p = filename; *p; p++)
if (*p == '/' || !--i)
return -1; /* Too long or a path. */
/**
*** Make sure we have the LD_LIBRARY_PATH environment
*** variable value.
**/
path = getenv(pathvar);
if (!path)
return -1; /* No path list. */
/**
*** Try in each path listed.
**/
q = path;
if (!*q)
return -1; /* No path list. */
for (;;) {
for (p = q; *p && *p != ':'; p++)
;
if (p > q) /* Ignore null path. */
if (!dlGetObjectName(qsysinfo, q, p - q, filename))
if (!testproc || (*testproc)(qsysinfo))
return 0; /* Found: return. */
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; /* Service program may not have members. */
if (!qsysinfo->Obj_Type[0])
return -1; /* Object must be specified. */
strcpy(qsysinfo->Obj_Type, "*SRVPGM"); /* Set our object type. */
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;
/**
*** Build the IFS path name for the DB2 object.
**/
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;
/**
*** Attempt to reinitialize the service program that was loaded.
*** The service program must be created to allow re-initialization:
*** ALWRINZ(*YES) for this to work. The default is
*** ALWRINZ(*NO).
**/
#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; /* Program cannot be reinitialized. */
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;
/**
*** Capture any type of error and if any occurs,
*** return not found.
**/
#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;
}
/**
*** Create a new DLL info block.
**/
dlip = (dlinfo *) malloc(sizeof *dlip);
if (!dlip)
return (void *) NULL; /* Cannot create block. */
#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;
/**
*** Activate the DLL.
**/
errinfo.Bytes_Provided = 0;
QleActBndPgmLong(&pgmptr, &actmark,
&dlip->actinfo, &actinfo_size, &errinfo);
dlip->actinfo.Act_Mark = actmark;
/**
*** Dummy string encoding activation mark to use as hash table key.
**/
for (i = 0; actmark; actmark >>= 6)
actmarkstr[i++] = 0x40 + (actmark & 0x3F);
actmarkstr[i] = '\0';
/**
*** Check if already activated.
**/
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)
/**
*** Bump activation counter.
**/
if (!(dlip->actcount++) && (_init = dlsym(dlip, "_init")))
(*_init)();
dlunlock();
/**
*** Return the handle.
**/
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;
}
/**
*** Try to locate the object in the following order:
*** _ `filename' is an IFS path.
*** _ `filename' is not a path and resides in one of
*** LD_LIBRARY_PATH colon-separated paths.
*** _ `filename' is not a path and resides in one of
*** PATH colon-separated paths.
*** _ `filename' is a DB2 path (as /library/object).
*** _ `filename' is a qualified object name.
*** _ `filename' is an object in *CURLIB.
*** _ `filename' is an object in *LIBL.
**/
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;
}