From 86a80c8a3dfdad2275a9937df5fe39cbe8f417dd Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 9 Nov 2016 09:32:54 +0100 Subject: [PATCH] Use `launchctl` to get running processes by bundle ID. --- .../cask/lib/hbc/artifact/uninstall_base.rb | 43 +++++++++++-------- .../cask/test/cask/artifact/uninstall_test.rb | 14 ++---- .../cask/test/cask/artifact/zap_test.rb | 14 ++---- 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb b/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb index 6d4d329ec5..ccb06a9ab0 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb @@ -1,4 +1,5 @@ require "pathname" +require "timeout" require "hbc/artifact/base" @@ -127,10 +128,18 @@ module Hbc def uninstall_quit(directives) Array(directives[:quit]).each do |id| ohai "Quitting application ID #{id}" - num_running = count_running_processes(id) - next unless num_running > 0 + next if running_processes(id).empty? @command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{id}" to quit)], sudo: true) - sleep 3 + + begin + Timeout.timeout(3) do + Kernel.loop do + break if running_processes(id).empty? + end + end + rescue Timeout::Error + next + end end end @@ -138,9 +147,9 @@ module Hbc def uninstall_signal(directives) Array(directives[:signal]).flatten.each_slice(2) do |pair| raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must have 2 elements.") unless pair.length == 2 - signal, id = pair - ohai "Signalling '#{signal}' to application ID '#{id}'" - pids = get_unix_pids(id) + signal, bundle_id = pair + ohai "Signalling '#{signal}' to application ID '#{bundle_id}'" + pids = running_processes(bundle_id).map(&:first) next unless pids.any? # Note that unlike :quit, signals are sent from the current user (not # upgraded to the superuser). This is a todo item for the future, but @@ -148,24 +157,20 @@ module Hbc # misapplied "kill" by root could bring down the system. The fact that we # learned the pid from AppleScript is already some degree of protection, # though indirect. - odebug "Unix ids are #{pids.inspect} for processes with bundle identifier #{id}" + odebug "Unix ids are #{pids.inspect} for processes with bundle identifier #{bundle_id}" Process.kill(signal, *pids) sleep 3 end end - def count_running_processes(bundle_id) - @command.run!("/usr/bin/osascript", - args: ["-e", %Q(tell application "System Events" to count processes whose bundle identifier is "#{bundle_id}")], - sudo: true).stdout.to_i - end - - def get_unix_pids(bundle_id) - pid_string = @command.run!("/usr/bin/osascript", - args: ["-e", %Q(tell application "System Events" to get the unix id of every process whose bundle identifier is "#{bundle_id}")], - sudo: true).stdout.chomp - return [] unless pid_string =~ /\A\d+(?:\s*,\s*\d+)*\Z/ # sanity check - pid_string.split(/\s*,\s*/).map(&:strip).map(&:to_i) + def running_processes(bundle_id) + @command.run!("/bin/launchctl", args: ["list"]).stdout.lines + .map { |line| line.chomp.split("\t") } + .map { |pid, state, id| [pid.to_i, state.to_i, id] } + .select do |fields| + next if fields[0].zero? + fields[2] =~ /^#{Regexp.escape(bundle_id)}($|\.\d+)/ + end end def uninstall_login_item(directives) diff --git a/Library/Homebrew/cask/test/cask/artifact/uninstall_test.rb b/Library/Homebrew/cask/test/cask/artifact/uninstall_test.rb index b9c5c3187b..b09d39833d 100644 --- a/Library/Homebrew/cask/test/cask/artifact/uninstall_test.rb +++ b/Library/Homebrew/cask/test/cask/artifact/uninstall_test.rb @@ -207,21 +207,17 @@ describe Hbc::Artifact::Uninstall do describe "when using quit" do let(:cask) { Hbc.load("with-uninstall-quit") } let(:bundle_id) { "my.fancy.package.app" } - let(:count_processes_script) { - 'tell application "System Events" to count processes ' + - %Q(whose bundle identifier is "#{bundle_id}") - } let(:quit_application_script) { %Q(tell application id "#{bundle_id}" to quit) } it "can uninstall" do Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{count_processes_script}]), "1" + %w[/bin/launchctl list], "999\t0\t#{bundle_id}\n" ) Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{quit_application_script}]) + %w[/bin/launchctl list] ) subject @@ -233,14 +229,10 @@ describe Hbc::Artifact::Uninstall do let(:bundle_id) { "my.fancy.package.app" } let(:signals) { %w[TERM KILL] } let(:unix_pids) { [12_345, 67_890] } - let(:get_unix_pids_script) { - 'tell application "System Events" to get the unix id of every process ' + - %Q(whose bundle identifier is "#{bundle_id}") - } it "can uninstall" do Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{get_unix_pids_script}]), unix_pids.join(", ") + %w[/bin/launchctl list], unix_pids.map { |pid| [pid, 0, bundle_id].join("\t") }.join("\n") ) signals.each do |signal| diff --git a/Library/Homebrew/cask/test/cask/artifact/zap_test.rb b/Library/Homebrew/cask/test/cask/artifact/zap_test.rb index fbbf520a0d..b65b0bf5cc 100644 --- a/Library/Homebrew/cask/test/cask/artifact/zap_test.rb +++ b/Library/Homebrew/cask/test/cask/artifact/zap_test.rb @@ -208,21 +208,17 @@ describe Hbc::Artifact::Zap do describe "when using quit" do let(:cask) { Hbc.load("with-zap-quit") } let(:bundle_id) { "my.fancy.package.app" } - let(:count_processes_script) { - 'tell application "System Events" to count processes ' + - %Q(whose bundle identifier is "#{bundle_id}") - } let(:quit_application_script) { %Q(tell application id "#{bundle_id}" to quit) } it "can zap" do Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{count_processes_script}]), "1" + %w[/bin/launchctl list], "999\t0\t#{bundle_id}\n" ) Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{quit_application_script}]) + %w[/bin/launchctl list] ) subject @@ -234,14 +230,10 @@ describe Hbc::Artifact::Zap do let(:bundle_id) { "my.fancy.package.app" } let(:signals) { %w[TERM KILL] } let(:unix_pids) { [12_345, 67_890] } - let(:get_unix_pids_script) { - 'tell application "System Events" to get the unix id of every process ' + - %Q(whose bundle identifier is "#{bundle_id}") - } it "can zap" do Hbc::FakeSystemCommand.stubs_command( - sudo(%W[/usr/bin/osascript -e #{get_unix_pids_script}]), unix_pids.join(", ") + %w[/bin/launchctl list], unix_pids.map { |pid| [pid, 0, bundle_id].join("\t") }.join("\n") ) signals.each do |signal|