diff --git a/Library/Homebrew/cask/artifact/abstract_uninstall.rb b/Library/Homebrew/cask/artifact/abstract_uninstall.rb index 19015c00d1..15427c9e6f 100644 --- a/Library/Homebrew/cask/artifact/abstract_uninstall.rb +++ b/Library/Homebrew/cask/artifact/abstract_uninstall.rb @@ -90,7 +90,22 @@ module Cask # :launchctl must come before :quit/:signal for cases where app would instantly re-launch def uninstall_launchctl(*services, command: nil, **_) booleans = [false, true] + + all_services = [] + + # if launchctl item contains a wildcard, find matching process(es) services.each do |service| + all_services.push(service) + next unless /\*/.match?(service) + + find_launchctl_with_wildcard(service) + .map { |_, _, id| id } + .each do |match| + all_services.push(match) + end + end + + all_services.each do |service| ohai "Removing launchctl service #{service}" booleans.each do |with_sudo| plist_status = command.run( @@ -268,6 +283,16 @@ module Cask end end + def find_launchctl_with_wildcard(search) + system_command!("/bin/launchctl", args: ["list"]) + .stdout.lines.drop(1) + .map { |line| line.chomp.split("\t") } + .map { |pid, state, id| [pid.to_i, state.to_i, id] } + .select do |(pid, _, id)| + pid.nonzero? && /\A#{Regexp.escape(search).gsub("\\*", ".*")}\Z/.match?(id) + end + end + # :kext should be unloaded before attempting to delete the relevant file def uninstall_kext(*kexts, command: nil, **_) kexts.each do |kext|