int TransferQueueManager::HandleRequest(int cmd,Stream *stream)
{
	ReliSock *sock = (ReliSock *)stream;
	ASSERT( cmd == TRANSFER_QUEUE_REQUEST );

	ClassAd msg;
	sock->decode();
	if( !getClassAd( sock, msg ) || !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"TransferQueueManager: failed to receive transfer request "
				"from %s.\n", sock->peer_description() );
		return FALSE;
	}

	bool downloading = false;
	MyString fname;
	MyString jobid;
	MyString queue_user;
	filesize_t sandbox_size;
	if( !msg.LookupBool(ATTR_DOWNLOADING,downloading) ||
		!msg.LookupString(ATTR_FILE_NAME,fname) ||
		!msg.LookupString(ATTR_JOB_ID,jobid) ||
		!msg.LookupString(ATTR_USER,queue_user) ||
		!msg.LookupInteger(ATTR_SANDBOX_SIZE,sandbox_size))
	{
		MyString msg_str;
		sPrintAd(msg_str, msg);
		dprintf(D_ALWAYS,"TransferQueueManager: invalid request from %s: %s\n",
				sock->peer_description(), msg_str.Value());
		return FALSE;
	}

		// Currently, we just create the client with the default max queue
		// age.  If it becomes necessary to customize the maximum age
		// on a case-by-case basis, it should be easy to adjust.

	TransferQueueRequest *client =
		new TransferQueueRequest(
			sock,
			sandbox_size,
			fname.Value(),
			jobid.Value(),
			queue_user.Value(),
			downloading,
			m_default_max_queue_age);

	if( !AddRequest( client ) ) {
		delete client;
		return KEEP_STREAM; // we have already closed this socket
	}

	return KEEP_STREAM;
}
Exemple #2
0
int
CCBServer::HandleRegistration(int cmd,Stream *stream)
{
	ReliSock *sock = (ReliSock *)stream;
	ASSERT( cmd == CCB_REGISTER );

		// Avoid lengthy blocking on communication with our peer.
		// This command-handler should not get called until data
		// is ready to read.
	sock->timeout(1);

	ClassAd msg;
	sock->decode();
	if( !msg.initFromStream( *sock ) || !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"CCB: failed to receive registration "
				"from %s.\n", sock->peer_description() );
		return FALSE;
	}

	SetSmallBuffers(sock);

	MyString name;
	if( msg.LookupString(ATTR_NAME,name) ) {
			// target daemon name is purely for debugging purposes
		name.formatstr_cat(" on %s",sock->peer_description());
		sock->set_peer_description(name.Value());
	}

	CCBTarget *target = new CCBTarget(sock);

	MyString reconnect_cookie_str,reconnect_ccbid_str;
	CCBID reconnect_cookie,reconnect_ccbid;
	bool reconnected = false;
	if( msg.LookupString(ATTR_CLAIM_ID,reconnect_cookie_str) &&
		CCBIDFromString(reconnect_cookie,reconnect_cookie_str.Value()) &&
		msg.LookupString( ATTR_CCBID,reconnect_ccbid_str) &&
		CCBIDFromContactString(reconnect_ccbid,reconnect_ccbid_str.Value()) )
	{
		target->setCCBID( reconnect_ccbid );
		reconnected = ReconnectTarget( target, reconnect_cookie );
	}

	if( !reconnected ) {
		AddTarget( target );
	}

	CCBReconnectInfo *reconnect_info = GetReconnectInfo( target->getCCBID() );
	ASSERT( reconnect_info );

	sock->encode();

	ClassAd reply_msg;
	MyString ccb_contact;
	CCBIDToString( reconnect_info->getReconnectCookie(),reconnect_cookie_str );
		// We send our address as part of the CCB contact string, rather
		// than letting the target daemon fill it in.  This is to give us
		// potential flexibility on the CCB server side to do things like
		// assign different targets to different CCB server sub-processes,
		// each with their own command port.
	CCBIDToContactString( m_address.Value(), target->getCCBID(), ccb_contact );

	reply_msg.Assign(ATTR_CCBID,ccb_contact.Value());
	reply_msg.Assign(ATTR_COMMAND,CCB_REGISTER);
	reply_msg.Assign(ATTR_CLAIM_ID,reconnect_cookie_str.Value());

	if( !reply_msg.put( *sock ) || !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"CCB: failed to send registration response "
				"to %s.\n", sock->peer_description() );

		RemoveTarget( target );
		return KEEP_STREAM; // we have already closed this socket
	}

	return KEEP_STREAM;
}
Exemple #3
0
int
CCBServer::HandleRequest(int cmd,Stream *stream)
{
	ReliSock *sock = (ReliSock *)stream;
	ASSERT( cmd == CCB_REQUEST );

		// Avoid lengthy blocking on communication with our peer.
		// This command-handler should not get called until data
		// is ready to read.
	sock->timeout(1);

	ClassAd msg;
	sock->decode();
	if( !msg.initFromStream( *sock ) || !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"CCB: failed to receive request "
				"from %s.\n", sock->peer_description() );
		return FALSE;
	}

	MyString name;
	if( msg.LookupString(ATTR_NAME,name) ) {
			// client name is purely for debugging purposes
		name.formatstr_cat(" on %s",sock->peer_description());
		sock->set_peer_description(name.Value());
	}
	MyString target_ccbid_str;
	MyString return_addr;
	MyString connect_id; // id target daemon should present to requester
	CCBID target_ccbid;

		// NOTE: using ATTR_CLAIM_ID for connect id so that it is
		// automatically treated as a secret over the network.
		// It must be presented by the target daemon when connecting
		// to the requesting client, so the client can confirm that
		// the connection is in response to its request.

	if( !msg.LookupString(ATTR_CCBID,target_ccbid_str) ||
		!msg.LookupString(ATTR_MY_ADDRESS,return_addr) ||
		!msg.LookupString(ATTR_CLAIM_ID,connect_id) )
	{
		MyString ad_str;
		msg.sPrint(ad_str);
		dprintf(D_ALWAYS,
				"CCB: invalid request from %s: %s\n",
				sock->peer_description(), ad_str.Value() );
		return FALSE;
	}
	if( !CCBIDFromString(target_ccbid,target_ccbid_str.Value()) ) {
		dprintf(D_ALWAYS,
				"CCB: request from %s contains invalid CCBID %s\n",
				sock->peer_description(), target_ccbid_str.Value() );
		return FALSE;
	}

	CCBTarget *target = GetTarget( target_ccbid );
	if( !target ) {
		dprintf(D_ALWAYS,
			"CCB: rejecting request from %s for ccbid %s because no daemon is "
			"currently registered with that id "
			"(perhaps it recently disconnected).\n",
			sock->peer_description(), target_ccbid_str.Value());

		MyString error_msg;
		error_msg.formatstr(
			"CCB server rejecting request for ccbid %s because no daemon is "
			"currently registered with that id "
			"(perhaps it recently disconnected).", target_ccbid_str.Value());
		RequestReply( sock, false, error_msg.Value(), 0, target_ccbid );
		return FALSE;
	}

	SetSmallBuffers(sock);

	CCBServerRequest *request =
		new CCBServerRequest(
			sock,
			target_ccbid,
			return_addr.Value(),
			connect_id.Value() );
	AddRequest( request, target );

	dprintf(D_FULLDEBUG,
			"CCB: received request id %lu from %s for target ccbid %s "
			"(registered as %s)\n",
			request->getRequestID(),
			request->getSock()->peer_description(),
			target_ccbid_str.Value(),
			target->getSock()->peer_description());

	ForwardRequestToTarget( request, target );

	return KEEP_STREAM;
}
SharedPortState::HandlerResult
SharedPortState::HandleUnbound(Stream *&s)
{
	if( !SharedPortClient::SharedPortIdIsValid(m_shared_port_id) ) {
			dprintf(D_ALWAYS,
							"ERROR: SharedPortClient: refusing to connect to shared port"
							"%s, because specified id is illegal! (%s)\n",
							m_requested_by.c_str(), m_shared_port_id );
			return FAILED;
	}

	std::string sock_name;
	std::string alt_sock_name;
	bool has_socket = SharedPortEndpoint::GetDaemonSocketDir(sock_name);;
	bool has_alt_socket = SharedPortEndpoint::GetAltDaemonSocketDir(alt_sock_name);;

	std::stringstream ss;
	ss << sock_name << DIR_DELIM_CHAR << m_shared_port_id;
	sock_name = ss.str();
	m_sock_name = m_shared_port_id;
	ss.str("");
	ss.clear();
	ss << alt_sock_name << DIR_DELIM_CHAR << m_shared_port_id;
	alt_sock_name = ss.str();
	m_shared_port_id = NULL;
	

	if( !m_requested_by.size() ) {
		formatstr(m_requested_by,
				" as requested by %s", m_sock->peer_description());
	}

	struct sockaddr_un named_sock_addr;
	memset(&named_sock_addr, 0, sizeof(named_sock_addr));
	named_sock_addr.sun_family = AF_UNIX;
	struct sockaddr_un alt_named_sock_addr;
	memset(&alt_named_sock_addr, 0, sizeof(alt_named_sock_addr));
	alt_named_sock_addr.sun_family = AF_UNIX;
	unsigned named_sock_addr_len, alt_named_sock_addr_len = 0;
	bool is_no_good;
#ifdef USE_ABSTRACT_DOMAIN_SOCKET
	strncpy(named_sock_addr.sun_path+1, sock_name.c_str(), sizeof(named_sock_addr.sun_path)-2);
	named_sock_addr_len = sizeof(named_sock_addr) - sizeof(named_sock_addr.sun_path) + 1 + strlen(named_sock_addr.sun_path+1);
	is_no_good = strcmp(named_sock_addr.sun_path+1, sock_name.c_str());
#else
	strncpy(named_sock_addr.sun_path, sock_name.c_str(), sizeof(named_sock_addr.sun_path)-1);
	named_sock_addr_len = SUN_LEN(&named_sock_addr);
	is_no_good = strcmp(named_sock_addr.sun_path, sock_name.c_str());
#endif
	if (has_alt_socket) {
		strncpy(alt_named_sock_addr.sun_path, alt_sock_name.c_str(), sizeof(named_sock_addr.sun_path)-1);
		has_alt_socket = !strcmp(alt_named_sock_addr.sun_path, alt_sock_name.c_str());
		alt_named_sock_addr_len = SUN_LEN(&alt_named_sock_addr);
		if (!has_socket && !has_alt_socket) {
			dprintf(D_ALWAYS,"ERROR: SharedPortClient: primary socket is not available and alternate socket name%s is too long: %s\n",
				m_requested_by.c_str(),
				alt_sock_name.c_str());
			return FAILED;
		}
	}

	if( is_no_good ) {
			dprintf(D_ALWAYS,"ERROR: SharedPortClient: full socket name%s is too long: %s\n",
							m_requested_by.c_str(),
							m_sock_name.c_str());
			return FAILED;
	}

	int named_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if( named_sock_fd == -1 ) {
			dprintf(D_ALWAYS,
					"ERROR: SharedPortClient: failed to created named socket%s to connect to %s: %s\n",
					m_requested_by.c_str(),
					m_sock_name.c_str(),
					strerror(errno));
			return FAILED;
	}

	// Make certain SO_LINGER is Off.  This will result in the default
	// of closesocket returning immediately and the system attempts to
	// send any unsent data.

	struct linger linger = {0,0};
	setsockopt(named_sock_fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));

	ReliSock *named_sock = new ReliSock();
	named_sock->assignDomainSocket( named_sock_fd );
	named_sock->set_deadline( m_sock->get_deadline() );

	// If non_blocking requested, put socket into nonblocking mode.
	// Nonblocking mode on a domain socket tells the OS that a connect() call should
	// fail as soon as the daemon's listen queue (backlog) is hit.
	// This is equal to the behavior where the daemon is listening on a network
	// socket (instead of a domain socket) and the TCP socket backlogs.
	if (m_non_blocking) {
		int flags = fcntl(named_sock_fd, F_GETFL, 0);
		fcntl(named_sock_fd, F_SETFL, flags | O_NONBLOCK);
	}

	int connect_rc = 0, connect_errno = 0, p_errno = 0;
	{
		TemporaryPrivSentry sentry(PRIV_ROOT);

		// Note: why not using condor_connect() here?
		// Probably because we are connecting to a unix domain socket here,
		// not a network (ipv4/ipv6) socket.
		if (has_socket)
		{
			connect_rc = connect(named_sock_fd,
					(struct sockaddr *)&named_sock_addr,
					named_sock_addr_len);
			p_errno = connect_errno = errno;
		}
		if (!has_socket || (has_alt_socket && connect_rc && (connect_errno == ENOENT || connect_errno == ECONNREFUSED)))
		{
			int tmp_rc;
			if (!(tmp_rc = connect(named_sock_fd,
				(struct sockaddr *)&alt_named_sock_addr,
				alt_named_sock_addr_len)))
			{
				connect_rc = 0;
				connect_errno = 0;
			}
			if (!has_socket)
			{
				connect_rc = tmp_rc;
				connect_errno = errno;
			}
		}
	}

	if( connect_rc != 0 )
	{
		/* Because we are connecting to a domain socket, we do not
		 * expect connect() to ever return EINPROGRESS.  In fact, inspecting
		 * the Linux 3.7.2 kernel source code, EINPROGRESS errno is indeed impossible
		 * for a unix domain socket.  Hopefully this is true of other platform
		 * as well, like MacOS.  If it is not true, we need to know about it, because
		 * this code will need to be modified to handle an EINPROGRESS via registering
		 * it with daemonCore, etc.  But instead of adding all that complicated code for
		 * a situation we know never occurs on Linux and likely noplace else, we will
		 * simply ASSERT that our expectation is correct.  -Todd Tannenbaum 2/1/2014
		 */
		ASSERT( connect_errno != EINPROGRESS );

		/* The most likely/expected reason the connect fails is because the target daemon's 
		 * listen queue is full, because the target daemon is swamped.
		 * Let's count that situation specifically, so anyone looking
		 * at general connection failures (i.e. m_failPassSocketCalls) can subtract out
		 * this "expected" overload situation to figure out if something else is broken.
		 * Note Linux gives EAGAIN when the listen queue is full (nice!), but other
		 * systems (BSD, MacOS) apparently do not distinguish between no listen posted -vs-
		 * listen queue full, and just return ECONNREFUSED in both events. -Todd Tannenbaum 2/1/2014
		 */
		bool server_busy = false;
		if ( connect_errno == EAGAIN || connect_errno == EWOULDBLOCK ||
			 connect_errno == ETIMEDOUT || connect_errno == ECONNREFUSED )
		{
			SharedPortClient::m_wouldBlockPassSocketCalls++;
			server_busy = true;
		}

		if( has_socket && has_alt_socket ) {
			dprintf( D_ALWAYS, "SharedPortServer:%s failed to connect %s%s: "
				"primary (%s): %s (%d); alt (%s): %s (%d)\n",
				server_busy ? " server was busy," : "",
				m_sock_name.c_str(),
				m_requested_by.c_str(),
				sock_name.c_str(),
				strerror( p_errno ), p_errno,
				alt_sock_name.c_str(),
				strerror( connect_errno ), connect_errno
				);
		} else {
			dprintf(D_ALWAYS,"SharedPortServer:%s failed to connect to %s%s: %s (err=%d)\n",
				server_busy ? " server was busy," : "",
				m_sock_name.c_str(),
				m_requested_by.c_str(),
				strerror(connect_errno),connect_errno);
		}
		delete named_sock;
		return FAILED;
	}

	if (m_non_blocking) {
		int flags = fcntl(named_sock_fd, F_GETFL, 0);
		fcntl(named_sock_fd, F_SETFL, flags & ~O_NONBLOCK);
	}

	s = named_sock;
	m_state = SEND_HEADER;

	return CONTINUE;
}