<indexterm>
<primary>pg_cancel_backend</primary>
</indexterm>
+ <indexterm>
+ <primary>pg_terminate_backend</primary>
+ </indexterm>
<indexterm>
<primary>pg_reload_conf</primary>
</indexterm>
<entry><type>boolean</type></entry>
<entry>Cancel a backend's current query</entry>
</row>
+ <row>
+ <entry>
+ <literal><function>pg_terminate_backend</function>(<parameter>pid</parameter> <type>int</>)</literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>Terminate a backend</entry>
+ </row>
<row>
<entry>
<literal><function>pg_reload_conf</function>()</literal>
</para>
<para>
- <function>pg_cancel_backend</> sends a query cancel
- (<systemitem>SIGINT</>) signal to a backend process identified by
- process ID. The process ID of an active backend can be found from
+ <function>pg_cancel_backend</> and <function>pg_terminate_backend</>
+ send a query cancel (<systemitem>SIGINT</>) signal to a backend process
+ identified by process ID. The
+ process ID of an active backend can be found from
the <structfield>procpid</structfield> column in the
<structname>pg_stat_activity</structname> view, or by listing the
<command>postgres</command> processes on the server with
well.
</para>
</important>
+
+ <para>
+ To terminate a session while allowing other sessions to continue, use
+ <function>pg_terminate_backend()</> (<xref
+ linkend="functions-admin-signal-table">) rather than sending a signal
+ to the child process.
+ </para>
</sect1>
<sect1 id="preventing-server-spoofing">
* waiting for input, however.
*/
if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
- CritSectionCount == 0 && !DoingCommandRead)
+ CritSectionCount == 0 &&
+ (!DoingCommandRead || MyProc->terminate))
{
/* bump holdoff count to make ProcessInterrupts() a no-op */
/* until we are done getting ready for it */
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
+ else if (MyProc->terminate)
+ ereport(ERROR,
+ (errcode(ERRCODE_ADMIN_SHUTDOWN),
+ errmsg("terminating backend due to administrator command")));
else
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
/* We don't have a transaction command open anymore */
xact_started = false;
+ if (MyProc->terminate)
+ die(SIGINT);
+
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
}
#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "storage/pmsignal.h"
+#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
#include "tcop/tcopprot.h"
* Functions to send signals to other backends.
*/
static bool
-pg_signal_backend(int pid, int sig)
+pg_signal_check(int pid)
{
if (!superuser())
ereport(ERROR,
(errmsg("PID %d is not a PostgreSQL server process", pid)));
return false;
}
+ else
+ return true;
+}
+/*
+ * Functions to send signals to other backends.
+ */
+static bool
+pg_signal_backend(int pid, int sig)
+{
/* If we have setsid(), signal the backend's whole process group */
#ifdef HAVE_SETSID
if (kill(-pid, sig))
Datum
pg_cancel_backend(PG_FUNCTION_ARGS)
{
- PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGINT));
+ int pid = PG_GETARG_INT32(0);
+
+ if (pg_signal_check(pid))
+ PG_RETURN_BOOL(pg_signal_backend(pid, SIGINT));
+ else
+ PG_RETURN_BOOL(false);
+}
+
+/*
+ * To cleanly terminate a backend, we set PGPROC(pid)->terminate
+ * then send a cancel signal. We get ProcArrayLock only when
+ * setting PGPROC->terminate so the function might fail in
+ * several places, but that is fine because in those cases the
+ * backend is already gone.
+ */
+Datum
+pg_terminate_backend(PG_FUNCTION_ARGS)
+{
+ int pid = PG_GETARG_INT32(0);
+ volatile PGPROC *term_proc;
+
+ /* Is this the super-user, and can we find the PGPROC entry for the pid? */
+ if (pg_signal_check(pid) && (term_proc = BackendPidGetProc(pid)) != NULL)
+ {
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ /* Recheck now that we have the ProcArray lock. */
+ if (term_proc->pid == pid)
+ {
+ term_proc->terminate = true;
+ LWLockRelease(ProcArrayLock);
+ PG_RETURN_BOOL(pg_signal_backend(pid, SIGINT));
+ }
+ else
+ LWLockRelease(ProcArrayLock);
+ }
+
+ PG_RETURN_BOOL(false);
}
Datum
PG_RETURN_BOOL(true);
}
-#ifdef NOT_USED
-
-/* Disabled in 8.0 due to reliability concerns; FIXME someday */
-Datum
-pg_terminate_backend(PG_FUNCTION_ARGS)
-{
- PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM));
-}
-#endif
-
-
/* Function to find out which databases make use of a tablespace */
typedef struct
DATA(insert OID = 2171 ( pg_cancel_backend PGNSP PGUID 12 1 0 f f t f v 1 16 "23" _null_ _null_ _null_ pg_cancel_backend - _null_ _null_ ));
DESCR("cancel a server process' current query");
+DATA(insert OID = 2096 ( pg_terminate_backend PGNSP PGUID 12 1 0 f f t f v 1 16 "23" _null_ _null_ _null_ pg_terminate_backend - _null_ _null_ ));
+DESCR("terminate a server process");
DATA(insert OID = 2172 ( pg_start_backup PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ pg_start_backup - _null_ _null_ ));
DESCR("prepare for taking an online backup");
DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ pg_stop_backup - _null_ _null_ ));
bool inCommit; /* true if within commit critical section */
+ bool terminate; /* admin requested termination */
+
uint8 vacuumFlags; /* vacuum-related flags, see above */
/* Info about LWLock the process is currently waiting for, if any. */
extern Datum current_database(PG_FUNCTION_ARGS);
extern Datum current_query(PG_FUNCTION_ARGS);
extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
extern Datum pg_rotate_logfile(PG_FUNCTION_ARGS);