5 * Gonzalo Paniagua Javier (gonzalo@novell.com
7 * (C) 2006 Novell, Inc.
9 * Permission is hereby granted, free of charge, to any person obtaining
10 * a copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sublicense, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #include <sys/types.h>
44 #ifdef HAVE_SYS_SELECT_H
45 #include <sys/select.h>
48 #ifdef HAVE_SYS_TIME_H
52 #ifdef HAVE_SYS_WAIT_H
56 #ifdef HAVE_SYS_RESOURCE_H
57 # include <sys/resource.h>
67 /* windows pipe api details: http://msdn2.microsoft.com/en-us/library/edze9h7e(VS.80).aspx */
68 #define pipe(x) _pipe(x, 256, 0)
71 #define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
72 #define set_error_cond(cond,msg, ...) do { if ((cond) && error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
73 #define set_error_status(status,msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, status, msg, __VA_ARGS__); } while (0)
74 #define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR)
75 #define CLOSE_PIPE(p) do { close (p [0]); close (p [1]); } while (0)
77 #if defined(__APPLE__)
78 #if defined (TARGET_OSX)
79 /* Apple defines this in crt_externs.h but doesn't provide that header for
80 * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
81 * in fact exist on all implementations (so far)
83 gchar
***_NSGetEnviron(void);
84 #define environ (*_NSGetEnviron())
86 static char *mono_environ
[1] = { NULL
};
87 #define environ mono_environ
88 #endif /* defined (TARGET_OSX) */
89 #elif defined(_MSC_VER)
90 /* MS defines this in stdlib.h */
92 extern char **environ
;
97 safe_read (int fd
, gchar
*buffer
, gint count
, GError
**error
)
101 NO_INTR (res
, read (fd
, buffer
, count
));
102 set_error_cond (res
== -1, "%s", "Error reading from pipe.");
107 read_pipes (int outfd
, gchar
**out_str
, int errfd
, gchar
**err_str
, GError
**error
)
115 gchar
*buffer
= NULL
;
118 out_closed
= (outfd
< 0);
119 err_closed
= (errfd
< 0);
122 out
= g_string_new ("");
127 err
= g_string_new ("");
131 if (out_closed
&& err_closed
)
135 #pragma warning(push)
136 #pragma warning(disable:4389)
140 if (!out_closed
&& outfd
>= 0)
141 FD_SET (outfd
, &rfds
);
142 if (!err_closed
&& errfd
>= 0)
143 FD_SET (errfd
, &rfds
);
149 res
= select (MAX (outfd
, errfd
) + 1, &rfds
, NULL
, NULL
, NULL
);
152 buffer
= g_malloc (1024);
153 if (!out_closed
&& FD_ISSET (outfd
, &rfds
)) {
154 nread
= safe_read (outfd
, buffer
, 1024, error
);
160 g_string_append_len (out
, buffer
, nread
);
167 if (!err_closed
&& FD_ISSET (errfd
, &rfds
)) {
168 nread
= safe_read (errfd
, buffer
, 1024, error
);
174 g_string_append_len (err
, buffer
, nread
);
181 } while (res
> 0 || (res
== -1 && errno
== EINTR
));
185 *out_str
= g_string_free (out
, FALSE
);
188 *err_str
= g_string_free (err
, FALSE
);
194 create_pipe (int *fds
, GError
**error
)
196 if (pipe (fds
) == -1) {
197 set_error ("%s", "Error creating pipe.");
202 #endif /* G_OS_WIN32 */
205 write_all (int fd
, const void *vbuf
, size_t n
)
207 const char *buf
= (const char *) vbuf
;
213 w
= write (fd
, buf
+ nwritten
, n
- nwritten
);
214 } while (w
== -1 && errno
== EINTR
);
220 } while (nwritten
< n
);
227 eg_getdtablesize (void)
229 #ifdef HAVE_GETRLIMIT
233 res
= getrlimit (RLIMIT_NOFILE
, &limit
);
235 return limit
.rlim_cur
;
237 return getdtablesize ();
242 eg_getdtablesize (void)
244 g_error ("Should not be called");
249 g_spawn_command_line_sync (const gchar
*command_line
,
250 gchar
**standard_output
,
251 gchar
**standard_error
,
256 #elif !defined (HAVE_FORK) || !defined (HAVE_EXECV)
257 fprintf (stderr
, "g_spawn_command_line_sync not supported on this platform\n");
263 int stdout_pipe
[2] = { -1, -1 };
264 int stderr_pipe
[2] = { -1, -1 };
268 if (!g_shell_parse_argv (command_line
, &argc
, &argv
, error
))
271 if (standard_output
&& !create_pipe (stdout_pipe
, error
))
274 if (standard_error
&& !create_pipe (stderr_pipe
, error
)) {
275 if (standard_output
) {
276 CLOSE_PIPE (stdout_pipe
);
285 if (standard_output
) {
286 close (stdout_pipe
[0]);
287 dup2 (stdout_pipe
[1], STDOUT_FILENO
);
290 if (standard_error
) {
291 close (stderr_pipe
[0]);
292 dup2 (stderr_pipe
[1], STDERR_FILENO
);
294 for (i
= eg_getdtablesize () - 1; i
>= 3; i
--)
297 /* G_SPAWN_SEARCH_PATH is always enabled for g_spawn_command_line_sync */
298 if (!g_path_is_absolute (argv
[0])) {
301 arg0
= g_find_program_in_path (argv
[0]);
308 execv (argv
[0], argv
);
309 exit (1); /* TODO: What now? */
314 close (stdout_pipe
[1]);
317 close (stderr_pipe
[1]);
319 if (standard_output
|| standard_error
) {
320 res
= read_pipes (stdout_pipe
[0], standard_output
, stderr_pipe
[0], standard_error
, error
);
322 waitpid (pid
, &status
, WNOHANG
); /* avoid zombie */
327 NO_INTR (res
, waitpid (pid
, &status
, 0));
329 /* TODO: What if error? */
330 if (WIFEXITED (status
) && exit_status
) {
331 *exit_status
= WEXITSTATUS (status
);
338 * This is the only use we have in mono/metadata
339 !g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)
342 g_spawn_async_with_pipes (const gchar
*working_directory
,
346 GSpawnChildSetupFunc child_setup
,
349 gint
*standard_input
,
350 gint
*standard_output
,
351 gint
*standard_error
,
355 #elif !defined (HAVE_FORK) || !defined (HAVE_EXECVE)
356 fprintf (stderr
, "g_spawn_async_with_pipes is not supported on this platform\n");
361 int in_pipe
[2] = { -1, -1 };
362 int out_pipe
[2] = { -1, -1 };
363 int err_pipe
[2] = { -1, -1 };
366 g_return_val_if_fail (argv
!= NULL
, FALSE
); /* Only mandatory arg */
368 if (!create_pipe (info_pipe
, error
))
371 if (standard_output
&& !create_pipe (out_pipe
, error
)) {
372 CLOSE_PIPE (info_pipe
);
376 if (standard_error
&& !create_pipe (err_pipe
, error
)) {
377 CLOSE_PIPE (info_pipe
);
378 CLOSE_PIPE (out_pipe
);
382 if (standard_input
&& !create_pipe (in_pipe
, error
)) {
383 CLOSE_PIPE (info_pipe
);
384 CLOSE_PIPE (out_pipe
);
385 CLOSE_PIPE (err_pipe
);
391 CLOSE_PIPE (info_pipe
);
392 CLOSE_PIPE (out_pipe
);
393 CLOSE_PIPE (err_pipe
);
394 CLOSE_PIPE (in_pipe
);
395 set_error ("%s", "Error in fork ()");
400 /* No zombie left behind */
401 if ((flags
& G_SPAWN_DO_NOT_REAP_CHILD
) == 0) {
406 exit (pid
== -1 ? 1 : 0);
414 close (info_pipe
[0]);
416 close (out_pipe
[0]);
417 close (err_pipe
[0]);
419 /* when exec* succeeds, we want to close this fd, which will return
420 * a 0 read on the parent. We're not supposed to keep it open forever.
421 * If exec fails, we still can write the error to it before closing.
423 fcntl (info_pipe
[1], F_SETFD
, FD_CLOEXEC
);
425 if ((flags
& G_SPAWN_DO_NOT_REAP_CHILD
) == 0) {
427 NO_INTR (unused
, write_all (info_pipe
[1], &pid
, sizeof (pid_t
)));
430 if (working_directory
&& chdir (working_directory
) == -1) {
432 NO_INTR (unused
, write_all (info_pipe
[1], &err
, sizeof (int)));
436 if (standard_output
) {
437 dup2 (out_pipe
[1], STDOUT_FILENO
);
438 } else if ((flags
& G_SPAWN_STDOUT_TO_DEV_NULL
) != 0) {
439 fd
= open ("/dev/null", O_WRONLY
);
440 dup2 (fd
, STDOUT_FILENO
);
443 if (standard_error
) {
444 dup2 (err_pipe
[1], STDERR_FILENO
);
445 } else if ((flags
& G_SPAWN_STDERR_TO_DEV_NULL
) != 0) {
446 fd
= open ("/dev/null", O_WRONLY
);
447 dup2 (fd
, STDERR_FILENO
);
450 if (standard_input
) {
451 dup2 (in_pipe
[0], STDIN_FILENO
);
452 } else if ((flags
& G_SPAWN_CHILD_INHERITS_STDIN
) == 0) {
453 fd
= open ("/dev/null", O_RDONLY
);
454 dup2 (fd
, STDIN_FILENO
);
457 if ((flags
& G_SPAWN_LEAVE_DESCRIPTORS_OPEN
) != 0) {
458 for (i
= eg_getdtablesize () - 1; i
>= 3; i
--)
462 actual_args
= ((flags
& G_SPAWN_FILE_AND_ARGV_ZERO
) == 0) ? argv
: argv
+ 1;
467 child_setup (user_data
);
470 if (!g_path_is_absolute (arg0
) || (flags
& G_SPAWN_SEARCH_PATH
) != 0) {
471 arg0
= g_find_program_in_path (argv
[0]);
474 write_all (info_pipe
[1], &err
, sizeof (int));
479 execve (arg0
, actual_args
, envp
);
480 write_all (info_pipe
[1], &errno
, sizeof (int));
483 } else if ((flags
& G_SPAWN_DO_NOT_REAP_CHILD
) == 0) {
485 /* Wait for the first child if two are created */
486 NO_INTR (w
, waitpid (pid
, &status
, 0));
487 if (status
== 1 || w
== -1) {
488 CLOSE_PIPE (info_pipe
);
489 CLOSE_PIPE (out_pipe
);
490 CLOSE_PIPE (err_pipe
);
491 CLOSE_PIPE (in_pipe
);
492 set_error ("Error in fork (): %d", status
);
496 close (info_pipe
[1]);
498 close (out_pipe
[1]);
499 close (err_pipe
[1]);
501 if ((flags
& G_SPAWN_DO_NOT_REAP_CHILD
) == 0) {
503 NO_INTR (x
, read (info_pipe
[0], &pid
, sizeof (pid_t
))); /* if we read < sizeof (pid_t)... */
510 if (read (info_pipe
[0], &status
, sizeof (int)) != 0) {
511 close (info_pipe
[0]);
513 close (out_pipe
[1]);
514 close (err_pipe
[1]);
515 set_error_status (status
, "Error in exec (%d -> %s)", status
, strerror (status
));
519 close (info_pipe
[0]);
521 *standard_input
= in_pipe
[1];
523 *standard_output
= out_pipe
[0];
525 *standard_error
= err_pipe
[0];