From: Tom Lane <tgl@sss.pgh.pa.us>

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

Some notes:
  * I modified the postmaster's packet "iodone" callback interface to allow
    the callback routine to return a continue-or-drop-connection return
    code; this was necessary to allow the connection to be closed after
    receiving a Cancel, rather than proceeding to launch a new backend...
    Being a neatnik, I also made the iodone proc have a typechecked
    parameter list.
  * I deleted all code I could find that had to do with OOB.
  * I made some edits to ensure that all signals mentioned in the code
    are referred to symbolically not by numbers ("SIGUSR2" not "2").
    I think Bruce may have already done at least some of the same edits;
    I hope that merging these patches is not too painful.
This commit is contained in:
Marc G. Fournier 1998-07-09 03:29:11 +00:00
parent 8bf61820f0
commit a0659e3e2c
20 changed files with 597 additions and 287 deletions

View File

@ -4,7 +4,7 @@
<FirstName>Phil</FirstName> <FirstName>Phil</FirstName>
<Surname>Thompson</Surname> <Surname>Thompson</Surname>
</Author> </Author>
<Date>1998-05-04</Date> <Date>1998-07-07</Date>
</DocInfo> </DocInfo>
<Title>Frontend/Backend Protocol</Title> <Title>Frontend/Backend Protocol</Title>
@ -54,8 +54,10 @@ invalid database name).
<Para> <Para>
Subsequent communications are query and result packets exchanged between the Subsequent communications are query and result packets exchanged between the
frontend and the backend. The postmaster takes no further part in the frontend and the backend. The postmaster takes no further part in ordinary
communication. 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> <Para>
When the frontend wishes to disconnect it sends an appropriate packet and 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> <Para>
<VariableList> <VariableList>
<VarListEntry> <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> <Term>
ReadyForQuery ReadyForQuery
</Term> </Term>
@ -218,6 +234,14 @@ The possible messages from the backend during this phase are:
</VariableList> </VariableList>
</Para> </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> <Sect2>
<Title>Query</Title> <Title>Query</Title>
@ -453,7 +477,7 @@ NotificationResponse messages at any time; see below.
<Para> <Para>
If a frontend issues a listen(l) command, then the backend will send a If a frontend issues a listen(l) command, then the backend will send a
NotificationResponse message (not to be confused with NoticeResponse!) 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> <Para>
Notification responses are permitted at any point in the protocol (after 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> </Term>
<ListItem> <ListItem>
<Para> <Para>
A notify(l) command has been executed for a relation for A notify(l) command has been executed for a name for which
which a previous listen(l) command was executed. Notifications a previous listen(l) command was executed. Notifications
may be sent at any time. may be sent at any time.
</Para> </Para>
</ListItem> </ListItem>
@ -479,29 +503,77 @@ NotificationResponse messages even when it is not engaged in a query.
</VariableList> </VariableList>
</Para> </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> <Sect2>
<Title>Cancelling Requests in Progress</Title> <Title>Cancelling Requests in Progress</Title>
<Para> <Para>
During the processing of a query, the frontend may request cancellation of the 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 query by sending an appropriate request to the postmaster. The cancel request
data byte should be zero (although the backend does not currently check this). is not sent directly to the backend for reasons of implementation efficiency:
If the cancellation is effective, it results in the current command being we don't want to have the backend constantly checking for new input from
terminated with an error message. Note that the backend makes no specific the frontend during query processing. Cancel requests should be relatively
reply to the cancel request itself. If the cancel request is ineffective infrequent, so we make them slightly cumbersome in order to avoid a penalty
(say, because it arrived after processing was complete) then it will have in the normal case.
no visible effect at all. Thus, the frontend must continue with its normal
processing of query cycle responses after issuing a cancel request. <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> <Sect2>
<Title>Termination</Title> <Title>Termination</Title>
<Para> <Para>
The frontend sends a Terminate message and immediately closes the connection. The normal, graceful termination procedure is that the frontend sends a
On receipt of the message, the backend immediately closes the connection and Terminate message and immediately closes the connection. On receipt of the
terminates. 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> <Sect1>
@ -824,6 +896,52 @@ AuthenticationEncryptedPassword (B)
</VarListEntry> </VarListEntry>
</VariableList> </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> </Para>
</ListItem> </ListItem>
</VarListEntry> </VarListEntry>
@ -892,6 +1010,63 @@ BinaryRow (B)
</VarListEntry> </VarListEntry>
</VariableList> </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> </Para>
</ListItem> </ListItem>
</VarListEntry> </VarListEntry>
@ -1092,31 +1267,6 @@ EncryptedPasswordPacket (F)
</VariableList> </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> </Para>
</ListItem> </ListItem>
</VarListEntry> </VarListEntry>
@ -1449,6 +1599,31 @@ Query (F)
</VariableList> </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> </Para>
</ListItem> </ListItem>
</VarListEntry> </VarListEntry>

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * 2.a If the process is the same as the backend process that issued
* notification (we are notifying something that we are listening), * notification (we are notifying something that we are listening),
* signal the corresponding frontend over the comm channel. * 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. * the listening backend.
* 3. Upon receiving a kill(2) signal from another backend process notifying * 3. Upon receiving a kill(SIGUSR2) signal from another backend process
* that one of the relation that we are listening is being notified, * notifying that one of the relation that we are listening is being
* we can be in either of two following states: * notified, we can be in either of two following states:
* 3.a We are sleeping, wake up and signal our frontend. * 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 * 3.b We are in middle of another transaction, wait until the end of
* of the current transaction and signal our frontend. * of the current transaction and signal our frontend.
@ -46,7 +46,7 @@
* (which takes place after commit) to all listeners on this relation. * (which takes place after commit) to all listeners on this relation.
* *
* 3. Async. notification results in all backends listening on 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. * passed in shared memory.
* *
* 4. Each backend notifies its respective frontend over the comm * 4. Each backend notifies its respective frontend over the comm

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.28 1998/06/13 04:27:14 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.29 1998/07/09 03:28:45 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -40,13 +40,13 @@
#include <libpq/crypt.h> #include <libpq/crypt.h>
static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ()); static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler);
static void handle_done_auth(Port *port); static int handle_done_auth(void *arg, PacketLen len, void *pkt);
static void handle_krb4_auth(Port *port); static int handle_krb4_auth(void *arg, PacketLen len, void *pkt);
static void handle_krb5_auth(Port *port); static int handle_krb5_auth(void *arg, PacketLen len, void *pkt);
static void handle_password_auth(Port *port); static int handle_password_auth(void *arg, PacketLen len, void *pkt);
static void readPasswordPacket(char *arg, PacketLen len, char *pkt); static int readPasswordPacket(void *arg, PacketLen len, void *pkt);
static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt); static int pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
static int checkPassword(Port *port, char *user, char *password); static int checkPassword(Port *port, char *user, char *password);
static int old_be_recvauth(Port *port); static int old_be_recvauth(Port *port);
static int map_old_to_new(Port *port, UserAuth old, int status); 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. * Handle a v0 password packet.
*/ */
static void static int
pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt) pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
{ {
Port *port; Port *port;
PasswordPacketV0 *pp; 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) if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
auth_failed(port); auth_failed(port);
} }
return (STATUS_OK); /* don't close the connection yet */
} }
@ -433,7 +435,7 @@ be_recvauth(Port *port)
else else
{ {
AuthRequest areq; AuthRequest areq;
void (*auth_handler) (); PacketDoneProc auth_handler;
/* Keep the compiler quiet. */ /* Keep the compiler quiet. */
@ -499,7 +501,7 @@ be_recvauth(Port *port)
*/ */
static void static void
sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ()) sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
{ {
char *dp, char *dp,
*sp; *sp;
@ -527,7 +529,7 @@ sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
i += 2; 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. * Called when we have told the front end that it is authorised.
*/ */
static void static int
handle_done_auth(Port *port) handle_done_auth(void *arg, PacketLen len, void *pkt)
{ {
/* /*
@ -544,7 +546,7 @@ handle_done_auth(Port *port)
* start. * start.
*/ */
return; return STATUS_OK;
} }
@ -553,13 +555,17 @@ handle_done_auth(Port *port)
* authentication. * authentication.
*/ */
static void static int
handle_krb4_auth(Port *port) handle_krb4_auth(void *arg, PacketLen len, void *pkt)
{ {
Port *port = (Port *) arg;
if (pg_krb4_recvauth(port) != STATUS_OK) if (pg_krb4_recvauth(port) != STATUS_OK)
auth_failed(port); auth_failed(port);
else else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
return STATUS_OK;
} }
@ -568,13 +574,17 @@ handle_krb4_auth(Port *port)
* authentication. * authentication.
*/ */
static void static int
handle_krb5_auth(Port *port) handle_krb5_auth(void *arg, PacketLen len, void *pkt)
{ {
Port *port = (Port *) arg;
if (pg_krb5_recvauth(port) != STATUS_OK) if (pg_krb5_recvauth(port) != STATUS_OK)
auth_failed(port); auth_failed(port);
else else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
return STATUS_OK;
} }
@ -583,12 +593,16 @@ handle_krb5_auth(Port *port)
* authentication. * authentication.
*/ */
static void static int
handle_password_auth(Port *port) handle_password_auth(void *arg, PacketLen len, void *pkt)
{ {
Port *port = (Port *) arg;
/* Set up the read of the password packet. */ /* 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. * Called when we have received the password packet.
*/ */
static void static int
readPasswordPacket(char *arg, PacketLen len, char *pkt) readPasswordPacket(void *arg, PacketLen len, void *pkt)
{ {
char password[sizeof(PasswordPacket) + 1]; char password[sizeof(PasswordPacket) + 1];
Port *port; Port *port = (Port *) arg;
port = (Port *) arg;
/* Silently truncate a password that is too big. */ /* Silently truncate a password that is too big. */
@ -615,6 +627,8 @@ readPasswordPacket(char *arg, PacketLen len, char *pkt)
auth_failed(port); auth_failed(port);
else else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); 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: case STARTUP_PASSWORD_MSG:
PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth, PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
(char *) port); (void *) port);
return STATUS_OK; return STATUS_OK;

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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_getinserv - initialize address from host and service name
* pq_connect - create remote input / output connection * pq_connect - create remote input / output connection
* pq_accept - accept remote input / output connection * pq_accept - accept remote input / output connection
* pq_async_notify - receive notification from backend.
* *
* NOTES * NOTES
* These functions are used by both frontend applications and * These functions are used by both frontend applications and
@ -79,7 +78,6 @@
FILE *Pfout, FILE *Pfout,
*Pfin; *Pfin;
FILE *Pfdebug; /* debugging libpq */ FILE *Pfdebug; /* debugging libpq */
int PQAsyncNotifyWaiting; /* for async. notification */
/* -------------------------------- /* --------------------------------
* pq_init - open portal file descriptors * pq_init - open portal file descriptors
@ -160,9 +158,7 @@ pq_close()
fclose(Pfout); fclose(Pfout);
Pfout = NULL; Pfout = NULL;
} }
PQAsyncNotifyWaiting = 0;
PQnotifies_init(); 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 * 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))); 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 * Streams -- wrapper around Unix socket system calls
* *
@ -620,7 +544,7 @@ StreamServerPort(char *hostName, short portName, int *fdP)
pqdebug("%s", PQerrormsg); pqdebug("%s", PQerrormsg);
return (STATUS_ERROR); return (STATUS_ERROR);
} }
bzero(&saddr, sizeof(saddr)); MemSet((char *) &saddr, 0, sizeof(saddr));
saddr.sa.sa_family = family; saddr.sa.sa_family = family;
if (family == AF_UNIX) if (family == AF_UNIX)
{ {

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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. * 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->nrtodo = sizeof(pkt->len);
pkt->ptr = (char *) &pkt->len; pkt->ptr = (char *) &pkt->len;
@ -94,8 +94,8 @@ PacketReceiveFragment(Packet *pkt, int sock)
if (pkt->iodone == NULL) if (pkt->iodone == NULL)
return STATUS_ERROR; return STATUS_ERROR;
(*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len), return (*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len),
(char *) &pkt->pkt); (void *) &pkt->pkt);
} }
return STATUS_OK; return STATUS_OK;
@ -107,7 +107,7 @@ PacketReceiveFragment(Packet *pkt, int sock)
if (errno == EINTR) if (errno == EINTR)
return STATUS_OK; return STATUS_OK;
fprintf(stderr, "read() system call failed\n"); perror("PacketReceiveFragment: read() failed");
return STATUS_ERROR; return STATUS_ERROR;
} }
@ -117,8 +117,9 @@ PacketReceiveFragment(Packet *pkt, int sock)
* Set up a packet write for the postmaster event loop. * 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->nrtodo = nbytes;
pkt->ptr = (char *) &pkt->pkt; pkt->ptr = (char *) &pkt->pkt;
pkt->iodone = iodone; pkt->iodone = iodone;
@ -153,7 +154,8 @@ PacketSendFragment(Packet *pkt, int sock)
if (pkt->iodone == NULL) if (pkt->iodone == NULL)
return STATUS_ERROR; return STATUS_ERROR;
(*pkt->iodone) (pkt->arg); return (*pkt->iodone) (pkt->arg, pkt->len,
(void *) &pkt->pkt);
} }
return STATUS_OK; return STATUS_OK;
@ -165,7 +167,7 @@ PacketSendFragment(Packet *pkt, int sock)
if (errno == EINTR) if (errno == EINTR)
return STATUS_OK; return STATUS_OK;
fprintf(stderr, "write() system call failed\n"); perror("PacketSendFragment: write() failed");
return STATUS_ERROR; return STATUS_ERROR;
} }

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* *
@ -206,7 +206,6 @@ static int orgsigmask = sigblock(0);
*/ */
static unsigned int random_seed = 0; static unsigned int random_seed = 0;
long MyCancelKey = 0;
extern char *optarg; extern char *optarg;
extern int optind, extern int optind,
@ -228,7 +227,8 @@ static void ExitPostmaster(int status);
static void usage(const char *); static void usage(const char *);
static int ServerLoop(void); static int ServerLoop(void);
static int BackendStartup(Port *port); 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 int initMasks(fd_set *rmask, fd_set *wmask);
static long PostmasterRandom(void); static long PostmasterRandom(void);
static void RandomSalt(char *salt); static void RandomSalt(char *salt);
@ -518,6 +518,10 @@ PostmasterMain(int argc, char *argv[])
if (silentflag) if (silentflag)
pmdaemonize(); pmdaemonize();
/*
* Set up signal handlers for the postmaster process.
*/
pqsignal(SIGINT, pmdie); pqsignal(SIGINT, pmdie);
pqsignal(SIGCHLD, reaper); pqsignal(SIGCHLD, reaper);
pqsignal(SIGTTIN, SIG_IGN); pqsignal(SIGTTIN, SIG_IGN);
@ -657,14 +661,14 @@ ServerLoop(void)
(port = ConnCreate(ServerSock_UNIX)) != NULL) (port = ConnCreate(ServerSock_UNIX)) != NULL)
PacketReceiveSetup(&port->pktInfo, PacketReceiveSetup(&port->pktInfo,
readStartupPacket, readStartupPacket,
(char *) port); (void *) port);
if (ServerSock_INET != INVALID_SOCK && if (ServerSock_INET != INVALID_SOCK &&
FD_ISSET(ServerSock_INET, &rmask) && FD_ISSET(ServerSock_INET, &rmask) &&
(port = ConnCreate(ServerSock_INET)) != NULL) (port = ConnCreate(ServerSock_INET)) != NULL)
PacketReceiveSetup(&port->pktInfo, PacketReceiveSetup(&port->pktInfo,
readStartupPacket, readStartupPacket,
(char *) port); (void *) port);
/* Build up new masks for select(). */ /* 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. * Called when the startup packet has been read.
*/ */
static void static int
readStartupPacket(char *arg, PacketLen len, char *pkt) readStartupPacket(void *arg, PacketLen len, void *pkt)
{ {
Port *port; Port *port;
StartupPacket *si; StartupPacket *si;
@ -799,6 +803,28 @@ readStartupPacket(char *arg, PacketLen len, char *pkt)
port = (Port *) arg; port = (Port *) arg;
si = (StartupPacket *) pkt; 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 * Get the parameters from the startup packet as C strings. The
* packet destination was cleared first so a short packet has zeros * 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') if (port->database[0] == '\0')
StrNCpy(port->database, si->user, sizeof(port->database) - 1); 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. */ /* Check a user name was given. */
if (port->user[0] == '\0') if (port->user[0] == '\0')
{ {
PacketSendError(&port->pktInfo, PacketSendError(&port->pktInfo,
"No Postgres username specified in startup packet."); "No Postgres username specified in startup packet.");
return; return STATUS_OK; /* don't close the connection yet */
} }
/* Start the authentication itself. */ /* Start the authentication itself. */
be_recvauth(port); 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]; char dbbuf[ARGV_SIZE + 1];
int ac = 0; int ac = 0;
int i; int i;
struct timeval now;
struct timezone tz;
/* /*
* Let's clean up ourselves as the postmaster child * Let's clean up ourselves as the postmaster child
@ -1254,7 +1325,16 @@ DoBackend(Port *port)
if (NetServer) if (NetServer)
StreamClose(ServerSock_INET); StreamClose(ServerSock_INET);
StreamClose(ServerSock_UNIX); 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 */ /* Now, on to standard postgres stuff */
MyProcPid = getpid(); MyProcPid = getpid();

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* this is the "main" module of the postgres backend and * 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() * 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. * occurs when elog(ERROR) is called.
* *
* quickdie() occurs when signalled by the postmaster. * 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 static void
QueryCancelHandler(SIGNAL_ARGS) QueryCancelHandler(SIGNAL_ARGS)
{ {
@ -787,12 +787,9 @@ QueryCancelHandler(SIGNAL_ARGS)
void void
CancelQuery(void) CancelQuery(void)
{ {
char dummy; /* QueryCancel flag will be reset in main loop, which we reach by
* longjmp from elog().
/* throw it away */ */
while (pq_recvoob(&dummy, 1) > 0)
;
/* QueryCancel reset in longjump after elog() call */
elog(ERROR, "Query was cancelled."); elog(ERROR, "Query was cancelled.");
} }
@ -1261,7 +1258,6 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
} }
pq_init(Portfd); pq_init(Portfd);
whereToSendOutput = Remote; whereToSendOutput = Remote;
pq_regoob(QueryCancelHandler); /* we do it here so the backend it connected */
} }
else else
whereToSendOutput = Debug; whereToSendOutput = Debug;
@ -1287,6 +1283,24 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
} }
#endif #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 * if an exception is encountered, processing resumes here
* so we abort the current transaction and start a new one. * 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 * so that the slaves signal the master to abort the transaction
* rather than calling AbortCurrentTransaction() themselves. * 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. * us back here.
* ---------------- * ----------------
*/ */
@ -1325,7 +1339,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
{ {
puts("\nPOSTGRES backend interactive interface"); 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; break;
default: default:
elog(ERROR, "unknown frontend message was recieved"); elog(ERROR, "unknown frontend message was received");
} }
/* ---------------- /* ----------------

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Globals used all over the place should be declared here and not * Globals used all over the place should be declared here and not
@ -44,6 +44,8 @@ bool QueryCancel = false;
int MyProcPid; int MyProcPid;
long MyCancelKey;
char *DataDir; char *DataDir;
/* /*

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * If interactive, we enable a SIGINT signal catcher that sends
* a cancel request to the backend. * a cancel request to the backend.
* Note that sending the cancel directly from the signal handler * Note that sending the cancel directly from the signal handler
* is safe only because the cancel is sent as an OOB message. * is safe only because PQrequestCancel is carefully written to
* If it were inline data, then we'd risk inserting it into the * make it so. We have to be very careful what else we do in the
* middle of a normal data message by doing this. * signal handler.
* (It's probably not too cool to write on stderr, for that matter... * Writing on stderr is potentially dangerous, if the signal interrupted
* but for debugging purposes we'll risk that.) * 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 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 static void
handle_sigint (SIGNAL_ARGS) handle_sigint (SIGNAL_ARGS)
{ {
@ -307,11 +319,13 @@ handle_sigint (SIGNAL_ARGS)
exit(1); /* accept signal if no connection */ exit(1); /* accept signal if no connection */
/* Try to send cancel request */ /* Try to send cancel request */
if (PQrequestCancel(cancelConn)) if (PQrequestCancel(cancelConn))
fprintf(stderr, "\nCANCEL request sent\n"); {
safe_write_stderr("\nCANCEL request sent\n");
}
else else
{ {
fprintf(stderr, "\nCannot send cancel request:\n%s\n", safe_write_stderr("\nCannot send cancel request:\n");
PQerrorMessage(cancelConn)); safe_write_stderr(PQerrorMessage(cancelConn));
} }
} }

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 * Originally from tmp/daemon.h. The functions declared in daemon.h does not
* exist; hence removed. -- AY 7/29/94 * exist; hence removed. -- AY 7/29/94
*/ */
#define SIGKILLDAEMON1 SIGINT #define SIGKILLDAEMON1 SIGTERM
#define SIGKILLDAEMON2 SIGTERM
extern void createdb(char *dbname, char *dbpath); extern void createdb(char *dbname, char *dbpath);
extern void destroydb(char *dbname); extern void destroydb(char *dbname);

