Next try: Everything after fork

From: Peter Eisentraut <peter_e(at)gmx(dot)net>
To: <pgsql-patches(at)postgresql(dot)org>
Subject: Next try: Everything after fork
Date: 2001-06-18 23:21:25
Message-ID: Pine.LNX.4.30.0106190058040.898-100000@peter.localdomain
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches

This patch now does the entire startup sequence after the fork. I've
tested password and ident authentication and query cancellation
successfully. There's still the shutdown race I mentioned on -hackers. (I
know, all the multitasking support code should be cleared out. I can do
that after the functionality is implemented.)

One problem currently is that when the backend requires password
authentication and the frontend doesn't want to participate (the user
neglected to provide a password), the frontend correctly closes the
connection. This gives an

pq_recvbuf: unexpected EOF on client connection

in the server log, because the routines in pqcomm.c are not prepared for
this case. Does anyone want to suggest a way to teach the API about that?

Index: src/backend/libpq/auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.52
diff -c -r1.52 auth.c
*** src/backend/libpq/auth.c 2001/03/22 03:59:30 1.52
--- src/backend/libpq/auth.c 2001/06/18 23:06:31
***************
*** 12,55 ****
*
*-------------------------------------------------------------------------
*/
! /*
! * INTERFACE ROUTINES
! *
! * backend (postmaster) routines:
! * be_recvauth receive authentication information
! */
! #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
! #ifndef MAXHOSTNAMELEN
! #include <netdb.h> /* for MAXHOSTNAMELEN on some */
! #endif
! #include <pwd.h>
! #include <ctype.h>

#include <sys/types.h> /* needed by in.h on Ultrix */
#include <netinet/in.h>
#include <arpa/inet.h>

- #include "postgres.h"
-
#include "libpq/auth.h"
#include "libpq/crypt.h"
#include "libpq/hba.h"
#include "libpq/libpq.h"
#include "libpq/password.h"
#include "miscadmin.h"

- 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);
static void auth_failed(Port *port);

char *pg_krb_server_keyfile;

--- 12,41 ----
*
*-------------------------------------------------------------------------
*/
!
! #include "postgres.h"

#include <sys/types.h> /* needed by in.h on Ultrix */
#include <netinet/in.h>
#include <arpa/inet.h>

#include "libpq/auth.h"
#include "libpq/crypt.h"
#include "libpq/hba.h"
#include "libpq/libpq.h"
#include "libpq/password.h"
+ #include "libpq/pqformat.h"
#include "miscadmin.h"
+
+ static void sendAuthRequest(Port *port, AuthRequest areq);

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);
static void auth_failed(Port *port);

+ static int recv_and_check_password_packet(Port *port);
+ static int recv_and_check_passwordv0(Port *port);

char *pg_krb_server_keyfile;

***************
*** 325,349 ****
/*
* Handle a v0 password packet.
*/
-
static int
! pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
{
! Port *port;
PasswordPacketV0 *pp;
char *user,
*password,
*cp,
*start;

! port = (Port *) arg;
! pp = (PasswordPacketV0 *) pkt;

/*
* The packet is supposed to comprise the user name and the password
* as C strings. Be careful the check that this is the case.
*/
-
user = password = NULL;

len -= sizeof(pp->unused);
--- 311,338 ----
/*
* Handle a v0 password packet.
*/
static int
! recv_and_check_passwordv0(Port *port)
{
! int32 len;
! char *buf;
PasswordPacketV0 *pp;
char *user,
*password,
*cp,
*start;
+
+ pq_getint(&len, 4);
+ len -= 4;
+ buf = palloc(len);
+ pq_getbytes(buf, len);

! pp = (PasswordPacketV0 *) buf;

/*
* The packet is supposed to comprise the user name and the password
* as C strings. Be careful the check that this is the case.
*/
user = password = NULL;

len -= sizeof(pp->unused);
***************
*** 371,376 ****
--- 360,366 ----
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);

+ pfree(buf);
auth_failed(port);
}
else
***************
*** 385,399 ****

status = checkPassword(port, user, password);

port->auth_method = saved;

/* Adjust the result if necessary. */
-
if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
auth_failed(port);
}

! return STATUS_OK; /* don't close the connection yet */
}

--- 375,389 ----

status = checkPassword(port, user, password);

+ pfree(buf);
port->auth_method = saved;

/* Adjust the result if necessary. */
if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
auth_failed(port);
}

! return STATUS_OK;
}

***************
*** 413,419 ****
static void
auth_failed(Port *port)
{
- char buffer[512];
const char *authmethod = "Unknown auth method:";

switch (port->auth_method)
--- 403,408 ----
***************
*** 440,459 ****
authmethod = "Password";
break;
}
-
- sprintf(buffer, "%s authentication failed for user '%s'",
- authmethod, port->user);

! PacketSendError(&port->pktInfo, buffer);
}

