2 *** dlopen(), dlclose() dlsym(), dlerror() emulation for OS/400.
4 *** See Copyright for the status of this software.
6 *** Author: Patrick Monnerat <pm@datasphere.ch>, DATASPHERE S.A.
19 #include <sys/types.h>
22 #include <except.h> /* AS400 exceptions. */
23 #include <miptrnam.h> /* MI pointers support. */
24 #include <qusec.h> /* Error structures. */
25 #include <qp0lstdi.h> /* Path to QSYS object name. */
26 #include <qp0z1170.h> /* For Qp0zInitEnv(). */
27 #include <qleawi.h> /* For QleActBndPgmLong() definitions. */
28 #include <qsy.h> /* Qualified name structure. */
29 #include <qmhrtvm.h> /* Retrieve message from message file. */
31 #include <mih/rinzstat.h>
32 #include <mih/matactex.h>
34 #include "libxml/hash.h"
39 *** Maximum internal path length.
42 #define MAXPATHLEN 5120
46 *** Maximum error string length.
49 #define MAX_ERR_STR 511
53 *** Field address macro.
56 #define offset_by(t, b, o) ((t *) ((char *) (b) + (unsigned int) (o)))
63 #define INITED 000001 /* Package has been initialized. */
64 #define THREADS 000002 /* Multithreaded job. */
65 #define MULTIBUF 000004 /* One error buffer per thread. */
69 *** DLL handle private structure.
73 Qle_ABP_Info_Long_t actinfo; /* Activation information. */
74 _SYSPTR pointer; /* Pointer to DLL object. */
75 unsigned int actcount; /* Activation count. */
80 *** Per-thread structure.
84 unsigned int lockcount; /* Mutex lock count. */
85 unsigned int iserror; /* Flag error present. */
86 char str[MAX_ERR_STR + 1]; /* Error string buffer. */
90 static pthread_mutex_t dlmutex = PTHREAD_MUTEX_INITIALIZER;
91 static xmlHashTablePtr dldir = (xmlHashTablePtr) NULL; /* DLL directory. */
92 static unsigned int dlflags = 0; /* Package flags. */
93 static pthread_key_t dlkey;
94 static dlts_t static_buf; /* Static error buffer. */
99 dlthreadterm(void * mem)
103 pthread_setspecific(dlkey, NULL);
113 if (dlflags & MULTIBUF) {
114 p = pthread_getspecific(dlkey);
120 if (dlflags & THREADS)
121 pthread_mutex_lock(&dlmutex);
124 xmlHashFree(dldir, (xmlHashDeallocator) NULL);
128 if (dlflags & MULTIBUF)
129 pthread_key_delete(dlkey);
131 dlflags |= ~(INITED | MULTIBUF);
132 pthread_mutex_unlock(&dlmutex);
133 pthread_mutex_destroy(&dlmutex);
144 *** Initialize the package.
145 *** Should be call once per process.
148 locked = !pthread_mutex_lock(&dlmutex);
150 if (!(dlflags & INITED)) {
157 dldir = xmlHashCreate(16);
158 dlflags &= ~MULTIBUF;
160 if (dlflags & THREADS)
161 if (!pthread_key_create(&dlkey, dlthreadterm))
169 pthread_mutex_unlock(&dlmutex);
179 if (!(dlflags & INITED))
182 if (dlflags & MULTIBUF) {
183 p = pthread_getspecific(dlkey);
186 p = (dlts_t *) malloc(sizeof *p);
192 if (pthread_setspecific(dlkey, p))
206 if (!(dlflags & THREADS))
209 if (dlflags & MULTIBUF) {
210 p = pthread_getspecific(dlkey);
212 if (p && p->lockcount) {
220 if (pthread_mutex_lock(&dlmutex))
234 if (!(dlflags & THREADS))
237 if (dlflags & MULTIBUF) {
238 p = pthread_getspecific(dlkey);
240 if (p && p->lockcount > 1) {
248 if (pthread_mutex_unlock(&dlmutex))
264 if (!(dlflags & MULTIBUF))
266 else if (!(p = (dlts_t *) pthread_getspecific(dlkey)))
270 return (const char *) NULL;
278 dlseterror_from_errno(unsigned int err_no)
283 if (!(dlflags & MULTIBUF))
285 else if (!(p = (dlts_t *) pthread_getspecific(dlkey)))
288 strcpy(p->str, strerror(err_no));
294 dlseterror_from_exception(volatile _INTRPT_Hndlr_Parms_T * excp)
298 Qmh_Rtvm_RTVM0300_t * imp;
300 _INTRPT_Hndlr_Parms_T * p;
305 p = (_INTRPT_Hndlr_Parms_T *) excp;
306 errinfo.Bytes_Provided = 0; /* Exception on error. */
307 QMHRTVM(rtvmbuf, sizeof rtvmbuf, "RTVM0300", p->Msg_Id,
308 "QCPFMSG QSYS ", p->Ex_Data, p->Msg_Data_Len,
309 "*YES ", "*NO ", &errinfo);
310 imp = offset_by(Qmh_Rtvm_RTVM0300_t, rtvmbuf, 0);
312 if (!(dlflags & MULTIBUF))
314 else if (!(q = (dlts_t *) pthread_getspecific(dlkey)))
317 if (i = imp->Length_Message_Returned)
318 cp = offset_by(char, imp, imp->Offset_Message_Returned);
319 else if (i = imp->Length_Help_Returned)
320 cp = offset_by(char, imp, imp->Offset_Help_Returned);
328 if (i > sizeof q->str - 1)
329 i = sizeof q->str - 1;
331 memcpy(q->str, cp, i);
337 dlparentpath(const char * path, size_t len)
343 while (path[--len] != '/')
351 dlmakepath(char * path, size_t pathlen, const char * tail, size_t taillen)
356 if (taillen && tail[0] == '/')
360 while (taillen && *tail == '/') {
368 for (i = 0; i < taillen; i++)
379 pathlen = dlparentpath(path, pathlen);
387 if (pathlen + i + 1 >= MAXPATHLEN) {
388 errno = ENAMETOOLONG;
392 path[pathlen++] = '/';
393 memcpy(path + pathlen, tail, i);
398 path[pathlen++] = '/';
400 path[pathlen] = '\0';
406 dlresolveLink(const char * path, char * buf, size_t bufsiz)
413 char buf1[MAXPATHLEN + 1];
414 char buf2[MAXPATHLEN + 1];
417 *** Resolve symbolic link to IFS object name.
425 if (!path || !*path || !bufsiz) {
431 if (!getcwd(buf1, sizeof buf1))
439 l1 = dlmakepath(buf1, l1, path, strlen(path));
451 if (lstat(buf1, &sbuf)) {
458 if (!S_ISLNK(sbuf.st_mode))
461 if (sbuf.st_size > MAXPATHLEN) {
462 errno = ENAMETOOLONG;
466 l2 = readlink(buf1, buf2, MAXPATHLEN + 1);
472 l1 = dlparentpath(buf1, l1);
474 l1 = dlmakepath(buf1, l1, buf2, l2);
478 errno = ENAMETOOLONG;
482 memcpy(buf, buf1, l1 + 1);
488 dlGetObjectName(Qp0l_QSYS_Info_t * qsysinfo, const char * dir,
489 int dirlen, const char * link)
494 Qlg_Path_Name_T * qptp;
495 char pathbuf[sizeof(Qlg_Path_Name_T) + _QP0L_DIR_NAME_LG + 4];
500 *** Get QSYS object library/name/member and type corresponding to
501 *** the symbolic `link' in directory `dir'.
514 qptp = (Qlg_Path_Name_T *) pathbuf;
515 namebuf = pathbuf + sizeof(Qlg_Path_Name_T);
523 if (dirlen < 0 || dirlen > _QP0L_DIR_NAME_LG + 4)
524 dirlen = _QP0L_DIR_NAME_LG + 4;
526 while (*dir && n < dirlen)
527 namebuf[n++] = *dir++;
530 if (n && namebuf[n - 1] == '/')
534 if (*link && *link != '/' && n < _QP0L_DIR_NAME_LG + 4)
537 while (*link && n < _QP0L_DIR_NAME_LG + 4)
538 namebuf[n++] = *link++;
541 if (!n || n > _QP0L_DIR_NAME_LG) {
542 errno = ENAMETOOLONG;
547 n = dlresolveLink(namebuf, namebuf, _QP0L_DIR_NAME_LG + 1);
552 if (stat(namebuf, &sbuf))
555 memset((char *) qptp, 0, sizeof *qptp);
556 qptp->Path_Length = n;
557 qptp->Path_Name_Delimiter[0] = '/';
558 errinfo.Bytes_Provided = sizeof errinfo;
559 Qp0lCvtPathToQSYSObjName(qptp, qsysinfo, "QSYS0100", sizeof *qsysinfo,
561 return errinfo.Bytes_Available? -1: 0;
566 getcomponent(char * dst, const char * src)
572 *** Get a path component of at most 10 characters and
573 *** map it to upper case.
574 *** Return the address of the next delimiter in source.
577 for (i = 0;; src++) {
578 if (!*src || *src == ' ' || *src == '/') {
584 *dst++ = toupper(*src);
592 dlpath2QSYS(Qp0l_QSYS_Info_t * qsysinfo, const char * path, const char * dftlib)
599 *** Convert the given path to a QSYS object name.
600 *** Syntax rules for paths are:
602 *** '/'+ [ <library> [ '/'+ <file> [ '/'+ <member> ] ] '/'* ]
603 *** <library> '/'+ <file> [ '/'+ <member> ] '/'*
606 *** If default library is not given, *LIBL is assumed.
607 *** Components may no contain spaces. They are translated to
608 *** uppercase. Only the first 10 characters are significant.
609 *** There is no check for the validity of the given components and
610 *** for the object existence.
611 *** Component types are not in the path, but generated internally.
612 *** CCSID is not processed.
614 *** Return 0 upon success, else -1.
617 if (!qsysinfo || !path) {
623 *** Strip leading spaces.
630 *** Check for null path.
639 *** Preset the result structure.
642 memset((char *) qsysinfo, 0, sizeof *qsysinfo);
645 *** Determine the format.
650 *** Library component present.
653 while (*++path == '/')
656 if (!*path || *path == ' ')
657 strcpy(qsysinfo->Lib_Name, "QSYS");
659 path = getcomponent(qsysinfo->Lib_Name, path);
662 *** Check for file component and get it.
666 while (*++path == '/')
669 if (*path && *path != ' ')
670 path = getcomponent(qsysinfo->Obj_Name, path);
675 *** The mandatory component is the <file>.
678 path = getcomponent(qsysinfo->Obj_Name, path);
684 *** If there is a second component, move the first to
685 *** the library name and parse the file name.
688 if (*path && *path != ' ') {
689 strcpy(qsysinfo->Lib_Name, qsysinfo->Obj_Name);
690 memset(qsysinfo->Obj_Name, 0,
691 sizeof qsysinfo->Obj_Name);
692 path = getcomponent(qsysinfo->Obj_Name, path);
695 strcpy(qsysinfo->Lib_Name, dftlib? dftlib: "*LIBL");
699 *** Check and set-up member.
705 if (*path && *path != ' ') {
706 path = getcomponent(qsysinfo->Mbr_Name, path);
707 strcpy(qsysinfo->Mbr_Type, "*MBR");
713 strcpy(qsysinfo->Lib_Type, "*LIB");
715 if (qsysinfo->Obj_Name[0])
716 strcpy(qsysinfo->Obj_Type, "*FILE");
718 qsysinfo->Bytes_Returned = sizeof *qsysinfo;
719 qsysinfo->Bytes_Available = sizeof *qsysinfo;
722 *** Strip trailing spaces.
738 dl_ifs_link(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
742 *** If `pathname' is a link found in IFS, set `qsysinfo' to its
744 *** Return 0 if OK, else -1.
747 return dlGetObjectName(qsysinfo, (const char *) NULL, 0, pathname);
752 dl_path_link(Qp0l_QSYS_Info_t * qsysinfo, const char * pathvar,
753 const char * filename, int (* testproc)(const Qp0l_QSYS_Info_t *))
762 *** If `filename' is not a path and is a link found in one of the
763 *** colon-separated paths in environment variable `pathvar',
764 *** set `qsysinfo' to its DB2 name.
765 *** Return 0 if OK, else -1.
768 i = _QP0L_DIR_NAME_LG;
770 for (p = filename; *p; p++)
771 if (*p == '/' || !--i)
772 return -1; /* Too long or a path. */
775 *** Make sure we have the LD_LIBRARY_PATH environment
779 path = getenv(pathvar);
782 return -1; /* No path list. */
785 *** Try in each path listed.
791 return -1; /* No path list. */
794 for (p = q; *p && *p != ':'; p++)
797 if (p > q) /* Ignore null path. */
798 if (!dlGetObjectName(qsysinfo, q, p - q, filename))
799 if (!testproc || (*testproc)(qsysinfo))
800 return 0; /* Found: return. */
814 dl_DB2_path(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
817 if (dlpath2QSYS(qsysinfo, pathname, (const char *) NULL))
820 if (qsysinfo->Mbr_Type[0])
821 return -1; /* Service program may not have members. */
823 if (!qsysinfo->Obj_Type[0])
824 return -1; /* Object must be specified. */
826 strcpy(qsysinfo->Obj_Type, "*SRVPGM"); /* Set our object type. */
832 dl_DB2_name(char * dst, const char * name)
837 for (i = 0; i < 10; i++) {
862 dl_qualified_object(Qp0l_QSYS_Info_t * qsysinfo, const char * pathname)
865 memset((char *) qsysinfo, 0, sizeof *qsysinfo);
867 if (dl_DB2_name(qsysinfo->Obj_Name, pathname) ||
868 dl_DB2_name(qsysinfo->Lib_Name, pathname + 10))
871 strcpy(qsysinfo->Lib_Type, "*LIB");
872 strcpy(qsysinfo->Obj_Type, "*SRVPGM");
878 dl_lib_object(Qp0l_QSYS_Info_t * qsysinfo,
879 const char * libname, const char * pathname)
885 strcpy(qsysinfo->Lib_Name, libname);
886 strcpy(qsysinfo->Lib_Type, "*LIB");
887 strcpy(qsysinfo->Obj_Type, "*SRVPGM");
888 cp = qsysinfo->Obj_Name;
890 while (*pathname == ' ')
893 for (i = 0;; pathname++) {
905 *cp++ = toupper(*pathname);
914 while (*pathname == ' ')
926 dl_is_srvpgm(const Qp0l_QSYS_Info_t * qsysinfo)
932 if (!qsysinfo->Lib_Name[0] || strcmp(qsysinfo->Lib_Type, "*LIB") ||
933 !qsysinfo->Obj_Name[0] || strcmp(qsysinfo->Obj_Type, "*SRVPGM") ||
934 qsysinfo->Mbr_Name[0] || qsysinfo->Mbr_Type[0])
938 *** Build the IFS path name for the DB2 object.
941 sprintf(namebuf, "%s/%s.LIB/%s.SRVPGM",
942 strcmp(qsysinfo->Lib_Name, "QSYS")? "/QSYS.LIB": "",
943 qsysinfo->Lib_Name, qsysinfo->Obj_Name);
945 return stat(namebuf, &sbuf) == 0;
950 dlreinit(dlinfo * dlip)
955 volatile _INTRPT_Hndlr_Parms_T excbuf;
957 if (dlip->actinfo.Flags & QLE_ABP_WAS_ACTIVE)
961 *** Attempt to reinitialize the service program that was loaded.
962 *** The service program must be created to allow re-initialization:
963 *** ALWRINZ(*YES) for this to work. The default is
967 #pragma exception_handler(err, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
969 t.rinz_pgm = dlip->pointer;
970 t.rinz_agpmk = dlip->actinfo.Act_Grp_Mark;
972 #pragma disable_handler
977 if (!memcmp((char *) excbuf.Msg_Id, "MCH4421", 7))
978 return 0; /* Program cannot be reinitialized. */
980 dlseterror_from_exception(&excbuf);
986 dlsym(void * handle, const char * symbol)
993 volatile _INTRPT_Hndlr_Parms_T excbuf;
998 if (!handle || !symbol) {
999 dlseterror_from_errno(EFAULT);
1000 return (void *) NULL;
1003 dlip = (dlinfo *) handle;
1005 #pragma exception_handler(error, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
1006 errinfo.Bytes_Provided = 0;
1007 QleGetExpLong(&dlip->actinfo.Act_Mark, &zero, &zero,
1008 (char *) symbol, &p, &export_type, &errinfo);
1010 #pragma disable_handler
1013 dlseterror_from_exception(&excbuf);
1014 return (void *) NULL;
1019 dlclose(void * handle)
1023 void (* _fini)(void);
1028 dlseterror_from_errno(EFAULT);
1032 dlip = (dlinfo *) handle;
1034 if (dlip->actcount) {
1035 if (--(dlip->actcount))
1038 if (_fini = dlsym(handle, "_fini"))
1042 return dlreinit(dlip);
1047 dlopenqsys(const Qp0l_QSYS_Info_t * dllinfo)
1052 void (* _init)(void);
1055 unsigned long long actmark;
1057 char actmarkstr[2 * sizeof actmark + 1];
1058 static int actinfo_size = sizeof dlip->actinfo;
1059 volatile _INTRPT_Hndlr_Parms_T excbuf;
1062 *** Capture any type of error and if any occurs,
1063 *** return not found.
1066 #pragma exception_handler(error1, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
1067 pgmptr = rslvsp(WLI_SRVPGM, (char *) dllinfo->Obj_Name,
1068 (char *) dllinfo->Lib_Name ,_AUTH_NONE);
1072 return (void *) NULL;
1076 *** Create a new DLL info block.
1079 dlip = (dlinfo *) malloc(sizeof *dlip);
1082 return (void *) NULL; /* Cannot create block. */
1083 #pragma disable_handler
1087 #pragma exception_handler(error2, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
1088 memset((char *) dlip, 0, sizeof *dlip);
1089 dlip->pointer = pgmptr;
1092 *** Activate the DLL.
1095 errinfo.Bytes_Provided = 0;
1096 QleActBndPgmLong(&pgmptr, &actmark,
1097 &dlip->actinfo, &actinfo_size, &errinfo);
1098 dlip->actinfo.Act_Mark = actmark;
1101 *** Dummy string encoding activation mark to use as hash table key.
1104 for (i = 0; actmark; actmark >>= 6)
1105 actmarkstr[i++] = 0x40 + (actmark & 0x3F);
1107 actmarkstr[i] = '\0';
1110 *** Check if already activated.
1113 dlip2 = (dlinfo *) xmlHashLookup(dldir, actmarkstr);
1116 free((char *) dlip);
1119 else if (xmlHashAddEntry(dldir, (const xmlChar *) actmarkstr, dlip)) {
1121 free((char *) dlip);
1123 return (void *) NULL;
1125 #pragma disable_handler
1127 #pragma exception_handler(error2, excbuf, 0, _C2_MH_ESCAPE, _CTLA_HANDLE_NO_MSG)
1130 *** Bump activation counter.
1133 if (!(dlip->actcount++) && (_init = dlsym(dlip, "_init")))
1139 *** Return the handle.
1142 return (void *) dlip;
1143 #pragma disable_handler
1146 free((char *) dlip);
1150 dlseterror_from_exception(&excbuf);
1151 return (void *) NULL;
1156 dlopen(const char * filename, int flag)
1161 Qp0l_QSYS_Info_t dllinfo;
1169 dlseterror_from_errno(EFAULT);
1175 *** Try to locate the object in the following order:
1176 *** _ `filename' is an IFS path.
1177 *** _ `filename' is not a path and resides in one of
1178 *** LD_LIBRARY_PATH colon-separated paths.
1179 *** _ `filename' is not a path and resides in one of
1180 *** PATH colon-separated paths.
1181 *** _ `filename' is a DB2 path (as /library/object).
1182 *** _ `filename' is a qualified object name.
1183 *** _ `filename' is an object in *CURLIB.
1184 *** _ `filename' is an object in *LIBL.
1187 if (!dl_ifs_link(&dllinfo, filename) && dl_is_srvpgm(&dllinfo))
1188 dlhandle = dlopenqsys(&dllinfo);
1189 else if (!dl_path_link(&dllinfo,
1190 "LD_LIBRARY_PATH", filename, dl_is_srvpgm))
1191 dlhandle = dlopenqsys(&dllinfo);
1192 else if (!dl_path_link(&dllinfo, "PATH", filename, dl_is_srvpgm))
1193 dlhandle = dlopenqsys(&dllinfo);
1194 else if (!dl_DB2_path(&dllinfo, filename) && dl_is_srvpgm(&dllinfo))
1195 dlhandle = dlopenqsys(&dllinfo);
1196 else if (!dl_qualified_object(&dllinfo, filename) &&
1197 dl_is_srvpgm(&dllinfo))
1198 dlhandle = dlopenqsys(&dllinfo);
1199 else if (!dl_lib_object(&dllinfo, "*CURLIB", filename) &&
1200 dl_is_srvpgm(&dllinfo))
1201 dlhandle = dlopenqsys(&dllinfo);
1202 else if (!dl_lib_object(&dllinfo, "*LIBL", filename) &&
1203 dl_is_srvpgm(&dllinfo))
1204 dlhandle = dlopenqsys(&dllinfo);
1208 if (!dlhandle && errno)
1209 dlseterror_from_errno(errno);