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-2024, 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 : :
22 : : /*
23 : : * On macOS, <dlfcn.h> insists on including <stdbool.h>. If we're not
24 : : * using stdbool, undef bool to undo the damage.
25 : : */
26 : : #ifndef PG_USE_STDBOOL
27 : : #ifdef bool
28 : : #undef bool
29 : : #endif
30 : : #endif
31 : : #endif /* !WIN32 */
32 : :
33 : : #include "fmgr.h"
34 : : #include "lib/stringinfo.h"
35 : : #include "miscadmin.h"
36 : : #include "storage/fd.h"
37 : : #include "storage/shmem.h"
38 : : #include "utils/hsearch.h"
39 : :
40 : :
41 : : /* signature for PostgreSQL-specific library init function */
42 : : typedef void (*PG_init_t) (void);
43 : :
44 : : /* hashtable entry for rendezvous variables */
45 : : typedef struct
46 : : {
47 : : char varName[NAMEDATALEN]; /* hash key (must be first) */
48 : : void *varValue;
49 : : } rendezvousHashEntry;
50 : :
51 : : /*
52 : : * List of dynamically loaded files (kept in malloc'd memory).
53 : : */
54 : :
55 : : typedef struct df_files
56 : : {
57 : : struct df_files *next; /* List link */
58 : : dev_t device; /* Device file is on */
59 : : #ifndef WIN32 /* ensures we never again depend on this under
60 : : * win32 */
61 : : ino_t inode; /* Inode number of file */
62 : : #endif
63 : : void *handle; /* a handle for pg_dl* functions */
64 : : char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
65 : : } DynamicFileList;
66 : :
67 : : static DynamicFileList *file_list = NULL;
68 : : static DynamicFileList *file_tail = NULL;
69 : :
70 : : /* stat() call under Win32 returns an st_ino field, but it has no meaning */
71 : : #ifndef WIN32
72 : : #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
73 : : #else
74 : : #define SAME_INODE(A,B) false
75 : : #endif
76 : :
77 : : char *Dynamic_library_path;
78 : :
79 : : static void *internal_load_library(const char *libname);
80 : : static void incompatible_module_error(const char *libname,
81 : : const Pg_magic_struct *module_magic_data) pg_attribute_noreturn();
82 : : static char *expand_dynamic_library_name(const char *name);
83 : : static void check_restricted_library_name(const char *name);
84 : : static char *substitute_libpath_macro(const char *name);
85 : : static char *find_in_dynamic_libpath(const char *basename);
86 : :
87 : : /* Magic structure that module needs to match to be accepted */
88 : : static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
89 : :
90 : :
91 : : /*
92 : : * Load the specified dynamic-link library file, and look for a function
93 : : * named funcname in it.
94 : : *
95 : : * If the function is not found, we raise an error if signalNotFound is true,
96 : : * else return NULL. Note that errors in loading the library
97 : : * will provoke ereport() regardless of signalNotFound.
98 : : *
99 : : * If filehandle is not NULL, then *filehandle will be set to a handle
100 : : * identifying the library file. The filehandle can be used with
101 : : * lookup_external_function to lookup additional functions in the same file
102 : : * at less cost than repeating load_external_function.
103 : : */
104 : : void *
2557 tgl@sss.pgh.pa.us 105 :CBC 6850 : load_external_function(const char *filename, const char *funcname,
106 : : bool signalNotFound, void **filehandle)
107 : : {
108 : : char *fullname;
109 : : void *lib_handle;
110 : : void *retval;
111 : :
112 : : /* Expand the possibly-abbreviated filename to an exact path name */
6452 113 : 6850 : fullname = expand_dynamic_library_name(filename);
114 : :
115 : : /* Load the shared library, unless we already did */
116 : 6850 : lib_handle = internal_load_library(fullname);
117 : :
118 : : /* Return handle if caller wants it */
119 [ + + ]: 6846 : if (filehandle)
120 : 5848 : *filehandle = lib_handle;
121 : :
122 : : /* Look up the function within the library. */
1370 peter@eisentraut.org 123 : 6846 : retval = dlsym(lib_handle, funcname);
124 : :
6452 tgl@sss.pgh.pa.us 125 [ + + + - ]: 6846 : if (retval == NULL && signalNotFound)
126 [ + - ]: 3 : ereport(ERROR,
127 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
128 : : errmsg("could not find function \"%s\" in file \"%s\"",
129 : : funcname, fullname)));
130 : :
131 : 6843 : pfree(fullname);
132 : 6843 : return retval;
133 : : }
134 : :
135 : : /*
136 : : * This function loads a shlib file without looking up any particular
137 : : * function in it. If the same shlib has previously been loaded,
138 : : * unload and reload it.
139 : : *
140 : : * When 'restricted' is true, only libraries in the presumed-secure
141 : : * directory $libdir/plugins may be referenced.
142 : : */
143 : : void
6451 144 : 1298 : load_file(const char *filename, bool restricted)
145 : : {
146 : : char *fullname;
147 : :
148 : : /* Apply security restriction if requested */
149 [ - + ]: 1298 : if (restricted)
6452 tgl@sss.pgh.pa.us 150 :UBC 0 : check_restricted_library_name(filename);
151 : :
152 : : /* Expand the possibly-abbreviated filename to an exact path name */
6452 tgl@sss.pgh.pa.us 153 :CBC 1298 : fullname = expand_dynamic_library_name(filename);
154 : :
155 : : /* Load the shared library */
156 : 1298 : (void) internal_load_library(fullname);
157 : :
158 : 1297 : pfree(fullname);
159 : 1297 : }
160 : :
161 : : /*
162 : : * Lookup a function whose library file is already loaded.
163 : : * Return NULL if not found.
164 : : */
165 : : void *
2557 166 : 5845 : lookup_external_function(void *filehandle, const char *funcname)
167 : : {
1370 peter@eisentraut.org 168 : 5845 : return dlsym(filehandle, funcname);
169 : : }
170 : :
171 : :
172 : : /*
173 : : * Load the specified dynamic-link library file, unless it already is
174 : : * loaded. Return the pg_dl* handle for the file.
175 : : *
176 : : * Note: libname is expected to be an exact name for the library file.
177 : : *
178 : : * NB: There is presently no way to unload a dynamically loaded file. We might
179 : : * add one someday if we can convince ourselves we have safe protocols for un-
180 : : * hooking from hook function pointers, releasing custom GUC variables, and
181 : : * perhaps other things that are definitely unsafe currently.
182 : : */
183 : : static void *
6452 tgl@sss.pgh.pa.us 184 : 9676 : internal_load_library(const char *libname)
185 : : {
186 : : DynamicFileList *file_scanner;
187 : : PGModuleMagicFunction magic_func;
188 : : char *load_error;
189 : : struct stat stat_buf;
190 : : PG_init_t PG_init;
191 : :
192 : : /*
193 : : * Scan the list of loaded FILES to see if the file has been loaded.
194 : : */
8722 195 : 9676 : for (file_scanner = file_list;
7403 neilc@samurai.com 196 [ + + ]: 13568 : file_scanner != NULL &&
6452 tgl@sss.pgh.pa.us 197 [ + + ]: 8831 : strcmp(libname, file_scanner->filename) != 0;
8722 198 : 3892 : file_scanner = file_scanner->next)
199 : : ;
200 : :
7403 neilc@samurai.com 201 [ + + ]: 9676 : if (file_scanner == NULL)
202 : : {
203 : : /*
204 : : * Check for same files - different paths (ie, symlink or link)
205 : : */
6452 tgl@sss.pgh.pa.us 206 [ + + ]: 4737 : if (stat(libname, &stat_buf) == -1)
7576 207 [ + - ]: 4 : ereport(ERROR,
208 : : (errcode_for_file_access(),
209 : : errmsg("could not access file \"%s\": %m",
210 : : libname)));
211 : :
9716 bruce@momjian.us 212 : 4733 : for (file_scanner = file_list;
7403 neilc@samurai.com 213 [ + + ]: 6605 : file_scanner != NULL &&
8424 bruce@momjian.us 214 [ + - - - ]: 1872 : !SAME_INODE(stat_buf, *file_scanner);
9716 215 : 1872 : file_scanner = file_scanner->next)
216 : : ;
217 : : }
218 : :
7403 neilc@samurai.com 219 [ + + ]: 9672 : if (file_scanner == NULL)
220 : : {
221 : : /*
222 : : * File not loaded yet.
223 : : */
224 : : file_scanner = (DynamicFileList *)
2489 tgl@sss.pgh.pa.us 225 : 4733 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
8722 226 [ - + ]: 4733 : if (file_scanner == NULL)
7576 tgl@sss.pgh.pa.us 227 [ # # ]:UBC 0 : ereport(ERROR,
228 : : (errcode(ERRCODE_OUT_OF_MEMORY),
229 : : errmsg("out of memory")));
230 : :
3340 tgl@sss.pgh.pa.us 231 [ + - + - :CBC 23665 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
+ - + - +
+ ]
6452 232 : 4733 : strcpy(file_scanner->filename, libname);
9716 bruce@momjian.us 233 : 4733 : file_scanner->device = stat_buf.st_dev;
234 : : #ifndef WIN32
235 : 4733 : file_scanner->inode = stat_buf.st_ino;
236 : : #endif
7403 neilc@samurai.com 237 : 4733 : file_scanner->next = NULL;
238 : :
2047 peter_e@gmx.net 239 : 4733 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
7403 neilc@samurai.com 240 [ - + ]: 4732 : if (file_scanner->handle == NULL)
241 : : {
2047 peter_e@gmx.net 242 :UBC 0 : load_error = dlerror();
597 peter@eisentraut.org 243 : 0 : free(file_scanner);
244 : : /* errcode_for_file_access might not be appropriate here? */
7576 tgl@sss.pgh.pa.us 245 [ # # ]: 0 : ereport(ERROR,
246 : : (errcode_for_file_access(),
247 : : errmsg("could not load library \"%s\": %s",
248 : : libname, load_error)));
249 : : }
250 : :
251 : : /* Check the magic function to determine compatibility */
6529 tgl@sss.pgh.pa.us 252 :CBC 4732 : magic_func = (PGModuleMagicFunction)
2047 peter_e@gmx.net 253 : 4732 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
6529 tgl@sss.pgh.pa.us 254 [ + - ]: 4732 : if (magic_func)
255 : : {
256 : 4732 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
257 : :
258 [ + - ]: 4732 : if (magic_data_ptr->len != magic_data.len ||
6402 bruce@momjian.us 259 [ - + ]: 4732 : memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
260 : : {
261 : : /* copy data block before unlinking library */
6529 tgl@sss.pgh.pa.us 262 :UBC 0 : Pg_magic_struct module_magic_data = *magic_data_ptr;
263 : :
264 : : /* try to close library */
2047 peter_e@gmx.net 265 : 0 : dlclose(file_scanner->handle);
597 peter@eisentraut.org 266 : 0 : free(file_scanner);
267 : :
268 : : /* issue suitable complaint */
5702 tgl@sss.pgh.pa.us 269 : 0 : incompatible_module_error(libname, &module_magic_data);
270 : : }
271 : : }
272 : : else
273 : : {
274 : : /* try to close library */
2047 peter_e@gmx.net 275 : 0 : dlclose(file_scanner->handle);
597 peter@eisentraut.org 276 : 0 : free(file_scanner);
277 : : /* complain */
6528 tgl@sss.pgh.pa.us 278 [ # # ]: 0 : ereport(ERROR,
279 : : (errmsg("incompatible library \"%s\": missing magic block",
280 : : libname),
281 : : errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
282 : : }
283 : :
284 : : /*
285 : : * If the library has a _PG_init() function, call it.
286 : : */
2047 peter_e@gmx.net 287 :CBC 4732 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
6459 tgl@sss.pgh.pa.us 288 [ + + ]: 4732 : if (PG_init)
6402 bruce@momjian.us 289 : 3225 : (*PG_init) ();
290 : :
291 : : /* OK to link it into list */
7403 neilc@samurai.com 292 [ + + ]: 4732 : if (file_list == NULL)
8722 tgl@sss.pgh.pa.us 293 : 3967 : file_list = file_scanner;
294 : : else
295 : 765 : file_tail->next = file_scanner;
9716 bruce@momjian.us 296 : 4732 : file_tail = file_scanner;
297 : : }
298 : :
6452 tgl@sss.pgh.pa.us 299 : 9671 : return file_scanner->handle;
300 : : }
301 : :
302 : : /*
303 : : * Report a suitable error for an incompatible magic block.
304 : : */
305 : : static void
5702 tgl@sss.pgh.pa.us 306 :UBC 0 : incompatible_module_error(const char *libname,
307 : : const Pg_magic_struct *module_magic_data)
308 : : {
309 : : StringInfoData details;
310 : :
311 : : /*
312 : : * If the version doesn't match, just report that, because the rest of the
313 : : * block might not even have the fields we expect.
314 : : */
315 [ # # ]: 0 : if (magic_data.version != module_magic_data->version)
316 : : {
317 : : char library_version[32];
318 : :
2798 319 [ # # ]: 0 : if (module_magic_data->version >= 1000)
320 : 0 : snprintf(library_version, sizeof(library_version), "%d",
321 : 0 : module_magic_data->version / 100);
322 : : else
323 : 0 : snprintf(library_version, sizeof(library_version), "%d.%d",
324 : 0 : module_magic_data->version / 100,
325 : 0 : module_magic_data->version % 100);
5702 326 [ # # ]: 0 : ereport(ERROR,
327 : : (errmsg("incompatible library \"%s\": version mismatch",
328 : : libname),
329 : : errdetail("Server is version %d, library is version %s.",
330 : : magic_data.version / 100, library_version)));
331 : : }
332 : :
333 : : /*
334 : : * Similarly, if the ABI extra field doesn't match, error out. Other
335 : : * fields below might also mismatch, but that isn't useful information if
336 : : * you're using the wrong product altogether.
337 : : */
874 peter@eisentraut.org 338 [ # # ]: 0 : if (strcmp(module_magic_data->abi_extra, magic_data.abi_extra) != 0)
339 : : {
340 [ # # ]: 0 : ereport(ERROR,
341 : : (errmsg("incompatible library \"%s\": ABI mismatch",
342 : : libname),
343 : : errdetail("Server has ABI \"%s\", library has \"%s\".",
344 : : magic_data.abi_extra,
345 : : module_magic_data->abi_extra)));
346 : : }
347 : :
348 : : /*
349 : : * Otherwise, spell out which fields don't agree.
350 : : *
351 : : * XXX this code has to be adjusted any time the set of fields in a magic
352 : : * block change!
353 : : */
5702 tgl@sss.pgh.pa.us 354 : 0 : initStringInfo(&details);
355 : :
356 [ # # ]: 0 : if (module_magic_data->funcmaxargs != magic_data.funcmaxargs)
357 : : {
358 [ # # ]: 0 : if (details.len)
359 : 0 : appendStringInfoChar(&details, '\n');
360 : 0 : appendStringInfo(&details,
361 : 0 : _("Server has FUNC_MAX_ARGS = %d, library has %d."),
362 : 0 : magic_data.funcmaxargs,
363 : 0 : module_magic_data->funcmaxargs);
364 : : }
365 [ # # ]: 0 : if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
366 : : {
367 [ # # ]: 0 : if (details.len)
368 : 0 : appendStringInfoChar(&details, '\n');
369 : 0 : appendStringInfo(&details,
370 : 0 : _("Server has INDEX_MAX_KEYS = %d, library has %d."),
371 : 0 : magic_data.indexmaxkeys,
372 : 0 : module_magic_data->indexmaxkeys);
373 : : }
374 [ # # ]: 0 : if (module_magic_data->namedatalen != magic_data.namedatalen)
375 : : {
376 [ # # ]: 0 : if (details.len)
377 : 0 : appendStringInfoChar(&details, '\n');
378 : 0 : appendStringInfo(&details,
379 : 0 : _("Server has NAMEDATALEN = %d, library has %d."),
380 : 0 : magic_data.namedatalen,
381 : 0 : module_magic_data->namedatalen);
382 : : }
383 [ # # ]: 0 : if (module_magic_data->float8byval != magic_data.float8byval)
384 : : {
385 [ # # ]: 0 : if (details.len)
386 : 0 : appendStringInfoChar(&details, '\n');
387 : 0 : appendStringInfo(&details,
2489 388 : 0 : _("Server has FLOAT8PASSBYVAL = %s, library has %s."),
5702 389 [ # # ]: 0 : magic_data.float8byval ? "true" : "false",
390 [ # # ]: 0 : module_magic_data->float8byval ? "true" : "false");
391 : : }
392 : :
393 [ # # ]: 0 : if (details.len == 0)
3818 rhaas@postgresql.org 394 : 0 : appendStringInfoString(&details,
2489 tgl@sss.pgh.pa.us 395 : 0 : _("Magic block has unexpected length or padding difference."));
396 : :
5702 397 [ # # ]: 0 : ereport(ERROR,
398 : : (errmsg("incompatible library \"%s\": magic block mismatch",
399 : : libname),
400 : : errdetail_internal("%s", details.data)));
401 : : }
402 : :
403 : :
404 : : /*
405 : : * If name contains a slash, check if the file exists, if so return
406 : : * the name. Else (no slash) try to expand using search path (see
407 : : * find_in_dynamic_libpath below); if that works, return the fully
408 : : * expanded file name. If the previous failed, append DLSUFFIX and
409 : : * try again. If all fails, just return the original name.
410 : : *
411 : : * The result will always be freshly palloc'd.
412 : : */
413 : : static char *
8368 peter_e@gmx.net 414 :CBC 8148 : expand_dynamic_library_name(const char *name)
415 : : {
416 : : bool have_slash;
417 : : char *new;
418 : : char *full;
419 : :
534 peter@eisentraut.org 420 [ - + ]: 8148 : Assert(name);
421 : :
7248 bruce@momjian.us 422 : 8148 : have_slash = (first_dir_separator(name) != NULL);
423 : :
8368 peter_e@gmx.net 424 [ + + ]: 8148 : if (!have_slash)
425 : : {
426 : 2249 : full = find_in_dynamic_libpath(name);
427 [ - + ]: 2249 : if (full)
8368 peter_e@gmx.net 428 :UBC 0 : return full;
429 : : }
430 : : else
431 : : {
8366 peter_e@gmx.net 432 :CBC 5899 : full = substitute_libpath_macro(name);
93 michael@paquier.xyz 433 [ + + ]:GNC 5899 : if (pg_file_exists(full))
8366 peter_e@gmx.net 434 :CBC 191 : return full;
8228 tgl@sss.pgh.pa.us 435 : 5708 : pfree(full);
436 : : }
437 : :
3836 peter_e@gmx.net 438 : 7957 : new = psprintf("%s%s", name, DLSUFFIX);
439 : :
8368 440 [ + + ]: 7957 : if (!have_slash)
441 : : {
442 : 2249 : full = find_in_dynamic_libpath(new);
443 : 2249 : pfree(new);
444 [ + + ]: 2249 : if (full)
445 : 2245 : return full;
446 : : }
447 : : else
448 : : {
8366 449 : 5708 : full = substitute_libpath_macro(new);
8228 tgl@sss.pgh.pa.us 450 : 5708 : pfree(new);
93 michael@paquier.xyz 451 [ + - ]:GNC 5708 : if (pg_file_exists(full))
8366 peter_e@gmx.net 452 :CBC 5708 : return full;
8228 tgl@sss.pgh.pa.us 453 :UBC 0 : pfree(full);
454 : : }
455 : :
456 : : /*
457 : : * If we can't find the file, just return the string as-is. The ensuing
458 : : * load attempt will fail and report a suitable message.
459 : : */
6452 tgl@sss.pgh.pa.us 460 :CBC 4 : return pstrdup(name);
461 : : }
462 : :
463 : : /*
464 : : * Check a restricted library name. It must begin with "$libdir/plugins/"
465 : : * and there must not be any directory separators after that (this is
466 : : * sufficient to prevent ".." style attacks).
467 : : */
468 : : static void
6452 tgl@sss.pgh.pa.us 469 :UBC 0 : check_restricted_library_name(const char *name)
470 : : {
471 [ # # # # ]: 0 : if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
472 : 0 : first_dir_separator(name + 16) != NULL)
473 [ # # ]: 0 : ereport(ERROR,
474 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
475 : : errmsg("access to library \"%s\" is not allowed",
476 : : name)));
477 : 0 : }
478 : :
479 : : /*
480 : : * Substitute for any macros appearing in the given string.
481 : : * Result is always freshly palloc'd.
482 : : */
483 : : static char *
8207 bruce@momjian.us 484 :CBC 16105 : substitute_libpath_macro(const char *name)
485 : : {
486 : : const char *sep_ptr;
487 : :
534 peter@eisentraut.org 488 [ - + ]: 16105 : Assert(name != NULL);
489 : :
490 : : /* Currently, we only recognize $libdir at the start of the string */
8226 tgl@sss.pgh.pa.us 491 [ + + ]: 16105 : if (name[0] != '$')
8366 peter_e@gmx.net 492 : 191 : return pstrdup(name);
493 : :
7248 bruce@momjian.us 494 [ + + ]: 15914 : if ((sep_ptr = first_dir_separator(name)) == NULL)
7272 495 : 4498 : sep_ptr = name + strlen(name);
496 : :
497 [ + - ]: 15914 : if (strlen("$libdir") != sep_ptr - name ||
498 [ - + ]: 15914 : strncmp(name, "$libdir", strlen("$libdir")) != 0)
7576 tgl@sss.pgh.pa.us 499 [ # # ]:UBC 0 : ereport(ERROR,
500 : : (errcode(ERRCODE_INVALID_NAME),
501 : : errmsg("invalid macro name in dynamic library path: %s",
502 : : name)));
503 : :
3836 peter_e@gmx.net 504 :CBC 15914 : return psprintf("%s%s", pkglib_path, sep_ptr);
505 : : }
506 : :
507 : :
508 : : /*
509 : : * Search for a file called 'basename' in the colon-separated search
510 : : * path Dynamic_library_path. If the file is found, the full file name
511 : : * is returned in freshly palloc'd memory. If the file is not found,
512 : : * return NULL.
513 : : */
514 : : static char *
8207 bruce@momjian.us 515 : 4498 : find_in_dynamic_libpath(const char *basename)
516 : : {
517 : : const char *p;
518 : : size_t baselen;
519 : :
534 peter@eisentraut.org 520 [ - + ]: 4498 : Assert(basename != NULL);
521 [ - + ]: 4498 : Assert(first_dir_separator(basename) == NULL);
522 [ - + ]: 4498 : Assert(Dynamic_library_path != NULL);
523 : :
8368 peter_e@gmx.net 524 : 4498 : p = Dynamic_library_path;
525 [ - + ]: 4498 : if (strlen(p) == 0)
8368 peter_e@gmx.net 526 :UBC 0 : return NULL;
527 : :
8368 peter_e@gmx.net 528 :CBC 4498 : baselen = strlen(basename);
529 : :
530 : : for (;;)
8228 tgl@sss.pgh.pa.us 531 :UBC 0 : {
532 : : size_t len;
533 : : char *piece;
534 : : char *mangled;
535 : : char *full;
536 : :
4820 bruce@momjian.us 537 :CBC 4498 : piece = first_path_var_separator(p);
7168 538 [ - + ]: 4498 : if (piece == p)
7576 tgl@sss.pgh.pa.us 539 [ # # ]:UBC 0 : ereport(ERROR,
540 : : (errcode(ERRCODE_INVALID_NAME),
541 : : errmsg("zero-length component in parameter dynamic_library_path")));
542 : :
4916 tgl@sss.pgh.pa.us 543 [ + - ]:CBC 4498 : if (piece == NULL)
7168 bruce@momjian.us 544 : 4498 : len = strlen(p);
545 : : else
7168 bruce@momjian.us 546 :UBC 0 : len = piece - p;
547 : :
8366 peter_e@gmx.net 548 :CBC 4498 : piece = palloc(len + 1);
6276 549 : 4498 : strlcpy(piece, p, len + 1);
550 : :
8366 551 : 4498 : mangled = substitute_libpath_macro(piece);
8228 tgl@sss.pgh.pa.us 552 : 4498 : pfree(piece);
553 : :
7184 554 : 4498 : canonicalize_path(mangled);
555 : :
556 : : /* only absolute paths */
7681 bruce@momjian.us 557 [ - + ]: 4498 : if (!is_absolute_path(mangled))
7576 tgl@sss.pgh.pa.us 558 [ # # ]:UBC 0 : ereport(ERROR,
559 : : (errcode(ERRCODE_INVALID_NAME),
560 : : errmsg("component in parameter dynamic_library_path is not an absolute path")));
561 : :
8366 peter_e@gmx.net 562 :CBC 4498 : full = palloc(strlen(mangled) + 1 + baselen + 1);
563 : 4498 : sprintf(full, "%s/%s", mangled, basename);
8228 tgl@sss.pgh.pa.us 564 : 4498 : pfree(mangled);
565 : :
7576 566 [ - + ]: 4498 : elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
567 : :
93 michael@paquier.xyz 568 [ + + ]:GNC 4498 : if (pg_file_exists(full))
8368 peter_e@gmx.net 569 :CBC 2245 : return full;
570 : :
571 : 2253 : pfree(full);
572 : :
573 [ + - ]: 2253 : if (p[len] == '\0')
574 : 2253 : break;
575 : : else
8368 peter_e@gmx.net 576 :UBC 0 : p += len + 1;
577 : : }
578 : :
8368 peter_e@gmx.net 579 :CBC 2253 : return NULL;
580 : : }
581 : :
582 : :
583 : : /*
584 : : * Find (or create) a rendezvous variable that one dynamically
585 : : * loaded library can use to meet up with another.
586 : : *
587 : : * On the first call of this function for a particular varName,
588 : : * a "rendezvous variable" is created with the given name.
589 : : * The value of the variable is a void pointer (initially set to NULL).
590 : : * Subsequent calls with the same varName just return the address of
591 : : * the existing variable. Once created, a rendezvous variable lasts
592 : : * for the life of the process.
593 : : *
594 : : * Dynamically loaded libraries can use rendezvous variables
595 : : * to find each other and share information: they just need to agree
596 : : * on the variable name and the data it will point to.
597 : : */
598 : : void **
6452 tgl@sss.pgh.pa.us 599 : 1971 : find_rendezvous_variable(const char *varName)
600 : : {
601 : : static HTAB *rendezvousHash = NULL;
602 : :
603 : : rendezvousHashEntry *hentry;
604 : : bool found;
605 : :
606 : : /* Create a hashtable if we haven't already done so in this process */
607 [ + + ]: 1971 : if (rendezvousHash == NULL)
608 : : {
609 : : HASHCTL ctl;
610 : :
6402 bruce@momjian.us 611 : 1968 : ctl.keysize = NAMEDATALEN;
612 : 1968 : ctl.entrysize = sizeof(rendezvousHashEntry);
6452 tgl@sss.pgh.pa.us 613 : 1968 : rendezvousHash = hash_create("Rendezvous variable hash",
614 : : 16,
615 : : &ctl,
616 : : HASH_ELEM | HASH_STRINGS);
617 : : }
618 : :
619 : : /* Find or create the hashtable entry for this varName */
620 : 1971 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
621 : : varName,
622 : : HASH_ENTER,
623 : : &found);
624 : :
625 : : /* Initialize to NULL if first time */
626 [ + - ]: 1971 : if (!found)
627 : 1971 : hentry->varValue = NULL;
628 : :
629 : 1971 : return &hentry->varValue;
630 : : }
631 : :
632 : : /*
633 : : * Estimate the amount of space needed to serialize the list of libraries
634 : : * we have loaded.
635 : : */
636 : : Size
3272 rhaas@postgresql.org 637 : 414 : EstimateLibraryStateSpace(void)
638 : : {
639 : : DynamicFileList *file_scanner;
3249 bruce@momjian.us 640 : 414 : Size size = 1;
641 : :
3272 rhaas@postgresql.org 642 : 414 : for (file_scanner = file_list;
643 [ + + ]: 863 : file_scanner != NULL;
644 : 449 : file_scanner = file_scanner->next)
645 : 449 : size = add_size(size, strlen(file_scanner->filename) + 1);
646 : :
647 : 414 : return size;
648 : : }
649 : :
650 : : /*
651 : : * Serialize the list of libraries we have loaded to a chunk of memory.
652 : : */
653 : : void
654 : 414 : SerializeLibraryState(Size maxsize, char *start_address)
655 : : {
656 : : DynamicFileList *file_scanner;
657 : :
658 : 414 : for (file_scanner = file_list;
659 [ + + ]: 863 : file_scanner != NULL;
660 : 449 : file_scanner = file_scanner->next)
661 : : {
662 : : Size len;
663 : :
664 : 449 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
665 [ - + ]: 449 : Assert(len < maxsize);
666 : 449 : maxsize -= len;
667 : 449 : start_address += len;
668 : : }
669 : 414 : start_address[0] = '\0';
670 : 414 : }
671 : :
672 : : /*
673 : : * Load every library the serializing backend had loaded.
674 : : */
675 : : void
676 : 1322 : RestoreLibraryState(char *start_address)
677 : : {
678 [ + + ]: 2850 : while (*start_address != '\0')
679 : : {
680 : 1528 : internal_load_library(start_address);
681 : 1528 : start_address += strlen(start_address) + 1;
682 : : }
683 : 1322 : }
|