View File

@ -7,7 +7,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 WritingPacket
} PacketState; } PacketState;
typedef int (*PacketDoneProc) (void * arg, PacketLen pktlen, void * pktdata);
typedef struct Packet typedef struct Packet
{ {
PacketState state; /* What's in progress. */ PacketState state; /* What's in progress. */
PacketLen len; /* Actual length */ PacketLen len; /* Actual length */
int nrtodo; /* Bytes still to transfer */ int nrtodo; /* Bytes still to transfer */
char *ptr; /* Buffer pointer */ char *ptr; /* Buffer pointer */
void (*iodone) (); /* I/O complete callback */ PacketDoneProc iodone; /* I/O complete callback */
char *arg; /* Argument to 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 union
{ {
@ -89,6 +93,7 @@ typedef struct Packet
/* These are incoming and have a packet length prepended. */ /* These are incoming and have a packet length prepended. */
StartupPacket si; StartupPacket si;
CancelRequestPacket canc;
PasswordPacketV0 pwv0; PasswordPacketV0 pwv0;
PasswordPacket pw; PasswordPacket pw;
} pkt; } pkt;
@ -126,16 +131,15 @@ typedef struct Port
extern FILE *Pfout, extern FILE *Pfout,
*Pfin; *Pfin;
extern int PQAsyncNotifyWaiting;
extern ProtocolVersion FrontendProtocol; extern ProtocolVersion FrontendProtocol;
/* /*
* prototypes for functions in pqpacket.c * 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); 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); int PacketSendFragment(Packet *pkt, int sock);
void PacketSendError(Packet *pkt, char *errormsg); void PacketSendError(Packet *pkt, char *errormsg);

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 typedef struct PQNotifyList
{ {
char relname[NAMEDATALEN]; /* name of relation containing char relname[NAMEDATALEN]; /* listen/notify name */
* data */
int be_pid; /* process id of backend */ int be_pid; /* process id of backend */
int valid; /* has this already been handled by user. */ int valid; /* has this already been handled by user. */
/* SLNode Node; */ /* SLNode Node; */
@ -268,8 +267,6 @@ extern int pq_getint(int b);
extern void pq_putstr(char *s); extern void pq_putstr(char *s);
extern void pq_putnchar(char *s, int n); extern void pq_putnchar(char *s, int n);
extern void pq_putint(int i, int b); 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_getinaddr(struct sockaddr_in * sin, char *host, int port);
extern int pq_getinserv(struct sockaddr_in * sin, char *host, char *serv); 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, pq_connect(char *dbname, char *user, char *args, char *hostName,
char *debugTty, char *execFile, short portName); char *debugTty, char *execFile, short portName);
extern int StreamOpen(char *hostName, short portName, Port *port); extern int StreamOpen(char *hostName, short portName, Port *port);
extern void pq_regoob(void (*fptr) ()); extern void StreamDoUnlink(void);
extern void pq_unregoob(void);
extern void pq_async_notify(void);
extern void StreamDoUnlink();
extern int StreamServerPort(char *hostName, short portName, int *fdP); extern int StreamServerPort(char *hostName, short portName, int *fdP);
extern int StreamConnection(int server_fd, Port *port); extern int StreamConnection(int server_fd, Port *port);
extern void StreamClose(int sock); extern void StreamClose(int sock);

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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; 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 */ /* in pqcompriv.c */
int pqGetShort(int *, FILE *); int pqGetShort(int *, FILE *);
int pqGetLong(int *, FILE *); int pqGetLong(int *, FILE *);

