#define HELPER_CONSOLE in gspawn-win32-helper-console.c
[platform/upstream/glib.git] / glib / gspawn-win32-helper.c
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 #undef G_LOG_DOMAIN
27 #include "glib.h"
28 #define GSPAWN_HELPER
29 #include "gspawn-win32.c"       /* For shared definitions */
30
31
32 static void
33 write_err_and_exit (gint fd,
34                     gint msg)
35 {
36   gint en = errno;
37   
38   write (fd, &msg, sizeof(msg));
39   write (fd, &en, sizeof(en));
40   
41   _exit (1);
42 }
43
44 #ifdef __GNUC__
45 #  ifndef _stdcall
46 #    define _stdcall  __attribute__((stdcall))
47 #  endif
48 #endif
49
50 /* We build gspawn-win32-helper.exe as a Windows GUI application
51  * to avoid any temporarily flashing console windows in case
52  * the gspawn function is invoked by a GUI program. Thus, no main()
53  * but a WinMain(). We do, however, still use argc and argv tucked
54  * away in the global __argc and __argv by the C runtime startup code.
55  */
56
57 /* Info peeked from mingw runtime's source code. __wgetmainargs() is a
58  * function to get the program's argv in wide char format.
59  */
60
61 typedef struct {
62   int newmode;
63 } _startupinfo;
64
65 extern void __wgetmainargs(int *argc,
66                            wchar_t ***wargv,
67                            wchar_t ***wenviron,
68                            int expand_wildcards,
69                            _startupinfo *startupinfo);
70
71 /* Copy of protect_argv that handles wchar_t strings */
72
73 static gint
74 protect_wargv (wchar_t  **wargv,
75                wchar_t ***new_wargv)
76 {
77   gint i;
78   gint argc = 0;
79   
80   while (wargv[argc])
81     ++argc;
82   *new_wargv = g_new (wchar_t *, argc+1);
83
84   /* Quote each argv element if necessary, so that it will get
85    * reconstructed correctly in the C runtime startup code.  Note that
86    * the unquoting algorithm in the C runtime is really weird, and
87    * rather different than what Unix shells do. See stdargv.c in the C
88    * runtime sources (in the Platform SDK, in src/crt).
89    *
90    * Note that an new_wargv[0] constructed by this function should
91    * *not* be passed as the filename argument to a _wspawn* or _wexec*
92    * family function. That argument should be the real file name
93    * without any quoting.
94    */
95   for (i = 0; i < argc; i++)
96     {
97       wchar_t *p = wargv[i];
98       wchar_t *q;
99       gint len = 0;
100       gboolean need_dblquotes = FALSE;
101       while (*p)
102         {
103           if (*p == ' ' || *p == '\t')
104             need_dblquotes = TRUE;
105           else if (*p == '"')
106             len++;
107           else if (*p == '\\')
108             {
109               wchar_t *pp = p;
110               while (*pp && *pp == '\\')
111                 pp++;
112               if (*pp == '"')
113                 len++;
114             }
115           len++;
116           p++;
117         }
118
119       q = (*new_wargv)[i] = g_new (wchar_t, len + need_dblquotes*2 + 1);
120       p = wargv[i];
121
122       if (need_dblquotes)
123         *q++ = '"';
124
125       while (*p)
126         {
127           if (*p == '"')
128             *q++ = '\\';
129           else if (*p == '\\')
130             {
131               wchar_t *pp = p;
132               while (*pp && *pp == '\\')
133                 pp++;
134               if (*pp == '"')
135                 *q++ = '\\';
136             }
137           *q++ = *p;
138           p++;
139         }
140
141       if (need_dblquotes)
142         *q++ = '"';
143       *q++ = '\0';
144     }
145   (*new_wargv)[argc] = NULL;
146
147   return argc;
148 }
149
150 #ifndef HELPER_CONSOLE
151 int _stdcall
152 WinMain (struct HINSTANCE__ *hInstance,
153          struct HINSTANCE__ *hPrevInstance,
154          char               *lpszCmdLine,
155          int                 nCmdShow)
156 #else
157 int
158 main (int ignored_argc, char **ignored_argv)
159 #endif
160 {
161   int child_err_report_fd = -1;
162   int helper_sync_fd = -1;
163   int i;
164   int fd;
165   int mode;
166   int handle;
167   int saved_errno;
168   int no_error = CHILD_NO_ERROR;
169   int zero = 0;
170   gint argv_zero_offset = ARG_PROGRAM;
171   wchar_t **new_wargv;
172   int argc;
173   wchar_t **wargv, **wenvp;
174   _startupinfo si = { 0 };
175   char c;
176
177   g_assert (__argc >= ARG_COUNT);
178
179   /* Fetch the wide-char argument vector */
180   __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
181
182   /* We still have the system codepage args in __argv. We can look
183    * at the first args in which gspawn-win32.c passes us flags and
184    * fd numbers in __argv, as we know those are just ASCII anyway.
185    */
186   g_assert (argc == __argc);
187
188   /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
189    * which write error messages.
190    */
191   child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
192
193   /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
194    * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
195    * the program to run and its argv[0] separately.
196    */
197   if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
198     argv_zero_offset++;
199
200   /* argv[ARG_HELPER_SYNC] is the file descriptor number we read a
201    * byte that tells us it is OK to exit. We have to wait until the
202    * parent allows us to exit, so that the parent has had time to
203    * duplicate the process handle we sent it. Duplicating a handle
204    * from another process works only if that other process exists.
205    */
206   helper_sync_fd = atoi (__argv[ARG_HELPER_SYNC]);
207
208   /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
209    * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
210    * should be left alone, and 'z' if it should be connected to the
211    * bit bucket NUL:.
212    */
213   if (__argv[ARG_STDIN][0] == '-')
214     ; /* Nothing */
215   else if (__argv[ARG_STDIN][0] == 'z')
216     {
217       fd = open ("NUL:", O_RDONLY);
218       if (fd != 0)
219         {
220           dup2 (fd, 0);
221           close (fd);
222         }
223     }
224   else
225     {
226       fd = atoi (__argv[ARG_STDIN]);
227       if (fd != 0)
228         {
229           dup2 (fd, 0);
230           close (fd);
231         }
232     }
233
234   if (__argv[ARG_STDOUT][0] == '-')
235     ; /* Nothing */
236   else if (__argv[ARG_STDOUT][0] == 'z')
237     {
238       fd = open ("NUL:", O_WRONLY);
239       if (fd != 1)
240         {
241           dup2 (fd, 1);
242           close (fd);
243         }
244     }
245   else
246     {
247       fd = atoi (__argv[ARG_STDOUT]);
248       if (fd != 1)
249         {
250           dup2 (fd, 1);
251           close (fd);
252         }
253     }
254
255   if (__argv[ARG_STDERR][0] == '-')
256     ; /* Nothing */
257   else if (__argv[ARG_STDERR][0] == 'z')
258     {
259       fd = open ("NUL:", O_WRONLY);
260       if (fd != 2)
261         {
262           dup2 (fd, 2);
263           close (fd);
264         }
265     }
266   else
267     {
268       fd = atoi (__argv[ARG_STDERR]);
269       if (fd != 2)
270         {
271           dup2 (fd, 2);
272           close (fd);
273         }
274     }
275
276   /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
277    * process.  If "-", don't change directory.
278    */
279   if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
280       __argv[ARG_WORKING_DIRECTORY][1] == 0)
281     ; /* Nothing */
282   else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
283     write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
284
285   /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
286    *  upwards should be closed
287    */
288   if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
289     for (i = 3; i < 1000; i++)  /* FIXME real limit? */
290       if (i != child_err_report_fd && i != helper_sync_fd)
291         close (i);
292
293   /* We don't want our child to inherit the error report and
294    * helper sync fds.
295    */
296   child_err_report_fd = dup_noninherited (child_err_report_fd, _O_WRONLY);
297   helper_sync_fd = dup_noninherited (helper_sync_fd, _O_RDONLY);
298
299   /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
300   if (__argv[ARG_WAIT][0] == 'w')
301     mode = P_WAIT;
302   else
303     mode = P_NOWAIT;
304
305   /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
306
307   /* __argv[ARG_PROGRAM] is executable file to run,
308    * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
309    * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
310    * case we have a separate executable name and argv[0].
311    */
312
313   /* For the program name passed to spawnv(), don't use the quoted
314    * version.
315    */
316   protect_wargv (wargv + argv_zero_offset, &new_wargv);
317
318   if (__argv[ARG_USE_PATH][0] == 'y')
319     handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
320   else
321     handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
322
323   saved_errno = errno;
324
325   if (handle == -1 && saved_errno != 0)
326     write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
327
328   write (child_err_report_fd, &no_error, sizeof (no_error));
329   if (mode == P_NOWAIT)
330     write (child_err_report_fd, &handle, sizeof (handle));
331   else
332     write (child_err_report_fd, &zero, sizeof (zero));
333
334   read (helper_sync_fd, &c, 1);
335
336   return 0;
337 }