Newer
Older
* ld - load code dynamically
*
* $Author: zimoch $
* $ID$
* $Date: 2015/06/29 09:47:30 $
*
* DISCLAIMER: Use at your own risc and so on. No warranty, no refund.
*/
#ifdef __unix
/* for vasprintf and dl_iterate_phdr */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#endif
/* for 64 bit (NFS) file systems */
#define _FILE_OFFSET_BITS 64
#include <recSup.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
/* This prototype is missing in older EPICS versions */
epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
int requireDebug;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#ifndef OS_CLASS
#ifdef __linux
#define OS_CLASS "Linux"
#endif
#ifdef SOLARIS
#define OS_CLASS "solaris"
#endif
#ifdef __rtems__
#define OS_CLASS "RTEMS"
#endif
#ifdef CYGWIN32
#define OS_CLASS "cygwin32"
#endif
#ifdef freebsd
#define OS_CLASS "freebsd"
#endif
#ifdef darwin
#define OS_CLASS "Darwin"
#endif
#ifdef _AIX32
#define OS_CLASS "AIX"
#endif
#endif
#include <dlfcn.h>
#define HMODULE void *
#define getAddress(module, name) dlsym(module, name)
#ifdef CYGWIN32
#define PREFIX
#define INFIX
#define EXT ".dll"
#else
#define PREFIX "lib"
#define INFIX
#define EXT ".so"
#endif
#elif defined(_WIN32)
#ifndef OS_CLASS
#define OS_CLASS "WIN32"
#endif
#include <Psapi.h>
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "psapi.lib")
#include "asprintf.h"
#define snprintf _snprintf
#define setenv(name, value, overwrite) _putenv_s(name, value)
#define PATH_MAX MAX_PATH
#define PREFIX
#define INFIX
#define EXT ".dll"
#define getAddress(module, name) GetProcAddress(module, name)
static char *realpath(const char *path, char *buffer) {
int len = MAX_PATH;
if (buffer == NULL) {
len = GetFullPathName(path, 0, NULL, NULL);
if (len == 0)
return NULL;
buffer = malloc(len);
return NULL;
}
GetFullPathName(path, len, buffer, NULL);
return buffer;
#warning unknown OS
#define PREFIX
#define INFIX
#define EXT
#define getAddress(module, name) NULL
/* for readdir: Windows or Posix */
#if defined(_WIN32)
#define DIR_HANDLE HANDLE
#define DIR_ENTRY WIN32_FIND_DATA
#define IF_OPEN_DIR(f) \
if (snprintf(f + modulediroffs, sizeof(f) - modulediroffs, "\\*.*"), \
(dir = FindFirstFile(filename, &direntry)) != INVALID_HANDLE_VALUE || \
(FindClose(dir), 0))
#define START_DIR_LOOP do
#define END_DIR_LOOP \
while (FindNextFile(dir, &direntry)) \
; \
FindClose(dir);
#define SKIP_NON_DIR(e) \
if (!(e.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || \
(e.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) \
continue;
#define FILENAME(e) e.cFileName
#include <dirent.h>
#define DIR_HANDLE DIR *
#define IF_OPEN_DIR(f) if ((dir = opendir(f)))
#define DIR_ENTRY struct dirent *
#define START_DIR_LOOP while ((errno = 0, direntry = readdir(dir)) != NULL)
#define END_DIR_LOOP \
if (!direntry && errno) \
fprintf(stderr, "error reading directory %s: %s\n", filename, \
strerror(errno)); \
if (dir) \
closedir(dir);
#ifdef _DIRENT_HAVE_D_TYPE
#define SKIP_NON_DIR(e) \
if (e->d_type != DT_DIR && e->d_type != DT_UNKNOWN) \
continue;
#else
#define SKIP_NON_DIR(e)
#define FILENAME(e) e->d_name
const char epicsRelease[] =
TOSTR(EPICS_VERSION)"."TOSTR(EPICS_REVISION)"."TOSTR(EPICS_MODIFICATION);
#ifndef T_A
#error T_A not defined: Compile with USR_CFLAGS += -DT_A='"${T_A}"'
#endif
const char targetArch[] = T_A;
*/
#ifndef OS_CLASS
#error OS_CLASS not defined: Try to compile with USR_CFLAGS += -DOS_CLASS='"${OS_CLASS}"'
#endif
/* loadlib (library)
Find a loadable library by name and load it.
*/
char epicsRelease[80];
char *targetArch;
void set_require_env() {
char *epics_version_major = getenv("EPICS_VERSION_MAJOR");
char *epics_version_middle = getenv("EPICS_VERSION_MIDDLE");
char *epics_version_minor = getenv("EPICS_VERSION_MINOR");
sprintf(epicsRelease, "%s.%s.%s", epics_version_major, epics_version_middle,
epics_version_minor);
targetArch = getenv("EPICS_HOST_ARCH");
return;
static HMODULE loadlib(const char *libname) {
HMODULE libhandle = NULL;
if (libname == NULL) {
fprintf(stderr, "missing library name\n");
return NULL;
}
if ((libhandle = dlopen(libname, RTLD_NOW | RTLD_GLOBAL)) == NULL) {
fprintf(stderr, "Loading %s library failed: %s\n", libname, dlerror());
}
if ((libhandle = LoadLibrary(libname)) == NULL) {
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);
}
fprintf(stderr, "cannot load libraries on this OS.\n");
typedef struct moduleitem {
struct moduleitem *next;
char content[0];
static moduleitem *loadedModules = NULL;
static unsigned long moduleCount = 0;
static unsigned long moduleListBufferSize = 1;
static unsigned long maxModuleNameLength = 0;
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
int putenvprintf(const char *format, ...) {
va_list ap;
char *var;
char *val;
int status = 0;
if (!format)
return -1;
va_start(ap, format);
if (vasprintf(&var, format, ap) < 0) {
perror("require putenvprintf");
return errno;
}
va_end(ap);
if (requireDebug)
printf("require: putenv(\"%s\")\n", var);
val = strchr(var, '=');
if (!val) {
fprintf(stderr, "putenvprintf: string contains no =: %s\n", var);
status = -1;
} else {
*val++ = 0;
if (setenv(var, val, 1) != 0) {
perror("require putenvprintf: setenv failed");
status = errno;
}
void pathAdd(const char *varname, const char *dirname) {
char *old_path;
if (!varname || !dirname) {
fprintf(stderr, "usage: pathAdd \"ENVIRONMENT_VARIABLE\",\"directory\"\n");
fprintf(stderr, " Adds or moves the directory to the front of the "
"ENVIRONMENT_VARIABLE\n");
fprintf(stderr, " but after a leading \".\".\n");
return;
}
/* add directory to front */
old_path = getenv(varname);
if (old_path == NULL)
putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s", varname, dirname);
else {
size_t len = strlen(dirname);
char *p;
/* skip over "." at the beginning */
if (old_path[0] == '.' && old_path[1] == OSI_PATH_LIST_SEPARATOR[0])
old_path += 2;
/* If directory is already in path, move it to front */
p = old_path;
while ((p = strstr(p, dirname)) != NULL) {
if ((p == old_path || *(p - 1) == OSI_PATH_LIST_SEPARATOR[0]) &&
(p[len] == 0 || p[len] == OSI_PATH_LIST_SEPARATOR[0])) {
if (p == old_path)
break; /* already at front, nothing to do */
memmove(old_path + len + 1, old_path, p - old_path - 1);
strcpy(old_path, dirname);
old_path[len] = OSI_PATH_LIST_SEPARATOR[0];
if (requireDebug)
printf("require: modified %s=%s\n", varname, old_path);
break;
}
p += len;
if (p == NULL)
/* add new directory to the front (after "." )*/
putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s" OSI_PATH_LIST_SEPARATOR
"%s",
varname, dirname, old_path);
}
char *realpathSeparator(const char *location) {
size_t ll;
char *buffer = malloc(PATH_MAX + strlen(OSI_PATH_SEPARATOR));
buffer = realpath(location, buffer);
if (!buffer) {
printf("require: realpath(%s) failed\n", location);
return NULL;
}
ll = strlen(buffer);
/* linux realpath removes trailing slash */
if (buffer[ll - strlen(OSI_PATH_SEPARATOR)] != OSI_PATH_SEPARATOR[0]) {
strcpy(buffer + ll + 1 - strlen(OSI_PATH_SEPARATOR), OSI_PATH_SEPARATOR);
}
return buffer;
int isModuleLoaded(const char *libname);
static int setupDbPath(const char *module, const char *dbdir) {
char *absdir =
realpathSeparator(dbdir); /* so we can change directory later safely */
if (absdir == NULL) {
if (requireDebug)
printf("require: cannot resolve %s\n", dbdir);
return -1;
}
if (requireDebug)
printf("require: found template directory %s\n", absdir);
/* set up db search path environment variables
<module>_DB template path of <module>
TEMPLATES template path of the current module (overwritten)
EPICS_DB_INCLUDE_PATH template path of all loaded modules (last in front
after ".")
*/
putenvprintf("%s_DB=%s", module, absdir);
putenvprintf("TEMPLATES=%s", absdir);
if (isModuleLoaded("stream")) {
pathAdd("STREAM_PROTOCOL_PATH", absdir);
}
pathAdd("EPICS_DB_INCLUDE_PATH", absdir);
free(absdir);
return 0;
}
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
static int getRecordHandle(const char *namepart, short type, long minsize,
DBADDR *paddr) {
/*
#define PVNAME_STRINGSZ 61
defined in EPICS_BASE/include/dbDefs.h
*/
char recordname[PVNAME_STRINGSZ] = "";
long dummy = 0L;
long offset = 0L;
sprintf(recordname, "%.*s%s", (int)(PVNAME_STRINGSZ - strlen(namepart) - 1),
getenv("REQUIRE_IOC"), namepart);
if (dbNameToAddr(recordname, paddr) != 0) {
fprintf(stderr, "require:getRecordHandle : record %s not found\n",
recordname);
return -1;
}
if (paddr->field_type != type) {
fprintf(
stderr,
"require:getRecordHandle : record %s has wrong type %s instead of %s\n",
recordname, pamapdbfType[paddr->field_type].strvalue,
pamapdbfType[type].strvalue);
return -1;
}
if (paddr->no_elements < minsize) {
fprintf(stderr,
"require:getRecordHandle : record %s has not enough elements: %lu "
"instead of %lu\n",
recordname, paddr->no_elements, minsize);
return -1;
}
if (paddr->pfield == NULL) {
fprintf(
stderr,
"require:getRecordHandle : record %s has not yet allocated memory\n",
recordname);
return -1;
}
/* update array information */
dbGetRset(paddr)->get_array_info(paddr, &dummy, &offset);
return 0;
We can fill the records only after they have been initialized, at
initHookAfterFinishDevSup. But use double indirection here because in 3.13 we
must wait until initHooks is loaded before we can register the hook.
static void fillModuleListRecord(initHookState state) {
if (state == initHookAfterFinishDevSup) /* MODULES record exists and has
allocated memory */
{
DBADDR modules, versions, modver;
int have_modules, have_versions, have_modver;
moduleitem *m;
int i = 0;
long c = 0;
if (requireDebug)
printf("require: fillModuleListRecord\n");
have_modules =
(getRecordHandle(":MODULES", DBF_STRING, moduleCount, &modules) == 0);
have_versions =
(getRecordHandle(":VERSIONS", DBF_STRING, moduleCount, &versions) == 0);
moduleListBufferSize += moduleCount * maxModuleNameLength;
have_modver = (getRecordHandle(":MOD_VER", DBF_CHAR, moduleListBufferSize,
&modver) == 0);
for (m = loadedModules, i = 0; m; m = m->next, i++) {
size_t lm = strlen(m->content) + 1;
if (have_modules) {
if (requireDebug)
printf("require: %s[%d] = \"%.*s\"\n", modules.precord->name, i,
MAX_STRING_SIZE - 1, m->content);
sprintf((char *)(modules.pfield) + i * MAX_STRING_SIZE, "%.*s",
MAX_STRING_SIZE - 1, m->content);
}
if (have_versions) {
if (requireDebug)
printf("require: %s[%d] = \"%.*s\"\n", versions.precord->name, i,
MAX_STRING_SIZE - 1, m->content + lm);
sprintf((char *)(versions.pfield) + i * MAX_STRING_SIZE, "%.*s",
MAX_STRING_SIZE - 1, m->content + lm);
}
if (have_modver) {
if (requireDebug)
printf("require: %s+=\"%-*s%s\"\n", modver.precord->name,
(int)maxModuleNameLength, m->content, m->content + lm);
c += sprintf((char *)(modver.pfield) + c, "%-*s%s\n",
(int)maxModuleNameLength, m->content, m->content + lm);
}
if (have_modules)
dbGetRset(&modules)->put_array_info(&modules, i);
if (have_versions)
dbGetRset(&versions)->put_array_info(&versions, i);
if (have_modver)
dbGetRset(&modver)->put_array_info(&modver, c + 1);
}
void registerModule(const char *module, const char *version,
const char *location) {
moduleitem *m, **pm;
size_t lm = strlen(module) + 1;
size_t lv = (version ? strlen(version) : 0) + 1;
size_t ll = 1;
char *absLocation = NULL;
char *absLocationRequire = NULL;
char *argstring = NULL;
const char *mylocation;
static int firstTime = 1;
if (requireDebug)
printf("require: registerModule(%s,%s,%s)\n", module, version, location);
if (firstTime) {
initHookRegister(fillModuleListRecord);
printf("require: initHookRegister\n");
firstTime = 0;
}
if (!version)
version = "";
if (location) {
absLocation = realpathSeparator(location);
ll = strlen(absLocation) + 1;
}
m = (moduleitem *)malloc(sizeof(moduleitem) + lm + lv + ll);
if (m == NULL) {
fprintf(stderr, "require: out of memory\n");
return;
}
strcpy(m->content, module);
strcpy(m->content + lm, version);
strcpy(m->content + lm + lv, absLocation ? absLocation : "");
free(absLocation);
for (pm = &loadedModules; *pm != NULL; pm = &(*pm)->next)
;
*pm = m;
if (lm > maxModuleNameLength)
maxModuleNameLength = lm;
moduleListBufferSize += lv;
moduleCount++;
putenvprintf("MODULE=%s", module);
putenvprintf("%s_VERSION=%s", module, version);
if (location) {
putenvprintf("%s_DIR=%s", module, m->content + lm + lv);
pathAdd("SCRIPT_PATH", m->content + lm + lv);
}
/* only do registration register stuff at init */
if (interruptAccept)
return;
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/* create a record with the version string */
mylocation = getenv("require_DIR");
if (mylocation == NULL)
return;
if (asprintf(&absLocationRequire,
"%s" OSI_PATH_SEPARATOR "db" OSI_PATH_SEPARATOR
"moduleversion.template",
mylocation) < 0)
return;
/*
Require DB has the following four PVs:
- $(REQUIRE_IOC):$(MODULE)_VER
- $(REQUIRE_IOC):MOD_VER
- $(REQUIRE_IOC):VERSIONS
- $(REQUIRE_IOC):MODULES
We reserved 30 chars for :$(MODULE)_VER, so MODULE has the maximum 24
chars. And we've reserved for 30 chars for $(REQUIRE_IOC). So, the whole PV
and record name in moduleversion.template has 59 + 1.
*/
if (asprintf(&argstring,
"REQUIRE_IOC=%.30s, MODULE=%.24s, VERSION=%.39s, "
"MODULE_COUNT=%lu, BUFFER_SIZE=%lu",
getenv("REQUIRE_IOC"), module, version, moduleCount,
moduleListBufferSize + maxModuleNameLength * moduleCount) < 0)
return;
printf("Loading module info records for %s\n", module);
dbLoadRecords(absLocationRequire, argstring);
free(argstring);
free(absLocationRequire);
/* This is the Linux link.h, not the EPICS link.h ! */
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
static int findLibRelease(struct dl_phdr_info *info, /* shared library info */
size_t size, /* size of info structure */
void *data /* user-supplied arg */
) {
void *handle;
char *location = NULL;
char *p;
char *version;
char *symname;
char name[PATH_MAX + 11]; /* get space for library path + "LibRelease" */
(void)data; /* unused */
if (size < sizeof(struct dl_phdr_info))
return 0; /* wrong version of struct dl_phdr_info */
/* find a symbol with a name like "_<module>LibRelease"
where <module> is from the library name "<location>/lib<module>.so" */
if (info->dlpi_name == NULL || info->dlpi_name[0] == 0)
return 0; /* no library name */
strcpy(name, info->dlpi_name); /* get a modifiable copy of the library name */
handle =
dlopen(info->dlpi_name, RTLD_LAZY); /* re-open already loaded library */
p = strrchr(name,
'/'); /* find file name part in "<location>/lib<module>.so" */
if (p) {
location = name;
*++p = 0;
} else
p = name; /* terminate "<location>/" (if exists) */
*(symname = p + 2) = '_'; /* replace "lib" with "_" */
p = strchr(symname, '.'); /* find ".so" extension */
if (p == NULL)
p = symname + strlen(symname); /* no file extension ? */
strcpy(p, "LibRelease"); /* append "LibRelease" to module name */
version = dlsym(handle, symname); /* find symbol "_<module>LibRelease" */
if (version) {
*p = 0;
symname++; /* get "<module>" from "_<module>LibRelease" */
if ((p = strstr(name, "/" LIBDIR)) != NULL)
p[1] = 0; /* cut "<location>" before LIBDIR */
if (getLibVersion(symname) == NULL)
registerModule(symname, version, location);
}
dlclose(handle);
return 0;
static void registerExternalModules() {
/* iterate over all loaded libraries */
dl_iterate_phdr(findLibRelease, NULL);
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
static void registerExternalModules() {
HMODULE hMods[100];
HANDLE hProcess = GetCurrentProcess();
DWORD cbNeeded;
char *location = NULL;
char *p;
char *version;
char *symname;
unsigned int i;
char name[MAX_PATH + 11]; /* get space for library path + "LibRelease" */
/* iterate over all loaded libraries */
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
return;
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
/* Get the full path to the module's file. */
if (!GetModuleFileName(hMods[i], name, MAX_PATH))
continue; /* no library name */
name[sizeof(name) - 1] = 0; /* WinXP may not terminate the string */
p = strrchr(name,
'\\'); /* find file name part in "<location>/<module>.dll" */
if (p) {
location = name;
} else
p = name; /* find end of "<location>\\" (if exists) */
symname = p;
p = strchr(symname, '.'); /* find ".dll" */
if (p == NULL)
p = symname + strlen(symname); /* no file extension ? */
memmove(symname + 2, symname, p - symname); /* make room for 0 and '_' */
*symname++ = 0; /* terminate "<location>/" */
*symname = '_'; /* prefix module name with '_' */
strcpy((p += 2), "LibRelease"); /* append "LibRelease" to module name */
version = (char *)GetProcAddress(
hMods[i], symname); /* find symbol "_<module>LibRelease" */
if (version) {
*p = 0;
symname++; /* get "<module>" from "_<module>LibRelease" */
if ((p = strstr(name, "\\" LIBDIR)) != NULL)
p[1] = 0; /* cut "<location>" before LIBDIR */
if (getLibVersion(symname) == NULL)
registerModule(symname, version, location);
size_t foreachLoadedLib(size_t (*func)(const char *name, const char *version,
const char *path, void *arg),
void *arg) {
moduleitem *m;
int result;
for (m = loadedModules; m; m = m->next) {
const char *name = m->content;
const char *version = name + strlen(name) + 1;
const char *path = version + strlen(version) + 1;
result = func(name, version, path, arg);
if (result)
return result;
}
return 0;
const char *getLibVersion(const char *libname) {
moduleitem *m;
for (m = loadedModules; m; m = m->next) {
if (strcmp(m->content, libname) == 0) {
return m->content + strlen(m->content) + 1;
const char *getLibLocation(const char *libname) {
moduleitem *m;
char *v;
for (m = loadedModules; m; m = m->next) {
if (strcmp(m->content, libname) == 0) {
v = m->content + strlen(m->content) + 1;
return v + strlen(v) + 1;
int isModuleLoaded(const char *libname) {
moduleitem *m;
for (m = loadedModules; m; m = m->next) {
if (strcmp(m->content, libname) == 0)
return TRUE;
}
return FALSE;
}
int libversionShow(const char *outfile) {
moduleitem *m;
size_t lm, lv;
Dirk Zimoch
committed
if (outfile) {
out = fopen(outfile, "w");
if (out == NULL) {
fprintf(stderr, "can't open %s: %s\n", outfile, strerror(errno));
return -1;
}
for (m = loadedModules; m; m = m->next) {
lm = strlen(m->content) + 1;
lv = strlen(m->content + lm) + 1;
fprintf(out, "%-*s%-20s %s\n", (int)maxModuleNameLength, m->content,
m->content + lm, m->content + lm + lv);
}
if (fflush(out) < 0 && outfile) {
fprintf(stderr, "can't write to %s: %s\n", outfile, strerror(errno));
return -1;
}
if (outfile)
fclose(out);
return 0;
#define MISMATCH -1
#define EXACT 0
#define MATCH 1
#define TESTVERS 2
#define debug(...) \
if (requireDebug) \
printf(__VA_ARGS__)
static int compareDigit(int found, int requested, const char *name) {
debug("require: compareDigit: found %d, requested %d for digit %s\n", found,
requested, name);
if (found < requested) {
debug("require: compareVersions: MISMATCH too low %s number\n", name);
return MISMATCH;
}
if (found > requested) {
debug("require: compareVersions: HIGHER %s number\n", name);
return HIGHER;
}
return MATCH;
static int compareNumericVersion(semver_t *sv_found, semver_t *sv_request,
int already_matched) {
int match;
match = compareDigit(sv_found->major, sv_request->major, "major");
if (match != MATCH) {
return match;
}
match = compareDigit(sv_found->minor, sv_request->minor, "minor");
if (match != MATCH) {
return match;
}
match = compareDigit(sv_found->patch, sv_request->patch, "patch");
if (match != MATCH) {
return match;
}
if (sv_request->build == -1) {
if (already_matched) {
debug("require: compareVersions: No build number requested. Returning "
"HIGHER\n");
return HIGHER;
} else {
debug("require: compareVersions: No build number requested. Returning "
"MATCH\n");
return MATCH;
}
return compareDigit(sv_found->build, sv_request->build, "build");
/*
* Returns if the version <found> is higher than <request>.
*/
static int compareVersions(const char *found, const char *request,
int already_matched) {
semver_t *sv_found, *sv_request;
int match;
debug("require: compareVersions(found=%s, request=%s)\n", found, request);
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
if (request == NULL || request[0] == 0) {
debug("require: compareVersions: MATCH empty version requested\n");
return MATCH;
}
if (found == NULL || found[0] == 0) {
debug("require: compareVersions: MISMATCH empty version found\n");
return MISMATCH;
}
sv_found = (semver_t *)calloc(1, sizeof(semver_t));
sv_request = (semver_t *)calloc(1, sizeof(semver_t));
parse_semver(found, sv_found);
parse_semver(request, sv_request);
// test version, look for exact.
if (strlen(sv_request->test_str) > 0) {
if (strcmp(found, request) == 0) {
debug("require: compareVersions: Test version requested and found, "
"matches exactly\n");
match = EXACT;
} else if (strlen(sv_found->test_str) > 0) {
debug("require: compareVersions: Test versions requested and found, no "
"match\n");
match = MISMATCH;
} else {
debug("require: compareVersions: found numeric version, higher than "
"test\n");
match = HIGHER;
} else if (strlen(sv_found->test_str) > 0) {
debug("require: compareVersions: Numeric version requested, test version "
"found\n");
match = MISMATCH;
} else {
match = compareNumericVersion(sv_found, sv_request, already_matched);
}
cleanup_semver(sv_found);
cleanup_semver(sv_request);
return match;
/* 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.
*/
/* wrapper to abort statup script */
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
static int require_priv(const char *module, const char *version,
const char *args, const char *versionstr);
int require(const char *module, const char *version, const char *args) {
int status;
char *versionstr;
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
set_require_env();
putenvprintf("T_A=%s", targetArch);
putenvprintf("EPICS_HOST_ARCH=%s", targetArch);
putenvprintf("EPICS_RELEASE=%s", epicsRelease);
putenvprintf("OS_CLASS=%s", osClass);
}
if (module == NULL) {
printf("Usage: require \"<module>\" [, \"<version>\" | \"ifexists\"] [, "
"\"<args>\"]\n");
printf("Loads " PREFIX "<module>" INFIX EXT " and <libname>.dbd\n");
printf("And calls <module>_registerRecordDeviceDriver\n");
printf("If available, runs startup script snippet (only before iocInit)\n");
return -1;
}
/* either order for version and args, either may be empty or NULL */
if (version && strchr(version, '=')) {
const char *v = version;
version = args;
args = v;
if (requireDebug)
printf("require: swap version and args\n");
}
if (version && version[0] == 0)
version = NULL;
Dirk Zimoch
committed
if (requireDebug)
printf("require: skip version=none\n");
return 0;
}
if (version) {
/* needed for old style only: */
if (asprintf(&versionstr, "-%s", version) < 0)
return errno;
if (isdigit((unsigned char)version[0]) &&
version[strlen(version) - 1] == '+') {
/*
user may give a minimal version (e.g. "1.2.4+")
load highest matching version (here "1.2") and check later
*/
char *p = strrchr(versionstr, '.');
if (p == NULL)
p = versionstr;
*p = 0;
}
} else
versionstr = "";
if (requireDebug)
printf("require: versionstr = \"%s\"\n", versionstr);
Dirk Zimoch
committed
status = require_priv(module, version, args, versionstr);
Dirk Zimoch
committed
if (status == 0)
return 0;
if (status != -1)
perror("require");
if (interruptAccept)
/* require failed in startup script before iocInit */
fprintf(stderr, "Aborting startup script\n");
epicsExit(1);
return status;
static off_t fileSize(const char *filename) {
struct stat filestat;
if (stat(filename, &filestat) != 0) {
if (requireDebug)
printf("require: %s does not exist\n", filename);
return -1;
}
switch (filestat.st_mode & S_IFMT) {
case S_IFREG:
if (requireDebug)
printf("require: file %s exists, size %lld bytes\n", filename,
(unsigned long long)filestat.st_size);
return filestat.st_size;
case S_IFDIR:
if (requireDebug)
printf("require: directory %s exists\n", filename);
return 0;
case S_IFBLK:
if (requireDebug)
printf("require: %s is a block device\n", filename);
return -1;
#endif
#ifdef S_IFCHR