nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* gspawn-win32-helper.c - Helper program for process launching on Win32. |
2 | * |
||
3 | * Copyright 2000 Red Hat, Inc. |
||
4 | * Copyright 2000 Tor Lillqvist |
||
5 | * |
||
6 | * GLib is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public License as |
||
8 | * published by the Free Software Foundation; either version 2 of the |
||
9 | * License, or (at your option) any later version. |
||
10 | * |
||
11 | * GLib is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 | * Lesser General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU Lesser General Public |
||
17 | * License along with GLib; see the file COPYING.LIB. If not, write |
||
18 | * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
||
19 | * Boston, MA 02111-1307, USA. |
||
20 | */ |
||
21 | |||
22 | #include "config.h" |
||
23 | |||
24 | #include <fcntl.h> |
||
25 | |||
26 | /* For _CrtSetReportMode, we don't want Windows CRT (2005 and later) |
||
27 | * to terminate the process if a bad file descriptor is passed into |
||
28 | * _get_osfhandle(). This is necessary because we use _get_osfhandle() |
||
29 | * to check the validity of the fd before we try to call close() on |
||
30 | * it as attempting to close an invalid fd will cause the Windows CRT |
||
31 | * to abort() this program internally. |
||
32 | * |
||
33 | * Please see http://msdn.microsoft.com/zh-tw/library/ks2530z6%28v=vs.80%29.aspx |
||
34 | * for an explanation on this. |
||
35 | */ |
||
36 | #if (defined (_MSC_VER) && _MSC_VER >= 1400) |
||
37 | #include <crtdbg.h> |
||
38 | #endif |
||
39 | |||
40 | #undef G_LOG_DOMAIN |
||
41 | #include "glib.h" |
||
42 | #define GSPAWN_HELPER |
||
43 | #include "gspawn-win32.c" /* For shared definitions */ |
||
44 | |||
45 | |||
46 | static void |
||
47 | write_err_and_exit (gint fd, |
||
48 | gintptr msg) |
||
49 | { |
||
50 | gintptr en = errno; |
||
51 | |||
52 | write (fd, &msg, sizeof(gintptr)); |
||
53 | write (fd, &en, sizeof(gintptr)); |
||
54 | |||
55 | _exit (1); |
||
56 | } |
||
57 | |||
58 | #ifdef __GNUC__ |
||
59 | # ifndef _stdcall |
||
60 | # define _stdcall __attribute__((stdcall)) |
||
61 | # endif |
||
62 | #endif |
||
63 | |||
64 | /* We build gspawn-win32-helper.exe as a Windows GUI application |
||
65 | * to avoid any temporarily flashing console windows in case |
||
66 | * the gspawn function is invoked by a GUI program. Thus, no main() |
||
67 | * but a WinMain(). |
||
68 | */ |
||
69 | |||
70 | /* Copy of protect_argv that handles wchar_t strings */ |
||
71 | |||
72 | static gint |
||
73 | protect_wargv (wchar_t **wargv, |
||
74 | wchar_t ***new_wargv) |
||
75 | { |
||
76 | gint i; |
||
77 | gint argc = 0; |
||
78 | |||
79 | while (wargv[argc]) |
||
80 | ++argc; |
||
81 | *new_wargv = g_new (wchar_t *, argc+1); |
||
82 | |||
83 | /* Quote each argv element if necessary, so that it will get |
||
84 | * reconstructed correctly in the C runtime startup code. Note that |
||
85 | * the unquoting algorithm in the C runtime is really weird, and |
||
86 | * rather different than what Unix shells do. See stdargv.c in the C |
||
87 | * runtime sources (in the Platform SDK, in src/crt). |
||
88 | * |
||
89 | * Note that an new_wargv[0] constructed by this function should |
||
90 | * *not* be passed as the filename argument to a _wspawn* or _wexec* |
||
91 | * family function. That argument should be the real file name |
||
92 | * without any quoting. |
||
93 | */ |
||
94 | for (i = 0; i < argc; i++) |
||
95 | { |
||
96 | wchar_t *p = wargv[i]; |
||
97 | wchar_t *q; |
||
98 | gint len = 0; |
||
99 | gboolean need_dblquotes = FALSE; |
||
100 | while (*p) |
||
101 | { |
||
102 | if (*p == ' ' || *p == '\t') |
||
103 | need_dblquotes = TRUE; |
||
104 | else if (*p == '"') |
||
105 | len++; |
||
106 | else if (*p == '\\') |
||
107 | { |
||
108 | wchar_t *pp = p; |
||
109 | while (*pp && *pp == '\\') |
||
110 | pp++; |
||
111 | if (*pp == '"') |
||
112 | len++; |
||
113 | } |
||
114 | len++; |
||
115 | p++; |
||
116 | } |
||
117 | |||
118 | q = (*new_wargv)[i] = g_new (wchar_t, len + need_dblquotes*2 + 1); |
||
119 | p = wargv[i]; |
||
120 | |||
121 | if (need_dblquotes) |
||
122 | *q++ = '"'; |
||
123 | |||
124 | while (*p) |
||
125 | { |
||
126 | if (*p == '"') |
||
127 | *q++ = '\\'; |
||
128 | else if (*p == '\\') |
||
129 | { |
||
130 | wchar_t *pp = p; |
||
131 | while (*pp && *pp == '\\') |
||
132 | pp++; |
||
133 | if (*pp == '"') |
||
134 | *q++ = '\\'; |
||
135 | } |
||
136 | *q++ = *p; |
||
137 | p++; |
||
138 | } |
||
139 | |||
140 | if (need_dblquotes) |
||
141 | *q++ = '"'; |
||
142 | *q++ = '\0'; |
||
143 | } |
||
144 | (*new_wargv)[argc] = NULL; |
||
145 | |||
146 | return argc; |
||
147 | } |
||
148 | |||
149 | #if (defined (_MSC_VER) && _MSC_VER >= 1400) |
||
150 | /* |
||
151 | * This is the (empty) invalid parameter handler |
||
152 | * that is used for Visual C++ 2005 (and later) builds |
||
153 | * so that we can use this instead of the system automatically |
||
154 | * aborting the process. |
||
155 | * |
||
156 | * This is necessary as we use _get_oshandle() to check the validity |
||
157 | * of the file descriptors as we close them, so when an invalid file |
||
158 | * descriptor is passed into that function as we check on it, we get |
||
159 | * -1 as the result, instead of the gspawn helper program aborting. |
||
160 | * |
||
161 | * Please see http://msdn.microsoft.com/zh-tw/library/ks2530z6%28v=vs.80%29.aspx |
||
162 | * for an explanation on this. |
||
163 | */ |
||
164 | void myInvalidParameterHandler( |
||
165 | const wchar_t * expression, |
||
166 | const wchar_t * function, |
||
167 | const wchar_t * file, |
||
168 | unsigned int line, |
||
169 | uintptr_t pReserved |
||
170 | ) |
||
171 | { |
||
172 | return; |
||
173 | } |
||
174 | #endif |
||
175 | |||
176 | |||
177 | #ifndef HELPER_CONSOLE |
||
178 | int _stdcall |
||
179 | WinMain (struct HINSTANCE__ *hInstance, |
||
180 | struct HINSTANCE__ *hPrevInstance, |
||
181 | char *lpszCmdLine, |
||
182 | int nCmdShow) |
||
183 | #else |
||
184 | int |
||
185 | main (int ignored_argc, char **ignored_argv) |
||
186 | #endif |
||
187 | { |
||
188 | int child_err_report_fd = -1; |
||
189 | int helper_sync_fd = -1; |
||
190 | int i; |
||
191 | int fd; |
||
192 | int mode; |
||
193 | gintptr handle; |
||
194 | int saved_errno; |
||
195 | gintptr no_error = CHILD_NO_ERROR; |
||
196 | gint argv_zero_offset = ARG_PROGRAM; |
||
197 | wchar_t **new_wargv; |
||
198 | int argc; |
||
199 | char **argv; |
||
200 | wchar_t **wargv; |
||
201 | char c; |
||
202 | |||
203 | #if (defined (_MSC_VER) && _MSC_VER >= 1400) |
||
204 | /* set up our empty invalid parameter handler */ |
||
205 | _invalid_parameter_handler oldHandler, newHandler; |
||
206 | newHandler = myInvalidParameterHandler; |
||
207 | oldHandler = _set_invalid_parameter_handler(newHandler); |
||
208 | |||
209 | /* Disable the message box for assertions. */ |
||
210 | _CrtSetReportMode(_CRT_ASSERT, 0); |
||
211 | #endif |
||
212 | |||
213 | /* Fetch the wide-char argument vector */ |
||
214 | wargv = CommandLineToArgvW (GetCommandLineW(), &argc); |
||
215 | |||
216 | g_assert (argc >= ARG_COUNT); |
||
217 | |||
218 | /* Convert unicode wargs to utf8 */ |
||
219 | argv = g_new(char *, argc + 1); |
||
220 | for (i = 0; i < argc; i++) |
||
221 | argv[i] = g_utf16_to_utf8(wargv[i], -1, NULL, NULL, NULL); |
||
222 | argv[i] = NULL; |
||
223 | |||
224 | /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto |
||
225 | * which write error messages. |
||
226 | */ |
||
227 | child_err_report_fd = atoi (argv[ARG_CHILD_ERR_REPORT]); |
||
228 | |||
229 | /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If |
||
230 | * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get |
||
231 | * the program to run and its argv[0] separately. |
||
232 | */ |
||
233 | if (argv[ARG_CHILD_ERR_REPORT][strlen (argv[ARG_CHILD_ERR_REPORT]) - 1] == '#') |
||
234 | argv_zero_offset++; |
||
235 | |||
236 | /* argv[ARG_HELPER_SYNC] is the file descriptor number we read a |
||
237 | * byte that tells us it is OK to exit. We have to wait until the |
||
238 | * parent allows us to exit, so that the parent has had time to |
||
239 | * duplicate the process handle we sent it. Duplicating a handle |
||
240 | * from another process works only if that other process exists. |
||
241 | */ |
||
242 | helper_sync_fd = atoi (argv[ARG_HELPER_SYNC]); |
||
243 | |||
244 | /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that |
||
245 | * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd |
||
246 | * should be left alone, and 'z' if it should be connected to the |
||
247 | * bit bucket NUL:. |
||
248 | */ |
||
249 | if (argv[ARG_STDIN][0] == '-') |
||
250 | ; /* Nothing */ |
||
251 | else if (argv[ARG_STDIN][0] == 'z') |
||
252 | { |
||
253 | fd = open ("NUL:", O_RDONLY); |
||
254 | if (fd != 0) |
||
255 | { |
||
256 | dup2 (fd, 0); |
||
257 | close (fd); |
||
258 | } |
||
259 | } |
||
260 | else |
||
261 | { |
||
262 | fd = atoi (argv[ARG_STDIN]); |
||
263 | if (fd != 0) |
||
264 | { |
||
265 | dup2 (fd, 0); |
||
266 | close (fd); |
||
267 | } |
||
268 | } |
||
269 | |||
270 | if (argv[ARG_STDOUT][0] == '-') |
||
271 | ; /* Nothing */ |
||
272 | else if (argv[ARG_STDOUT][0] == 'z') |
||
273 | { |
||
274 | fd = open ("NUL:", O_WRONLY); |
||
275 | if (fd != 1) |
||
276 | { |
||
277 | dup2 (fd, 1); |
||
278 | close (fd); |
||
279 | } |
||
280 | } |
||
281 | else |
||
282 | { |
||
283 | fd = atoi (argv[ARG_STDOUT]); |
||
284 | if (fd != 1) |
||
285 | { |
||
286 | dup2 (fd, 1); |
||
287 | close (fd); |
||
288 | } |
||
289 | } |
||
290 | |||
291 | if (argv[ARG_STDERR][0] == '-') |
||
292 | ; /* Nothing */ |
||
293 | else if (argv[ARG_STDERR][0] == 'z') |
||
294 | { |
||
295 | fd = open ("NUL:", O_WRONLY); |
||
296 | if (fd != 2) |
||
297 | { |
||
298 | dup2 (fd, 2); |
||
299 | close (fd); |
||
300 | } |
||
301 | } |
||
302 | else |
||
303 | { |
||
304 | fd = atoi (argv[ARG_STDERR]); |
||
305 | if (fd != 2) |
||
306 | { |
||
307 | dup2 (fd, 2); |
||
308 | close (fd); |
||
309 | } |
||
310 | } |
||
311 | |||
312 | /* argv[ARG_WORKING_DIRECTORY] is the directory in which to run the |
||
313 | * process. If "-", don't change directory. |
||
314 | */ |
||
315 | if (argv[ARG_WORKING_DIRECTORY][0] == '-' && |
||
316 | argv[ARG_WORKING_DIRECTORY][1] == 0) |
||
317 | ; /* Nothing */ |
||
318 | else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0) |
||
319 | write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED); |
||
320 | |||
321 | /* argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3 |
||
322 | * upwards should be closed |
||
323 | */ |
||
324 | if (argv[ARG_CLOSE_DESCRIPTORS][0] == 'y') |
||
325 | for (i = 3; i < 1000; i++) /* FIXME real limit? */ |
||
326 | if (i != child_err_report_fd && i != helper_sync_fd) |
||
327 | if (_get_osfhandle (i) != -1) |
||
328 | close (i); |
||
329 | |||
330 | /* We don't want our child to inherit the error report and |
||
331 | * helper sync fds. |
||
332 | */ |
||
333 | child_err_report_fd = dup_noninherited (child_err_report_fd, _O_WRONLY); |
||
334 | helper_sync_fd = dup_noninherited (helper_sync_fd, _O_RDONLY); |
||
335 | |||
336 | /* argv[ARG_WAIT] is "w" to wait for the program to exit */ |
||
337 | if (argv[ARG_WAIT][0] == 'w') |
||
338 | mode = P_WAIT; |
||
339 | else |
||
340 | mode = P_NOWAIT; |
||
341 | |||
342 | /* argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */ |
||
343 | |||
344 | /* argv[ARG_PROGRAM] is executable file to run, |
||
345 | * argv[argv_zero_offset]... is its argv. argv_zero_offset equals |
||
346 | * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which |
||
347 | * case we have a separate executable name and argv[0]. |
||
348 | */ |
||
349 | |||
350 | /* For the program name passed to spawnv(), don't use the quoted |
||
351 | * version. |
||
352 | */ |
||
353 | protect_wargv (wargv + argv_zero_offset, &new_wargv); |
||
354 | |||
355 | if (argv[ARG_USE_PATH][0] == 'y') |
||
356 | handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv); |
||
357 | else |
||
358 | handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv); |
||
359 | |||
360 | saved_errno = errno; |
||
361 | |||
362 | if (handle == -1 && saved_errno != 0) |
||
363 | write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED); |
||
364 | |||
365 | write (child_err_report_fd, &no_error, sizeof (no_error)); |
||
366 | write (child_err_report_fd, &handle, sizeof (handle)); |
||
367 | |||
368 | read (helper_sync_fd, &c, 1); |
||
369 | |||
370 | LocalFree (wargv); |
||
371 | g_strfreev (argv); |
||
372 | |||
373 | return 0; |
||
374 | } |