/*
! * be_recvauth -- server demux routine for incoming authentication information
*/
void
! be_recvauth(Port *port)
{

/*
* Get the authentication method to use for this frontend/database
--- 429,449 ----
authmethod = "Password";
break;
}

! elog(FATAL, "%s authentication failed for user '%s'",
! authmethod, port->user);
}

+
/*
! * Client authentication starts here. If there is an error, this
! * function does not return and the backend process is terminated.
*/
void
! ClientAuthentication(Port *port)
{
+ int status;

/*
* Get the authentication method to use for this frontend/database
***************
*** 463,559 ****
*/

if (hba_getauthmethod(port) != STATUS_OK)
! PacketSendError(&port->pktInfo,
! "Missing or erroneous pg_hba.conf file, see postmaster log for details");

else if (PG_PROTOCOL_MAJOR(port->proto) == 0)
{
- /* Handle old style authentication. */
-
if (old_be_recvauth(port) != STATUS_OK)
auth_failed(port);
}
- else
- {
- /* Handle new style authentication. */
-
- AuthRequest areq = AUTH_REQ_OK;
- PacketDoneProc auth_handler = NULL;

! switch (port->auth_method)
! {
! case uaReject:
!
! /*
! * This could have come from an explicit "reject" entry in
! * pg_hba.conf, but more likely it means there was no
! * matching entry. Take pity on the poor user and issue a
! * helpful error message. NOTE: this is not a security
! * breach, because all the info reported here is known at
! * the frontend and must be assumed known to bad guys.
! * We're merely helping out the less clueful good guys.
! * NOTE 2: libpq-be.h defines the maximum error message
! * length as 99 characters. It probably wouldn't hurt
! * anything to increase it, but there might be some client
! * out there that will fail. So, be terse.
! */
! {
! char buffer[512];
! const char *hostinfo = "localhost";
!
! if (port->raddr.sa.sa_family == AF_INET)
! hostinfo = inet_ntoa(port->raddr.in.sin_addr);
! sprintf(buffer,
! "No pg_hba.conf entry for host %s, user %s, database %s",
! hostinfo, port->user, port->database);
! PacketSendError(&port->pktInfo, buffer);
! return;
! }
! break;

! case uaKrb4:
! areq = AUTH_REQ_KRB4;
! auth_handler = handle_krb4_auth;
! break;

! case uaKrb5:
! areq = AUTH_REQ_KRB5;
! auth_handler = handle_krb5_auth;
! break;

! case uaTrust:
! areq = AUTH_REQ_OK;
! auth_handler = handle_done_auth;
! break;

! case uaIdent:
! if (authident(&port->raddr.in, &port->laddr.in,
! port->user, port->auth_arg) == STATUS_OK)
! {
! areq = AUTH_REQ_OK;
! auth_handler = handle_done_auth;
! }

! break;

! case uaPassword:
! areq = AUTH_REQ_PASSWORD;
! auth_handler = handle_password_auth;
! break;

! case uaCrypt:
! areq = AUTH_REQ_CRYPT;
! auth_handler = handle_password_auth;
! break;
! }

! /* Tell the frontend what we want next. */

! if (auth_handler != NULL)
! sendAuthRequest(port, areq, auth_handler);
! else
! auth_failed(port);
}
}

--- 453,529 ----
*/

if (hba_getauthmethod(port) != STATUS_OK)
! elog(FATAL, "Missing or erroneous pg_hba.conf file, see postmaster log for details");

+ /* Handle old style authentication. */
else if (PG_PROTOCOL_MAJOR(port->proto) == 0)
{
if (old_be_recvauth(port) != STATUS_OK)
auth_failed(port);
+ return;
}

! /* Handle new style authentication. */

! switch (port->auth_method)
! {
! case uaReject:

! /*
! * This could have come from an explicit "reject" entry in
! * pg_hba.conf, but more likely it means there was no
! * matching entry. Take pity on the poor user and issue a
! * helpful error message. NOTE: this is not a security
! * breach, because all the info reported here is known at
! * the frontend and must be assumed known to bad guys.
! * We're merely helping out the less clueful good guys.
! */
! {
! const char *hostinfo = "localhost";

! if (port->raddr.sa.sa_family == AF_INET)
! hostinfo = inet_ntoa(port->raddr.in.sin_addr);
! elog(FATAL,
! "No pg_hba.conf entry for host %s, user %s, database %s",
! hostinfo, port->user, port->database);
! return;
! }
! break;

! case uaKrb4:
! sendAuthRequest(port, AUTH_REQ_KRB4);
! status = pg_krb4_recvauth(port);
! break;

! case uaKrb5:
! sendAuthRequest(port, AUTH_REQ_KRB5);
! status = pg_krb5_recvauth(port);
! break;

! case uaIdent:
! status = authident(&port->raddr.in, &port->laddr.in,
! port->user, port->auth_arg);
! break;

! case uaPassword:
! sendAuthRequest(port, AUTH_REQ_PASSWORD);
! status = recv_and_check_password_packet(port);
! break;

! case uaCrypt:
! sendAuthRequest(port, AUTH_REQ_CRYPT);
! status = recv_and_check_password_packet(port);
! break;

! case uaTrust:
! status = STATUS_OK;
! break;
}
+
+ if (status == STATUS_OK)
+ sendAuthRequest(port, AUTH_REQ_OK);
+ else
+ auth_failed(port);
}

***************
*** 562,695 ****
*/

static void
! sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
{
! char *dp,
! *sp;
! int i;
! uint32 net_areq;
!
! /* Convert to a byte stream. */
!
! net_areq = htonl(areq);

! dp = port->pktInfo.pkt.ar.data;
! sp = (char *) &net_areq;

- *dp++ = 'R';
-
- for (i = 1; i <= 4; ++i)
- *dp++ = *sp++;
-
/* Add the salt for encrypted passwords. */
-
if (areq == AUTH_REQ_CRYPT)
{
! *dp++ = port->salt[0];
! *dp++ = port->salt[1];
! i += 2;
}

! PacketSendSetup(&port->pktInfo, i, handler, (void *) port);
}

