// Called when the schedd initially connects to the transferd to finish // the registration process. int TransferD::setup_transfer_request_handler(int /*cmd*/, Stream *sock) { ReliSock *rsock = (ReliSock*)sock; MyString sock_id; dprintf(D_ALWAYS, "Got TRANSFER_CONTROL_CHANNEL!\n"); rsock->decode(); /////////////////////////////////////////////////////////////// // make sure we are authenticated /////////////////////////////////////////////////////////////// if( ! rsock->triedAuthentication() ) { CondorError errstack; if( ! SecMan::authenticate_sock(rsock, WRITE, &errstack) ) { // we failed to authenticate, we should bail out now // since we don't know what user is trying to perform // this action. // TODO: it'd be nice to print out what failed, but we // need better error propagation for that... errstack.push( "TransferD::setup_transfer_request_handler()", 42, "Failure to register transferd - Authentication failed" ); dprintf( D_ALWAYS, "setup_transfer_request_handler() " "aborting: %s\n", errstack.getFullText().c_str() ); refuse(rsock); return CLOSE_STREAM; } } rsock->decode(); /////////////////////////////////////////////////////////////// // Register this socket with a socket handler to handle incoming requests /////////////////////////////////////////////////////////////// sock_id += "<TreqChannel-Socket>"; char* _sock_id = strdup( sock_id.Value() ); //de-const // register the handler for any future transfer requests on this socket. daemonCore->Register_Socket((Sock*)rsock, _sock_id, (SocketHandlercpp)&TransferD::accept_transfer_request_handler, "TransferD::accept_transfer_request_handler", this, ALLOW); free( _sock_id ); dprintf(D_ALWAYS, "Treq channel established.\n"); dprintf(D_ALWAYS, "Accepting Transfer Requests.\n"); return KEEP_STREAM; }
// This handler is called when a client wishes to write files from the // transferd's storage. int TransferD::write_files_handler(int cmd, Stream *sock) { ReliSock *rsock = (ReliSock*)sock; MyString capability; int protocol = FTP_UNKNOWN; TransferRequest *treq = NULL; MyString fquser; static int transfer_reaper_id = -1; ThreadArg *thread_arg; int tid; ClassAd reqad; ClassAd respad; cmd = cmd; // quiet the compiler. dprintf(D_ALWAYS, "Got TRANSFERD_WRITE_FILES!\n"); ///////////////////////////////////////////////////////////////////////// // make sure we are authenticated ///////////////////////////////////////////////////////////////////////// if( ! rsock->triedAuthentication() ) { CondorError errstack; if( ! SecMan::authenticate_sock(rsock, WRITE, &errstack) ) { // we failed to authenticate, we should bail out now // since we don't know what user is trying to perform // this action. // TODO: it'd be nice to print out what failed, but we // need better error propagation for that... errstack.push( "TransferD::setup_transfer_request_handler()", 42, "Failure to register transferd - Authentication failed" ); dprintf( D_ALWAYS, "setup_transfer_request_handler() " "aborting: %s\n", errstack.getFullText() ); refuse( rsock ); return CLOSE_STREAM; } } fquser = rsock->getFullyQualifiedUser(); ///////////////////////////////////////////////////////////////////////// // Check to see if the capability the client tells us is something that // we have knowledge of. We ONLY check the capability and not the // identity of the person in question. This allows people of different // identities to write files here as long as they had the right // capability. While this might not sound secure, they STILL had to have // authenticated as someone this daemon trusts. // Similarly, check the protocol it wants to use as well as ensure that // the direction the transfer request was supposed to be is being honored. ///////////////////////////////////////////////////////////////////////// rsock->decode(); // soak the request ad from the client about what it wants to transfer reqad.initFromStream(*rsock); rsock->end_of_message(); reqad.LookupString(ATTR_TREQ_CAPABILITY, capability); rsock->encode(); // do I know of such a capability? if (m_treqs.lookup(capability, treq) != 0) { // didn't find it. Log it and tell them to leave and close up shop respad.Assign(ATTR_TREQ_INVALID_REQUEST, TRUE); respad.Assign(ATTR_TREQ_INVALID_REASON, "Invalid capability!"); respad.put(*rsock); rsock->end_of_message(); dprintf(D_ALWAYS, "Client identity '%s' tried to write some files " "using capability '%s', but there was no such capability. " "Access denied.\n", fquser.Value(), capability.Value()); return CLOSE_STREAM; } reqad.LookupInteger(ATTR_TREQ_FTP, protocol); // am I willing to use this protocol? switch(protocol) { case FTP_CFTP: // FileTrans protocol, I'm happy. break; default: respad.Assign(ATTR_TREQ_INVALID_REQUEST, TRUE); respad.Assign(ATTR_TREQ_INVALID_REASON, "Invalid file transfer protocol!"); respad.put(*rsock); rsock->end_of_message(); dprintf(D_ALWAYS, "Client identity '%s' tried to write some files " "using protocol '%d', but I don't support that protocol. " "Access denied.\n", fquser.Value(), protocol); return CLOSE_STREAM; } // nsure that this transfer request was of the uploading variety if (treq->get_direction() != FTPD_UPLOAD) { respad.Assign(ATTR_TREQ_INVALID_REQUEST, TRUE); respad.Assign(ATTR_TREQ_INVALID_REASON, "Transfer Request was not an uploading request!"); respad.put(*rsock); rsock->end_of_message(); dprintf(D_ALWAYS, "Client identity '%s' tried to write some files " "to a transfer request that wasn't expecting to be written. " "Access denied.\n", fquser.Value()); } ///////////////////////////////////////////////////////////////////////// // Tell the client everything was ok. ///////////////////////////////////////////////////////////////////////// respad.Assign(ATTR_TREQ_INVALID_REQUEST, FALSE); respad.put(*rsock); rsock->end_of_message(); ///////////////////////////////////////////////////////////////////////// // Set up a thread (a process under unix) to read ALL of the job files // for all of the ads in the TransferRequest. ///////////////////////////////////////////////////////////////////////// // now create a thread, passing in the sock, which uses the file transfer // object to accept the files. if (transfer_reaper_id == -1) { // only set this up ONCE so each and every thread gets one. transfer_reaper_id = daemonCore->Register_Reaper( "write_files_reaper", (ReaperHandlercpp) &TransferD::write_files_reaper, "write_files_reaper", this ); } thread_arg = new ThreadArg(protocol, treq); // Start a new thread (process on Unix) to do the work tid = daemonCore->Create_Thread( (ThreadStartFunc)&TransferD::write_files_thread, (void *)thread_arg, rsock, transfer_reaper_id ); if (tid == FALSE) { // XXX How do I handle this failure? } // associate the tid with the request so I can deal with it propery in // the reaper m_client_to_transferd_threads.insert(tid, treq); // The stream is inherited to the thread, who does the transfer and // finishes the protocol, but in the parent, I'm closing it. return CLOSE_STREAM; }
// Note: logfile is not passed as a reference because we need a local // copy to modify anyhow. bool ReadMultipleUserLogs::monitorLogFile( MyString logfile, bool truncateIfFirst, CondorError &errstack ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs::monitorLogFile(%s, %d)\n", logfile.Value(), truncateIfFirst ); MyString fileID; if ( !GetFileID( logfile, fileID, errstack ) ) { errstack.push( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting file ID in monitorLogFile()" ); return false; } LogFileMonitor *monitor; if ( allLogFiles.lookup( fileID, monitor ) == 0 ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: found " "LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); } else { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: didn't " "find LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); // Make sure the log file is in the correct state -- it must // exist, and be truncated if necessary. if ( !MultiLogFiles::InitializeFile( logfile.Value(), truncateIfFirst, errstack ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error initializing log file %s", logfile.Value() ); return false; } monitor = new LogFileMonitor( logfile ); ASSERT( monitor ); dprintf( D_LOG_FILES, "ReadMultipleUserLogs: created LogFileMonitor " "object for log file %s\n", logfile.Value() ); // Note: we're only putting a pointer to the LogFileMonitor // object into the hash table; the actual LogFileMonitor should // only be deleted in this object's destructor. if ( allLogFiles.insert( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error inserting %s into allLogFiles", logfile.Value() ); delete monitor; return false; } } if ( monitor->refCount < 1 ) { // Open the log file (return to previous location if it was // opened before). if ( monitor->state ) { // If we get here, we've monitored this log file before, // so restore the previous state. if ( monitor->stateError ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Monitoring log file %s fails because of " "previous error saving file state", logfile.Value() ); return false; } monitor->readUserLog = new ReadUserLog( *(monitor->state) ); } else { // Monitoring this log file for the first time, so create // the log reader from scratch. monitor->readUserLog = new ReadUserLog( monitor->logFile.Value() ); } if ( activeLogFiles.insert( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error inserting %s (%s) into activeLogFiles", logfile.Value(), fileID.Value() ); return false; } else { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: added log " "file %s (%s) to active list\n", logfile.Value(), fileID.Value() ); } } monitor->refCount++; return true; }
// Note: logfile is not passed as a reference because we need a local // copy to modify anyhow. bool ReadMultipleUserLogs::unmonitorLogFile( MyString logfile, CondorError &errstack ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs::unmonitorLogFile(%s)\n", logfile.Value() ); MyString fileID; if ( !GetFileID( logfile, fileID, errstack ) ) { errstack.push( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting file ID in unmonitorLogFile()" ); return false; } LogFileMonitor *monitor; if ( activeLogFiles.lookup( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Didn't find LogFileMonitor object for log " "file %s (%s)!", logfile.Value(), fileID.Value() ); dprintf( D_ALWAYS, "ReadMultipleUserLogs error: %s\n", errstack.message() ); printAllLogMonitors( NULL ); return false; } dprintf( D_LOG_FILES, "ReadMultipleUserLogs: found " "LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); monitor->refCount--; if ( monitor->refCount < 1 ) { // Okay, if we are no longer monitoring this file at all, // we need to close it. We do that by saving its state // into a ReadUserLog::FileState object (so we can go back // to the right place if we later monitor it again) and // then deleting the ReadUserLog object. dprintf( D_LOG_FILES, "Closing file <%s>\n", logfile.Value() ); if ( !monitor->state ) { monitor->state = new ReadUserLog::FileState(); if ( !ReadUserLog::InitFileState( *(monitor->state) ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Unable to initialize ReadUserLog::FileState " "object for log file %s", logfile.Value() ); monitor->stateError = true; delete monitor->state; monitor->state = NULL; return false; } } if ( !monitor->readUserLog->GetFileState( *(monitor->state) ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting state for log file %s", logfile.Value() ); monitor->stateError = true; delete monitor->state; monitor->state = NULL; return false; } delete monitor->readUserLog; monitor->readUserLog = NULL; // Now we remove this file from the "active" list, so // we don't check it the next time we get an event. if ( activeLogFiles.remove( fileID ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error removing %s (%s) from activeLogFiles", logfile.Value(), fileID.Value() ); dprintf( D_ALWAYS, "ReadMultipleUserLogs error: %s\n", errstack.message() ); printAllLogMonitors( NULL ); return false; } dprintf( D_LOG_FILES, "ReadMultipleUserLogs: removed " "log file %s (%s) from active list\n", logfile.Value(), fileID.Value() ); } return true; }