From: Tom Lane
authorMarc G. Fournier
Thu, 9 Jul 1998 03:29:11 +0000 (03:29 +0000)
committerMarc G. Fournier
Thu, 9 Jul 1998 03:29:11 +0000 (03:29 +0000)
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.

20 files changed:
doc/src/sgml/protocol.sgml
src/backend/commands/async.c
src/backend/libpq/auth.c
src/backend/libpq/pqcomm.c
src/backend/libpq/pqpacket.c
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/backend/utils/init/globals.c
src/bin/psql/psql.c
src/include/commands/dbcommands.h
src/include/libpq/libpq-be.h
src/include/libpq/libpq.h
src/include/libpq/pqcomm.h
src/include/miscadmin.h
src/include/port/sco.h
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-fe.h
src/man/listen.l
src/man/notify.l

index f40914583c9e7cbed6c02e5075d368c58f4f4e10..151ca3cb4228a190e3e5ba27a2699a561bbb4194 100644 (file)
@@ -4,7 +4,7 @@
 Phil
 Thompson
 
-1998-05-04
+1998-07-07
 
 Frontend/Backend Protocol
 
@@ -54,8 +54,10 @@ invalid database name).
 
 
 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.)
 
 
 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:
 
 
 
+
+   BackendKeyData
+
+
+
+       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.
+
+
+
+
 
    ReadyForQuery
 
@@ -218,6 +234,14 @@ The possible messages from the backend during this phase are:
 
 
 
+
+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.
+
 
 
 Query
@@ -453,7 +477,7 @@ NotificationResponse messages at any time; see below.
 
 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.
 
 
 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.
 
 
 
-       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.
 
 
@@ -479,29 +503,77 @@ NotificationResponse messages even when it is not engaged in a query.
 
 
 
+
+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.
+
 
 
 Cancelling Requests in Progress
 
 
 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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
 
 
 
 Termination
 
 
-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.
+
+
+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.
 
 
 
@@ -824,6 +896,52 @@ AuthenticationEncryptedPassword (B)
 
 
 
+
+
+
+
+
+BackendKeyData (B)
+
+
+
+
+
+
+
+   Byte1('K')
+
+
+
+       Identifies the message as cancellation key data.
+       The frontend must save these values if it wishes to be
+       able to issue CancelRequest messages later.
+
+
+
+
+
+   Int32
+
+
+
+       The process ID of this backend.
+
+
+
+
+
+   Int32
+
+
+
+       The secret key of this backend.
+
+
+
+
+
+
 
 
 
@@ -892,6 +1010,63 @@ BinaryRow (B)
 
 
 
+
+
+
+
+
+CancelRequest (F)
+
+
+
+
+
+
+
+   Int32(16)
+
+
+
+       The size of the packet in bytes.
+
+
+
+
+
+   Int32(80877102)
+
+
+
+       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.)
+
+
+
+
+
+   Int32
+
+
+
+       The process ID of the target backend.
+
+
+
+
+
+   Int32
+
+
+
+       The secret key for the target backend.
+
+
+
+
+
+
 
 
 
@@ -1092,31 +1267,6 @@ EncryptedPasswordPacket (F)
 
 
 
-
-
-
-
-
-ReadyForQuery (B)
-
-
-
-
-
-
-
-   Byte1('Z')
-
-
-
-       Identifies the message type.  ReadyForQuery is sent
-       whenever the backend is ready for a new query cycle.
-
-
-
-
-
-
 
 
 
@@ -1449,6 +1599,31 @@ Query (F)
 
 
 
+
+
+
+
+
+ReadyForQuery (B)
+
+
+
+
+
+
+
+   Byte1('Z')
+
+
+
+       Identifies the message type.  ReadyForQuery is sent
+       whenever the backend is ready for a new query cycle.
+
+
+
+
+
+
 
 
 
index b4b354cfc524deb4da6bfed5fdee373adf2251f0..b80cbb8f345df96a99bf89f88492def89464d5d7 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
  *   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
index 4aee9b9197a8bf5cc825baf950a15f56962c19a9..c1cc08f4c797cb7c7adf6048bbf41df02d36c238 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #include 
 
 
-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;
 
index a70bbc22e9c83efc3b31c3b4370de20a3318ce39..4c5b85b248b7fb26ba0f2929232964f3c0519701 100644 (file)
@@ -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)
    {
index 97caae952acfc11e17fc923347cab8cac11ea173..631af78ce299a6bf8ca7a7f6dccfb9ca2d547f35 100644 (file)
@@ -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;
 }
index 60d998814d1c18788bbccbe5ddf0d8cb07cb7269..7f33dfedba33148932044e03c50905c9352fb583 100644 (file)
@@ -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();
index 97ac571d2c6a9fbc02e4621aa671d9e2ea37163e..0a7408a7b94ba2dbf934d038e7757e2a29147505 100644 (file)
@@ -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");
        }
 
        /* ----------------
index 2deec81de00667235d76457ec8f040cdc5ac3145..585471d37f66ca03e4aa0bd3eaa80310f891babd 100644 (file)
@@ -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;
 
  /*
index 1ed9e4561fd7c525511f751f0fdbe7bbecf13276..65692bd9a1187b49868d16026b5039898f81ad85 100644 (file)
@@ -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));
    }
 }
 
index 7717fd9d153f1b253e1701665f367c23ed248d8b..06a291070e9f32d59e52c3765cf622e9904cdd9a 100644 (file)
@@ -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);
index 7058eec246243429e40a2a3823c8b10246ae26c6..5d0c6d7ac6e8bec6e2d64fcbd1a6319e9295f28f 100644 (file)
@@ -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);
 
index 9a347989c4091ddbebeef5216aced9f404620c5c..804bdf0cc482a8a6246e3b9d66dd9317370bf983 100644 (file)
@@ -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);
index 867e91c060f90897b9bc9caf3998d0e7ed24a0d4..98d4ad0350333b73c1e36f34fdf30f220db0f7f5 100644 (file)
@@ -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 *);
index 6f4d6f4028f410ee13193a583536327b057c0efb..a6f22432994c966ea5852c947d15c5d2ad942959 100644 (file)
@@ -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[];
 
 /*
index 5bc74f7279c637026213948eae18e196f4b18db3..30db00386fdb8817bc77bd25f43fd85acdbd88fc 100644 (file)
@@ -1,6 +1,5 @@
 #include                 /* For _POSIX_PATH_MAX */
 
 #define MAXPATHLEN     _POSIX_PATH_MAX
-#define SIGURG         SIGUSR1
 
 #define NOFILE         NOFILES_MIN
index 672948a287a4bd5d1702eec9b6a0c5dedf26a62e..47aa2b9bca02f994fbd1e329aba31e6953430911 100644 (file)
@@ -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
index 49bd6d07e513363c1bf655b014cdfd4afa72c9ea..6a68af49d3e86d6c0720a4bb7e37a3d8ef2bd908 100644 (file)
@@ -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.
index 1bfd0f5a3e4d10b40311cc250b5d582faeddb94b..a4fad81a3730aae261ff8391b4c5c94b8deb206a 100644 (file)
@@ -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);
index d18d3fdd7f07e8ee0b85d966f67c4be710e9ac0f..49801408f7363d2c94da2d629f380d421c8888ec 100644 (file)
@@ -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 
index 50f44b442e55f0d0e42e6e9c9221ed4e1eea6dea..26b3de6f6fa2ca931cba748bb80e57b2469e7cc8 100644 (file)
@@ -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