From a0659e3e2c79be49feb4aa527d823c71d7bcaf07 Mon Sep 17 00:00:00 2001
From: "Marc G. Fournier" <scrappy@hub.org>
Date: Thu, 9 Jul 1998 03:29:11 +0000
Subject: [PATCH] From: Tom Lane <tgl@sss.pgh.pa.us>

Making PQrequestCancel safe to call in a signal handler turned out to be
much easier than I feared.  So here are the diffs.

Some notes:
  * I modified the postmaster's packet "iodone" callback interface to allow
    the callback routine to return a continue-or-drop-connection return
    code; this was necessary to allow the connection to be closed after
    receiving a Cancel, rather than proceeding to launch a new backend...
    Being a neatnik, I also made the iodone proc have a typechecked
    parameter list.
  * I deleted all code I could find that had to do with OOB.
  * I made some edits to ensure that all signals mentioned in the code
    are referred to symbolically not by numbers ("SIGUSR2" not "2").
    I think Bruce may have already done at least some of the same edits;
    I hope that merging these patches is not too painful.
---
 doc/src/sgml/protocol.sgml          | 259 +++++++++++++++++++++++-----
 src/backend/commands/async.c        |  12 +-
 src/backend/libpq/auth.c            |  72 ++++----
 src/backend/libpq/pqcomm.c          |  80 +--------
 src/backend/libpq/pqpacket.c        |  18 +-
 src/backend/postmaster/postmaster.c | 124 ++++++++++---
 src/backend/tcop/postgres.c         |  40 +++--
 src/backend/utils/init/globals.c    |   4 +-
 src/bin/psql/psql.c                 |  32 +++-
 src/include/commands/dbcommands.h   |   5 +-
 src/include/libpq/libpq-be.h        |  18 +-
 src/include/libpq/libpq.h           |  12 +-
 src/include/libpq/pqcomm.h          |  21 ++-
 src/include/miscadmin.h             |   4 +-
 src/include/port/sco.h              |   1 -
 src/interfaces/libpq/fe-connect.c   | 107 +++++++++++-
 src/interfaces/libpq/fe-exec.c      |  50 ++----
 src/interfaces/libpq/libpq-fe.h     |   8 +-
 src/man/listen.l                    |   6 +-
 src/man/notify.l                    |  11 +-
 20 files changed, 597 insertions(+), 287 deletions(-)

diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index f40914583c..151ca3cb42 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -4,7 +4,7 @@
 <FirstName>Phil</FirstName>
 <Surname>Thompson</Surname>
 </Author>
-<Date>1998-05-04</Date>
+<Date>1998-07-07</Date>
 </DocInfo>
 <Title>Frontend/Backend Protocol</Title>
 
@@ -54,8 +54,10 @@ invalid database name).
 
 <Para>
 Subsequent communications are query and result packets exchanged between the
-frontend and the backend.  The postmaster takes no further part in the
-communication.
+frontend and the backend.  The postmaster takes no further part in ordinary
+query/result communication.  (However, the postmaster is involved when the
+frontend wishes to cancel a query currently being executed by its backend.
+Further details about that appear below.)
 
 <Para>
 When the frontend wishes to disconnect it sends an appropriate packet and
@@ -182,6 +184,20 @@ The possible messages from the backend during this phase are:
 <Para>
 <VariableList>
 <VarListEntry>
+<Term>
+	BackendKeyData
+</Term>
+<ListItem>
+<Para>
+		This message is issued after successful backend startup.
+		It provides secret-key data that the frontend must save
+		if it wants to be able to issue cancel requests later.
+		The frontend should not respond to this message, but should
+		continue listening for a ReadyForQuery message.
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
 <Term>
 	ReadyForQuery
 </Term>
@@ -218,6 +234,14 @@ The possible messages from the backend during this phase are:
 </VariableList>
 </Para>
 
+<Para>
+The ReadyForQuery message is the same one that the backend will issue after
+each query cycle.  Depending on the coding needs of the frontend, it is
+reasonable to consider ReadyForQuery as starting a query cycle (and then
+BackendKeyData indicates successful conclusion of the startup phase),
+or to consider ReadyForQuery as ending the startup phase and each subsequent
+query cycle.
+
 
 <Sect2>
 <Title>Query</Title>
@@ -453,7 +477,7 @@ NotificationResponse messages at any time; see below.
 <Para>
 If a frontend issues a listen(l) command, then the backend will send a
 NotificationResponse message (not to be confused with NoticeResponse!)
