Subversion Repositories Games.Chess Giants

Rev

Rev 150 | Rev 175 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
150 pmbaty 1
// pipe.cpp -- somewhat portable pipe implementation that resembles POSIX popen()/pclose()
2
 
3
#include "common.h"
4
 
5
 
6
// definitions used in this module only
7
#define RD 0
8
#define WR 1
9
 
10
 
11
#ifdef _WIN32
12
typedef struct pipe_s
13
{
14
   PROCESS_INFORMATION procinfo;
15
   int descriptors[2]; // RD and WR
16
} pipe_t;
17
#endif // _WIN32
18
 
19
 
20
// function prototypes
21
FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode);
22
int pipe_close (FILE *stream);
23
int pipe_isalive (FILE *stream);
24
int pipe_hasdata (FILE *stream);
25
int pipe_read (FILE *stream, void *dstbuf, int nbytes);
26
int pipe_write (FILE *stream, void *srcbuf, int nbytes);
27
 
28
 
29
FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode)
30
{
31
   // wrapper around popen(), and simple reimplementation of it for systems that don't have it
32
   // mode shall be "r", "w", or "r+"
33
 
34
#ifdef _WIN32
35
   #define IT_TO_ME 0
36
   #define ME_TO_IT 1
37
   SECURITY_ATTRIBUTES security_attributes;
38
   wchar_t current_path[MAX_PATH];
39
   wchar_t *command_pathname;
40
   STARTUPINFO startup_info;
41
   HANDLE hStdIO[2][2];
42
   pipe_t *pipe;
43
 
44
   (void) mode; // all pipes are open read-write
45
 
46
   command_pathname = (wchar_t *) malloc ((MAX_PATH + 4096) * sizeof (wchar_t)); // should be enough
47
   if (command_pathname == NULL)
48
      return (NULL);
49
 
50
   // figure out command pathname out of full command string
51
   wcscpy_s (command_pathname, MAX_PATH + 4096, shell_command);
52
   if ((command_pathname[0] == L'"') && (wcschr (&command_pathname[1], L'"') != NULL))
53
   {
54
      memmove (command_pathname, &command_pathname[1], (MAX_PATH + 4096 - 1) * sizeof (wchar_t));
55
      *wcschr (&command_pathname[1], L'"') = 0;
56
   }
57
   else if (wcschr (command_pathname, L' ') != NULL)
58
      *wcschr (command_pathname, L' ') = 0;
59
 
60
   // build the bidirectional I/O pipe
61
   pipe = (pipe_t *) malloc (sizeof (pipe_t));
62
   memset (&security_attributes, 0, sizeof (security_attributes)); // prepare the pipes' security attributes
63
   security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES);
64
   security_attributes.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited
65
   hStdIO[0][0] = hStdIO[0][1] = hStdIO[1][0] = hStdIO[1][1] = NULL;
66
   CreatePipe (&hStdIO[ME_TO_IT][RD], &hStdIO[ME_TO_IT][WR], &security_attributes, 0); // create a pipe for the child process's stdin
67
   CreatePipe (&hStdIO[IT_TO_ME][RD], &hStdIO[IT_TO_ME][WR], &security_attributes, 0); // create a pipe for the child process's stdout
68
   SetHandleInformation (hStdIO[ME_TO_IT][WR], HANDLE_FLAG_INHERIT, 0); // ensure the write handle to the pipe for STDIN is not inherited
69
   SetHandleInformation (hStdIO[IT_TO_ME][RD], HANDLE_FLAG_INHERIT, 0); // ensure the read handle to the pipe for STDOUT is not inherited
70
   pipe->descriptors[RD] = _open_osfhandle ((intptr_t) hStdIO[IT_TO_ME][RD], _O_BINARY);
71
   pipe->descriptors[WR] = _open_osfhandle ((intptr_t) hStdIO[ME_TO_IT][WR], _O_BINARY | _O_APPEND);
72
 
174 pmbaty 73
   // spawn the child process with redirected input and output
150 pmbaty 74
   memset (&startup_info, 0, sizeof (startup_info));
75
   startup_info.cb = sizeof (STARTUPINFOA);
76
   startup_info.dwFlags = STARTF_USESTDHANDLES;
77
   startup_info.hStdInput = hStdIO[ME_TO_IT][RD];
78
   startup_info.hStdOutput = hStdIO[IT_TO_ME][WR];
79
   startup_info.hStdError = hStdIO[IT_TO_ME][WR];
80
   GetCurrentDirectory (WCHAR_SIZEOF (current_path), current_path);
81
   if (!CreateProcess (command_pathname, (wchar_t *) shell_command, NULL, NULL, true, CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, NULL, current_path, &startup_info, &pipe->procinfo))
82
   {
83
      pipe_close ((FILE *) pipe);
84
      free (command_pathname);
85
      return (NULL);
86
   }
87
 
88
   // close the handles we won't use, free the command pathname memory space and return a pointer to our pipe object
89
   CloseHandle (hStdIO[IT_TO_ME][WR]);
90
   CloseHandle (hStdIO[ME_TO_IT][RD]);
91
   #undef IT_TO_ME
92
   #undef ME_TO_IT
93
   free (command_pathname);
94
   return ((FILE *) pipe);
95
#else // !_WIN32
96
   return (popen (shell_command, mode));
97
#endif // _WIN32
98
}
99
 
100
 
101
int pipe_close (FILE *stream)
102
{
103
   // wrapper around pclose(), and simple reimplementation of it for systems that don't have it
104
 
105
#ifdef _WIN32
106
   HANDLE hRemoteThread;
107
   HANDLE hProcessDup;
108
   DWORD handle_flags;
109
   DWORD thread_id;
110
   DWORD exit_code;
111
   pipe_t *pipe;
112
   bool was_duplicated;
113
 
114
   pipe = (pipe_t *) stream; // cast FILE * back into our own structure
115
 
116
   // close all handles
117
   if (pipe->descriptors[RD] != -1) _close (pipe->descriptors[RD]); pipe->descriptors[RD] = -1;
118
   if (pipe->descriptors[WR] != -1) _close (pipe->descriptors[WR]); pipe->descriptors[WR] = -1;
119
 
120
   // terminate process if it hasn't terminated by itself yet
121
   exit_code = 0;
122
   if (GetExitCodeProcess (pipe->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE))
123
   {
124
      // taken from Dr. Dobbs. How to terminate any process cleanly: create a remote thread in it,
125
      // and make its start address point right into kernel32's ExitProcess() function...
126
      was_duplicated = (DuplicateHandle (GetCurrentProcess (), pipe->procinfo.hProcess, GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS, FALSE, 0) != 0);
127
      if (!was_duplicated)
128
         hProcessDup = pipe->procinfo.hProcess;
129
      hRemoteThread = CreateRemoteThread (hProcessDup, NULL, 0, (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle (L"Kernel32"), "ExitProcess"), (void *) 0, 0, &thread_id);
130
      if (hRemoteThread != NULL)
131
      {
132
         WaitForSingleObject (hProcessDup, INFINITE); // must wait process to terminate to guarantee that it has exited
133
         CloseHandle (hRemoteThread);
134
      }
135
      else
136
         TerminateProcess (pipe->procinfo.hProcess, 0); // if process refuses our remote thread, kill it
137
      if (was_duplicated)
138
         CloseHandle (hProcessDup);
139
   }
140
 
141
   // close process handles
142
   if (pipe->procinfo.hProcess != NULL)
143
      CloseHandle (pipe->procinfo.hProcess);
144
   if (GetHandleInformation (pipe->procinfo.hThread, &handle_flags))
145
      CloseHandle (pipe->procinfo.hThread);
146
 
147
   // and finally free the pipe structure
148
   free (pipe);
149
   return ((int) exit_code);
150
#else // !_WIN32
151
   return (pclose (stream));
152
#endif // _WIN32
153
}
154
 
155
 
156
int pipe_isalive (FILE *stream)
157
{
158
   // wrapper around ioctl() on pipes for systems that don't have it
159
 
160
#ifdef _WIN32
161
   DWORD exit_code;
162
   return (GetExitCodeProcess (((pipe_t *) stream)->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE));
163
#else // !_WIN32
164
   return (1); // UNIX doesn't provide a portable way for this
165
#endif // _WIN32
166
}
167
 
168
 
169
int pipe_hasdata (FILE *stream)
170
{
171
   // wrapper around ioctl() on pipes for systems that don't have it
172
 
173
#ifdef _WIN32
174
   return (!_eof (((pipe_t *) stream)->descriptors[RD]));
175
#else // !_WIN32
176
   return (feof (stream));
177
#endif // _WIN32
178
}
179
 
180
 
181
int pipe_read (FILE *stream, void *dstbuf, int nbytes)
182
{
183
   // wrapper around fread() on pipes for systems that don't support it
184
 
185
#ifdef _WIN32
186
   return (_read (((pipe_t *) stream)->descriptors[RD], dstbuf, nbytes));
187
#else // !_WIN32
188
   return (fread (dstbuf, 1, nbytes, stream));
189
#endif // _WIN32
190
}
191
 
192
 
193
int pipe_write (FILE *stream, void *srcbuf, int nbytes)
194
{
195
   // wrapper around fwrite() on pipes for systems that don't support it
196
 
197
#ifdef _WIN32
198
   return (_write (((pipe_t *) stream)->descriptors[WR], srcbuf, nbytes));
199
#else // !_WIN32
200
   return (fwrite (srcbuf, 1, nbytes, stream));
201
#endif // _WIN32
202
}