common,w32: Fix setting environment variables on Windows.
authorJustus Winter <justus@g10code.com>
Thu, 20 Oct 2016 14:45:18 +0000 (16:45 +0200)
committerJustus Winter <justus@g10code.com>
Thu, 20 Oct 2016 15:01:24 +0000 (17:01 +0200)
* common/sysutils.c (gnupg_setenv): Also update the environment block
maintained by the C runtime.
(gnupg_unsetenv): Likewise.
* tests/gpgscm/ffi.c (do_setenv): Fix error handling.

Signed-off-by: Justus Winter <justus@g10code.com>
common/sysutils.c
tests/gpgscm/ffi.c

index 2e663bc..85eb203 100644 (file)
@@ -754,38 +754,51 @@ gnupg_setenv (const char *name, const char *value, int overwrite)
   (void)value;
   (void)overwrite;
   return 0;
-#elif defined(HAVE_W32_SYSTEM)
-  if (!overwrite)
-    {
-      char tmpbuf[10];
-      if (GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf))
-        return 0; /* Exists but overwrite was not requested.  */
-    }
-  if (!SetEnvironmentVariable (name, value))
-    {
-      gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */
-      return -1;
-    }
-  return 0;
-#elif defined(HAVE_SETENV)
-  return setenv (name, value, overwrite);
 #else
-  char *buf;
+#if defined(HAVE_W32_SYSTEM)
+  /*  Windows maintains (at least) two sets of environment variables.
+      One set can be accessed by GetEnvironmentVariable and
+      SetEnvironmentVariable.  This set is inherited by the children.
+      The other set is maintained in the C runtime, and is accessed
+      using getenv and putenv.  We try to keep them in sync by
+      modifying both sets.  */
+  {
+    int exists;
+    char tmpbuf[10];
+    exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf);
+
+    if ((! exists || overwrite) && !SetEnvironmentVariable (name, value))
+      {
+        gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */
+        return -1;
+      }
+  }
+#endif
 
-  (void)overwrite;
-  if (!name || !value)
+#if defined(HAVE_SETENV)
+  return setenv (name, value, overwrite);
+#else
+  if (! getenv (name) || overwrite)
     {
-      gpg_err_set_errno (EINVAL);
-      return -1;
-    }
-  buf = xtrymalloc (strlen (name) + 1 + strlen (value) + 1);
-  if (!buf)
-    return -1;
-  strcpy (stpcpy (stpcpy (buf, name), "="), value);
+      char *buf;
+
+      (void)overwrite;
+      if (!name || !value)
+        {
+          gpg_err_set_errno (EINVAL);
+          return -1;
+        }
+      buf = xtrymalloc (strlen (name) + 1 + strlen (value) + 1);
+      if (!buf)
+        return -1;
+      strcpy (stpcpy (stpcpy (buf, name), "="), value);
 #if __GNUC__
 # warning no setenv - using putenv but leaking memory.
 #endif
-  return putenv (buf);
+      return putenv (buf);
+    }
+  return 0;
+#endif
 #endif
 }
 
@@ -796,30 +809,40 @@ gnupg_unsetenv (const char *name)
 #ifdef HAVE_W32CE_SYSTEM
   (void)name;
   return 0;
-#elif defined(HAVE_W32_SYSTEM)
+#else
+#if defined(HAVE_W32_SYSTEM)
+  /*  Windows maintains (at least) two sets of environment variables.
+      One set can be accessed by GetEnvironmentVariable and
+      SetEnvironmentVariable.  This set is inherited by the children.
+      The other set is maintained in the C runtime, and is accessed
+      using getenv and putenv.  We try to keep them in sync by
+      modifying both sets.  */
   if (!SetEnvironmentVariable (name, NULL))
     {
       gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */
       return -1;
     }
-  return 0;
-#elif defined(HAVE_UNSETENV)
+#endif
+#if defined(HAVE_UNSETENV)
   return unsetenv (name);
 #else
-  char *buf;
-
-  if (!name)
-    {
-      gpg_err_set_errno (EINVAL);
+  {
+    char *buf;
+
+    if (!name)
+      {
+        gpg_err_set_errno (EINVAL);
+        return -1;
+      }
+    buf = xtrystrdup (name);
+    if (!buf)
       return -1;
-    }
-  buf = xtrystrdup (name);
-  if (!buf)
-    return -1;
 #if __GNUC__
 # warning no unsetenv - trying putenv but leaking memory.
 #endif
-  return putenv (buf);
+    return putenv (buf);
+  }
+#endif
 #endif
 }
 
index a0fbe2e..8e21ba6 100644 (file)
@@ -236,7 +236,9 @@ do_setenv (scheme *sc, pointer args)
   FFI_ARG_OR_RETURN (sc, char *, value, string, args);
   FFI_ARG_OR_RETURN (sc, int, overwrite, bool, args);
   FFI_ARGS_DONE_OR_RETURN (sc, args);
-  FFI_RETURN_ERR (sc, gnupg_setenv (name, value, overwrite));
+  if (gnupg_setenv (name, value, overwrite))
+    FFI_RETURN_ERR (sc, gpg_error_from_syserror ());
+  FFI_RETURN (sc);
 }
 
 static pointer