Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/storage/ipc/signalfuncs.c
Line
Count
Source
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
0
#define SIGNAL_BACKEND_SUCCESS 0
47
#define SIGNAL_BACKEND_ERROR 1
48
0
#define SIGNAL_BACKEND_NOPERMISSION 2
49
0
#define SIGNAL_BACKEND_NOSUPERUSER 3
50
0
#define SIGNAL_BACKEND_NOAUTOVAC 4
51
static int
52
pg_signal_backend(int pid, int sig)
53
{
54
  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
  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
    ereport(WARNING,
75
        (errmsg("PID %d is not a PostgreSQL backend process", pid)));
76
77
    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
   */
89
  if (!OidIsValid(proc->roleId) || superuser_arg(proc->roleId))
90
  {
91
    if (proc->backendType == B_AUTOVAC_WORKER)
92
    {
93
      if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_AUTOVACUUM_WORKER))
94
        return SIGNAL_BACKEND_NOAUTOVAC;
95
    }
96
    else if (!superuser())
97
      return SIGNAL_BACKEND_NOSUPERUSER;
98
  }
99
  else if (!has_privs_of_role(GetUserId(), proc->roleId) &&
100
       !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
101
    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
114
  if (kill(-pid, sig))
115
#else
116
  if (kill(pid, sig))
117
#endif
118
  {
119
    /* Again, just a warning to allow loops */
120
    ereport(WARNING,
121
        (errmsg("could not send signal to process %d: %m", pid)));
122
    return SIGNAL_BACKEND_ERROR;
123
  }
124
  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
pg_cancel_backend(PG_FUNCTION_ARGS)
135
0
{
136
0
  int     r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
137
138
0
  if (r == SIGNAL_BACKEND_NOSUPERUSER)
139
0
    ereport(ERROR,
140
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
141
0
         errmsg("permission denied to cancel query"),
142
0
         errdetail("Only roles with the %s attribute may cancel queries of roles with the %s attribute.",
143
0
               "SUPERUSER", "SUPERUSER")));
144
145
0
  if (r == SIGNAL_BACKEND_NOAUTOVAC)
146
0
    ereport(ERROR,
147
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
148
0
         errmsg("permission denied to cancel query"),
149
0
         errdetail("Only roles with privileges of the \"%s\" role may cancel autovacuum workers.",
150
0
               "pg_signal_autovacuum_worker")));
151
152
0
  if (r == SIGNAL_BACKEND_NOPERMISSION)
153
0
    ereport(ERROR,
154
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
155
0
         errmsg("permission denied to cancel query"),
156
0
         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
0
               "pg_signal_backend")));
158
159
0
  PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
160
0
}
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
167
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
   */
173
  int64   waittime = 100;
174
175
  /*
176
   * Initially remaining time is the entire timeout specified by the user.
177
   */
178
  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
  {
188
    if (remainingtime < waittime)
189
      waittime = remainingtime;
190
191
    if (kill(pid, 0) == -1)
192
    {
193
      if (errno == ESRCH)
194
        return true;
195
      else
196
        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 */
203
    CHECK_FOR_INTERRUPTS();
204
205
    (void) WaitLatch(MyLatch,
206
             WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
207
             waittime,
208
             WAIT_EVENT_BACKEND_TERMINATION);
209
210
    ResetLatch(MyLatch);
211
212
    remainingtime -= waittime;
213
  } while (remainingtime > 0);
214
215
  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
  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
235
pg_terminate_backend(PG_FUNCTION_ARGS)
236
0
{
237
0
  int     pid;
238
0
  int     r;
239
0
  int     timeout;    /* milliseconds */
240
241
0
  pid = PG_GETARG_INT32(0);
242
0
  timeout = PG_GETARG_INT64(1);
243
244
0
  if (timeout < 0)
245
0
    ereport(ERROR,
246
0
        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
247
0
         errmsg("\"timeout\" must not be negative")));
248
249
0
  r = pg_signal_backend(pid, SIGTERM);
250
251
0
  if (r == SIGNAL_BACKEND_NOSUPERUSER)
252
0
    ereport(ERROR,
253
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
254
0
         errmsg("permission denied to terminate process"),
255
0
         errdetail("Only roles with the %s attribute may terminate processes of roles with the %s attribute.",
256
0
               "SUPERUSER", "SUPERUSER")));
257
258
0
  if (r == SIGNAL_BACKEND_NOAUTOVAC)
259
0
    ereport(ERROR,
260
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
261
0
         errmsg("permission denied to terminate process"),
262
0
         errdetail("Only roles with privileges of the \"%s\" role may terminate autovacuum workers.",
263
0
               "pg_signal_autovacuum_worker")));
264
265
0
  if (r == SIGNAL_BACKEND_NOPERMISSION)
266
0
    ereport(ERROR,
267
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
268
0
         errmsg("permission denied to terminate process"),
269
0
         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
0
               "pg_signal_backend")));
271
272
  /* Wait only on success and if actually requested */
273
0
  if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
274
0
    PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
275
0
  else
276
0
    PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
277
0
}
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
286
pg_reload_conf(PG_FUNCTION_ARGS)
287
{
288
  if (kill(PostmasterPid, SIGHUP))
289
  {
290
    ereport(WARNING,
291
        (errmsg("failed to send signal to postmaster: %m")));
292
    PG_RETURN_BOOL(false);
293
  }
294
295
  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
306
pg_rotate_logfile(PG_FUNCTION_ARGS)
307
{
308
  if (!Logging_collector)
309
  {
310
    ereport(WARNING,
311
        (errmsg("rotation not possible because log collection not active")));
312
    PG_RETURN_BOOL(false);
313
  }
314
315
  SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
316
  PG_RETURN_BOOL(true);
317
}