View File

@ -11,7 +11,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* some of the information in this file will be moved to * some of the information in this file will be moved to
@ -42,6 +42,8 @@ extern char *DataDir;
extern int MyProcPid; extern int MyProcPid;
extern long MyCancelKey;
extern char OutputFileName[]; extern char OutputFileName[];
/* /*

View File

@ -1,6 +1,5 @@
#include <limits.h> /* For _POSIX_PATH_MAX */ #include <limits.h> /* For _POSIX_PATH_MAX */
#define MAXPATHLEN _POSIX_PATH_MAX #define MAXPATHLEN _POSIX_PATH_MAX
#define SIGURG SIGUSR1
#define NOFILE NOFILES_MIN #define NOFILE NOFILES_MIN

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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; PGresult *res;
struct hostent *hp; struct hostent *hp;
StartupPacket sp; StartupPacket sp;
AuthRequest areq; AuthRequest areq;
int laddrlen = sizeof(SockAddr); int laddrlen = sizeof(SockAddr);
int portno, int portno,
family, family;
len;
char beresp; char beresp;
int on = 1; int on = 1;
@ -561,11 +559,11 @@ connectDB(PGconn *conn)
(char *) hp->h_addr, (char *) hp->h_addr,
hp->h_length); hp->h_length);
conn->raddr.in.sin_port = htons((unsigned short) (portno)); conn->raddr.in.sin_port = htons((unsigned short) (portno));
len = sizeof(struct sockaddr_in); conn->raddr_len = sizeof(struct sockaddr_in);
} }
#ifndef WIN32 #ifndef WIN32
else else
len = UNIXSOCK_PATH(conn->raddr.un, portno); conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);
#endif #endif
@ -577,7 +575,7 @@ connectDB(PGconn *conn)
errno, strerror(errno)); errno, strerror(errno));
goto connect_errReturn; 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, (void) sprintf(conn->errorMessage,
"connectDB() failed: Is the postmaster running and accepting%s connections at '%s' on port '%s'?\n", "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, * A ReadyForQuery message indicates that startup is successful,
* but we might also get an Error message indicating failure. * but we might also get an Error message indicating failure.
* (Notice messages indicating nonfatal warnings are also allowed * (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. * 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. * 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. * PacketSend() -- send a single-packet message.
* this is like PacketSend(), defined in backend/libpq/pqpacket.c * this is like PacketSend(), defined in backend/libpq/pqpacket.c

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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); PGRES_EMPTY_QUERY);
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
break; 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 */ case 'N': /* notices from the backend */
if (getNotice(conn)) if (getNotice(conn))
return; 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. * Attempt to read a Notice response message.
* This is possible in several places, so we break it out as a subroutine. * This is possible in several places, so we break it out as a subroutine.

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 */ int sock; /* Unix FD for socket, -1 if not connected */
SockAddr laddr; /* Local address */ SockAddr laddr; /* Local address */
SockAddr raddr; /* Remote address */ SockAddr raddr; /* Remote address */
int raddr_len; /* Length of remote address */
/* Miscellaneous stuff */ /* 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 */ char salt[2]; /* password salt received from backend */
PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */ 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) #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 */ /* close the current connection and free the PGconn data structure */
extern void PQfinish(PGconn *conn); 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 * close the current connection and restablish a new one with the same
@ -305,7 +310,6 @@ extern "C"
/* Routines for managing an asychronous query */ /* Routines for managing an asychronous query */
extern int PQisBusy(PGconn *conn); extern int PQisBusy(PGconn *conn);
extern void PQconsumeInput(PGconn *conn); extern void PQconsumeInput(PGconn *conn);
extern int PQrequestCancel(PGconn *conn);
/* Routines for copy in/out */ /* Routines for copy in/out */
extern int PQgetline(PGconn *conn, char *string, int length); extern int PQgetline(PGconn *conn, char *string, int length);
extern void PQputline(PGconn *conn, const char *string); extern void PQputline(PGconn *conn, const char *string);

