/* * Lock_AF_UNIX -- configure unix socket file path */ static int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName) { UNIXSOCK_PATH(sock_path, portNumber, unixSocketName); if (strlen(sock_path) >= UNIXSOCK_PATH_BUFLEN) { ereport(LOG, (errmsg("Unix-domain socket path \"%s\" is too long (maximum %d bytes)", sock_path, (int) (UNIXSOCK_PATH_BUFLEN - 1)))); return STATUS_ERROR; } /* * Grab an interlock file associated with the socket file. * * Note: there are two reasons for using a socket lock file, rather than * trying to interlock directly on the socket itself. First, it's a lot * more portable, and second, it lets us remove any pre-existing socket * file without race conditions. */ CreateSocketLockFile(sock_path, true); /* * Once we have the interlock, we can safely delete any pre-existing * socket file to avoid failure at bind() time. */ unlink(sock_path); return STATUS_OK; }
/* * Lock_AF_UNIX -- configure unix socket file path */ static int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName) { UNIXSOCK_PATH(sock_path, portNumber, unixSocketName); /* * Grab an interlock file associated with the socket file. */ CreateSocketLockFile(sock_path, true); /* * Once we have the interlock, we can safely delete any pre-existing * socket file to avoid failure at bind() time. */ unlink(sock_path); return STATUS_OK; }
/* * Lock_AF_UNIX -- configure unix socket file path */ static int Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName) { UNIXSOCK_PATH(sock_path, portNumber, unixSocketName); /* * Grab an interlock file associated with the socket file. * * Note: there are two reasons for using a socket lock file, rather than * trying to interlock directly on the socket itself. First, it's a lot * more portable, and second, it lets us remove any pre-existing socket * file without race conditions. */ CreateSocketLockFile(sock_path, true); /* * Once we have the interlock, we can safely delete any pre-existing * socket file to avoid failure at bind() time. */ unlink(sock_path); return STATUS_OK; }
char SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname, long timeout) { struct addrinfo rest, *addrs = NULL, *curadr = NULL; int family = 0; char retval = 0; int gerrno; if (self->socket != (SOCKETFD) -1) { SOCK_set_error(self, SOCKET_ALREADY_CONNECTED, "Socket is already connected"); return 0; } #if defined(_MSC_VER) && (_MSC_VER < 1300) if (ws2_hnd == NULL) ws2_hnd = GetModuleHandle("ws2_32.dll"); if (freeaddrinfo_ptr == NULL) freeaddrinfo_ptr = (freeaddrinfo_func)GetProcAddress(ws2_hnd, "freeaddrinfo"); if (getaddrinfo_ptr == NULL) getaddrinfo_ptr = (getaddrinfo_func)GetProcAddress(ws2_hnd, "getaddrinfo"); if (getnameinfo_ptr == NULL) getnameinfo_ptr = (getnameinfo_func)GetProcAddress(ws2_hnd, "getnameinfo"); #endif /* * Hostname lookup. */ if (hostname && hostname[0] #ifndef WIN32 && '/' != hostname[0] #endif /* WIN32 */ ) { char portstr[16]; int ret; memset(&rest, 0, sizeof(rest)); rest.ai_socktype = SOCK_STREAM; rest.ai_family = AF_UNSPEC; snprintf(portstr, sizeof(portstr), "%d", port); if (inet_addr(hostname) != INADDR_NONE) rest.ai_flags |= AI_NUMERICHOST; ret = getaddrinfo_ptr(hostname, portstr, &rest, &addrs); if (ret || !addrs) { SOCK_set_error(self, SOCKET_HOST_NOT_FOUND, "Could not resolve hostname."); if (addrs) freeaddrinfo_ptr(addrs); return 0; } curadr = addrs; } else #ifdef HAVE_UNIX_SOCKETS { struct sockaddr_un *un = (struct sockaddr_un *) &(self->sadr_area); family = un->sun_family = AF_UNIX; /* passing NULL or '' means pg default "/tmp" */ UNIXSOCK_PATH(un, port, hostname); self->sadr_len = UNIXSOCK_LEN(un); } #else { SOCK_set_error(self, SOCKET_HOST_NOT_FOUND, "Hostname isn't specified."); return 0; } #endif /* HAVE_UNIX_SOCKETS */ retry: if (curadr) family = curadr->ai_family; self->socket = socket(family, SOCK_STREAM, 0); if (self->socket == (SOCKETFD) -1) { SOCK_set_error(self, SOCKET_COULD_NOT_CREATE_SOCKET, "Could not create Socket."); goto cleanup; } #ifdef TCP_NODELAY if (family != AF_UNIX) { int i; socklen_t len; i = 1; len = sizeof(i); if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, len) < 0) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not set socket to NODELAY."); goto cleanup; } } #endif /* TCP_NODELAY */ #ifdef WIN32 { long ioctlsocket_ret = 1; /* Returns non-0 on failure, while fcntl() returns -1 on failure */ ioctlsocket(self->socket, FIONBIO, &ioctlsocket_ret); } #else fcntl(self->socket, F_SETFL, O_NONBLOCK); #endif if (curadr) { struct sockaddr *in = (struct sockaddr *) &(self->sadr_area); memset((char *) in, 0, sizeof(self->sadr_area)); memcpy(in, curadr->ai_addr, curadr->ai_addrlen); self->sadr_len = (int) curadr->ai_addrlen; } if (connect(self->socket, (struct sockaddr *) &(self->sadr_area), self->sadr_len) < 0) { int ret, optval; int wait_sec = 0; #ifdef HAVE_POLL struct pollfd fds; #else fd_set fds, except_fds; struct timeval tm; #endif /* HAVE_POLL */ socklen_t optlen = sizeof(optval); time_t t_now, t_finish = 0; BOOL tm_exp = FALSE; gerrno = SOCK_ERRNO; switch (gerrno) { case 0: case EINPROGRESS: case EINTR: #ifdef EAGAIN case EAGAIN: #endif /* EAGAIN */ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) case EWOULDBLOCK: #endif /* EWOULDBLOCK */ break; default: SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect to remote socket immedaitely"); goto cleanup; } if (timeout > 0) { t_now = time(NULL); t_finish = t_now + timeout; wait_sec = timeout; } do { #ifdef HAVE_POLL fds.fd = self->socket; fds.events = POLLOUT; fds.revents = 0; ret = poll(&fds, 1, timeout > 0 ? wait_sec * 1000 : -1); #else tm.tv_sec = wait_sec; tm.tv_usec = 0; FD_ZERO(&fds); FD_ZERO(&except_fds); FD_SET(self->socket, &fds); FD_SET(self->socket, &except_fds); ret = select((int) self->socket + 1, NULL, &fds, &except_fds, timeout > 0 ? &tm : NULL); #endif /* HAVE_POLL */ gerrno = SOCK_ERRNO; if (0 < ret) break; else if (0 == ret) tm_exp = TRUE; else if (EINTR != gerrno) break; else if (timeout > 0) { if (t_now = time(NULL), t_now >= t_finish) tm_exp = TRUE; else wait_sec = (int) (t_finish - t_now); } } while (!tm_exp); if (tm_exp) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. timeout occured."); goto cleanup; } else if (0 > ret) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. select error occured."); mylog("select error ret=%d ERROR=%d\n", ret, gerrno); goto cleanup; } if (getsockopt(self->socket, SOL_SOCKET, SO_ERROR, (char *) &optval, &optlen) == -1) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. getsockopt error."); } else if (optval != 0) { char errmsg[256], host[64]; host[0] = '\0'; #if defined(_MSC_VER) && (_MSC_VER < 1300) getnameinfo_ptr #else getnameinfo #endif ((struct sockaddr *) &(self->sadr_area), self->sadr_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); /* snprintf(errmsg, sizeof(errmsg), "connect getsockopt val %d addr=%s\n", optval, host); */ format_sockerr(errmsg, sizeof(errmsg), optval, "connect", host, port); mylog(errmsg); SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, errmsg); } else retval = 1; } else retval = 1; cleanup: if (0 == retval) { if (self->socket >= 0) { closesocket(self->socket); self->socket = (SOCKETFD) -1; } if (curadr && curadr->ai_next) { curadr = curadr->ai_next; goto retry; } } else SOCK_set_error(self, 0, NULL); if (addrs) freeaddrinfo_ptr(addrs); return retval; }
int StreamServerPort(int family, char *hostName, unsigned short portNumber, char *unixSocketDir, pgsocket ListenSocket[], int MaxListen) { pgsocket fd; int err; int maxconn; int ret; char portNumberStr[32]; const char *familyDesc; char familyDescBuf[64]; char *service; struct addrinfo *addrs = NULL, *addr; struct addrinfo hint; int listen_index = 0; int added = 0; #ifdef HAVE_UNIX_SOCKETS char unixSocketPath[MAXPGPATH]; #endif #if !defined(WIN32) || defined(IPV6_V6ONLY) int one = 1; #endif /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_family = family; hint.ai_flags = AI_PASSIVE; hint.ai_socktype = SOCK_STREAM; #ifdef HAVE_UNIX_SOCKETS if (family == AF_UNIX) { /* * Create unixSocketPath from portNumber and unixSocketDir and lock * that file path */ UNIXSOCK_PATH(unixSocketPath, portNumber, unixSocketDir); if (strlen(unixSocketPath) >= UNIXSOCK_PATH_BUFLEN) { ereport(LOG, (errmsg("Unix-domain socket path \"%s\" is too long (maximum %d bytes)", unixSocketPath, (int) (UNIXSOCK_PATH_BUFLEN - 1)))); return STATUS_ERROR; } if (Lock_AF_UNIX(unixSocketDir, unixSocketPath) != STATUS_OK) return STATUS_ERROR; service = unixSocketPath; } else #endif /* HAVE_UNIX_SOCKETS */ { snprintf(portNumberStr, sizeof(portNumberStr), "%d", portNumber); service = portNumberStr; } ret = pg_getaddrinfo_all(hostName, service, &hint, &addrs); if (ret || !addrs) { if (hostName) ereport(LOG, (errmsg("could not translate host name \"%s\", service \"%s\" to address: %s", hostName, service, gai_strerror(ret)))); else ereport(LOG, (errmsg("could not translate service \"%s\" to address: %s", service, gai_strerror(ret)))); if (addrs) pg_freeaddrinfo_all(hint.ai_family, addrs); return STATUS_ERROR; } for (addr = addrs; addr; addr = addr->ai_next) { if (!IS_AF_UNIX(family) && IS_AF_UNIX(addr->ai_family)) { /* * Only set up a unix domain socket when they really asked for it. * The service/port is different in that case. */ continue; } /* See if there is still room to add 1 more socket. */ for (; listen_index < MaxListen; listen_index++) { if (ListenSocket[listen_index] == PGINVALID_SOCKET) break; } if (listen_index >= MaxListen) { ereport(LOG, (errmsg("could not bind to all requested addresses: MAXLISTEN (%d) exceeded", MaxListen))); break; } /* set up family name for possible error messages */ switch (addr->ai_family) { case AF_INET: familyDesc = _("IPv4"); break; #ifdef HAVE_IPV6 case AF_INET6: familyDesc = _("IPv6"); break; #endif #ifdef HAVE_UNIX_SOCKETS case AF_UNIX: familyDesc = _("Unix"); break; #endif default: snprintf(familyDescBuf, sizeof(familyDescBuf), _("unrecognized address family %d"), addr->ai_family); familyDesc = familyDescBuf; break; } if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET) { ereport(LOG, (errcode_for_socket_access(), /* translator: %s is IPv4, IPv6, or Unix */ errmsg("could not create %s socket: %m", familyDesc))); continue; } #ifndef WIN32 /* * Without the SO_REUSEADDR flag, a new postmaster can't be started * right away after a stop or crash, giving "address already in use" * error on TCP ports. * * On win32, however, this behavior only happens if the * SO_EXLUSIVEADDRUSE is set. With SO_REUSEADDR, win32 allows multiple * servers to listen on the same address, resulting in unpredictable * behavior. With no flags at all, win32 behaves as Unix with * SO_REUSEADDR. */ if (!IS_AF_UNIX(addr->ai_family)) { if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one))) == -1) { ereport(LOG, (errcode_for_socket_access(), errmsg("setsockopt(SO_REUSEADDR) failed: %m"))); closesocket(fd); continue; } } #endif #ifdef IPV6_V6ONLY if (addr->ai_family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &one, sizeof(one)) == -1) { ereport(LOG, (errcode_for_socket_access(), errmsg("setsockopt(IPV6_V6ONLY) failed: %m"))); closesocket(fd); continue; } } #endif /* * Note: This might fail on some OS's, like Linux older than * 2.4.21-pre3, that don't have the IPV6_V6ONLY socket option, and map * ipv4 addresses to ipv6. It will show ::ffff:ipv4 for all ipv4 * connections. */ err = bind(fd, addr->ai_addr, addr->ai_addrlen); if (err < 0) { ereport(LOG, (errcode_for_socket_access(), /* translator: %s is IPv4, IPv6, or Unix */ errmsg("could not bind %s socket: %m", familyDesc), (IS_AF_UNIX(addr->ai_family)) ? errhint("Is another postmaster already running on port %d?" " If not, remove socket file \"%s\" and retry.", (int) portNumber, service) : errhint("Is another postmaster already running on port %d?" " If not, wait a few seconds and retry.", (int) portNumber))); closesocket(fd); continue; } #ifdef HAVE_UNIX_SOCKETS if (addr->ai_family == AF_UNIX) { if (Setup_AF_UNIX(service) != STATUS_OK) { closesocket(fd); break; } } #endif /* * 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) { ereport(LOG, (errcode_for_socket_access(), /* translator: %s is IPv4, IPv6, or Unix */ errmsg("could not listen on %s socket: %m", familyDesc))); closesocket(fd); continue; } ListenSocket[listen_index] = fd; added++; } pg_freeaddrinfo_all(hint.ai_family, addrs); if (!added) return STATUS_ERROR; return STATUS_OK; }