void run_preen() { int child_pid; char *args=NULL; const char *preen_base; ArgList arglist; MyString error_msg; dprintf(D_FULLDEBUG, "Entered run_preen.\n"); if( FS_Preen == NULL ) { return; } preen_base = condor_basename( FS_Preen ); arglist.AppendArg(preen_base); args = param("PREEN_ARGS"); if(!arglist.AppendArgsV1RawOrV2Quoted(args,&error_msg)) { EXCEPT("ERROR: failed to parse preen args: %s",error_msg.Value()); } free(args); child_pid = daemonCore->Create_Process( FS_Preen, // program to exec arglist, // args PRIV_ROOT, // privledge level 1, // which reaper ID to use; use default reaper FALSE ); // we do _not_ want this process to have a command port; PREEN is not a daemon core process dprintf( D_ALWAYS, "Preen pid is %d\n", child_pid ); }
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; }
GridUniverseLogic::gman_node_t * GridUniverseLogic::StartOrFindGManager(const char* owner, const char* domain, const char* attr_value, const char* attr_name, int cluster, int proc) { gman_node_t* gman_node; int pid; // If attr_value is an empty string, convert to NULL since code // after this point expects that. if ( attr_value && strlen(attr_value)==0 ) { attr_value = NULL; attr_name = NULL; } if ( (gman_node=lookupGmanByOwner(owner, attr_value, cluster, proc)) ) { // found it return gman_node; } // not found. fire one up! we want to run the GManager as the user. // but first, make certain we are not shutting down... if (!gman_pid_table) { // destructor has already been called; we are probably // closing down. return NULL; } #ifndef WIN32 if (owner && strcasecmp(owner, "root") == 0 ) { dprintf(D_ALWAYS, "Tried to start condor_gmanager as root.\n"); return NULL; } #endif dprintf( D_FULLDEBUG, "Starting condor_gmanager for owner %s (%d.%d)\n", owner, cluster, proc); char *gman_binary; gman_binary = param("GRIDMANAGER"); if ( !gman_binary ) { dprintf(D_ALWAYS,"ERROR - GRIDMANAGER not defined in config file\n"); return NULL; } ArgList args; MyString error_msg; args.AppendArg("condor_gridmanager"); args.AppendArg("-f"); char *gman_args = param("GRIDMANAGER_ARGS"); if(!args.AppendArgsV1RawOrV2Quoted(gman_args,&error_msg)) { dprintf( D_ALWAYS, "ERROR: failed to parse gridmanager args: %s\n", error_msg.Value()); free(gman_binary); free(gman_args); return NULL; } free(gman_args); // build a constraint if ( !owner ) { dprintf(D_ALWAYS,"ERROR - missing owner field\n"); free(gman_binary); return NULL; } MyString constraint; if ( !attr_name ) { constraint.formatstr("(%s=?=\"%s\"&&%s==%d)", ATTR_OWNER,owner, ATTR_JOB_UNIVERSE,CONDOR_UNIVERSE_GRID); } else { constraint.formatstr("(%s=?=\"%s\"&&%s=?=\"%s\"&&%s==%d)", ATTR_OWNER,owner, attr_name,attr_value, ATTR_JOB_UNIVERSE,CONDOR_UNIVERSE_GRID); args.AppendArg("-A"); args.AppendArg(attr_value); } args.AppendArg("-C"); args.AppendArg(constraint.Value()); MyString full_owner_name(owner); if ( domain && *domain ) { full_owner_name.formatstr_cat( "@%s", domain ); } args.AppendArg("-o"); args.AppendArg(full_owner_name.Value()); if (!init_user_ids(owner, domain)) { dprintf(D_ALWAYS,"ERROR - init_user_ids() failed in GRIDMANAGER\n"); free(gman_binary); return NULL; } static bool first_time_through = true; if ( first_time_through ) { // Note: Because first_time_through is static, this block runs only // once per schedd invocation. first_time_through = false; // Clean up any old / abandoned scratch dirs. dprintf(D_FULLDEBUG,"Checking for old gridmanager scratch dirs\n"); char *prefix = temp_dir_path(); ASSERT(prefix); Directory tmp( prefix, PRIV_USER ); const char *f; char const *dot; int fname_pid; int mypid = daemonCore->getpid(); int scratch_pre_len = strlen(scratch_prefix); while ( (f=tmp.Next()) ) { // skip regular files -- we only need to inspect subdirs if ( !tmp.IsDirectory() ) { continue; } // skip if it does not start with our prefix if ( strncmp(scratch_prefix,f,scratch_pre_len) ) { continue; } // skip if does not end w/ a pid dot = strrchr(f,'.'); if ( !dot ) { continue; } // skip if this pid is still alive and not ours dot++; // skip over period fname_pid = atoi(dot); if ( fname_pid != mypid && daemonCore->Is_Pid_Alive(fname_pid) ) { continue; } // if we made it here, blow away this subdir if ( tmp.Remove_Current_File() ) { dprintf(D_ALWAYS,"Removed old scratch dir %s\n", tmp.GetFullPath()); } } // end of while for cleanup of old scratch dirs dprintf(D_FULLDEBUG,"Done checking for old scratch dirs\n"); if (prefix != NULL) { free(prefix); prefix = NULL; } } // end of once-per-schedd invocation block // Create a temp dir for the gridmanager and append proper // command-line arguments to tell where it is. bool failed = false; gman_node = new gman_node_t; char *finalpath = scratchFilePath(gman_node); priv_state saved_priv = set_user_priv(); if ( (mkdir(finalpath,0700)) < 0 ) { // mkdir failed. dprintf(D_ALWAYS,"ERROR - mkdir(%s,0700) failed in GRIDMANAGER, errno=%d (%s)\n", finalpath, errno, strerror(errno)); failed = true; } set_priv(saved_priv); uninit_user_ids(); args.AppendArg("-S"); // -S = "ScratchDir" argument args.AppendArg(finalpath); delete [] finalpath; if ( failed ) { // we already did dprintf reason to the log... free(gman_binary); delete gman_node; return NULL; } if(IsFulldebug(D_FULLDEBUG)) { MyString args_string; args.GetArgsStringForDisplay(&args_string); dprintf(D_FULLDEBUG,"Really Execing %s\n",args_string.Value()); } pid = daemonCore->Create_Process( gman_binary, // Program to exec args, // Command-line args PRIV_ROOT, // Run as root, so it can switch to // PRIV_CONDOR rid // Reaper ID ); free(gman_binary); if ( pid <= 0 ) { dprintf ( D_ALWAYS, "StartOrFindGManager: Create_Process problems!\n" ); if (gman_node) delete gman_node; return NULL; } // If we made it here, we happily started up a new gridmanager process dprintf( D_ALWAYS, "Started condor_gmanager for owner %s pid=%d\n", owner,pid); // Make a new gman_node entry for our hashtable & insert it if ( !gman_node ) { gman_node = new gman_node_t; } gman_node->pid = pid; gman_node->owner[0] = '\0'; gman_node->domain[0] = '\0'; if ( owner ) { strcpy(gman_node->owner,owner); } if ( domain ) { strcpy(gman_node->domain,domain); } MyString owner_key(owner); if(attr_value){ owner_key += attr_value; } if (cluster) { owner_key.formatstr_cat( "-%d.%d", cluster, proc ); } ASSERT( gman_pid_table->insert(owner_key,gman_node) == 0 ); // start timer to signal gridmanager if we haven't already if ( gman_node->add_timer_id == -1 ) { // == -1 means no timer set gman_node->add_timer_id = daemonCore->Register_Timer(job_added_delay, GridUniverseLogic::SendAddSignal, "GridUniverseLogic::SendAddSignal"); daemonCore->Register_DataPtr(gman_node); } // All done return gman_node; }
void do_process_request(const ClassAd *inputAd, ClassAd *resultAd, const int req_number, const char *iwd, const char *stdio_iwd) { // Check for inputAd if ( !inputAd ) { handle_process_request_error("No input ad",req_number,resultAd); return; } // Map the CMD specified in the input via the config file. MyString UnmappedJobName,JobName; if (inputAd->LookupString(ATTR_JOB_CMD,UnmappedJobName) == 0 ) { // no CMD specified. handle_process_request_error("No CMD specified",req_number,resultAd); return; } char *auth_commands = param("SOAPSHELL_AUTHORIZED_COMMANDS"); StringList auth_list(auth_commands,","); if ( auth_commands ) free(auth_commands); // Each command needs four tuples; anything else is a misconfiguration if ( auth_list.number() % 4 != 0 ) { handle_process_request_error("Service is misconfigured: SOAPSHELL_AUTHORIZED_COMMANDS malformed",req_number,resultAd); return; } if ( auth_list.contains_anycase(UnmappedJobName.Value()) == TRUE ) { JobName = auth_list.next(); } if ( JobName.IsEmpty() ) { // the CMD not authorized handle_process_request_error("Requested CMD not authorized via SOAPSHELL_AUTHORIZED_COMMANDS",req_number,resultAd); return; } // handle command line arguments. ArgList args; args.SetArgV1SyntaxToCurrentPlatform(); args.AppendArg(JobName.Value()); // set argv[0] to command char *soapshell_args = auth_list.next(); if ( soapshell_args && strcmp(soapshell_args,"*") ) { if(!args.AppendArgsV1RawOrV2Quoted(soapshell_args,NULL)) { dprintf( D_ALWAYS, "ERROR: SOAPSHELL_ARGS config macro invalid\n" ); } } else if(!args.AppendArgsFromClassAd(inputAd,NULL)) { handle_process_request_error("Failed to setup CMD arguments",req_number,resultAd); return; } // handle the environment. Env job_env; char *env_str = auth_list.next(); if ( env_str && strcmp(env_str,"*") ) { if(!job_env.MergeFromV1RawOrV2Quoted(env_str,NULL) ) { dprintf(D_ALWAYS,"ERROR: SOAPSHELL_ENVIRONMENT config macro invalid\n"); } } else if(!job_env.MergeFrom(inputAd,NULL)) { // bad environment string in job ad! handle_process_request_error("Request has faulty environment string",req_number,resultAd); return; } // Write input files into iwd (we will write stdin later) if ( !write_input_files(inputAd, iwd) ) { // failed to write input files handle_process_request_error("Failed to write input files",req_number,resultAd); return; } // handle stdin, stdout, and stderr redirection const char* jobstdin_ = dircat(stdio_iwd,"stdin"); MyString jobstdin(jobstdin_); const char* jobstdout_ = dircat(stdio_iwd,"stdout"); MyString jobstdout(jobstdout_); const char* jobstderr_ = dircat(stdio_iwd,"stderr"); MyString jobstderr(jobstderr_); delete [] jobstdin_; delete [] jobstdout_; delete [] jobstderr_; int flags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND | O_LARGEFILE; // write stdin file is needed { char *input = NULL; unsigned char *output = NULL; int output_length = 0; int fd = -1; inputAd->LookupString(ATTR_JOB_INPUT,&input); if ( input ) { // Caller needs to free *output if non-NULL condor_base64_decode(input,&output,&output_length); if ( output ) { fd = safe_open_wrapper_follow( jobstdin.Value(), flags, 0666 ); if ( fd > -1 ) { write(fd,output,output_length); close(fd); } free(output); } free(input); if ( fd < 0 ) { handle_process_request_error("Failed to write stdin",req_number,resultAd); return; } } } int fds[3]; // initialize these to -2 to mean they're not specified. // -1 will be treated as an error. fds[0] = -2; fds[1] = -2; fds[2] = -2; fds[0] = safe_open_wrapper_follow( jobstdin.Value(), O_RDONLY | O_LARGEFILE ); // stdin fds[1] = safe_open_wrapper_follow( jobstdout.Value(), flags, 0666 ); // stdout fds[2] = safe_open_wrapper_follow( jobstderr.Value(), flags, 0666 ); // stderr /* Bail out if we couldn't open stdout/err files correctly */ if( fds[1]==-1 || fds[2]==-1 ) { /* only close ones that had been opened correctly */ for ( int i = 0; i <= 2; i++ ) { if ( fds[i] >= 0 ) { daemonCore->Close_FD ( fds[i] ); } } handle_process_request_error("Failed to write stdout/err files",req_number,resultAd); return; } // Print what we are about to do to the log MyString args_string; args.GetArgsStringForDisplay(&args_string,1); dprintf( D_ALWAYS, "About to exec %s %s\n", JobName.Value(), args_string.Value() ); // Spawn a process, baby!!! int JobPid = daemonCore->Create_Process( JobName.Value(), // executable args, // args PRIV_UNKNOWN, // priv_state - TODO 0, // reaper id - TODO FALSE, // want_command_port &job_env, // job environment iwd, // job iwd NULL, // family_info - TODO NULL, // sock_inherit_list fds // stdio redirection ); // NOTE: Create_Process() saves the errno for us if it is an // "interesting" error. char const *create_process_error = NULL; if(JobPid == FALSE && errno) create_process_error = strerror(errno); // now close the descriptors in fds array. our child has inherited // them already, so we should close them so we do not leak descriptors. for ( int i = 0; i <= 2; i++ ) { if ( fds[i] >= 0 ) { daemonCore->Close_FD ( fds[i] ); } } if ( JobPid == FALSE ) { JobPid = -1; MyString errormsg; errormsg.formatstr("Create_Process failed %s",create_process_error ? create_process_error : ""); handle_process_request_error(errormsg.Value(),req_number,resultAd); return; } dprintf(D_ALWAYS,"Create_Process succeeded, pid=%d\n",JobPid); // TODO - For now, just deal w/ one at a time. :( // So for now just wait for the child to exit. #ifdef WIN32 #error This service does not yet work on Windows #else { int exit_status; pid_t pid; for (;;) { pid = wait(&exit_status); dprintf(D_FULLDEBUG,"WAIT returned %d, errno=%d\n",pid,errno); if (pid == JobPid ) break; if (pid == -1 && errno != EINTR) { EXCEPT("waitpid failed errno=%d",errno); } } if ( WIFEXITED(exit_status) ) { int status = WEXITSTATUS(exit_status); resultAd->Assign("EXIT_STATUS",status); } } #endif // Job has completed, exit status is in the ad. Now put // the output files into the result ad. stash_output_file(resultAd, jobstdout.Value(), ATTR_JOB_OUTPUT); stash_output_file(resultAd, jobstderr.Value(), ATTR_JOB_ERROR); }
// I really need a good way to determine the type of a classad // attribute. Right now I just try all four possibilities, which is a // horrible mess... bool VirshType::CreateVirshConfigFile(const char* /*filename*/) { vmprintf(D_FULLDEBUG, "In VirshType::CreateVirshConfigFile\n"); // std::string name; char * tmp = param("LIBVIRT_XML_SCRIPT"); if(tmp == NULL) { vmprintf(D_ALWAYS, "LIBVIRT_XML_SCRIPT not defined\n"); return false; } // This probably needs some work... ArgList args; args.AppendArg(tmp); free(tmp); // We might want to have specific debugging output enabled in the // helper script; however, it is not clear where that output should // go. This gives us a way to do so even in cases where the script // is unable to read from condor_config (why would this ever // happen?) tmp = param("LIBVIRT_XML_SCRIPT_ARGS"); if(tmp != NULL) { MyString errormsg; args.AppendArgsV1RawOrV2Quoted(tmp,&errormsg); free(tmp); } StringList input_strings, output_strings, error_strings; MyString classad_string; m_classAd.sPrint(classad_string); classad_string += VMPARAM_XEN_BOOTLOADER; classad_string += " = \""; classad_string += m_xen_bootloader; classad_string += "\"\n"; if(classad_string.find(VMPARAM_XEN_INITRD) < 1) { classad_string += VMPARAM_XEN_INITRD; classad_string += " = \""; classad_string += m_xen_initrd_file; classad_string += "\"\n"; } if(!m_vm_bridge_interface.empty()) { classad_string += VMPARAM_BRIDGE_INTERFACE; classad_string += " = \""; classad_string += m_vm_bridge_interface.c_str(); classad_string += "\"\n"; } if(classad_string.find(ATTR_JOB_VM_NETWORKING_TYPE) < 1) { classad_string += ATTR_JOB_VM_NETWORKING_TYPE; classad_string += " = \""; classad_string += m_vm_networking_type.Value(); classad_string += "\"\n"; } input_strings.append(classad_string.Value()); tmp = input_strings.print_to_string(); vmprintf(D_FULLDEBUG, "LIBVIRT_XML_SCRIPT_ARGS input_strings= %s\n", tmp); free(tmp); int ret = systemCommand(args, PRIV_ROOT, &output_strings, &input_strings, &error_strings, false); error_strings.rewind(); if(ret != 0) { vmprintf(D_ALWAYS, "XML helper script could not be executed\n"); output_strings.rewind(); // If there is any output from the helper, write it to the debug // log. Presumably, this is separate from the script's own // debug log. while((tmp = error_strings.next()) != NULL) { vmprintf(D_FULLDEBUG, "Helper stderr output: %s\n", tmp); } return false; } error_strings.rewind(); while((tmp = error_strings.next()) != NULL) { vmprintf(D_ALWAYS, "Helper stderr output: %s\n", tmp); } output_strings.rewind(); while((tmp = output_strings.next()) != NULL) { m_xml += tmp; } return true; }