diff --git a/require.c b/require.c index 1de95c660d3c2b9a671670926e0b8a3a565a6113..42a574665daea918191ee43d842c0c724f03dd8b 100644 --- a/require.c +++ b/require.c @@ -1,32 +1,180 @@ -#include <vxWorks.h> -#include <symLib.h> -#include <sysSymTbl.h> -#include <sysLib.h> -#include <symLib.h> -#include <loadLib.h> -#include <shellLib.h> -#include <usrLib.h> -#include <taskLib.h> -#include <stat.h> +/* +* ld - load code dynamically +* +* $Author: zimoch $ +* $ID$ +* $Date: 2011/07/21 14:57:25 $ +* +* DISCLAIMER: Use at your own risc and so on. No warranty, no refund. +*/ +#include <sys/stat.h> #include <stdio.h> -#include <ioLib.h> +#include <stdlib.h> #include <string.h> #include <ctype.h> -#include <require.h> + #include <epicsVersion.h> #ifdef BASE_VERSION #define EPICS_3_13 int dbLoadDatabase(char *filename, char *path, char *substitutions); +extern volatile int interruptAccept; #else #define EPICS_3_14 #include <iocsh.h> -extern int iocshCmd (const char *cmd); #include <dbAccess.h> +extern int iocshCmd (const char *cmd); #include <epicsExit.h> #include <epicsExport.h> #endif -static int validate(char* version, char* loaded) +#include "require.h" + +int requireDebug=0; + +#define DIRSEP "/" +#define PATHSEP ":" +#define PREFIX +#define INFIX + +#if defined (__vxworks) + + #include <symLib.h> + #include <sysSymTbl.h> + #include <sysLib.h> + #include <symLib.h> + #include <loadLib.h> + #include <shellLib.h> + #include <usrLib.h> + #include <taskLib.h> + #include <ioLib.h> + #include <errno.h> + + #define HMODULE MODULE_ID + #undef INFIX + #define INFIX "Lib" + #define EXT ".munch" + +#elif defined (UNIX) + + #include <dlfcn.h> + #define HMODULE void * + + #ifdef CYGWIN32 + + #define EXT ".dll" + + #else + + #undef PREFIX + #define PREFIX "lib" + #define EXT ".so" + + #endif + +#elif defined (_WIN32) + + #include <windows.h> + #undef DIRSEP + #define DIRSEP "\\" + #undef PATHSEP + #define PATHSEP ";" + #define EXT ".dll" + +#else + + #error unknwn OS + +#endif + +/* loadlib (library) +Find a loadable library by name and load it. +*/ + +static HMODULE loadlib(const char* libname) +{ + HMODULE libhandle = NULL; + + if (!libname) + { + fprintf (stderr, "missing library name\n"); + return NULL; + } + +#if defined (UNIX) + if (!(libhandle = dlopen(libname, RTLD_NOW|RTLD_GLOBAL))) + { + fprintf (stderr, "Loading %s library failed: %s\n", + libname, dlerror()); + } +#elif defined (_WIN32) + if (!(libhandle = LoadLibrary(libname))) + { + LPVOID lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + fprintf (stderr, "Loading %s library failed: %s\n", + libname, lpMsgBuf); + LocalFree(lpMsgBuf); + } +#elif defined (__vxworks) + { + int fd, loaderror; + fd = open(libname, O_RDONLY, 0); + loaderror = errno; + if (fd >= 0) + { + errno = 0; + libhandle = loadModule(fd, LOAD_GLOBAL_SYMBOLS); + if (errno == S_symLib_SYMBOL_NOT_FOUND) + { + libhandle = NULL; + } + loaderror = errno; + close (fd); + } + if (libhandle == NULL) + { + fprintf(stderr, "Loading %s library failed: %s\n", + libname, strerror(loaderror)); + } + } +#else + fprintf (stderr, "cannot load libraries on this OS.\n"); +#endif + return libhandle; +} + +typedef struct moduleitem +{ + struct moduleitem* next; + char name[100]; + char version[20]; +} moduleitem; + +moduleitem* loadedModules = NULL; + +const char* getLibVersion(const char* libname) +{ + moduleitem* m; + + for (m = loadedModules; m; m=m->next) + { + if (strncmp(m->name, libname, sizeof(m->name)) == 0) + { + return m->version; + } + } + return NULL; +} + +static int validate(const char* version, const char* loaded) { int lmajor, lminor, lpatch, lmatches; int major, minor, patch, matches; @@ -34,14 +182,14 @@ static int validate(char* version, char* loaded) if (!version || !*version || strcmp(loaded, version) == 0) { /* no version requested or exact match */ - return OK; + return 0; } if (strcmp(loaded, "test") == 0) { /* test version already loaded */ printf("Warning: version is test where %s was requested\n", version); - return OK; + return 0; } /* non-numerical versions must match exactly numerical versions must have exact match in major version and @@ -56,44 +204,109 @@ static int validate(char* version, char* loaded) || (matches >= 2 && minor > lminor) || (matches > 2 && minor == lminor && patch > lpatch)) { - return ERROR; + return -1; } - return OK; + return 0; } -int require(char* lib, char* vers) +/* require (module) +Look if module is already loaded. +If module is already loaded check for version mismatch. +If module is not yet loaded load the library with ld, +load <module>.dbd with dbLoadDatabase (if file exists) +and call <module>_registerRecordDeviceDriver function. + +If require is called from the iocsh before iocInit and fails, +it calls epicsExit to abort the application. +*/ + +#ifdef __vxworks +/* wrapper to abort statup script */ +static int require_priv(const char* module, const char* ver); + +int require(const char* module, const char* ver) { - char** path; - char* loaded; - SYM_TYPE type; - struct stat filestat; - char version[20]; - int fd; - MODULE_ID libhandle = NULL; - - if (symFindByName(sysSymTbl, "LIB", (char**)&path, &type) != OK) + if (require_priv(module, ver) != 0 && !interruptAccept) { - static char* here = "."; - path = &here; + /* require failed in startup script before iocInit */ + fprintf(stderr, "Aborting startup script\n"); + shellScriptAbort(); + return -1; } - if (!lib) + return 0; +} +#define require require_priv +static +#endif + +int require(const char* module, const char* vers) +{ + char* driverpath = "."; + char version[20]; + const char* loaded; + moduleitem* m; + struct stat filestat; + HMODULE libhandle; + char* p; + char *end; /* end of string */ + const char sep[1] = PATHSEP; +#ifdef __vxworks + SYM_TYPE type; +#endif + + if (requireDebug) + printf("require: checking module %s version %s\n", + module, vers); + driverpath = getenv("EPICS_DRIVER_PATH"); + if (requireDebug) + printf("require: searchpath=%s\n", + driverpath); + if (!module) { - printf("Usage: require \"<libname>\" [, \"<version>\"]\n"); - printf("Loads <libname>Lib[-<version>] and dbd/<libname>[-<version>].dbd\n"); - printf("Directory is LIB = %s\n", *path); - return ERROR; + printf("Usage: require \"<module>\" [, \"<version>\"]\n"); + printf("Loads " PREFIX "<module>" INFIX "[-<version>]" EXT " and dbd/<libname>[-<version>].dbd\n"); +#ifdef EPICS_3_14 + printf("And calls <module>_registerRecordDeviceDriver\n"); +#endif + printf("Search path is %s\n", driverpath); + return -1; } bzero(version, sizeof(version)); if (vers) strncpy(version, vers, sizeof(version)); - loaded = getLibVersion(lib); - if (!loaded) + loaded = getLibVersion(module); + if (requireDebug) + printf("require: loaded version of %s is %s\n", + module, loaded); + if (loaded) + { + /* Library already loaded. Check Version. */ + if (validate(version, loaded) != 0) + { + printf("Conflict between requested %s version %s\n" + "and already loaded version %s.\n", + module, version, loaded); + return -1; + } + /* Loaded version is ok */ + printf ("%s %s already loaded\n", module, loaded); + return 0; + } + else { char libname[256]; char dbdname[256]; char depname[256]; + char libdir[256]; + char fulllibname[256]; + char fulldbdname[256]; + char fulldepname[256]; + char symbolname[256]; + /* user may give a minimal version (e.g. "1.2.4+") + load highest matching version (here "1.2") and check later + */ if (version[strlen(version)-1] == '+') { char* p = strrchr(version, '.'); @@ -101,202 +314,243 @@ int require(char* lib, char* vers) *p = 0; } - /* try to find module in local /bin directory first, then in path - prefer *Lib.munch file over *Lib file - check for dependencies in *.dep file - load *.dbd file if it exists - */ - - /* first try local library */ - if (version && *version) + /* make filenames with or without version string */ + + if (version[0]) { - sprintf(libname, "bin/%sLib-%s.munch", lib, version); - sprintf(depname, "bin/%s-%s.dep", lib, version); - sprintf(dbdname, "dbd/%s-%s.dbd", lib, version); + sprintf(libname, PREFIX "%s" INFIX "-%s" EXT, module, version); + sprintf(depname, "%s-%s.dep", module, version); + sprintf(dbdname, "%s-%s.dbd", module, version); } else { - sprintf(libname, "bin/%sLib.munch", lib); - sprintf(depname, "bin/%s.dep", lib); - sprintf(dbdname, "dbd/%s.dbd", lib); + sprintf(libname, PREFIX "%s" INFIX EXT, module); + sprintf(depname, "%s.dep", module); + sprintf(dbdname, "%s.dbd", module); } - if (stat(libname, &filestat) == ERROR) + if (requireDebug) { - /* no munched local lib */ - libname[strlen(libname)-6]=0; /* skip ".munch" */ + printf("require: libname is %s\n", libname); + printf("require: depname is %s\n", depname); + printf("require: dbdname is %s\n", dbdname); } - if (stat(libname, &filestat) == ERROR) - { - /* no local lib at all */ - libname[strlen(libname)-6]=0; /* skip ".munch" */ - if (version && *version) + + /* search for library in driverpath */ + for (p = driverpath; p != NULL; p = end) + { + end = strchr(p, sep[0]); + if (end) { - sprintf(libname, "%s/%sLib-%s.munch", *path, lib, version); - sprintf(depname, "%s/%s-%s.dep", *path, lib, version); - sprintf(dbdname, "%s/dbd/%s-%s.dbd", *path, lib, version); + sprintf (libdir, "%.*s", end-p, p); + end++; } else { - sprintf(libname, "%s/%sLib.munch", *path, lib); - sprintf(depname, "%s/%s.dep", *path, lib); - sprintf(dbdname, "%s/dbd/%s.dbd", *path, lib); - } - if (stat(libname, &filestat) == ERROR) - { - /* no munched lib */ - libname[strlen(libname)-6]=0; /* skip ".munch" */ - } - if (stat(libname, &filestat) == ERROR && - /* allow alias without library */ - stat(depname, &filestat) == ERROR) - { - /* still no library found */ - printf("Library %s not found\n", libname); - printf("Aborting startup stript.\n"); - shellScriptAbort(); - return ERROR; + sprintf (libdir, "%s", p); } + /* ignore empty driverpath elements */ + if (libdir[0] == 0) continue; + + sprintf (fulllibname, "%s" DIRSEP "%s", libdir, libname); + sprintf (fulldepname, "%s" DIRSEP "%s", libdir, depname); + if (requireDebug) + printf("require: looking for %s\n", fulllibname); + if (stat(fulllibname, &filestat) == 0) break; +#ifdef __vxworks + /* now without the .munch */ + fulllibname[strlen(fulllibname)-6] = 0; + if (requireDebug) + printf("require: looking for %s\n", fulllibname); + if (stat(fulllibname, &filestat) == 0) break; +#endif + /* allow dependency without library for aliasing */ + if (requireDebug) + printf("require: looking for %s\n", fulldepname); + if (stat(fulldepname, &filestat) == 0) break; + } + if (requireDebug) + printf("require: found in %s\n", p); + if (!p) + { + fprintf(stderr, "Library %s not found in EPICS_DRIVER_PATH=%s\n", + libname, driverpath); + return -1; } - errno = 0; - /* check dependencies */ - if (stat(depname, &filestat) != ERROR) + /* parse dependency file if exists */ + if (stat(fulldepname, &filestat) == 0) { FILE* depfile; char buffer[40]; - char *l; /* required library */ - char *v; /* required version */ - char *e; /* end */ + char *rmodule; /* required module */ + char *rversion; /* required version */ - depfile = fopen(depname, "r"); + if (requireDebug) + printf("require: parsing dependency file %s\n", fulldepname); + depfile = fopen(fulldepname, "r"); while (fgets(buffer, sizeof(buffer), depfile)) { - l = buffer; - while (isspace((int)*l)) l++; - if (*l == 0 || *l == '#') continue; - v = l; - while (*v && !isspace((int)*v)) v++; - *v++ = 0; - while (isspace((int)*v)) v++; - e = v; - while (*e && !isspace((int)*e)) e++; - *e++ = '+'; - *e = 0; - printf ("%s depends on %s %s\n", lib, l, v); - if (require(l, v) != OK) + rmodule = buffer; + /* ignore leading spaces */ + while (isspace((int)*rmodule)) rmodule++; + /* ignore empty lines and comment lines */ + if (*rmodule == 0 || *rmodule == '#') continue; + /* rmodule at start of module name */ + rversion = rmodule; + /* find end of module name */ + while (*rversion && !isspace((int)*rversion)) rversion++; + /* terminate module name */ + *rversion++ = 0; + /* ignore spaces */ + while (isspace((int)*rversion)) rversion++; + /* rversion at start of version */ + end = rversion; + /* find end of version */ + while (*end && !isspace((int)*end)) end++; + /* append + to version to allow newer compaible versions */ + *end++ = '+'; + /* terminate version */ + *end = 0; + printf("%s depends on %s %s\n", module, rmodule, rversion); + if (require(rmodule, rversion) != 0) { fclose(depfile); - return ERROR; + return -1; } } fclose(depfile); } - if (stat(libname, &filestat) == ERROR) + if (stat(fulllibname, &filestat) != 0) { /* no library, dep file was an alias */ + if (requireDebug) + printf("require: no library to load\n"); return 0; } + /* load library */ - printf("Loading %s\n", libname); - fd = open(libname, O_RDONLY, 0); - if (fd >= 0) + if (requireDebug) + printf("require: loading library %s\n", fulllibname); + if (!(libhandle = loadlib(fulllibname))) { - errno = 0; - libhandle = loadModule(fd, LOAD_GLOBAL_SYMBOLS); - close (fd); + if (requireDebug) + printf("require: loading failed\n"); + return -1; + } + + /* now check if we got what we wanted (with original version number) */ + sprintf (symbolname, "_%sLibRelease", module); +#if defined (UNIX) + loaded = (char*) dlsym(libhandle, symbolname); +#elif defined (_WIN32) + loaded = (char*) GetProcAddress(libhandle, symbolname); +#elif defined (__vxworks) + loaded = NULL; + symFindByName(sysSymTbl, symbolname, (char**)&loaded, &type); +#endif + if (loaded) + { + printf("Loading %s (version %s)\n", fulllibname, loaded); } - if (libhandle == NULL || errno == S_symLib_SYMBOL_NOT_FOUND) + else { - printf("Loading %s library failed: %s\n", lib, strerror(errno)); - printf("Aborting startup stript.\n"); - shellScriptAbort(); - return ERROR; + printf("Loading %s (no version)\n", fulllibname); + loaded = "(no version)"; } - loaded = getLibVersion(lib); - /* load dbd file */ - if (stat(dbdname, &filestat) != ERROR && filestat.st_size > 0) + if (validate(vers, loaded) != 0) + { + fprintf(stderr, "Requested %s version %s not available, found only %s.\n", + module, vers, loaded); + return -1; + } + + /* look for dbd in . ./dbd ../dbd ../../dbd (relative to lib dir) */ + p = PATHSEP DIRSEP "dbd" + PATHSEP DIRSEP ".." DIRSEP "dbd" + PATHSEP DIRSEP ".." DIRSEP ".." DIRSEP "dbd"; + while (p) { - /* If file exists and is not empty */ - printf("Loading %s\n", dbdname); - if (dbLoadDatabase(dbdname, NULL, NULL) != OK) + end = strchr(p, sep[0]); + if (end) { - taskDelay(sysClkRateGet()); - printf ("Aborting startup stript.\n"); - shellScriptAbort(); - return ERROR; + sprintf(fulldbdname, "%s%.*s" DIRSEP "%s", + libdir, end-p, p, dbdname); + end++; } -#ifdef EPICS_3_14 - /* call register function for R3.14 */ + else { - char initfunc[256]; - - sprintf (initfunc, "%s_registerRecordDeviceDriver", lib); - printf("calling %s\n", initfunc); - iocshCmd (initfunc); + sprintf(fulldbdname, "%s%s" DIRSEP "%s", + libdir, p, dbdname); + } + if (requireDebug) + printf("require: Looking for %s\n", fulldbdname); + if (stat(fulldbdname, &filestat) == 0) break; + p=end; + } + + /* if dbd file exists and is not empty load it */ + if (p && filestat.st_size > 0) + { + printf("Loading %s\n", fulldbdname); + if (dbLoadDatabase(fulldbdname, NULL, NULL) != 0) + { + fprintf (stderr, "require: can't load %s\n", fulldbdname); + return -1; } -#endif } - if (validate(vers, loaded) == ERROR) + else { - printf("Requested %s version %s not available, found only %s.\n" - "Aborting startup stript.\n", - lib, vers, loaded); - shellScriptAbort(); - return ERROR; + /* no dbd file, but that might be OK */ + printf("no dbd file %s\n", dbdname); } - if (loaded) printf("%s version is %s\n", lib, loaded); - return OK; - } - else - { - /* Library already loaded. Check Version. */ - if (validate(version, loaded) == ERROR) + + /* register module */ + m = (moduleitem*) calloc(sizeof (moduleitem),1); + if (!m) { - printf("Conflict between requested %s version %s\n" - "and already loaded version %s.\n" - "Aborting startup stript.\n", - lib, version, loaded); - shellScriptAbort(); - return ERROR; + printf ("require: out of memory\n"); } - /* Loaded version is ok */ - printf("%sLib-%s already loaded\n", lib, loaded); - return OK; + else + { + strncpy (m->name, module, sizeof(m->name)); + strncpy (m->version, loaded, sizeof(m->version)); + m->next = loadedModules; + loadedModules = m; + } + +#ifdef EPICS_3_14 + /* call register function for R3.14 */ + /* call register function */ + sprintf (symbolname, "%s_registerRecordDeviceDriver", module); + printf ("Calling %s function\n", symbolname); +#ifdef __vxworks + { + FUNCPTR f; + if (symFindByName(sysSymTbl, symbolname, (char**)&f, &type) == 0) + f(pdbbase); + else + fprintf (stderr, "require: can't find %s function\n", symbolname); + } +#else + iocshCmd(symbolname); +#endif +#endif + return 0; } } -char* getLibVersion(char* lib) +int libversionShow(const char* pattern) { - char symbol[256]; - char* loaded; - SYM_TYPE type; - - sprintf(symbol, "_%sLibRelease", lib); - if (symFindByName(sysSymTbl, symbol, &loaded, &type) != OK) return NULL; - return loaded; -} + moduleitem* m; -static BOOL printIfLibversion(char* name, int val, - SYM_TYPE type, int arg, UINT16 group) -{ - int l; - char* pattern = (char*) arg; - - l = strlen(name); - if (l > 10 && strcmp(name+l-10, "LibRelease") == 0) + for (m = loadedModules; m; m=m->next) { - if (pattern && !strstr(name, pattern)) return TRUE; - printf("%15.*s %s\n", l-11, name+1, (char*)val); + if (pattern && !strstr(m->name, pattern)) return 0; + printf("%15s %s\n", m->name, m->version); } - return TRUE; -} - -int libversionShow(char* pattern) -{ - symEach(sysSymTbl, (FUNCPTR)printIfLibversion, (int)pattern); - return OK; + return 0; } #ifdef EPICS_3_14 @@ -306,24 +560,36 @@ static const iocshArg * const requireArgs[2] = { &requireArg0, &requireArg1 }; static const iocshFuncDef requireDef = { "require", 2, requireArgs }; static void requireFunc (const iocshArgBuf *args) { - if (require (args[0].sval, args[1].sval) != 0 - && !interruptAccept) - { - /* require failed in startup script before iocInit */ - fprintf (stderr, "Aborting startup script\n"); - epicsExit (1); - } + require(args[0].sval, args[1].sval); +} + +static const iocshArg libversionShowArg0 = { "pattern", iocshArgString }; +static const iocshArg * const libversionArgs[1] = { &libversionShowArg0 }; +static const iocshFuncDef libversionShowDef = { "libversionShow", 1, libversionArgs }; +static void libversionShowFunc (const iocshArgBuf *args) +{ + libversionShow(args[0].sval); +} + +static const iocshArg ldArg0 = { "library", iocshArgString }; +static const iocshArg * const ldArgs[1] = { &ldArg0 }; +static const iocshFuncDef ldDef = { "ld", 1, ldArgs }; +static void ldFunc (const iocshArgBuf *args) +{ + loadlib(args[0].sval); } static void requireRegister(void) { static int firstTime = 1; if (firstTime) { + iocshRegister (&ldDef, ldFunc); + iocshRegister (&libversionShowDef, libversionShowFunc); iocshRegister (&requireDef, requireFunc); firstTime = 0; } } epicsExportRegistrar(requireRegister); - +epicsExportAddress(int, requireDebug); #endif diff --git a/require.h b/require.h index d94e74ea76d12f1c6b3276920cd951fdb3b6a9a2..cf9889501ad0a05c96da53a2ed8dcbe62e5af733 100644 --- a/require.h +++ b/require.h @@ -1,8 +1,8 @@ #ifndef require_h #define require_h -int require(char* lib, char* version); -char* getLibVersion(char* lib); -int libversionShow(char* pattern); +int require(const char* libname, const char* version); +const char* getLibVersion(const char* libname); +int libversionShow(const char* pattern); #endif