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
7688pg_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+ */
109150Datum
110151pg_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+ */
115166Datum
116167pg_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+ */
121181Datum
122182pg_reload_conf (PG_FUNCTION_ARGS )
123183{
0 commit comments