Age Owner Branch data TLA 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 *
3067 tgl@sss.pgh.pa.us 95 :CBC 7810 : 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 : : */
94 peter@eisentraut.org 111 [ + + ]: 7810 : if (strncmp(filename, "$libdir/", 8) == 0)
112 : : {
10 113 [ + - ]: 6520 : if (first_dir_separator(filename + 8) == NULL)
114 : 6520 : filename += 8;
115 : : }
116 : :
117 : : /* Expand the possibly-abbreviated filename to an exact path name */
6962 tgl@sss.pgh.pa.us 118 : 7810 : fullname = expand_dynamic_library_name(filename);
119 : :
120 : : /* Load the shared library, unless we already did */
121 : 7810 : lib_handle = internal_load_library(fullname);
122 : :
123 : : /* Return handle if caller wants it */
124 [ + + ]: 7806 : if (filehandle)
125 : 6645 : *filehandle = lib_handle;
126 : :
127 : : /* Look up the function within the library. */
1880 peter@eisentraut.org 128 : 7806 : retval = dlsym(lib_handle, funcname);
129 : :
6962 tgl@sss.pgh.pa.us 130 [ + + + - ]: 7806 : 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 : 7803 : pfree(fullname);
137 : 7803 : 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
6961 149 : 1379 : load_file(const char *filename, bool restricted)
150 : : {
151 : : char *fullname;
152 : :
153 : : /* Apply security restriction if requested */
154 [ - + ]: 1379 : if (restricted)
6962 tgl@sss.pgh.pa.us 155 :UBC 0 : check_restricted_library_name(filename);
156 : :
157 : : /* Expand the possibly-abbreviated filename to an exact path name */
6962 tgl@sss.pgh.pa.us 158 :CBC 1379 : fullname = expand_dynamic_library_name(filename);
159 : :
160 : : /* Load the shared library, unless we already did */
161 : 1379 : (void) internal_load_library(fullname);
162 : :
163 : 1379 : pfree(fullname);
164 : 1379 : }
165 : :
166 : : /*
167 : : * Lookup a function whose library file is already loaded.
168 : : * Return NULL if not found.
169 : : */
170 : : void *
3067 171 : 6642 : lookup_external_function(void *filehandle, const char *funcname)
172 : : {
1880 peter@eisentraut.org 173 : 6642 : 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 *
6962 tgl@sss.pgh.pa.us 189 : 10759 : 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 : : */
9232 200 : 10759 : for (file_scanner = file_list;
7913 neilc@samurai.com 201 [ + + ]: 14749 : file_scanner != NULL &&
6962 tgl@sss.pgh.pa.us 202 [ + + ]: 9974 : strcmp(libname, file_scanner->filename) != 0;
9232 203 : 3990 : file_scanner = file_scanner->next)
204 : : ;
205 : :
7913 neilc@samurai.com 206 [ + + ]: 10759 : if (file_scanner == NULL)
207 : : {
208 : : /*
209 : : * Check for same files - different paths (ie, symlink or link)
210 : : */
6962 tgl@sss.pgh.pa.us 211 [ + + ]: 4775 : if (stat(libname, &stat_buf) == -1)
8086 212 [ + - ]: 4 : ereport(ERROR,
213 : : (errcode_for_file_access(),
214 : : errmsg("could not access file \"%s\": %m",
215 : : libname)));
216 : :
10226 bruce@momjian.us 217 : 4771 : for (file_scanner = file_list;
7913 neilc@samurai.com 218 [ + + ]: 6651 : file_scanner != NULL &&
8934 bruce@momjian.us 219 [ + - - - ]: 1880 : !SAME_INODE(stat_buf, *file_scanner);
10226 220 : 1880 : file_scanner = file_scanner->next)
221 : : ;
222 : : }
223 : :
7913 neilc@samurai.com 224 [ + + ]: 10755 : if (file_scanner == NULL)
225 : : {
226 : : /*
227 : : * File not loaded yet.
228 : : */
229 : : file_scanner = (DynamicFileList *)
2999 tgl@sss.pgh.pa.us 230 : 4771 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
9232 231 [ - + ]: 4771 : if (file_scanner == NULL)
8086 tgl@sss.pgh.pa.us 232 [ # # ]:UBC 0 : ereport(ERROR,
233 : : (errcode(ERRCODE_OUT_OF_MEMORY),
234 : : errmsg("out of memory")));
235 : :
3850 tgl@sss.pgh.pa.us 236 [ + - + - :CBC 28626 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
+ - + - +
+ ]
6962 237 : 4771 : strcpy(file_scanner->filename, libname);
10226 bruce@momjian.us 238 : 4771 : file_scanner->device = stat_buf.st_dev;
239 : : #ifndef WIN32
240 : 4771 : file_scanner->inode = stat_buf.st_ino;
241 : : #endif
7913 neilc@samurai.com 242 : 4771 : file_scanner->next = NULL;
243 : :
2557 peter_e@gmx.net 244 : 4771 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
7913 neilc@samurai.com 245 [ - + ]: 4771 : if (file_scanner->handle == NULL)
246 : : {
2557 peter_e@gmx.net 247 :UBC 0 : load_error = dlerror();
1107 peter@eisentraut.org 248 : 0 : free(file_scanner);
249 : : /* errcode_for_file_access might not be appropriate here? */
8086 tgl@sss.pgh.pa.us 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 */
7039 tgl@sss.pgh.pa.us 257 :CBC 4771 : magic_func = (PGModuleMagicFunction)
2557 peter_e@gmx.net 258 : 4771 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
7039 tgl@sss.pgh.pa.us 259 [ + - ]: 4771 : if (magic_func)
260 : : {
261 : 4771 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
262 : :
263 : : /* Check ABI compatibility fields */
164 264 [ + - ]: 4771 : if (magic_data_ptr->len != sizeof(Pg_magic_struct) ||
265 [ - + ]: 4771 : memcmp(&magic_data_ptr->abi_fields, &magic_data,
266 : : sizeof(Pg_abi_values)) != 0)
267 : : {
268 : : /* copy data block before unlinking library */
7039 tgl@sss.pgh.pa.us 269 :UBC 0 : Pg_magic_struct module_magic_data = *magic_data_ptr;
270 : :
271 : : /* try to close library */
2557 peter_e@gmx.net 272 : 0 : dlclose(file_scanner->handle);
1107 peter@eisentraut.org 273 : 0 : free(file_scanner);
274 : :
275 : : /* issue suitable complaint */
164 tgl@sss.pgh.pa.us 276 : 0 : incompatible_module_error(libname, &module_magic_data.abi_fields);
277 : : }
278 : :
279 : : /* Remember the magic block's location for future use */
164 tgl@sss.pgh.pa.us 280 :CBC 4771 : file_scanner->magic = magic_data_ptr;
281 : : }
282 : : else
283 : : {
284 : : /* try to close library */
2557 peter_e@gmx.net 285 :UBC 0 : dlclose(file_scanner->handle);
1107 peter@eisentraut.org 286 : 0 : free(file_scanner);
287 : : /* complain */
7038 tgl@sss.pgh.pa.us 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 : : */
2557 peter_e@gmx.net 297 :CBC 4771 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
6969 tgl@sss.pgh.pa.us 298 [ + + ]: 4771 : if (PG_init)
6912 bruce@momjian.us 299 : 3296 : (*PG_init) ();
300 : :
301 : : /* OK to link it into list */
7913 neilc@samurai.com 302 [ + + ]: 4771 : if (file_list == NULL)
9232 tgl@sss.pgh.pa.us 303 : 3983 : file_list = file_scanner;
304 : : else
305 : 788 : file_tail->next = file_scanner;
10226 bruce@momjian.us 306 : 4771 : file_tail = file_scanner;
307 : : }
308 : :
6962 tgl@sss.pgh.pa.us 309 : 10755 : return file_scanner->handle;
310 : : }
311 : :
312 : : /*
313 : : * Report a suitable error for an incompatible magic block.
314 : : */
315 : : static void
6212 tgl@sss.pgh.pa.us 316 :UBC 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 : :
3308 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);
6212 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 : : */
1384 peter@eisentraut.org 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 : : */
6212 tgl@sss.pgh.pa.us 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 */
367 michael@paquier.xyz 372 : 0 : _("Server has %s = %d, library has %d."),
373 : 0 : "FUNC_MAX_ARGS", magic_data.funcmaxargs,
6212 tgl@sss.pgh.pa.us 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 */
367 michael@paquier.xyz 382 : 0 : _("Server has %s = %d, library has %d."),
383 : 0 : "INDEX_MAX_KEYS", magic_data.indexmaxkeys,
6212 tgl@sss.pgh.pa.us 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 */
367 michael@paquier.xyz 392 : 0 : _("Server has %s = %d, library has %d."),
393 : 0 : "NAMEDATALEN", magic_data.namedatalen,
6212 tgl@sss.pgh.pa.us 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 */
367 michael@paquier.xyz 402 : 0 : _("Server has %s = %s, library has %s."),
403 [ # # ]: 0 : "FLOAT8PASSBYVAL", magic_data.float8byval ? "true" : "false",
6212 tgl@sss.pgh.pa.us 404 [ # # ]: 0 : module_magic_data->float8byval ? "true" : "false");
405 : : }
406 : :
407 [ # # ]: 0 : if (details.len == 0)
4328 rhaas@postgresql.org 408 : 0 : appendStringInfoString(&details,
2999 tgl@sss.pgh.pa.us 409 : 0 : _("Magic block has unexpected length or padding difference."));
410 : :
6212 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 *
164 tgl@sss.pgh.pa.us 425 :LBC (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 *
8878 peter_e@gmx.net 466 :CBC 9189 : expand_dynamic_library_name(const char *name)
467 : : {
468 : : bool have_slash;
469 : : char *new;
470 : : char *full;
471 : :
1044 peter@eisentraut.org 472 [ - + ]: 9189 : Assert(name);
473 : :
7758 bruce@momjian.us 474 : 9189 : have_slash = (first_dir_separator(name) != NULL);
475 : :
8878 peter_e@gmx.net 476 [ + + ]: 9189 : if (!have_slash)
477 : : {
171 peter@eisentraut.org 478 : 9033 : full = find_in_path(name, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
8878 peter_e@gmx.net 479 [ - + ]: 9033 : if (full)
8878 peter_e@gmx.net 480 :UBC 0 : return full;
481 : : }
482 : : else
483 : : {
171 peter@eisentraut.org 484 :CBC 156 : full = substitute_path_macro(name, "$libdir", pkglib_path);
603 michael@paquier.xyz 485 [ + - ]: 156 : if (pg_file_exists(full))
8876 peter_e@gmx.net 486 : 156 : return full;
8738 tgl@sss.pgh.pa.us 487 :UBC 0 : pfree(full);
488 : : }
489 : :
4346 peter_e@gmx.net 490 :CBC 9033 : new = psprintf("%s%s", name, DLSUFFIX);
491 : :
8878 492 [ + - ]: 9033 : if (!have_slash)
493 : : {
171 peter@eisentraut.org 494 : 9033 : full = find_in_path(new, Dynamic_library_path, "dynamic_library_path", "$libdir", pkglib_path);
8878 peter_e@gmx.net 495 : 9033 : pfree(new);
496 [ + + ]: 9033 : if (full)
497 : 9029 : return full;
498 : : }
499 : : else
500 : : {
171 peter@eisentraut.org 501 :UBC 0 : full = substitute_path_macro(new, "$libdir", pkglib_path);
8738 tgl@sss.pgh.pa.us 502 : 0 : pfree(new);
603 michael@paquier.xyz 503 [ # # ]: 0 : if (pg_file_exists(full))
8876 peter_e@gmx.net 504 : 0 : return full;
8738 tgl@sss.pgh.pa.us 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 : : */
6962 tgl@sss.pgh.pa.us 512 :CBC 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
6962 tgl@sss.pgh.pa.us 521 :UBC 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 *
171 peter@eisentraut.org 536 :CBC 18542 : substitute_path_macro(const char *str, const char *macro, const char *value)
537 : : {
538 : : const char *sep_ptr;
539 : :
540 [ - + ]: 18542 : Assert(str != NULL);
541 [ - + ]: 18542 : Assert(macro[0] == '$');
542 : :
543 : : /* Currently, we only recognize $macro at the start of the string */
544 [ + + ]: 18542 : if (str[0] != '$')
545 : 156 : return pstrdup(str);
546 : :
547 [ + - ]: 18386 : if ((sep_ptr = first_dir_separator(str)) == NULL)
548 : 18386 : sep_ptr = str + strlen(str);
549 : :
550 [ + - ]: 18386 : if (strlen(macro) != sep_ptr - str ||
551 [ - + ]: 18386 : strncmp(str, macro, strlen(macro)) != 0)
8086 tgl@sss.pgh.pa.us 552 [ # # ]:UBC 0 : ereport(ERROR,
553 : : (errcode(ERRCODE_INVALID_NAME),
554 : : errmsg("invalid macro name in path: %s",
555 : : str)));
556 : :
171 peter@eisentraut.org 557 :CBC 18386 : 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 : 18066 : 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 : :
1044 580 [ - + ]: 18066 : Assert(basename != NULL);
581 [ - + ]: 18066 : Assert(first_dir_separator(basename) == NULL);
171 582 [ - + ]: 18066 : Assert(path != NULL);
583 [ - + ]: 18066 : Assert(path_param != NULL);
584 : :
585 : 18066 : p = path;
586 : :
587 : : /*
588 : : * If the path variable is empty, don't do a path search.
589 : : */
8878 peter_e@gmx.net 590 [ - + ]: 18066 : if (strlen(p) == 0)
8878 peter_e@gmx.net 591 :UBC 0 : return NULL;
592 : :
8878 peter_e@gmx.net 593 :CBC 18066 : baselen = strlen(basename);
594 : :
595 : : for (;;)
8738 tgl@sss.pgh.pa.us 596 :UBC 0 : {
597 : : size_t len;
598 : : char *piece;
599 : : char *mangled;
600 : : char *full;
601 : :
5330 bruce@momjian.us 602 :CBC 18066 : piece = first_path_var_separator(p);
7678 603 [ - + ]: 18066 : if (piece == p)
8086 tgl@sss.pgh.pa.us 604 [ # # ]:UBC 0 : ereport(ERROR,
605 : : (errcode(ERRCODE_INVALID_NAME),
606 : : errmsg("zero-length component in parameter \"%s\"", path_param)));
607 : :
5426 tgl@sss.pgh.pa.us 608 [ + - ]:CBC 18066 : if (piece == NULL)
7678 bruce@momjian.us 609 : 18066 : len = strlen(p);
610 : : else
7678 bruce@momjian.us 611 :UBC 0 : len = piece - p;
612 : :
8876 peter_e@gmx.net 613 :CBC 18066 : piece = palloc(len + 1);
6786 614 : 18066 : strlcpy(piece, p, len + 1);
615 : :
171 peter@eisentraut.org 616 : 18066 : mangled = substitute_path_macro(piece, macro, macro_val);
8738 tgl@sss.pgh.pa.us 617 : 18066 : pfree(piece);
618 : :
7694 619 : 18066 : canonicalize_path(mangled);
620 : :
621 : : /* only absolute paths */
8191 bruce@momjian.us 622 [ - + ]: 18066 : if (!is_absolute_path(mangled))
8086 tgl@sss.pgh.pa.us 623 [ # # ]:UBC 0 : ereport(ERROR,
624 : : (errcode(ERRCODE_INVALID_NAME),
625 : : errmsg("component in parameter \"%s\" is not an absolute path", path_param)));
626 : :
8876 peter_e@gmx.net 627 :CBC 18066 : full = palloc(strlen(mangled) + 1 + baselen + 1);
628 : 18066 : sprintf(full, "%s/%s", mangled, basename);
8738 tgl@sss.pgh.pa.us 629 : 18066 : pfree(mangled);
630 : :
171 peter@eisentraut.org 631 [ + + ]: 18066 : elog(DEBUG3, "%s: trying \"%s\"", __func__, full);
632 : :
603 michael@paquier.xyz 633 [ + + ]: 18066 : if (pg_file_exists(full))
8878 peter_e@gmx.net 634 : 9029 : return full;
635 : :
636 : 9037 : pfree(full);
637 : :
638 [ + - ]: 9037 : if (p[len] == '\0')
639 : 9037 : break;
640 : : else
8878 peter_e@gmx.net 641 :UBC 0 : p += len + 1;
642 : : }
643 : :
8878 peter_e@gmx.net 644 :CBC 9037 : 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 **
6962 tgl@sss.pgh.pa.us 664 : 1878 : 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 [ + + ]: 1878 : if (rendezvousHash == NULL)
673 : : {
674 : : HASHCTL ctl;
675 : :
6912 bruce@momjian.us 676 : 1875 : ctl.keysize = NAMEDATALEN;
677 : 1875 : ctl.entrysize = sizeof(rendezvousHashEntry);
6962 tgl@sss.pgh.pa.us 678 : 1875 : 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 : 1878 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
686 : : varName,
687 : : HASH_ENTER,
688 : : &found);
689 : :
690 : : /* Initialize to NULL if first time */
691 [ + - ]: 1878 : if (!found)
692 : 1878 : hentry->varValue = NULL;
693 : :
694 : 1878 : 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
3782 rhaas@postgresql.org 702 : 456 : EstimateLibraryStateSpace(void)
703 : : {
704 : : DynamicFileList *file_scanner;
3759 bruce@momjian.us 705 : 456 : Size size = 1;
706 : :
3782 rhaas@postgresql.org 707 : 456 : for (file_scanner = file_list;
708 [ + + ]: 937 : file_scanner != NULL;
709 : 481 : file_scanner = file_scanner->next)
710 : 481 : size = add_size(size, strlen(file_scanner->filename) + 1);
711 : :
712 : 456 : return size;
713 : : }
714 : :
715 : : /*
716 : : * Serialize the list of libraries we have loaded to a chunk of memory.
717 : : */
718 : : void
719 : 456 : SerializeLibraryState(Size maxsize, char *start_address)
720 : : {
721 : : DynamicFileList *file_scanner;
722 : :
723 : 456 : for (file_scanner = file_list;
724 [ + + ]: 937 : file_scanner != NULL;
725 : 481 : file_scanner = file_scanner->next)
726 : : {
727 : : Size len;
728 : :
729 : 481 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
730 [ - + ]: 481 : Assert(len < maxsize);
731 : 481 : maxsize -= len;
732 : 481 : start_address += len;
733 : : }
734 : 456 : start_address[0] = '\0';
735 : 456 : }
736 : :
737 : : /*
738 : : * Load every library the serializing backend had loaded.
739 : : */
740 : : void
741 : 1378 : RestoreLibraryState(char *start_address)
742 : : {
743 [ + + ]: 2948 : while (*start_address != '\0')
744 : : {
745 : 1570 : internal_load_library(start_address);
746 : 1570 : start_address += strlen(start_address) + 1;
747 : : }
748 : 1378 : }
|