podman

Форк
0
/
main.go 
210 строк · 4.9 Кб
1
//go:build windows
2

3
package main
4

5
import (
6
	"errors"
7
	"io/fs"
8
	"os"
9
	"path/filepath"
10
	"strings"
11
	"syscall"
12
	"unsafe"
13

14
	"golang.org/x/sys/windows"
15
	"golang.org/x/sys/windows/registry"
16
)
17

18
type operation int
19

20
const (
21
	//nolint:stylecheck
22
	HWND_BROADCAST = 0xFFFF
23
	//nolint:stylecheck
24
	WM_SETTINGCHANGE = 0x001A
25
	//nolint:stylecheck
26
	SMTO_ABORTIFHUNG = 0x0002
27
	//nolint:stylecheck
28
	ERR_BAD_ARGS = 0x000A
29
	//nolint:stylecheck
30
	OPERATION_FAILED = 0x06AC
31

32
	Environment           = "Environment"
33
	Add         operation = iota
34
	Remove
35
	Open
36
	NotSpecified
37
)
38

39
func main() {
40
	op := NotSpecified
41
	if len(os.Args) >= 2 {
42
		switch os.Args[1] {
43
		case "add":
44
			op = Add
45
		case "remove":
46
			op = Remove
47
		case "open":
48
			op = Open
49
		}
50
	}
51

52
	// Stay silent since ran from an installer
53
	if op == NotSpecified {
54
		alert("Usage: " + filepath.Base(os.Args[0]) + " [add|remove]\n\nThis utility adds or removes the podman directory to the Windows Path.")
55
		os.Exit(ERR_BAD_ARGS)
56
	}
57

58
	// Hidden operation as a workaround for the installer
59
	if op == Open && len(os.Args) > 2 {
60
		if err := winOpenFile(os.Args[2]); err != nil {
61
			os.Exit(OPERATION_FAILED)
62
		}
63
		os.Exit(0)
64
	}
65

66
	if err := modify(op); err != nil {
67
		os.Exit(OPERATION_FAILED)
68
	}
69
}
70

71
func modify(op operation) error {
72
	exe, err := os.Executable()
73
	if err != nil {
74
		return err
75
	}
76
	exe, err = filepath.EvalSymlinks(exe)
77
	if err != nil {
78
		return err
79
	}
80
	target := filepath.Dir(exe)
81

82
	if op == Remove {
83
		return removePathFromRegistry(target)
84
	}
85

86
	return addPathToRegistry(target)
87
}
88

89
// Appends a directory to the Windows Path stored in the registry
90
func addPathToRegistry(dir string) error {
91
	k, _, err := registry.CreateKey(registry.CURRENT_USER, Environment, registry.WRITE|registry.READ)
92
	if err != nil {
93
		return err
94
	}
95

96
	defer k.Close()
97

98
	existing, typ, err := k.GetStringValue("Path")
99
	if err != nil {
100
		return err
101
	}
102

103
	// Is this directory already on the windows path?
104
	for _, element := range strings.Split(existing, ";") {
105
		if strings.EqualFold(element, dir) {
106
			// Path already added
107
			return nil
108
		}
109
	}
110

111
	// If the existing path is empty we don't want to start with a delimiter
112
	if len(existing) > 0 {
113
		existing += ";"
114
	}
115

116
	existing += dir
117

118
	// It's important to preserve the registry key type so that it will be interpreted correctly
119
	// EXPAND = evaluate variables in the expression, e.g. %PATH% should be expanded to the system path
120
	// STRING = treat the contents as a string literal
121
	if typ == registry.EXPAND_SZ {
122
		err = k.SetExpandStringValue("Path", existing)
123
	} else {
124
		err = k.SetStringValue("Path", existing)
125
	}
126

127
	if err == nil {
128
		broadcastEnvironmentChange()
129
	}
130

131
	return err
132
}
133

134
// Removes all occurrences of a directory path from the Windows path stored in the registry
135
func removePathFromRegistry(path string) error {
136
	k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE)
137
	if err != nil {
138
		if errors.Is(err, fs.ErrNotExist) {
139
			// Nothing to clean up, the Environment registry key does not exist.
140
			return nil
141
		}
142
		return err
143
	}
144

145
	defer k.Close()
146

147
	existing, typ, err := k.GetStringValue("Path")
148
	if err != nil {
149
		return err
150
	}
151

152
	// No point preallocating we can't know how big the array needs to be.
153
	//nolint:prealloc
154
	var elements []string
155
	for _, element := range strings.Split(existing, ";") {
156
		if strings.EqualFold(element, path) {
157
			continue
158
		}
159
		elements = append(elements, element)
160
	}
161

162
	newPath := strings.Join(elements, ";")
163
	// Preserve value type (see corresponding comment above)
164
	if typ == registry.EXPAND_SZ {
165
		err = k.SetExpandStringValue("Path", newPath)
166
	} else {
167
		err = k.SetStringValue("Path", newPath)
168
	}
169

170
	if err == nil {
171
		broadcastEnvironmentChange()
172
	}
173

174
	return err
175
}
176

177
// Sends a notification message to all top level windows informing them the environmental settings have changed.
178
// Applications such as the Windows command prompt and powershell will know to stop caching stale values on
179
// subsequent restarts. Since applications block the sender when receiving a message, we set a 3 second timeout
180
func broadcastEnvironmentChange() {
181
	env, _ := syscall.UTF16PtrFromString(Environment)
182
	user32 := syscall.NewLazyDLL("user32")
183
	proc := user32.NewProc("SendMessageTimeoutW")
184
	millis := 3000
185
	//nolint:dogsled
186
	_, _, _ = proc.Call(HWND_BROADCAST, WM_SETTINGCHANGE, 0, uintptr(unsafe.Pointer(env)), SMTO_ABORTIFHUNG, uintptr(millis), 0)
187
}
188

189
// Creates an "error" style pop-up window
190
func alert(caption string) int {
191
	// Error box style
192
	format := 0x10
193

194
	user32 := syscall.NewLazyDLL("user32.dll")
195
	captionPtr, _ := syscall.UTF16PtrFromString(caption)
196
	titlePtr, _ := syscall.UTF16PtrFromString("winpath")
197
	ret, _, _ := user32.NewProc("MessageBoxW").Call(
198
		uintptr(0),
199
		uintptr(unsafe.Pointer(captionPtr)),
200
		uintptr(unsafe.Pointer(titlePtr)),
201
		uintptr(format))
202

203
	return int(ret)
204
}
205

206
func winOpenFile(file string) error {
207
	verb, _ := syscall.UTF16PtrFromString("open")
208
	fileW, _ := syscall.UTF16PtrFromString(file)
209
	return windows.ShellExecute(0, verb, fileW, nil, nil, windows.SW_NORMAL)
210
}
211

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.