Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * signalfuncs.c
4 : : * Functions for signaling backends
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/storage/ipc/signalfuncs.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include <signal.h>
18 : :
19 : : #include "catalog/pg_authid.h"
20 : : #include "miscadmin.h"
21 : : #include "pgstat.h"
22 : : #include "postmaster/syslogger.h"
23 : : #include "storage/pmsignal.h"
24 : : #include "storage/proc.h"
25 : : #include "storage/procarray.h"
26 : : #include "utils/acl.h"
27 : : #include "utils/fmgrprotos.h"
28 : : #include "utils/wait_event.h"
29 : :
30 : :
31 : : /*
32 : : * Send a signal to another backend.
33 : : *
34 : : * The signal is delivered if the user is either a superuser or the same
35 : : * role as the backend being signaled. For "dangerous" signals, an explicit
36 : : * check for superuser needs to be done prior to calling this function.
37 : : *
38 : : * Returns 0 on success, 1 on general failure, 2 on normal permission error,
39 : : * 3 if the caller needs to be a superuser, and 4 if the caller needs to have
40 : : * privileges of pg_signal_autovacuum_worker.
41 : : *
42 : : * In the event of a general failure (return code 1), a warning message will
43 : : * be emitted. For permission errors, doing that is the responsibility of
44 : : * the caller.
45 : : */
46 : : #define SIGNAL_BACKEND_SUCCESS 0
47 : : #define SIGNAL_BACKEND_ERROR 1
48 : : #define SIGNAL_BACKEND_NOPERMISSION 2
49 : : #define SIGNAL_BACKEND_NOSUPERUSER 3
50 : : #define SIGNAL_BACKEND_NOAUTOVAC 4
51 : : static int
2719 michael@paquier.xyz 52 :CBC 71 : pg_signal_backend(int pid, int sig)
53 : : {
54 : 71 : PGPROC *proc = BackendPidGetProc(pid);
55 : :
56 : : /*
57 : : * BackendPidGetProc returns NULL if the pid isn't valid; but by the time
58 : : * we reach kill(), a process for which we get a valid proc here might
59 : : * have terminated on its own. There's no way to acquire a lock on an
60 : : * arbitrary process to prevent that. But since so far all the callers of
61 : : * this mechanism involve some request for ending the process anyway, that
62 : : * it might end on its own first is not a problem.
63 : : *
64 : : * Note that proc will also be NULL if the pid refers to an auxiliary
65 : : * process or the postmaster (neither of which can be signaled via
66 : : * pg_signal_backend()).
67 : : */
68 [ + + ]: 71 : if (proc == NULL)
69 : : {
70 : : /*
71 : : * This is just a warning so a loop-through-resultset will not abort
72 : : * if one backend terminated on its own during the run.
73 : : */
74 [ + - ]: 18 : ereport(WARNING,
75 : : (errmsg("PID %d is not a PostgreSQL backend process", pid)));
76 : :
77 : 18 : return SIGNAL_BACKEND_ERROR;
78 : : }
79 : :
80 : : /*
81 : : * Only allow superusers to signal superuser-owned backends. Any process
82 : : * not advertising a role might have the importance of a superuser-owned
83 : : * backend, so treat it that way. As an exception, we allow roles with
84 : : * privileges of pg_signal_autovacuum_worker to signal autovacuum workers
85 : : * (which do not advertise a role).
86 : : *
87 : : * Otherwise, users can signal backends for roles they have privileges of.
88 : : */
614 nathan@postgresql.or 89 [ + + + - ]: 53 : if (!OidIsValid(proc->roleId) || superuser_arg(proc->roleId))
614 nathan@postgresql.or 90 :ECB (38) : {
39 heikki.linnakangas@i 91 [ + + ]:GNC 53 : if (proc->backendType == B_AUTOVAC_WORKER)
92 : : {
614 nathan@postgresql.or 93 [ + + ]:CBC 3 : if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_AUTOVACUUM_WORKER))
94 : 2 : return SIGNAL_BACKEND_NOAUTOVAC;
95 : : }
96 [ + + ]: 50 : else if (!superuser())
97 : 10 : return SIGNAL_BACKEND_NOSUPERUSER;
98 : : }
614 nathan@postgresql.or 99 [ # # ]:UBC 0 : else if (!has_privs_of_role(GetUserId(), proc->roleId) &&
100 [ # # ]: 0 : !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
2719 michael@paquier.xyz 101 : 0 : return SIGNAL_BACKEND_NOPERMISSION;
102 : :
103 : : /*
104 : : * Can the process we just validated above end, followed by the pid being
105 : : * recycled for a new process, before reaching here? Then we'd be trying
106 : : * to kill the wrong thing. Seems near impossible when sequential pid
107 : : * assignment and wraparound is used. Perhaps it could happen on a system
108 : : * where pid re-use is randomized. That race condition possibility seems
109 : : * too unlikely to worry about.
110 : : */
111 : :
112 : : /* If we have setsid(), signal the backend's whole process group */
113 : : #ifdef HAVE_SETSID
2719 michael@paquier.xyz 114 [ - + ]:CBC 41 : if (kill(-pid, sig))
115 : : #else
116 : : if (kill(pid, sig))
117 : : #endif
118 : : {
119 : : /* Again, just a warning to allow loops */
2719 michael@paquier.xyz 120 [ # # ]:UBC 0 : ereport(WARNING,
121 : : (errmsg("could not send signal to process %d: %m", pid)));
122 : 0 : return SIGNAL_BACKEND_ERROR;
123 : : }
2719 michael@paquier.xyz 124 :CBC 41 : return SIGNAL_BACKEND_SUCCESS;
125 : : }
126 : :
127 : : /*
128 : : * Signal to cancel a backend process. This is allowed if you are a member of
129 : : * the role whose process is being canceled.
130 : : *
131 : : * Note that only superusers can signal superuser-owned processes.
132 : : */
133 : : Datum
134 : 31 : pg_cancel_backend(PG_FUNCTION_ARGS)
135 : : {
136 : 31 : int r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
137 : :
138 [ - + ]: 31 : if (r == SIGNAL_BACKEND_NOSUPERUSER)
2719 michael@paquier.xyz 139 [ # # ]:UBC 0 : ereport(ERROR,
140 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
141 : : errmsg("permission denied to cancel query"),
142 : : errdetail("Only roles with the %s attribute may cancel queries of roles with the %s attribute.",
143 : : "SUPERUSER", "SUPERUSER")));
144 : :
614 nathan@postgresql.or 145 [ - + ]:CBC 31 : if (r == SIGNAL_BACKEND_NOAUTOVAC)
614 nathan@postgresql.or 146 [ # # ]:UBC 0 : ereport(ERROR,
147 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
148 : : errmsg("permission denied to cancel query"),
149 : : errdetail("Only roles with privileges of the \"%s\" role may cancel autovacuum workers.",
150 : : "pg_signal_autovacuum_worker")));
151 : :
2719 michael@paquier.xyz 152 [ - + ]:CBC 31 : if (r == SIGNAL_BACKEND_NOPERMISSION)
2719 michael@paquier.xyz 153 [ # # ]:UBC 0 : ereport(ERROR,
154 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
155 : : errmsg("permission denied to cancel query"),
156 : : errdetail("Only roles with privileges of the role whose query is being canceled or with privileges of the \"%s\" role may cancel this query.",
157 : : "pg_signal_backend")));
158 : :
2719 michael@paquier.xyz 159 :CBC 31 : PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
160 : : }
161 : :
162 : : /*
163 : : * Wait until there is no backend process with the given PID and return true.
164 : : * On timeout, a warning is emitted and false is returned.
165 : : */
166 : : static bool
1802 magnus@hagander.net 167 : 3 : pg_wait_until_termination(int pid, int64 timeout)
168 : : {
169 : : /*
170 : : * Wait in steps of waittime milliseconds until this function exits or
171 : : * timeout.
172 : : */
1768 tgl@sss.pgh.pa.us 173 : 3 : int64 waittime = 100;
174 : :
175 : : /*
176 : : * Initially remaining time is the entire timeout specified by the user.
177 : : */
178 : 3 : int64 remainingtime = timeout;
179 : :
180 : : /*
181 : : * Check existence of the backend. If the backend still exists, then wait
182 : : * for waittime milliseconds, again check for the existence. Repeat this
183 : : * until timeout or an error occurs or a pending interrupt such as query
184 : : * cancel gets processed.
185 : : */
186 : : do
187 : : {
1802 magnus@hagander.net 188 [ - + ]: 10 : if (remainingtime < waittime)
1802 magnus@hagander.net 189 :UBC 0 : waittime = remainingtime;
190 : :
1802 magnus@hagander.net 191 [ + + ]:CBC 10 : if (kill(pid, 0) == -1)
192 : : {
193 [ + - ]: 3 : if (errno == ESRCH)
194 : 3 : return true;
195 : : else
1802 magnus@hagander.net 196 [ # # ]:UBC 0 : ereport(ERROR,
197 : : (errcode(ERRCODE_INTERNAL_ERROR),
198 : : errmsg("could not check the existence of the backend with PID %d: %m",
199 : : pid)));
200 : : }
201 : :
202 : : /* Process interrupts, if any, before waiting */
1802 magnus@hagander.net 203 [ - + ]:CBC 7 : CHECK_FOR_INTERRUPTS();
204 : :
205 : 7 : (void) WaitLatch(MyLatch,
206 : : WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
207 : : waittime,
208 : : WAIT_EVENT_BACKEND_TERMINATION);
209 : :
210 : 7 : ResetLatch(MyLatch);
211 : :
212 : 7 : remainingtime -= waittime;
213 [ + - ]: 7 : } while (remainingtime > 0);
214 : :
1802 magnus@hagander.net 215 [ # # ]:UBC 0 : ereport(WARNING,
216 : : (errmsg_plural("backend with PID %d did not terminate within %" PRId64 " millisecond",
217 : : "backend with PID %d did not terminate within %" PRId64 " milliseconds",
218 : : timeout,
219 : : pid, timeout)));
220 : :
221 : 0 : return false;
222 : : }
223 : :
224 : : /*
225 : : * Send a signal to terminate a backend process. This is allowed if you are a
226 : : * member of the role whose process is being terminated. If the timeout input
227 : : * argument is 0, then this function just signals the backend and returns
228 : : * true. If timeout is nonzero, then it waits until no process has the given
229 : : * PID; if the process ends within the timeout, true is returned, and if the
230 : : * timeout is exceeded, a warning is emitted and false is returned.
231 : : *
232 : : * Note that only superusers can signal superuser-owned processes.
233 : : */
234 : : Datum
2719 michael@paquier.xyz 235 :CBC 40 : pg_terminate_backend(PG_FUNCTION_ARGS)
236 : : {
237 : : int pid;
238 : : int r;
239 : : int timeout; /* milliseconds */
240 : :
1802 magnus@hagander.net 241 : 40 : pid = PG_GETARG_INT32(0);
242 : 40 : timeout = PG_GETARG_INT64(1);
243 : :
244 [ - + ]: 40 : if (timeout < 0)
1802 magnus@hagander.net 245 [ # # ]:UBC 0 : ereport(ERROR,
246 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
247 : : errmsg("\"timeout\" must not be negative")));
248 : :
1802 magnus@hagander.net 249 :CBC 40 : r = pg_signal_backend(pid, SIGTERM);
250 : :
2719 michael@paquier.xyz 251 [ + + ]: 40 : if (r == SIGNAL_BACKEND_NOSUPERUSER)
252 [ + - ]: 10 : ereport(ERROR,
253 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
254 : : errmsg("permission denied to terminate process"),
255 : : errdetail("Only roles with the %s attribute may terminate processes of roles with the %s attribute.",
256 : : "SUPERUSER", "SUPERUSER")));
257 : :
614 nathan@postgresql.or 258 [ + + ]: 30 : if (r == SIGNAL_BACKEND_NOAUTOVAC)
259 [ + - ]: 2 : ereport(ERROR,
260 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
261 : : errmsg("permission denied to terminate process"),
262 : : errdetail("Only roles with privileges of the \"%s\" role may terminate autovacuum workers.",
263 : : "pg_signal_autovacuum_worker")));
264 : :
2719 michael@paquier.xyz 265 [ - + ]: 28 : if (r == SIGNAL_BACKEND_NOPERMISSION)
2719 michael@paquier.xyz 266 [ # # ]:UBC 0 : ereport(ERROR,
267 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
268 : : errmsg("permission denied to terminate process"),
269 : : errdetail("Only roles with privileges of the role whose process is being terminated or with privileges of the \"%s\" role may terminate this process.",
270 : : "pg_signal_backend")));
271 : :
272 : : /* Wait only on success and if actually requested */
1802 magnus@hagander.net 273 [ + + + + ]:CBC 28 : if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
274 : 3 : PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
275 : : else
276 : 25 : PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
277 : : }
278 : :
279 : : /*
280 : : * Signal to reload the database configuration
281 : : *
282 : : * Permission checking for this function is managed through the normal
283 : : * GRANT system.
284 : : */
285 : : Datum
2719 michael@paquier.xyz 286 : 33 : pg_reload_conf(PG_FUNCTION_ARGS)
287 : : {
288 [ - + ]: 33 : if (kill(PostmasterPid, SIGHUP))
289 : : {
2719 michael@paquier.xyz 290 [ # # ]:UBC 0 : ereport(WARNING,
291 : : (errmsg("failed to send signal to postmaster: %m")));
292 : 0 : PG_RETURN_BOOL(false);
293 : : }
294 : :
2719 michael@paquier.xyz 295 :CBC 33 : PG_RETURN_BOOL(true);
296 : : }
297 : :
298 : :
299 : : /*
300 : : * Rotate log file
301 : : *
302 : : * Permission checking for this function is managed through the normal
303 : : * GRANT system.
304 : : */
305 : : Datum
741 dgustafsson@postgres 306 :UBC 0 : pg_rotate_logfile(PG_FUNCTION_ARGS)
307 : : {
2719 michael@paquier.xyz 308 [ # # ]: 0 : if (!Logging_collector)
309 : : {
310 [ # # ]: 0 : ereport(WARNING,
311 : : (errmsg("rotation not possible because log collection not active")));
312 : 0 : PG_RETURN_BOOL(false);
313 : : }
314 : :
315 : 0 : SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
316 : 0 : PG_RETURN_BOOL(true);
317 : : }
|