Coverage Report

Created: 2025-10-09 06:07

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