- /*
- * Called when we have told the front end that it is authorised.
- */

- static int
- handle_done_auth(void *arg, PacketLen len, void *pkt)
- {
-
- /*
- * Don't generate any more traffic. This will cause the backend to
- * start.
- */
-
- return STATUS_OK;
- }
-
-
- /*
- * Called when we have told the front end that it should use Kerberos V4
- * authentication.
- */
-
- 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;
- }
-
-
- /*
- * Called when we have told the front end that it should use Kerberos V5
- * authentication.
- */
-
- 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;
- }
-
-
/*
- * Called when we have told the front end that it should use password
- * authentication.
- */
-
- 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, (void *) port);
-
- return STATUS_OK;
- }
-
-
- /*
* Called when we have received the password packet.
*/

static int
! readPasswordPacket(void *arg, PacketLen len, void *pkt)
{
! char password[sizeof(PasswordPacket) + 1];
! Port *port = (Port *) arg;
!
! /* Silently truncate a password that is too big. */
!
! if (len > sizeof(PasswordPacket))
! len = sizeof(PasswordPacket);
!
! StrNCpy(password, ((PasswordPacket *) pkt)->passwd, len);
!
! if (checkPassword(port, port->user, password) != STATUS_OK)
! auth_failed(port);
! else
! sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
!
! return STATUS_OK; /* don't close the connection yet */
}

--- 532,581 ----
*/

static void
! sendAuthRequest(Port *port, AuthRequest areq)
{
! StringInfoData buf;

! pq_beginmessage(&buf);
! pq_sendbyte(&buf, 'R');
! pq_sendint(&buf, (int32) areq, sizeof(int32));

/* Add the salt for encrypted passwords. */
if (areq == AUTH_REQ_CRYPT)
{
! pq_sendint(&buf, port->salt[0], 1);
! pq_sendint(&buf, port->salt[1], 1);
}

! pq_endmessage(&buf);
! pq_flush();
}

/*
* Called when we have received the password packet.
*/

static int
! recv_and_check_password_packet(Port *port)
{
! StringInfoData buf;
! int32 len;
! int result;
!
! if (pq_getint(&len, 4) == EOF)
! return STATUS_ERROR; /* client didn't want to send password */
! initStringInfo(&buf);
! pq_getstr(&buf);
!
! if (DebugLvl)
! fprintf(stderr, "received password packet with len=%d, pw=%s\n",
! len, buf.data);
!
! result = checkPassword(port, port->user, buf.data);
! pfree(buf.data);
! return result;
}

***************
*** 734,743 ****
break;

case STARTUP_PASSWORD_MSG:
! PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
! (void *) port);
!
! return STATUS_OK;

default:
fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
--- 620,627 ----
break;

case STARTUP_PASSWORD_MSG:
! status = recv_and_check_passwordv0(port);
! break;

