summaryrefslogtreecommitdiff
path: root/src/backend/libpq/pqcomm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/pqcomm.c')
-rw-r--r--src/backend/libpq/pqcomm.c778
1 files changed, 0 insertions, 778 deletions
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
deleted file mode 100644
index dd9c6e75d21..00000000000
--- a/src/backend/libpq/pqcomm.c
+++ /dev/null
@@ -1,778 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * pqcomm.c
- * Communication functions between the Frontend and the Backend
- *
- * These routines handle the low-level details of communication between
- * frontend and backend. They just shove data across the communication
- * channel, and are ignorant of the semantics of the data --- or would be,
- * except for major brain damage in the design of the COPY OUT protocol.
- * Unfortunately, COPY OUT is designed to commandeer the communication
- * channel (it just transfers data without wrapping it into messages).
- * No other messages can be sent while COPY OUT is in progress; and if the
- * copy is aborted by an elog(ERROR), we need to close out the copy so that
- * the frontend gets back into sync. Therefore, these routines have to be
- * aware of COPY OUT state.
- *
- * NOTE: generally, it's a bad idea to emit outgoing messages directly with
- * pq_putbytes(), especially if the message would require multiple calls
- * to send. Instead, use the routines in pqformat.c to construct the message
- * in a buffer and then emit it in one call to pq_putmessage. This helps
- * ensure that the channel will not be clogged by an incomplete message
- * if execution is aborted by elog(ERROR) partway through the message.
- * The only non-libpq code that should call pq_putbytes directly is COPY OUT.
- *
- * At one time, libpq was shared between frontend and backend, but now
- * the backend's "backend/libpq" is quite separate from "interfaces/libpq".
- * All that remains is similarities of names to trap the unwary...
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: pqcomm.c,v 1.137 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-/*------------------------
- * INTERFACE ROUTINES
- *
- * setup/teardown:
- * StreamServerPort - Open postmaster's server port
- * StreamConnection - Create new connection with client
- * StreamClose - Close a client/backend connection
- * pq_init - initialize libpq at backend startup
- * pq_close - shutdown libpq at backend exit
- *
- * low-level I/O:
- * pq_getbytes - get a known number of bytes from connection
- * pq_getstring - get a null terminated string from connection
- * pq_getbyte - get next byte from connection
- * pq_peekbyte - peek at next byte from connection
- * pq_putbytes - send bytes to connection (not flushed until pq_flush)
- * pq_flush - flush pending output
- *
- * message-level I/O (and COPY OUT cruft):
- * pq_putmessage - send a normal message (suppressed in COPY OUT mode)
- * pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
- * pq_endcopyout - end a COPY OUT transfer
- *
- *------------------------
- */
-#include "postgres.h"
-
-#include <signal.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <arpa/inet.h>
-#include <sys/file.h>
-
-#include "libpq/libpq.h"
-#include "miscadmin.h"
-#include "storage/ipc.h"
-
-extern void secure_close(Port *);
-extern ssize_t secure_read(Port *, void *, size_t);
-extern ssize_t secure_write(Port *, const void *, size_t);
-
-static void pq_close(void);
-
-
-/*
- * Configuration options
- */
-int Unix_socket_permissions;
-char *Unix_socket_group;
-
-
-/*
- * Buffers for low-level I/O
- */
-
-#define PQ_BUFFER_SIZE 8192
-
-static unsigned char PqSendBuffer[PQ_BUFFER_SIZE];
-static int PqSendPointer; /* Next index to store a byte in
- * PqSendBuffer */
-
-static unsigned char PqRecvBuffer[PQ_BUFFER_SIZE];
-static int PqRecvPointer; /* Next index to read a byte from
- * PqRecvBuffer */
-static int PqRecvLength; /* End of data available in PqRecvBuffer */
-
-/*
- * Message status
- */
-static bool DoingCopyOut;
-
-
-/* --------------------------------
- * pq_init - initialize libpq at backend startup
- * --------------------------------
- */
-void
-pq_init(void)
-{
- PqSendPointer = PqRecvPointer = PqRecvLength = 0;
- DoingCopyOut = false;
- on_proc_exit(pq_close, 0);
-}
-
-
-/* --------------------------------
- * pq_close - shutdown libpq at backend exit
- *
- * Note: in a standalone backend MyProcPort will be null,
- * don't crash during exit...
- * --------------------------------
- */
-static void
-pq_close(void)
-{
- if (MyProcPort != NULL)
- {
- secure_close(MyProcPort);
- close(MyProcPort->sock);
- /* make sure any subsequent attempts to do I/O fail cleanly */
- MyProcPort->sock = -1;
- }
-}
-
-
-
-/*
- * Streams -- wrapper around Unix socket system calls
- *
- *
- * Stream functions are used for vanilla TCP connection protocol.
- */
-
-static char sock_path[MAXPGPATH];
-
-
-/* StreamDoUnlink()
- * Shutdown routine for backend connection
- * If a Unix socket is used for communication, explicitly close it.
- */
-static void
-StreamDoUnlink(void)
-{
- Assert(sock_path[0]);
- unlink(sock_path);
-}
-
-/*
- * StreamServerPort -- open a sock stream "listening" port.
- *
- * This initializes the Postmaster's connection-accepting port *fdP.
- *
- * RETURNS: STATUS_OK or STATUS_ERROR
- */
-
-int
-StreamServerPort(int family, char *hostName, unsigned short portNumber,
- char *unixSocketName, int *fdP)
-{
- SockAddr saddr;
- int fd,
- err;
- int maxconn;
- size_t len = 0;
- int one = 1;
-
- Assert(family == AF_INET || family == AF_UNIX);
-
- if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
- {
- elog(LOG, "StreamServerPort: socket() failed: %m");
- return STATUS_ERROR;
- }
-
- if (family == AF_INET)
- {
- if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
- sizeof(one))) == -1)
- {
- elog(LOG, "StreamServerPort: setsockopt(SO_REUSEADDR) failed: %m");
- return STATUS_ERROR;
- }
- }
-
- MemSet((char *) &saddr, 0, sizeof(saddr));
- saddr.sa.sa_family = family;
-
-#ifdef HAVE_UNIX_SOCKETS
- if (family == AF_UNIX)
- {
- UNIXSOCK_PATH(saddr.un, portNumber, unixSocketName);
- len = UNIXSOCK_LEN(saddr.un);
- strcpy(sock_path, saddr.un.sun_path);
-
- /*
- * Grab an interlock file associated with the socket file.
- */
- if (!CreateSocketLockFile(sock_path, true))
- return STATUS_ERROR;
-
- /*
- * Once we have the interlock, we can safely delete any
- * pre-existing socket file to avoid failure at bind() time.
- */
- unlink(sock_path);
- }
-#endif /* HAVE_UNIX_SOCKETS */
-
- if (family == AF_INET)
- {
- /* TCP/IP socket */
- if (hostName[0] == '\0')
- saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
- else
- {
- struct hostent *hp;
-
- hp = gethostbyname(hostName);
- if ((hp == NULL) || (hp->h_addrtype != AF_INET))
- {
- elog(LOG, "StreamServerPort: gethostbyname(%s) failed",
- hostName);
- return STATUS_ERROR;
- }
- memmove((char *) &(saddr.in.sin_addr), (char *) hp->h_addr,
- hp->h_length);
- }
-
- saddr.in.sin_port = htons(portNumber);
- len = sizeof(struct sockaddr_in);
- }
-
- err = bind(fd, (struct sockaddr *) & saddr.sa, len);
- if (err < 0)
- {
- if (family == AF_UNIX)
- elog(LOG, "StreamServerPort: bind() failed: %m\n"
- "\tIs another postmaster already running on port %d?\n"
- "\tIf not, remove socket node (%s) and retry.",
- (int) portNumber, sock_path);
- else
- elog(LOG, "StreamServerPort: bind() failed: %m\n"
- "\tIs another postmaster already running on port %d?\n"
- "\tIf not, wait a few seconds and retry.",
- (int) portNumber);
- return STATUS_ERROR;
- }
-
-#ifdef HAVE_UNIX_SOCKETS
- if (family == AF_UNIX)
- {
- /* Arrange to unlink the socket file at exit */
- on_proc_exit(StreamDoUnlink, 0);
-
- /*
- * Fix socket ownership/permission if requested. Note we must do
- * this before we listen() to avoid a window where unwanted
- * connections could get accepted.
- */
- Assert(Unix_socket_group);
- if (Unix_socket_group[0] != '\0')
- {
- char *endptr;
- unsigned long int val;
- gid_t gid;
-
- val = strtoul(Unix_socket_group, &endptr, 10);
- if (*endptr == '\0')
- {
- /* numeric group id */
- gid = val;
- }
- else
- {
- /* convert group name to id */
- struct group *gr;
-
- gr = getgrnam(Unix_socket_group);
- if (!gr)
- {
- elog(LOG, "No such group as '%s'",
- Unix_socket_group);
- return STATUS_ERROR;
- }
- gid = gr->gr_gid;
- }
- if (chown(sock_path, -1, gid) == -1)
- {
- elog(LOG, "Could not set group of %s: %m",
- sock_path);
- return STATUS_ERROR;
- }
- }
-
- if (chmod(sock_path, Unix_socket_permissions) == -1)
- {
- elog(LOG, "Could not set permissions on %s: %m",
- sock_path);
- return STATUS_ERROR;
- }
- }
-#endif /* HAVE_UNIX_SOCKETS */
-
- /*
- * Select appropriate accept-queue length limit. PG_SOMAXCONN is only
- * intended to provide a clamp on the request on platforms where an
- * overly large request provokes a kernel error (are there any?).
- */
- maxconn = MaxBackends * 2;
- if (maxconn > PG_SOMAXCONN)
- maxconn = PG_SOMAXCONN;
-
- err = listen(fd, maxconn);
- if (err < 0)
- {
- elog(LOG, "StreamServerPort: listen() failed: %m");
- return STATUS_ERROR;
- }
-
- *fdP = fd;
-
- return STATUS_OK;
-}
-
-/*
- * StreamConnection -- create a new connection with client using
- * server port.
- *
- * ASSUME: that this doesn't need to be non-blocking because
- * the Postmaster uses select() to tell when the server master
- * socket is ready for accept().
- *
- * RETURNS: STATUS_OK or STATUS_ERROR
- */
-int
-StreamConnection(int server_fd, Port *port)
-{
- ACCEPT_TYPE_ARG3 addrlen;
-
- /* accept connection (and fill in the client (remote) address) */
- addrlen = sizeof(port->raddr);
- if ((port->sock = accept(server_fd,
- (struct sockaddr *) & port->raddr,
- &addrlen)) < 0)
- {
- elog(LOG, "StreamConnection: accept() failed: %m");
- return STATUS_ERROR;
- }
-
-#ifdef SCO_ACCEPT_BUG
-
- /*
- * UnixWare 7+ and OpenServer 5.0.4 are known to have this bug, but it
- * shouldn't hurt to catch it for all versions of those platforms.
- */
- if (port->raddr.sa.sa_family == 0)
- port->raddr.sa.sa_family = AF_UNIX;
-#endif
-
- /* fill in the server (local) address */
- addrlen = sizeof(port->laddr);
- if (getsockname(port->sock, (struct sockaddr *) & port->laddr,
- &addrlen) < 0)
- {
- elog(LOG, "StreamConnection: getsockname() failed: %m");
- return STATUS_ERROR;
- }
-
- /* select NODELAY and KEEPALIVE options if it's a TCP connection */
- if (port->laddr.sa.sa_family == AF_INET)
- {
- int on = 1;
-
- if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY,
- (char *) &on, sizeof(on)) < 0)
- {
- elog(LOG, "StreamConnection: setsockopt(TCP_NODELAY) failed: %m");
- return STATUS_ERROR;
- }
- if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
- (char *) &on, sizeof(on)) < 0)
- {
- elog(LOG, "StreamConnection: setsockopt(SO_KEEPALIVE) failed: %m");
- return STATUS_ERROR;
- }
- }
-
- return STATUS_OK;
-}
-
-/*
- * StreamClose -- close a client/backend connection
- */
-void
-StreamClose(int sock)
-{
- close(sock);
-}
-
-
-/* --------------------------------
- * Low-level I/O routines begin here.
- *
- * These routines communicate with a frontend client across a connection
- * already established by the preceding routines.
- * --------------------------------
- */
-
-
-/* --------------------------------
- * pq_recvbuf - load some bytes into the input buffer
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-static int
-pq_recvbuf(void)
-{
- if (PqRecvPointer > 0)
- {
- if (PqRecvLength > PqRecvPointer)
- {
- /* still some unread data, left-justify it in the buffer */
- memmove(PqRecvBuffer, PqRecvBuffer + PqRecvPointer,
- PqRecvLength - PqRecvPointer);
- PqRecvLength -= PqRecvPointer;
- PqRecvPointer = 0;
- }
- else
- PqRecvLength = PqRecvPointer = 0;
- }
-
- /* Can fill buffer from PqRecvLength and upwards */
- for (;;)
- {
- int r;
-
- r = secure_read(MyProcPort, PqRecvBuffer + PqRecvLength,
- PQ_BUFFER_SIZE - PqRecvLength);
-
- if (r < 0)
- {
- if (errno == EINTR)
- continue; /* Ok if interrupted */
-
- /*
- * Careful: an elog() that tries to write to the client
- * would cause recursion to here, leading to stack overflow
- * and core dump! This message must go *only* to the postmaster
- * log.
- */
- elog(COMMERROR, "pq_recvbuf: recv() failed: %m");
- return EOF;
- }
- if (r == 0)
- {
- /* as above, only write to postmaster log */
- elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
- return EOF;
- }
- /* r contains number of bytes read, so just incr length */
- PqRecvLength += r;
- return 0;
- }
-}
-
-/* --------------------------------
- * pq_getbyte - get a single byte from connection, or return EOF
- * --------------------------------
- */
-int
-pq_getbyte(void)
-{
- while (PqRecvPointer >= PqRecvLength)
- {
- if (pq_recvbuf()) /* If nothing in buffer, then recv some */
- return EOF; /* Failed to recv data */
- }
- return PqRecvBuffer[PqRecvPointer++];
-}
-
-/* --------------------------------
- * pq_peekbyte - peek at next byte from connection
- *
- * Same as pq_getbyte() except we don't advance the pointer.
- * --------------------------------
- */
-int
-pq_peekbyte(void)
-{
- while (PqRecvPointer >= PqRecvLength)
- {
- if (pq_recvbuf()) /* If nothing in buffer, then recv some */
- return EOF; /* Failed to recv data */
- }
- return PqRecvBuffer[PqRecvPointer];
-}
-
-/* --------------------------------
- * pq_getbytes - get a known number of bytes from connection
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_getbytes(char *s, size_t len)
-{
- size_t amount;
-
- while (len > 0)
- {
- while (PqRecvPointer >= PqRecvLength)
- {
- if (pq_recvbuf()) /* If nothing in buffer, then recv some */
- return EOF; /* Failed to recv data */
- }
- amount = PqRecvLength - PqRecvPointer;
- if (amount > len)
- amount = len;
- memcpy(s, PqRecvBuffer + PqRecvPointer, amount);
- PqRecvPointer += amount;
- s += amount;
- len -= amount;
- }
- return 0;
-}
-
-/* --------------------------------
- * pq_getstring - get a null terminated string from connection
- *
- * The return value is placed in an expansible StringInfo.
- * Note that space allocation comes from the current memory context!
- *
- * NOTE: this routine does not do any MULTIBYTE conversion,
- * even though it is presumably useful only for text, because
- * no code in this module should depend on MULTIBYTE mode.
- * See pq_getstr in pqformat.c for that.
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_getstring(StringInfo s)
-{
- int i;
-
- /* Reset string to empty */
- s->len = 0;
- s->data[0] = '\0';
-
- /* Read until we get the terminating '\0' */
- for(;;)
- {
- while (PqRecvPointer >= PqRecvLength)
- {
- if (pq_recvbuf()) /* If nothing in buffer, then recv some */
- return EOF; /* Failed to recv data */
- }
-
- for (i = PqRecvPointer; i < PqRecvLength; i++)
- {
- if (PqRecvBuffer[i] == '\0')
- {
- /* does not copy the \0 */
- appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
- i - PqRecvPointer);
- PqRecvPointer = i + 1; /* advance past \0 */
- return 0;
- }
- }
-
- /* If we're here we haven't got the \0 in the buffer yet. */
-
- appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
- PqRecvLength - PqRecvPointer);
- PqRecvPointer = PqRecvLength;
- }
-}
-
-
-/* --------------------------------
- * pq_putbytes - send bytes to connection (not flushed until pq_flush)
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_putbytes(const char *s, size_t len)
-{
- size_t amount;
-
- while (len > 0)
- {
- if (PqSendPointer >= PQ_BUFFER_SIZE)
- if (pq_flush()) /* If buffer is full, then flush it out */
- return EOF;
- amount = PQ_BUFFER_SIZE - PqSendPointer;
- if (amount > len)
- amount = len;
- memcpy(PqSendBuffer + PqSendPointer, s, amount);
- PqSendPointer += amount;
- s += amount;
- len -= amount;
- }
- return 0;
-}
-
-/* --------------------------------
- * pq_flush - flush pending output
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_flush(void)
-{
- static int last_reported_send_errno = 0;
-
- unsigned char *bufptr = PqSendBuffer;
- unsigned char *bufend = PqSendBuffer + PqSendPointer;
-
- while (bufptr < bufend)
- {
- int r;
-
- r = secure_write(MyProcPort, bufptr, bufend - bufptr);
-
- if (r <= 0)
- {
- if (errno == EINTR)
- continue; /* Ok if we were interrupted */
-
- /*
- * Careful: an elog() that tries to write to the client
- * would cause recursion to here, leading to stack overflow
- * and core dump! This message must go *only* to the postmaster
- * log.
- *
- * If a client disconnects while we're in the midst of output,
- * we might write quite a bit of data before we get to a safe
- * query abort point. So, suppress duplicate log messages.
- */
- if (errno != last_reported_send_errno)
- {
- last_reported_send_errno = errno;
- elog(COMMERROR, "pq_flush: send() failed: %m");
- }
-
- /*
- * We drop the buffered data anyway so that processing can
- * continue, even though we'll probably quit soon.
- */
- PqSendPointer = 0;
- return EOF;
- }
-
- last_reported_send_errno = 0; /* reset after any successful send */
- bufptr += r;
- }
-
- PqSendPointer = 0;
- return 0;
-}
-
-
-/*
- * Return EOF if the connection has been broken, else 0.
- */
-int
-pq_eof(void)
-{
- char x;
- int res;
-
- res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
-
- if (res < 0)
- {
- /* can log to postmaster log only */
- elog(COMMERROR, "pq_eof: recv() failed: %m");
- return EOF;
- }
- if (res == 0)
- return EOF;
- else
- return 0;
-}
-
-
-/* --------------------------------
- * Message-level I/O routines begin here.
- *
- * These routines understand about COPY OUT protocol.
- * --------------------------------
- */
-
-
-/* --------------------------------
- * pq_putmessage - send a normal message (suppressed in COPY OUT mode)
- *
- * If msgtype is not '\0', it is a message type code to place before
- * the message body (len counts only the body size!).
- * If msgtype is '\0', then the buffer already includes the type code.
- *
- * All normal messages are suppressed while COPY OUT is in progress.
- * (In practice only a few messages might get emitted then; dropping
- * them is annoying, but at least they will still appear in the
- * postmaster log.)
- *
- * returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_putmessage(char msgtype, const char *s, size_t len)
-{
- if (DoingCopyOut)
- return 0;
- if (msgtype)
- if (pq_putbytes(&msgtype, 1))
- return EOF;
- return pq_putbytes(s, len);
-}
-
-/* --------------------------------
- * pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
- * --------------------------------
- */
-void
-pq_startcopyout(void)
-{
- DoingCopyOut = true;
-}
-
-/* --------------------------------
- * pq_endcopyout - end a COPY OUT transfer
- *
- * If errorAbort is indicated, we are aborting a COPY OUT due to an error,
- * and must send a terminator line. Since a partial data line might have
- * been emitted, send a couple of newlines first (the first one could
- * get absorbed by a backslash...)
- * --------------------------------
- */
-void
-pq_endcopyout(bool errorAbort)
-{
- if (!DoingCopyOut)
- return;
- if (errorAbort)
- pq_putbytes("\n\n\\.\n", 5);
- /* in non-error case, copy.c will have emitted the terminator line */
- DoingCopyOut = false;
-}