# typed: strict # frozen_string_literal: true 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 @launchctl ||= T.let(which("launchctl"), T.nilable(Pathname)) end # Is this a launchctl system sig { returns(T::Boolean) } def self.launchctl? launchctl.present? end # Is this a systemd system sig { returns(T::Boolean) } def self.systemctl? Systemctl.executable.present? end # Woohoo, we are root dude! sig { returns(T::Boolean) } def self.root? Process.euid.zero? end # Current user running `[sudo] brew services`. sig { returns(T.nilable(String)) } def self.user @user ||= T.let(ENV["USER"].presence || Utils.safe_popen_read("/usr/bin/whoami").chomp, T.nilable(String)) end sig { params(pid: T.nilable(Integer)).returns(T.nilable(String)) } def self.user_of_process(pid) if pid.nil? || pid.zero? user else Utils.safe_popen_read("ps", "-o", "user", "-p", pid.to_s).lines.second&.chomp end end # Run at boot. sig { returns(T.nilable(Pathname)) } def self.boot_path if launchctl? Pathname.new("/Library/LaunchDaemons") elsif systemctl? Pathname.new("/usr/lib/systemd/system") end end # Run at login. sig { returns(T.nilable(Pathname)) } def self.user_path if launchctl? Pathname.new("#{Dir.home}/Library/LaunchAgents") elsif systemctl? Pathname.new("#{Dir.home}/.config/systemd/user") end end # If root, return `boot_path`, else return `user_path`. sig { returns(T.nilable(Pathname)) } def self.path root? ? boot_path : user_path end sig { returns(String) } def self.domain_target if root? "system" elsif (ssh_tty = ENV.fetch("HOMEBREW_SSH_TTY", nil).present? && File.stat("/dev/console").uid != Process.uid) || (sudo_user = ENV.fetch("HOMEBREW_SUDO_USER", nil).present?) || (Process.uid != Process.euid) if @output_warning.blank? && ENV.fetch("HOMEBREW_SERVICES_NO_DOMAIN_WARNING", nil).blank? if ssh_tty opoo "running over SSH without /dev/console ownership, using user/* instead of gui/* domain!" elsif sudo_user opoo "running through sudo, using user/* instead of gui/* domain!" else opoo "uid and euid do not match, using user/* instead of gui/* domain!" end unless Homebrew::EnvConfig.no_env_hints? puts "Hide this warning by setting HOMEBREW_SERVICES_NO_DOMAIN_WARNING." puts "Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`)." end @output_warning = T.let(true, T.nilable(TrueClass)) end "user/#{Process.euid}" else "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