github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/shim/proc/init_state.go (about) 1 // Copyright 2018 The containerd Authors. 2 // Copyright 2018 The gVisor Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package proc 17 18 import ( 19 "context" 20 "fmt" 21 22 "github.com/containerd/containerd/errdefs" 23 "github.com/containerd/containerd/pkg/process" 24 runc "github.com/containerd/go-runc" 25 "golang.org/x/sys/unix" 26 ) 27 28 type stateTransition int 29 30 const ( 31 running stateTransition = iota 32 stopped 33 deleted 34 ) 35 36 func (s stateTransition) String() string { 37 switch s { 38 case running: 39 return "running" 40 case stopped: 41 return "stopped" 42 case deleted: 43 return "deleted" 44 default: 45 panic(fmt.Sprintf("unknown state: %d", s)) 46 } 47 } 48 49 type initState interface { 50 Start(context.Context) error 51 Delete(context.Context) error 52 Exec(context.Context, string, *ExecConfig) (process.Process, error) 53 State(ctx context.Context) (string, error) 54 Stats(context.Context, string) (*runc.Stats, error) 55 Kill(context.Context, uint32, bool) error 56 SetExited(int) 57 } 58 59 type createdState struct { 60 p *Init 61 } 62 63 func (s *createdState) name() string { 64 return "created" 65 } 66 67 func (s *createdState) transition(transition stateTransition) { 68 switch transition { 69 case running: 70 s.p.initState = &runningState{p: s.p} 71 case stopped: 72 s.p.initState = &stoppedState{process: s.p} 73 case deleted: 74 s.p.initState = &deletedState{} 75 default: 76 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 77 } 78 } 79 80 func (s *createdState) Start(ctx context.Context) error { 81 if err := s.p.start(ctx); err != nil { 82 // Containerd doesn't allow deleting container in created state. 83 // However, for gvisor, a non-root container in created state can 84 // only go to running state. If the container can't be started, 85 // it can only stay in created state, and never be deleted. 86 // To work around that, we treat non-root container in start failure 87 // state as stopped. 88 if !s.p.Sandbox { 89 s.p.io.Close() 90 s.p.setExited(internalErrorCode) 91 s.transition(stopped) 92 } 93 return err 94 } 95 s.transition(running) 96 return nil 97 } 98 99 func (s *createdState) Delete(ctx context.Context) error { 100 if err := s.p.delete(ctx); err != nil { 101 return err 102 } 103 s.transition(deleted) 104 return nil 105 } 106 107 func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error { 108 return s.p.kill(ctx, sig, all) 109 } 110 111 func (s *createdState) SetExited(status int) { 112 s.p.setExited(status) 113 s.transition(stopped) 114 } 115 116 func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) { 117 return s.p.exec(path, r) 118 } 119 120 func (s *createdState) State(ctx context.Context) (string, error) { 121 state, err := s.p.state(ctx) 122 if err == nil && state == statusStopped { 123 s.transition(stopped) 124 } 125 return state, err 126 } 127 128 func (s *createdState) Stats(ctx context.Context, id string) (*runc.Stats, error) { 129 return s.p.stats(ctx, id) 130 } 131 132 type runningState struct { 133 p *Init 134 } 135 136 func (s *runningState) name() string { 137 return "running" 138 } 139 140 func (s *runningState) transition(transition stateTransition) { 141 switch transition { 142 case stopped: 143 s.p.initState = &stoppedState{process: s.p} 144 default: 145 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 146 } 147 } 148 149 func (s *runningState) Start(ctx context.Context) error { 150 return fmt.Errorf("cannot start a running container") 151 } 152 153 func (s *runningState) Delete(ctx context.Context) error { 154 return fmt.Errorf("cannot delete a running container") 155 } 156 157 func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error { 158 return s.p.kill(ctx, sig, all) 159 } 160 161 func (s *runningState) SetExited(status int) { 162 s.p.setExited(status) 163 s.transition(stopped) 164 } 165 166 func (s *runningState) Exec(_ context.Context, path string, r *ExecConfig) (process.Process, error) { 167 return s.p.exec(path, r) 168 } 169 170 func (s *runningState) State(ctx context.Context) (string, error) { 171 state, err := s.p.state(ctx) 172 if err == nil && state == "stopped" { 173 s.transition(stopped) 174 } 175 return state, err 176 } 177 178 func (s *runningState) Stats(ctx context.Context, id string) (*runc.Stats, error) { 179 return s.p.stats(ctx, id) 180 } 181 182 type stoppedState struct { 183 process *Init 184 } 185 186 func (s *stoppedState) name() string { 187 return "stopped" 188 } 189 190 func (s *stoppedState) transition(transition stateTransition) { 191 switch transition { 192 case deleted: 193 s.process.initState = &deletedState{} 194 default: 195 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 196 } 197 } 198 199 func (s *stoppedState) Start(context.Context) error { 200 return fmt.Errorf("cannot start a stopped container") 201 } 202 203 func (s *stoppedState) Delete(ctx context.Context) error { 204 if err := s.process.delete(ctx); err != nil { 205 return err 206 } 207 s.transition(deleted) 208 return nil 209 } 210 211 func (s *stoppedState) Kill(_ context.Context, signal uint32, _ bool) error { 212 return handleStoppedKill(signal) 213 } 214 215 func (s *stoppedState) SetExited(status int) { 216 s.process.setExited(status) 217 } 218 219 func (s *stoppedState) Exec(context.Context, string, *ExecConfig) (process.Process, error) { 220 return nil, fmt.Errorf("cannot exec in a stopped state") 221 } 222 223 func (s *stoppedState) State(context.Context) (string, error) { 224 return "stopped", nil 225 } 226 227 func (s *stoppedState) Stats(context.Context, string) (*runc.Stats, error) { 228 return nil, fmt.Errorf("cannot stat a stopped container") 229 } 230 231 func handleStoppedKill(signal uint32) error { 232 switch unix.Signal(signal) { 233 case unix.SIGTERM, unix.SIGKILL: 234 // Container is already stopped, so everything inside the container has 235 // already been killed. 236 return nil 237 default: 238 return errdefs.ToGRPCf(errdefs.ErrNotFound, "process not found") 239 } 240 }