char * SharedPortEndpoint::deserialize(char *inherit_buf) { char *ptr; ptr = strchr(inherit_buf,'*'); ASSERT( ptr ); m_full_name.formatstr("%.*s",(int)(ptr-inherit_buf),inherit_buf); inherit_buf = ptr+1; m_local_id = condor_basename( m_full_name.Value() ); char *socket_dir = condor_dirname( m_full_name.Value() ); m_socket_dir = socket_dir; free( socket_dir ); #ifdef WIN32 /* Deserializing requires getting the handles out of the buffer and getting the pid pipe name stored. Registering the pipe is handled by StartListener(). */ sscanf_s(inherit_buf, "%d", (int*)&pipe_end); //m_pipe_out = daemonCore->Inherit_Pipe_Handle(out_pipe, false, true, true, 4096); #else inherit_buf = m_listener_sock.serialize(inherit_buf); #endif m_listening = true; ASSERT( StartListener() ); return inherit_buf; }
const char * SharedPortEndpoint::deserialize(const char *inherit_buf) { YourStringDeserializer in(inherit_buf); if ( ! in.deserialize_string(m_full_name, "*") || ! in.deserialize_sep("*") ) { EXCEPT("Failed to parse serialized shared-port information at offset %d: '%s'", (int)in.offset(), inherit_buf); } m_local_id = condor_basename(m_full_name.c_str()); auto_free_ptr socket_dir(condor_dirname(m_full_name.c_str())); m_socket_dir = socket_dir.ptr(); #ifdef WIN32 /* Deserializing requires getting the handles out of the buffer and getting the pid pipe name stored. Registering the pipe is handled by StartListener(). */ in.deserialize_int((LONG_PTR*)&pipe_end); in.deserialize_sep("*"); // note: terminator is missing from HTCondor prior to 8.5.7 so it is optional here... inherit_buf = in.next_pos(); #else inherit_buf = m_listener_sock.serialize(in.next_pos()); #endif m_listening = true; ASSERT( StartListener() ); return inherit_buf; }
// IT IS legitimate for the command DESTROY_SANDBOX to happen while a file // transfer is currently happening. so, tear it down properly to avoid any // memory leaks. this means looking it up in our map, which should be O(1), // and then removing the actual filesystem portion. bool destroy_sandbox(std::string sid, std::string &err) { dprintf(D_ALWAYS, "BOSCO: destroy, sandbox id: %s\n", sid.c_str()); std::string iwd; define_sandbox_path(sid, iwd); dprintf(D_ALWAYS, "BOSCO: destroy, sandbox path: %s\n", iwd.c_str()); // remove (rm -rf) the sandbox dir char *buff = condor_dirname( iwd.c_str() ); std::string parent_dir = buff; free( buff ); buff = condor_dirname( parent_dir.c_str() ); std::string gparent_dir = buff; free( buff ); dprintf( D_FULLDEBUG, "parent_dir: %s\n", parent_dir.c_str() ); dprintf( D_FULLDEBUG, "gparent_dir: %s\n", gparent_dir.c_str() ); Directory d( parent_dir.c_str() ); if ( !d.Remove_Full_Path( iwd.c_str() ) ) { dprintf(D_ALWAYS, "Failed to remove %s\n", iwd.c_str()); // TODO Should we return failure? } d.Rewind(); if ( d.Next() == NULL ) { dprintf(D_FULLDEBUG, "Removing empty directory %s\n", parent_dir.c_str()); Directory d2( gparent_dir.c_str() ); if ( !d2.Remove_Full_Path( parent_dir.c_str() ) ) { dprintf(D_ALWAYS, "Failed to remove %s\n", parent_dir.c_str()); // TODO Should we return failure? } d2.Rewind(); if ( d2.Next() == NULL ) { dprintf(D_FULLDEBUG, "Removing empty directory %s\n", gparent_dir.c_str()); if ( !d2.Remove_Full_Path( gparent_dir.c_str() ) ) { dprintf(D_ALWAYS, "Failed to remove %s\n", gparent_dir.c_str()); // TODO Should we return failure? } } } err = "NULL"; return true; }
static bool test_null() { emit_test("Does a NULL path return a period?"); const char *param = "NULL"; const char *expect = "."; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_root_directory() { emit_test("Does a path with a backslash in the root directory return the parent directory?"); const char *param = "\\foo\\bar"; const char *expect = "\\foo"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_period_and_backslash_with_special_file() { emit_test("Does a path using a period and a backslash return the parent directory?"); const char *param = ".foo\\.bar.txt"; const char *expect = ".foo"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_double_backslash() { emit_test("Does a path with one directory using a double backslash and the other using a single backslash return the full parent directory?"); const char *param = "\\\\foo\\bar\\zap.txt"; const char *expect = "\\\\foo\\bar"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_backslash_and_file_extension() { emit_test("Does a path with two directories and a file extension using backslashes return the full parent directory?"); const char *param = "foo\\bar\\zap.txt"; const char *expect = "foo\\bar"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_period_and_forward_slash() { emit_test("Does a path using both a period and a forward slash return the parent directory"); const char *param = ".foo/bar"; const char *expect = ".foo"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_period_and_forward_slash_2() { emit_test("Does a path with only a period then forwardslash return a period?"); const char *param = "./"; const char *expect = "."; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_backslash_and_period() { emit_test("Does a path with only a backslash then a period return a backslash?"); const char *param = "\\."; const char *expect = "\\"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_directory_and_file_1() { emit_test("Does a path with both a directory and file return the parent directory?"); const char *param = "foo/bar"; const char *expect = "foo"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_directory_and_directory_in_root() { emit_test("Does a path with two directories in the root directory return the full parent directory?"); const char *param = "/foo/bar/"; const char *expect = "/foo/bar"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_simple_directory_2() { emit_test("Does a path starting with a backslash return a backslash?"); const char *param = "\\foo"; const char *expect = "\\"; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
static bool test_simple_path_2() { emit_test("Does a multiple character path return the currect directory?"); const char *param = "foo"; const char *expect = "."; emit_input_header(); emit_param("STRING", param); emit_output_expected_header(); emit_retval("%s", expect); char *path = condor_dirname(param); emit_output_actual_header(); emit_retval("%s", path); if(strcmp(path, expect) != MATCH) { free(path); FAIL; } free(path); PASS; }
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; }
char* dirname( const char* path ) { return condor_dirname( path ); }
bool SharedPortEndpoint::UseSharedPort(MyString *why_not,bool already_open) { #ifndef HAVE_SHARED_PORT if( why_not ) { *why_not = "shared ports not supported on this platform"; } return false; #else // The shared port server itself should not try to operate as // a shared point endpoint, since it needs to be the one // daemon with its own port. // This subsys check is appropriate for when we are inside of // the daemon in question, not when we are the master trying // to decide whether to create a shared port for our child. // In the latter case, other methods are used to determine // that a shared port should not be used. bool never_use_shared_port = get_mySubSystem()->isType(SUBSYSTEM_TYPE_SHARED_PORT); if( never_use_shared_port ) { if( why_not ) { *why_not = "this daemon requires its own port"; } return false; } if( !param_boolean("USE_SHARED_PORT",false) ) { if( why_not ) { *why_not = "USE_SHARED_PORT=false"; } return false; } if( already_open ) { // skip following tests of writability of daemon socket dir, // since we already have a socket (perhaps created for us by // our parent) return true; } #ifdef WIN32 return true; #endif if( can_switch_ids() ) { // If we are running as root, assume that we will be able to // write to the daemon socket dir (as condor). If we can't, // it is better to try and fail so that the admin will see // that something is broken. return true; } // If we can write to the daemon socket directory, we can use // the shared port service. Cache this result briefly so we // don't check access too often when spawning lots of children. // Invalidate the cache both forwards and backwards in time in // case of system clock jumps. static bool cached_result = false; static time_t cached_time = 0; time_t now = time(NULL); if( abs((int)now-(int)cached_time) > 10 || cached_time==0 || why_not ) { cached_time = now; std::string socket_dir; bool is_file_socket = true; #ifdef USE_ABSTRACT_DOMAIN_SOCKET is_file_socket = false; #endif if (!GetDaemonSocketDir(socket_dir)) { is_file_socket = true; if (!GetAltDaemonSocketDir(socket_dir)) { why_not->formatstr("No DAEMON_SOCKET_DIR is available.\n"); cached_result = false; return cached_result; } } if (!is_file_socket) { cached_result = true; return cached_result; } cached_result = access(socket_dir.c_str(),W_OK)==0; if( !cached_result && errno == ENOENT ) { // if socket_dir doesn't exist, see if we are allowed to // create it char *parent_dir = condor_dirname( socket_dir.c_str() ); if( parent_dir ) { cached_result = access(parent_dir,W_OK)==0; free( parent_dir ); } } if( !cached_result && why_not ) { why_not->formatstr("cannot write to %s: %s", socket_dir.c_str(), strerror(errno)); } } return cached_result; #endif }