No access to list of environment variables possible from within Swift


(Johannes Schriewer) #1

Hello,

Currently there is no list of environment variables visible from within a Swift program.

You can use `getenv` and `setenv` from the C Standard library to access a variable of which you know the name, but sometimes that’s not enough.

One simple case is when using `posix_spawn` to execute a child-process. This API needs a list of environment variables to present to the child process. Usually (meaning in C) you could work around that by inheriting the environment by using `fork` but that’s not allowed in Swift.

So if you want a child process to inherit the environment you’ll have to have a list.

Foundation does use a CoreFoundation method to get that list as seen here:
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSTask.swift#L226

If you want to stay in Swift there is no access.

So how can a C program get to the environment? That’s rather simple, there are 2 ways:
1. The (often ignored) third parameter to `main()` is a `char **env` which is just a list of all environment variables that ends with a `NULL` entry
2. Include `unistd.h` and use the exported variable `extern char **environ`

So what I would propose is just harnessing the third `main()` parameter and creating a new static computed property named `environment`, that is a dictionary of `[String:String]`, on the `Process` enum. There would be a bit of parsing because the environment list has the format `Key=Value` so the string would have to be split at the first equals character.

There is a Bug on the bugtracker: https://bugs.swift.org/browse/SR-1636
And I have built a patch already in a pull-request that was shot down recently: https://github.com/apple/swift/pull/2757

I tried to keep as closely as possible to the implementation of `argv`/`Process.arguments` as that comes from the same source as the environment list. The patch touches `SILGen.cpp`, `GlobalObjects.cpp/h` and of course `Process.swift` and was created as small as possible.

If you compile with that patch you’ll get access to `Process.environment`, which is static (does not change when using `setenv` to change or add a variable) and just contains a dictionary with the environment, and `Process.unsafeEnvp` which is like `unsafeArgv` the original representation of the value at it’s original location.

As this patch only adds another static variable it will not affect existing Swift programs.

Thanks for reading,
Johannes Schriewer