-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcygnative.c
324 lines (276 loc) · 8.51 KB
/
cygnative.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*-
* Copyright (c) 2009 Frank Behrens <frank@pinky.sax.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $HeadURL: svn+ssh://moon.behrens/var/repos/sources/cygwinNativeWrapper/cygnative.c $
* $LastChangedDate: 2009-08-18 08:21:46 +0200 (Di, 18 Aug 2009) $
* $LastChangedRevision: 157 $
* $LastChangedBy: frank $
*/
#define PTHREAD 1
#include <windows.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <io.h>
#include <errno.h>
#if PTHREAD
#include <pthread.h>
#include <signal.h>
#endif
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
#define BUFSIZE (100*1024)
#define MAXCMDLINESIZE (32*1024)
static HANDLE client_r;
static HANDLE client_w;
static DWORD retcode;
static volatile int doRun = TRUE;
static char cmdline[MAXCMDLINESIZE];
static void
checkerrno (const char *message, const char *message2) {
LPVOID lpMsgBuf;
DWORD errnum;
errnum = GetLastError();
if (errnum == 0L)
return;
if (message2 == NULL)
message2 = "";
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errnum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
fprintf (stderr, "ERROR %s %s: %s (%ld)\n", message, message2, (char*) lpMsgBuf, (long)errnum);
// Free the buffer.
LocalFree(lpMsgBuf );
exit (10);
}
#if PTHREAD
static void*
clientReaderThread(void* lpParameter __unused) {
#else
static DWORD WINAPI
clientReaderThread(LPVOID lpParameter __unused) {
#endif
static char buf[BUFSIZE];
#if DEBUG
fprintf(stderr, "start child->parent write loop\n");
#endif
while (doRun) {
DWORD bytes;
boolean fSuccess = ReadFile(client_r, buf, sizeof(buf), &bytes, NULL);
if (!fSuccess || bytes <= 0 || GetLastError() == ERROR_HANDLE_EOF)
break;
// with cygwin we may get unexpected EAGAIN errors on write call, thanks to Ilya Basin for research
ssize_t wbytes;
int maxRepeat = 100;
int errNo = 0;
do {
wbytes = write(STDOUT_FILENO, buf, bytes);
if (wbytes == -1) {
errNo = errno;
usleep(50); // give parent process time to process data
}
} while (wbytes == -1 && errNo == EAGAIN && --maxRepeat > 0);
if (wbytes != (ssize_t)bytes) {
perror("could not write to parent");
break;
}
}
CloseHandle(client_r);
close(STDOUT_FILENO);
#if DEBUG
fprintf(stderr, "handle child->parent closed\n");
#endif
#if PTHREAD
pthread_exit(NULL);
return NULL;
#else
return 0;
#endif
}
#if PTHREAD
static void*
clientWriterThread(void* lpParameter __unused) {
#else
static DWORD WINAPI
clientWriterThread(LPVOID lpParameter __unused) {
#endif
static char buf[BUFSIZE];
#if DEBUG
fprintf(stderr, "start parent->child write loop\n");
#endif
while (doRun) {
ssize_t rbytes = read(STDIN_FILENO, buf, sizeof(buf));
if (rbytes <= 0)
break;
DWORD bytes;
boolean fSuccess = WriteFile(client_w, buf, rbytes, &bytes, NULL);
if (!fSuccess || (ssize_t)bytes != rbytes)
break;
}
CloseHandle(client_w);
close(STDIN_FILENO);
#if DEBUG
fprintf(stderr, "handle parent->child closed\n");
#endif
#if PTHREAD
pthread_exit(NULL);
return NULL;
#else
return 0;
#endif
}
#if PTHREAD
static void*
exitWatchDog(void* lpParameter __unused) {
#else
static DWORD WINAPI
exitWatchDog(LPVOID lpParameter __unused) {
#endif
HANDLE self = OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId());
if (self == NULL)
checkerrno("OpenProcess", "");
Sleep(10000);
#if DEBUG
fprintf(stderr, "emergency exit for wrapper with state %ld\n", (long)retcode);
#endif
if (!TerminateProcess(self, retcode))
checkerrno("TerminateProcess", "");
#if PTHREAD
pthread_exit(NULL);
return NULL;
#else
return 0;
#endif
}
static void
usage(char *progname) {
fprintf(stderr, "%s - a programm to use stdin/stdout redirection \non native win32 programms called from cygwin\n", progname);
fprintf(stderr, "(C) Copyright 2009, Frank Behrens, Version %s\n", VERSION);
fprintf(stderr, "Usage: %s <prog> [args...]\n", progname);
exit(1);
}
int
main(int argc, char *argv[]) {
if (argc == 1)
usage(*argv);
argv++;
#if DEBUG
fprintf(stderr, "native wrapper started for %s\n", *argv);
#endif
setmode(STDIN_FILENO, O_BINARY);
setmode(STDOUT_FILENO, O_BINARY);
HANDLE newstdin;
HANDLE newstdout;
HANDLE newstderr;
PROCESS_INFORMATION procinfo;
STARTUPINFO startinfo;
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&client_r, &newstdout, &saAttr, 0))
checkerrno("CreatePipe", "");
SetHandleInformation(client_r, HANDLE_FLAG_INHERIT, 0);
if (!CreatePipe(&newstdin, &client_w, &saAttr, 0))
checkerrno("CreatePipe", "");
SetHandleInformation(client_w, HANDLE_FLAG_INHERIT, 0);
newstderr = GetStdHandle(STD_ERROR_HANDLE); // no redirect
ZeroMemory(&procinfo, sizeof(procinfo));
ZeroMemory(&startinfo, sizeof(startinfo));
cmdline[0] = '\0';
char** c = argv;
strlcat(cmdline, "\"", sizeof(cmdline));
strlcat(cmdline, *(c++), sizeof(cmdline));
strlcat(cmdline, "\" ", sizeof(cmdline));
while (*c != NULL) {
strlcat(cmdline, *c, sizeof(cmdline));
strlcat(cmdline, " ", sizeof(cmdline));
c++;
}
startinfo.cb = sizeof(startinfo);
startinfo.hStdInput = newstdin;
startinfo.hStdOutput = newstdout;
startinfo.hStdError = newstderr;
startinfo.dwFlags |= STARTF_USESTDHANDLES;
BOOL res = CreateProcessA(NULL, cmdline,
NULL /*processAttrs*/,
NULL /*threadAttrs*/,
TRUE /*inheritHandles*/,
0 /*creationFlags*/,
NULL /*environment*/,
NULL /*currentDirectory*/,
&startinfo,
&procinfo);
if (!res)
checkerrno("CreateProcess", *argv);
CloseHandle(procinfo.hThread);
#if PTHREAD
#if DEBUG
fprintf(stderr, "client process started with pid %ld, now starting posix worker threads\n", (long)procinfo.dwProcessId);
#endif
pthread_t read_id, write_id;
pthread_create(&read_id, NULL, &clientReaderThread, NULL);
pthread_create(&write_id, NULL, &clientWriterThread, NULL);
#else
#if DEBUG
fprintf(stderr, "client process started with pid %ld, now starting win32 worker threads\n", (long)procinfo.dwProcessId);
#endif
DWORD read_id, write_id;
CreateThread(NULL, 0, &clientReaderThread, NULL, 0, &read_id);
CreateThread(NULL, 0, &clientWriterThread, NULL, 0, &write_id);
#endif
WaitForSingleObject(procinfo.hProcess, INFINITE);
doRun = FALSE;
GetExitCodeProcess(procinfo.hProcess, &retcode);
CloseHandle(procinfo.hProcess);
#if DEBUG
fprintf(stderr, "client returned with code %ld\n", (long)retcode);
#endif
#if 0
pthread_kill(read_id, SIGINT);
pthread_kill(write_id, SIGINT);
pthread_join(read_id, NULL);
pthread_join(write_id, NULL);
#endif
#if PTHREAD
pthread_t exit_id;
pthread_create(&exit_id, NULL, &exitWatchDog, NULL);
#else
DWORD exit_id;
CreateThread(NULL, 0, &exitWatchDog, NULL, 0, &exit_id);
#endif
#if DEBUG
fprintf(stderr, "native wrapper returned with error code %ld\n", (long)retcode);
#endif
exit(retcode);
}