diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index e7f7fe0..cf77586 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14244,8 +14244,8 @@ SELECT set_config('log_statement_stats', 'off', false); The functions shown in send control signals to - other server processes. Use of these functions is restricted - to superusers. + other server processes. Use of these functions is usually restricted + to superusers, with noted exceptions. @@ -14262,7 +14262,10 @@ SELECT set_config('log_statement_stats', 'off', false); pg_cancel_backend(pid int)boolean - Cancel a backend's current query + Cancel a backend's current query. You can execute this against + another backend that has exactly the same role as the user calling the + function. In all other cases, you must be a superuser. + @@ -14304,6 +14307,10 @@ SELECT set_config('log_statement_stats', 'off', false); postgres processes on the server (using ps on Unix or the Task Manager on Windows). + For the less restrictive pg_cancel_backend, the role of an + active backend can be found from + the usename column of the + pg_stat_activity view. diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 7a2e0c8..45520b6 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -30,6 +30,7 @@ #include "postmaster/syslogger.h" #include "storage/fd.h" #include "storage/pmsignal.h" +#include "storage/proc.h" #include "storage/procarray.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" @@ -70,15 +71,45 @@ current_query(PG_FUNCTION_ARGS) } /* - * Functions to send signals to other backends. + * Send a signal to another backend. + * If allow_same_role is false, actionstr must be set to a string + * indicating what the signal does, to be inserted in the error message, and + * hint should be set to a hint to be sent along with this message. */ static bool -pg_signal_backend(int pid, int sig) +pg_signal_backend(int pid, int sig, bool allow_same_role, const char *actionstr, const char *hint) { + PGPROC *proc; + if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to signal other server processes")))); + { + if (allow_same_role) + { + /* + * When same role permission is allowed, check for matching roles. + * Trust that BackendPidGetProc will return NULL if the pid isn't + * valid, even though the check for whether it's a backend process + * is below. The IsBackendPid check can't be relied on as + * definitive even if it was first. The process might end between + * successive checks regardless of their order. There's no way to + * acquire a lock on an arbitrary process to prevent that. But + * since so far all the callers of this mechanism involve some + * request for ending the process anyway, that it might end on its + * own first is not a problem. + */ + proc = BackendPidGetProc(pid); + + if (proc == NULL || proc->roleId != GetUserId()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser or have the same role to signal other server processes")))); + } + else + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to %s other server processes", actionstr), + errhint("%s", hint))); + } if (!IsBackendPid(pid)) { @@ -91,6 +122,15 @@ pg_signal_backend(int pid, int sig) return false; } + /* + * Can the process we just validated above end, followed by the pid being + * recycled for a new process, before reaching here? Then we'd be trying + * to kill the wrong thing. Seems near impossible when sequential pid + * assignment and wraparound is used. Perhaps it could happen on a system + * where pid re-use is randomized. That race condition possibility seems + * too unlikely to worry about. + */ + /* If we have setsid(), signal the backend's whole process group */ #ifdef HAVE_SETSID if (kill(-pid, sig)) @@ -106,18 +146,30 @@ pg_signal_backend(int pid, int sig) return true; } +/* + * Signal to cancel a backend process. This is allowed if you are superuser or + * have the same role as the process being canceled. + */ Datum pg_cancel_backend(PG_FUNCTION_ARGS) { - PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGINT)); + PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGINT, true, NULL, NULL)); } +/* + * Signal to terminate a backend process. Only allowed by superuser. + */ Datum pg_terminate_backend(PG_FUNCTION_ARGS) { - PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM)); + PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM, false, + gettext_noop("terminate"), + gettext_noop("You can cancel your own processes with pg_cancel_backend()."))); } +/* + * Signal to reload the database configuration + */ Datum pg_reload_conf(PG_FUNCTION_ARGS) {