30
30
#include "postmaster/syslogger.h"
31
31
#include "storage/fd.h"
32
32
#include "storage/pmsignal.h"
33
+ #include "storage/proc.h"
33
34
#include "storage/procarray.h"
34
35
#include "tcop/tcopprot.h"
35
36
#include "utils/builtins.h"
@@ -70,15 +71,42 @@ current_query(PG_FUNCTION_ARGS)
70
71
}
71
72
72
73
/*
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.
74
83
*/
75
- static bool
84
+ #define SIGNAL_BACKEND_SUCCESS 0
85
+ #define SIGNAL_BACKEND_ERROR 1
86
+ #define SIGNAL_BACKEND_NOPERMISSION 2
87
+ static int
76
88
pg_signal_backend (int pid , int sig )
77
89
{
90
+ PGPROC * proc ;
91
+
78
92
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
+ }
82
110
83
111
if (!IsBackendPid (pid ))
84
112
{
@@ -88,9 +116,18 @@ pg_signal_backend(int pid, int sig)
88
116
*/
89
117
ereport (WARNING ,
90
118
(errmsg ("PID %d is not a PostgreSQL server process" , pid )));
91
- return false ;
119
+ return SIGNAL_BACKEND_ERROR ;
92
120
}
93
121
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
+
94
131
/* If we have setsid(), signal the backend's whole process group */
95
132
#ifdef HAVE_SETSID
96
133
if (kill (- pid , sig ))
@@ -101,23 +138,46 @@ pg_signal_backend(int pid, int sig)
101
138
/* Again, just a warning to allow loops */
102
139
ereport (WARNING ,
103
140
(errmsg ("could not send signal to process %d: %m" , pid )));
104
- return false ;
141
+ return SIGNAL_BACKEND_ERROR ;
105
142
}
106
- return true ;
143
+ return SIGNAL_BACKEND_SUCCESS ;
107
144
}
108
145
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
+ */
109
150
Datum
110
151
pg_cancel_backend (PG_FUNCTION_ARGS )
111
152
{
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 );
113
161
}
114
162
163
+ /*
164
+ * Signal to terminate a backend process. Only allowed by superuser.
165
+ */
115
166
Datum
116
167
pg_terminate_backend (PG_FUNCTION_ARGS )
117
168
{
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 );
119
176
}
120
177
178
+ /*
179
+ * Signal to reload the database configuration
180
+ */
121
181
Datum
122
182
pg_reload_conf (PG_FUNCTION_ARGS )
123
183
{
0 commit comments