mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
gspawn: Add g_spawn_async_with_fds variant
Add a new process spawning function variant which allows the caller to pass specific file descriptors for stdin, stdout and stderr. It is otherwise identical to g_spawn_async_with_pipes. Allow the same fd to be passed in multiple parameters. To make this workable, the child process logic that closes the fd after the first time it has been dup2'ed needed tweaking; we now just set those fds to be closed upon exec using the CLOEXEC flag. Add a test for this case. This will be used by gnome-shell to avoid performing equivalent dup2 actions in a child_setup function. Dropping use of child_setup will enable use of an upcoming optimized process spawning codepath.
This commit is contained in:
parent
c26558c1b1
commit
3524de16e4
@ -1232,6 +1232,7 @@ GSpawnError
|
||||
G_SPAWN_ERROR
|
||||
GSpawnFlags
|
||||
GSpawnChildSetupFunc
|
||||
g_spawn_async_with_fds
|
||||
g_spawn_async_with_pipes
|
||||
g_spawn_async
|
||||
g_spawn_sync
|
||||
|
@ -520,19 +520,19 @@ do_spawn_directly (gint *exit_status,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_spawn_with_pipes (gint *exit_status,
|
||||
gboolean do_return_handle,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
char **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
GPid *child_handle,
|
||||
gint *standard_input,
|
||||
gint *standard_output,
|
||||
gint *standard_error,
|
||||
gint *err_report,
|
||||
GError **error)
|
||||
do_spawn_with_fds (gint *exit_status,
|
||||
gboolean do_return_handle,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
char **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
GPid *child_handle,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
gint *err_report,
|
||||
GError **error)
|
||||
{
|
||||
char **protected_argv;
|
||||
char args[ARG_COUNT][10];
|
||||
@ -541,9 +541,6 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
gintptr rc = -1;
|
||||
int errsv;
|
||||
int argc;
|
||||
int stdin_pipe[2] = { -1, -1 };
|
||||
int stdout_pipe[2] = { -1, -1 };
|
||||
int stderr_pipe[2] = { -1, -1 };
|
||||
int child_err_report_pipe[2] = { -1, -1 };
|
||||
int helper_sync_pipe[2] = { -1, -1 };
|
||||
gintptr helper_report[2];
|
||||
@ -562,7 +559,7 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
|
||||
argc = protect_argv (argv, &protected_argv);
|
||||
|
||||
if (!standard_input && !standard_output && !standard_error &&
|
||||
if (stdin_fd == -1 && stdout_fd == -1 && stderr_fd == -1 &&
|
||||
(flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
|
||||
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
|
||||
!(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
|
||||
@ -578,15 +575,6 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (standard_input && !make_pipe (stdin_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_output && !make_pipe (stdout_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_error && !make_pipe (stderr_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (!make_pipe (child_err_report_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
@ -639,9 +627,9 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
*/
|
||||
helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
|
||||
|
||||
if (standard_input)
|
||||
if (stdin_fd != -1)
|
||||
{
|
||||
_g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
|
||||
_g_sprintf (args[ARG_STDIN], "%d", stdin_fd);
|
||||
new_argv[ARG_STDIN] = args[ARG_STDIN];
|
||||
}
|
||||
else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
|
||||
@ -655,9 +643,9 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
new_argv[ARG_STDIN] = "z";
|
||||
}
|
||||
|
||||
if (standard_output)
|
||||
if (stdout_fd != -1)
|
||||
{
|
||||
_g_sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
|
||||
_g_sprintf (args[ARG_STDOUT], "%d", stdout_fd);
|
||||
new_argv[ARG_STDOUT] = args[ARG_STDOUT];
|
||||
}
|
||||
else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
|
||||
@ -669,9 +657,9 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
new_argv[ARG_STDOUT] = "-";
|
||||
}
|
||||
|
||||
if (standard_error)
|
||||
if (stdout_fd != -1)
|
||||
{
|
||||
_g_sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
|
||||
_g_sprintf (args[ARG_STDERR], "%d", stderr_fd);
|
||||
new_argv[ARG_STDERR] = args[ARG_STDERR];
|
||||
}
|
||||
else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
|
||||
@ -770,9 +758,6 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
*/
|
||||
close_and_invalidate (&child_err_report_pipe[1]);
|
||||
close_and_invalidate (&helper_sync_pipe[0]);
|
||||
close_and_invalidate (&stdin_pipe[0]);
|
||||
close_and_invalidate (&stdout_pipe[1]);
|
||||
close_and_invalidate (&stderr_pipe[1]);
|
||||
|
||||
g_strfreev (protected_argv);
|
||||
|
||||
@ -842,12 +827,6 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
|
||||
/* Success against all odds! return the information */
|
||||
|
||||
if (standard_input)
|
||||
*standard_input = stdin_pipe[1];
|
||||
if (standard_output)
|
||||
*standard_output = stdout_pipe[0];
|
||||
if (standard_error)
|
||||
*standard_error = stderr_pipe[0];
|
||||
if (rc != -1)
|
||||
CloseHandle ((HANDLE) rc);
|
||||
|
||||
@ -865,6 +844,71 @@ do_spawn_with_pipes (gint *exit_status,
|
||||
close (helper_sync_pipe[0]);
|
||||
if (helper_sync_pipe[1] != -1)
|
||||
close (helper_sync_pipe[1]);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_spawn_with_pipes (gint *exit_status,
|
||||
gboolean do_return_handle,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
char **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
GPid *child_handle,
|
||||
gint *standard_input,
|
||||
gint *standard_output,
|
||||
gint *standard_error,
|
||||
gint *err_report,
|
||||
GError **error)
|
||||
{
|
||||
int stdin_pipe[2] = { -1, -1 };
|
||||
int stdout_pipe[2] = { -1, -1 };
|
||||
int stderr_pipe[2] = { -1, -1 };
|
||||
|
||||
if (standard_input && !make_pipe (stdin_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_output && !make_pipe (stdout_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_error && !make_pipe (stderr_pipe, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (!do_spawn_with_fds (exit_status,
|
||||
do_return_handle,
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
flags,
|
||||
child_setup,
|
||||
child_handle,
|
||||
stdin_pipe[0],
|
||||
stdout_pipe[1],
|
||||
stderr_pipe[1],
|
||||
err_report,
|
||||
error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
/* Close the other process's ends of the pipes in this process,
|
||||
* otherwise the reader will never get EOF.
|
||||
*/
|
||||
close_and_invalidate (&stdin_pipe[0]);
|
||||
close_and_invalidate (&stdout_pipe[1]);
|
||||
close_and_invalidate (&stderr_pipe[1]);
|
||||
|
||||
if (standard_input)
|
||||
*standard_input = stdin_pipe[1];
|
||||
if (standard_output)
|
||||
*standard_output = stdout_pipe[0];
|
||||
if (standard_error)
|
||||
*standard_error = stderr_pipe[0];
|
||||
|
||||
return TRUE;
|
||||
|
||||
cleanup_and_fail:
|
||||
|
||||
if (stdin_pipe[0] != -1)
|
||||
close (stdin_pipe[0]);
|
||||
if (stdin_pipe[1] != -1)
|
||||
@ -1160,6 +1204,43 @@ g_spawn_async_with_pipes (const gchar *working_directory,
|
||||
error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_spawn_async_with_fds (const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_handle,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (argv != NULL, FALSE);
|
||||
g_return_val_if_fail (stdin_fd == -1 ||
|
||||
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
|
||||
g_return_val_if_fail (stderr_fd == -1 ||
|
||||
!(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
|
||||
/* can't inherit stdin if we have an input pipe. */
|
||||
g_return_val_if_fail (stdin_fd == -1 ||
|
||||
!(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
|
||||
|
||||
return do_spawn_with_fds (NULL,
|
||||
(flags & G_SPAWN_DO_NOT_REAP_CHILD),
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
flags,
|
||||
child_setup,
|
||||
child_handle,
|
||||
stdin_fd,
|
||||
stdout_fd,
|
||||
stderr_fd,
|
||||
NULL,
|
||||
error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_spawn_command_line_sync (const gchar *command_line,
|
||||
gchar **standard_output,
|
||||
|
279
glib/gspawn.c
279
glib/gspawn.c
@ -142,6 +142,27 @@ static gboolean fork_exec_with_pipes (gboolean intermediate_child,
|
||||
gint *standard_error,
|
||||
GError **error);
|
||||
|
||||
static gboolean fork_exec_with_fds (gboolean intermediate_child,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
gboolean close_descriptors,
|
||||
gboolean search_path,
|
||||
gboolean search_path_from_envp,
|
||||
gboolean stdout_to_null,
|
||||
gboolean stderr_to_null,
|
||||
gboolean child_inherits_stdin,
|
||||
gboolean file_and_argv_zero,
|
||||
gboolean cloexec_pipes,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint *child_close_fds,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_QUARK (g-exec-error-quark, g_spawn_error)
|
||||
G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
|
||||
|
||||
@ -728,6 +749,87 @@ g_spawn_async_with_pipes (const gchar *working_directory,
|
||||
error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_spawn_async_with_fds:
|
||||
* @working_directory: (type filename) (nullable): child's current working directory, or %NULL to inherit parent's, in the GLib file name encoding
|
||||
* @argv: (array zero-terminated=1): child's argument vector, in the GLib file name encoding
|
||||
* @envp: (array zero-terminated=1) (nullable): child's environment, or %NULL to inherit parent's, in the GLib file name encoding
|
||||
* @flags: flags from #GSpawnFlags
|
||||
* @child_setup: (scope async) (nullable): function to run in the child just before exec()
|
||||
* @user_data: (closure): user data for @child_setup
|
||||
* @child_pid: (out) (optional): return location for child process ID, or %NULL
|
||||
* @stdin_fd: file descriptor to use for child's stdin, or -1
|
||||
* @stdout_fd: file descriptor to use for child's stdout, or -1
|
||||
* @stderr_fd: file descriptor to use for child's stderr, or -1
|
||||
* @error: return location for error
|
||||
*
|
||||
* Identical to g_spawn_async_with_pipes() but instead of
|
||||
* creating pipes for the stdin/stdout/stderr, you can pass existing
|
||||
* file descriptors into this function through the @stdin_fd,
|
||||
* @stdout_fd and @stderr_fd parameters. The following @flags
|
||||
* also have their behaviour slightly tweaked as a result:
|
||||
*
|
||||
* %G_SPAWN_STDOUT_TO_DEV_NULL means that the child's standard output
|
||||
* will be discarded, instead of going to the same location as the parent's
|
||||
* standard output. If you use this flag, @standard_output must be -1.
|
||||
* %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error
|
||||
* will be discarded, instead of going to the same location as the parent's
|
||||
* standard error. If you use this flag, @standard_error must be -1.
|
||||
* %G_SPAWN_CHILD_INHERITS_STDIN means that the child will inherit the parent's
|
||||
* standard input (by default, the child's standard input is attached to
|
||||
* /dev/null). If you use this flag, @standard_input must be -1.
|
||||
*
|
||||
* It is valid to pass the same fd in multiple parameters (e.g. you can pass
|
||||
* a single fd for both stdout and stderr).
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE if an error was set
|
||||
*
|
||||
* Since: 2.58
|
||||
*/
|
||||
gboolean
|
||||
g_spawn_async_with_fds (const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (argv != NULL, FALSE);
|
||||
g_return_val_if_fail (stdout_fd == -1 ||
|
||||
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
|
||||
g_return_val_if_fail (stderr_fd == -1 ||
|
||||
!(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
|
||||
/* can't inherit stdin if we have an input pipe. */
|
||||
g_return_val_if_fail (stdin_fd == -1 ||
|
||||
!(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
|
||||
|
||||
return fork_exec_with_fds (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
|
||||
(flags & G_SPAWN_SEARCH_PATH) != 0,
|
||||
(flags & G_SPAWN_SEARCH_PATH_FROM_ENVP) != 0,
|
||||
(flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
|
||||
(flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
|
||||
(flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
|
||||
(flags & G_SPAWN_FILE_AND_ARGV_ZERO) != 0,
|
||||
(flags & G_SPAWN_CLOEXEC_PIPES) != 0,
|
||||
child_setup,
|
||||
user_data,
|
||||
child_pid,
|
||||
NULL,
|
||||
stdin_fd,
|
||||
stdout_fd,
|
||||
stderr_fd,
|
||||
error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_spawn_command_line_sync:
|
||||
* @command_line: (type filename): a command line
|
||||
@ -1118,8 +1220,7 @@ do_exec (gint child_err_report_fd,
|
||||
write_err_and_exit (child_err_report_fd,
|
||||
CHILD_DUP2_FAILED);
|
||||
|
||||
/* ignore this if it doesn't work */
|
||||
close_and_invalidate (&stdin_fd);
|
||||
set_cloexec (GINT_TO_POINTER(0), stdin_fd);
|
||||
}
|
||||
else if (!child_inherits_stdin)
|
||||
{
|
||||
@ -1138,8 +1239,7 @@ do_exec (gint child_err_report_fd,
|
||||
write_err_and_exit (child_err_report_fd,
|
||||
CHILD_DUP2_FAILED);
|
||||
|
||||
/* ignore this if it doesn't work */
|
||||
close_and_invalidate (&stdout_fd);
|
||||
set_cloexec (GINT_TO_POINTER(0), stdout_fd);
|
||||
}
|
||||
else if (stdout_to_null)
|
||||
{
|
||||
@ -1157,8 +1257,7 @@ do_exec (gint child_err_report_fd,
|
||||
write_err_and_exit (child_err_report_fd,
|
||||
CHILD_DUP2_FAILED);
|
||||
|
||||
/* ignore this if it doesn't work */
|
||||
close_and_invalidate (&stderr_fd);
|
||||
set_cloexec (GINT_TO_POINTER(0), stderr_fd);
|
||||
}
|
||||
else if (stderr_to_null)
|
||||
{
|
||||
@ -1232,30 +1331,28 @@ read_ints (int fd,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fork_exec_with_pipes (gboolean intermediate_child,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
gboolean close_descriptors,
|
||||
gboolean search_path,
|
||||
gboolean search_path_from_envp,
|
||||
gboolean stdout_to_null,
|
||||
gboolean stderr_to_null,
|
||||
gboolean child_inherits_stdin,
|
||||
gboolean file_and_argv_zero,
|
||||
gboolean cloexec_pipes,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint *standard_input,
|
||||
gint *standard_output,
|
||||
gint *standard_error,
|
||||
GError **error)
|
||||
fork_exec_with_fds (gboolean intermediate_child,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
gboolean close_descriptors,
|
||||
gboolean search_path,
|
||||
gboolean search_path_from_envp,
|
||||
gboolean stdout_to_null,
|
||||
gboolean stderr_to_null,
|
||||
gboolean child_inherits_stdin,
|
||||
gboolean file_and_argv_zero,
|
||||
gboolean cloexec_pipes,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint *child_close_fds,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
GError **error)
|
||||
{
|
||||
GPid pid = -1;
|
||||
gint stdin_pipe[2] = { -1, -1 };
|
||||
gint stdout_pipe[2] = { -1, -1 };
|
||||
gint stderr_pipe[2] = { -1, -1 };
|
||||
gint child_err_report_pipe[2] = { -1, -1 };
|
||||
gint child_pid_report_pipe[2] = { -1, -1 };
|
||||
guint pipe_flags = cloexec_pipes ? FD_CLOEXEC : 0;
|
||||
@ -1267,15 +1364,6 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
if (intermediate_child && !g_unix_open_pipe (child_pid_report_pipe, pipe_flags, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_input && !g_unix_open_pipe (stdin_pipe, pipe_flags, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_output && !g_unix_open_pipe (stdout_pipe, pipe_flags, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_error && !g_unix_open_pipe (stderr_pipe, FD_CLOEXEC, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
pid = fork ();
|
||||
|
||||
if (pid < 0)
|
||||
@ -1313,9 +1401,12 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
*/
|
||||
close_and_invalidate (&child_err_report_pipe[0]);
|
||||
close_and_invalidate (&child_pid_report_pipe[0]);
|
||||
close_and_invalidate (&stdin_pipe[1]);
|
||||
close_and_invalidate (&stdout_pipe[0]);
|
||||
close_and_invalidate (&stderr_pipe[0]);
|
||||
if (child_close_fds != NULL)
|
||||
{
|
||||
int i = -1;
|
||||
while (child_close_fds[++i] != -1)
|
||||
close_and_invalidate (&child_close_fds[i]);
|
||||
}
|
||||
|
||||
if (intermediate_child)
|
||||
{
|
||||
@ -1341,9 +1432,9 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
{
|
||||
close_and_invalidate (&child_pid_report_pipe[1]);
|
||||
do_exec (child_err_report_pipe[1],
|
||||
stdin_pipe[0],
|
||||
stdout_pipe[1],
|
||||
stderr_pipe[1],
|
||||
stdin_fd,
|
||||
stdout_fd,
|
||||
stderr_fd,
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
@ -1371,9 +1462,9 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
*/
|
||||
|
||||
do_exec (child_err_report_pipe[1],
|
||||
stdin_pipe[0],
|
||||
stdout_pipe[1],
|
||||
stderr_pipe[1],
|
||||
stdin_fd,
|
||||
stdout_fd,
|
||||
stderr_fd,
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
@ -1398,9 +1489,6 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
/* Close the uncared-about ends of the pipes */
|
||||
close_and_invalidate (&child_err_report_pipe[1]);
|
||||
close_and_invalidate (&child_pid_report_pipe[1]);
|
||||
close_and_invalidate (&stdin_pipe[0]);
|
||||
close_and_invalidate (&stdout_pipe[1]);
|
||||
close_and_invalidate (&stderr_pipe[1]);
|
||||
|
||||
/* If we had an intermediate child, reap it */
|
||||
if (intermediate_child)
|
||||
@ -1513,13 +1601,6 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
if (child_pid)
|
||||
*child_pid = pid;
|
||||
|
||||
if (standard_input)
|
||||
*standard_input = stdin_pipe[1];
|
||||
if (standard_output)
|
||||
*standard_output = stdout_pipe[0];
|
||||
if (standard_error)
|
||||
*standard_error = stderr_pipe[0];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -1548,6 +1629,92 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
||||
close_and_invalidate (&child_err_report_pipe[1]);
|
||||
close_and_invalidate (&child_pid_report_pipe[0]);
|
||||
close_and_invalidate (&child_pid_report_pipe[1]);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fork_exec_with_pipes (gboolean intermediate_child,
|
||||
const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
gboolean close_descriptors,
|
||||
gboolean search_path,
|
||||
gboolean search_path_from_envp,
|
||||
gboolean stdout_to_null,
|
||||
gboolean stderr_to_null,
|
||||
gboolean child_inherits_stdin,
|
||||
gboolean file_and_argv_zero,
|
||||
gboolean cloexec_pipes,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint *standard_input,
|
||||
gint *standard_output,
|
||||
gint *standard_error,
|
||||
GError **error)
|
||||
{
|
||||
guint pipe_flags = cloexec_pipes ? FD_CLOEXEC : 0;
|
||||
gint stdin_pipe[2] = { -1, -1 };
|
||||
gint stdout_pipe[2] = { -1, -1 };
|
||||
gint stderr_pipe[2] = { -1, -1 };
|
||||
gint child_close_fds[4];
|
||||
gboolean ret;
|
||||
|
||||
if (standard_input && !g_unix_open_pipe (stdin_pipe, pipe_flags, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_output && !g_unix_open_pipe (stdout_pipe, pipe_flags, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
if (standard_error && !g_unix_open_pipe (stderr_pipe, FD_CLOEXEC, error))
|
||||
goto cleanup_and_fail;
|
||||
|
||||
child_close_fds[0] = stdin_pipe[1];
|
||||
child_close_fds[1] = stdout_pipe[0];
|
||||
child_close_fds[2] = stderr_pipe[0];
|
||||
child_close_fds[3] = -1;
|
||||
|
||||
ret = fork_exec_with_fds (intermediate_child,
|
||||
working_directory,
|
||||
argv,
|
||||
envp,
|
||||
close_descriptors,
|
||||
search_path,
|
||||
search_path_from_envp,
|
||||
stdout_to_null,
|
||||
stderr_to_null,
|
||||
child_inherits_stdin,
|
||||
file_and_argv_zero,
|
||||
pipe_flags,
|
||||
child_setup,
|
||||
user_data,
|
||||
child_pid,
|
||||
child_close_fds,
|
||||
stdin_pipe[0],
|
||||
stdout_pipe[1],
|
||||
stderr_pipe[1],
|
||||
error);
|
||||
if (!ret)
|
||||
goto cleanup_and_fail;
|
||||
|
||||
/* Close the uncared-about ends of the pipes */
|
||||
close_and_invalidate (&stdin_pipe[0]);
|
||||
close_and_invalidate (&stdout_pipe[1]);
|
||||
close_and_invalidate (&stderr_pipe[1]);
|
||||
|
||||
if (standard_input)
|
||||
*standard_input = stdin_pipe[1];
|
||||
|
||||
if (standard_output)
|
||||
*standard_output = stdout_pipe[0];
|
||||
|
||||
if (standard_error)
|
||||
*standard_error = stderr_pipe[0];
|
||||
|
||||
return TRUE;
|
||||
|
||||
cleanup_and_fail:
|
||||
close_and_invalidate (&stdin_pipe[0]);
|
||||
close_and_invalidate (&stdin_pipe[1]);
|
||||
close_and_invalidate (&stdout_pipe[0]);
|
||||
|
@ -215,6 +215,19 @@ gboolean g_spawn_async_with_pipes (const gchar *working_directory,
|
||||
gint *standard_error,
|
||||
GError **error);
|
||||
|
||||
/* Lets you provide fds for stdin/stdout/stderr */
|
||||
GLIB_AVAILABLE_IN_2_58
|
||||
gboolean g_spawn_async_with_fds (const gchar *working_directory,
|
||||
gchar **argv,
|
||||
gchar **envp,
|
||||
GSpawnFlags flags,
|
||||
GSpawnChildSetupFunc child_setup,
|
||||
gpointer user_data,
|
||||
GPid *child_pid,
|
||||
gint stdin_fd,
|
||||
gint stdout_fd,
|
||||
gint stderr_fd,
|
||||
GError **error);
|
||||
|
||||
/* If standard_output or standard_error are non-NULL, the full
|
||||
* standard output or error of the command will be placed there.
|
||||
|
@ -25,8 +25,14 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <glib-unix.h>
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <io.h>
|
||||
#define LINEEND "\r\n"
|
||||
#else
|
||||
#define LINEEND "\n"
|
||||
@ -156,6 +162,145 @@ test_spawn_async (void)
|
||||
g_free (arg);
|
||||
}
|
||||
|
||||
/* Windows close() causes failure through the Invalid Parameter Handler
|
||||
* Routine if the file descriptor does not exist.
|
||||
*/
|
||||
static void
|
||||
sane_close (int fd)
|
||||
{
|
||||
if (fd >= 0)
|
||||
close (fd);
|
||||
}
|
||||
|
||||
/* Test g_spawn_async_with_fds() with a variety of different inputs */
|
||||
static void
|
||||
test_spawn_async_with_fds (void)
|
||||
{
|
||||
int tnum = 1;
|
||||
GPtrArray *argv;
|
||||
char *arg;
|
||||
int i;
|
||||
|
||||
/* Each test has 3 variable parameters: stdin, stdout, stderr */
|
||||
enum fd_type {
|
||||
NO_FD, /* don't pass a fd */
|
||||
PIPE, /* pass fd of new/unique pipe */
|
||||
STDOUT_PIPE, /* pass the same pipe as stdout */
|
||||
} tests[][3] = {
|
||||
{ NO_FD, NO_FD, NO_FD }, /* Test with no fds passed */
|
||||
{ PIPE, PIPE, PIPE }, /* Test with unique fds passed */
|
||||
{ NO_FD, PIPE, STDOUT_PIPE }, /* Test the same fd for stdout + stderr */
|
||||
};
|
||||
|
||||
arg = g_strdup_printf ("thread %d", tnum);
|
||||
|
||||
argv = g_ptr_array_new ();
|
||||
g_ptr_array_add (argv, echo_prog_path);
|
||||
g_ptr_array_add (argv, arg);
|
||||
g_ptr_array_add (argv, NULL);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GPid pid;
|
||||
GMainContext *context;
|
||||
GMainLoop *loop;
|
||||
GIOChannel *channel = NULL;
|
||||
GSource *source;
|
||||
SpawnAsyncMultithreadedData data;
|
||||
enum fd_type *fd_info = tests[i];
|
||||
gint test_pipe[3][2];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
switch (fd_info[j])
|
||||
{
|
||||
case NO_FD:
|
||||
test_pipe[j][0] = -1;
|
||||
test_pipe[j][1] = -1;
|
||||
break;
|
||||
case PIPE:
|
||||
#ifdef G_OS_UNIX
|
||||
g_unix_open_pipe (test_pipe[j], FD_CLOEXEC, &error);
|
||||
g_assert_no_error (error);
|
||||
#else
|
||||
g_assert_cmpint (_pipe (test_pipe[j], 4096, _O_BINARY), >=, 0);
|
||||
#endif
|
||||
break;
|
||||
case STDOUT_PIPE:
|
||||
g_assert_cmpint (j, ==, 2); /* only works for stderr */
|
||||
test_pipe[j][0] = test_pipe[1][0];
|
||||
test_pipe[j][1] = test_pipe[1][1];
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
context = g_main_context_new ();
|
||||
loop = g_main_loop_new (context, TRUE);
|
||||
|
||||
g_spawn_async_with_fds (NULL, (char**)argv->pdata, NULL,
|
||||
G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid,
|
||||
test_pipe[0][0], test_pipe[1][1], test_pipe[2][1],
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
sane_close (test_pipe[0][0]);
|
||||
sane_close (test_pipe[1][1]);
|
||||
if (fd_info[2] != STDOUT_PIPE)
|
||||
sane_close (test_pipe[2][1]);
|
||||
|
||||
data.loop = loop;
|
||||
data.stdout_done = FALSE;
|
||||
data.child_exited = FALSE;
|
||||
data.stdout_buf = g_string_new (0);
|
||||
|
||||
source = g_child_watch_source_new (pid);
|
||||
g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
|
||||
g_source_attach (source, context);
|
||||
g_source_unref (source);
|
||||
|
||||
if (test_pipe[1][0] != -1)
|
||||
{
|
||||
channel = g_io_channel_unix_new (test_pipe[1][0]);
|
||||
source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
|
||||
g_source_set_callback (source, (GSourceFunc)on_child_stdout,
|
||||
&data, NULL);
|
||||
g_source_attach (source, context);
|
||||
g_source_unref (source);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't check stdout data if we didn't pass a fd */
|
||||
data.stdout_done = TRUE;
|
||||
}
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_true (data.child_exited);
|
||||
|
||||
if (test_pipe[1][0] != -1)
|
||||
{
|
||||
/* Check for echo on stdout */
|
||||
g_assert_true (data.stdout_done);
|
||||
g_assert_cmpstr (data.stdout_buf->str, ==, arg);
|
||||
g_io_channel_unref (channel);
|
||||
}
|
||||
g_string_free (data.stdout_buf, TRUE);
|
||||
|
||||
g_main_context_unref (context);
|
||||
g_main_loop_unref (loop);
|
||||
sane_close (test_pipe[0][1]);
|
||||
sane_close (test_pipe[1][0]);
|
||||
if (fd_info[2] != STDOUT_PIPE)
|
||||
sane_close (test_pipe[2][0]);
|
||||
}
|
||||
|
||||
g_ptr_array_free (argv, TRUE);
|
||||
g_free (arg);
|
||||
}
|
||||
|
||||
static void
|
||||
test_spawn_sync (void)
|
||||
{
|
||||
@ -251,6 +396,7 @@ main (int argc,
|
||||
|
||||
g_test_add_func ("/gthread/spawn-single-sync", test_spawn_sync);
|
||||
g_test_add_func ("/gthread/spawn-single-async", test_spawn_async);
|
||||
g_test_add_func ("/gthread/spawn-single-async-with-fds", test_spawn_async_with_fds);
|
||||
g_test_add_func ("/gthread/spawn-script", test_spawn_script);
|
||||
g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user