mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-04 18:26:19 +01:00
Merge branch 'master' into 'master'
Use posix_spawn for optimized process launching See merge request GNOME/glib!95
This commit is contained in:
commit
ca98ce4280
@ -480,7 +480,7 @@ AM_CONDITIONAL(OS_WIN32_AND_DLL_COMPILATION, [test x$glib_native_win32 = xyes -a
|
|||||||
# Checks for library functions.
|
# Checks for library functions.
|
||||||
AC_FUNC_ALLOCA
|
AC_FUNC_ALLOCA
|
||||||
AC_CHECK_FUNCS(mmap posix_memalign memalign valloc fsync pipe2 issetugid)
|
AC_CHECK_FUNCS(mmap posix_memalign memalign valloc fsync pipe2 issetugid)
|
||||||
AC_CHECK_FUNCS(timegm gmtime_r)
|
AC_CHECK_FUNCS(timegm gmtime_r posix_spawn)
|
||||||
AC_FUNC_STRERROR_R()
|
AC_FUNC_STRERROR_R()
|
||||||
|
|
||||||
AC_CHECK_SIZEOF(char)
|
AC_CHECK_SIZEOF(char)
|
||||||
|
@ -1624,6 +1624,7 @@ g_desktop_app_info_get_boolean
|
|||||||
g_desktop_app_info_has_key
|
g_desktop_app_info_has_key
|
||||||
GDesktopAppLaunchCallback
|
GDesktopAppLaunchCallback
|
||||||
g_desktop_app_info_launch_uris_as_manager
|
g_desktop_app_info_launch_uris_as_manager
|
||||||
|
g_desktop_app_info_launch_uris_as_manager_with_fds
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
g_desktop_app_info_list_actions
|
g_desktop_app_info_list_actions
|
||||||
g_desktop_app_info_get_action_name
|
g_desktop_app_info_get_action_name
|
||||||
|
@ -1233,6 +1233,7 @@ GSpawnError
|
|||||||
G_SPAWN_ERROR
|
G_SPAWN_ERROR
|
||||||
GSpawnFlags
|
GSpawnFlags
|
||||||
GSpawnChildSetupFunc
|
GSpawnChildSetupFunc
|
||||||
|
g_spawn_async_with_fds
|
||||||
g_spawn_async_with_pipes
|
g_spawn_async_with_pipes
|
||||||
g_spawn_async
|
g_spawn_async
|
||||||
g_spawn_sync
|
g_spawn_sync
|
||||||
|
@ -819,7 +819,7 @@ gio.def: libgio-2.0.la
|
|||||||
gio-2.0.lib: libgio-2.0.la gio.def
|
gio-2.0.lib: libgio-2.0.la gio.def
|
||||||
$(AM_V_GEN) lib.exe -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgio-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:$(builddir)/gio.def -out:$@
|
$(AM_V_GEN) lib.exe -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgio-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:$(builddir)/gio.def -out:$@
|
||||||
|
|
||||||
bin_PROGRAMS = gio-querymodules glib-compile-schemas glib-compile-resources gsettings
|
bin_PROGRAMS = gio-querymodules glib-compile-schemas glib-compile-resources gsettings gio-launch-desktop
|
||||||
|
|
||||||
glib_compile_resources_LDADD = libgio-2.0.la \
|
glib_compile_resources_LDADD = libgio-2.0.la \
|
||||||
$(top_builddir)/gobject/libgobject-2.0.la \
|
$(top_builddir)/gobject/libgobject-2.0.la \
|
||||||
@ -840,6 +840,8 @@ gio_querymodules_LDADD = libgio-2.0.la \
|
|||||||
$(top_builddir)/glib/libglib-2.0.la \
|
$(top_builddir)/glib/libglib-2.0.la \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
gio_launch_desktop_SOURCES = gio-launch-desktop.c
|
||||||
|
|
||||||
gconstructor_as_data.h: $(top_srcdir)/glib/gconstructor.h data-to-c.py
|
gconstructor_as_data.h: $(top_srcdir)/glib/gconstructor.h data-to-c.py
|
||||||
$(AM_V_GEN) $(srcdir)/data-to-c.py $(top_srcdir)/glib/gconstructor.h gconstructor_code $@
|
$(AM_V_GEN) $(srcdir)/data-to-c.py $(top_srcdir)/glib/gconstructor.h gconstructor_code $@
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ static guint n_desktop_file_dirs;
|
|||||||
static const guint desktop_file_dir_user_config_index = 0;
|
static const guint desktop_file_dir_user_config_index = 0;
|
||||||
static guint desktop_file_dir_user_data_index;
|
static guint desktop_file_dir_user_data_index;
|
||||||
static GMutex desktop_file_dir_lock;
|
static GMutex desktop_file_dir_lock;
|
||||||
|
static const gchar *gio_launch_desktop_path = NULL;
|
||||||
|
|
||||||
/* Monitor 'changed' signal handler {{{2 */
|
/* Monitor 'changed' signal handler {{{2 */
|
||||||
static void desktop_file_dir_reset (DesktopFileDir *dir);
|
static void desktop_file_dir_reset (DesktopFileDir *dir);
|
||||||
@ -2562,41 +2563,6 @@ create_files_for_uris (GList *uris)
|
|||||||
return g_list_reverse (res);
|
return g_list_reverse (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
GSpawnChildSetupFunc user_setup;
|
|
||||||
gpointer user_setup_data;
|
|
||||||
|
|
||||||
char *pid_envvar;
|
|
||||||
} ChildSetupData;
|
|
||||||
|
|
||||||
static void
|
|
||||||
child_setup (gpointer user_data)
|
|
||||||
{
|
|
||||||
ChildSetupData *data = user_data;
|
|
||||||
|
|
||||||
if (data->pid_envvar)
|
|
||||||
{
|
|
||||||
pid_t pid = getpid ();
|
|
||||||
char buf[20];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Write the pid into the space already reserved for it in the
|
|
||||||
* environment array. We can't use sprintf because it might
|
|
||||||
* malloc, so we do it by hand. It's simplest to write the pid
|
|
||||||
* out backwards first, then copy it over.
|
|
||||||
*/
|
|
||||||
for (i = 0; pid; i++, pid /= 10)
|
|
||||||
buf[i] = (pid % 10) + '0';
|
|
||||||
for (i--; i >= 0; i--)
|
|
||||||
*(data->pid_envvar++) = buf[i];
|
|
||||||
*data->pid_envvar = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->user_setup)
|
|
||||||
data->user_setup (data->user_setup_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
notify_desktop_launch (GDBusConnection *session_bus,
|
notify_desktop_launch (GDBusConnection *session_bus,
|
||||||
GDesktopAppInfo *info,
|
GDesktopAppInfo *info,
|
||||||
@ -2675,6 +2641,9 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
gpointer user_setup_data,
|
gpointer user_setup_data,
|
||||||
GDesktopAppLaunchCallback pid_callback,
|
GDesktopAppLaunchCallback pid_callback,
|
||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
|
gint stdin_fd,
|
||||||
|
gint stdout_fd,
|
||||||
|
gint stderr_fd,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
gboolean completed = FALSE;
|
gboolean completed = FALSE;
|
||||||
@ -2683,7 +2652,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
|
|
||||||
char **argv, **envp;
|
char **argv, **envp;
|
||||||
int argc;
|
int argc;
|
||||||
ChildSetupData data;
|
|
||||||
|
|
||||||
g_return_val_if_fail (info != NULL, FALSE);
|
g_return_val_if_fail (info != NULL, FALSE);
|
||||||
|
|
||||||
@ -2705,6 +2673,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
GList *launched_uris;
|
GList *launched_uris;
|
||||||
GList *iter;
|
GList *iter;
|
||||||
char *sn_id = NULL;
|
char *sn_id = NULL;
|
||||||
|
char **wrapped_argv;
|
||||||
|
int i;
|
||||||
|
|
||||||
old_uris = dup_uris;
|
old_uris = dup_uris;
|
||||||
if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
|
if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
|
||||||
@ -2723,25 +2693,11 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.user_setup = user_setup;
|
|
||||||
data.user_setup_data = user_setup_data;
|
|
||||||
|
|
||||||
if (info->filename)
|
if (info->filename)
|
||||||
{
|
envp = g_environ_setenv (envp,
|
||||||
envp = g_environ_setenv (envp,
|
"GIO_LAUNCHED_DESKTOP_FILE",
|
||||||
"GIO_LAUNCHED_DESKTOP_FILE",
|
info->filename,
|
||||||
info->filename,
|
TRUE);
|
||||||
TRUE);
|
|
||||||
envp = g_environ_setenv (envp,
|
|
||||||
"GIO_LAUNCHED_DESKTOP_FILE_PID",
|
|
||||||
"XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
|
|
||||||
TRUE);
|
|
||||||
data.pid_envvar = (char *)g_environ_getenv (envp, "GIO_LAUNCHED_DESKTOP_FILE_PID");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.pid_envvar = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sn_id = NULL;
|
sn_id = NULL;
|
||||||
if (launch_context)
|
if (launch_context)
|
||||||
@ -2760,14 +2716,40 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
g_list_free_full (launched_files, g_object_unref);
|
g_list_free_full (launched_files, g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_spawn_async (info->path,
|
if (g_once_init_enter (&gio_launch_desktop_path))
|
||||||
argv,
|
{
|
||||||
envp,
|
const gchar *tmp;
|
||||||
spawn_flags,
|
|
||||||
child_setup,
|
/* Allow test suite to specify path to gio-launch-desktop */
|
||||||
&data,
|
tmp = g_getenv ("GIO_LAUNCH_DESKTOP");
|
||||||
&pid,
|
|
||||||
error))
|
/* Fall back on usual searching in $PATH */
|
||||||
|
if (tmp == NULL)
|
||||||
|
tmp = "gio-launch-desktop";
|
||||||
|
g_once_init_leave (&gio_launch_desktop_path, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped_argv = g_new (char *, argc + 2);
|
||||||
|
wrapped_argv[0] = g_strdup (gio_launch_desktop_path);
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
wrapped_argv[i + 1] = g_steal_pointer (&argv[i]);
|
||||||
|
|
||||||
|
wrapped_argv[i + 1] = NULL;
|
||||||
|
g_free (argv);
|
||||||
|
argv = NULL;
|
||||||
|
|
||||||
|
if (!g_spawn_async_with_fds (info->path,
|
||||||
|
wrapped_argv,
|
||||||
|
envp,
|
||||||
|
spawn_flags,
|
||||||
|
user_setup,
|
||||||
|
user_setup_data,
|
||||||
|
&pid,
|
||||||
|
stdin_fd,
|
||||||
|
stdout_fd,
|
||||||
|
stderr_fd,
|
||||||
|
error))
|
||||||
{
|
{
|
||||||
if (sn_id)
|
if (sn_id)
|
||||||
g_app_launch_context_launch_failed (launch_context, sn_id);
|
g_app_launch_context_launch_failed (launch_context, sn_id);
|
||||||
@ -2805,8 +2787,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
|||||||
g_free (sn_id);
|
g_free (sn_id);
|
||||||
g_list_free (launched_uris);
|
g_list_free (launched_uris);
|
||||||
|
|
||||||
g_strfreev (argv);
|
g_strfreev (wrapped_argv);
|
||||||
argv = NULL;
|
wrapped_argv = NULL;
|
||||||
}
|
}
|
||||||
while (dup_uris != NULL);
|
while (dup_uris != NULL);
|
||||||
|
|
||||||
@ -2940,6 +2922,9 @@ g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
|
|||||||
gpointer user_setup_data,
|
gpointer user_setup_data,
|
||||||
GDesktopAppLaunchCallback pid_callback,
|
GDesktopAppLaunchCallback pid_callback,
|
||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
|
gint stdin_fd,
|
||||||
|
gint stdout_fd,
|
||||||
|
gint stderr_fd,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
|
GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
|
||||||
@ -2953,7 +2938,8 @@ g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
|
|||||||
else
|
else
|
||||||
success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
|
success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
|
||||||
spawn_flags, user_setup, user_setup_data,
|
spawn_flags, user_setup, user_setup_data,
|
||||||
pid_callback, pid_callback_data, error);
|
pid_callback, pid_callback_data,
|
||||||
|
stdin_fd, stdout_fd, stderr_fd, error);
|
||||||
|
|
||||||
if (session_bus != NULL)
|
if (session_bus != NULL)
|
||||||
{
|
{
|
||||||
@ -2978,6 +2964,7 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
launch_context,
|
launch_context,
|
||||||
_SPAWN_FLAGS_DEFAULT,
|
_SPAWN_FLAGS_DEFAULT,
|
||||||
NULL, NULL, NULL, NULL,
|
NULL, NULL, NULL, NULL,
|
||||||
|
-1, -1, -1,
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3028,6 +3015,61 @@ g_desktop_app_info_launch (GAppInfo *appinfo,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_desktop_app_info_launch_uris_as_manager_with_fds:
|
||||||
|
* @appinfo: a #GDesktopAppInfo
|
||||||
|
* @uris: (element-type utf8): List of URIs
|
||||||
|
* @launch_context: (nullable): a #GAppLaunchContext
|
||||||
|
* @spawn_flags: #GSpawnFlags, used for each process
|
||||||
|
* @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once
|
||||||
|
* for each process.
|
||||||
|
* @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
|
||||||
|
* @pid_callback: (scope call) (nullable): Callback for child processes
|
||||||
|
* @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
|
||||||
|
* @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 a #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Equivalent to g_desktop_app_info_launch_uris_as_manager() but allows
|
||||||
|
* you to pass in file descriptors for the stdin, stdout and stderr streams
|
||||||
|
* of the launched process.
|
||||||
|
*
|
||||||
|
* If application launching occurs via some non-spawn mechanism (e.g. D-Bus
|
||||||
|
* activation) then @stdin_fd, @stdout_fd and @stderr_fd are ignored.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||||
|
*
|
||||||
|
* Since: 2.58
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
g_desktop_app_info_launch_uris_as_manager_with_fds (GDesktopAppInfo *appinfo,
|
||||||
|
GList *uris,
|
||||||
|
GAppLaunchContext *launch_context,
|
||||||
|
GSpawnFlags spawn_flags,
|
||||||
|
GSpawnChildSetupFunc user_setup,
|
||||||
|
gpointer user_setup_data,
|
||||||
|
GDesktopAppLaunchCallback pid_callback,
|
||||||
|
gpointer pid_callback_data,
|
||||||
|
gint stdin_fd,
|
||||||
|
gint stdout_fd,
|
||||||
|
gint stderr_fd,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
|
||||||
|
uris,
|
||||||
|
launch_context,
|
||||||
|
spawn_flags,
|
||||||
|
user_setup,
|
||||||
|
user_setup_data,
|
||||||
|
pid_callback,
|
||||||
|
pid_callback_data,
|
||||||
|
stdin_fd,
|
||||||
|
stdout_fd,
|
||||||
|
stderr_fd,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_desktop_app_info_launch_uris_as_manager:
|
* g_desktop_app_info_launch_uris_as_manager:
|
||||||
* @appinfo: a #GDesktopAppInfo
|
* @appinfo: a #GDesktopAppInfo
|
||||||
@ -3046,11 +3088,12 @@ g_desktop_app_info_launch (GAppInfo *appinfo,
|
|||||||
* launch applications. Ordinary applications should use
|
* launch applications. Ordinary applications should use
|
||||||
* g_app_info_launch_uris().
|
* g_app_info_launch_uris().
|
||||||
*
|
*
|
||||||
* If the application is launched via traditional UNIX fork()/exec()
|
* If the application is launched via GSpawn, then @spawn_flags, @user_setup
|
||||||
* then @spawn_flags, @user_setup and @user_setup_data are used for the
|
* and @user_setup_data are used for the call to g_spawn_async().
|
||||||
* call to g_spawn_async(). Additionally, @pid_callback (with
|
* Additionally, @pid_callback (with @pid_callback_data) will be called to
|
||||||
* @pid_callback_data) will be called to inform about the PID of the
|
* inform about the PID of the created process. See g_spawn_async_with_pipes()
|
||||||
* created process.
|
* for information on certain parameter conditions that can enable an
|
||||||
|
* optimized posix_spawn() codepath to be used.
|
||||||
*
|
*
|
||||||
* If application launching occurs via some other mechanism (eg: D-Bus
|
* If application launching occurs via some other mechanism (eg: D-Bus
|
||||||
* activation) then @spawn_flags, @user_setup, @user_setup_data,
|
* activation) then @spawn_flags, @user_setup, @user_setup_data,
|
||||||
@ -3069,15 +3112,16 @@ g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo,
|
|||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
|
return g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
|
||||||
uris,
|
uris,
|
||||||
launch_context,
|
launch_context,
|
||||||
spawn_flags,
|
spawn_flags,
|
||||||
user_setup,
|
user_setup,
|
||||||
user_setup_data,
|
user_setup_data,
|
||||||
pid_callback,
|
pid_callback,
|
||||||
pid_callback_data,
|
pid_callback_data,
|
||||||
error);
|
-1, -1, -1,
|
||||||
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OnlyShowIn API support {{{2 */
|
/* OnlyShowIn API support {{{2 */
|
||||||
@ -4652,7 +4696,8 @@ g_desktop_app_info_launch_action (GDesktopAppInfo *info,
|
|||||||
|
|
||||||
if (exec_line)
|
if (exec_line)
|
||||||
g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
|
g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
|
||||||
_SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL, NULL);
|
_SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL,
|
||||||
|
-1, -1, -1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_bus != NULL)
|
if (session_bus != NULL)
|
||||||
|
@ -169,6 +169,20 @@ gboolean g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
|
|||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_58
|
||||||
|
gboolean g_desktop_app_info_launch_uris_as_manager_with_fds (GDesktopAppInfo *appinfo,
|
||||||
|
GList *uris,
|
||||||
|
GAppLaunchContext *launch_context,
|
||||||
|
GSpawnFlags spawn_flags,
|
||||||
|
GSpawnChildSetupFunc user_setup,
|
||||||
|
gpointer user_setup_data,
|
||||||
|
GDesktopAppLaunchCallback pid_callback,
|
||||||
|
gpointer pid_callback_data,
|
||||||
|
gint stdin_fd,
|
||||||
|
gint stdout_fd,
|
||||||
|
gint stderr_fd,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_2_40
|
GLIB_AVAILABLE_IN_2_40
|
||||||
gchar *** g_desktop_app_info_search (const gchar *search_string);
|
gchar *** g_desktop_app_info_search (const gchar *search_string);
|
||||||
|
|
||||||
|
52
gio/gio-launch-desktop.c
Normal file
52
gio/gio-launch-desktop.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Endless Mobile, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Author: Daniel Drake <drake@endlessm.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gio-launch-desktop: GDesktopAppInfo helper
|
||||||
|
* Executable wrapper to set GIO_LAUNCHED_DESKTOP_FILE_PID
|
||||||
|
* There are complications when doing this in a fork()/exec() codepath,
|
||||||
|
* and it cannot otherwise be done with posix_spawn().
|
||||||
|
* This wrapper is designed to be minimal and lightweight.
|
||||||
|
* It does not even link against glib.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
pid_t pid = getpid ();
|
||||||
|
char buf[50];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
r = snprintf (buf, sizeof (buf), "GIO_LAUNCHED_DESKTOP_FILE_PID=%ld", (long) pid);
|
||||||
|
if (r >= sizeof (buf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
putenv (buf);
|
||||||
|
|
||||||
|
return execvp (argv[1], argv + 1);
|
||||||
|
}
|
@ -411,6 +411,12 @@ if host_system != 'windows'
|
|||||||
contenttype_sources += files('gcontenttype.c')
|
contenttype_sources += files('gcontenttype.c')
|
||||||
appinfo_sources += files('gdesktopappinfo.c')
|
appinfo_sources += files('gdesktopappinfo.c')
|
||||||
gio_unix_include_headers += files('gdesktopappinfo.h')
|
gio_unix_include_headers += files('gdesktopappinfo.h')
|
||||||
|
|
||||||
|
executable('gio-launch-desktop', 'gio-launch-desktop.c',
|
||||||
|
install : true,
|
||||||
|
c_args : gio_c_args,
|
||||||
|
# intl.lib is not compatible with SAFESEH
|
||||||
|
link_args : noseh_link_args)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
subdir('xdgmime')
|
subdir('xdgmime')
|
||||||
|
@ -15,7 +15,9 @@ LDADD = \
|
|||||||
AM_CPPFLAGS = $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) -I$(top_builddir)/gio -I$(top_srcdir)/gio
|
AM_CPPFLAGS = $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) -I$(top_builddir)/gio -I$(top_srcdir)/gio
|
||||||
DEFS = -DG_LOG_DOMAIN=\"GLib-GIO\" -DTEST_SERVICES=\""$(abs_top_builddir)/gio/tests/services"\"
|
DEFS = -DG_LOG_DOMAIN=\"GLib-GIO\" -DTEST_SERVICES=\""$(abs_top_builddir)/gio/tests/services"\"
|
||||||
AM_CFLAGS = $(GLIB_WARN_CFLAGS)
|
AM_CFLAGS = $(GLIB_WARN_CFLAGS)
|
||||||
AM_TESTS_ENVIRONMENT += GIO_MODULE_DIR=
|
AM_TESTS_ENVIRONMENT += \
|
||||||
|
GIO_MODULE_DIR= \
|
||||||
|
GIO_LAUNCH_DESKTOP="$(top_builddir)/gio/gio-launch-desktop"
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Test programs buildable on all platforms
|
# Test programs buildable on all platforms
|
||||||
|
@ -788,6 +788,46 @@ test_show_in (void)
|
|||||||
assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic");
|
assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test g_desktop_app_info_launch_uris_as_manager() and
|
||||||
|
* g_desktop_app_info_launch_uris_as_manager_with_fds()
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
test_launch_as_manager (void)
|
||||||
|
{
|
||||||
|
GDesktopAppInfo *appinfo;
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean retval;
|
||||||
|
const gchar *path;
|
||||||
|
|
||||||
|
if (g_getenv ("DISPLAY") == NULL || g_getenv ("DISPLAY")[0] == '\0')
|
||||||
|
{
|
||||||
|
g_test_skip ("No DISPLAY. Skipping test.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL);
|
||||||
|
appinfo = g_desktop_app_info_new_from_filename (path);
|
||||||
|
g_assert_nonnull (appinfo);
|
||||||
|
|
||||||
|
retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, NULL, 0,
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
&error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (retval);
|
||||||
|
|
||||||
|
retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
|
||||||
|
NULL, NULL, 0,
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
-1, -1, -1,
|
||||||
|
&error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (retval);
|
||||||
|
|
||||||
|
g_object_unref (appinfo);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc,
|
main (int argc,
|
||||||
char *argv[])
|
char *argv[])
|
||||||
@ -816,6 +856,7 @@ main (int argc,
|
|||||||
g_test_add_func ("/desktop-app-info/search", test_search);
|
g_test_add_func ("/desktop-app-info/search", test_search);
|
||||||
g_test_add_func ("/desktop-app-info/implements", test_implements);
|
g_test_add_func ("/desktop-app-info/implements", test_implements);
|
||||||
g_test_add_func ("/desktop-app-info/show-in", test_show_in);
|
g_test_add_func ("/desktop-app-info/show-in", test_show_in);
|
||||||
|
g_test_add_func ("/desktop-app-info/launch-as-manager", test_launch_as_manager);
|
||||||
|
|
||||||
result = g_test_run ();
|
result = g_test_run ();
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ test_env = [
|
|||||||
'G_TEST_SRCDIR=' + meson.current_source_dir(),
|
'G_TEST_SRCDIR=' + meson.current_source_dir(),
|
||||||
'G_TEST_BUILDDIR=' + meson.current_build_dir(),
|
'G_TEST_BUILDDIR=' + meson.current_build_dir(),
|
||||||
'GIO_MODULE_DIR=',
|
'GIO_MODULE_DIR=',
|
||||||
|
'GIO_LAUNCH_DESKTOP=' + meson.build_root() + '/gio/gio-launch-desktop',
|
||||||
]
|
]
|
||||||
|
|
||||||
test_c_args = [
|
test_c_args = [
|
||||||
|
@ -520,19 +520,19 @@ do_spawn_directly (gint *exit_status,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
do_spawn_with_pipes (gint *exit_status,
|
do_spawn_with_fds (gint *exit_status,
|
||||||
gboolean do_return_handle,
|
gboolean do_return_handle,
|
||||||
const gchar *working_directory,
|
const gchar *working_directory,
|
||||||
gchar **argv,
|
gchar **argv,
|
||||||
char **envp,
|
char **envp,
|
||||||
GSpawnFlags flags,
|
GSpawnFlags flags,
|
||||||
GSpawnChildSetupFunc child_setup,
|
GSpawnChildSetupFunc child_setup,
|
||||||
GPid *child_handle,
|
GPid *child_handle,
|
||||||
gint *standard_input,
|
gint stdin_fd,
|
||||||
gint *standard_output,
|
gint stdout_fd,
|
||||||
gint *standard_error,
|
gint stderr_fd,
|
||||||
gint *err_report,
|
gint *err_report,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
char **protected_argv;
|
char **protected_argv;
|
||||||
char args[ARG_COUNT][10];
|
char args[ARG_COUNT][10];
|
||||||
@ -541,9 +541,6 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
gintptr rc = -1;
|
gintptr rc = -1;
|
||||||
int errsv;
|
int errsv;
|
||||||
int argc;
|
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 child_err_report_pipe[2] = { -1, -1 };
|
||||||
int helper_sync_pipe[2] = { -1, -1 };
|
int helper_sync_pipe[2] = { -1, -1 };
|
||||||
gintptr helper_report[2];
|
gintptr helper_report[2];
|
||||||
@ -562,7 +559,7 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
|
|
||||||
argc = protect_argv (argv, &protected_argv);
|
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_CHILD_INHERITS_STDIN) &&
|
||||||
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
|
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
|
||||||
!(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
|
!(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
|
||||||
@ -578,15 +575,6 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
return retval;
|
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))
|
if (!make_pipe (child_err_report_pipe, error))
|
||||||
goto cleanup_and_fail;
|
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);
|
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];
|
new_argv[ARG_STDIN] = args[ARG_STDIN];
|
||||||
}
|
}
|
||||||
else if (flags & G_SPAWN_CHILD_INHERITS_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";
|
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];
|
new_argv[ARG_STDOUT] = args[ARG_STDOUT];
|
||||||
}
|
}
|
||||||
else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
|
else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
|
||||||
@ -669,9 +657,9 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
new_argv[ARG_STDOUT] = "-";
|
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];
|
new_argv[ARG_STDERR] = args[ARG_STDERR];
|
||||||
}
|
}
|
||||||
else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
|
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 (&child_err_report_pipe[1]);
|
||||||
close_and_invalidate (&helper_sync_pipe[0]);
|
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);
|
g_strfreev (protected_argv);
|
||||||
|
|
||||||
@ -842,12 +827,6 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
|
|
||||||
/* Success against all odds! return the information */
|
/* 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)
|
if (rc != -1)
|
||||||
CloseHandle ((HANDLE) rc);
|
CloseHandle ((HANDLE) rc);
|
||||||
|
|
||||||
@ -865,6 +844,71 @@ do_spawn_with_pipes (gint *exit_status,
|
|||||||
close (helper_sync_pipe[0]);
|
close (helper_sync_pipe[0]);
|
||||||
if (helper_sync_pipe[1] != -1)
|
if (helper_sync_pipe[1] != -1)
|
||||||
close (helper_sync_pipe[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)
|
if (stdin_pipe[0] != -1)
|
||||||
close (stdin_pipe[0]);
|
close (stdin_pipe[0]);
|
||||||
if (stdin_pipe[1] != -1)
|
if (stdin_pipe[1] != -1)
|
||||||
@ -1160,6 +1204,43 @@ g_spawn_async_with_pipes (const gchar *working_directory,
|
|||||||
error);
|
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
|
gboolean
|
||||||
g_spawn_command_line_sync (const gchar *command_line,
|
g_spawn_command_line_sync (const gchar *command_line,
|
||||||
gchar **standard_output,
|
gchar **standard_output,
|
||||||
|
538
glib/gspawn.c
538
glib/gspawn.c
@ -30,6 +30,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h> /* for fdwalk */
|
#include <stdlib.h> /* for fdwalk */
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
|
||||||
#ifdef HAVE_SYS_SELECT_H
|
#ifdef HAVE_SYS_SELECT_H
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
@ -54,6 +55,22 @@
|
|||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
#include "glib-unix.h"
|
#include "glib-unix.h"
|
||||||
|
|
||||||
|
/* posix_spawn() is assumed the fastest way to spawn, but glibc's
|
||||||
|
* implementation was buggy before glibc 2.24, so avoid it on old versions.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_POSIX_SPAWN
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
|
||||||
|
#if __GLIBC_PREREQ(2,24)
|
||||||
|
#define POSIX_SPAWN_AVAILABLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else /* !__GLIBC__ */
|
||||||
|
/* Assume that all non-glibc posix_spawn implementations are fine. */
|
||||||
|
#define POSIX_SPAWN_AVAILABLE
|
||||||
|
#endif /* __GLIBC__ */
|
||||||
|
#endif /* HAVE_POSIX_SPAWN */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:spawn
|
* SECTION:spawn
|
||||||
* @Short_description: process launching
|
* @Short_description: process launching
|
||||||
@ -142,6 +159,27 @@ static gboolean fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
gint *standard_error,
|
gint *standard_error,
|
||||||
GError **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-exec-error-quark, g_spawn_error)
|
||||||
G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
|
G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
|
||||||
|
|
||||||
@ -600,10 +638,11 @@ g_spawn_sync (const gchar *working_directory,
|
|||||||
* is equivalent to calling CloseHandle() on the process handle returned
|
* is equivalent to calling CloseHandle() on the process handle returned
|
||||||
* in @child_pid). See g_child_watch_add().
|
* in @child_pid). See g_child_watch_add().
|
||||||
*
|
*
|
||||||
* %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
|
* Open UNIX file descriptors marked as `FD_CLOEXEC` will be automatically
|
||||||
* descriptors will be inherited by the child; otherwise all descriptors
|
* closed in the child process. %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that
|
||||||
* except stdin/stdout/stderr will be closed before calling exec() in
|
* other open file descriptors will be inherited by the child; otherwise all
|
||||||
* the child. %G_SPAWN_SEARCH_PATH means that @argv[0] need not be an
|
* descriptors except stdin/stdout/stderr will be closed before calling exec()
|
||||||
|
* in the child. %G_SPAWN_SEARCH_PATH means that @argv[0] need not be an
|
||||||
* absolute path, it will be looked for in the `PATH` environment
|
* absolute path, it will be looked for in the `PATH` environment
|
||||||
* variable. %G_SPAWN_SEARCH_PATH_FROM_ENVP means need not be an
|
* variable. %G_SPAWN_SEARCH_PATH_FROM_ENVP means need not be an
|
||||||
* absolute path, it will be looked for in the `PATH` variable from
|
* absolute path, it will be looked for in the `PATH` variable from
|
||||||
@ -678,6 +717,21 @@ g_spawn_sync (const gchar *working_directory,
|
|||||||
* If @child_pid is not %NULL and an error does not occur then the returned
|
* If @child_pid is not %NULL and an error does not occur then the returned
|
||||||
* process reference must be closed using g_spawn_close_pid().
|
* process reference must be closed using g_spawn_close_pid().
|
||||||
*
|
*
|
||||||
|
* On modern UNIX platforms, GLib can use an efficient process launching
|
||||||
|
* codepath driven internally by posix_spawn(). This has the advantage of
|
||||||
|
* avoiding the fork-time performance costs of cloning the parent process
|
||||||
|
* address space, and avoiding associated memory overcommit checks that are
|
||||||
|
* not relevant in the context of immediately executing a distinct process.
|
||||||
|
* This optimized codepath will be used provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. %G_SPAWN_DO_NOT_REAP_CHILD is set
|
||||||
|
* 2. %G_SPAWN_LEAVE_DESCRIPTORS_OPEN is set
|
||||||
|
* 3. %G_SPAWN_SEARCH_PATH_FROM_ENVP is not set
|
||||||
|
* 4. @working_directory is %NULL
|
||||||
|
* 5. @child_setup is %NULL
|
||||||
|
* 6. The program is of a recognised binary format, or has a shebang. Otherwise, GLib will have to execute the program through the shell, which is not done using the optimized codepath.
|
||||||
|
*
|
||||||
* If you are writing a GTK+ application, and the program you are spawning is a
|
* If you are writing a GTK+ application, and the program you are spawning is a
|
||||||
* graphical application too, then to ensure that the spawned program opens its
|
* graphical application too, then to ensure that the spawned program opens its
|
||||||
* windows on the right screen, you may want to use #GdkAppLaunchContext,
|
* windows on the right screen, you may want to use #GdkAppLaunchContext,
|
||||||
@ -728,6 +782,87 @@ g_spawn_async_with_pipes (const gchar *working_directory,
|
|||||||
error);
|
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:
|
* g_spawn_command_line_sync:
|
||||||
* @command_line: (type filename): a command line
|
* @command_line: (type filename): a command line
|
||||||
@ -1118,13 +1253,12 @@ do_exec (gint child_err_report_fd,
|
|||||||
write_err_and_exit (child_err_report_fd,
|
write_err_and_exit (child_err_report_fd,
|
||||||
CHILD_DUP2_FAILED);
|
CHILD_DUP2_FAILED);
|
||||||
|
|
||||||
/* ignore this if it doesn't work */
|
set_cloexec (GINT_TO_POINTER(0), stdin_fd);
|
||||||
close_and_invalidate (&stdin_fd);
|
|
||||||
}
|
}
|
||||||
else if (!child_inherits_stdin)
|
else if (!child_inherits_stdin)
|
||||||
{
|
{
|
||||||
/* Keep process from blocking on a read of stdin */
|
/* Keep process from blocking on a read of stdin */
|
||||||
gint read_null = open ("/dev/null", O_RDONLY);
|
gint read_null = sane_open ("/dev/null", O_RDONLY);
|
||||||
g_assert (read_null != -1);
|
g_assert (read_null != -1);
|
||||||
sane_dup2 (read_null, 0);
|
sane_dup2 (read_null, 0);
|
||||||
close_and_invalidate (&read_null);
|
close_and_invalidate (&read_null);
|
||||||
@ -1138,8 +1272,7 @@ do_exec (gint child_err_report_fd,
|
|||||||
write_err_and_exit (child_err_report_fd,
|
write_err_and_exit (child_err_report_fd,
|
||||||
CHILD_DUP2_FAILED);
|
CHILD_DUP2_FAILED);
|
||||||
|
|
||||||
/* ignore this if it doesn't work */
|
set_cloexec (GINT_TO_POINTER(0), stdout_fd);
|
||||||
close_and_invalidate (&stdout_fd);
|
|
||||||
}
|
}
|
||||||
else if (stdout_to_null)
|
else if (stdout_to_null)
|
||||||
{
|
{
|
||||||
@ -1157,8 +1290,7 @@ do_exec (gint child_err_report_fd,
|
|||||||
write_err_and_exit (child_err_report_fd,
|
write_err_and_exit (child_err_report_fd,
|
||||||
CHILD_DUP2_FAILED);
|
CHILD_DUP2_FAILED);
|
||||||
|
|
||||||
/* ignore this if it doesn't work */
|
set_cloexec (GINT_TO_POINTER(0), stderr_fd);
|
||||||
close_and_invalidate (&stderr_fd);
|
|
||||||
}
|
}
|
||||||
else if (stderr_to_null)
|
else if (stderr_to_null)
|
||||||
{
|
{
|
||||||
@ -1231,51 +1363,256 @@ read_ints (int fd,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef POSIX_SPAWN_AVAILABLE
|
||||||
static gboolean
|
static gboolean
|
||||||
fork_exec_with_pipes (gboolean intermediate_child,
|
do_posix_spawn (gchar **argv,
|
||||||
const gchar *working_directory,
|
gchar **envp,
|
||||||
gchar **argv,
|
gboolean search_path,
|
||||||
gchar **envp,
|
gboolean stdout_to_null,
|
||||||
gboolean close_descriptors,
|
gboolean stderr_to_null,
|
||||||
gboolean search_path,
|
gboolean child_inherits_stdin,
|
||||||
gboolean search_path_from_envp,
|
gboolean file_and_argv_zero,
|
||||||
gboolean stdout_to_null,
|
GPid *child_pid,
|
||||||
gboolean stderr_to_null,
|
gint *child_close_fds,
|
||||||
gboolean child_inherits_stdin,
|
gint stdin_fd,
|
||||||
gboolean file_and_argv_zero,
|
gint stdout_fd,
|
||||||
gboolean cloexec_pipes,
|
gint stderr_fd)
|
||||||
GSpawnChildSetupFunc child_setup,
|
{
|
||||||
gpointer user_data,
|
pid_t pid;
|
||||||
GPid *child_pid,
|
gchar **argv_pass;
|
||||||
gint *standard_input,
|
posix_spawnattr_t attr;
|
||||||
gint *standard_output,
|
posix_spawn_file_actions_t file_actions;
|
||||||
gint *standard_error,
|
gint parent_close_fds[3];
|
||||||
GError **error)
|
gint num_parent_close_fds = 0;
|
||||||
|
GSList *child_close = NULL;
|
||||||
|
GSList *elem;
|
||||||
|
sigset_t mask;
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
if (*argv[0] == '\0')
|
||||||
|
{
|
||||||
|
/* We check the simple case first. */
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = posix_spawnattr_init (&attr);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (child_close_fds)
|
||||||
|
{
|
||||||
|
int i = -1;
|
||||||
|
while (child_close_fds[++i] != -1)
|
||||||
|
child_close = g_slist_prepend (child_close,
|
||||||
|
GINT_TO_POINTER (child_close_fds[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
r = posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_free_spawnattr;
|
||||||
|
|
||||||
|
/* Reset some signal handlers that we may use */
|
||||||
|
sigemptyset (&mask);
|
||||||
|
sigaddset (&mask, SIGCHLD);
|
||||||
|
sigaddset (&mask, SIGINT);
|
||||||
|
sigaddset (&mask, SIGTERM);
|
||||||
|
sigaddset (&mask, SIGHUP);
|
||||||
|
|
||||||
|
r = posix_spawnattr_setsigdefault (&attr, &mask);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_free_spawnattr;
|
||||||
|
|
||||||
|
r = posix_spawn_file_actions_init (&file_actions);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_free_spawnattr;
|
||||||
|
|
||||||
|
/* Redirect pipes as required */
|
||||||
|
|
||||||
|
if (stdin_fd >= 0)
|
||||||
|
{
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, stdin_fd, 0);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
|
||||||
|
if (!g_slist_find (child_close, GINT_TO_POINTER (stdin_fd)))
|
||||||
|
child_close = g_slist_prepend (child_close, GINT_TO_POINTER (stdin_fd));
|
||||||
|
}
|
||||||
|
else if (!child_inherits_stdin)
|
||||||
|
{
|
||||||
|
/* Keep process from blocking on a read of stdin */
|
||||||
|
gint read_null = sane_open ("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||||
|
g_assert (read_null != -1);
|
||||||
|
parent_close_fds[num_parent_close_fds++] = read_null;
|
||||||
|
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, read_null, 0);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout_fd >= 0)
|
||||||
|
{
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, stdout_fd, 1);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
|
||||||
|
if (!g_slist_find (child_close, GINT_TO_POINTER (stdout_fd)))
|
||||||
|
child_close = g_slist_prepend (child_close, GINT_TO_POINTER (stdout_fd));
|
||||||
|
}
|
||||||
|
else if (stdout_to_null)
|
||||||
|
{
|
||||||
|
gint write_null = sane_open ("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||||
|
g_assert (write_null != -1);
|
||||||
|
parent_close_fds[num_parent_close_fds++] = write_null;
|
||||||
|
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, write_null, 1);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr_fd >= 0)
|
||||||
|
{
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, stderr_fd, 2);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
|
||||||
|
if (!g_slist_find (child_close, GINT_TO_POINTER (stderr_fd)))
|
||||||
|
child_close = g_slist_prepend (child_close, GINT_TO_POINTER (stderr_fd));
|
||||||
|
}
|
||||||
|
else if (stderr_to_null)
|
||||||
|
{
|
||||||
|
gint write_null = sane_open ("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||||
|
g_assert (write_null != -1);
|
||||||
|
parent_close_fds[num_parent_close_fds++] = write_null;
|
||||||
|
|
||||||
|
r = posix_spawn_file_actions_adddup2 (&file_actions, write_null, 2);
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intentionally close the fds in the child as the last file action,
|
||||||
|
* having been careful not to add the same fd to this list twice.
|
||||||
|
*
|
||||||
|
* This is important to allow (e.g.) for the same fd to be passed as stdout
|
||||||
|
* and stderr (we must not close it before we have dupped it in both places,
|
||||||
|
* and we must not attempt to close it twice).
|
||||||
|
*/
|
||||||
|
for (elem = child_close; elem != NULL; elem = elem->next)
|
||||||
|
{
|
||||||
|
r = posix_spawn_file_actions_addclose (&file_actions,
|
||||||
|
GPOINTER_TO_INT (elem->data));
|
||||||
|
if (r != 0)
|
||||||
|
goto out_close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv_pass = file_and_argv_zero ? argv + 1 : argv;
|
||||||
|
if (envp == NULL)
|
||||||
|
envp = environ;
|
||||||
|
|
||||||
|
/* Don't search when it contains a slash. */
|
||||||
|
if (!search_path || strchr (argv[0], '/') != NULL)
|
||||||
|
r = posix_spawn (&pid, argv[0], &file_actions, &attr, argv_pass, envp);
|
||||||
|
else
|
||||||
|
r = posix_spawnp (&pid, argv[0], &file_actions, &attr, argv_pass, envp);
|
||||||
|
|
||||||
|
if (r == 0 && child_pid != NULL)
|
||||||
|
*child_pid = pid;
|
||||||
|
|
||||||
|
out_close_fds:
|
||||||
|
for (i = 0; i < num_parent_close_fds; i++)
|
||||||
|
close_and_invalidate (&parent_close_fds [i]);
|
||||||
|
|
||||||
|
posix_spawn_file_actions_destroy (&file_actions);
|
||||||
|
out_free_spawnattr:
|
||||||
|
posix_spawnattr_destroy (&attr);
|
||||||
|
g_slist_free (child_close);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif /* POSIX_SPAWN_AVAILABLE */
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
GPid pid = -1;
|
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_err_report_pipe[2] = { -1, -1 };
|
||||||
gint child_pid_report_pipe[2] = { -1, -1 };
|
gint child_pid_report_pipe[2] = { -1, -1 };
|
||||||
guint pipe_flags = cloexec_pipes ? FD_CLOEXEC : 0;
|
guint pipe_flags = cloexec_pipes ? FD_CLOEXEC : 0;
|
||||||
gint status;
|
gint status;
|
||||||
|
|
||||||
|
#ifdef POSIX_SPAWN_AVAILABLE
|
||||||
|
if (!intermediate_child && working_directory == NULL && !close_descriptors &&
|
||||||
|
!search_path_from_envp && child_setup == NULL)
|
||||||
|
{
|
||||||
|
g_debug ("Launching with posix_spawn");
|
||||||
|
status = do_posix_spawn (argv,
|
||||||
|
envp,
|
||||||
|
search_path,
|
||||||
|
stdout_to_null,
|
||||||
|
stderr_to_null,
|
||||||
|
child_inherits_stdin,
|
||||||
|
file_and_argv_zero,
|
||||||
|
child_pid,
|
||||||
|
child_close_fds,
|
||||||
|
stdin_fd,
|
||||||
|
stdout_fd,
|
||||||
|
stderr_fd);
|
||||||
|
if (status == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (status != ENOEXEC)
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_SPAWN_ERROR,
|
||||||
|
G_SPAWN_ERROR_FAILED,
|
||||||
|
_("Failed to spawn child process \"%s\" (%s)"),
|
||||||
|
argv[0],
|
||||||
|
g_strerror (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* posix_spawn is not intended to support script execution. It does in
|
||||||
|
* some situations on some glibc versions, but that will be fixed.
|
||||||
|
* So if it fails with ENOEXEC, we fall through to the regular
|
||||||
|
* gspawn codepath so that script execution can be attempted,
|
||||||
|
* per standard gspawn behaviour. */
|
||||||
|
g_debug ("posix_spawn failed (ENOEXEC), fall back to regular gspawn");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_debug ("posix_spawn avoided %s%s%s%s%s",
|
||||||
|
!intermediate_child ? "" : "(automatic reaping requested) ",
|
||||||
|
working_directory == NULL ? "" : "(workdir specified) ",
|
||||||
|
!close_descriptors ? "" : "(fd close requested) ",
|
||||||
|
!search_path_from_envp ? "" : "(using envp for search path) ",
|
||||||
|
child_setup == NULL ? "" : "(child_setup specified) ");
|
||||||
|
}
|
||||||
|
#endif /* POSIX_SPAWN_AVAILABLE */
|
||||||
|
|
||||||
if (!g_unix_open_pipe (child_err_report_pipe, pipe_flags, error))
|
if (!g_unix_open_pipe (child_err_report_pipe, pipe_flags, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (intermediate_child && !g_unix_open_pipe (child_pid_report_pipe, pipe_flags, error))
|
if (intermediate_child && !g_unix_open_pipe (child_pid_report_pipe, pipe_flags, error))
|
||||||
goto cleanup_and_fail;
|
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 ();
|
pid = fork ();
|
||||||
|
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
@ -1313,9 +1650,12 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
*/
|
*/
|
||||||
close_and_invalidate (&child_err_report_pipe[0]);
|
close_and_invalidate (&child_err_report_pipe[0]);
|
||||||
close_and_invalidate (&child_pid_report_pipe[0]);
|
close_and_invalidate (&child_pid_report_pipe[0]);
|
||||||
close_and_invalidate (&stdin_pipe[1]);
|
if (child_close_fds != NULL)
|
||||||
close_and_invalidate (&stdout_pipe[0]);
|
{
|
||||||
close_and_invalidate (&stderr_pipe[0]);
|
int i = -1;
|
||||||
|
while (child_close_fds[++i] != -1)
|
||||||
|
close_and_invalidate (&child_close_fds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
if (intermediate_child)
|
if (intermediate_child)
|
||||||
{
|
{
|
||||||
@ -1341,9 +1681,9 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
{
|
{
|
||||||
close_and_invalidate (&child_pid_report_pipe[1]);
|
close_and_invalidate (&child_pid_report_pipe[1]);
|
||||||
do_exec (child_err_report_pipe[1],
|
do_exec (child_err_report_pipe[1],
|
||||||
stdin_pipe[0],
|
stdin_fd,
|
||||||
stdout_pipe[1],
|
stdout_fd,
|
||||||
stderr_pipe[1],
|
stderr_fd,
|
||||||
working_directory,
|
working_directory,
|
||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
@ -1371,9 +1711,9 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
do_exec (child_err_report_pipe[1],
|
do_exec (child_err_report_pipe[1],
|
||||||
stdin_pipe[0],
|
stdin_fd,
|
||||||
stdout_pipe[1],
|
stdout_fd,
|
||||||
stderr_pipe[1],
|
stderr_fd,
|
||||||
working_directory,
|
working_directory,
|
||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
@ -1398,9 +1738,6 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
/* Close the uncared-about ends of the pipes */
|
/* Close the uncared-about ends of the pipes */
|
||||||
close_and_invalidate (&child_err_report_pipe[1]);
|
close_and_invalidate (&child_err_report_pipe[1]);
|
||||||
close_and_invalidate (&child_pid_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 we had an intermediate child, reap it */
|
||||||
if (intermediate_child)
|
if (intermediate_child)
|
||||||
@ -1513,13 +1850,6 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
if (child_pid)
|
if (child_pid)
|
||||||
*child_pid = 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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1548,6 +1878,92 @@ fork_exec_with_pipes (gboolean intermediate_child,
|
|||||||
close_and_invalidate (&child_err_report_pipe[1]);
|
close_and_invalidate (&child_err_report_pipe[1]);
|
||||||
close_and_invalidate (&child_pid_report_pipe[0]);
|
close_and_invalidate (&child_pid_report_pipe[0]);
|
||||||
close_and_invalidate (&child_pid_report_pipe[1]);
|
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[0]);
|
||||||
close_and_invalidate (&stdin_pipe[1]);
|
close_and_invalidate (&stdin_pipe[1]);
|
||||||
close_and_invalidate (&stdout_pipe[0]);
|
close_and_invalidate (&stdout_pipe[0]);
|
||||||
|
@ -215,6 +215,19 @@ gboolean g_spawn_async_with_pipes (const gchar *working_directory,
|
|||||||
gint *standard_error,
|
gint *standard_error,
|
||||||
GError **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
|
/* If standard_output or standard_error are non-NULL, the full
|
||||||
* standard output or error of the command will be placed there.
|
* standard output or error of the command will be placed there.
|
||||||
|
@ -25,8 +25,14 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
|
#include <io.h>
|
||||||
#define LINEEND "\r\n"
|
#define LINEEND "\r\n"
|
||||||
#else
|
#else
|
||||||
#define LINEEND "\n"
|
#define LINEEND "\n"
|
||||||
@ -156,6 +162,145 @@ test_spawn_async (void)
|
|||||||
g_free (arg);
|
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
|
static void
|
||||||
test_spawn_sync (void)
|
test_spawn_sync (void)
|
||||||
{
|
{
|
||||||
@ -181,6 +326,35 @@ test_spawn_sync (void)
|
|||||||
g_ptr_array_free (argv, TRUE);
|
g_ptr_array_free (argv, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Like test_spawn_sync but uses spawn flags that trigger the optimized
|
||||||
|
* posix_spawn codepath.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
test_posix_spawn (void)
|
||||||
|
{
|
||||||
|
int tnum = 1;
|
||||||
|
GError *error = NULL;
|
||||||
|
GPtrArray *argv;
|
||||||
|
char *arg;
|
||||||
|
char *stdout_str;
|
||||||
|
int estatus;
|
||||||
|
GSpawnFlags flags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
g_spawn_sync (NULL, (char**)argv->pdata, NULL, flags, NULL, NULL, &stdout_str, NULL, &estatus, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_cmpstr (arg, ==, stdout_str);
|
||||||
|
g_free (arg);
|
||||||
|
g_free (stdout_str);
|
||||||
|
g_ptr_array_free (argv, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_spawn_script (void)
|
test_spawn_script (void)
|
||||||
{
|
{
|
||||||
@ -251,8 +425,10 @@ main (int argc,
|
|||||||
|
|
||||||
g_test_add_func ("/gthread/spawn-single-sync", test_spawn_sync);
|
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", 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-script", test_spawn_script);
|
||||||
g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
|
g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
|
||||||
|
g_test_add_func ("/gthread/spawn-posix-spawn", test_posix_spawn);
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
|
|
||||||
|
@ -487,6 +487,11 @@ if cc.has_function('posix_memalign', prefix : '#include <stdlib.h>')
|
|||||||
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
|
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Check that posix_spawn() is usable; must use header
|
||||||
|
if cc.has_function('posix_spawn', prefix : '#include <spawn.h>')
|
||||||
|
glib_conf.set('HAVE_POSIX_SPAWN', 1)
|
||||||
|
endif
|
||||||
|
|
||||||
# Check whether strerror_r returns char *
|
# Check whether strerror_r returns char *
|
||||||
if have_func_strerror_r
|
if have_func_strerror_r
|
||||||
if cc.compiles('''#define _GNU_SOURCE
|
if cc.compiles('''#define _GNU_SOURCE
|
||||||
|
Loading…
Reference in New Issue
Block a user