/* * This gets called when a connect() attempt has become writable. * It's entirely possible that the node we're trying to connect * to has connected to us while we were waiting for them, in * which case we need to figure out which of the two connections * we're supposed to use. */ static int conn_writable(int sd, int events, void *node_) { merlin_node *node = (merlin_node *)node_; int result; int sel_sd; /* unregister so we don't peg one cpu at 100% */ ldebug("CONN: In conn_writable(): node=%s; sd=%d; node->conn_sock=%d", node->name, sd, node->conn_sock); iobroker_unregister(nagios_iobs, sd); if (node->sock < 0) { /* no inbound connection accept()'ed yet */ node->sock = sd; node->conn_sock = -1; if (!net_is_connected(node)) { node_disconnect(node, "Connection attempt failed: %s", strerror(errno)); close(sd); return 0; } iobroker_register(nagios_iobs, sd, node, net_input); node_set_state(node, STATE_NEGOTIATING, "Connect completed successfully. Negotiating protocol"); return 0; } sel_sd = net_negotiate_socket(node, node->conn_sock, node->sock); if (sel_sd < 0) { node_disconnect(node, "Failed to negotiate socket"); return 0; } if (sel_sd == node->conn_sock) { iobroker_close(nagios_iobs, node->sock); } else if (sel_sd == node->sock) { iobroker_close(nagios_iobs, node->conn_sock); } node->sock = sel_sd; node->conn_sock = -1; node_set_state(node, STATE_NEGOTIATING, "polled for writability"); /* now re-register for input */ ldebug("IOB: registering %s(%d) for input events", node->name, node->sock); result = iobroker_register(nagios_iobs, node->sock, node, net_input); if (result < 0) { lerr("IOB: Failed to register %s(%d) for input events: %s", node->name, node->sock, iobroker_strerror(result)); } return 0; }
void node_del_data_event(struct node *node) { int ret; log_error("%s: del data event for node %d", __func__, node->id); ret = pthread_spin_trylock(&node->spinlock); if(ret != 0) { log_error("%s: del data event get lock failed", __func__); return; } if(node->data_event == NULL) { log_error("%s: null data_event for node %d, cancel", __func__, node->id); goto err; } shutdown(node->dfd, SHUT_RDWR); sock_close(node->dfd); node->dfd = -1; node->data_conn_state = NODE_DFD_DISCONNECTED; event_free(node->data_event); node->data_event = NULL; pthread_spin_unlock(&node->spinlock); clean_packet_queue(node->data_q); node_disconnect(node); return; err: pthread_spin_unlock(&node->spinlock); }
/* * Fetch one event from the node's iocache. If the cache is * exhausted, we handle partial events and iocache resets and * return NULL */ merlin_event *node_get_event(merlin_node *node) { merlin_event *pkt; iocache *ioc = node->ioc; pkt = (merlin_event *)(iocache_use_size(ioc, HDR_SIZE)); /* * buffer is empty */ if (pkt == NULL) { return NULL; } /* * If buffer is smaller than expected, put the header back * and wait for more data */ if (pkt->hdr.len > iocache_available(ioc)) { ldebug("IOC: packet is longer (%i) than remaining data (%lu) from %s - will read more and try again", pkt->hdr.len, iocache_available(ioc), node->name); if (iocache_unuse_size(ioc, HDR_SIZE) < 0) lerr("IOC: Failed to unuse %d bytes from iocache. Next packet from %s will be invalid\n", HDR_SIZE, node->name); return NULL; } if (pkt->hdr.sig.id != MERLIN_SIGNATURE) { lerr("Invalid signature on packet from '%s'. Disconnecting node", node->name); node_disconnect(node, "Invalid signature"); return NULL; } node->stats.events.read++; iocache_use_size(ioc, pkt->hdr.len); return pkt; }
/* * Accept an inbound connection from a remote host * Returns 0 on success and -1 on errors */ static int net_accept_one(int sd, int events, void *discard) { int sock, result; merlin_node *node; struct sockaddr_in sain; socklen_t slen = sizeof(struct sockaddr_in); sock = accept(sd, (struct sockaddr *)&sain, &slen); if (sock < 0) { lerr("accept() failed: %s", strerror(errno)); return -1; } node = find_node(&sain); linfo("NODESTATE: %s connected from %s:%d. Current state is %s", node ? node->name : "An unregistered node", inet_ntoa(sain.sin_addr), ntohs(sain.sin_port), node ? node_state_name(node->state) : "unknown"); if (!node) { close(sock); return 0; } switch (node->state) { case STATE_NEGOTIATING: case STATE_CONNECTED: case STATE_PENDING: /* if node->sock >= 0, we must negotiate which one to use */ if (node->sock >= 0) { int sel_sd = net_negotiate_socket(node, node->sock, sock); if (sel_sd != sock) { close(sock); } } break; case STATE_NONE: /* * we must close it unconditionally or we'll leak fd's * for reconnecting nodes that were previously connected */ node_disconnect(node, "fd leak prevention for connecting nodes"); node->sock = sock; break; default: lerr("%s %s has an unknown status", node_type(node), node->name); break; } node_set_state(node, STATE_NEGOTIATING, "Inbound connection accepted. Negotiating protocol version"); result = iobroker_register(nagios_iobs, node->sock, node, net_input); if (result < 0) { lerr("IOB: Failed to register %d for %s node %s for input events: %s", node->sock, node_type(node), node->name, iobroker_strerror(result)); } return sock; }
/* * Accept an inbound connection from a remote host * Returns 0 on success and -1 on errors */ int net_accept_one(void) { int sock; merlin_node *node; struct sockaddr_in sain; socklen_t slen = sizeof(struct sockaddr_in); /* * we get called from polling_loop(). If so, check for readability * to see if anyone has connected and, if not, return early */ if (!io_read_ok(net_sock, 0)) return -1; sock = accept(net_sock, (struct sockaddr *)&sain, &slen); if (sock < 0) { lerr("accept() failed: %s", strerror(errno)); return -1; } node = find_node(&sain, NULL); linfo("%s connected from %s:%d. Current state is %s", node ? node->name : "An unregistered node", inet_ntoa(sain.sin_addr), ntohs(sain.sin_port), node ? node_state_name(node->state) : "unknown"); if (!node) { close(sock); return 0; } switch (node->state) { case STATE_NEGOTIATING: /* this should *NEVER EVER* happen */ lerr("Aieee! Negotiating connection with one attempting inbound. Bad Thing(tm)"); /* fallthrough */ case STATE_CONNECTED: case STATE_PENDING: /* if node->sock >= 0, we must negotiate which one to use */ node->sock = net_negotiate_socket(node, sock); break; case STATE_NONE: /* * we must close it unconditionally or we'll leak fd's * for reconnecting nodes that were previously connected */ node_disconnect(node, "fd leak prevention for connecting nodes"); node->sock = sock; break; default: lerr("%s %s has an unknown status", node_type(node), node->name); break; } node_set_state(node, STATE_CONNECTED, "Inbound connection accepted or negotiated"); return sock; }
/* close all sockets and release the memory used by * static global vars for networking purposes */ int net_deinit(void) { uint i; for (i = 0; i < num_nodes; i++) { node_disconnect(node_table[i], "Deinitializing networking"); } if (node_table) free(node_table); return 0; }
/* * wraps io_send_all() and adds proper error handling when we run * into sending errors. It's up to the caller to poll the socket * for writability, or pass the proper flags and ignore errors */ int node_send(merlin_node *node, void *data, int len, int flags) { merlin_event *pkt = (merlin_event *)data; int sent, sd = 0; if (!node || node->sock < 0) return 0; if (len >= HDR_SIZE && pkt->hdr.type == CTRL_PACKET) { ldebug("Sending %s to %s", ctrl_name(pkt->hdr.code), node->name); if (pkt->hdr.code == CTRL_ACTIVE) { merlin_nodeinfo *info = (merlin_nodeinfo *)&pkt->body; ldebug(" start time: %lu.%lu", info->start.tv_sec, info->start.tv_usec); ldebug(" config hash: %s", tohex(info->config_hash, 20)); ldebug(" config mtime: %lu", info->last_cfg_change); } } sent = io_send_all(node->sock, data, len); /* success. Should be the normal case */ if (sent == len) { node->stats.bytes.sent += sent; node->last_action = node->last_sent = time(NULL); return sent; } /* * partial writes and complete failures can only be handled * by disconnecting and re-syncing the stream */ sd = node->sock; node_disconnect(node, "Partial or failed write() (sent=%d; len=%d): %s", sent, len, strerror(errno)); if (sent < 0) { /* if we would have blocked, we simply return 0 */ if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; /* otherwise we log the error and disconnect the node */ lerr("Failed to send(%d, %p, %d, %d) to %s: %s", sd, data, len, flags, node->name, strerror(errno)); return sent; } /* partial write. ugh... */ lerr("Partial send() to %s. %d of %d bytes sent", node->name, sent, len); return -1; }
/* close all sockets and release the memory used by * static global vars for networking purposes */ int net_deinit(void) { uint i; for (i = 0; i < num_nodes; i++) { node_disconnect(node_table[i], "Deinitializing networking"); } iobroker_close(nagios_iobs, net_sock); close(net_sock); net_sock = -1; return 0; }
/* * If a node hasn't been heard from in too long, we mark it as no * longer connected and send a CTRL_INACTIVE event to the module, * signalling that our Nagios should, potentially, take over checks * for the awol node */ static void check_node_activity(merlin_node *node) { time_t now = time(NULL); if (node->sock == -1 || node->state != STATE_CONNECTED) return; /* this one's on a reaaaally slow link */ if (!node->data_timeout) return; if (node->last_recv < now - node->data_timeout) node_disconnect(node, "Too long since last action"); }
/* * If a node hasn't been heard from in too long, we mark it as no * longer connected, signalling that we should, potentially, take * over checks for the AWOL node */ void disconnect_inactive(merlin_node *node) { time_t now = time(NULL); unsigned int delta_receive_time = now - node->last_recv; if (node->sock == -1 || node->state != STATE_CONNECTED) return; /* this one's on a reaaaally slow link */ if (!node->data_timeout) return; if (delta_receive_time >= node->data_timeout) node_disconnect(node, "Too long since last action"); }
void tcp_newconn( void ) { struct epoll_event ev; ITEM *listItem = NULL; TCP_NODE *n = NULL; int connfd; IP c_addr; socklen_t c_addrlen = sizeof( IP ); for( ;; ) { /* Prepare incoming connection */ memset( ( char * ) &c_addr, '\0', c_addrlen ); /* Accept */ connfd = accept( _main->tcp->sockfd, ( struct sockaddr * ) &c_addr, &c_addrlen ); if( connfd < 0 ) { if( errno == EAGAIN || errno == EWOULDBLOCK ) { break; } else { fail( strerror( errno ) ); } } /* New connection: Create node object */ if( ( listItem = node_put() ) == NULL ) { info( NULL, "The linked list reached its limits" ); node_disconnect( connfd ); break; } /* Store data */ n = list_value( listItem ); n->connfd = connfd; memcpy( &n->c_addr, &c_addr, c_addrlen ); n->c_addrlen = c_addrlen; /* Non blocking */ if( tcp_nonblocking( n->connfd ) < 0 ) { fail( "tcp_nonblocking() failed" ); } ev.events = EPOLLET | EPOLLIN | EPOLLONESHOT; ev.data.ptr = listItem; if( epoll_ctl( _main->tcp->epollfd, EPOLL_CTL_ADD, n->connfd, &ev ) == -1 ) { info( NULL, strerror( errno ) ); fail( "tcp_newconn: epoll_ctl( ) failed" ); } } }
/* * Read as much data as we possibly can from the node so * that whatever parsing code there is can handle it later. * All information the caller needs will reside in the * nodes own merlin_iocache function, and we return the * number of bytes read, or -1 on errors. * The io-cache buffer must be allocated before we get * to this point, and if the caller wants to poll the * socket for input, it'll have to do so itself. */ int node_recv(merlin_node *node) { int bytes_read; iocache *ioc = node->ioc; if (!node || node->sock < 0) { return -1; } bytes_read = iocache_read(ioc, node->sock); /* * If we read something, update the stat counter * and return. The caller will have to handle the * input as it sees fit */ if (bytes_read > 0) { node->last_action = node->last_recv = time(NULL); node->stats.bytes.read += bytes_read; return bytes_read; } /* no real error, but no new data, so return 0 */ if (errno == EAGAIN || errno == EWOULDBLOCK) { ldebug("No input available from %s node %s.", node_type(node), node->name); return 0; } /* * Remote endpoint shut down, or we ran into some random error * we can't handle any other way than disconnecting the node and * letting the write machinery attempt to reconnect later */ if (bytes_read < 0) { lerr("Failed to read from socket %d into %p for %s node %s: %s", node->sock, ioc, node_type(node), node->name, strerror(errno)); } /* zero-read. We've been disconnected for some reason */ ldebug("bytes_read: %d; errno: %d; strerror(%d): %s", bytes_read, errno, errno, strerror(errno)); node_disconnect(node, "recv() failed"); return -1; }
/* * Check if a socket is connected by looking up * ip and port of the remote host. * Returns 0 if not, and 1 if it is. */ int net_is_connected(merlin_node *node) { struct sockaddr_in sain; socklen_t slen; int optval = 0, gsoerr = 0, gsores = 0, gpnres = 0, gpnerr = 0; if (!node || node->sock < 0) return 0; if (node->state == STATE_CONNECTED) return 1; if (node->state == STATE_NONE) return 0; /* * yes, getpeername() actually has to be here, or getsockopt() * won't return errors when we're not yet connected. It's * important that we read the socket error state though, or * some older kernels will maintain the link in SYN_SENT state * more or less indefinitely, so get all the syscalls taken * care of no matter if they actually work or not. */ errno = 0; slen = sizeof(struct sockaddr_in); gpnres = getpeername(node->sock, (struct sockaddr *)&sain, &slen); gpnerr = errno; slen = sizeof(optval); gsores = getsockopt(node->sock, SOL_SOCKET, SO_ERROR, &optval, &slen); gsoerr = errno; if (!gpnres && !gsores && !optval && !gpnerr && !gsoerr) { return 1; } if (optval) { node_disconnect(node, "connect() to %s node %s (%s:%d) failed: %s", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port), strerror(optval)); return 0; } if (gsores < 0 && gsoerr != ENOTCONN) { node_disconnect(node, "getsockopt(%d) failed for %s node %s: %s", node->sock, node_type(node), node->name, strerror(gsoerr)); } if (gpnres < 0 && gpnerr != ENOTCONN) { lerr("getpeername(%d) failed for %s: %s", node->sock, node->name, strerror(gpnerr)); return 0; } /* * if a connection is in progress, we should be getting * ENOTCONN, but we need to give it time to complete * first. 30 seconds should be enough. */ if (node->last_conn_attempt + MERLIN_CONNECT_TIMEOUT < time(NULL)) { node_disconnect(node, "connect() timed out after %d seconds", MERLIN_CONNECT_TIMEOUT); } return 0; }
/* * Initiate a connection attempt to a node and mark it as PENDING. * Note that since we're using sockets in non-blocking mode (in order * to be able to effectively multiplex), the connection attempt will * never be completed in this function */ int net_try_connect(merlin_node *node) { int sockopt = 1; struct sockaddr *sa = (struct sockaddr *)&node->sain; int should_log = 0; struct timeval connect_timeout = { MERLIN_CONNECT_TIMEOUT, 0 }; struct sockaddr_in sain; time_t interval = MERLIN_CONNECT_INTERVAL; int result; /* don't log obsessively */ if (node->last_conn_attempt_logged + 30 <= time(NULL)) { should_log = 1; node->last_conn_attempt_logged = time(NULL); } if (!(node->flags & MERLIN_NODE_CONNECT)) { if (should_log) { linfo("CONN: Connect attempt blocked by config to %s node %s", node_type(node), node->name); } return 0; } /* don't bother trying to connect if it's pending or done */ switch (node->state) { case STATE_NEGOTIATING: if (node->conn_sock < 0) break; case STATE_CONNECTED: case STATE_PENDING: ldebug("CONN: node %s state is %s, so bailing", node->name, node_state_name(node->state)); return 0; } /* if it's not yet time to connect, don't even try it */ if (node->last_conn_attempt + interval > time(NULL)) { return 0; } /* mark the time so we can time it out ourselves if need be */ node->last_conn_attempt = time(NULL); /* create the socket if necessary */ if (node->conn_sock < 0) { node_disconnect(node, "struct reset (no real disconnect)"); node->conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (node->conn_sock < 0) { lerr("CONN: Failed to obtain connection socket for node %s: %s", node->name, strerror(errno)); lerr("CONN: Aborting connection attempt to %s", node->name); return -1; } } sa->sa_family = AF_INET; if (should_log) { linfo("CONN: Connecting to %s %s@%s:%d", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int))) { ldebug("CONN: Failed to set sockopt SO_REUSEADDR for node %s connect socket %d: %s", node->name, node->conn_sock, strerror(errno)); } if (node->flags & MERLIN_NODE_FIXED_SRCPORT) { ldebug("CONN: Using fixed source port %d for %s node %s", net_source_port(node), node_type(node), node->name); /* * first we bind() to a local port calculated by our own * listening port + the target port. */ sain.sin_family = AF_INET; sain.sin_port = htons(net_source_port(node)); sain.sin_addr.s_addr = 0; if (bind(node->conn_sock, (struct sockaddr *)&sain, sizeof(sain))) { lerr("CONN: Failed to bind() outgoing socket %d for node %s to port %d: %s", node->conn_sock, node->name, ntohs(sain.sin_port), strerror(errno)); if (errno == EBADF || errno == EADDRINUSE) { close(node->conn_sock); node->conn_sock = -1; return -1; } } } if (fcntl(node->conn_sock, F_SETFL, O_NONBLOCK) < 0) { lwarn("CONN: Failed to set socket %d for %s non-blocking: %s", node->conn_sock, node->name, strerror(errno)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_RCVTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("CONN: Failed to set receive timeout for %d, node %s: %s", node->conn_sock, node->name, strerror(errno)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_SNDTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("CONN: Failed to set send timeout for %d, node %s: %s", node->conn_sock, node->name, strerror(errno)); } if (connect(node->conn_sock, sa, sizeof(struct sockaddr_in)) < 0) { if (errno == EINPROGRESS) { /* * non-blocking socket and connect() can't be completed * immediately (ie, the normal case) */ node_set_state(node, STATE_PENDING, "Connecting"); } else if (errno == EALREADY) { ldebug("CONN: Connect already in progress for socket %d to %s. This should never happen", node->conn_sock, node->name); node_set_state(node, STATE_PENDING, "connect() already in progress"); } else { /* a real connection error */ ldebug("CONN: connect() via %d to %s failed: %s", node->conn_sock, node->name, strerror(errno)); close(node->conn_sock); node->conn_sock = -1; if (should_log) { node_disconnect(node, "CONN: connect() failed to %s node '%s' (%s:%d): %s", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port), strerror(errno)); } else { node_disconnect(node, NULL); } return -1; } } result = iobroker_register_out(nagios_iobs, node->conn_sock, node, conn_writable); if (result < 0) { node_disconnect(node, "IOB: Failed to register %s connect socket %d with iobroker: %s", node->name, node->conn_sock, iobroker_strerror(result)); close(node->conn_sock); node->conn_sock = -1; return -1; } return 0; }
/* * Negotiate which socket to use for communication when the remote * host has accepted a connection attempt from us while we have * accepted one from the remote host. We must make sure both ends * agree on one socket to use. */ static int net_negotiate_socket(merlin_node *node, int con, int lis) { struct sockaddr_in lissain, consain; socklen_t slen = sizeof(struct sockaddr_in); linfo("negotiate: Choosing socket for %s %s (%d or %d)", node_type(node), node->name, con, lis); if (con < 0) return lis; if (lis < 0) return con; /* we prefer the socket with the lowest ip-address */ if (getsockname(lis, (struct sockaddr *)&lissain, &slen) < 0) { lerr("negotiate: getsockname(%d, ...) failed: %s", lis, strerror(errno)); return con; } if (getpeername(con, (struct sockaddr *)&consain, &slen) < 0) { lerr("negotiate: getpeername(%d, ...) failed: %s", con, strerror(errno)); return lis; } ldebug("negotiate: lis(%d): %s:%d", lis, inet_ntoa(lissain.sin_addr), ntohs(lissain.sin_port)); ldebug("negotiate: con(%d): %s:%d", con, inet_ntoa(consain.sin_addr), ntohs(consain.sin_port)); if (lissain.sin_addr.s_addr > consain.sin_addr.s_addr) { ldebug("negotiate: con has lowest ip. using that"); return con; } if (consain.sin_addr.s_addr > lissain.sin_addr.s_addr) { ldebug("negotiate: lis has lowest ip. using that"); return lis; } /* * this will happen if multiple merlin instances run * on the same server, such as when we're testing * things. In that case, let the portnumber decide * the tiebreak */ if (lissain.sin_port > consain.sin_port) { ldebug("negotiate: con has lowest port. using that"); return con; } if (consain.sin_port > lissain.sin_port) { ldebug("negotiate: lis has lowest port. using that"); return lis; } ldebug("negotiate: con and lis are equal. killing both"); node->last_conn_attempt_logged = 0; node_disconnect(node, "socket negotiation failed"); iobroker_close(nagios_iobs, lis); node->sock = -1; return -1; }
/* * Initiate a connection attempt to a node and mark it as PENDING. * Note that since we're using sockets in non-blocking mode (in order * to be able to effectively multiplex), the connection attempt will * never be completed in this function */ int net_try_connect(merlin_node *node) { int sockopt = 1; struct sockaddr *sa = (struct sockaddr *)&node->sain; int connected = 0, should_log = 0; struct timeval connect_timeout = { MERLIN_CONNECT_TIMEOUT, 0 }; struct sockaddr_in sain; time_t interval = MERLIN_CONNECT_INTERVAL; /* don't log obsessively */ if (node->last_conn_attempt_logged + 30 <= time(NULL)) { should_log = 1; node->last_conn_attempt_logged = time(NULL); } if (!(node->flags & MERLIN_NODE_CONNECT)) { if (should_log) { linfo("Connect attempt blocked by config to %s node %s", node_type(node), node->name); } return 0; } /* if it's not yet time to connect, don't even try it */ if (node->last_conn_attempt + interval > time(NULL)) { ldebug("connect to %s blocked for %lu more seconds", node->name, node->last_conn_attempt + interval - time(NULL)); return 0; } /* mark the time so we can time it out ourselves if need be */ node->last_conn_attempt = time(NULL); /* create the socket if necessary */ if (node->sock < 0) { node_disconnect(node, "struct reset (no real disconnect)"); node->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (node->sock < 0) { lerr("Failed to obtain socket for node %s: %s", node->name, strerror(errno)); lerr("Aborting connection attempt to %s", node->name); return -1; } } /* * don't try to connect to a node if an attempt is already pending, * but do check if the connection has completed successfully */ if (node->state == STATE_PENDING || node->state == STATE_CONNECTED) { if (net_is_connected(node)) node_set_state(node, STATE_CONNECTED, "Attempted connect completed"); return 0; } sa->sa_family = AF_INET; if (should_log) { linfo("Connecting to %s %s@%s:%d", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port)); } (void)setsockopt(node->sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)); if (node->flags & MERLIN_NODE_FIXED_SRCPORT) { ldebug("Using fixed source port for %s node %s", node_type(node), node->name); /* * first we bind() to a local port calculated by our own * listening port + the target port. */ sain.sin_family = AF_INET; sain.sin_port = htons(net_source_port(node)); sain.sin_addr.s_addr = 0; if (bind(node->sock, (struct sockaddr *)&sain, sizeof(sain))) { lerr("Failed to bind() outgoing socket for node %s to port %d: %s", node->name, ntohs(sain.sin_port), strerror(errno)); } } if (fcntl(node->sock, F_SETFL, O_NONBLOCK) < 0) { lwarn("Failed to set socket for %s non-blocking: %s", node->name, strerror(errno)); } if (setsockopt(node->sock, SOL_SOCKET, SO_RCVTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("Failed to set receive timeout for node %s: %s", node->name, strerror(errno)); } if (setsockopt(node->sock, SOL_SOCKET, SO_SNDTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("Failed to set send timeout for node %s: %s", node->name, strerror(errno)); } if (connect(node->sock, sa, sizeof(struct sockaddr_in)) < 0) { if (errno == EINPROGRESS || errno == EALREADY) { node_set_state(node, STATE_PENDING, "connect() already in progress"); } else if (errno == EISCONN) { connected = 1; } else { if (should_log) { node_disconnect(node, "connect() failed to %s node '%s' (%s:%d): %s", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port), strerror(errno)); } else { node_disconnect(node, NULL); } return -1; } } if (connected || net_is_connected(node)) { linfo("Successfully connected to %s %s@%s:%d", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port)); node_set_state(node, STATE_CONNECTED, "connect() successful"); } else { if (should_log) { linfo("Connection pending to %s %s@%s:%d", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port)); } node_set_state(node, STATE_PENDING, "connect() in progress"); } return 0; }