github.com/containerd/Containerd@v1.4.13/runtime/v1/shim/service.go (about)

     1  // +build !windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package shim
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"sync"
    29  
    30  	"github.com/containerd/console"
    31  	eventstypes "github.com/containerd/containerd/api/events"
    32  	"github.com/containerd/containerd/api/types/task"
    33  	"github.com/containerd/containerd/errdefs"
    34  	"github.com/containerd/containerd/events"
    35  	"github.com/containerd/containerd/log"
    36  	"github.com/containerd/containerd/mount"
    37  	"github.com/containerd/containerd/namespaces"
    38  	"github.com/containerd/containerd/pkg/process"
    39  	"github.com/containerd/containerd/pkg/stdio"
    40  	"github.com/containerd/containerd/runtime"
    41  	"github.com/containerd/containerd/runtime/linux/runctypes"
    42  	shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
    43  	"github.com/containerd/containerd/sys/reaper"
    44  	runc "github.com/containerd/go-runc"
    45  	"github.com/containerd/typeurl"
    46  	ptypes "github.com/gogo/protobuf/types"
    47  	specs "github.com/opencontainers/runtime-spec/specs-go"
    48  	"github.com/pkg/errors"
    49  	"github.com/sirupsen/logrus"
    50  	"google.golang.org/grpc/codes"
    51  	"google.golang.org/grpc/status"
    52  )
    53  
    54  var (
    55  	empty   = &ptypes.Empty{}
    56  	bufPool = sync.Pool{
    57  		New: func() interface{} {
    58  			buffer := make([]byte, 4096)
    59  			return &buffer
    60  		},
    61  	}
    62  )
    63  
    64  // Config contains shim specific configuration
    65  type Config struct {
    66  	Path          string
    67  	Namespace     string
    68  	WorkDir       string
    69  	Criu          string
    70  	RuntimeRoot   string
    71  	SystemdCgroup bool
    72  }
    73  
    74  // NewService returns a new shim service that can be used via GRPC
    75  func NewService(config Config, publisher events.Publisher) (*Service, error) {
    76  	if config.Namespace == "" {
    77  		return nil, fmt.Errorf("shim namespace cannot be empty")
    78  	}
    79  	ctx := namespaces.WithNamespace(context.Background(), config.Namespace)
    80  	ctx = log.WithLogger(ctx, logrus.WithFields(logrus.Fields{
    81  		"namespace": config.Namespace,
    82  		"path":      config.Path,
    83  		"pid":       os.Getpid(),
    84  	}))
    85  	s := &Service{
    86  		config:    config,
    87  		context:   ctx,
    88  		processes: make(map[string]process.Process),
    89  		events:    make(chan interface{}, 128),
    90  		ec:        reaper.Default.Subscribe(),
    91  	}
    92  	go s.processExits()
    93  	if err := s.initPlatform(); err != nil {
    94  		return nil, errors.Wrap(err, "failed to initialized platform behavior")
    95  	}
    96  	go s.forward(publisher)
    97  	return s, nil
    98  }
    99  
   100  // Service is the shim implementation of a remote shim over GRPC
   101  type Service struct {
   102  	mu sync.Mutex
   103  
   104  	config    Config
   105  	context   context.Context
   106  	processes map[string]process.Process
   107  	events    chan interface{}
   108  	platform  stdio.Platform
   109  	ec        chan runc.Exit
   110  
   111  	// Filled by Create()
   112  	id     string
   113  	bundle string
   114  }
   115  
   116  // Create a new initial process and container with the underlying OCI runtime
   117  func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ *shimapi.CreateTaskResponse, err error) {
   118  	var mounts []process.Mount
   119  	for _, m := range r.Rootfs {
   120  		mounts = append(mounts, process.Mount{
   121  			Type:    m.Type,
   122  			Source:  m.Source,
   123  			Target:  m.Target,
   124  			Options: m.Options,
   125  		})
   126  	}
   127  
   128  	rootfs := ""
   129  	if len(mounts) > 0 {
   130  		rootfs = filepath.Join(r.Bundle, "rootfs")
   131  		if err := os.Mkdir(rootfs, 0711); err != nil && !os.IsExist(err) {
   132  			return nil, err
   133  		}
   134  	}
   135  
   136  	config := &process.CreateConfig{
   137  		ID:               r.ID,
   138  		Bundle:           r.Bundle,
   139  		Runtime:          r.Runtime,
   140  		Rootfs:           mounts,
   141  		Terminal:         r.Terminal,
   142  		Stdin:            r.Stdin,
   143  		Stdout:           r.Stdout,
   144  		Stderr:           r.Stderr,
   145  		Checkpoint:       r.Checkpoint,
   146  		ParentCheckpoint: r.ParentCheckpoint,
   147  		Options:          r.Options,
   148  	}
   149  	defer func() {
   150  		if err != nil {
   151  			if err2 := mount.UnmountAll(rootfs, 0); err2 != nil {
   152  				log.G(ctx).WithError(err2).Warn("Failed to cleanup rootfs mount")
   153  			}
   154  		}
   155  	}()
   156  	for _, rm := range mounts {
   157  		m := &mount.Mount{
   158  			Type:    rm.Type,
   159  			Source:  rm.Source,
   160  			Options: rm.Options,
   161  		}
   162  		if err := m.Mount(rootfs); err != nil {
   163  			return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m)
   164  		}
   165  	}
   166  
   167  	s.mu.Lock()
   168  	defer s.mu.Unlock()
   169  
   170  	process, err := newInit(
   171  		ctx,
   172  		s.config.Path,
   173  		s.config.WorkDir,
   174  		s.config.RuntimeRoot,
   175  		s.config.Namespace,
   176  		s.config.Criu,
   177  		s.config.SystemdCgroup,
   178  		s.platform,
   179  		config,
   180  		rootfs,
   181  	)
   182  	if err != nil {
   183  		return nil, errdefs.ToGRPC(err)
   184  	}
   185  	if err := process.Create(ctx, config); err != nil {
   186  		return nil, errdefs.ToGRPC(err)
   187  	}
   188  	// save the main task id and bundle to the shim for additional requests
   189  	s.id = r.ID
   190  	s.bundle = r.Bundle
   191  	pid := process.Pid()
   192  	s.processes[r.ID] = process
   193  	return &shimapi.CreateTaskResponse{
   194  		Pid: uint32(pid),
   195  	}, nil
   196  }
   197  
   198  // Start a process
   199  func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.StartResponse, error) {
   200  	p, err := s.getExecProcess(r.ID)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	if err := p.Start(ctx); err != nil {
   205  		return nil, err
   206  	}
   207  	return &shimapi.StartResponse{
   208  		ID:  p.ID(),
   209  		Pid: uint32(p.Pid()),
   210  	}, nil
   211  }
   212  
   213  // Delete the initial process and container
   214  func (s *Service) Delete(ctx context.Context, r *ptypes.Empty) (*shimapi.DeleteResponse, error) {
   215  	p, err := s.getInitProcess()
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	if err := p.Delete(ctx); err != nil {
   220  		return nil, errdefs.ToGRPC(err)
   221  	}
   222  	s.mu.Lock()
   223  	delete(s.processes, s.id)
   224  	s.mu.Unlock()
   225  	s.platform.Close()
   226  	return &shimapi.DeleteResponse{
   227  		ExitStatus: uint32(p.ExitStatus()),
   228  		ExitedAt:   p.ExitedAt(),
   229  		Pid:        uint32(p.Pid()),
   230  	}, nil
   231  }
   232  
   233  // DeleteProcess deletes an exec'd process
   234  func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) {
   235  	if r.ID == s.id {
   236  		return nil, status.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess")
   237  	}
   238  	p, err := s.getExecProcess(r.ID)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	if err := p.Delete(ctx); err != nil {
   243  		return nil, errdefs.ToGRPC(err)
   244  	}
   245  	s.mu.Lock()
   246  	delete(s.processes, r.ID)
   247  	s.mu.Unlock()
   248  	return &shimapi.DeleteResponse{
   249  		ExitStatus: uint32(p.ExitStatus()),
   250  		ExitedAt:   p.ExitedAt(),
   251  		Pid:        uint32(p.Pid()),
   252  	}, nil
   253  }
   254  
   255  // Exec an additional process inside the container
   256  func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*ptypes.Empty, error) {
   257  	s.mu.Lock()
   258  
   259  	if p := s.processes[r.ID]; p != nil {
   260  		s.mu.Unlock()
   261  		return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ID)
   262  	}
   263  
   264  	p := s.processes[s.id]
   265  	s.mu.Unlock()
   266  	if p == nil {
   267  		return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
   268  	}
   269  
   270  	process, err := p.(*process.Init).Exec(ctx, s.config.Path, &process.ExecConfig{
   271  		ID:       r.ID,
   272  		Terminal: r.Terminal,
   273  		Stdin:    r.Stdin,
   274  		Stdout:   r.Stdout,
   275  		Stderr:   r.Stderr,
   276  		Spec:     r.Spec,
   277  	})
   278  	if err != nil {
   279  		return nil, errdefs.ToGRPC(err)
   280  	}
   281  	s.mu.Lock()
   282  	s.processes[r.ID] = process
   283  	s.mu.Unlock()
   284  	return empty, nil
   285  }
   286  
   287  // ResizePty of a process
   288  func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*ptypes.Empty, error) {
   289  	if r.ID == "" {
   290  		return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "id not provided")
   291  	}
   292  	ws := console.WinSize{
   293  		Width:  uint16(r.Width),
   294  		Height: uint16(r.Height),
   295  	}
   296  	s.mu.Lock()
   297  	p := s.processes[r.ID]
   298  	s.mu.Unlock()
   299  	if p == nil {
   300  		return nil, errors.Errorf("process does not exist %s", r.ID)
   301  	}
   302  	if err := p.Resize(ws); err != nil {
   303  		return nil, errdefs.ToGRPC(err)
   304  	}
   305  	return empty, nil
   306  }
   307  
   308  // State returns runtime state information for a process
   309  func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.StateResponse, error) {
   310  	p, err := s.getExecProcess(r.ID)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	st, err := p.Status(ctx)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  	status := task.StatusUnknown
   319  	switch st {
   320  	case "created":
   321  		status = task.StatusCreated
   322  	case "running":
   323  		status = task.StatusRunning
   324  	case "stopped":
   325  		status = task.StatusStopped
   326  	case "paused":
   327  		status = task.StatusPaused
   328  	case "pausing":
   329  		status = task.StatusPausing
   330  	}
   331  	sio := p.Stdio()
   332  	return &shimapi.StateResponse{
   333  		ID:         p.ID(),
   334  		Bundle:     s.bundle,
   335  		Pid:        uint32(p.Pid()),
   336  		Status:     status,
   337  		Stdin:      sio.Stdin,
   338  		Stdout:     sio.Stdout,
   339  		Stderr:     sio.Stderr,
   340  		Terminal:   sio.Terminal,
   341  		ExitStatus: uint32(p.ExitStatus()),
   342  		ExitedAt:   p.ExitedAt(),
   343  	}, nil
   344  }
   345  
   346  // Pause the container
   347  func (s *Service) Pause(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) {
   348  	p, err := s.getInitProcess()
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	if err := p.(*process.Init).Pause(ctx); err != nil {
   353  		return nil, err
   354  	}
   355  	return empty, nil
   356  }
   357  
   358  // Resume the container
   359  func (s *Service) Resume(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) {
   360  	p, err := s.getInitProcess()
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	if err := p.(*process.Init).Resume(ctx); err != nil {
   365  		return nil, err
   366  	}
   367  	return empty, nil
   368  }
   369  
   370  // Kill a process with the provided signal
   371  func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*ptypes.Empty, error) {
   372  	if r.ID == "" {
   373  		p, err := s.getInitProcess()
   374  		if err != nil {
   375  			return nil, err
   376  		}
   377  		if err := p.Kill(ctx, r.Signal, r.All); err != nil {
   378  			return nil, errdefs.ToGRPC(err)
   379  		}
   380  		return empty, nil
   381  	}
   382  
   383  	p, err := s.getExecProcess(r.ID)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	if err := p.Kill(ctx, r.Signal, r.All); err != nil {
   388  		return nil, errdefs.ToGRPC(err)
   389  	}
   390  	return empty, nil
   391  }
   392  
   393  // ListPids returns all pids inside the container
   394  func (s *Service) ListPids(ctx context.Context, r *shimapi.ListPidsRequest) (*shimapi.ListPidsResponse, error) {
   395  	pids, err := s.getContainerPids(ctx, r.ID)
   396  	if err != nil {
   397  		return nil, errdefs.ToGRPC(err)
   398  	}
   399  	var processes []*task.ProcessInfo
   400  	for _, pid := range pids {
   401  		pInfo := task.ProcessInfo{
   402  			Pid: pid,
   403  		}
   404  		for _, p := range s.processes {
   405  			if p.Pid() == int(pid) {
   406  				d := &runctypes.ProcessDetails{
   407  					ExecID: p.ID(),
   408  				}
   409  				a, err := typeurl.MarshalAny(d)
   410  				if err != nil {
   411  					return nil, errors.Wrapf(err, "failed to marshal process %d info", pid)
   412  				}
   413  				pInfo.Info = a
   414  				break
   415  			}
   416  		}
   417  		processes = append(processes, &pInfo)
   418  	}
   419  	return &shimapi.ListPidsResponse{
   420  		Processes: processes,
   421  	}, nil
   422  }
   423  
   424  // CloseIO of a process
   425  func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*ptypes.Empty, error) {
   426  	p, err := s.getExecProcess(r.ID)
   427  	if err != nil {
   428  		return nil, err
   429  	}
   430  	if stdin := p.Stdin(); stdin != nil {
   431  		if err := stdin.Close(); err != nil {
   432  			return nil, errors.Wrap(err, "close stdin")
   433  		}
   434  	}
   435  	return empty, nil
   436  }
   437  
   438  // Checkpoint the container
   439  func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskRequest) (*ptypes.Empty, error) {
   440  	p, err := s.getInitProcess()
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  	var options runctypes.CheckpointOptions
   445  	if r.Options != nil {
   446  		v, err := typeurl.UnmarshalAny(r.Options)
   447  		if err != nil {
   448  			return nil, err
   449  		}
   450  		options = *v.(*runctypes.CheckpointOptions)
   451  	}
   452  	if err := p.(*process.Init).Checkpoint(ctx, &process.CheckpointConfig{
   453  		Path:                     r.Path,
   454  		Exit:                     options.Exit,
   455  		AllowOpenTCP:             options.OpenTcp,
   456  		AllowExternalUnixSockets: options.ExternalUnixSockets,
   457  		AllowTerminal:            options.Terminal,
   458  		FileLocks:                options.FileLocks,
   459  		EmptyNamespaces:          options.EmptyNamespaces,
   460  		WorkDir:                  options.WorkPath,
   461  	}); err != nil {
   462  		return nil, errdefs.ToGRPC(err)
   463  	}
   464  	return empty, nil
   465  }
   466  
   467  // ShimInfo returns shim information such as the shim's pid
   468  func (s *Service) ShimInfo(ctx context.Context, r *ptypes.Empty) (*shimapi.ShimInfoResponse, error) {
   469  	return &shimapi.ShimInfoResponse{
   470  		ShimPid: uint32(os.Getpid()),
   471  	}, nil
   472  }
   473  
   474  // Update a running container
   475  func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*ptypes.Empty, error) {
   476  	p, err := s.getInitProcess()
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  	if err := p.(*process.Init).Update(ctx, r.Resources); err != nil {
   481  		return nil, errdefs.ToGRPC(err)
   482  	}
   483  	return empty, nil
   484  }
   485  
   486  // Wait for a process to exit
   487  func (s *Service) Wait(ctx context.Context, r *shimapi.WaitRequest) (*shimapi.WaitResponse, error) {
   488  	p, err := s.getExecProcess(r.ID)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  	p.Wait()
   493  
   494  	return &shimapi.WaitResponse{
   495  		ExitStatus: uint32(p.ExitStatus()),
   496  		ExitedAt:   p.ExitedAt(),
   497  	}, nil
   498  }
   499  
   500  func (s *Service) processExits() {
   501  	for e := range s.ec {
   502  		s.checkProcesses(e)
   503  	}
   504  }
   505  
   506  func (s *Service) checkProcesses(e runc.Exit) {
   507  	var p process.Process
   508  	s.mu.Lock()
   509  	for _, proc := range s.processes {
   510  		if proc.Pid() == e.Pid {
   511  			p = proc
   512  			break
   513  		}
   514  	}
   515  	s.mu.Unlock()
   516  	if p == nil {
   517  		log.G(s.context).Debugf("process with id:%d wasn't found", e.Pid)
   518  		return
   519  	}
   520  	if ip, ok := p.(*process.Init); ok {
   521  		// Ensure all children are killed
   522  		if shouldKillAllOnExit(s.context, s.bundle) {
   523  			if err := ip.KillAll(s.context); err != nil {
   524  				log.G(s.context).WithError(err).WithField("id", ip.ID()).
   525  					Error("failed to kill init's children")
   526  			}
   527  		}
   528  	}
   529  
   530  	p.SetExited(e.Status)
   531  	s.events <- &eventstypes.TaskExit{
   532  		ContainerID: s.id,
   533  		ID:          p.ID(),
   534  		Pid:         uint32(e.Pid),
   535  		ExitStatus:  uint32(e.Status),
   536  		ExitedAt:    p.ExitedAt(),
   537  	}
   538  }
   539  
   540  func shouldKillAllOnExit(ctx context.Context, bundlePath string) bool {
   541  	var bundleSpec specs.Spec
   542  	bundleConfigContents, err := ioutil.ReadFile(filepath.Join(bundlePath, "config.json"))
   543  	if err != nil {
   544  		log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to read config.json")
   545  		return true
   546  	}
   547  	if err := json.Unmarshal(bundleConfigContents, &bundleSpec); err != nil {
   548  		log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to unmarshal bundle json")
   549  		return true
   550  	}
   551  	if bundleSpec.Linux != nil {
   552  		for _, ns := range bundleSpec.Linux.Namespaces {
   553  			if ns.Type == specs.PIDNamespace && ns.Path == "" {
   554  				return false
   555  			}
   556  		}
   557  	}
   558  	return true
   559  }
   560  
   561  func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
   562  	p, err := s.getInitProcess()
   563  	if err != nil {
   564  		return nil, err
   565  	}
   566  
   567  	ps, err := p.(*process.Init).Runtime().Ps(ctx, id)
   568  	if err != nil {
   569  		return nil, err
   570  	}
   571  	pids := make([]uint32, 0, len(ps))
   572  	for _, pid := range ps {
   573  		pids = append(pids, uint32(pid))
   574  	}
   575  	return pids, nil
   576  }
   577  
   578  func (s *Service) forward(publisher events.Publisher) {
   579  	for e := range s.events {
   580  		if err := publisher.Publish(s.context, getTopic(s.context, e), e); err != nil {
   581  			log.G(s.context).WithError(err).Error("post event")
   582  		}
   583  	}
   584  }
   585  
   586  // getInitProcess returns initial process
   587  func (s *Service) getInitProcess() (process.Process, error) {
   588  	s.mu.Lock()
   589  	defer s.mu.Unlock()
   590  
   591  	p := s.processes[s.id]
   592  	if p == nil {
   593  		return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
   594  	}
   595  	return p, nil
   596  }
   597  
   598  // getExecProcess returns exec process
   599  func (s *Service) getExecProcess(id string) (process.Process, error) {
   600  	s.mu.Lock()
   601  	defer s.mu.Unlock()
   602  
   603  	p := s.processes[id]
   604  	if p == nil {
   605  		return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s does not exist", id)
   606  	}
   607  	return p, nil
   608  }
   609  
   610  func getTopic(ctx context.Context, e interface{}) string {
   611  	switch e.(type) {
   612  	case *eventstypes.TaskCreate:
   613  		return runtime.TaskCreateEventTopic
   614  	case *eventstypes.TaskStart:
   615  		return runtime.TaskStartEventTopic
   616  	case *eventstypes.TaskOOM:
   617  		return runtime.TaskOOMEventTopic
   618  	case *eventstypes.TaskExit:
   619  		return runtime.TaskExitEventTopic
   620  	case *eventstypes.TaskDelete:
   621  		return runtime.TaskDeleteEventTopic
   622  	case *eventstypes.TaskExecAdded:
   623  		return runtime.TaskExecAddedEventTopic
   624  	case *eventstypes.TaskExecStarted:
   625  		return runtime.TaskExecStartedEventTopic
   626  	case *eventstypes.TaskPaused:
   627  		return runtime.TaskPausedEventTopic
   628  	case *eventstypes.TaskResumed:
   629  		return runtime.TaskResumedEventTopic
   630  	case *eventstypes.TaskCheckpointed:
   631  		return runtime.TaskCheckpointedEventTopic
   632  	default:
   633  		logrus.Warnf("no topic for type %#v", e)
   634  	}
   635  	return runtime.TaskUnknownTopic
   636  }
   637  
   638  func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform stdio.Platform, r *process.CreateConfig, rootfs string) (*process.Init, error) {
   639  	var options runctypes.CreateOptions
   640  	if r.Options != nil {
   641  		v, err := typeurl.UnmarshalAny(r.Options)
   642  		if err != nil {
   643  			return nil, err
   644  		}
   645  		options = *v.(*runctypes.CreateOptions)
   646  	}
   647  
   648  	runtime := process.NewRunc(runtimeRoot, path, namespace, r.Runtime, criu, systemdCgroup)
   649  	p := process.New(r.ID, runtime, stdio.Stdio{
   650  		Stdin:    r.Stdin,
   651  		Stdout:   r.Stdout,
   652  		Stderr:   r.Stderr,
   653  		Terminal: r.Terminal,
   654  	})
   655  	p.Bundle = r.Bundle
   656  	p.Platform = platform
   657  	p.Rootfs = rootfs
   658  	p.WorkDir = workDir
   659  	p.IoUID = int(options.IoUid)
   660  	p.IoGID = int(options.IoGid)
   661  	p.NoPivotRoot = options.NoPivotRoot
   662  	p.NoNewKeyring = options.NoNewKeyring
   663  	p.CriuWorkPath = options.CriuWorkPath
   664  	if p.CriuWorkPath == "" {
   665  		// if criu work path not set, use container WorkDir
   666  		p.CriuWorkPath = p.WorkDir
   667  	}
   668  
   669  	return p, nil
   670  }