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 }