mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 01:58:54 +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:
		@@ -480,7 +480,7 @@ AM_CONDITIONAL(OS_WIN32_AND_DLL_COMPILATION, [test x$glib_native_win32 = xyes -a
 | 
			
		||||
# Checks for library functions.
 | 
			
		||||
AC_FUNC_ALLOCA
 | 
			
		||||
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_CHECK_SIZEOF(char)
 | 
			
		||||
 
 | 
			
		||||
@@ -1624,6 +1624,7 @@ g_desktop_app_info_get_boolean
 | 
			
		||||
g_desktop_app_info_has_key
 | 
			
		||||
GDesktopAppLaunchCallback
 | 
			
		||||
g_desktop_app_info_launch_uris_as_manager
 | 
			
		||||
g_desktop_app_info_launch_uris_as_manager_with_fds
 | 
			
		||||
<SUBSECTION>
 | 
			
		||||
g_desktop_app_info_list_actions
 | 
			
		||||
g_desktop_app_info_get_action_name
 | 
			
		||||
 
 | 
			
		||||
@@ -1233,6 +1233,7 @@ GSpawnError
 | 
			
		||||
G_SPAWN_ERROR
 | 
			
		||||
GSpawnFlags
 | 
			
		||||
GSpawnChildSetupFunc
 | 
			
		||||
g_spawn_async_with_fds
 | 
			
		||||
g_spawn_async_with_pipes
 | 
			
		||||
g_spawn_async
 | 
			
		||||
g_spawn_sync
 | 
			
		||||
 
 | 
			
		||||
@@ -819,7 +819,7 @@ gio.def: libgio-2.0.la
 | 
			
		||||
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:$@
 | 
			
		||||
 | 
			
		||||
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 		\
 | 
			
		||||
	$(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		\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
gio_launch_desktop_SOURCES = gio-launch-desktop.c
 | 
			
		||||
 | 
			
		||||
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 $@
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -155,6 +155,7 @@ static guint           n_desktop_file_dirs;
 | 
			
		||||
static const guint     desktop_file_dir_user_config_index = 0;
 | 
			
		||||
static guint           desktop_file_dir_user_data_index;
 | 
			
		||||
static GMutex          desktop_file_dir_lock;
 | 
			
		||||
static const gchar    *gio_launch_desktop_path = NULL;
 | 
			
		||||
 | 
			
		||||
/* Monitor 'changed' signal handler {{{2 */
 | 
			
		||||
static void desktop_file_dir_reset (DesktopFileDir *dir);
 | 
			
		||||
@@ -2562,41 +2563,6 @@ create_files_for_uris (GList *uris)
 | 
			
		||||
  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
 | 
			
		||||
notify_desktop_launch (GDBusConnection  *session_bus,
 | 
			
		||||
                       GDesktopAppInfo  *info,
 | 
			
		||||
@@ -2675,6 +2641,9 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
 | 
			
		||||
                                           gpointer                    user_setup_data,
 | 
			
		||||
                                           GDesktopAppLaunchCallback   pid_callback,
 | 
			
		||||
                                           gpointer                    pid_callback_data,
 | 
			
		||||
                                           gint                        stdin_fd,
 | 
			
		||||
                                           gint                        stdout_fd,
 | 
			
		||||
                                           gint                        stderr_fd,
 | 
			
		||||
                                           GError                    **error)
 | 
			
		||||
{
 | 
			
		||||
  gboolean completed = FALSE;
 | 
			
		||||
@@ -2683,7 +2652,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
 | 
			
		||||
 | 
			
		||||
  char **argv, **envp;
 | 
			
		||||
  int argc;
 | 
			
		||||
  ChildSetupData data;
 | 
			
		||||
 | 
			
		||||
  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 *iter;
 | 
			
		||||
      char *sn_id = NULL;
 | 
			
		||||
      char **wrapped_argv;
 | 
			
		||||
      int i;
 | 
			
		||||
 | 
			
		||||
      old_uris = dup_uris;
 | 
			
		||||
      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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      data.user_setup = user_setup;
 | 
			
		||||
      data.user_setup_data = user_setup_data;
 | 
			
		||||
 | 
			
		||||
      if (info->filename)
 | 
			
		||||
        {
 | 
			
		||||
          envp = g_environ_setenv (envp,
 | 
			
		||||
                                   "GIO_LAUNCHED_DESKTOP_FILE",
 | 
			
		||||
                                   info->filename,
 | 
			
		||||
                                   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;
 | 
			
		||||
        }
 | 
			
		||||
        envp = g_environ_setenv (envp,
 | 
			
		||||
                                 "GIO_LAUNCHED_DESKTOP_FILE",
 | 
			
		||||
                                 info->filename,
 | 
			
		||||
                                 TRUE);
 | 
			
		||||
 | 
			
		||||
      sn_id = NULL;
 | 
			
		||||
      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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      if (!g_spawn_async (info->path,
 | 
			
		||||
                          argv,
 | 
			
		||||
                          envp,
 | 
			
		||||
                          spawn_flags,
 | 
			
		||||
                          child_setup,
 | 
			
		||||
                          &data,
 | 
			
		||||
                          &pid,
 | 
			
		||||
                          error))
 | 
			
		||||
      if (g_once_init_enter (&gio_launch_desktop_path))
 | 
			
		||||
        {
 | 
			
		||||
          const gchar *tmp;
 | 
			
		||||
 | 
			
		||||
          /* Allow test suite to specify path to gio-launch-desktop */
 | 
			
		||||
          tmp = g_getenv ("GIO_LAUNCH_DESKTOP");
 | 
			
		||||
 | 
			
		||||
          /* 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)
 | 
			
		||||
            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_list_free (launched_uris);
 | 
			
		||||
 | 
			
		||||
      g_strfreev (argv);
 | 
			
		||||
      argv = NULL;
 | 
			
		||||
      g_strfreev (wrapped_argv);
 | 
			
		||||
      wrapped_argv = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  while (dup_uris != NULL);
 | 
			
		||||
 | 
			
		||||
@@ -2940,6 +2922,9 @@ g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
 | 
			
		||||
                                         gpointer                    user_setup_data,
 | 
			
		||||
                                         GDesktopAppLaunchCallback   pid_callback,
 | 
			
		||||
                                         gpointer                    pid_callback_data,
 | 
			
		||||
                                         gint                        stdin_fd,
 | 
			
		||||
                                         gint                        stdout_fd,
 | 
			
		||||
                                         gint                        stderr_fd,
 | 
			
		||||
                                         GError                     **error)
 | 
			
		||||
{
 | 
			
		||||
  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
 | 
			
		||||
@@ -2953,7 +2938,8 @@ g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
 | 
			
		||||
  else
 | 
			
		||||
    success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
 | 
			
		||||
                                                         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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -2978,6 +2964,7 @@ g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
 | 
			
		||||
                                                  launch_context,
 | 
			
		||||
                                                  _SPAWN_FLAGS_DEFAULT,
 | 
			
		||||
                                                  NULL, NULL, NULL, NULL,
 | 
			
		||||
                                                  -1, -1, -1,
 | 
			
		||||
                                                  error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3028,6 +3015,61 @@ g_desktop_app_info_launch (GAppInfo           *appinfo,
 | 
			
		||||
  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:
 | 
			
		||||
 * @appinfo: a #GDesktopAppInfo
 | 
			
		||||
@@ -3046,11 +3088,12 @@ g_desktop_app_info_launch (GAppInfo           *appinfo,
 | 
			
		||||
 * launch applications.  Ordinary applications should use
 | 
			
		||||
 * g_app_info_launch_uris().
 | 
			
		||||
 *
 | 
			
		||||
 * If the application is launched via traditional UNIX fork()/exec()
 | 
			
		||||
 * then @spawn_flags, @user_setup and @user_setup_data are used for the
 | 
			
		||||
 * call to g_spawn_async().  Additionally, @pid_callback (with
 | 
			
		||||
 * @pid_callback_data) will be called to inform about the PID of the
 | 
			
		||||
 * created process.
 | 
			
		||||
 * If the application is launched via GSpawn, then @spawn_flags, @user_setup
 | 
			
		||||
 * and @user_setup_data are used for the call to g_spawn_async().
 | 
			
		||||
 * Additionally, @pid_callback (with @pid_callback_data) will be called to
 | 
			
		||||
 * inform about the PID of the created process. See g_spawn_async_with_pipes()
 | 
			
		||||
 * 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
 | 
			
		||||
 * 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,
 | 
			
		||||
                                           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,
 | 
			
		||||
                                                  error);
 | 
			
		||||
  return g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
 | 
			
		||||
                                                             uris,
 | 
			
		||||
                                                             launch_context,
 | 
			
		||||
                                                             spawn_flags,
 | 
			
		||||
                                                             user_setup,
 | 
			
		||||
                                                             user_setup_data,
 | 
			
		||||
                                                             pid_callback,
 | 
			
		||||
                                                             pid_callback_data,
 | 
			
		||||
                                                             -1, -1, -1,
 | 
			
		||||
                                                             error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OnlyShowIn API support {{{2 */
 | 
			
		||||
@@ -4652,7 +4696,8 @@ g_desktop_app_info_launch_action (GDesktopAppInfo   *info,
 | 
			
		||||
 | 
			
		||||
      if (exec_line)
 | 
			
		||||
        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)
 | 
			
		||||
 
 | 
			
		||||
@@ -169,6 +169,20 @@ gboolean    g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
 | 
			
		||||
						       gpointer                    pid_callback_data,
 | 
			
		||||
						       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
 | 
			
		||||
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')
 | 
			
		||||
    appinfo_sources += files('gdesktopappinfo.c')
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  subdir('xdgmime')
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,9 @@ LDADD = \
 | 
			
		||||
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"\"
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -788,6 +788,46 @@ test_show_in (void)
 | 
			
		||||
  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
 | 
			
		||||
main (int   argc,
 | 
			
		||||
      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/implements", test_implements);
 | 
			
		||||
  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 ();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,7 @@ test_env = [
 | 
			
		||||
  'G_TEST_SRCDIR=' + meson.current_source_dir(),
 | 
			
		||||
  'G_TEST_BUILDDIR=' + meson.current_build_dir(),
 | 
			
		||||
  'GIO_MODULE_DIR=',
 | 
			
		||||
  'GIO_LAUNCH_DESKTOP=' + meson.build_root() + '/gio/gio-launch-desktop',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
test_c_args = [
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										540
									
								
								glib/gspawn.c
									
									
									
									
									
								
							
							
						
						
									
										540
									
								
								glib/gspawn.c
									
									
									
									
									
								
							@@ -30,6 +30,7 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>   /* for fdwalk */
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <spawn.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SYS_SELECT_H
 | 
			
		||||
#include <sys/select.h>
 | 
			
		||||
@@ -54,6 +55,22 @@
 | 
			
		||||
#include "glibintl.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
 | 
			
		||||
 * @Short_description: process launching
 | 
			
		||||
@@ -142,6 +159,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)
 | 
			
		||||
 | 
			
		||||
@@ -600,10 +638,11 @@ g_spawn_sync (const gchar          *working_directory,
 | 
			
		||||
 * is equivalent to calling CloseHandle() on the process handle returned
 | 
			
		||||
 * in @child_pid). See g_child_watch_add().
 | 
			
		||||
 *
 | 
			
		||||
 * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
 | 
			
		||||
 * descriptors will be inherited by the child; otherwise all 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
 | 
			
		||||
 * Open UNIX file descriptors marked as `FD_CLOEXEC` will be automatically
 | 
			
		||||
 * closed in the child process. %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that
 | 
			
		||||
 * other open file descriptors will be inherited by the child; otherwise all
 | 
			
		||||
 * 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
 | 
			
		||||
 * variable. %G_SPAWN_SEARCH_PATH_FROM_ENVP means need not be an
 | 
			
		||||
 * 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
 | 
			
		||||
 * 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
 | 
			
		||||
 * graphical application too, then to ensure that the spawned program opens its
 | 
			
		||||
 * 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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,13 +1253,12 @@ 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)
 | 
			
		||||
    {
 | 
			
		||||
      /* 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);
 | 
			
		||||
      sane_dup2 (read_null, 0);
 | 
			
		||||
      close_and_invalidate (&read_null);
 | 
			
		||||
@@ -1138,8 +1272,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 +1290,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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -1231,51 +1363,256 @@ read_ints (int      fd,
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef POSIX_SPAWN_AVAILABLE
 | 
			
		||||
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)     
 | 
			
		||||
do_posix_spawn (gchar     **argv,
 | 
			
		||||
                gchar     **envp,
 | 
			
		||||
                gboolean    search_path,
 | 
			
		||||
                gboolean    stdout_to_null,
 | 
			
		||||
                gboolean    stderr_to_null,
 | 
			
		||||
                gboolean    child_inherits_stdin,
 | 
			
		||||
                gboolean    file_and_argv_zero,
 | 
			
		||||
                GPid       *child_pid,
 | 
			
		||||
                gint       *child_close_fds,
 | 
			
		||||
                gint        stdin_fd,
 | 
			
		||||
                gint        stdout_fd,
 | 
			
		||||
                gint        stderr_fd)
 | 
			
		||||
{
 | 
			
		||||
  pid_t pid;
 | 
			
		||||
  gchar **argv_pass;
 | 
			
		||||
  posix_spawnattr_t attr;
 | 
			
		||||
  posix_spawn_file_actions_t file_actions;
 | 
			
		||||
  gint parent_close_fds[3];
 | 
			
		||||
  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;
 | 
			
		||||
  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;
 | 
			
		||||
  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))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  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 +1650,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 +1681,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 +1711,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 +1738,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 +1850,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 +1878,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -181,6 +326,35 @@ test_spawn_sync (void)
 | 
			
		||||
  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
 | 
			
		||||
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-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);
 | 
			
		||||
  g_test_add_func ("/gthread/spawn-posix-spawn", test_posix_spawn);
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
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 *
 | 
			
		||||
if have_func_strerror_r
 | 
			
		||||
  if cc.compiles('''#define _GNU_SOURCE
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user