示例#1
0
文件: net.c 项目: op5/merlin
/*
 * 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;
}
示例#2
0
文件: node.c 项目: forthewatch/xdm
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);
}
示例#3
0
文件: node.c 项目: ageric/merlin
/*
 * 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;
}
示例#4
0
文件: net.c 项目: 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;
}
示例#5
0
文件: net.c 项目: ageric/merlin
/*
 * 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;
}
示例#6
0
文件: net.c 项目: ageric/merlin
/* 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;
}
示例#7
0
文件: node.c 项目: ageric/merlin
/*
 * 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;
}
示例#8
0
文件: net.c 项目: op5/merlin
/* 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;
}
示例#9
0
文件: net.c 项目: ageric/merlin
/*
 * 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");
}
示例#10
0
文件: net.c 项目: op5/merlin
/*
 * 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");
}
示例#11
0
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" );
		}
	}
}
示例#12
0
文件: node.c 项目: ageric/merlin
/*
 * 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;
}
示例#13
0
文件: net.c 项目: op5/merlin
/*
 * 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;
}
示例#14
0
文件: net.c 项目: 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;
}
示例#15
0
文件: net.c 项目: op5/merlin
/*
 * 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;
}
示例#16
0
文件: net.c 项目: ageric/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 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;
}