github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 "github.com/SagerNet/gvisor/pkg/shim/utils" 27 ) 28 29 type stateTransition int 30 31 const ( 32 running stateTransition = iota 33 stopped 34 deleted 35 ) 36 37 func (s stateTransition) String() string { 38 switch s { 39 case running: 40 return "running" 41 case stopped: 42 return "stopped" 43 case deleted: 44 return "deleted" 45 default: 46 panic(fmt.Sprintf("unknown state: %d", s)) 47 } 48 } 49 50 type initState interface { 51 Start(context.Context) error 52 Delete(context.Context) error 53 Exec(context.Context, string, *ExecConfig) (process.Process, error) 54 State(ctx context.Context) (string, error) 55 Stats(context.Context, string) (*runc.Stats, error) 56 Kill(context.Context, uint32, bool) error 57 SetExited(int) 58 } 59 60 type createdState struct { 61 p *Init 62 } 63 64 func (s *createdState) name() string { 65 return "created" 66 } 67 68 func (s *createdState) transition(transition stateTransition) { 69 switch transition { 70 case running: 71 s.p.initState = &runningState{p: s.p} 72 case stopped: 73 s.p.initState = &stoppedState{process: s.p} 74 case deleted: 75 s.p.initState = &deletedState{} 76 default: 77 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 78 } 79 } 80 81 func (s *createdState) Start(ctx context.Context) error { 82 if err := s.p.start(ctx); err != nil { 83 // Containerd doesn't allow deleting container in created state. 84 // However, for gvisor, a non-root container in created state can 85 // only go to running state. If the container can't be started, 86 // it can only stay in created state, and never be deleted. 87 // To work around that, we treat non-root container in start failure 88 // state as stopped. 89 if !s.p.Sandbox { 90 s.p.io.Close() 91 s.p.setExited(internalErrorCode) 92 s.transition(stopped) 93 } 94 return err 95 } 96 s.transition(running) 97 return nil 98 } 99 100 func (s *createdState) Delete(ctx context.Context) error { 101 if err := s.p.delete(ctx); err != nil { 102 return err 103 } 104 s.transition(deleted) 105 return nil 106 } 107 108 func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error { 109 return s.p.kill(ctx, sig, all) 110 } 111 112 func (s *createdState) SetExited(status int) { 113 s.p.setExited(status) 114 s.transition(stopped) 115 } 116 117 func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) { 118 return s.p.exec(path, r) 119 } 120 121 func (s *createdState) State(ctx context.Context) (string, error) { 122 state, err := s.p.state(ctx) 123 if err == nil && state == statusStopped { 124 s.transition(stopped) 125 } 126 return state, err 127 } 128 129 func (s *createdState) Stats(ctx context.Context, id string) (*runc.Stats, error) { 130 return s.p.stats(ctx, id) 131 } 132 133 type runningState struct { 134 p *Init 135 } 136 137 func (s *runningState) name() string { 138 return "running" 139 } 140 141 func (s *runningState) transition(transition stateTransition) { 142 switch transition { 143 case stopped: 144 s.p.initState = &stoppedState{process: s.p} 145 default: 146 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 147 } 148 } 149 150 func (s *runningState) Start(ctx context.Context) error { 151 return fmt.Errorf("cannot start a running container") 152 } 153 154 func (s *runningState) Delete(ctx context.Context) error { 155 return fmt.Errorf("cannot delete a running container") 156 } 157 158 func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error { 159 return s.p.kill(ctx, sig, all) 160 } 161 162 func (s *runningState) SetExited(status int) { 163 s.p.setExited(status) 164 s.transition(stopped) 165 } 166 167 func (s *runningState) Exec(_ context.Context, path string, r *ExecConfig) (process.Process, error) { 168 return s.p.exec(path, r) 169 } 170 171 func (s *runningState) State(ctx context.Context) (string, error) { 172 state, err := s.p.state(ctx) 173 if err == nil && state == "stopped" { 174 s.transition(stopped) 175 } 176 return state, err 177 } 178 179 func (s *runningState) Stats(ctx context.Context, id string) (*runc.Stats, error) { 180 return s.p.stats(ctx, id) 181 } 182 183 type stoppedState struct { 184 process *Init 185 } 186 187 func (s *stoppedState) name() string { 188 return "stopped" 189 } 190 191 func (s *stoppedState) transition(transition stateTransition) { 192 switch transition { 193 case deleted: 194 s.process.initState = &deletedState{} 195 default: 196 panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition)) 197 } 198 } 199 200 func (s *stoppedState) Start(context.Context) error { 201 return fmt.Errorf("cannot start a stopped container") 202 } 203 204 func (s *stoppedState) Delete(ctx context.Context) error { 205 if err := s.process.delete(ctx); err != nil { 206 return err 207 } 208 s.transition(deleted) 209 return nil 210 } 211 212 func (s *stoppedState) Kill(_ context.Context, signal uint32, _ bool) error { 213 return handleStoppedKill(signal) 214 } 215 216 func (s *stoppedState) SetExited(status int) { 217 s.process.setExited(status) 218 } 219 220 func (s *stoppedState) Exec(context.Context, string, *ExecConfig) (process.Process, error) { 221 return nil, fmt.Errorf("cannot exec in a stopped state") 222 } 223 224 func (s *stoppedState) State(context.Context) (string, error) { 225 return "stopped", nil 226 } 227 228 func (s *stoppedState) Stats(context.Context, string) (*runc.Stats, error) { 229 return nil, fmt.Errorf("cannot stat a stopped container") 230 } 231 232 func handleStoppedKill(signal uint32) error { 233 switch unix.Signal(signal) { 234 case unix.SIGTERM, unix.SIGKILL: 235 // Container is already stopped, so everything inside the container has 236 // already been killed. 237 return nil 238 default: 239 return utils.ErrToGRPCf(errdefs.ErrNotFound, "process not found") 240 } 241 }