]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* spawn.cc |
2 | ||
d1fb625d | 3 | Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc. |
1fd5e000 CF |
4 | |
5 | This file is part of Cygwin. | |
6 | ||
7 | This software is a copyrighted work licensed under the terms of the | |
8 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
9 | details. */ | |
10 | ||
4c8d72de | 11 | #include "winsup.h" |
1fd5e000 CF |
12 | #include <stdlib.h> |
13 | #include <stdarg.h> | |
14 | #include <unistd.h> | |
15 | #include <process.h> | |
16 | #include <sys/wait.h> | |
1fd5e000 | 17 | #include <limits.h> |
d0b178fe DD |
18 | #include <wingdi.h> |
19 | #include <winuser.h> | |
1fd5e000 | 20 | #include <ctype.h> |
bccd5e0d | 21 | #include "cygerrno.h" |
f0338f54 | 22 | #include <sys/cygwin.h> |
6b91b8d5 | 23 | #include "security.h" |
d1fb625d | 24 | #include "fhandler.h" |
bc54734d | 25 | #include "path.h" |
e2ebe117 | 26 | #include "dtable.h" |
bccd5e0d | 27 | #include "sigproc.h" |
8dec7b03 | 28 | #include "cygheap.h" |
488c7683 | 29 | #include "child_info.h" |
2a6fc028 | 30 | #include "shared_info.h" |
e2ebe117 | 31 | #include "pinfo.h" |
f0338f54 | 32 | #define NEED_VFORK |
bccd5e0d | 33 | #include "perthread.h" |
f0338f54 CF |
34 | #include "registry.h" |
35 | #include "environ.h" | |
3f5046a5 | 36 | #include "cygthread.h" |
1fd5e000 | 37 | |
1fd5e000 CF |
38 | #define LINE_BUF_CHUNK (MAX_PATH * 2) |
39 | ||
4c45a897 | 40 | static suffix_info std_suffixes[] = |
1fd5e000 CF |
41 | { |
42 | suffix_info (".exe", 1), suffix_info ("", 1), | |
43 | suffix_info (".com"), suffix_info (".cmd"), | |
44 | suffix_info (".bat"), suffix_info (".dll"), | |
45 | suffix_info (NULL) | |
46 | }; | |
47 | ||
4c45a897 | 48 | HANDLE hExeced; |
c0a8e8d0 | 49 | DWORD dwExeced; |
4c45a897 | 50 | |
1fd5e000 CF |
51 | /* Add .exe to PROG if not already present and see if that exists. |
52 | If not, return PROG (converted from posix to win32 rules if necessary). | |
53 | The result is always BUF. | |
54 | ||
55 | Returns (possibly NULL) suffix */ | |
56 | ||
57 | static const char * | |
c0a8e8d0 | 58 | perhaps_suffix (const char *prog, path_conv& buf) |
1fd5e000 CF |
59 | { |
60 | char *ext; | |
61 | ||
62 | debug_printf ("prog '%s'", prog); | |
5bc584ba | 63 | buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes); |
1fd5e000 | 64 | |
af39152f | 65 | if (!buf.exists () || buf.isdir ()) |
1fd5e000 | 66 | ext = NULL; |
55fc91b9 | 67 | else if (buf.known_suffix) |
47063f00 | 68 | ext = (char *) buf + (buf.known_suffix - buf.get_win32 ()); |
1fd5e000 CF |
69 | else |
70 | ext = strchr (buf, '\0'); | |
71 | ||
55fc91b9 | 72 | debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext); |
1fd5e000 CF |
73 | return ext; |
74 | } | |
75 | ||
76 | /* Find an executable name, possibly by appending known executable | |
77 | suffixes to it. The win32-translated name is placed in 'buf'. | |
78 | Any found suffix is returned in known_suffix. | |
79 | ||
80 | If the file is not found and !null_if_not_found then the win32 version | |
81 | of name is placed in buf and returned. Otherwise the contents of buf | |
82 | is undefined and NULL is returned. */ | |
83 | ||
84 | const char * __stdcall | |
55fc91b9 | 85 | find_exec (const char *name, path_conv& buf, const char *mywinenv, |
6ea0c04e | 86 | unsigned opt, const char **known_suffix) |
1fd5e000 CF |
87 | { |
88 | const char *suffix = ""; | |
89 | debug_printf ("find_exec (%s)", name); | |
45d2ea8a | 90 | const char *retval = buf; |
6ea0c04e CF |
91 | char tmp[MAX_PATH]; |
92 | const char *posix = (opt & FE_NATIVE) ? NULL : name; | |
93 | bool has_slash = strchr (name, '/'); | |
1fd5e000 CF |
94 | |
95 | /* Check to see if file can be opened as is first. | |
96 | Win32 systems always check . first, but PATH may not be set up to | |
97 | do this. */ | |
6ea0c04e CF |
98 | if ((has_slash || opt & FE_CWD) |
99 | && (suffix = perhaps_suffix (name, buf)) != NULL) | |
100 | { | |
101 | if (posix && !has_slash) | |
102 | { | |
103 | tmp[0] = '.'; | |
104 | tmp[1] = '/'; | |
105 | strcpy (tmp + 2, name); | |
106 | posix = tmp; | |
107 | } | |
108 | goto out; | |
109 | } | |
1fd5e000 CF |
110 | |
111 | win_env *winpath; | |
112 | const char *path; | |
6ea0c04e | 113 | const char *posix_path; |
1fd5e000 CF |
114 | |
115 | /* Return the error condition if this is an absolute path or if there | |
116 | is no PATH to search. */ | |
117 | if (strchr (name, '/') || strchr (name, '\\') || | |
75858e8a | 118 | isdrive (name) || |
1fd5e000 CF |
119 | !(winpath = getwinenv (mywinenv)) || |
120 | !(path = winpath->get_native ()) || | |
121 | *path == '\0') | |
122 | goto errout; | |
123 | ||
124 | debug_printf ("%s%s", mywinenv, path); | |
125 | ||
6ea0c04e CF |
126 | posix = (opt & FE_NATIVE) ? NULL : tmp; |
127 | posix_path = winpath->get_posix () - 1; | |
1fd5e000 CF |
128 | /* Iterate over the specified path, looking for the file with and |
129 | without executable extensions. */ | |
130 | do | |
131 | { | |
6ea0c04e | 132 | posix_path++; |
1fd5e000 CF |
133 | char *eotmp = strccpy (tmp, &path, ';'); |
134 | /* An empty path or '.' means the current directory, but we've | |
135 | already tried that. */ | |
6ea0c04e | 136 | if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0'))) |
1fd5e000 CF |
137 | continue; |
138 | ||
139 | *eotmp++ = '\\'; | |
140 | strcpy (eotmp, name); | |
141 | ||
142 | debug_printf ("trying %s", tmp); | |
143 | ||
144 | if ((suffix = perhaps_suffix (tmp, buf)) != NULL) | |
6ea0c04e CF |
145 | { |
146 | if (posix == tmp) | |
147 | { | |
148 | eotmp = strccpy (tmp, &posix_path, ':'); | |
149 | if (eotmp == tmp) | |
150 | *eotmp++ = '.'; | |
151 | *eotmp++ = '/'; | |
152 | strcpy (eotmp, name); | |
153 | } | |
154 | goto out; | |
155 | } | |
1fd5e000 | 156 | } |
6ea0c04e | 157 | while (*path && *++path && (posix_path = strchr (posix_path, ':'))); |
1fd5e000 | 158 | |
0a047e8f | 159 | errout: |
6ea0c04e | 160 | posix = NULL; |
1fd5e000 CF |
161 | /* Couldn't find anything in the given path. |
162 | Take the appropriate action based on null_if_not_found. */ | |
6ea0c04e | 163 | if (opt & FE_NNF) |
1fd5e000 | 164 | retval = NULL; |
6ea0c04e | 165 | else if (opt & FE_NATIVE) |
55fc91b9 | 166 | buf.check (name); |
45d2ea8a CF |
167 | else |
168 | retval = name; | |
1fd5e000 | 169 | |
0a047e8f | 170 | out: |
6ea0c04e CF |
171 | if (posix) |
172 | buf.set_path (posix); | |
55fc91b9 | 173 | debug_printf ("%s = find_exec (%s)", (char *) buf, name); |
1fd5e000 CF |
174 | if (known_suffix) |
175 | *known_suffix = suffix ?: strchr (buf, '\0'); | |
176 | return retval; | |
177 | } | |
178 | ||
179 | /* Utility for spawn_guts. */ | |
180 | ||
181 | static HANDLE | |
182 | handle (int n, int direction) | |
183 | { | |
0381fec6 | 184 | fhandler_base *fh = cygheap->fdtab[n]; |
1fd5e000 CF |
185 | |
186 | if (!fh) | |
187 | return INVALID_HANDLE_VALUE; | |
188 | if (fh->get_close_on_exec ()) | |
189 | return INVALID_HANDLE_VALUE; | |
190 | if (direction == 0) | |
191 | return fh->get_handle (); | |
192 | return fh->get_output_handle (); | |
193 | } | |
194 | ||
1fd5e000 CF |
195 | int |
196 | iscmd (const char *argv0, const char *what) | |
197 | { | |
198 | int n; | |
199 | n = strlen (argv0) - strlen (what); | |
200 | if (n >= 2 && argv0[1] != ':') | |
201 | return 0; | |
ebbd4e8f | 202 | return n >= 0 && strcasematch (argv0 + n, what) && |
1fd5e000 CF |
203 | (n == 0 || isdirsep (argv0[n - 1])); |
204 | } | |
205 | ||
206 | class linebuf | |
207 | { | |
0a047e8f | 208 | public: |
1fd5e000 CF |
209 | size_t ix; |
210 | char *buf; | |
211 | size_t alloced; | |
a333dca2 | 212 | linebuf () : ix (0), buf (NULL), alloced (0) {} |
7a44ba05 | 213 | ~linebuf () {if (buf) free (buf);} |
1fd5e000 CF |
214 | void add (const char *what, int len); |
215 | void add (const char *what) {add (what, strlen (what));} | |
216 | void prepend (const char *what, int len); | |
217 | }; | |
218 | ||
219 | void | |
220 | linebuf::add (const char *what, int len) | |
221 | { | |
222 | size_t newix; | |
223 | if ((newix = ix + len) >= alloced || !buf) | |
224 | { | |
225 | alloced += LINE_BUF_CHUNK + newix; | |
226 | buf = (char *) realloc (buf, alloced + 1); | |
227 | } | |
228 | memcpy (buf + ix, what, len); | |
229 | ix = newix; | |
230 | buf[ix] = '\0'; | |
231 | } | |
232 | ||
233 | void | |
234 | linebuf::prepend (const char *what, int len) | |
235 | { | |
236 | int buflen; | |
237 | size_t newix; | |
238 | if ((newix = ix + len) >= alloced) | |
239 | { | |
240 | alloced += LINE_BUF_CHUNK + newix; | |
241 | buf = (char *) realloc (buf, alloced + 1); | |
242 | buf[ix] = '\0'; | |
243 | } | |
244 | if ((buflen = strlen (buf))) | |
245 | memmove (buf + len, buf, buflen + 1); | |
246 | else | |
247 | buf[newix] = '\0'; | |
248 | memcpy (buf, what, len); | |
249 | ix = newix; | |
250 | } | |
251 | ||
99d7d12a | 252 | class av |
b0e82b74 | 253 | { |
b0e82b74 | 254 | char **argv; |
99d7d12a | 255 | int calloced; |
0a047e8f | 256 | public: |
7da53596 | 257 | int error; |
99d7d12a | 258 | int argc; |
7da53596 | 259 | av (int ac, const char * const *av) : calloced (0), error (false), argc (ac) |
b0e82b74 | 260 | { |
aece55b9 | 261 | argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *)); |
b0e82b74 CF |
262 | memcpy (argv, av, (argc + 1) * sizeof (char *)); |
263 | } | |
264 | ~av () | |
265 | { | |
7da53596 CF |
266 | if (argv) |
267 | { | |
268 | for (int i = 0; i < calloced; i++) | |
269 | if (argv[i]) | |
270 | cfree (argv[i]); | |
271 | cfree (argv); | |
272 | } | |
b0e82b74 CF |
273 | } |
274 | int unshift (const char *what, int conv = 0); | |
275 | operator char **() {return argv;} | |
99d7d12a CF |
276 | void all_calloced () {calloced = argc;} |
277 | void replace0_maybe (const char *arg0) | |
278 | { | |
279 | /* Note: Assumes that argv array has not yet been "unshifted" */ | |
7da53596 CF |
280 | if (!calloced |
281 | && (argv[0] = cstrdup1 (arg0))) | |
282 | calloced = true; | |
283 | else | |
284 | error = errno; | |
99d7d12a | 285 | } |
fb5956da | 286 | void dup_maybe (int i) |
99d7d12a | 287 | { |
7da53596 CF |
288 | if (i >= calloced |
289 | && !(argv[i] = cstrdup1 (argv[i]))) | |
290 | error = errno; | |
99d7d12a CF |
291 | } |
292 | void dup_all () | |
293 | { | |
294 | for (int i = calloced; i < argc; i++) | |
7da53596 CF |
295 | if (!(argv[i] = cstrdup1 (argv[i]))) |
296 | error = errno; | |
99d7d12a | 297 | } |
b0e82b74 CF |
298 | }; |
299 | ||
300 | int | |
301 | av::unshift (const char *what, int conv) | |
302 | { | |
303 | char **av; | |
304 | av = (char **) crealloc (argv, (argc + 2) * sizeof (char *)); | |
305 | if (!av) | |
306 | return 0; | |
307 | ||
308 | argv = av; | |
309 | memmove (argv + 1, argv, (argc + 1) * sizeof (char *)); | |
310 | char buf[MAX_PATH + 1]; | |
311 | if (conv) | |
312 | { | |
313 | cygwin_conv_to_posix_path (what, buf); | |
314 | char *p = strchr (buf, '\0') - 4; | |
315 | if (p > buf && strcasematch (p, ".exe")) | |
316 | *p = '\0'; | |
317 | what = buf; | |
318 | } | |
7da53596 CF |
319 | if (!(*argv = cstrdup1 (what))) |
320 | error = errno; | |
b0e82b74 CF |
321 | argc++; |
322 | calloced++; | |
323 | return 1; | |
324 | } | |
325 | ||
0199487e CF |
326 | struct pthread_cleanup |
327 | { | |
328 | _sig_func_ptr oldint; | |
329 | _sig_func_ptr oldquit; | |
330 | sigset_t oldmask; | |
331 | pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask (0) {} | |
332 | }; | |
333 | ||
334 | static void | |
335 | do_cleanup (void *args) | |
336 | { | |
337 | # define cleanup ((pthread_cleanup *) args) | |
338 | if (cleanup->oldint) | |
339 | signal (SIGINT, cleanup->oldint); | |
340 | if (cleanup->oldquit) | |
341 | signal (SIGQUIT, cleanup->oldquit); | |
342 | if (cleanup->oldmask) | |
343 | sigprocmask (SIG_SETMASK, &(cleanup->oldmask), NULL); | |
344 | # undef cleanup | |
345 | } | |
346 | ||
347 | ||
84c7d409 | 348 | static int __stdcall |
380aaf2d | 349 | spawn_guts (const char * prog_arg, const char *const *argv, |
84c7d409 | 350 | const char *const envp[], int mode) |
1fd5e000 | 351 | { |
1fd5e000 | 352 | BOOL rc; |
84c7d409 | 353 | pid_t cygpid; |
e46db834 | 354 | sigframe thisframe (mainthread); |
1fd5e000 | 355 | |
1fd5e000 CF |
356 | MALLOC_CHECK; |
357 | ||
358 | if (prog_arg == NULL) | |
359 | { | |
360 | syscall_printf ("prog_arg is NULL"); | |
51cb7ca7 | 361 | set_errno (EINVAL); |
1fd5e000 CF |
362 | return -1; |
363 | } | |
364 | ||
d8ee8ca5 | 365 | syscall_printf ("spawn_guts (%d, %.9500s)", mode, prog_arg); |
1fd5e000 CF |
366 | |
367 | if (argv == NULL) | |
368 | { | |
369 | syscall_printf ("argv is NULL"); | |
51cb7ca7 | 370 | set_errno (EINVAL); |
7da53596 | 371 | return -1; |
1fd5e000 CF |
372 | } |
373 | ||
b0e82b74 CF |
374 | path_conv real_path; |
375 | ||
376 | linebuf one_line; | |
377 | ||
378 | STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}; | |
379 | ||
380 | child_info_spawn ciresrv; | |
381 | si.lpReserved2 = (LPBYTE) &ciresrv; | |
382 | si.cbReserved2 = sizeof (ciresrv); | |
383 | ||
b0e82b74 | 384 | DWORD chtype; |
8af0f81d | 385 | if (mode != _P_OVERLAY) |
b0e82b74 CF |
386 | chtype = PROC_SPAWN; |
387 | else | |
aece55b9 CF |
388 | chtype = PROC_EXEC; |
389 | ||
0301bfd0 | 390 | HANDLE subproc_ready; |
8af0f81d | 391 | if (chtype != PROC_EXEC) |
0301bfd0 | 392 | subproc_ready = NULL; |
5457dfcb CF |
393 | else |
394 | { | |
0301bfd0 CF |
395 | subproc_ready = CreateEvent (&sec_all, TRUE, FALSE, NULL); |
396 | ProtectHandleINH (subproc_ready); | |
5457dfcb | 397 | } |
b0e82b74 | 398 | |
8af0f81d | 399 | init_child_info (chtype, &ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1, |
0301bfd0 | 400 | subproc_ready); |
b0e82b74 CF |
401 | if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &ciresrv.parent, 0, 1, |
402 | DUPLICATE_SAME_ACCESS)) | |
403 | { | |
404 | system_printf ("couldn't create handle to myself for child, %E"); | |
405 | return -1; | |
406 | } | |
407 | ||
e5648465 | 408 | ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info)); |
1dc16fc7 | 409 | ciresrv.moreinfo->old_title = NULL; |
b0e82b74 | 410 | |
1fd5e000 CF |
411 | /* CreateProcess takes one long string that is the command line (sigh). |
412 | We need to quote any argument that has whitespace or embedded "'s. */ | |
413 | ||
b0e82b74 CF |
414 | int ac; |
415 | for (ac = 0; argv[ac]; ac++) | |
1fd5e000 CF |
416 | /* nothing */; |
417 | ||
b0e82b74 | 418 | av newargv (ac, argv); |
1fd5e000 | 419 | |
a333dca2 | 420 | int null_app_name = 0; |
b0e82b74 | 421 | if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' && |
1fd5e000 CF |
422 | (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe"))) |
423 | { | |
a333dca2 | 424 | real_path.check (prog_arg); |
b0de2aa2 | 425 | one_line.add ("\""); |
a333dca2 CF |
426 | if (!real_path.error) |
427 | one_line.add (real_path); | |
428 | else | |
429 | one_line.add (argv[0]); | |
b0de2aa2 | 430 | one_line.add ("\""); |
1fd5e000 CF |
431 | one_line.add (" "); |
432 | one_line.add (argv[1]); | |
433 | one_line.add (" "); | |
1fd5e000 | 434 | one_line.add (argv[2]); |
b0e82b74 | 435 | strcpy (real_path, argv[0]); |
a333dca2 | 436 | null_app_name = 1; |
1fd5e000 CF |
437 | goto skip_arg_parsing; |
438 | } | |
439 | ||
1fd5e000 | 440 | const char *ext; |
b0e82b74 | 441 | if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL) |
1fd5e000 CF |
442 | { |
443 | set_errno (ENOENT); | |
444 | return -1; | |
445 | } | |
446 | ||
447 | MALLOC_CHECK; | |
1fd5e000 CF |
448 | |
449 | /* If the file name ends in either .exe, .com, .bat, or .cmd we assume | |
450 | that it is NOT a script file */ | |
451 | while (*ext == '\0') | |
452 | { | |
580e99a1 CF |
453 | HANDLE hnd = CreateFile (real_path, GENERIC_READ, |
454 | FILE_SHARE_READ | FILE_SHARE_WRITE, | |
455 | &sec_none_nih, OPEN_EXISTING, | |
456 | FILE_ATTRIBUTE_NORMAL, 0); | |
1fd5e000 CF |
457 | if (hnd == INVALID_HANDLE_VALUE) |
458 | { | |
459 | __seterrno (); | |
460 | return -1; | |
461 | } | |
462 | ||
463 | DWORD done; | |
464 | ||
465 | char buf[2 * MAX_PATH + 1]; | |
51cb7ca7 | 466 | buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0'; |
90fe7739 | 467 | if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0)) |
1fd5e000 CF |
468 | { |
469 | CloseHandle (hnd); | |
470 | __seterrno (); | |
471 | return -1; | |
472 | } | |
473 | ||
474 | CloseHandle (hnd); | |
475 | ||
476 | if (buf[0] == 'M' && buf[1] == 'Z') | |
477 | break; | |
478 | ||
99d7d12a | 479 | debug_printf ("%s is a script", (char *) real_path); |
1fd5e000 | 480 | |
99d7d12a | 481 | char *pgm, *arg1; |
1fd5e000 CF |
482 | |
483 | if (buf[0] != '#' || buf[1] != '!') | |
484 | { | |
b0e82b74 | 485 | pgm = (char *) "/bin/sh"; |
1fd5e000 CF |
486 | arg1 = NULL; |
487 | } | |
488 | else | |
489 | { | |
99d7d12a | 490 | char *ptr; |
1fd5e000 CF |
491 | pgm = buf + 2; |
492 | pgm += strspn (pgm, " \t"); | |
493 | for (ptr = pgm, arg1 = NULL; | |
494 | *ptr && *ptr != '\r' && *ptr != '\n'; | |
495 | ptr++) | |
496 | if (!arg1 && (*ptr == ' ' || *ptr == '\t')) | |
497 | { | |
498 | /* Null terminate the initial command and step over | |
499 | any additional white space. If we've hit the | |
90fe7739 CF |
500 | end of the line, exit the loop. Otherwise, we've |
501 | found the first argument. Position the current | |
1fd5e000 CF |
502 | pointer on the last known white space. */ |
503 | *ptr = '\0'; | |
504 | char *newptr = ptr + 1; | |
505 | newptr += strspn (newptr, " \t"); | |
506 | if (!*newptr || *newptr == '\r' || *newptr == '\n') | |
507 | break; | |
508 | arg1 = newptr; | |
509 | ptr = newptr - 1; | |
510 | } | |
511 | ||
1fd5e000 CF |
512 | *ptr = '\0'; |
513 | } | |
514 | ||
99d7d12a CF |
515 | /* Replace argv[0] with the full path to the script if this is the |
516 | first time through the loop. */ | |
b9631756 | 517 | newargv.replace0_maybe (prog_arg); |
75a57bf0 | 518 | |
1fd5e000 CF |
519 | /* pointers: |
520 | * pgm interpreter name | |
521 | * arg1 optional string | |
1fd5e000 | 522 | */ |
b0e82b74 CF |
523 | if (arg1) |
524 | newargv.unshift (arg1); | |
1fd5e000 | 525 | |
6ea0c04e | 526 | /* FIXME: This should not be using FE_NATIVE. It should be putting |
9c510edc | 527 | the posix path on the argv list. */ |
6ea0c04e | 528 | find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext); |
b0e82b74 | 529 | newargv.unshift (real_path, 1); |
1fd5e000 CF |
530 | } |
531 | ||
b0e82b74 | 532 | if (real_path.iscygexec ()) |
99d7d12a | 533 | newargv.dup_all (); |
b0e82b74 | 534 | else |
1fd5e000 | 535 | { |
b0e82b74 | 536 | for (int i = 0; i < newargv.argc; i++) |
1fd5e000 | 537 | { |
b0e82b74 CF |
538 | char *p = NULL; |
539 | const char *a; | |
540 | ||
99d7d12a | 541 | newargv.dup_maybe (i); |
4eafa56e | 542 | a = i ? newargv[i] : (char *) real_path; |
b0e82b74 CF |
543 | int len = strlen (a); |
544 | if (len != 0 && !strpbrk (a, " \t\n\r\"")) | |
545 | one_line.add (a, len); | |
546 | else | |
1fd5e000 | 547 | { |
b0e82b74 | 548 | one_line.add ("\"", 1); |
9784d54d CF |
549 | /* Handle embedded special characters " and \. |
550 | A " is always preceded by a \. | |
551 | A \ is not special unless it precedes a ". If it does, | |
552 | then all preceding \'s must be doubled to avoid having | |
553 | the Windows command line parser interpret the \ as quoting | |
554 | the ". This rule applies to a string of \'s before the end | |
555 | of the string, since cygwin/windows uses a " to delimit the | |
556 | argument. */ | |
b0e82b74 CF |
557 | for (; (p = strpbrk (a, "\"\\")); a = ++p) |
558 | { | |
559 | one_line.add (a, p - a); | |
9784d54d CF |
560 | /* Find length of string of backslashes */ |
561 | int n = strspn (p, "\\"); | |
562 | if (!n) | |
563 | one_line.add ("\\\"", 2); /* No backslashes, so it must be a ". | |
564 | The " has to be protected with a backslash. */ | |
565 | else | |
099efae0 | 566 | { |
9784d54d CF |
567 | one_line.add (p, n); /* Add the run of backslashes */ |
568 | /* Need to double up all of the preceding | |
569 | backslashes if they precede a quote or EOS. */ | |
570 | if (!p[n] || p[n] == '"') | |
571 | one_line.add (p, n); | |
572 | p += n - 1; /* Point to last backslash */ | |
099efae0 | 573 | } |
b0e82b74 CF |
574 | } |
575 | if (*a) | |
576 | one_line.add (a); | |
577 | one_line.add ("\"", 1); | |
1fd5e000 | 578 | } |
b0e82b74 CF |
579 | MALLOC_CHECK; |
580 | one_line.add (" ", 1); | |
581 | MALLOC_CHECK; | |
1fd5e000 | 582 | } |
b0e82b74 | 583 | |
1fd5e000 | 584 | MALLOC_CHECK; |
b0e82b74 CF |
585 | if (one_line.ix) |
586 | one_line.buf[one_line.ix - 1] = '\0'; | |
587 | else | |
588 | one_line.add ("", 1); | |
1fd5e000 CF |
589 | MALLOC_CHECK; |
590 | } | |
99d7d12a | 591 | |
da086d02 | 592 | char *envblock; |
99d7d12a | 593 | newargv.all_calloced (); |
7da53596 CF |
594 | if (newargv.error) |
595 | { | |
596 | set_errno (newargv.error); | |
597 | return -1; | |
598 | } | |
599 | ||
b0e82b74 CF |
600 | ciresrv.moreinfo->argc = newargv.argc; |
601 | ciresrv.moreinfo->argv = newargv; | |
166b2571 | 602 | ciresrv.hexec_proc = hexec_proc; |
da086d02 | 603 | |
b0e82b74 | 604 | if (mode != _P_OVERLAY || |
16828fc5 CF |
605 | !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc, |
606 | &ciresrv.moreinfo->myself_pinfo, 0, | |
b0e82b74 CF |
607 | TRUE, DUPLICATE_SAME_ACCESS)) |
608 | ciresrv.moreinfo->myself_pinfo = NULL; | |
1fd5e000 | 609 | |
0a047e8f | 610 | skip_arg_parsing: |
01cf5d0f | 611 | PROCESS_INFORMATION pi = {NULL, 0, 0, 0}; |
1fd5e000 CF |
612 | si.lpReserved = NULL; |
613 | si.lpDesktop = NULL; | |
614 | si.dwFlags = STARTF_USESTDHANDLES; | |
615 | si.hStdInput = handle (0, 0); /* Get input handle */ | |
616 | si.hStdOutput = handle (1, 1); /* Get output handle */ | |
617 | si.hStdError = handle (2, 1); /* Get output handle */ | |
618 | si.cb = sizeof (si); | |
619 | ||
166b2571 | 620 | int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc); |
1fd5e000 CF |
621 | |
622 | if (mode == _P_DETACH || !set_console_state_for_spawn ()) | |
623 | flags |= DETACHED_PROCESS; | |
166b2571 CF |
624 | if (mode != _P_OVERLAY) |
625 | flags |= CREATE_SUSPENDED; | |
1fd5e000 | 626 | |
dd4f0b23 CV |
627 | /* Some file types (currently only sockets) need extra effort in the |
628 | parent after CreateProcess and before copying the datastructures | |
629 | to the child. So we have to start the child in suspend state, | |
630 | unfortunately, to avoid a race condition. */ | |
0381fec6 | 631 | if (cygheap->fdtab.need_fixup_before ()) |
dd4f0b23 CV |
632 | flags |= CREATE_SUSPENDED; |
633 | ||
634 | ||
a333dca2 CF |
635 | const char *runpath = null_app_name ? NULL : (const char *) real_path; |
636 | ||
d8ee8ca5 | 637 | syscall_printf ("null_app_name %d (%s, %.9500s)", null_app_name, runpath, one_line.buf); |
a333dca2 | 638 | |
e2e07827 | 639 | void *newheap; |
380aaf2d CF |
640 | /* Preallocated buffer for `sec_user' call */ |
641 | char sa_buf[1024]; | |
642 | ||
a333dca2 | 643 | cygbench ("spawn-guts"); |
9a771b29 | 644 | |
fef1edbc | 645 | cygheap->fdtab.set_file_pointers_for_exec (); |
70249d56 | 646 | cygheap->user.deimpersonate (); |
271c1935 CV |
647 | /* When ruid != euid we create the new process under the current original |
648 | account and impersonate in child, this way maintaining the different | |
649 | effective vs. real ids. | |
1498189c | 650 | FIXME: If ruid != euid and ruid != saved_uid we currently give |
271c1935 CV |
651 | up on ruid. The new process will have ruid == euid. */ |
652 | if (!cygheap->user.issetuid () | |
1498189c CV |
653 | || (cygheap->user.saved_uid == cygheap->user.real_uid |
654 | && cygheap->user.saved_gid == cygheap->user.real_gid | |
271c1935 | 655 | && !cygheap->user.groups.issetgroups ())) |
166b2571 | 656 | { |
d5377829 | 657 | PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf); |
9a771b29 CF |
658 | ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, |
659 | real_path.iscygexec ()); | |
6d171b44 | 660 | newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ()); |
a333dca2 | 661 | rc = CreateProcess (runpath, /* image name - with full path */ |
166b2571 | 662 | one_line.buf, /* what was passed to exec */ |
d5377829 CF |
663 | sec_attribs, /* process security attrs */ |
664 | sec_attribs, /* thread security attrs */ | |
665 | TRUE, /* inherit handles from parent */ | |
166b2571 | 666 | flags, |
d5377829 CF |
667 | envblock, /* environment */ |
668 | 0, /* use current drive/directory */ | |
166b2571 CF |
669 | &si, |
670 | &pi); | |
671 | } | |
672 | else | |
1fd5e000 | 673 | { |
9a771b29 CF |
674 | PSID sid = cygheap->user.sid (); |
675 | ||
676 | /* Set security attributes with sid */ | |
cecb74ae | 677 | PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, sid); |
57ff940d | 678 | |
1fd5e000 CF |
679 | /* allow the child to interact with our window station/desktop */ |
680 | HANDLE hwst, hdsk; | |
681 | SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION; | |
682 | DWORD n; | |
683 | char wstname[1024]; | |
684 | char dskname[1024]; | |
685 | ||
51cb7ca7 CF |
686 | hwst = GetProcessWindowStation (); |
687 | SetUserObjectSecurity (hwst, &dsi, get_null_sd ()); | |
688 | GetUserObjectInformation (hwst, UOI_NAME, wstname, 1024, &n); | |
689 | hdsk = GetThreadDesktop (GetCurrentThreadId ()); | |
690 | SetUserObjectSecurity (hdsk, &dsi, get_null_sd ()); | |
691 | GetUserObjectInformation (hdsk, UOI_NAME, dskname, 1024, &n); | |
1fd5e000 CF |
692 | strcat (wstname, "\\"); |
693 | strcat (wstname, dskname); | |
694 | si.lpDesktop = wstname; | |
1fd5e000 | 695 | |
9a771b29 CF |
696 | ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, |
697 | real_path.iscygexec ()); | |
6d171b44 | 698 | newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ()); |
70249d56 | 699 | rc = CreateProcessAsUser (cygheap->user.token (), |
a333dca2 | 700 | runpath, /* image name - with full path */ |
1fd5e000 | 701 | one_line.buf, /* what was passed to exec */ |
b0e82b74 CF |
702 | sec_attribs, /* process security attrs */ |
703 | sec_attribs, /* thread security attrs */ | |
d5377829 | 704 | TRUE, /* inherit handles from parent */ |
1fd5e000 | 705 | flags, |
d5377829 CF |
706 | envblock, /* environment */ |
707 | 0, /* use current drive/directory */ | |
1fd5e000 CF |
708 | &si, |
709 | &pi); | |
710 | } | |
d1fb625d CF |
711 | |
712 | /* FIXME: There is a small race here */ | |
713 | ||
0199487e CF |
714 | DWORD res; |
715 | pthread_cleanup cleanup; | |
716 | pthread_cleanup_push (do_cleanup, (void *) &cleanup); | |
d1fb625d CF |
717 | if (mode == _P_SYSTEM) |
718 | { | |
719 | sigset_t child_block; | |
0199487e CF |
720 | cleanup.oldint = signal (SIGINT, SIG_IGN); |
721 | cleanup.oldquit = signal (SIGQUIT, SIG_IGN); | |
d1fb625d CF |
722 | sigemptyset (&child_block); |
723 | sigaddset (&child_block, SIGCHLD); | |
0199487e | 724 | (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask); |
d1fb625d CF |
725 | } |
726 | ||
271c1935 CV |
727 | /* Restore impersonation. In case of _P_OVERLAY this isn't |
728 | allowed since it would overwrite child data. */ | |
70249d56 CV |
729 | if (mode != _P_OVERLAY) |
730 | cygheap->user.reimpersonate (); | |
1fd5e000 CF |
731 | |
732 | MALLOC_CHECK; | |
b0e82b74 CF |
733 | if (envblock) |
734 | free (envblock); | |
1fd5e000 CF |
735 | MALLOC_CHECK; |
736 | ||
737 | /* Set errno now so that debugging messages from it appear before our | |
738 | final debugging message [this is a general rule for debugging | |
739 | messages]. */ | |
47026c07 CF |
740 | if (!rc) |
741 | { | |
47026c07 CF |
742 | __seterrno (); |
743 | syscall_printf ("CreateProcess failed, %E"); | |
0301bfd0 CF |
744 | if (subproc_ready) |
745 | ForceCloseHandle (subproc_ready); | |
e2e07827 | 746 | cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0); |
47026c07 CF |
747 | return -1; |
748 | } | |
1fd5e000 | 749 | |
dd4f0b23 CV |
750 | /* Fixup the parent datastructure if needed and resume the child's |
751 | main thread. */ | |
e2e07827 CF |
752 | if (!cygheap->fdtab.need_fixup_before ()) |
753 | cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0); | |
754 | else | |
dd4f0b23 | 755 | { |
0381fec6 | 756 | cygheap->fdtab.fixup_before_exec (pi.dwProcessId); |
e2e07827 | 757 | cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1); |
dd4f0b23 | 758 | if (mode == _P_OVERLAY) |
969203ce CF |
759 | { |
760 | ResumeThread (pi.hThread); | |
761 | cygthread::terminate (); | |
762 | } | |
dd4f0b23 CV |
763 | } |
764 | ||
3f5046a5 | 765 | if (mode != _P_OVERLAY) |
84c7d409 | 766 | cygpid = cygwin_pid (pi.dwProcessId); |
3f5046a5 CF |
767 | else |
768 | cygpid = myself->pid; | |
1fd5e000 CF |
769 | |
770 | /* We print the original program name here so the user can see that too. */ | |
d8ee8ca5 | 771 | syscall_printf ("%d = spawn_guts (%s, %.9500s)", |
164a681c | 772 | rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf); |
1fd5e000 | 773 | |
84c7d409 CF |
774 | /* Name the handle similarly to proc_subproc. */ |
775 | ProtectHandle1 (pi.hProcess, childhProc); | |
1fd5e000 CF |
776 | |
777 | if (mode == _P_OVERLAY) | |
778 | { | |
166b2571 CF |
779 | /* These are both duplicated in the child code. We do this here, |
780 | primarily for strace. */ | |
4c45a897 CF |
781 | strace.execing = 1; |
782 | hExeced = pi.hProcess; | |
c0a8e8d0 | 783 | dwExeced = pi.dwProcessId; |
12520587 | 784 | strcpy (myself->progname, real_path); |
e5ba4c06 | 785 | close_all_files (); |
1fd5e000 CF |
786 | } |
787 | else | |
788 | { | |
f8f9b12e | 789 | myself->set_has_pgid_children (); |
166b2571 | 790 | ProtectHandle (pi.hThread); |
84c7d409 CF |
791 | pinfo child (cygpid, 1); |
792 | if (!child) | |
793 | { | |
794 | set_errno (EAGAIN); | |
795 | syscall_printf ("-1 = spawnve (), process table full"); | |
796 | return -1; | |
797 | } | |
1fd5e000 CF |
798 | child->dwProcessId = pi.dwProcessId; |
799 | child->hProcess = pi.hProcess; | |
84c7d409 | 800 | child.remember (); |
12520587 | 801 | strcpy (child->progname, real_path); |
8af0f81d | 802 | /* FIXME: This introduces an unreferenced, open handle into the child. |
9c510edc | 803 | The purpose is to keep the pid shared memory open so that all of |
8af0f81d CF |
804 | the fields filled out by child.remember do not disappear and so there |
805 | is not a brief period during which the pid is not available. | |
806 | However, we should try to find another way to do this eventually. */ | |
807 | (void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess, | |
808 | NULL, 0, 0, DUPLICATE_SAME_ACCESS); | |
166b2571 CF |
809 | /* Start the child running */ |
810 | ResumeThread (pi.hThread); | |
1fd5e000 CF |
811 | } |
812 | ||
1fd5e000 CF |
813 | ForceCloseHandle (pi.hThread); |
814 | ||
166b2571 CF |
815 | sigproc_printf ("spawned windows pid %d", pi.dwProcessId); |
816 | ||
aece55b9 | 817 | BOOL exited; |
1fd5e000 | 818 | |
aece55b9 CF |
819 | res = 0; |
820 | exited = FALSE; | |
821 | MALLOC_CHECK; | |
5457dfcb | 822 | if (mode == _P_OVERLAY) |
aece55b9 | 823 | { |
5457dfcb | 824 | int nwait = 3; |
0301bfd0 | 825 | HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, subproc_ready}; |
5457dfcb | 826 | for (int i = 0; i < 100; i++) |
1fd5e000 | 827 | { |
5457dfcb | 828 | switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE)) |
1fd5e000 | 829 | { |
5457dfcb CF |
830 | case WAIT_OBJECT_0: |
831 | sigproc_printf ("subprocess exited"); | |
832 | DWORD exitcode; | |
833 | if (!GetExitCodeProcess (pi.hProcess, &exitcode)) | |
834 | exitcode = 1; | |
835 | res |= exitcode; | |
836 | exited = TRUE; | |
837 | break; | |
838 | case WAIT_OBJECT_0 + 1: | |
839 | sigproc_printf ("signal arrived"); | |
840 | reset_signal_arrived (); | |
841 | continue; | |
842 | case WAIT_OBJECT_0 + 2: | |
0c8fe172 | 843 | if (my_parent_is_alive ()) |
941fa5ad | 844 | res |= EXIT_REPARENTING; |
0c8fe172 | 845 | else if (!myself->ppid_handle) |
332600d8 | 846 | { |
5457dfcb | 847 | nwait = 2; |
aece55b9 CF |
848 | sigproc_terminate (); |
849 | continue; | |
332600d8 | 850 | } |
5457dfcb CF |
851 | break; |
852 | case WAIT_FAILED: | |
853 | system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E", | |
854 | nwait, myself->pid, myself->dwProcessId); | |
855 | system_printf ("waitbuf[0] %p %d", waitbuf[0], | |
856 | WaitForSingleObject (waitbuf[0], 0)); | |
857 | system_printf ("waitbuf[1] %p = %d", waitbuf[1], | |
858 | WaitForSingleObject (waitbuf[1], 0)); | |
859 | system_printf ("waitbuf[w] %p = %d", waitbuf[2], | |
860 | WaitForSingleObject (waitbuf[2], 0)); | |
861 | set_errno (ECHILD); | |
862 | try_to_debug (); | |
863 | return -1; | |
1fd5e000 | 864 | } |
1fd5e000 | 865 | break; |
1fd5e000 CF |
866 | } |
867 | ||
0301bfd0 | 868 | ForceCloseHandle (subproc_ready); |
1fd5e000 | 869 | |
5457dfcb | 870 | sigproc_printf ("res = %x", res); |
1fd5e000 | 871 | |
5457dfcb | 872 | if (res & EXIT_REPARENTING) |
1fd5e000 | 873 | { |
5457dfcb CF |
874 | /* Try to reparent child process. |
875 | * Make handles to child available to parent process and exit with | |
876 | * EXIT_REPARENTING status. Wait() syscall in parent will then wait | |
877 | * for newly created child. | |
878 | */ | |
879 | HANDLE oldh = myself->hProcess; | |
880 | HANDLE h = myself->ppid_handle; | |
881 | sigproc_printf ("parent handle %p", h); | |
882 | int rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess, | |
883 | 0, FALSE, DUPLICATE_SAME_ACCESS); | |
884 | sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p", | |
885 | rc, oldh, myself->hProcess); | |
886 | if (!rc && my_parent_is_alive ()) | |
887 | { | |
888 | system_printf ("Reparent failed, parent handle %p, %E", h); | |
889 | system_printf ("my dwProcessId %d, myself->dwProcessId %d", | |
890 | GetCurrentProcessId (), myself->dwProcessId); | |
891 | system_printf ("old hProcess %p, hProcess %p", oldh, myself->hProcess); | |
892 | } | |
1fd5e000 | 893 | } |
aece55b9 | 894 | |
5457dfcb | 895 | } |
07c3cd5b | 896 | |
aece55b9 | 897 | MALLOC_CHECK; |
1fd5e000 | 898 | |
b0e82b74 CF |
899 | switch (mode) |
900 | { | |
aece55b9 | 901 | case _P_OVERLAY: |
166b2571 | 902 | ForceCloseHandle1 (pi.hProcess, childhProc); |
aece55b9 | 903 | proc_terminate (); |
eca7c729 | 904 | myself->exit (res, 1); |
aece55b9 | 905 | break; |
b0e82b74 | 906 | case _P_WAIT: |
d1fb625d | 907 | case _P_SYSTEM: |
b0e82b74 CF |
908 | waitpid (cygpid, (int *) &res, 0); |
909 | break; | |
910 | case _P_DETACH: | |
911 | res = 0; /* Lose all memory of this child. */ | |
912 | break; | |
913 | case _P_NOWAIT: | |
914 | case _P_NOWAITO: | |
915 | case _P_VFORK: | |
916 | res = cygpid; | |
917 | break; | |
918 | default: | |
919 | break; | |
920 | } | |
1fd5e000 | 921 | |
0199487e | 922 | pthread_cleanup_pop (1); |
1fd5e000 CF |
923 | return (int) res; |
924 | } | |
925 | ||
0a047e8f | 926 | extern "C" int |
1fd5e000 CF |
927 | cwait (int *result, int pid, int) |
928 | { | |
929 | return waitpid (pid, result, 0); | |
930 | } | |
931 | ||
932 | /* | |
933 | * Helper function for spawn runtime calls. | |
934 | * Doesn't search the path. | |
935 | */ | |
936 | ||
937 | extern "C" int | |
380aaf2d CF |
938 | spawnve (int mode, const char *path, const char *const *argv, |
939 | const char *const *envp) | |
1fd5e000 | 940 | { |
1fd5e000 CF |
941 | int ret; |
942 | vfork_save *vf = vfork_storage.val (); | |
943 | ||
944 | if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY) | |
945 | mode = _P_NOWAIT; | |
946 | else | |
947 | vf = NULL; | |
948 | ||
380aaf2d | 949 | syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp); |
1fd5e000 CF |
950 | |
951 | switch (mode) | |
952 | { | |
b0e82b74 CF |
953 | case _P_OVERLAY: |
954 | /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/ | |
955 | /* Just act as an exec if _P_OVERLAY set. */ | |
380aaf2d | 956 | spawn_guts (path, argv, envp, mode); |
b0e82b74 CF |
957 | /* Errno should be set by spawn_guts. */ |
958 | ret = -1; | |
959 | break; | |
960 | case _P_VFORK: | |
961 | case _P_NOWAIT: | |
962 | case _P_NOWAITO: | |
963 | case _P_WAIT: | |
964 | case _P_DETACH: | |
d1fb625d | 965 | case _P_SYSTEM: |
b0e82b74 | 966 | subproc_init (); |
380aaf2d | 967 | ret = spawn_guts (path, argv, envp, mode); |
8dca9e23 | 968 | if (vf) |
b0e82b74 | 969 | { |
12520587 | 970 | debug_printf ("longjmping due to vfork"); |
8dca9e23 CF |
971 | if (ret < 0) |
972 | vf->restore_exit (ret); | |
973 | else | |
974 | vf->restore_pid (ret); | |
b0e82b74 CF |
975 | } |
976 | break; | |
977 | default: | |
978 | set_errno (EINVAL); | |
979 | ret = -1; | |
980 | break; | |
1fd5e000 CF |
981 | } |
982 | return ret; | |
983 | } | |
984 | ||
985 | /* | |
986 | * spawn functions as implemented in the MS runtime library. | |
987 | * Most of these based on (and copied from) newlib/libc/posix/execXX.c | |
988 | */ | |
989 | ||
0a047e8f | 990 | extern "C" int |
1fd5e000 CF |
991 | spawnl (int mode, const char *path, const char *arg0, ...) |
992 | { | |
993 | int i; | |
994 | va_list args; | |
995 | const char *argv[256]; | |
996 | ||
997 | va_start (args, arg0); | |
998 | argv[0] = arg0; | |
999 | i = 1; | |
1000 | ||
1001 | do | |
1002 | argv[i] = va_arg (args, const char *); | |
1003 | while (argv[i++] != NULL); | |
1004 | ||
1005 | va_end (args); | |
1006 | ||
380aaf2d | 1007 | return spawnve (mode, path, (char * const *) argv, cur_environ ()); |
1fd5e000 CF |
1008 | } |
1009 | ||
0a047e8f | 1010 | extern "C" int |
1fd5e000 CF |
1011 | spawnle (int mode, const char *path, const char *arg0, ...) |
1012 | { | |
1013 | int i; | |
1014 | va_list args; | |
1015 | const char * const *envp; | |
1016 | const char *argv[256]; | |
1017 | ||
1018 | va_start (args, arg0); | |
1019 | argv[0] = arg0; | |
1020 | i = 1; | |
1021 | ||
1022 | do | |
1023 | argv[i] = va_arg (args, const char *); | |
1024 | while (argv[i++] != NULL); | |
1025 | ||
1026 | envp = va_arg (args, const char * const *); | |
1027 | va_end (args); | |
1028 | ||
380aaf2d | 1029 | return spawnve (mode, path, (char * const *) argv, (char * const *) envp); |
1fd5e000 CF |
1030 | } |
1031 | ||
0a047e8f | 1032 | extern "C" int |
1fd5e000 CF |
1033 | spawnlp (int mode, const char *path, const char *arg0, ...) |
1034 | { | |
1035 | int i; | |
1036 | va_list args; | |
1037 | const char *argv[256]; | |
1038 | ||
1039 | va_start (args, arg0); | |
1040 | argv[0] = arg0; | |
1041 | i = 1; | |
1042 | ||
1043 | do | |
1044 | argv[i] = va_arg (args, const char *); | |
1045 | while (argv[i++] != NULL); | |
1046 | ||
1047 | va_end (args); | |
1048 | ||
4c8d72de | 1049 | return spawnvpe (mode, path, (char * const *) argv, cur_environ ()); |
1fd5e000 CF |
1050 | } |
1051 | ||
0a047e8f | 1052 | extern "C" int |
1fd5e000 CF |
1053 | spawnlpe (int mode, const char *path, const char *arg0, ...) |
1054 | { | |
1055 | int i; | |
1056 | va_list args; | |
1057 | const char * const *envp; | |
1058 | const char *argv[256]; | |
1059 | ||
1060 | va_start (args, arg0); | |
1061 | argv[0] = arg0; | |
1062 | i = 1; | |
1063 | ||
1064 | do | |
1065 | argv[i] = va_arg (args, const char *); | |
1066 | while (argv[i++] != NULL); | |
1067 | ||
1068 | envp = va_arg (args, const char * const *); | |
1069 | va_end (args); | |
1070 | ||
1071 | return spawnvpe (mode, path, (char * const *) argv, envp); | |
1072 | } | |
1073 | ||
0a047e8f | 1074 | extern "C" int |
1fd5e000 CF |
1075 | spawnv (int mode, const char *path, const char * const *argv) |
1076 | { | |
380aaf2d | 1077 | return spawnve (mode, path, argv, cur_environ ()); |
1fd5e000 CF |
1078 | } |
1079 | ||
0a047e8f | 1080 | extern "C" int |
1fd5e000 CF |
1081 | spawnvp (int mode, const char *path, const char * const *argv) |
1082 | { | |
4c8d72de | 1083 | return spawnvpe (mode, path, argv, cur_environ ()); |
1fd5e000 CF |
1084 | } |
1085 | ||
0a047e8f | 1086 | extern "C" int |
1fd5e000 CF |
1087 | spawnvpe (int mode, const char *file, const char * const *argv, |
1088 | const char * const *envp) | |
1089 | { | |
55fc91b9 | 1090 | path_conv buf; |
380aaf2d | 1091 | return spawnve (mode, find_exec (file, buf), argv, envp); |
1fd5e000 | 1092 | } |