-whenever a notify(l) command is executed for the same relation name.
+whenever a notify(l) command is executed for the same notification name.
 
 <Para>
 Notification responses are permitted at any point in the protocol (after
@@ -470,8 +494,8 @@ NotificationResponse messages even when it is not engaged in a query.
 </Term>
 <ListItem>
 <Para>
-		A notify(l) command has been executed for a relation for
-		which a previous listen(l) command was executed.  Notifications
+		A notify(l) command has been executed for a name for which
+		a previous listen(l) command was executed.  Notifications
 		may be sent at any time.
 </Para>
 </ListItem>
@@ -479,29 +503,77 @@ NotificationResponse messages even when it is not engaged in a query.
 </VariableList>
 </Para>
 
+<Para>
+It may be worth pointing out that the names used in listen and notify
+commands need not have anything to do with names of relations (tables)
+in the SQL database.  Notification names are simply arbitrarily chosen
+condition names.
+
 
 <Sect2>
 <Title>Cancelling Requests in Progress</Title>
 
 <Para>
 During the processing of a query, the frontend may request cancellation of the
-query by sending a single byte of OOB (out-of-band) data.  The contents of the
-data byte should be zero (although the backend does not currently check this).
-If the cancellation is effective, it results in the current command being
-terminated with an error message.  Note that the backend makes no specific
-reply to the cancel request itself.  If the cancel request is ineffective
-(say, because it arrived after processing was complete) then it will have
-no visible effect at all.  Thus, the frontend must continue with its normal
-processing of query cycle responses after issuing a cancel request.
+query by sending an appropriate request to the postmaster.  The cancel request
+is not sent directly to the backend for reasons of implementation efficiency:
+we don't want to have the backend constantly checking for new input from
+the frontend during query processing.  Cancel requests should be relatively
+infrequent, so we make them slightly cumbersome in order to avoid a penalty
+in the normal case.
+
+<Para>
+To issue a cancel request, the frontend opens a new connection to the
+postmaster and sends a CancelRequest message, rather than the StartupPacket
+message that would ordinarily be sent across a new connection.  The postmaster
+will process this request and then close the connection.  For security
+reasons, no direct reply is made to the cancel request message.
+
+<Para>
+A CancelRequest message will be ignored unless it contains the same key data
+(PID and secret key) passed to the frontend during connection startup.  If the
+request matches the PID and secret key for a currently executing backend, the
+postmaster signals the backend to abort processing of the current query.
+
+<Para>
+The cancellation signal may or may not have any effect --- for example, if it
+arrives after the backend has finished processing the query, then it will have
+no effect.  If the cancellation is effective, it results in the current
+command being terminated early with an error message.
+
+<Para>
+The upshot of all this is that for reasons of both security and efficiency,
+the frontend has no direct way to tell whether a cancel request has succeeded.
+It must continue to wait for the backend to respond to the query.  Issuing a
+cancel simply improves the odds that the current query will finish soon,
+and improves the odds that it will fail with an error message instead of
+succeeding.
+
+<Para>
+Since the cancel request is sent to the postmaster and not across the
+regular frontend/backend communication link, it is possible for the cancel
+request to be issued by any process, not just the frontend whose query is
+to be canceled.  This may have some benefits of flexibility in building
+multiple-process applications.  It also introduces a security risk, in that
+unauthorized persons might try to cancel queries.  The security risk is
+addressed by requiring a dynamically generated secret key to be supplied
+in cancel requests.
 
 
 <Sect2>
 <Title>Termination</Title>
 
 <Para>
-The frontend sends a Terminate message and immediately closes the connection.
-On receipt of the message, the backend immediately closes the connection and
-terminates.
+The normal, graceful termination procedure is that the frontend sends a
+Terminate message and immediately closes the connection.  On receipt of the
+message, the backend immediately closes the connection and terminates.
+
+<Para>
+An ungraceful termination may occur due to software failure (i.e., core dump)
+at either end.  If either frontend or backend sees an unexpected closure of
+the connection, it should clean up and terminate.  The frontend has the option
+of launching a new backend by recontacting the postmaster, if it doesn't want
+to terminate itself.
 
 
 <Sect1>
@@ -824,6 +896,52 @@ AuthenticationEncryptedPassword (B)
 </VarListEntry>
 </VariableList>
 
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+BackendKeyData (B)
+</Term>
+<ListItem>
+<Para>
+
+<VariableList>
+<VarListEntry>
+<Term>
+	Byte1('K')
+</Term>
+<ListItem>
+<Para>
+		Identifies the message as cancellation key data.
+		The frontend must save these values if it wishes to be
+		able to issue CancelRequest messages later.
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+	Int32
+</Term>
+<ListItem>
+<Para>
+		The process ID of this backend.
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+	Int32
+</Term>
+<ListItem>
+<Para>
+		The secret key of this backend.
+</Para>
+</ListItem>
+</VarListEntry>
+</VariableList>
+
+
 </Para>
 </ListItem>
 </VarListEntry>
@@ -892,6 +1010,63 @@ BinaryRow (B)
 </VarListEntry>
 </VariableList>
 
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+CancelRequest (F)
+</Term>
+<ListItem>
+<Para>
+
+<VariableList>
+<VarListEntry>
+<Term>
+	Int32(16)
+</Term>
+<ListItem>
+<Para>
+		The size of the packet in bytes.
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+	Int32(80877102)
+</Term>
+<ListItem>
+<Para>
+		The cancel request code.  The value is chosen to contain
+		"1234" in the most significant 16 bits, and "5678" in the
+		least 16 significant bits.  (To avoid confusion, this code
+		must not be the same as any protocol version number.)
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+	Int32
+</Term>
+<ListItem>
+<Para>
+		The process ID of the target backend.
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+	Int32
+</Term>
+<ListItem>
+<Para>
+		The secret key for the target backend.
+</Para>
+</ListItem>
+</VarListEntry>
+</VariableList>
+
+
 </Para>
 </ListItem>
 </VarListEntry>
@@ -1092,31 +1267,6 @@ EncryptedPasswordPacket (F)
 </VariableList>
 
 
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-ReadyForQuery (B)
-</Term>
-<ListItem>
-<Para>
-
-<VariableList>
-<VarListEntry>
-<Term>
-	Byte1('Z')
-</Term>
-<ListItem>
-<Para>
-		Identifies the message type.  ReadyForQuery is sent
-		whenever the backend is ready for a new query cycle.
-</Para>
-</ListItem>
-</VarListEntry>
-</VariableList>
-
-
 </Para>
 </ListItem>
 </VarListEntry>
@@ -1449,6 +1599,31 @@ Query (F)
 </VariableList>
 
 
+</Para>
+</ListItem>
+</VarListEntry>
+<VarListEntry>
+<Term>
+ReadyForQuery (B)
+</Term>
+<ListItem>
+<Para>
+
+<VariableList>
+<VarListEntry>
+<Term>
+	Byte1('Z')
+</Term>
+<ListItem>
+<Para>
+		Identifies the message type.  ReadyForQuery is sent
+		whenever the backend is ready for a new query cycle.
+</Para>
+</ListItem>
+</VarListEntry>
+</VariableList>
+
+
 </Para>
 </ListItem>
 </VarListEntry>
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index b4b354cfc5..b80cbb8f34 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.34 1998/06/27 04:53:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.35 1998/07/09 03:28:44 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,11 +21,11 @@
  *	  2.a  If the process is the same as the backend process that issued
  *		   notification (we are notifying something that we are listening),
  *		   signal the corresponding frontend over the comm channel.
- *	  2.b  For all other listening processes, we send kill(2) to wake up
+ *	  2.b  For all other listening processes, we send kill(SIGUSR2) to wake up
  *		   the listening backend.
- * 3. Upon receiving a kill(2) signal from another backend process notifying
- *	  that one of the relation that we are listening is being notified,
- *	  we can be in either of two following states:
+ * 3. Upon receiving a kill(SIGUSR2) signal from another backend process
+ *	  notifying that one of the relation that we are listening is being
+ *	  notified, we can be in either of two following states:
  *	  3.a  We are sleeping, wake up and signal our frontend.
  *	  3.b  We are in middle of another transaction, wait until the end of
  *		   of the current transaction and signal our frontend.
@@ -46,7 +46,7 @@
  *	  (which takes place after commit) to all listeners on this relation.
  *
  * 3. Async. notification results in all backends listening on relation
- *	  to be woken up, by a process signal kill(2), with name of relation
+ *	  to be woken up, by a process signal kill(SIGUSR2), with name of relation
  *	  passed in shared memory.
  *
  * 4. Each backend notifies its respective frontend over the comm
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 4aee9b9197..c1cc08f4c7 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.28 1998/06/13 04:27:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.29 1998/07/09 03:28:45 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,13 +40,13 @@
 #include <libpq/crypt.h>
 
 
-static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ());
-static void handle_done_auth(Port *port);
-static void handle_krb4_auth(Port *port);
-static void handle_krb5_auth(Port *port);
-static void handle_password_auth(Port *port);
-static void readPasswordPacket(char *arg, PacketLen len, char *pkt);
-static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt);
+static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler);
+static int	handle_done_auth(void *arg, PacketLen len, void *pkt);
+static int	handle_krb4_auth(void *arg, PacketLen len, void *pkt);
+static int	handle_krb5_auth(void *arg, PacketLen len, void *pkt);
+static int	handle_password_auth(void *arg, PacketLen len, void *pkt);
+static int	readPasswordPacket(void *arg, PacketLen len, void *pkt);
+static int	pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
 static int	checkPassword(Port *port, char *user, char *password);
 static int	old_be_recvauth(Port *port);
 static int	map_old_to_new(Port *port, UserAuth old, int status);
@@ -327,8 +327,8 @@ pg_krb5_recvauth(Port *port)
  * Handle a v0 password packet.
  */
 
-static void
-pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
+static int
+pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
 {
 	Port	   *port;
 	PasswordPacketV0 *pp;
@@ -393,6 +393,8 @@ pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
 		if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
 			auth_failed(port);
 	}
+
+	return (STATUS_OK);			/* don't close the connection yet */
 }
 
 
@@ -433,7 +435,7 @@ be_recvauth(Port *port)
 	else
 	{
 		AuthRequest areq;
-		void		(*auth_handler) ();
+		PacketDoneProc auth_handler;
 
 		/* Keep the compiler quiet. */
 
@@ -499,7 +501,7 @@ be_recvauth(Port *port)
  */
 
 static void
-sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
+sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
 {
 	char	   *dp,
 			   *sp;
@@ -527,7 +529,7 @@ sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
 		i += 2;
 	}
 
-	PacketSendSetup(&port->pktInfo, i, handler, (char *) port);
+	PacketSendSetup(&port->pktInfo, i, handler, (void *) port);
 }
 
 
@@ -535,8 +537,8 @@ sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
  * Called when we have told the front end that it is authorised.
  */
 
-static void
-handle_done_auth(Port *port)
+static int
+handle_done_auth(void *arg, PacketLen len, void *pkt)
 {
 
 	/*
@@ -544,7 +546,7 @@ handle_done_auth(Port *port)
 	 * start.
 	 */
 
-	return;
+	return STATUS_OK;
 }
 
 
@@ -553,13 +555,17 @@ handle_done_auth(Port *port)
  * authentication.
  */
 
-static void
-handle_krb4_auth(Port *port)
+static int
+handle_krb4_auth(void *arg, PacketLen len, void *pkt)
 {
+	Port	   *port = (Port *) arg;
+
 	if (pg_krb4_recvauth(port) != STATUS_OK)
 		auth_failed(port);
 	else
 		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+	return STATUS_OK;
 }
 
 
@@ -568,13 +574,17 @@ handle_krb4_auth(Port *port)
  * authentication.
  */
 
-static void
-handle_krb5_auth(Port *port)
+static int
+handle_krb5_auth(void *arg, PacketLen len, void *pkt)
 {
+	Port	   *port = (Port *) arg;
+
 	if (pg_krb5_recvauth(port) != STATUS_OK)
 		auth_failed(port);
 	else
 		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+	return STATUS_OK;
 }
 
 
@@ -583,12 +593,16 @@ handle_krb5_auth(Port *port)
  * authentication.
  */
 
-static void
-handle_password_auth(Port *port)
+static int
+handle_password_auth(void *arg, PacketLen len, void *pkt)
 {
+	Port	   *port = (Port *) arg;
+
 	/* Set up the read of the password packet. */
 
-	PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (char *) port);
+	PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (void *) port);
+
+	return STATUS_OK;
 }
 
 
@@ -596,13 +610,11 @@ handle_password_auth(Port *port)
  * Called when we have received the password packet.
  */
 
-static void
-readPasswordPacket(char *arg, PacketLen len, char *pkt)
+static int
+readPasswordPacket(void *arg, PacketLen len, void *pkt)
 {
 	char		password[sizeof(PasswordPacket) + 1];
-	Port	   *port;
-
-	port = (Port *) arg;
+	Port	   *port = (Port *) arg;
 
 	/* Silently truncate a password that is too big. */
 
@@ -615,6 +627,8 @@ readPasswordPacket(char *arg, PacketLen len, char *pkt)
 		auth_failed(port);
 	else
 		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+	return (STATUS_OK);			/* don't close the connection yet */
 }
 
 
@@ -662,7 +676,7 @@ old_be_recvauth(Port *port)
 
 		case STARTUP_PASSWORD_MSG:
 			PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
-							   (char *) port);
+							   (void *) port);
 
 			return STATUS_OK;
 
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index a70bbc22e9..4c5b85b248 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.47 1998/06/27 04:53:30 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.48 1998/07/09 03:28:46 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,6 @@
  *		pq_getinserv	- initialize address from host and service name
  *		pq_connect		- create remote input / output connection
  *		pq_accept		- accept remote input / output connection
- *		pq_async_notify - receive notification from backend.
  *
  * NOTES
  *		These functions are used by both frontend applications and
@@ -79,7 +78,6 @@
 FILE	   *Pfout,
 		   *Pfin;
 FILE	   *Pfdebug;			/* debugging libpq */
-int			PQAsyncNotifyWaiting;		/* for async. notification */
 
 /* --------------------------------
  *		pq_init - open portal file descriptors
@@ -160,9 +158,7 @@ pq_close()
 		fclose(Pfout);
 		Pfout = NULL;
 	}
-	PQAsyncNotifyWaiting = 0;
 	PQnotifies_init();
-	pq_unregoob();
 }
 
 /* --------------------------------
@@ -418,29 +414,6 @@ pq_putint(int i, int b)
 	}
 }
 
-/* ---
- *	   pq_sendoob - send a string over the out-of-band channel
- *	   pq_recvoob - receive a string over the oob channel
- *	NB: Fortunately, the out-of-band channel doesn't conflict with
- *		buffered I/O because it is separate from regular com. channel.
- * ---
- */
-int
-pq_sendoob(char *msg, int len)
-{
-	int			fd = fileno(Pfout);
-
-	return send(fd, msg, len, MSG_OOB);
-}
-
-int
-pq_recvoob(char *msgPtr, int len)
-{
-	int			fd = fileno(Pfout);
-
-	return recv(fd, msgPtr, len, MSG_OOB);
-}
-
 /* --------------------------------
  *		pq_getinaddr - initialize address from host and port number
  * --------------------------------
@@ -507,55 +480,6 @@ pq_getinserv(struct sockaddr_in * sin, char *host, char *serv)
 	return (pq_getinaddr(sin, host, ntohs(ss->s_port)));
 }
 
-/*
- * register an out-of-band listener proc--at most one allowed.
- * This is used for receiving async. notification from the backend.
- */
-void
-pq_regoob(void (*fptr) ())
-{
-	int			fd = fileno(Pfout);
-
-#if defined(hpux)
-	ioctl(fd, FIOSSAIOOWN, MyProcPid);
-#elif defined(sco)
-	ioctl(fd, SIOCSPGRP, MyProcPid);
-#else
-	fcntl(fd, F_SETOWN, MyProcPid);
-#endif							/* hpux */
-	pqsignal(SIGURG, fptr);
-}
-
-void
-pq_unregoob()
-{
-	pqsignal(SIGURG, SIG_DFL);
-}
-
-
-void
-pq_async_notify()
-{
-	char		msg[20];
-
-	/* int len = sizeof(msg); */
-	int			len = 20;
-
-	if (pq_recvoob(msg, len) >= 0)
-	{
-		/* debugging */
-		printf("received notification: %s\n", msg);
-		PQAsyncNotifyWaiting = 1;
-		/* PQappendNotify(msg+1); */
-	}
-	else
-	{
-		extern int	errno;
-
-		printf("SIGURG but no data: len = %d, err=%d\n", len, errno);
-	}
-}
-
 /*
  * Streams -- wrapper around Unix socket system calls
  *
@@ -620,7 +544,7 @@ StreamServerPort(char *hostName, short portName, int *fdP)
 		pqdebug("%s", PQerrormsg);
 		return (STATUS_ERROR);
 	}
-	bzero(&saddr, sizeof(saddr));
+	MemSet((char *) &saddr, 0, sizeof(saddr));
 	saddr.sa.sa_family = family;
 	if (family == AF_UNIX)
 	{
diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c
index 97caae952a..631af78ce2 100644
--- a/src/backend/libpq/pqpacket.c
+++ b/src/backend/libpq/pqpacket.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.15 1998/02/26 04:31:56 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.16 1998/07/09 03:28:46 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,7 @@
  * Set up a packet read for the postmaster event loop.
  */
 
