File: preflight_checks_darwin.go

package info (click to toggle)
golang-github-crc-org-crc 2.34.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,548 kB
  • sloc: sh: 398; makefile: 326; javascript: 40
file content (238 lines) | stat: -rw-r--r-- 6,924 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package preflight

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/crc-org/crc/v2/pkg/crc/cache"
	"github.com/crc-org/crc/v2/pkg/crc/constants"
	"github.com/crc-org/crc/v2/pkg/crc/logging"
	"github.com/crc-org/crc/v2/pkg/crc/machine/vfkit"
	"github.com/crc-org/crc/v2/pkg/crc/version"
	crcos "github.com/crc-org/crc/v2/pkg/os"
	"github.com/crc-org/crc/v2/pkg/os/darwin"
	"github.com/crc-org/crc/v2/pkg/os/darwin/launchd"
	"github.com/klauspost/cpuid/v2"
	"golang.org/x/sys/unix"
)

const (
	resolverDir  = "/etc/resolver"
	resolverFile = "/etc/resolver/testing"
)

func checkAppleSilicon() error {
	if strings.HasPrefix(cpuid.CPU.BrandName, "VirtualApple") {
		logging.Debugf("Running with an emulated x86_64 CPU")
		return fmt.Errorf("This version of CRC for AMD64/Intel64 CPUs is unsupported on Apple M1 hardware")
	}

	return nil
}

func checkVfkitInstalled() error {
	if version.IsInstaller() {
		return nil
	}

	h := cache.NewVfkitCache()
	if !h.IsCached() {
		return fmt.Errorf("%s executable is not cached", h.GetExecutableName())
	}
	vfkitPath := h.GetExecutablePath()
	err := unix.Access(vfkitPath, unix.X_OK)
	if err != nil {
		return fmt.Errorf("%s not executable", vfkitPath)
	}
	return h.CheckVersion()
}

func fixVfkitInstallation() error {
	if version.IsInstaller() {
		return nil
	}

	h := cache.NewVfkitCache()

	logging.Debugf("Installing %s", h.GetExecutableName())

	if err := h.EnsureIsCached(); err != nil {
		return fmt.Errorf("Unable to download %s : %v", h.GetExecutableName(), err)
	}
	return nil
}

func checkResolverFilePermissions() error {
	return isUserHaveFileWritePermission(resolverFile)
}

func fixResolverFilePermissions() error {
	// Check if resolver directory available or not
	if _, err := os.Stat(resolverDir); os.IsNotExist(err) {
		logging.Debugf("Creating %s directory", resolverDir)
		stdOut, stdErr, err := crcos.RunPrivileged(fmt.Sprintf("Creating dir %s", resolverDir), "mkdir", resolverDir)
		if err != nil {
			return fmt.Errorf("Unable to create the resolver Dir: %s %v: %s", stdOut, err, stdErr)
		}
	}
	logging.Debugf("Making %s readable/writable by the current user", resolverFile)
	stdOut, stdErr, err := crcos.RunPrivileged(fmt.Sprintf("Creating file %s", resolverFile), "touch", resolverFile)
	if err != nil {
		return fmt.Errorf("Unable to create the resolver file: %s %v: %s", stdOut, err, stdErr)
	}

	return addFileWritePermissionToUser(resolverFile)
}

func removeResolverFile() error {
	// Check if the resolver file exist or not
	if _, err := os.Stat(resolverFile); !os.IsNotExist(err) {
		logging.Debugf("Removing %s file", resolverFile)
		err := crcos.RemoveFileAsRoot(fmt.Sprintf("Removing file %s", resolverFile), resolverFile)
		if err != nil {
			return fmt.Errorf("Unable to delete the resolver File: %s %v", resolverFile, err)
		}
	}
	return nil
}

func isUserHaveFileWritePermission(filename string) error {
	err := unix.Access(filename, unix.R_OK|unix.W_OK)
	if err != nil {
		return fmt.Errorf("%s is not readable/writable by the current user", filename)
	}
	return nil
}

