mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
Merge pull request #21597 from n1hility/wsl-refactor
Complete WSL implementation in Podman 5
This commit is contained in:
@ -128,7 +128,6 @@ func setMachine(cmd *cobra.Command, args []string) error {
|
|||||||
setOpts.DiskSize = &newDiskSizeGB
|
setOpts.DiskSize = &newDiskSizeGB
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("user-mode-networking") {
|
if cmd.Flags().Changed("user-mode-networking") {
|
||||||
// TODO This needs help
|
|
||||||
setOpts.UserModeNetworking = &setFlags.UserModeNetworking
|
setOpts.UserModeNetworking = &setFlags.UserModeNetworking
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("usb") {
|
if cmd.Flags().Changed("usb") {
|
||||||
|
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
|||||||
github.com/containers/ocicrypt v1.1.9
|
github.com/containers/ocicrypt v1.1.9
|
||||||
github.com/containers/psgo v1.9.0
|
github.com/containers/psgo v1.9.0
|
||||||
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565
|
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565
|
||||||
|
github.com/containers/winquit v1.1.0
|
||||||
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09
|
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09
|
||||||
github.com/coreos/stream-metadata-go v0.4.4
|
github.com/coreos/stream-metadata-go v0.4.4
|
||||||
github.com/crc-org/crc/v2 v2.32.0
|
github.com/crc-org/crc/v2 v2.32.0
|
||||||
|
2
go.sum
2
go.sum
@ -93,6 +93,8 @@ github.com/containers/psgo v1.9.0 h1:eJ74jzSaCHnWt26OlKZROSyUyRcGDf+gYBdXnxrMW4g
|
|||||||
github.com/containers/psgo v1.9.0/go.mod h1:0YoluUm43Mz2UnBIh1P+6V6NWcbpTL5uRtXyOcH0B5A=
|
github.com/containers/psgo v1.9.0/go.mod h1:0YoluUm43Mz2UnBIh1P+6V6NWcbpTL5uRtXyOcH0B5A=
|
||||||
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565 h1:Gcirfx2DNoayB/+ypLgl5+ABzIPPDAoncs1qgZHHQHE=
|
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565 h1:Gcirfx2DNoayB/+ypLgl5+ABzIPPDAoncs1qgZHHQHE=
|
||||||
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565/go.mod h1:2E/QBqWVcJXwumP7nVUrampwRNL4XKjHL/aQya7ZdhI=
|
github.com/containers/storage v1.52.1-0.20240202181245-1419a5980565/go.mod h1:2E/QBqWVcJXwumP7nVUrampwRNL4XKjHL/aQya7ZdhI=
|
||||||
|
github.com/containers/winquit v1.1.0 h1:jArun04BNDQvt2W0Y78kh9TazN2EIEMG5Im6/JY7+pE=
|
||||||
|
github.com/containers/winquit v1.1.0/go.mod h1:PsPeZlnbkmGGIToMPHF1zhWjBUkd8aHjMOr/vFcPxw8=
|
||||||
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
|
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
|
||||||
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
|
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/containers/podman/v5/pkg/machine/applehv/vfkit"
|
"github.com/containers/podman/v5/pkg/machine/applehv/vfkit"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/containers/podman/v5/pkg/machine/ignition"
|
"github.com/containers/podman/v5/pkg/machine/ignition"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
|
||||||
"github.com/containers/podman/v5/pkg/machine/sockets"
|
"github.com/containers/podman/v5/pkg/machine/sockets"
|
||||||
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
"github.com/containers/podman/v5/utils"
|
"github.com/containers/podman/v5/utils"
|
||||||
@ -38,6 +39,18 @@ type AppleHVStubber struct {
|
|||||||
vmconfigs.AppleHVConfig
|
vmconfigs.AppleHVConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AppleHVStubber) UserModeNetworkEnabled(_ *vmconfigs.MachineConfig) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AppleHVStubber) UseProviderNetworkSetup() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AppleHVStubber) RequireExclusiveActive() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (a AppleHVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, ignBuilder *ignition.IgnitionBuilder) error {
|
func (a AppleHVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, ignBuilder *ignition.IgnitionBuilder) error {
|
||||||
mc.AppleHypervisor = new(vmconfigs.AppleHVConfig)
|
mc.AppleHypervisor = new(vmconfigs.AppleHVConfig)
|
||||||
mc.AppleHypervisor.Vfkit = vfkit.VfkitHelper{}
|
mc.AppleHypervisor.Vfkit = vfkit.VfkitHelper{}
|
||||||
@ -314,6 +327,10 @@ func (a AppleHVStubber) PrepareIgnition(_ *vmconfigs.MachineConfig, _ *ignition.
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AppleHVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig) error {
|
func (a AppleHVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AppleHVStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||||
|
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, a.VMType(), mc.Name)
|
||||||
|
}
|
||||||
|
@ -32,8 +32,8 @@ func Decompress(localPath *define.VMFile, uncompressedPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := uncompressedFileWriter.Close(); err != nil {
|
if err := uncompressedFileWriter.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
||||||
logrus.Errorf("unable to to close decompressed file %s: %q", uncompressedPath, err)
|
logrus.Warnf("unable to close decompressed file %s: %q", uncompressedPath, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
sourceFile, err := localPath.Read()
|
sourceFile, err := localPath.Read()
|
||||||
|
@ -331,11 +331,55 @@ func NewVirtualization(artifact define.Artifact, compression compression.ImageCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dialSocket(socket string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
scheme := "unix"
|
||||||
|
if strings.Contains(socket, "://") {
|
||||||
|
url, err := url.Parse(socket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
scheme = url.Scheme
|
||||||
|
socket = url.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
var dial func() (net.Conn, error)
|
||||||
|
switch scheme {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case "unix":
|
||||||
|
dial = func() (net.Conn, error) {
|
||||||
|
var dialer net.Dialer
|
||||||
|
return dialer.DialContext(ctx, "unix", socket)
|
||||||
|
}
|
||||||
|
case "npipe":
|
||||||
|
dial = func() (net.Conn, error) {
|
||||||
|
return DialNamedPipe(ctx, socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backoff := 500 * time.Millisecond
|
||||||
|
for {
|
||||||
|
conn, err := dial()
|
||||||
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(backoff):
|
||||||
|
backoff *= 2
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WaitAndPingAPI(sock string) {
|
func WaitAndPingAPI(sock string) {
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
||||||
con, err := net.DialTimeout("unix", sock, apiUpTimeout)
|
con, err := dialSocket(sock, apiUpTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CreateVMOpts struct {
|
type CreateVMOpts struct {
|
||||||
Name string
|
Name string
|
||||||
Dirs *MachineDirs
|
Dirs *MachineDirs
|
||||||
|
ReExec bool
|
||||||
|
UserModeNetworking bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineDirs struct {
|
type MachineDirs struct {
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/wsl"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/pkg/machine"
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
"github.com/containers/podman/v5/pkg/machine/compression"
|
"github.com/containers/podman/v5/pkg/machine/compression"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
@ -61,9 +63,20 @@ var _ = BeforeSuite(func() {
|
|||||||
|
|
||||||
downloadLocation := os.Getenv("MACHINE_IMAGE")
|
downloadLocation := os.Getenv("MACHINE_IMAGE")
|
||||||
if downloadLocation == "" {
|
if downloadLocation == "" {
|
||||||
downloadLocation, err = GetDownload(testProvider.VMType())
|
// TODO so beautifully gross ... ideally we can spend some time
|
||||||
if err != nil {
|
// here making life easier on the next person
|
||||||
Fail("unable to derive download disk from fedora coreos")
|
switch testProvider.VMType() {
|
||||||
|
case define.WSLVirt:
|
||||||
|
dl, _, _, _, err := wsl.GetFedoraDownloadForWSL()
|
||||||
|
if err != nil {
|
||||||
|
Fail("unable to determine WSL download")
|
||||||
|
}
|
||||||
|
downloadLocation = dl.String()
|
||||||
|
default:
|
||||||
|
downloadLocation, err = GetDownload(testProvider.VMType())
|
||||||
|
if err != nil {
|
||||||
|
Fail("unable to derive download disk from fedora coreos")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +171,9 @@ var _ = Describe("podman machine set", func() {
|
|||||||
if testProvider.VMType() != define.WSLVirt {
|
if testProvider.VMType() != define.WSLVirt {
|
||||||
Skip("Test is only for WSL")
|
Skip("Test is only for WSL")
|
||||||
}
|
}
|
||||||
|
// TODO - this currently fails
|
||||||
|
Skip("test fails bc usermode network needs plumbing for WSL")
|
||||||
|
|
||||||
name := randomString()
|
name := randomString()
|
||||||
i := new(initMachine)
|
i := new(initMachine)
|
||||||
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run()
|
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
psutil "github.com/shirou/gopsutil/v3/process"
|
psutil "github.com/shirou/gopsutil/v3/process"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,49 +35,6 @@ func backoffForProcess(p *psutil.Process) error {
|
|||||||
return fmt.Errorf("process %d has not ended", p.Pid)
|
return fmt.Errorf("process %d has not ended", p.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitOnProcess takes a pid and sends a sigterm to it. it then waits for the
|
|
||||||
// process to not exist. if the sigterm does not end the process after an interval,
|
|
||||||
// then sigkill is sent. it also waits for the process to exit after the sigkill too.
|
|
||||||
func waitOnProcess(processID int) error {
|
|
||||||
logrus.Infof("Going to stop gvproxy (PID %d)", processID)
|
|
||||||
|
|
||||||
p, err := psutil.NewProcess(int32(processID))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("looking up PID %d: %w", processID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to kill the pid with sigterm
|
|
||||||
if runtime.GOOS != "windows" { // FIXME: temporary work around because signals are lame in windows
|
|
||||||
if err := p.SendSignal(syscall.SIGTERM); err != nil {
|
|
||||||
if errors.Is(err, syscall.ESRCH) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("sending SIGTERM to grproxy: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := backoffForProcess(p); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
running, err := p.IsRunning()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("checking if gvproxy is running: %w", err)
|
|
||||||
}
|
|
||||||
if !running {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.Kill(); err != nil {
|
|
||||||
if errors.Is(err, syscall.ESRCH) {
|
|
||||||
logrus.Debugf("Gvproxy already dead, exiting cleanly")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return backoffForProcess(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it
|
// CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it
|
||||||
func CleanupGVProxy(f define.VMFile) error {
|
func CleanupGVProxy(f define.VMFile) error {
|
||||||
gvPid, err := f.Read()
|
gvPid, err := f.Read()
|
||||||
|
41
pkg/machine/gvproxy_unix.go
Normal file
41
pkg/machine/gvproxy_unix.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
psutil "github.com/shirou/gopsutil/v3/process"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// / waitOnProcess takes a pid and sends a sigterm to it. it then waits for the
|
||||||
|
// process to not exist. if the sigterm does not end the process after an interval,
|
||||||
|
// then sigkill is sent. it also waits for the process to exit after the sigkill too.
|
||||||
|
func waitOnProcess(processID int) error {
|
||||||
|
logrus.Infof("Going to stop gvproxy (PID %d)", processID)
|
||||||
|
|
||||||
|
p, err := psutil.NewProcess(int32(processID))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("looking up PID %d: %w", processID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
running, err := p.IsRunning()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("checking if gvproxy is running: %w", err)
|
||||||
|
}
|
||||||
|
if !running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Kill(); err != nil {
|
||||||
|
if errors.Is(err, syscall.ESRCH) {
|
||||||
|
logrus.Debugf("Gvproxy already dead, exiting cleanly")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return backoffForProcess(p)
|
||||||
|
}
|
45
pkg/machine/gvproxy_windows.go
Normal file
45
pkg/machine/gvproxy_windows.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/winquit/pkg/winquit"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func waitOnProcess(processID int) error {
|
||||||
|
logrus.Infof("Going to stop gvproxy (PID %d)", processID)
|
||||||
|
|
||||||
|
p, err := os.FindProcess(processID)
|
||||||
|
if err != nil {
|
||||||
|
// FindProcess on Windows will return an error when the process is not found
|
||||||
|
// if a process can not be found then it has already exited and there is
|
||||||
|
// nothing left to do, so return without error
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gracefully quit and force kill after 30 seconds
|
||||||
|
if err := winquit.QuitProcess(processID, 30*time.Second); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("completed grace quit || kill of gvproxy (PID %d)", processID)
|
||||||
|
|
||||||
|
// Make sure the process is gone (Hard kills are async)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
_, _ = p.Wait()
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
logrus.Debugf("verified gvproxy termination (PID %d)", processID)
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
// Very unlikely but track just in case
|
||||||
|
logrus.Errorf("was not able to kill gvproxy (PID %d)", processID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -10,6 +10,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
|
||||||
|
|
||||||
"github.com/Microsoft/go-winio"
|
"github.com/Microsoft/go-winio"
|
||||||
"github.com/containers/common/pkg/strongunits"
|
"github.com/containers/common/pkg/strongunits"
|
||||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||||
@ -27,6 +29,18 @@ type HyperVStubber struct {
|
|||||||
vmconfigs.HyperVConfig
|
vmconfigs.HyperVConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h HyperVStubber) UserModeNetworkEnabled(mc *vmconfigs.MachineConfig) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HyperVStubber) UseProviderNetworkSetup() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HyperVStubber) RequireExclusiveActive() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (h HyperVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, builder *ignition.IgnitionBuilder) error {
|
func (h HyperVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, builder *ignition.IgnitionBuilder) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -368,7 +382,7 @@ func (h HyperVStubber) PrepareIgnition(mc *vmconfigs.MachineConfig, ignBuilder *
|
|||||||
return &ignOpts, nil
|
return &ignOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HyperVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig) error {
|
func (h HyperVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
executable string
|
executable string
|
||||||
@ -377,25 +391,6 @@ func (h HyperVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig) error {
|
|||||||
defer callbackFuncs.CleanIfErr(&err)
|
defer callbackFuncs.CleanIfErr(&err)
|
||||||
go callbackFuncs.CleanOnSignal()
|
go callbackFuncs.CleanOnSignal()
|
||||||
|
|
||||||
winProxyOpts := machine.WinProxyOpts{
|
|
||||||
Name: mc.Name,
|
|
||||||
IdentityPath: mc.SSH.IdentityPath,
|
|
||||||
Port: mc.SSH.Port,
|
|
||||||
RemoteUsername: mc.SSH.RemoteUsername,
|
|
||||||
Rootful: mc.HostUser.Rootful,
|
|
||||||
VMType: h.VMType(),
|
|
||||||
}
|
|
||||||
// TODO Should this process be fatal on error; currenty, no error is
|
|
||||||
// returned but an error can occur in the func itself
|
|
||||||
// TODO we do not currently pass "noinfo" (quiet) into the StartVM
|
|
||||||
// func so this is hard set to false
|
|
||||||
machine.LaunchWinProxy(winProxyOpts, false)
|
|
||||||
|
|
||||||
winProxyCallbackFunc := func() error {
|
|
||||||
return machine.StopWinProxy(mc.Name, h.VMType())
|
|
||||||
}
|
|
||||||
callbackFuncs.Add(winProxyCallbackFunc)
|
|
||||||
|
|
||||||
if len(mc.Mounts) != 0 {
|
if len(mc.Mounts) != 0 {
|
||||||
var (
|
var (
|
||||||
dirs *define.MachineDirs
|
dirs *define.MachineDirs
|
||||||
@ -459,6 +454,10 @@ func (h HyperVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h HyperVStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||||
|
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, h.VMType(), mc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
func resizeDisk(newSize strongunits.GiB, imagePath *define.VMFile) error {
|
func resizeDisk(newSize strongunits.GiB, imagePath *define.VMFile) error {
|
||||||
resize := exec.Command("powershell", []string{"-command", fmt.Sprintf("Resize-VHD %s %d", imagePath.GetPath(), newSize.ToBytes())}...)
|
resize := exec.Command("powershell", []string{"-command", fmt.Sprintf("Resize-VHD %s %d", imagePath.GetPath(), newSize.ToBytes())}...)
|
||||||
logrus.Debug(resize.Args)
|
logrus.Debug(resize.Args)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/pkg/machine/connection"
|
"github.com/containers/podman/v5/pkg/machine/connection"
|
||||||
"github.com/containers/storage/pkg/ioutils"
|
"github.com/containers/storage/pkg/ioutils"
|
||||||
@ -46,19 +47,6 @@ func WaitAPIAndPrintInfo(forwardState APIForwardingState, name, helper, forwardS
|
|||||||
WaitAndPingAPI(forwardSock)
|
WaitAndPingAPI(forwardSock)
|
||||||
|
|
||||||
if !noInfo {
|
if !noInfo {
|
||||||
if !rootful {
|
|
||||||
fmtString = `
|
|
||||||
This machine is currently configured in rootless mode. If your containers
|
|
||||||
require root permissions (e.g. ports < 1024), or if you run into compatibility
|
|
||||||
issues with non-podman clients, you can switch using the following command:
|
|
||||||
|
|
||||||
podman machine set --rootful%s
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
fmt.Printf(fmtString, suffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("API forwarding listening on: %s\n", forwardSock)
|
fmt.Printf("API forwarding listening on: %s\n", forwardSock)
|
||||||
if forwardState == DockerGlobal {
|
if forwardState == DockerGlobal {
|
||||||
fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n")
|
fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n")
|
||||||
@ -79,7 +67,7 @@ address can't be used by podman. `
|
|||||||
sudo %s install
|
sudo %s install
|
||||||
podman machine stop%[2]s; podman machine start%[2]s
|
podman machine stop%[2]s; podman machine start%[2]s
|
||||||
|
|
||||||
`
|
`
|
||||||
fmt.Printf(fmtString, helper, suffix)
|
fmt.Printf(fmtString, helper, suffix)
|
||||||
}
|
}
|
||||||
case MachineLocal:
|
case MachineLocal:
|
||||||
@ -93,15 +81,35 @@ address can't be used by podman. `
|
|||||||
fmtString = `You can %sconnect Docker API clients by setting DOCKER_HOST using the
|
fmtString = `You can %sconnect Docker API clients by setting DOCKER_HOST using the
|
||||||
following command in your terminal session:
|
following command in your terminal session:
|
||||||
|
|
||||||
export DOCKER_HOST='unix://%s'
|
%s'
|
||||||
|
|
||||||
`
|
`
|
||||||
|
prefix := ""
|
||||||
fmt.Printf(fmtString, stillString, forwardSock)
|
if !strings.Contains(forwardSock, "://") {
|
||||||
|
prefix = "unix://"
|
||||||
|
}
|
||||||
|
fmt.Printf(fmtString, stillString, GetEnvSetString("DOCKER_HOST", prefix+forwardSock))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PrintRootlessWarning(name string) {
|
||||||
|
suffix := ""
|
||||||
|
if name != DefaultMachineName {
|
||||||
|
suffix = " " + name
|
||||||
|
}
|
||||||
|
|
||||||
|
fmtString := `
|
||||||
|
This machine is currently configured in rootless mode. If your containers
|
||||||
|
require root permissions (e.g. ports < 1024), or if you run into compatibility
|
||||||
|
issues with non-podman clients, you can switch using the following command:
|
||||||
|
|
||||||
|
podman machine set --rootful%s
|
||||||
|
|
||||||
|
`
|
||||||
|
fmt.Printf(fmtString, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
// SetRootful modifies the machine's default connection to be either rootful or
|
// SetRootful modifies the machine's default connection to be either rootful or
|
||||||
// rootless
|
// rootless
|
||||||
func SetRootful(rootful bool, name, rootfulName string) error {
|
func SetRootful(rootful bool, name, rootfulName string) error {
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,3 +35,11 @@ func ParseVolumeFromPath(v string) (source, target, options string, readonly boo
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DialNamedPipe(ctx context.Context, path string) (net.Conn, error) {
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnvSetString(env string, val string) string {
|
||||||
|
return fmt.Sprintf("export %s='%s'", env, val)
|
||||||
|
}
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -11,17 +15,22 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pipePrefix = "npipe:////./pipe/"
|
NamedPipePrefix = "npipe:////./pipe/"
|
||||||
globalPipe = "docker_engine"
|
GlobalNamedPipe = "docker_engine"
|
||||||
winSShProxy = "win-sshproxy.exe"
|
winSShProxy = "win-sshproxy.exe"
|
||||||
winSshProxyTid = "win-sshproxy.tid"
|
winSshProxyTid = "win-sshproxy.tid"
|
||||||
rootfulSock = "/run/podman/podman.sock"
|
rootfulSock = "/run/podman/podman.sock"
|
||||||
rootlessSock = "/run/user/1000/podman/podman.sock"
|
rootlessSock = "/run/user/1000/podman/podman.sock"
|
||||||
|
|
||||||
|
// machine wait is longer since we must hard fail
|
||||||
|
MachineNameWait = 5 * time.Second
|
||||||
|
GlobalNameWait = 250 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
const WM_QUIT = 0x12 //nolint
|
const WM_QUIT = 0x12 //nolint
|
||||||
@ -50,9 +59,20 @@ func GetProcessState(pid int) (active bool, exitCode int) {
|
|||||||
return code == 259, int(code)
|
return code == 259, int(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PipeNameAvailable(pipeName string) bool {
|
func PipeNameAvailable(pipeName string, maxWait time.Duration) bool {
|
||||||
_, err := os.Stat(`\\.\pipe\` + pipeName)
|
const interval = 250 * time.Millisecond
|
||||||
return os.IsNotExist(err)
|
var wait time.Duration
|
||||||
|
for {
|
||||||
|
_, err := os.Stat(`\\.\pipe\` + pipeName)
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wait >= maxWait {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
time.Sleep(interval)
|
||||||
|
wait += interval
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WaitPipeExists(pipeName string, retries int, checkFailure func() error) error {
|
func WaitPipeExists(pipeName string, retries int, checkFailure func() error) error {
|
||||||
@ -71,6 +91,11 @@ func WaitPipeExists(pipeName string, retries int, checkFailure func() error) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DialNamedPipe(ctx context.Context, path string) (net.Conn, error) {
|
||||||
|
path = strings.Replace(path, "/", "\\", -1)
|
||||||
|
return winio.DialPipeContext(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
func LaunchWinProxy(opts WinProxyOpts, noInfo bool) {
|
func LaunchWinProxy(opts WinProxyOpts, noInfo bool) {
|
||||||
globalName, pipeName, err := launchWinProxy(opts)
|
globalName, pipeName, err := launchWinProxy(opts)
|
||||||
if !noInfo {
|
if !noInfo {
|
||||||
@ -97,12 +122,12 @@ func LaunchWinProxy(opts WinProxyOpts, noInfo bool) {
|
|||||||
|
|
||||||
func launchWinProxy(opts WinProxyOpts) (bool, string, error) {
|
func launchWinProxy(opts WinProxyOpts) (bool, string, error) {
|
||||||
machinePipe := ToDist(opts.Name)
|
machinePipe := ToDist(opts.Name)
|
||||||
if !PipeNameAvailable(machinePipe) {
|
if !PipeNameAvailable(machinePipe, MachineNameWait) {
|
||||||
return false, "", fmt.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
|
return false, "", fmt.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
|
||||||
}
|
}
|
||||||
|
|
||||||
globalName := false
|
globalName := false
|
||||||
if PipeNameAvailable(globalPipe) {
|
if PipeNameAvailable(GlobalNamedPipe, GlobalNameWait) {
|
||||||
globalName = true
|
globalName = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,11 +150,11 @@ func launchWinProxy(opts WinProxyOpts) (bool, string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dest := fmt.Sprintf("ssh://%s@localhost:%d%s", forwardUser, opts.Port, destSock)
|
dest := fmt.Sprintf("ssh://%s@localhost:%d%s", forwardUser, opts.Port, destSock)
|
||||||
args := []string{opts.Name, stateDir, pipePrefix + machinePipe, dest, opts.IdentityPath}
|
args := []string{opts.Name, stateDir, NamedPipePrefix + machinePipe, dest, opts.IdentityPath}
|
||||||
waitPipe := machinePipe
|
waitPipe := machinePipe
|
||||||
if globalName {
|
if globalName {
|
||||||
args = append(args, pipePrefix+globalPipe, dest, opts.IdentityPath)
|
args = append(args, NamedPipePrefix+GlobalNamedPipe, dest, opts.IdentityPath)
|
||||||
waitPipe = globalPipe
|
waitPipe = GlobalNamedPipe
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(command, args...)
|
cmd := exec.Command(command, args...)
|
||||||
@ -138,7 +163,7 @@ func launchWinProxy(opts WinProxyOpts) (bool, string, error) {
|
|||||||
return globalName, "", err
|
return globalName, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return globalName, pipePrefix + waitPipe, WaitPipeExists(waitPipe, 80, func() error {
|
return globalName, NamedPipePrefix + waitPipe, WaitPipeExists(waitPipe, 80, func() error {
|
||||||
active, exitCode := GetProcessState(cmd.Process.Pid)
|
active, exitCode := GetProcessState(cmd.Process.Pid)
|
||||||
if !active {
|
if !active {
|
||||||
return fmt.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
|
return fmt.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
|
||||||
@ -240,3 +265,7 @@ func ToDist(name string) string {
|
|||||||
}
|
}
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetEnvSetString(env string, val string) string {
|
||||||
|
return fmt.Sprintf("$Env:%s=\"%s\"", env, val)
|
||||||
|
}
|
||||||
|
@ -2,9 +2,11 @@ package provider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/wsl"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/containers/podman/v5/pkg/machine/hyperv"
|
"github.com/containers/podman/v5/pkg/machine/hyperv"
|
||||||
@ -27,9 +29,8 @@ func Get() (vmconfigs.VMProvider, error) {
|
|||||||
|
|
||||||
logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String())
|
logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String())
|
||||||
switch resolvedVMType {
|
switch resolvedVMType {
|
||||||
// TODO re-enable this with WSL
|
case define.WSLVirt:
|
||||||
//case define.WSLVirt:
|
return new(wsl.WSLStubber), nil
|
||||||
// return wsl.VirtualizationProvider(), nil
|
|
||||||
case define.HyperVVirt:
|
case define.HyperVVirt:
|
||||||
return new(hyperv.HyperVStubber), nil
|
return new(hyperv.HyperVStubber), nil
|
||||||
default:
|
default:
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -266,6 +267,22 @@ func (q *QEMUStubber) State(mc *vmconfigs.MachineConfig, bypass bool) (define.St
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if err := monitor.Connect(); err != nil {
|
if err := monitor.Connect(); err != nil {
|
||||||
|
// There is a case where if we stop the same vm (from running) two
|
||||||
|
// consecutive times we can get an econnreset when trying to get the
|
||||||
|
// state
|
||||||
|
if errors.Is(err, syscall.ECONNRESET) {
|
||||||
|
// try again
|
||||||
|
logrus.Debug("received ECCONNRESET from QEMU monitor; trying again")
|
||||||
|
secondTry := monitor.Connect()
|
||||||
|
if errors.Is(secondTry, io.EOF) {
|
||||||
|
return define.Stopped, nil
|
||||||
|
}
|
||||||
|
if secondTry != nil {
|
||||||
|
logrus.Debugf("second attempt to connect to QEMU monitor failed")
|
||||||
|
return "", secondTry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -11,14 +11,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/pkg/machine/ignition"
|
|
||||||
|
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/common/pkg/strongunits"
|
"github.com/containers/common/pkg/strongunits"
|
||||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||||
"github.com/containers/podman/v5/pkg/machine"
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/ignition"
|
||||||
"github.com/containers/podman/v5/pkg/machine/qemu/command"
|
"github.com/containers/podman/v5/pkg/machine/qemu/command"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
|
||||||
"github.com/containers/podman/v5/pkg/machine/sockets"
|
"github.com/containers/podman/v5/pkg/machine/sockets"
|
||||||
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -30,6 +30,18 @@ type QEMUStubber struct {
|
|||||||
Command command.QemuCmd
|
Command command.QemuCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q QEMUStubber) UserModeNetworkEnabled(*vmconfigs.MachineConfig) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QEMUStubber) UseProviderNetworkSetup() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QEMUStubber) RequireExclusiveActive() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (q *QEMUStubber) setQEMUCommandLine(mc *vmconfigs.MachineConfig) error {
|
func (q *QEMUStubber) setQEMUCommandLine(mc *vmconfigs.MachineConfig) error {
|
||||||
qemuBinary, err := findQEMUBinary()
|
qemuBinary, err := findQEMUBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -69,7 +81,7 @@ func (q *QEMUStubber) setQEMUCommandLine(mc *vmconfigs.MachineConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, _ *ignition.IgnitionBuilder) error {
|
func (q *QEMUStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, builder *ignition.IgnitionBuilder) error {
|
||||||
monitor, err := command.NewQMPMonitor(opts.Name, opts.Dirs.RuntimeDir)
|
monitor, err := command.NewQMPMonitor(opts.Name, opts.Dirs.RuntimeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -323,6 +335,10 @@ func (q *QEMUStubber) MountType() vmconfigs.VolumeMountType {
|
|||||||
return vmconfigs.NineP
|
return vmconfigs.NineP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *QEMUStubber) PostStartNetworking(mc *vmconfigs.MachineConfig) error {
|
func (q *QEMUStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *QEMUStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||||
|
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, q.VMType(), mc.Name)
|
||||||
|
}
|
||||||
|
32
pkg/machine/shim/diskpull/diskpull.go
Normal file
32
pkg/machine/shim/diskpull/diskpull.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package diskpull
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDisk(userInputPath string, dirs *define.MachineDirs, imagePath *define.VMFile, vmType define.VMType, name string) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
mydisk ocipull.Disker
|
||||||
|
)
|
||||||
|
|
||||||
|
if userInputPath == "" {
|
||||||
|
mydisk, err = ocipull.NewVersioned(context.Background(), dirs.DataDir, name, vmType.String(), imagePath)
|
||||||
|
} else {
|
||||||
|
if strings.HasPrefix(userInputPath, "http") {
|
||||||
|
// TODO probably should use tempdir instead of datadir
|
||||||
|
mydisk, err = stdpull.NewDiskFromURL(userInputPath, imagePath, dirs.DataDir, nil)
|
||||||
|
} else {
|
||||||
|
mydisk, err = stdpull.NewStdDiskPull(userInputPath, imagePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return mydisk.Get()
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package shim
|
package shim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/util"
|
"github.com/containers/common/pkg/util"
|
||||||
@ -14,30 +12,13 @@ import (
|
|||||||
"github.com/containers/podman/v5/pkg/machine/connection"
|
"github.com/containers/podman/v5/pkg/machine/connection"
|
||||||
machineDefine "github.com/containers/podman/v5/pkg/machine/define"
|
machineDefine "github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/containers/podman/v5/pkg/machine/ignition"
|
"github.com/containers/podman/v5/pkg/machine/ignition"
|
||||||
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
|
||||||
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
|
||||||
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Host
|
|
||||||
├ Info
|
|
||||||
├ OS Apply
|
|
||||||
├ SSH
|
|
||||||
├ List
|
|
||||||
├ Init
|
|
||||||
├ VMExists
|
|
||||||
├ CheckExclusiveActiveVM *HyperV/WSL need to check their hypervisors as well
|
|
||||||
*/
|
|
||||||
|
|
||||||
func Info() {}
|
|
||||||
func OSApply() {}
|
|
||||||
func SSH() {}
|
|
||||||
|
|
||||||
// List is done at the host level to allow for a *possible* future where
|
// List is done at the host level to allow for a *possible* future where
|
||||||
// more than one provider is used
|
// more than one provider is used
|
||||||
func List(vmstubbers []vmconfigs.VMProvider, opts machine.ListOptions) ([]*machine.ListResponse, error) {
|
func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.ListResponse, error) {
|
||||||
var (
|
var (
|
||||||
lrs []*machine.ListResponse
|
lrs []*machine.ListResponse
|
||||||
)
|
)
|
||||||
@ -114,6 +95,10 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
|
|||||||
Dirs: dirs,
|
Dirs: dirs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if umn := opts.UserModeNetworking; umn != nil {
|
||||||
|
createOpts.UserModeNetworking = *umn
|
||||||
|
}
|
||||||
|
|
||||||
// Get Image
|
// Get Image
|
||||||
// TODO This needs rework bigtime; my preference is most of below of not living in here.
|
// TODO This needs rework bigtime; my preference is most of below of not living in here.
|
||||||
// ideally we could get a func back that pulls the image, and only do so IF everything works because
|
// ideally we could get a func back that pulls the image, and only do so IF everything works because
|
||||||
@ -137,8 +122,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
mc.ImagePath = imagePath
|
||||||
var mydisk ocipull.Disker
|
|
||||||
|
|
||||||
// TODO The following stanzas should be re-written in a differeent place. It should have a custom
|
// TODO The following stanzas should be re-written in a differeent place. It should have a custom
|
||||||
// parser for our image pulling. It would be nice if init just got an error and mydisk back.
|
// parser for our image pulling. It would be nice if init just got an error and mydisk back.
|
||||||
@ -149,25 +133,12 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
|
|||||||
// "/path
|
// "/path
|
||||||
// "docker://quay.io/something/someManifest
|
// "docker://quay.io/something/someManifest
|
||||||
|
|
||||||
if opts.ImagePath == "" {
|
// TODO Ideally this changes into some way better ...
|
||||||
mydisk, err = ocipull.NewVersioned(context.Background(), dirs.DataDir, opts.Name, mp.VMType().String(), imagePath)
|
err = mp.GetDisk(opts.ImagePath, dirs, mc)
|
||||||
} else {
|
|
||||||
if strings.HasPrefix(opts.ImagePath, "http") {
|
|
||||||
// TODO probably should use tempdir instead of datadir
|
|
||||||
mydisk, err = stdpull.NewDiskFromURL(opts.ImagePath, imagePath, dirs.DataDir)
|
|
||||||
} else {
|
|
||||||
mydisk, err = stdpull.NewStdDiskPull(opts.ImagePath, imagePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = mydisk.Get()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mc.ImagePath = imagePath
|
|
||||||
callbackFuncs.Add(mc.ImagePath.Delete)
|
callbackFuncs.Add(mc.ImagePath.Delete)
|
||||||
|
|
||||||
logrus.Debugf("--> imagePath is %q", imagePath.GetPath())
|
logrus.Debugf("--> imagePath is %q", imagePath.GetPath())
|
||||||
@ -182,8 +153,18 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
|
|||||||
uid = 1000
|
uid = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO the definition of "user" should go into
|
||||||
|
// common for WSL
|
||||||
|
userName := opts.Username
|
||||||
|
if mp.VMType() == machineDefine.WSLVirt {
|
||||||
|
if opts.Username == "core" {
|
||||||
|
userName = "user"
|
||||||
|
mc.SSH.RemoteUsername = "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ignBuilder := ignition.NewIgnitionBuilder(ignition.DynamicIgnition{
|
ignBuilder := ignition.NewIgnitionBuilder(ignition.DynamicIgnition{
|
||||||
Name: opts.Username,
|
Name: userName,
|
||||||
Key: sshKey,
|
Key: sshKey,
|
||||||
TimeZone: opts.TimeZone,
|
TimeZone: opts.TimeZone,
|
||||||
UID: uid,
|
UID: uid,
|
||||||
@ -223,7 +204,9 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) (*vmconfigs.M
|
|||||||
ignBuilder.WithUnit(readyUnit)
|
ignBuilder.WithUnit(readyUnit)
|
||||||
|
|
||||||
// Mounts
|
// Mounts
|
||||||
mc.Mounts = CmdLineVolumesToMounts(opts.Volumes, mp.MountType())
|
if mp.VMType() != machineDefine.WSLVirt {
|
||||||
|
mc.Mounts = CmdLineVolumesToMounts(opts.Volumes, mp.MountType())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO AddSSHConnectionToPodmanSocket could take an machineconfig instead
|
// TODO AddSSHConnectionToPodmanSocket could take an machineconfig instead
|
||||||
if err := connection.AddSSHConnectionsToPodmanSocket(mc.HostUser.UID, mc.SSH.Port, mc.SSH.IdentityPath, mc.Name, mc.SSH.RemoteUsername, opts); err != nil {
|
if err := connection.AddSSHConnectionsToPodmanSocket(mc.HostUser.UID, mc.SSH.Port, mc.SSH.IdentityPath, mc.Name, mc.SSH.RemoteUsername, opts); err != nil {
|
||||||
@ -273,6 +256,11 @@ func VMExists(name string, vmstubbers []vmconfigs.VMProvider) (*vmconfigs.Machin
|
|||||||
|
|
||||||
// CheckExclusiveActiveVM checks if any of the machines are already running
|
// CheckExclusiveActiveVM checks if any of the machines are already running
|
||||||
func CheckExclusiveActiveVM(provider vmconfigs.VMProvider, mc *vmconfigs.MachineConfig) error {
|
func CheckExclusiveActiveVM(provider vmconfigs.VMProvider, mc *vmconfigs.MachineConfig) error {
|
||||||
|
// Don't check if provider supports parallel running machines
|
||||||
|
if !provider.RequireExclusiveActive() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if any other machines are running; if so, we error
|
// Check if any other machines are running; if so, we error
|
||||||
localMachines, err := getMCsOverProviders([]vmconfigs.VMProvider{provider})
|
localMachines, err := getMCsOverProviders([]vmconfigs.VMProvider{provider})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -347,21 +335,23 @@ func Stop(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDef
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop GvProxy and remove PID file
|
// Stop GvProxy and remove PID file
|
||||||
gvproxyPidFile, err := dirs.RuntimeDir.AppendToNewVMFile("gvproxy.pid", nil)
|
if !mp.UseProviderNetworkSetup() {
|
||||||
if err != nil {
|
gvproxyPidFile, err := dirs.RuntimeDir.AppendToNewVMFile("gvproxy.pid", nil)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := machine.CleanupGVProxy(*gvproxyPidFile); err != nil {
|
|
||||||
logrus.Errorf("unable to clean up gvproxy: %q", err)
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
defer func() {
|
||||||
|
if err := machine.CleanupGVProxy(*gvproxyPidFile); err != nil {
|
||||||
|
logrus.Errorf("unable to clean up gvproxy: %q", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDefine.MachineDirs, opts machine.StartOptions) error {
|
func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, _ *machineDefine.MachineDirs, opts machine.StartOptions) error {
|
||||||
defaultBackoff := 500 * time.Millisecond
|
defaultBackoff := 500 * time.Millisecond
|
||||||
maxBackoffs := 6
|
maxBackoffs := 6
|
||||||
|
|
||||||
@ -396,7 +386,11 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mp.PostStartNetworking(mc)
|
if !opts.NoInfo && !mc.HostUser.Rootful {
|
||||||
|
machine.PrintRootlessWarning(mc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mp.PostStartNetworking(mc, opts.NoInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -423,15 +417,6 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDe
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
machine.WaitAPIAndPrintInfo(
|
|
||||||
forwardingState,
|
|
||||||
mc.Name,
|
|
||||||
findClaimHelper(),
|
|
||||||
forwardSocketPath,
|
|
||||||
opts.NoInfo,
|
|
||||||
mc.HostUser.Rootful,
|
|
||||||
)
|
|
||||||
|
|
||||||
// update the podman/docker socket service if the host user has been modified at all (UID or Rootful)
|
// update the podman/docker socket service if the host user has been modified at all (UID or Rootful)
|
||||||
if mc.HostUser.Modified {
|
if mc.HostUser.Modified {
|
||||||
if machine.UpdatePodmanDockerSockService(mc) == nil {
|
if machine.UpdatePodmanDockerSockService(mc) == nil {
|
||||||
@ -443,5 +428,22 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, dirs *machineDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provider is responsible for waiting
|
||||||
|
if mp.UseProviderNetworkSetup() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
noInfo := opts.NoInfo
|
||||||
|
|
||||||
|
machine.WaitAPIAndPrintInfo(
|
||||||
|
forwardingState,
|
||||||
|
mc.Name,
|
||||||
|
findClaimHelper(),
|
||||||
|
forwardSocketPath,
|
||||||
|
noInfo,
|
||||||
|
mc.HostUser.Rootful,
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package shim
|
package shim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -23,16 +22,17 @@ const (
|
|||||||
dockerConnectTimeout = 5 * time.Second
|
dockerConnectTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider) (string, machine.APIForwardingState, error) {
|
var (
|
||||||
var (
|
ErrNotRunning = errors.New("machine not in running state")
|
||||||
forwardingState machine.APIForwardingState
|
ErrSSHNotListening = errors.New("machine is not listening on ssh port")
|
||||||
forwardSock string
|
)
|
||||||
)
|
|
||||||
// the guestSock is "inside" the guest machine
|
func startHostForwarder(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider, dirs *define.MachineDirs, hostSocks []string) error {
|
||||||
guestSock := fmt.Sprintf(defaultGuestSock, mc.HostUser.UID)
|
|
||||||
forwardUser := mc.SSH.RemoteUsername
|
forwardUser := mc.SSH.RemoteUsername
|
||||||
|
|
||||||
// TODO should this go up the stack higher
|
// TODO should this go up the stack higher or
|
||||||
|
// the guestSock is "inside" the guest machine
|
||||||
|
guestSock := fmt.Sprintf(defaultGuestSock, mc.HostUser.UID)
|
||||||
if mc.HostUser.Rootful {
|
if mc.HostUser.Rootful {
|
||||||
guestSock = "/run/podman/podman.sock"
|
guestSock = "/run/podman/podman.sock"
|
||||||
forwardUser = "root"
|
forwardUser = "root"
|
||||||
@ -40,38 +40,18 @@ func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider)
|
|||||||
|
|
||||||
cfg, err := config.Default()
|
cfg, err := config.Default()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
binary, err := cfg.FindHelperBinary(machine.ForwarderBinaryName, false)
|
binary, err := cfg.FindHelperBinary(machine.ForwarderBinaryName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
dataDir, err := mc.DataDir()
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, err
|
|
||||||
}
|
|
||||||
hostSocket, err := dataDir.AppendToNewVMFile("podman.sock", nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
runDir, err := mc.RuntimeDir()
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
linkSocketPath := filepath.Dir(dataDir.GetPath())
|
|
||||||
linkSocket, err := define.NewMachineFile(filepath.Join(linkSocketPath, "podman.sock"), nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := gvproxy.NewGvproxyCommand()
|
cmd := gvproxy.NewGvproxyCommand()
|
||||||
|
|
||||||
// GvProxy PID file path is now derived
|
// GvProxy PID file path is now derived
|
||||||
cmd.PidFile = filepath.Join(runDir.GetPath(), "gvproxy.pid")
|
cmd.PidFile = filepath.Join(dirs.RuntimeDir.GetPath(), "gvproxy.pid")
|
||||||
|
|
||||||
// TODO This can be re-enabled when gvisor-tap-vsock #305 is merged
|
// TODO This can be re-enabled when gvisor-tap-vsock #305 is merged
|
||||||
// debug is set, we dump to a logfile as well
|
// debug is set, we dump to a logfile as well
|
||||||
@ -81,10 +61,13 @@ func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider)
|
|||||||
|
|
||||||
cmd.SSHPort = mc.SSH.Port
|
cmd.SSHPort = mc.SSH.Port
|
||||||
|
|
||||||
cmd.AddForwardSock(hostSocket.GetPath())
|
// Windows providers listen on multiple sockets since they do not involve links
|
||||||
cmd.AddForwardDest(guestSock)
|
for _, hostSock := range hostSocks {
|
||||||
cmd.AddForwardUser(forwardUser)
|
cmd.AddForwardSock(hostSock)
|
||||||
cmd.AddForwardIdentity(mc.SSH.IdentityPath)
|
cmd.AddForwardDest(guestSock)
|
||||||
|
cmd.AddForwardUser(forwardUser)
|
||||||
|
cmd.AddForwardIdentity(mc.SSH.IdentityPath)
|
||||||
|
}
|
||||||
|
|
||||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||||
cmd.Debug = true
|
cmd.Debug = true
|
||||||
@ -94,82 +77,42 @@ func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider)
|
|||||||
// This allows a provider to perform additional setup as well as
|
// This allows a provider to perform additional setup as well as
|
||||||
// add in any provider specific options for gvproxy
|
// add in any provider specific options for gvproxy
|
||||||
if err := provider.StartNetworking(mc, &cmd); err != nil {
|
if err := provider.StartNetworking(mc, &cmd); err != nil {
|
||||||
return "", 0, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if mc.HostUser.UID != -1 {
|
|
||||||
forwardSock, forwardingState = setupAPIForwarding(hostSocket, linkSocket)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cmd.Cmd(binary)
|
c := cmd.Cmd(binary)
|
||||||
|
|
||||||
logrus.Debugf("gvproxy command-line: %s %s", binary, strings.Join(cmd.ToCmdline(), " "))
|
logrus.Debugf("gvproxy command-line: %s %s", binary, strings.Join(cmd.ToCmdline(), " "))
|
||||||
if err := c.Start(); err != nil {
|
if err := c.Start(); err != nil {
|
||||||
return forwardSock, 0, fmt.Errorf("unable to execute: %q: %w", cmd.ToCmdline(), err)
|
return fmt.Errorf("unable to execute: %q: %w", cmd.ToCmdline(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider) (string, machine.APIForwardingState, error) {
|
||||||
|
// Provider has its own networking code path (e.g. WSL)
|
||||||
|
if provider.UseProviderNetworkSetup() {
|
||||||
|
return "", 0, provider.StartNetworking(mc, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs, err := machine.GetMachineDirs(provider.VMType())
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostSocks, forwardSock, forwardingState, err := setupMachineSockets(mc.Name, dirs)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := startHostForwarder(mc, provider, dirs, hostSocks); err != nil {
|
||||||
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return forwardSock, forwardingState, nil
|
return forwardSock, forwardingState, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiOptions struct { //nolint:unused
|
|
||||||
socketpath, destinationSocketPath *define.VMFile
|
|
||||||
fowardUser string
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupAPIForwarding(hostSocket, linkSocket *define.VMFile) (string, machine.APIForwardingState) {
|
|
||||||
// The linking pattern is /var/run/docker.sock -> user global sock (link) -> machine sock (socket)
|
|
||||||
// This allows the helper to only have to maintain one constant target to the user, which can be
|
|
||||||
// repositioned without updating docker.sock.
|
|
||||||
|
|
||||||
if !dockerClaimSupported() {
|
|
||||||
return hostSocket.GetPath(), machine.ClaimUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dockerClaimHelperInstalled() {
|
|
||||||
return hostSocket.GetPath(), machine.NotInstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alreadyLinked(hostSocket.GetPath(), linkSocket.GetPath()) {
|
|
||||||
if checkSockInUse(linkSocket.GetPath()) {
|
|
||||||
return hostSocket.GetPath(), machine.MachineLocal
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = linkSocket.Delete()
|
|
||||||
|
|
||||||
if err := os.Symlink(hostSocket.GetPath(), linkSocket.GetPath()); err != nil {
|
|
||||||
logrus.Warnf("could not create user global API forwarding link: %s", err.Error())
|
|
||||||
return hostSocket.GetPath(), machine.MachineLocal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alreadyLinked(linkSocket.GetPath(), dockerSock) {
|
|
||||||
if checkSockInUse(dockerSock) {
|
|
||||||
return hostSocket.GetPath(), machine.MachineLocal
|
|
||||||
}
|
|
||||||
|
|
||||||
if !claimDockerSock() {
|
|
||||||
logrus.Warn("podman helper is installed, but was not able to claim the global docker sock")
|
|
||||||
return hostSocket.GetPath(), machine.MachineLocal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dockerSock, machine.DockerGlobal
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// conductVMReadinessCheck checks to make sure the machine is in the proper state
|
// conductVMReadinessCheck checks to make sure the machine is in the proper state
|
||||||
// and that SSH is up and running
|
// and that SSH is up and running
|
||||||
func conductVMReadinessCheck(mc *vmconfigs.MachineConfig, maxBackoffs int, backoff time.Duration, stateF func() (define.Status, error)) (connected bool, sshError error, err error) {
|
func conductVMReadinessCheck(mc *vmconfigs.MachineConfig, maxBackoffs int, backoff time.Duration, stateF func() (define.Status, error)) (connected bool, sshError error, err error) {
|
||||||
@ -182,22 +125,30 @@ func conductVMReadinessCheck(mc *vmconfigs.MachineConfig, maxBackoffs int, backo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
if state == define.Running && isListening(mc.SSH.Port) {
|
if state != define.Running {
|
||||||
// Also make sure that SSH is up and running. The
|
sshError = ErrNotRunning
|
||||||
// ready service's dependencies don't fully make sure
|
continue
|
||||||
// that clients can SSH into the machine immediately
|
|
||||||
// after boot.
|
|
||||||
//
|
|
||||||
// CoreOS users have reported the same observation but
|
|
||||||
// the underlying source of the issue remains unknown.
|
|
||||||
|
|
||||||
if sshError = machine.CommonSSH(mc.SSH.RemoteUsername, mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, []string{"true"}); sshError != nil {
|
|
||||||
logrus.Debugf("SSH readiness check for machine failed: %v", sshError)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
connected = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
if !isListening(mc.SSH.Port) {
|
||||||
|
sshError = ErrSSHNotListening
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also make sure that SSH is up and running. The
|
||||||
|
// ready service's dependencies don't fully make sure
|
||||||
|
// that clients can SSH into the machine immediately
|
||||||
|
// after boot.
|
||||||
|
//
|
||||||
|
// CoreOS users have reported the same observation but
|
||||||
|
// the underlying source of the issue remains unknown.
|
||||||
|
|
||||||
|
if sshError = machine.CommonSSHSilent(mc.SSH.RemoteUsername, mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, []string{"true"}); sshError != nil {
|
||||||
|
logrus.Debugf("SSH readiness check for machine failed: %v", sshError)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
connected = true
|
||||||
|
sshError = nil
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
84
pkg/machine/shim/networking_unix.go
Normal file
84
pkg/machine/shim/networking_unix.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//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/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupMachineSockets(name string, dirs *define.MachineDirs) ([]string, string, machine.APIForwardingState, error) {
|
||||||
|
hostSocket, err := dirs.DataDir.AppendToNewVMFile("podman.sock", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
linkSocketPath := filepath.Dir(dirs.DataDir.GetPath())
|
||||||
|
linkSocket, err := define.NewMachineFile(filepath.Join(linkSocketPath, "podman.sock"), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardSock, state := setupForwardingLinks(hostSocket, linkSocket)
|
||||||
|
return []string{hostSocket.GetPath()}, forwardSock, state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupForwardingLinks(hostSocket, linkSocket *define.VMFile) (string, machine.APIForwardingState) {
|
||||||
|
// The linking pattern is /var/run/docker.sock -> user global sock (link) -> machine sock (socket)
|
||||||
|
// This allows the helper to only have to maintain one constant target to the user, which can be
|
||||||
|
// repositioned without updating docker.sock.
|
||||||
|
|
||||||
|
if !dockerClaimSupported() {
|
||||||
|
return hostSocket.GetPath(), machine.ClaimUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dockerClaimHelperInstalled() {
|
||||||
|
return hostSocket.GetPath(), machine.NotInstalled
|
||||||
|
}
|
||||||
|
|
||||||
|
if !alreadyLinked(hostSocket.GetPath(), linkSocket.GetPath()) {
|
||||||
|
if checkSockInUse(linkSocket.GetPath()) {
|
||||||
|
return hostSocket.GetPath(), machine.MachineLocal
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = linkSocket.Delete()
|
||||||
|
|
||||||
|
if err := os.Symlink(hostSocket.GetPath(), linkSocket.GetPath()); err != nil {
|
||||||
|
logrus.Warnf("could not create user global API forwarding link: %s", err.Error())
|
||||||
|
return hostSocket.GetPath(), machine.MachineLocal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !alreadyLinked(linkSocket.GetPath(), dockerSock) {
|
||||||
|
if checkSockInUse(dockerSock) {
|
||||||
|
return hostSocket.GetPath(), machine.MachineLocal
|
||||||
|
}
|
||||||
|
|
||||||
|
if !claimDockerSock() {
|
||||||
|
logrus.Warn("podman helper is installed, but was not able to claim the global docker sock")
|
||||||
|
return hostSocket.GetPath(), machine.MachineLocal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockerSock, machine.DockerGlobal
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
24
pkg/machine/shim/networking_windows.go
Normal file
24
pkg/machine/shim/networking_windows.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package shim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupMachineSockets(name string, dirs *define.MachineDirs) ([]string, string, machine.APIForwardingState, error) {
|
||||||
|
machinePipe := machine.ToDist(name)
|
||||||
|
if !machine.PipeNameAvailable(machinePipe, machine.MachineNameWait) {
|
||||||
|
return nil, "", 0, fmt.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
|
||||||
|
}
|
||||||
|
sockets := []string{machine.NamedPipePrefix + machinePipe}
|
||||||
|
state := machine.MachineLocal
|
||||||
|
|
||||||
|
if machine.PipeNameAvailable(machine.GlobalNamedPipe, machine.GlobalNameWait) {
|
||||||
|
sockets = append(sockets, machine.NamedPipePrefix+machine.GlobalNamedPipe)
|
||||||
|
state = machine.DockerGlobal
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockets, sockets[len(sockets)-1], state, nil
|
||||||
|
}
|
@ -2,7 +2,6 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -13,24 +12,38 @@ import (
|
|||||||
// and a port
|
// and a port
|
||||||
// TODO This should probably be taught about an machineconfig to reduce input
|
// TODO This should probably be taught about an machineconfig to reduce input
|
||||||
func CommonSSH(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
func CommonSSH(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||||||
|
return commonSSH(username, identityPath, name, sshPort, inputArgs, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonSSHSilent(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||||||
|
return commonSSH(username, identityPath, name, sshPort, inputArgs, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonSSH(username, identityPath, name string, sshPort int, inputArgs []string, silent bool) error {
|
||||||
sshDestination := username + "@localhost"
|
sshDestination := username + "@localhost"
|
||||||
port := strconv.Itoa(sshPort)
|
port := strconv.Itoa(sshPort)
|
||||||
|
interactive := true
|
||||||
|
|
||||||
args := []string{"-i", identityPath, "-p", port, sshDestination,
|
args := []string{"-i", identityPath, "-p", port, sshDestination,
|
||||||
"-o", "IdentitiesOnly=yes",
|
"-o", "IdentitiesOnly=yes",
|
||||||
"-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="}
|
"-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="}
|
||||||
if len(inputArgs) > 0 {
|
if len(inputArgs) > 0 {
|
||||||
|
interactive = false
|
||||||
args = append(args, inputArgs...)
|
args = append(args, inputArgs...)
|
||||||
} else {
|
} else {
|
||||||
|
// ensure we have a tty
|
||||||
|
args = append(args, "-t")
|
||||||
fmt.Printf("Connecting to vm %s. To close connection, use `~.` or `exit`\n", name)
|
fmt.Printf("Connecting to vm %s. To close connection, use `~.` or `exit`\n", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("ssh", args...)
|
cmd := exec.Command("ssh", args...)
|
||||||
logrus.Debugf("Executing: ssh %v\n", args)
|
logrus.Debugf("Executing: ssh %v\n", args)
|
||||||
|
|
||||||
cmd.Stdout = os.Stdout
|
if !silent {
|
||||||
cmd.Stderr = os.Stderr
|
if err := setupIOPassthrough(cmd, interactive); err != nil {
|
||||||
cmd.Stdin = os.Stdin
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
16
pkg/machine/ssh_unix.go
Normal file
16
pkg/machine/ssh_unix.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupIOPassthrough(cmd *exec.Cmd, interactive bool) error {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
42
pkg/machine/ssh_windows.go
Normal file
42
pkg/machine/ssh_windows.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupIOPassthrough(cmd *exec.Cmd, interactive bool) error {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
if interactive {
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenSSh mucks with the associated virtual console when there is no pty,
|
||||||
|
// leaving it in a broken state. Pipe the output to isolate stdout/stderr
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copier := func(name string, dest string, from io.Reader, to io.Writer) {
|
||||||
|
if _, err := io.Copy(to, from); err != nil {
|
||||||
|
logrus.Warnf("could not copy output from command %s to %s", name, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go copier(cmd.Path, "stdout", stdout, os.Stdout)
|
||||||
|
go copier(cmd.Path, "stderr", stderr, os.Stderr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -23,7 +23,7 @@ type DiskFromURL struct {
|
|||||||
tempLocation *define.VMFile
|
tempLocation *define.VMFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.VMFile) (*DiskFromURL, error) {
|
func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.VMFile, optionalTempFileName *string) (*DiskFromURL, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
@ -40,6 +40,9 @@ func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.
|
|||||||
}
|
}
|
||||||
|
|
||||||
remoteImageName := path.Base(inputPath)
|
remoteImageName := path.Base(inputPath)
|
||||||
|
if optionalTempFileName != nil {
|
||||||
|
remoteImageName = *optionalTempFileName
|
||||||
|
}
|
||||||
if remoteImageName == "" {
|
if remoteImageName == "" {
|
||||||
return nil, fmt.Errorf("invalid url: unable to determine image name in %q", inputPath)
|
return nil, fmt.Errorf("invalid url: unable to determine image name in %q", inputPath)
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,10 @@ func (f fcosMachineImage) path() string {
|
|||||||
|
|
||||||
type VMProvider interface { //nolint:interfacebloat
|
type VMProvider interface { //nolint:interfacebloat
|
||||||
CreateVM(opts define.CreateVMOpts, mc *MachineConfig, builder *ignition.IgnitionBuilder) error
|
CreateVM(opts define.CreateVMOpts, mc *MachineConfig, builder *ignition.IgnitionBuilder) error
|
||||||
|
// GetDisk should be only temporary. It is largely here only because WSL disk pulling is different
|
||||||
|
// TODO
|
||||||
|
// Let's deprecate this ASAP
|
||||||
|
GetDisk(userInputPath string, dirs *define.MachineDirs, mc *MachineConfig) error
|
||||||
PrepareIgnition(mc *MachineConfig, ignBuilder *ignition.IgnitionBuilder) (*ignition.ReadyUnitOpts, error)
|
PrepareIgnition(mc *MachineConfig, ignBuilder *ignition.IgnitionBuilder) (*ignition.ReadyUnitOpts, error)
|
||||||
GetHyperVisorVMs() ([]string, error)
|
GetHyperVisorVMs() ([]string, error)
|
||||||
MountType() VolumeMountType
|
MountType() VolumeMountType
|
||||||
@ -115,12 +119,15 @@ type VMProvider interface { //nolint:interfacebloat
|
|||||||
RemoveAndCleanMachines(dirs *define.MachineDirs) error
|
RemoveAndCleanMachines(dirs *define.MachineDirs) error
|
||||||
SetProviderAttrs(mc *MachineConfig, opts define.SetOptions) error
|
SetProviderAttrs(mc *MachineConfig, opts define.SetOptions) error
|
||||||
StartNetworking(mc *MachineConfig, cmd *gvproxy.GvproxyCommand) error
|
StartNetworking(mc *MachineConfig, cmd *gvproxy.GvproxyCommand) error
|
||||||
PostStartNetworking(mc *MachineConfig) error
|
PostStartNetworking(mc *MachineConfig, noInfo bool) error
|
||||||
StartVM(mc *MachineConfig) (func() error, func() error, error)
|
StartVM(mc *MachineConfig) (func() error, func() error, error)
|
||||||
State(mc *MachineConfig, bypass bool) (define.Status, error)
|
State(mc *MachineConfig, bypass bool) (define.Status, error)
|
||||||
StopVM(mc *MachineConfig, hardStop bool) error
|
StopVM(mc *MachineConfig, hardStop bool) error
|
||||||
StopHostNetworking(mc *MachineConfig, vmType define.VMType) error
|
StopHostNetworking(mc *MachineConfig, vmType define.VMType) error
|
||||||
VMType() define.VMType
|
VMType() define.VMType
|
||||||
|
UserModeNetworkEnabled(mc *MachineConfig) bool
|
||||||
|
UseProviderNetworkSetup() bool
|
||||||
|
RequireExclusiveActive() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostUser describes the host user
|
// HostUser describes the host user
|
||||||
|
@ -13,7 +13,8 @@ type HyperVConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WSLConfig struct {
|
type WSLConfig struct {
|
||||||
//wslstuff *aThing
|
// Uses usermode networking
|
||||||
|
UserModeNetworking bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stubs
|
// Stubs
|
||||||
|
@ -77,6 +77,7 @@ func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIden
|
|||||||
}
|
}
|
||||||
mc.Resources = mrc
|
mc.Resources = mrc
|
||||||
|
|
||||||
|
// TODO WSL had a locking port mechanism, we should consider this.
|
||||||
sshPort, err := utils.GetRandomPort()
|
sshPort, err := utils.GetRandomPort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
249
pkg/machine/wsl/declares.go
Normal file
249
pkg/machine/wsl/declares.go
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package wsl
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrorSuccessRebootInitiated = 1641
|
||||||
|
ErrorSuccessRebootRequired = 3010
|
||||||
|
currentMachineVersion = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const containersConf = `[containers]
|
||||||
|
|
||||||
|
[engine]
|
||||||
|
cgroup_manager = "cgroupfs"
|
||||||
|
`
|
||||||
|
|
||||||
|
const registriesConf = `unqualified-search-registries=["docker.io"]
|
||||||
|
`
|
||||||
|
|
||||||
|
const appendPort = `grep -q Port\ %d /etc/ssh/sshd_config || echo Port %d >> /etc/ssh/sshd_config`
|
||||||
|
|
||||||
|
const changePort = `sed -E -i 's/^Port[[:space:]]+[0-9]+/Port %d/' /etc/ssh/sshd_config`
|
||||||
|
|
||||||
|
const configServices = `ln -fs /usr/lib/systemd/system/sshd.service /etc/systemd/system/multi-user.target.wants/sshd.service
|
||||||
|
ln -fs /usr/lib/systemd/system/podman.socket /etc/systemd/system/sockets.target.wants/podman.socket
|
||||||
|
rm -f /etc/systemd/system/getty.target.wants/console-getty.service
|
||||||
|
rm -f /etc/systemd/system/getty.target.wants/getty@tty1.service
|
||||||
|
rm -f /etc/systemd/system/multi-user.target.wants/systemd-resolved.service
|
||||||
|
rm -f /etc/systemd/system/sysinit.target.wants//systemd-resolved.service
|
||||||
|
rm -f /etc/systemd/system/dbus-org.freedesktop.resolve1.service
|
||||||
|
ln -fs /dev/null /etc/systemd/system/console-getty.service
|
||||||
|
ln -fs /dev/null /etc/systemd/system/systemd-oomd.socket
|
||||||
|
mkdir -p /etc/systemd/system/systemd-sysusers.service.d/
|
||||||
|
echo CREATE_MAIL_SPOOL=no >> /etc/default/useradd
|
||||||
|
adduser -m [USER] -G wheel
|
||||||
|
mkdir -p /home/[USER]/.config/systemd/[USER]/
|
||||||
|
chown [USER]:[USER] /home/[USER]/.config
|
||||||
|
`
|
||||||
|
|
||||||
|
const sudoers = `%wheel ALL=(ALL) NOPASSWD: ALL
|
||||||
|
`
|
||||||
|
|
||||||
|
const bootstrap = `#!/bin/bash
|
||||||
|
ps -ef | grep -v grep | grep -q systemd && exit 0
|
||||||
|
nohup unshare --kill-child --fork --pid --mount --mount-proc --propagation shared /lib/systemd/systemd >/dev/null 2>&1 &
|
||||||
|
sleep 0.1
|
||||||
|
`
|
||||||
|
|
||||||
|
const wslmotd = `
|
||||||
|
You will be automatically entered into a nested process namespace where
|
||||||
|
systemd is running. If you need to access the parent namespace, hit ctrl-d
|
||||||
|
or type exit. This also means to log out you need to exit twice.
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const sysdpid = "SYSDPID=`ps -eo cmd,pid | grep -m 1 ^/lib/systemd/systemd | awk '{print $2}'`"
|
||||||
|
|
||||||
|
const profile = sysdpid + `
|
||||||
|
if [ ! -z "$SYSDPID" ] && [ "$SYSDPID" != "1" ]; then
|
||||||
|
cat /etc/wslmotd
|
||||||
|
/usr/local/bin/enterns
|
||||||
|
fi
|
||||||
|
`
|
||||||
|
|
||||||
|
const enterns = "#!/bin/bash\n" + sysdpid + `
|
||||||
|
if [ ! -z "$SYSDPID" ] && [ "$SYSDPID" != "1" ]; then
|
||||||
|
NSENTER=("nsenter" "-m" "-p" "-t" "$SYSDPID" "--wd=$PWD")
|
||||||
|
|
||||||
|
if [ "$UID" != "0" ]; then
|
||||||
|
NSENTER=("sudo" "${NSENTER[@]}")
|
||||||
|
if [ "$#" != "0" ]; then
|
||||||
|
NSENTER+=("sudo" "-u" "$USER")
|
||||||
|
else
|
||||||
|
NSENTER+=("su" "-l" "$USER")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
"${NSENTER[@]}" "$@"
|
||||||
|
fi`
|
||||||
|
|
||||||
|
const waitTerm = sysdpid + `
|
||||||
|
if [ ! -z "$SYSDPID" ]; then
|
||||||
|
timeout 60 tail -f /dev/null --pid $SYSDPID
|
||||||
|
fi
|
||||||
|
`
|
||||||
|
|
||||||
|
const wslConf = `[user]
|
||||||
|
default=[USER]
|
||||||
|
`
|
||||||
|
|
||||||
|
const wslConfUserNet = `
|
||||||
|
[network]
|
||||||
|
generateResolvConf = false
|
||||||
|
`
|
||||||
|
|
||||||
|
const resolvConfUserNet = `
|
||||||
|
nameserver 192.168.127.1
|
||||||
|
`
|
||||||
|
|
||||||
|
// WSL kernel does not have sg and crypto_user modules
|
||||||
|
const overrideSysusers = `[Service]
|
||||||
|
LoadCredential=
|
||||||
|
`
|
||||||
|
|
||||||
|
const lingerService = `[Unit]
|
||||||
|
Description=A systemd user unit demo
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target podman.socket
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/sleep infinity
|
||||||
|
`
|
||||||
|
|
||||||
|
const lingerSetup = `mkdir -p /home/[USER]/.config/systemd/user/default.target.wants
|
||||||
|
ln -fs /home/[USER]/.config/systemd/user/linger-example.service \
|
||||||
|
/home/[USER]/.config/systemd/user/default.target.wants/linger-example.service
|
||||||
|
`
|
||||||
|
|
||||||
|
const bindMountSystemService = `
|
||||||
|
[Unit]
|
||||||
|
Description=Bind mount for system podman sockets
|
||||||
|
After=podman.socket
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
RemainAfterExit=true
|
||||||
|
Type=oneshot
|
||||||
|
# Ensure user services can register sockets as well
|
||||||
|
ExecStartPre=mkdir -p -m 777 /mnt/wsl/podman-sockets
|
||||||
|
ExecStartPre=mkdir -p -m 777 /mnt/wsl/podman-sockets/%[1]s
|
||||||
|
ExecStartPre=touch /mnt/wsl/podman-sockets/%[1]s/podman-root.sock
|
||||||
|
ExecStart=mount --bind %%t/podman/podman.sock /mnt/wsl/podman-sockets/%[1]s/podman-root.sock
|
||||||
|
ExecStop=umount /mnt/wsl/podman-sockets/%[1]s/podman-root.sock
|
||||||
|
`
|
||||||
|
|
||||||
|
const bindMountUserService = `
|
||||||
|
[Unit]
|
||||||
|
Description=Bind mount for user podman sockets
|
||||||
|
After=podman.socket
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
RemainAfterExit=true
|
||||||
|
Type=oneshot
|
||||||
|
# Consistency with system service (supports racing)
|
||||||
|
ExecStartPre=mkdir -p -m 777 /mnt/wsl/podman-sockets
|
||||||
|
ExecStartPre=mkdir -p -m 777 /mnt/wsl/podman-sockets/%[1]s
|
||||||
|
ExecStartPre=touch /mnt/wsl/podman-sockets/%[1]s/podman-user.sock
|
||||||
|
# Relies on /etc/fstab entry for user mounting
|
||||||
|
ExecStart=mount /mnt/wsl/podman-sockets/%[1]s/podman-user.sock
|
||||||
|
ExecStop=umount /mnt/wsl/podman-sockets/%[1]s/podman-user.sock
|
||||||
|
`
|
||||||
|
|
||||||
|
const bindMountFsTab = `/run/user/1000/podman/podman.sock /mnt/wsl/podman-sockets/%s/podman-user.sock none noauto,user,bind,defaults 0 0
|
||||||
|
`
|
||||||
|
const (
|
||||||
|
defaultTargetWants = "default.target.wants"
|
||||||
|
userSystemdPath = "/home/%[1]s/.config/systemd/user"
|
||||||
|
sysSystemdPath = "/etc/systemd/system"
|
||||||
|
userSystemdWants = userSystemdPath + "/" + defaultTargetWants
|
||||||
|
sysSystemdWants = sysSystemdPath + "/" + defaultTargetWants
|
||||||
|
bindUnitFileName = "podman-mnt-bindings.service"
|
||||||
|
bindUserUnitPath = userSystemdPath + "/" + bindUnitFileName
|
||||||
|
bindUserUnitWant = userSystemdWants + "/" + bindUnitFileName
|
||||||
|
bindSysUnitPath = sysSystemdPath + "/" + bindUnitFileName
|
||||||
|
bindSysUnitWant = sysSystemdWants + "/" + bindUnitFileName
|
||||||
|
podmanSocketDropin = "podman.socket.d"
|
||||||
|
podmanSocketDropinPath = sysSystemdPath + "/" + podmanSocketDropin
|
||||||
|
)
|
||||||
|
|
||||||
|
const configBindServices = "mkdir -p " + userSystemdWants + " " + sysSystemdWants + " " + podmanSocketDropinPath + "\n" +
|
||||||
|
"ln -fs " + bindUserUnitPath + " " + bindUserUnitWant + "\n" +
|
||||||
|
"ln -fs " + bindSysUnitPath + " " + bindSysUnitWant + "\n"
|
||||||
|
|
||||||
|
const overrideSocketGroup = `
|
||||||
|
[Socket]
|
||||||
|
SocketMode=0660
|
||||||
|
SocketGroup=wheel
|
||||||
|
`
|
||||||
|
|
||||||
|
const proxyConfigSetup = `#!/bin/bash
|
||||||
|
|
||||||
|
SYSTEMD_CONF=/etc/systemd/system.conf.d/default-env.conf
|
||||||
|
ENVD_CONF=/etc/environment.d/default-env.conf
|
||||||
|
PROFILE_CONF=/etc/profile.d/default-env.sh
|
||||||
|
|
||||||
|
IFS="|"
|
||||||
|
read proxies
|
||||||
|
|
||||||
|
mkdir -p /etc/profile.d /etc/environment.d /etc/systemd/system.conf.d/
|
||||||
|
rm -f $SYSTEMD_CONF
|
||||||
|
for proxy in $proxies; do
|
||||||
|
output+="$proxy "
|
||||||
|
done
|
||||||
|
echo "[Manager]" >> $SYSTEMD_CONF
|
||||||
|
echo -ne "DefaultEnvironment=" >> $SYSTEMD_CONF
|
||||||
|
|
||||||
|
echo $output >> $SYSTEMD_CONF
|
||||||
|
rm -f $ENVD_CONF
|
||||||
|
for proxy in $proxies; do
|
||||||
|
echo "$proxy" >> $ENVD_CONF
|
||||||
|
done
|
||||||
|
rm -f $PROFILE_CONF
|
||||||
|
for proxy in $proxies; do
|
||||||
|
echo "export $proxy" >> $PROFILE_CONF
|
||||||
|
done
|
||||||
|
`
|
||||||
|
|
||||||
|
const proxyConfigAttempt = `if [ -f /usr/local/bin/proxyinit ]; \
|
||||||
|
then /usr/local/bin/proxyinit; \
|
||||||
|
else exit 42; \
|
||||||
|
fi`
|
||||||
|
|
||||||
|
const clearProxySettings = `rm -f /etc/systemd/system.conf.d/default-env.conf \
|
||||||
|
/etc/environment.d/default-env.conf \
|
||||||
|
/etc/profile.d/default-env.sh`
|
||||||
|
|
||||||
|
const wslInstallError = `Could not %s. See previous output for any potential failure details.
|
||||||
|
If you can not resolve the issue, and rerunning fails, try the "wsl --install" process
|
||||||
|
outlined in the following article:
|
||||||
|
|
||||||
|
http://docs.microsoft.com/en-us/windows/wsl/install
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const wslKernelError = `Could not %s. See previous output for any potential failure details.
|
||||||
|
If you can not resolve the issue, try rerunning the "podman machine init command". If that fails
|
||||||
|
try the "wsl --update" command and then rerun "podman machine init". Finally, if all else fails,
|
||||||
|
try following the steps outlined in the following article:
|
||||||
|
|
||||||
|
http://docs.microsoft.com/en-us/windows/wsl/install
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const wslInstallKernel = "install the WSL Kernel"
|
||||||
|
|
||||||
|
const wslOldVersion = `Automatic installation of WSL can not be performed on this version of Windows
|
||||||
|
Either update to Build 19041 (or later), or perform the manual installation steps
|
||||||
|
outlined in the following article:
|
||||||
|
|
||||||
|
http://docs.microsoft.com/en-us/windows/wsl/install\
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const (
|
||||||
|
gvProxy = "gvproxy.exe"
|
||||||
|
winSShProxy = "win-sshproxy.exe"
|
||||||
|
pipePrefix = "npipe:////./pipe/"
|
||||||
|
globalPipe = "docker_engine"
|
||||||
|
userModeDist = "podman-net-usermode"
|
||||||
|
rootfulSock = "/run/podman/podman.sock"
|
||||||
|
rootlessSock = "/run/user/1000/podman/podman.sock"
|
||||||
|
)
|
@ -1,5 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
package wsl
|
package wsl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,6 +12,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/pkg/machine"
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
)
|
)
|
||||||
@ -27,8 +27,10 @@ type FedoraDownload struct {
|
|||||||
machine.Download
|
machine.Download
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFedoraDownloader
|
||||||
|
// deprecated
|
||||||
func NewFedoraDownloader(vmType define.VMType, vmName, releaseStream string) (machine.DistributionDownload, error) {
|
func NewFedoraDownloader(vmType define.VMType, vmName, releaseStream string) (machine.DistributionDownload, error) {
|
||||||
downloadURL, version, arch, size, err := getFedoraDownload()
|
downloadURL, version, arch, size, err := GetFedoraDownloadForWSL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -82,7 +84,7 @@ func (f FedoraDownload) CleanCache() error {
|
|||||||
return machine.RemoveImageAfterExpire(f.CacheDir, expire)
|
return machine.RemoveImageAfterExpire(f.CacheDir, expire)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFedoraDownload() (*url.URL, string, string, int64, error) {
|
func GetFedoraDownloadForWSL() (*url.URL, string, string, int64, error) {
|
||||||
var releaseURL string
|
var releaseURL string
|
||||||
arch := machine.DetermineMachineArch()
|
arch := machine.DetermineMachineArch()
|
||||||
switch arch {
|
switch arch {
|
||||||
@ -118,11 +120,14 @@ func getFedoraDownload() (*url.URL, string, string, int64, error) {
|
|||||||
return nil, "", "", -1, fmt.Errorf("get request failed: %s: %w", verURL.String(), err)
|
return nil, "", "", -1, fmt.Errorf("get request failed: %s: %w", verURL.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer func() {
|
||||||
bytes, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: 1024})
|
if err := resp.Body.Close(); err != nil {
|
||||||
|
logrus.Errorf("error closing http boddy: %q", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
b, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: 1024})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", "", -1, fmt.Errorf("failed reading: %s: %w", verURL.String(), err)
|
return nil, "", "", -1, fmt.Errorf("failed reading: %s: %w", verURL.String(), err)
|
||||||
}
|
}
|
||||||
|
return downloadURL, strings.TrimSpace(string(b)), arch, contentLen, nil
|
||||||
return downloadURL, strings.TrimSpace(string(bytes)), arch, contentLen, nil
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
356
pkg/machine/wsl/stubber.go
Normal file
356
pkg/machine/wsl/stubber.go
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package wsl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
||||||
|
|
||||||
|
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/ignition"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WSLStubber struct {
|
||||||
|
vmconfigs.WSLConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineConfig, _ *ignition.IgnitionBuilder) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
// cleanup half-baked files if init fails at any point
|
||||||
|
callbackFuncs := machine.InitCleanup()
|
||||||
|
defer callbackFuncs.CleanIfErr(&err)
|
||||||
|
go callbackFuncs.CleanOnSignal()
|
||||||
|
mc.WSLHypervisor = new(vmconfigs.WSLConfig)
|
||||||
|
// TODO
|
||||||
|
// USB opts are unsupported in WSL. Need to account for that here
|
||||||
|
// or up the stack
|
||||||
|
// if len(opts.USBs) > 0 {
|
||||||
|
// return nil, fmt.Errorf("USB host passthrough is not supported for WSL machines")
|
||||||
|
// }
|
||||||
|
|
||||||
|
if cont, err := checkAndInstallWSL(opts.ReExec); !cont {
|
||||||
|
appendOutputIfError(opts.ReExec, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = setupWslProxyEnv()
|
||||||
|
|
||||||
|
if opts.UserModeNetworking {
|
||||||
|
if err = verifyWSLUserModeCompat(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mc.WSLHypervisor.UserModeNetworking = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = "Importing operating system into WSL (this may take a few minutes on a new WSL install)..."
|
||||||
|
dist, err := provisionWSLDist(mc.Name, mc.ImagePath.GetPath(), prompt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
unprovisionCallbackFunc := func() error {
|
||||||
|
return unprovisionWSL(mc)
|
||||||
|
}
|
||||||
|
callbackFuncs.Add(unprovisionCallbackFunc)
|
||||||
|
|
||||||
|
if mc.WSLHypervisor.UserModeNetworking {
|
||||||
|
if err = installUserModeDist(dist, mc.ImagePath.GetPath()); err != nil {
|
||||||
|
_ = unregisterDist(dist)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Configuring system...")
|
||||||
|
if err = configureSystem(mc, dist); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = installScripts(dist); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = createKeys(mc, dist); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// recycle vm
|
||||||
|
return terminateDist(dist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) PrepareIgnition(_ *vmconfigs.MachineConfig, _ *ignition.IgnitionBuilder) (*ignition.ReadyUnitOpts, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) GetHyperVisorVMs() ([]string, error) {
|
||||||
|
vms, err := getAllWSLDistros(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wslVMs := make([]string, 0)
|
||||||
|
for name := range vms {
|
||||||
|
wslVMs = append(wslVMs, name)
|
||||||
|
}
|
||||||
|
return wslVMs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) MountType() vmconfigs.VolumeMountType {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) MountVolumesToVM(mc *vmconfigs.MachineConfig, quiet bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error, error) {
|
||||||
|
// Note: we could consider swapping the two conditionals
|
||||||
|
// below if we wanted to hard error on the wsl unregister
|
||||||
|
// of the vm
|
||||||
|
wslRemoveFunc := func() error {
|
||||||
|
if err := runCmdPassThrough("wsl", "--unregister", machine.ToDist(mc.Name)); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
return machine.ReleaseMachinePort(mc.SSH.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{}, wslRemoveFunc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) RemoveAndCleanMachines(_ *define.MachineDirs) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (w WSLStubber) SetProviderAttrs(mc *vmconfigs.MachineConfig, opts define.SetOptions) error {
|
||||||
|
mc.Lock()
|
||||||
|
defer mc.Unlock()
|
||||||
|
|
||||||
|
// TODO the check for running when setting rootful is something I have not
|
||||||
|
// seen in the other distributions. I wonder if this is true everywhere or just
|
||||||
|
// with WSL?
|
||||||
|
// TODO maybe the "rule" for set is that it must be done when the machine is
|
||||||
|
// stopped?
|
||||||
|
// if opts.Rootful != nil && v.Rootful != *opts.Rootful {
|
||||||
|
// err := v.setRootful(*opts.Rootful)
|
||||||
|
// if err != nil {
|
||||||
|
// setErrors = append(setErrors, fmt.Errorf("setting rootful option: %w", err))
|
||||||
|
// } else {
|
||||||
|
// if v.isRunning() {
|
||||||
|
// logrus.Warn("restart is necessary for rootful change to go into effect")
|
||||||
|
// }
|
||||||
|
// v.Rootful = *opts.Rootful
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if opts.Rootful != nil && mc.HostUser.Rootful != *opts.Rootful {
|
||||||
|
if err := mc.SetRootful(*opts.Rootful); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.CPUs != nil {
|
||||||
|
return errors.New("changing CPUs not supported for WSL machines")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Memory != nil {
|
||||||
|
return errors.New("changing memory not supported for WSL machines")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO USB still needs to be plumbed for all providers
|
||||||
|
// if USBs != nil {
|
||||||
|
// setErrors = append(setErrors, errors.New("changing USBs not supported for WSL machines"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
if opts.DiskSize != nil {
|
||||||
|
return errors.New("changing disk size not supported for WSL machines")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.UserModeNetworking != nil && mc.WSLHypervisor.UserModeNetworking != *opts.UserModeNetworking {
|
||||||
|
if running, _ := isRunning(mc.Name); running {
|
||||||
|
return errors.New("user-mode networking can only be changed when the machine is not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
dist := machine.ToDist(mc.Name)
|
||||||
|
if err := changeDistUserModeNetworking(dist, mc.SSH.RemoteUsername, mc.ImagePath.GetPath(), *opts.UserModeNetworking); err != nil {
|
||||||
|
return fmt.Errorf("failure changing state of user-mode networking setting", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.WSLHypervisor.UserModeNetworking = *opts.UserModeNetworking
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) StartNetworking(mc *vmconfigs.MachineConfig, cmd *gvproxy.GvproxyCommand) error {
|
||||||
|
// Startup user-mode networking if enabled
|
||||||
|
if mc.WSLHypervisor.UserModeNetworking {
|
||||||
|
return startUserModeNetworking(mc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) UserModeNetworkEnabled(mc *vmconfigs.MachineConfig) bool {
|
||||||
|
return mc.WSLHypervisor.UserModeNetworking
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) UseProviderNetworkSetup() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) RequireExclusiveActive() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool) error {
|
||||||
|
winProxyOpts := machine.WinProxyOpts{
|
||||||
|
Name: mc.Name,
|
||||||
|
IdentityPath: mc.SSH.IdentityPath,
|
||||||
|
Port: mc.SSH.Port,
|
||||||
|
RemoteUsername: mc.SSH.RemoteUsername,
|
||||||
|
Rootful: mc.HostUser.Rootful,
|
||||||
|
VMType: w.VMType(),
|
||||||
|
}
|
||||||
|
machine.LaunchWinProxy(winProxyOpts, noInfo)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) {
|
||||||
|
useProxy := setupWslProxyEnv()
|
||||||
|
dist := machine.ToDist(mc.Name)
|
||||||
|
|
||||||
|
// TODO Quiet is hard set to false: follow up
|
||||||
|
if err := configureProxy(dist, useProxy, false); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO The original code checked to see if the SSH port was actually open and re-assigned if it was
|
||||||
|
// we could consider this but it should be higher up the stack
|
||||||
|
// if !machine.IsLocalPortAvailable(v.Port) {
|
||||||
|
// logrus.Warnf("SSH port conflict detected, reassigning a new port")
|
||||||
|
// if err := v.reassignSshPort(); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
err := wslInvoke(dist, "/root/bootstrap")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("the WSL bootstrap script failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readyFunc := func() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, readyFunc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) State(mc *vmconfigs.MachineConfig, bypass bool) (define.Status, error) {
|
||||||
|
running, err := isRunning(mc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
return define.Running, nil
|
||||||
|
}
|
||||||
|
return define.Stopped, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
mc.Lock()
|
||||||
|
defer mc.Unlock()
|
||||||
|
|
||||||
|
// recheck after lock
|
||||||
|
if running, err := isRunning(mc.Name); !running {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dist := machine.ToDist(mc.Name)
|
||||||
|
|
||||||
|
// Stop user-mode networking if enabled
|
||||||
|
if err := stopUserModeNetworking(mc); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Could not cleanly stop user-mode networking: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := machine.StopWinProxy(mc.Name, vmtype); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "sh")
|
||||||
|
cmd.Stdin = strings.NewReader(waitTerm)
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
return fmt.Errorf("executing wait command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exitCmd := exec.Command("wsl", "-u", "root", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
|
||||||
|
if err = exitCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("stopping sysd: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return terminateDist(dist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) StopHostNetworking(mc *vmconfigs.MachineConfig, vmType define.VMType) error {
|
||||||
|
return stopUserModeNetworking(mc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) VMType() define.VMType {
|
||||||
|
return define.WSLVirt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WSLStubber) GetDisk(_ string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
|
||||||
|
var (
|
||||||
|
myDisk ocipull.Disker
|
||||||
|
)
|
||||||
|
|
||||||
|
// check github for the latest version of the WSL dist
|
||||||
|
downloadURL, downloadVersion, _, _, err := GetFedoraDownloadForWSL()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we now save the "cached" rootfs in the form of "v<version-number>-rootfs.tar.xz"
|
||||||
|
// i.e.v39.0.31-rootfs.tar.xz
|
||||||
|
versionedBase := fmt.Sprintf("%s-%s", downloadVersion, filepath.Base(downloadURL.Path))
|
||||||
|
|
||||||
|
// TODO we need a mechanism for "flushing" old cache files
|
||||||
|
cachedFile, err := dirs.DataDir.AppendToNewVMFile(versionedBase, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we find the same file cached (determined by filename only), then dont pull
|
||||||
|
if _, err = os.Stat(cachedFile.GetPath()); err == nil {
|
||||||
|
logrus.Debugf("%q already exists locally", cachedFile.GetPath())
|
||||||
|
myDisk, err = stdpull.NewStdDiskPull(cachedFile.GetPath(), mc.ImagePath)
|
||||||
|
} else {
|
||||||
|
// no cached file
|
||||||
|
myDisk, err = stdpull.NewDiskFromURL(downloadURL.String(), mc.ImagePath, dirs.DataDir, &versionedBase)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// up until now, nothing has really happened
|
||||||
|
// pull if needed and decompress to image location
|
||||||
|
return myDisk.Get()
|
||||||
|
}
|
@ -5,6 +5,7 @@ package wsl
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -69,8 +70,8 @@ func verifyWSLUserModeCompat() error {
|
|||||||
prefix)
|
prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) startUserModeNetworking() error {
|
func startUserModeNetworking(mc *vmconfigs.MachineConfig) error {
|
||||||
if !v.UserModeNetworking {
|
if !mc.WSLHypervisor.UserModeNetworking {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func (v *MachineVM) startUserModeNetworking() error {
|
|||||||
return fmt.Errorf("could not locate %s, which is necessary for user-mode networking, please reinstall", gvProxy)
|
return fmt.Errorf("could not locate %s, which is necessary for user-mode networking, please reinstall", gvProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
flock, err := v.obtainUserModeNetLock()
|
flock, err := obtainUserModeNetLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -93,17 +94,17 @@ func (v *MachineVM) startUserModeNetworking() error {
|
|||||||
|
|
||||||
// Start or reuse
|
// Start or reuse
|
||||||
if !running {
|
if !running {
|
||||||
if err := v.launchUserModeNetDist(exe); err != nil {
|
if err := launchUserModeNetDist(exe); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := createUserModeResolvConf(toDist(v.Name)); err != nil {
|
if err := createUserModeResolvConf(machine.ToDist(mc.Name)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register in-use
|
// Register in-use
|
||||||
err = v.addUserModeNetEntry()
|
err = addUserModeNetEntry(mc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -111,23 +112,23 @@ func (v *MachineVM) startUserModeNetworking() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) stopUserModeNetworking(dist string) error {
|
func stopUserModeNetworking(mc *vmconfigs.MachineConfig) error {
|
||||||
if !v.UserModeNetworking {
|
if !mc.WSLHypervisor.UserModeNetworking {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
flock, err := v.obtainUserModeNetLock()
|
flock, err := obtainUserModeNetLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer flock.unlock()
|
defer flock.unlock()
|
||||||
|
|
||||||
err = v.removeUserModeNetEntry()
|
err = removeUserModeNetEntry(mc.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := v.cleanupAndCountNetEntries()
|
count, err := cleanupAndCountNetEntries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -159,7 +160,7 @@ func isGvProxyVMRunning() bool {
|
|||||||
return wslInvoke(userModeDist, "bash", "-c", "ps -eo args | grep -q -m1 ^/usr/local/bin/vm || exit 42") == nil
|
return wslInvoke(userModeDist, "bash", "-c", "ps -eo args | grep -q -m1 ^/usr/local/bin/vm || exit 42") == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) launchUserModeNetDist(exeFile string) error {
|
func launchUserModeNetDist(exeFile string) error {
|
||||||
fmt.Println("Starting user-mode networking...")
|
fmt.Println("Starting user-mode networking...")
|
||||||
|
|
||||||
exe, err := specgen.ConvertWinMountPath(exeFile)
|
exe, err := specgen.ConvertWinMountPath(exeFile)
|
||||||
@ -220,7 +221,7 @@ func createUserModeResolvConf(dist string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) getUserModeNetDir() (string, error) {
|
func getUserModeNetDir() (string, error) {
|
||||||
vmDataDir, err := machine.GetDataDir(vmtype)
|
vmDataDir, err := machine.GetDataDir(vmtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -234,8 +235,8 @@ func (v *MachineVM) getUserModeNetDir() (string, error) {
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) getUserModeNetEntriesDir() (string, error) {
|
func getUserModeNetEntriesDir() (string, error) {
|
||||||
netDir, err := v.getUserModeNetDir()
|
netDir, err := getUserModeNetDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -248,13 +249,13 @@ func (v *MachineVM) getUserModeNetEntriesDir() (string, error) {
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) addUserModeNetEntry() error {
|
func addUserModeNetEntry(mc *vmconfigs.MachineConfig) error {
|
||||||
entriesDir, err := v.getUserModeNetEntriesDir()
|
entriesDir, err := getUserModeNetEntriesDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(entriesDir, toDist(v.Name))
|
path := filepath.Join(entriesDir, machine.ToDist(mc.Name))
|
||||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not add user-mode networking registration: %w", err)
|
return fmt.Errorf("could not add user-mode networking registration: %w", err)
|
||||||
@ -263,18 +264,18 @@ func (v *MachineVM) addUserModeNetEntry() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) removeUserModeNetEntry() error {
|
func removeUserModeNetEntry(name string) error {
|
||||||
entriesDir, err := v.getUserModeNetEntriesDir()
|
entriesDir, err := getUserModeNetEntriesDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(entriesDir, toDist(v.Name))
|
path := filepath.Join(entriesDir, machine.ToDist(name))
|
||||||
return os.Remove(path)
|
return os.Remove(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) cleanupAndCountNetEntries() (uint, error) {
|
func cleanupAndCountNetEntries() (uint, error) {
|
||||||
entriesDir, err := v.getUserModeNetEntriesDir()
|
entriesDir, err := getUserModeNetEntriesDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -302,8 +303,8 @@ func (v *MachineVM) cleanupAndCountNetEntries() (uint, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) obtainUserModeNetLock() (*fileLock, error) {
|
func obtainUserModeNetLock() (*fileLock, error) {
|
||||||
dir, err := v.getUserModeNetDir()
|
dir, err := getUserModeNetDir()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
202
vendor/github.com/containers/winquit/LICENSE
generated
vendored
Normal file
202
vendor/github.com/containers/winquit/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
http://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.
|
50
vendor/github.com/containers/winquit/pkg/winquit/channels_windows.go
generated
vendored
Normal file
50
vendor/github.com/containers/winquit/pkg/winquit/channels_windows.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baseChannelType interface {
|
||||||
|
getKey() any
|
||||||
|
notifyNonBlocking()
|
||||||
|
notifyBlocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
type boolChannelType struct {
|
||||||
|
channel chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolChannelType) getKey() any {
|
||||||
|
return b.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolChannelType) notifyNonBlocking() {
|
||||||
|
select {
|
||||||
|
case b.channel <- true:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *boolChannelType) notifyBlocking() {
|
||||||
|
s.channel <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
type sigChannelType struct {
|
||||||
|
channel chan os.Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sigChannelType) getKey() any {
|
||||||
|
return s.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sigChannelType) notifyNonBlocking() {
|
||||||
|
select {
|
||||||
|
case s.channel <- syscall.SIGTERM:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sigChannelType) notifyBlocking() {
|
||||||
|
s.channel <- syscall.SIGTERM
|
||||||
|
}
|
31
vendor/github.com/containers/winquit/pkg/winquit/client.go
generated
vendored
Normal file
31
vendor/github.com/containers/winquit/pkg/winquit/client.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestQuit sends a Windows quit notification to the specified process id.
|
||||||
|
// Since communication is performed over the Win32 GUI messaging facilities,
|
||||||
|
// console applications may not respond, as they require special handling to do
|
||||||
|
// so. Additionally incorrectly written or buggy GUI applications may not listen
|
||||||
|
// or respond appropriately to the event.
|
||||||
|
//
|
||||||
|
// All applications, console or GUI, which use the notification mechanisms
|
||||||
|
// provided by this package (NotifyOnQuit, SimulateSigTermOnQuit) will react
|
||||||
|
// appropriately to the event sent by RequestQuit.
|
||||||
|
//
|
||||||
|
// Callers must have appropriate security permissions, otherwise an error will
|
||||||
|
// be returned. See the notes in the package documentation for more details.
|
||||||
|
func RequestQuit(pid int) error {
|
||||||
|
return requestQuit(pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuitProcess first sends a Windows quit notification to the specified process id,
|
||||||
|
// and waits, up the amount of time passed in the waitNicely argument, for it to
|
||||||
|
// exit. If the process does not exit in time, it is forcefully terminated.
|
||||||
|
//
|
||||||
|
// Callers must have appropriate security permissions, otherwise an error will
|
||||||
|
// be returned. See the notes in the package documentation for more details.
|
||||||
|
func QuitProcess(pid int, waitNicely time.Duration) error {
|
||||||
|
return quitProcess(pid, waitNicely)
|
||||||
|
}
|
17
vendor/github.com/containers/winquit/pkg/winquit/client_unsupported.go
generated
vendored
Normal file
17
vendor/github.com/containers/winquit/pkg/winquit/client_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requestQuit(pid int) error {
|
||||||
|
return fmt.Errorf("not implemented on non-Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func quitProcess(pid int, waitNicely time.Duration) error {
|
||||||
|
return fmt.Errorf("not implemented on non-Windows")
|
||||||
|
}
|
47
vendor/github.com/containers/winquit/pkg/winquit/client_windows.go
generated
vendored
Normal file
47
vendor/github.com/containers/winquit/pkg/winquit/client_windows.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/winquit/pkg/winquit/win32"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requestQuit(pid int) error {
|
||||||
|
threads, err := win32.GetProcThreads(uint32(pid))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, thread := range threads {
|
||||||
|
logrus.Debugf("Closing windows on thread %d", thread)
|
||||||
|
win32.CloseThreadWindows(uint32(thread))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func quitProcess(pid int, waitNicely time.Duration) error {
|
||||||
|
_ = RequestQuit(pid)
|
||||||
|
|
||||||
|
proc, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
proc.Wait()
|
||||||
|
done <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-time.After(waitNicely):
|
||||||
|
}
|
||||||
|
|
||||||
|
return proc.Kill()
|
||||||
|
}
|
135
vendor/github.com/containers/winquit/pkg/winquit/doc.go
generated
vendored
Normal file
135
vendor/github.com/containers/winquit/pkg/winquit/doc.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Package winquit supports graceful shutdown of Windows applications through
|
||||||
|
// the sending and receiving of Windows quit events on Win32 message queues.
|
||||||
|
// This allows golang applications to implement behavior comparable to SIGTERM
|
||||||
|
// signal handling on UNIX derived systems. Additionally, it supports the
|
||||||
|
// graceful shutdown mechanism employed by Windows system tools, such as
|
||||||
|
// taskkill. See the "How it works" section for more details.
|
||||||
|
//
|
||||||
|
// To aid application portability, and provide familiarity, the API follows a
|
||||||
|
// similar convention and approach as the os.signal package. Additionally, the
|
||||||
|
// SimulateSigTermOnQuit function supports reuse of the same underlying channel,
|
||||||
|
// supporting the blending of os.signal and winquit together (a subset of
|
||||||
|
// signals provided by os.signal are still relevant and desirable on Windows,
|
||||||
|
// for example, break handling in console applications).
|
||||||
|
//
|
||||||
|
// # Simple server example
|
||||||
|
//
|
||||||
|
// The following example demonstrates usage of NotifyOnQuit() to wait for a
|
||||||
|
// windows quit event before shutting down:
|
||||||
|
//
|
||||||
|
// func server() {
|
||||||
|
// fmt.Println("Starting server")
|
||||||
|
//
|
||||||
|
// // Create a channel, and register it
|
||||||
|
// done := make(chan bool, 1)
|
||||||
|
// winquit.NotifyOnQuit(done)
|
||||||
|
//
|
||||||
|
// // Wait until we receive a quit event
|
||||||
|
// <-done
|
||||||
|
//
|
||||||
|
// fmt.Println("Shutting down")
|
||||||
|
// // Perform cleanup tasks
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// # Blended signal example
|
||||||
|
//
|
||||||
|
// The following example demonstrates usage of SimulateSigTermOnQuit() in
|
||||||
|
// concert with signal.Notify():
|
||||||
|
//
|
||||||
|
// func server() {
|
||||||
|
// fmt.Println("Starting server")
|
||||||
|
//
|
||||||
|
// // Create a channel, and register it
|
||||||
|
// done := make(chan os.Signal, 1)
|
||||||
|
//
|
||||||
|
// // Wait on console interrupt events
|
||||||
|
// signal.Notify(done, syscall.SIGINT)
|
||||||
|
//
|
||||||
|
// // Simulate SIGTERM when a quit occurs
|
||||||
|
// winquit.SimulateSigTermOnQuit(done)
|
||||||
|
//
|
||||||
|
// // Wait until we receive a signal or quit event
|
||||||
|
// <-done
|
||||||
|
//
|
||||||
|
// fmt.Println("Shutting down")
|
||||||
|
// // Perform cleanup tasks
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// # Client example
|
||||||
|
//
|
||||||
|
// The following example demonstrates how an application can ask or
|
||||||
|
// force other windows programs to quit:
|
||||||
|
//
|
||||||
|
// func client() {
|
||||||
|
// // Ask nicely for program "one" to quit. This request may not
|
||||||
|
// // be honored if its a console application, or if the program
|
||||||
|
// // is hung
|
||||||
|
// if err := winquit.RequestQuit(pidOne); err != nil {
|
||||||
|
// fmt.Printf("error sending quit request, %s", err.Error())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Force program "two" to quit, but give it 20 seconds to
|
||||||
|
// // perform any cleanup tasks and quit on it's own
|
||||||
|
// timeout := time.Second * 20
|
||||||
|
// if err := winquit.QuitProcess(pidTwo, timeout); err != nil {
|
||||||
|
// fmt.Printf("error killing process, %s", err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// # How it works
|
||||||
|
//
|
||||||
|
// Windows GUI applications consist of multiple components (and windows) which
|
||||||
|
// intercommunicate with events over per-thread message queues and/or direct
|
||||||
|
// event handoff to window procedures for cross-thread communication.
|
||||||
|
// Additionally, GUI applications can use the same mechanism to communicate with
|
||||||
|
// windows and threads owned by other applications, including common desktop
|
||||||
|
// components.
|
||||||
|
//
|
||||||
|
// winquit utilizes this mechanism by creating a standard win32 message loop
|
||||||
|
// thread and registering a non-visible window to relay a quit message (WM_QUIT)
|
||||||
|
// in the event of a window close event. WM_CLOSE is sent by Windows in response
|
||||||
|
// to certain system events, or by other requesting applications. For example,
|
||||||
|
// the system provided taskkill.exe (similar to the kill command on Unix), works
|
||||||
|
// by iterating all windows on the system, and sending a WM_CLOSE when the
|
||||||
|
// process owner matches the specified pid. Note that, unlike UNIX/X11 style
|
||||||
|
// systems, on Windows the graphical APIs are built-in and accessible to all
|
||||||
|
// win32 applications, including console based applications. Therefore, the APIs
|
||||||
|
// provided by winquit *do not* require compilation as a windowsgui app to
|
||||||
|
// effectively use them.
|
||||||
|
//
|
||||||
|
// winquit also provides APIs to trigger a quit of another process using a
|
||||||
|
// WM_CLOSE event, although in a more efficient manner than taskkill.exe. It
|
||||||
|
// instead captures a thread snapshot of the target process (effectively a
|
||||||
|
// memory read on Windows), and enumerates each thread's associated Windows, and
|
||||||
|
// sending the event to each. In addition to supporting a graceful close of any
|
||||||
|
// Windows application, which may have multiple message loops, this approach
|
||||||
|
// also obviates the need for cumbersome approaches to lock code to the main
|
||||||
|
// thread of the application. The message loop used by winquit does not care
|
||||||
|
// which thread the golang runtime internally designates. Note that winquit
|
||||||
|
// purposefully relays through a thread's windows as opposed to posting directly
|
||||||
|
// to each thread's message queue, since the former is more likely to be
|
||||||
|
// expected by an application, and it ensures all window procedures have an
|
||||||
|
// opportunity to perform cleanup work not associated with the thread's message
|
||||||
|
// loop.
|
||||||
|
//
|
||||||
|
// # Limitations
|
||||||
|
//
|
||||||
|
// This API is only implemented on Windows platforms. Non-operational stubs
|
||||||
|
// are provided for compilation purposes.
|
||||||
|
//
|
||||||
|
// In addition to requiring appropriate security permissions (typically a user
|
||||||
|
// can only send events to other applications ran by the same user), Windows
|
||||||
|
// also restricts inter-app messaging operations to programs running in the same
|
||||||
|
// user logon session. While logons migrate between RDP and console sessions,
|
||||||
|
// non-graphical logins (e.g sshd) typically create a logon per connection. For
|
||||||
|
// this reason, tools like taskkill and winquit are normally disallowed from
|
||||||
|
// crossing this boundary. Therefore, a user will not be able to gracefully stop
|
||||||
|
// applications between ssh/winrm sessions, and in between ssh and graphical
|
||||||
|
// logons. However, the typical user use-case of logging into Windows and
|
||||||
|
// running multiple applications and terminals will work fine. Additionally,
|
||||||
|
// multiple back-grounded processes in the same ssh session will be able to
|
||||||
|
// communicate. Finally, it's possible to bypass this limitation by executing
|
||||||
|
// code under the system user using the SeTcbPrivilege. The psexec tool does
|
||||||
|
// exactly this, and can additionally be used as a workaround to this
|
||||||
|
// limitation.
|
||||||
|
package winquit
|
45
vendor/github.com/containers/winquit/pkg/winquit/server.go
generated
vendored
Normal file
45
vendor/github.com/containers/winquit/pkg/winquit/server.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotifyOnQuit relays a Windows quit notification to the boolean done channel.
|
||||||
|
// This is a one-shot operation (will only be delivered once), however multiple
|
||||||
|
// channels may be registered. Each registered channel is sent one copy of the
|
||||||
|
// same one-shot value.
|
||||||
|
//
|
||||||
|
// This function is a no-op on non-Windows platforms. While the call will
|
||||||
|
// succeed, no notifications will be delivered to the passed channel. Each
|
||||||
|
// channel will only ever receive a "true" value.
|
||||||
|
//
|
||||||
|
// It is recommended that registered channels establish a buffer of 1, since
|
||||||
|
// values are sent non-blocking. Blocking redelivery may be attempted to reduce
|
||||||
|
// the chance of bugs; however, it should not be relied upon.
|
||||||
|
//
|
||||||
|
// If this function is called after a Windows quit notification has occurred, it
|
||||||
|
// will immediately deliver a "true" value.
|
||||||
|
func NotifyOnQuit(done chan bool) {
|
||||||
|
notifyOnQuit(done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateSigTermOnQuit relays a Windows quit notification following the same
|
||||||
|
// semantics as NotifyOnQuit; however, instead of a boolean message value, this
|
||||||
|
// function will send a SIGTERM signal to the passed channel.
|
||||||
|
//
|
||||||
|
// This function allows for the reuse of the same underlying channel used with
|
||||||
|
// in a separate os.signal.Notify method call.
|
||||||
|
func SimulateSigTermOnQuit(handler chan os.Signal) {
|
||||||
|
simulateSigTermOnQuit(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the thread id of the message loop thread created by winquit, or "0"
|
||||||
|
// if one is not running. The latter indicates a mistake, as this function
|
||||||
|
// should only be called after a call to one of the _OnQuit functions.
|
||||||
|
//
|
||||||
|
// In most cases this method should not be necessary, as RequestQuit and
|
||||||
|
// QuitProcess do not require it. It is primarily provided to enable legacy
|
||||||
|
// patterns that serialize the thread id for later direct signaling.
|
||||||
|
func GetCurrentMessageLoopThreadId() uint32 {
|
||||||
|
return getCurrentMessageLoopThreadId()
|
||||||
|
}
|
18
vendor/github.com/containers/winquit/pkg/winquit/server_unsupported.go
generated
vendored
Normal file
18
vendor/github.com/containers/winquit/pkg/winquit/server_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func notifyOnQuit(done chan bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateSigTermOnQuit(handler chan os.Signal) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentMessageLoopThreadId() uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
147
vendor/github.com/containers/winquit/pkg/winquit/server_windows.go
generated
vendored
Normal file
147
vendor/github.com/containers/winquit/pkg/winquit/server_windows.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package winquit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/winquit/pkg/winquit/win32"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type receiversType struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
result bool
|
||||||
|
channels map[any]baseChannelType
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
receivers *receiversType = &receiversType{
|
||||||
|
channels: make(map[any]baseChannelType),
|
||||||
|
}
|
||||||
|
|
||||||
|
loopInit sync.Once
|
||||||
|
loopTid uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *receiversType) add(channel baseChannelType) {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
|
||||||
|
if _, ok := r.channels[channel.getKey()]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.result {
|
||||||
|
go func() {
|
||||||
|
channel.notifyBlocking()
|
||||||
|
}()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.channels[channel.getKey()] = channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *receiversType) notifyAll() {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
r.result = true
|
||||||
|
for _, channel := range r.channels {
|
||||||
|
channel.notifyNonBlocking()
|
||||||
|
delete(r.channels, channel.getKey())
|
||||||
|
}
|
||||||
|
for _, channel := range r.channels {
|
||||||
|
channel.notifyBlocking()
|
||||||
|
delete(r.channels, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLoop() {
|
||||||
|
loopInit.Do(func() {
|
||||||
|
go messageLoop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyOnQuit(done chan bool) {
|
||||||
|
receivers.add(&boolChannelType{done})
|
||||||
|
initLoop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateSigTermOnQuit(handler chan os.Signal) {
|
||||||
|
receivers.add(&sigChannelType{handler})
|
||||||
|
initLoop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentMessageLoopThreadId() uint32 {
|
||||||
|
return loopTid
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageLoop() {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
loopTid = windows.GetCurrentThreadId()
|
||||||
|
registerDummyWindow()
|
||||||
|
|
||||||
|
logrus.Debug("Entering loop for quit")
|
||||||
|
for {
|
||||||
|
ret, msg, err := win32.GetMessage(0, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error receiving win32 message, %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ret == 0 {
|
||||||
|
logrus.Debug("Received QUIT notification")
|
||||||
|
receivers.notifyAll()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Unhandled message: %d", msg.Message)
|
||||||
|
win32.TranslateMessage(msg)
|
||||||
|
win32.DispatchMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAppName() (string, error) {
|
||||||
|
exeName, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
suffix := filepath.Ext(exeName)
|
||||||
|
return strings.TrimSuffix(filepath.Base(exeName), suffix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerDummyWindow() error {
|
||||||
|
var app syscall.Handle
|
||||||
|
var err error
|
||||||
|
|
||||||
|
app, err = win32.GetModuleHandle("")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
appName, err := getAppName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
className := appName + "-rclass"
|
||||||
|
winName := appName + "-root"
|
||||||
|
|
||||||
|
_, err = win32.RegisterDummyWinClass(className, app)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = win32.CreateDummyWindow(winName, className, app)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
17
vendor/github.com/containers/winquit/pkg/winquit/win32/common.go
generated
vendored
Normal file
17
vendor/github.com/containers/winquit/pkg/winquit/win32/common.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package win32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ERROR_NO_MORE_ITEMS = 259
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
user32 = syscall.NewLazyDLL("user32.dll")
|
||||||
|
)
|
4
vendor/github.com/containers/winquit/pkg/winquit/win32/common_unsupported.go
generated
vendored
Normal file
4
vendor/github.com/containers/winquit/pkg/winquit/win32/common_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package win32
|
87
vendor/github.com/containers/winquit/pkg/winquit/win32/msg.go
generated
vendored
Normal file
87
vendor/github.com/containers/winquit/pkg/winquit/win32/msg.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package win32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MSG struct {
|
||||||
|
HWnd uintptr
|
||||||
|
Message uint32
|
||||||
|
WParam uintptr
|
||||||
|
LParam uintptr
|
||||||
|
Time uint32
|
||||||
|
Pt struct{ X, Y int32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
WM_QUIT = 0x12
|
||||||
|
WM_DESTROY = 0x02
|
||||||
|
WM_CLOSE = 0x10
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
postQuitMessage = user32.NewProc("PostQuitMessage")
|
||||||
|
procGetMessage = user32.NewProc("GetMessageW")
|
||||||
|
procTranslateMessage = user32.NewProc("TranslateMessage")
|
||||||
|
procDispatchMessage = user32.NewProc("DispatchMessageW")
|
||||||
|
procSendMessage = user32.NewProc("SendMessageW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TranslateMessage(msg *MSG) bool {
|
||||||
|
ret, _, _ :=
|
||||||
|
procTranslateMessage.Call( // BOOL TranslateMessage()
|
||||||
|
uintptr(unsafe.Pointer(msg)), // [in] const MSG *lpMsg
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret != 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func DispatchMessage(msg *MSG) uintptr {
|
||||||
|
ret, _, _ :=
|
||||||
|
procDispatchMessage.Call( // LRESULT DispatchMessage()
|
||||||
|
uintptr(unsafe.Pointer(msg)), // [in] const MSG *lpMsg
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendMessage(handle syscall.Handle, message uint, wparm uintptr, lparam uintptr) uintptr {
|
||||||
|
ret, _, _ :=
|
||||||
|
procSendMessage.Call( // LRESULT SendMessage()
|
||||||
|
uintptr(handle), // [in] HWND hWnd
|
||||||
|
uintptr(message), // [in] UINT Msg
|
||||||
|
wparm, // [in] WPARAM wParam
|
||||||
|
lparam, // [in] LPARAM lParam
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostQuitMessage(code int) {
|
||||||
|
_, _, _ =
|
||||||
|
postQuitMessage.Call( // void PostQuitMessage()
|
||||||
|
uintptr(code), // [in] int nExitCode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMessage(handle syscall.Handle, int, max int) (int32, *MSG, error) {
|
||||||
|
var msg MSG
|
||||||
|
ret, _, err :=
|
||||||
|
procGetMessage.Call( // // BOOL GetMessage()
|
||||||
|
uintptr(unsafe.Pointer(&msg)), // [out] LPMSG lpMsg,
|
||||||
|
uintptr(handle), // [in, optional] HWND hWnd,
|
||||||
|
0, // [in] UINT wMsgFilterMin,
|
||||||
|
0, // [in] UINT wMsgFilterMax
|
||||||
|
)
|
||||||
|
|
||||||
|
if int32(ret) == -1 {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(ret), &msg, nil
|
||||||
|
}
|
59
vendor/github.com/containers/winquit/pkg/winquit/win32/proc.go
generated
vendored
Normal file
59
vendor/github.com/containers/winquit/pkg/winquit/win32/proc.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package win32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAXIMUM_ALLOWED = 0x02000000
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procOpenProcess = kernel32.NewProc("OpenProcess")
|
||||||
|
procCloseHandle = kernel32.NewProc("CloseHandle")
|
||||||
|
procGetModuleHandle = kernel32.NewProc("GetModuleHandleW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func OpenProcess(pid uint32) (syscall.Handle, error) {
|
||||||
|
ret, _, err :=
|
||||||
|
procOpenProcess.Call( // HANDLE OpenProcess()
|
||||||
|
MAXIMUM_ALLOWED, // [in] DWORD dwDesiredAccess,
|
||||||
|
0, // [in] BOOL bInheritHandle,
|
||||||
|
uintptr(pid), // [in] DWORD dwProcessId
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Handle(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseHandle(handle syscall.Handle) error {
|
||||||
|
ret, _, err :=
|
||||||
|
procCloseHandle.Call( // BOOL CloseHandle()
|
||||||
|
uintptr(handle), // [in] HANDLE hObject
|
||||||
|
)
|
||||||
|
if ret != 0 {
|
||||||
|
return fmt.Errorf("error closing handle: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProcThreads(pid uint32) ([]uint, error) {
|
||||||
|
process, err := OpenProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = CloseHandle(process)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return GetProcThreadIds(process)
|
||||||
|
}
|
160
vendor/github.com/containers/winquit/pkg/winquit/win32/pss.go
generated
vendored
Normal file
160
vendor/github.com/containers/winquit/pkg/winquit/win32/pss.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package win32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PSS_THREAD_ENTRY struct {
|
||||||
|
ExitStatus uint32
|
||||||
|
TebBaseAddress uintptr
|
||||||
|
ProcessId uint32
|
||||||
|
ThreadId uint32
|
||||||
|
AffinityMask uintptr
|
||||||
|
Priority int32
|
||||||
|
BasePriority int32
|
||||||
|
LastSyscallFirstArgument uintptr
|
||||||
|
LastSyscallNumber uint16
|
||||||
|
CreateTime uint64
|
||||||
|
ExitTime uint64
|
||||||
|
KernelTime uint64
|
||||||
|
UserTime uint64
|
||||||
|
Win32StartAddress uintptr
|
||||||
|
CaptureTime uint64
|
||||||
|
Flags uint32
|
||||||
|
SuspendCount uint16
|
||||||
|
SizeOfContextRecord uint16
|
||||||
|
ContextRecord uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PSS_CAPTURE_THREADS = 0x00000080
|
||||||
|
PSS_WALK_THREADS = 3
|
||||||
|
PSS_QUERY_THREAD_INFORMATION = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procPssCaptureSnapshot = kernel32.NewProc("PssCaptureSnapshot")
|
||||||
|
procPssFreeSnapshot = kernel32.NewProc("PssFreeSnapshot")
|
||||||
|
procPssWalkMarkerCreate = kernel32.NewProc("PssWalkMarkerCreate")
|
||||||
|
procPssWalkMarkerFree = kernel32.NewProc("PssWalkMarkerFree")
|
||||||
|
procPssWalkSnapshot = kernel32.NewProc("PssWalkSnapshot")
|
||||||
|
)
|
||||||
|
|
||||||
|
func PssCaptureSnapshot(process syscall.Handle, flags int32, contextFlags int32) (syscall.Handle, error) {
|
||||||
|
var snapshot syscall.Handle
|
||||||
|
ret, _, err :=
|
||||||
|
procPssCaptureSnapshot.Call( // DWORD PssCaptureSnapshot()
|
||||||
|
uintptr(process), // [in] HANDLE ProcessHandle,
|
||||||
|
uintptr(flags), // [in] PSS_CAPTURE_FLAGS CaptureFlags,
|
||||||
|
uintptr(contextFlags), // [in, optional] DWORD ThreadContextFlags,
|
||||||
|
uintptr(unsafe.Pointer(&snapshot)), // [out] HPSS *SnapshotHandle
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret != 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PssFreeSnapshot(process syscall.Handle, snapshot syscall.Handle) error {
|
||||||
|
ret, _, _ :=
|
||||||
|
procPssFreeSnapshot.Call( // DWORD PssFreeSnapshot()
|
||||||
|
uintptr(process), // [in] HANDLE ProcessHandle,
|
||||||
|
uintptr(snapshot), // [in] HPSS SnapshotHandle
|
||||||
|
)
|
||||||
|
if ret != 0 {
|
||||||
|
return fmt.Errorf("error freeing snapshot: %d", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PssWalkMarkerCreate() (syscall.Handle, error) {
|
||||||
|
var walkptr uintptr
|
||||||
|
|
||||||
|
ret, _, _ :=
|
||||||
|
procPssWalkMarkerCreate.Call( // // DWORD PssWalkMarkerCreate()
|
||||||
|
0, // [in, optional] PSS_ALLOCATOR const *Allocator
|
||||||
|
uintptr(unsafe.Pointer(&walkptr)), // [out] HPSSWALK *WalkMarkerHandle
|
||||||
|
)
|
||||||
|
if ret != 0 {
|
||||||
|
return 0, fmt.Errorf("error creating process walker mark: %d", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Handle(walkptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PssWalkMarkerFree(handle syscall.Handle) error {
|
||||||
|
ret, _, _ :=
|
||||||
|
procPssWalkMarkerFree.Call( // DWORD PssWalkMarkerFree()
|
||||||
|
uintptr(handle), // [in] HPSSWALK WalkMarkerHandle
|
||||||
|
)
|
||||||
|
if ret != 0 {
|
||||||
|
return fmt.Errorf("error freeing process walker mark: %d", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PssWalkThreadSnapshot(snapshot syscall.Handle, marker syscall.Handle) (*PSS_THREAD_ENTRY, error) {
|
||||||
|
var thread PSS_THREAD_ENTRY
|
||||||
|
ret, _, err :=
|
||||||
|
procPssWalkSnapshot.Call( // // DWORD PssWalkSnapshot()
|
||||||
|
uintptr(snapshot), // [in] HPSS SnapshotHandle,
|
||||||
|
PSS_WALK_THREADS, // [in] PSS_WALK_INFORMATION_CLASS InformationClass,
|
||||||
|
uintptr(marker), // [in] HPSSWALK WalkMarkerHandle,
|
||||||
|
uintptr(unsafe.Pointer(&thread)), // [out] void *Buffer,
|
||||||
|
unsafe.Sizeof(thread), // [in] DWORD BufferLength
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret == ERROR_NO_MORE_ITEMS {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret != 0 {
|
||||||
|
return nil, fmt.Errorf("error waling thread snapshot: %d (%w)", ret, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &thread, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProcThreadIds(process syscall.Handle) ([]uint, error) {
|
||||||
|
snapshot, err := PssCaptureSnapshot(process, PSS_CAPTURE_THREADS, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = PssFreeSnapshot(process, snapshot)
|
||||||
|
}()
|
||||||
|
|
||||||
|
marker, err := PssWalkMarkerCreate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = PssWalkMarkerFree(marker)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var threads []uint
|
||||||
|
|
||||||
|
for {
|
||||||
|
thread, err := PssWalkThreadSnapshot(snapshot, marker)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if thread == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
threads = append(threads, uint(thread.ThreadId))
|
||||||
|
}
|
||||||
|
|
||||||
|
return threads, nil
|
||||||
|
}
|
162
vendor/github.com/containers/winquit/pkg/winquit/win32/win.go
generated
vendored
Normal file
162
vendor/github.com/containers/winquit/pkg/winquit/win32/win.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package win32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WNDCLASSEX struct {
|
||||||
|
cbSize uint32
|
||||||
|
style uint32
|
||||||
|
lpfnWndProc uintptr
|
||||||
|
cbClsExtra int32
|
||||||
|
cbWndExtra int32
|
||||||
|
hInstance syscall.Handle
|
||||||
|
hIcon syscall.Handle
|
||||||
|
hCursor syscall.Handle
|
||||||
|
hbrBackground syscall.Handle
|
||||||
|
menuName *uint16
|
||||||
|
className *uint16
|
||||||
|
hIconSm syscall.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
COLOR_WINDOW = 0x05
|
||||||
|
CW_USEDEFAULT = ^0x7fffffff
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procEnumThreadWindows = user32.NewProc("EnumThreadWindows")
|
||||||
|
procRegisterClassEx = user32.NewProc("RegisterClassExW")
|
||||||
|
procCreateWindowEx = user32.NewProc("CreateWindowExW")
|
||||||
|
procDefWinProc = user32.NewProc("DefWindowProcW")
|
||||||
|
|
||||||
|
callbackEnumThreadWindows = syscall.NewCallback(wndProcCloseWindow)
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefWindowProc(hWnd syscall.Handle, msg uint32, wParam uintptr, lParam uintptr) int32 {
|
||||||
|
|
||||||
|
ret, _, _ :=
|
||||||
|
procDefWinProc.Call( // LRESULT DefWindowProcW()
|
||||||
|
uintptr(hWnd), // [in] HWND hWnd,
|
||||||
|
uintptr(msg), // [in] UINT Msg,
|
||||||
|
wParam, // [in] WPARAM wParam,
|
||||||
|
lParam, // [in] LPARAM lParam
|
||||||
|
)
|
||||||
|
return int32(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetModuleHandle(name string) (syscall.Handle, error) {
|
||||||
|
var name16 *uint16
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(name) > 0 {
|
||||||
|
if name16, err = syscall.UTF16PtrFromString(name); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, _, err :=
|
||||||
|
procGetModuleHandle.Call( // HMODULE GetModuleHandleW()
|
||||||
|
uintptr(unsafe.Pointer(name16)), // [in, optional] LPCWSTR lpModuleName
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
return 0, fmt.Errorf("could not obtain module handle: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Handle(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterClassEx(class *WNDCLASSEX) (uint16, error) {
|
||||||
|
|
||||||
|
ret, _, err :=
|
||||||
|
procRegisterClassEx.Call( // ATOM RegisterClassExW()
|
||||||
|
uintptr(unsafe.Pointer(class)), // [in] const WNDCLASSEXW *unnamedParam1
|
||||||
|
)
|
||||||
|
if ret == 0 {
|
||||||
|
return 0, fmt.Errorf("could not register window class: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint16(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wndProc(hWnd syscall.Handle, msg uint32, wParam uintptr, lParam uintptr) uintptr {
|
||||||
|
switch msg {
|
||||||
|
case WM_DESTROY:
|
||||||
|
PostQuitMessage(0)
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return uintptr(DefWindowProc(hWnd, msg, wParam, lParam))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseThreadWindows(threadId uint32) {
|
||||||
|
_, _, _ =
|
||||||
|
procEnumThreadWindows.Call( // // BOOL EnumThreadWindows()
|
||||||
|
uintptr(threadId), // [in] DWORD dwThreadId,
|
||||||
|
callbackEnumThreadWindows, // [in] WNDENUMPROC lpfn,
|
||||||
|
0, // [in] LPARAM lParam
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wndProcCloseWindow(hwnd uintptr, lparam uintptr) uintptr {
|
||||||
|
SendMessage(syscall.Handle(hwnd), WM_CLOSE, 0, 0)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterDummyWinClass(name string, appInstance syscall.Handle) (uint16, error) {
|
||||||
|
var class16 *uint16
|
||||||
|
var err error
|
||||||
|
if class16, err = syscall.UTF16PtrFromString(name); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
class := WNDCLASSEX{
|
||||||
|
className: class16,
|
||||||
|
hInstance: appInstance,
|
||||||
|
lpfnWndProc: syscall.NewCallback(wndProc),
|
||||||
|
}
|
||||||
|
|
||||||
|
class.cbSize = uint32(unsafe.Sizeof(class))
|
||||||
|
|
||||||
|
return RegisterClassEx(&class)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateDummyWindow(name string, className string, appInstance syscall.Handle) (syscall.Handle, error) {
|
||||||
|
var name16, class16 *uint16
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cwDefault := CW_USEDEFAULT
|
||||||
|
|
||||||
|
if name16, err = syscall.UTF16PtrFromString(name); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if class16, err = syscall.UTF16PtrFromString(className); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
ret, _, err := procCreateWindowEx.Call( //HWND CreateWindowExW(
|
||||||
|
0, // [in] DWORD dwExStyle,
|
||||||
|
uintptr(unsafe.Pointer(class16)), // [in, optional] LPCWSTR lpClassName,
|
||||||
|
uintptr(unsafe.Pointer(name16)), // [in, optional] LPCWSTR lpWindowName,
|
||||||
|
0, // [in] DWORD dwStyle,
|
||||||
|
uintptr(cwDefault), // [in] int X,
|
||||||
|
uintptr(cwDefault), // [in] int Y,
|
||||||
|
0, // [in] int nWidth,
|
||||||
|
0, // [in] int nHeight,
|
||||||
|
0, // [in, optional] HWND hWndParent,
|
||||||
|
0, // [in, optional] HMENU hMenu,
|
||||||
|
uintptr(appInstance), // [in, optional] HINSTANCE hInstance,
|
||||||
|
0, // [in, optional] LPVOID lpParam
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
return 0, fmt.Errorf("could not create window: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Handle(ret), nil
|
||||||
|
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -401,6 +401,10 @@ github.com/containers/storage/pkg/tarlog
|
|||||||
github.com/containers/storage/pkg/truncindex
|
github.com/containers/storage/pkg/truncindex
|
||||||
github.com/containers/storage/pkg/unshare
|
github.com/containers/storage/pkg/unshare
|
||||||
github.com/containers/storage/types
|
github.com/containers/storage/types
|
||||||
|
# github.com/containers/winquit v1.1.0
|
||||||
|
## explicit; go 1.19
|
||||||
|
github.com/containers/winquit/pkg/winquit
|
||||||
|
github.com/containers/winquit/pkg/winquit/win32
|
||||||
# github.com/coreos/go-oidc/v3 v3.9.0
|
# github.com/coreos/go-oidc/v3 v3.9.0
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/coreos/go-oidc/v3/oidc
|
github.com/coreos/go-oidc/v3/oidc
|
||||||
|
Reference in New Issue
Block a user