LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_linker.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 145 240 60.4 %
Date: 2024-04-25 20:03:45 Functions: 7 9 77.8 %

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

Generated by: LCOV version 1.14