LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - signalfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 68.1 % 69 47
Test Date: 2026-03-24 01:16:09 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit

            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
      52           80 : pg_signal_backend(int pid, int sig)
      53              : {
      54           80 :     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           80 :     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           24 :         ereport(WARNING,
      75              :                 (errmsg("PID %d is not a PostgreSQL backend process", pid)));
      76              : 
      77           24 :         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           56 :     if (!OidIsValid(proc->roleId) || superuser_arg(proc->roleId))
      90              :     {
      91           56 :         if (proc->backendType == B_AUTOVAC_WORKER)
      92              :         {
      93            2 :             if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_AUTOVACUUM_WORKER))
      94            1 :                 return SIGNAL_BACKEND_NOAUTOVAC;
      95              :         }
      96           54 :         else if (!superuser())
      97           13 :             return SIGNAL_BACKEND_NOSUPERUSER;
      98              :     }
      99            0 :     else if (!has_privs_of_role(GetUserId(), proc->roleId) &&
     100            0 :              !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
     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
     114           42 :     if (kill(-pid, sig))
     115              : #else
     116              :     if (kill(pid, sig))
     117              : #endif
     118              :     {
     119              :         /* Again, just a warning to allow loops */
     120            0 :         ereport(WARNING,
     121              :                 (errmsg("could not send signal to process %d: %m", pid)));
     122            0 :         return SIGNAL_BACKEND_ERROR;
     123              :     }
     124           42 :     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           32 : pg_cancel_backend(PG_FUNCTION_ARGS)
     135              : {
     136           32 :     int         r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
     137              : 
     138           32 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     139            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              : 
     145           32 :     if (r == SIGNAL_BACKEND_NOAUTOVAC)
     146            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              : 
     152           32 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     153            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              : 
     159           32 :     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
     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              :      */
     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              :     {
     188           10 :         if (remainingtime < waittime)
     189            0 :             waittime = remainingtime;
     190              : 
     191           10 :         if (kill(pid, 0) == -1)
     192              :         {
     193            3 :             if (errno == ESRCH)
     194            3 :                 return true;
     195              :             else
     196            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 */
     203            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              : 
     215            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
     235           48 : pg_terminate_backend(PG_FUNCTION_ARGS)
     236              : {
     237              :     int         pid;
     238              :     int         r;
     239              :     int         timeout;        /* milliseconds */
     240              : 
     241           48 :     pid = PG_GETARG_INT32(0);
     242           48 :     timeout = PG_GETARG_INT64(1);
     243              : 
     244           48 :     if (timeout < 0)
     245            0 :         ereport(ERROR,
     246              :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     247              :                  errmsg("\"timeout\" must not be negative")));
     248              : 
     249           48 :     r = pg_signal_backend(pid, SIGTERM);
     250              : 
     251           48 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     252           13 :         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              : 
     258           35 :     if (r == SIGNAL_BACKEND_NOAUTOVAC)
     259            1 :         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              : 
     265           34 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     266            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 */
     273           34 :     if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
     274            3 :         PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
     275              :     else
     276           31 :         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
     286           33 : pg_reload_conf(PG_FUNCTION_ARGS)
     287              : {
     288           33 :     if (kill(PostmasterPid, SIGHUP))
     289              :     {
     290            0 :         ereport(WARNING,
     291              :                 (errmsg("failed to send signal to postmaster: %m")));
     292            0 :         PG_RETURN_BOOL(false);
     293              :     }
     294              : 
     295           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
     306            0 : pg_rotate_logfile(PG_FUNCTION_ARGS)
     307              : {
     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              : }
        

Generated by: LCOV version 2.0-1