LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_linker.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 146 242 60.3 %
Date: 2024-10-04 20:04:04 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         637 : fileexists(const char *path)
      68             : {
      69         637 :         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        2600 : loadLibrary(const char *filename, int flag)
     145             : {
     146        2600 :         int mode = RTLD_NOW | RTLD_GLOBAL;
     147        2600 :         char nme[FILENAME_MAX];
     148        2600 :         void *handle = NULL;
     149        2600 :         const char *s;
     150        2600 :         int idx;
     151        2600 :         const char *mod_path = GDKgetenv("monet_mod_path");
     152        2600 :         bool is_mod;
     153        2600 :         bool is_monetdb5 = strcmp(filename, "monetdb5") == 0;
     154             : 
     155        2600 :         is_mod = (!is_monetdb5 && strcmp(filename, "embedded") != 0);
     156             : 
     157        2600 :         if (lastfile == 0 && is_mod) {  /* first load reference to local functions */
     158         330 :                 str msg = loadLibrary("monetdb5", flag>=0?flag:0);
     159         330 :                 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       11754 :         for (idx = 0; idx < lastfile; idx++)
     169        9154 :                 if (filesLoaded[idx].modname &&
     170        9154 :                         strcmp(filesLoaded[idx].modname, filename) == 0)
     171             :                         /* already loaded */
     172             :                         return MAL_SUCCEED;
     173             : 
     174             :         /* ignore any path given */
     175        2600 :         if ((s = strrchr(filename, DIR_SEP)) == NULL)
     176        2600 :                 s = filename;
     177             : 
     178        2600 :         if (mod_path != NULL) {
     179        2572 :                 while (*mod_path == PATH_SEP)
     180           0 :                         mod_path++;
     181        2572 :                 if (*mod_path == 0)
     182             :                         mod_path = NULL;
     183             :         }
     184             :         if (mod_path == NULL) {
     185          28 :                 int len;
     186             : 
     187          28 :                 if (is_mod && flag < 0)
     188           0 :                         len = snprintf(nme, FILENAME_MAX, ".%c%s_%s%s", DIR_SEP, SO_PREFIX, s, SO_EXT);
     189          28 :                 else if (is_mod)
     190          15 :                         len = snprintf(nme, FILENAME_MAX, "%s_%s%s", SO_PREFIX, s, SO_EXT);
     191             :                 else
     192          13 :                         len = snprintf(nme, FILENAME_MAX, "%s%s%s", SO_PREFIX, s, SO_EXT);
     193          28 :                 if (len == -1 || len >= FILENAME_MAX)
     194           0 :                         throw(LOADER, "loadLibrary",
     195             :                                   RUNTIME_LOAD_ERROR "Library filename path is too large");
     196             : 
     197             : #ifdef __APPLE__
     198             :                 handle = mdlopen(is_monetdb5 ? NULL : nme, RTLD_NOW | RTLD_GLOBAL);
     199             : #else
     200          44 :                 handle = dlopen(is_monetdb5 ? NULL : nme, RTLD_NOW | RTLD_GLOBAL);
     201             : #endif
     202          28 :                 if (!handle) {
     203          16 :                         if (flag>0)
     204           0 :                                 throw(LOADER, "loadLibrary", RUNTIME_FILE_NOT_FOUND ":%s", s);
     205             :                         return MAL_SUCCEED;
     206             :                 }
     207             :         }
     208             : 
     209        2584 :         while (!handle && *mod_path) {
     210             :                 int len;
     211             :                 const char *p;
     212             : 
     213      221192 :                 for (p = mod_path; *p && *p != PATH_SEP; p++)
     214             :                         ;
     215             : 
     216        2572 :                 if (is_mod)
     217        2254 :                         len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s%s",
     218        2254 :                                                    (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX,
     219             :                                                    s, SO_EXT);
     220             :                 else
     221         318 :                         len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s%s",
     222         318 :                                                    (int) (p - mod_path), mod_path, DIR_SEP, SO_PREFIX,
     223             :                                                    s, SO_EXT);
     224        2572 :                 if (len == -1 || len >= FILENAME_MAX)
     225           0 :                         throw(LOADER, "loadLibrary",
     226             :                                   RUNTIME_LOAD_ERROR "Library filename path is too large");
     227        2572 :                 handle = dlopen(nme, mode);
     228        3209 :                 if (handle == NULL && fileexists(nme))
     229           0 :                         throw(LOADER, "loadLibrary",
     230             :                                   RUNTIME_LOAD_ERROR
     231             :                                   " failed to open library %s (from within file '%s'): %s", s,
     232             :                                   nme, dlerror());
     233        2572 :                 if (handle == NULL && strcmp(SO_EXT, ".so") != /* DISABLES CODE */ (0)) {
     234             :                         /* try .so */
     235             :                         if (is_mod)
     236             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s.so",
     237             :                                                            (int) (p - mod_path), mod_path, DIR_SEP,
     238             :                                                            SO_PREFIX, s);
     239             :                         else
     240             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s.so",
     241             :                                                            (int) (p - mod_path), mod_path, DIR_SEP,
     242             :                                                            SO_PREFIX, s);
     243             :                         if (len == -1 || len >= FILENAME_MAX)
     244             :                                 throw(LOADER, "loadLibrary",
     245             :                                           RUNTIME_LOAD_ERROR "Library filename path is too large");
     246             :                         handle = dlopen(nme, mode);
     247             :                         if (handle == NULL && fileexists(nme))
     248             :                                 throw(LOADER, "loadLibrary",
     249             :                                           RUNTIME_LOAD_ERROR
     250             :                                           " failed to open library %s (from within file '%s'): %s",
     251             :                                           s, nme, dlerror());
     252             :                 }
     253             : #ifdef __APPLE__
     254             :                 if (handle == NULL && strcmp(SO_EXT, ".bundle") != 0) {
     255             :                         /* try .bundle */
     256             :                         if (is_mod)
     257             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s_%s.bundle",
     258             :                                                            (int) (p - mod_path), mod_path, DIR_SEP,
     259             :                                                            SO_PREFIX, s);
     260             :                         else
     261             :                                 len = snprintf(nme, FILENAME_MAX, "%.*s%c%s%s.bundle",
     262             :                                                            (int) (p - mod_path), mod_path, DIR_SEP,
     263             :                                                            SO_PREFIX, s);
     264             :                         if (len == -1 || len >= FILENAME_MAX)
     265             :                                 throw(LOADER, "loadLibrary",
     266             :                                           RUNTIME_LOAD_ERROR "Library filename path is too large");
     267             :                         handle = dlopen(nme, mode);
     268             :                         if (handle == NULL && fileexists(nme))
     269             :                                 throw(LOADER, "loadLibrary",
     270             :                                           RUNTIME_LOAD_ERROR
     271             :                                           " failed to open library %s (from within file '%s'): %s",
     272             :                                           s, nme, dlerror());
     273             :                 }
     274             : #endif
     275             : 
     276        2572 :                 if (*p == 0 || handle != NULL)
     277             :                         break;
     278           0 :                 mod_path = p + 1;
     279             :         }
     280             : 
     281        2584 :         if (handle == NULL) {
     282         637 :                 if (!is_monetdb5
     283         319 :                         && strcmp(filename, "sql") != 0
     284           1 :                         && strcmp(filename, "generator") != 0
     285             : #ifdef HAVE_GEOM
     286           1 :                         && strcmp(filename, "geom") != 0
     287             : #endif
     288             : #ifdef HAVE_LIBR
     289           1 :                         && strcmp(filename, "rapi") != 0
     290             : #endif
     291             : #ifdef HAVE_LIBPY3
     292           1 :                         && strcmp(filename, "pyapi3") != 0
     293             : #endif
     294             : #ifdef HAVE_CUDF
     295           1 :                         && strcmp(filename, "capi") != 0
     296             : #endif
     297             : #ifdef HAVE_FITS
     298           1 :                         && strcmp(filename, "fits") != 0
     299             : #endif
     300             : #ifdef HAVE_NETCDF
     301           1 :                         && strcmp(filename, "netcdf") != 0
     302             : #endif
     303             : #ifdef HAVE_SHP
     304           1 :                         && strcmp(filename, "shp") != 0
     305             : #endif
     306             :                                 )
     307           1 :                         throw(LOADER, "loadLibrary",
     308             :                                   RUNTIME_LOAD_ERROR
     309             :                                   " could not locate library %s (from within file '%s'): %s", s,
     310             :                                   filename, dlerror());
     311             :         }
     312             : 
     313        2583 :         MT_lock_set(&mal_contextLock);
     314        2583 :         if (lastfile == maxfiles) {
     315           0 :                 MT_lock_unset(&mal_contextLock);
     316           0 :                 if (handle)
     317           0 :                         dlclose(handle);
     318           0 :                 throw(MAL, "mal.linker",
     319             :                           "loadModule internal error, too many modules loaded");
     320             :         } else {
     321        2583 :                 filesLoaded[lastfile].modname = GDKstrdup(filename);
     322        2583 :                 if (filesLoaded[lastfile].modname == NULL) {
     323           0 :                         MT_lock_unset(&mal_contextLock);
     324           0 :                         if (handle)
     325           0 :                                 dlclose(handle);
     326           0 :                         throw(LOADER, "loadLibrary",
     327             :                                   RUNTIME_LOAD_ERROR " could not allocate space");
     328             :                 }
     329        3219 :                 filesLoaded[lastfile].fullname = GDKstrdup(handle ? nme : "");
     330        2583 :                 if (filesLoaded[lastfile].fullname == NULL) {
     331           0 :                         GDKfree(filesLoaded[lastfile].modname);
     332           0 :                         MT_lock_unset(&mal_contextLock);
     333           0 :                         if (handle)
     334           0 :                                 dlclose(handle);
     335           0 :                         throw(LOADER, "loadLibrary",
     336             :                                   RUNTIME_LOAD_ERROR " could not allocate space");
     337             :                 }
     338        2583 :                 filesLoaded[lastfile].handle = handle ? handle : filesLoaded[0].handle;
     339        2583 :                 lastfile++;
     340             :         }
     341        2583 :         MT_lock_unset(&mal_contextLock);
     342             : 
     343        2583 :         return MAL_SUCCEED;
     344             : }
     345             : 
     346             : /*
     347             :  * For analysis of memory leaks we should cleanup the libraries before
     348             :  * we exit the server. This does not involve the libraries themselves,
     349             :  * because they may still be in use.
     350             :  */
     351             : void
     352         328 : mal_linker_reset(void)
     353             : {
     354         328 :         int i;
     355             : 
     356         328 :         MT_lock_set(&mal_contextLock);
     357        3230 :         for (i = 0; i < lastfile; i++) {
     358        2574 :                 if (filesLoaded[i].fullname) {
     359             :                         /* dlclose(filesLoaded[i].handle); */
     360        2574 :                         GDKfree(filesLoaded[i].modname);
     361        2574 :                         GDKfree(filesLoaded[i].fullname);
     362             :                 }
     363        2574 :                 filesLoaded[i].modname = NULL;
     364        2574 :                 filesLoaded[i].fullname = NULL;
     365             :         }
     366         328 :         lastfile = 0;
     367         328 :         MT_lock_unset(&mal_contextLock);
     368         328 : }
     369             : 
     370             : /*
     371             :  * Handling of Module Library Search Path
     372             :  * The plausible locations of the modules can be designated by
     373             :  * an environment variable.
     374             :  */
     375             : static int
     376           0 : cmpstr(const void *_p1, const void *_p2)
     377             : {
     378           0 :         const char *p1 = *(char *const *) _p1;
     379           0 :         const char *p2 = *(char *const *) _p2;
     380           0 :         const char *f1 = strrchr(p1, (int) DIR_SEP);
     381           0 :         const char *f2 = strrchr(p2, (int) DIR_SEP);
     382           0 :         return strcmp(f1 ? f1 : p1, f2 ? f2 : p2);
     383             : }
     384             : 
     385             : 
     386             : #define MAXMULTISCRIPT 48
     387             : char *
     388           7 : locate_file(const char *basename, const char *ext, bit recurse)
     389             : {
     390           7 :         const char *mod_path = GDKgetenv("monet_mod_path");
     391           7 :         char *fullname;
     392           7 :         size_t fullnamelen;
     393           7 :         size_t filelen = strlen(basename) + strlen(ext);
     394           7 :         str strs[MAXMULTISCRIPT];       /* hardwired limit */
     395           7 :         int lasts = 0;
     396             : 
     397           7 :         if (mod_path == NULL)
     398             :                 return NULL;
     399             : 
     400           7 :         while (*mod_path == PATH_SEP)
     401           0 :                 mod_path++;
     402           7 :         if (*mod_path == 0)
     403             :                 return NULL;
     404           7 :         fullnamelen = 512;
     405           7 :         fullname = GDKmalloc(fullnamelen);
     406           7 :         if (fullname == NULL)
     407             :                 return NULL;
     408           7 :         while (*mod_path) {
     409           7 :                 size_t i;
     410           7 :                 const char *p;
     411           7 :                 int fd;
     412           7 :                 DIR *rdir;
     413             : 
     414           7 :                 if ((p = strchr(mod_path, PATH_SEP)) != NULL) {
     415           0 :                         i = p - mod_path;
     416             :                 } else {
     417           7 :                         i = strlen(mod_path);
     418             :                 }
     419           7 :                 while (i + filelen + 2 > fullnamelen) {
     420           0 :                         char *tmp;
     421           0 :                         fullnamelen += 512;
     422           0 :                         tmp = GDKrealloc(fullname, fullnamelen);
     423           0 :                         if (tmp == NULL) {
     424           0 :                                 GDKfree(fullname);
     425           0 :                                 return NULL;
     426             :                         }
     427             :                         fullname = tmp;
     428             :                 }
     429             :                 /* we are now sure the directory name, file
     430             :                    base name, extension, and separator fit
     431             :                    into fullname, so we don't need to do any
     432             :                    extra checks */
     433           7 :                 strncpy(fullname, mod_path, i);
     434           7 :                 fullname[i] = DIR_SEP;
     435           7 :                 strcpy(fullname + i + 1, basename);
     436             :                 /* see if this is a directory, if so, recurse */
     437           7 :                 if (recurse == 1 && (rdir = opendir(fullname)) != NULL) {
     438             :                         struct dirent *e;
     439             :                         /* list *ext, sort, return */
     440           0 :                         while ((e = readdir(rdir)) != NULL) {
     441           0 :                                 if (strcmp(e->d_name, "..") == 0 || strcmp(e->d_name, ".") == 0)
     442           0 :                                         continue;
     443           0 :                                 if (strcmp(e->d_name + strlen(e->d_name) - strlen(ext), ext) == 0) {
     444           0 :                                         int len;
     445           0 :                                         strs[lasts] = GDKmalloc(strlen(fullname) + sizeof(DIR_SEP)
     446             :                                                                                         + strlen(e->d_name) +
     447             :                                                                                         sizeof(PATH_SEP) + 1);
     448           0 :                                         if (strs[lasts] == NULL) {
     449           0 :                                                 while (lasts >= 0)
     450           0 :                                                         GDKfree(strs[lasts--]);
     451           0 :                                                 GDKfree(fullname);
     452           0 :                                                 (void) closedir(rdir);
     453           0 :                                                 return NULL;
     454             :                                         }
     455           0 :                                         len = sprintf(strs[lasts], "%s%c%s%c", fullname, DIR_SEP,
     456             :                                                                   e->d_name, PATH_SEP);
     457           0 :                                         if (len == -1 || len >= FILENAME_MAX) {
     458           0 :                                                 while (lasts >= 0)
     459           0 :                                                         GDKfree(strs[lasts--]);
     460           0 :                                                 GDKfree(fullname);
     461           0 :                                                 (void) closedir(rdir);
     462           0 :                                                 return NULL;
     463             :                                         }
     464           0 :                                         lasts++;
     465             :                                 }
     466           0 :                                 if (lasts >= MAXMULTISCRIPT)
     467             :                                         break;
     468             :                         }
     469           0 :                         (void) closedir(rdir);
     470             :                 } else {
     471           7 :                         strcat(fullname + i + 1, ext);
     472           7 :                         if ((fd = MT_open(fullname, O_RDONLY | O_CLOEXEC)) >= 0) {
     473           6 :                                 char *tmp;
     474           6 :                                 close(fd);
     475           6 :                                 tmp = GDKrealloc(fullname, strlen(fullname) + 1);
     476           6 :                                 if (tmp == NULL)
     477             :                                         return fullname;
     478           6 :                                 return tmp;
     479             :                         }
     480             :                 }
     481           1 :                 if ((mod_path = p) == NULL)
     482             :                         break;
     483           0 :                 while (*mod_path == PATH_SEP)
     484           0 :                         mod_path++;
     485             :         }
     486           1 :         if (lasts > 0) {
     487           0 :                 size_t i = 0;
     488           0 :                 int c;
     489           0 :                 char *tmp;
     490             :                 /* assure that an ordering such as 10_first, 20_second works */
     491           0 :                 qsort(strs, lasts, sizeof(char *), cmpstr);
     492           0 :                 for (c = 0; c < lasts; c++)
     493           0 :                         i += strlen(strs[c]) + 1;       /* PATH_SEP or \0 */
     494           0 :                 tmp = GDKrealloc(fullname, i);
     495           0 :                 if (tmp == NULL) {
     496           0 :                         GDKfree(fullname);
     497           0 :                         return NULL;
     498             :                 }
     499           0 :                 fullname = tmp;
     500             :                 i = 0;
     501           0 :                 for (c = 0; c < lasts; c++) {
     502           0 :                         if (strstr(fullname, strs[c]) == NULL) {
     503           0 :                                 strcpy(fullname + i, strs[c]);
     504           0 :                                 i += strlen(strs[c]);
     505             :                         }
     506           0 :                         GDKfree(strs[c]);
     507             :                 }
     508           0 :                 fullname[i - 1] = '\0';
     509           0 :                 return fullname;
     510             :         }
     511             :         /* not found */
     512           1 :         GDKfree(fullname);
     513           1 :         return NULL;
     514             : }
     515             : 
     516             : char *
     517           1 : MSP_locate_script(const char *filename)
     518             : {
     519           1 :         return locate_file(filename, MAL_EXT, 1);
     520             : }
     521             : 
     522             : char *
     523           0 : MSP_locate_sqlscript(const char *filename, bit recurse)
     524             : {
     525             :         /* no directory semantics (yet) */
     526           0 :         return locate_file(filename, SQL_EXT, recurse);
     527             : }
     528             : 
     529             : int
     530       21659 : malLibraryEnabled(const char *name)
     531             : {
     532       21659 :         if (strcmp(name, "pyapi3") == 0) {
     533         328 :                 const char *val = GDKgetenv("embedded_py");
     534         328 :                 return val && (strcmp(val, "3") == 0 ||
     535           0 :                                            strcasecmp(val, "true") == 0 ||
     536           0 :                                            strcasecmp(val, "yes") == 0);
     537       21331 :         } else if (strcmp(name, "rapi") == 0) {
     538         324 :                 const char *val = GDKgetenv("embedded_r");
     539         324 :                 return val && (strcasecmp(val, "true") == 0 ||
     540           4 :                                            strcasecmp(val, "yes") == 0);
     541       21007 :         } else if (strcmp(name, "capi") == 0) {
     542         324 :                 const char *val = GDKgetenv("embedded_c");
     543         324 :                 return val && (strcasecmp(val, "true") == 0 ||
     544           2 :                                            strcasecmp(val, "yes") == 0);
     545             :         }
     546             :         return true;
     547             : }
     548             : 
     549             : #define HOW_TO_ENABLE_ERROR(LANGUAGE, OPTION)                                           \
     550             :         do {                                                                                                                    \
     551             :                 if (malLibraryEnabled(name))                                                            \
     552             :                         return "Embedded " LANGUAGE " has not been installed. "     \
     553             :                                 "Please install it first, then start server with "    \
     554             :                                 "--set " OPTION;                                                                      \
     555             :                 return "Embedded " LANGUAGE " has not been enabled. "               \
     556             :                         "Start server with --set " OPTION;                                            \
     557             :         } while (0)
     558             : 
     559             : char *
     560          21 : malLibraryHowToEnable(const char *name)
     561             : {
     562          21 :         if (strcmp(name, "pyapi3") == 0) {
     563           0 :                 HOW_TO_ENABLE_ERROR("Python 3", "embedded_py=3");
     564          21 :         } else if (strcmp(name, "rapi") == 0) {
     565           0 :                 HOW_TO_ENABLE_ERROR("R", "embedded_r=true");
     566          21 :         } else if (strcmp(name, "capi") == 0) {
     567           0 :                 HOW_TO_ENABLE_ERROR("C/C++", "embedded_c=true");
     568             :         }
     569             :         return "";
     570             : }

Generated by: LCOV version 1.14