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

Generated by: LCOV version 1.14