Skip to content

Commit

Permalink
daemon: start daemon from a powershell script that hides window
Browse files Browse the repository at this point in the history
after windows terminal became the default terminal/console host on
windows the daemon scheduled task would show a console window upon
running as its not recognizing the powershell cmdline argument for
hiding the window

this powershell script hides the window by using win32 api from
powershell

fixes #3726
  • Loading branch information
anjannath committed Jul 28, 2023
1 parent 6b61c2f commit 4ee2908
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 11 deletions.
71 changes: 65 additions & 6 deletions pkg/crc/preflight/preflight_daemon_task_check_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import (
"fmt"
"os"
"os/user"
"path/filepath"
"strings"

"github.com/crc-org/crc/pkg/crc/constants"
"github.com/crc-org/crc/pkg/crc/logging"
"github.com/crc-org/crc/pkg/crc/version"
crcos "github.com/crc-org/crc/pkg/os"
"github.com/crc-org/crc/pkg/os/windows/powershell"
)

Expand Down Expand Up @@ -51,6 +53,43 @@ var (
</Task>
`
errOlderVersion = fmt.Errorf("expected %s task to be on version '%s'", constants.DaemonTaskName, version.GetCRCVersion())

daemonPoshScriptTemplate = `# Following script is from https://stackoverflow.com/a/74976541
function Hide-ConsoleWindow() {
$ShowWindowAsyncCode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$ShowWindowAsync = Add-Type -MemberDefinition $ShowWindowAsyncCode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$hwnd = (Get-Process -PID $pid).MainWindowHandle
if ($hwnd -ne [System.IntPtr]::Zero) {
# When you got HWND of the console window:
# (It would appear that Windows Console Host is the default terminal application)
$ShowWindowAsync::ShowWindowAsync($hwnd, 0)
} else {
# When you failed to get HWND of the console window:
# (It would appear that Windows Terminal is the default terminal application)
# Mark the current console window with a unique string.
$UniqueWindowTitle = New-Guid
$Host.UI.RawUI.WindowTitle = $UniqueWindowTitle
$StringBuilder = New-Object System.Text.StringBuilder 1024
# Search the process that has the window title generated above.
$TerminalProcess = (Get-Process | Where-Object { $_.MainWindowTitle -eq $UniqueWindowTitle })
# Get the window handle of the terminal process.
# Note that GetConsoleWindow() in Win32 API returns the HWND of
# powershell.exe itself rather than the terminal process.
# When you call ShowWindowAsync(HWND, 0) with the HWND from GetConsoleWindow(),
# the Windows Terminal window will be just minimized rather than hidden.
$hwnd = $TerminalProcess.MainWindowHandle
if ($hwnd -ne [System.IntPtr]::Zero) {
$ShowWindowAsync::ShowWindowAsync($hwnd, 0)
} else {
Write-Host "Failed to hide the console window."
}
}
}
Hide-ConsoleWindow
%s`
)

func genDaemonTaskInstallTemplate(crcVersion, userName, daemonCommand string) (string, error) {
Expand Down Expand Up @@ -83,12 +122,7 @@ func fixDaemonTaskInstalled() error {
if err := removeDaemonTask(); err != nil {
return err
}
// prepare the task script
binPath, err := os.Executable()
if err != nil {
return fmt.Errorf("unable to find the current executable location: %v", err)
}
binPathWithArgs := fmt.Sprintf("& '%s' daemon", binPath)
binPathWithArgs := fmt.Sprintf("& '%s'", daemonPoshScriptPath)
// Get current user along with domain
u, err := user.Current()
if err != nil {
Expand Down Expand Up @@ -170,3 +204,28 @@ func checkIfOlderTask() error {
}
return nil
}

var daemonPoshScriptPath = filepath.Join(constants.CrcBinDir, "hidden_daemon.ps1")

func getDaemonPoshScriptContent() []byte {
binPath, err := os.Executable()
if err != nil {
return []byte{}
}
daemonCmd := fmt.Sprintf("%s daemon --log-level debug", binPath)
return []byte(fmt.Sprintf(daemonPoshScriptTemplate, daemonCmd))
}

func checkDaemonPoshScript() error {
if exists := crcos.FileExists(daemonPoshScriptPath); exists {
// check the script contains the path to the current executable
if err := crcos.FileContentMatches(daemonPoshScriptPath, getDaemonPoshScriptContent()); err == nil {
return nil
}
}
return fmt.Errorf("Powershell script for running the daemon does not exist")
}

func fixDaemonPoshScript() error {
return os.WriteFile(daemonPoshScriptPath, getDaemonPoshScriptContent(), 0600)
}
11 changes: 11 additions & 0 deletions pkg/crc/preflight/preflight_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ var vsockChecks = []Check{
}

var daemonTaskChecks = []Check{
{
configKeySuffix: "check-daemon-task-posh-script-present",
checkDescription: "Checking if the daemon task powershell script is present",
check: checkDaemonPoshScript,
fixDescription: "Creating the daemon task powershell script",
fix: fixDaemonPoshScript,
cleanupDescription: "Removing the daemon task powershell script",
cleanup: func() error { return os.Remove(daemonPoshScriptPath) },

labels: labels{Os: Windows},
},
{
configKeySuffix: "check-daemon-task-install",
checkDescription: "Checking if the daemon task is installed",
Expand Down
10 changes: 5 additions & 5 deletions pkg/crc/preflight/preflight_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import (
func TestCountConfigurationOptions(t *testing.T) {
cfg := config.New(config.NewEmptyInMemoryStorage(), config.NewEmptyInMemorySecretStorage())
RegisterSettings(cfg)
assert.Len(t, cfg.AllConfigs(), 13)
assert.Len(t, cfg.AllConfigs(), 14)
}

func TestCountPreflights(t *testing.T) {
assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 21)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 21)

assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 19)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 19)
assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
}

0 comments on commit 4ee2908

Please sign in to comment.