| /** |
| *** 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; |
| } |