/** * @brief * Open a connection with a pbs server. * Do not allow TCP to block us if Server host is down * At this point, this does not attempt to find a fail_over Server * * @param[in] server - specifies the server to which to connect * @param[in] tout - timeout value for select * * @return int * @retval >= 0 index to the internal connection table representing the * connection made. * @retval -1 error encountered in getting index */ int pbs_connect_noblk(char *server, int tout) { int out; int i; pbs_socklen_t l; int n; struct timeval tv; fd_set fdset; struct batch_reply *reply; char server_name[PBS_MAXSERVERNAME+1]; unsigned int server_port; struct addrinfo *aip, *pai; struct addrinfo hints; struct sockaddr_in *inp; short int connect_err = 0; struct sockaddr_in sockname; pbs_socklen_t socknamelen; #ifdef WIN32 int non_block = 1; struct sockaddr_in to_sock; struct sockaddr_in from_sock; #endif #ifndef WIN32 int nflg; int oflg; #endif /* initialize the thread context data, if not already initialized */ if (pbs_client_thread_init_thread_context() != 0) return -1; if (pbs_loadconf(0) == 0) return -1; /* get server host and port */ server = PBS_get_server(server, server_name, &server_port); if (server == NULL) { pbs_errno = PBSE_NOSERVER; return -1; } /* Reserve a connection state record */ if (pbs_client_thread_lock_conntable() != 0) return -1; out = -1; for (i=1;i<NCONNECTS;i++) { if (connection[i].ch_inuse) continue; out = i; connection[out].ch_inuse = 1; connection[out].ch_errno = 0; connection[out].ch_socket= -1; connection[out].ch_errtxt = NULL; break; } if (pbs_client_thread_unlock_conntable() != 0) return -1; /* pbs_errno set by the function */ if (out < 0) { pbs_errno = PBSE_NOCONNECTS; return -1; } /* get socket */ #ifdef WIN32 /* the following lousy hack is needed since the socket call needs */ /* SYSTEMROOT env variable properly set! */ if (getenv("SYSTEMROOT") == NULL) { setenv("SYSTEMROOT", "C:\\WINNT", 1); setenv("SystemRoot", "C:\\WINNT", 1); } connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); if (connection[out].ch_socket < 0) { setenv("SYSTEMROOT", "C:\\WINDOWS", 1); setenv("SystemRoot", "C:\\WINDOWS", 1); connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); } #else connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); #endif if (connection[out].ch_socket < 0) { connection[out].ch_inuse = 0; pbs_errno = ERRORNO; return -1; } /* set socket non-blocking */ #ifdef WIN32 if (ioctlsocket(connection[out].ch_socket, FIONBIO, &non_block) == SOCKET_ERROR) #else oflg = fcntl(connection[out].ch_socket, F_GETFL) & ~O_ACCMODE; nflg = oflg | O_NONBLOCK; if (fcntl(connection[out].ch_socket, F_SETFL, nflg) == -1) #endif goto err; /* and connect... */ strcpy(pbs_server, server); /* set for error messages from commands */ memset(&hints, 0, sizeof(struct addrinfo)); /* * Why do we use AF_UNSPEC rather than AF_INET? Some * implementations of getaddrinfo() will take an IPv6 * address and map it to an IPv4 one if we ask for AF_INET * only. We don't want that - we want only the addresses * that are genuinely, natively, IPv4 so we start with * AF_UNSPEC and filter ai_family below. */ hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(server, NULL, &hints, &pai) != 0) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; pbs_errno = PBSE_BADHOST; return -1; } for (aip = pai; aip != NULL; aip = aip->ai_next) { /* skip non-IPv4 addresses */ if (aip->ai_family == AF_INET) { inp = (struct sockaddr_in *) aip->ai_addr; break; } } if (aip == NULL) { /* treat no IPv4 addresses as getaddrinfo() failure */ CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; pbs_errno = PBSE_BADHOST; freeaddrinfo(pai); return -1; } else inp->sin_port = htons(server_port); if (connect(connection[out].ch_socket, aip->ai_addr, aip->ai_addrlen) < 0) { connect_err = 1; } if (connect_err == 1) { /* connect attempt failed */ pbs_errno = ERRORNO; switch (pbs_errno) { #ifdef WIN32 case WSAEWOULDBLOCK: #else case EINPROGRESS: case EWOULDBLOCK: #endif while (1) { FD_ZERO(&fdset); FD_SET(connection[out].ch_socket, &fdset); tv.tv_sec = tout; tv.tv_usec = 0; n = select(connection[out].ch_socket+1, NULL, &fdset, NULL, &tv); if (n > 0) { pbs_errno = 0; l = sizeof(pbs_errno); (void)getsockopt(connection[out].ch_socket, SOL_SOCKET, SO_ERROR, &pbs_errno, &l); if (pbs_errno == 0) break; else goto err; } if ((n < 0) && #ifdef WIN32 (ERRORNO == WSAEINTR) #else (ERRORNO == EINTR) #endif ) { continue; } else { goto err; } } break; default: err: CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; freeaddrinfo(pai); return -1; /* cannot connect */ } } freeaddrinfo(pai); /* reset socket blocking */ #ifdef WIN32 non_block = 0; if (ioctlsocket(connection[out].ch_socket, FIONBIO, &non_block) == SOCKET_ERROR) #else if (fcntl(connection[out].ch_socket, F_SETFL, oflg) < 0) #endif goto err; /* * multiple threads cant get the same connection id above, * so no need to lock this piece of code */ /* setup connection level thread context */ if (pbs_client_thread_init_connect_context(out) != 0) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; /* pbs_errno set by the pbs_connect_init_context routine */ return -1; } /* * even though the following is communication with server on * a connection handle, it does not need to be lock since * this connection handle has not be returned back yet to the client * so others threads cannot use it */ /* send "dummy" connect message */ DIS_tcp_setup(connection[out].ch_socket); if ((i = encode_DIS_ReqHdr(connection[out].ch_socket, PBS_BATCH_Connect, pbs_current_user)) || (i = encode_DIS_ReqExtend(connection[out].ch_socket, NULL))) { pbs_errno = PBSE_SYSTEM; return -1; } if (DIS_tcp_wflush(connection[out].ch_socket)) { pbs_errno = PBSE_SYSTEM; return -1; } reply = PBSD_rdrpy(out); PBSD_FreeReply(reply); /*do configured authentication (kerberos, pbs_iff, whatever)*/ /*Get the socket port for engage_authentication()*/ socknamelen = sizeof(sockname); if (getsockname(connection[out].ch_socket, (struct sockaddr *)&sockname, &socknamelen)) return -1; if (engage_authentication(connection[out].ch_socket, server, server_port, &sockname) == -1) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; pbs_errno = PBSE_PERM; return -1; } /* setup DIS support routines for following pbs_* calls */ DIS_tcp_setup(connection[out].ch_socket); pbs_tcp_timeout = PBS_DIS_TCP_TIMEOUT_VLONG; /* set for 3 hours */ return out; }
int client_to_svr_extend(pbs_net_t hostaddr, unsigned int port, int authport_flags, char *localaddr) { struct sockaddr_in remote; int sock; int local_port; int errn; int rc; #ifdef WIN32 int ret; int non_block = 1; struct linger li; struct sockaddr_in from; struct timeval tv; fd_set writeset; #else struct pollfd fds[1]; pbs_socklen_t len = sizeof(rc); int oflag; #endif /* If local privilege port requested, bind to one */ /* Must be root privileged to do this */ local_port = authport_flags & B_RESERVED; if (local_port) { #ifdef IP_PORTRANGE_LOW int lport = IPPORT_RESERVED - 1; sock = rresvport(&lport); if (sock < 0) { if (errno == EAGAIN) return PBS_NET_RC_RETRY; else return PBS_NET_RC_FATAL; } #else /* IP_PORTRANGE_LOW */ struct sockaddr_in local; unsigned short tryport; static unsigned short start_port = 0; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { return PBS_NET_RC_FATAL; } if (start_port == 0) { /* arbitrary start point */ start_port = (getpid() %(IPPORT_RESERVED/2)) + IPPORT_RESERVED/2; } else if (--start_port < IPPORT_RESERVED/2) start_port = IPPORT_RESERVED - 1; tryport = start_port; memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; if (localaddr != NULL) { local.sin_addr.s_addr = inet_addr(localaddr); if (local.sin_addr.s_addr == INADDR_NONE) { perror("inet_addr failed"); return (PBS_NET_RC_FATAL); } } else if (pbs_conf.pbs_public_host_name) { pbs_net_t public_addr; public_addr = get_hostaddr(pbs_conf.pbs_public_host_name); if (public_addr == (pbs_net_t)0) { return (PBS_NET_RC_FATAL); } local.sin_addr.s_addr = htonl(public_addr); } for (;;) { local.sin_port = htons(tryport); if (bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) break; #ifdef WIN32 errno = WSAGetLastError(); if (errno != EADDRINUSE && errno != EADDRNOTAVAIL && errno != WSAEACCES) { closesocket(sock); #else if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) { close(sock); #endif return PBS_NET_RC_FATAL; } else if (--tryport < (IPPORT_RESERVED/2)) { tryport = IPPORT_RESERVED - 1; } if (tryport == start_port) { #ifdef WIN32 closesocket(sock); #else close(sock); #endif return PBS_NET_RC_RETRY; } } /* ** Ensure last tryport becomes start port on next call. */ start_port = tryport; #endif /* IP_PORTRANGE_LOW */ } else { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { return PBS_NET_RC_FATAL; } } remote.sin_addr.s_addr = htonl(hostaddr); remote.sin_port = htons((unsigned short)port); remote.sin_family = AF_INET; #ifdef WIN32 li.l_onoff = 1; li.l_linger = 5; setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)); if (ioctlsocket(sock, FIONBIO, &non_block) == SOCKET_ERROR) { errno = WSAGetLastError(); closesocket(sock); return (PBS_NET_RC_FATAL); } #else oflag = fcntl(sock, F_GETFL); if (fcntl(sock, F_SETFL, (oflag | O_NONBLOCK)) == -1) { close(sock); return (PBS_NET_RC_FATAL); } #endif if (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) < 0) { #ifdef WIN32 errno = WSAGetLastError(); #endif /* * Bacause of threading, pbs_errno is actually a macro * pointing to a variable within a tread context. On certain * platforms, the threading library resulted in errno being * cleared after pbs_errno was set set from it, so save * errno into a local variable first, then test it. */ errn = errno; pbs_errno = errn; switch (errn) { #ifdef WIN32 case WSAEINTR: #else case EINTR: #endif case EADDRINUSE: case ETIMEDOUT: case ECONNREFUSED: #ifdef WIN32 closesocket(sock); #else close(sock); #endif return (PBS_NET_RC_RETRY); #ifdef WIN32 case WSAEWOULDBLOCK: FD_ZERO(&writeset); FD_SET((unsigned int)sock, &writeset); tv.tv_sec = conn_timeout; /* connect timeout */ tv.tv_usec = 0; ret = select(1, NULL, &writeset, NULL, &tv); if (ret == SOCKET_ERROR) { errno = WSAGetLastError(); errn = errno; pbs_errno = errn; closesocket(sock); return PBS_NET_RC_FATAL; } else if (ret == 0) { closesocket(sock); return PBS_NET_RC_RETRY; } break; #else /* UNIX */ case EWOULDBLOCK: case EINPROGRESS: while (1) { fds[0].fd = sock; fds[0].events = POLLOUT; fds[0].revents = 0; rc = poll(fds, (nfds_t)1, conn_timeout * 1000); if (rc == -1) { errn = errno; if ((errn != EAGAIN) && (errn != EINTR)) break; } else break; /* no error */ } if (rc == 1) { /* socket may be connected and ready to write */ rc = 0; if ((getsockopt(sock, SOL_SOCKET, SO_ERROR, &rc, &len) == -1) || (rc != 0)) { close(sock); return PBS_NET_RC_FATAL; } break; } else if (rc == 0) { /* socket not ready - not connected in time */ close(sock); return PBS_NET_RC_RETRY; } else { /* socket not ready - error */ close(sock); return PBS_NET_RC_FATAL; } #endif /* end UNIX */ default: #ifdef WIN32 closesocket(sock); #else close(sock); #endif return (PBS_NET_RC_FATAL); } } /* reset socket to blocking */ #ifdef WIN32 non_block = 0; if (ioctlsocket(sock, FIONBIO, &non_block) == SOCKET_ERROR) { errno = WSAGetLastError(); closesocket(sock); return PBS_NET_RC_FATAL; } #else /* UNIX */ if (fcntl(sock, F_SETFL, oflag) == -1) { close(sock); return (PBS_NET_RC_FATAL); } #endif if (engage_authentication(sock, remote.sin_addr, port, authport_flags) != -1) return sock; /*authentication unsuccessful*/ #ifdef WIN32 closesocket(sock); #else close(sock); #endif return (PBS_NET_RC_FATAL); }
/** * @brief * Makes a PBS_BATCH_Connect request to 'server'. * * @param[in] server - the hostname of the pbs server to connect to. * @param[in] extend_data - a string to send as "extend" data. * * @return int * @retval >= 0 index to the internal connection table representing the * connection made. * @retval -1 error encountered setting up the connection. */ int __pbs_connect_extend(char *server, char *extend_data) { struct sockaddr_in server_addr; struct sockaddr_in my_sockaddr; int out; int i; int f; char *altservers[2]; int have_alt = 0; struct batch_reply *reply; char server_name[PBS_MAXSERVERNAME+1]; unsigned int server_port; struct sockaddr_in sockname; pbs_socklen_t socknamelen; #ifdef WIN32 struct sockaddr_in to_sock; struct sockaddr_in from_sock; #endif #ifndef WIN32 char pbsrc[_POSIX_PATH_MAX]; struct stat sb; int using_secondary = 0; #endif /* not WIN32 */ /* initialize the thread context data, if not already initialized */ if (pbs_client_thread_init_thread_context() != 0) return -1; if (pbs_loadconf(0) == 0) return -1; /* get server host and port */ server = PBS_get_server(server, server_name, &server_port); if (server == NULL) { pbs_errno = PBSE_NOSERVER; return -1; } if (pbs_conf.pbs_primary && pbs_conf.pbs_secondary) { /* failover configuered ... */ if (hostnmcmp(server, pbs_conf.pbs_primary) == 0) { have_alt = 1; /* We want to try the one last seen as "up" first to not */ /* have connection delays. If the primary was up, there */ /* is no .pbsrc.NAME file. If the last command connected */ /* to the Secondary, then it created the .pbsrc.USER file. */ /* see if already seen Primary down */ #ifdef WIN32 /* due to windows quirks, all try both in same order */ altservers[0] = pbs_conf.pbs_primary; altservers[1] = pbs_conf.pbs_secondary; #else (void)snprintf(pbsrc, _POSIX_PATH_MAX, "%s/.pbsrc.%s", pbs_conf.pbs_tmpdir, pbs_current_user); if (stat(pbsrc, &sb) == -1) { /* try primary first */ altservers[0] = pbs_conf.pbs_primary; altservers[1] = pbs_conf.pbs_secondary; using_secondary = 0; } else { /* try secondary first */ altservers[0] = pbs_conf.pbs_secondary; altservers[1] = pbs_conf.pbs_primary; using_secondary = 1; } #endif } } /* if specific host name declared for the host on which */ /* this client is running, get its address */ if (pbs_conf.pbs_public_host_name) { if (get_hostsockaddr(pbs_conf.pbs_public_host_name, &my_sockaddr) != 0) return -1; /* pbs_errno was set */ } /* Reserve a connection state record */ if (pbs_client_thread_lock_conntable() != 0) return -1; out = -1; for (i=1;i<NCONNECTS;i++) { if (connection[i].ch_inuse) continue; out = i; connection[out].ch_errno = 0; connection[out].ch_socket= -1; connection[out].ch_errtxt = NULL; connection[out].ch_inuse = 1; /* reserve the socket */ break; } if (pbs_client_thread_unlock_conntable() != 0) return -1; /* pbs_errno set by the function */ if (out < 0) { pbs_errno = PBSE_NOCONNECTS; return -1; } /* * connect to server ... * If attempt to connect fails and if Failover configured and * if attempting to connect to Primary, try the Secondary * if attempting to connect to Secondary, try the Primary */ for (i=0; i<(have_alt+1); ++i) { /* get socket */ #ifdef WIN32 /* the following lousy hack is needed since the socket call needs */ /* SYSTEMROOT env variable properly set! */ if (getenv("SYSTEMROOT") == NULL) { setenv("SYSTEMROOT", "C:\\WINNT", 1); setenv("SystemRoot", "C:\\WINNT", 1); } connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); if (connection[out].ch_socket < 0) { setenv("SYSTEMROOT", "C:\\WINDOWS", 1); setenv("SystemRoot", "C:\\WINDOWS", 1); connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); } #else connection[out].ch_socket = socket(AF_INET, SOCK_STREAM, 0); #endif if (connection[out].ch_socket < 0) { connection[out].ch_inuse = 0; pbs_errno = errno; return -1; } /* and connect... */ if (have_alt) { server = altservers[i]; } strcpy(pbs_server, server); /* set for error messages from commands */ /* If a specific host name is defined which the client should use */ if (pbs_conf.pbs_public_host_name) { /* my address will be in my_sockaddr, bind the socket to it */ my_sockaddr.sin_port = 0; if (bind(connection[out].ch_socket, (struct sockaddr *)&my_sockaddr, sizeof(my_sockaddr)) != 0) { return -1; } } if (get_hostsockaddr(server, &server_addr) != 0) return -1; server_addr.sin_port = htons(server_port); if (connect(connection[out].ch_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == 0) { break; } else { /* connect attempt failed */ CLOSESOCKET(connection[out].ch_socket); pbs_errno = errno; } } if (i >= (have_alt+1)) { connection[out].ch_inuse = 0; return -1; /* cannot connect */ } #ifndef WIN32 if (have_alt && (i == 1)) { /* had to use the second listed server ... */ if (using_secondary == 1) { /* remove file that causes trying the Secondary first */ unlink(pbsrc); } else { /* create file that causes trying the Primary first */ f = open(pbsrc, O_WRONLY|O_CREAT, 0200); if (f != -1) (void)close(f); } } #endif /* setup connection level thread context */ if (pbs_client_thread_init_connect_context(out) != 0) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; /* pbs_errno set by the pbs_connect_init_context routine */ return -1; } /* * No need for global lock now on, since rest of the code * is only communication on a connection handle. * But we dont need to lock the connection handle, since this * connection handle is not yet been returned to the client */ /* The following code was originally put in for HPUX systems to deal * with the issue where returning from the connect() call doesn't * mean the connection is complete. However, this has also been * experienced in some Linux ppc64 systems like js-2. Decision was * made to enable this harmless code for all architectures. * FIX: Need to use the socket to send * a message to complete the process. For IFF authentication there is * no leading authentication message needing to be sent on the client * socket, so will send a "dummy" message and discard the replyback. */ #if !defined(PBS_SECURITY ) || (PBS_SECURITY == STD ) DIS_tcp_setup(connection[out].ch_socket); if ((i = encode_DIS_ReqHdr(connection[out].ch_socket, PBS_BATCH_Connect, pbs_current_user)) || (i = encode_DIS_ReqExtend(connection[out].ch_socket, extend_data))) { pbs_errno = PBSE_SYSTEM; return -1; } if (DIS_tcp_wflush(connection[out].ch_socket)) { pbs_errno = PBSE_SYSTEM; return -1; } reply = PBSD_rdrpy(out); PBSD_FreeReply(reply); #endif /* PBS_SECURITY ... */ /*do configured authentication (kerberos, pbs_iff, whatever)*/ /*Get the socket port for engage_authentication() */ socknamelen = sizeof(sockname); if (getsockname(connection[out].ch_socket, (struct sockaddr *)&sockname, &socknamelen)) return -1; if (engage_authentication(connection[out].ch_socket, server, server_port, &sockname) == -1) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; pbs_errno = PBSE_PERM; return -1; } /* setup DIS support routines for following pbs_* calls */ DIS_tcp_setup(connection[out].ch_socket); pbs_tcp_timeout = PBS_DIS_TCP_TIMEOUT_VLONG; /* set for 3 hours */ /* * Disable Nagle's algorithm on the TCP connection to server. * Nagle's algorithm is hurting cmd-server communication. */ if (pbs_connection_set_nodelay(out) == -1) { CLOSESOCKET(connection[out].ch_socket); connection[out].ch_inuse = 0; pbs_errno = PBSE_SYSTEM; return -1; } return out; }