-void		PacketReceiveSetup(Packet *pkt, void (*iodone) (), char *arg)
+void		PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg)
 {
 	pkt->nrtodo = sizeof(pkt->len);
 	pkt->ptr = (char *) &pkt->len;
@@ -94,8 +94,8 @@ PacketReceiveFragment(Packet *pkt, int sock)
 			if (pkt->iodone == NULL)
 				return STATUS_ERROR;
 
-			(*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len),
-							(char *) &pkt->pkt);
+			return (*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len),
+								   (void *) &pkt->pkt);
 		}
 
 		return STATUS_OK;
@@ -107,7 +107,7 @@ PacketReceiveFragment(Packet *pkt, int sock)
 	if (errno == EINTR)
 		return STATUS_OK;
 
-	fprintf(stderr, "read() system call failed\n");
+	perror("PacketReceiveFragment: read() failed");
 
 	return STATUS_ERROR;
 }
@@ -117,8 +117,9 @@ PacketReceiveFragment(Packet *pkt, int sock)
  * Set up a packet write for the postmaster event loop.
  */
 
-void		PacketSendSetup(Packet *pkt, int nbytes, void (*iodone) (), char *arg)
+void		PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg)
 {
+	pkt->len = (PacketLen) nbytes;
 	pkt->nrtodo = nbytes;
 	pkt->ptr = (char *) &pkt->pkt;
 	pkt->iodone = iodone;
@@ -153,7 +154,8 @@ PacketSendFragment(Packet *pkt, int sock)
 			if (pkt->iodone == NULL)
 				return STATUS_ERROR;
 
-			(*pkt->iodone) (pkt->arg);
+			return (*pkt->iodone) (pkt->arg, pkt->len,
+								   (void *) &pkt->pkt);
 		}
 
 		return STATUS_OK;
@@ -165,7 +167,7 @@ PacketSendFragment(Packet *pkt, int sock)
 	if (errno == EINTR)
 		return STATUS_OK;
 
-	fprintf(stderr, "write() system call failed\n");
+	perror("PacketSendFragment: write() failed");
 
 	return STATUS_ERROR;
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 60d998814d..7f33dfedba 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.92 1998/06/27 14:06:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.93 1998/07/09 03:28:47 scrappy Exp $
  *
  * NOTES
  *
@@ -206,7 +206,6 @@ static	int			orgsigmask = sigblock(0);
  */
 
 static unsigned int random_seed = 0;
-long MyCancelKey = 0;
 
 extern char *optarg;
 extern int	optind,
@@ -228,7 +227,8 @@ static void ExitPostmaster(int status);
 static void usage(const char *);
 static int ServerLoop(void);
 static int BackendStartup(Port *port);
-static void readStartupPacket(char *arg, PacketLen len, char *pkt);
+static int readStartupPacket(void *arg, PacketLen len, void *pkt);
+static int processCancelRequest(Port *port, PacketLen len, void *pkt);
 static int initMasks(fd_set *rmask, fd_set *wmask);
 static long PostmasterRandom(void);
 static void RandomSalt(char *salt);
@@ -518,6 +518,10 @@ PostmasterMain(int argc, char *argv[])
 	if (silentflag)
 		pmdaemonize();
 
+	/*
+	 * Set up signal handlers for the postmaster process.
+	 */
+
 	pqsignal(SIGINT, pmdie);
 	pqsignal(SIGCHLD, reaper);
 	pqsignal(SIGTTIN, SIG_IGN);
@@ -657,14 +661,14 @@ ServerLoop(void)
 			(port = ConnCreate(ServerSock_UNIX)) != NULL)
 			PacketReceiveSetup(&port->pktInfo,
 							   readStartupPacket,
-							   (char *) port);
+							   (void *) port);
 
 		if (ServerSock_INET != INVALID_SOCK &&
 			FD_ISSET(ServerSock_INET, &rmask) &&
 			(port = ConnCreate(ServerSock_INET)) != NULL)
 			PacketReceiveSetup(&port->pktInfo,
 							   readStartupPacket,
-							   (char *) port);
+							   (void *) port);
 
 		/* Build up new masks for select(). */
 
@@ -790,8 +794,8 @@ initMasks(fd_set *rmask, fd_set *wmask)
  * Called when the startup packet has been read.
  */
 
