// // spawnvp.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // // Defines the -vp and -vpe flavors of the _exec() and _spawn() functions. See // the comments in spawnv.cpp for details of the various flavors of these // functions. // #include #include #include #include #include #include #include #include template static intptr_t __cdecl common_spawnvp( int const mode, Character const* const file_name, Character const* const* const arguments, Character const* const* const environment ) throw() { typedef __crt_char_traits traits; _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1); _VALIDATE_RETURN(file_name[0] != '\0', EINVAL, -1); _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1); _VALIDATE_RETURN(arguments[0] != nullptr, EINVAL, -1); _VALIDATE_RETURN(arguments[0][0] != '\0', EINVAL, -1); __crt_errno_guard const guard_errno; // Attempt to perform the spawn with the given file name. If this succeeds // and is a spawn operation (mode != _P_OVERLAY), then it returns a status // code other than -1 and we can just return it. If this succeds and is an // exec operation (mode == _P_OVERLAY), then this call does not return. intptr_t const initial_result = traits::tspawnve(mode, file_name, arguments, environment); if (initial_result != -1) return initial_result; // If the spawn attempt failed, try to determine why. ENOENT indicates that // the spawn operation itself failed, and there's nothing we can do: if (errno != ENOENT) return -1; // If the file name is an absolute or relative path, then we do not search // the %PATH%, so there is nothing left to do: if (traits::tcschr(file_name, '\\') != nullptr) return -1; if (traits::tcschr(file_name, '/') != nullptr) return -1; if (file_name[1] == ':') return -1; // Otherwise, the file name string just names an executable. Let's try // appending it to each path in the %PATH% and see if we can find the // file to execute: Character const path_name[] = { 'P', 'A', 'T', 'H', '\0' }; __crt_unique_heap_ptr path_value; if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(path_value.get_address_of(), nullptr, path_name)) != 0) return -1; if (!path_value) return -1; // This will be used to store alternative path names to the executable: __crt_unique_heap_ptr const owned_file_buffer(_calloc_crt_t(Character, _MAX_PATH)); if (!owned_file_buffer) return -1; Character* file_buffer = owned_file_buffer.get(); Character* path_state = path_value.get(); while ((path_state = traits::tgetpath(path_state, file_buffer, _MAX_PATH - 1)) != 0) { if (!*file_buffer) break; // Append a '\' if necessary: Character* const last_character_it = file_buffer + traits::tcslen(file_buffer) - 1; if (last_character_it != traits::tcsrchr(file_buffer, '\\') && last_character_it != traits::tcsrchr(file_buffer, '/')) { Character const backslash_string[] = { '\\', '\0' }; _ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, backslash_string)); } // Make sure that the buffer is sufficiently large to hold the file name // concatenated onto the end of this %PATH% component. If it is not, we // return immediately (errno will still be set from the last call to // _spawnve(). if (traits::tcslen(file_buffer) + traits::tcslen(file_name) >= _MAX_PATH) break; _ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, file_name)); // Try the spawn again with the newly constructed path. Like before, if // this succeeds and is a spawn operation, then a status is returned and // we can return immediately. If this succeeds and is an exec operation, // the call does not return. errno = 0; intptr_t const result = traits::tspawnve(mode, file_buffer, arguments, environment); if (result != -1) return result; // Two classes of failures are acceptable here: either the operation // failed because the spawn operation itself failed (e.g., if the file // could not be found)... if (errno == ENOENT || _doserrno == ERROR_NOT_READY) continue; // ...or the path is a UNC path... bool const is_unc_path_with_slashes = traits::tcschr(file_buffer, '/') == file_buffer && traits::tcschr(file_buffer + 1, '/') == file_buffer + 1; bool const is_unc_path_with_backslashes = traits::tcschr(file_buffer, '\\') == file_buffer && traits::tcschr(file_buffer + 1, '\\') == file_buffer + 1; if (is_unc_path_with_slashes || is_unc_path_with_backslashes) continue; // ...otherwise, we report the error back to the caller: break; } return -1; } extern "C" intptr_t __cdecl _execvp( char const* const file_name, char const* const* const arguments ) { return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast(nullptr)); } extern "C" intptr_t __cdecl _execvpe( char const* const file_name, char const* const* const arguments, char const* const* const environment ) { return common_spawnvp(_P_OVERLAY, file_name, arguments, environment); } extern "C" intptr_t __cdecl _spawnvp( int const mode, char const* const file_name, char const* const* const arguments ) { return common_spawnvp(mode, file_name, arguments, static_cast(nullptr)); } extern "C" intptr_t __cdecl _spawnvpe( int const mode, char const* const file_name, char const* const* const arguments, char const* const* const environment ) { return common_spawnvp(mode, file_name, arguments, environment); } extern "C" intptr_t __cdecl _wexecvp( wchar_t const* const file_name, wchar_t const* const* const arguments ) { return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast(nullptr)); } extern "C" intptr_t __cdecl _wexecvpe( wchar_t const* const file_name, wchar_t const* const* const arguments, wchar_t const* const* const environment ) { return common_spawnvp(_P_OVERLAY, file_name, arguments, environment); } extern "C" intptr_t __cdecl _wspawnvp( int const mode, wchar_t const* const file_name, wchar_t const* const* const arguments ) { return common_spawnvp(mode, file_name, arguments, static_cast(nullptr)); } extern "C" intptr_t __cdecl _wspawnvpe( int const mode, wchar_t const* const file_name, wchar_t const* const* const arguments, wchar_t const* const* const environment ) { return common_spawnvp(mode, file_name, arguments, environment); }