diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2019-07-03 15:58:26 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-03 15:58:26 +0200 |
commit | a22a32a0a5507811168e0251a92122cf3e74ef51 (patch) | |
tree | 28f9cd8cfb7b45bf94e5c84b0cb19136c95c6749 /libpod | |
parent | bf7d5a9ce8455e4480b5e0ef92b02099f119ee71 (diff) | |
parent | 473d0604546ef472f167ee671fdf1110bf74eb63 (diff) | |
download | podman-a22a32a0a5507811168e0251a92122cf3e74ef51.tar.gz podman-a22a32a0a5507811168e0251a92122cf3e74ef51.tar.bz2 podman-a22a32a0a5507811168e0251a92122cf3e74ef51.zip |
Merge pull request #3437 from giuseppe/fix-nocgo
build: allow to build without cgo on RISC-V
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_attach_linux.go | 6 | ||||
-rw-r--r-- | libpod/container_attach_linux_cgo.go | 11 | ||||
-rw-r--r-- | libpod/container_attach_linux_nocgo.go | 7 | ||||
-rw-r--r-- | libpod/lock/file/file_lock.go | 175 | ||||
-rw-r--r-- | libpod/lock/file/file_lock_test.go | 74 | ||||
-rw-r--r-- | libpod/lock/file_lock_manager.go | 110 | ||||
-rw-r--r-- | libpod/lock/shm/shm_lock.go | 2 | ||||
-rw-r--r-- | libpod/lock/shm/shm_lock_nocgo.go | 102 | ||||
-rw-r--r-- | libpod/runtime.go | 91 |
9 files changed, 544 insertions, 34 deletions
diff --git a/libpod/container_attach_linux.go b/libpod/container_attach_linux.go index f5aac5794..fc53268c3 100644 --- a/libpod/container_attach_linux.go +++ b/libpod/container_attach_linux.go @@ -19,10 +19,6 @@ import ( "k8s.io/client-go/tools/remotecommand" ) -//#include <sys/un.h> -// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;} -import "C" - /* Sync with stdpipe_t in conmon.c */ const ( AttachPipeStdin = 1 @@ -80,7 +76,7 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi socketPath := c.AttachSocketPath() - maxUnixLength := int(C.unix_path_length()) + maxUnixLength := unixPathLength() if maxUnixLength < len(socketPath) { socketPath = socketPath[0:maxUnixLength] } diff --git a/libpod/container_attach_linux_cgo.go b/libpod/container_attach_linux_cgo.go new file mode 100644 index 000000000..d81243360 --- /dev/null +++ b/libpod/container_attach_linux_cgo.go @@ -0,0 +1,11 @@ +//+build linux,cgo + +package libpod + +//#include <sys/un.h> +// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;} +import "C" + +func unixPathLength() int { + return int(C.unix_path_length()) +} diff --git a/libpod/container_attach_linux_nocgo.go b/libpod/container_attach_linux_nocgo.go new file mode 100644 index 000000000..a514a555d --- /dev/null +++ b/libpod/container_attach_linux_nocgo.go @@ -0,0 +1,7 @@ +//+build linux,!cgo + +package libpod + +func unixPathLength() int { + return 107 +} diff --git a/libpod/lock/file/file_lock.go b/libpod/lock/file/file_lock.go new file mode 100644 index 000000000..e50d67321 --- /dev/null +++ b/libpod/lock/file/file_lock.go @@ -0,0 +1,175 @@ +package file + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "syscall" + + "github.com/containers/storage" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// FileLocks is a struct enabling POSIX lock locking in a shared memory +// segment. +type FileLocks struct { // nolint + lockPath string + valid bool +} + +// CreateFileLock sets up a directory containing the various lock files. +func CreateFileLock(path string) (*FileLocks, error) { + _, err := os.Stat(path) + if err == nil { + return nil, errors.Wrapf(syscall.EEXIST, "directory %s exists", path) + } + if err := os.MkdirAll(path, 0711); err != nil { + return nil, errors.Wrapf(err, "cannot create %s", path) + } + + locks := new(FileLocks) + locks.lockPath = path + locks.valid = true + + return locks, nil +} + +// OpenFileLock opens an existing directory with the lock files. +func OpenFileLock(path string) (*FileLocks, error) { + _, err := os.Stat(path) + if err != nil { + return nil, errors.Wrapf(err, "accessing directory %s", path) + } + + locks := new(FileLocks) + locks.lockPath = path + locks.valid = true + + return locks, nil +} + +// Close closes an existing shared-memory segment. +// The segment will be rendered unusable after closing. +// WARNING: If you Close() while there are still locks locked, these locks may +// fail to release, causing a program freeze. +// Close() is only intended to be used while testing the locks. +func (locks *FileLocks) Close() error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + err := os.RemoveAll(locks.lockPath) + if err != nil { + return errors.Wrapf(err, "deleting directory %s", locks.lockPath) + } + return nil +} + +func (locks *FileLocks) getLockPath(lck uint32) string { + return filepath.Join(locks.lockPath, strconv.FormatInt(int64(lck), 10)) +} + +// AllocateLock allocates a lock and returns the index of the lock that was allocated. +func (locks *FileLocks) AllocateLock() (uint32, error) { + if !locks.valid { + return 0, errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + id := uint32(0) + for ; ; id++ { + path := locks.getLockPath(id) + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + if os.IsExist(err) { + continue + } + return 0, errors.Wrapf(err, "creating lock file") + } + f.Close() + break + } + return id, nil +} + +// AllocateGivenLock allocates the given lock from the shared-memory +// segment for use by a container or pod. +// If the lock is already in use or the index is invalid an error will be +// returned. +func (locks *FileLocks) AllocateGivenLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + f, err := os.OpenFile(locks.getLockPath(lck), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + return errors.Wrapf(err, "error creating lock %d", lck) + } + f.Close() + + return nil +} + +// DeallocateLock frees a lock in a shared-memory segment so it can be +// reallocated to another container or pod. +// The given lock must be already allocated, or an error will be returned. +func (locks *FileLocks) DeallocateLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + if err := os.Remove(locks.getLockPath(lck)); err != nil { + return errors.Wrapf(err, "deallocating lock %d", lck) + } + return nil +} + +// DeallocateAllLocks frees all locks so they can be reallocated to +// other containers and pods. +func (locks *FileLocks) DeallocateAllLocks() error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + files, err := ioutil.ReadDir(locks.lockPath) + if err != nil { + return errors.Wrapf(err, "error reading directory %s", locks.lockPath) + } + var lastErr error + for _, f := range files { + p := filepath.Join(locks.lockPath, f.Name()) + err := os.Remove(p) + if err != nil { + lastErr = err + logrus.Errorf("deallocating lock %s", p) + } + } + return lastErr +} + +// LockFileLock locks the given lock. +func (locks *FileLocks) LockFileLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + l, err := storage.GetLockfile(locks.getLockPath(lck)) + if err != nil { + return errors.Wrapf(err, "error acquiring lock") + } + + l.Lock() + return nil +} + +// UnlockFileLock unlocks the given lock. +func (locks *FileLocks) UnlockFileLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + l, err := storage.GetLockfile(locks.getLockPath(lck)) + if err != nil { + return errors.Wrapf(err, "error acquiring lock") + } + + l.Unlock() + return nil +} diff --git a/libpod/lock/file/file_lock_test.go b/libpod/lock/file/file_lock_test.go new file mode 100644 index 000000000..6320d6b70 --- /dev/null +++ b/libpod/lock/file/file_lock_test.go @@ -0,0 +1,74 @@ +package file + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Test that creating and destroying locks work +func TestCreateAndDeallocate(t *testing.T) { + d, err := ioutil.TempDir("", "filelock") + assert.NoError(t, err) + defer os.RemoveAll(d) + + l, err := OpenFileLock(filepath.Join(d, "locks")) + assert.Error(t, err) + + l, err = CreateFileLock(filepath.Join(d, "locks")) + assert.NoError(t, err) + + lock, err := l.AllocateLock() + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.Error(t, err) + + err = l.DeallocateLock(lock) + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.NoError(t, err) + + err = l.DeallocateAllLocks() + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.NoError(t, err) + + err = l.DeallocateAllLocks() + assert.NoError(t, err) +} + +// Test that creating and destroying locks work +func TestLockAndUnlock(t *testing.T) { + d, err := ioutil.TempDir("", "filelock") + assert.NoError(t, err) + defer os.RemoveAll(d) + + l, err := CreateFileLock(filepath.Join(d, "locks")) + assert.NoError(t, err) + + lock, err := l.AllocateLock() + assert.NoError(t, err) + + err = l.LockFileLock(lock) + assert.NoError(t, err) + + lslocks, err := exec.LookPath("lslocks") + if err == nil { + lockPath := l.getLockPath(lock) + out, err := exec.Command(lslocks, "--json", "-p", fmt.Sprintf("%d", os.Getpid())).CombinedOutput() + assert.NoError(t, err) + + assert.Contains(t, string(out), lockPath) + } + + err = l.UnlockFileLock(lock) + assert.NoError(t, err) +} diff --git a/libpod/lock/file_lock_manager.go b/libpod/lock/file_lock_manager.go new file mode 100644 index 000000000..8a4d939d3 --- /dev/null +++ b/libpod/lock/file_lock_manager.go @@ -0,0 +1,110 @@ +package lock + +import ( + "github.com/containers/libpod/libpod/lock/file" +) + +// FileLockManager manages shared memory locks. +type FileLockManager struct { + locks *file.FileLocks +} + +// NewFileLockManager makes a new FileLockManager at the specified directory. +func NewFileLockManager(lockPath string) (Manager, error) { + locks, err := file.CreateFileLock(lockPath) + if err != nil { + return nil, err + } + + manager := new(FileLockManager) + manager.locks = locks + + return manager, nil +} + +// OpenFileLockManager opens an existing FileLockManager at the specified directory. +func OpenFileLockManager(path string) (Manager, error) { + locks, err := file.OpenFileLock(path) + if err != nil { + return nil, err + } + + manager := new(FileLockManager) + manager.locks = locks + + return manager, nil +} + +// AllocateLock allocates a new lock from the manager. +func (m *FileLockManager) AllocateLock() (Locker, error) { + semIndex, err := m.locks.AllocateLock() + if err != nil { + return nil, err + } + + lock := new(FileLock) + lock.lockID = semIndex + lock.manager = m + + return lock, nil +} + +// AllocateAndRetrieveLock allocates the lock with the given ID and returns it. +// If the lock is already allocated, error. +func (m *FileLockManager) AllocateAndRetrieveLock(id uint32) (Locker, error) { + lock := new(FileLock) + lock.lockID = id + lock.manager = m + + if err := m.locks.AllocateGivenLock(id); err != nil { + return nil, err + } + + return lock, nil +} + +// RetrieveLock retrieves a lock from the manager given its ID. +func (m *FileLockManager) RetrieveLock(id uint32) (Locker, error) { + lock := new(FileLock) + lock.lockID = id + lock.manager = m + + return lock, nil +} + +// FreeAllLocks frees all locks in the manager. +// This function is DANGEROUS. Please read the full comment in locks.go before +// trying to use it. +func (m *FileLockManager) FreeAllLocks() error { + return m.locks.DeallocateAllLocks() +} + +// FileLock is an individual shared memory lock. +type FileLock struct { + lockID uint32 + manager *FileLockManager +} + +// ID returns the ID of the lock. +func (l *FileLock) ID() uint32 { + return l.lockID +} + +// Lock acquires the lock. +func (l *FileLock) Lock() { + if err := l.manager.locks.LockFileLock(l.lockID); err != nil { + panic(err.Error()) + } +} + +// Unlock releases the lock. +func (l *FileLock) Unlock() { + if err := l.manager.locks.UnlockFileLock(l.lockID); err != nil { + panic(err.Error()) + } +} + +// Free releases the lock, allowing it to be reused. +func (l *FileLock) Free() error { + return l.manager.locks.DeallocateLock(l.lockID) +} diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go index 76dd5729e..37c8dea7d 100644 --- a/libpod/lock/shm/shm_lock.go +++ b/libpod/lock/shm/shm_lock.go @@ -1,3 +1,5 @@ +// +build linux,cgo + package shm // #cgo LDFLAGS: -lrt -lpthread diff --git a/libpod/lock/shm/shm_lock_nocgo.go b/libpod/lock/shm/shm_lock_nocgo.go new file mode 100644 index 000000000..ea1488c90 --- /dev/null +++ b/libpod/lock/shm/shm_lock_nocgo.go @@ -0,0 +1,102 @@ +// +build linux,!cgo + +package shm + +import ( + "github.com/sirupsen/logrus" +) + +// SHMLocks is a struct enabling POSIX semaphore locking in a shared memory +// segment. +type SHMLocks struct { +} + +// CreateSHMLock sets up a shared-memory segment holding a given number of POSIX +// semaphores, and returns a struct that can be used to operate on those locks. +// numLocks must not be 0, and may be rounded up to a multiple of the bitmap +// size used by the underlying implementation. +func CreateSHMLock(path string, numLocks uint32) (*SHMLocks, error) { + logrus.Error("locks are not supported without cgo") + return &SHMLocks{}, nil +} + +// OpenSHMLock opens an existing shared-memory segment holding a given number of +// POSIX semaphores. numLocks must match the number of locks the shared memory +// segment was created with. +func OpenSHMLock(path string, numLocks uint32) (*SHMLocks, error) { + logrus.Error("locks are not supported without cgo") + return &SHMLocks{}, nil +} + +// GetMaxLocks returns the maximum number of locks in the SHM +func (locks *SHMLocks) GetMaxLocks() uint32 { + logrus.Error("locks are not supported without cgo") + return 0 +} + +// Close closes an existing shared-memory segment. +// The segment will be rendered unusable after closing. +// WARNING: If you Close() while there are still locks locked, these locks may +// fail to release, causing a program freeze. +// Close() is only intended to be used while testing the locks. +func (locks *SHMLocks) Close() error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// AllocateSemaphore allocates a semaphore from a shared-memory segment for use +// by a container or pod. +// Returns the index of the semaphore that was allocated. +// Allocations past the maximum number of locks given when the SHM segment was +// created will result in an error, and no semaphore will be allocated. +func (locks *SHMLocks) AllocateSemaphore() (uint32, error) { + logrus.Error("locks are not supported without cgo") + return 0, nil +} + +// AllocateGivenSemaphore allocates the given semaphore from the shared-memory +// segment for use by a container or pod. +// If the semaphore is already in use or the index is invalid an error will be +// returned. +func (locks *SHMLocks) AllocateGivenSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// DeallocateSemaphore frees a semaphore in a shared-memory segment so it can be +// reallocated to another container or pod. +// The given semaphore must be already allocated, or an error will be returned. +func (locks *SHMLocks) DeallocateSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// DeallocateAllSemaphores frees all semaphores so they can be reallocated to +// other containers and pods. +func (locks *SHMLocks) DeallocateAllSemaphores() error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// LockSemaphore locks the given semaphore. +// If the semaphore is already locked, LockSemaphore will block until the lock +// can be acquired. +// There is no requirement that the given semaphore be allocated. +// This ensures that attempts to lock a container after it has been deleted, +// but before the caller has queried the database to determine this, will +// succeed. +func (locks *SHMLocks) LockSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// UnlockSemaphore unlocks the given semaphore. +// Unlocking a semaphore that is already unlocked with return EBUSY. +// There is no requirement that the given semaphore be allocated. +// This ensures that attempts to lock a container after it has been deleted, +// but before the caller has queried the database to determine this, will +// succeed. +func (locks *SHMLocks) UnlockSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} diff --git a/libpod/runtime.go b/libpod/runtime.go index e358fe4c4..6c61e15d3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -239,6 +239,9 @@ type RuntimeConfig struct { // pods. NumLocks uint32 `toml:"num_locks,omitempty"` + // LockType is the type of locking to use. + LockType string `toml:"lock_type,omitempty"` + // EventsLogger determines where events should be logged EventsLogger string `toml:"events_logger"` // EventsLogFilePath is where the events log is stored. @@ -318,6 +321,7 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { NumLocks: 2048, EventsLogger: events.DefaultEventerType.String(), DetachKeys: DefaultDetachKeys, + LockType: "shm", }, nil } @@ -659,6 +663,62 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return runtime, nil } +func getLockManager(runtime *Runtime) (lock.Manager, error) { + var err error + var manager lock.Manager + + switch runtime.config.LockType { + case "file": + lockPath := filepath.Join(runtime.config.TmpDir, "locks") + manager, err = lock.OpenFileLockManager(lockPath) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewFileLockManager(lockPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to get new file lock manager") + } + } else { + return nil, err + } + } + + case "", "shm": + lockPath := DefaultSHMLockPath + if rootless.IsRootless() { + lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) + } + // Set up the lock manager + manager, err = lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return nil, errors.Wrapf(err, "failed to get new shm lock manager") + } + } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { + logrus.Debugf("Number of locks does not match - removing old locks") + + // ERANGE indicates a lock numbering mismatch. + // Since we're renumbering, this is not fatal. + // Remove the earlier set of locks and recreate. + if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { + return nil, errors.Wrapf(err, "error removing libpod locks file %s", lockPath) + } + + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return nil, err + } + } else { + return nil, err + } + } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unknown lock type %s", runtime.config.LockType) + } + return manager, nil +} + // Make a new runtime based on the given configuration // Sets up containers/storage, state store, OCI runtime func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { @@ -1044,37 +1104,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } } - lockPath := DefaultSHMLockPath - if rootless.IsRootless() { - lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) - } - // Set up the lock manager - manager, err := lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + runtime.lockManager, err = getLockManager(runtime) if err != nil { - if os.IsNotExist(errors.Cause(err)) { - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return errors.Wrapf(err, "failed to get new shm lock manager") - } - } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { - logrus.Debugf("Number of locks does not match - removing old locks") - - // ERANGE indicates a lock numbering mismatch. - // Since we're renumbering, this is not fatal. - // Remove the earlier set of locks and recreate. - if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { - return errors.Wrapf(err, "error removing libpod locks file %s", lockPath) - } - - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return err - } - } else { - return err - } + return err } - runtime.lockManager = manager // If we're renumbering locks, do it now. // It breaks out of normal runtime init, and will not return a valid |