forked from microsoft/hcsshim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreateprocess.go
More file actions
166 lines (141 loc) · 4.24 KB
/
Copy pathcreateprocess.go
File metadata and controls
166 lines (141 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package hcsshim
import (
"encoding/json"
"fmt"
"io"
"runtime"
"syscall"
"unsafe"
"github.com/Sirupsen/logrus"
)
// CreateProcessParams is used as both the input of CreateProcessInComputeSystem
// and to convert the parameters to JSON for passing onto the HCS
type CreateProcessParams struct {
ApplicationName string
CommandLine string
WorkingDirectory string
Environment map[string]string
EmulateConsole bool
ConsoleSize [2]int
}
// pipe struct used for the stdin/stdout/stderr pipes
type pipe struct {
handle syscall.Handle
}
func makePipe(h syscall.Handle) *pipe {
p := &pipe{h}
runtime.SetFinalizer(p, (*pipe).closeHandle)
return p
}
func (p *pipe) closeHandle() {
if p.handle != 0 {
syscall.CloseHandle(p.handle)
p.handle = 0
}
}
func (p *pipe) Close() error {
p.closeHandle()
runtime.SetFinalizer(p, nil)
return nil
}
func (p *pipe) Read(b []byte) (int, error) {
// syscall.Read returns 0, nil on ERROR_BROKEN_PIPE, but for
// our purposes this should indicate EOF. This may be a go bug.
var read uint32
err := syscall.ReadFile(p.handle, b, &read, nil)
if err != nil {
if err == syscall.ERROR_BROKEN_PIPE {
return 0, io.EOF
}
return 0, err
}
return int(read), nil
}
func (p *pipe) Write(b []byte) (int, error) {
return syscall.Write(p.handle, b)
}
// CreateProcessInComputeSystem starts a process in a container. This is invoked, for example,
// as a result of docker run, docker exec, or RUN in Dockerfile. If successful,
// it returns the PID of the process.
func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useStderr bool, params CreateProcessParams) (uint32, io.WriteCloser, io.ReadCloser, io.ReadCloser, uint32, error) {
var (
stdin io.WriteCloser
stdout, stderr io.ReadCloser
)
title := "HCSShim::CreateProcessInComputeSystem"
logrus.Debugf(title+" id=%s", id)
// Load the DLL and get a handle to the procedure we need
dll, proc, err := loadAndFind(procCreateProcessWithStdHandlesInComputeSystem)
if dll != nil {
defer dll.Release()
}
if err != nil {
return 0, nil, nil, nil, 0xFFFFFFFF, err
}
// Convert id to uint16 pointer for calling the procedure
idp, err := syscall.UTF16PtrFromString(id)
if err != nil {
err = fmt.Errorf(title+" - Failed conversion of id %s to pointer %s", id, err)
logrus.Error(err)
return 0, nil, nil, nil, 0xFFFFFFFF, err
}
// If we are not emulating a console, ignore any console size passed to us
if !params.EmulateConsole {
params.ConsoleSize[0] = 0
params.ConsoleSize[1] = 0
}
paramsJson, err := json.Marshal(params)
if err != nil {
err = fmt.Errorf(title+" - Failed to marshall params %v %s", params, err)
return 0, nil, nil, nil, 0xFFFFFFFF, err
}
// Convert paramsJson to uint16 pointer for calling the procedure
paramsJsonp, err := syscall.UTF16PtrFromString(string(paramsJson))
if err != nil {
return 0, nil, nil, nil, 0xFFFFFFFF, err
}
// Get a POINTER to variable to take the pid outparm
pid := new(uint32)
logrus.Debugf(title+" - Calling Win32 %s %s", id, paramsJson)
var stdinHandle, stdoutHandle, stderrHandle syscall.Handle
var stdinParam, stdoutParam, stderrParam uintptr
if useStdin {
stdinParam = uintptr(unsafe.Pointer(&stdinHandle))
}
if useStdout {
stdoutParam = uintptr(unsafe.Pointer(&stdoutHandle))
}
if useStderr {
stderrParam = uintptr(unsafe.Pointer(&stderrHandle))
}
// Call the procedure itself.
r1, _, _ := proc.Call(
uintptr(unsafe.Pointer(idp)),
uintptr(unsafe.Pointer(paramsJsonp)),
uintptr(unsafe.Pointer(pid)),
stdinParam,
stdoutParam,
stderrParam)
use(unsafe.Pointer(idp))
use(unsafe.Pointer(paramsJsonp))
if r1 != 0 {
err = fmt.Errorf(title+" - Win32 API call returned error r1=%d err=%s id=%s params=%v", r1, syscall.Errno(r1), id, params)
// Windows TP4: Hyper-V Containers may return this error with more than one
// concurrent exec. Do not log it as an error
if uint32(r1) != Win32InvalidArgument {
logrus.Error(err)
}
return 0, nil, nil, nil, uint32(r1), err
}
if useStdin {
stdin = makePipe(stdinHandle)
}
if useStdout {
stdout = makePipe(stdoutHandle)
}
if useStderr {
stderr = makePipe(stderrHandle)
}
logrus.Debugf(title+" - succeeded id=%s params=%s pid=%d", id, paramsJson, *pid)
return *pid, stdin, stdout, stderr, 0, nil
}