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