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