aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Contributions/cmd/brew-services.rb
diff options
context:
space:
mode:
Diffstat (limited to 'Library/Contributions/cmd/brew-services.rb')
-rwxr-xr-xLibrary/Contributions/cmd/brew-services.rb381
1 files changed, 0 insertions, 381 deletions
diff --git a/Library/Contributions/cmd/brew-services.rb b/Library/Contributions/cmd/brew-services.rb
deleted file mode 100755
index a14dea86f..000000000
--- a/Library/Contributions/cmd/brew-services.rb
+++ /dev/null
@@ -1,381 +0,0 @@
-#!/usr/bin/env ruby -w
-
-# brew-services(1) - Easily start and stop formulae via launchctl
-# ===============================================================
-#
-# ## SYNOPSIS
-#
-# [<sudo>] `brew services` `list`<br>
-# [<sudo>] `brew services` `restart` <formula><br>
-# [<sudo>] `brew services` `start` <formula> [<plist>]<br>
-# [<sudo>] `brew services` `stop` <formula><br>
-# [<sudo>] `brew services` `cleanup`<br>
-#
-# ## DESCRIPTION
-#
-# Integrates homebrew formulae with MacOS X' `launchctl` manager. Services
-# can either be added to `/Library/LaunchDaemons` or `~/Library/LaunchAgents`.
-# Basically items added to `/Library/LaunchDaemons` are started at boot,
-# those in `~/Library/LaunchAgents` at login.
-#
-# When started with `sudo` it operates on `/Library/LaunchDaemons`, else
-# in the user space.
-#
-# Basically on `start` the plist file is generated and written to a `Tempfile`,
-# then copied to the launch path (existing plists are overwritten).
-#
-# ## OPTIONS
-#
-# To access everything quickly, some aliases have been added:
-#
-# * `rm`:
-# Shortcut for `cleanup`, because that's basically whats being done.
-#
-# * `ls`:
-# Because `list` is too much to type :)
-#
-# * `reload', 'r':
-# Alias for `restart`, which gracefully restarts selected service.
-#
-# * `load`, `s`:
-# Alias for `start`, guess what it does...
-#
-# * `unload`, `term`, `t`:
-# Alias for `stop`, stops and unloads selected service.
-#
-# ## SYNTAX
-#
-# Several existing formulae (like mysql, nginx) already write custom plist
-# files to the formulae prefix. Most of these implement `#startup_plist`
-# which then in turn returns a neat-o plist file as string.
-#
-# `brew services` operates on `#startup_plist` as well and requires
-# supporting formulae to implement it. This method should either string
-# containing the generated XML file, or return a `Pathname` instance which
-# points to a plist template, or a hash like:
-#
-# { :url => "https://gist.github.com/raw/534777/63c4698872aaef11fe6e6c0c5514f35fd1b1687b/nginx.plist.xml" }
-#
-# Some simple template parsing is performed, all variables like `{{name}}` are
-# replaced by basically doing:
-# `formula.send('name').to_s if formula.respond_to?('name')`, a bit like
-# mustache. So any variable in the `Formula` is available as template
-# variable, like `{{var}}`, `{{bin}}` usw.
-#
-# ## EXAMPLES
-#
-# Install and start service mysql at boot:
-#
-# $ brew install mysql
-# $ sudo brew services start mysql
-#
-# Stop service mysql (when launched at boot):
-#
-# $ sudo brew services stop mysql
-#
-# Start memcached at login:
-#
-# $ brew install memcached
-# $ brew services start memcached
-#
-# List all running services for current user, and root:
-#
-# $ brew services list
-# $ sudo brew services list
-#
-# ## BUGS
-#
-# `brew-services.rb` might not handle all edge cases, though it tries
-# to fix problems by running `brew services cleanup`.
-#
-module ServicesCli
- class << self
- # Binary name.
- def bin; "brew services" end
-
- # Path to launchctl binary.
- def launchctl; which("launchctl") end
-
- # Wohoo, we are root dude!
- def root?; Process.uid == 0 end
-
- # Current user, i.e. owner of `HOMEBREW_CELLAR`.
- def user; @user ||= %x{/usr/bin/stat -f '%Su' #{HOMEBREW_CELLAR} 2>/dev/null}.chomp || %x{/usr/bin/whoami}.chomp end
-
- # Run at boot.
- def boot_path; Pathname.new("/Library/LaunchDaemons") end
-
- # Run at login.
- def user_path; Pathname.new(ENV['HOME'] + '/Library/LaunchAgents') end
-
- # If root returns `boot_path` else `user_path`.
- def path; root? ? boot_path : user_path end
-
- # Find all currently running services via launchctl list
- def running; %x{#{launchctl} list | grep homebrew.mxcl}.chomp.split("\n").map { |svc| $1 if svc =~ /(homebrew\.mxcl\..+)\z/ }.compact end
-
- # Check if running as homebre and load required libraries et al.
- def homebrew!
- abort("Runtime error: homebrew is required, please start via `#{bin} ...`") unless defined?(HOMEBREW_LIBRARY_PATH)
- %w{fileutils pathname tempfile formula utils}.each { |req| require(req) }
- self.send(:extend, ::FileUtils)
- ::Formula.send(:include, Service::PlistSupport)
- end
-
- # Access current service
- def service; @service ||= Service.new(Formula.factory(@formula)) if @formula end
-
- # Print usage and `exit(...)` with supplied exit code, if code
- # is set to `false`, then exit is ignored.
- def usage(code = 0)
- puts "usage: [sudo] #{bin} [--help] <command> [<formula>]"
- puts
- puts "Small wrapper around `launchctl` for supported formulae, commands available:"
- puts " cleanup Get rid of stale services and unused plists"
- puts " list List all services managed by `#{bin}`"
- puts " restart Gracefully restart selected service"
- puts " start Start selected service"
- puts " stop Stop selected service"
- puts
- puts "Options, sudo and paths:"
- puts
- puts " sudo When run as root, operates on #{boot_path} (run at boot!)"
- puts " Run at boot: #{boot_path}"
- puts " Run at login: #{user_path}"
- puts
- exit(code) unless code == false
- true
- end
-
- # Run and start the command loop.
- def run!
- homebrew!
- usage if ARGV.empty? || ARGV.include?('help') || ARGV.include?('--help') || ARGV.include?('-h')
-
- # parse arguments
- @args = ARGV.reject { |arg| arg[0] == 45 }.map { |arg| arg.include?("/") ? arg : arg.downcase } # 45.chr == '-'
- @cmd = @args.shift
- @formula = @args.shift
-
- # dispatch commands and aliases
- case @cmd
- when 'cleanup', 'clean', 'cl', 'rm' then cleanup
- when 'list', 'ls' then list
- when 'restart', 'relaunch', 'reload', 'r' then check and restart
- when 'start', 'launch', 'load', 's', 'l' then check and start
- when 'stop', 'unload', 'terminate', 'term', 't', 'u' then check and stop
- else
- onoe "Unknown command `#{@cmd}`"
- usage(1)
- end
- end
-
- # Check if formula has been found
- def check
- odie("Formula missing, please provide a formula name") unless service
- true
- end
-
- # List all running services with PID and status and path to plist file, if available
- def list
- opoo("No %s services controlled by `#{bin}` running..." % [root? ? 'root' : 'user-space']) and return if running.empty?
- running.each do |label|
- if svc = Service.from(label)
- status = !svc.dest.file? ? "#{Tty.red}stale " : "#{Tty.white}started"
- puts "%-10.10s %s#{Tty.reset} %7s %s" % [svc.name, status, svc.pid ? svc.pid.to_s : '-', svc.dest.file? ? svc.dest : label]
- else
- puts "%-10.10s #{Tty.red}unknown#{Tty.reset} %7s #{label}" % ["?", "-"]
- end
- end
- end
-
- # Kill services without plist file and remove unused plists
- def cleanup
- cleaned = []
-
- # 1. kill services which have no plist file
- running.each do |label|
- if svc = Service.from(label)
- if !svc.dest.file?
- puts "%-15.15s #{Tty.white}stale#{Tty.reset} => killing service..." % svc.name
- kill(svc)
- cleaned << label
- end
- else
- opoo "Service #{label} not managed by `#{bin}` => skipping"
- end
- end
-
- # 2. remove unused plist files
- Dir[path + 'homebrew.mxcl.*.plist'].each do |file|
- unless running.include?(File.basename(file).sub(/\.plist$/i, ''))
- puts "Removing unused plist #{file}"
- rm file
- cleaned << file
- end
- end
-
- puts "All #{root? ? 'root' : 'user-space'} services OK, nothing cleaned..." if cleaned.empty?
- end
-
- # Stop if loaded, then start again
- def restart
- stop if service.loaded?
- start
- end
-
- # Start a service
- def start
- odie "Service `#{service.name}` already started, use `#{bin} restart #{service.name}`" if service.loaded?
-
- custom_plist = @args.first
- if custom_plist
- if custom_plist =~ %r{\Ahttps?://.+}
- custom_plist = { :url => custom_plist }
- elsif File.exist?(custom_plist)
- custom_plist = Pathname.new(custom_plist)
- else
- odie "#{custom_plist} is not a url or exising file"
- end
- end
-
- odie "Formula `#{service.name}` not installed, #startup_plist not implemented or no plist file found" if !custom_plist && !service.plist?
-
- temp = Tempfile.new(service.label)
- temp << service.generate_plist(custom_plist)
- temp.flush
-
- rm service.dest if service.dest.exist?
- service.dest_dir.mkpath unless service.dest_dir.directory?
- cp temp.path, service.dest
-
- # clear tempfile
- temp.close
-
- safe_system launchctl, "load", "-w", service.dest.to_s
- $?.to_i != 0 ? odie("Failed to start `#{service.name}`") : ohai("Successfully started `#{service.name}` (label: #{service.label})")
- end
-
- # Stop a service or kill if no plist file available...
- def stop
- unless service.loaded?
- rm service.dest if service.dest.exist? # get rid of installed plist anyway, dude
- odie "Service `#{service.name}` not running, wanna start it? Try `#{bin} start #{service.name}`"
- end
-
- if service.dest.exist?
- puts "Stopping `#{service.name}`... (might take a while)"
- safe_system launchctl, "unload", "-w", service.dest.to_s
- $?.to_i != 0 ? odie("Failed to stop `#{service.name}`") : ohai("Successfully stopped `#{service.name}` (label: #{service.label})")
- else
- puts "Stopping stale service `#{service.name}`... (might take a while)"
- kill(service)
- end
- rm service.dest if service.dest.exist?
- end
-
- # Kill service without plist file by issuing a `launchctl remove` command
- def kill(svc)
- safe_system launchctl, "remove", svc.label
- odie("Failed to remove `#{svc.name}`, try again?") unless $?.to_i == 0
- while svc.loaded?
- puts " ...checking status"
- sleep(5)
- end
- ohai "Successfully stopped `#{svc.name}` via #{svc.label}"
- end
- end
-end
-
-# Wrapper for a formula to handle service related stuff like parsing
-# and generating the plist file.
-class Service
-
- # Support module which will be used to extend Formula with a method :)
- module PlistSupport
- # As a replacement value for `<key>UserName</key>`.
- def startup_user; ServicesCli.user end
- end
-
- # Access the `Formula` instance
- attr_reader :formula
-
- # Create a new `Service` instance from either a path or label.
- def self.from(path_or_label)
- return nil unless path_or_label =~ /homebrew\.mxcl\.([^\.]+)(\.plist)?\z/
- new(Formula.factory($1)) rescue nil
- end
-
- # Initialize new `Service` instance with supplied formula.
- def initialize(formula); @formula = formula end
-
- # Delegate access to `formula.name`.
- def name; @name ||= formula.name end
-
- # Label delegates to formula.plist_name, e.g `homebrew.mxcl.<formula>`.
- def label; @label ||= formula.plist_name end
-
- # Path to a static plist file, this is always `homebrew.mxcl.<formula>.plist`.
- def plist; @plist ||= formula.prefix + "#{label}.plist" end
-
- # Path to destination plist directory, if run as root it's `boot_path`, else `user_path`.
- def dest_dir; (ServicesCli.root? ? ServicesCli.boot_path : ServicesCli.user_path) end
-
- # Path to destination plist, if run as root it's in `boot_path`, else `user_path`.
- def dest; dest_dir + "#{label}.plist" end
-
- # Returns `true` if formula implements #startup_plist or file exists.
- def plist?; formula.installed? && (plist.file? || formula.respond_to?(:startup_plist)) end
-
- # Returns `true` if service is loaded, else false.
- def loaded?; %x{#{ServicesCli.launchctl} list | grep #{label} 2>/dev/null}.chomp =~ /#{label}\z/ end
-
- # Get current PID of daemon process from launchctl.
- def pid
- status = %x{#{ServicesCli.launchctl} list | grep #{label} 2>/dev/null}.chomp
- return $1.to_i if status =~ /\A([\d]+)\s+.+#{label}\z/
- end
-
- # Generate that plist file, dude.
- def generate_plist(data = nil)
- data ||= plist.file? ? plist : formula.startup_plist
-
- if data.respond_to?(:file?) && data.file?
- data = data.read
- elsif data.respond_to?(:keys) && data.keys.include?(:url)
- require 'open-uri'
- data = open(data).read
- end
-
- # replace "template" variables and ensure label is always, always homebrew.mxcl.<formula>
- data = data.to_s.gsub(/\{\{([a-z][a-z0-9_]*)\}\}/i) { |m| formula.send($1).to_s if formula.respond_to?($1) }.
- gsub(%r{(<key>Label</key>\s*<string>)[^<]*(</string>)}, '\1' + label + '\2')
-
- # and force fix UserName, if necessary
- if formula.startup_user != "root" && data =~ %r{<key>UserName</key>\s*<string>root</string>}
- data = data.gsub(%r{(<key>UserName</key>\s*<string>)[^<]*(</string>)}, '\1' + formula.startup_user + '\2')
- elsif ServicesCli.root? && formula.startup_user != "root" && data !~ %r{<key>UserName</key>}
- data = data.gsub(%r{(</dict>\s*</plist>)}, " <key>UserName</key><string>#{formula.startup_user}</string>\n\\1")
- end
-
- if ARGV.verbose?
- ohai "Generated plist for #{formula.name}:"
- puts " " + data.gsub("\n", "\n ")
- puts
- end
-
- data
- end
-end
-
-# Start the cli dispatch stuff.
-#
-
-opoo <<-EOS.undent
- brew services is unsupported and will be removed soon.
- You should use launchctl instead.
- Please feel free volunteer to support it in a tap.
-
-EOS
-
-ServicesCli.run!