mirror of
https://github.com/containers/podman.git
synced 2025-05-19 16:18:51 +08:00

* Replace "setup", "lookup", "cleanup", "backup" with "set up", "look up", "clean up", "back up" when used as verbs. Replace also variations of those. * Improve language in a few places. Signed-off-by: Erik Sjölund <erik.sjolund@gmail.com>
203 lines
4.8 KiB
Go
203 lines
4.8 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/registry"
|
|
)
|
|
|
|
type operation int
|
|
|
|
const (
|
|
HWND_BROADCAST = 0xFFFF
|
|
WM_SETTINGCHANGE = 0x001A
|
|
SMTO_ABORTIFHUNG = 0x0002
|
|
ERR_BAD_ARGS = 0x000A
|
|
OPERATION_FAILED = 0x06AC
|
|
Environment = "Environment"
|
|
Add operation = iota
|
|
Remove
|
|
Open
|
|
NotSpecified
|
|
)
|
|
|
|
func main() {
|
|
op := NotSpecified
|
|
if len(os.Args) >= 2 {
|
|
switch os.Args[1] {
|
|
case "add":
|
|
op = Add
|
|
case "remove":
|
|
op = Remove
|
|
case "open":
|
|
op = Open
|
|
}
|
|
}
|
|
|
|
// Stay silent since ran from an installer
|
|
if op == NotSpecified {
|
|
alert("Usage: " + filepath.Base(os.Args[0]) + " [add|remove]\n\nThis utility adds or removes the podman directory to the Windows Path.")
|
|
os.Exit(ERR_BAD_ARGS)
|
|
}
|
|
|
|
// Hidden operation as a workaround for the installer
|
|
if op == Open && len(os.Args) > 2 {
|
|
if err := winOpenFile(os.Args[2]); err != nil {
|
|
os.Exit(OPERATION_FAILED)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
if err := modify(op); err != nil {
|
|
os.Exit(OPERATION_FAILED)
|
|
}
|
|
}
|
|
|
|
func modify(op operation) error {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
exe, err = filepath.EvalSymlinks(exe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target := filepath.Dir(exe)
|
|
|
|
if op == Remove {
|
|
return removePathFromRegistry(target)
|
|
}
|
|
|
|
return addPathToRegistry(target)
|
|
}
|
|
|
|
// Appends a directory to the Windows Path stored in the registry
|
|
func addPathToRegistry(dir string) error {
|
|
k, _, err := registry.CreateKey(registry.CURRENT_USER, Environment, registry.WRITE|registry.READ)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer k.Close()
|
|
|
|
existing, typ, err := k.GetStringValue("Path")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Is this directory already on the windows path?
|
|
for _, element := range strings.Split(existing, ";") {
|
|
if strings.EqualFold(element, dir) {
|
|
// Path already added
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// If the existing path is empty we don't want to start with a delimiter
|
|
if len(existing) > 0 {
|
|
existing += ";"
|
|
}
|
|
|
|
existing += dir
|
|
|
|
// It's important to preserve the registry key type so that it will be interpreted correctly
|
|
// EXPAND = evaluate variables in the expression, e.g. %PATH% should be expanded to the system path
|
|
// STRING = treat the contents as a string literal
|
|
if typ == registry.EXPAND_SZ {
|
|
err = k.SetExpandStringValue("Path", existing)
|
|
} else {
|
|
err = k.SetStringValue("Path", existing)
|
|
}
|
|
|
|
if err == nil {
|
|
broadcastEnvironmentChange()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Removes all occurrences of a directory path from the Windows path stored in the registry
|
|
func removePathFromRegistry(path string) error {
|
|
k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE)
|
|
if err != nil {
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
// Nothing to clean up, the Environment registry key does not exist.
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
defer k.Close()
|
|
|
|
existing, typ, err := k.GetStringValue("Path")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var elements []string
|
|
for _, element := range strings.Split(existing, ";") {
|
|
if strings.EqualFold(element, path) {
|
|
continue
|
|
}
|
|
elements = append(elements, element)
|
|
}
|
|
|
|
newPath := strings.Join(elements, ";")
|
|
// Preserve value type (see corresponding comment above)
|
|
if typ == registry.EXPAND_SZ {
|
|
err = k.SetExpandStringValue("Path", newPath)
|
|
} else {
|
|
err = k.SetStringValue("Path", newPath)
|
|
}
|
|
|
|
if err == nil {
|
|
broadcastEnvironmentChange()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Sends a notification message to all top level windows informing them the environmental settings have changed.
|
|
// Applications such as the Windows command prompt and powershell will know to stop caching stale values on
|
|
// subsequent restarts. Since applications block the sender when receiving a message, we set a 3 second timeout
|
|
func broadcastEnvironmentChange() {
|
|
env, _ := syscall.UTF16PtrFromString(Environment)
|
|
user32 := syscall.NewLazyDLL("user32")
|
|
proc := user32.NewProc("SendMessageTimeoutW")
|
|
millis := 3000
|
|
_, _, _ = proc.Call(HWND_BROADCAST, WM_SETTINGCHANGE, 0, uintptr(unsafe.Pointer(env)), SMTO_ABORTIFHUNG, uintptr(millis), 0)
|
|
}
|
|
|
|
// Creates an "error" style pop-up window
|
|
func alert(caption string) int {
|
|
// Error box style
|
|
format := 0x10
|
|
|
|
user32 := syscall.NewLazyDLL("user32.dll")
|
|
captionPtr, _ := syscall.UTF16PtrFromString(caption)
|
|
titlePtr, _ := syscall.UTF16PtrFromString("winpath")
|
|
ret, _, _ := user32.NewProc("MessageBoxW").Call(
|
|
uintptr(0),
|
|
uintptr(unsafe.Pointer(captionPtr)),
|
|
uintptr(unsafe.Pointer(titlePtr)),
|
|
uintptr(format))
|
|
|
|
return int(ret)
|
|
}
|
|
|
|
func winOpenFile(file string) error {
|
|
verb, _ := syscall.UTF16PtrFromString("open")
|
|
fileW, _ := syscall.UTF16PtrFromString(file)
|
|
return windows.ShellExecute(0, verb, fileW, nil, nil, windows.SW_NORMAL)
|
|
}
|