Reactos
at master 451 lines 16 kB view raw
1// 2// spawnv.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines the -v and -ve flavors of the _exec() and _spawn() functions. 7// 8// There are many varieties of the _exec() and _spawn() functions. A high level 9// summary of the behavioral differences is as follows: 10// 11// All of these functions execute a new process. The _spawn() functions all take 12// a 'mode' parameter, which specifies the way in which the process is started. 13// The 'mode' must be one of the _P_-prefixed modes from <process.h>. The _exec() 14// family of functions execute a new process, then call _exit() to terminate the 15// calling process. Each _exit() function is equivalent to the corresponding 16// _spawn() function with the _P_OVERLAY mode. 17// 18// There are eight variants each of _exec() and _spawn(), suffixed with -l, -le, 19// -lp, -lpe, -v, -ve, -vp, and -vpe. 20// 21// If a function has 'e' in its suffix, it accepts an environment with which to 22// create the new process. If a function does not have 'e' in its suffix, it 23// does not accept an environment and instead uses the environment of the calling 24// process. A call to an 'e' function with a null environment argument has the 25// same effect as calling the non-'e'-suffixed equivalent. 26// 27// Each _exec() or _spawn() function has either an 'l' or 'v' suffix. These have 28// equivalent functionality; they differ only in how they accept their arguments. 29// The 'l'-suffixed functions accept the command-line arguments and the environment 30// as varargs. There must be at least one command line argument (conventionally 31// the name of the program to be executed). If the function accepts an environment 32// (if it is 'e'-suffixed), then there must be a null pointer between the last 33// argument and the first environment variable. The arguments are terminated by a 34// null pointer. 35// 36// The 'v'-suffixed functions accept a pair of pointers: one to the argv vector 37// and one to the envp vector. Each is an array of pointers terminated by a null 38// pointer, similar to how arguments and the environment are passed to main(). 39// 40// Finally, if a function has a 'p' in its suffix, and if the provided executable 41// file name is just a file name (and does not contain any path component), the 42// %PATH% is searched for an executable with the given name. If a function does 43// not have a 'p' suffix, then the environment is not searched; the executable 44// must be found simply by passing its name to CreateProcess. 45// 46// All functions return -1 and set errno on failure. On success, the _exec() 47// functions do not return. On success, the _spawn() functions return different 48// things, depending on the provided mode. See the CreateProcess invocation 49// logic in this file for details. 50// 51// Note that the only supported modes are wait and overlay. 52// 53// These functions may set errno to one of the following values: 54// * E2BIG: Failed in argument or environment processing because the argument 55// list or environment is too large. 56// * EACCESS: Locking or sharing violation on a file 57// * EMFILE: Too many files open 58// * ENOENT: Failed to find the program (no such file or directory) 59// * ENOEXEC: Failed in a call to exec() due to a bad executable format 60// * ENOMEM: Failed to allocate memory required for the spawn operation 61// * EINVAL: Invalid mode argumnt or process state for spawn (note that most 62// invalid arguments cause the invalid parameter handler to be 63// invoked). 64// 65#include <corecrt_internal.h> 66#include <corecrt_internal_lowio.h> 67#include <corecrt_internal_traits.h> 68#include <errno.h> 69#include <io.h> 70#include <mbstring.h> 71#include <process.h> 72#include <stdlib.h> 73#include <string.h> 74 75 76 77// Accumulates the inheritable file handles into *data, in the structure expected 78// by the spawnee (see the lowio initialization code for the logic that decodes 79// this data structure). On success, *data and *size have the handle data array 80// and the size of the handle data, and true is returned. The caller must free 81// *data. On failure, false is returned and errno is set. 82static bool accumulate_inheritable_handles( 83 BYTE** const data, 84 size_t* const size, 85 bool const include_std_handles 86 ) throw() 87{ 88 return __acrt_lock_and_call(__acrt_lowio_index_lock, [&]() -> bool 89 { 90 *data = nullptr; 91 *size = 0; 92 93 // Count the number of handles to be inherited: 94 size_t handle_count = 0; 95 for (handle_count = _nhandle; handle_count != 0 && _osfile(handle_count - 1) != 0; --handle_count) 96 { 97 } 98 99 size_t const max_handle_count = (USHRT_MAX - sizeof(int)) / (sizeof(char) + sizeof(intptr_t)); 100 _VALIDATE_RETURN_NOEXC(handle_count < max_handle_count, ENOMEM, false); 101 102 size_t const handle_data_header_size = sizeof(int); 103 size_t const handle_data_element_size = sizeof(char) + sizeof(intptr_t); 104 105 unsigned short const handle_data_size = static_cast<unsigned short>( 106 handle_data_header_size + 107 handle_count * handle_data_element_size); 108 109 __crt_unique_heap_ptr<BYTE> handle_data(_calloc_crt_t(BYTE, handle_data_size)); 110 _VALIDATE_RETURN_NOEXC(handle_data.get() != nullptr, ENOMEM, false); 111 112 // Set the handle count in the data: 113 *reinterpret_cast<int*>(handle_data.get()) = static_cast<int>(handle_count); 114 115 auto const first_flags = reinterpret_cast<char*>(handle_data.get() + sizeof(int)); 116 auto const first_handle = reinterpret_cast<intptr_t UNALIGNED*>(first_flags + (handle_count * sizeof(char))); 117 118 // Copy the handle data: 119 auto flags_it = first_flags; 120 auto handle_it = first_handle; 121 for (size_t i = 0; i != handle_count; ++i, ++flags_it, ++handle_it) 122 { 123 __crt_lowio_handle_data* const pio = _pioinfo(i); 124 if ((pio->osfile & FNOINHERIT) == 0) 125 { 126 *flags_it = pio->osfile; 127 *handle_it = pio->osfhnd; 128 } 129 else 130 { 131 *flags_it = 0; 132 *handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE); 133 } 134 } 135 136 // Exclude the first three handles (stdin, stdout, stderr) if asked: 137 if (!include_std_handles) 138 { 139 flags_it = first_flags; 140 handle_it = first_handle; 141 for (size_t i = 0; i != __min(handle_count, 3); ++i, ++flags_it, ++handle_it) 142 { 143 *flags_it = 0; 144 *handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE); 145 } 146 } 147 148 *data = handle_data.detach(); 149 *size = handle_data_size; 150 return true; 151 }); 152} 153 154 155 156static bool __cdecl should_create_unicode_environment(char) throw() { return false; } 157static bool __cdecl should_create_unicode_environment(wchar_t) throw() { return true; } 158 159 160 161// Spawns a child process. The mode must be one of the _P-modes from <process.h>. 162// The return value depends on the mode: 163// * _P_OVERLAY: On success, calls _exit() and does not return. Returns -1 on failure. 164// * _P_WAIT: Returns (termination_code << 8 + result_code) 165// * _P_DETACH: Returns 0 on success; -1 on failure 166// * Others: Returns a handle to the process. The caller must close the handle. 167template <typename Character> 168static intptr_t __cdecl execute_command( 169 int const mode, 170 Character const* const file_name, 171 Character const* const* const arguments, 172 Character const* const* const environment 173 ) throw() 174{ 175 typedef __crt_char_traits<Character> traits; 176 177 _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1); 178 _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1); 179 180 _VALIDATE_CLEAR_OSSERR_RETURN(mode >= 0 && mode <= _P_DETACH, EINVAL, -1); 181 182 __crt_unique_heap_ptr<Character> command_line; 183 __crt_unique_heap_ptr<Character> environment_block; 184 if (traits::pack_command_line_and_environment( 185 arguments, 186 environment, 187 command_line.get_address_of(), 188 environment_block.get_address_of() 189 ) == -1) 190 { 191 return -1; 192 } 193 194 __crt_unique_heap_ptr<BYTE> handle_data; 195 size_t handle_data_size; 196 if (!accumulate_inheritable_handles(handle_data.get_address_of(), &handle_data_size, mode != _P_DETACH)) 197 return -1; 198 199 DWORD creation_flags = 0; 200 if (mode == _P_DETACH) 201 creation_flags |= DETACHED_PROCESS; 202 203 if (should_create_unicode_environment(Character())) 204 creation_flags |= CREATE_UNICODE_ENVIRONMENT; 205 206 _doserrno = 0; 207 208 STARTUPINFOW startup_info = { }; 209 startup_info.cb = sizeof(startup_info); 210 startup_info.cbReserved2 = static_cast<WORD>(handle_data_size); 211 startup_info.lpReserved2 = handle_data.get(); 212 213 PROCESS_INFORMATION process_info; 214 BOOL const create_process_status = traits::create_process( 215 const_cast<Character*>(file_name), 216 command_line.get(), 217 nullptr, 218 nullptr, 219 TRUE, 220 creation_flags, 221 environment_block.get(), 222 nullptr, 223 &startup_info, 224 &process_info); 225 226 __crt_unique_handle process_handle(process_info.hProcess); 227 __crt_unique_handle thread_handle(process_info.hThread); 228 229 if (!create_process_status) 230 { 231 __acrt_errno_map_os_error(GetLastError()); 232 return -1; 233 } 234 235 if (mode == _P_OVERLAY) 236 { 237 // Destroy ourselves: 238 _exit(0); 239 } 240 else if (mode == _P_WAIT) 241 { 242 WaitForSingleObject(process_info.hProcess, static_cast<DWORD>(-1)); 243 244 // Return the termination code and exit code. Note that we return 245 // the full exit code. 246 DWORD exit_code; 247 if (0 != GetExitCodeProcess(process_info.hProcess, &exit_code)) 248 { 249 return static_cast<int>(exit_code); 250 } 251 else 252 { 253 __acrt_errno_map_os_error(GetLastError()); 254 return -1; 255 } 256 } 257 else if (mode == _P_DETACH) 258 { 259 /* like totally detached asynchronous spawn, dude, 260 close process handle, return 0 for success */ 261 return 0; 262 } 263 else 264 { 265 // Asynchronous spawn: return process handle: 266 return reinterpret_cast<intptr_t>(process_handle.detach()); 267 } 268} 269 270 271 272template <typename Character> 273static intptr_t __cdecl common_spawnv( 274 int const mode, 275 Character const* const file_name, 276 Character const* const* const arguments, 277 Character const* const* const environment 278 ) throw() 279{ 280 typedef __crt_char_traits<Character> traits; 281 282 _VALIDATE_RETURN(file_name != nullptr, EINVAL, -1); 283 _VALIDATE_RETURN(file_name[0] != '\0', EINVAL, -1); 284 _VALIDATE_RETURN(arguments != nullptr, EINVAL, -1); 285 _VALIDATE_RETURN(arguments[0] != nullptr, EINVAL, -1); 286 _VALIDATE_RETURN(arguments[0][0] != '\0', EINVAL, -1); 287 288 Character const* const final_backslash = traits::tcsrchr(file_name, '\\'); 289 Character const* const final_slash = traits::tcsrchr(file_name, '/'); 290 291 Character const* mutated_file_name = file_name; 292 Character const* end_of_directory = final_backslash; 293 if (!final_slash) 294 { 295 if (!final_backslash) 296 { 297 Character const* const final_colon = traits::tcsrchr(file_name, ':'); 298 if (final_colon) 299 { 300 end_of_directory = final_colon; 301 } 302 else 303 { 304 // The path is a file name only. We force it to be a relative 305 // path name. 306 size_t const file_name_size = traits::tcslen(file_name) + 3; 307 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, file_name_size)); 308 if (!buffer) 309 return -1; 310 311 static Character const dot_slash[] = { '.', '\\', '\0' }; 312 _ERRCHECK(traits::tcscpy_s(buffer.get(), file_name_size, dot_slash)); 313 _ERRCHECK(traits::tcscat_s(buffer.get(), file_name_size, file_name)); 314 315 mutated_file_name = buffer.detach(); 316 end_of_directory = mutated_file_name + 2; // Adjust for ".\" 317 } 318 } 319 } 320 else if (!final_backslash || final_slash > final_backslash) 321 { 322 end_of_directory = final_slash; 323 } 324 325 // If we allocated a file name above, make sure we clean it up: 326 __crt_unique_heap_ptr<Character const> const mutated_file_name_cleanup(file_name == mutated_file_name 327 ? nullptr 328 : mutated_file_name); 329 330 if (traits::tcsrchr(end_of_directory, '.')) 331 { 332 // If an extension was provided, just invoke the path: 333 if (traits::taccess_s(mutated_file_name, 0) == 0) 334 { 335 return execute_command(mode, mutated_file_name, arguments, environment); 336 } 337 } 338 else 339 { 340 // If no extension was provided, try known executable extensions: 341 size_t const buffer_size = traits::tcslen(mutated_file_name) + 5; 342 __crt_unique_heap_ptr<Character> const buffer(_calloc_crt_t(Character, buffer_size)); 343 if (!buffer) 344 return -1; 345 346 _ERRCHECK(traits::tcscpy_s(buffer.get(), buffer_size, mutated_file_name)); 347 Character* extension_buffer = buffer.get() + buffer_size - 5; 348 349 typedef Character const extension_type[5]; 350 static extension_type const extensions[4] = 351 { 352 { '.', 'c', 'o', 'm', '\0' }, 353 { '.', 'e', 'x', 'e', '\0' }, 354 { '.', 'b', 'a', 't', '\0' }, 355 { '.', 'c', 'm', 'd', '\0' } 356 }; 357 358 errno_t const saved_errno = errno; 359 360 extension_type const* const first_extension = extensions; 361 extension_type const* const last_extension = first_extension + _countof(extensions); 362 for (auto it = first_extension; it != last_extension; ++it) 363 { 364 _ERRCHECK(traits::tcscpy_s(extension_buffer, 5, *it)); 365 366 if (traits::taccess_s(buffer.get(), 0) == 0) 367 { 368 errno = saved_errno; 369 return execute_command(mode, buffer.get(), arguments, environment); 370 } 371 } 372 } 373 374 return -1; 375} 376 377 378 379extern "C" intptr_t __cdecl _execv( 380 char const* const file_name, 381 char const* const* const arguments 382 ) 383{ 384 return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<char const* const*>(nullptr)); 385} 386 387extern "C" intptr_t __cdecl _execve( 388 char const* const file_name, 389 char const* const* const arguments, 390 char const* const* const environment 391 ) 392{ 393 return common_spawnv(_P_OVERLAY, file_name, arguments, environment); 394} 395 396extern "C" intptr_t __cdecl _spawnv( 397 int const mode, 398 char const* const file_name, 399 char const* const* const arguments 400 ) 401{ 402 return common_spawnv(mode, file_name, arguments, static_cast<char const* const*>(nullptr)); 403} 404 405extern "C" intptr_t __cdecl _spawnve( 406 int const mode, 407 char const* const file_name, 408 char const* const* const arguments, 409 char const* const* const environment 410 ) 411{ 412 return common_spawnv(mode, file_name, arguments, environment); 413} 414 415 416 417extern "C" intptr_t __cdecl _wexecv( 418 wchar_t const* const file_name, 419 wchar_t const* const* const arguments 420 ) 421{ 422 return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<wchar_t const* const*>(nullptr)); 423} 424 425extern "C" intptr_t __cdecl _wexecve( 426 wchar_t const* const file_name, 427 wchar_t const* const* const arguments, 428 wchar_t const* const* const environment 429 ) 430{ 431 return common_spawnv(_P_OVERLAY, file_name, arguments, environment); 432} 433 434extern "C" intptr_t __cdecl _wspawnv( 435 int const mode, 436 wchar_t const* const file_name, 437 wchar_t const* const* const arguments 438 ) 439{ 440 return common_spawnv(mode, file_name, arguments, static_cast<wchar_t const* const*>(nullptr)); 441} 442 443extern "C" intptr_t __cdecl _wspawnve( 444 int const mode, 445 wchar_t const* const file_name, 446 wchar_t const* const* const arguments, 447 wchar_t const* const* const environment 448 ) 449{ 450 return common_spawnv(mode, file_name, arguments, environment); 451}