1 // 2 // popen.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // The _popen() and _pclose() functions, which open a pipe to a child process. 7 // 8 #include <corecrt_internal_stdio.h> 9 #include <process.h> 10 11 12 13 #define STDIN 0 14 #define STDOUT 1 15 16 17 18 namespace { 19 20 template <typename Character> 21 struct fdopen_mode 22 { 23 Character mode[3]; 24 }; 25 26 // This is the entry type for the stream pointer / process handle pairs that 27 // are stored for each outstanding popen. 28 struct process_handle_pair 29 { 30 FILE* stream; 31 intptr_t process_handle; 32 }; 33 34 struct stream_traits 35 { 36 typedef FILE* type; 37 38 static bool close(_In_ type h) throw() 39 { 40 fclose(h); 41 return true; 42 } 43 44 static type get_invalid_value() throw() 45 { 46 return nullptr; 47 } 48 }; 49 50 struct process_handle_pair_traits 51 { 52 typedef process_handle_pair* type; 53 54 static bool close(_In_ type h) throw() 55 { 56 h->process_handle = 0; 57 h->stream = nullptr; 58 return true; 59 } 60 61 static type get_invalid_value() throw() 62 { 63 return nullptr; 64 } 65 }; 66 67 typedef __crt_unique_handle_t<stream_traits> unique_stream; 68 typedef __crt_unique_handle_t<process_handle_pair_traits> unique_process_handle_pair; 69 } 70 71 72 73 // The global table of stream pointer / process handle pairs. Access to this 74 // global tbale is only done via the idtab function. The table is expanded as 75 // necessary (by idtab), and free table entries are reused. (An entry is free 76 // if its stream is null.) The table is never contracted. 77 static unsigned __idtabsiz; 78 static process_handle_pair* __idpairs; 79 80 81 82 // Finds the entry for the given stream in the global table. If the stream is 83 // found, a pointer to it is returned; if the stream is not found, null is 84 // returned. 85 // 86 // If the stream is null, a new entry is allocated and a pointer to it is 87 // returned. If no entries are available and expansion of the table fails, 88 // null is returned. 89 // 90 // This function assumes the caller has acquired the lock on the table already. 91 static process_handle_pair* __cdecl idtab(FILE* const stream) throw() 92 { 93 // Search the table, and return the matching entry if one is found: 94 process_handle_pair* const first = __idpairs; 95 process_handle_pair* const last = first + __idtabsiz; 96 for (process_handle_pair* it = first; it != last; ++it) 97 { 98 if (it->stream == stream) 99 return it; 100 } 101 102 // We did not find an entry in the table. If the stream is null, then we 103 // try creating or expanding the table. Otherwise, we return null. Note 104 // that when the table is created or expanded, exactly one new entry is 105 // produced. This must not be changed unless code is added to mark the 106 // extra entries as being free (e.g., by setting their stream fields to null. 107 if (stream != nullptr) 108 return nullptr; 109 110 if (__idtabsiz + 1 < __idtabsiz) 111 return nullptr; 112 113 if (__idtabsiz + 1 >= SIZE_MAX / sizeof(process_handle_pair)) 114 return nullptr; 115 116 process_handle_pair* const newptr = _recalloc_crt_t(process_handle_pair, __idpairs, __idtabsiz + 1).detach(); 117 if (newptr == nullptr) 118 return nullptr; 119 120 __idpairs = newptr; 121 process_handle_pair* const pairptr = newptr + __idtabsiz; 122 ++__idtabsiz; 123 124 return pairptr; 125 } 126 127 128 129 template <typename Character> 130 static fdopen_mode<Character> __cdecl convert_popen_type_to_fdopen_mode( 131 Character const* const type 132 ) throw() 133 { 134 fdopen_mode<Character> result = fdopen_mode<Character>(); 135 136 Character const* type_it = type; 137 138 while (*type_it == ' ') 139 ++type_it; 140 141 _VALIDATE_RETURN(*type_it == 'w' || *type_it == 'r', EINVAL, result); 142 result.mode[0] = *type_it++; 143 144 while (*type_it == ' ') 145 ++type_it; 146 147 _VALIDATE_RETURN(*type_it == '\0' || *type_it == 't' || *type_it == 'b', EINVAL, result); 148 result.mode[1] = *type_it; 149 150 return result; 151 } 152 153 154 155 template <typename Character> 156 static Character const* __cdecl get_comspec() throw() 157 { 158 typedef __acrt_stdio_char_traits<Character> stdio_traits; 159 160 static Character const comspec_name[] = { 'C', 'O', 'M', 'S', 'P', 'E', 'C', '\0' }; 161 162 Character* comspec_value = nullptr; 163 if (_ERRCHECK_EINVAL(stdio_traits::tdupenv_s_crt(&comspec_value, nullptr, comspec_name)) != 0) 164 return nullptr; 165 166 return comspec_value; 167 } 168 169 170 171 template <typename Character> 172 static Character const* __cdecl get_path() throw() 173 { 174 typedef __acrt_stdio_char_traits<Character> stdio_traits; 175 176 static Character const path_name[] = { 'P', 'A', 'T', 'H', '\0' }; 177 178 Character* path_value = nullptr; 179 if (_ERRCHECK_EINVAL(stdio_traits::tdupenv_s_crt(&path_value, nullptr, path_name)) != 0) 180 return nullptr; 181 182 return path_value; 183 } 184 185 186 187 template <typename Character> 188 static Character const* __cdecl get_executable_path( 189 Character const* const executable 190 ) throw() 191 { 192 typedef __acrt_stdio_char_traits<Character> stdio_traits; 193 194 // If we can access the given path, just use it: 195 if (stdio_traits::taccess_s(executable, 0) == 0) 196 return executable; 197 198 // Otherwise, we need to search the PATH: 199 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, MAX_PATH)); 200 if (buffer.get() == nullptr) 201 return nullptr; 202 203 __crt_unique_heap_ptr<Character const> path(get_path<Character>()); 204 205 Character const* current = path.get(); 206 while ((current = stdio_traits::tgetpath(current, buffer.get(), MAX_PATH - 1)) != 0) 207 { 208 if (__crt_stdio_path_requires_backslash(buffer.get())) 209 { 210 static Character const backslash[] = { '\\', '\0' }; 211 _ERRCHECK(stdio_traits::tcscat_s(buffer.get(), MAX_PATH, backslash)); 212 } 213 214 if (stdio_traits::tcslen(buffer.get()) + stdio_traits::tcslen(executable) >= MAX_PATH) 215 return nullptr; 216 217 _ERRCHECK(stdio_traits::tcscat_s(buffer.get(), MAX_PATH, executable)); 218 219 if (stdio_traits::taccess_s(buffer.get(), 0) == 0) 220 return buffer.detach(); 221 } 222 223 return nullptr; 224 } 225 226 227 228 template <typename Character> 229 static FILE* __cdecl common_popen_nolock( 230 Character const* const command, 231 Character const* const fdopen_mode, 232 int const std_fh, 233 int (&pipe_handles)[2] 234 ) throw() 235 { 236 typedef __acrt_stdio_char_traits<Character> stdio_traits; 237 238 HANDLE const process_handle = GetCurrentProcess(); 239 240 // We only return the second pipe handle to the caller; for the first pipe, 241 // we just need to use the HANDLE: 242 __crt_unique_handle new_pipe_handle; 243 if (!DuplicateHandle( 244 process_handle, 245 reinterpret_cast<HANDLE>(_osfhnd(pipe_handles[0])), 246 process_handle, 247 new_pipe_handle.get_address_of(), 248 0, 249 TRUE, 250 DUPLICATE_SAME_ACCESS)) 251 { 252 return nullptr; 253 } 254 255 _close(pipe_handles[0]); 256 pipe_handles[0] = -1; 257 258 // Associate a stream with the pipe handle to be returned to the caller: 259 unique_stream pipe_stream(stdio_traits::tfdopen(pipe_handles[1], fdopen_mode)); 260 if (!pipe_stream) 261 return nullptr; 262 263 // Obtain a proces handle pair in which to store the process handle: 264 unique_process_handle_pair id_pair(idtab(nullptr)); 265 if (!id_pair) 266 return nullptr; 267 268 // Determine which command processor to use: command.com or cmd.exe: 269 static Character const default_cmd_exe[] = { 'c', 'm', 'd', '.', 'e', 'x', 'e', '\0' }; 270 271 __crt_unique_heap_ptr<Character const> const comspec_variable(get_comspec<Character>()); 272 Character const* const cmd_exe = comspec_variable.get() != nullptr 273 ? comspec_variable.get() 274 : default_cmd_exe; 275 276 STARTUPINFOW startup_info = { 0 }; 277 startup_info.cb = sizeof(startup_info); 278 279 // The following arguments are used by the OS for duplicating the handles: 280 startup_info.dwFlags = STARTF_USESTDHANDLES; 281 startup_info.hStdInput = std_fh == STDIN ? new_pipe_handle.get() : reinterpret_cast<HANDLE>(_osfhnd(0)); 282 startup_info.hStdOutput = std_fh == STDOUT ? new_pipe_handle.get() : reinterpret_cast<HANDLE>(_osfhnd(1)); 283 startup_info.hStdError = reinterpret_cast<HANDLE>(_osfhnd(2)); 284 285 static Character const slash_c[] = { ' ', '/', 'c', ' ', '\0' }; 286 287 size_t const command_line_count = 288 stdio_traits::tcslen(cmd_exe) + 289 stdio_traits::tcslen(slash_c) + 290 stdio_traits::tcslen(command) + 291 1; 292 293 __crt_unique_heap_ptr<Character> const command_line(_calloc_crt_t(Character, command_line_count)); 294 if (command_line.get() == nullptr) 295 return nullptr; 296 297 _ERRCHECK(stdio_traits::tcscpy_s(command_line.get(), command_line_count, cmd_exe)); 298 _ERRCHECK(stdio_traits::tcscat_s(command_line.get(), command_line_count, slash_c)); 299 _ERRCHECK(stdio_traits::tcscat_s(command_line.get(), command_line_count, command)); 300 301 // Find the path at which the executable is accessible: 302 Character const* const selected_cmd_exe(get_executable_path(cmd_exe)); 303 if (selected_cmd_exe == nullptr) 304 return nullptr; 305 306 // If get_executable_path() returned a path other than the one we gave it, 307 // we must be sure to free the string when we return: 308 __crt_unique_heap_ptr<Character const> const owned_final_exe_path(selected_cmd_exe != cmd_exe 309 ? selected_cmd_exe 310 : nullptr); 311 312 PROCESS_INFORMATION process_info = PROCESS_INFORMATION(); 313 BOOL const child_status = stdio_traits::create_process( 314 selected_cmd_exe, 315 command_line.get(), 316 nullptr, 317 nullptr, 318 TRUE, 319 0, 320 nullptr, 321 nullptr, 322 &startup_info, 323 &process_info); 324 325 if (!child_status) 326 return nullptr; 327 328 FILE* const result_stream = pipe_stream.detach(); 329 330 CloseHandle(process_info.hThread); 331 id_pair.get()->process_handle = reinterpret_cast<intptr_t>(process_info.hProcess); 332 id_pair.get()->stream = result_stream; 333 id_pair.detach(); 334 return result_stream; 335 } 336 337 338 339 template <typename Character> 340 static FILE* __cdecl common_popen( 341 Character const* const command, 342 Character const* const type 343 ) throw() 344 { 345 _VALIDATE_RETURN(command != nullptr, EINVAL, nullptr); 346 _VALIDATE_RETURN(type != nullptr, EINVAL, nullptr); 347 348 fdopen_mode<Character> const fdopen_mode = convert_popen_type_to_fdopen_mode(type); 349 if (fdopen_mode.mode[0] == '\0') 350 return nullptr; 351 352 // Do the _pipe(). Note that neither of the resulting handles is inheritable. 353 int pipe_mode = _O_NOINHERIT; 354 if (fdopen_mode.mode[1] == 't') { pipe_mode |= _O_TEXT; } 355 if (fdopen_mode.mode[1] == 'b') { pipe_mode |= _O_BINARY; } 356 357 int pipe_handles[2]; 358 if (_pipe(pipe_handles, 1024, pipe_mode) == -1) 359 return nullptr; 360 361 int const std_fh = fdopen_mode.mode[0] == 'w' 362 ? STDIN 363 : STDOUT; 364 365 int ordered_pipe_handles[] = 366 { 367 std_fh == STDIN ? pipe_handles[0] : pipe_handles[1], 368 std_fh == STDIN ? pipe_handles[1] : pipe_handles[0] 369 }; 370 371 FILE* return_value = nullptr; 372 373 __acrt_lock(__acrt_popen_lock); 374 __try 375 { 376 errno_t const saved_errno = errno; 377 378 return_value = common_popen_nolock( 379 command, 380 fdopen_mode.mode, 381 std_fh, 382 ordered_pipe_handles); 383 384 errno = saved_errno; 385 386 if (return_value != nullptr) 387 __leave; 388 389 // If the implementation function returned successfully, everything was 390 // cleaned up except the lock. 391 int* const first = ordered_pipe_handles; 392 int* const last = first + _countof(ordered_pipe_handles); 393 for (int* it = first; it != last; ++it) 394 { 395 if (*it != -1) 396 _close(*it); 397 } 398 } 399 __finally 400 { 401 __acrt_unlock(__acrt_popen_lock); 402 } 403 __endtry 404 405 return return_value; 406 } 407 408 409 410 // Starts a child process using the given 'command' and opens a pipe to it, as 411 // requested via the 'type'. If the 'type' string contains an 'r', the calling 412 // process can read the child command's standard output via the returned stream. 413 // If the 'type' string contains a 'w', the calling process can write to the 414 // child command's standard input via the returned stream. 415 // 416 // Returns a usable stream on success; returns null on failure. 417 extern "C" FILE* __cdecl _popen( 418 char const* const command, 419 char const* const type 420 ) 421 { 422 return common_popen(command, type); 423 } 424 425 426 427 extern "C" FILE* __cdecl _wpopen( 428 wchar_t const* const command, 429 wchar_t const* const type 430 ) 431 { 432 return common_popen(command, type); 433 } 434 435 436 437 // Waits on the child command with which the 'stream' is associated, then closes 438 // the stream and its associated pipe. The 'stream' must have been returned from 439 // a previous call to _popen(). This function looks up the process handle in the 440 // global table, waits on it, then closes the stream. 441 // 442 // On success, the exit status of the child command is returned. The format of 443 // the return value is the same as for cwait(), except that the low order and 444 // high order bytes are swapped. If an error occurs, -1 is returned. 445 extern "C" int __cdecl _pclose(FILE* const stream) 446 { 447 _VALIDATE_RETURN(stream != nullptr, EINVAL, -1); 448 449 int return_value = -1; 450 451 __acrt_lock(__acrt_popen_lock); 452 __try 453 { 454 process_handle_pair* const id_pair = idtab(stream); 455 if (id_pair == nullptr) 456 { 457 errno = EBADF; 458 __leave; 459 } 460 461 fclose(stream); 462 463 intptr_t const process_handle = id_pair->process_handle; 464 465 // Mark the id pair as free (we will close the handle in the call to _cwait): 466 id_pair->stream = nullptr; 467 id_pair->process_handle = 0; 468 469 // Wait on the child copy of the command processor and its children: 470 errno_t const saved_errno = errno; 471 errno = 0; 472 473 int status = 0; 474 if (_cwait(&status, process_handle, _WAIT_GRANDCHILD) != -1 || errno == EINTR) 475 { 476 errno = saved_errno; 477 return_value = status; 478 __leave; 479 } 480 481 errno = saved_errno; 482 } 483 __finally 484 { 485 __acrt_unlock(__acrt_popen_lock); 486 } 487 __endtry 488 489 return return_value; 490 } 491