/* -------------------------------- * pq_set_nonblocking - set socket blocking/non-blocking * * Sets the socket non-blocking if nonblocking is TRUE, or sets it * blocking otherwise. * -------------------------------- */ static void pq_set_nonblocking(bool nonblocking) { if (MyProcPort->noblock == nonblocking) return; #ifdef WIN32 pgwin32_noblock = nonblocking ? 1 : 0; #else /* * Use COMMERROR on failure, because ERROR would try to send the error * to the client, which might require changing the mode again, leading * to infinite recursion. */ if (nonblocking) { if (!pg_set_noblock(MyProcPort->sock)) ereport(COMMERROR, (errmsg("could not set socket to non-blocking mode: %m"))); } else { if (!pg_set_block(MyProcPort->sock)) ereport(COMMERROR, (errmsg("could not set socket to blocking mode: %m"))); } #endif MyProcPort->noblock = nonblocking; }
/* -------------------------------- * pq_init - initialize libpq at backend startup * -------------------------------- */ void pq_init(void) { PqSendBufferSize = PQ_SEND_BUFFER_SIZE; PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize); PqSendPointer = PqSendStart = PqRecvPointer = PqRecvLength = 0; PqCommBusy = false; PqCommReadingMsg = false; DoingCopyOut = false; on_proc_exit(socket_close, 0); /* * In backends (as soon as forked) we operate the underlying socket in * nonblocking mode and use latches to implement blocking semantics if * needed. That allows us to provide safely interruptible reads and * writes. * * Use COMMERROR on failure, because ERROR would try to send the error to * the client, which might require changing the mode again, leading to * infinite recursion. */ #ifndef WIN32 if (!pg_set_noblock(MyProcPort->sock)) ereport(COMMERROR, (errmsg("could not set socket to nonblocking mode: %m"))); #endif }
/* -------------------------------- * pq_init - initialize libpq at backend startup * -------------------------------- */ void pq_init(void) { /* initialize state variables */ PqSendBufferSize = PQ_SEND_BUFFER_SIZE; PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize); PqSendPointer = PqSendStart = PqRecvPointer = PqRecvLength = 0; PqCommBusy = false; PqCommReadingMsg = false; DoingCopyOut = false; /* set up process-exit hook to close the socket */ on_proc_exit(socket_close, 0); /* * In backends (as soon as forked) we operate the underlying socket in * nonblocking mode and use latches to implement blocking semantics if * needed. That allows us to provide safely interruptible reads and * writes. * * Use COMMERROR on failure, because ERROR would try to send the error to * the client, which might require changing the mode again, leading to * infinite recursion. */ #ifndef WIN32 if (!pg_set_noblock(MyProcPort->sock)) ereport(COMMERROR, (errmsg("could not set socket to nonblocking mode: %m"))); #endif FeBeWaitSet = CreateWaitEventSet(TopMemoryContext, 3); AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE, MyProcPort->sock, NULL, NULL); AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, -1, MyLatch, NULL); AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, -1, NULL, NULL); }
/* * pg_krb5_sendauth -- client routine to send authentication information to * the server */ static int pg_krb5_sendauth(PGconn *conn) { krb5_error_code retval; int ret; krb5_principal server; krb5_auth_context auth_context = NULL; krb5_error *err_ret = NULL; struct krb5_info info; info.pg_krb5_initialised = 0; if (!(conn->pghost && conn->pghost[0] != '\0')) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } ret = pg_krb5_init(&conn->errorMessage, &info); if (ret != STATUS_OK) return ret; retval = krb5_sname_to_principal(info.pg_krb5_context, conn->pghost, conn->krbsrvname, KRB5_NT_SRV_HST, &server); if (retval) { printfPQExpBuffer(&conn->errorMessage, "pg_krb5_sendauth: krb5_sname_to_principal: %s\n", error_message(retval)); pg_krb5_destroy(&info); return STATUS_ERROR; } /* * libpq uses a non-blocking socket. But kerberos needs a blocking socket, * and we have to block somehow to do mutual authentication anyway. So we * temporarily make it blocking. */ if (!pg_set_block(conn->sock)) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); krb5_free_principal(info.pg_krb5_context, server); pg_krb5_destroy(&info); return STATUS_ERROR; } retval = krb5_sendauth(info.pg_krb5_context, &auth_context, (krb5_pointer) & conn->sock, (char *) conn->krbsrvname, info.pg_krb5_client, server, AP_OPTS_MUTUAL_REQUIRED, NULL, 0, /* no creds, use ccache instead */ info.pg_krb5_ccache, &err_ret, NULL, NULL); if (retval) { if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { #if defined(HAVE_KRB5_ERROR_TEXT_DATA) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->text.length, err_ret->text.data); #elif defined(HAVE_KRB5_ERROR_E_DATA) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->e_data->length, (const char *) err_ret->e_data->data); #else #error "bogus configuration" #endif } else { printfPQExpBuffer(&conn->errorMessage, "krb5_sendauth: %s\n", error_message(retval)); } if (err_ret) krb5_free_error(info.pg_krb5_context, err_ret); ret = STATUS_ERROR; } krb5_free_principal(info.pg_krb5_context, server); if (!pg_set_noblock(conn->sock)) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not restore non-blocking mode on socket: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); ret = STATUS_ERROR; } pg_krb5_destroy(&info); return ret; }
/* -------------------------------- * pq_getbyte_if_available - get a single byte from connection, * if available * * The received byte is stored in *c. Returns 1 if a byte was read, * 0 if no data was available, or EOF if trouble. * -------------------------------- */ int pq_getbyte_if_available(unsigned char *c) { int r; if (PqRecvPointer < PqRecvLength) { *c = PqRecvBuffer[PqRecvPointer++]; return 1; } /* Temporarily put the socket into non-blocking mode */ #ifdef WIN32 pgwin32_noblock = 1; #else if (!pg_set_noblock(MyProcPort->sock)) ereport(ERROR, (errmsg("could not set socket to non-blocking mode: %m"))); #endif MyProcPort->noblock = true; PG_TRY(); { r = secure_read(MyProcPort, c, 1); if (r < 0) { /* * Ok if no data available without blocking or interrupted (though * EINTR really shouldn't happen with a non-blocking socket). * Report other errors. */ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) r = 0; else { /* * Careful: an ereport() 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. */ ereport(COMMERROR, (errcode_for_socket_access(), errmsg("could not receive data from client: %m"))); r = EOF; } } else if (r == 0) { /* EOF detected */ r = EOF; } } PG_CATCH(); { /* * The rest of the backend code assumes the socket is in blocking * mode, so treat failure as FATAL. */ #ifdef WIN32 pgwin32_noblock = 0; #else if (!pg_set_block(MyProcPort->sock)) ereport(FATAL, (errmsg("could not set socket to blocking mode: %m"))); #endif MyProcPort->noblock = false; PG_RE_THROW(); } PG_END_TRY(); #ifdef WIN32 pgwin32_noblock = 0; #else if (!pg_set_block(MyProcPort->sock)) ereport(FATAL, (errmsg("could not set socket to blocking mode: %m"))); #endif MyProcPort->noblock = false; return r; }
/* * pg_krb5_sendauth -- client routine to send authentication information to * the server */ static int pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname) { krb5_error_code retval; int ret; krb5_principal server; krb5_auth_context auth_context = NULL; krb5_error *err_ret = NULL; if (!hostname) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n"); return STATUS_ERROR; } ret = pg_krb5_init(PQerrormsg); if (ret != STATUS_OK) return ret; retval = krb5_sname_to_principal(pg_krb5_context, hostname, PG_KRB_SRVNAM, KRB5_NT_SRV_HST, &server); if (retval) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pg_krb5_sendauth: krb5_sname_to_principal: %s\n", error_message(retval)); return STATUS_ERROR; } /* * libpq uses a non-blocking socket. But kerberos needs a blocking * socket, and we have to block somehow to do mutual authentication * anyway. So we temporarily make it blocking. */ if (!pg_set_block(sock)) { char sebuf[256]; snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); krb5_free_principal(pg_krb5_context, server); return STATUS_ERROR; } retval = krb5_sendauth(pg_krb5_context, &auth_context, (krb5_pointer) & sock, PG_KRB_SRVNAM, pg_krb5_client, server, AP_OPTS_MUTUAL_REQUIRED, NULL, 0, /* no creds, use ccache instead */ pg_krb5_ccache, &err_ret, NULL, NULL); if (retval) { if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { #if defined(HAVE_KRB5_ERROR_TEXT_DATA) snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->text.length, err_ret->text.data); #elif defined(HAVE_KRB5_ERROR_E_DATA) snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->e_data->length, (const char *) err_ret->e_data->data); #else #error "bogus configuration" #endif } else { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "krb5_sendauth: %s\n", error_message(retval)); } if (err_ret) krb5_free_error(pg_krb5_context, err_ret); ret = STATUS_ERROR; } krb5_free_principal(pg_krb5_context, server); if (!pg_set_noblock(sock)) { char sebuf[256]; snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("could not restore non-blocking mode on socket: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); ret = STATUS_ERROR; } return ret; }
static void ServiceListenLoop(ServiceCtrl *serviceCtrl) { ServiceConfig *serviceConfig = (ServiceConfig*)serviceCtrl->serviceConfig; uint8 *inputBuff; int n, highsock = 0, newsockfd; mpp_fd_set rset, rrset; struct sockaddr_in addr; socklen_t addrlen; List *connectedSockets = NIL; ListCell *cell; Assert(TopMemoryContext != NULL); MemoryContextSwitchTo(TopMemoryContext); Assert(CurrentMemoryContext == TopMemoryContext); /* * Setup scratch buffer. */ inputBuff = palloc(serviceConfig->requestLen); MPP_FD_ZERO(&rset); MPP_FD_SET(serviceCtrl->listenerFd, &rset); highsock = serviceCtrl->listenerFd + 1; /* we'll handle many incoming sockets but keep the sockets in blocking * mode since we are dealing with very small messages. */ while(true) { struct timeval shutdownTimeout = {1,0}; // 1 second. // Use local variable since select modifies // the timeout parameter with remaining time. CHECK_FOR_INTERRUPTS(); if (serviceConfig->ServiceShutdownRequested()) { if (serviceConfig->ServiceShutdown != NULL) { serviceConfig->ServiceShutdown(); } break; } /* no need to live on if postmaster has died */ if (!PostmasterIsAlive(true)) { if (serviceConfig->ServicePostmasterDied != NULL) { serviceConfig->ServicePostmasterDied(); } else { ereport(LOG, (errmsg("exiting because postmaster has died"))); proc_exit(1); } } memcpy(&rrset, &rset, sizeof(mpp_fd_set)); n = select(highsock + 1, (fd_set *)&rrset, NULL, NULL, &shutdownTimeout); if (n == 0 || (n < 0 && errno == EINTR)) { /* intr or timeout: Have we been here too long ? */ continue; } if (n < 0) { /* this may be a little severe, but if we error on select() * we'll just go ahead and blow up. This will result in the * postmaster re-spawning a new process. */ ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': error during select() call (error:%d).", serviceConfig->title, errno))); break; } /* is it someone tickling our listener port? */ if (MPP_FD_ISSET(serviceCtrl->listenerFd, &rrset)) { addrlen = sizeof(addr); if ((newsockfd = accept(serviceCtrl->listenerFd, (struct sockaddr *) & addr, &addrlen)) < 0) { /* * TODO: would be nice to read the errno and try and provide * more useful info as to why this happened. */ ereport(NOTICE, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': error from client connection: %s)", serviceConfig->title, strerror(errno)))); } /* make socket non-blocking BEFORE we connect. */ if (!pg_set_noblock(newsockfd)) { /* * TODO: would be nice to read the errno and try and provide * more useful info as to why this happened. */ ereport(NOTICE, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': could not set outbound socket to non-blocking mode: %s", serviceConfig->title, strerror(errno)))); } if (newsockfd > highsock) highsock = newsockfd + 1; MPP_FD_SET(newsockfd, &rset); /* * Read connection message. */ // UNDONE: temporarily turn off new connection flag... if( !ServiceProcessRequest(serviceCtrl, newsockfd, inputBuff, false)) { /* close it down */ MPP_FD_CLR( newsockfd, &rset); shutdown(newsockfd, SHUT_WR); close(newsockfd); } else { connectedSockets = lappend_int(connectedSockets, newsockfd); } } /* loop through all of our established sockets */ cell = list_head(connectedSockets); while (cell != NULL) { int fd = lfirst_int(cell); /* get the next cell ready before we delete */ cell = lnext(cell); if (MPP_FD_ISSET(fd, &rrset)) { if( !ServiceProcessRequest(serviceCtrl, fd, inputBuff, false)) { /* close it down */ MPP_FD_CLR( fd, &rset); connectedSockets = list_delete_int(connectedSockets, fd); shutdown(fd, SHUT_WR); close(fd); } } } } ereport(LOG, (errmsg("normal shutdown"))); proc_exit(0); }
static bool ServiceDoConnect(ServiceConfig *serviceConfig, int listenerPort, ServiceClient *serviceClient, bool complain) { int n; struct sockaddr_in addr; int saved_err; char *message; bool result = false; DECLARE_SAVE_SUPPRESS_PANIC(); PG_TRY(); { SUPPRESS_PANIC(); for (;;) { /* * Open a connection to the service. */ serviceClient->sockfd = socket(AF_INET, SOCK_STREAM, 0); addr.sin_family = AF_INET; addr.sin_port = htons(listenerPort); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if ((n = connect(serviceClient->sockfd, (struct sockaddr *)&addr, sizeof(addr))) < 0) { saved_err = errno; close(serviceClient->sockfd); serviceClient->sockfd = -1; if (errno == EINTR) continue; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Could not connect to '%s': %s", serviceConfig->title, strerror(saved_err)))); } else { //success. we're done here! break; } } /* make socket non-blocking BEFORE we connect. */ if (!pg_set_noblock(serviceClient->sockfd)) { saved_err = errno; close(serviceClient->sockfd); serviceClient->sockfd = -1; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Could not set '%s' socket to non-blocking mode: %s", serviceConfig->title, strerror(saved_err)))); } result = true; RESTORE_PANIC(); } PG_CATCH(); { RESTORE_PANIC(); /* Report the error to the server log */ if (!elog_demote(WARNING)) { elog(LOG,"unable to demote error"); PG_RE_THROW(); } message = elog_message(); if (message != NULL && strlen(message) + 1 < sizeof(ClientErrorString)) strcpy(ClientErrorString, message); else strcpy(ClientErrorString, ""); if (complain) EmitErrorReport(); FlushErrorState(); result = false; } PG_END_TRY(); return result; }