Use launchctl to get running processes by bundle ID.

This commit is contained in:
Markus Reiter 2016-11-09 09:32:54 +01:00
parent 88d0e8c2e6
commit 86a80c8a3d
3 changed files with 30 additions and 41 deletions

View File

@ -1,4 +1,5 @@
require "pathname" require "pathname"
require "timeout"
require "hbc/artifact/base" require "hbc/artifact/base"
@ -127,10 +128,18 @@ module Hbc
def uninstall_quit(directives) def uninstall_quit(directives)
Array(directives[:quit]).each do |id| Array(directives[:quit]).each do |id|
ohai "Quitting application ID #{id}" ohai "Quitting application ID #{id}"
num_running = count_running_processes(id) next if running_processes(id).empty?
next unless num_running > 0
@command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{id}" to quit)], sudo: true) @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
end end
@ -138,9 +147,9 @@ module Hbc
def uninstall_signal(directives) def uninstall_signal(directives)
Array(directives[:signal]).flatten.each_slice(2) do |pair| Array(directives[:signal]).flatten.each_slice(2) do |pair|
raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must have 2 elements.") unless pair.length == 2 raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must have 2 elements.") unless pair.length == 2
signal, id = pair signal, bundle_id = pair
ohai "Signalling '#{signal}' to application ID '#{id}'" ohai "Signalling '#{signal}' to application ID '#{bundle_id}'"
pids = get_unix_pids(id) pids = running_processes(bundle_id).map(&:first)
next unless pids.any? next unless pids.any?
# Note that unlike :quit, signals are sent from the current user (not # 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 # 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 # misapplied "kill" by root could bring down the system. The fact that we
# learned the pid from AppleScript is already some degree of protection, # learned the pid from AppleScript is already some degree of protection,
# though indirect. # 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) Process.kill(signal, *pids)
sleep 3 sleep 3
end end
end end
def count_running_processes(bundle_id) def running_processes(bundle_id)
@command.run!("/usr/bin/osascript", @command.run!("/bin/launchctl", args: ["list"]).stdout.lines
args: ["-e", %Q(tell application "System Events" to count processes whose bundle identifier is "#{bundle_id}")], .map { |line| line.chomp.split("\t") }
sudo: true).stdout.to_i .map { |pid, state, id| [pid.to_i, state.to_i, id] }
end .select do |fields|
next if fields[0].zero?
def get_unix_pids(bundle_id) fields[2] =~ /^#{Regexp.escape(bundle_id)}($|\.\d+)/
pid_string = @command.run!("/usr/bin/osascript", end
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)
end end
def uninstall_login_item(directives) def uninstall_login_item(directives)

View File

@ -207,21 +207,17 @@ describe Hbc::Artifact::Uninstall do
describe "when using quit" do describe "when using quit" do
let(:cask) { Hbc.load("with-uninstall-quit") } let(:cask) { Hbc.load("with-uninstall-quit") }
let(:bundle_id) { "my.fancy.package.app" } 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) { let(:quit_application_script) {
%Q(tell application id "#{bundle_id}" to quit) %Q(tell application id "#{bundle_id}" to quit)
} }
it "can uninstall" do it "can uninstall" do
Hbc::FakeSystemCommand.stubs_command( 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( Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{quit_application_script}]) %w[/bin/launchctl list]
) )
subject subject
@ -233,14 +229,10 @@ describe Hbc::Artifact::Uninstall do
let(:bundle_id) { "my.fancy.package.app" } let(:bundle_id) { "my.fancy.package.app" }
let(:signals) { %w[TERM KILL] } let(:signals) { %w[TERM KILL] }
let(:unix_pids) { [12_345, 67_890] } 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 it "can uninstall" do
Hbc::FakeSystemCommand.stubs_command( 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| signals.each do |signal|

View File

@ -208,21 +208,17 @@ describe Hbc::Artifact::Zap do
describe "when using quit" do describe "when using quit" do
let(:cask) { Hbc.load("with-zap-quit") } let(:cask) { Hbc.load("with-zap-quit") }
let(:bundle_id) { "my.fancy.package.app" } 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) { let(:quit_application_script) {
%Q(tell application id "#{bundle_id}" to quit) %Q(tell application id "#{bundle_id}" to quit)
} }
it "can zap" do it "can zap" do
Hbc::FakeSystemCommand.stubs_command( 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( Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{quit_application_script}]) %w[/bin/launchctl list]
) )
subject subject
@ -234,14 +230,10 @@ describe Hbc::Artifact::Zap do
let(:bundle_id) { "my.fancy.package.app" } let(:bundle_id) { "my.fancy.package.app" }
let(:signals) { %w[TERM KILL] } let(:signals) { %w[TERM KILL] }
let(:unix_pids) { [12_345, 67_890] } 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 it "can zap" do
Hbc::FakeSystemCommand.stubs_command( 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| signals.each do |signal|