-
Notifications
You must be signed in to change notification settings - Fork 553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows:Have native CommandLine in Process #998
Conversation
You need to update the schema here: https://github.com/opencontainers/runtime-spec/blob/master/schema/config-schema.json#L54 |
Would the solution be to not ignore quotes when converting to JSON? i.e. ["cmd", "/S", "/C", "mkdir", "\"c:/foo\""] |
4707e4a
to
cd10319
Compare
@thaJeztah No, that still won't work (argv escaping) |
@jterry75 Updated |
eb5e1fb
to
6a6ef8e
Compare
Fixed CI issues. Ready for review. |
@jhowardmsft - Might be worth adding a few unit tests to https://github.com/opencontainers/runtime-tools once this merges with the different Windows |
@@ -50,13 +50,15 @@ | |||
"process": { | |||
"type": "object", | |||
"required": [ | |||
"cwd", | |||
"args" | |||
"cwd" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@crosbymichael - Is there a way to do required: oneOf{ "args", "commandLine" }
or something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Hold fire merging this until I have verified the whole end-to-end scenario |
OK, end-to-end has been validated. This is good to go if another maintainer can review please. Thanks. |
@tianon Any chance you could look at this quickly? |
This really makes no sense to me -- don't Windows applications still have In rough Go pseudocode, I would imagine something like |
@tianon I guess it doesn't when having a Linux background ;) If you look at the |
Don't shoot the messenger. I'm just saying what we have to work with here 😇 |
I do come from a Windows background, but it's been a very long time now
since I read the "CreateProcess" docs, sorry 😅
So, I guess to put this another way, why does it make sense to embed this
legacy design quirk into the runtime specification? It seems to me it'd
make more sense to link to docs about how to quote an argv properly for use
in a Windows command line (like Go does) than to complicate the spec by
having two incompatible and platform specific ways to specify the same
thing? Isn't consistency and standardization what we're going for here?
|
I forgot. Sorry 😇 Unfortunately legacy design quirk is reality/how Windows still works today. If I had a time machine things might be different..... Perhaps if you see this commit moby/moby@3d1c46d (in flux, although capacitor free, it's still being iterated on, but look at moby/moby#38541 for latest), it will start to make more sense. It's absolutely impossible to get moby to use containerd for its runtime, while preserving existing dockerfile compatibility, without moving to what is natural on Windows for launching process which is a full command line, not a set of argv which need escaping prior to building a command line. Consistency and standardisation are all well and good when there is parity between platforms. This is not the case though here. |
@tianon See my message on slack too. |
Perhaps a real example where fidelity is lost might help.
Running this:
And showing how the double quotes are required in the real world: |
Ah, so if I'm understanding correctly, |
@opencontainers/runtime-spec-maintainers Any other comments or questions before I merge this? |
I get why the argv array can be lossy, it's a shame we don't have a generic fix rather than a platform specific thing. But so it goes |
I said this offline to @tianon, but for completeness, adding it here too: You essentially have it right, but there's a tiny bit of clarification. Indeed what you say is the case for a GUI app using standard Windows entrypoints. However, for all command line apps using the standard C or C++ runtime, even go programs, they too get invoked by Windows as a command line. In fact, ALL programs on Windows gets invoked by a command line. Not by an array of arguments. So you would still have a C-style In go, that's the In the Microsoft C++ startup code, it does exactly the same processing as documented here prior to invoking main itself: https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments?view=vs-2017. The processing it describes is documented https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx and https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw |
I see a lot of background is captured in the discussion here on GitHub; should a summary be created and added as commit message? |
Sure, I'll add that. |
Signed-off-by: John Howard <jhoward@microsoft.com> This adds a new field `CommandLine` in the `Process` structure for use on Windows. A Windows runtime will check this first and use it as-is when launching WCOW processes in a container. If omitted, the Windows runtime will fall back to the previous behaviour of escaping (eg Windows.EscapeArg in golang) each of the `args` array elements, and space-concatenating them. The reason for this change is to avoid loss of fidelity for launching processes. One such example is the following: `cmd /S /C mkdir "c:/foo"` When parsed into a JSON array such as `Args`, it becomes 5 elements - cmd - /S - /C - mkdir - c:/foo Here, note the lost information being the double-quotes around `c:/foo`. When using the required contenation, space separation required on Windows, (https://github.com/golang/sys/blob/c4afb3effaa53fd9a06ca61262dc7ce8df4c081b/windows/exec_windows.go#L9-L18), this becomes the following command line: `cmd /S /C mkdir c:/foo` When the double-quotes are missing, mkdir would fail, but with the double-quotes, it succeeds as expected: ``` C:\>cmd /s /c mkdir c:/foo The syntax of the command is incorrect. C:\>cmd /s /c mkdir "c:/foo" C:\> ``` The addition of a full `commandLine` in Process for use on Windows alleviates issues where fidelity can be lost. Some further background: For historical reasons, Windows only has native support for launching processes using a command line. It does not support an argument array as per Linux. See the `CreateProcess` API documentation on MSDN. What happens under the covers is that prior to invoking a programs main, the language runtime will convert the command line to a set of argv[] suach as in the C-style `int main(int argc, char* argv), or the golang `os.Args` prior to the programs `main` being invoked, using Windows- specific rules. In go, that's the `commandLineToArgv` function which is called in the init() function of the os package https://github.com/golang/go/blob/ff7b245a31394b700a252fd547cf16ad0ad838b6/src/os/exec_windows.go#L100, In the Microsoft C++ startup code, it does exactly the same processing as documented here prior to invoking main itself: https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments?view=vs-2017. The processing it describes is documented at https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx and https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw Some related links which provide a lot more information about the very specific (and unique to Windows) command line escaping rules, and handling of them are below: https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ https://stackoverflow.com/questions/31838469/how-do-i-convert-argv-to-lpcommandline-parameter-of-createprocess https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments?view=vs-2017
6a6ef8e
to
deb4d95
Compare
@thaJeztah @tianon @crosbymichael Have updated the commit message to capture the discussion above. Unfortunately, that means that the approvals have been lost due to an updated commit ID. |
ok, we are unblocked |
Thanks for the 😍 commit message, @jhowardmsft |
Signed-off-by: John Howard jhoward@microsoft.com
Windows natively doesn't launch processes through an array of arguments. Instead, it uses a command line single string, which the startup code (eg https://docs.microsoft.com/en-us/previous-versions/ms880421(v=msdn.10)) interprets into an argv set. Golang does identical parsing of the command-line on Windows in commandLineToArgv: https://github.com/golang/go/blob/ff7b245a31394b700a252fd547cf16ad0ad838b6/src/os/exec_windows.go#L100-L174
See the MSDN
CreateProcess
documentation for the actual API into Windows: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessaUsing args alone on Windows has possible loss of fidelity causing some commands to not operate as expected. (This has long been the case on Windows in docker for example). A contrived (but real) example in a test case in the moby repo where fidelity is lost is the following command-line:
cmd /S /C mkdir "c:/foo"
When parsed into a JSON array (such as the args), this becomes 5 elements as follows:
cmd
/S
/C
mkdir
c:/foo
Note specifically that the lost information are the double-quotes around
c:/foo
. When using the required contatenation/space separation, and argument escaping required on Windows (https://github.com/golang/sys/blob/c4afb3effaa53fd9a06ca61262dc7ce8df4c081b/windows/exec_windows.go#L9-L18), this becomes the following command line:cmd /S /C mkdir c:/foo
When the double-quotes are missing, mkdir here fails, but with the double-quotes succeeds as expected, as shown in the following screenshot.
The addition of a full
commandLine
in Process for use on Windows alleviates issues where fidelity can be lost.If commandLine is omitted, the Windows runtime will fall-back to existing behaviour of contatenation/escaping described previously.
@crosbymichael PTAL
@dmcgowan & @jterry75 FYI.