github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/container/state.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/docker/go-units" 9 ) 10 11 // State holds the current container state, and has methods to get and 12 // set the state. Container has an embed, which allows all of the 13 // functions defined against State to run against Container. 14 type State struct { 15 sync.Mutex 16 // FIXME: Why do we have both paused and running if a 17 // container cannot be paused and running at the same time? 18 Running bool 19 Paused bool 20 Restarting bool 21 OOMKilled bool 22 RemovalInProgress bool // Not need for this to be persistent on disk. 23 Dead bool 24 Pid int 25 ExitCode int 26 Error string // contains last known error when starting the container 27 StartedAt time.Time 28 FinishedAt time.Time 29 waitChan chan struct{} 30 } 31 32 // NewState creates a default state object with a fresh channel for state changes. 33 func NewState() *State { 34 return &State{ 35 waitChan: make(chan struct{}), 36 } 37 } 38 39 // String returns a human-readable description of the state 40 func (s *State) String() string { 41 if s.Running { 42 if s.Paused { 43 return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 44 } 45 if s.Restarting { 46 return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 47 } 48 49 return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) 50 } 51 52 if s.RemovalInProgress { 53 return "Removal In Progress" 54 } 55 56 if s.Dead { 57 return "Dead" 58 } 59 60 if s.StartedAt.IsZero() { 61 return "Created" 62 } 63 64 if s.FinishedAt.IsZero() { 65 return "" 66 } 67 68 return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) 69 } 70 71 // StateString returns a single string to describe state 72 func (s *State) StateString() string { 73 if s.Running { 74 if s.Paused { 75 return "paused" 76 } 77 if s.Restarting { 78 return "restarting" 79 } 80 return "running" 81 } 82 83 if s.Dead { 84 return "dead" 85 } 86 87 if s.StartedAt.IsZero() { 88 return "created" 89 } 90 91 return "exited" 92 } 93 94 // IsValidStateString checks if the provided string is a valid container state or not. 95 func IsValidStateString(s string) bool { 96 if s != "paused" && 97 s != "restarting" && 98 s != "running" && 99 s != "dead" && 100 s != "created" && 101 s != "exited" { 102 return false 103 } 104 return true 105 } 106 107 func wait(waitChan <-chan struct{}, timeout time.Duration) error { 108 if timeout < 0 { 109 <-waitChan 110 return nil 111 } 112 select { 113 case <-time.After(timeout): 114 return fmt.Errorf("Timed out: %v", timeout) 115 case <-waitChan: 116 return nil 117 } 118 } 119 120 // WaitRunning waits until state is running. If state is already 121 // running it returns immediately. If you want wait forever you must 122 // supply negative timeout. Returns pid, that was passed to 123 // SetRunning. 124 func (s *State) WaitRunning(timeout time.Duration) (int, error) { 125 s.Lock() 126 if s.Running { 127 pid := s.Pid 128 s.Unlock() 129 return pid, nil 130 } 131 waitChan := s.waitChan 132 s.Unlock() 133 if err := wait(waitChan, timeout); err != nil { 134 return -1, err 135 } 136 return s.GetPID(), nil 137 } 138 139 // WaitStop waits until state is stopped. If state already stopped it returns 140 // immediately. If you want wait forever you must supply negative timeout. 141 // Returns exit code, that was passed to SetStoppedLocking 142 func (s *State) WaitStop(timeout time.Duration) (int, error) { 143 s.Lock() 144 if !s.Running { 145 exitCode := s.ExitCode 146 s.Unlock() 147 return exitCode, nil 148 } 149 waitChan := s.waitChan 150 s.Unlock() 151 if err := wait(waitChan, timeout); err != nil { 152 return -1, err 153 } 154 return s.getExitCode(), nil 155 } 156 157 // IsRunning returns whether the running flag is set. Used by Container to check whether a container is running. 158 func (s *State) IsRunning() bool { 159 s.Lock() 160 res := s.Running 161 s.Unlock() 162 return res 163 } 164 165 // GetPID holds the process id of a container. 166 func (s *State) GetPID() int { 167 s.Lock() 168 res := s.Pid 169 s.Unlock() 170 return res 171 } 172 173 func (s *State) getExitCode() int { 174 s.Lock() 175 res := s.ExitCode 176 s.Unlock() 177 return res 178 } 179 180 // SetRunning sets the state of the container to "running". 181 func (s *State) SetRunning(pid int, initial bool) { 182 s.Error = "" 183 s.Running = true 184 s.Paused = false 185 s.Restarting = false 186 s.ExitCode = 0 187 s.Pid = pid 188 if initial { 189 s.StartedAt = time.Now().UTC() 190 } 191 close(s.waitChan) // fire waiters for start 192 s.waitChan = make(chan struct{}) 193 } 194 195 // SetStoppedLocking locks the container state is sets it to "stopped". 196 func (s *State) SetStoppedLocking(exitStatus *ExitStatus) { 197 s.Lock() 198 s.SetStopped(exitStatus) 199 s.Unlock() 200 } 201 202 // SetStopped sets the container state to "stopped" without locking. 203 func (s *State) SetStopped(exitStatus *ExitStatus) { 204 s.Running = false 205 s.Paused = false 206 s.Restarting = false 207 s.Pid = 0 208 s.FinishedAt = time.Now().UTC() 209 s.setFromExitStatus(exitStatus) 210 close(s.waitChan) // fire waiters for stop 211 s.waitChan = make(chan struct{}) 212 } 213 214 // SetRestartingLocking is when docker handles the auto restart of containers when they are 215 // in the middle of a stop and being restarted again 216 func (s *State) SetRestartingLocking(exitStatus *ExitStatus) { 217 s.Lock() 218 s.SetRestarting(exitStatus) 219 s.Unlock() 220 } 221 222 // SetRestarting sets the container state to "restarting". 223 // It also sets the container PID to 0. 224 func (s *State) SetRestarting(exitStatus *ExitStatus) { 225 // we should consider the container running when it is restarting because of 226 // all the checks in docker around rm/stop/etc 227 s.Running = true 228 s.Restarting = true 229 s.Pid = 0 230 s.FinishedAt = time.Now().UTC() 231 s.setFromExitStatus(exitStatus) 232 close(s.waitChan) // fire waiters for stop 233 s.waitChan = make(chan struct{}) 234 } 235 236 // SetError sets the container's error state. This is useful when we want to 237 // know the error that occurred when container transits to another state 238 // when inspecting it 239 func (s *State) SetError(err error) { 240 s.Error = err.Error() 241 } 242 243 // IsPaused returns whether the container is paused or not. 244 func (s *State) IsPaused() bool { 245 s.Lock() 246 res := s.Paused 247 s.Unlock() 248 return res 249 } 250 251 // IsRestarting returns whether the container is restarting or not. 252 func (s *State) IsRestarting() bool { 253 s.Lock() 254 res := s.Restarting 255 s.Unlock() 256 return res 257 } 258 259 // SetRemovalInProgress sets the container state as being removed. 260 // It returns true if the container was already in that state. 261 func (s *State) SetRemovalInProgress() bool { 262 s.Lock() 263 defer s.Unlock() 264 if s.RemovalInProgress { 265 return true 266 } 267 s.RemovalInProgress = true 268 return false 269 } 270 271 // ResetRemovalInProgress make the RemovalInProgress state to false. 272 func (s *State) ResetRemovalInProgress() { 273 s.Lock() 274 s.RemovalInProgress = false 275 s.Unlock() 276 } 277 278 // SetDead sets the container state to "dead" 279 func (s *State) SetDead() { 280 s.Lock() 281 s.Dead = true 282 s.Unlock() 283 }