joyent / libuv

Go to
https://github.com/libuv/libuv
3.27k stars 654 forks source link

uv_spawn env+path semantics #122

Open piscisaureus opened 13 years ago

piscisaureus commented 13 years ago

uv_spawn uses the PATH environment variable to locate the executable to be spawned.

On unix, if the user explicitly supplies an environment block, the PATH from this environment is used. If the supplied environment does not contain a PATH variable, :/bin:/usr/bin is used as a fallback.

On windows the search path from the parent process is always used.

This semantic difference may need to be resolved.

piscisaureus commented 13 years ago

Dropping the path search code entirely and using the search mechanics built into CreateProcess should also be considered.

The downside is that it uses a different search rules than what (I think) people are familiar with from cmd.exe / spawnvpe(). On the upside, we could delete quite some code. Also the CreateProcess search rules are well-documented.

DrPizza commented 13 years ago

There are many options. They all suck to a greater or lesser extent. Windows has a bunch of rules in different places. I'm not really sure users will have any consistent expectations.

Not directly used, but another Win32 lookup behaviour is found in SearchPath(), which will check $CWD and then $PATH, or $PATH followed by $CWD, depending on options.

With the exception of SearchPath(), none of these functions has a search API available. _spawnvpe() is, I believe, directly equivalent to:

if(filename contains '\' or '/') {
  CreateProcess(filename, commandline, ...); // try absolute filenames directly, do nothing else
} else {
  filename = SearchPath(filename, ...); // search CWD and then PATH
  CreateProcess(filename, commandline, ...);
}

I don't think trying to replicate ShellExecute() or cmd.exe is a worthwhile endeavour. The rules are complex, not documented, and it'll just be a nightmare of complexity.

CreateProcess() using lpApplicationName is far and away the simplest and easiest to understand, and easiest to implement, but it is probably too user-hostile.

CreateProcess() without lpApplicationName uses well-defined, reasonably well-known rules. We get a lot of magic for free, including $CWD and $PATH searching. We do lose the ability to spawn executables with long filenames (the executable must have a fully-qualified filename that is no longer than MAX_PATH), but Explorer itself struggles with such files, so we're not any worse than the operating system itself. Calling this is only marginally more dificult; we have to make sure that the program name is wrapped in quote marks if it contains spaces, but that's all the processing we'd need to do.

The _spawnvpe() rules are tolerably defined, probably not widely known, closely analogous to what happens on UNIX platforms, and if we use SearchPath() to implement them (as the pseudocode above), not too tricky to implement.

I think any of these three options is reasonable. I think it boils down to a question of whether we want:

  1. No magic; clear, predictable, simple rules, but more work on the part of the caller (CreateProcess(), lpApplicationName)
  2. Lots of standard magic, loss of an esoteric corner case (paths longer than MAX_PATH) (CreateProcess(), no lpApplicationName)
  3. Close approximation to UNIX rules (CreateProcess() with lpApplicationName, using the raw filename if an absolute path is provided, and SearchPath() if a bare filename is used).

I am a strong -1 on the current code--it's complex, it doesn't correspond with any built-in API or C/POSIX function, its rules aren't really documented to callers--and a +0 on each of the above three. Perhaps a +0.5 on option 3.