default:
fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
***************
*** 760,767 ****
{
switch (port->auth_method)
{
! case uaCrypt:
! case uaReject:
status = STATUS_ERROR;
break;

--- 644,651 ----
{
switch (port->auth_method)
{
! case uaCrypt:
! case uaReject:
status = STATUS_ERROR;
break;

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.221
diff -c -r1.221 postmaster.c
*** src/backend/postmaster/postmaster.c 2001/06/16 22:58:12 1.221
--- src/backend/postmaster/postmaster.c 2001/06/18 23:06:35
***************
*** 116,131 ****
*/
typedef struct bkend
{
! int pid; /* process id of backend */
long cancel_key; /* cancel key for cancels for this backend */
} Backend;

/* list of active backends. For garbage collection only now. */
static Dllist *BackendList;

- /* list of ports associated with still open, but incomplete connections */
- static Dllist *PortList;
-
/* The socket number we are listening for connections on */
int PostPortNumber;
char *UnixSocketDir;
--- 116,128 ----
*/
typedef struct bkend
{
! pid_t pid; /* process id of backend */
long cancel_key; /* cancel key for cancels for this backend */
} Backend;

/* list of active backends. For garbage collection only now. */
static Dllist *BackendList;

/* The socket number we are listening for connections on */
int PostPortNumber;
char *UnixSocketDir;
***************
*** 221,241 ****
static void pmdaemonize(int argc, char *argv[]);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
! static void ClosePostmasterPorts(Port *myConn);
static void reset_shared(unsigned short port);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
static void schedule_checkpoint(SIGNAL_ARGS);
- static void dumpstatus(SIGNAL_ARGS);
static void CleanupProc(int pid, int exitstatus);
static int DoBackend(Port *port);
static void ExitPostmaster(int status);
static void usage(const char *);
static int ServerLoop(void);
static int BackendStartup(Port *port);
! 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 char *canAcceptConnections(void);
static long PostmasterRandom(void);
--- 218,237 ----
static void pmdaemonize(int argc, char *argv[]);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
! static void ClosePostmasterPorts(void);
static void reset_shared(unsigned short port);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
static void schedule_checkpoint(SIGNAL_ARGS);
static void CleanupProc(int pid, int exitstatus);
static int DoBackend(Port *port);
static void ExitPostmaster(int status);
static void usage(const char *);
static int ServerLoop(void);
static int BackendStartup(Port *port);
! static int ProcessStartupPacket(Port *port);
! static void processCancelRequest(Port *port, void *pkt);
static int initMasks(fd_set *rmask, fd_set *wmask);
static char *canAcceptConnections(void);
static long PostmasterRandom(void);
***************
*** 661,667 ****
* garbage collecting the backend processes.
*/
BackendList = DLNewList();
- PortList = DLNewList();

/*
* Record postmaster options. We delay this till now to avoid
--- 657,662 ----
***************
*** 690,696 ****
pqsignal(SIGCHLD, reaper); /* handle child termination */
pqsignal(SIGTTIN, SIG_IGN); /* ignored */
pqsignal(SIGTTOU, SIG_IGN); /* ignored */
- pqsignal(SIGWINCH, dumpstatus); /* dump port status */

/*
* We're ready to rock and roll...
--- 685,690 ----
***************
*** 794,800 ****
fd_set readmask,
writemask;
int nSockets;
- Dlelem *curr;
struct timeval now,
later;
struct timezone tz;
--- 788,793 ----
***************
*** 841,867 ****
}
}

- #ifdef USE_SSL
-
- /*
- * If we are using SSL, there may be input data already read and
- * pending in SSL's input buffers. If so, check for additional
- * input from other clients, but don't delay before processing.
- */
- for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr))
- {
- Port *port = (Port *) DLE_VAL(curr);
-
- if (port->ssl && SSL_pending(port->ssl))
- {
- timeout_tv.tv_sec = 0;
- timeout_tv.tv_usec = 0;
- timeout = &timeout_tv;
- break;
- }
- }
- #endif
-
/*
* Wait for something to happen.
*/
--- 834,839 ----
***************
*** 915,1040 ****
*/

#ifdef HAVE_UNIX_SOCKETS
! if (ServerSock_UNIX != INVALID_SOCK &&
! FD_ISSET(ServerSock_UNIX, &rmask) &&
! (port = ConnCreate(ServerSock_UNIX)) != NULL)
{
! PacketReceiveSetup(&port->pktInfo,
! readStartupPacket,
! (void *) port);
}
#endif

! if (ServerSock_INET != INVALID_SOCK &&
! FD_ISSET(ServerSock_INET, &rmask) &&
! (port = ConnCreate(ServerSock_INET)) != NULL)
{
! PacketReceiveSetup(&port->pktInfo,
! readStartupPacket,
! (void *) port);
}
-
- /*
- * Scan active ports, processing any available input. While we
- * are at it, build up new masks for next select().
- */
- nSockets = initMasks(&readmask, &writemask);
-
- curr = DLGetHead(PortList);
-
- while (curr)
- {
- Port *port = (Port *) DLE_VAL(curr);
- int status = STATUS_OK;
- Dlelem *next;
-
- if (FD_ISSET(port->sock, &rmask)
- #ifdef USE_SSL
- || (port->ssl && SSL_pending(port->ssl))
- #endif
- )
- {
- if (DebugLvl > 1)
- postmaster_error("ServerLoop: handling reading %d", port->sock);
-
- if (PacketReceiveFragment(port) != STATUS_OK)
- status = STATUS_ERROR;
- }
-
- if (FD_ISSET(port->sock, &wmask))
- {
- if (DebugLvl > 1)
- postmaster_error("ServerLoop: handling writing %d", port->sock);
-
- if (PacketSendFragment(port) != STATUS_OK)
- status = STATUS_ERROR;
- }
-
- /* Get this before the connection might be closed. */
-
- next = DLGetSucc(curr);
-
- /*
- * If there is no error and no outstanding data transfer going
- * on, then the authentication handshake must be complete to
- * the postmaster's satisfaction. So, start the backend.
- */
-
- if (status == STATUS_OK && port->pktInfo.state == Idle)
- {
-
- /*
- * Can we accept a connection now?
- *
- * Even though readStartupPacket() already checked, we have
- * to check again in case conditions changed while
- * negotiating authentication.
- */
- char *rejectMsg = canAcceptConnections();
-
- if (rejectMsg != NULL)
- PacketSendError(&port->pktInfo, rejectMsg);
- else
- {
-
- /*
- * If the backend start fails then keep the connection
- * open to report it. Otherwise, pretend there is an
- * error to close our descriptor for the connection,
- * which will now be managed by the backend.
- */
- if (BackendStartup(port) != STATUS_OK)
- PacketSendError(&port->pktInfo,
- "Backend startup failed");
- else
- status = STATUS_ERROR;
- }
- }
-
- /* Close the connection if required. */
-
- if (status != STATUS_OK)
- {
- StreamClose(port->sock);
- DLRemove(curr);
- ConnFree(port);
- DLFreeElem(curr);
- }
- else
- {
- /* Set the masks for this connection. */
-
- if (nSockets <= port->sock)
- nSockets = port->sock + 1;
-
- if (port->pktInfo.state == WritingPacket)
- FD_SET(port->sock, &writemask);
- else
- FD_SET(port->sock, &readmask);
- }
-
- curr = next;
- } /* loop over active ports */
}
}

--- 887,912 ----
*/

#ifdef HAVE_UNIX_SOCKETS
! if (ServerSock_UNIX != INVALID_SOCK
! && FD_ISSET(ServerSock_UNIX, &rmask))
{
! port = ConnCreate(ServerSock_UNIX);
! if (port)
! BackendStartup(port);
! StreamClose(port->sock);
! ConnFree(port);
}
#endif

! if (ServerSock_INET != INVALID_SOCK
! && FD_ISSET(ServerSock_INET, &rmask))
{
! port = ConnCreate(ServerSock_INET);
! if (port)
! BackendStartup(port);
! StreamClose(port->sock);
! ConnFree(port);
}
}
}

***************
*** 1074,1101 ****

/*
! * Called when the startup packet has been read.
*/
-
static int
! readStartupPacket(void *arg, PacketLen len, void *pkt)
{
! Port *port;
! StartupPacket *si;
char *rejectMsg;

! 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);

if (port->proto == NEGOTIATE_SSL_CODE)
{
--- 946,987 ----

/*
! * Read the startup packet and do something according to it.
! *
! * Returns STATUS_OK or STATUS_ERROR, or might call elog(FATAL) and
! * not return at all.
*/
static int
! ProcessStartupPacket(Port *port)
{
! StartupPacket *packet;
char *rejectMsg;
+ int32 len;
+ void *buf;

! pq_getbytes((char *)&len, 4);
! len = ntohl(len);
! len -= 4;
!
! if (len < sizeof(len) || len > sizeof(len) + sizeof(StartupPacket))
! elog(FATAL, "invalid length of startup packet");
!
! buf = palloc(len);
! pq_getbytes(buf, len);
!
! packet = buf;

/*
* The first field is either a protocol version number or a special
* request code.
*/
! port->proto = ntohl(packet->protoVersion);

if (port->proto == CANCEL_REQUEST_CODE)
! {
! processCancelRequest(port, packet);
! return 127; /* XXX */
! }

if (port->proto == NEGOTIATE_SSL_CODE)
{
***************
*** 1114,1120 ****
{
postmaster_error("failed to send SSL negotiation response: %s",
strerror(errno));
! return STATUS_ERROR;/* Close connection */
}

#ifdef USE_SSL
--- 1000,1006 ----
{
postmaster_error("failed to send SSL negotiation response: %s",
strerror(errno));
! return STATUS_ERROR; /* close the connection */
}

#ifdef USE_SSL
***************
*** 1130,1140 ****
}
}
#endif
! /* ready for the normal startup packet */
! PacketReceiveSetup(&port->pktInfo,
! readStartupPacket,
! (void *) port);
! return STATUS_OK; /* Do not close connection */
}

/* Could add additional special packet types here */
--- 1016,1025 ----
}
}
#endif
! /* regular startup packet should follow... */
! /* FIXME: by continuing to send SSL negotiation packets, a
! client could run us out of stack space */
! return ProcessStartupPacket(port);
}

/* Could add additional special packet types here */
***************
*** 1146,1221 ****
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
* silently added and a long packet is silently truncated.
*/
!
! StrNCpy(port->database, si->database, sizeof(port->database));
! StrNCpy(port->user, si->user, sizeof(port->user));
! StrNCpy(port->options, si->options, sizeof(port->options));
! StrNCpy(port->tty, si->tty, sizeof(port->tty));

/* The database defaults to the user name. */
-
if (port->database[0] == '\0')
! StrNCpy(port->database, si->user, sizeof(port->database));

/*
* Truncate given database and user names to length of a Postgres
! * name.
*/
- /* This avoids lookup failures when overlength names are given. */
-
if ((int) sizeof(port->database) >= NAMEDATALEN)
port->database[NAMEDATALEN - 1] = '\0';
if ((int) sizeof(port->user) >= NAMEDATALEN)
port->user[NAMEDATALEN - 1] = '\0';

/* Check a user name was given. */
-
if (port->user[0] == '\0')
! {
! PacketSendError(&port->pktInfo,
! "No Postgres username specified in startup packet.");
! return STATUS_OK; /* don't close the connection yet */
! }

/*
* If we're going to reject the connection due to database state, say
* so now instead of wasting cycles on an authentication exchange.
* (This also allows a pg_ping utility to be written.)
*/
rejectMsg = canAcceptConnections();

if (rejectMsg != NULL)
! {
! PacketSendError(&port->pktInfo, rejectMsg);
! 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;
--- 1031,1088 ----
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)))
! elog(FATAL, "unsupported frontend protocol");

/*
* Get the parameters from the startup packet as C strings. The
* packet destination was cleared first so a short packet has zeros
* silently added and a long packet is silently truncated.
*/
! StrNCpy(port->database, packet->database, sizeof(port->database));
! StrNCpy(port->user, packet->user, sizeof(port->user));
! StrNCpy(port->options, packet->options, sizeof(port->options));
! StrNCpy(port->tty, packet->tty, sizeof(port->tty));

/* The database defaults to the user name. */
if (port->database[0] == '\0')
! StrNCpy(port->database, packet->user, sizeof(port->database));

/*
* Truncate given database and user names to length of a Postgres
! * name. This avoids lookup failures when overlength names are
! * given.
*/
if ((int) sizeof(port->database) >= NAMEDATALEN)
port->database[NAMEDATALEN - 1] = '\0';
if ((int) sizeof(port->user) >= NAMEDATALEN)
port->user[NAMEDATALEN - 1] = '\0';

/* Check a user name was given. */
if (port->user[0] == '\0')
! elog(FATAL, "no PostgreSQL user name specified in startup packet");

