Beispiel #1
0
Datei: net.c Projekt: op5/merlin
/*
 * 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;
}
Beispiel #2
0
/*
 * 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;
}
Beispiel #3
0
Datei: net.c Projekt: op5/merlin
/*
 * 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;
}
Beispiel #4
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);
	}
}