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