/*
* If we're going to reject the connection due to database state, say
* so now instead of wasting cycles on an authentication exchange.
* (This also allows a pg_ping utility to be written.)
*/
+ /* FIXME: way too late now to detect shutdowns */
rejectMsg = canAcceptConnections();

if (rejectMsg != NULL)
! elog(FATAL, "%s", rejectMsg);

! return STATUS_OK;
}

+
/*
* The client has sent a cancel request packet, not a normal
! * start-a-new-connection packet. Perform the necessary processing.
! * Nothing is sent back to the client.
*/
! static void
! processCancelRequest(Port *port, void *pkt)
{
CancelRequestPacket *canc = (CancelRequestPacket *) pkt;
int backendPID;
***************
*** 1230,1236 ****
{
if (DebugLvl)
postmaster_error("processCancelRequest: CheckPointPID in cancel request for process %d", backendPID);
! return STATUS_ERROR;
}

/* See if we have a matching backend */
--- 1097,1103 ----
{
if (DebugLvl)
postmaster_error("processCancelRequest: CheckPointPID in cancel request for process %d", backendPID);
! return;
}

/* See if we have a matching backend */
***************
*** 1244,1267 ****
{
/* Found a match; signal that backend to cancel current op */
if (DebugLvl)
! postmaster_error("processCancelRequest: sending SIGINT to process %d", bp->pid);
kill(bp->pid, SIGINT);
}
else
{
/* Right PID, wrong key: no way, Jose */
if (DebugLvl)
! postmaster_error("processCancelRequest: bad key in cancel request for process %d", bp->pid);
}
! return STATUS_ERROR;
}
}

/* No matching backend */
if (DebugLvl)
! postmaster_error("processCancelRequest: bad PID in cancel request for process %d", backendPID);
!
! return STATUS_ERROR;
}

/*
--- 1111,1134 ----
{
/* Found a match; signal that backend to cancel current op */
if (DebugLvl)
! elog(DEBUG, "processing cancel request: sending SIGINT to process %d",
! backendPID);
kill(bp->pid, SIGINT);
}
else
{
/* Right PID, wrong key: no way, Jose */
if (DebugLvl)
! elog(DEBUG, "bad key in cancel request for process %d",
! backendPID);
}
! return;
}
}

/* No matching backend */
if (DebugLvl)
! elog(DEBUG, "bad pid in cancel request for process %d", backendPID);
}

/*
***************
*** 1295,1300 ****
--- 1162,1168 ----
return NULL;
}

+
/*
* ConnCreate -- create a local connection data structure
*/
***************
*** 1318,1324 ****
}
else
{
- DLAddHead(PortList, DLNewElem(port));
RandomSalt(port->salt);
port->pktInfo.state = Idle;
}
--- 1186,1191 ----
***************
*** 1326,1331 ****
--- 1193,1199 ----
return port;
}

+
/*
* ConnFree -- free a local connection data structure
*/
***************
*** 1339,1360 ****
free(conn);
}

/*
* ClosePostmasterPorts -- close all the postmaster's open sockets
*
* This is called during child process startup to release file descriptors
! * that are not needed by that child process. All descriptors other than
! * the one for myConn (if it's not null) are closed.
*
* Note that closing the child's descriptor does not destroy the client
* connection prematurely, since the parent (postmaster) process still
* has the socket open.
*/
static void
! ClosePostmasterPorts(Port *myConn)
{
- Dlelem *curr;
-
/* Close the listen sockets */
if (NetServer)
StreamClose(ServerSock_INET);
--- 1207,1226 ----
free(conn);
}

+
/*
* ClosePostmasterPorts -- close all the postmaster's open sockets
*
* This is called during child process startup to release file descriptors
! * that are not needed by that child process.
*
* Note that closing the child's descriptor does not destroy the client
* connection prematurely, since the parent (postmaster) process still
* has the socket open.
*/
static void
! ClosePostmasterPorts(void)
{
/* Close the listen sockets */
if (NetServer)
StreamClose(ServerSock_INET);
***************
*** 1363,1387 ****
StreamClose(ServerSock_UNIX);
ServerSock_UNIX = INVALID_SOCK;
#endif
-
- /* Close any sockets for other clients, and release memory too */
- curr = DLGetHead(PortList);
-
- while (curr)
- {
- Port *port = (Port *) DLE_VAL(curr);
- Dlelem *next = DLGetSucc(curr);
-
- if (port != myConn)
- {
- StreamClose(port->sock);
- DLRemove(curr);
- ConnFree(port);
- DLFreeElem(curr);
- }
-
- curr = next;
- }
}

