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>
|
<para>
|
||||||
The functions shown in <xref
|
The functions shown in <xref
|
||||||
linkend="functions-admin-signal-table"> send control signals to
|
linkend="functions-admin-signal-table"> send control signals to
|
||||||
other server processes. Use of these functions is restricted
|
other server processes. Use of these functions is usually restricted
|
||||||
to superusers.
|
to superusers, with noted exceptions.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<table id="functions-admin-signal-table">
|
<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>
|
<literal><function>pg_cancel_backend(<parameter>pid</parameter> <type>int</>)</function></literal>
|
||||||
</entry>
|
</entry>
|
||||||
<entry><type>boolean</type></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>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
@ -14322,6 +14325,10 @@ SELECT set_config('log_statement_stats', 'off', false);
|
|||||||
<command>postgres</command> processes on the server (using
|
<command>postgres</command> processes on the server (using
|
||||||
<application>ps</> on Unix or the <application>Task
|
<application>ps</> on Unix or the <application>Task
|
||||||
Manager</> on <productname>Windows</>).
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "postmaster/syslogger.h"
|
#include "postmaster/syslogger.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
#include "storage/pmsignal.h"
|
#include "storage/pmsignal.h"
|
||||||
|
#include "storage/proc.h"
|
||||||
#include "storage/procarray.h"
|
#include "storage/procarray.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.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)
|
pg_signal_backend(int pid, int sig)
|
||||||
{
|
{
|
||||||
|
PGPROC *proc;
|
||||||
|
|
||||||
if (!superuser())
|
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))
|
if (!IsBackendPid(pid))
|
||||||
{
|
{
|
||||||
@ -88,9 +116,18 @@ pg_signal_backend(int pid, int sig)
|
|||||||
*/
|
*/
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
(errmsg("PID %d is not a PostgreSQL server process", pid)));
|
(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 */
|
/* If we have setsid(), signal the backend's whole process group */
|
||||||
#ifdef HAVE_SETSID
|
#ifdef HAVE_SETSID
|
||||||
if (kill(-pid, sig))
|
if (kill(-pid, sig))
|
||||||
@ -101,23 +138,46 @@ pg_signal_backend(int pid, int sig)
|
|||||||
/* Again, just a warning to allow loops */
|
/* Again, just a warning to allow loops */
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
(errmsg("could not send signal to process %d: %m", pid)));
|
(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
|
Datum
|
||||||
pg_cancel_backend(PG_FUNCTION_ARGS)
|
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
|
Datum
|
||||||
pg_terminate_backend(PG_FUNCTION_ARGS)
|
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
|
Datum
|
||||||
pg_reload_conf(PG_FUNCTION_ARGS)
|
pg_reload_conf(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user