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