ReactOS 0.4.16-dev-732-g2d1144a
popen.cpp
Go to the documentation of this file.
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//
9#include <process.h>
10
11
12
13#define STDIN 0
14#define STDOUT 1
15
16
17
18namespace {
19
20 template <typename Character>
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.
29 {
32 };
33
35 {
36 typedef FILE* type;
37
38 static bool close(_In_ type h) throw()
39 {
40 fclose(h);
41 return true;
42 }
43
45 {
46 return nullptr;
47 }
48 };
49
51 {
53
54 static bool close(_In_ type h) throw()
55 {
56 h->process_handle = 0;
57 h->stream = nullptr;
58 return true;
59 }
60
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.
77static unsigned __idtabsiz;
78static 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.
91static 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
129template <typename Character>
130static 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
155template <typename Character>
156static 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
171template <typename Character>
172static 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
187template <typename Character>
188static 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 {
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
228template <typename Character>
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,
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
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
339template <typename Character>
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
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 }
400 {
402 }
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.
417extern "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
427extern "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.
445extern "C" int __cdecl _pclose(FILE* const stream)
446{
447 _VALIDATE_RETURN(stream != nullptr, EINVAL, -1);
448
449 int return_value = -1;
450
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 }
484 {
486 }
488
489 return return_value;
490}
#define EINVAL
Definition: acclib.h:90
#define EINTR
Definition: acclib.h:80
#define EBADF
Definition: acclib.h:82
#define __cdecl
Definition: accygwin.h:79
void __cdecl __acrt_unlock(_In_ __acrt_lock_id lock)
Definition: locks.cpp:57
@ __acrt_popen_lock
#define _ERRCHECK(e)
#define _ERRCHECK_EINVAL(e)
#define _osfhnd(i)
bool __cdecl __crt_stdio_path_requires_backslash(char const *const first)
#define _VALIDATE_RETURN(expr, errorcode, retexpr)
result_buffer_count char *const _In_ int const _In_ bool const _In_ unsigned const _In_ STRFLT const _In_ bool const _Inout_ __crt_cached_ptd_host &ptd throw()
Definition: cvt.cpp:119
__acrt_lock(__acrt_heap_lock)
#define TRUE
Definition: types.h:120
#define _O_BINARY
Definition: cabinet.h:51
#define _O_NOINHERIT
Definition: cabinet.h:45
#define _O_TEXT
Definition: cabinet.h:50
#define CloseHandle
Definition: compat.h:739
#define GetCurrentProcess()
Definition: compat.h:759
#define MAX_PATH
Definition: compat.h:34
BOOL WINAPI DuplicateHandle(IN HANDLE hSourceProcessHandle, IN HANDLE hSourceHandle, IN HANDLE hTargetProcessHandle, OUT LPHANDLE lpTargetHandle, IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN DWORD dwOptions)
Definition: handle.c:149
unsigned int BOOL
Definition: ntddk_ex.h:94
static char * path_name(DOS_FILE *file)
Definition: check.c:208
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLuint buffer
Definition: glext.h:5915
GLenum mode
Definition: glext.h:6217
const GLint * first
Definition: glext.h:5794
GLuint64EXT * result
Definition: glext.h:11304
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
_Check_return_opt_ _CRTIMP int __cdecl fclose(_Inout_ FILE *_File)
struct task_struct * current
Definition: linux.c:32
static UINT UINT last
Definition: font.c:45
__crt_unique_handle_t< stream_traits > unique_stream
Definition: popen.cpp:67
__crt_unique_handle_t< process_handle_pair_traits > unique_process_handle_pair
Definition: popen.cpp:68
#define _In_
Definition: no_sal2.h:158
static Character const *__cdecl get_comspec()
Definition: popen.cpp:156
static Character const *__cdecl get_path()
Definition: popen.cpp:172
FILE *__cdecl _wpopen(wchar_t const *const command, wchar_t const *const type)
Definition: popen.cpp:427
#define STDOUT
Definition: popen.cpp:14
static Character const *__cdecl get_executable_path(Character const *const executable)
Definition: popen.cpp:188
int __cdecl _pclose(FILE *const stream)
Definition: popen.cpp:445
static FILE *__cdecl common_popen_nolock(Character const *const command, Character const *const fdopen_mode, int const std_fh, int(&pipe_handles)[2])
Definition: popen.cpp:229
#define STDIN
Definition: popen.cpp:13
static process_handle_pair *__cdecl idtab(FILE *const stream)
Definition: popen.cpp:91
static fdopen_mode< Character > __cdecl convert_popen_type_to_fdopen_mode(Character const *const type)
Definition: popen.cpp:130
static process_handle_pair * __idpairs
Definition: popen.cpp:78
static FILE *__cdecl common_popen(Character const *const command, Character const *const type)
Definition: popen.cpp:340
FILE *__cdecl _popen(char const *const command, char const *const type)
Definition: popen.cpp:417
static unsigned __idtabsiz
Definition: popen.cpp:77
#define __try
Definition: pseh2_64.h:172
#define __leave
Definition: pseh2_64.h:176
#define __endtry
Definition: pseh2_64.h:175
#define __finally
Definition: pseh2_64.h:174
#define errno
Definition: errno.h:18
_Check_return_opt_ _CRTIMP int __cdecl _close(_In_ int _FileHandle)
_Check_return_ _CRTIMP int __cdecl _pipe(_Inout_updates_(2) int *_PtHandles, _In_ unsigned int _PipeSize, _In_ int _TextMode)
#define _WAIT_GRANDCHILD
Definition: process.h:28
_CRTIMP intptr_t __cdecl _cwait(_Out_opt_ int *_TermStat, _In_ intptr_t _ProcHandle, _In_ int _Action)
#define SIZE_MAX
Definition: compat.h:66
#define _countof(array)
Definition: sndvol32.h:70
DWORD cb
Definition: winbase.h:877
HANDLE hStdError
Definition: winbase.h:894
DWORD dwFlags
Definition: winbase.h:888
HANDLE hStdOutput
Definition: winbase.h:893
HANDLE hStdInput
Definition: winbase.h:892
Definition: ps.c:97
Definition: parse.h:23
int errno_t
Definition: corecrt.h:615
int intptr_t
Definition: vcruntime.h:134
struct _PROCESS_INFORMATION PROCESS_INFORMATION
#define STARTF_USESTDHANDLES
Definition: winbase.h:525
#define DUPLICATE_SAME_ACCESS