diff --git a/gio/Makefile.am b/gio/Makefile.am index 7c7f247c0..13a85edb1 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -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 $@ diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index a2aa760c7..4eac5ada2 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -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, @@ -2683,7 +2649,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 +2670,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 +2690,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,12 +2713,35 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, g_list_free_full (launched_files, g_object_unref); } + 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 (info->path, - argv, + wrapped_argv, envp, spawn_flags, - child_setup, - &data, + user_setup, + user_setup_data, &pid, error)) { @@ -2805,8 +2781,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); @@ -3046,11 +3022,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, diff --git a/gio/gio-launch-desktop.c b/gio/gio-launch-desktop.c new file mode 100644 index 000000000..03845df28 --- /dev/null +++ b/gio/gio-launch-desktop.c @@ -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 . + * + * Author: Daniel Drake + */ + +/* + * 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 +#include +#include +#include + +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); +} diff --git a/gio/meson.build b/gio/meson.build index 9544ab395..89246fea3 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -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') diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 599823893..e0bc6c7c2 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -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 diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 558f3d721..cde1372ad 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -74,6 +74,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 = [