github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/utils_linux.go (about)

     1  // +build linux
     2  
     3  package main
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"syscall"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/coreos/go-systemd/activation"
    15  	"github.com/opencontainers/runc/libcontainer"
    16  	"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
    17  	"github.com/opencontainers/runc/libcontainer/specconv"
    18  	"github.com/opencontainers/runtime-spec/specs-go"
    19  	"github.com/urfave/cli"
    20  )
    21  
    22  var errEmptyID = errors.New("container id cannot be empty")
    23  
    24  var container libcontainer.Container
    25  
    26  // loadFactory returns the configured factory instance for execing containers.
    27  func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
    28  	root := context.GlobalString("root")
    29  	abs, err := filepath.Abs(root)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	cgroupManager := libcontainer.Cgroupfs
    34  	if context.GlobalBool("systemd-cgroup") {
    35  		if systemd.UseSystemd() {
    36  			cgroupManager = libcontainer.SystemdCgroups
    37  		} else {
    38  			return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available")
    39  		}
    40  	}
    41  	return libcontainer.New(abs, cgroupManager, libcontainer.CriuPath(context.GlobalString("criu")))
    42  }
    43  
    44  // getContainer returns the specified container instance by loading it from state
    45  // with the default factory.
    46  func getContainer(context *cli.Context) (libcontainer.Container, error) {
    47  	id := context.Args().First()
    48  	if id == "" {
    49  		return nil, errEmptyID
    50  	}
    51  	factory, err := loadFactory(context)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	return factory.Load(id)
    56  }
    57  
    58  func fatalf(t string, v ...interface{}) {
    59  	fatal(fmt.Errorf(t, v...))
    60  }
    61  
    62  func getDefaultImagePath(context *cli.Context) string {
    63  	cwd, err := os.Getwd()
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  	return filepath.Join(cwd, "checkpoint")
    68  }
    69  
    70  // newProcess returns a new libcontainer Process with the arguments from the
    71  // spec and stdio from the current process.
    72  func newProcess(p specs.Process) (*libcontainer.Process, error) {
    73  	lp := &libcontainer.Process{
    74  		Args: p.Args,
    75  		Env:  p.Env,
    76  		// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
    77  		User:            fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
    78  		Cwd:             p.Cwd,
    79  		Capabilities:    p.Capabilities,
    80  		Label:           p.SelinuxLabel,
    81  		NoNewPrivileges: &p.NoNewPrivileges,
    82  		AppArmorProfile: p.ApparmorProfile,
    83  	}
    84  	for _, gid := range p.User.AdditionalGids {
    85  		lp.AdditionalGroups = append(lp.AdditionalGroups, strconv.FormatUint(uint64(gid), 10))
    86  	}
    87  	for _, rlimit := range p.Rlimits {
    88  		rl, err := createLibContainerRlimit(rlimit)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  		lp.Rlimits = append(lp.Rlimits, rl)
    93  	}
    94  	return lp, nil
    95  }
    96  
    97  func dupStdio(process *libcontainer.Process, rootuid, rootgid int) error {
    98  	process.Stdin = os.Stdin
    99  	process.Stdout = os.Stdout
   100  	process.Stderr = os.Stderr
   101  	for _, fd := range []uintptr{
   102  		os.Stdin.Fd(),
   103  		os.Stdout.Fd(),
   104  		os.Stderr.Fd(),
   105  	} {
   106  		if err := syscall.Fchown(int(fd), rootuid, rootgid); err != nil {
   107  			return err
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  // If systemd is supporting sd_notify protocol, this function will add support
   114  // for sd_notify protocol from within the container.
   115  func setupSdNotify(spec *specs.Spec, notifySocket string) {
   116  	spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
   117  	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
   118  }
   119  
   120  func destroy(container libcontainer.Container) {
   121  	if err := container.Destroy(); err != nil {
   122  		logrus.Error(err)
   123  	}
   124  }
   125  
   126  // setupIO sets the proper IO on the process depending on the configuration
   127  // If there is a nil error then there must be a non nil tty returned
   128  func setupIO(process *libcontainer.Process, rootuid, rootgid int, console string, createTTY, detach bool) (*tty, error) {
   129  	// detach and createTty will not work unless a console path is passed
   130  	// so error out here before changing any terminal settings
   131  	if createTTY && detach && console == "" {
   132  		return nil, fmt.Errorf("cannot allocate tty if runc will detach")
   133  	}
   134  	if createTTY {
   135  		return createTty(process, rootuid, rootgid, console)
   136  	}
   137  	if detach {
   138  		if err := dupStdio(process, rootuid, rootgid); err != nil {
   139  			return nil, err
   140  		}
   141  		return &tty{}, nil
   142  	}
   143  	return createStdioPipes(process, rootuid, rootgid)
   144  }
   145  
   146  // createPidFile creates a file with the processes pid inside it atomically
   147  // it creates a temp file with the paths filename + '.' infront of it
   148  // then renames the file
   149  func createPidFile(path string, process *libcontainer.Process) error {
   150  	pid, err := process.Pid()
   151  	if err != nil {
   152  		return err
   153  	}
   154  	var (
   155  		tmpDir  = filepath.Dir(path)
   156  		tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path)))
   157  	)
   158  	f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	_, err = fmt.Fprintf(f, "%d", pid)
   163  	f.Close()
   164  	if err != nil {
   165  		return err
   166  	}
   167  	return os.Rename(tmpName, path)
   168  }
   169  
   170  func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) {
   171  	config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
   172  		CgroupName:       id,
   173  		UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
   174  		NoPivotRoot:      context.Bool("no-pivot"),
   175  		NoNewKeyring:     context.Bool("no-new-keyring"),
   176  		Spec:             spec,
   177  	})
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	factory, err := loadFactory(context)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	return factory.Create(id, config)
   187  }
   188  
   189  type runner struct {
   190  	enableSubreaper bool
   191  	shouldDestroy   bool
   192  	detach          bool
   193  	listenFDs       []*os.File
   194  	pidFile         string
   195  	console         string
   196  	container       libcontainer.Container
   197  	create          bool
   198  }
   199  
   200  func (r *runner) run(config *specs.Process) (int, error) {
   201  	process, err := newProcess(*config)
   202  	if err != nil {
   203  		r.destroy()
   204  		return -1, err
   205  	}
   206  	if len(r.listenFDs) > 0 {
   207  		process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1")
   208  		process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...)
   209  	}
   210  	rootuid, err := r.container.Config().HostUID()
   211  	if err != nil {
   212  		r.destroy()
   213  		return -1, err
   214  	}
   215  	rootgid, err := r.container.Config().HostGID()
   216  	if err != nil {
   217  		r.destroy()
   218  		return -1, err
   219  	}
   220  	tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach || r.create)
   221  	if err != nil {
   222  		r.destroy()
   223  		return -1, err
   224  	}
   225  	handler := newSignalHandler(tty, r.enableSubreaper)
   226  	startFn := r.container.Start
   227  	if !r.create {
   228  		startFn = r.container.Run
   229  	}
   230  	defer tty.Close()
   231  	if err := startFn(process); err != nil {
   232  		r.destroy()
   233  		return -1, err
   234  	}
   235  	if err := tty.ClosePostStart(); err != nil {
   236  		r.terminate(process)
   237  		r.destroy()
   238  		return -1, err
   239  	}
   240  	if r.pidFile != "" {
   241  		if err := createPidFile(r.pidFile, process); err != nil {
   242  			r.terminate(process)
   243  			r.destroy()
   244  			return -1, err
   245  		}
   246  	}
   247  	if r.detach || r.create {
   248  		return 0, nil
   249  	}
   250  	status, err := handler.forward(process)
   251  	if err != nil {
   252  		r.terminate(process)
   253  	}
   254  	r.destroy()
   255  	return status, err
   256  }
   257  
   258  func (r *runner) destroy() {
   259  	if r.shouldDestroy {
   260  		destroy(r.container)
   261  	}
   262  }
   263  
   264  func (r *runner) terminate(p *libcontainer.Process) {
   265  	p.Signal(syscall.SIGKILL)
   266  	p.Wait()
   267  }
   268  
   269  func validateProcessSpec(spec *specs.Process) error {
   270  	if spec.Cwd == "" {
   271  		return fmt.Errorf("Cwd property must not be empty")
   272  	}
   273  	if !filepath.IsAbs(spec.Cwd) {
   274  		return fmt.Errorf("Cwd must be an absolute path")
   275  	}
   276  	if len(spec.Args) == 0 {
   277  		return fmt.Errorf("args must not be empty")
   278  	}
   279  	return nil
   280  }
   281  
   282  func startContainer(context *cli.Context, spec *specs.Spec, create bool) (int, error) {
   283  	id := context.Args().First()
   284  	if id == "" {
   285  		return -1, errEmptyID
   286  	}
   287  	container, err := createContainer(context, id, spec)
   288  	if err != nil {
   289  		return -1, err
   290  	}
   291  	// Support on-demand socket activation by passing file descriptors into the container init process.
   292  	listenFDs := []*os.File{}
   293  	if os.Getenv("LISTEN_FDS") != "" {
   294  		listenFDs = activation.Files(false)
   295  	}
   296  	r := &runner{
   297  		enableSubreaper: !context.Bool("no-subreaper"),
   298  		shouldDestroy:   true,
   299  		container:       container,
   300  		listenFDs:       listenFDs,
   301  		console:         context.String("console"),
   302  		detach:          context.Bool("detach"),
   303  		pidFile:         context.String("pid-file"),
   304  		create:          create,
   305  	}
   306  	return r.run(&spec.Process)
   307  }