github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/domain/infra/abi/system.go (about)

     1  package abi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/containers/common/pkg/config"
    14  	"github.com/containers/podman/v2/libpod"
    15  	"github.com/containers/podman/v2/libpod/define"
    16  	"github.com/containers/podman/v2/pkg/cgroups"
    17  	"github.com/containers/podman/v2/pkg/domain/entities"
    18  	"github.com/containers/podman/v2/pkg/rootless"
    19  	"github.com/containers/podman/v2/pkg/util"
    20  	"github.com/containers/podman/v2/utils"
    21  	"github.com/containers/storage"
    22  	"github.com/pkg/errors"
    23  	"github.com/sirupsen/logrus"
    24  	"github.com/spf13/cobra"
    25  	"github.com/spf13/pflag"
    26  )
    27  
    28  func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
    29  	info, err := ic.Libpod.Info()
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	xdg, err := util.GetRuntimeDir()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	if len(xdg) == 0 {
    38  		// If no xdg is returned, assume root socket
    39  		xdg = "/run"
    40  	}
    41  
    42  	// Glue the socket path together
    43  	socketPath := filepath.Join(xdg, "podman", "podman.sock")
    44  	rs := define.RemoteSocket{
    45  		Path:   socketPath,
    46  		Exists: false,
    47  	}
    48  
    49  	// Check if the socket exists
    50  	if fi, err := os.Stat(socketPath); err == nil {
    51  		if fi.Mode()&os.ModeSocket != 0 {
    52  			rs.Exists = true
    53  		}
    54  	}
    55  	// TODO
    56  	// it was suggested future versions of this could perform
    57  	// a ping on the socket for greater confidence the socket is
    58  	// actually active.
    59  	info.Host.RemoteSocket = &rs
    60  	return info, err
    61  }
    62  
    63  func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error {
    64  	// do it only after podman has already re-execed and running with uid==0.
    65  	if os.Geteuid() == 0 {
    66  		ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
    67  		if err != nil {
    68  			logrus.Warnf("Failed to detect the owner for the current cgroup: %v", err)
    69  		}
    70  		if !ownsCgroup {
    71  			conf, err := ic.Config(context.Background())
    72  			if err != nil {
    73  				return err
    74  			}
    75  
    76  			initCommand, err := ioutil.ReadFile("/proc/1/comm")
    77  			// On errors, default to systemd
    78  			runsUnderSystemd := err != nil || strings.TrimRight(string(initCommand), "\n") == "systemd"
    79  
    80  			unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
    81  			if runsUnderSystemd || conf.Engine.CgroupManager == config.SystemdCgroupsManager {
    82  				if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
    83  					logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err)
    84  				}
    85  			}
    86  		}
    87  		return nil
    88  	}
    89  
    90  	tmpDir, err := ic.Libpod.TmpDir()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir)
    95  	if err != nil {
    96  		return errors.Wrapf(err, "could not get pause process pid file path")
    97  	}
    98  
    99  	became, ret, err := rootless.TryJoinPauseProcess(pausePidPath)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	if became {
   104  		os.Exit(ret)
   105  	}
   106  
   107  	// if there is no pid file, try to join existing containers, and create a pause process.
   108  	ctrs, err := ic.Libpod.GetRunningContainers()
   109  	if err != nil {
   110  		logrus.Error(err.Error())
   111  		os.Exit(1)
   112  	}
   113  
   114  	paths := []string{}
   115  	for _, ctr := range ctrs {
   116  		paths = append(paths, ctr.Config().ConmonPidFile)
   117  	}
   118  
   119  	became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
   120  	if err := movePauseProcessToScope(ic.Libpod); err != nil {
   121  		conf, err := ic.Config(context.Background())
   122  		if err != nil {
   123  			return err
   124  		}
   125  		if conf.Engine.CgroupManager == config.SystemdCgroupsManager {
   126  			logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err)
   127  		} else {
   128  			logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err)
   129  		}
   130  	}
   131  	if err != nil {
   132  		logrus.Error(errors.Wrapf(err, "invalid internal status, try resetting the pause process with %q", os.Args[0]+" system migrate"))
   133  		os.Exit(1)
   134  	}
   135  	if became {
   136  		os.Exit(ret)
   137  	}
   138  	return nil
   139  }
   140  
   141  func movePauseProcessToScope(r *libpod.Runtime) error {
   142  	tmpDir, err := r.TmpDir()
   143  	if err != nil {
   144  		return err
   145  	}
   146  	pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir)
   147  	if err != nil {
   148  		return errors.Wrapf(err, "could not get pause process pid file path")
   149  	}
   150  
   151  	data, err := ioutil.ReadFile(pausePidPath)
   152  	if err != nil {
   153  		return errors.Wrapf(err, "cannot read pause pid file")
   154  	}
   155  	pid, err := strconv.ParseUint(string(data), 10, 0)
   156  	if err != nil {
   157  		return errors.Wrapf(err, "cannot parse pid file %s", pausePidPath)
   158  	}
   159  
   160  	return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope")
   161  }
   162  
   163  // checkInput can be used to verify any of the globalopt values
   164  func checkInput() error { // nolint:deadcode,unused
   165  	return nil
   166  }
   167  
   168  // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images.
   169  func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
   170  	var systemPruneReport = new(entities.SystemPruneReport)
   171  	podPruneReport, err := ic.prunePodHelper(ctx)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	systemPruneReport.PodPruneReport = podPruneReport
   176  
   177  	containerPruneReport, err := ic.pruneContainersHelper(nil)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	systemPruneReport.ContainerPruneReport = containerPruneReport
   182  
   183  	results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	report := entities.ImagePruneReport{
   188  		Report: entities.Report{
   189  			Id:  results,
   190  			Err: nil,
   191  		},
   192  	}
   193  
   194  	systemPruneReport.ImagePruneReport = &report
   195  
   196  	if options.Volume {
   197  		volumePruneReport, err := ic.pruneVolumesHelper(ctx)
   198  		if err != nil {
   199  			return nil, err
   200  		}
   201  		systemPruneReport.VolumePruneReport = volumePruneReport
   202  	}
   203  	return systemPruneReport, nil
   204  }
   205  
   206  func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) {
   207  	var (
   208  		dfImages = []*entities.SystemDfImageReport{}
   209  	)
   210  
   211  	// Compute disk-usage stats for all local images.
   212  	imgs, err := ic.Libpod.ImageRuntime().GetImages()
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	imageStats, err := ic.Libpod.ImageRuntime().DiskUsage(ctx, imgs)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	for _, stat := range imageStats {
   223  		report := entities.SystemDfImageReport{
   224  			Repository: stat.Repository,
   225  			Tag:        stat.Tag,
   226  			ImageID:    stat.ID,
   227  			Created:    stat.Created,
   228  			Size:       int64(stat.Size),
   229  			SharedSize: int64(stat.SharedSize),
   230  			UniqueSize: int64(stat.UniqueSize),
   231  			Containers: stat.Containers,
   232  		}
   233  		dfImages = append(dfImages, &report)
   234  	}
   235  
   236  	// Get Containers and iterate them
   237  	cons, err := ic.Libpod.GetAllContainers()
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	dfContainers := make([]*entities.SystemDfContainerReport, 0, len(cons))
   242  	for _, c := range cons {
   243  		iid, _ := c.Image()
   244  		state, err := c.State()
   245  		if err != nil {
   246  			return nil, errors.Wrapf(err, "Failed to get state of container %s", c.ID())
   247  		}
   248  		conSize, err := c.RootFsSize()
   249  		if err != nil {
   250  			if errors.Cause(err) == storage.ErrContainerUnknown {
   251  				logrus.Error(errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID()))
   252  			} else {
   253  				return nil, errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID())
   254  			}
   255  		}
   256  		rwsize, err := c.RWSize()
   257  		if err != nil {
   258  			if errors.Cause(err) == storage.ErrContainerUnknown {
   259  				logrus.Error(errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID()))
   260  			} else {
   261  				return nil, errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID())
   262  			}
   263  		}
   264  		report := entities.SystemDfContainerReport{
   265  			ContainerID:  c.ID(),
   266  			Image:        iid,
   267  			Command:      c.Command(),
   268  			LocalVolumes: len(c.UserVolumes()),
   269  			RWSize:       rwsize,
   270  			Size:         conSize,
   271  			Created:      c.CreatedTime(),
   272  			Status:       state.String(),
   273  			Names:        c.Name(),
   274  		}
   275  		dfContainers = append(dfContainers, &report)
   276  	}
   277  
   278  	//	Get volumes and iterate them
   279  	vols, err := ic.Libpod.GetAllVolumes()
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  
   284  	running, err := ic.Libpod.GetRunningContainers()
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	runningContainers := make([]string, 0, len(running))
   289  	for _, c := range running {
   290  		runningContainers = append(runningContainers, c.ID())
   291  	}
   292  
   293  	dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols))
   294  	var reclaimableSize int64
   295  	for _, v := range vols {
   296  		var consInUse int
   297  		volSize, err := sizeOfPath(v.MountPoint())
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  		inUse, err := v.VolumeInUse()
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  		if len(inUse) == 0 {
   306  			reclaimableSize += volSize
   307  		}
   308  		for _, viu := range inUse {
   309  			if util.StringInSlice(viu, runningContainers) {
   310  				consInUse++
   311  			}
   312  		}
   313  		report := entities.SystemDfVolumeReport{
   314  			VolumeName:      v.Name(),
   315  			Links:           consInUse,
   316  			Size:            volSize,
   317  			ReclaimableSize: reclaimableSize,
   318  		}
   319  		dfVolumes = append(dfVolumes, &report)
   320  	}
   321  	return &entities.SystemDfReport{
   322  		Images:     dfImages,
   323  		Containers: dfContainers,
   324  		Volumes:    dfVolumes,
   325  	}, nil
   326  }
   327  
   328  // sizeOfPath determines the file usage of a given path. it was called volumeSize in v1
   329  // and now is made to be generic and take a path instead of a libpod volume
   330  func sizeOfPath(path string) (int64, error) {
   331  	var size int64
   332  	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
   333  		if err == nil && !info.IsDir() {
   334  			size += info.Size()
   335  		}
   336  		return err
   337  	})
   338  	return size, err
   339  }
   340  
   341  func (se *SystemEngine) Reset(ctx context.Context) error {
   342  	return se.Libpod.Reset(ctx)
   343  }
   344  
   345  func (se *SystemEngine) Renumber(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig) error {
   346  	return nil
   347  }
   348  
   349  func (se SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error {
   350  	return nil
   351  }
   352  
   353  func (se SystemEngine) Shutdown(ctx context.Context) {
   354  	if err := se.Libpod.Shutdown(false); err != nil {
   355  		logrus.Error(err)
   356  	}
   357  }
   358  
   359  func unshareEnv(graphroot, runroot string) []string {
   360  	return append(os.Environ(), "_CONTAINERS_USERNS_CONFIGURED=done",
   361  		fmt.Sprintf("CONTAINERS_GRAPHROOT=%s", graphroot),
   362  		fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
   363  }
   364  
   365  func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
   366  	cmd := exec.Command(args[0], args[1:]...)
   367  	cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
   368  	cmd.Stdin = os.Stdin
   369  	cmd.Stdout = os.Stdout
   370  	cmd.Stderr = os.Stderr
   371  	return cmd.Run()
   372  }
   373  
   374  func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
   375  	var report entities.SystemVersionReport
   376  	v, err := define.GetVersion()
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	report.Client = &v
   381  	return &report, err
   382  }