LCOV - code coverage report
Current view: top level - src/backend/storage/ipc - signalfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 36 69 52.2 %
Date: 2021-12-04 22:09:09 Functions: 5 7 71.4 %
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-2021, 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/builtins.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             :  * and 3 if the caller needs to be a superuser.
      39             :  *
      40             :  * In the event of a general failure (return code 1), a warning message will
      41             :  * be emitted. For permission errors, doing that is the responsibility of
      42             :  * the caller.
      43             :  */
      44             : #define SIGNAL_BACKEND_SUCCESS 0
      45             : #define SIGNAL_BACKEND_ERROR 1
      46             : #define SIGNAL_BACKEND_NOPERMISSION 2
      47             : #define SIGNAL_BACKEND_NOSUPERUSER 3
      48             : static int
      49          60 : pg_signal_backend(int pid, int sig)
      50             : {
      51          60 :     PGPROC     *proc = BackendPidGetProc(pid);
      52             : 
      53             :     /*
      54             :      * BackendPidGetProc returns NULL if the pid isn't valid; but by the time
      55             :      * we reach kill(), a process for which we get a valid proc here might
      56             :      * have terminated on its own.  There's no way to acquire a lock on an
      57             :      * arbitrary process to prevent that. But since so far all the callers of
      58             :      * this mechanism involve some request for ending the process anyway, that
      59             :      * it might end on its own first is not a problem.
      60             :      */
      61          60 :     if (proc == NULL)
      62             :     {
      63             :         /*
      64             :          * This is just a warning so a loop-through-resultset will not abort
      65             :          * if one backend terminated on its own during the run.
      66             :          */
      67           0 :         ereport(WARNING,
      68             :                 (errmsg("PID %d is not a PostgreSQL server process", pid)));
      69           0 :         return SIGNAL_BACKEND_ERROR;
      70             :     }
      71             : 
      72             :     /* Only allow superusers to signal superuser-owned backends. */
      73          60 :     if (superuser_arg(proc->roleId) && !superuser())
      74           0 :         return SIGNAL_BACKEND_NOSUPERUSER;
      75             : 
      76             :     /* Users can signal backends they have role membership in. */
      77          60 :     if (!has_privs_of_role(GetUserId(), proc->roleId) &&
      78           0 :         !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
      79           0 :         return SIGNAL_BACKEND_NOPERMISSION;
      80             : 
      81             :     /*
      82             :      * Can the process we just validated above end, followed by the pid being
      83             :      * recycled for a new process, before reaching here?  Then we'd be trying
      84             :      * to kill the wrong thing.  Seems near impossible when sequential pid
      85             :      * assignment and wraparound is used.  Perhaps it could happen on a system
      86             :      * where pid re-use is randomized.  That race condition possibility seems
      87             :      * too unlikely to worry about.
      88             :      */
      89             : 
      90             :     /* If we have setsid(), signal the backend's whole process group */
      91             : #ifdef HAVE_SETSID
      92          60 :     if (kill(-pid, sig))
      93             : #else
      94             :     if (kill(pid, sig))
      95             : #endif
      96             :     {
      97             :         /* Again, just a warning to allow loops */
      98           0 :         ereport(WARNING,
      99             :                 (errmsg("could not send signal to process %d: %m", pid)));
     100           0 :         return SIGNAL_BACKEND_ERROR;
     101             :     }
     102          60 :     return SIGNAL_BACKEND_SUCCESS;
     103             : }
     104             : 
     105             : /*
     106             :  * Signal to cancel a backend process.  This is allowed if you are a member of
     107             :  * the role whose process is being canceled.
     108             :  *
     109             :  * Note that only superusers can signal superuser-owned processes.
     110             :  */
     111             : Datum
     112          56 : pg_cancel_backend(PG_FUNCTION_ARGS)
     113             : {
     114          56 :     int         r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
     115             : 
     116          56 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     117           0 :         ereport(ERROR,
     118             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     119             :                  errmsg("must be a superuser to cancel superuser query")));
     120             : 
     121          56 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     122           0 :         ereport(ERROR,
     123             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     124             :                  errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend")));
     125             : 
     126          56 :     PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     127             : }
     128             : 
     129             : /*
     130             :  * Wait until there is no backend process with the given PID and return true.
     131             :  * On timeout, a warning is emitted and false is returned.
     132             :  */
     133             : static bool
     134           4 : pg_wait_until_termination(int pid, int64 timeout)
     135             : {
     136             :     /*
     137             :      * Wait in steps of waittime milliseconds until this function exits or
     138             :      * timeout.
     139             :      */
     140           4 :     int64       waittime = 100;
     141             : 
     142             :     /*
     143             :      * Initially remaining time is the entire timeout specified by the user.
     144             :      */
     145           4 :     int64       remainingtime = timeout;
     146             : 
     147             :     /*
     148             :      * Check existence of the backend. If the backend still exists, then wait
     149             :      * for waittime milliseconds, again check for the existence. Repeat this
     150             :      * until timeout or an error occurs or a pending interrupt such as query
     151             :      * cancel gets processed.
     152             :      */
     153             :     do
     154             :     {
     155           8 :         if (remainingtime < waittime)
     156           0 :             waittime = remainingtime;
     157             : 
     158           8 :         if (kill(pid, 0) == -1)
     159             :         {
     160           4 :             if (errno == ESRCH)
     161           4 :                 return true;
     162             :             else
     163           0 :                 ereport(ERROR,
     164             :                         (errcode(ERRCODE_INTERNAL_ERROR),
     165             :                          errmsg("could not check the existence of the backend with PID %d: %m",
     166             :                                 pid)));
     167             :         }
     168             : 
     169             :         /* Process interrupts, if any, before waiting */
     170           4 :         CHECK_FOR_INTERRUPTS();
     171             : 
     172           4 :         (void) WaitLatch(MyLatch,
     173             :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
     174             :                          waittime,
     175             :                          WAIT_EVENT_BACKEND_TERMINATION);
     176             : 
     177           4 :         ResetLatch(MyLatch);
     178             : 
     179           4 :         remainingtime -= waittime;
     180           4 :     } while (remainingtime > 0);
     181             : 
     182           0 :     ereport(WARNING,
     183             :             (errmsg_plural("backend with PID %d did not terminate within %lld millisecond",
     184             :                            "backend with PID %d did not terminate within %lld milliseconds",
     185             :                            timeout,
     186             :                            pid, (long long int) timeout)));
     187             : 
     188           0 :     return false;
     189             : }
     190             : 
     191             : /*
     192             :  * Send a signal to terminate a backend process. This is allowed if you are a
     193             :  * member of the role whose process is being terminated. If the timeout input
     194             :  * argument is 0, then this function just signals the backend and returns
     195             :  * true.  If timeout is nonzero, then it waits until no process has the given
     196             :  * PID; if the process ends within the timeout, true is returned, and if the
     197             :  * timeout is exceeded, a warning is emitted and false is returned.
     198             :  *
     199             :  * Note that only superusers can signal superuser-owned processes.
     200             :  */
     201             : Datum
     202           4 : pg_terminate_backend(PG_FUNCTION_ARGS)
     203             : {
     204             :     int         pid;
     205             :     int         r;
     206             :     int         timeout;        /* milliseconds */
     207             : 
     208           4 :     pid = PG_GETARG_INT32(0);
     209           4 :     timeout = PG_GETARG_INT64(1);
     210             : 
     211           4 :     if (timeout < 0)
     212           0 :         ereport(ERROR,
     213             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     214             :                  errmsg("\"timeout\" must not be negative")));
     215             : 
     216           4 :     r = pg_signal_backend(pid, SIGTERM);
     217             : 
     218           4 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     219           0 :         ereport(ERROR,
     220             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     221             :                  errmsg("must be a superuser to terminate superuser process")));
     222             : 
     223           4 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     224           0 :         ereport(ERROR,
     225             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     226             :                  errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")));
     227             : 
     228             :     /* Wait only on success and if actually requested */
     229           4 :     if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
     230           4 :         PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
     231             :     else
     232           0 :         PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     233             : }
     234             : 
     235             : /*
     236             :  * Signal to reload the database configuration
     237             :  *
     238             :  * Permission checking for this function is managed through the normal
     239             :  * GRANT system.
     240             :  */
     241             : Datum
     242          32 : pg_reload_conf(PG_FUNCTION_ARGS)
     243             : {
     244          32 :     if (kill(PostmasterPid, SIGHUP))
     245             :     {
     246           0 :         ereport(WARNING,
     247             :                 (errmsg("failed to send signal to postmaster: %m")));
     248           0 :         PG_RETURN_BOOL(false);
     249             :     }
     250             : 
     251          32 :     PG_RETURN_BOOL(true);
     252             : }
     253             : 
     254             : 
     255             : /*
     256             :  * Rotate log file
     257             :  *
     258             :  * This function is kept to support adminpack 1.0.
     259             :  */
     260             : Datum
     261           0 : pg_rotate_logfile(PG_FUNCTION_ARGS)
     262             : {
     263           0 :     if (!superuser())
     264           0 :         ereport(ERROR,
     265             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     266             :                  errmsg("must be superuser to rotate log files with adminpack 1.0"),
     267             :         /* translator: %s is a SQL function name */
     268             :                  errhint("Consider using %s, which is part of core, instead.",
     269             :                          "pg_logfile_rotate()")));
     270             : 
     271           0 :     if (!Logging_collector)
     272             :     {
     273           0 :         ereport(WARNING,
     274             :                 (errmsg("rotation not possible because log collection not active")));
     275           0 :         PG_RETURN_BOOL(false);
     276             :     }
     277             : 
     278           0 :     SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
     279           0 :     PG_RETURN_BOOL(true);
     280             : }
     281             : 
     282             : /*
     283             :  * Rotate log file
     284             :  *
     285             :  * Permission checking for this function is managed through the normal
     286             :  * GRANT system.
     287             :  */
     288             : Datum
     289           0 : pg_rotate_logfile_v2(PG_FUNCTION_ARGS)
     290             : {
     291           0 :     if (!Logging_collector)
     292             :     {
     293           0 :         ereport(WARNING,
     294             :                 (errmsg("rotation not possible because log collection not active")));
     295           0 :         PG_RETURN_BOOL(false);
     296             :     }
     297             : 
     298           0 :     SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
     299           0 :     PG_RETURN_BOOL(true);
     300             : }

Generated by: LCOV version 1.14