Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-18234

environment var not set on job process, and not propagated to forked sub-process

    XMLWordPrintable

    Details

    • Similar Issues:

      Description

      the EnvInject plugin does not really set the "environment variables" at the OS level, using native POSIX call "putenv()".
      It is only available in some jenkins sub-tasks when calling appropriate jenkins API to exec tasks... but not when calling plain old java code "System.exec()" within a Mojo.... so most Mojo don't work (my problem was when using maven-dotnet-plugin, that forks to execute "Msbuild.exe")

      When doing few search over google to set an environment variable in java, I found a piece of code that might be "portable enough", using JNA native calls to standards dlls (msvcrt on Windows / libc on Linux, which solve 100% of my needs)

      It simply exposes a method "putenv()" to be called as this:

      SetEnvUtil.putenv("testvar", "123", 1);

      So my request is to add such calls within the EnvInject plugin, but I don't know how to patch exactly the EnvInject jenkins plugin

      It depends on the JNA jar, for example using the maven dependency

          <dependency>
      	  <groupId>net.java.dev.jna</groupId>
      	  <artifactId>jna</artifactId>
      	  <version>3.4.0</version>
          </dependency>
      

      Here is the utility class

      SetEnvUtil.java
      import com.sun.jna.Library;
      import com.sun.jna.Native;
      
      public class SetEnvUtil {
      
          // Inside the Environment class...
          public interface WinLibC extends Library {
              public String getenv(String name);
              public int _putenv(String name);
          }
      
          public interface LinuxLibC extends Library {
              public String getenv(String name);
              public int setenv(String name, String value, int overwrite);
              public int unsetenv(String name);
          }
      
          private static Object libc;
          static {
              String osName = System.getProperty("os.name");
              if (osName.equals("Linux")) {
                  libc = Native.loadLibrary("c", LinuxLibC.class);
              } else {
                  libc = Native.loadLibrary("msvcrt", WinLibC.class);
              }
          }
      
          public static String getenv(String name) {
              if (libc instanceof LinuxLibC) {
                  return ((LinuxLibC) libc).getenv(name);
              } else {
                  return ((WinLibC) libc).getenv(name);
              }
          }
          
          public static int setenv(String name, String value, int overwrite) {
              if (libc instanceof LinuxLibC) {
                  return ((LinuxLibC) libc).setenv(name, value, overwrite);
              } else {
                  return ((WinLibC) libc)._putenv(name + "=" + value);
              }
          }
      
          public static int unsetenv(String name) {
              if (libc instanceof LinuxLibC) {
                  return ((LinuxLibC) libc).unsetenv(name);
              } else {
                  return ((WinLibC) libc)._putenv(name + "=");
              }
          }
      
      }
      

      I have done a simple Main for testing purpose, so you can convinced it works simply:

      SetEnvMain.java
      public class SetEnvMain {
      
          public static final String TEST_VAR_NAME  = "TEST_VAR";
      
          public static void main(String[] args) {
              dump("before setenv() ... ");
              
              // *** first setenv()  : add new env var 
              SetEnvUtil.setenv(TEST_VAR_NAME , "123", 1);
              
              dump("after setenv(\"" + TEST_VAR_NAME + ", \"123\") ... ");
      
              // *** second call setenv()  : override existing var 
              SetEnvUtil.setenv(TEST_VAR_NAME , "456", 1);
              
              dump("after setenv(\"" + TEST_VAR_NAME + ", \"456\") ... ");
          }
          
          public static void dump(String messagePrefix) {
              String var_native = SetEnvUtil.getenv(TEST_VAR_NAME);
              String var_cached = System.getenv(TEST_VAR_NAME); // FALSE... cached by java!
              System.out.println(messagePrefix + " " + TEST_VAR_NAME + ": " + var_native + ", (cached): " + var_cached);
          }
          
      }
      

        Attachments

          Activity

          arnaud_nauwynck Arnaud Nauwynck created issue -
          arnaud_nauwynck Arnaud Nauwynck made changes -
          Field Original Value New Value
          Description the EnvInject plugin does not really set the "environment variables" at the OS level, using native POSIX call "putenv()".
          It is only available in some jenkins sub-tasks when calling appropriate jenkins API to exec tasks... but not when calling plain old java code "System.exec()" within a Mojo.... so most Mojo don't work (my problem was when using maven-dotnet-plugin, that forks to execute "Msbuild.exe")

          When doing few search over google to set an environment variable in java, I found a piece of code that might be "portable enough", using JNA native calls to standards dlls (msvcrt on Windows / libc on Linux, which solve 100% of my needs)

          It simply exposes a method "putenv()" to be called as this:
          {quote}SetEnvUtil.putenv("testvar", "123", 1); {quote}

          So my request is to add such calls within the EnvInject plugin, but I don't know how to patch exactly the EnvInject jenkins plugin


          It depends on the JNA jar, for example using the maven dependency
          {quote}
              <dependency>
          <groupId>net.java.dev.jna</groupId>
          <artifactId>jna</artifactId>
          <version>3.4.0</version>
              </dependency>
          {quote}

          Here is the utility class
          {quote}
          import com.sun.jna.Library;
          import com.sun.jna.Native;

          public class SetEnvUtil {

              // Inside the Environment class...
              public interface WinLibC extends Library {
                  public String getenv(String name);
                  public int _putenv(String name);
              }

              public interface LinuxLibC extends Library {
                  public String getenv(String name);
                  public int setenv(String name, String value, int overwrite);
                  public int unsetenv(String name);
              }

              private static Object libc;
              static {
                  String osName = System.getProperty("os.name");
                  if (osName.equals("Linux")) {
                      libc = Native.loadLibrary("c", LinuxLibC.class);
                  } else {
                      libc = Native.loadLibrary("msvcrt", WinLibC.class);
                  }
              }

              public static String getenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).getenv(name);
                  } else {
                      return ((WinLibC) libc).getenv(name);
                  }
              }
              
              public static int setenv(String name, String value, int overwrite) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).setenv(name, value, overwrite);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=" + value);
                  }
              }

              public static int unsetenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).unsetenv(name);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=");
                  }
              }

          }
          {quote}



          I have done a simple Main for testing purpose, so you can convinced it works simply:
          {quote}
          public class SetEnvMain {

              public static final String TEST_VAR_NAME = "TEST_VAR";

              public static void main(String[] args) {
                  dump("before setenv() ... ");
                  
                  // *** first setenv() : add new env var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "123", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"123\") ... ");

                  // *** second call setenv() : override existing var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "456", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"456\") ... ");
              }
              
              public static void dump(String messagePrefix) {
                  String var_native = SetEnvUtil.getenv(TEST_VAR_NAME);
                  String var_cached = System.getenv(TEST_VAR_NAME); // FALSE... cached by java!
                  System.out.println(messagePrefix + " " + TEST_VAR_NAME + ": " + var_native + ", (cached): " + var_cached);
              }
              
          }
          {quote}
          the EnvInject plugin does not really set the "environment variables" at the OS level, using native POSIX call "putenv()".
          It is only available in some jenkins sub-tasks when calling appropriate jenkins API to exec tasks... but not when calling plain old java code "System.exec()" within a Mojo.... so most Mojo don't work (my problem was when using maven-dotnet-plugin, that forks to execute "Msbuild.exe")

          When doing few search over google to set an environment variable in java, I found a piece of code that might be "portable enough", using JNA native calls to standards dlls (msvcrt on Windows / libc on Linux, which solve 100% of my needs)

          It simply exposes a method "putenv()" to be called as this:
          {quote}SetEnvUtil.putenv("testvar", "123", 1); {quote}

          So my request is to add such calls within the EnvInject plugin, but I don't know how to patch exactly the EnvInject jenkins plugin


          It depends on the JNA jar, for example using the maven dependency
          {{monospaced}}
              <dependency>
          <groupId>net.java.dev.jna</groupId>
          <artifactId>jna</artifactId>
          <version>3.4.0</version>
              </dependency>
          {{monospaced}}

          Here is the utility class
          {{monospaced}}
          import com.sun.jna.Library;
          import com.sun.jna.Native;

          public class SetEnvUtil {

              // Inside the Environment class...
              public interface WinLibC extends Library {
                  public String getenv(String name);
                  public int _putenv(String name);
              }

              public interface LinuxLibC extends Library {
                  public String getenv(String name);
                  public int setenv(String name, String value, int overwrite);
                  public int unsetenv(String name);
              }

              private static Object libc;
              static {
                  String osName = System.getProperty("os.name");
                  if (osName.equals("Linux")) {
                      libc = Native.loadLibrary("c", LinuxLibC.class);
                  } else {
                      libc = Native.loadLibrary("msvcrt", WinLibC.class);
                  }
              }

              public static String getenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).getenv(name);
                  } else {
                      return ((WinLibC) libc).getenv(name);
                  }
              }
              
              public static int setenv(String name, String value, int overwrite) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).setenv(name, value, overwrite);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=" + value);
                  }
              }

              public static int unsetenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).unsetenv(name);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=");
                  }
              }

          }
          {{monospaced}}



          I have done a simple Main for testing purpose, so you can convinced it works simply:
          {{monospaced}}
          public class SetEnvMain {

              public static final String TEST_VAR_NAME = "TEST_VAR";

              public static void main(String[] args) {
                  dump("before setenv() ... ");
                  
                  // *** first setenv() : add new env var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "123", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"123\") ... ");

                  // *** second call setenv() : override existing var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "456", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"456\") ... ");
              }
              
              public static void dump(String messagePrefix) {
                  String var_native = SetEnvUtil.getenv(TEST_VAR_NAME);
                  String var_cached = System.getenv(TEST_VAR_NAME); // FALSE... cached by java!
                  System.out.println(messagePrefix + " " + TEST_VAR_NAME + ": " + var_native + ", (cached): " + var_cached);
              }
              
          }
          {{monospaced}}
          arnaud_nauwynck Arnaud Nauwynck made changes -
          Description the EnvInject plugin does not really set the "environment variables" at the OS level, using native POSIX call "putenv()".
          It is only available in some jenkins sub-tasks when calling appropriate jenkins API to exec tasks... but not when calling plain old java code "System.exec()" within a Mojo.... so most Mojo don't work (my problem was when using maven-dotnet-plugin, that forks to execute "Msbuild.exe")

          When doing few search over google to set an environment variable in java, I found a piece of code that might be "portable enough", using JNA native calls to standards dlls (msvcrt on Windows / libc on Linux, which solve 100% of my needs)

          It simply exposes a method "putenv()" to be called as this:
          {quote}SetEnvUtil.putenv("testvar", "123", 1); {quote}

          So my request is to add such calls within the EnvInject plugin, but I don't know how to patch exactly the EnvInject jenkins plugin


          It depends on the JNA jar, for example using the maven dependency
          {{monospaced}}
              <dependency>
          <groupId>net.java.dev.jna</groupId>
          <artifactId>jna</artifactId>
          <version>3.4.0</version>
              </dependency>
          {{monospaced}}

          Here is the utility class
          {{monospaced}}
          import com.sun.jna.Library;
          import com.sun.jna.Native;

          public class SetEnvUtil {

              // Inside the Environment class...
              public interface WinLibC extends Library {
                  public String getenv(String name);
                  public int _putenv(String name);
              }

              public interface LinuxLibC extends Library {
                  public String getenv(String name);
                  public int setenv(String name, String value, int overwrite);
                  public int unsetenv(String name);
              }

              private static Object libc;
              static {
                  String osName = System.getProperty("os.name");
                  if (osName.equals("Linux")) {
                      libc = Native.loadLibrary("c", LinuxLibC.class);
                  } else {
                      libc = Native.loadLibrary("msvcrt", WinLibC.class);
                  }
              }

              public static String getenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).getenv(name);
                  } else {
                      return ((WinLibC) libc).getenv(name);
                  }
              }
              
              public static int setenv(String name, String value, int overwrite) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).setenv(name, value, overwrite);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=" + value);
                  }
              }

              public static int unsetenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).unsetenv(name);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=");
                  }
              }

          }
          {{monospaced}}



          I have done a simple Main for testing purpose, so you can convinced it works simply:
          {{monospaced}}
          public class SetEnvMain {

              public static final String TEST_VAR_NAME = "TEST_VAR";

              public static void main(String[] args) {
                  dump("before setenv() ... ");
                  
                  // *** first setenv() : add new env var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "123", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"123\") ... ");

                  // *** second call setenv() : override existing var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "456", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"456\") ... ");
              }
              
              public static void dump(String messagePrefix) {
                  String var_native = SetEnvUtil.getenv(TEST_VAR_NAME);
                  String var_cached = System.getenv(TEST_VAR_NAME); // FALSE... cached by java!
                  System.out.println(messagePrefix + " " + TEST_VAR_NAME + ": " + var_native + ", (cached): " + var_cached);
              }
              
          }
          {{monospaced}}
          the EnvInject plugin does not really set the "environment variables" at the OS level, using native POSIX call "putenv()".
          It is only available in some jenkins sub-tasks when calling appropriate jenkins API to exec tasks... but not when calling plain old java code "System.exec()" within a Mojo.... so most Mojo don't work (my problem was when using maven-dotnet-plugin, that forks to execute "Msbuild.exe")

          When doing few search over google to set an environment variable in java, I found a piece of code that might be "portable enough", using JNA native calls to standards dlls (msvcrt on Windows / libc on Linux, which solve 100% of my needs)

          It simply exposes a method "putenv()" to be called as this:
          {quote}SetEnvUtil.putenv("testvar", "123", 1); {quote}

          So my request is to add such calls within the EnvInject plugin, but I don't know how to patch exactly the EnvInject jenkins plugin


          It depends on the JNA jar, for example using the maven dependency
          {code}
              <dependency>
          <groupId>net.java.dev.jna</groupId>
          <artifactId>jna</artifactId>
          <version>3.4.0</version>
              </dependency>
          {code}

          Here is the utility class
          {code:title=SetEnvUtil.java}
          import com.sun.jna.Library;
          import com.sun.jna.Native;

          public class SetEnvUtil {

              // Inside the Environment class...
              public interface WinLibC extends Library {
                  public String getenv(String name);
                  public int _putenv(String name);
              }

              public interface LinuxLibC extends Library {
                  public String getenv(String name);
                  public int setenv(String name, String value, int overwrite);
                  public int unsetenv(String name);
              }

              private static Object libc;
              static {
                  String osName = System.getProperty("os.name");
                  if (osName.equals("Linux")) {
                      libc = Native.loadLibrary("c", LinuxLibC.class);
                  } else {
                      libc = Native.loadLibrary("msvcrt", WinLibC.class);
                  }
              }

              public static String getenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).getenv(name);
                  } else {
                      return ((WinLibC) libc).getenv(name);
                  }
              }
              
              public static int setenv(String name, String value, int overwrite) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).setenv(name, value, overwrite);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=" + value);
                  }
              }

              public static int unsetenv(String name) {
                  if (libc instanceof LinuxLibC) {
                      return ((LinuxLibC) libc).unsetenv(name);
                  } else {
                      return ((WinLibC) libc)._putenv(name + "=");
                  }
              }

          }
          {code}



          I have done a simple Main for testing purpose, so you can convinced it works simply:
          {code:title=SetEnvMain.java}
          public class SetEnvMain {

              public static final String TEST_VAR_NAME = "TEST_VAR";

              public static void main(String[] args) {
                  dump("before setenv() ... ");
                  
                  // *** first setenv() : add new env var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "123", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"123\") ... ");

                  // *** second call setenv() : override existing var
                  SetEnvUtil.setenv(TEST_VAR_NAME , "456", 1);
                  
                  dump("after setenv(\"" + TEST_VAR_NAME + ", \"456\") ... ");
              }
              
              public static void dump(String messagePrefix) {
                  String var_native = SetEnvUtil.getenv(TEST_VAR_NAME);
                  String var_cached = System.getenv(TEST_VAR_NAME); // FALSE... cached by java!
                  System.out.println(messagePrefix + " " + TEST_VAR_NAME + ": " + var_native + ", (cached): " + var_cached);
              }
              
          }
          {code}
          Hide
          ravi116 Ravi Varanasi added a comment -

          Is there a workaround for this ? Can you set the environment variables through Manage Jenkins ?

          Show
          ravi116 Ravi Varanasi added a comment - Is there a workaround for this ? Can you set the environment variables through Manage Jenkins ?
          Hide
          arnaud_nauwynck Arnaud Nauwynck added a comment -

          I did not found a simple solution.
          My workaround was to have several jenkins slave and schedule jobs with different env variables to different slaves! I am still interested by a better solution.

          Show
          arnaud_nauwynck Arnaud Nauwynck added a comment - I did not found a simple solution. My workaround was to have several jenkins slave and schedule jobs with different env variables to different slaves! I am still interested by a better solution.
          rtyler R. Tyler Croy made changes -
          Workflow JNJira [ 149539 ] JNJira + In-Review [ 177398 ]
          oleg_nenashev Oleg Nenashev made changes -
          Labels windows

            People

            Assignee:
            gbois Gregory Boissinot
            Reporter:
            arnaud_nauwynck Arnaud Nauwynck
            Votes:
            3 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated: