Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0495aaa

Browse files
committed
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.
1 parent 652300f commit 0495aaa

File tree

2 files changed

+80
-13
lines changed

2 files changed

+80
-13
lines changed

doc/src/sgml/func.sgml

+10-3
Original file line numberDiff line numberDiff line change
@@ -14262,8 +14262,8 @@ SELECT set_config('log_statement_stats', 'off', false);
1426214262
<para>
1426314263
The functions shown in <xref
1426414264
linkend="functions-admin-signal-table"> send control signals to
14265-
other server processes. Use of these functions is restricted
14266-
to superusers.
14265+
other server processes. Use of these functions is usually restricted
14266+
to superusers, with noted exceptions.
1426714267
</para>
1426814268

1426914269
<table id="functions-admin-signal-table">
@@ -14280,7 +14280,10 @@ SELECT set_config('log_statement_stats', 'off', false);
1428014280
<literal><function>pg_cancel_backend(<parameter>pid</parameter> <type>int</>)</function></literal>
1428114281
</entry>
1428214282
<entry><type>boolean</type></entry>
14283-
<entry>Cancel a backend's current query</entry>
14283+
<entry>Cancel a backend's current query. You can execute this against
14284+
another backend that has exactly the same role as the user calling the
14285+
function. In all other cases, you must be a superuser.
14286+
</entry>
1428414287
</row>
1428514288
<row>
1428614289
<entry>
@@ -14322,6 +14325,10 @@ SELECT set_config('log_statement_stats', 'off', false);
1432214325
<command>postgres</command> processes on the server (using
1432314326
<application>ps</> on Unix or the <application>Task
1432414327
Manager</> on <productname>Windows</>).
14328+
For the less restrictive <function>pg_cancel_backend</>, the role of an
14329+
active backend can be found from
14330+
the <structfield>usename</structfield> column of the
14331+
<structname>pg_stat_activity</structname> view.
1432514332
</para>
1432614333

1432714334
<para>

src/backend/utils/adt/misc.c

+70-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "postmaster/syslogger.h"
3131
#include "storage/fd.h"
3232
#include "storage/pmsignal.h"
33+
#include "storage/proc.h"
3334
#include "storage/procarray.h"
3435
#include "tcop/tcopprot.h"
3536
#include "utils/builtins.h"
@@ -70,15 +71,42 @@ current_query(PG_FUNCTION_ARGS)
7071
}
7172

7273
/*
73-
* Functions to send signals to other backends.
74+
* Send a signal to another backend.
75+
* The signal is delivered if the user is either a superuser or the same
76+
* role as the backend being signaled. For "dangerous" signals, an explicit
77+
* check for superuser needs to be done prior to calling this function.
78+
*
79+
* Returns 0 on success, 1 on general failure, and 2 on permission error.
80+
* In the event of a general failure (returncode 1), a warning message will
81+
* be emitted. For permission errors, doing that is the responsibility of
82+
* the caller.
7483
*/
75-
static bool
84+
#define SIGNAL_BACKEND_SUCCESS 0
85+
#define SIGNAL_BACKEND_ERROR 1
86+
#define SIGNAL_BACKEND_NOPERMISSION 2
87+
static int
7688
pg_signal_backend(int pid, int sig)
7789
{
90+
PGPROC *proc;
91+
7892
if (!superuser())
79-
ereport(ERROR,
80-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
81-
(errmsg("must be superuser to signal other server processes"))));
93+
{
94+
/*
95+
* Since the user is not superuser, check for matching roles. Trust
96+
* that BackendPidGetProc will return NULL if the pid isn't valid,
97+
* even though the check for whether it's a backend process is below.
98+
* The IsBackendPid check can't be relied on as definitive even if it
99+
* was first. The process might end between successive checks
100+
* regardless of their order. There's no way to acquire a lock on an
101+
* arbitrary process to prevent that. But since so far all the callers
102+
* of this mechanism involve some request for ending the process
103+
* anyway, that it might end on its own first is not a problem.
104+
*/
105+
proc = BackendPidGetProc(pid);
106+
107+
if (proc == NULL || proc->roleId != GetUserId())
108+
return SIGNAL_BACKEND_NOPERMISSION;
109+
}
82110

83111
if (!IsBackendPid(pid))
84112
{
@@ -88,9 +116,18 @@ pg_signal_backend(int pid, int sig)
88116
*/
89117
ereport(WARNING,
90118
(errmsg("PID %d is not a PostgreSQL server process", pid)));
91-
return false;
119+
return SIGNAL_BACKEND_ERROR;
92120
}
93121

122+
/*
123+
* Can the process we just validated above end, followed by the pid being
124+
* recycled for a new process, before reaching here? Then we'd be trying
125+
* to kill the wrong thing. Seems near impossible when sequential pid
126+
* assignment and wraparound is used. Perhaps it could happen on a system
127+
* where pid re-use is randomized. That race condition possibility seems
128+
* too unlikely to worry about.
129+
*/
130+
94131
/* If we have setsid(), signal the backend's whole process group */
95132
#ifdef HAVE_SETSID
96133
if (kill(-pid, sig))
@@ -101,23 +138,46 @@ pg_signal_backend(int pid, int sig)
101138
/* Again, just a warning to allow loops */
102139
ereport(WARNING,
103140
(errmsg("could not send signal to process %d: %m", pid)));
104-
return false;
141+
return SIGNAL_BACKEND_ERROR;
105142
}
106-
return true;
143+
return SIGNAL_BACKEND_SUCCESS;
107144
}
108145

146+
/*
147+
* Signal to cancel a backend process. This is allowed if you are superuser or
148+
* have the same role as the process being canceled.
149+
*/
109150
Datum
110151
pg_cancel_backend(PG_FUNCTION_ARGS)
111152
{
112-
PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGINT));
153+
int r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
154+
155+
if (r == SIGNAL_BACKEND_NOPERMISSION)
156+
ereport(ERROR,
157+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
158+
(errmsg("must be superuser or have the same role to cancel queries running in other server processes"))));
159+
160+
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
113161
}
114162

163+
/*
164+
* Signal to terminate a backend process. Only allowed by superuser.
165+
*/
115166
Datum
116167
pg_terminate_backend(PG_FUNCTION_ARGS)
117168
{
118-
PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM));
169+
if (!superuser())
170+
ereport(ERROR,
171+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
172+
errmsg("must be superuser to terminate other server processes"),
173+
errhint("You can cancel your own processes with pg_cancel_backend().")));
174+
175+
PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM) == SIGNAL_BACKEND_SUCCESS);
119176
}
120177

178+
/*
179+
* Signal to reload the database configuration
180+
*/
121181
Datum
122182
pg_reload_conf(PG_FUNCTION_ARGS)
123183
{

0 commit comments

Comments
 (0)