LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - signalfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 50 72 69.4 %
Date: 2024-11-21 08:14:44 Functions: 5 6 83.3 %
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-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             :  * 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         118 : pg_signal_backend(int pid, int sig)
      52             : {
      53         118 :     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         118 :     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          18 :         ereport(WARNING,
      74             :                 (errmsg("PID %d is not a PostgreSQL backend process", pid)));
      75             : 
      76          18 :         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         100 :     if (!OidIsValid(proc->roleId) || superuser_arg(proc->roleId))
      89          78 :     {
      90         100 :         ProcNumber  procNumber = GetNumberFromPGProc(proc);
      91         100 :         PgBackendStatus *procStatus = pgstat_get_beentry_by_proc_number(procNumber);
      92             : 
      93         100 :         if (procStatus && procStatus->st_backendType == B_AUTOVAC_WORKER)
      94             :         {
      95           4 :             if (!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_AUTOVACUUM_WORKER))
      96           2 :                 return SIGNAL_BACKEND_NOAUTOVAC;
      97             :         }
      98          96 :         else if (!superuser())
      99          20 :             return SIGNAL_BACKEND_NOSUPERUSER;
     100             :     }
     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          78 :     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          78 :     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          62 : pg_cancel_backend(PG_FUNCTION_ARGS)
     137             : {
     138          62 :     int         r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
     139             : 
     140          62 :     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          62 :     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          62 :     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          62 :     PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     162             : }
     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           6 : 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           6 :     int64       waittime = 100;
     176             : 
     177             :     /*
     178             :      * Initially remaining time is the entire timeout specified by the user.
     179             :      */
     180           6 :     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          12 :         if (remainingtime < waittime)
     191           0 :             waittime = remainingtime;
     192             : 
     193          12 :         if (kill(pid, 0) == -1)
     194             :         {
     195           6 :             if (errno == ESRCH)
     196           6 :                 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             :         }
     203             : 
     204             :         /* Process interrupts, if any, before waiting */
     205           6 :         CHECK_FOR_INTERRUPTS();
     206             : 
     207           6 :         (void) WaitLatch(MyLatch,
     208             :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
     209             :                          waittime,
     210             :                          WAIT_EVENT_BACKEND_TERMINATION);
     211             : 
     212           6 :         ResetLatch(MyLatch);
     213             : 
     214           6 :         remainingtime -= waittime;
     215           6 :     } while (remainingtime > 0);
     216             : 
     217           0 :     ereport(WARNING,
     218             :             (errmsg_plural("backend with PID %d did not terminate within %lld millisecond",
     219             :                            "backend with PID %d did not terminate within %lld milliseconds",
     220             :                            timeout,
     221             :                            pid, (long long int) timeout)));
     222             : 
     223           0 :     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          56 : pg_terminate_backend(PG_FUNCTION_ARGS)
     238             : {
     239             :     int         pid;
     240             :     int         r;
     241             :     int         timeout;        /* milliseconds */
     242             : 
     243          56 :     pid = PG_GETARG_INT32(0);
     244          56 :     timeout = PG_GETARG_INT64(1);
     245             : 
     246          56 :     if (timeout < 0)
     247           0 :         ereport(ERROR,
     248             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     249             :                  errmsg("\"timeout\" must not be negative")));
     250             : 
     251          56 :     r = pg_signal_backend(pid, SIGTERM);
     252             : 
     253          56 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     254          20 :         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          36 :     if (r == SIGNAL_BACKEND_NOAUTOVAC)
     261           2 :         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          34 :     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          34 :     if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
     276           6 :         PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
     277             :     else
     278          28 :         PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     279             : }
     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          34 : pg_reload_conf(PG_FUNCTION_ARGS)
     289             : {
     290          34 :     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          34 :     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           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             : }

Generated by: LCOV version 1.14