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  }