[glib] GAppLaunchContext: add environment-manipulating functions



commit de834bed306565c0652050665eafff4dfcdf0d8b
Author: Dan Winship <danw gnome org>
Date:   Sat Oct 15 16:59:59 2011 -0400

    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

 docs/reference/gio/gio-sections.txt |    3 +
 gio/gappinfo.c                      |   99 ++++++++++++++++++++++-
 gio/gappinfo.h                      |   66 +++++++++-------
 gio/gdesktopappinfo.c               |  147 ++++++++++++++++++++--------------
 gio/gio.symbols                     |    3 +
 gio/gwin32appinfo.c                 |    9 ++
 6 files changed, 233 insertions(+), 94 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 7d7def3..ecfbc1c 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -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
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index 231a6c5..666d2b0 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -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_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_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 *launch_context)
+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,
diff --git a/gio/gappinfo.h b/gio/gappinfo.h
index bf44420..b6786d6 100644
--- a/gio/gappinfo.h
+++ b/gio/gappinfo.h
@@ -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
 
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 381dbe4..b68d50e 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -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];
-
-      g_setenv ("GIO_LAUNCHED_DESKTOP_FILE", data->desktop_file, TRUE);
+      pid_t pid = getpid ();
+      char buf[20];
+      int i;
 
-      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);
-
-	  data.display = g_app_launch_context_get_display (launch_context,
-						           appinfo,
-						           launched_files);
-
-	  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);
-	}
+        {
+          GList *launched_files = create_files_for_uris (launched_uris);
+
+          display = g_app_launch_context_get_display (launch_context,
+                                                      appinfo,
+                                                      launched_files);
+          envp = g_environ_setenv (envp, "DISPLAY", display, TRUE);
+
+          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.
  */
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 029c102..0ce8fd5 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -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
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index a441251..cb0093f 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -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);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]