10 "github.com/Sirupsen/logrus"
13 // ContainerError is an error encountered in HCS
15 handleLock sync.RWMutex
19 cachedPipes *cachedPipes
20 callbackNumber uintptr
23 type cachedPipes struct {
29 type processModifyRequest struct {
31 ConsoleSize *consoleSize `json:",omitempty"`
32 CloseHandle *closeHandle `json:",omitempty"`
35 type consoleSize struct {
40 type closeHandle struct {
44 type processStatus struct {
52 stdIn string = "StdIn"
53 stdOut string = "StdOut"
54 stdErr string = "StdErr"
58 modifyConsoleSize string = "ConsoleSize"
59 modifyCloseHandle string = "CloseHandle"
62 // Pid returns the process ID of the process within the container.
63 func (process *process) Pid() int {
64 return process.processID
67 // Kill signals the process to terminate but does not wait for it to finish terminating.
68 func (process *process) Kill() error {
69 process.handleLock.RLock()
70 defer process.handleLock.RUnlock()
72 title := "HCSShim::Process::" + operation
73 logrus.Debugf(title+" processid=%d", process.processID)
75 if process.handle == 0 {
76 return makeProcessError(process, operation, "", ErrAlreadyClosed)
80 err := hcsTerminateProcess(process.handle, &resultp)
81 err = processHcsResult(err, resultp)
83 return makeProcessError(process, operation, "", err)
86 logrus.Debugf(title+" succeeded processid=%d", process.processID)
90 // Wait waits for the process to exit.
91 func (process *process) Wait() error {
93 title := "HCSShim::Process::" + operation
94 logrus.Debugf(title+" processid=%d", process.processID)
96 err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil)
98 return makeProcessError(process, operation, "", err)
101 logrus.Debugf(title+" succeeded processid=%d", process.processID)
105 // WaitTimeout waits for the process to exit or the duration to elapse. It returns
106 // false if timeout occurs.
107 func (process *process) WaitTimeout(timeout time.Duration) error {
108 operation := "WaitTimeout"
109 title := "HCSShim::Process::" + operation
110 logrus.Debugf(title+" processid=%d", process.processID)
112 err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout)
114 return makeProcessError(process, operation, "", err)
117 logrus.Debugf(title+" succeeded processid=%d", process.processID)
121 // ExitCode returns the exit code of the process. The process must have
122 // already terminated.
123 func (process *process) ExitCode() (int, error) {
124 process.handleLock.RLock()
125 defer process.handleLock.RUnlock()
126 operation := "ExitCode"
127 title := "HCSShim::Process::" + operation
128 logrus.Debugf(title+" processid=%d", process.processID)
130 if process.handle == 0 {
131 return 0, makeProcessError(process, operation, "", ErrAlreadyClosed)
134 properties, err := process.properties()
136 return 0, makeProcessError(process, operation, "", err)
139 if properties.Exited == false {
140 return 0, makeProcessError(process, operation, "", ErrInvalidProcessState)
143 if properties.LastWaitResult != 0 {
144 return 0, makeProcessError(process, operation, "", syscall.Errno(properties.LastWaitResult))
147 logrus.Debugf(title+" succeeded processid=%d exitCode=%d", process.processID, properties.ExitCode)
148 return int(properties.ExitCode), nil
151 // ResizeConsole resizes the console of the process.
152 func (process *process) ResizeConsole(width, height uint16) error {
153 process.handleLock.RLock()
154 defer process.handleLock.RUnlock()
155 operation := "ResizeConsole"
156 title := "HCSShim::Process::" + operation
157 logrus.Debugf(title+" processid=%d", process.processID)
159 if process.handle == 0 {
160 return makeProcessError(process, operation, "", ErrAlreadyClosed)
163 modifyRequest := processModifyRequest{
164 Operation: modifyConsoleSize,
165 ConsoleSize: &consoleSize{
171 modifyRequestb, err := json.Marshal(modifyRequest)
176 modifyRequestStr := string(modifyRequestb)
179 err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
180 err = processHcsResult(err, resultp)
182 return makeProcessError(process, operation, "", err)
185 logrus.Debugf(title+" succeeded processid=%d", process.processID)
189 func (process *process) properties() (*processStatus, error) {
190 operation := "properties"
191 title := "HCSShim::Process::" + operation
192 logrus.Debugf(title+" processid=%d", process.processID)
198 err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
199 err = processHcsResult(err, resultp)
204 if propertiesp == nil {
205 return nil, ErrUnexpectedValue
207 propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
209 properties := &processStatus{}
210 if err := json.Unmarshal(propertiesRaw, properties); err != nil {
214 logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw)
215 return properties, nil
218 // Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
219 // these pipes does not close the underlying pipes; it should be possible to
220 // call this multiple times to get multiple interfaces.
221 func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
222 process.handleLock.RLock()
223 defer process.handleLock.RUnlock()
225 title := "HCSShim::Process::" + operation
226 logrus.Debugf(title+" processid=%d", process.processID)
228 if process.handle == 0 {
229 return nil, nil, nil, makeProcessError(process, operation, "", ErrAlreadyClosed)
232 var stdIn, stdOut, stdErr syscall.Handle
234 if process.cachedPipes == nil {
236 processInfo hcsProcessInformation
239 err := hcsGetProcessInfo(process.handle, &processInfo, &resultp)
240 err = processHcsResult(err, resultp)
242 return nil, nil, nil, makeProcessError(process, operation, "", err)
245 stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError
248 stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr
250 // Invalidate the cache
251 process.cachedPipes = nil
254 pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr})
256 return nil, nil, nil, makeProcessError(process, operation, "", err)
259 logrus.Debugf(title+" succeeded processid=%d", process.processID)
260 return pipes[0], pipes[1], pipes[2], nil
263 // CloseStdin closes the write side of the stdin pipe so that the process is
264 // notified on the read side that there is no more data in stdin.
265 func (process *process) CloseStdin() error {
266 process.handleLock.RLock()
267 defer process.handleLock.RUnlock()
268 operation := "CloseStdin"
269 title := "HCSShim::Process::" + operation
270 logrus.Debugf(title+" processid=%d", process.processID)
272 if process.handle == 0 {
273 return makeProcessError(process, operation, "", ErrAlreadyClosed)
276 modifyRequest := processModifyRequest{
277 Operation: modifyCloseHandle,
278 CloseHandle: &closeHandle{
283 modifyRequestb, err := json.Marshal(modifyRequest)
288 modifyRequestStr := string(modifyRequestb)
291 err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
292 err = processHcsResult(err, resultp)
294 return makeProcessError(process, operation, "", err)
297 logrus.Debugf(title+" succeeded processid=%d", process.processID)
301 // Close cleans up any state associated with the process but does not kill
303 func (process *process) Close() error {
304 process.handleLock.Lock()
305 defer process.handleLock.Unlock()
307 title := "HCSShim::Process::" + operation
308 logrus.Debugf(title+" processid=%d", process.processID)
310 // Don't double free this
311 if process.handle == 0 {
315 if err := process.unregisterCallback(); err != nil {
316 return makeProcessError(process, operation, "", err)
319 if err := hcsCloseProcess(process.handle); err != nil {
320 return makeProcessError(process, operation, "", err)
325 logrus.Debugf(title+" succeeded processid=%d", process.processID)
329 func (process *process) registerCallback() error {
330 context := ¬ifcationWatcherContext{
331 channels: newChannels(),
334 callbackMapLock.Lock()
335 callbackNumber := nextCallback
337 callbackMap[callbackNumber] = context
338 callbackMapLock.Unlock()
340 var callbackHandle hcsCallback
341 err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
345 context.handle = callbackHandle
346 process.callbackNumber = callbackNumber
351 func (process *process) unregisterCallback() error {
352 callbackNumber := process.callbackNumber
354 callbackMapLock.RLock()
355 context := callbackMap[callbackNumber]
356 callbackMapLock.RUnlock()
362 handle := context.handle
368 // hcsUnregisterProcessCallback has its own syncronization
369 // to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
370 err := hcsUnregisterProcessCallback(handle)
375 closeChannels(context.channels)
377 callbackMapLock.Lock()
378 callbackMap[callbackNumber] = nil
379 callbackMapLock.Unlock()