View File

@ -1,6 +1,6 @@
.\" This is -*-nroff-*- .\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here.... .\" 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 .TH "LISTEN" SQL 03/12/94 PostgreSQL PostgreSQL
.SH NAME .SH NAME
listen - listen for notification on a relation listen - listen for notification on a relation
@ -21,9 +21,7 @@ is cleared.
.PP .PP
This event notification is performed through the Libpq protocol This event notification is performed through the Libpq protocol
and frontend application interface. The application program and frontend application interface. The application program
must explicitly poll a Libpq global variable, must call the routine
.IR PQAsyncNotifyWaiting ,
and call the routine
.IR PQnotifies .IR PQnotifies
in order to find out the name of the class to which a given in order to find out the name of the class to which a given
notification corresponds. If this code is not included in notification corresponds. If this code is not included in

View File

@ -1,6 +1,6 @@
.\" This is -*-nroff-*- .\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here.... .\" 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 .TH "NOTIFY" SQL 11/05/95 PostgreSQL PostgreSQL
.SH NAME .SH NAME
notify - signal all frontends and backends listening on a class 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 .IR class_name
in some application-specific way. in some application-specific way.
.PP .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 This event notification is performed through the Libpq protocol
and frontend application interface. The application program and frontend application interface. The application program
must explicitly poll a Libpq global variable, must call the routine
.IR PQAsyncNotifyWaiting ,
and call the routine
.IR PQnotifies .IR PQnotifies
in order to find out the name of the class to which a given in order to find out the name of the class to which a given
notification corresponds. If this code is not included in notification corresponds. If this code is not included in