diff --git a/src/shell-util.c b/src/shell-util.c index 00e3aa16c..311576c0e 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -17,9 +17,11 @@ #include #include "shell-app-cache-private.h" +#include "shell-global.h" #include "shell-util.h" #include #include +#include #include #include @@ -810,3 +812,175 @@ shell_util_get_translated_folder_name (const char *name) { return shell_app_cache_translate_folder (shell_app_cache_get_default (), name); } + +static void +spawn_child_setup (gpointer user_data) +{ + MetaContext *meta_context = user_data; + + /* No async-signal-unsafe code should be called here, so we don't either + * pass the GError to the function, as it may lead to dynamic allocations. + */ + meta_context_restore_rlimit_nofile (meta_context, NULL); +} + +/** + * shell_util_spawn_async_with_pipes_and_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; it must be non-empty and %NULL-terminated + * @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 + * @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` + * @source_fds: (array length=n_fds) (nullable): array of FDs from the parent + * process to make available in the child process + * @target_fds: (array length=n_fds) (nullable): array of FDs to remap + * @source_fds to in the child process + * @n_fds: number of FDs in @source_fds and @target_fds + * @stdin_pipe_out: (out) (optional): return location for file descriptor to write to child's stdin, or %NULL + * @stdout_pipe_out: (out) (optional): return location for file descriptor to read child's stdout, or %NULL + * @stderr_pipe_out: (out) (optional): return location for file descriptor to read child's stderr, or %NULL + * @error: return location for error + * + * A wrapper around g_spawn_async_with_pipes_and_fds() with async-signal-safe + * implementation of #GSpawnChildSetupFunc to launch a child program + * asynchronously resetting the rlimit nofile on child setup. + * + * Returns: the PID of the child on success, 0 if error is set + **/ +GPid +shell_util_spawn_async_with_pipes_and_fds (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int stdin_fd, + int stdout_fd, + int stderr_fd, + const int *source_fds, + const int *target_fds, + size_t n_fds, + int *stdin_pipe_out, + int *stdout_pipe_out, + int *stderr_pipe_out, + GError **error) +{ + ShellGlobal *shell_global = shell_global_get (); + GPid child_pid = 0; + + if (!g_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags, + spawn_child_setup, shell_global, + stdin_fd, stdout_fd, stderr_fd, + source_fds, target_fds, n_fds, + &child_pid, + stdin_pipe_out, stdout_pipe_out, stderr_pipe_out, + error)) + return 0; + + return child_pid; +} + +/** + * shell_util_spawn_async_with_pipes: + * @working_directory: (type filename) (nullable): child's current working + * directory, or %NULL to inherit parent's + * @argv: (array zero-terminated=1): + * child's argument vector + * @envp: (array zero-terminated=1) (nullable): + * child's environment, or %NULL to inherit parent's + * @flags: flags from #GSpawnFlags + * @standard_input: (out) (optional): return location for file descriptor to write to child's stdin, or %NULL + * @standard_output: (out) (optional): return location for file descriptor to read child's stdout, or %NULL + * @standard_error: (out) (optional): return location for file descriptor to read child's stderr, or %NULL + * @error: return location for error + * + * A wrapper around g_spawn_async_with_pipes() with async-signal-safe + * implementation of #GSpawnChildSetupFunc to launch a child program + * asynchronously resetting the rlimit nofile on child setup. + * + * Returns: the PID of the child on success, 0 if error is set + **/ +GPid +shell_util_spawn_async_with_pipes (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int *standard_input, + int *standard_output, + int *standard_error, + GError **error) +{ + return shell_util_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags, + -1, -1, -1, NULL, NULL, 0, + standard_input, standard_output, standard_error, + error); +} + +/** + * shell_util_spawn_async_with_fds: + * @working_directory: (type filename) (nullable): child's current working + * directory, or %NULL to inherit parent's + * @argv: (array zero-terminated=1): + * child's argument vector + * @envp: (array zero-terminated=1) (nullable): + * child's environment, or %NULL to inherit parent's + * @flags: flags from #GSpawnFlags + * @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 + * + * A wrapper around g_spawn_async_with_fds() with async-signal-safe + * implementation of #GSpawnChildSetupFunc to launch a child program + * asynchronously resetting the rlimit nofile on child setup. + * + * Returns: the PID of the child on success, 0 if error is set + **/ +GPid +shell_util_spawn_async_with_fds (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int stdin_fd, + int stdout_fd, + int stderr_fd, + GError **error) +{ + return shell_util_spawn_async_with_pipes_and_fds (working_directory, argv, envp, flags, + stdin_fd, stdout_fd, stderr_fd, + NULL, NULL, 0, + NULL, NULL, NULL, + error); +} + +/** + * shell_util_spawn_async: + * @working_directory: (type filename) (nullable): child's current working + * directory, or %NULL to inherit parent's + * @argv: (array zero-terminated=1): + * child's argument vector + * @envp: (array zero-terminated=1) (nullable): + * child's environment, or %NULL to inherit parent's + * @flags: flags from #GSpawnFlags + * @error: return location for error + * + * A wrapper around g_spawn_async() with async-signal-safe implementation of + * #GSpawnChildSetupFunc to launch a child program asynchronously resetting the + * rlimit nofile on child setup. + * + * Returns: the PID of the child on success, 0 if error is set + **/ +GPid +shell_util_spawn_async (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + GError **error) +{ + return shell_util_spawn_async_with_pipes (working_directory, argv, envp, + flags, NULL, NULL, NULL, error); +} diff --git a/src/shell-util.h b/src/shell-util.h index 03e75abec..8464fbc2b 100644 --- a/src/shell-util.h +++ b/src/shell-util.h @@ -80,6 +80,45 @@ char *shell_util_get_translated_folder_name (const char *name); gint shell_util_get_uid (void); +GPid shell_util_spawn_async_with_pipes_and_fds (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int stdin_fd, + int stdout_fd, + int stderr_fd, + const int *source_fds, + const int *target_fds, + size_t n_fds, + int *stdin_pipe_out, + int *stdout_pipe_out, + int *stderr_pipe_out, + GError **error); + +GPid shell_util_spawn_async_with_pipes (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int *standard_input, + int *standard_output, + int *standard_error, + GError **error); + +GPid shell_util_spawn_async_with_fds (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + int stdin_fd, + int stdout_fd, + int stderr_fd, + GError **error); + +GPid shell_util_spawn_async (const char *working_directory, + const char * const *argv, + const char * const *envp, + GSpawnFlags flags, + GError **error); + G_END_DECLS #endif /* __SHELL_UTIL_H__ */