GAppLaunchContext: add environment-manipulating functions

Add functions for manipulating the environment under which a
GAppLaunchContext will launch its children, to avoid thread-related
bugs with using setenv() directly.

FIXME: win32 side isn't implemented yet

https://bugzilla.gnome.org/show_bug.cgi?id=659326
This commit is contained in:
Dan Winship 2011-10-15 16:59:59 -04:00 committed by Matthias Clasen
parent 117e534091
commit de834bed30
6 changed files with 231 additions and 92 deletions

View File

@ -1269,6 +1269,9 @@ g_app_info_get_default_for_uri_scheme
g_app_info_get_fallback_for_type
g_app_info_get_recommended_for_type
g_app_info_launch_default_for_uri
g_app_launch_context_setenv
g_app_launch_context_unsetenv
g_app_launch_context_get_environment
g_app_launch_context_get_display
g_app_launch_context_get_startup_notify_id
g_app_launch_context_launch_failed

View File

@ -500,10 +500,14 @@ g_app_info_get_icon (GAppInfo *appinfo)
* no way to detect this.
*
* Some URIs can be changed when passed through a GFile (for instance
* unsupported uris with strange formats like mailto:), so if you have
* a textual uri you want to pass in as argument, consider using
* unsupported URIs with strange formats like mailto:), so if you have
* a textual URI you want to pass in as argument, consider using
* g_app_info_launch_uris() instead.
*
* The launched application inherits the environment of the launching
* process, but it can be modified with g_app_launch_context_setenv() and
* g_app_launch_context_unsetenv().
*
* On UNIX, this function sets the <envar>GIO_LAUNCHED_DESKTOP_FILE</envar>
* environment variable with the path of the launched desktop file and
* <envar>GIO_LAUNCHED_DESKTOP_FILE_PID</envar> to the process
@ -733,6 +737,10 @@ g_app_info_delete (GAppInfo *appinfo)
G_DEFINE_TYPE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT);
struct _GAppLaunchContextPrivate {
char **envp;
};
/**
* g_app_launch_context_new:
*
@ -748,13 +756,96 @@ g_app_launch_context_new (void)
}
static void
g_app_launch_context_class_init (GAppLaunchContextClass *klass)
g_app_launch_context_finalize (GObject *object)
{
GAppLaunchContext *context = G_APP_LAUNCH_CONTEXT (object);
g_strfreev (context->priv->envp);
G_OBJECT_CLASS (g_app_launch_context_parent_class)->finalize (object);
}
static void
g_app_launch_context_init (GAppLaunchContext *launch_context)
g_app_launch_context_class_init (GAppLaunchContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (GAppLaunchContextPrivate));
object_class->finalize = g_app_launch_context_finalize;
}
static void
g_app_launch_context_init (GAppLaunchContext *context)
{
context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context, G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextPrivate);
}
/**
* g_app_launch_context_setenv:
* @context: a #GAppLaunchContext
* @variable: the environment variable to set
* @value: the value for to set the variable to.
*
* Arranges for @variable to be set to @value in the child's
* environment when @context is used to launch an application.
*
* Since: 2.32
*/
void
g_app_launch_context_setenv (GAppLaunchContext *context,
const char *variable,
const char *value)
{
if (!context->priv->envp)
context->priv->envp = g_get_environ ();
context->priv->envp =
g_environ_setenv (context->priv->envp, variable, value, TRUE);
}
/**
* g_app_launch_context_unsetenv:
* @context: a #GAppLaunchContext
* @variable: the environment variable to remove
*
* Arranges for @variable to be unset in the child's environment
* when @context is used to launch an application.
*
* Since: 2.32
*/
void
g_app_launch_context_unsetenv (GAppLaunchContext *context,
const char *variable)
{
if (!context->priv->envp)
context->priv->envp = g_get_environ ();
context->priv->envp =
g_environ_unsetenv (context->priv->envp, variable);
}
/**
* g_app_launch_context_get_environment:
* @context: a #GAppLaunchContext
*
* Gets the complete environment variable list to be passed to
* the child process when @context is used to launch an application.
* This is a %NULL-terminated array of strings, where each string has
* the form <literal>KEY=VALUE</literal>.
*
* Return value: (array zero-terminated=1) (transfer full): the
* child's environment
*
* Since: 2.32
*/
char **
g_app_launch_context_get_environment (GAppLaunchContext *context)
{
if (!context->priv->envp)
context->priv->envp = g_get_environ ();
return g_strdupv (context->priv->envp);
}
/**
@ -768,7 +859,7 @@ g_app_launch_context_init (GAppLaunchContext *launch_context)
* application, by setting the <envar>DISPLAY</envar> environment variable.
*
* Returns: a display string for the display.
**/
*/
char *
g_app_launch_context_get_display (GAppLaunchContext *context,
GAppInfo *info,

View File

@ -33,7 +33,7 @@ G_BEGIN_DECLS
#define G_TYPE_APP_INFO (g_app_info_get_type ())
#define G_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_APP_INFO, GAppInfo))
#define G_IS_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APP_INFO))
#define G_IS_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APP_INFO))
#define G_APP_INFO_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_APP_INFO, GAppInfoIface))
#define G_TYPE_APP_LAUNCH_CONTEXT (g_app_launch_context_get_type ())
@ -136,12 +136,12 @@ struct _GAppInfoIface
GType g_app_info_get_type (void) G_GNUC_CONST;
GAppInfo * g_app_info_create_from_commandline (const char *commandline,
const char *application_name,
GAppInfoCreateFlags flags,
GError **error);
const char *application_name,
GAppInfoCreateFlags flags,
GError **error);
GAppInfo * g_app_info_dup (GAppInfo *appinfo);
gboolean g_app_info_equal (GAppInfo *appinfo1,
GAppInfo *appinfo2);
GAppInfo *appinfo2);
const char *g_app_info_get_id (GAppInfo *appinfo);
const char *g_app_info_get_name (GAppInfo *appinfo);
const char *g_app_info_get_display_name (GAppInfo *appinfo);
@ -150,36 +150,36 @@ const char *g_app_info_get_executable (GAppInfo *appin
const char *g_app_info_get_commandline (GAppInfo *appinfo);
GIcon * g_app_info_get_icon (GAppInfo *appinfo);
gboolean g_app_info_launch (GAppInfo *appinfo,
GList *files,
GAppLaunchContext *launch_context,
GError **error);
GList *files,
GAppLaunchContext *launch_context,
GError **error);
gboolean g_app_info_supports_uris (GAppInfo *appinfo);
gboolean g_app_info_supports_files (GAppInfo *appinfo);
gboolean g_app_info_launch_uris (GAppInfo *appinfo,
GList *uris,
GAppLaunchContext *launch_context,
GError **error);
GList *uris,
GAppLaunchContext *launch_context,
GError **error);
gboolean g_app_info_should_show (GAppInfo *appinfo);
gboolean g_app_info_set_as_default_for_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
const char *content_type,
GError **error);
gboolean g_app_info_set_as_default_for_extension (GAppInfo *appinfo,
const char *extension,
GError **error);
const char *extension,
GError **error);
gboolean g_app_info_add_supports_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
const char *content_type,
GError **error);
gboolean g_app_info_can_remove_supports_type (GAppInfo *appinfo);
gboolean g_app_info_remove_supports_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
const char *content_type,
GError **error);
gboolean g_app_info_can_delete (GAppInfo *appinfo);
gboolean g_app_info_delete (GAppInfo *appinfo);
gboolean g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
const char *content_type,
GError **error);
GList * g_app_info_get_all (void);
GList * g_app_info_get_all_for_type (const char *content_type);
@ -188,12 +188,12 @@ GList * g_app_info_get_fallback_for_type (const gchar *content_type);
void g_app_info_reset_type_associations (const char *content_type);
GAppInfo *g_app_info_get_default_for_type (const char *content_type,
gboolean must_support_uris);
gboolean must_support_uris);
GAppInfo *g_app_info_get_default_for_uri_scheme (const char *uri_scheme);
gboolean g_app_info_launch_default_for_uri (const char *uri,
GAppLaunchContext *launch_context,
GError **error);
GAppLaunchContext *launch_context,
GError **error);
/**
* GAppLaunchContext:
@ -233,14 +233,22 @@ struct _GAppLaunchContextClass
GType g_app_launch_context_get_type (void) G_GNUC_CONST;
GAppLaunchContext *g_app_launch_context_new (void);
void g_app_launch_context_setenv (GAppLaunchContext *context,
const char *variable,
const char *value);
void g_app_launch_context_unsetenv (GAppLaunchContext *context,
const char *variable);
char ** g_app_launch_context_get_environment (GAppLaunchContext *context);
char * g_app_launch_context_get_display (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
GAppInfo *info,
GList *files);
char * g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
GAppInfo *info,
GList *files);
void g_app_launch_context_launch_failed (GAppLaunchContext *context,
const char * startup_notify_id);
const char * startup_notify_id);
G_END_DECLS

View File

@ -1126,9 +1126,8 @@ typedef struct
{
GSpawnChildSetupFunc user_setup;
gpointer user_setup_data;
char *display;
char *sn_id;
char *desktop_file;
char *pid_envvar;
} ChildSetupData;
static void
@ -1136,20 +1135,22 @@ child_setup (gpointer user_data)
{
ChildSetupData *data = user_data;
if (data->display)
g_setenv ("DISPLAY", data->display, TRUE);
if (data->sn_id)
g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
if (data->desktop_file)
if (data->pid_envvar)
{
gchar pid[20];
pid_t pid = getpid ();
char buf[20];
int i;
g_setenv ("GIO_LAUNCHED_DESKTOP_FILE", data->desktop_file, TRUE);
g_snprintf (pid, 20, "%ld", (long)getpid ());
g_setenv ("GIO_LAUNCHED_DESKTOP_FILE_PID", pid, TRUE);
/* 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)
@ -1238,7 +1239,7 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
GDBusConnection *session_bus;
gboolean completed = FALSE;
GList *old_uris;
char **argv;
char **argv, **envp;
int argc;
ChildSetupData data;
@ -1248,66 +1249,89 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
if (launch_context)
envp = g_app_launch_context_get_environment (launch_context);
else
envp = g_get_environ ();
do
{
GPid pid;
GList *launched_uris;
GList *iter;
char *display, *sn_id;
old_uris = uris;
if (!expand_application_parameters (info, &uris,
&argc, &argv, error))
goto out;
&argc, &argv, error))
goto out;
/* Get the subset of URIs we're launching with this process */
launched_uris = NULL;
for (iter = old_uris; iter != NULL && iter != uris; iter = iter->next)
launched_uris = g_list_prepend (launched_uris, iter->data);
launched_uris = g_list_prepend (launched_uris, iter->data);
launched_uris = g_list_reverse (launched_uris);
if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Unable to find terminal required for application"));
goto out;
}
goto out;
}
data.user_setup = user_setup;
data.user_setup_data = user_setup_data;
data.display = NULL;
data.sn_id = NULL;
data.desktop_file = info->filename;
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");
}
display = NULL;
sn_id = NULL;
if (launch_context)
{
GList *launched_files = create_files_for_uris (launched_uris);
{
GList *launched_files = create_files_for_uris (launched_uris);
data.display = g_app_launch_context_get_display (launch_context,
appinfo,
launched_files);
display = g_app_launch_context_get_display (launch_context,
appinfo,
launched_files);
envp = g_environ_setenv (envp, "DISPLAY", display, TRUE);
if (info->startup_notify)
data.sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
appinfo,
launched_files);
g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
g_list_free (launched_files);
}
if (info->startup_notify)
{
sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
appinfo,
launched_files);
envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE);
}
g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
g_list_free (launched_files);
}
if (!g_spawn_async (info->path,
argv,
NULL,
spawn_flags,
child_setup,
&data,
&pid,
error))
{
if (data.sn_id)
g_app_launch_context_launch_failed (launch_context, data.sn_id);
argv,
envp,
spawn_flags,
child_setup,
&data,
&pid,
error))
{
if (sn_id)
g_app_launch_context_launch_failed (launch_context, sn_id);
g_free (data.sn_id);
g_free (data.display);
g_free (display);
g_free (sn_id);
g_list_free (launched_uris);
goto out;
@ -1319,12 +1343,12 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
notify_desktop_launch (session_bus,
info,
pid,
data.display,
data.sn_id,
display,
sn_id,
launched_uris);
g_free (data.sn_id);
g_free (data.display);
g_free (display);
g_free (sn_id);
g_list_free (launched_uris);
g_strfreev (argv);
@ -1349,6 +1373,7 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
out:
g_strfreev (argv);
g_strfreev (envp);
return completed;
}
@ -1438,9 +1463,9 @@ g_desktop_app_info_launch (GAppInfo *appinfo,
*
* This guarantee allows additional control over the exact environment
* of the child processes, which is provided via a setup function
* @setup, as well as the process identifier of each child process via
* @pid_callback. See g_spawn_async() for more information about the
* semantics of the @setup function.
* @user_setup, as well as the process identifier of each child process
* via @pid_callback. See g_spawn_async() for more information about the
* semantics of the @user_setup function.
*
* Returns: %TRUE on successful launch, %FALSE otherwise.
*/

View File

@ -66,6 +66,9 @@ g_app_info_launch_default_for_uri
g_app_info_can_delete
g_app_info_delete
g_app_launch_context_new
g_app_launch_context_setenv
g_app_launch_context_unsetenv
g_app_launch_context_get_environment
g_app_launch_context_get_display
g_app_launch_context_get_startup_notify_id
g_app_launch_context_launch_failed

View File

@ -278,6 +278,15 @@ g_win32_app_info_launch (GAppInfo *appinfo,
}
#endif
/* FIXME: Need to do something with
* g_app_launch_context_get_environment()... ShellExecuteExW()
* doesn't have any way to pass an environment though. We need to
* either (a) update environment, ShellExecuteExW(), revert
* environment; or (b) find an API to figure out what app
* ShellExecuteExW() would launch, and then use g_spawn_async()
* instead.
*/
for (l = files; l != NULL; l = l->next)
{
char *path = g_file_get_path (l->data);