github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/abi/system.go (about)

     1  package abi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  
    11  	"github.com/containers/common/pkg/cgroups"
    12  	"github.com/containers/common/pkg/config"
    13  	cutil "github.com/containers/common/pkg/util"
    14  	"github.com/hanks177/podman/v4/libpod/define"
    15  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    16  	"github.com/hanks177/podman/v4/pkg/domain/entities/reports"
    17  	"github.com/hanks177/podman/v4/pkg/rootless"
    18  	"github.com/hanks177/podman/v4/pkg/util"
    19  	"github.com/hanks177/podman/v4/utils"
    20  	"github.com/containers/storage"
    21  	"github.com/containers/storage/pkg/unshare"
    22  	"github.com/pkg/errors"
    23  	"github.com/sirupsen/logrus"
    24  	"github.com/spf13/pflag"
    25  )
    26  
    27  func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
    28  	info, err := ic.Libpod.Info()
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	info.Host.RemoteSocket = &define.RemoteSocket{Path: ic.Libpod.RemoteURI()}
    33  
    34  	// `podman system connection add` invokes podman via ssh to fill in connection string. Here
    35  	// we are reporting the default systemd activation socket path as we cannot know if a future
    36  	// service may be run with another URI.
    37  	if ic.Libpod.RemoteURI() == "" {
    38  		xdg := "/run"
    39  		if path, err := util.GetRuntimeDir(); err != nil {
    40  			// Info is as good as we can guess...
    41  			return info, err
    42  		} else if path != "" {
    43  			xdg = path
    44  		}
    45  
    46  		uri := url.URL{
    47  			Scheme: "unix",
    48  			Path:   filepath.Join(xdg, "podman", "podman.sock"),
    49  		}
    50  		ic.Libpod.SetRemoteURI(uri.String())
    51  		info.Host.RemoteSocket.Path = uri.Path
    52  	}
    53  
    54  	uri, err := url.Parse(ic.Libpod.RemoteURI())
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	if uri.Scheme == "unix" {
    60  		_, err := os.Stat(uri.Path)
    61  		info.Host.RemoteSocket.Exists = err == nil
    62  	} else {
    63  		info.Host.RemoteSocket.Exists = true
    64  	}
    65  
    66  	return info, err
    67  }
    68  
    69  func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) error {
    70  	// do it only after podman has already re-execed and running with uid==0.
    71  	hasCapSysAdmin, err := unshare.HasCapSysAdmin()
    72  	if err != nil {
    73  		return err
    74  	}
    75  	if hasCapSysAdmin {
    76  		ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
    77  		if err != nil {
    78  			logrus.Infof("Failed to detect the owner for the current cgroup: %v", err)
    79  		}
    80  		if !ownsCgroup {
    81  			conf, err := ic.Config(context.Background())
    82  			if err != nil {
    83  				return err
    84  			}
    85  			runsUnderSystemd := utils.RunsOnSystemd()
    86  			unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
    87  			if runsUnderSystemd || conf.Engine.CgroupManager == config.SystemdCgroupsManager {
    88  				if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
    89  					logrus.Debugf("Failed to add podman to systemd sandbox cgroup: %v", err)
    90  				}
    91  			}
    92  		}
    93  		return nil
    94  	}
    95  
    96  	tmpDir, err := ic.Libpod.TmpDir()
    97  	if err != nil {
    98  		return err
    99  	}
   100  	pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir)
   101  	if err != nil {
   102  		return errors.Wrapf(err, "could not get pause process pid file path")
   103  	}
   104  
   105  	became, ret, err := rootless.TryJoinPauseProcess(pausePidPath)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	if became {
   110  		os.Exit(ret)
   111  	}
   112  	if noMoveProcess {
   113  		return nil
   114  	}
   115  
   116  	// if there is no pid file, try to join existing containers, and create a pause process.
   117  	ctrs, err := ic.Libpod.GetRunningContainers()
   118  	if err != nil {
   119  		logrus.Error(err.Error())
   120  		os.Exit(1)
   121  	}
   122  
   123  	paths := []string{}
   124  	for _, ctr := range ctrs {
   125  		paths = append(paths, ctr.Config().ConmonPidFile)
   126  	}
   127  
   128  	became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
   129  	utils.MovePauseProcessToScope(pausePidPath)
   130  	if err != nil {
   131  		logrus.Error(errors.Wrapf(err, "invalid internal status, try resetting the pause process with %q", os.Args[0]+" system migrate"))
   132  		os.Exit(1)
   133  	}
   134  	if became {
   135  		os.Exit(ret)
   136  	}
   137  	return nil
   138  }
   139  
   140  // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images.
   141  func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
   142  	var systemPruneReport = new(entities.SystemPruneReport)
   143  	filters := []string{}
   144  	for k, v := range options.Filters {
   145  		filters = append(filters, fmt.Sprintf("%s=%s", k, v[0]))
   146  	}
   147  	reclaimedSpace := (uint64)(0)
   148  	found := true
   149  	for found {
   150  		found = false
   151  		podPruneReport, err := ic.prunePodHelper(ctx)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		if len(podPruneReport) > 0 {
   156  			found = true
   157  		}
   158  		systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...)
   159  
   160  		// TODO: Figure out cleaner way to handle all of the different PruneOptions
   161  		containerPruneOptions := entities.ContainerPruneOptions{}
   162  		containerPruneOptions.Filters = (url.Values)(options.Filters)
   163  
   164  		containerPruneReports, err := ic.ContainerPrune(ctx, containerPruneOptions)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  		reclaimedSpace += reports.PruneReportsSize(containerPruneReports)
   169  		systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...)
   170  		imagePruneOptions := entities.ImagePruneOptions{
   171  			All:    options.All,
   172  			Filter: filters,
   173  		}
   174  		imageEngine := ImageEngine{Libpod: ic.Libpod}
   175  		imagePruneReports, err := imageEngine.Prune(ctx, imagePruneOptions)
   176  		reclaimedSpace += reports.PruneReportsSize(imagePruneReports)
   177  
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  		if len(imagePruneReports) > 0 {
   182  			found = true
   183  		}
   184  
   185  		systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...)
   186  		if options.Volume {
   187  			volumePruneOptions := entities.VolumePruneOptions{}
   188  			volumePruneOptions.Filters = (url.Values)(options.Filters)
   189  			volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions)
   190  			if err != nil {
   191  				return nil, err
   192  			}
   193  			if len(volumePruneReport) > 0 {
   194  				found = true
   195  			}
   196  			reclaimedSpace += reports.PruneReportsSize(volumePruneReport)
   197  			systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...)
   198  		}
   199  	}
   200  	systemPruneReport.ReclaimedSpace = reclaimedSpace
   201  	return systemPruneReport, nil
   202  }
   203  
   204  func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) {
   205  	var (
   206  		dfImages = []*entities.SystemDfImageReport{}
   207  	)
   208  
   209  	imageStats, err := ic.Libpod.LibimageRuntime().DiskUsage(ctx)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	for _, stat := range imageStats {
   215  		report := entities.SystemDfImageReport{
   216  			Repository: stat.Repository,
   217  			Tag:        stat.Tag,
   218  			ImageID:    stat.ID,
   219  			Created:    stat.Created,
   220  			Size:       stat.Size,
   221  			SharedSize: stat.SharedSize,
   222  			UniqueSize: stat.UniqueSize,
   223  			Containers: stat.Containers,
   224  		}
   225  		dfImages = append(dfImages, &report)
   226  	}
   227  
   228  	// Get Containers and iterate them
   229  	cons, err := ic.Libpod.GetAllContainers()
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	dfContainers := make([]*entities.SystemDfContainerReport, 0, len(cons))
   234  	for _, c := range cons {
   235  		iid, _ := c.Image()
   236  		state, err := c.State()
   237  		if err != nil {
   238  			return nil, errors.Wrapf(err, "Failed to get state of container %s", c.ID())
   239  		}
   240  		conSize, err := c.RootFsSize()
   241  		if err != nil {
   242  			if errors.Cause(err) == storage.ErrContainerUnknown {
   243  				logrus.Error(errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID()))
   244  			} else {
   245  				return nil, errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID())
   246  			}
   247  		}
   248  		rwsize, err := c.RWSize()
   249  		if err != nil {
   250  			if errors.Cause(err) == storage.ErrContainerUnknown {
   251  				logrus.Error(errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID()))
   252  			} else {
   253  				return nil, errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID())
   254  			}
   255  		}
   256  		report := entities.SystemDfContainerReport{
   257  			ContainerID:  c.ID(),
   258  			Image:        iid,
   259  			Command:      c.Command(),
   260  			LocalVolumes: len(c.UserVolumes()),
   261  			RWSize:       rwsize,
   262  			Size:         conSize,
   263  			Created:      c.CreatedTime(),
   264  			Status:       state.String(),
   265  			Names:        c.Name(),
   266  		}
   267  		dfContainers = append(dfContainers, &report)
   268  	}
   269  
   270  	//	Get volumes and iterate them
   271  	vols, err := ic.Libpod.GetAllVolumes()
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  
   276  	running, err := ic.Libpod.GetRunningContainers()
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	runningContainers := make([]string, 0, len(running))
   281  	for _, c := range running {
   282  		runningContainers = append(runningContainers, c.ID())
   283  	}
   284  
   285  	dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols))
   286  	var reclaimableSize uint64
   287  	for _, v := range vols {
   288  		var consInUse int
   289  		mountPoint, err := v.MountPoint()
   290  		if err != nil {
   291  			return nil, err
   292  		}
   293  		if mountPoint == "" {
   294  			// We can't get any info on this volume, as it's not
   295  			// mounted.
   296  			// TODO: fix this.
   297  			continue
   298  		}
   299  		volSize, err := util.SizeOfPath(mountPoint)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  		inUse, err := v.VolumeInUse()
   304  		if err != nil {
   305  			return nil, err
   306  		}
   307  		if len(inUse) == 0 {
   308  			reclaimableSize += volSize
   309  		}
   310  		for _, viu := range inUse {
   311  			if cutil.StringInSlice(viu, runningContainers) {
   312  				consInUse++
   313  			}
   314  		}
   315  		report := entities.SystemDfVolumeReport{
   316  			VolumeName:      v.Name(),
   317  			Links:           consInUse,
   318  			Size:            int64(volSize),
   319  			ReclaimableSize: int64(reclaimableSize),
   320  		}
   321  		dfVolumes = append(dfVolumes, &report)
   322  	}
   323  	return &entities.SystemDfReport{
   324  		Images:     dfImages,
   325  		Containers: dfContainers,
   326  		Volumes:    dfVolumes,
   327  	}, nil
   328  }
   329  
   330  func (se *SystemEngine) Reset(ctx context.Context) error {
   331  	return nil
   332  }
   333  
   334  func (se *SystemEngine) Renumber(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig) error {
   335  	return nil
   336  }
   337  
   338  func (se SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error {
   339  	return nil
   340  }
   341  
   342  func (se SystemEngine) Shutdown(ctx context.Context) {
   343  	if err := se.Libpod.Shutdown(false); err != nil {
   344  		logrus.Error(err)
   345  	}
   346  }
   347  
   348  func unshareEnv(graphroot, runroot string) []string {
   349  	return append(os.Environ(), "_CONTAINERS_USERNS_CONFIGURED=done",
   350  		fmt.Sprintf("CONTAINERS_GRAPHROOT=%s", graphroot),
   351  		fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
   352  }
   353  
   354  func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
   355  	unshare := func() error {
   356  		cmd := exec.Command(args[0], args[1:]...)
   357  		cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
   358  		cmd.Stdin = os.Stdin
   359  		cmd.Stdout = os.Stdout
   360  		cmd.Stderr = os.Stderr
   361  		return cmd.Run()
   362  	}
   363  
   364  	if options.RootlessNetNS {
   365  		rootlessNetNS, err := ic.Libpod.GetRootlessNetNs(true)
   366  		if err != nil {
   367  			return err
   368  		}
   369  		// Make sure to unlock, unshare can run for a long time.
   370  		rootlessNetNS.Lock.Unlock()
   371  		// We do not want to cleanup the netns after unshare.
   372  		// The problem is that we cannot know if we need to cleanup and
   373  		// secondly unshare should allow user to setup the namespace with
   374  		// special things, e.g. potentially macvlan or something like that.
   375  		return rootlessNetNS.Do(unshare)
   376  	}
   377  	return unshare()
   378  }
   379  
   380  func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
   381  	var report entities.SystemVersionReport
   382  	v, err := define.GetVersion()
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  	report.Client = &v
   387  	return &report, err
   388  }