1package hcsshim
2
3import (
4	"fmt"
5	"syscall"
6
7	"github.com/Microsoft/hcsshim/internal/hns"
8
9	"github.com/Microsoft/hcsshim/internal/hcs"
10	"github.com/Microsoft/hcsshim/internal/hcserror"
11)
12
13var (
14	// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist
15	ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist
16
17	// ErrElementNotFound is an error encountered when the object being referenced does not exist
18	ErrElementNotFound = hcs.ErrElementNotFound
19
20	// ErrElementNotFound is an error encountered when the object being referenced does not exist
21	ErrNotSupported = hcs.ErrNotSupported
22
23	// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
24	// decimal -2147024883 / hex 0x8007000d
25	ErrInvalidData = hcs.ErrInvalidData
26
27	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
28	ErrHandleClose = hcs.ErrHandleClose
29
30	// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
31	ErrAlreadyClosed = hcs.ErrAlreadyClosed
32
33	// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
34	ErrInvalidNotificationType = hcs.ErrInvalidNotificationType
35
36	// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
37	ErrInvalidProcessState = hcs.ErrInvalidProcessState
38
39	// ErrTimeout is an error encountered when waiting on a notification times out
40	ErrTimeout = hcs.ErrTimeout
41
42	// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
43	// a different expected notification
44	ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit
45
46	// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
47	// is lost while waiting for a notification
48	ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort
49
50	// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
51	ErrUnexpectedValue = hcs.ErrUnexpectedValue
52
53	// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
54	ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped
55
56	// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
57	ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending
58
59	// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
60	ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState
61
62	// ErrProcNotFound is an error encountered when the the process cannot be found
63	ErrProcNotFound = hcs.ErrProcNotFound
64
65	// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
66	// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
67	ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied
68
69	// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
70	ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON
71
72	// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
73	ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage
74
75	// ErrNotSupported is an error encountered when hcs doesn't support the request
76	ErrPlatformNotSupported = hcs.ErrPlatformNotSupported
77)
78
79type EndpointNotFoundError = hns.EndpointNotFoundError
80type NetworkNotFoundError = hns.NetworkNotFoundError
81
82// ProcessError is an error encountered in HCS during an operation on a Process object
83type ProcessError struct {
84	Process   *process
85	Operation string
86	ExtraInfo string
87	Err       error
88	Events    []hcs.ErrorEvent
89}
90
91// ContainerError is an error encountered in HCS during an operation on a Container object
92type ContainerError struct {
93	Container *container
94	Operation string
95	ExtraInfo string
96	Err       error
97	Events    []hcs.ErrorEvent
98}
99
100func (e *ContainerError) Error() string {
101	if e == nil {
102		return "<nil>"
103	}
104
105	if e.Container == nil {
106		return "unexpected nil container for error: " + e.Err.Error()
107	}
108
109	s := "container " + e.Container.system.ID()
110
111	if e.Operation != "" {
112		s += " encountered an error during " + e.Operation
113	}
114
115	switch e.Err.(type) {
116	case nil:
117		break
118	case syscall.Errno:
119		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
120	default:
121		s += fmt.Sprintf(": %s", e.Err.Error())
122	}
123
124	for _, ev := range e.Events {
125		s += "\n" + ev.String()
126	}
127
128	if e.ExtraInfo != "" {
129		s += " extra info: " + e.ExtraInfo
130	}
131
132	return s
133}
134
135func makeContainerError(container *container, operation string, extraInfo string, err error) error {
136	// Don't double wrap errors
137	if _, ok := err.(*ContainerError); ok {
138		return err
139	}
140	containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
141	return containerError
142}
143
144func (e *ProcessError) Error() string {
145	if e == nil {
146		return "<nil>"
147	}
148
149	if e.Process == nil {
150		return "Unexpected nil process for error: " + e.Err.Error()
151	}
152
153	s := fmt.Sprintf("process %d in container %s", e.Process.p.Pid(), e.Process.p.SystemID())
154	if e.Operation != "" {
155		s += " encountered an error during " + e.Operation
156	}
157
158	switch e.Err.(type) {
159	case nil:
160		break
161	case syscall.Errno:
162		s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
163	default:
164		s += fmt.Sprintf(": %s", e.Err.Error())
165	}
166
167	for _, ev := range e.Events {
168		s += "\n" + ev.String()
169	}
170
171	return s
172}
173
174func makeProcessError(process *process, operation string, extraInfo string, err error) error {
175	// Don't double wrap errors
176	if _, ok := err.(*ProcessError); ok {
177		return err
178	}
179	processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
180	return processError
181}
182
183// IsNotExist checks if an error is caused by the Container or Process not existing.
184// Note: Currently, ErrElementNotFound can mean that a Process has either
185// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
186// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
187func IsNotExist(err error) bool {
188	if _, ok := err.(EndpointNotFoundError); ok {
189		return true
190	}
191	if _, ok := err.(NetworkNotFoundError); ok {
192		return true
193	}
194	return hcs.IsNotExist(getInnerError(err))
195}
196
197// IsAlreadyClosed checks if an error is caused by the Container or Process having been
198// already closed by a call to the Close() method.
199func IsAlreadyClosed(err error) bool {
200	return hcs.IsAlreadyClosed(getInnerError(err))
201}
202
203// IsPending returns a boolean indicating whether the error is that
204// the requested operation is being completed in the background.
205func IsPending(err error) bool {
206	return hcs.IsPending(getInnerError(err))
207}
208
209// IsTimeout returns a boolean indicating whether the error is caused by
210// a timeout waiting for the operation to complete.
211func IsTimeout(err error) bool {
212	return hcs.IsTimeout(getInnerError(err))
213}
214
215// IsAlreadyStopped returns a boolean indicating whether the error is caused by
216// a Container or Process being already stopped.
217// Note: Currently, ErrElementNotFound can mean that a Process has either
218// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
219// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
220func IsAlreadyStopped(err error) bool {
221	return hcs.IsAlreadyStopped(getInnerError(err))
222}
223
224// IsNotSupported returns a boolean indicating whether the error is caused by
225// unsupported platform requests
226// Note: Currently Unsupported platform requests can be mean either
227// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
228// is thrown from the Platform
229func IsNotSupported(err error) bool {
230	return hcs.IsNotSupported(getInnerError(err))
231}
232
233func getInnerError(err error) error {
234	switch pe := err.(type) {
235	case nil:
236		return nil
237	case *ContainerError:
238		err = pe.Err
239	case *ProcessError:
240		err = pe.Err
241	}
242	return err
243}
244
245func convertSystemError(err error, c *container) error {
246	if serr, ok := err.(*hcs.SystemError); ok {
247		return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events}
248	}
249	return err
250}
251
252func convertProcessError(err error, p *process) error {
253	if perr, ok := err.(*hcs.ProcessError); ok {
254		return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events}
255	}
256	return err
257}
258