-static void
-readStartupPacket(char *arg, PacketLen len, char *pkt)
+static int
+readStartupPacket(void *arg, PacketLen len, void *pkt)
 {
 	Port	   *port;
 	StartupPacket *si;
@@ -799,6 +803,28 @@ readStartupPacket(char *arg, PacketLen len, char *pkt)
 	port = (Port *) arg;
 	si = (StartupPacket *) pkt;
 
+	/* The first field is either a protocol version number or
+	 * a special request code.
+	 */
+
+	port->proto = ntohl(si->protoVersion);
+
+	if (port->proto == CANCEL_REQUEST_CODE)
+		return processCancelRequest(port, len, pkt);
+
+	/* Could add additional special packet types here */
+
+	/* Check we can handle the protocol the frontend is using. */
+
+	if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
+		PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
+		(PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
+		 PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
+	{
+		PacketSendError(&port->pktInfo, "Unsupported frontend protocol.");
+		return STATUS_OK;		/* don't close the connection yet */
+	}
+
 	/*
 	 * Get the parameters from the startup packet as C strings.  The
 	 * packet destination was cleared first so a short packet has zeros
@@ -815,31 +841,74 @@ readStartupPacket(char *arg, PacketLen len, char *pkt)
 	if (port->database[0] == '\0')
 		StrNCpy(port->database, si->user, sizeof(port->database) - 1);
 
-	/* Check we can handle the protocol the frontend is using. */
-
-	port->proto = ntohl(si->protoVersion);
-
-	if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
-		PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
-		(PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
-		 PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
-	{
-		PacketSendError(&port->pktInfo, "Unsupported frontend protocol.");
-		return;
-	}
-
 	/* Check a user name was given. */
 
 	if (port->user[0] == '\0')
 	{
 		PacketSendError(&port->pktInfo,
 					"No Postgres username specified in startup packet.");
-		return;
+		return STATUS_OK;		/* don't close the connection yet */
 	}
 
 	/* Start the authentication itself. */
 
 	be_recvauth(port);
+
+	return STATUS_OK;			/* don't close the connection yet */
+}
+
+
+/*
+ * The client has sent a cancel request packet, not a normal
+ * start-a-new-backend packet.  Perform the necessary processing.
+ * Note that in any case, we return STATUS_ERROR to close the
+ * connection immediately.  Nothing is sent back to the client.
+ */
+
+static int
+processCancelRequest(Port *port, PacketLen len, void *pkt)
+{
+	CancelRequestPacket	*canc = (CancelRequestPacket *) pkt;
+	int			backendPID;
+	long		cancelAuthCode;
+	Dlelem	   *curr;
+	Backend    *bp;
+
+	backendPID = (int) ntohl(canc->backendPID);
+	cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
+
+	/* See if we have a matching backend */
+
+	for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
+	{
+		bp = (Backend *) DLE_VAL(curr);
+		if (bp->pid == backendPID)
+		{
+			if (bp->cancel_key == cancelAuthCode)
+			{
+				/* Found a match; signal that backend to cancel current op */
+				if (DebugLvl)
+					fprintf(stderr, "%s: processCancelRequest: sending SIGINT to process %d\n",
+							progname, bp->pid);
+				kill(bp->pid, SIGINT);
+			}
+			else
+			{
+				/* Right PID, wrong key: no way, Jose */
+				if (DebugLvl)
+					fprintf(stderr, "%s: processCancelRequest: bad key in cancel request for process %d\n",
+							progname, bp->pid);
+			}
+			return STATUS_ERROR;
+		}
+	}
+
+	/* No matching backend */
+	if (DebugLvl)
+		fprintf(stderr, "%s: processCancelRequest: bad PID in cancel request for process %d\n",
+				progname, backendPID);
+
+	return STATUS_ERROR;
 }
 
 
@@ -1221,6 +1290,8 @@ DoBackend(Port *port)
 	char		dbbuf[ARGV_SIZE + 1];
 	int			ac = 0;
 	int			i;
+	struct timeval	now;
+	struct timezone	tz;
 
 	/*
 	 *	Let's clean up ourselves as the postmaster child
@@ -1254,7 +1325,16 @@ DoBackend(Port *port)
 	if (NetServer)
 		StreamClose(ServerSock_INET);
 	StreamClose(ServerSock_UNIX);
-	
+
+	/*
+	 * Don't want backend to be able to see the postmaster random number
+	 * generator state.  We have to clobber the static random_seed *and*
+	 * start a new random sequence in the random() library function.
+	 */
+	random_seed = 0;
+	gettimeofday(&now, &tz);
+	srandom(now.tv_usec);
+
 	/* Now, on to standard postgres stuff */
 	
 	MyProcPid = getpid();
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 97ac571d2c..0a7408a7b9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.78 1998/06/27 04:53:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.79 1998/07/09 03:28:48 scrappy Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -724,7 +724,7 @@ pg_exec_query_dest(char *query_string,	/* string to execute */
 /* --------------------------------
  *		signal handler routines used in PostgresMain()
  *
- *		handle_warn() is used to catch kill(getpid(),1) which
+ *		handle_warn() is used to catch kill(getpid(), SIGHUP) which
  *		occurs when elog(ERROR) is called.
  *
  *		quickdie() occurs when signalled by the postmaster.
@@ -777,7 +777,7 @@ FloatExceptionHandler(SIGNAL_ARGS)
 }
 
 
-/* signal handler for query cancel */
+/* signal handler for query cancel signal from postmaster */
 static void
 QueryCancelHandler(SIGNAL_ARGS)
 {
@@ -787,12 +787,9 @@ QueryCancelHandler(SIGNAL_ARGS)
 void
 CancelQuery(void)
 {
-	char dummy;
-
-	/* throw it away */
-	while (pq_recvoob(&dummy, 1) > 0)
-		;
-	/* QueryCancel reset in longjump after elog() call */
+	/* QueryCancel flag will be reset in main loop, which we reach by
+	 * longjmp from elog().
+	 */
 	elog(ERROR, "Query was cancelled.");
 }
 
@@ -1261,7 +1258,6 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 		}
 		pq_init(Portfd);
 		whereToSendOutput = Remote;
-		pq_regoob(QueryCancelHandler); /* we do it here so the backend it connected */	
 	}
 	else
 		whereToSendOutput = Debug;
@@ -1287,6 +1283,24 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	}
 #endif
 
+	/* ----------------
+	 *  Set up handler for cancel-request signal, and
+	 *	send this backend's cancellation info to the frontend.
+	 *	This should not be done until we are sure startup is successful.
+	 * ----------------
+	 */
+
+	pqsignal(SIGINT, QueryCancelHandler);
+
+	if (whereToSendOutput == Remote &&
+		PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
+	{
+		pq_putnchar("K", 1);
+		pq_putint((int32) MyProcPid, sizeof(int32));
+		pq_putint((int32) MyCancelKey, sizeof(int32));
+		/* Need not flush since ReadyForQuery will do it. */
+	}
+
 	/* ----------------
 	 *	if an exception is encountered, processing resumes here
 	 *	so we abort the current transaction and start a new one.
@@ -1294,7 +1308,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	 *	so that the slaves signal the master to abort the transaction
 	 *	rather than calling AbortCurrentTransaction() themselves.
 	 *
-	 *	Note:  elog(ERROR) causes a kill(getpid(),1) to occur sending
+	 *	Note:  elog(ERROR) causes a kill(getpid(), SIGHUP) to occur sending
 	 *		   us back here.
 	 * ----------------
 	 */
@@ -1325,7 +1339,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface");
-		puts("$Revision: 1.78 $ $Date: 1998/06/27 04:53:43 $");
+		puts("$Revision: 1.79 $ $Date: 1998/07/09 03:28:48 $");
 	}
 
 	/* ----------------
@@ -1431,7 +1445,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 				break;
 
 			default:
-				elog(ERROR, "unknown frontend message was recieved");
+				elog(ERROR, "unknown frontend message was received");
 		}
 
 		/* ----------------
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 2deec81de0..585471d37f 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.23 1998/05/29 17:00:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.24 1998/07/09 03:28:51 scrappy Exp $
  *
  * NOTES
  *	  Globals used all over the place should be declared here and not
@@ -44,6 +44,8 @@ bool		QueryCancel = false;
 
 int			MyProcPid;
 
+long		MyCancelKey;
+
 char	   *DataDir;
 
  /*
diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c
index 1ed9e4561f..65692bd9a1 100644
--- a/src/bin/psql/psql.c
+++ b/src/bin/psql/psql.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.146 1998/06/16 07:29:38 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.147 1998/07/09 03:28:53 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -291,15 +291,27 @@ PSQLexec(PsqlSettings *pset, char *query)
  * If interactive, we enable a SIGINT signal catcher that sends
  * a cancel request to the backend.
  * Note that sending the cancel directly from the signal handler
- * is safe only because the cancel is sent as an OOB message.
- * If it were inline data, then we'd risk inserting it into the
- * middle of a normal data message by doing this.
- * (It's probably not too cool to write on stderr, for that matter...
- *  but for debugging purposes we'll risk that.)
+ * is safe only because PQrequestCancel is carefully written to
+ * make it so.  We have to be very careful what else we do in the
+ * signal handler.
+ * Writing on stderr is potentially dangerous, if the signal interrupted
+ * some stdio operation on stderr.  On Unix we can avoid trouble by using
+ * write() instead; on Windows that's probably not workable, but we can
+ * at least avoid trusting printf by using the more primitive fputs.
  */
 
 static PGconn * cancelConn = NULL; /* connection to try cancel on */
 
+static void
+safe_write_stderr (const char * s)
+{
+#ifdef WIN32
+	fputs(s, stderr);
+#else
+	write(fileno(stderr), s, strlen(s));
+#endif
+}
+
 static void
 handle_sigint (SIGNAL_ARGS)
 {
@@ -307,11 +319,13 @@ handle_sigint (SIGNAL_ARGS)
 		exit(1);				/* accept signal if no connection */
 	/* Try to send cancel request */
 	if (PQrequestCancel(cancelConn))
-		fprintf(stderr, "\nCANCEL request sent\n");
+	{
+		safe_write_stderr("\nCANCEL request sent\n");
+	}
 	else
 	{
-		fprintf(stderr, "\nCannot send cancel request:\n%s\n",
-				PQerrorMessage(cancelConn));
+		safe_write_stderr("\nCannot send cancel request:\n");
+		safe_write_stderr(PQerrorMessage(cancelConn));
 	}
 }
 
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 7717fd9d15..06a291070e 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dbcommands.h,v 1.1 1997/11/24 05:32:51 momjian Exp $
+ * $Id: dbcommands.h,v 1.2 1998/07/09 03:28:56 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,8 +17,7 @@
  * Originally from tmp/daemon.h. The functions declared in daemon.h does not
  * exist; hence removed.		-- AY 7/29/94
  */
-#define SIGKILLDAEMON1	SIGINT
-#define SIGKILLDAEMON2	SIGTERM
+#define SIGKILLDAEMON1	SIGTERM
 
 extern void createdb(char *dbname, char *dbpath);
 extern void destroydb(char *dbname);
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 7058eec246..5d0c6d7ac6 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-be.h,v 1.10 1998/02/26 04:41:49 momjian Exp $
+ * $Id: libpq-be.h,v 1.11 1998/07/09 03:29:00 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,16 +68,20 @@ typedef enum
 	WritingPacket
 } PacketState;
 
