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; }
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; }
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; }