LCOV - code coverage report
Current view: top level - src/backend/utils/fmgr - dfmgr.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 144 210 68.6 %
Date: 2025-04-01 16:15:31 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * dfmgr.c
       4             :  *    Dynamic function manager code.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/fmgr/dfmgr.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include <sys/stat.h>
      18             : 
      19             : #ifndef WIN32
      20             : #include <dlfcn.h>
      21             : #endif                          /* !WIN32 */
      22             : 
      23             : #include "fmgr.h"
      24             : #include "lib/stringinfo.h"
      25             : #include "miscadmin.h"
      26             : #include "storage/fd.h"
      27             : #include "storage/shmem.h"
      28             : #include "utils/hsearch.h"
      29             : 
      30             : 
      31             : /* signature for PostgreSQL-specific library init function */
      32             : typedef void (*PG_init_t) (void);
      33             : 
      34             : /* hashtable entry for rendezvous variables */
      35             : typedef struct
      36             : {
      37             :     char        varName[NAMEDATALEN];   /* hash key (must be first) */
      38             :     void       *varValue;
      39             : } rendezvousHashEntry;
      40             : 
      41             : /*
      42             :  * List of dynamically loaded files (kept in malloc'd memory).
      43             :  *
      44             :  * Note: "typedef struct DynamicFileList DynamicFileList" appears in fmgr.h.
      45             :  */
      46             : struct DynamicFileList
      47             : {
      48             :     DynamicFileList *next;      /* List link */
      49             :     dev_t       device;         /* Device file is on */
      50             : #ifndef WIN32                   /* ensures we never again depend on this under
      51             :                                  * win32 */
      52             :     ino_t       inode;          /* Inode number of file */
      53             : #endif
      54             :     void       *handle;         /* a handle for pg_dl* functions */
      55             :     const Pg_magic_struct *magic;   /* Location of module's magic block */
      56             :     char        filename[FLEXIBLE_ARRAY_MEMBER];    /* Full pathname of file */
      57             : };
      58             : 
      59             : static DynamicFileList *file_list = NULL;
      60             : static DynamicFileList *file_tail = NULL;
      61             : 
      62             : /* stat() call under Win32 returns an st_ino field, but it has no meaning */
      63             : #ifndef WIN32
      64             : #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
      65             : #else
      66             : #define SAME_INODE(A,B) false
      67             : #endif
      68             : 
      69             : char       *Dynamic_library_path;
      70             : 
      71             : static void *internal_load_library(const char *libname);
      72             : pg_noreturn static void incompatible_module_error(const char *libname,
      73             :                                                   const Pg_abi_values *module_magic_data);
      74             : static char *expand_dynamic_library_name(const char *name);
      75             : static void check_restricted_library_name(const char *name);
      76             : 
      77             : /* ABI values that module needs to match to be accepted */
      78             : static const Pg_abi_values magic_data = PG_MODULE_ABI_DATA;
      79             : 
      80             : 
      81             : /*
      82             :  * Load the specified dynamic-link library file, and look for a function
      83             :  * named funcname in it.
      84             :  *
      85             :  * If the function is not found, we raise an error if signalNotFound is true,
      86             :  * else return NULL.  Note that errors in loading the library
      87             :  * will provoke ereport() regardless of signalNotFound.
      88             :  *
      89             :  * If filehandle is not NULL, then *filehandle will be set to a handle
      90             :  * identifying the library file.  The filehandle can be used with
      91             :  * lookup_external_function to lookup additional functions in the same file
      92             :  * at less cost than repeating load_external_function.
      93             :  */
      94             : void *
      95       15120 : load_external_function(const char *filename, const char *funcname,
      96             :                        bool signalNotFound, void **filehandle)
      97             : {
      98             :     char       *fullname;
      99             :     void       *lib_handle;
     100             :     void       *retval;
     101             : 
     102             :     /* Expand the possibly-abbreviated filename to an exact path name */
     103       15120 :     fullname = expand_dynamic_library_name(filename);
     104             : 
     105             :     /* Load the shared library, unless we already did */
     106       15120 :     lib_handle = internal_load_library(fullname);
     107             : 
     108             :     /* Return handle if caller wants it */
     109       15112 :     if (filehandle)
     110       12398 :         *filehandle = lib_handle;
     111             : 
     112             :     /* Look up the function within the library. */
     113       15112 :     retval = dlsym(lib_handle, funcname);
     114             : 
     115       15112 :     if (retval == NULL && signalNotFound)
     116           6 :         ereport(ERROR,
     117             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     118             :                  errmsg("could not find function \"%s\" in file \"%s\"",
     119             :                         funcname, fullname)));
     120             : 
     121       15106 :     pfree(fullname);
     122       15106 :     return retval;
     123             : }
     124             : 
     125             : /*
     126             :  * This function loads a shlib file without looking up any particular
     127             :  * function in it.  If the same shlib has previously been loaded,
     128             :  * we do not load it again.
     129             :  *
     130             :  * When 'restricted' is true, only libraries in the presumed-secure
     131             :  * directory $libdir/plugins may be referenced.
     132             :  */
     133             : void
     134        2498 : load_file(const char *filename, bool restricted)
     135             : {
     136             :     char       *fullname;
     137             : 
     138             :     /* Apply security restriction if requested */
     139        2498 :     if (restricted)
     140           0 :         check_restricted_library_name(filename);
     141             : 
     142             :     /* Expand the possibly-abbreviated filename to an exact path name */
     143        2498 :     fullname = expand_dynamic_library_name(filename);
     144             : 
     145             :     /* Load the shared library, unless we already did */
     146        2498 :     (void) internal_load_library(fullname);
     147             : 
     148        2498 :     pfree(fullname);
     149        2498 : }
     150             : 
     151             : /*
     152             :  * Lookup a function whose library file is already loaded.
     153             :  * Return NULL if not found.
     154             :  */
     155             : void *
     156       12392 : lookup_external_function(void *filehandle, const char *funcname)
     157             : {
     158       12392 :     return dlsym(filehandle, funcname);
     159             : }
     160             : 
     161             : 
     162             : /*
     163             :  * Load the specified dynamic-link library file, unless it already is
     164             :  * loaded.  Return the pg_dl* handle for the file.
     165             :  *
     166             :  * Note: libname is expected to be an exact name for the library file.
     167             :  *
     168             :  * NB: There is presently no way to unload a dynamically loaded file.  We might
     169             :  * add one someday if we can convince ourselves we have safe protocols for un-
     170             :  * hooking from hook function pointers, releasing custom GUC variables, and
     171             :  * perhaps other things that are definitely unsafe currently.
     172             :  */
     173             : static void *
     174       22532 : internal_load_library(const char *libname)
     175             : {
     176             :     DynamicFileList *file_scanner;
     177             :     PGModuleMagicFunction magic_func;
     178             :     char       *load_error;
     179             :     struct stat stat_buf;
     180             :     PG_init_t   PG_init;
     181             : 
     182             :     /*
     183             :      * Scan the list of loaded FILES to see if the file has been loaded.
     184             :      */
     185       33998 :     for (file_scanner = file_list;
     186       22886 :          file_scanner != NULL &&
     187       22886 :          strcmp(libname, file_scanner->filename) != 0;
     188       11466 :          file_scanner = file_scanner->next)
     189             :         ;
     190             : 
     191       22532 :     if (file_scanner == NULL)
     192             :     {
     193             :         /*
     194             :          * Check for same files - different paths (ie, symlink or link)
     195             :          */
     196       11112 :         if (stat(libname, &stat_buf) == -1)
     197           8 :             ereport(ERROR,
     198             :                     (errcode_for_file_access(),
     199             :                      errmsg("could not access file \"%s\": %m",
     200             :                             libname)));
     201             : 
     202       17584 :         for (file_scanner = file_list;
     203        6480 :              file_scanner != NULL &&
     204        6480 :              !SAME_INODE(stat_buf, *file_scanner);
     205        6480 :              file_scanner = file_scanner->next)
     206             :             ;
     207             :     }
     208             : 
     209       22524 :     if (file_scanner == NULL)
     210             :     {
     211             :         /*
     212             :          * File not loaded yet.
     213             :          */
     214             :         file_scanner = (DynamicFileList *)
     215       11104 :             malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
     216       11104 :         if (file_scanner == NULL)
     217           0 :             ereport(ERROR,
     218             :                     (errcode(ERRCODE_OUT_OF_MEMORY),
     219             :                      errmsg("out of memory")));
     220             : 
     221       66624 :         MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
     222       11104 :         strcpy(file_scanner->filename, libname);
     223       11104 :         file_scanner->device = stat_buf.st_dev;
     224             : #ifndef WIN32
     225       11104 :         file_scanner->inode = stat_buf.st_ino;
     226             : #endif
     227       11104 :         file_scanner->next = NULL;
     228             : 
     229       11104 :         file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
     230       11104 :         if (file_scanner->handle == NULL)
     231             :         {
     232           0 :             load_error = dlerror();
     233           0 :             free(file_scanner);
     234             :             /* errcode_for_file_access might not be appropriate here? */
     235           0 :             ereport(ERROR,
     236             :                     (errcode_for_file_access(),
     237             :                      errmsg("could not load library \"%s\": %s",
     238             :                             libname, load_error)));
     239             :         }
     240             : 
     241             :         /* Check the magic function to determine compatibility */
     242       11104 :         magic_func = (PGModuleMagicFunction)
     243       11104 :             dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
     244       11104 :         if (magic_func)
     245             :         {
     246       11104 :             const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
     247             : 
     248             :             /* Check ABI compatibility fields */
     249       11104 :             if (magic_data_ptr->len != sizeof(Pg_magic_struct) ||
     250       11104 :                 memcmp(&magic_data_ptr->abi_fields, &magic_data,
     251             :                        sizeof(Pg_abi_values)) != 0)
     252             :             {
     253             :                 /* copy data block before unlinking library */
     254           0 :                 Pg_magic_struct module_magic_data = *magic_data_ptr;
     255             : 
     256             :                 /* try to close library */
     257           0 :                 dlclose(file_scanner->handle);
     258           0 :                 free(file_scanner);
     259             : 
     260             :                 /* issue suitable complaint */
     261           0 :                 incompatible_module_error(libname, &module_magic_data.abi_fields);
     262             :             }
     263             : 
     264             :             /* Remember the magic block's location for future use */
     265       11104 :             file_scanner->magic = magic_data_ptr;
     266             :         }
     267             :         else
     268             :         {
     269             :             /* try to close library */
     270           0 :             dlclose(file_scanner->handle);
     271           0 :             free(file_scanner);
     272             :             /* complain */
     273           0 :             ereport(ERROR,
     274             :                     (errmsg("incompatible library \"%s\": missing magic block",
     275             :                             libname),
     276             :                      errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
     277             :         }
     278             : 
     279             :         /*
     280             :          * If the library has a _PG_init() function, call it.
     281             :          */
     282       11104 :         PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
     283       11104 :         if (PG_init)
     284        6068 :             (*PG_init) ();
     285             : 
     286             :         /* OK to link it into list */
     287       11104 :         if (file_list == NULL)
     288        7612 :             file_list = file_scanner;
     289             :         else
     290        3492 :             file_tail->next = file_scanner;
     291       11104 :         file_tail = file_scanner;
     292             :     }
     293             : 
     294       22524 :     return file_scanner->handle;
     295             : }
     296             : 
     297             : /*
     298             :  * Report a suitable error for an incompatible magic block.
     299             :  */
     300             : static void
     301           0 : incompatible_module_error(const char *libname,
     302             :                           const Pg_abi_values *module_magic_data)
     303             : {
     304             :     StringInfoData details;
     305             : 
     306             :     /*
     307             :      * If the version doesn't match, just report that, because the rest of the
     308             :      * block might not even have the fields we expect.
     309             :      */
     310           0 :     if (magic_data.version != module_magic_data->version)
     311             :     {
     312             :         char        library_version[32];
     313             : 
     314           0 :         if (module_magic_data->version >= 1000)
     315           0 :             snprintf(library_version, sizeof(library_version), "%d",
     316           0 :                      module_magic_data->version / 100);
     317             :         else
     318           0 :             snprintf(library_version, sizeof(library_version), "%d.%d",
     319           0 :                      module_magic_data->version / 100,
     320           0 :                      module_magic_data->version % 100);
     321           0 :         ereport(ERROR,
     322             :                 (errmsg("incompatible library \"%s\": version mismatch",
     323             :                         libname),
     324             :                  errdetail("Server is version %d, library is version %s.",
     325             :                            magic_data.version / 100, library_version)));
     326             :     }
     327             : 
     328             :     /*
     329             :      * Similarly, if the ABI extra field doesn't match, error out.  Other
     330             :      * fields below might also mismatch, but that isn't useful information if
     331             :      * you're using the wrong product altogether.
     332             :      */
     333           0 :     if (strcmp(module_magic_data->abi_extra, magic_data.abi_extra) != 0)
     334             :     {
     335           0 :         ereport(ERROR,
     336             :                 (errmsg("incompatible library \"%s\": ABI mismatch",
     337             :                         libname),
     338             :                  errdetail("Server has ABI \"%s\", library has \"%s\".",
     339             :                            magic_data.abi_extra,
     340             :                            module_magic_data->abi_extra)));
     341             :     }
     342             : 
     343             :     /*
     344             :      * Otherwise, spell out which fields don't agree.
     345             :      *
     346             :      * XXX this code has to be adjusted any time the set of fields in a magic
     347             :      * block change!
     348             :      */
     349           0 :     initStringInfo(&details);
     350             : 
     351           0 :     if (module_magic_data->funcmaxargs != magic_data.funcmaxargs)
     352             :     {
     353           0 :         if (details.len)
     354           0 :             appendStringInfoChar(&details, '\n');
     355           0 :         appendStringInfo(&details,
     356             :         /* translator: %s is a variable name and %d its values */
     357           0 :                          _("Server has %s = %d, library has %d."),
     358             :                          "FUNC_MAX_ARGS", magic_data.funcmaxargs,
     359             :                          module_magic_data->funcmaxargs);
     360             :     }
     361           0 :     if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
     362             :     {
     363           0 :         if (details.len)
     364           0 :             appendStringInfoChar(&details, '\n');
     365           0 :         appendStringInfo(&details,
     366             :         /* translator: %s is a variable name and %d its values */
     367           0 :                          _("Server has %s = %d, library has %d."),
     368             :                          "INDEX_MAX_KEYS", magic_data.indexmaxkeys,
     369             :                          module_magic_data->indexmaxkeys);
     370             :     }
     371           0 :     if (module_magic_data->namedatalen != magic_data.namedatalen)
     372             :     {
     373           0 :         if (details.len)
     374           0 :             appendStringInfoChar(&details, '\n');
     375           0 :         appendStringInfo(&details,
     376             :         /* translator: %s is a variable name and %d its values */
     377           0 :                          _("Server has %s = %d, library has %d."),
     378             :                          "NAMEDATALEN", magic_data.namedatalen,
     379             :                          module_magic_data->namedatalen);
     380             :     }
     381           0 :     if (module_magic_data->float8byval != magic_data.float8byval)
     382             :     {
     383           0 :         if (details.len)
     384           0 :             appendStringInfoChar(&details, '\n');
     385           0 :         appendStringInfo(&details,
     386             :         /* translator: %s is a variable name and %d its values */
     387           0 :                          _("Server has %s = %s, library has %s."),
     388           0 :                          "FLOAT8PASSBYVAL", magic_data.float8byval ? "true" : "false",
     389           0 :                          module_magic_data->float8byval ? "true" : "false");
     390             :     }
     391             : 
     392           0 :     if (details.len == 0)
     393           0 :         appendStringInfoString(&details,
     394           0 :                                _("Magic block has unexpected length or padding difference."));
     395             : 
     396           0 :     ereport(ERROR,
     397             :             (errmsg("incompatible library \"%s\": magic block mismatch",
     398             :                     libname),
     399             :              errdetail_internal("%s", details.data)));
     400             : }
     401             : 
     402             : 
     403             : /*
     404             :  * Iterator functions to allow callers to scan the list of loaded modules.
     405             :  *
     406             :  * Note: currently, there is no special provision for dealing with changes
     407             :  * in the list while a scan is happening.  Current callers don't need it.
     408             :  */
     409             : DynamicFileList *
     410           2 : get_first_loaded_module(void)
     411             : {
     412           2 :     return file_list;
     413             : }
     414             : 
     415             : DynamicFileList *
     416           2 : get_next_loaded_module(DynamicFileList *dfptr)
     417             : {
     418           2 :     return dfptr->next;
     419             : }
     420             : 
     421             : /*
     422             :  * Return some details about the specified module.
     423             :  *
     424             :  * Note that module_name and module_version could be returned as NULL.
     425             :  *
     426             :  * We could dispense with this function by exposing struct DynamicFileList
     427             :  * globally, but this way seems preferable.
     428             :  */
     429             : void
     430           2 : get_loaded_module_details(DynamicFileList *dfptr,
     431             :                           const char **library_path,
     432             :                           const char **module_name,
     433             :                           const char **module_version)
     434             : {
     435           2 :     *library_path = dfptr->filename;
     436           2 :     *module_name = dfptr->magic->name;
     437           2 :     *module_version = dfptr->magic->version;
     438           2 : }
     439             : 
     440             : 
     441             : /*
     442             :  * If name contains a slash, check if the file exists, if so return
     443             :  * the name.  Else (no slash) try to expand using search path (see
     444             :  * find_in_path below); if that works, return the fully
     445             :  * expanded file name.  If the previous failed, append DLSUFFIX and
     446             :  * try again.  If all fails, just return the original name.
     447             :  *
     448             :  * The result will always be freshly palloc'd.
     449             :  */
     450             : static char *
     451       17618 : expand_dynamic_library_name(const char *name)
     452             : {
     453             :     bool        have_slash;
     454             :     char       *new;
     455             :     char       *full;
     456             : 
     457             :     Assert(name);
     458             : 
     459             :     /*
     460             :      * If the value starts with "$libdir/", strip that.  This is because many
     461             :      * extensions have hardcoded '$libdir/foo' as their library name, which
     462             :      * prevents using the path.
     463             :      */
     464       17618 :     if (strncmp(name, "$libdir/", 8) == 0)
     465       12088 :         name += 8;
     466             : 
     467       17618 :     have_slash = (first_dir_separator(name) != NULL);
     468             : 
     469       17618 :     if (!have_slash)
     470             :     {
     471       16630 :         full = find_in_path(name, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
     472       16630 :         if (full)
     473           0 :             return full;
     474             :     }
     475             :     else
     476             :     {
     477         988 :         full = substitute_path_macro(name, "$libdir", pkglib_path);
     478         988 :         if (pg_file_exists(full))
     479         988 :             return full;
     480           0 :         pfree(full);
     481             :     }
     482             : 
     483       16630 :     new = psprintf("%s%s", name, DLSUFFIX);
     484             : 
     485       16630 :     if (!have_slash)
     486             :     {
     487       16630 :         full = find_in_path(new, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
     488       16630 :         pfree(new);
     489       16630 :         if (full)
     490       16622 :             return full;
     491             :     }
     492             :     else
     493             :     {
     494           0 :         full = substitute_path_macro(new, "$libdir", pkglib_path);
     495           0 :         pfree(new);
     496           0 :         if (pg_file_exists(full))
     497           0 :             return full;
     498           0 :         pfree(full);
     499             :     }
     500             : 
     501             :     /*
     502             :      * If we can't find the file, just return the string as-is. The ensuing
     503             :      * load attempt will fail and report a suitable message.
     504             :      */
     505           8 :     return pstrdup(name);
     506             : }
     507             : 
     508             : /*
     509             :  * Check a restricted library name.  It must begin with "$libdir/plugins/"
     510             :  * and there must not be any directory separators after that (this is
     511             :  * sufficient to prevent ".." style attacks).
     512             :  */
     513             : static void
     514           0 : check_restricted_library_name(const char *name)
     515             : {
     516           0 :     if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
     517           0 :         first_dir_separator(name + 16) != NULL)
     518           0 :         ereport(ERROR,
     519             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     520             :                  errmsg("access to library \"%s\" is not allowed",
     521             :                         name)));
     522           0 : }
     523             : 
     524             : /*
     525             :  * Substitute for any macros appearing in the given string.
     526             :  * Result is always freshly palloc'd.
     527             :  */
     528             : char *
     529       34912 : substitute_path_macro(const char *str, const char *macro, const char *value)
     530             : {
     531             :     const char *sep_ptr;
     532             : 
     533             :     Assert(str != NULL);
     534             :     Assert(macro[0] == '$');
     535             : 
     536             :     /* Currently, we only recognize $macro at the start of the string */
     537       34912 :     if (str[0] != '$')
     538         996 :         return pstrdup(str);
     539             : 
     540       33916 :     if ((sep_ptr = first_dir_separator(str)) == NULL)
     541       33916 :         sep_ptr = str + strlen(str);
     542             : 
     543       33916 :     if (strlen(macro) != sep_ptr - str ||
     544       33916 :         strncmp(str, macro, strlen(macro)) != 0)
     545           0 :         ereport(ERROR,
     546             :                 (errcode(ERRCODE_INVALID_NAME),
     547             :                  errmsg("invalid macro name in path: %s",
     548             :                         str)));
     549             : 
     550       33916 :     return psprintf("%s%s", value, sep_ptr);
     551             : }
     552             : 
     553             : 
     554             : /*
     555             :  * Search for a file called 'basename' in the colon-separated search
     556             :  * path given.  If the file is found, the full file name
     557             :  * is returned in freshly palloc'd memory.  If the file is not found,
     558             :  * return NULL.
     559             :  *
     560             :  * path_param is the name of the parameter that path came from, for error
     561             :  * messages.
     562             :  *
     563             :  * macro and macro_val allow substituting a macro; see
     564             :  * substitute_path_macro().
     565             :  */
     566             : char *
     567       33836 : find_in_path(const char *basename, const char *path, const char *path_param,
     568             :              const char *macro, const char *macro_val)
     569             : {
     570             :     const char *p;
     571             :     size_t      baselen;
     572             : 
     573             :     Assert(basename != NULL);
     574             :     Assert(first_dir_separator(basename) == NULL);
     575             :     Assert(path != NULL);
     576             :     Assert(path_param != NULL);
     577             : 
     578       33836 :     p = path;
     579             : 
     580             :     /*
     581             :      * If the path variable is empty, don't do a path search.
     582             :      */
     583       33836 :     if (strlen(p) == 0)
     584           0 :         return NULL;
     585             : 
     586       33836 :     baselen = strlen(basename);
     587             : 
     588             :     for (;;)
     589           2 :     {
     590             :         size_t      len;
     591             :         char       *piece;
     592             :         char       *mangled;
     593             :         char       *full;
     594             : 
     595       33838 :         piece = first_path_var_separator(p);
     596       33838 :         if (piece == p)
     597           0 :             ereport(ERROR,
     598             :                     (errcode(ERRCODE_INVALID_NAME),
     599             :                      errmsg("zero-length component in parameter \"%s\"", path_param)));
     600             : 
     601       33838 :         if (piece == NULL)
     602       33836 :             len = strlen(p);
     603             :         else
     604           2 :             len = piece - p;
     605             : 
     606       33838 :         piece = palloc(len + 1);
     607       33838 :         strlcpy(piece, p, len + 1);
     608             : 
     609       33838 :         mangled = substitute_path_macro(piece, macro, macro_val);
     610       33838 :         pfree(piece);
     611             : 
     612       33838 :         canonicalize_path(mangled);
     613             : 
     614             :         /* only absolute paths */
     615       33838 :         if (!is_absolute_path(mangled))
     616           0 :             ereport(ERROR,
     617             :                     (errcode(ERRCODE_INVALID_NAME),
     618             :                      errmsg("component in parameter \"%s\" is not an absolute path", path_param)));
     619             : 
     620       33838 :         full = palloc(strlen(mangled) + 1 + baselen + 1);
     621       33838 :         sprintf(full, "%s/%s", mangled, basename);
     622       33838 :         pfree(mangled);
     623             : 
     624       33838 :         elog(DEBUG3, "%s: trying \"%s\"", __func__, full);
     625             : 
     626       33838 :         if (pg_file_exists(full))
     627       17198 :             return full;
     628             : 
     629       16640 :         pfree(full);
     630             : 
     631       16640 :         if (p[len] == '\0')
     632       16638 :             break;
     633             :         else
     634           2 :             p += len + 1;
     635             :     }
     636             : 
     637       16638 :     return NULL;
     638             : }
     639             : 
     640             : 
     641             : /*
     642             :  * Find (or create) a rendezvous variable that one dynamically
     643             :  * loaded library can use to meet up with another.
     644             :  *
     645             :  * On the first call of this function for a particular varName,
     646             :  * a "rendezvous variable" is created with the given name.
     647             :  * The value of the variable is a void pointer (initially set to NULL).
     648             :  * Subsequent calls with the same varName just return the address of
     649             :  * the existing variable.  Once created, a rendezvous variable lasts
     650             :  * for the life of the process.
     651             :  *
     652             :  * Dynamically loaded libraries can use rendezvous variables
     653             :  * to find each other and share information: they just need to agree
     654             :  * on the variable name and the data it will point to.
     655             :  */
     656             : void      **
     657        3568 : find_rendezvous_variable(const char *varName)
     658             : {
     659             :     static HTAB *rendezvousHash = NULL;
     660             : 
     661             :     rendezvousHashEntry *hentry;
     662             :     bool        found;
     663             : 
     664             :     /* Create a hashtable if we haven't already done so in this process */
     665        3568 :     if (rendezvousHash == NULL)
     666             :     {
     667             :         HASHCTL     ctl;
     668             : 
     669        3562 :         ctl.keysize = NAMEDATALEN;
     670        3562 :         ctl.entrysize = sizeof(rendezvousHashEntry);
     671        3562 :         rendezvousHash = hash_create("Rendezvous variable hash",
     672             :                                      16,
     673             :                                      &ctl,
     674             :                                      HASH_ELEM | HASH_STRINGS);
     675             :     }
     676             : 
     677             :     /* Find or create the hashtable entry for this varName */
     678        3568 :     hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
     679             :                                                  varName,
     680             :                                                  HASH_ENTER,
     681             :                                                  &found);
     682             : 
     683             :     /* Initialize to NULL if first time */
     684        3568 :     if (!found)
     685        3568 :         hentry->varValue = NULL;
     686             : 
     687        3568 :     return &hentry->varValue;
     688             : }
     689             : 
     690             : /*
     691             :  * Estimate the amount of space needed to serialize the list of libraries
     692             :  * we have loaded.
     693             :  */
     694             : Size
     695         910 : EstimateLibraryStateSpace(void)
     696             : {
     697             :     DynamicFileList *file_scanner;
     698         910 :     Size        size = 1;
     699             : 
     700        2238 :     for (file_scanner = file_list;
     701             :          file_scanner != NULL;
     702        1328 :          file_scanner = file_scanner->next)
     703        1328 :         size = add_size(size, strlen(file_scanner->filename) + 1);
     704             : 
     705         910 :     return size;
     706             : }
     707             : 
     708             : /*
     709             :  * Serialize the list of libraries we have loaded to a chunk of memory.
     710             :  */
     711             : void
     712         910 : SerializeLibraryState(Size maxsize, char *start_address)
     713             : {
     714             :     DynamicFileList *file_scanner;
     715             : 
     716        2238 :     for (file_scanner = file_list;
     717             :          file_scanner != NULL;
     718        1328 :          file_scanner = file_scanner->next)
     719             :     {
     720             :         Size        len;
     721             : 
     722        1328 :         len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
     723             :         Assert(len < maxsize);
     724        1328 :         maxsize -= len;
     725        1328 :         start_address += len;
     726             :     }
     727         910 :     start_address[0] = '\0';
     728         910 : }
     729             : 
     730             : /*
     731             :  * Load every library the serializing backend had loaded.
     732             :  */
     733             : void
     734        2740 : RestoreLibraryState(char *start_address)
     735             : {
     736        7654 :     while (*start_address != '\0')
     737             :     {
     738        4914 :         internal_load_library(start_address);
     739        4914 :         start_address += strlen(start_address) + 1;
     740             :     }
     741        2740 : }

Generated by: LCOV version 1.14