LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - signalfuncs.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 32.9 % 85 28
Test Date: 2026-01-26 10:56:24 Functions: 33.3 % 6 2
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 17.3 % 104 18

             Branch data     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                 :             : 
      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
      51                 :           9 : pg_signal_backend(int pid, int sig)
      52                 :             : {
      53                 :           9 :         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         [ +  + ]:           9 :         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   [ -  +  +  - ]:           6 :                 ereport(WARNING,
      74                 :             :                                 (errmsg("PID %d is not a PostgreSQL backend process", pid)));
      75                 :             : 
      76                 :           6 :                 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   [ +  +  +  - ]:           3 :         if (!OidIsValid(proc->roleId) || superuser_arg(proc->roleId))
      89                 :             :         {
      90                 :           3 :                 ProcNumber      procNumber = GetNumberFromPGProc(proc);
      91                 :           3 :                 BackendType backendType = pgstat_get_backend_type_by_proc_number(procNumber);
      92                 :             : 
      93         [ -  + ]:           3 :                 if (backendType == B_AUTOVAC_WORKER)
      94                 :             :                 {
      95         [ #  # ]:           0 :                         if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_AUTOVACUUM_WORKER))
      96                 :           0 :                                 return SIGNAL_BACKEND_NOAUTOVAC;
      97                 :           0 :                 }
      98         [ -  + ]:           3 :                 else if (!superuser())
      99                 :           3 :                         return SIGNAL_BACKEND_NOSUPERUSER;
     100         [ +  - ]:           3 :         }
     101   [ #  #  #  # ]:           0 :         else if (!has_privs_of_role(GetUserId(), proc->roleId) &&
     102                 :           0 :                          !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
     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
     116         [ #  # ]:           0 :         if (kill(-pid, sig))
     117                 :             : #else
     118                 :             :         if (kill(pid, sig))
     119                 :             : #endif
     120                 :             :         {
     121                 :             :                 /* Again, just a warning to allow loops */
     122   [ #  #  #  # ]:           0 :                 ereport(WARNING,
     123                 :             :                                 (errmsg("could not send signal to process %d: %m", pid)));
     124                 :           0 :                 return SIGNAL_BACKEND_ERROR;
     125                 :             :         }
     126                 :           0 :         return SIGNAL_BACKEND_SUCCESS;
     127                 :           9 : }
     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                 :           0 : pg_cancel_backend(PG_FUNCTION_ARGS)
     137                 :             : {
     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                 :             :                                 (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                 :             : 
     147         [ #  # ]:           0 :         if (r == SIGNAL_BACKEND_NOAUTOVAC)
     148   [ #  #  #  # ]:           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                 :             : 
     154         [ #  # ]:           0 :         if (r == SIGNAL_BACKEND_NOPERMISSION)
     155   [ #  #  #  # ]:           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                 :             : 
     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                 :           0 : 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                 :           0 :         int64           waittime = 100;
     176                 :             : 
     177                 :             :         /*
     178                 :             :          * Initially remaining time is the entire timeout specified by the user.
     179                 :             :          */
     180                 :           0 :         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                 :           0 :         do
     189                 :             :         {
     190         [ #  # ]:           0 :                 if (remainingtime < waittime)
     191                 :           0 :                         waittime = remainingtime;
     192                 :             : 
     193         [ #  # ]:           0 :                 if (kill(pid, 0) == -1)
     194                 :             :                 {
     195         [ #  # ]:           0 :                         if (errno == ESRCH)
     196                 :           0 :                                 return true;
     197                 :             :                         else
     198   [ #  #  #  # ]:           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                 :           0 :                 }
     203                 :             : 
     204                 :             :                 /* Process interrupts, if any, before waiting */
     205         [ #  # ]:           0 :                 CHECK_FOR_INTERRUPTS();
     206                 :             : 
     207                 :           0 :                 (void) WaitLatch(MyLatch,
     208                 :             :                                                  WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
     209                 :           0 :                                                  waittime,
     210                 :             :                                                  WAIT_EVENT_BACKEND_TERMINATION);
     211                 :             : 
     212                 :           0 :                 ResetLatch(MyLatch);
     213                 :             : 
     214                 :           0 :                 remainingtime -= waittime;
     215         [ #  # ]:           0 :         } while (remainingtime > 0);
     216                 :             : 
     217   [ #  #  #  # ]:           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                 :           0 : }
     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                 :           9 : pg_terminate_backend(PG_FUNCTION_ARGS)
     238                 :             : {
     239                 :           9 :         int                     pid;
     240                 :           9 :         int                     r;
     241                 :           9 :         int                     timeout;                /* milliseconds */
     242                 :             : 
     243                 :           9 :         pid = PG_GETARG_INT32(0);
     244                 :           9 :         timeout = PG_GETARG_INT64(1);
     245                 :             : 
     246         [ +  - ]:           9 :         if (timeout < 0)
     247   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     248                 :             :                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     249                 :             :                                  errmsg("\"timeout\" must not be negative")));
     250                 :             : 
     251                 :           9 :         r = pg_signal_backend(pid, SIGTERM);
     252                 :             : 
     253         [ +  + ]:           9 :         if (r == SIGNAL_BACKEND_NOSUPERUSER)
     254   [ +  -  +  - ]:           3 :                 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                 :             : 
     260         [ +  - ]:           6 :         if (r == SIGNAL_BACKEND_NOAUTOVAC)
     261   [ #  #  #  # ]:           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                 :             : 
     267         [ +  - ]:           6 :         if (r == SIGNAL_BACKEND_NOPERMISSION)
     268   [ #  #  #  # ]:           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 */
     275   [ -  +  #  # ]:           6 :         if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
     276                 :           0 :                 PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
     277                 :             :         else
     278                 :           6 :                 PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     279                 :           6 : }
     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                 :           0 : pg_reload_conf(PG_FUNCTION_ARGS)
     289                 :             : {
     290         [ #  # ]:           0 :         if (kill(PostmasterPid, SIGHUP))
     291                 :             :         {
     292   [ #  #  #  # ]:           0 :                 ereport(WARNING,
     293                 :             :                                 (errmsg("failed to send signal to postmaster: %m")));
     294                 :           0 :                 PG_RETURN_BOOL(false);
     295                 :             :         }
     296                 :             : 
     297                 :           0 :         PG_RETURN_BOOL(true);
     298                 :           0 : }
     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                 :           0 : pg_rotate_logfile(PG_FUNCTION_ARGS)
     309                 :             : {
     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                 :           0 : }
        

Generated by: LCOV version 2.3.2-1