github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/state_linux.go (about) 1 package libcontainer 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/opencontainers/runc/libcontainer/configs" 9 "github.com/opencontainers/runtime-spec/specs-go" 10 "golang.org/x/sys/unix" 11 ) 12 13 func newStateTransitionError(from, to containerState) error { 14 return &stateTransitionError{ 15 From: from.status().String(), 16 To: to.status().String(), 17 } 18 } 19 20 // stateTransitionError is returned when an invalid state transition happens from one 21 // state to another. 22 type stateTransitionError struct { 23 From string 24 To string 25 } 26 27 func (s *stateTransitionError) Error() string { 28 return fmt.Sprintf("invalid state transition from %s to %s", s.From, s.To) 29 } 30 31 type containerState interface { 32 transition(containerState) error 33 destroy() error 34 status() Status 35 } 36 37 func destroy(c *Container) error { 38 // Usually, when a container init is gone, all other processes in its 39 // cgroup are killed by the kernel. This is not the case for a shared 40 // PID namespace container, which may have some processes left after 41 // its init is killed or exited. 42 // 43 // As the container without init process running is considered stopped, 44 // and destroy is supposed to remove all the container resources, we need 45 // to kill those processes here. 46 if !c.config.Namespaces.IsPrivate(configs.NEWPID) { 47 _ = signalAllProcesses(c.cgroupManager, unix.SIGKILL) 48 } 49 if err := c.cgroupManager.Destroy(); err != nil { 50 return fmt.Errorf("unable to remove container's cgroup: %w", err) 51 } 52 if c.intelRdtManager != nil { 53 if err := c.intelRdtManager.Destroy(); err != nil { 54 return fmt.Errorf("unable to remove container's IntelRDT group: %w", err) 55 } 56 } 57 if err := os.RemoveAll(c.stateDir); err != nil { 58 return fmt.Errorf("unable to remove container state dir: %w", err) 59 } 60 c.initProcess = nil 61 err := runPoststopHooks(c) 62 c.state = &stoppedState{c: c} 63 return err 64 } 65 66 func runPoststopHooks(c *Container) error { 67 hooks := c.config.Hooks 68 if hooks == nil { 69 return nil 70 } 71 72 s, err := c.currentOCIState() 73 if err != nil { 74 return err 75 } 76 s.Status = specs.StateStopped 77 78 return hooks.Run(configs.Poststop, s) 79 } 80 81 // stoppedState represents a container is a stopped/destroyed state. 82 type stoppedState struct { 83 c *Container 84 } 85 86 func (b *stoppedState) status() Status { 87 return Stopped 88 } 89 90 func (b *stoppedState) transition(s containerState) error { 91 switch s.(type) { 92 case *runningState, *restoredState: 93 b.c.state = s 94 return nil 95 case *stoppedState: 96 return nil 97 } 98 return newStateTransitionError(b, s) 99 } 100 101 func (b *stoppedState) destroy() error { 102 return destroy(b.c) 103 } 104 105 // runningState represents a container that is currently running. 106 type runningState struct { 107 c *Container 108 } 109 110 func (r *runningState) status() Status { 111 return Running 112 } 113 114 func (r *runningState) transition(s containerState) error { 115 switch s.(type) { 116 case *stoppedState: 117 if r.c.hasInit() { 118 return ErrRunning 119 } 120 r.c.state = s 121 return nil 122 case *pausedState: 123 r.c.state = s 124 return nil 125 case *runningState: 126 return nil 127 } 128 return newStateTransitionError(r, s) 129 } 130 131 func (r *runningState) destroy() error { 132 if r.c.hasInit() { 133 return ErrRunning 134 } 135 return destroy(r.c) 136 } 137 138 type createdState struct { 139 c *Container 140 } 141 142 func (i *createdState) status() Status { 143 return Created 144 } 145 146 func (i *createdState) transition(s containerState) error { 147 switch s.(type) { 148 case *runningState, *pausedState, *stoppedState: 149 i.c.state = s 150 return nil 151 case *createdState: 152 return nil 153 } 154 return newStateTransitionError(i, s) 155 } 156 157 func (i *createdState) destroy() error { 158 _ = i.c.initProcess.signal(unix.SIGKILL) 159 return destroy(i.c) 160 } 161 162 // pausedState represents a container that is currently pause. It cannot be destroyed in a 163 // paused state and must transition back to running first. 164 type pausedState struct { 165 c *Container 166 } 167 168 func (p *pausedState) status() Status { 169 return Paused 170 } 171 172 func (p *pausedState) transition(s containerState) error { 173 switch s.(type) { 174 case *runningState, *stoppedState: 175 p.c.state = s 176 return nil 177 case *pausedState: 178 return nil 179 } 180 return newStateTransitionError(p, s) 181 } 182 183 func (p *pausedState) destroy() error { 184 if p.c.hasInit() { 185 return ErrPaused 186 } 187 if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil { 188 return err 189 } 190 return destroy(p.c) 191 } 192 193 // restoredState is the same as the running state but also has associated checkpoint 194 // information that maybe need destroyed when the container is stopped and destroy is called. 195 type restoredState struct { 196 imageDir string 197 c *Container 198 } 199 200 func (r *restoredState) status() Status { 201 return Running 202 } 203 204 func (r *restoredState) transition(s containerState) error { 205 switch s.(type) { 206 case *stoppedState, *runningState: 207 return nil 208 } 209 return newStateTransitionError(r, s) 210 } 211 212 func (r *restoredState) destroy() error { 213 if _, err := os.Stat(filepath.Join(r.c.stateDir, "checkpoint")); err != nil { 214 if !os.IsNotExist(err) { 215 return err 216 } 217 } 218 return destroy(r.c) 219 } 220 221 // loadedState is used whenever a container is restored, loaded, or setting additional 222 // processes inside and it should not be destroyed when it is exiting. 223 type loadedState struct { 224 c *Container 225 s Status 226 } 227 228 func (n *loadedState) status() Status { 229 return n.s 230 } 231 232 func (n *loadedState) transition(s containerState) error { 233 n.c.state = s 234 return nil 235 } 236 237 func (n *loadedState) destroy() error { 238 if err := n.c.refreshState(); err != nil { 239 return err 240 } 241 return n.c.state.destroy() 242 }