mirror of
https://github.com/containers/podman.git
synced 2025-10-18 11:42:55 +08:00
Resurrect auto-port reassignment, but for all providers
- Updates common to pull in new locked edit [NO NEW TESTS NEEDED] Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
144
vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go
generated
vendored
144
vendor/github.com/containers/common/pkg/cgroups/cgroups_linux.go
generated
vendored
@ -13,6 +13,9 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
@ -22,6 +25,7 @@ import (
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -30,6 +34,10 @@ var (
|
||||
// ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environment
|
||||
ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments")
|
||||
ErrStatCgroup = errors.New("no cgroup available for gathering user statistics")
|
||||
|
||||
isUnifiedOnce sync.Once
|
||||
isUnified bool
|
||||
isUnifiedErr error
|
||||
)
|
||||
|
||||
// CgroupControl controls a cgroup hierarchy
|
||||
@ -731,3 +739,139 @@ func SystemCPUUsage() (uint64, error) {
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
|
||||
func IsCgroup2UnifiedMode() (bool, error) {
|
||||
isUnifiedOnce.Do(func() {
|
||||
var st syscall.Statfs_t
|
||||
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
|
||||
isUnified, isUnifiedErr = false, err
|
||||
} else {
|
||||
isUnified, isUnifiedErr = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
|
||||
}
|
||||
})
|
||||
return isUnified, isUnifiedErr
|
||||
}
|
||||
|
||||
// UserConnection returns an user connection to D-BUS
|
||||
func UserConnection(uid int) (*systemdDbus.Conn, error) {
|
||||
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
|
||||
})
|
||||
}
|
||||
|
||||
// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
|
||||
// current cgroup.
|
||||
func UserOwnsCurrentSystemdCgroup() (bool, error) {
|
||||
uid := os.Geteuid()
|
||||
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
f, err := os.Open("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, ":", 3)
|
||||
|
||||
if len(parts) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
var cgroupPath string
|
||||
|
||||
if cgroup2 {
|
||||
cgroupPath = filepath.Join(cgroupRoot, parts[2])
|
||||
} else {
|
||||
if parts[1] != "name=systemd" {
|
||||
continue
|
||||
}
|
||||
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
|
||||
}
|
||||
|
||||
st, err := os.Stat(cgroupPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
s := st.Sys()
|
||||
if s == nil {
|
||||
return false, fmt.Errorf("stat cgroup path %s", cgroupPath)
|
||||
}
|
||||
|
||||
if int(s.(*syscall.Stat_t).Uid) != uid {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return false, fmt.Errorf("parsing file /proc/self/cgroup: %w", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// rmDirRecursively delete recursively a cgroup directory.
|
||||
// It differs from os.RemoveAll as it doesn't attempt to unlink files.
|
||||
// On cgroupfs we are allowed only to rmdir empty directories.
|
||||
func rmDirRecursively(path string) error {
|
||||
killProcesses := func(signal syscall.Signal) {
|
||||
if signal == unix.SIGKILL {
|
||||
if err := os.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// kill all the processes that are still part of the cgroup
|
||||
if procs, err := os.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil {
|
||||
for _, pidS := range strings.Split(string(procs), "\n") {
|
||||
if pid, err := strconv.Atoi(pidS); err == nil {
|
||||
_ = unix.Kill(pid, signal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Remove(path); err == nil || errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range entries {
|
||||
if i.IsDir() {
|
||||
if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attempts := 0
|
||||
for {
|
||||
err := os.Remove(path)
|
||||
if err == nil || errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, unix.EBUSY) {
|
||||
// send a SIGTERM after 3 second
|
||||
if attempts == 300 {
|
||||
killProcesses(unix.SIGTERM)
|
||||
}
|
||||
// send SIGKILL after 8 seconds
|
||||
if attempts == 800 {
|
||||
killProcesses(unix.SIGKILL)
|
||||
}
|
||||
// give up after 10 seconds
|
||||
if attempts < 1000 {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
attempts++
|
||||
continue
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("remove %s: %w", path, err)
|
||||
}
|
||||
}
|
||||
|
162
vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go
generated
vendored
162
vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go
generated
vendored
@ -1,162 +0,0 @@
|
||||
//go:build linux
|
||||
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
isUnifiedOnce sync.Once
|
||||
isUnified bool
|
||||
isUnifiedErr error
|
||||
)
|
||||
|
||||
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
|
||||
func IsCgroup2UnifiedMode() (bool, error) {
|
||||
isUnifiedOnce.Do(func() {
|
||||
var st syscall.Statfs_t
|
||||
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
|
||||
isUnified, isUnifiedErr = false, err
|
||||
} else {
|
||||
isUnified, isUnifiedErr = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
|
||||
}
|
||||
})
|
||||
return isUnified, isUnifiedErr
|
||||
}
|
||||
|
||||
// UserConnection returns an user connection to D-BUS
|
||||
func UserConnection(uid int) (*systemdDbus.Conn, error) {
|
||||
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
|
||||
})
|
||||
}
|
||||
|
||||
// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
|
||||
// current cgroup.
|
||||
func UserOwnsCurrentSystemdCgroup() (bool, error) {
|
||||
uid := os.Geteuid()
|
||||
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
f, err := os.Open("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, ":", 3)
|
||||
|
||||
if len(parts) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
var cgroupPath string
|
||||
|
||||
if cgroup2 {
|
||||
cgroupPath = filepath.Join(cgroupRoot, parts[2])
|
||||
} else {
|
||||
if parts[1] != "name=systemd" {
|
||||
continue
|
||||
}
|
||||
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
|
||||
}
|
||||
|
||||
st, err := os.Stat(cgroupPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
s := st.Sys()
|
||||
if s == nil {
|
||||
return false, fmt.Errorf("stat cgroup path %s", cgroupPath)
|
||||
}
|
||||
|
||||
if int(s.(*syscall.Stat_t).Uid) != uid {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return false, fmt.Errorf("parsing file /proc/self/cgroup: %w", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// rmDirRecursively delete recursively a cgroup directory.
|
||||
// It differs from os.RemoveAll as it doesn't attempt to unlink files.
|
||||
// On cgroupfs we are allowed only to rmdir empty directories.
|
||||
func rmDirRecursively(path string) error {
|
||||
killProcesses := func(signal syscall.Signal) {
|
||||
if signal == unix.SIGKILL {
|
||||
if err := os.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// kill all the processes that are still part of the cgroup
|
||||
if procs, err := os.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil {
|
||||
for _, pidS := range strings.Split(string(procs), "\n") {
|
||||
if pid, err := strconv.Atoi(pidS); err == nil {
|
||||
_ = unix.Kill(pid, signal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Remove(path); err == nil || errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range entries {
|
||||
if i.IsDir() {
|
||||
if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attempts := 0
|
||||
for {
|
||||
err := os.Remove(path)
|
||||
if err == nil || errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, unix.EBUSY) {
|
||||
// send a SIGTERM after 3 second
|
||||
if attempts == 300 {
|
||||
killProcesses(unix.SIGTERM)
|
||||
}
|
||||
// send SIGKILL after 8 seconds
|
||||
if attempts == 800 {
|
||||
killProcesses(unix.SIGKILL)
|
||||
}
|
||||
// give up after 10 seconds
|
||||
if attempts < 1000 {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
attempts++
|
||||
continue
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("remove %s: %w", path, err)
|
||||
}
|
||||
}
|
8
vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go
generated
vendored
8
vendor/github.com/containers/common/pkg/cgroups/cgroups_unsupported.go
generated
vendored
@ -3,10 +3,7 @@
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
)
|
||||
|
||||
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
|
||||
@ -23,8 +20,3 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) {
|
||||
func rmDirRecursively(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
// UserConnection returns an user connection to D-BUS
|
||||
func UserConnection(uid int) (*systemdDbus.Conn, error) {
|
||||
return nil, fmt.Errorf("systemd d-bus is not supported on this platform")
|
||||
}
|
||||
|
80
vendor/github.com/containers/common/pkg/cgroups/systemd.go
generated
vendored
80
vendor/github.com/containers/common/pkg/cgroups/systemd.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
//go:build !linux
|
||||
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
func systemdCreate(path string, c *systemdDbus.Conn) error {
|
||||
slice, name := filepath.Split(path)
|
||||
slice = strings.TrimSuffix(slice, "/")
|
||||
|
||||
var lastError error
|
||||
for i := 0; i < 2; i++ {
|
||||
properties := []systemdDbus.Property{
|
||||
systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
|
||||
systemdDbus.PropWants(slice),
|
||||
}
|
||||
pMap := map[string]bool{
|
||||
"DefaultDependencies": false,
|
||||
"MemoryAccounting": true,
|
||||
"CPUAccounting": true,
|
||||
"BlockIOAccounting": true,
|
||||
}
|
||||
if i == 0 {
|
||||
pMap["Delegate"] = true
|
||||
}
|
||||
for k, v := range pMap {
|
||||
p := systemdDbus.Property{
|
||||
Name: k,
|
||||
Value: dbus.MakeVariant(v),
|
||||
}
|
||||
properties = append(properties, p)
|
||||
}
|
||||
|
||||
ch := make(chan string)
|
||||
_, err := c.StartTransientUnitContext(context.TODO(), name, "replace", properties, ch)
|
||||
if err != nil {
|
||||
lastError = err
|
||||
continue
|
||||
}
|
||||
<-ch
|
||||
return nil
|
||||
}
|
||||
return lastError
|
||||
}
|
||||
|
||||
/*
|
||||
systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that
|
||||
has the following license:
|
||||
Copyright The containerd Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
|
||||
name := filepath.Base(path)
|
||||
|
||||
ch := make(chan string)
|
||||
_, err := c.StopUnitContext(context.TODO(), name, "replace", ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
<-ch
|
||||
return nil
|
||||
}
|
54
vendor/github.com/containers/common/pkg/config/connections.go
generated
vendored
54
vendor/github.com/containers/common/pkg/config/connections.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/lockfile"
|
||||
)
|
||||
|
||||
const connectionsFile = "podman-connections.json"
|
||||
@ -64,28 +65,24 @@ type Farm struct {
|
||||
ReadWrite bool
|
||||
}
|
||||
|
||||
func readConnectionConf() (*ConnectionsFile, string, error) {
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
func readConnectionConf(path string) (*ConnectionsFile, error) {
|
||||
conf := new(ConnectionsFile)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
// return empty config if file does not exists
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return conf, path, nil
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = json.NewDecoder(f).Decode(conf)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("parse %q: %w", path, err)
|
||||
return nil, fmt.Errorf("parse %q: %w", path, err)
|
||||
}
|
||||
return conf, path, nil
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func writeConnectionConf(path string, conf *ConnectionsFile) error {
|
||||
@ -113,7 +110,20 @@ func writeConnectionConf(path string, conf *ConnectionsFile) error {
|
||||
// The function will read and write the file automatically and the
|
||||
// callback function just needs to modify the cfg as needed.
|
||||
func EditConnectionConfig(callback func(cfg *ConnectionsFile) error) error {
|
||||
conf, path, err := readConnectionConf()
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lockPath := path + ".lock"
|
||||
lock, err := lockfile.GetLockFile(lockPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("obtain lock file: %w", err)
|
||||
}
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
conf, err := readConnectionConf(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read connections file: %w", err)
|
||||
}
|
||||
@ -139,7 +149,11 @@ func makeConnection(name string, dst Destination, def, readWrite bool) *Connecti
|
||||
|
||||
// GetConnection return the connection for the given name or if def is set to true then return the default connection.
|
||||
func (c *Config) GetConnection(name string, def bool) (*Connection, error) {
|
||||
conConf, _, err := readConnectionConf()
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conConf, err := readConnectionConf(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -167,7 +181,11 @@ func (c *Config) GetConnection(name string, def bool) (*Connection, error) {
|
||||
|
||||
// GetAllConnections return all configured connections
|
||||
func (c *Config) GetAllConnections() ([]Connection, error) {
|
||||
conConf, _, err := readConnectionConf()
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conConf, err := readConnectionConf(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -222,7 +240,11 @@ func (c *Config) GetDefaultFarmConnections() (string, []Connection, error) {
|
||||
// if def is true it will use the default farm instead of the name.
|
||||
// Returns the name of the farm and the connections for it.
|
||||
func (c *Config) getFarmConnections(name string, def bool) (string, []Connection, error) {
|
||||
conConf, _, err := readConnectionConf()
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
conConf, err := readConnectionConf(path)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
@ -259,7 +281,11 @@ func makeFarm(name string, cons []string, def, readWrite bool) Farm {
|
||||
|
||||
// GetAllFarms returns all configured farms
|
||||
func (c *Config) GetAllFarms() ([]Farm, error) {
|
||||
conConf, _, err := readConnectionConf()
|
||||
path, err := connectionsConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conConf, err := readConnectionConf(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user