LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_linker.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 124 217 57.1 %
Date: 2021-09-14 22:17:06 Functions: 7 9 77.8 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * (author) M. Kersten
      11             :  * An include file name is also used as library name
      12             :  */
      13             : #include "monetdb_config.h"
      14             : #include "mal_module.h"
      15             : #include "mal_linker.h"
      16             : #include "mal_function.h"     /* for throw() */
      17             : #include "mal_import.h"               /* for slash_2_dir_sep() */
      18             : #include "mal_private.h"
      19             : 
      20             : #include "mutils.h"
      21             : #include <sys/types.h> /* opendir */
      22             : #ifdef HAVE_DIRENT_H
      23             : #include <dirent.h>
      24             : #endif
      25             : #ifdef HAVE_FCNTL_H
      26             : #include <fcntl.h>
      27             : #endif
      28             : #include <unistd.h>
      29             : 
      30             : #if defined(_MSC_VER) && _MSC_VER >= 1400
      31             : #define open _open
      32             : #define close _close
      33             : #endif
      34             : 
      35             : #define MAXMODULES 128
      36             : 
      37             : typedef struct{
      38             :         str modname;
      39             :         str fullname;
      40             :         void *handle;
      41             : } FileRecord;
      42             : 
      43             : static FileRecord filesLoaded[MAXMODULES];
      44             : static int maxfiles = MAXMODULES;
      45             : static int lastfile = 0;
      46             : 
      47             : #ifndef O_CLOEXEC
      48             : #define O_CLOEXEC 0
      49             : #endif
      50             : 
      51             : /*
      52             :  * returns 1 if the file exists
      53             :  */
      54             : #ifndef F_OK
      55             : #define F_OK 0
      56             : #endif
      57             : static inline int
      58             : fileexists(const char *path)
      59             : {
      60             :         return MT_access(path, F_OK) == 0;
      61             : }
      62             : 
      63             : /* Search for occurrence of the function in the library identified by the filename.  */
      64             : MALfcn
      65          31 : getAddress(const char *modname, const char *fcnname)
      66             : {
      67             :         void *dl;
      68             :         MALfcn adr;
      69             :         int idx=0;
      70             :         static int prev= -1;
      71             : 
      72          31 :         if ((adr = findFunctionImplementation(fcnname)) != NULL)
      73             :                 return adr;
      74             : 
      75             :         /* First try the last module loaded */
      76          12 :         if( prev >= 0 && strcmp(filesLoaded[prev].modname, modname) == 0){ /* test if just pointer compare could work */
      77           8 :                 adr = (MALfcn) dlsym(filesLoaded[prev].handle, fcnname);
      78           8 :                 if( adr != NULL)
      79             :                         return adr; /* found it */
      80             :         }
      81             :         /*
      82             :          * Search for occurrence of the function in any library already loaded.
      83             :          * This deals with the case that files are linked together to reduce
      84             :          * the loading time, while the signatures of the functions are still
      85             :          * obtained from the source-file MAL script.
      86             :          */
      87          33 :         for (idx =0; idx < lastfile; idx++)
      88          32 :                 if (idx != prev &&              /* skip already searched module */
      89          31 :                         filesLoaded[idx].handle &&
      90          23 :                         strcmp(filesLoaded[idx].modname, modname) == 0 &&
      91           3 :                         (idx == 0 || filesLoaded[idx].handle != filesLoaded[0].handle)) {
      92           3 :                         adr = (MALfcn) dlsym(filesLoaded[idx].handle, fcnname);
      93           3 :                         if (adr != NULL)  {
      94           3 :                                 prev = idx;
      95           3 :                                 return adr; /* found it */
      96             :                         }
      97             :                 }
      98             : 
      99           1 :         if (lastfile) {
     100             :                 /* first should be monetdb5 */
     101           1 :                 assert(strcmp(filesLoaded[0].modname, "monetdb5") == 0 || strcmp(filesLoaded[0].modname, "embedded") == 0);
     102           1 :                 adr = (MALfcn) dlsym(filesLoaded[0].handle, fcnname);
     103           1 :                 if (adr != NULL)  {
     104           0 :                         prev = 0;
     105           0 :                         return adr; /* found it */
     106             :                 }
     107             :                 return NULL;
     108             :         }
     109             :         /*
     110             :          * Try the program libraries at large or run through all
     111             :          * loaded files and try to resolve the functionname again.
     112             :          *
     113             :          * the first argument must be the same as the base name of the
     114             :          * library that is created in src/tools */
     115             : #ifdef __APPLE__
     116             :         dl = mdlopen(SO_PREFIX "monetdb5" SO_EXT, RTLD_NOW | RTLD_GLOBAL);
     117             : #else
     118           0 :         dl = dlopen(SO_PREFIX "monetdb5" SO_EXT, RTLD_NOW | RTLD_GLOBAL);
     119             : #endif
     120           0 :         if (dl == NULL)
     121             :                 return NULL;
     122             : 
     123           0 :         adr = (MALfcn) dlsym(dl, fcnname);
     124           0 :         filesLoaded[lastfile].modname = GDKstrdup("libmonetdb5");
     125           0 :         if(filesLoaded[lastfile].modname == NULL) {
     126           0 :                 dlclose(dl);
     127           0 :                 return NULL;
     128             :         }
     129           0 :         filesLoaded[lastfile].fullname = GDKstrdup("libmonetdb5");
     130           0 :         if(filesLoaded[lastfile].fullname == NULL) {
     131           0 :                 dlclose(dl);
     132           0 :                 GDKfree(filesLoaded[lastfile].modname);
     133           0 :                 return NULL;
     134             :         }
     135           0 :         filesLoaded[lastfile].handle = dl;
     136           0 :         lastfile ++;
     137           0 :         return adr;
     138             : }
     139             : /*
     140             :  * Module file loading
     141             :  * The default location to search for the module is in monet_mod_path
     142             :  * unless an absolute path is given.
     143             :  * Loading further relies on the Linux policy to search for the module
     144             :  * location in the following order: 1) the colon-separated list of
     145             :  * directories in the user's LD_LIBRARY_PATH, 2) the libraries specified
     146             :  * in /etc/ld.so.cache and 3) /usr/lib followed by /lib.
     147             :  * If the module contains a routine _init, then that code is executed
     148             :  * before the loader returns. Likewise the routine _fini is called just
     149             :  * before the module is unloaded.
     150             :  *
     151             :  * A module loading conflict emerges if a function is redefined.
     152             :  * A duplicate load is simply ignored by keeping track of modules
     153             :  * already loaded.
     154             :  */
     155             : 
     156             : str
     157        1864 : loadLibrary(const char *filename, int flag)
     158             : {
     159             :         int mode = RTLD_NOW | RTLD_GLOBAL;
     160             :         char nme[FILENAME_MAX];
     161             :         void *handle = NULL;
     162             :         const char *s;
     163             :         int idx;
     164        1864 :         const char *mod_path = GDKgetenv("monet_mod_path");
     165             :         int is_mod;
     166             : 
     167        1864 :         is_mod = (strcmp(filename, "monetdb5") != 0 && strcmp(filename, "embedded") != 0);
     168             : 
     169        1864 :         if (lastfile == 0 && strcmp(filename, "monetdb5") != 0 && strcmp(filename, "embedded") != 0) { /* first load reference to local functions */
     170         266 :                 str msg = loadLibrary("monetdb5", flag);
     171         266 :                 if (msg != MAL_SUCCEED)
     172             :                         return msg;
     173             :         }
     174             :         /* AIX requires RTLD_MEMBER to load a module that is a member of an
     175             :          * archive.  */
     176             : #ifdef RTLD_MEMBER
     177             :         mode |= RTLD_MEMBER;
     178             : #endif
     179             : 
     180        7637 :         for (idx = 0; idx < lastfile; idx++)
     181        5773 :                 if (filesLoaded[idx].modname &&
     182        5773 :                     strcmp(filesLoaded[idx].modname, filename) == 0)
     183             :                         /* already loaded */
     184             :                         return MAL_SUCCEED;
     185             : 
     186             :         /* ignore any path given */
     187        1864 :         if ((s = strrchr(filename, DIR_SEP)) == NULL)
     188             :                 s = filename;
     189             : 
     190        1864 :         if (mod_path != NULL) {
     191        1859 :                 while (*mod_path == PATH_SEP)
     192           0 :                         mod_path++;
     193        1859 :                 if (*mod_path == 0)
     194             :                         mod_path = NULL;
     195             :         }
     196        1864 :         if (mod_path == NULL) {
     197             :                 int len;
     198             : 
     199           5 :                 if (is_mod)
     200           3 :                         len = snprintf(nme, FILENAME_MAX, "%s_%s%s", SO_PREFIX, s, SO_EXT);
     201             :                 else
     202           2 :                         len = snprintf(nme, FILENAME_MAX, "%s%s%s", SO_PREFIX, s, SO_EXT);
     203           5 :                 if (len == -1 || len >= FILENAME_MAX)
     204           0 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR "Library filename path is too large");
     205             : 
     206             : #ifdef __APPLE__
     207             :                 handle = mdlopen(nme, RTLD_NOW | RTLD_GLOBAL);
     208             : #else
     209           5 :                 handle = dlopen(nme, RTLD_NOW | RTLD_GLOBAL);
     210             : #endif
     211           5 :                 if (!handle) {
     212           2 :                         if (flag)
     213           0 :                                 throw(LOADER, "loadLibrary", RUNTIME_FILE_NOT_FOUND ":%s", s);
     214             :                         return MAL_SUCCEED;
     215             :                 }
     216             :         }
     217             : 
     218        1862 :         while (!handle && *mod_path) {
     219             :                 int len;
     220             :                 const char *p;
     221             : 
     222      145002 :                 for (p = mod_path; *p && *p != PATH_SEP; p++)
     223             :                         ;
     224             : 
     225        1859 :                 if (is_mod)
     226        1594 :                         len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s%s", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s, SO_EXT);
     227             :                 else
     228         265 :                         len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s%s", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s, SO_EXT);
     229        1859 :                 if (len == -1 || len >= FILENAME_MAX)
     230           0 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR "Library filename path is too large");
     231        1859 :                 handle = dlopen(nme, mode);
     232        2390 :                 if (handle == NULL && fileexists(nme))
     233           0 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " failed to open library %s (from within file '%s'): %s", s, nme, dlerror());
     234             :                 if (handle == NULL && strcmp(SO_EXT, ".so") != /* DISABLES CODE */ (0)) {
     235             :                         /* try .so */
     236             :                         if (is_mod)
     237             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s.so", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s);
     238             :                         else
     239             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s.so", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s);
     240             :                         if (len == -1 || len >= FILENAME_MAX)
     241             :                                 throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR "Library filename path is too large");
     242             :                         handle = dlopen(nme, mode);
     243             :                         if (handle == NULL && fileexists(nme))
     244             :                                 throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " failed to open library %s (from within file '%s'): %s", s, nme, dlerror());
     245             :                 }
     246             : #ifdef __APPLE__
     247             :                 if (handle == NULL && strcmp(SO_EXT, ".bundle") != 0) {
     248             :                         /* try .bundle */
     249             :                         if (is_mod)
     250             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s.bundle", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s);
     251             :                         else
     252             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s.bundle", (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX, s);
     253             :                         if (len == -1 || len >= FILENAME_MAX)
     254             :                                 throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR "Library filename path is too large");
     255             :                         handle = dlopen(nme, mode);
     256             :                         if (handle == NULL && fileexists(nme))
     257             :                                 throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " failed to open library %s (from within file '%s'): %s", s, nme, dlerror());
     258             :                 }
     259             : #endif
     260             : 
     261        1859 :                 if (*p == 0 || handle != NULL)
     262             :                         break;
     263           0 :                 mod_path = p + 1;
     264             :         }
     265             : 
     266        1862 :         if (handle == NULL) {
     267         531 :                 if (strcmp(filename, "monetdb5") != 0 && strcmp(filename, "sql") != 0
     268           1 :                         && strcmp(filename, "generator") != 0
     269             : #ifdef HAVE_GEOM
     270           1 :                         && strcmp(filename, "geom") != 0
     271             : #endif
     272             : #ifdef HAVE_LIBR
     273           1 :                         && strcmp(filename, "rapi") != 0
     274             : #endif
     275             : #ifdef HAVE_LIBPY3
     276           1 :                         && strcmp(filename, "pyapi3") != 0
     277             : #endif
     278             : #ifdef HAVE_CUDF
     279           1 :                         && strcmp(filename, "capi") != 0
     280             : #endif
     281             : #ifdef HAVE_FITS
     282           1 :                         && strcmp(filename, "fits") != 0
     283             : #endif
     284             : #ifdef HAVE_NETCDF
     285           1 :                         && strcmp(filename, "netcdf") != 0
     286             : #endif
     287             : #ifdef HAVE_SHP
     288           1 :                         && strcmp(filename, "shp") != 0
     289             : #endif
     290             :                         )
     291           1 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " could not locate library %s (from within file '%s'): %s", s, filename, dlerror());
     292             :         }
     293             : 
     294        1861 :         MT_lock_set(&mal_contextLock);
     295        1861 :         if (lastfile == maxfiles) {
     296           0 :                 MT_lock_unset(&mal_contextLock);
     297           0 :                 if (handle)
     298           0 :                         dlclose(handle);
     299           0 :                 throw(MAL,"mal.linker", "loadModule internal error, too many modules loaded");
     300             :         } else {
     301        1861 :                 filesLoaded[lastfile].modname = GDKstrdup(filename);
     302        1861 :                 if(filesLoaded[lastfile].modname == NULL) {
     303           0 :                         MT_lock_unset(&mal_contextLock);
     304           0 :                         if (handle)
     305           0 :                                 dlclose(handle);
     306           0 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " could not allocate space");
     307             :                 }
     308        2391 :                 filesLoaded[lastfile].fullname = GDKstrdup(handle ? nme : "");
     309        1861 :                 if(filesLoaded[lastfile].fullname == NULL) {
     310           0 :                         GDKfree(filesLoaded[lastfile].modname);
     311           0 :                         MT_lock_unset(&mal_contextLock);
     312           0 :                         if (handle)
     313           0 :                                 dlclose(handle);
     314           0 :                         throw(LOADER, "loadLibrary", RUNTIME_LOAD_ERROR " could not allocate space");
     315             :                 }
     316        1861 :                 filesLoaded[lastfile].handle = handle ? handle : filesLoaded[0].handle;
     317        1861 :                 lastfile ++;
     318             :         }
     319        1861 :         MT_lock_unset(&mal_contextLock);
     320             : 
     321        1861 :         return MAL_SUCCEED;
     322             : }
     323             : 
     324             : /*
     325             :  * For analysis of memory leaks we should cleanup the libraries before
     326             :  * we exit the server. This does not involve the libraries themselves,
     327             :  * because they may still be in use.
     328             :  */
     329             : void
     330         264 : mal_linker_reset(void)
     331             : {
     332             :         int i;
     333             : 
     334         264 :         MT_lock_set(&mal_contextLock);
     335        2116 :         for (i = 0; i < lastfile; i++){
     336        1852 :                 if (filesLoaded[i].fullname) {
     337             :                         /* dlclose(filesLoaded[i].handle);*/
     338        1852 :                         GDKfree(filesLoaded[i].modname);
     339        1852 :                         GDKfree(filesLoaded[i].fullname);
     340             :                 }
     341        1852 :                 filesLoaded[i].modname = NULL;
     342        1852 :                 filesLoaded[i].fullname = NULL;
     343             :         }
     344         264 :         lastfile = 0;
     345         264 :         MT_lock_unset(&mal_contextLock);
     346         264 : }
     347             : 
     348             : /*
     349             :  * Handling of Module Library Search Path
     350             :  * The plausible locations of the modules can be designated by
     351             :  * an environment variable.
     352             :  */
     353             : static int
     354           0 : cmpstr(const void *_p1, const void *_p2)
     355             : {
     356           0 :         const char *p1 = *(char* const*)_p1;
     357           0 :         const char *p2 = *(char* const*)_p2;
     358           0 :         const char *f1 = strrchr(p1, (int) DIR_SEP);
     359           0 :         const char *f2 = strrchr(p2, (int) DIR_SEP);
     360           0 :         return strcmp(f1?f1:p1, f2?f2:p2);
     361             : }
     362             : 
     363             : 
     364             : #define MAXMULTISCRIPT 48
     365             : char *
     366          11 : locate_file(const char *basename, const char *ext, bit recurse)
     367             : {
     368          11 :         const char *mod_path = GDKgetenv("monet_mod_path");
     369             :         char *fullname;
     370             :         size_t fullnamelen;
     371          11 :         size_t filelen = strlen(basename) + strlen(ext);
     372             :         str strs[MAXMULTISCRIPT]; /* hardwired limit */
     373             :         int lasts = 0;
     374             : 
     375          11 :         if (mod_path == NULL)
     376             :                 return NULL;
     377             : 
     378          11 :         while (*mod_path == PATH_SEP)
     379           0 :                 mod_path++;
     380          11 :         if (*mod_path == 0)
     381             :                 return NULL;
     382             :         fullnamelen = 512;
     383          11 :         fullname = GDKmalloc(fullnamelen);
     384          11 :         if (fullname == NULL)
     385             :                 return NULL;
     386          11 :         while (*mod_path) {
     387             :                 size_t i;
     388             :                 const char *p;
     389             :                 int fd;
     390             :                 DIR *rdir;
     391             : 
     392          11 :                 if ((p = strchr(mod_path, PATH_SEP)) != NULL) {
     393           0 :                         i = p - mod_path;
     394             :                 } else {
     395          11 :                         i = strlen(mod_path);
     396             :                 }
     397          11 :                 while (i + filelen + 2 > fullnamelen) {
     398             :                         char *tmp;
     399           0 :                         fullnamelen += 512;
     400           0 :                         tmp = GDKrealloc(fullname, fullnamelen);
     401           0 :                         if (tmp == NULL) {
     402           0 :                                 GDKfree(fullname);
     403           0 :                                 return NULL;
     404             :                         }
     405             :                         fullname = tmp;
     406             :                 }
     407             :                 /* we are now sure the directory name, file
     408             :                    base name, extension, and separator fit
     409             :                    into fullname, so we don't need to do any
     410             :                    extra checks */
     411          11 :                 strncpy(fullname, mod_path, i);
     412          11 :                 fullname[i] = DIR_SEP;
     413          11 :                 strcpy(fullname + i + 1, basename);
     414             :                 /* see if this is a directory, if so, recurse */
     415          11 :                 if (recurse == 1 && (rdir = opendir(fullname)) != NULL) {
     416             :                         struct dirent *e;
     417             :                         /* list *ext, sort, return */
     418           0 :                         while ((e = readdir(rdir)) != NULL) {
     419           0 :                                 if (strcmp(e->d_name, "..") == 0 || strcmp(e->d_name, ".") == 0)
     420           0 :                                         continue;
     421           0 :                                 if (strcmp(e->d_name + strlen(e->d_name) - strlen(ext), ext) == 0) {
     422             :                                         int len;
     423           0 :                                         strs[lasts] = GDKmalloc(strlen(fullname) + sizeof(DIR_SEP)
     424             :                                                         + strlen(e->d_name) + sizeof(PATH_SEP) + 1);
     425           0 :                                         if (strs[lasts] == NULL) {
     426           0 :                                                 while (lasts >= 0)
     427           0 :                                                         GDKfree(strs[lasts--]);
     428           0 :                                                 GDKfree(fullname);
     429           0 :                                                 (void)closedir(rdir);
     430           0 :                                                 return NULL;
     431             :                                         }
     432           0 :                                         len = sprintf(strs[lasts], "%s%c%s%c", fullname, DIR_SEP, e->d_name, PATH_SEP);
     433           0 :                                         if (len == -1 || len >= FILENAME_MAX) {
     434           0 :                                                 while (lasts >= 0)
     435           0 :                                                         GDKfree(strs[lasts--]);
     436           0 :                                                 GDKfree(fullname);
     437           0 :                                                 (void)closedir(rdir);
     438           0 :                                                 return NULL;
     439             :                                         }
     440           0 :                                         lasts++;
     441             :                                 }
     442           0 :                                 if (lasts >= MAXMULTISCRIPT)
     443             :                                         break;
     444             :                         }
     445           0 :                         (void)closedir(rdir);
     446             :                 } else {
     447          11 :                         strcat(fullname + i + 1, ext);
     448          11 :                         if ((fd = MT_open(fullname, O_RDONLY | O_CLOEXEC)) >= 0) {
     449             :                                 char *tmp;
     450          10 :                                 close(fd);
     451          10 :                                 tmp = GDKrealloc(fullname, strlen(fullname) + 1);
     452          10 :                                 if (tmp == NULL)
     453             :                                         return fullname;
     454          10 :                                 return tmp;
     455             :                         }
     456             :                 }
     457           1 :                 if ((mod_path = p) == NULL)
     458             :                         break;
     459           0 :                 while (*mod_path == PATH_SEP)
     460           0 :                         mod_path++;
     461             :         }
     462           1 :         if (lasts > 0) {
     463             :                 size_t i = 0;
     464             :                 int c;
     465             :                 char *tmp;
     466             :                 /* assure that an ordering such as 10_first, 20_second works */
     467           0 :                 qsort(strs, lasts, sizeof(char *), cmpstr);
     468           0 :                 for (c = 0; c < lasts; c++)
     469           0 :                         i += strlen(strs[c]) + 1; /* PATH_SEP or \0 */
     470           0 :                 tmp = GDKrealloc(fullname, i);
     471           0 :                 if( tmp == NULL){
     472           0 :                         GDKfree(fullname);
     473           0 :                         return NULL;
     474             :                 }
     475             :                 fullname = tmp;
     476             :                 i = 0;
     477           0 :                 for (c = 0; c < lasts; c++) {
     478           0 :                         if (strstr(fullname, strs[c]) == NULL) {
     479           0 :                                 strcpy(fullname + i, strs[c]);
     480           0 :                                 i += strlen(strs[c]);
     481             :                         }
     482           0 :                         GDKfree(strs[c]);
     483             :                 }
     484           0 :                 fullname[i - 1] = '\0';
     485           0 :                 return fullname;
     486             :         }
     487             :         /* not found */
     488           1 :         GDKfree(fullname);
     489           1 :         return NULL;
     490             : }
     491             : 
     492             : char *
     493           5 : MSP_locate_script(const char *filename)
     494             : {
     495           5 :         return locate_file(filename, MAL_EXT, 1);
     496             : }
     497             : 
     498             : char *
     499           0 : MSP_locate_sqlscript(const char *filename, bit recurse)
     500             : {
     501             :         /* no directory semantics (yet) */
     502           0 :         return locate_file(filename, SQL_EXT, recurse);
     503             : }
     504             : 
     505             : int
     506       18560 : malLibraryEnabled(const char *name)
     507             : {
     508       18560 :         if (strcmp(name, "pyapi3") == 0 || strcmp(name, "pyapi3map") == 0) {
     509         262 :                 const char *val = GDKgetenv("embedded_py");
     510         262 :                 return val && (strcmp(val, "3") == 0 ||
     511           0 :                                            strcasecmp(val, "true") == 0 ||
     512           0 :                                            strcasecmp(val, "yes") == 0);
     513       18298 :         } else if (strcmp(name, "rapi") == 0) {
     514         260 :                 const char *val = GDKgetenv("embedded_r");
     515         260 :                 return val && (strcasecmp(val, "true") == 0 ||
     516           4 :                                            strcasecmp(val, "yes") == 0);
     517       18038 :         } else if (strcmp(name, "capi") == 0) {
     518         291 :                 const char *val = GDKgetenv("embedded_c");
     519         291 :                 return val && (strcasecmp(val, "true") == 0 ||
     520           2 :                                            strcasecmp(val, "yes") == 0);
     521             :         }
     522             :         return true;
     523             : }
     524             : 
     525             : #define HOW_TO_ENABLE_ERROR(LANGUAGE, OPTION)                                           \
     526             :         do {                                                                                                                    \
     527             :                 if (malLibraryEnabled(name))                                                            \
     528             :                         return "Embedded " LANGUAGE " has not been installed. "     \
     529             :                                 "Please install it first, then start server with "    \
     530             :                                 "--set " OPTION;                                                                      \
     531             :                 return "Embedded " LANGUAGE " has not been enabled. "               \
     532             :                         "Start server with --set " OPTION;                                            \
     533             :         } while (0)
     534             : 
     535             : char *
     536          26 : malLibraryHowToEnable(const char *name)
     537             : {
     538          26 :         if (strcmp(name, "pyapi3") == 0 || strcmp(name, "pyapi3map") == 0) {
     539           0 :                 HOW_TO_ENABLE_ERROR("Python 3", "embedded_py=3");
     540          26 :         } else if (strcmp(name, "rapi") == 0) {
     541           0 :                 HOW_TO_ENABLE_ERROR("R", "embedded_r=true");
     542          26 :         } else if (strcmp(name, "capi") == 0) {
     543           0 :                 HOW_TO_ENABLE_ERROR("C/C++", "embedded_c=true");
     544             :         }
     545             :         return "";
     546             : }

Generated by: LCOV version 1.14