--- 1229,1234 ----
***************
*** 1847,1853 ****
BackendStartup(Port *port)
{
Backend *bn; /* for backend cleanup */
! int pid;

/*
* Compute the cancel key that will be assigned to this backend. The
--- 1694,1700 ----
BackendStartup(Port *port)
{
Backend *bn; /* for backend cleanup */
! pid_t pid;

/*
* Compute the cancel key that will be assigned to this backend. The
***************
*** 1871,1913 ****
/* Specific beos actions before backend startup */
beos_before_backend_startup();
#endif

! if ((pid = fork()) == 0)
! { /* child */
#ifdef __BEOS__
/* Specific beos backend startup actions */
beos_backend_startup();
#endif
-
- #ifdef CYR_RECODE
- {
- /* Save charset for this host while we still have client addr */
- char ChTable[80];
- static char cyrEnvironment[100];
-
- GetCharSetByHost(ChTable, port->raddr.in.sin_addr.s_addr, DataDir);
- if (*ChTable != '\0')
- {
- snprintf(cyrEnvironment, sizeof(cyrEnvironment),
- "PG_RECODETABLE=%s", ChTable);
- putenv(cyrEnvironment);
- }
- }
- #endif

! if (DoBackend(port))
{
fprintf(stderr, gettext("%s child[%d]: BackendStartup: backend startup failed\n"),
progname, (int) getpid());
! ExitPostmaster(1);
}
else
! ExitPostmaster(0);
}

! /* in parent */
if (pid < 0)
{
#ifdef __BEOS__
/* Specific beos backend startup actions */
beos_backend_startup_failed();
--- 1718,1763 ----
/* Specific beos actions before backend startup */
beos_before_backend_startup();
#endif
+
+ /*
+ * Make room for backend data structure. Better before the fork()
+ * so we can handle failure cleanly.
+ */
+ bn = (Backend *) malloc(sizeof(Backend));
+ if (!bn)
+ {
+ fprintf(stderr, gettext("%s: BackendStartup: malloc failed\n"),
+ progname);
+ return STATUS_ERROR;
+ }

! pid = fork();
!
! if (pid == 0) /* child */
! {
! int status;
!
! free(bn);
#ifdef __BEOS__
/* Specific beos backend startup actions */
beos_backend_startup();
#endif

! status = DoBackend(port);
! if (status != 0)
{
fprintf(stderr, gettext("%s child[%d]: BackendStartup: backend startup failed\n"),
progname, (int) getpid());
! proc_exit(status);
}
else
! proc_exit(0);
}

! /* in parent, error */
if (pid < 0)
{
+ free(bn);
#ifdef __BEOS__
/* Specific beos backend startup actions */
beos_backend_startup_failed();
***************
*** 1917,1924 ****
return STATUS_ERROR;
}

! if (DebugLvl)
! fprintf(stderr, gettext("%s: BackendStartup: pid %d user %s db %s socket %d\n"),
progname, pid, port->user, port->database,
port->sock);

--- 1767,1775 ----
return STATUS_ERROR;
}

! /* in parent, normal */
! if (DebugLvl >= 1)
! fprintf(stderr, gettext("%s: BackendStartup: pid=%d user=%s db=%s socket=%d\n"),
progname, pid, port->user, port->database,
port->sock);

***************
*** 1926,1938 ****
* Everything's been successful, it's safe to add this backend to our
* list of backends.
*/
- if (!(bn = (Backend *) calloc(1, sizeof(Backend))))
- {
- fprintf(stderr, gettext("%s: BackendStartup: malloc failed\n"),
- progname);
- ExitPostmaster(1);
- }
-
bn->pid = pid;
bn->cancel_key = MyCancelKey;
DLAddHead(BackendList, DLNewElem(bn));
--- 1777,1782 ----
***************
*** 1940,1945 ****
--- 1784,1790 ----
return STATUS_OK;
}

+
/*
* split_opts -- split a string of options and append it to an argv array
*
***************
*** 1990,1995 ****
--- 1835,1841 ----
char optbuf[ARGV_SIZE];
char ttybuf[ARGV_SIZE];
int i;
+ int status;
struct timeval now;
struct timezone tz;

***************
*** 2010,2017 ****
/* Reset MyProcPid to new backend's pid */
MyProcPid = getpid();

/* Close the postmaster's other sockets */
! ClosePostmasterPorts(port);

/*
* Don't want backend to be able to see the postmaster random number
--- 1856,1869 ----
/* Reset MyProcPid to new backend's pid */
MyProcPid = getpid();

+ whereToSendOutput = Remote;
+
+ status = ProcessStartupPacket(port);
+ if (status == 127)
+ return 0; /* cancel request processed */
+
/* Close the postmaster's other sockets */
! ClosePostmasterPorts();

/*
* Don't want backend to be able to see the postmaster random number
***************
*** 2162,2187 ****
errno = save_errno;
}

- static void
- dumpstatus(SIGNAL_ARGS)
- {
- int save_errno = errno;
- Dlelem *curr;
-
- PG_SETMASK(&BlockSig);
-
- fprintf(stderr, "%s: dumpstatus:\n", progname);
-
- curr = DLGetHead(PortList);
- while (curr)
- {
- Port *port = DLE_VAL(curr);
-
- fprintf(stderr, "\tsock %d\n", port->sock);
- curr = DLGetSucc(curr);
- }
- errno = save_errno;
- }

/*
* CharRemap
--- 2014,2019 ----
***************
*** 2336,2342 ****
on_exit_reset();

/* Close the postmaster's sockets */
! ClosePostmasterPorts(NULL);

/* Set up command-line arguments for subprocess */
av[ac++] = "postgres";
--- 2168,2174 ----
on_exit_reset();

/* Close the postmaster's sockets */
! ClosePostmasterPorts();

/* Set up command-line arguments for subprocess */
av[ac++] = "postgres";
***************
*** 2463,2469 ****

fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
! fprintf(stderr, gettext(fmt), ap);
va_end(ap);
fprintf(stderr, "\n");
}
--- 2295,2301 ----

fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
! vfprintf(stderr, gettext(fmt), ap);
va_end(ap);
fprintf(stderr, "\n");
}
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.220
diff -c -r1.220 postgres.c
*** src/backend/tcop/postgres.c 2001/06/12 22:54:06 1.220
--- src/backend/tcop/postgres.c 2001/06/18 23:06:38
***************
*** 45,50 ****
--- 45,51 ----
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
+ #include "libpq/auth.h"
#include "nodes/print.h"
#include "optimizer/cost.h"
#include "optimizer/planner.h"
***************
*** 1148,1153 ****
--- 1149,1157 ----

SetProcessingMode(InitProcessing);

+ if (IsUnderPostmaster)
+ ClientAuthentication(MyProcPort); /* might not return */
+
/*
* Set default values for command-line options.
*/
***************
*** 1567,1579 ****
* restart... */
}
pq_init(); /* initialize libpq at backend startup */
- whereToSendOutput = Remote;
BaseInit();
}
else
{
/* interactive case: database name can be last arg on command line */
- whereToSendOutput = Debug;
if (errs || argc - optind > 1)
{
fprintf(stderr, "%s: invalid command line arguments\nTry -? for help.\n", argv[0]);
--- 1571,1581 ----
Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/error/elog.c,v
retrieving revision 1.86
diff -c -r1.86 elog.c
*** src/backend/utils/error/elog.c 2001/06/08 21:16:48 1.86
--- src/backend/utils/error/elog.c 2001/06/18 23:06:39
***************
*** 129,138 ****
/* size of the prefix needed for timestamp and pid, if enabled */
size_t timestamp_size;

- /* ignore debug msgs if noplace to send */
- if (lev == DEBUG && Debugfile < 0)
- return;
-
/* Save error str before calling any function that might change errno */
errorstr = useful_strerror(errno);

--- 129,134 ----
***************
*** 336,345 ****
/* syslog doesn't want a trailing newline, but other destinations do */
strcat(msg_buf, "\n");

! /* Write to debug file, if open and enabled */
! /* NOTE: debug file is typically pointed at stderr */
! if (Debugfile >= 0 && Use_syslog <= 1)
! write(Debugfile, msg_buf, strlen(msg_buf));

if (lev > DEBUG && whereToSendOutput == Remote)
{
--- 332,340 ----
/* syslog doesn't want a trailing newline, but other destinations do */
strcat(msg_buf, "\n");

! /* write to terminal */
! if (Use_syslog <= 1 || whereToSendOutput == Debug)
! write(2, msg_buf, strlen(msg_buf));

if (lev > DEBUG && whereToSendOutput == Remote)
{
***************
*** 369,385 ****
}

MemoryContextSwitchTo(oldcxt);
- }
-
- if (lev > DEBUG && whereToSendOutput != Remote)
- {
- /*
- * We are running as an interactive backend, so just send the
- * message to stderr. But don't send a duplicate if Debugfile
- * write, above, already sent to stderr.
- */
- if (Debugfile != fileno(stderr))
- fputs(msg_buf, stderr);
}

/* done with the message, release space */
--- 364,369 ----
Index: src/backend/utils/init/miscinit.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/init/miscinit.c,v
retrieving revision 1.71
diff -c -r1.71 miscinit.c
*** src/backend/utils/init/miscinit.c 2001/06/14 01:09:22 1.71
--- src/backend/utils/init/miscinit.c 2001/06/18 23:06:40
***************
*** 279,284 ****
--- 279,285 ----
int i;
unsigned char FromChar,
ToChar;
+ char ChTable[80];

for (i = 0; i < 128; i++)
{
***************
*** 286,296 ****
RecodeBackTable[i] = i + 128;
}

! p = getenv("PG_RECODETABLE");
if (p && *p != '\0')
{
! map_file = (char *) malloc((strlen(DataDir) +
! strlen(p) + 2) * sizeof(char));
if (! map_file)
elog(FATAL, "out of memory");
sprintf(map_file, "%s/%s", DataDir, p);
--- 287,303 ----
RecodeBackTable[i] = i + 128;
}

! if (IsUnderPostmaster)
! {
! GetCharSetByHost(ChTable, MyProcPort->raddr.in.sin_addr.s_addr, DataDir);
! p = ChTable;
! }
! else
! p = getenv("PG_RECODETABLE");
!
if (p && *p != '\0')
{
! map_file = malloc(strlen(DataDir) + strlen(p) + 2));
if (! map_file)
elog(FATAL, "out of memory");
sprintf(map_file, "%s/%s", DataDir, p);
Index: src/include/libpq/auth.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/auth.h,v
retrieving revision 1.16
diff -c -r1.16 auth.h
*** src/include/libpq/auth.h 2001/03/22 04:00:47 1.16
--- src/include/libpq/auth.h 2001/06/18 23:06:41
***************
*** 21,27 ****
*----------------------------------------------------------------
*/

! void be_recvauth(Port *port);

#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
#define PG_KRB5_VERSION "PGVER5.1"
--- 21,27 ----
*----------------------------------------------------------------
*/

! void ClientAuthentication(Port *port);

#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
#define PG_KRB5_VERSION "PGVER5.1"

--
Peter Eisentraut peter_e(at)gmx(dot)net http://funkturm.homeip.net/~peter

Browse pgsql-patches by date

  From Date Subject
Next Message Peter Eisentraut 2001-06-18 23:23:14 Re: use GUC for cmdline
Previous Message Bruce Momjian 2001-06-18 21:38:19 Re: [PATCHES] Cygwin contrib patch