1package container // import "github.com/docker/docker/container"
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"sync"
8	"time"
9
10	"github.com/docker/docker/api/types"
11	"github.com/docker/go-units"
12)
13
14// State holds the current container state, and has methods to get and
15// set the state. Container has an embed, which allows all of the
16// functions defined against State to run against Container.
17type State struct {
18	sync.Mutex
19	// Note that `Running` and `Paused` are not mutually exclusive:
20	// When pausing a container (on Linux), the cgroups freezer is used to suspend
21	// all processes in the container. Freezing the process requires the process to
22	// be running. As a result, paused containers are both `Running` _and_ `Paused`.
23	Running           bool
24	Paused            bool
25	Restarting        bool
26	OOMKilled         bool
27	RemovalInProgress bool // Not need for this to be persistent on disk.
28	Dead              bool
29	Pid               int
30	ExitCodeValue     int    `json:"ExitCode"`
31	ErrorMsg          string `json:"Error"` // contains last known error during container start, stop, or remove
32	StartedAt         time.Time
33	FinishedAt        time.Time
34	Health            *Health
35
36	waitStop   chan struct{}
37	waitRemove chan struct{}
38}
39
40// StateStatus is used to return container wait results.
41// Implements exec.ExitCode interface.
42// This type is needed as State include a sync.Mutex field which make
43// copying it unsafe.
44type StateStatus struct {
45	exitCode int
46	err      error
47}
48
49// ExitCode returns current exitcode for the state.
50func (s StateStatus) ExitCode() int {
51	return s.exitCode
52}
53
54// Err returns current error for the state. Returns nil if the container had
55// exited on its own.
56func (s StateStatus) Err() error {
57	return s.err
58}
59
60// NewState creates a default state object with a fresh channel for state changes.
61func NewState() *State {
62	return &State{
63		waitStop:   make(chan struct{}),
64		waitRemove: make(chan struct{}),
65	}
66}
67
68// String returns a human-readable description of the state
69func (s *State) String() string {
70	if s.Running {
71		if s.Paused {
72			return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
73		}
74		if s.Restarting {
75			return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
76		}
77
78		if h := s.Health; h != nil {
79			return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)), h.String())
80		}
81
82		return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
83	}
84
85	if s.RemovalInProgress {
86		return "Removal In Progress"
87	}
88
89	if s.Dead {
90		return "Dead"
91	}
92
93	if s.StartedAt.IsZero() {
94		return "Created"
95	}
96
97	if s.FinishedAt.IsZero() {
98		return ""
99	}
100
101	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCodeValue, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
102}
103
104// IsValidHealthString checks if the provided string is a valid container health status or not.
105func IsValidHealthString(s string) bool {
106	return s == types.Starting ||
107		s == types.Healthy ||
108		s == types.Unhealthy ||
109		s == types.NoHealthcheck
110}
111
112// StateString returns a single string to describe state
113func (s *State) StateString() string {
114	if s.Running {
115		if s.Paused {
116			return "paused"
117		}
118		if s.Restarting {
119			return "restarting"
120		}
121		return "running"
122	}
123
124	if s.RemovalInProgress {
125		return "removing"
126	}
127
128	if s.Dead {
129		return "dead"
130	}
131
132	if s.StartedAt.IsZero() {
133		return "created"
134	}
135
136	return "exited"
137}
138
139// IsValidStateString checks if the provided string is a valid container state or not.
140func IsValidStateString(s string) bool {
141	if s != "paused" &&
142		s != "restarting" &&
143		s != "removing" &&
144		s != "running" &&
145		s != "dead" &&
146		s != "created" &&
147		s != "exited" {
148		return false
149	}
150	return true
151}
152
153// WaitCondition is an enum type for different states to wait for.
154type WaitCondition int
155
156// Possible WaitCondition Values.
157//
158// WaitConditionNotRunning (default) is used to wait for any of the non-running
159// states: "created", "exited", "dead", "removing", or "removed".
160//
161// WaitConditionNextExit is used to wait for the next time the state changes
162// to a non-running state. If the state is currently "created" or "exited",
163// this would cause Wait() to block until either the container runs and exits
164// or is removed.
165//
166// WaitConditionRemoved is used to wait for the container to be removed.
167const (
168	WaitConditionNotRunning WaitCondition = iota
169	WaitConditionNextExit
170	WaitConditionRemoved
171)
172
173// Wait waits until the container is in a certain state indicated by the given
174// condition. A context must be used for cancelling the request, controlling
175// timeouts, and avoiding goroutine leaks. Wait must be called without holding
176// the state lock. Returns a channel from which the caller will receive the
177// result. If the container exited on its own, the result's Err() method will
178// be nil and its ExitCode() method will return the container's exit code,
179// otherwise, the results Err() method will return an error indicating why the
180// wait operation failed.
181func (s *State) Wait(ctx context.Context, condition WaitCondition) <-chan StateStatus {
182	s.Lock()
183	defer s.Unlock()
184
185	if condition == WaitConditionNotRunning && !s.Running {
186		// Buffer so we can put it in the channel now.
187		resultC := make(chan StateStatus, 1)
188
189		// Send the current status.
190		resultC <- StateStatus{
191			exitCode: s.ExitCode(),
192			err:      s.Err(),
193		}
194
195		return resultC
196	}
197
198	// If we are waiting only for removal, the waitStop channel should
199	// remain nil and block forever.
200	var waitStop chan struct{}
201	if condition < WaitConditionRemoved {
202		waitStop = s.waitStop
203	}
204
205	// Always wait for removal, just in case the container gets removed
206	// while it is still in a "created" state, in which case it is never
207	// actually stopped.
208	waitRemove := s.waitRemove
209
210	resultC := make(chan StateStatus)
211
212	go func() {
213		select {
214		case <-ctx.Done():
215			// Context timeout or cancellation.
216			resultC <- StateStatus{
217				exitCode: -1,
218				err:      ctx.Err(),
219			}
220			return
221		case <-waitStop:
222		case <-waitRemove:
223		}
224
225		s.Lock()
226		result := StateStatus{
227			exitCode: s.ExitCode(),
228			err:      s.Err(),
229		}
230		s.Unlock()
231
232		resultC <- result
233	}()
234
235	return resultC
236}
237
238// IsRunning returns whether the running flag is set. Used by Container to check whether a container is running.
239func (s *State) IsRunning() bool {
240	s.Lock()
241	res := s.Running
242	s.Unlock()
243	return res
244}
245
246// GetPID holds the process id of a container.
247func (s *State) GetPID() int {
248	s.Lock()
249	res := s.Pid
250	s.Unlock()
251	return res
252}
253
254// ExitCode returns current exitcode for the state. Take lock before if state
255// may be shared.
256func (s *State) ExitCode() int {
257	return s.ExitCodeValue
258}
259
260// SetExitCode sets current exitcode for the state. Take lock before if state
261// may be shared.
262func (s *State) SetExitCode(ec int) {
263	s.ExitCodeValue = ec
264}
265
266// SetRunning sets the state of the container to "running".
267func (s *State) SetRunning(pid int, initial bool) {
268	s.ErrorMsg = ""
269	s.Paused = false
270	s.Running = true
271	s.Restarting = false
272	if initial {
273		s.Paused = false
274	}
275	s.ExitCodeValue = 0
276	s.Pid = pid
277	if initial {
278		s.StartedAt = time.Now().UTC()
279	}
280}
281
282// SetStopped sets the container state to "stopped" without locking.
283func (s *State) SetStopped(exitStatus *ExitStatus) {
284	s.Running = false
285	s.Paused = false
286	s.Restarting = false
287	s.Pid = 0
288	if exitStatus.ExitedAt.IsZero() {
289		s.FinishedAt = time.Now().UTC()
290	} else {
291		s.FinishedAt = exitStatus.ExitedAt
292	}
293	s.ExitCodeValue = exitStatus.ExitCode
294	s.OOMKilled = exitStatus.OOMKilled
295	close(s.waitStop) // fire waiters for stop
296	s.waitStop = make(chan struct{})
297}
298
299// SetRestarting sets the container state to "restarting" without locking.
300// It also sets the container PID to 0.
301func (s *State) SetRestarting(exitStatus *ExitStatus) {
302	// we should consider the container running when it is restarting because of
303	// all the checks in docker around rm/stop/etc
304	s.Running = true
305	s.Restarting = true
306	s.Paused = false
307	s.Pid = 0
308	s.FinishedAt = time.Now().UTC()
309	s.ExitCodeValue = exitStatus.ExitCode
310	s.OOMKilled = exitStatus.OOMKilled
311	close(s.waitStop) // fire waiters for stop
312	s.waitStop = make(chan struct{})
313}
314
315// SetError sets the container's error state. This is useful when we want to
316// know the error that occurred when container transits to another state
317// when inspecting it
318func (s *State) SetError(err error) {
319	s.ErrorMsg = ""
320	if err != nil {
321		s.ErrorMsg = err.Error()
322	}
323}
324
325// IsPaused returns whether the container is paused or not.
326func (s *State) IsPaused() bool {
327	s.Lock()
328	res := s.Paused
329	s.Unlock()
330	return res
331}
332
333// IsRestarting returns whether the container is restarting or not.
334func (s *State) IsRestarting() bool {
335	s.Lock()
336	res := s.Restarting
337	s.Unlock()
338	return res
339}
340
341// SetRemovalInProgress sets the container state as being removed.
342// It returns true if the container was already in that state.
343func (s *State) SetRemovalInProgress() bool {
344	s.Lock()
345	defer s.Unlock()
346	if s.RemovalInProgress {
347		return true
348	}
349	s.RemovalInProgress = true
350	return false
351}
352
353// ResetRemovalInProgress makes the RemovalInProgress state to false.
354func (s *State) ResetRemovalInProgress() {
355	s.Lock()
356	s.RemovalInProgress = false
357	s.Unlock()
358}
359
360// IsRemovalInProgress returns whether the RemovalInProgress flag is set.
361// Used by Container to check whether a container is being removed.
362func (s *State) IsRemovalInProgress() bool {
363	s.Lock()
364	res := s.RemovalInProgress
365	s.Unlock()
366	return res
367}
368
369// SetDead sets the container state to "dead"
370func (s *State) SetDead() {
371	s.Lock()
372	s.Dead = true
373	s.Unlock()
374}
375
376// IsDead returns whether the Dead flag is set. Used by Container to check whether a container is dead.
377func (s *State) IsDead() bool {
378	s.Lock()
379	res := s.Dead
380	s.Unlock()
381	return res
382}
383
384// SetRemoved assumes this container is already in the "dead" state and
385// closes the internal waitRemove channel to unblock callers waiting for a
386// container to be removed.
387func (s *State) SetRemoved() {
388	s.SetRemovalError(nil)
389}
390
391// SetRemovalError is to be called in case a container remove failed.
392// It sets an error and closes the internal waitRemove channel to unblock
393// callers waiting for the container to be removed.
394func (s *State) SetRemovalError(err error) {
395	s.SetError(err)
396	s.Lock()
397	close(s.waitRemove) // Unblock those waiting on remove.
398	// Recreate the channel so next ContainerWait will work
399	s.waitRemove = make(chan struct{})
400	s.Unlock()
401}
402
403// Err returns an error if there is one.
404func (s *State) Err() error {
405	if s.ErrorMsg != "" {
406		return errors.New(s.ErrorMsg)
407	}
408	return nil
409}
410