Skip to content

Commit

Permalink
Windows:Have native CommandLine in Process
Browse files Browse the repository at this point in the history
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
  • Loading branch information
John Howard committed Feb 7, 2019
1 parent 1722abf commit deb4d95
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 5 deletions.
7 changes: 5 additions & 2 deletions config.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,11 @@ For POSIX platforms the `mounts` structure has the following fields:
* **`cwd`** (string, REQUIRED) is the working directory that will be set for the executable.
This value MUST be an absolute path.
* **`env`** (array of strings, OPTIONAL) with the same semantics as [IEEE Std 1003.1-2008's `environ`][ieee-1003.1-2008-xbd-c8.1].
* **`args`** (array of strings, REQUIRED) with similar semantics to [IEEE Std 1003.1-2008 `execvp`'s *argv*][ieee-1003.1-2008-functions-exec].
This specification extends the IEEE standard in that at least one entry is REQUIRED, and that entry is used with the same semantics as `execvp`'s *file*.
* **`args`** (array of strings, OPTIONAL) with similar semantics to [IEEE Std 1003.1-2008 `execvp`'s *argv*][ieee-1003.1-2008-functions-exec].
This specification extends the IEEE standard in that at least one entry is REQUIRED (non-Windows), and that entry is used with the same semantics as `execvp`'s *file*. This field is OPTIONAL on Windows, and `commandLine` is REQUIRED if this field is omitted.
* **`commandLine`** (string, OPTIONAL) specifies the full command line to be executed on Windows.
This is the preferred means of supplying the command line on Windows. If omitted, the runtime will fall back to escaping and concatenating fields from `args` before making the system call into Windows.


### <a name="configPOSIXProcess" />POSIX process

Expand Down
6 changes: 4 additions & 2 deletions schema/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@
"process": {
"type": "object",
"required": [
"cwd",
"args"
"cwd"
],
"properties": {
"args": {
"$ref": "defs.json#/definitions/ArrayOfStrings"
},
"commandLine": {
"type": "string"
},
"consoleSize": {
"type": "object",
"required": [
Expand Down
4 changes: 3 additions & 1 deletion specs-go/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ type Process struct {
// User specifies user information for the process.
User User `json:"user"`
// Args specifies the binary and arguments for the application to execute.
Args []string `json:"args"`
Args []string `json:"args,omitempty"`
// CommandLine specifies the full command line for the application to execute on Windows.
CommandLine string `json:"commandLine,omitempty" platform:"windows"`
// Env populates the process environment for the process.
Env []string `json:"env,omitempty"`
// Cwd is the current working directory for the process and must be
Expand Down

0 comments on commit deb4d95

Please sign in to comment.