コード例 #1
0
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;
}
コード例 #2
0
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;
}
コード例 #3
0
ファイル: io_loop.cpp プロジェクト: zhangzhehust/htcondor
// 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;
}
コード例 #4
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #5
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #6
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #7
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #8
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #9
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #10
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #11
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #12
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #13
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #14
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #15
0
ファイル: FTEST_dirname.cpp プロジェクト: AlainRoy/htcondor
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;
}
コード例 #16
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;
}
コード例 #17
0
ファイル: basename.cpp プロジェクト: bbockelm/htcondor
char*
dirname( const char* path )
{
	return condor_dirname( path );
}
コード例 #18
0
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
}