func addFileWritePermissionToUser(filename string) error {
	logging.Debugf("Making %s readable/writable by the current user", filename)
	currentUser, err := user.Current()
	if err != nil {
		logging.Debugf("user.Current() failed: %v", err)
		return fmt.Errorf("Failed to get current user id")
	}

	stdOut, stdErr, err := crcos.RunPrivileged(fmt.Sprintf("Changing ownership of %s", filename), "chown", currentUser.Username, filename)
	if err != nil {
		return fmt.Errorf("Unable to change ownership of the filename: %s %v: %s", stdOut, err, stdErr)
	}

	err = os.Chmod(filename, 0600)
	if err != nil {
		return fmt.Errorf("Unable to change permissions of the filename: %s %v: %s", stdOut, err, stdErr)
	}
	logging.Debugf("%s is readable/writable by current user", filename)

	return nil
}

func killVfkitProcess() error {
	pgrepPath, err := exec.LookPath("pgrep")
	if err != nil {
		return fmt.Errorf("Could not find 'pgrep'. %w", err)
	}
	if _, _, err := crcos.RunWithDefaultLocale(pgrepPath, "-f", vfkit.ExecutablePath()); err != nil {
		var exitErr *exec.ExitError
		if errors.As(err, &exitErr) {
			/* 1: no processes matched */
			if exitErr.ExitCode() == 1 {
				logging.Debugf("No running 'vfkit' process started by crc")
				return nil
			}
		}
		logging.Debugf("Failed to find 'vfkit' process. %v", err)
		/* Unclear what pgrep failure was, don't return, maybe pkill will be more successful */
	}

	pkillPath, err := exec.LookPath("pkill")
	if err != nil {
		return fmt.Errorf("Could not find 'pkill'. %w", err)
	}
	if _, _, err := crcos.RunWithDefaultLocale(pkillPath, "-SIGKILL", "-f", vfkit.ExecutablePath()); err != nil {
		return fmt.Errorf("Failed to kill 'vfkit' process. %w", err)
	}
	return nil
}

func getDaemonConfig() (*launchd.AgentConfig, error) {
	logFilePath := filepath.Join(constants.CrcBaseDir, ".launchd-crcd.log")

	env := map[string]string{"Version": version.GetCRCVersion()}
	daemonConfig := launchd.AgentConfig{
		Label:          constants.DaemonAgentLabel,
		ExecutablePath: constants.CrcSymlinkPath,
		StdOutFilePath: logFilePath,
		StdErrFilePath: logFilePath,
		Args:           []string{"daemon", "--log-level=debug"},
		Env:            env,
	}

	return &daemonConfig, nil
}

func checkIfDaemonPlistFileExists() error {
	daemonConfig, err := getDaemonConfig()
	if err != nil {
		return err
	}
	if err := launchd.CheckPlist(*daemonConfig); err != nil {
		return err
	}
	if !launchd.AgentRunning(daemonConfig.Label) && !daemonRunning() {
		return fmt.Errorf("launchd agent '%s' is not running", daemonConfig.Label)
	}
	return nil
}

func fixDaemonPlistFileExists() error {
	if daemonRunning() {
		if err := killDaemonProcess(); err != nil {
			return err
		}
	}
	daemonConfig, err := getDaemonConfig()
	if err != nil {
		return err
	}
	return fixPlistFileExists(*daemonConfig)
}

func removeDaemonPlistFile() error {
	if err := launchd.UnloadPlist(constants.DaemonAgentLabel); err != nil {
		return err
	}
	return launchd.RemovePlist(constants.DaemonAgentLabel)
}

func fixPlistFileExists(agentConfig launchd.AgentConfig) error {
	logging.Debugf("Creating plist for %s", agentConfig.Label)
	err := launchd.CreatePlist(agentConfig)
	if err != nil {
		return err
	}
	if err := launchd.LoadPlist(agentConfig.Label); err != nil {
		logging.Debugf("failed to load launchd agent '%s': %v", agentConfig.Label, err.Error())
		return err
	}
	if err := launchd.RestartAgent(agentConfig.Label); err != nil {
		logging.Debugf("failed to restart launchd agent '%s': %v", agentConfig.Label, err.Error())
		return err
	}
	return waitForDaemonRunning()
}

func deprecationNotice() error {
	supports, err := darwin.AtLeast("13.0.0")
	if err != nil {
		return err
	}
	if !supports {
		logging.Warnf("This version of macOS is going to be unsupported for CRC, Please update to macOS 13 or newer")
	}
	return nil
}