forked from containers/podman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch to a new installer approach using a path manipulation helper
Fixes containers#11089 - cleanup PATH on MSI uninstall Additionally fixes scenarios where the path can be overwritten by setx Also removes the console flash, since the helper is built as a silent gui Helper executable can be rerun by user to repair PATHs broken by other tools Utilizes executable location instead of passed parameters to remove delicate escaping requirements [NO NEW TESTS NEEDED] Signed-off-by: Jason T. Greene <[email protected]>
- 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.