//go:build unix package hostexec import ( "errors" "golang.org/x/sys/unix" ) // Observers, including root and adopt, never create or repair an agent's // lifetime locks. Missing locks deny admission without changing the namespace. func probeLifetime(dir, host string) (func(), error) { return probeExisting(dir, host, ".lock") } func probeLive(dir, host string) (func(), error) { return probeExisting(dir, host, ".live") } func probeExisting(dir, host, suffix string) (func(), error) { if _, err := Key(host, "check"); err != nil { return nil, err } fd, err := openDir(dir) if err != nil { return nil, err } defer unix.Close(fd) lock, err := unix.Openat(fd, host+suffix, unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_CLOEXEC|unix.O_NONBLOCK, 0) if err != nil { return nil, err } fail := func(err error) (func(), error) { _ = unix.Close(lock); return nil, err } var st unix.Stat_t if err = unix.Fstat(lock, &st); err != nil { return fail(err) } if st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 { return fail(ErrState) } if err = unix.Flock(lock, unix.LOCK_EX|unix.LOCK_NB); err == nil { return func() { _ = unix.Flock(lock, unix.LOCK_UN); _ = unix.Close(lock) }, nil } if errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, unix.EAGAIN) { return fail(ErrControllerBusy) } return fail(err) }