diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/libpq/pqcomm.c | |
download | postgresql-PG95-1_01.tar.gz postgresql-PG95-1_01.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/libpq/pqcomm.c')
-rw-r--r-- | src/backend/libpq/pqcomm.c | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c new file mode 100644 index 00000000000..7fc4a85f200 --- /dev/null +++ b/src/backend/libpq/pqcomm.c @@ -0,0 +1,724 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.c-- + * Communication functions between the Frontend and the Backend + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * pq_gettty - return the name of the tty in the given buffer + * pq_getport - return the PGPORT setting + * pq_close - close input / output connections + * pq_flush - flush pending output + * pq_getstr - get a null terminated string from connection + * pq_getnchar - get n characters from connection + * pq_getint - get an integer from connection + * pq_putstr - send a null terminated string to connection + * pq_putnchar - send n characters to connection + * pq_putint - send an integer to connection + * pq_getinaddr - initialize address from host and port number + * pq_getinserv - initialize address from host and service name + * pq_connect - create remote input / output connection + * pq_accept - accept remote input / output connection + * pq_async_notify - receive notification from backend. + * + * NOTES + * These functions are used by both frontend applications and + * the postgres backend. + * + */ +#include "libpq/pqsignal.h" /* substitute for <signal.h> */ +#include <stdio.h> +#include <string.h> +#ifndef WIN32 +#include <unistd.h> /* for ttyname() */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#else +#include <winsock.h> +#endif /* WIN32 */ +#include <errno.h> +#include <fcntl.h> + +#ifdef PORTNAME_linux +#ifndef SOMAXCONN +#define SOMAXCONN 5 /* from Linux listen(2) man page */ +#endif /* SOMAXCONN */ +#endif /* PORTNAME_linux */ + +#include "c.h" +#include "libpq/auth.h" +#include "libpq/libpq.h" /* where the declarations go */ +#include "libpq/pqcomm.h" +#include "utils/elog.h" + +/* ---------------- + * declarations + * ---------------- + */ +FILE *Pfout, *Pfin; +FILE *Pfdebug; /* debugging libpq */ +int PQAsyncNotifyWaiting; /* for async. notification */ + +/* -------------------------------- + * pq_init - open portal file descriptors + * -------------------------------- + */ +void +pq_init(int fd) +{ +#ifdef WIN32 + int in, out; + + in = _open_osfhandle(fd, _O_RDONLY); + out = _open_osfhandle(fd, _O_APPEND); + Pfin = fdopen(in, "rb"); + Pfout = fdopen(out, "wb"); +#else + Pfin = fdopen(fd, "r"); + Pfout = fdopen(dup(fd), "w"); +#endif /* WIN32 */ + if (!Pfin || !Pfout) + elog(FATAL, "pq_init: Couldn't initialize socket connection"); + PQnotifies_init(); + if (getenv("LIBPQ_DEBUG")) { + Pfdebug = stderr; + }else { + Pfdebug = NULL; + } +} + +/* ------------------------- + * pq_getc(File* fin) + * + * get a character from the input file, + * + * if Pfdebug is set, also echo the character fetched into Pfdebug + * + * used for debugging libpq + */ +static int +pq_getc(FILE* fin) +{ + int c; + + c = getc(fin); + if (Pfdebug && c != EOF) + putc(c,Pfdebug); + return c; +} + +/* -------------------------------- + * pq_gettty - return the name of the tty in the given buffer + * -------------------------------- + */ +void +pq_gettty(char *tp) +{ + (void) strncpy(tp, ttyname(0), 19); +} + +/* -------------------------------- + * pq_getport - return the PGPORT setting + * -------------------------------- + */ +int +pq_getport() +{ + char *envport = getenv("PGPORT"); + + if (envport) + return(atoi(envport)); + return(atoi(POSTPORT)); +} + +/* -------------------------------- + * pq_close - close input / output connections + * -------------------------------- + */ +void +pq_close() +{ + if (Pfin) { + fclose(Pfin); + Pfin = NULL; + } + if (Pfout) { + fclose(Pfout); + Pfout = NULL; + } + PQAsyncNotifyWaiting = 0; + PQnotifies_init(); + pq_unregoob(); +} + +/* -------------------------------- + * pq_flush - flush pending output + * -------------------------------- + */ +void +pq_flush() +{ + if (Pfout) + fflush(Pfout); +} + +/* -------------------------------- + * pq_getstr - get a null terminated string from connection + * -------------------------------- + */ +int +pq_getstr(char *s, int maxlen) +{ + int c; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "Input descriptor is null"); */ + return(EOF); + } + + while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c) + *s++ = c; + *s = '\0'; + + /* ----------------- + * If EOF reached let caller know. + * (This will only happen if we hit EOF before the string + * delimiter is reached.) + * ----------------- + */ + if (c == EOF) + return(EOF); + return(!EOF); +} + +/* + * USER FUNCTION - gets a newline-terminated string from the backend. + * + * Chiefly here so that applications can use "COPY <rel> to stdout" + * and read the output string. Returns a null-terminated string in s. + * + * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips + * the terminating \n (like gets(3)). + * + * RETURNS: + * EOF if it is detected or invalid arguments are given + * 0 if EOL is reached (i.e., \n has been read) + * (this is required for backward-compatibility -- this + * routine used to always return EOF or 0, assuming that + * the line ended within maxlen bytes.) + * 1 in other cases + */ +int +PQgetline(char *s, int maxlen) +{ + int c = '\0'; + + if (!Pfin || !s || maxlen <= 1) + return(EOF); + + for (; maxlen > 1 && (c = pq_getc(Pfin)) != '\n' && c != EOF; --maxlen) { + *s++ = c; + } + *s = '\0'; + + if (c == EOF) { + return(EOF); /* error -- reached EOF before \n */ + } else if (c == '\n') { + return(0); /* done with this line */ + } + return(1); /* returning a full buffer */ +} + +/* + * USER FUNCTION - sends a string to the backend. + * + * Chiefly here so that applications can use "COPY <rel> from stdin". + * + * RETURNS: + * 0 in all cases. + */ +int +PQputline(char *s) +{ + if (Pfout) { + (void) fputs(s, Pfout); + fflush(Pfout); + } + return(0); +} + +/* -------------------------------- + * pq_getnchar - get n characters from connection + * -------------------------------- + */ +int +pq_getnchar(char *s, int off, int maxlen) +{ + int c; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "Input descriptor is null"); */ + return(EOF); + } + + s += off; + while (maxlen-- && (c = pq_getc(Pfin)) != EOF) + *s++ = c; + + /* ----------------- + * If EOF reached let caller know + * ----------------- + */ + if (c == EOF) + return(EOF); + return(!EOF); +} + +/* -------------------------------- + * pq_getint - get an integer from connection + * we receive an integer a byte at a type and reconstruct it so that + * machines with different ENDIAN representations can talk to each + * other + * -------------------------------- + */ +int +pq_getint(int b) +{ + int n, c, p; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "pq_getint: Input descriptor is null"); */ + return(EOF); + } + + n = p = 0; + while (b-- && (c = pq_getc(Pfin)) != EOF && p < 32) { + n |= (c & 0xff) << p; + p += 8; + } + + return(n); +} + +/* -------------------------------- + * pq_putstr - send a null terminated string to connection + * -------------------------------- + */ +void +pq_putstr(char *s) +{ + int status; + + if (Pfout) { + status = fputs(s, Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putstr: fputs() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + status = fputc('\0', Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putstr: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } +} + +/* -------------------------------- + * pq_putnchar - send n characters to connection + * -------------------------------- + */ +void +pq_putnchar(char *s, int n) +{ + int status; + + if (Pfout) { + while (n--) { + status = fputc(*s++, Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putnchar: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } + } +} + +/* -------------------------------- + * pq_putint - send an integer to connection + * we chop an integer into bytes and send individual bytes + * machines with different ENDIAN representations can still talk to each + * other + * -------------------------------- + */ +void +pq_putint(int i, int b) +{ + int status; + + if (b > 4) + b = 4; + + if (Pfout) { + while (b--) { + status = fputc(i & 0xff, Pfout); + i >>= 8; + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putint: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } + } +} + +/* --- + * 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 *lenPtr) +{ + int fd = fileno(Pfout); + int len = 0; + + len = recv(fd,msgPtr+len,*lenPtr,MSG_OOB); + *lenPtr = len; + return(len); +} + +/* -------------------------------- + * pq_getinaddr - initialize address from host and port number + * -------------------------------- + */ +int +pq_getinaddr(struct sockaddr_in *sin, + char *host, + int port) +{ + struct hostent *hs; + + memset((char *) sin, 0, sizeof(*sin)); + + if (host) { + if (*host >= '0' && *host <= '9') + sin->sin_addr.s_addr = inet_addr(host); + else { + if (!(hs = gethostbyname(host))) { + perror(host); + return(1); + } + if (hs->h_addrtype != AF_INET) { + (void) sprintf(PQerrormsg, + "FATAL: pq_getinaddr: %s not on Internet\n", + host); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(1); + } + memmove((char *) &sin->sin_addr, + hs->h_addr, + hs->h_length); + } + } + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + return(0); +} + +/* -------------------------------- + * pq_getinserv - initialize address from host and servive name + * -------------------------------- + */ +int +pq_getinserv(struct sockaddr_in *sin, char *host, char *serv) +{ + struct servent *ss; + + if (*serv >= '0' && *serv <= '9') + return(pq_getinaddr(sin, host, atoi(serv))); + if (!(ss = getservbyname(serv, NULL))) { + (void) sprintf(PQerrormsg, + "FATAL: pq_getinserv: unknown service: %s\n", + serv); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(1); + } + 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)()) +{ +#ifdef WIN32 + /* Who knows what to do here? */ + return; +#else + int fd = fileno(Pfout); +#ifdef PORTNAME_hpux + ioctl(fd, FIOSSAIOOWN, getpid()); +#else /* PORTNAME_hpux */ + fcntl(fd, F_SETOWN, getpid()); +#endif /* PORTNAME_hpux */ + (void) signal(SIGURG,fptr); +#endif /* WIN32 */ +} + +void +pq_unregoob() +{ +#ifndef WIN32 + signal(SIGURG,SIG_DFL); +#endif /* WIN32 */ +} + + +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 + * + * + * Stream functions are used for vanilla TCP connection protocol. + */ + +/* + * StreamServerPort -- open a sock stream "listening" port. + * + * This initializes the Postmaster's connection + * accepting port. + * + * ASSUME: that this doesn't need to be non-blocking because + * the Postmaster uses select() to tell when the socket + * is ready. + * + * RETURNS: STATUS_OK or STATUS_ERROR + */ +int +StreamServerPort(char *hostName, short portName, int *fdP) +{ + struct sockaddr_in sin; + int fd; + +#ifdef WIN32 + /* This is necessary to make it possible for a backend to use + ** stdio to read from the socket. + */ + int optionvalue = SO_SYNCHRONOUS_NONALERT; + + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionvalue, + sizeof(optionvalue)); +#endif /* WIN32 */ + + if (! hostName) + hostName = "localhost"; + + memset((char *)&sin, 0, sizeof sin); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamServerPort: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(portName); + + if (bind(fd, (struct sockaddr *)&sin, sizeof sin) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamServerPort: bind() failed: errno=%d\n", + errno); + pqdebug("%s", PQerrormsg); + (void) strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n"); + (void) strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n"); + fputs(PQerrormsg, stderr); + return(STATUS_ERROR); + } + + listen(fd, SOMAXCONN); + + /* MS: I took this code from Dillon's version. It makes the + * listening port non-blocking. That is not necessary (and + * may tickle kernel bugs). + + (void) fcntl(fd, F_SETFD, 1); + (void) fcntl(fd, F_SETFL, FNDELAY); + */ + + *fdP = fd; + return(STATUS_OK); +} + +/* + * StreamConnection -- create a new connection with client using + * server port. + * + * This one should be non-blocking. + * + * RETURNS: STATUS_OK or STATUS_ERROR + */ +int +StreamConnection(int server_fd, Port *port) +{ + int addrlen; + + /* accept connection (and fill in the client (remote) address) */ + addrlen = sizeof(struct sockaddr_in); + if ((port->sock = accept(server_fd, + (struct sockaddr *) &port->raddr, + &addrlen)) < 0) { + elog(WARN, "postmaster: StreamConnection: accept: %m"); + return(STATUS_ERROR); + } + + /* fill in the server (local) address */ + addrlen = sizeof(struct sockaddr_in); + if (getsockname(port->sock, (struct sockaddr *) &port->laddr, + &addrlen) < 0) { + elog(WARN, "postmaster: StreamConnection: getsockname: %m"); + return(STATUS_ERROR); + } + + port->mask = 1 << port->sock; + +#ifndef WIN32 + /* reset to non-blocking */ + fcntl(port->sock, F_SETFL, 1); +#endif /* WIN32 */ + + return(STATUS_OK); +} + +/* + * StreamClose -- close a client/backend connection + */ +void +StreamClose(int sock) +{ + (void) close(sock); +} + +/* --------------------------- + * StreamOpen -- From client, initiate a connection with the + * server (Postmaster). + * + * RETURNS: STATUS_OK or STATUS_ERROR + * + * NOTE: connection is NOT established just because this + * routine exits. Local state is ok, but we haven't + * spoken to the postmaster yet. + * --------------------------- + */ +int +StreamOpen(char *hostName, short portName, Port *port) +{ + struct hostent *hp; + int laddrlen = sizeof(struct sockaddr_in); + extern int errno; + + if (!hostName) + hostName = "localhost"; + + /* set up the server (remote) address */ + if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: unknown hostname: %s\n", + hostName); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + memset((char *) &port->raddr, 0, sizeof(port->raddr)); + memmove((char *) &(port->raddr.sin_addr), + (char *) hp->h_addr, + hp->h_length); + port->raddr.sin_family = AF_INET; + port->raddr.sin_port = htons(portName); + + /* connect to the server */ + if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (connect(port->sock, (struct sockaddr *)&port->raddr, + sizeof(port->raddr)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: connect() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + /* fill in the client address */ + if (getsockname(port->sock, (struct sockaddr *) &port->laddr, + &laddrlen) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: getsockname() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + return(STATUS_OK); +} |