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