static int set_print_mask_from_stream( AttrListPrintMask & print_mask, std::string & constraint, const char * streamid, bool is_filename) { StringList attrs; // used for projection, which we don't currently do. std::string messages; printmask_aggregation_t aggregation; printmask_headerfooter_t headFoot = STD_HEADFOOT; std::vector<GroupByKeyInfo> group_by_keys; SimpleInputStream * pstream = NULL; FILE *file = NULL; if (MATCH == strcmp("-", streamid)) { pstream = new SimpleFileInputStream(stdin, false); } else if (is_filename) { file = safe_fopen_wrapper_follow(streamid, "r"); if (file == NULL) { fprintf(stderr, "Can't open select file: %s\n", streamid); return -1; } pstream = new SimpleFileInputStream(file, true); } else { pstream = new StringLiteralInputStream(streamid); } ASSERT(pstream); int err = SetAttrListPrintMaskFromStream( *pstream, LocalPrintFormatsTable, print_mask, headFoot, aggregation, group_by_keys, constraint, attrs, messages); delete pstream; pstream = NULL; if ( ! err) { customFormat = true; if ( ! constraint.empty()) { ExprTree *constraintExpr=NULL; if ( ! ParseClassAdRvalExpr(constraint.c_str(), constraintExpr)) { formatstr_cat(messages, "WHERE expression is not valid: %s\n", constraint.c_str()); err = -1; } else { delete constraintExpr; } } if (aggregation) { formatstr_cat(messages, "AUTOCLUSTER or UNIQUE aggregation is not supported.\n"); err = -1; } } if ( ! messages.empty()) { fprintf(stderr, "%s", messages.c_str()); } return err; }
void TimerManager::DumpTimerList(int flag, const char* indent) { Timer *timer_ptr; const char *ptmp; // we want to allow flag to be "D_FULLDEBUG | D_DAEMONCORE", // and only have output if _both_ are specified by the user // in the condor_config. this is a little different than // what dprintf does by itself ( which is just // flag & DebugFlags > 0 ), so our own check here: if ( ! IsDebugCatAndVerbosity(flag) ) return; if ( indent == NULL) indent = DEFAULT_INDENT; dprintf(flag, "\n"); dprintf(flag, "%sTimers\n", indent); dprintf(flag, "%s~~~~~~\n", indent); for(timer_ptr = timer_list; timer_ptr != NULL; timer_ptr = timer_ptr->next) { if ( timer_ptr->event_descrip ) ptmp = timer_ptr->event_descrip; else ptmp = "NULL"; std::string slice_desc; if( !timer_ptr->timeslice ) { formatstr(slice_desc, "period = %d, ", timer_ptr->period); } else { formatstr_cat(slice_desc, "timeslice = %.3g, ", timer_ptr->timeslice->getTimeslice()); if( !IS_ZERO(timer_ptr->timeslice->getDefaultInterval()) ) { formatstr_cat(slice_desc, "period = %.1f, ", timer_ptr->timeslice->getDefaultInterval()); } if( !IS_ZERO(timer_ptr->timeslice->getInitialInterval()) ) { formatstr_cat(slice_desc, "initial period = %.1f, ", timer_ptr->timeslice->getInitialInterval()); } if( !IS_ZERO(timer_ptr->timeslice->getMinInterval()) ) { formatstr_cat(slice_desc, "min period = %.1f, ", timer_ptr->timeslice->getMinInterval()); } if( !IS_ZERO(timer_ptr->timeslice->getMaxInterval()) ) { formatstr_cat(slice_desc, "max period = %.1f, ", timer_ptr->timeslice->getMaxInterval()); } } dprintf(flag, "%sid = %d, when = %ld, %shandler_descrip=<%s>\n", indent, timer_ptr->id, (long)timer_ptr->when, slice_desc.c_str(),ptmp); } dprintf(flag, "\n"); }
INFNBatchResource::INFNBatchResource( const char *batch_type, const char * resource_name ) : BaseResource( resource_name ) { m_batchType = batch_type; m_gahpIsRemote = false; m_remoteHostname = resource_name; size_t pos = m_remoteHostname.find( '@' ); if ( pos != m_remoteHostname.npos ) { m_remoteHostname.erase( 0, pos + 1 ); } gahp = NULL; std::string gahp_name = batch_type; if ( resource_name && *resource_name ) { formatstr_cat( gahp_name, "/%s", resource_name ); m_gahpIsRemote = true; } gahp = new GahpClient( gahp_name.c_str() ); gahp->setNotificationTimerId( pingTimerId ); gahp->setMode( GahpClient::normal ); gahp->setTimeout( INFNBatchJob::gahpCallTimeout ); }
const char * INFNBatchResource::HashName( const char * batch_type, const char * resource_name ) { static std::string hash_name; formatstr( hash_name, "batch %s", batch_type ); if ( resource_name && resource_name[0] ) { formatstr_cat( hash_name, " %s", resource_name ); } return hash_name.c_str(); }
std::string SourceRoute::serialize() { std::string rv; formatstr( rv, "p=\"%s\"; a=\"%s\"; port=%d; n=\"%s\";", condor_protocol_to_str( p ).c_str(), a.c_str(), port, n.c_str() ); if(! alias.empty()) { rv += " alias=\"" + alias + "\";"; } if(! spid.empty()) { rv += " spid=\"" + spid + "\";"; } if(! ccbid.empty()) { rv += " ccbid=\"" + ccbid + "\";"; } if(! ccbspid.empty()) { rv += " ccbspid=\"" + ccbspid + "\";"; } if( noUDP ) { rv += " noUDP=true;"; } if( brokerIndex != -1 ) { formatstr_cat( rv, " brokerIndex=%d;", brokerIndex ); } formatstr( rv, "[ %s ]", rv.c_str() ); return rv; }
void CondorResource::CondorRegisterJob( CondorJob *job, const char *submitter_id ) { BaseResource::RegisterJob( job ); if ( submitter_ids.contains( submitter_id ) == false ) { submitter_ids.append( submitter_id ); if ( submitter_constraint.empty() ) { formatstr( submitter_constraint, "(%s=?=\"%s\")", ATTR_SUBMITTER_ID, submitter_id ); } else { formatstr_cat( submitter_constraint, "||(%s=?=\"%s\")", ATTR_SUBMITTER_ID, submitter_id ); } } }
std::string makeHostname(ClassAd *machineAd, ClassAd *jobAd) { std::string hostname; std::string owner("unknown"); jobAd->LookupString(ATTR_OWNER, owner); hostname += owner; int cluster = 1; int proc = 1; jobAd->LookupInteger(ATTR_CLUSTER_ID, cluster); jobAd->LookupInteger(ATTR_PROC_ID, proc); formatstr_cat(hostname, "-%d.%d-", cluster, proc); std::string machine("host"); machineAd->LookupString(ATTR_MACHINE, machine); hostname += machine; return hostname; }
void BaseResource::UpdateLeases() { dprintf(D_FULLDEBUG,"*** UpdateLeases called\n"); if ( hasLeases == false ) { dprintf(D_FULLDEBUG," Leases not supported, cancelling timer\n" ); daemonCore->Cancel_Timer( updateLeasesTimerId ); updateLeasesTimerId = TIMER_UNSET; return; } // Don't start a new lease update too soon after the previous one. int delay; delay = (lastUpdateLeases + UPDATE_LEASE_DELAY) - time(NULL); if ( delay > 0 ) { daemonCore->Reset_Timer( updateLeasesTimerId, delay ); dprintf(D_FULLDEBUG," UpdateLeases: last update too recent, delaying %d secs\n",delay); return; } daemonCore->Reset_Timer( updateLeasesTimerId, TIMER_NEVER ); if ( updateLeasesActive == false ) { BaseJob *curr_job; int new_lease_duration = INT_MAX; dprintf(D_FULLDEBUG," UpdateLeases: calc'ing new leases\n"); registeredJobs.Rewind(); while ( registeredJobs.Next( curr_job ) ) { int job_lease_duration = m_defaultLeaseDuration; curr_job->jobAd->LookupInteger( ATTR_JOB_LEASE_DURATION, job_lease_duration ); if ( job_lease_duration > 0 && job_lease_duration < new_lease_duration ) { new_lease_duration = job_lease_duration; } } dprintf(D_FULLDEBUG," UpdateLeases: new shared lease duration: %d\n", new_lease_duration ); // This is how close to the lease expiration time we want to be // when we try a renewal. int renew_threshold = ( new_lease_duration * 2 / 3 ) + 10; if ( new_lease_duration == INT_MAX || m_sharedLeaseExpiration > time(NULL) + renew_threshold ) { // Lease doesn't need renewal, yet. time_t next_renew_time = m_sharedLeaseExpiration - renew_threshold; if ( new_lease_duration == INT_MAX || next_renew_time > time(NULL) + 3600 ) { next_renew_time = time(NULL) + 3600; } dprintf(D_FULLDEBUG," UpdateLeases: nothing to renew, resetting timer for %ld secs\n",next_renew_time - time(NULL)); lastUpdateLeases = time(NULL); daemonCore->Reset_Timer( updateLeasesTimerId, next_renew_time - time(NULL) ); return; } else { // Time to renew the lease m_sharedLeaseExpiration = time(NULL) + new_lease_duration; if ( !m_hasSharedLeases ) { registeredJobs.Rewind(); while ( registeredJobs.Next( curr_job ) ) { std::string job_id; int tmp; if ( curr_job->jobAd->LookupString( ATTR_GRID_JOB_ID, job_id ) && curr_job->jobAd->LookupInteger( ATTR_JOB_LEASE_DURATION, tmp ) ) { leaseUpdates.Append( curr_job ); } } } dprintf(D_FULLDEBUG," new shared lease expiration at %ld, performing renewal...\n",m_sharedLeaseExpiration); requestScheddUpdateNotification( updateLeasesTimerId ); updateLeasesActive = true; } } unsigned update_delay = 0; bool update_complete; SimpleList<PROC_ID> update_succeeded; bool update_success; dprintf(D_FULLDEBUG," UpdateLeases: calling DoUpdateLeases\n"); if ( m_hasSharedLeases ) { DoUpdateSharedLease( update_delay, update_complete, update_success ); } else { DoUpdateLeases( update_delay, update_complete, update_succeeded ); } if ( update_delay ) { daemonCore->Reset_Timer( updateLeasesTimerId, update_delay ); dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases wants delay of %u secs\n",update_delay); return; } if ( !update_complete ) { updateLeasesCmdActive = true; dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases in progress\n"); return; } dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases complete, processing results\n"); bool first_update = lastUpdateLeases == 0; updateLeasesCmdActive = false; lastUpdateLeases = time(NULL); if ( m_hasSharedLeases ) { BaseJob *curr_job; std::string tmp; registeredJobs.Rewind(); while ( registeredJobs.Next( curr_job ) ) { if ( first_update ) { // New jobs may be waiting for the lease be to established // before they proceed with submission. curr_job->SetEvaluateState(); } if ( curr_job->jobAd->LookupString( ATTR_GRID_JOB_ID, tmp ) ) { curr_job->UpdateJobLeaseSent( m_sharedLeaseExpiration ); } } } else { std::string msg = " update_succeeded:"; BaseJob *curr_job; PROC_ID curr_id; update_succeeded.Rewind(); while ( update_succeeded.Next( curr_id ) ) { formatstr_cat(msg, " %d.%d", curr_id.cluster, curr_id.proc); if ( BaseJob::JobsByProcId.lookup( curr_id, curr_job ) == 0 ) { curr_job->UpdateJobLeaseSent( m_sharedLeaseExpiration ); } } dprintf(D_FULLDEBUG,"%s\n",msg.c_str()); leaseUpdates.Clear(); } updateLeasesActive = false; dprintf(D_FULLDEBUG," UpdateLeases: lease update complete, resetting timer for 30 secs\n"); daemonCore->Reset_Timer( updateLeasesTimerId, UPDATE_LEASE_DELAY ); }
int Starter::execDCStarter( ArgList const &args, Env const *env, int* std_fds, Stream* s ) { Stream *inherit_list[] = { 0 /*ClassAd update stream (assigned below)*/, s /*shadow syscall sock*/, 0 /*terminal NULL*/ }; const ArgList* final_args = &args; const char* final_path = s_path; Env new_env; if( env ) { new_env.MergeFrom( *env ); } // The starter figures out its execute directory by paraming // for EXECUTE, which we override in the environment here. // This way, all the logic about choosing a directory to use // is in only one place. ASSERT( executeDir() ); new_env.SetEnv( "_CONDOR_EXECUTE", executeDir() ); env = &new_env; // Build the affinity string to pass to the starter via env std::string affinityString; if (s_claim && s_claim->rip() && s_claim->rip()->get_affinity_set()) { std::list<int> *l = s_claim->rip()->get_affinity_set(); bool needComma = false; for (std::list<int>::iterator it = l->begin(); it != l->end(); it++) { if (needComma) { formatstr_cat(affinityString, ", %d", *it); } else { formatstr_cat(affinityString, "%d ", *it); needComma = true; } } } int slotId = s_claim->rip()->r_sub_id; if (slotId == 0) { // Isn't a paritionable slot, use the main id slotId = s_claim->rip()->r_id; } std::string affinityKnob; formatstr(affinityKnob, "_CONDOR_SLOT%d_CPU_AFFINITY", slotId); if (param_boolean("ASSIGN_CPU_AFFINITY", false)) { new_env.SetEnv(affinityKnob.c_str(), affinityString.c_str()); new_env.SetEnv("_CONDOR_ENFORCE_CPU_AFFINITY", "true"); dprintf(D_FULLDEBUG, "Setting affinity env to %s = %s\n", affinityKnob.c_str(), affinityString.c_str()); } ReliSock child_job_update_sock; // child inherits this socket ASSERT( !s_job_update_sock ); s_job_update_sock = new ReliSock; // parent (yours truly) keeps this socket ASSERT( s_job_update_sock ); // Connect parent and child sockets together so child can send us // udpates to the job ClassAd. if( !s_job_update_sock->connect_socketpair( child_job_update_sock ) ) { dprintf( D_ALWAYS, "ERROR: Failed to create job ClassAd update socket!\n"); s_pid = 0; return s_pid; } inherit_list[0] = &child_job_update_sock; // Pass the machine ad to the starter if (s_claim) s_claim->writeMachAd( s_job_update_sock ); if( daemonCore->Register_Socket( s_job_update_sock, "starter ClassAd update socket", (SocketHandlercpp)&Starter::receiveJobClassAdUpdate, "receiveJobClassAdUpdate", this) < 0 ) { EXCEPT("Failed to register ClassAd update socket."); } #if defined(LINUX) // see if we should be using glexec to spawn the starter. // if we are, the cmd, args, env, and stdin to use will be // modified ArgList glexec_args; Env glexec_env; int glexec_std_fds[3]; if( param_boolean( "GLEXEC_STARTER", false ) ) { if( ! glexec_starter_prepare( s_path, s_claim->client()->proxyFile(), args, env, std_fds, glexec_args, glexec_env, glexec_std_fds ) ) { // something went wrong; prepareForGlexec will // have already logged it cleanupAfterGlexec(); return 0; } final_path = glexec_args.GetArg(0); final_args = &glexec_args; env = &glexec_env; std_fds = glexec_std_fds; } #endif int reaper_id; if( s_reaper_id > 0 ) { reaper_id = s_reaper_id; } else { reaper_id = main_reaper; } if(IsFulldebug(D_FULLDEBUG)) { MyString args_string; final_args->GetArgsStringForDisplay(&args_string); dprintf( D_FULLDEBUG, "About to Create_Process \"%s\"\n", args_string.Value() ); } FamilyInfo fi; fi.max_snapshot_interval = pid_snapshot_interval; s_pid = daemonCore-> Create_Process( final_path, *final_args, PRIV_ROOT, reaper_id, TRUE, env, NULL, &fi, inherit_list, std_fds ); if( s_pid == FALSE ) { dprintf( D_ALWAYS, "ERROR: exec_starter failed!\n"); s_pid = 0; } #if defined(LINUX) if( param_boolean( "GLEXEC_STARTER", false ) ) { // if we used glexec to spawn the Starter, we now need to send // the Starter's environment to our glexec wrapper script so it // can exec the Starter with all the environment variablew we rely // on it inheriting // if ( !glexec_starter_handle_env(s_pid) ) { // something went wrong; handleGlexecEnvironment will // have already logged it cleanupAfterGlexec(); return 0; } } #endif return s_pid; }
// Read a stream a line at a time, and parse it to fill out the print mask, // header, group_by, where expression, and projection attributes. // int SetAttrListPrintMaskFromStream ( SimpleInputStream & stream, // in: fetch lines from this stream until nextline() returns NULL const CustomFormatFnTable & FnTable, // in: table of custom output functions for SELECT AttrListPrintMask & mask, // out: columns and headers set in SELECT printmask_headerfooter_t & headfoot, // out: header and footer flags set in SELECT or SUMMARY printmask_aggregation_t & aggregate, // out: aggregation mode in SELECT std::vector<GroupByKeyInfo> & group_by, // out: ordered set of attributes/expressions in GROUP BY std::string & where_expression, // out: classad expression from WHERE StringList & attrs, // out ClassAd attributes referenced in mask or group_by outputs std::string & error_message) // out, if return is non-zero, this will be an error message { ClassAd ad; // so we can GetExprReferences enum section_t { NOWHERE=0, SELECT, SUMMARY, WHERE, GROUP}; enum cust_t { PRINTAS_STRING, PRINTAS_INT, PRINTAS_FLOAT }; bool label_fields = false; const char * labelsep = " = "; const char * prowpre = NULL; const char * pcolpre = " "; const char * pcolsux = NULL; const char * prowsux = "\n"; mask.SetAutoSep(prowpre, pcolpre, pcolsux, prowsux); error_message.clear(); aggregate = PR_NO_AGGREGATION; printmask_headerfooter_t usingHeadFoot = (printmask_headerfooter_t)(HF_CUSTOM | HF_NOSUMMARY); section_t sect = SELECT; tokener toke(""); while (toke.set(stream.nextline())) { if ( ! toke.next()) continue; if (toke.matches("#")) continue; if (toke.matches("SELECT")) { while (toke.next()) { if (toke.matches("FROM")) { if (toke.next()) { if (toke.matches("AUTOCLUSTER")) { aggregate = PR_FROM_AUTOCLUSTER; } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Warning: Unknown header argument %s for SELECT FROM\n", aa.c_str()); } } } else if (toke.matches("UNIQUE")) { aggregate = PR_COUNT_UNIQUE; } else if (toke.matches("BARE")) { usingHeadFoot = HF_BARE; } else if (toke.matches("NOTITLE")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOTITLE); } else if (toke.matches("NOHEADER")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOHEADER); } else if (toke.matches("NOSUMMARY")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOSUMMARY); } else if (toke.matches("LABEL")) { label_fields = true; } else if (label_fields && toke.matches("SEPARATOR")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); labelsep = mask.store(tmp.c_str()); } } else if (toke.matches("RECORDPREFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); prowpre = mask.store(tmp.c_str()); } } else if (toke.matches("RECORDSUFFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); prowsux = mask.store(tmp.c_str()); } } else if (toke.matches("FIELDPREFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); pcolpre = mask.store(tmp.c_str()); } } else if (toke.matches("FIELDSUFFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); pcolsux = mask.store(tmp.c_str()); } } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Warning: Unknown header argument %s for SELECT\n", aa.c_str()); } } mask.SetAutoSep(prowpre, pcolpre, pcolsux, prowsux); sect = SELECT; continue; } else if (toke.matches("WHERE")) { sect = WHERE; if ( ! toke.next()) continue; } else if (toke.matches("GROUP")) { sect = GROUP; if ( ! toke.next() || (toke.matches("BY") && ! toke.next())) continue; } else if (toke.matches("SUMMARY")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot & ~HF_NOSUMMARY); while (toke.next()) { if (toke.matches("STANDARD")) { // attrs.insert(ATTR_JOB_STATUS); } else if (toke.matches("NONE")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOSUMMARY); } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Unknown argument %s for SELECT\n", aa.c_str()); } } sect = SUMMARY; continue; } switch (sect) { case SELECT: { toke.mark(); std::string attr; std::string name; int opts = FormatOptionAutoWidth | FormatOptionNoTruncate; const char * fmt = "%v"; int wid = 0; CustomFormatFn cust; bool got_attr = false; while (toke.next()) { const Keyword * pkw = SelectKeywords.find_match(toke); if ( ! pkw) continue; // next token is a keyword, if we havent set the attribute yet // it's everything up to the current token. int kw = pkw->value; if ( ! got_attr) { toke.copy_marked(attr); got_attr = true; } switch (kw) { case kw_AS: { if (toke.next()) { toke.copy_token(name); if (toke.is_quoted_string()) { collapse_escapes(name); } } else { expected_token(error_message, "column name after AS", "SELECT", stream, toke); } toke.mark_after(); } break; case kw_PRINTF: { if (toke.next()) { std::string val; toke.copy_token(val); fmt = mask.store(val.c_str()); } else { expected_token(error_message, "format after PRINTF", "SELECT", stream, toke); } } break; case kw_PRINTAS: { if (toke.next()) { const CustomFormatFnTableItem * pcffi = FnTable.find_match(toke); if (pcffi) { cust = pcffi->cust; //cust_type = pcffi->cust; const char * pszz = pcffi->extra_attribs; if (pszz) { size_t cch = strlen(pszz); while (cch > 0) { attrs.insert(pszz); pszz += cch+1; cch = strlen(pszz); } } } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Unknown argument %s for PRINTAS\n", aa.c_str()); } } else { expected_token(error_message, "function name after PRINTAS", "SELECT", stream, toke); } } break; case kw_NOSUFFIX: { opts |= FormatOptionNoSuffix; } break; case kw_NOPREFIX: { opts |= FormatOptionNoPrefix; } break; case kw_LEFT: { opts |= FormatOptionLeftAlign; } break; case kw_RIGHT: { opts &= ~FormatOptionLeftAlign; } break; case kw_TRUNCATE: { opts &= ~FormatOptionNoTruncate; } break; case kw_WIDTH: { if (toke.next()) { std::string val; toke.copy_token(val); if (toke.matches("AUTO")) { opts |= FormatOptionAutoWidth; } else { wid = atoi(val.c_str()); //if (wid) opts &= ~FormatOptionAutoWidth; //PRAGMA_REMIND("TJ: decide how LEFT & RIGHT interact with pos and neg widths." } } else { expected_token(error_message, "number or AUTO after WIDTH", "SELECT", stream, toke); } } break; default: unexpected_token(error_message, "SELECT", stream, toke); break; } // switch } // while if ( ! got_attr) { attr = toke.content(); } trim(attr); if (attr.empty() || attr[0] == '#') continue; const char * lbl = name.empty() ? attr.c_str() : name.c_str(); if (label_fields) { // build a format string that contains the label std::string label(lbl); if (labelsep) { label += labelsep; } if (fmt) { label += fmt; } else { label += "%"; if (wid) { if (opts & FormatOptionNoTruncate) formatstr_cat(label, "%d", wid); else formatstr_cat(label, "%d.%d", wid, wid < 0 ? -wid : wid); } label += cust ? "s" : "v"; } lbl = mask.store(label.c_str()); fmt = lbl; wid = 0; } else { if ( ! wid) { wid = 0 - (int)strlen(lbl); } mask.set_heading(lbl); lbl = NULL; } if (cust) { mask.registerFormat (lbl, wid, opts, cust, attr.c_str()); } else { mask.registerFormat(fmt, wid, opts, attr.c_str()); } ad.GetExprReferences(attr.c_str(), NULL, &attrs); } break; case WHERE: { toke.copy_to_end(where_expression); trim(where_expression); } break; case SUMMARY: { } break; case GROUP: { toke.mark(); GroupByKeyInfo key; // in case we end up finding no keywords, copy the remainder of the line now as the expression toke.copy_to_end(key.expr); bool got_expr = false; while (toke.next()) { const Keyword * pgw = GroupKeywords.find_match(toke); if ( ! pgw) continue; // got a keyword int gw = pgw->value; if ( ! got_expr) { toke.copy_marked(key.expr); got_expr = true; } switch (gw) { case gw_AS: { if (toke.next()) { toke.copy_token(key.name); } toke.mark_after(); } break; case gw_DECENDING: { key.decending = true; toke.mark_after(); } break; case gw_ASCENDING: { key.decending = false; toke.mark_after(); } break; default: unexpected_token(error_message, "GROUP BY", stream, toke); break; } // switch } // while toke.next trim(key.expr); if (key.expr.empty() || key.expr[0] == '#') continue; if ( ! ad.GetExprReferences(key.expr.c_str(), NULL, &attrs)) { formatstr_cat(error_message, "GROUP BY expression is not valid: %s\n", key.expr.c_str()); } else { group_by.push_back(key); } } break; default: break; } } headfoot = usingHeadFoot; return 0; }
static void expected_token(std::string & message, const char * reason, const char * tag, SimpleInputStream & stream, tokener & toke) { std::string tok; toke.copy_token(tok); formatstr_cat(message, "expected %s at line %d offset %d in %s\n", reason, stream.count_of_lines_read(), (int)toke.offset(), tag); }
int Starter::execDCStarter( ArgList const &args, Env const *env, int* std_fds, Stream* s ) { Stream *inherit_list[] = { 0 /*ClassAd update stream (assigned below)*/, s /*shadow syscall sock*/, 0 /*terminal NULL*/ }; const ArgList* final_args = &args; const char* final_path = s_path; Env new_env; if( env ) { new_env.MergeFrom( *env ); } // The starter figures out its execute directory by paraming // for EXECUTE, which we override in the environment here. // This way, all the logic about choosing a directory to use // is in only one place. ASSERT( executeDir() ); new_env.SetEnv( "_CONDOR_EXECUTE", executeDir() ); // Handle encrypted execute directory FilesystemRemap fs_remap_obj; // put on stack so destroyed when leave this method FilesystemRemap* fs_remap = NULL; // If admin desires encrypted exec dir in config, do it bool encrypt_execdir = param_boolean_crufty("ENCRYPT_EXECUTE_DIRECTORY",false); // Or if user wants encrypted exec in job ad, do it if (!encrypt_execdir && s_claim->ad()) { s_claim->ad()->LookupBool(ATTR_ENCRYPT_EXECUTE_DIRECTORY,encrypt_execdir); } if ( encrypt_execdir ) { #ifdef LINUX // On linux, setup a directory $EXECUTE/encryptedX subdirectory // to serve as an ecryptfs mount point; pass this directory // down to the condor_starter as if it were $EXECUTE so // that the starter creates its dir_<pid> directory on the // ecryptfs filesystem setup by doing an AddEncryptedMapping. static int unsigned long privdirnum = 0; TemporaryPrivSentry sentry(PRIV_CONDOR); s_encrypted_execute_dir.formatstr("%s%cencrypted%lu",executeDir(), DIR_DELIM_CHAR,privdirnum++); if( mkdir(encryptedExecuteDir(), 0755) < 0 ) { dprintf( D_FAILURE|D_ALWAYS, "Failed to create encrypted dir %s: %s\n", encryptedExecuteDir(), strerror(errno) ); return 0; } dprintf( D_ALWAYS, "Created encrypted dir %s\n", encryptedExecuteDir() ); fs_remap = &fs_remap_obj; if ( fs_remap->AddEncryptedMapping(encryptedExecuteDir()) ) { // FilesystemRemap object dprintfs out an error message for us return 0; } new_env.SetEnv( "_CONDOR_EXECUTE", encryptedExecuteDir() ); #endif } env = &new_env; // Build the affinity string to pass to the starter via env std::string affinityString; if (s_claim && s_claim->rip() && s_claim->rip()->get_affinity_set()) { std::list<int> *l = s_claim->rip()->get_affinity_set(); bool needComma = false; for (std::list<int>::iterator it = l->begin(); it != l->end(); it++) { if (needComma) { formatstr_cat(affinityString, ", %d", *it); } else { formatstr_cat(affinityString, "%d ", *it); needComma = true; } } } bool affinityBool = false; if ( ! s_claim || ! s_claim->ad()) { affinityBool = param_boolean("ASSIGN_CPU_AFFINITY", false); } else { auto_free_ptr assign_cpu_affinity(param("ASSIGN_CPU_AFFINITY")); if ( ! assign_cpu_affinity.empty()) { classad::Value value; if (s_claim->ad()->EvaluateExpr(assign_cpu_affinity.ptr(), value)) { if ( ! value.IsBooleanValueEquiv(affinityBool)) { // was an expression, but not a bool, so report it and continue EXCEPT("ASSIGN_CPU_AFFINITY does not evaluate to a boolean, it is : %s", ClassAdValueToString(value)); } } } } if (affinityBool) { new_env.SetEnv("_CONDOR_STARTD_ASSIGNED_AFFINITY", affinityString.c_str()); new_env.SetEnv("_CONDOR_ENFORCE_CPU_AFFINITY", "true"); dprintf(D_ALWAYS, "Setting affinity env to %s\n", affinityString.c_str()); } ReliSock child_job_update_sock; // child inherits this socket ASSERT( !s_job_update_sock ); s_job_update_sock = new ReliSock; // parent (yours truly) keeps this socket ASSERT( s_job_update_sock ); // Connect parent and child sockets together so child can send us // udpates to the job ClassAd. if( !s_job_update_sock->connect_socketpair( child_job_update_sock ) ) { dprintf( D_ALWAYS, "ERROR: Failed to create job ClassAd update socket!\n"); s_pid = 0; return s_pid; } inherit_list[0] = &child_job_update_sock; // Pass the machine ad to the starter if (s_claim) s_claim->writeMachAd( s_job_update_sock ); if( daemonCore->Register_Socket( s_job_update_sock, "starter ClassAd update socket", (SocketHandlercpp)&Starter::receiveJobClassAdUpdate, "receiveJobClassAdUpdate", this) < 0 ) { EXCEPT("Failed to register ClassAd update socket."); } #if defined(LINUX) // see if we should be using glexec to spawn the starter. // if we are, the cmd, args, env, and stdin to use will be // modified ArgList glexec_args; Env glexec_env; int glexec_std_fds[3]; if( param_boolean( "GLEXEC_STARTER", false ) ) { if( ! glexec_starter_prepare( s_path, s_claim->client()->proxyFile(), args, env, std_fds, glexec_args, glexec_env, glexec_std_fds ) ) { // something went wrong; prepareForGlexec will // have already logged it cleanupAfterGlexec(); return 0; } final_path = glexec_args.GetArg(0); final_args = &glexec_args; env = &glexec_env; std_fds = glexec_std_fds; } #endif int reaper_id; if( s_reaper_id > 0 ) { reaper_id = s_reaper_id; } else { reaper_id = main_reaper; } if(IsFulldebug(D_FULLDEBUG)) { MyString args_string; final_args->GetArgsStringForDisplay(&args_string); dprintf( D_FULLDEBUG, "About to Create_Process \"%s\"\n", args_string.Value() ); } FamilyInfo fi; fi.max_snapshot_interval = pid_snapshot_interval; s_pid = daemonCore-> Create_Process( final_path, *final_args, PRIV_ROOT, reaper_id, TRUE, TRUE, env, NULL, &fi, inherit_list, std_fds, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, fs_remap); if( s_pid == FALSE ) { dprintf( D_ALWAYS, "ERROR: exec_starter failed!\n"); s_pid = 0; } #if defined(LINUX) if( param_boolean( "GLEXEC_STARTER", false ) ) { // if we used glexec to spawn the Starter, we now need to send // the Starter's environment to our glexec wrapper script so it // can exec the Starter with all the environment variablew we rely // on it inheriting // if ( !glexec_starter_handle_env(s_pid) ) { // something went wrong; handleGlexecEnvironment will // have already logged it cleanupAfterGlexec(); return 0; } } #endif return s_pid; }
void BaseResource::UpdateLeases() { dprintf(D_FULLDEBUG,"*** UpdateLeases called\n"); if ( hasLeases == false ) { dprintf(D_FULLDEBUG," Leases not supported, cancelling timer\n" ); daemonCore->Cancel_Timer( updateLeasesTimerId ); updateLeasesTimerId = TIMER_UNSET; return; } // Don't start a new lease update too soon after the previous one. int delay; delay = (lastUpdateLeases + UPDATE_LEASE_DELAY) - time(NULL); if ( delay > 0 ) { daemonCore->Reset_Timer( updateLeasesTimerId, delay ); dprintf(D_FULLDEBUG," UpdateLeases: last update too recent, delaying %d secs\n",delay); return; } daemonCore->Reset_Timer( updateLeasesTimerId, TIMER_NEVER ); if ( updateLeasesActive == false ) { BaseJob *curr_job; time_t next_renew_time = INT_MAX; time_t job_renew_time; int min_new_expire = INT_MAX; dprintf(D_FULLDEBUG," UpdateLeases: calc'ing new leases\n"); registeredJobs.Rewind(); dprintf(D_FULLDEBUG," starting min_new_expire=%d next_renew_time=%ld\n",min_new_expire,next_renew_time); while ( registeredJobs.Next( curr_job ) ) { int new_expire; std::string job_id; job_renew_time = next_renew_time; // Don't update the lease for a job that isn't submitted // anywhere. The Job object will start the lease when it // submits the job. if ( ( m_hasSharedLeases || curr_job->jobAd->LookupString( ATTR_GRID_JOB_ID, job_id ) ) && CalculateJobLease( curr_job->jobAd, new_expire, m_defaultLeaseDuration, &job_renew_time ) ) { if ( new_expire < min_new_expire ) { min_new_expire = new_expire; } if ( !m_hasSharedLeases ) { curr_job->UpdateJobLeaseSent( new_expire ); leaseUpdates.Append( curr_job ); } } else if ( job_renew_time < next_renew_time ) { next_renew_time = job_renew_time; } dprintf(D_FULLDEBUG," after %d.%d: min_new_expire=%d next_renew_time=%ld job_renew_time=%ld\n",curr_job->procID.cluster,curr_job->procID.proc,min_new_expire,next_renew_time,job_renew_time); } if ( min_new_expire == INT_MAX || ( m_hasSharedLeases && next_renew_time < INT_MAX && m_sharedLeaseExpiration != 0 ) ) { if ( next_renew_time > time(NULL) + 3600 ) { next_renew_time = time(NULL) + 3600; } dprintf(D_FULLDEBUG," UpdateLeases: nothing to renew, resetting timer for %ld secs\n",next_renew_time - time(NULL)); lastUpdateLeases = time(NULL); daemonCore->Reset_Timer( updateLeasesTimerId, next_renew_time - time(NULL) ); } else { if ( m_hasSharedLeases ) { registeredJobs.Rewind(); while ( registeredJobs.Next( curr_job ) ) { std::string job_id; if ( curr_job->jobAd->LookupString( ATTR_GRID_JOB_ID, job_id ) ) { curr_job->UpdateJobLeaseSent( min_new_expire ); } } m_sharedLeaseExpiration = min_new_expire; dprintf(D_FULLDEBUG," new shared lease expiration at %ld, updating job ads...\n",m_sharedLeaseExpiration); } requestScheddUpdateNotification( updateLeasesTimerId ); updateLeasesActive = true; leaseAttrsSynched = false; } return; } if ( leaseAttrsSynched == false ) { bool still_dirty = false; BaseJob *curr_job; leaseUpdates.Rewind(); while ( leaseUpdates.Next( curr_job ) ) { bool exists, dirty; curr_job->jobAd->GetDirtyFlag( ATTR_JOB_LEASE_EXPIRATION, &exists, &dirty ); if ( !exists ) { // What!? The attribute disappeared? Forget about renewing // the lease then dprintf( D_ALWAYS, "Lease attribute disappeared for job %d.%d, ignoring it\n", curr_job->procID.cluster, curr_job->procID.proc ); leaseUpdates.DeleteCurrent(); } if ( dirty ) { still_dirty = true; requestScheddUpdate( curr_job, false ); } } if ( still_dirty ) { requestScheddUpdateNotification( updateLeasesTimerId ); dprintf(D_FULLDEBUG," UpdateLeases: waiting for schedd synch\n"); return; } else dprintf(D_FULLDEBUG," UpdateLeases: leases synched\n"); } leaseAttrsSynched = true; unsigned update_delay = 0; bool update_complete; SimpleList<PROC_ID> update_succeeded; bool update_success; dprintf(D_FULLDEBUG," UpdateLeases: calling DoUpdateLeases\n"); if ( m_hasSharedLeases ) { DoUpdateSharedLease( update_delay, update_complete, update_success ); } else { DoUpdateLeases( update_delay, update_complete, update_succeeded ); } if ( update_delay ) { daemonCore->Reset_Timer( updateLeasesTimerId, update_delay ); dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases wants delay of %uld secs\n",update_delay); return; } if ( !update_complete ) { updateLeasesCmdActive = true; dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases in progress\n"); return; } dprintf(D_FULLDEBUG," UpdateLeases: DoUpdateLeases complete, processing results\n"); bool first_update = lastUpdateLeases == 0; updateLeasesCmdActive = false; lastUpdateLeases = time(NULL); if ( m_hasSharedLeases ) { BaseJob *curr_job; std::string tmp; registeredJobs.Rewind(); while ( registeredJobs.Next( curr_job ) ) { if ( first_update ) { // New jobs may be waiting for the lease be to established // before they proceed with submission. curr_job->SetEvaluateState(); } if ( !curr_job->jobAd->LookupString( ATTR_GRID_JOB_ID, tmp ) ) { continue; } bool curr_renewal_failed = !update_success; bool last_renewal_failed = false; curr_job->jobAd->LookupBool( ATTR_LAST_JOB_LEASE_RENEWAL_FAILED, last_renewal_failed ); if ( curr_renewal_failed != last_renewal_failed ) { curr_job->jobAd->Assign( ATTR_LAST_JOB_LEASE_RENEWAL_FAILED, curr_renewal_failed ); requestScheddUpdate( curr_job, false ); } } } else { update_succeeded.Rewind(); PROC_ID id; std::string msg = " update_succeeded:"; while(update_succeeded.Next(id)) formatstr_cat(msg, " %d.%d", id.cluster, id.proc); dprintf(D_FULLDEBUG,"%s\n",msg.c_str()); BaseJob *curr_job; leaseUpdates.Rewind(); while ( leaseUpdates.Next( curr_job ) ) { bool curr_renewal_failed; bool last_renewal_failed = false; if ( update_succeeded.IsMember( curr_job->procID ) ) { dprintf(D_FULLDEBUG," %d.%d is in succeeded list\n",curr_job->procID.cluster,curr_job->procID.proc); curr_renewal_failed = false; } else { dprintf(D_FULLDEBUG," %d.%d is not in succeeded list\n",curr_job->procID.cluster,curr_job->procID.proc); curr_renewal_failed = true; } curr_job->jobAd->LookupBool( ATTR_LAST_JOB_LEASE_RENEWAL_FAILED, last_renewal_failed ); if ( curr_renewal_failed != last_renewal_failed ) { curr_job->jobAd->Assign( ATTR_LAST_JOB_LEASE_RENEWAL_FAILED, curr_renewal_failed ); requestScheddUpdate( curr_job, false ); } leaseUpdates.DeleteCurrent(); } } updateLeasesActive = false; dprintf(D_FULLDEBUG," UpdateLeases: lease update complete, resetting timer for 30 secs\n"); daemonCore->Reset_Timer( updateLeasesTimerId, UPDATE_LEASE_DELAY ); }
int SharedPortClient::PassSocket(Sock *sock_to_pass,char const *shared_port_id,char const *requested_by, bool non_blocking) { #ifndef HAVE_SHARED_PORT dprintf(D_ALWAYS,"SharedPortClient::PassSocket() not supported on this platform\n"); SharedPortClient::m_failPassSocketCalls++; return FALSE; #elif WIN32 /* Handle Windows */ if( !SharedPortIdIsValid(shared_port_id) ) { dprintf(D_ALWAYS, "ERROR: SharedPortClient: refusing to connect to shared port" "%s, because specified id is illegal! (%s)\n", requested_by, shared_port_id ); SharedPortClient::m_failPassSocketCalls++; return FALSE; } std::string pipe_name; SharedPortEndpoint::GetDaemonSocketDir(pipe_name); formatstr_cat(pipe_name, "%c%s", DIR_DELIM_CHAR, shared_port_id); MyString requested_by_buf; if( !requested_by ) { requested_by_buf.formatstr( " as requested by %s", sock_to_pass->peer_description()); requested_by = requested_by_buf.Value(); } HANDLE child_pipe; while(true) { child_pipe = CreateFile( pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(child_pipe != INVALID_HANDLE_VALUE) break; if(GetLastError() == ERROR_PIPE_BUSY) { dprintf(D_FULLDEBUG, "SharedPortClient: pipe id '%s' %s is busy, waiting\n", shared_port_id, requested_by); #if 1 // tj: this *might*? make a difference? bool timeout = true; for (int ii = 0; ii < 5; ++ii) { if (WaitNamedPipe(pipe_name.c_str(), 3 * 1000)) { timeout = false; break; } DWORD err = GetLastError(); dprintf(D_FULLDEBUG, "SharedPortClient: pipe id '%s' %s wait returned %d\n", shared_port_id, requested_by, err); } if (timeout) #else if (!WaitNamedPipe(pipe_name.c_str(), 20 * 1000)) #endif { DWORD err = GetLastError(); dprintf(D_ALWAYS, "ERROR: SharedPortClient: Wait for named pipe id '%s' %s for sending failed: %d %s\n", shared_port_id, requested_by, err, GetLastErrorString(err)); SharedPortClient::m_failPassSocketCalls++; return FALSE; } dprintf(D_FULLDEBUG, "SharedPortClient: wait for pipe id '%s' %s succeeded.\n", shared_port_id, requested_by); } else { DWORD err = GetLastError(); dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to open named pipe id '%s' %s for sending socket: %d %s\n", shared_port_id, requested_by, err, GetLastErrorString(err)); SharedPortClient::m_failPassSocketCalls++; return FALSE; } } DWORD child_pid; DWORD read_bytes = 0; BOOL read_result = ReadFile(child_pipe, &child_pid, sizeof(DWORD), &read_bytes, NULL); if(!read_result) { DWORD last_error = GetLastError(); dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to read PID from pipe: %d.\n", last_error); CloseHandle(child_pipe); SharedPortClient::m_failPassSocketCalls++; return FALSE; } else { dprintf(D_FULLDEBUG, "SharedPortClient: Read PID: %d\n", child_pid); } #pragma pack(push, 4) struct { int id; // condor commmand id WSAPROTOCOL_INFO wsa; // payload. } protocol_command; #pragma pack(pop) ZeroMemory(&protocol_command, sizeof(protocol_command)); int dup_result = WSADuplicateSocket(sock_to_pass->get_file_desc(), child_pid, &protocol_command.wsa); if(dup_result == SOCKET_ERROR) { dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to duplicate socket.\n"); CloseHandle(child_pipe); SharedPortClient::m_failPassSocketCalls++; return FALSE; } protocol_command.id = SHARED_PORT_PASS_SOCK; BOOL write_result = WriteFile(child_pipe, &protocol_command, sizeof(protocol_command), &read_bytes, 0); if(!write_result) { dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to send WSAPROTOCOL_INFO struct: %d\n", GetLastError()); CloseHandle(child_pipe); SharedPortClient::m_failPassSocketCalls++; return FALSE; } dprintf(D_FULLDEBUG, "SharedPortClient: Wrote %d bytes to named pipe.\n", read_bytes); FlushFileBuffers(child_pipe); CloseHandle(child_pipe); SharedPortClient::m_successPassSocketCalls++; return TRUE; #elif HAVE_SCM_RIGHTS_PASSFD /* Handle most (all?) Linux/Unix and MacOS platforms */ SharedPortState * state = new SharedPortState(static_cast<ReliSock*>(sock_to_pass), shared_port_id, requested_by, non_blocking); int result = state->Handle(); switch (result) { case KEEP_STREAM: // pass thru so that we return KEEP_STREAM; the PassSocket call is // pending, we want to keep the passed socket open until we get an // ack from endpoint. ASSERT( non_blocking ); // should only get KEEP_STREAM if non_blocking is true break; case SharedPortState::FAILED: result = FALSE; break; case SharedPortState::DONE: result = TRUE; break; case SharedPortState::CONTINUE: case SharedPortState::WAIT: default: // coding logic error if Handle() returns anything else EXCEPT("ERROR SharedPortState::Handle() unexpected return code %d",result); break; } return result; #else #error HAVE_SHARED_PORT is defined, but no method for passing fds is enabled. #endif }
std::string *NordugridJob::buildSubmitRSL() { int transfer_exec = TRUE; std::string *rsl = new std::string; StringList *stage_list = NULL; StringList *stage_local_list = NULL; char *attr_value = NULL; std::string rsl_suffix; std::string iwd; std::string executable; if ( jobAd->LookupString( ATTR_NORDUGRID_RSL, rsl_suffix ) && rsl_suffix[0] == '&' ) { *rsl = rsl_suffix; return rsl; } if ( jobAd->LookupString( ATTR_JOB_IWD, iwd ) != 1 ) { errorString = "ATTR_JOB_IWD not defined"; delete rsl; return NULL; } //Start off the RSL attr_value = param( "FULL_HOSTNAME" ); formatstr( *rsl, "&(savestate=yes)(action=request)(hostname=%s)", attr_value ); free( attr_value ); attr_value = NULL; //We're assuming all job clasads have a command attribute jobAd->LookupString( ATTR_JOB_CMD, executable ); jobAd->LookupBool( ATTR_TRANSFER_EXECUTABLE, transfer_exec ); *rsl += "(executable="; // If we're transferring the executable, strip off the path for the // remote machine, since it refers to the submit machine. if ( transfer_exec ) { *rsl += condor_basename( executable.c_str() ); } else { *rsl += executable; } { ArgList args; MyString arg_errors; MyString rsl_args; if(!args.AppendArgsFromClassAd(jobAd,&arg_errors)) { dprintf(D_ALWAYS,"(%d.%d) Failed to read job arguments: %s\n", procID.cluster, procID.proc, arg_errors.Value()); formatstr(errorString,"Failed to read job arguments: %s\n", arg_errors.Value()); delete rsl; return NULL; } if(args.Count() != 0) { if(args.InputWasV1()) { // In V1 syntax, the user's input _is_ RSL if(!args.GetArgsStringV1Raw(&rsl_args,&arg_errors)) { dprintf(D_ALWAYS, "(%d.%d) Failed to get job arguments: %s\n", procID.cluster,procID.proc,arg_errors.Value()); formatstr(errorString,"Failed to get job arguments: %s\n", arg_errors.Value()); delete rsl; return NULL; } } else { // In V2 syntax, we convert the ArgList to RSL for(int i=0;i<args.Count();i++) { if(i) { rsl_args += ' '; } rsl_args += rsl_stringify(args.GetArg(i)); } } *rsl += ")(arguments="; *rsl += rsl_args; } } // If we're transferring the executable, tell Nordugrid to set the // execute bit on the transferred executable. if ( transfer_exec ) { *rsl += ")(executables="; *rsl += condor_basename( executable.c_str() ); } if ( jobAd->LookupString( ATTR_JOB_INPUT, &attr_value ) == 1) { // only add to list if not NULL_FILE (i.e. /dev/null) if ( ! nullFile(attr_value) ) { *rsl += ")(stdin="; *rsl += condor_basename(attr_value); } free( attr_value ); attr_value = NULL; } stage_list = buildStageInList(); if ( stage_list->isEmpty() == false ) { char *file; stage_list->rewind(); *rsl += ")(inputfiles="; while ( (file = stage_list->next()) != NULL ) { *rsl += "("; *rsl += condor_basename(file); if ( IsUrl( file ) ) { formatstr_cat( *rsl, " \"%s\")", file ); } else { *rsl += " \"\")"; } } } delete stage_list; stage_list = NULL; if ( jobAd->LookupString( ATTR_JOB_OUTPUT, &attr_value ) == 1) { // only add to list if not NULL_FILE (i.e. /dev/null) if ( ! nullFile(attr_value) ) { *rsl += ")(stdout=" REMOTE_STDOUT_NAME; } free( attr_value ); attr_value = NULL; } if ( jobAd->LookupString( ATTR_JOB_ERROR, &attr_value ) == 1) { // only add to list if not NULL_FILE (i.e. /dev/null) if ( ! nullFile(attr_value) ) { *rsl += ")(stderr=" REMOTE_STDERR_NAME; } free( attr_value ); } stage_list = buildStageOutList(); stage_local_list = buildStageOutLocalList( stage_list ); if ( stage_list->isEmpty() == false ) { char *file; char *local_file; stage_list->rewind(); stage_local_list->rewind(); *rsl += ")(outputfiles="; while ( (file = stage_list->next()) != NULL ) { local_file = stage_local_list->next(); *rsl += "("; *rsl += condor_basename(file); if ( IsUrl( local_file ) ) { formatstr_cat( *rsl, " \"%s\")", local_file ); } else { *rsl += " \"\")"; } } } delete stage_list; stage_list = NULL; delete stage_local_list; stage_local_list = NULL; *rsl += ')'; if ( !rsl_suffix.empty() ) { *rsl += rsl_suffix; } dprintf(D_FULLDEBUG,"*** RSL='%s'\n",rsl->c_str()); return rsl; }