diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2025-03-26 10:59:42 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2025-03-26 11:06:12 -0400 |
commit | 9324c8c580655800331b0582b770e88c01b7a5c4 (patch) | |
tree | 7abf1f4eebd29cb26fa05fc984db644fb174328d /src/backend | |
parent | e92c0632c1473fe57383c58f0dfdde3bae7044f4 (diff) | |
download | postgresql-9324c8c580655800331b0582b770e88c01b7a5c4.tar.gz postgresql-9324c8c580655800331b0582b770e88c01b7a5c4.zip |
Introduce PG_MODULE_MAGIC_EXT macro.
This macro allows dynamically loaded shared libraries (modules) to
provide a wired-in module name and version, and possibly other
compile-time-constant fields in future. This information can be
retrieved with the new pg_get_loaded_modules() function.
This feature is expected to be particularly useful for modules
that do not have any exposed SQL functionality and thus are
not associated with a SQL-level extension object. But even for
modules that do belong to extensions, being able to verify the
actual code version can be useful.
Author: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Yurii Rashkovskii <yrashk@omnigres.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/dd4d1b59-d0fe-49d5-b28f-1e463b68fa32@gmail.com
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/extension.c | 53 | ||||
-rw-r--r-- | src/backend/utils/fmgr/dfmgr.c | 67 |
2 files changed, 109 insertions, 11 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index dc38c325770..180f4af9be3 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -2812,6 +2812,59 @@ pg_extension_config_dump(PG_FUNCTION_ARGS) } /* + * pg_get_loaded_modules + * + * SQL-callable function to get per-loaded-module information. Modules + * (shared libraries) aren't necessarily one-to-one with extensions, but + * they're sufficiently closely related to make this file a good home. + */ +Datum +pg_get_loaded_modules(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + DynamicFileList *file_scanner; + + /* Build tuplestore to hold the result rows */ + InitMaterializedSRF(fcinfo, 0); + + for (file_scanner = get_first_loaded_module(); file_scanner != NULL; + file_scanner = get_next_loaded_module(file_scanner)) + { + const char *library_path, + *module_name, + *module_version; + const char *sep; + Datum values[3] = {0}; + bool nulls[3] = {0}; + + get_loaded_module_details(file_scanner, + &library_path, + &module_name, + &module_version); + + if (module_name == NULL) + nulls[0] = true; + else + values[0] = CStringGetTextDatum(module_name); + if (module_version == NULL) + nulls[1] = true; + else + values[1] = CStringGetTextDatum(module_version); + + /* For security reasons, we don't show the directory path */ + sep = last_dir_separator(library_path); + if (sep) + library_path = sep + 1; + values[2] = CStringGetTextDatum(library_path); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, + values, nulls); + } + + return (Datum) 0; +} + +/* * extension_config_remove * * Remove the specified table OID from extension's extconfig, if present. diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index dd4c83d1bba..603632581d0 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -40,19 +40,21 @@ typedef struct /* * List of dynamically loaded files (kept in malloc'd memory). + * + * Note: "typedef struct DynamicFileList DynamicFileList" appears in fmgr.h. */ - -typedef struct df_files +struct DynamicFileList { - struct df_files *next; /* List link */ + DynamicFileList *next; /* List link */ dev_t device; /* Device file is on */ #ifndef WIN32 /* ensures we never again depend on this under * win32 */ ino_t inode; /* Inode number of file */ #endif void *handle; /* a handle for pg_dl* functions */ + const Pg_magic_struct *magic; /* Location of module's magic block */ char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */ -} DynamicFileList; +}; static DynamicFileList *file_list = NULL; static DynamicFileList *file_tail = NULL; @@ -68,12 +70,12 @@ char *Dynamic_library_path; static void *internal_load_library(const char *libname); pg_noreturn static void incompatible_module_error(const char *libname, - const Pg_magic_struct *module_magic_data); + const Pg_abi_values *module_magic_data); static char *expand_dynamic_library_name(const char *name); static void check_restricted_library_name(const char *name); -/* Magic structure that module needs to match to be accepted */ -static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA; +/* ABI values that module needs to match to be accepted */ +static const Pg_abi_values magic_data = PG_MODULE_ABI_DATA; /* @@ -243,8 +245,10 @@ internal_load_library(const char *libname) { const Pg_magic_struct *magic_data_ptr = (*magic_func) (); - if (magic_data_ptr->len != magic_data.len || - memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0) + /* Check ABI compatibility fields */ + if (magic_data_ptr->len != sizeof(Pg_magic_struct) || + memcmp(&magic_data_ptr->abi_fields, &magic_data, + sizeof(Pg_abi_values)) != 0) { /* copy data block before unlinking library */ Pg_magic_struct module_magic_data = *magic_data_ptr; @@ -254,8 +258,11 @@ internal_load_library(const char *libname) free(file_scanner); /* issue suitable complaint */ - incompatible_module_error(libname, &module_magic_data); + incompatible_module_error(libname, &module_magic_data.abi_fields); } + + /* Remember the magic block's location for future use */ + file_scanner->magic = magic_data_ptr; } else { @@ -292,7 +299,7 @@ internal_load_library(const char *libname) */ static void incompatible_module_error(const char *libname, - const Pg_magic_struct *module_magic_data) + const Pg_abi_values *module_magic_data) { StringInfoData details; @@ -394,6 +401,44 @@ incompatible_module_error(const char *libname, /* + * Iterator functions to allow callers to scan the list of loaded modules. + * + * Note: currently, there is no special provision for dealing with changes + * in the list while a scan is happening. Current callers don't need it. + */ +DynamicFileList * +get_first_loaded_module(void) +{ + return file_list; +} + +DynamicFileList * +get_next_loaded_module(DynamicFileList *dfptr) +{ + return dfptr->next; +} + +/* + * Return some details about the specified module. + * + * Note that module_name and module_version could be returned as NULL. + * + * We could dispense with this function by exposing struct DynamicFileList + * globally, but this way seems preferable. + */ +void +get_loaded_module_details(DynamicFileList *dfptr, + const char **library_path, + const char **module_name, + const char **module_version) +{ + *library_path = dfptr->filename; + *module_name = dfptr->magic->name; + *module_version = dfptr->magic->version; +} + + +/* * If name contains a slash, check if the file exists, if so return * the name. Else (no slash) try to expand using search path (see * find_in_path below); if that works, return the fully |