| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  | # typed: strict | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "abstract_command" | 
					
						
							| 
									
										
										
										
											2025-03-13 14:50:03 +00:00
										 |  |  | require "services/system" | 
					
						
							|  |  |  | require "services/commands/list" | 
					
						
							|  |  |  | require "services/commands/cleanup" | 
					
						
							|  |  |  | require "services/commands/info" | 
					
						
							|  |  |  | require "services/commands/restart" | 
					
						
							|  |  |  | require "services/commands/run" | 
					
						
							|  |  |  | require "services/commands/start" | 
					
						
							|  |  |  | require "services/commands/stop" | 
					
						
							|  |  |  | require "services/commands/kill" | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   module Cmd | 
					
						
							|  |  |  |     class Services < AbstractCommand | 
					
						
							|  |  |  |       cmd_args do | 
					
						
							|  |  |  |         usage_banner <<~EOS | 
					
						
							|  |  |  |           `services` [<subcommand>] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           Manage background services with macOS' `launchctl`(1) daemon manager or | 
					
						
							|  |  |  |           Linux's `systemctl`(1) service manager. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           If `sudo` is passed, operate on `/Library/LaunchDaemons` or `/usr/lib/systemd/system`  (started at boot). | 
					
						
							|  |  |  |           Otherwise, operate on `~/Library/LaunchAgents` or `~/.config/systemd/user` (started at login). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           [`sudo`] `brew services` [`list`] (`--json`) (`--debug`): | 
					
						
							|  |  |  |           List information about all managed services for the current user (or root). | 
					
						
							|  |  |  |           Provides more output from Homebrew and `launchctl`(1) or `systemctl`(1) if run with `--debug`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           [`sudo`] `brew services info` (<formula>|`--all`|`--json`): | 
					
						
							|  |  |  |           List all managed services for the current user (or root). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 05:49:26 +00:00
										 |  |  |           [`sudo`] `brew services run` (<formula>|`--all`|`--file=`): | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           Run the service <formula> without registering to launch at login (or boot). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           [`sudo`] `brew services start` (<formula>|`--all`|`--file=`): | 
					
						
							|  |  |  |           Start the service <formula> immediately and register it to launch at login (or boot). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-25 03:43:54 +00:00
										 |  |  |           [`sudo`] `brew services stop` (`--keep`) (`--no-wait`|`--max-wait=`) (<formula>|`--all`): | 
					
						
							|  |  |  |           Stop the service <formula> immediately and unregister it from launching at login (or boot), | 
					
						
							|  |  |  |           unless `--keep` is specified. | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |           [`sudo`] `brew services kill` (<formula>|`--all`): | 
					
						
							|  |  |  |           Stop the service <formula> immediately but keep it registered to launch at login (or boot). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-27 06:10:24 +00:00
										 |  |  |           [`sudo`] `brew services restart` (<formula>|`--all`|`--file=`): | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           Stop (if necessary) and start the service <formula> immediately and register it to launch at login (or boot). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           [`sudo`] `brew services cleanup`: | 
					
						
							|  |  |  |           Remove all unused services. | 
					
						
							|  |  |  |         EOS | 
					
						
							|  |  |  |         flag "--file=", description: "Use the service file from this location to `start` the service." | 
					
						
							|  |  |  |         flag "--sudo-service-user=", description: "When run as root on macOS, run the service(s) as this user." | 
					
						
							|  |  |  |         flag "--max-wait=", description: "Wait at most this many seconds for `stop` to finish stopping a service. " \ | 
					
						
							|  |  |  |                                          "Omit this flag or set this to zero (0) seconds to wait indefinitely." | 
					
						
							|  |  |  |         switch "--all", description: "Run <subcommand> on all services." | 
					
						
							|  |  |  |         switch "--json", description: "Output as JSON." | 
					
						
							|  |  |  |         switch "--no-wait", description: "Don't wait for `stop` to finish stopping the service." | 
					
						
							| 
									
										
										
										
											2025-03-25 03:43:54 +00:00
										 |  |  |         switch "--keep", description: "When stopped, don't unregister the service from launching at login (or boot)." | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |         conflicts "--max-wait=", "--no-wait" | 
					
						
							| 
									
										
										
										
											2025-03-28 08:49:26 +00:00
										 |  |  |         named_args %w[list info run start stop kill restart cleanup] | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       sig { override.void } | 
					
						
							|  |  |  |       def run | 
					
						
							|  |  |  |         # pbpaste's exit status is a proxy for detecting the use of reattach-to-user-namespace | 
					
						
							| 
									
										
										
										
											2025-03-26 11:00:11 -07:00
										 |  |  |         if ENV.fetch("HOMEBREW_TMUX", nil) && File.exist?("/usr/bin/pbpaste") && !quiet_system("/usr/bin/pbpaste") | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           raise UsageError, | 
					
						
							|  |  |  |                 "`brew services` cannot run under tmux!" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Keep this after the .parse to keep --help fast. | 
					
						
							|  |  |  |         require "utils" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         if !Homebrew::Services::System.launchctl? && !Homebrew::Services::System.systemctl? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           raise UsageError, | 
					
						
							|  |  |  |                 "`brew services` is supported only on macOS or Linux (with systemd)!" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (sudo_service_user = args.sudo_service_user) | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |           unless Homebrew::Services::System.root? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |             raise UsageError, | 
					
						
							|  |  |  |                   "`brew services` is supported only when running as root!" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |           unless Homebrew::Services::System.launchctl? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |             raise UsageError, | 
					
						
							|  |  |  |                   "`brew services --sudo-service-user` is currently supported only on macOS " \ | 
					
						
							|  |  |  |                   "(but we'd love a PR to add Linux support)!" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |           Homebrew::Services::Cli.sudo_service_user = sudo_service_user | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Parse arguments. | 
					
						
							| 
									
										
										
										
											2025-03-21 03:48:44 +00:00
										 |  |  |         subcommand, *formulae = args.named | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         no_named_formula_commands = [ | 
					
						
							|  |  |  |           *Homebrew::Services::Commands::List::TRIGGERS, | 
					
						
							|  |  |  |           *Homebrew::Services::Commands::Cleanup::TRIGGERS, | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         if no_named_formula_commands.include?(subcommand) | 
					
						
							| 
									
										
										
										
											2025-03-21 03:48:44 +00:00
										 |  |  |           raise UsageError, "The `#{subcommand}` subcommand does not accept a formula argument!" if formulae.present? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           raise UsageError, "The `#{subcommand}` subcommand does not accept the --all argument!" if args.all? | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if args.file | 
					
						
							| 
									
										
										
										
											2025-03-19 05:49:26 +00:00
										 |  |  |           file_commands = [ | 
					
						
							|  |  |  |             *Homebrew::Services::Commands::Start::TRIGGERS, | 
					
						
							|  |  |  |             *Homebrew::Services::Commands::Run::TRIGGERS, | 
					
						
							| 
									
										
										
										
											2025-03-27 06:10:24 +00:00
										 |  |  |             *Homebrew::Services::Commands::Restart::TRIGGERS, | 
					
						
							| 
									
										
										
										
											2025-03-19 05:49:26 +00:00
										 |  |  |           ] | 
					
						
							|  |  |  |           if file_commands.exclude?(subcommand) | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |             raise UsageError, "The `#{subcommand}` subcommand does not accept the --file= argument!" | 
					
						
							|  |  |  |           elsif args.all? | 
					
						
							| 
									
										
										
										
											2025-03-19 05:49:26 +00:00
										 |  |  |             raise UsageError, | 
					
						
							|  |  |  |                   "The `#{subcommand}` subcommand does not accept the --all and --file= arguments at the same time!" | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-25 03:43:54 +00:00
										 |  |  |         unless Homebrew::Services::Commands::Stop::TRIGGERS.include?(subcommand) | 
					
						
							|  |  |  |           raise UsageError, "The `#{subcommand}` subcommand does not accept the --keep argument!" if args.keep? | 
					
						
							|  |  |  |           raise UsageError, "The `#{subcommand}` subcommand does not accept the --no-wait argument!" if args.no_wait? | 
					
						
							|  |  |  |           if args.max_wait | 
					
						
							|  |  |  |             raise UsageError, "The `#{subcommand}` subcommand does not accept the --max-wait= argument!" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 03:48:44 +00:00
										 |  |  |         opoo "The --all argument overrides provided formula argument!" if formulae.present? && args.all? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         targets = if args.all? | 
					
						
							|  |  |  |           if subcommand == "start" | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |             Homebrew::Services::Formulae.available_services( | 
					
						
							|  |  |  |               loaded:    false, | 
					
						
							|  |  |  |               skip_root: !Homebrew::Services::System.root?, | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           elsif subcommand == "stop" | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |             Homebrew::Services::Formulae.available_services( | 
					
						
							|  |  |  |               loaded:    true, | 
					
						
							|  |  |  |               skip_root: !Homebrew::Services::System.root?, | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           else | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |             Homebrew::Services::Formulae.available_services | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-03-21 03:48:44 +00:00
										 |  |  |         elsif formulae.present? | 
					
						
							|  |  |  |           formulae.map { |formula| Homebrew::Services::FormulaWrapper.new(Formulary.factory(formula)) } | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |         else | 
					
						
							|  |  |  |           [] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Exit successfully if --all was used but there is nothing to do | 
					
						
							|  |  |  |         return if args.all? && targets.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         if Homebrew::Services::System.systemctl? | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |           ENV["DBUS_SESSION_BUS_ADDRESS"] = ENV.fetch("HOMEBREW_DBUS_SESSION_BUS_ADDRESS", nil) | 
					
						
							|  |  |  |           ENV["XDG_RUNTIME_DIR"] = ENV.fetch("HOMEBREW_XDG_RUNTIME_DIR", nil) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Dispatch commands and aliases. | 
					
						
							|  |  |  |         case subcommand.presence | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         when *Homebrew::Services::Commands::List::TRIGGERS | 
					
						
							|  |  |  |           Homebrew::Services::Commands::List.run(json: args.json?) | 
					
						
							|  |  |  |         when *Homebrew::Services::Commands::Cleanup::TRIGGERS | 
					
						
							|  |  |  |           Homebrew::Services::Commands::Cleanup.run | 
					
						
							|  |  |  |         when *Homebrew::Services::Commands::Info::TRIGGERS | 
					
						
							|  |  |  |           Homebrew::Services::Commands::Info.run(targets, verbose: args.verbose?, json: args.json?) | 
					
						
							|  |  |  |         when *Homebrew::Services::Commands::Restart::TRIGGERS | 
					
						
							| 
									
										
										
										
											2025-03-27 06:10:24 +00:00
										 |  |  |           Homebrew::Services::Commands::Restart.run(targets, args.file, verbose: args.verbose?) | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         when *Homebrew::Services::Commands::Run::TRIGGERS | 
					
						
							| 
									
										
										
										
											2025-03-19 05:49:26 +00:00
										 |  |  |           Homebrew::Services::Commands::Run.run(targets, args.file, verbose: args.verbose?) | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         when *Homebrew::Services::Commands::Start::TRIGGERS | 
					
						
							|  |  |  |           Homebrew::Services::Commands::Start.run(targets, args.file, verbose: args.verbose?) | 
					
						
							|  |  |  |         when *Homebrew::Services::Commands::Stop::TRIGGERS | 
					
						
							| 
									
										
										
										
											2025-03-25 03:43:54 +00:00
										 |  |  |           Homebrew::Services::Commands::Stop.run( | 
					
						
							|  |  |  |             targets, | 
					
						
							|  |  |  |             verbose:  args.verbose?, | 
					
						
							|  |  |  |             no_wait:  args.no_wait?, | 
					
						
							|  |  |  |             max_wait: args.max_wait.to_f, | 
					
						
							|  |  |  |             keep:     args.keep?, | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2025-03-14 04:35:30 +00:00
										 |  |  |         when *Homebrew::Services::Commands::Kill::TRIGGERS | 
					
						
							|  |  |  |           Homebrew::Services::Commands::Kill.run(targets, verbose: args.verbose?) | 
					
						
							| 
									
										
										
										
											2025-02-26 13:26:37 +01:00
										 |  |  |         else | 
					
						
							|  |  |  |           raise UsageError, "unknown subcommand: `#{subcommand}`" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |