Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

### Features

**autoupdate:** GitHub predefined hashes support ([#6416](https://github.com/ScoopInstaller/Scoop/issues/6416), [#6435](https://github.com/ScoopInstaller/Scoop/issues/6435))
- **scoop-forcekill:** Support killing running applications and services ([#6603](https://github.com/ScoopInstaller/Scoop/pull/6603))
- **autoupdate:** GitHub predefined hashes support ([#6416](https://github.com/ScoopInstaller/Scoop/issues/6416), [#6435](https://github.com/ScoopInstaller/Scoop/issues/6435))

### Bug Fixes

Expand Down
130 changes: 119 additions & 11 deletions lib/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -530,23 +530,131 @@ function persist_permission($manifest, $global) {
}
}

# test if there are running processes
function test_running_process($app, $global) {
# check if there are running processes
function check_running_process($app, $global) {
if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
}

$json = install_info $app $version $global
$forcekill = $false
$forcekill_services = @()

if ($json) {
if ($json.forcekill) { $forcekill = $true }
if ($json.forcekill_services) { $forcekill_services = @($json.forcekill_services) }
}

$processdir = appdir $app $global | Convert-Path
$running_processes = Get-Process | Where-Object { $_.Path -like "$processdir\*" } | Out-String

$servicesToStop = @()
if ($forcekill) {
$wmi_dir = $processdir -replace '\\', '\\' -replace "'", "\'"
$filter = "PathName LIKE '%$wmi_dir%'"
Comment thread
MetalDevOps marked this conversation as resolved.
Outdated
$foundServices = @(Get-CimInstance -ClassName Win32_Service -Filter $filter)
if ($foundServices) {
$servicesToStop += $foundServices.Name
}
if ($forcekill_services) {
$servicesToStop += $forcekill_services
}

if ($running_processes) {
if (get_config IGNORE_RUNNING_PROCESSES) {
if ($servicesToStop.Count -gt 0) {
$servicesToStop = @($servicesToStop | Select-Object -Unique)
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

$running_processes = @(Get-Process | Where-Object { $_.Path -like "$processdir\*" })

$blocked = $false
if ($running_processes -and !$forcekill -and !(get_config IGNORE_RUNNING_PROCESSES)) {
$blocked = $true
}

return @{
Blocked = $blocked
Forcekill = $forcekill
App = $app
RunningProcesses = @($running_processes)
ServicesToStop = @($servicesToStop)
ProcessDir = $processdir
}
}

function stop_running_process($test_result) {
$forcekill = $test_result.Forcekill
$app = $test_result.App
$running_processes = $test_result.RunningProcesses
$servicesToStop = $test_result.ServicesToStop
$processdir = $test_result.ProcessDir

$stopped_services = @()
$stopped_processes = @()
$blocked = $test_result.Blocked

if ($running_processes.Count -gt 0) {
if ($forcekill) {
warn "The following instances of `"$app`" are still running. Scoop is configured to force kill them."
Write-Host ($running_processes | Out-String)
$stopped_processes = @($running_processes | Where-Object Path | Select-Object -ExpandProperty Path | Select-Object -Unique)

foreach ($proc in $running_processes) {
try {
Stop-Process -Id $proc.Id -Force -ErrorAction Stop
} catch {
warn "Failed to stop process $($proc.Name) (ID $($proc.Id)): $($_.Exception.Message)"
}
}

$still_running = @(Get-Process | Where-Object { $_.Path -like "$processdir\*" })
if ($still_running.Count -gt 0) {
warn "Some instances of `"$app`" could not be stopped."
$blocked = $true
}

} elseif (get_config IGNORE_RUNNING_PROCESSES) {
warn "The following instances of `"$app`" are still running. Scoop is configured to ignore this condition."
Write-Host $running_processes
return $false
Write-Host ($running_processes | Out-String)
} else {
error "The following instances of `"$app`" are still running. Close them and try again."
Write-Host $running_processes
return $true
Write-Host ($running_processes | Out-String)
$blocked = $true
}
} else {
return $false
}

if ($forcekill -and $servicesToStop.Count -gt 0) {
if (-not (is_admin)) {
warn 'Administrative privileges are required to stop services.'
$blocked = $true
} else {
Comment thread
MetalDevOps marked this conversation as resolved.
Outdated
foreach ($svc in $servicesToStop) {
$status = (Get-Service -Name $svc -ErrorAction SilentlyContinue).Status
if ($status -eq 'Running') {
warn "Stopping service '$svc' associated with '$app'..."
try {
Stop-Service -Name $svc -Force -ErrorAction Stop
$stopped_services += $svc
} catch {
warn "Failed to stop service '$svc': $($_.Exception.Message)"
}

if ((Get-Service -Name $svc -ErrorAction SilentlyContinue).Status -eq 'Running') {
warn "Service '$svc' is still running."
$blocked = $true
}
} elseif ($status) {
$stopped_services += $svc
}
Comment thread
MetalDevOps marked this conversation as resolved.
Outdated
}
}
}

return @{
Blocked = $blocked
ServicesToRestart = @($stopped_services)
ProcessesToRestart = @($stopped_processes)
}
}

Expand Down
88 changes: 88 additions & 0 deletions libexec/scoop-forcekill.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Usage: scoop forcekill <apps>
# Summary: Force close apps before updating
# Help: To mark a user-scoped app to force close:
# scoop forcekill <app>
#
# To mark a global app to force close:
# scoop forcekill -g <app>
#
# To explicitly define services to stop:
# scoop forcekill <app> --service <servicename>
#
# Options:
# -g, --global Force close globally installed apps
# -s, --service Services to stop (comma separated)

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\install.ps1"

$opt, $apps, $err = getopt $args 'gs:' 'global', 'service='
if ($err) { "scoop forcekill: $err"; exit 1 }

$exitcode = 0

$global = $opt.g -or $opt.global

if (!$apps) {
my_usage
exit 1
}

if ($global -and !(is_admin)) {
error 'You need admin rights to mark a global app for forcekill.'
exit 1
}

foreach ($app in $apps) {
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
} else {
error "'$app' is not installed."
}
$exitcode = 1
continue
}

if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to configure forcekill for '$app'."
$exitcode = 1
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }

$install.forcekill = $true

if ($opt.service) {
$services = $opt.service -split ',' | ForEach-Object { $_.Trim() }
$valid_services = @()
foreach ($svc in $services) {
if (Get-Service -Name $svc -ErrorAction SilentlyContinue) {
$valid_services += $svc
} else {
warn "Could not find service '$svc'. Skipping."
$exitcode = 1
}
}
if ($valid_services.Count -gt 0) {
$install.forcekill_services = @($valid_services | Select-Object -Unique)
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

save_install_info $install $dir
success "$app is now marked to force close on update."
}

exit $exitcode
Comment thread
coderabbitai[bot] marked this conversation as resolved.
24 changes: 13 additions & 11 deletions libexec/scoop-reset.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
. "$PSScriptRoot\..\lib\shortcuts.ps1"

$opt, $apps, $err = getopt $args 'a' 'all'
if($err) { "scoop reset: $err"; exit 1 }
if ($err) { "scoop reset: $err"; exit 1 }
$all = $opt.a -or $opt.all

if(!$apps -and !$all) { error '<app> missing'; my_usage; exit 1 }
if (!$apps -and !$all) { error '<app> missing'; my_usage; exit 1 }

if($apps -eq '*' -or $all) {
$local = installed_apps $false | ForEach-Object { ,@($_, $false) }
$global = installed_apps $true | ForEach-Object { ,@($_, $true) }
if ($apps -eq '*' -or $all) {
$local = installed_apps $false | ForEach-Object { , @($_, $false) }
$global = installed_apps $true | ForEach-Object { , @($_, $true) }
$apps = @($local) + @($global)
}

Expand All @@ -30,17 +30,17 @@ $apps | ForEach-Object {

$app, $bucket, $version = parse_app $app

if(($global -eq $null) -and (installed $app $true)) {
if (($global -eq $null) -and (installed $app $true)) {
# set global flag when running reset command on specific app
$global = $true
}

if($app -eq 'scoop') {
if ($app -eq 'scoop') {
# skip scoop
return
}

if(!(installed $app)) {
if (!(installed $app)) {
error "'$app' isn't installed"
return
}
Expand All @@ -57,19 +57,21 @@ $apps | ForEach-Object {
return
}

if($global -and !(is_admin)) {
if ($global -and !(is_admin)) {
warn "'$app' ($version) is a global app. You need admin rights to reset it. Skipping."
return
}

write-host "Resetting $app ($version)."
Write-Host "Resetting $app ($version)."

$dir = Convert-Path (versiondir $app $version $global)
$original_dir = $dir
$persist_dir = persistdir $app $global

#region Workaround for #2952
if (test_running_process $app $global) {
$running_ret = check_running_process $app $global
$stop_ret = stop_running_process $running_ret
if ($stop_ret.Blocked) {
return
}
#endregion Workaround for #2952
Comment thread
MetalDevOps marked this conversation as resolved.
Expand Down
68 changes: 68 additions & 0 deletions libexec/scoop-unforcekill.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Usage: scoop unforcekill <apps>
# Summary: Remove force close setting from an app
# Help: To unmark a user-scoped app:
# scoop unforcekill <app>
#
# To unmark a global app:
# scoop unforcekill -g <app>
#
# Options:
# -g, --global Unmark globally installed apps

. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\install.ps1"

$opt, $apps, $err = getopt $args 'g' 'global'
if ($err) { "scoop unforcekill: $err"; exit 1 }

$global = $opt.g -or $opt.global

if (!$apps) {
my_usage
exit 1
}

if ($global -and !(is_admin)) {
error 'You need admin rights to unmark a global app.'
exit 1
}

foreach ($app in $apps) {
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
} else {
error "'$app' is not installed."
}
continue
}

if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to unmark forcekill for '$app'."
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }

if (!$install.forcekill -and !$install.forcekill_services) {
info "'$app' is not marked for forcekill."
continue
}
$install.Remove('forcekill')
$install.Remove('forcekill_services')
save_install_info $install $dir
success "$app is no longer marked to force close on update."
}

exit $exitcode
4 changes: 3 additions & 1 deletion libexec/scoop-uninstall.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ if (!$apps) { exit 0 }
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $manifest -Arch $architecture

#region Workaround for #2952
if (test_running_process $app $global) {
$running_ret = check_running_process $app $global
$stop_ret = stop_running_process $running_ret
if ($stop_ret.Blocked) {
continue
}
#endregion Workaround for #2952
Expand Down
Loading