Allow a user to kill his own queries using pg_cancel_backend()
Allows a user to use pg_cancel_queries() to cancel queries in other backends if they are running under the same role. pg_terminate_backend() still requires superuser permissoins. Short patch, many authors working on the bikeshed: Magnus Hagander, Josh Kupershmidt, Edward Muller, Greg Smith.
This commit is contained in:
parent
652300f539
commit
0495aaad8b
@ -14262,8 +14262,8 @@ SELECT set_config('log_statement_stats', 'off', false);
|
||||
<para>
|
||||
The functions shown in <xref
|
||||
linkend="functions-admin-signal-table"> 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.
|
||||
</para>
|
||||
|
||||
<table id="functions-admin-signal-table">
|
||||
@ -14280,7 +14280,10 @@ SELECT set_config('log_statement_stats', 'off', false);
|
||||
<literal><function>pg_cancel_backend(<parameter>pid</parameter> <type>int</>)</function></literal>
|
||||
</entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
<entry>Cancel a backend's current query</entry>
|
||||
<entry>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.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
@ -14322,6 +14325,10 @@ SELECT set_config('log_statement_stats', 'off', false);
|
||||
<command>postgres</command> processes on the server (using
|
||||
<application>ps</> on Unix or the <application>Task
|
||||
Manager</> on <productname>Windows</>).
|
||||
For the less restrictive <function>pg_cancel_backend</>, the role of an
|
||||
active backend can be found from
|
||||
the <structfield>usename</structfield> column of the
|
||||
<structname>pg_stat_activity</structname> view.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -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,42 @@ current_query(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions to send signals to other backends.
|
||||
* Send a signal to another backend.
|
||||
* The signal is delivered if the user is either a superuser or the same
|
||||
* role as the backend being signaled. For "dangerous" signals, an explicit
|
||||
* check for superuser needs to be done prior to calling this function.
|
||||
*
|
||||
* Returns 0 on success, 1 on general failure, and 2 on permission error.
|
||||
* In the event of a general failure (returncode 1), a warning message will
|
||||
* be emitted. For permission errors, doing that is the responsibility of
|
||||
* the caller.
|
||||
*/
|
||||
static bool
|
||||
#define SIGNAL_BACKEND_SUCCESS 0
|
||||
#define SIGNAL_BACKEND_ERROR 1
|
||||
#define SIGNAL_BACKEND_NOPERMISSION 2
|
||||
static int
|
||||
pg_signal_backend(int pid, int sig)
|
||||
{
|
||||
PGPROC *proc;
|
||||
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("must be superuser to signal other server processes"))));
|
||||
{
|
||||
/*
|
||||
* Since the user is not superuser, 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())
|
||||
return SIGNAL_BACKEND_NOPERMISSION;
|
||||
}
|
||||
|
||||
if (!IsBackendPid(pid))
|
||||
{
|
||||
@ -88,9 +116,18 @@ pg_signal_backend(int pid, int sig)
|
||||
*/
|
||||
ereport(WARNING,
|
||||
(errmsg("PID %d is not a PostgreSQL server process", pid)));
|
||||
return false;
|
||||
return SIGNAL_BACKEND_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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))
|
||||
@ -101,23 +138,46 @@ pg_signal_backend(int pid, int sig)
|
||||
/* Again, just a warning to allow loops */
|
||||
ereport(WARNING,
|
||||
(errmsg("could not send signal to process %d: %m", pid)));
|
||||
return false;
|
||||
return SIGNAL_BACKEND_ERROR;
|
||||
}
|
||||
return true;
|
||||
return SIGNAL_BACKEND_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
int r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
|
||||
|
||||
if (r == SIGNAL_BACKEND_NOPERMISSION)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("must be superuser or have the same role to cancel queries running in other server processes"))));
|
||||
|
||||
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to terminate other server processes"),
|
||||
errhint("You can cancel your own processes with pg_cancel_backend().")));
|
||||
|
||||
PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM) == SIGNAL_BACKEND_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal to reload the database configuration
|
||||
*/
|
||||
Datum
|
||||
pg_reload_conf(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user