From ae58b3ef2165e9923f25c570e478963fd0c1541f Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Thu, 1 May 2025 06:29:37 +0100 Subject: [PATCH] services: try multiple domains when stopping --- Library/Homebrew/services/cli.rb | 42 ++++++++++++++------ Library/Homebrew/services/formula_wrapper.rb | 7 +++- Library/Homebrew/services/system.rb | 9 +++++ Library/Homebrew/test/services/cli_spec.rb | 1 + 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Library/Homebrew/services/cli.rb b/Library/Homebrew/services/cli.rb index d5a1042a1c..9dbea84385 100644 --- a/Library/Homebrew/services/cli.rb +++ b/Library/Homebrew/services/cli.rb @@ -196,18 +196,31 @@ module Homebrew System::Systemctl.quiet_run(*systemctl_args, "disable", "--now", service.service_name) end elsif System.launchctl? - quiet_system System.launchctl, "bootout", "#{System.domain_target}/#{service.service_name}" - unless no_wait - time_slept = 0 - sleep_time = 1 - max_wait = T.must(max_wait) - while ($CHILD_STATUS.to_i == 9216 || service.loaded?) && (max_wait.zero? || time_slept < max_wait) - sleep(sleep_time) - time_slept += sleep_time - quiet_system System.launchctl, "bootout", "#{System.domain_target}/#{service.service_name}" + dont_wait_statuses = [ + Errno::ESRCH::Errno, + System::LAUNCHCTL_DOMAIN_ACTION_NOT_SUPPORTED, + ] + System.candidate_domain_targets.each do |domain_target| + break unless service.loaded? + + quiet_system System.launchctl, "bootout", "#{domain_target}/#{service.service_name}" + unless no_wait + time_slept = 0 + sleep_time = 1 + max_wait = T.must(max_wait) + exit_status = $CHILD_STATUS.exitstatus + while dont_wait_statuses.exclude?(exit_status) && + (exit_status == Errno::EINPROGRESS::Errno || service.loaded?) && + (max_wait.zero? || time_slept < max_wait) + sleep(sleep_time) + time_slept += sleep_time + quiet_system System.launchctl, "bootout", "#{domain_target}/#{service.service_name}" + exit_status = $CHILD_STATUS.exitstatus + end end + service.reset_cache! + quiet_system System.launchctl, "stop", "#{domain_target}/#{service.service_name}" if service.pid? end - quiet_system System.launchctl, "stop", "#{System.domain_target}/#{service.service_name}" if service.pid? end unless keep @@ -216,7 +229,7 @@ module Homebrew System::Systemctl.run(*systemctl_args, "daemon-reload") if System.systemctl? end - if service.pid? || service.loaded? + if service.loaded? || service.pid? opoo "Unable to stop `#{service.name}` (label: #{service.service_name})" else ohai "Successfully stopped `#{service.name}` (label: #{service.service_name})" @@ -237,7 +250,12 @@ module Homebrew if System.systemctl? System::Systemctl.quiet_run("stop", service.service_name) elsif System.launchctl? - quiet_system System.launchctl, "stop", "#{System.domain_target}/#{service.service_name}" + System.candidate_domain_targets.each do |domain_target| + break unless service.pid? + + quiet_system System.launchctl, "stop", "#{domain_target}/#{service.service_name}" + service.reset_cache! + end end if service.pid? diff --git a/Library/Homebrew/services/formula_wrapper.rb b/Library/Homebrew/services/formula_wrapper.rb index 2921391fcf..834e6a815b 100644 --- a/Library/Homebrew/services/formula_wrapper.rb +++ b/Library/Homebrew/services/formula_wrapper.rb @@ -113,12 +113,17 @@ module Homebrew false end + sig { void } + def reset_cache! + @status_output_success_type = nil + end + # Returns `true` if the service is loaded, else false. # TODO: this should either be T::Boolean or renamed to `loaded` sig { params(cached: T::Boolean).returns(T.nilable(T::Boolean)) } def loaded?(cached: false) if System.launchctl? - @status_output_success_type = nil unless cached + reset_cache! unless cached _, status_success, = status_output_success_type status_success elsif System.systemctl? diff --git a/Library/Homebrew/services/system.rb b/Library/Homebrew/services/system.rb index 905e511b21..b821ca60d2 100644 --- a/Library/Homebrew/services/system.rb +++ b/Library/Homebrew/services/system.rb @@ -6,6 +6,8 @@ require_relative "system/systemctl" module Homebrew module Services module System + LAUNCHCTL_DOMAIN_ACTION_NOT_SUPPORTED = T.let(125, Integer) + # Path to launchctl binary. sig { returns(T.nilable(Pathname)) } def self.launchctl @@ -98,6 +100,13 @@ module Homebrew "gui/#{Process.uid}" end end + + sig { returns(T::Array[String]) } + def self.candidate_domain_targets + candidates = [domain_target] + candidates += ["user/#{Process.euid}", "gui/#{Process.uid}"] unless root? + candidates.uniq + end end end end diff --git a/Library/Homebrew/test/services/cli_spec.rb b/Library/Homebrew/test/services/cli_spec.rb index 8135b8fe13..e1ab8457c7 100644 --- a/Library/Homebrew/test/services/cli_spec.rb +++ b/Library/Homebrew/test/services/cli_spec.rb @@ -74,6 +74,7 @@ RSpec.describe Homebrew::Services::Cli do keep_alive?: false, ) allow(service).to receive(:service_name) + allow(service).to receive(:reset_cache!) allow(Homebrew::Services::FormulaWrapper).to receive(:from).and_return(service) allow(services_cli).to receive(:running).and_return(["example_service"]) expect do