-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12597 from n1hility/msi-winpath
[Fixes #11089] Switch to a new installer approach using a path manipulation helper
- Loading branch information
Showing
9 changed files
with
958 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package main | ||
|
||
import ( | ||
"errors" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
"unsafe" | ||
|
||
"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 | ||
NotSpecified | ||
) | ||
|
||
func main() { | ||
op := NotSpecified | ||
if len(os.Args) >= 2 { | ||
switch os.Args[1] { | ||
case "add": | ||
op = Add | ||
case "remove": | ||
op = Remove | ||
} | ||
} | ||
|
||
// 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) | ||
} | ||
|
||
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 occurences 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 cleanup, 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 setings 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.