/* * 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; }
/* * 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; }
void node_set_state(merlin_node *node, int state, const char *reason) { int prev_state, add; if (!node) return; if (node->state == state) return; if (reason) { linfo("NODESTATE: %s: %s -> %s: %s", node->name, node_state_name(node->state), node_state_name(state), reason); } /* * Keep track of active nodes. Setting 'add' to the proper * value means we needn't bother with an insane chain of * if()'s later. * add = +1 if state changes TO 'STATE_CONNECTED'. * add = -1 if state changes FROM 'STATE_CONNECTED' * add remains zero for all other cases */ if (state == STATE_CONNECTED) { add = 1; node->connect_time = time(NULL); } else if (node->state == STATE_CONNECTED) { add = -1; } else { add = 0; } if (node->type == MODE_POLLER) self.active_pollers += add; else if (node->type == MODE_PEER) self.active_peers += add; else if (node->type == MODE_MASTER) self.active_masters += add; prev_state = node->state; node->state = state; if (node->state != STATE_CONNECTED && prev_state != STATE_CONNECTED) return; if (node->action) node->action(node, prev_state); if (node->state == STATE_CONNECTED && node->sock >= 0) { int snd, rcv; socklen_t size = sizeof(int); /* mark this so we can disconnect nodes that never send data */ node->last_recv = time(NULL); merlin_set_socket_options(node->sock, 224 * 1024); getsockopt(node->sock, SOL_SOCKET, SO_SNDBUF, &snd, &size); getsockopt(node->sock, SOL_SOCKET, SO_SNDBUF, &rcv, &size); ldebug("send / receive buffers are %s / %s for node %s", human_bytes(snd), human_bytes(rcv), node->name); } }