From 73f756b9b961588c4dbd732cc4fc0493e8e0f2a3 Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Sun, 21 Nov 2021 03:13:18 +0000 Subject: [PATCH v3] Add pg_ls_logicalsnapdir, pg_ls_logicalmapdir, pg_ls_replslotdir functions These functions lists the contents of the respective directories, and are intended to be used by monitoring tools. Unlike pg_ls_dir(), access to it can be granted to non-superusers so that those monitoring tools can observe the principle of least privilege. Access is also given by default to members of pg_monitor. Note: Bump the CATALOG_VERSION_NO --- doc/src/sgml/func.sgml | 73 ++++++++++++++++++++ src/backend/catalog/system_functions.sql | 12 ++++ src/backend/utils/adt/genfile.c | 47 +++++++++++++ src/include/catalog/pg_proc.dat | 15 ++++ src/test/regress/expected/misc_functions.out | 65 +++++++++++++++++ src/test/regress/sql/misc_functions.sql | 31 +++++++++ 6 files changed, 243 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 24447c0017..5e2a7753a5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -27417,6 +27417,79 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); can be granted EXECUTE to run the function. + + + + + pg_ls_logicalsnapdir + + pg_ls_logicalsnapdir () + setof record + ( name text, + size bigint, + modification timestamp with time zone ) + + + Returns the name, size, and last modification time (mtime) of each + ordinary file in the server's pg_logical/snapshots + directory. Filenames beginning with a dot, directories, and other + special files are excluded. + + + This function is restricted to superusers and members of + the pg_monitor role by default, but other users can + be granted EXECUTE to run the function. + + + + + + + pg_ls_logicalmapdir + + pg_ls_logicalmapdir () + setof record + ( name text, + size bigint, + modification timestamp with time zone ) + + + Returns the name, size, and last modification time (mtime) of each + ordinary file in the server's pg_logical/mappings + directory. Filenames beginning with a dot, directories, and other + special files are excluded. + + + This function is restricted to superusers and members of + the pg_monitor role by default, but other users can + be granted EXECUTE to run the function. + + + + + + + pg_ls_replslotdir + + pg_ls_replslotdir ( slot_name text ) + setof record + ( name text, + size bigint, + modification timestamp with time zone ) + + + Returns the name, size, and last modification time (mtime) of each + ordinary file in the server's pg_replslot/slot_name + (where slot_name is name of the replication slot + provided as an input to the function) directory. Filenames beginning + with a dot, directories, and other special files are excluded. + + + This function is restricted to superusers and members of + the pg_monitor role by default, but other users can + be granted EXECUTE to run the function. + + diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index 54c93b16c4..f6789025a5 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -701,6 +701,12 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public; REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION pg_ls_logicalsnapdir() FROM PUBLIC; + +REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC; + +REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC; + -- -- We also set up some things as accessible to standard roles. -- @@ -715,6 +721,12 @@ GRANT EXECUTE ON FUNCTION pg_ls_tmpdir() TO pg_monitor; GRANT EXECUTE ON FUNCTION pg_ls_tmpdir(oid) TO pg_monitor; +GRANT EXECUTE ON FUNCTION pg_ls_logicalsnapdir() TO pg_monitor; + +GRANT EXECUTE ON FUNCTION pg_ls_logicalmapdir() TO pg_monitor; + +GRANT EXECUTE ON FUNCTION pg_ls_replslotdir(text) TO pg_monitor; + GRANT pg_read_all_settings TO pg_monitor; GRANT pg_read_all_stats TO pg_monitor; diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index c436d9318b..6805a00d2c 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -29,6 +29,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "postmaster/syslogger.h" +#include "replication/slot.h" #include "storage/fd.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -720,3 +721,49 @@ pg_ls_archive_statusdir(PG_FUNCTION_ARGS) { return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true); } + +/* + * Function to return the list of files in the pg_logical/snapshots directory. + */ +Datum +pg_ls_logicalsnapdir(PG_FUNCTION_ARGS) +{ + return pg_ls_dir_files(fcinfo, "pg_logical/snapshots", false); +} + +/* + * Function to return the list of files in the pg_logical/mappings directory. + */ +Datum +pg_ls_logicalmapdir(PG_FUNCTION_ARGS) +{ + return pg_ls_dir_files(fcinfo, "pg_logical/mappings", false); +} + +/* + * Function to return the list of files in the pg_replslot/ + * directory. + */ +Datum +pg_ls_replslotdir(PG_FUNCTION_ARGS) +{ + text *slotname_t; + char path[MAXPGPATH]; + char *slotname; + ReplicationSlot *slot; + + slotname_t = PG_GETARG_TEXT_PP(0); + + slotname = text_to_cstring(slotname_t); + + slot = SearchNamedReplicationSlot(slotname, true); + + if (!slot) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("replication slot \"%s\" does not exist", + slotname))); + + snprintf(path, sizeof(path), "pg_replslot/%s", slotname); + return pg_ls_dir_files(fcinfo, path, false); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6412f369f1..509b9824fd 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -11623,6 +11623,21 @@ proallargtypes => '{oid,text,int8,timestamptz}', proargmodes => '{i,o,o,o}', proargnames => '{tablespace,name,size,modification}', prosrc => 'pg_ls_tmpdir_1arg' }, +{ oid => '4642', descr => 'list of files in the pg_logical/snapshots directory', + proname => 'pg_ls_logicalsnapdir', procost => '10', prorows => '20', proretset => 't', + provolatile => 'v', prorettype => 'record', proargtypes => '', + proallargtypes => '{text,int8,timestamptz}', proargmodes => '{o,o,o}', + proargnames => '{name,size,modification}', prosrc => 'pg_ls_logicalsnapdir' }, +{ oid => '4643', descr => 'list of files in the pg_logical/mappings directory', + proname => 'pg_ls_logicalmapdir', procost => '10', prorows => '20', proretset => 't', + provolatile => 'v', prorettype => 'record', proargtypes => '', + proallargtypes => '{text,int8,timestamptz}', proargmodes => '{o,o,o}', + proargnames => '{name,size,modification}', prosrc => 'pg_ls_logicalmapdir' }, +{ oid => '4644', descr => 'list of files in the pg_logical/mappings directory', + proname => 'pg_ls_replslotdir', procost => '10', prorows => '20', proretset => 't', + provolatile => 'v', prorettype => 'record', proargtypes => 'text', + proallargtypes => '{text,text,int8,timestamptz}', proargmodes => '{i,o,o,o}', + proargnames => '{slot_name,name,size,modification}', prosrc => 'pg_ls_replslotdir' }, # hash partitioning constraint function { oid => '5028', descr => 'hash partition CHECK constraint', diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index 71d316cad3..dbe9e57ddb 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -243,6 +243,71 @@ select count(*) > 0 from t (1 row) +-- +-- Test replication slot directory functions +-- +-- The outputs of these are variable, so we can't just print their results +-- directly, but we can at least verify that the code doesn't fail, and that +-- the permissions are set properly. +-- +CREATE ROLE regress_slot_dir_funcs; +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_logicalsnapdir()', 'EXECUTE'); -- no + has_function_privilege +------------------------ + f +(1 row) + +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_logicalmapdir()', 'EXECUTE'); -- no + has_function_privilege +------------------------ + f +(1 row) + +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_replslotdir(text)', 'EXECUTE'); -- no + has_function_privilege +------------------------ + f +(1 row) + +GRANT pg_monitor TO regress_slot_dir_funcs; +SELECT 'init' FROM pg_create_physical_replication_slot('slot_dir_funcs'); + ?column? +---------- + init +(1 row) + +SET ROLE regress_slot_dir_funcs; +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_logicalsnapdir(); + ok +---- + t +(1 row) + +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_logicalmapdir(); + ok +---- + t +(1 row) + +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_replslotdir('slot_dir_funcs'); + ok +---- + t +(1 row) + +SELECT pg_ls_replslotdir('non_existent_slot'); -- ERROR +ERROR: replication slot "non_existent_slot" does not exist +RESET ROLE; +SELECT pg_drop_replication_slot('slot_dir_funcs'); + pg_drop_replication_slot +-------------------------- + +(1 row) + +DROP ROLE regress_slot_dir_funcs; -- -- Test adding a support function to a subject function -- diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index 8c23874b3f..c47d013624 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -91,6 +91,37 @@ select count(*) > 0 from where spcname = 'pg_default') pts join pg_database db on pts.pts = db.oid; +-- +-- Test replication slot directory functions +-- +-- The outputs of these are variable, so we can't just print their results +-- directly, but we can at least verify that the code doesn't fail, and that +-- the permissions are set properly. +-- + +CREATE ROLE regress_slot_dir_funcs; + +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_logicalsnapdir()', 'EXECUTE'); -- no +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_logicalmapdir()', 'EXECUTE'); -- no +SELECT has_function_privilege('regress_slot_dir_funcs', + 'pg_ls_replslotdir(text)', 'EXECUTE'); -- no + +GRANT pg_monitor TO regress_slot_dir_funcs; + +SELECT 'init' FROM pg_create_physical_replication_slot('slot_dir_funcs'); + +SET ROLE regress_slot_dir_funcs; +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_logicalsnapdir(); +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_logicalmapdir(); +SELECT COUNT(*) >= 0 AS OK FROM pg_ls_replslotdir('slot_dir_funcs'); +SELECT pg_ls_replslotdir('non_existent_slot'); -- ERROR +RESET ROLE; + +SELECT pg_drop_replication_slot('slot_dir_funcs'); +DROP ROLE regress_slot_dir_funcs; + -- -- Test adding a support function to a subject function -- -- 2.25.1