aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2025-03-26 10:59:42 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2025-03-26 11:06:12 -0400
commit9324c8c580655800331b0582b770e88c01b7a5c4 (patch)
tree7abf1f4eebd29cb26fa05fc984db644fb174328d /src/backend
parente92c0632c1473fe57383c58f0dfdde3bae7044f4 (diff)
downloadpostgresql-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.c53
-rw-r--r--src/backend/utils/fmgr/dfmgr.c67
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