diff options
Diffstat (limited to 'packages/meshbay-client/build')
| -rw-r--r-- | packages/meshbay-client/build/installer.nsh | 123 | ||||
| -rw-r--r-- | packages/meshbay-client/build/stop-node.ps1 | 45 |
2 files changed, 149 insertions, 19 deletions
diff --git a/packages/meshbay-client/build/installer.nsh b/packages/meshbay-client/build/installer.nsh index 3157f22..ef9c82a 100644 --- a/packages/meshbay-client/build/installer.nsh +++ b/packages/meshbay-client/build/installer.nsh @@ -55,7 +55,55 @@ !macroend !macro customInit + ; What this machine already runs, before the previous version's uninstaller + ; deletes the sign-in launcher: an upgrade keeps the mode it finds, instead + ; of defaulting to "background service" -- which a silent upgrade cannot even + ; set up (no elevation), so an "at sign-in" install came out of one with no + ; autostart at all and its node stopped (found upgrading a real install). + ; A fresh install still defaults to the service. StrCpy $MB_AutoMode "2" + nsExec::Exec 'schtasks /query /tn "MeshBay Node"' + Pop $0 + ${If} $0 == 0 + StrCpy $MB_AutoMode "2" + ${ElseIf} ${FileExists} "$APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\MeshBay Node.vbs" + StrCpy $MB_AutoMode "1" + ${ElseIf} ${FileExists} "$INSTDIR\${APP_EXECUTABLE_FILENAME}" + StrCpy $MB_AutoMode "0" + ${EndIf} +!macroend + +; ── stop the node before a single file is touched ───────────────────────── +; electron-builder inserts customCheckAppRunning in place of its own +; app-running check, which it runs before uninstallOldVersion and before the +; files are extracted (installSection.nsh); customInstall only runs after both. +; A service-mode daemon lives in the task's S4U logon session, where an +; unelevated taskkill gets "Access is denied". Left running, it keeps +; meshbay-node.exe and its DLLs locked, their copy fails, and electron-builder's +; last-resort extract ignores the failure. build/stop-node.ps1 asks the node to +; stop through its own control API first -- any session, no elevation, a proper +; shutdown -- then Task Scheduler, then taskkill. It is embedded and run from +; the plugins directory: the installed copy of anything may be what is being +; replaced. + +; Defining customCheckAppRunning makes allowOnlyOneInstallerInstance.nsh skip +; these two, which its own _CHECK_APP_RUNNING (inserted below) still needs. +!include "getProcessInfo.nsh" +Var pid + +!macro customCheckAppRunning + InitPluginsDir + File "/oname=$PLUGINSDIR\mb-stop-node.ps1" "${BUILD_RESOURCES_DIR}\stop-node.ps1" + mb_stop_node: + DetailPrint "Stopping the MeshBay node..." + nsExec::Exec `"${MB_PWSH}" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$PLUGINSDIR\mb-stop-node.ps1"` + Pop $0 + ${If} $0 != 0 + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "The MeshBay node is still running and holds files that setup must replace. Stop it (meshbay-node service stop, or end meshbay-node.exe in Task Manager), then click Retry." /SD IDCANCEL IDRETRY mb_stop_node + Quit + ${EndIf} + !insertmacro IS_POWERSHELL_AVAILABLE + !insertmacro _CHECK_APP_RUNNING !macroend ; ── the autostart choice, as a radio page ───────────────────────────────── @@ -123,9 +171,8 @@ !macroend !macro customInstall - ; resources\node-runtime\meshbay-node.exe is about to be overwritten; a - ; daemon still running from a previous version holds the file open. - nsExec::Exec 'taskkill /IM meshbay-node.exe /F' + ; The node was stopped by customCheckAppRunning, before the files were + ; copied -- by the time this runs they already have been. ; Add the daemon dir to the per-user PATH (HKCU\Environment). WordAdd is a ; stock NSIS macro over a ';'-delimited list -- it is a no-op if the entry is @@ -146,9 +193,12 @@ ${If} $MB_AutoMode == "2" ; Background service: the boot-time Scheduled Task AND the firewall ; rules, in ONE elevation (service-mode.ps1 does both). Skip it only - ; when the task already exists and the rules are already there. + ; when the task is already there and current and so are the rules. A + ; stale task (2: another executable, or the 72-hour / battery defaults + ; of an older setup) is registered again -- the owner cannot change it + ; without elevation. nsExec::Exec '"${MB_PWSH}" -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\service.ps1" status' - Pop $R1 ; 0 = task installed + Pop $R1 ; 0 = installed and current, 1 = absent, 2 = stale ${If} $R1 == 0 ${AndIf} $R0 == 0 Goto mb_auto_done @@ -159,28 +209,58 @@ Goto mb_auto_done ${EndIf} - ; Modes 0 and 1: no scheduled task. One elevation for the firewall rules, - ; and only if one is actually missing. + ; Modes 0 and 1. A boot task left from an earlier "background service" + ; choice would start the node a second time, at boot and at sign-in: take + ; it out (service-mode.ps1 remove keeps the firewall rules every mode needs). + nsExec::Exec 'schtasks /query /tn "MeshBay Node"' + Pop $R1 + ${If} $R1 == 0 + ExecShellWait "runas" "${MB_PWSH}" \ + '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\service-mode.ps1" -Action remove' \ + SW_HIDE + ${EndIf} + ; One elevation for the firewall rules, and only if one is actually missing. ${If} $R0 != 0 ExecShellWait "runas" "${MB_PWSH}" \ '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\firewall.ps1" add' \ SW_HIDE ${EndIf} - ; Mode 1 also drops the per-user sign-in launcher (no admin -- it is just - ; a .vbs in this account's Startup folder). Idempotent, so a repeat run is - ; harmless. meshbay_node.platform.service_install() removes this itself if - ; the user later switches to service mode from the Node page. - ${If} $MB_AutoMode == "1" - nsExec::Exec '"${MB_NODE_BIN}\meshbay-node.exe" autostart install' - ${EndIf} - mb_auto_done: ${EndIf} + + ; The sign-in launcher as the mode wants it -- silent installs too: during an + ; upgrade the previous version's uninstaller has just deleted it. No admin, + ; idempotent; `autostart install` refuses while a boot task exists. + ${If} $MB_AutoMode == "1" + nsExec::Exec '"${MB_NODE_BIN}\meshbay-node.exe" autostart install' + ${Else} + nsExec::Exec '"${MB_NODE_BIN}\meshbay-node.exe" autostart remove' + ${EndIf} + Pop $R2 + + ; Start the node customCheckAppRunning stopped, the way this mode runs it, + ; rather than leave it down until the next boot or sign-in. Only a node that + ; has been set up: a fresh install's has no account yet, and node:start + ; provisions and starts it. Mode 0 is the app's to start (runAfterFinish). + ${If} ${FileExists} "$LOCALAPPDATA\meshbay\node.toml" + ${If} $MB_AutoMode == "2" + nsExec::Exec 'schtasks /query /tn "MeshBay Node"' + Pop $R2 + ${If} $R2 == 0 + nsExec::Exec 'schtasks /run /tn "MeshBay Node"' + Pop $R2 + ${EndIf} + ${ElseIf} $MB_AutoMode == "1" + nsExec::Exec '"${MB_NODE_BIN}\meshbay-node.exe" autostart start' + Pop $R2 + ${EndIf} + ${EndIf} !macroend !macro customUnInstall - nsExec::Exec 'taskkill /IM meshbay-node.exe /F' + ; The node is already stopped: the uninstaller runs customCheckAppRunning + ; (un.checkAppRunning) before this. ; Take our entry back out of PATH, leaving the rest of it alone. ReadRegStr $0 HKCU "Environment" "Path" @@ -196,17 +276,22 @@ ; default-No; a silent uninstall skips it entirely. customUnInstall runs ; before the files are removed, so service-mode.ps1 is still there. ${IfNot} ${Silent} + ${AndIfNot} ${isUpdated} MessageBox MB_YESNO|MB_ICONQUESTION \ "Remove MeshBay's Windows Firewall rules and its boot-time service task, if you set one up? This needs one administrator confirmation. Both are harmless if left." \ /SD IDNO IDNO mb_keep_privileged ExecShellWait "runas" "${MB_PWSH}" \ - '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\service-mode.ps1" -Action remove' \ + '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\service-mode.ps1" -Action uninstall' \ SW_HIDE mb_keep_privileged: ${EndIf} ; meshbay_node.platform.autostart_install() -- if the user picked "at sign-in" ; (here, or later in the client), this points wscript at the binary we are - ; about to delete, and would error at every sign-in. - Delete "$APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\MeshBay Node.vbs" + ; about to delete, and would error at every sign-in. Not in an upgrade: the + ; new version is about to take this path's place, and deleting the launcher + ; here is what left upgraded "at sign-in" installs with no autostart at all. + ${IfNot} ${isUpdated} + Delete "$APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\MeshBay Node.vbs" + ${EndIf} !macroend diff --git a/packages/meshbay-client/build/stop-node.ps1 b/packages/meshbay-client/build/stop-node.ps1 new file mode 100644 index 0000000..cfaf2f8 --- /dev/null +++ b/packages/meshbay-client/build/stop-node.ps1 @@ -0,0 +1,45 @@ +# Stop every MeshBay node on this machine before setup touches its files, or +# before the uninstaller removes them. Embedded in the installer and run from +# its plugins directory: the installed copy of anything may be the one being +# replaced. +# +# 1. Ask the node through its own control API (/api/shutdown, loopback, per-run +# token): the only stop that reaches a node in any session with no +# elevation, and the one that lets it shut down properly -- WebRTC sessions +# closed, transcodes stopped. +# 2. Task Scheduler for a background-service node (the owner may end the task; +# taskkill from here gets "Access is denied" in its session). +# 3. taskkill for anything left in this session. +# Exit 0 once no meshbay-node.exe is left, 1 otherwise. +$ErrorActionPreference = "SilentlyContinue" + +function NodeLeft { [bool](Get-Process -Name meshbay-node -ErrorAction SilentlyContinue) } +function WaitGone([int]$Seconds) { + $deadline = (Get-Date).AddSeconds($Seconds) + while ((NodeLeft) -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 250 } + -not (NodeLeft) +} + +if (-not (NodeLeft)) { exit 0 } + +$cfg = Join-Path $env:LOCALAPPDATA "meshbay" +$port = 18000 +$toml = Join-Path $cfg "node.toml" +if (Test-Path $toml) { + $m = Select-String -Path $toml -Pattern '^\s*ui_port\s*=\s*(\d+)' | Select-Object -First 1 + if ($m) { $port = [int]$m.Matches[0].Groups[1].Value } +} +$tokenFile = Join-Path $cfg "data\ui-token" +if (Test-Path $tokenFile) { + $token = (Get-Content $tokenFile -Raw).Trim() + try { + Invoke-WebRequest -UseBasicParsing -Method Post -TimeoutSec 5 ` + -Uri "http://127.0.0.1:$port/api/shutdown?t=$token" | Out-Null + if (WaitGone 20) { exit 0 } + } catch { } +} + +& schtasks /end /tn "MeshBay Node" 2>&1 | Out-Null +Get-Process -Name meshbay-node -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue +if (WaitGone 20) { exit 0 } +exit 1 |