+typedef int (*PacketDoneProc) (void * arg, PacketLen pktlen, void * pktdata);
+
 typedef struct Packet
 {
 	PacketState state;			/* What's in progress. */
 	PacketLen	len;			/* Actual length */
 	int			nrtodo;			/* Bytes still to transfer */
 	char	   *ptr;			/* Buffer pointer */
-	void		(*iodone) ();	/* I/O complete callback */
-	char	   *arg;			/* Argument to callback */
+	PacketDoneProc	iodone;		/* I/O complete callback */
+	void	   *arg;			/* Argument to callback */
 
-	/* A union of all the different packets. */
+	/* We declare the data buffer as a union of the allowed packet types,
+	 * mainly to ensure that enough space is allocated for the largest one.
+	 */
 
 	union
 	{
@@ -89,6 +93,7 @@ typedef struct Packet
 		/* These are incoming and have a packet length prepended. */
 
 		StartupPacket si;
+		CancelRequestPacket canc;
 		PasswordPacketV0 pwv0;
 		PasswordPacket pw;
 	}			pkt;
@@ -126,16 +131,15 @@ typedef struct Port
 
 extern FILE *Pfout,
 		   *Pfin;
-extern int	PQAsyncNotifyWaiting;
 extern ProtocolVersion FrontendProtocol;
 
 
 /*
  * prototypes for functions in pqpacket.c
  */
-void		PacketReceiveSetup(Packet *pkt, void (*iodone) (), char *arg);
+void		PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg);
 int			PacketReceiveFragment(Packet *pkt, int sock);
