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

Generated by: LCOV version 2.0-1