@ -18,6 +18,7 @@
# include "catalog/pg_authid.h"
# include "catalog/pg_authid.h"
# include "miscadmin.h"
# include "miscadmin.h"
# include "pgstat.h"
# include "postmaster/syslogger.h"
# include "postmaster/syslogger.h"
# include "storage/pmsignal.h"
# include "storage/pmsignal.h"
# include "storage/proc.h"
# include "storage/proc.h"
@ -125,16 +126,91 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
PG_RETURN_BOOL ( r = = SIGNAL_BACKEND_SUCCESS ) ;
PG_RETURN_BOOL ( r = = SIGNAL_BACKEND_SUCCESS ) ;
}
}
/*
* Wait until there is no backend process with the given PID and return true .
* On timeout , a warning is emitted and false is returned .
*/
static bool
pg_wait_until_termination ( int pid , int64 timeout )
{
/*
* Wait in steps of waittime milliseconds until this function exits or
* timeout .
*/
int64 waittime = 100 ;
/*
* Initially remaining time is the entire timeout specified by the user .
*/
int64 remainingtime = timeout ;
/*
* Check existence of the backend . If the backend still exists , then wait
* for waittime milliseconds , again check for the existence . Repeat this
* until timeout or an error occurs or a pending interrupt such as query
* cancel gets processed .
*/
do
{
if ( remainingtime < waittime )
waittime = remainingtime ;
if ( kill ( pid , 0 ) = = - 1 )
{
if ( errno = = ESRCH )
return true ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_INTERNAL_ERROR ) ,
errmsg ( " could not check the existence of the backend with PID %d: %m " ,
pid ) ) ) ;
}
/* Process interrupts, if any, before waiting */
CHECK_FOR_INTERRUPTS ( ) ;
( void ) WaitLatch ( MyLatch ,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH ,
waittime ,
WAIT_EVENT_BACKEND_TERMINATION ) ;
ResetLatch ( MyLatch ) ;
remainingtime - = waittime ;
} while ( remainingtime > 0 ) ;
ereport ( WARNING ,
( errmsg ( " backend with PID %d did not terminate within %lld milliseconds " ,
pid , ( long long int ) timeout ) ) ) ;
return false ;
}
/*
/*
* Signal to terminate a backend process . This is allowed if you are a member
* Signal to terminate a backend process . This is allowed if you are a member
* of the role whose process is being terminated .
* of the role whose process is being terminated . If timeout input argument is
* 0 ( which is default ) , then this function just signals the backend and
* doesn ' t wait . Otherwise it waits until given the timeout milliseconds or no
* process has the given PID and returns true . On timeout , a warning is emitted
* and false is returned .
*
*
* Note that only superusers can signal superuser - owned processes .
* Note that only superusers can signal superuser - owned processes .
*/
*/
Datum
Datum
pg_terminate_backend ( PG_FUNCTION_ARGS )
pg_terminate_backend ( PG_FUNCTION_ARGS )
{
{
int r = pg_signal_backend ( PG_GETARG_INT32 ( 0 ) , SIGTERM ) ;
int pid ;
int r ;
int timeout ;
pid = PG_GETARG_INT32 ( 0 ) ;
timeout = PG_GETARG_INT64 ( 1 ) ;
if ( timeout < 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " \" timeout \" must not be negative " ) ) ) ;
r = pg_signal_backend ( pid , SIGTERM ) ;
if ( r = = SIGNAL_BACKEND_NOSUPERUSER )
if ( r = = SIGNAL_BACKEND_NOSUPERUSER )
ereport ( ERROR ,
ereport ( ERROR ,
@ -146,9 +222,49 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
( errcode ( ERRCODE_INSUFFICIENT_PRIVILEGE ) ,
( errcode ( ERRCODE_INSUFFICIENT_PRIVILEGE ) ,
errmsg ( " must be a member of the role whose process is being terminated or member of pg_signal_backend " ) ) ) ;
errmsg ( " must be a member of the role whose process is being terminated or member of pg_signal_backend " ) ) ) ;
/* Wait only on success and if actually requested */
if ( r = = SIGNAL_BACKEND_SUCCESS & & timeout > 0 )
PG_RETURN_BOOL ( pg_wait_until_termination ( pid , timeout ) ) ;
else
PG_RETURN_BOOL ( r = = SIGNAL_BACKEND_SUCCESS ) ;
PG_RETURN_BOOL ( r = = SIGNAL_BACKEND_SUCCESS ) ;
}
}
/*
* Wait for a backend process with the given PID to exit or until the given
* timeout milliseconds occurs . Returns true if the backend has exited . On
* timeout a warning is emitted and false is returned .
*
* We allow any user to call this function , consistent with any user being
* able to view the pid of the process in pg_stat_activity etc .
*/
Datum
pg_wait_for_backend_termination ( PG_FUNCTION_ARGS )
{
int pid ;
int64 timeout ;
PGPROC * proc = NULL ;
pid = PG_GETARG_INT32 ( 0 ) ;
timeout = PG_GETARG_INT64 ( 1 ) ;
if ( timeout < = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ) ,
errmsg ( " \" timeout \" must not be negative or zero " ) ) ) ;
proc = BackendPidGetProc ( pid ) ;
if ( proc = = NULL )
{
ereport ( WARNING ,
( errmsg ( " PID %d is not a PostgreSQL server process " , pid ) ) ) ;
PG_RETURN_BOOL ( false ) ;
}
PG_RETURN_BOOL ( pg_wait_until_termination ( pid , timeout ) ) ;
}
/*
/*
* Signal to reload the database configuration
* Signal to reload the database configuration
*
*