]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
From: Tom Lane <tgl@sss.pgh.pa.us>
authorMarc G. Fournier <scrappy@hub.org>
Thu, 9 Jul 1998 03:29:11 +0000 (03:29 +0000)
committerMarc G. Fournier <scrappy@hub.org>
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 @@
 <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>
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 <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;
 
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 <limits.h>                            /* 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