-void		PacketSendSetup(Packet *pkt, int nbytes, void (*iodone) (), char *arg);
+void		PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg);
 int			PacketSendFragment(Packet *pkt, int sock);
 void		PacketSendError(Packet *pkt, char *errormsg);
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 9a347989c4..804bdf0cc4 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq.h,v 1.16 1998/06/16 07:29:41 momjian Exp $
+ * $Id: libpq.h,v 1.17 1998/07/09 03:29:01 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -126,8 +126,7 @@ extern size_t portals_array_size;
  */
 typedef struct PQNotifyList
 {
-	char		relname[NAMEDATALEN];	/* name of relation containing
-										 * data */
+	char		relname[NAMEDATALEN];	/* listen/notify name */
 	int			be_pid;			/* process id of backend */
 	int			valid;			/* has this already been handled by user. */
 /*	  SLNode Node; */
@@ -268,8 +267,6 @@ extern int	pq_getint(int b);
 extern void pq_putstr(char *s);
 extern void pq_putnchar(char *s, int n);
 extern void pq_putint(int i, int b);
-extern int	pq_sendoob(char *msg, int len);
-extern int	pq_recvoob(char *msgPtr, int len);
 extern int	pq_getinaddr(struct sockaddr_in * sin, char *host, int port);
 extern int	pq_getinserv(struct sockaddr_in * sin, char *host, char *serv);
 
@@ -281,10 +278,7 @@ extern int
 pq_connect(char *dbname, char *user, char *args, char *hostName,
 		   char *debugTty, char *execFile, short portName);
 extern int	StreamOpen(char *hostName, short portName, Port *port);
-extern void pq_regoob(void (*fptr) ());
-extern void pq_unregoob(void);
-extern void pq_async_notify(void);
-extern void StreamDoUnlink();
+extern void StreamDoUnlink(void);
 extern int StreamServerPort(char *hostName, short portName, int *fdP);
 extern int StreamConnection(int server_fd, Port *port);
 extern void StreamClose(int sock);
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 867e91c060..98d4ad0350 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.25 1998/05/06 23:50:32 momjian Exp $
+ * $Id: pqcomm.h,v 1.26 1998/07/09 03:29:01 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -122,6 +122,25 @@ typedef uint32 AuthRequest;
 typedef ProtocolVersion MsgType;
 
 
+/* A client can also send a cancel-current-operation request to the postmaster.
+ * This is uglier than sending it directly to the client's backend, but it
+ * avoids depending on out-of-band communication facilities.
+ */
+
+/* The cancel request code must not match any protocol version number
+ * we're ever likely to use.  This random choice should do.
+ */
+#define CANCEL_REQUEST_CODE	PG_PROTOCOL(1234,5678)
+
+typedef struct CancelRequestPacket
+{
+	/* Note that each field is stored in network byte order! */
+	MsgType	cancelRequestCode;		/* code to identify a cancel request */
+	uint32	backendPID;				/* PID of client's backend */
+	uint32	cancelAuthCode;			/* secret key to authorize cancel */
+} CancelRequestPacket;
+
+
 /* in pqcompriv.c */
 int			pqGetShort(int *, FILE *);
 int			pqGetLong(int *, FILE *);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 6f4d6f4028..a6f2243299 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: miscadmin.h,v 1.26 1998/06/09 17:13:06 momjian Exp $
+ * $Id: miscadmin.h,v 1.27 1998/07/09 03:28:55 scrappy Exp $
  *
  * NOTES
  *	  some of the information in this file will be moved to
@@ -42,6 +42,8 @@ extern char *DataDir;
 
 extern int	MyProcPid;
 
+extern long	MyCancelKey;
+
 extern char OutputFileName[];
 
 /*
diff --git a/src/include/port/sco.h b/src/include/port/sco.h
index 5bc74f7279..30db00386f 100644
--- a/src/include/port/sco.h
+++ b/src/include/port/sco.h
@@ -1,6 +1,5 @@
 #include <limits.h>				/* For _POSIX_PATH_MAX */
 
 #define MAXPATHLEN		_POSIX_PATH_MAX
-#define SIGURG			SIGUSR1
 
 #define NOFILE			NOFILES_MIN
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 672948a287..47aa2b9bca 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.72 1998/07/07 18:00:09 scrappy Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.73 1998/07/09 03:29:07 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -499,13 +499,11 @@ connectDB(PGconn *conn)
 {
 	PGresult   *res;
 	struct hostent *hp;
-
 	StartupPacket sp;
 	AuthRequest areq;
 	int			laddrlen = sizeof(SockAddr);
 	int			portno,
-				family,
-				len;
+				family;
 	char		beresp;
 	int			on = 1;
 
@@ -561,11 +559,11 @@ connectDB(PGconn *conn)
 				(char *) hp->h_addr,
 				hp->h_length);
 		conn->raddr.in.sin_port = htons((unsigned short) (portno));
-		len = sizeof(struct sockaddr_in);
+		conn->raddr_len = sizeof(struct sockaddr_in);
 	}
 #ifndef WIN32
 	else
-		len = UNIXSOCK_PATH(conn->raddr.un, portno);
+		conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);
 #endif
 	
 
@@ -577,7 +575,7 @@ connectDB(PGconn *conn)
 					   errno, strerror(errno));
 		goto connect_errReturn;
 	}
-	if (connect(conn->sock, &conn->raddr.sa, len) < 0)
+	if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
 	{
 		(void) sprintf(conn->errorMessage,
 					   "connectDB() failed: Is the postmaster running and accepting%s connections at '%s' on port '%s'?\n",
@@ -724,7 +722,7 @@ connectDB(PGconn *conn)
 	 * A ReadyForQuery message indicates that startup is successful,
 	 * but we might also get an Error message indicating failure.
 	 * (Notice messages indicating nonfatal warnings are also allowed
-	 * by the protocol.)
+	 * by the protocol, as is a BackendKeyData message.)
 	 * Easiest way to handle this is to let PQgetResult() read the messages.
 	 * We just have to fake it out about the state of the connection.
 	 */
@@ -994,6 +992,99 @@ PQreset(PGconn *conn)
 	}
 }
 
