OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / process_stub_win.c
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #define _WIN32_WINNT 0x0501 /* WinXP, needed for DebugActiveProcessStop() */
34
35 #include <windows.h>
36 #include <shellapi.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <direct.h>
41
42 static FILE *qtcFd;
43 static wchar_t *sleepMsg;
44
45 enum RunMode { Run, Debug, Suspend };
46
47 /* Print some "press enter" message, wait for that, exit. */
48 static void doExit(int code)
49 {
50     char buf[2];
51     _putws(sleepMsg);
52     fgets(buf, 2, stdin); /* Minimal size to make it wait */
53     exit(code);
54 }
55
56 /* Print an error message for unexpected Windows system errors, wait, exit. */
57 static void systemError(const char *str)
58 {
59     fprintf(stderr, str, GetLastError());
60     doExit(3);
61 }
62
63 /* Send a message to the master. */
64 static void sendMsg(const char *msg, int num)
65 {
66     int pidStrLen;
67     char pidStr[64];
68
69     pidStrLen = sprintf(pidStr, msg, num);
70     if (fwrite(pidStr, pidStrLen, 1, qtcFd) != 1 || fflush(qtcFd)) {
71         fprintf(stderr, "Cannot write to creator comm socket: %s\n",
72                 strerror(errno));
73         doExit(3);
74     }
75 }
76
77 /* Ignore the first ctrl-c/break within a second. */
78 static BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
79 {
80     static ULARGE_INTEGER lastTime;
81     ULARGE_INTEGER thisTime;
82     SYSTEMTIME sysTime;
83     FILETIME fileTime;
84
85     if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
86         GetSystemTime(&sysTime);
87         SystemTimeToFileTime(&sysTime, &fileTime);
88         thisTime.LowPart = fileTime.dwLowDateTime;
89         thisTime.HighPart = fileTime.dwHighDateTime;
90         if (lastTime.QuadPart + 10000000 < thisTime.QuadPart) {
91             lastTime.QuadPart = thisTime.QuadPart;
92             return TRUE;
93         }
94     }
95     return FALSE;
96 }
97
98 enum {
99     ArgCmd = 0,
100     ArgAction,
101     ArgSocket,
102     ArgDir,
103     ArgEnv,
104     ArgCmdLine,
105     ArgMsg,
106     ArgCount
107 };
108
109 /* syntax: $0 {"run"|"debug"} <pid-socket> <workdir> <env-file> <cmdline> <continuation-msg> */
110 /* exit codes: 0 = ok, 1 = invocation error, 3 = internal error */
111 int main()
112 {
113     int argc;
114     int creationFlags;
115     wchar_t **argv;
116     wchar_t *env = 0;
117     STARTUPINFOW si;
118     PROCESS_INFORMATION pi;
119     DEBUG_EVENT dbev;
120     enum RunMode mode = Run;
121
122     argv = CommandLineToArgvW(GetCommandLine(), &argc);
123
124     if (argc != ArgCount) {
125         fprintf(stderr, "This is an internal helper of Qt Creator. Do not run it manually.\n");
126         return 1;
127     }
128     sleepMsg = argv[ArgMsg];
129
130     /* Connect to the master, i.e. Creator. */
131     if (!(qtcFd = _wfopen(argv[ArgSocket], L"w"))) {
132         fprintf(stderr, "Cannot connect creator comm pipe %S: %s\n",
133                 argv[ArgSocket], strerror(errno));
134         doExit(1);
135     }
136
137     if (*argv[ArgDir] && !SetCurrentDirectoryW(argv[ArgDir])) {
138         /* Only expected error: no such file or direcotry */
139         sendMsg("err:chdir %d\n", GetLastError());
140         return 1;
141     }
142
143     if (*argv[ArgEnv]) {
144         FILE *envFd;
145         long size;
146         if (!(envFd = _wfopen(argv[ArgEnv], L"r"))) {
147             fprintf(stderr, "Cannot read creator env file %S: %s\n",
148                     argv[ArgEnv], strerror(errno));
149             doExit(1);
150         }
151         fseek(envFd, 0, SEEK_END);
152         size = ftell(envFd);
153         rewind(envFd);
154         env = malloc(size);
155         if (fread(env, 1, size, envFd) != size) {
156             perror("Failed to read env file");
157             doExit(1);
158         }
159         fclose(envFd);
160     }
161
162     ZeroMemory(&pi, sizeof(pi));
163     ZeroMemory(&si, sizeof(si));
164     si.cb = sizeof(si);
165
166     creationFlags = CREATE_UNICODE_ENVIRONMENT;
167     if (!wcscmp(argv[ArgAction], L"debug")) {
168         mode = Debug;
169     } else if (!wcscmp(argv[ArgAction], L"suspend")) {
170         mode = Suspend;
171     }
172
173     switch (mode) {
174     case Debug:
175         creationFlags |= DEBUG_ONLY_THIS_PROCESS;
176         break;
177     case Suspend:
178         creationFlags |= CREATE_SUSPENDED;
179         break;
180     default:
181         break;
182     }
183     if (!CreateProcessW(0, argv[ArgCmdLine], 0, 0, FALSE, creationFlags, env, 0, &si, &pi)) {
184         /* Only expected error: no such file or direcotry, i.e. executable not found */
185         sendMsg("err:exec %d\n", GetLastError());
186         doExit(1);
187     }
188
189     /* This is somewhat convoluted. What we actually want is creating a
190        suspended process and letting gdb attach to it. Unfortunately,
191        the Windows kernel runs amok when we attempt this.
192        So instead we start a debugged process, eat all the initial
193        debug events, suspend the process and detach from it. If gdb
194        tries to attach *now*, everything goes smoothly. Yay. */
195     if (mode == Debug) {
196         do {
197             if (!WaitForDebugEvent (&dbev, INFINITE))
198                 systemError("Cannot fetch debug event, error %d\n");
199             if (dbev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
200                 /* The first exception to be delivered is a trap
201                    which indicates completion of startup. */
202                 if (SuspendThread(pi.hThread) == (DWORD)-1)
203                     systemError("Cannot suspend debugee, error %d\n");
204             }
205             if (!ContinueDebugEvent(dbev.dwProcessId, dbev.dwThreadId, DBG_CONTINUE))
206                 systemError("Cannot continue debug event, error %d\n");
207         } while (dbev.dwDebugEventCode != EXCEPTION_DEBUG_EVENT);
208         if (!DebugActiveProcessStop(dbev.dwProcessId))
209             systemError("Cannot detach from debugee, error %d\n");
210     }
211
212     SetConsoleCtrlHandler(ctrlHandler, TRUE);
213
214     sendMsg("thread %d\n", pi.dwThreadId);
215     sendMsg("pid %d\n", pi.dwProcessId);
216
217     if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
218         systemError("Wait for debugee failed, error %d\n");
219     doExit(0);
220     return 0;
221 }