/** Parse the arguments line of an existing .condor.sub file, extracing the arguments we want to preserve when updating the .condor.sub file. @param subLine: the arguments line from the .condor.sub file @param shallowOpts: the condor_submit_dag shallow options @return 0 if successful, 1 if failed */ int parseArgumentsLine( const MyString &subLine, SubmitDagShallowOptions &shallowOpts ) { const char *line = subLine.Value(); const char *start = strchr( line, '"' ); const char *end = strrchr( line, '"' ); MyString arguments; if ( start && end ) { arguments = subLine.Substr( start - line, end - line ); } else { fprintf( stderr, "Missing quotes in arguments line: <%s>\n", subLine.Value() ); return 1; } ArgList arglist; MyString error; if ( !arglist.AppendArgsV2Quoted( arguments.Value(), &error ) ) { fprintf( stderr, "Error parsing arguments: %s\n", error.Value() ); return 1; } for ( int argNum = 0; argNum < arglist.Count(); argNum++ ) { MyString strArg = arglist.GetArg( argNum ); strArg.lower_case(); (void)parsePreservedArgs( strArg, argNum, arglist.Count(), arglist.GetStringArray(), shallowOpts); } return 0; }
void privsep_exec_set_args(FILE* fp, ArgList& args) { int num_args = args.Count(); for (int i = 0; i < num_args; i++) { fprintf(fp, "exec-arg<%lu>\n", (unsigned long)strlen(args.GetArg(i))); fprintf(fp, "%s\n", args.GetArg(i)); } }
int GLExecPrivSepHelper::create_process(const char* path, ArgList& args, Env& env, const char* iwd, int job_std_fds[3], const char* std_file_names[3], int nice_inc, size_t* core_size_ptr, int reaper_id, int dc_job_opts, FamilyInfo* family_info, int *, MyString *error_msg) { ASSERT(m_initialized); if (!proxy_valid_right_now()) { dprintf(D_ALWAYS, "GLExecPrivSepHelper::create_process: not invoking glexec since the proxy is not valid!\n"); if( error_msg ) { error_msg->formatstr_cat("The job proxy is invalid."); } return -1; } // make a copy of std FDs so we're not messing w/ our caller's // memory int std_fds[3] = {-1, -1, -1}; ArgList modified_args; modified_args.AppendArg(m_run_script); modified_args.AppendArg(m_glexec); modified_args.AppendArg(m_proxy); modified_args.AppendArg(m_sandbox); modified_args.AppendArg(m_wrapper_script); for (int i = 0; i < 3; i++) { modified_args.AppendArg((job_std_fds == NULL || job_std_fds[i] == -1) ? std_file_names[i] : "-"); } modified_args.AppendArg(path); for (int i = 1; i < args.Count(); i++) { modified_args.AppendArg(args.GetArg(i)); } int glexec_errors = 0; while(1) { // setup a UNIX domain socket for communicating with // condor_glexec_wrapper (see comment above feed_wrapper() // for details // int sock_fds[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_fds) == -1) { dprintf(D_ALWAYS, "GLEXEC: socketpair error: %s\n", strerror(errno)); return false; } std_fds[0] = sock_fds[1]; // now create a pipe for receiving diagnostic stdout/stderr from glexec int glexec_out_fds[2]; if (pipe(glexec_out_fds) < 0) { dprintf(D_ALWAYS, "GLEXEC: pipe() error: %s\n", strerror(errno)); close(sock_fds[0]); close(sock_fds[1]); return false; } std_fds[1] = glexec_out_fds[1]; std_fds[2] = std_fds[1]; // collect glexec stderr and stdout together FamilyInfo fi; FamilyInfo* fi_ptr = (family_info != NULL) ? family_info : &fi; MyString proxy_path; proxy_path.formatstr("%s.condor/%s", m_sandbox, m_proxy); fi_ptr->glexec_proxy = proxy_path.Value(); // At the very least, we need to pass the condor daemon's // X509_USER_PROXY to condor_glexec_run. Currently, we just // pass all daemon environment. We do _not_ run // condor_glexec_run in the job environment, because that // would be a security risk and would serve no purpose, since // glexec cleanses the environment anyway. dc_job_opts &= ~(DCJOBOPT_NO_ENV_INHERIT); int pid = daemonCore->Create_Process(m_run_script.Value(), modified_args, PRIV_USER_FINAL, reaper_id, FALSE, FALSE, NULL, iwd, fi_ptr, NULL, std_fds, NULL, nice_inc, NULL, dc_job_opts, core_size_ptr, NULL, NULL, error_msg); // close our handle to glexec's end of the diagnostic output pipe close(glexec_out_fds[1]); MyString glexec_error_msg; int glexec_rc = 0; int ret_val = feed_wrapper(pid, sock_fds, env, dc_job_opts, job_std_fds, glexec_out_fds[0], &glexec_error_msg, &glexec_rc); // if not closed in feed_wrapper, close the glexec error pipe now if( glexec_out_fds[0] != -1 ) { close(glexec_out_fds[0]); } // Unlike the other glexec operations where we handle // glexec retry inside the helper script, // condor_glexec_run cannot handle retry for us, because // it must exec glexec rather than spawning it off in a // new process. Therefore, we handle here retries in case // of transient errors. if( ret_val != 0 ) { return ret_val; // success } bool retry = true; if( glexec_rc != 202 && glexec_rc != 203 ) { // Either a non-transient glexec error, or some other // non-glexec error. retry = false; } else { // This _could_ be a transient glexec issue, such as a // communication error with GUMS, so retry up to some // limit. glexec_errors += 1; if( glexec_errors > m_glexec_retries ) { retry = false; } } if( !retry ) { // return the most recent glexec error output if( error_msg ) { error_msg->formatstr_cat(glexec_error_msg.Value()); } return 0; } // truncated exponential backoff int delay_rand = 1 + (get_random_int() % glexec_errors) % 100; int delay = m_glexec_retry_delay * delay_rand; dprintf(D_ALWAYS,"Glexec exited with status %d; retrying in %d seconds.\n", glexec_rc, delay ); sleep(delay); // now try again ... } // should never get here return 0; }
bool glexec_starter_prepare(const char* starter_path, const char* proxy_file, const ArgList& orig_args, const Env* orig_env, const int orig_std_fds[3], ArgList& glexec_args, Env& glexec_env, int glexec_std_fds[3]) { // if GLEXEC_STARTER is set, use glexec to invoke the // starter (or fail if we can't). this involves: // - verifying that we have a delegated proxy from // the user stored, since we need to hand it to // glexec so it can look up the UID/GID // - invoking 'glexec_starter_setup.sh' via glexec to // setup the starter's "private directory" for a copy // of the job's proxy to go into, as well as the StarterLog // and execute dir // - adding the contents of the GLEXEC and config param // and the path to 'condor_glexec_wrapper' to the front // of the command line // - setting up glexec's environment (setting the // mode, handing off the proxy, etc.) // - creating a UNIX-domain socket to use to communicate // with our wrapper script, and munging the std_fds // array // verify that we have a stored proxy if( proxy_file == NULL ) { dprintf( D_ALWAYS, "cannot use glexec to spawn starter: no proxy " "(is GLEXEC_STARTER set in the shadow?)\n" ); return false; } // using the file name of the proxy that was stashed ealier, construct // the name of the starter's "private directory". the naming scheme is // (where XXXXXX is randomly generated via condor_mkstemp): // - $(GLEXEC_USER_DIR)/startd-tmp-proxy-XXXXXX // - startd's copy of the job's proxy // - $(GLEXEC_USER_DIR)/starter-tmp-dir-XXXXXX // - starter's private dir // MyString glexec_private_dir; char* dir_part = condor_dirname(proxy_file); ASSERT(dir_part != NULL); glexec_private_dir = dir_part; free(dir_part); glexec_private_dir += "/starter-tmp-dir-"; const char* random_part = proxy_file; random_part += strlen(random_part) - 6; glexec_private_dir += random_part; dprintf(D_ALWAYS, "GLEXEC: starter private dir is '%s'\n", glexec_private_dir.Value()); // get the glexec command line prefix from config char* glexec_argstr = param( "GLEXEC" ); if ( ! glexec_argstr ) { dprintf( D_ALWAYS, "cannot use glexec to spawn starter: " "GLEXEC not given in config\n" ); return false; } // cons up a command line for my_system. we'll run the // script $(LIBEXEC)/glexec_starter_setup.sh, which // will create the starter's "private directory" (and // its log and execute subdirectories). the value of // glexec_private_dir will be passed as an argument to // the script // parse the glexec args for invoking glexec_starter_setup.sh. // do not free them yet, except on an error, as we use them // again below. MyString setup_err; ArgList glexec_setup_args; glexec_setup_args.SetArgV1SyntaxToCurrentPlatform(); if( ! glexec_setup_args.AppendArgsV1RawOrV2Quoted( glexec_argstr, &setup_err ) ) { dprintf( D_ALWAYS, "GLEXEC: failed to parse GLEXEC from config: %s\n", setup_err.Value() ); free( glexec_argstr ); return 0; } // set up the rest of the arguments for the glexec setup script char* tmp = param("LIBEXEC"); if (tmp == NULL) { dprintf( D_ALWAYS, "GLEXEC: LIBEXEC not defined; can't find setup script\n" ); free( glexec_argstr ); return 0; } MyString libexec = tmp; free(tmp); MyString setup_script = libexec; setup_script += "/glexec_starter_setup.sh"; glexec_setup_args.AppendArg(setup_script.Value()); glexec_setup_args.AppendArg(glexec_private_dir.Value()); // debug info. this display format totally screws up the quoting, but // my_system gets it right. MyString disp_args; glexec_setup_args.GetArgsStringForDisplay(&disp_args, 0); dprintf (D_ALWAYS, "GLEXEC: about to glexec: ** %s **\n", disp_args.Value()); // the only thing actually needed by glexec at this point is the cert, so // that it knows who to map to. the pipe outputs the username that glexec // ended up using, on a single text line by itself. SetEnv( "GLEXEC_CLIENT_CERT", proxy_file ); // create the starter's private dir int ret = my_system(glexec_setup_args); // clean up UnsetEnv( "GLEXEC_CLIENT_CERT"); if ( ret != 0 ) { dprintf(D_ALWAYS, "GLEXEC: error creating private dir: my_system returned %d\n", ret); free( glexec_argstr ); return 0; } // now prepare the starter command line, starting with glexec and its // options (if any), then condor_glexec_wrapper. MyString err; if( ! glexec_args.AppendArgsV1RawOrV2Quoted( glexec_argstr, &err ) ) { dprintf( D_ALWAYS, "failed to parse GLEXEC from config: %s\n", err.Value() ); free( glexec_argstr ); return 0; } free( glexec_argstr ); MyString wrapper_path = libexec; wrapper_path += "/condor_glexec_wrapper"; glexec_args.AppendArg(wrapper_path.Value()); // complete the command line by adding in the original // arguments. we also make sure that the full path to the // starter is given int starter_path_pos = glexec_args.Count(); glexec_args.AppendArgsFromArgList( orig_args ); glexec_args.RemoveArg( starter_path_pos ); glexec_args.InsertArg( starter_path, starter_path_pos ); // set up the environment stuff if( orig_env ) { // first merge in the original glexec_env.MergeFrom( *orig_env ); } // GLEXEC_MODE - get account from lcmaps glexec_env.SetEnv( "GLEXEC_MODE", "lcmaps_get_account" ); // GLEXEC_CLIENT_CERT - cert to use for the mapping glexec_env.SetEnv( "GLEXEC_CLIENT_CERT", proxy_file ); #if defined(HAVE_EXT_GLOBUS) && !defined(SKIP_AUTHENTICATION) // GLEXEC_SOURCE_PROXY - proxy to provide to the child // (file is owned by us) glexec_env.SetEnv( "GLEXEC_SOURCE_PROXY", proxy_file ); dprintf (D_ALWAYS, "GLEXEC: setting GLEXEC_SOURCE_PROXY to %s\n", proxy_file); // GLEXEC_TARGET_PROXY - child-owned file to copy its proxy to. // this needs to be in a directory owned by that user, and not world // writable. glexec enforces this. hence, all the whoami/mkdir mojo // above. MyString child_proxy_file = glexec_private_dir; child_proxy_file += "/glexec_starter_proxy"; dprintf (D_ALWAYS, "GLEXEC: setting GLEXEC_TARGET_PROXY to %s\n", child_proxy_file.Value()); glexec_env.SetEnv( "GLEXEC_TARGET_PROXY", child_proxy_file.Value() ); // _CONDOR_GSI_DAEMON_PROXY - starter's proxy MyString var_name; var_name.sprintf("_CONDOR_%s", STR_GSI_DAEMON_PROXY); glexec_env.SetEnv( var_name.Value(), child_proxy_file.Value() ); var_name.sprintf("_condor_%s", STR_GSI_DAEMON_PROXY); glexec_env.SetEnv( var_name.Value(), child_proxy_file.Value() ); #endif // the EXECUTE dir should be owned by the mapped user. we created this // earlier, and now we override it in the condor_config via the // environment. MyString execute_dir = glexec_private_dir; execute_dir += "/execute"; glexec_env.SetEnv ( "_CONDOR_EXECUTE", execute_dir.Value()); glexec_env.SetEnv ( "_condor_EXECUTE", execute_dir.Value()); // the LOG dir should be owned by the mapped user. we created this // earlier, and now we override it in the condor_config via the // environment. MyString log_dir = glexec_private_dir; log_dir += "/log"; glexec_env.SetEnv ( "_CONDOR_LOG", log_dir.Value()); glexec_env.SetEnv ( "_condor_LOG", log_dir.Value()); glexec_env.SetEnv ( "_CONDOR_LOCK", log_dir.Value()); glexec_env.SetEnv ( "_condor_LOCK", log_dir.Value()); // PROCD_ADDRESS: the Starter that we are about to create will // not have access to our ProcD. we'll explicitly set PROCD_ADDRESS // to be in its LOG directory. the Starter will see that its // PROCD_ADDRESS knob is different from what it inherits in // CONDOR_PROCD_ADDRESS, and know it needs to create its own ProcD // MyString procd_address = log_dir; procd_address += "/procd_pipe"; glexec_env.SetEnv( "_CONDOR_PROCD_ADDRESS", procd_address.Value() ); glexec_env.SetEnv( "_condor_PROCD_ADDRESS", procd_address.Value() ); // CONDOR_GLEXEC_STARTER_CLEANUP_FLAG: this serves as a flag in the // Starter's environment that it will check for in order to determine // whether to do GLEXEC_STARTER-specific cleanup // glexec_env.SetEnv( "CONDOR_GLEXEC_STARTER_CLEANUP_FLAG", "CONDOR_GLEXEC_STARTER_CLEANUP_FLAG" ); // now set up a socket pair for communication with // condor_glexec_wrapper // if (socketpair(PF_UNIX, SOCK_STREAM, 0, s_saved_sock_fds) == -1) { dprintf(D_ALWAYS, "GLEXEC: socketpair error: %s\n", strerror(errno)); return false; } glexec_std_fds[0] = s_saved_sock_fds[1]; if (orig_std_fds == NULL) { s_saved_starter_stdin = -1; glexec_std_fds[1] = glexec_std_fds[2] = -1; } else { s_saved_starter_stdin = orig_std_fds[0]; glexec_std_fds[1] = orig_std_fds[1]; glexec_std_fds[2] = orig_std_fds[2]; } // save the environment we're handing back to the caller for use in // glexec_starter_handle_env() // s_saved_env.Clear(); s_saved_env.MergeFrom(glexec_env); return true; }
int GLExecPrivSepHelper::create_process(const char* path, ArgList& args, Env& env, const char* iwd, int job_std_fds[3], const char* std_file_names[3], int nice_inc, size_t* core_size_ptr, int reaper_id, int dc_job_opts, FamilyInfo* family_info, int *, MyString *error_msg) { ASSERT(m_initialized); if (!proxy_valid_right_now()) { dprintf(D_ALWAYS, "GLExecPrivSepHelper::create_process: not invoking glexec since the proxy is not valid!\n"); return -1; } // make a copy of std FDs so we're not messing w/ our caller's // memory int std_fds[3] = {-1, -1, -1}; ArgList modified_args; modified_args.AppendArg(m_run_script); modified_args.AppendArg(m_glexec); modified_args.AppendArg(m_proxy); modified_args.AppendArg(m_sandbox); modified_args.AppendArg(m_wrapper_script); for (int i = 0; i < 3; i++) { modified_args.AppendArg((job_std_fds == NULL || job_std_fds[i] == -1) ? std_file_names[i] : "-"); } modified_args.AppendArg(path); for (int i = 1; i < args.Count(); i++) { modified_args.AppendArg(args.GetArg(i)); } // setup a UNIX domain socket for communicating with // condor_glexec_wrapper (see comment above feed_wrapper() // for details // int sock_fds[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_fds) == -1) { dprintf(D_ALWAYS, "GLEXEC: socketpair error: %s\n", strerror(errno)); return false; } std_fds[0] = sock_fds[1]; // now create a pipe for receiving diagnostic stdout/stderr from glexec int glexec_out_fds[2]; if (pipe(glexec_out_fds) < 0) { dprintf(D_ALWAYS, "GLEXEC: pipe() error: %s\n", strerror(errno)); close(sock_fds[0]); close(sock_fds[1]); return false; } std_fds[1] = glexec_out_fds[1]; std_fds[2] = std_fds[1]; // collect glexec stderr and stdout together FamilyInfo fi; FamilyInfo* fi_ptr = (family_info != NULL) ? family_info : &fi; MyString proxy_path; proxy_path.formatstr("%s.condor/%s", m_sandbox, m_proxy); fi_ptr->glexec_proxy = proxy_path.Value(); // At the very least, we need to pass the condor daemon's // X509_USER_PROXY to condor_glexec_run. Currently, we just // pass all daemon environment. We do _not_ run // condor_glexec_run in the job environment, because that // would be a security risk and would serve no purpose, since // glexec cleanses the environment anyway. dc_job_opts &= ~(DCJOBOPT_NO_ENV_INHERIT); int pid = daemonCore->Create_Process(m_run_script.Value(), modified_args, PRIV_USER_FINAL, reaper_id, FALSE, NULL, iwd, fi_ptr, NULL, std_fds, NULL, nice_inc, NULL, dc_job_opts, core_size_ptr, NULL, NULL, error_msg); // close our handle to glexec's end of the diagnostic output pipe close(glexec_out_fds[1]); int ret_val = feed_wrapper(pid, sock_fds, env, dc_job_opts, job_std_fds, glexec_out_fds[0], error_msg); // if not closed in feed_wrapper, close the glexec error pipe now if( glexec_out_fds[0] != -1 ) { close(glexec_out_fds[0]); } return ret_val; }
int main (int argc, char *argv[]) { #if !defined(WIN32) install_sig_handler(SIGPIPE, (SIG_HANDLER)SIG_IGN ); #endif // initialize to read from config file myDistro->Init( argc, argv ); myName = argv[0]; config(); dprintf_config_tool_on_error(0); // The arguments take two passes to process --- the first pass // figures out the mode, after which we can instantiate the required // query object. We add implied constraints from the command line in // the second pass. firstPass (argc, argv); // if the mode has not been set, it is STARTD_NORMAL if (mode == MODE_NOTSET) { setMode (MODE_STARTD_NORMAL, 0, DEFAULT); } // instantiate query object if (!(query = new CondorQuery (type))) { dprintf_WriteOnErrorBuffer(stderr, true); fprintf (stderr, "Error: Out of memory\n"); exit (1); } // if a first-pass setMode set a mode_constraint, apply it now to the query object if (mode_constraint && ! explicit_format) { query->addANDConstraint(mode_constraint); } // set pretty print style implied by the type of entity being queried // but do it with default priority, so that explicitly requested options // can override it switch (type) { #ifdef HAVE_EXT_POSTGRESQL case QUILL_AD: setPPstyle(PP_QUILL_NORMAL, 0, DEFAULT); break; #endif /* HAVE_EXT_POSTGRESQL */ case DEFRAG_AD: setPPstyle(PP_GENERIC_NORMAL, 0, DEFAULT); break; case STARTD_AD: setPPstyle(PP_STARTD_NORMAL, 0, DEFAULT); break; case SCHEDD_AD: setPPstyle(PP_SCHEDD_NORMAL, 0, DEFAULT); break; case MASTER_AD: setPPstyle(PP_MASTER_NORMAL, 0, DEFAULT); break; case CKPT_SRVR_AD: setPPstyle(PP_CKPT_SRVR_NORMAL, 0, DEFAULT); break; case COLLECTOR_AD: setPPstyle(PP_COLLECTOR_NORMAL, 0, DEFAULT); break; case STORAGE_AD: setPPstyle(PP_STORAGE_NORMAL, 0, DEFAULT); break; case NEGOTIATOR_AD: setPPstyle(PP_NEGOTIATOR_NORMAL, 0, DEFAULT); break; case GRID_AD: setPPstyle(PP_GRID_NORMAL, 0, DEFAULT); break; case GENERIC_AD: setPPstyle(PP_GENERIC, 0, DEFAULT); break; case ANY_AD: setPPstyle(PP_ANY_NORMAL, 0, DEFAULT); break; default: setPPstyle(PP_VERBOSE, 0, DEFAULT); } // set the constraints implied by the mode switch (mode) { #ifdef HAVE_EXT_POSTGRESQL case MODE_QUILL_NORMAL: #endif /* HAVE_EXT_POSTGRESQL */ case MODE_DEFRAG_NORMAL: case MODE_STARTD_NORMAL: case MODE_MASTER_NORMAL: case MODE_CKPT_SRVR_NORMAL: case MODE_SCHEDD_NORMAL: case MODE_SCHEDD_SUBMITTORS: case MODE_COLLECTOR_NORMAL: case MODE_NEGOTIATOR_NORMAL: case MODE_STORAGE_NORMAL: case MODE_GENERIC_NORMAL: case MODE_ANY_NORMAL: case MODE_GRID_NORMAL: case MODE_HAD_NORMAL: break; case MODE_OTHER: // tell the query object what the type we're querying is query->setGenericQueryType(genericType); free(genericType); genericType = NULL; break; case MODE_STARTD_AVAIL: // For now, -avail shows you machines avail to anyone. sprintf (buffer, "%s == \"%s\"", ATTR_STATE, state_to_string(unclaimed_state)); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; case MODE_STARTD_RUN: sprintf (buffer, "%s == \"%s\"", ATTR_STATE, state_to_string(claimed_state)); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; case MODE_STARTD_COD: sprintf (buffer, "%s > 0", ATTR_NUM_COD_CLAIMS ); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; default: break; } if(javaMode) { sprintf( buffer, "%s == TRUE", ATTR_HAS_JAVA ); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addANDConstraint (buffer); projList.AppendArg(ATTR_HAS_JAVA); projList.AppendArg(ATTR_JAVA_MFLOPS); projList.AppendArg(ATTR_JAVA_VENDOR); projList.AppendArg(ATTR_JAVA_VERSION); } if(offlineMode) { query->addANDConstraint( "size( OfflineUniverses ) != 0" ); projList.AppendArg( "OfflineUniverses" ); // // Since we can't add a regex to a projection, explicitly list all // the attributes we know about. // projList.AppendArg( "HasVM" ); projList.AppendArg( "VMOfflineReason" ); projList.AppendArg( "VMOfflineTime" ); } if(absentMode) { sprintf( buffer, "%s == TRUE", ATTR_ABSENT ); if (diagnose) { printf( "Adding constraint %s\n", buffer ); } query->addANDConstraint( buffer ); projList.AppendArg( ATTR_ABSENT ); projList.AppendArg( ATTR_LAST_HEARD_FROM ); projList.AppendArg( ATTR_CLASSAD_LIFETIME ); } if(vmMode) { sprintf( buffer, "%s == TRUE", ATTR_HAS_VM); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addANDConstraint (buffer); projList.AppendArg(ATTR_VM_TYPE); projList.AppendArg(ATTR_VM_MEMORY); projList.AppendArg(ATTR_VM_NETWORKING); projList.AppendArg(ATTR_VM_NETWORKING_TYPES); projList.AppendArg(ATTR_VM_HARDWARE_VT); projList.AppendArg(ATTR_VM_AVAIL_NUM); projList.AppendArg(ATTR_VM_ALL_GUEST_MACS); projList.AppendArg(ATTR_VM_ALL_GUEST_IPS); projList.AppendArg(ATTR_VM_GUEST_MAC); projList.AppendArg(ATTR_VM_GUEST_IP); } // second pass: add regular parameters and constraints if (diagnose) { printf ("----------\n"); } secondPass (argc, argv); // initialize the totals object if (ppStyle == PP_CUSTOM && using_print_format) { if (pmHeadFoot & HF_NOSUMMARY) ppTotalStyle = PP_CUSTOM; } else { ppTotalStyle = ppStyle; } TrackTotals totals(ppTotalStyle); // fetch the query QueryResult q; if ((mode == MODE_STARTD_NORMAL) && (ppStyle == PP_STARTD_NORMAL)) { projList.AppendArg("Name"); projList.AppendArg("Machine"); projList.AppendArg("Opsys"); projList.AppendArg("Arch"); projList.AppendArg("State"); projList.AppendArg("Activity"); projList.AppendArg("LoadAvg"); projList.AppendArg("Memory"); projList.AppendArg("ActvtyTime"); projList.AppendArg("MyCurrentTime"); projList.AppendArg("EnteredCurrentActivity"); } else if( ppStyle == PP_VERBOSE ) { // Remove everything from the projection list if we're displaying // the "long form" of the ads. projList.Clear(); // but if -attributes was supplied, show only those attributes if ( ! dashAttributes.isEmpty()) { const char * s; dashAttributes.rewind(); while ((s = dashAttributes.next())) { projList.AppendArg(s); } } } if( projList.Count() > 0 ) { char **attr_list = projList.GetStringArray(); query->setDesiredAttrs(attr_list); deleteStringArray(attr_list); } // if diagnose was requested, just print the query ad if (diagnose) { ClassAd queryAd; // print diagnostic information about inferred internal state setMode ((Mode) 0, 0, NULL); setType (NULL, 0, NULL); setPPstyle ((ppOption) 0, 0, DEFAULT); printf ("----------\n"); q = query->getQueryAd (queryAd); fPrintAd (stdout, queryAd); printf ("----------\n"); fprintf (stderr, "Result of making query ad was: %d\n", q); exit (1); } // Address (host:port) is taken from requested pool, if given. char* addr = (NULL != pool) ? pool->addr() : NULL; Daemon* requested_daemon = pool; // If we're in "direct" mode, then we attempt to locate the daemon // associated with the requested subsystem (here encoded by value of mode) // In this case the host:port of pool (if given) denotes which // pool is being consulted if( direct ) { Daemon *d = NULL; switch( mode ) { case MODE_MASTER_NORMAL: d = new Daemon( DT_MASTER, direct, addr ); break; case MODE_STARTD_NORMAL: case MODE_STARTD_AVAIL: case MODE_STARTD_RUN: case MODE_STARTD_COD: d = new Daemon( DT_STARTD, direct, addr ); break; #ifdef HAVE_EXT_POSTGRESQL case MODE_QUILL_NORMAL: d = new Daemon( DT_QUILL, direct, addr ); break; #endif /* HAVE_EXT_POSTGRESQL */ case MODE_SCHEDD_NORMAL: case MODE_SCHEDD_SUBMITTORS: d = new Daemon( DT_SCHEDD, direct, addr ); break; case MODE_NEGOTIATOR_NORMAL: d = new Daemon( DT_NEGOTIATOR, direct, addr ); break; case MODE_CKPT_SRVR_NORMAL: case MODE_COLLECTOR_NORMAL: case MODE_LICENSE_NORMAL: case MODE_STORAGE_NORMAL: case MODE_GENERIC_NORMAL: case MODE_ANY_NORMAL: case MODE_OTHER: case MODE_GRID_NORMAL: case MODE_HAD_NORMAL: // These have to go to the collector, anyway. break; default: fprintf( stderr, "Error: Illegal mode %d\n", mode ); exit( 1 ); break; } // Here is where we actually override 'addr', if we can obtain // address of the requested daemon/subsys. If it can't be // located, then fail with error msg. // 'd' will be null (unset) if mode is one of above that must go to // collector (MODE_ANY_NORMAL, MODE_COLLECTOR_NORMAL, etc) if (NULL != d) { if( d->locate() ) { addr = d->addr(); requested_daemon = d; } else { const char* id = d->idStr(); if (NULL == id) id = d->name(); dprintf_WriteOnErrorBuffer(stderr, true); if (NULL == id) id = "daemon"; fprintf(stderr, "Error: Failed to locate %s\n", id); fprintf(stderr, "%s\n", d->error()); exit( 1 ); } } } ClassAdList result; CondorError errstack; if (NULL != ads_file) { MyString req; // query requirements q = query->getRequirements(req); const char * constraint = req.empty() ? NULL : req.c_str(); if (read_classad_file(ads_file, result, constraint)) { q = Q_OK; } } else if (NULL != addr) { // this case executes if pool was provided, or if in "direct" mode with // subsystem that corresponds to a daemon (above). // Here 'addr' represents either the host:port of requested pool, or // alternatively the host:port of daemon associated with requested subsystem (direct mode) q = query->fetchAds (result, addr, &errstack); } else { // otherwise obtain list of collectors and submit query that way CollectorList * collectors = CollectorList::create(); q = collectors->query (*query, result, &errstack); delete collectors; } // if any error was encountered during the query, report it and exit if (Q_OK != q) { dprintf_WriteOnErrorBuffer(stderr, true); // we can always provide these messages: fprintf( stderr, "Error: %s\n", getStrQueryResult(q) ); fprintf( stderr, "%s\n", errstack.getFullText(true).c_str() ); if ((NULL != requested_daemon) && ((Q_NO_COLLECTOR_HOST == q) || (requested_daemon->type() == DT_COLLECTOR))) { // Specific long message if connection to collector failed. const char* fullhost = requested_daemon->fullHostname(); if (NULL == fullhost) fullhost = "<unknown_host>"; const char* daddr = requested_daemon->addr(); if (NULL == daddr) daddr = "<unknown>"; char info[1000]; sprintf(info, "%s (%s)", fullhost, daddr); printNoCollectorContact( stderr, info, !expert ); } else if ((NULL != requested_daemon) && (Q_COMMUNICATION_ERROR == q)) { // more helpful message for failure to connect to some daemon/subsys const char* id = requested_daemon->idStr(); if (NULL == id) id = requested_daemon->name(); if (NULL == id) id = "daemon"; const char* daddr = requested_daemon->addr(); if (NULL == daddr) daddr = "<unknown>"; fprintf(stderr, "Error: Failed to contact %s at %s\n", id, daddr); } // fail exit (1); } if (noSort) { // do nothing } else if (sortSpecs.empty()) { // default classad sorting result.Sort((SortFunctionType)lessThanFunc); } else { // User requested custom sorting expressions: // insert attributes related to custom sorting result.Open(); while (ClassAd* ad = result.Next()) { for (vector<SortSpec>::iterator ss(sortSpecs.begin()); ss != sortSpecs.end(); ++ss) { ss->expr->SetParentScope(ad); classad::Value v; ss->expr->Evaluate(v); stringstream vs; // This will properly render all supported value types, // including undefined and error, although current semantic // pre-filters classads where sort expressions are undef/err: vs << ((v.IsStringValue())?"\"":"") << v << ((v.IsStringValue())?"\"":""); ad->AssignExpr(ss->keyAttr.c_str(), vs.str().c_str()); // Save the full expr in case user wants to examine on output: ad->AssignExpr(ss->keyExprAttr.c_str(), ss->arg.c_str()); } } result.Open(); result.Sort((SortFunctionType)customLessThanFunc); } // output result prettyPrint (result, &totals); delete query; return 0; }
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; }