+
+/*
+ * PQrequestCancel: attempt to request cancellation of the current operation.
+ *
+ * The return value is TRUE if the cancel request was successfully
+ * dispatched, FALSE if not (in which case errorMessage is set).
+ * Note: successful dispatch is no guarantee that there will be any effect at
+ * the backend.  The application must read the operation result as usual.
+ *
+ * CAUTION: we want this routine to be safely callable from a signal handler
+ * (for example, an application might want to call it in a SIGINT handler).
+ * This means we cannot use any C library routine that might be non-reentrant.
+ * malloc/free are often non-reentrant, and anything that might call them is
+ * just as dangerous.  We avoid sprintf here for that reason.  Building up
+ * error messages with strcpy/strcat is tedious but should be quite safe.
+ */
+
+int
+PQrequestCancel(PGconn *conn)
+{
+	int			tmpsock = -1;
+	struct {
+		uint32				packetlen;
+		CancelRequestPacket	cp;
+	}			crp;
+
+	/* Check we have an open connection */
+	if (!conn)
+		return FALSE;
+
+	if (conn->sock < 0)
+	{
+		strcpy(conn->errorMessage,
+			   "PQrequestCancel() -- connection is not open\n");
+		return FALSE;
+	}
+
+	/*
+	 * We need to open a temporary connection to the postmaster.
+	 * Use the information saved by connectDB to do this with
+	 * only kernel calls.
+	 */
+	if ((tmpsock = socket(conn->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
+	{
+		strcpy(conn->errorMessage, "PQrequestCancel() -- socket() failed: ");
+		goto cancel_errReturn;
+	}
+	if (connect(tmpsock, &conn->raddr.sa, conn->raddr_len) < 0)
+	{
+		strcpy(conn->errorMessage, "PQrequestCancel() -- connect() failed: ");
+		goto cancel_errReturn;
+	}
+	/*
+	 * We needn't set nonblocking I/O or NODELAY options here.
+	 */
+
+	/* Create and send the cancel request packet. */
+
+	crp.packetlen = htonl((uint32) sizeof(crp));
+	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
+	crp.cp.backendPID = htonl(conn->be_pid);
+	crp.cp.cancelAuthCode = htonl(conn->be_key);
+
+	if (send(tmpsock, (char*) &crp, sizeof(crp), 0) != (int) sizeof(crp))
+	{
+		strcpy(conn->errorMessage, "PQrequestCancel() -- send() failed: ");
+		goto cancel_errReturn;
+	}
+
+	/* Sent it, done */
+#ifdef WIN32
+	closesocket(tmpsock);
+#else
+	close(tmpsock);
+#endif
+
+	return TRUE;
+
+cancel_errReturn:
+	strcat(conn->errorMessage, strerror(errno));
+	strcat(conn->errorMessage, "\n");
+	if (tmpsock >= 0)
+	{
+#ifdef WIN32
+		closesocket(tmpsock);
+#else
+		close(tmpsock);
+#endif
+	}
+	return FALSE;
+}
+
+
 /*
  * PacketSend() -- send a single-packet message.
  * this is like PacketSend(), defined in backend/libpq/pqpacket.c
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 49bd6d07e5..6a68af49d3 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.55 1998/07/03 04:24:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.56 1998/07/09 03:29:08 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -361,6 +361,16 @@ parseInput(PGconn *conn)
 														 PGRES_EMPTY_QUERY);
 					conn->asyncStatus = PGASYNC_READY;
 					break;
+				case 'K':		/* secret key data from the backend */
+					/* This is expected only during backend startup,
+					 * but it's just as easy to handle it as part of the
+					 * main loop.  Save the data and continue processing.
+					 */
+					if (pqGetInt(&(conn->be_pid), 4, conn))
+						return;
+					if (pqGetInt(&(conn->be_key), 4, conn))
+						return;
+					break;
 				case 'N':		/* notices from the backend */
 					if (getNotice(conn))
 						return;
@@ -761,44 +771,6 @@ PQexec(PGconn *conn, const char *query)
 }
 
 
-/*
- * Attempt to request cancellation of the current operation.
- *
- * The return value is TRUE if the cancel request was successfully
- * dispatched, FALSE if not (in which case errorMessage is set).
- * Note: successful dispatch is no guarantee that there will be any effect at
- * the backend.  The application must read the operation result as usual.
- */
-
-int
-PQrequestCancel(PGconn *conn)
-{
-	char msg[1];
-
-	if (!conn)
-		return FALSE;
-
-	if (conn->sock < 0)
-	{
-		sprintf(conn->errorMessage,
-				"PQrequestCancel() -- connection is not open\n");
-		return FALSE;
-	}
-
-	msg[0] = '\0';
-
-	if (send(conn->sock, msg, 1, MSG_OOB) < 0)
-	{
-		sprintf(conn->errorMessage,
-				"PQrequestCancel() -- couldn't send OOB data: errno=%d\n%s\n",
-				errno, strerror(errno));
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-
 /*
  * Attempt to read a Notice response message.
  * This is possible in several places, so we break it out as a subroutine.
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 1bfd0f5a3e..a4fad81a37 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.30 1998/06/16 07:29:49 momjian Exp $
+ * $Id: libpq-fe.h,v 1.31 1998/07/09 03:29:09 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -174,8 +174,11 @@ extern		"C"
 		int			sock;		/* Unix FD for socket, -1 if not connected */
 		SockAddr	laddr;		/* Local address */
 		SockAddr	raddr;		/* Remote address */
+		int			raddr_len;	/* Length of remote address */
 
 		/* Miscellaneous stuff */
+		int			be_pid;		/* PID of backend --- needed for cancels */
+		int			be_key;		/* key of backend --- needed for cancels */
 		char		salt[2];	/* password salt received from backend */
 		PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */
 
@@ -273,6 +276,8 @@ extern		"C"
 #define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME)   PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL)
 	/* close the current connection and free the PGconn data structure */
 	extern void PQfinish(PGconn *conn);
+	/* issue a cancel request */
+	extern int	PQrequestCancel(PGconn *conn);
 
 	/*
 	 * close the current connection and restablish a new one with the same
@@ -305,7 +310,6 @@ extern		"C"
 	/* Routines for managing an asychronous query */
 	extern int	PQisBusy(PGconn *conn);
 	extern void PQconsumeInput(PGconn *conn);
-	extern int	PQrequestCancel(PGconn *conn);
 	/* Routines for copy in/out */
 	extern int	PQgetline(PGconn *conn, char *string, int length);
 	extern void PQputline(PGconn *conn, const char *string);
diff --git a/src/man/listen.l b/src/man/listen.l
index d18d3fdd7f..49801408f7 100644
--- a/src/man/listen.l
+++ b/src/man/listen.l
@@ -1,6 +1,6 @@
 .\" This is -*-nroff-*-
 .\" XXX standard disclaimer belongs here....
-.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.6 1998/06/24 13:21:27 momjian Exp $
+.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.7 1998/07/09 03:29:09 scrappy Exp $
 .TH "LISTEN" SQL 03/12/94 PostgreSQL PostgreSQL
 .SH NAME
 listen - listen for notification on a relation
@@ -21,9 +21,7 @@ is cleared.
 .PP
 This event notification is performed through the Libpq protocol
 and frontend application interface.  The application program 
-must explicitly poll a Libpq global variable, 
-.IR PQAsyncNotifyWaiting ,
-and call the routine
+must call the routine
 .IR PQnotifies
 in order to find out the name of the class to which a given 
 notification corresponds.  If this code is not included in 
diff --git a/src/man/notify.l b/src/man/notify.l
index 50f44b442e..26b3de6f6f 100644
--- a/src/man/notify.l
+++ b/src/man/notify.l
@@ -1,6 +1,6 @@
 .\" This is -*-nroff-*-
 .\" XXX standard disclaimer belongs here....
-.\" $Header: /cvsroot/pgsql/src/man/Attic/notify.l,v 1.4 1998/06/24 13:21:27 momjian Exp $
+.\" $Header: /cvsroot/pgsql/src/man/Attic/notify.l,v 1.5 1998/07/09 03:29:11 scrappy Exp $
 .TH "NOTIFY" SQL 11/05/95 PostgreSQL PostgreSQL
 .SH NAME
 notify - signal all frontends and backends listening on a class
@@ -32,11 +32,14 @@ does is indicate that some backend wishes its peers to examine
 .IR class_name
 in some application-specific way.
 .PP
+In fact,
+.IR class_name
+need not be the name of an SQL class at all.  It is best thought of
+as a condition name that the application programmer selects.
+.PP
 This event notification is performed through the Libpq protocol
 and frontend application interface.  The application program
-must explicitly poll a Libpq global variable,
-.IR PQAsyncNotifyWaiting ,
-and call the routine
+must call the routine
 .IR PQnotifies
 in order to find out the name of the class to which a given
 notification corresponds.  If this code is not included in 
-- 
2.40.0