Module: Locd::Launchctl
- Includes:
- NRSER::Log::Mixin
- Defined in:
- lib/locd/launchctl.rb
Overview
Thin wrapper to run launchctl
commands and capture outputs via Cmds.
Basically just centralizes this stuff all in one place and presents a friendly API, does some common debug logging, etc.
Defined Under Namespace
Classes: Error, ParseError
Constant Summary collapse
- OPTS_MAP =
Mappings between any option keyword names we use to the ones
launchctl
expects. { force: :F, write: :w, }
- STATUS_RE =
Regexp to parse
launchd list
output for #status method. /^(?<pid>\S+)\s+(?<status>\S+)\s+(?<label>\S+)/.freeze
- STATUS_CACHE_TIMEOUT_SEC =
Max number of seconds to
5
- @@bin =
launchctl
executable to use. You shouldn't need to touch this, but might be useful for testing or if you have path weirdness. 'launchctl'
Class Method Summary collapse
- .disabled ⇒ Object
-
.execute(subcmd, *args, **opts) ⇒ Cmds::Result
Run a
launchctl
subcommand with positional args and options. -
.execute!(*args) ⇒ Cmds::Result
Just like #execute but will raise if the exit status is not
0
. -
.map_opts(**opts) ⇒ Hash<Symbol, V>
Swap any keys in OPTS_MAP.
- .refresh_status? ⇒ Boolean
-
.status(refresh: refresh_status? ) ⇒ Hash<String, {pid: Fixnum?, status: Fixnum?}>
Map of agent labels to hash with process ID (if any) and last exit code (if any).
Class Method Details
.disabled ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/locd/launchctl.rb', line 160 def self.disabled uid = Cmds.chomp! 'id -u' result = execute :'print-disabled', "user/#{ uid }" lines = if match = /disabled\ services\ =\ \{([^\}]+)\}/.match( result.out ) match[1].lines else raise ParseError.new binding.erb <<~END Unable to find 'disabled services' section output Command: <%= result.cmd %> Output: <%= result.out %> END end lines end |
.execute(subcmd, *args, **opts) ⇒ Cmds::Result
Run a launchctl
subcommand with positional args and options.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/locd/launchctl.rb', line 119 def self.execute subcmd, *args, **opts logger.debug "Executing `#{ @@bin } #{ subcmd }` command", args: args, opts: opts result = Cmds "#{ @@bin } #{ subcmd } <%= opts %> <%= *args %>", *args, opts: map_opts( **opts ) logger.debug "`#{ @@bin }` command result", cmd: result.cmd, status: result.status, out: NRSER.ellipsis( result.out.lines, 20 ), err: NRSER.ellipsis( result.err.lines, 20 ) result end |
.execute!(*args) ⇒ Cmds::Result
Just like #execute but will raise if the exit status is not 0
.
144 145 146 |
# File 'lib/locd/launchctl.rb', line 144 def self.execute! *args execute( *args ).assert end |
.map_opts(**opts) ⇒ Hash<Symbol, V>
Swap any keys in OPTS_MAP.
93 94 95 96 97 98 99 100 101 |
# File 'lib/locd/launchctl.rb', line 93 def self.map_opts **opts opts.map { |key, value| if OPTS_MAP[key] [OPTS_MAP[key], value] else [key, value] end }.to_h end |
.refresh_status? ⇒ Boolean
185 186 187 188 |
# File 'lib/locd/launchctl.rb', line 185 def self.refresh_status? @last_status_time.nil? || (Time.now - @last_status_time) > STATUS_CACHE_TIMEOUT_SEC end |
.status(refresh: refresh_status? ) ⇒ Hash<String, {pid: Fixnum?, status: Fixnum?}>
Returns Map of agent labels to hash with process ID (if any) and last exit code (if any).
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/locd/launchctl.rb', line 199 def self.status refresh: refresh_status? if refresh || @status.nil? @status = execute!( :list ).out.lines.rest. map { |line| if match = STATUS_RE.match( line ) pid = case match[:pid] when '-' nil when /^\d+$/ match[:pid].to_i else logger.error "Unexpected `pid` parse in {#status}", captures: match.captures, line: line nil end status = case match[:status] when /^\-?\d+$/ match[:status].to_i else logger.error "Unexpected `status` parse in {#status}", captures: match.captures, line: line nil end label = match[:label].freeze [label, {pid: pid, status: status}] else logger.error "FAILED to parse status line", line: line nil end }. reject( &:nil? ). to_h # Set the time so we can compare next call @last_status_time = Time.now end @status end |