shell-util: Add async-signal-safe wrappers for GLib.spawn_async implementations
In the shell code we often use GLib.spawn_async to launch processes with a GSpawnChildSetupFunc implementation in JavaScript to reset the mutter nofile rlimit in the new child process. However, this is highly unsafe to do because this implies that the child setup function code is executed in gjs where a lot of allocations are done and even more not-async-signal-safe code is executed, in fact leading to dead-locks as reported in the past. To prevent this, declare a new functions that do the same of the GLib counterpart but without providing a GSpawnChildSetupFunc that is instead implemented in the C-side doing the cleanup that mutter requires without allocations or async-signal-unsafe code. Helps: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6698 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3293>
This commit is contained in:
parent
dcb5956dea
commit
781010be66
174
src/shell-util.c
174
src/shell-util.c
@ -17,9 +17,11 @@
|
||||
#include <cogl/cogl.h>
|
||||
|
||||
#include "shell-app-cache-private.h"
|
||||
#include "shell-global.h"
|
||||
#include "shell-util.h"
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <meta/meta-context.h>
|
||||
#include <meta/display.h>
|
||||
#include <meta/meta-x11-display.h>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user