Files
podman/pkg/machine/shim/networking_unix.go
Jason T. Greene e58cb97de1 Change API socket to be machine name isolated
- Fixes conflicts such as removal of second machine deleting a socket of a
  the first machine while it's running
- Move API socket into runtime directory for consistency
- Add API and gvproxy sockets to removal list
- Cleanup related logic

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
2024-03-13 13:28:14 -05:00

114 lines
4.0 KiB
Go

//go:build dragonfly || freebsd || linux || netbsd || openbsd || darwin
package shim
import (
"io/fs"
"net"
"os"
"path/filepath"
"github.com/containers/podman/v5/pkg/machine"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/sirupsen/logrus"
)
func setupMachineSockets(mc *vmconfigs.MachineConfig, dirs *define.MachineDirs) ([]string, string, machine.APIForwardingState, error) {
hostSocket, err := mc.APISocket()
if err != nil {
return nil, "", 0, err
}
forwardSock, state, err := setupForwardingLinks(hostSocket, dirs.DataDir)
if err != nil {
return nil, "", 0, err
}
return []string{hostSocket.GetPath()}, forwardSock, state, nil
}
func setupForwardingLinks(hostSocket, dataDir *define.VMFile) (string, machine.APIForwardingState, error) {
// Sets up a cooperative link structure to help a separate privileged
// service manage /var/run/docker.sock (currently only on MacOS via
// podman-mac-helper, but potentially other OSs in the future).
//
// The linking pattern is:
//
// /var/run/docker.sock (link) -> user global sock (link) -> machine sock
//
// This allows the helper to only have to maintain one constant target to
// the user, which can be repositioned without updating docker.sock.
//
// Since these link locations are global/shared across multiple machine
// instances, they must coordinate on the winner. The scheme is first come
// first serve, whoever is actively answering on the socket first wins. All
// other machine instances backs off. As soon as the winner is no longer
// active another machine instance start will become the new active winner.
// The same applies to a competing container runtime trying to use
// /var/run/docker.sock, if the socket is in use by another runtime, podman
// machine will back off. In the start message "Losing" machine instances
// will instead advertise the direct machine socket, while "winning"
// instances will simply note they listen on the standard
// /var/run/docker.sock address. The APIForwardingState return value is
// returned by this function to indicate how the start message should behave
// Skip any OS not supported for helper usage
if !dockerClaimSupported() {
return hostSocket.GetPath(), machine.ClaimUnsupported, nil
}
// Verify the helper system service was installed and report back if not
if !dockerClaimHelperInstalled() {
return hostSocket.GetPath(), machine.NotInstalled, nil
}
dataPath := filepath.Dir(dataDir.GetPath())
userGlobalSocket, err := define.NewMachineFile(filepath.Join(dataPath, "podman.sock"), nil)
if err != nil {
return "", 0, err
}
// Setup the user global socket if not in use
// (e.g ~/.local/share/containers/podman/machine/podman.sock)
if !alreadyLinked(hostSocket.GetPath(), userGlobalSocket.GetPath()) {
if checkSockInUse(userGlobalSocket.GetPath()) {
return hostSocket.GetPath(), machine.MachineLocal, nil
}
_ = userGlobalSocket.Delete()
if err := os.Symlink(hostSocket.GetPath(), userGlobalSocket.GetPath()); err != nil {
logrus.Warnf("could not create user global API forwarding link: %s", err.Error())
return hostSocket.GetPath(), machine.MachineLocal, nil
}
}
// Setup /var/run/docker.sock if not in use
if !alreadyLinked(userGlobalSocket.GetPath(), dockerSock) {
if checkSockInUse(dockerSock) {
return hostSocket.GetPath(), machine.MachineLocal, nil
}
if !claimDockerSock() {
logrus.Warn("podman helper is installed, but was not able to claim the global docker sock")
return hostSocket.GetPath(), machine.MachineLocal, nil
}
}
return dockerSock, machine.DockerGlobal, nil
}
func alreadyLinked(target string, link string) bool {
read, err := os.Readlink(link)
return err == nil && read == target
}
func checkSockInUse(sock string) bool {
if info, err := os.Stat(sock); err == nil && info.Mode()&fs.ModeSocket == fs.ModeSocket {
_, err = net.DialTimeout("unix", dockerSock, dockerConnectTimeout)
return err == nil
}
return false
}