Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

uv_spawn env+path semantics #122

Open
piscisaureus opened this issue Aug 1, 2011 · 2 comments
Open

uv_spawn env+path semantics #122

piscisaureus opened this issue Aug 1, 2011 · 2 comments

Comments

@piscisaureus
Copy link

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
Copy link
Author

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
Copy link
Contributor

DrPizza commented Aug 18, 2011

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.

  • ShellExecute() is probably the most complex API. It checks working directories, PATH, various registry keys, and probably more. Awful.
  • cmd.exe's behaviour is perhaps even more complex than ShellExecute(), since it seems to do most of the things ShellExecute() does, with the added bonus of also considering cmd.exe built-ins and the PATHEXT environment variable.
  • CreateProcess()'s rules with lpApplicationName are very simple and easy to understand. They're also utterly unforgiving: no filename extension is added (no automatic .exe or .com or anything else), no PATH searching. If you give it an absolute path to a filename it'll try that absolute path and no other; if you give it a bare filename (or maybe a relative path? It doesn't specify) it'll try $CWD\filename. And that's it.
  • CreateProcess() without lpApplicationName does a search; application folder, $CWD, system32, system, \windows, $PATH, in that order. It will try each location with and without a .exe extension.
  • VC++'s spawn-with-path _spawnvpe() will first try CreateProcess()'s lpApplicationName strategy. If that fails, and if a bare filename was supplied (with neither an absolute nor a relative path), it will then try each directory in the parent process's $PATH. Even if a child environment $PATH is specified, it will not be examined.

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants