Class: Locd::Agent
- Inherits:
-
Object
- Object
- Locd::Agent
- Includes:
- Comparable, NRSER::Log::Mixin
- Defined in:
- lib/locd/agent.rb
Overview
Represents a backend agent that the proxy can route to.
Agents are managed by launchd
, macOS's system service manager, and
created on-demand by generating "property list" files (.plist
, XML format)
and installing them in the user's ~/Library/LaunchAgents
directory.
From there they can be managed directly with macOS's launchctl
utility,
with the lunchy
gem, etc.
Defined Under Namespace
Modules: System Classes: Job, Proxy, RotateLogs, Site
Constant Summary collapse
- TO_H_NAMES =
Attribute / method names that #to_h uses.
Hamster::SortedSet[:label, :path, :plist, :status]
Creating Agents collapse
-
#path ⇒ Pathname
readonly
Absolute path to the agent's
.plist
file. -
#plist ⇒ Hash<String, V>
readonly
Hash of the agent's
.plist
file (keys and values from the top-level<dict>
element).
Computing Paths collapse
-
.default_log_path(workdir, label) ⇒ Pathname?
Default path for a Agent output file.
-
.plist_abs_path(label) ⇒ Pathname
Absolute path for the plist file given it's label, equivalent to expanding
~/Library/LaunchAgents/<label>.plist
. -
.resolve_log_path(log_path:, workdir:, label:) ⇒ Pathname?
Get the path to log
STDOUT
andSTDERR
to given the option value and other options we need to figure it out if that doesn't suffice. -
.user_plist_abs_dir ⇒ Pathname
Absolute path to
launchd
plist directory for the current user, which is an expansion of~/Library/LaunchAgents
.
Instantiating collapse
-
.class_for(plist) ⇒ Class<Locd::Agent>?
Get the agent class that a plist should be instantiated as.
-
.from_path(plist_path) ⇒ Locd::Agent
Instantiate a Agent from the path to it's
.plist
file.
Querying collapse
-
.all ⇒ Hamster::Hash<String, Locd::Agent>
All Agent that are instances of
self
. -
.exists?(label) ⇒ Boolean
Does this agent exist?.
-
.find_all(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Find all the agents that match a pattern.
-
.find_all!(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Just like Agent.find_all but raises if result is empty.
-
.find_only!(*find_all_args) ⇒ Locd::Agent
Find a single Agent matching
pattern
or raise. -
.get(label) ⇒ Locd::Agent?
Get a Loc'd agent by it's label.
-
.labels ⇒ Hamster::Vector<String>
Labels for all installed Loc'd agents.
-
.plists ⇒ Hamster::Hash<String, Locd::Agent>
All installed Loc'd agents.
Creating Agents collapse
-
.add(label:, force: false, workdir: Pathname.getwd, **kwds) ⇒ Locd::Agent
Add an agent, writing a
.plist
to~/Library/LaunchAgents
. -
.add_or_update(label:, **values) ⇒ Array<((:add | :update), Locd::Agent)] Whether the agent was added or updated, followed by the agent instance.
Pretty much what the name says.
-
.create_plist_data(cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras) ⇒ Hash<String, Object>
Create the
launchd
property list data for a new Agent. -
.render_cmd(cmd_template:, label:, workdir:, **extras) ⇒ String
Render a command string by substituting any
{name}
parts for their values. -
#default_log_path? ⇒ Boolean
true
if the #log_path is the default one we generate. -
#initialize(plist:, path:) ⇒ Agent
constructor
Constructor ============================================================================.
Instance Methods: Attribute Readers collapse
- #cmd_template ⇒ Object
- #config ⇒ Object
-
#err_path ⇒ Pathname?
Path the agent is logging
STDERR
to. - #label ⇒ String
- #last_exit_code(refresh: false) ⇒ nil, Fixnum
-
#log_paths ⇒ Array<Pathname>
Get a list of all unique log paths.
-
#out_path ⇒ Pathname?
(also: #log_path)
Path the agent is logging
STDOUT
to. -
#pid(refresh: false) ⇒ nil, Fixnum
Current process ID of the agent (if running).
- #running?(refresh: false) ⇒ Boolean
- #stopped?(refresh: false) ⇒ Boolean
-
#workdir ⇒ Pathname
The working directory of the agent.
Instance Methods: `launchctl` Interface collapse
-
#load(force: false, enable: false) ⇒ self
Load the agent by executing
launchctl load [OPTIONS] LABEL
. - #reload(force: false, enable: false) ⇒ self
-
#remove(logs: false) ⇒ self
Remove the agent by removing it's #path file.
- #restart(reload: true, force: true, enable: false) ⇒ self
-
#start(load: true, force: false, enable: false) ⇒ self
Start the agent.
-
#status(refresh: false) ⇒ Hash{pid: (Fixnum | nil), status: (Fixnum | nil)}?
The agent's status from parsing
launchctl list
. -
#stop(unload: true, disable: false) ⇒ self
Stop the agent.
-
#unload(disable: false) ⇒ self
Unload the agent by executing
launchctl unload [OPTIONS] LABEL
.
Modifying Agents collapse
-
#update(force: false, **values) ⇒ Locd::Agent
Update specific values on the agent, which may change it's file path if a different label is provided.
Instance Methods: Language Integration collapse
-
#<=>(other) ⇒ Fixnum
Compare to another agent by their labels.
- #to_h ⇒ Object
- #to_json(*args) ⇒ Object
-
#to_yaml(*args) ⇒ Object
TODO Doesn't work!.
Class Method Summary collapse
-
.plist?(plist) ⇒ Boolean
Test if the parse of a property list is for a Loc'd agent by seeing if it has the config key (from
Locd.config[:agent, :config_key]
) as a key.
Constructor Details
#initialize(plist:, path:) ⇒ Agent
Constructor
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 |
# File 'lib/locd/agent.rb', line 697 def initialize plist:, path: @path = path.to_pn. @plist = plist @status = :UNKNOWN # Sanity check... unless plist.key? Locd.config[:agent, :config_key] raise ArgumentError.new binding.erb " Not a Loc'd plist (no <%= Locd.config[:agent, :config_key] %> key)\n \n path: <%= path %>\n plist:\n \n <%= plist.pretty_inspect %>\n \n END\n end\n \n unless @path.basename( '.plist' ).to_s == label\n raise ArgumentError.new binding.erb <<~END\n Label and filename don't match.\n \n Filename should be `<label>.plist`, found\n \n label: <%= label %>\n filename: <%= @path.basename %>\n path: <%= path %>\n \n END\n end\n \n init_ensure_out_dirs_exist\nend\n" |
Instance Attribute Details
#path ⇒ Pathname (readonly)
Absolute path to the agent's .plist
file.
691 692 693 |
# File 'lib/locd/agent.rb', line 691 def path @path end |
#plist ⇒ Hash<String, V> (readonly)
Hash of the agent's .plist
file (keys and values from the top-level
<dict>
element).
684 685 686 |
# File 'lib/locd/agent.rb', line 684 def plist @plist end |
Class Method Details
.add(label:, force: false, workdir: Pathname.getwd, **kwds) ⇒ Locd::Agent
Add an agent, writing a .plist
to ~/Library/LaunchAgents
.
Does not start the agent.
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
# File 'lib/locd/agent.rb', line 605 def self.add label:, force: false, workdir: Pathname.getwd, **kwds logger.debug "Creating {Agent}...", label: label, force: force, workdir: workdir, **kwds plist_abs_path = self.plist_abs_path label # Handle file already existing if File.exists? plist_abs_path logger.debug "Agent already exists!", label: label, path: plist_abs_path if force logger.info "Forcing agent creation (overwrite)", label: label, path: plist_abs_path else raise binding.erb " Agent <%= label %> already exists at:\n \n <%= plist_abs_path %>\n \n END\n end\n end\n\n plist_data = create_plist_data label: label, workdir: workdir, **kwds\n logger.debug \"Property list created\", data: plist_data\n \n plist_string = Plist::Emit.dump plist_data\n plist_abs_path.write plist_string\n logger.debug \"Property list written\", path: plist_abs_path\n \n from_path( plist_abs_path ).tap { |agent|\n agent.send :log_info, \"added\"\n }\nend\n" |
.add_or_update(label:, **values) ⇒ Array<((:add | :update), Locd::Agent)] Whether the agent was added or updated, followed by the agent instance.
Pretty much what the name says.
664 665 666 667 668 669 670 |
# File 'lib/locd/agent.rb', line 664 def self.add_or_update label:, **values if exists? label [:update, get( label ).update( **values )] else [:add, add( label: label, **values )] end end |
.all ⇒ Hamster::Hash<String, Locd::Agent>
All Locd::Agent that are instances of self
.
So, when invoked through all returns all agents.
When invoked from a Locd::Agent subclass, returns all agents that are instances of that subclass - Locd::Agent::Site.all returns all Locd::Agent that are Site instances.
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/locd/agent.rb', line 292 def self.all # If we're being invoked through {Locd::Agent} itself actually find and # load everyone if self == Locd::Agent plists.each_pair.map { |path, plist| begin agent_class = class_for plist if agent_class.nil? nil else agent = agent_class.new path: path, plist: plist [agent.label, agent] end rescue Exception => error logger.error "Failed to parse Loc'd Agent plist", path: path, error: error, backtrace: error.backtrace nil end }. compact. thru( &Hamster::Hash.method( :new ) ) else # We're being invoked through a {Locd::Agent} subclass, so invoke # through {Locd::Agent} and filter the results to instance of `self`. Locd::Agent.all.select { |label, agent| agent.is_a? self } end end |
.class_for(plist) ⇒ Class<Locd::Agent>?
Get the agent class that a plist should be instantiated as.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/locd/agent.rb', line 197 def self.class_for plist # 1. See if it's a Loc'd system agent plist if system_class = Locd::Agent::System.class_for( plist ) return system_class end # 2. See if it's a Loc'd user agent plist if user_class = [ Locd::Agent::Site, Locd::Agent::Job, ].find_bounded( max: 1 ) { |cls| cls.plist? plist }.first return user_class end # 3. Return a vanilla agent if it's a plist for one # # Really, not sure when this would happen... # if Locd::Agent.plist?( plist ) return Locd::Agent end # Nada nil end |
.create_plist_data(cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras) ⇒ Hash<String, Object>
Create the launchd
property list data for a new Locd::Agent.
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
# File 'lib/locd/agent.rb', line 485 def self.create_plist_data cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras # Configure daemon variables... # Normalize `workdir` to an expanded {Pathname} workdir = workdir.to_pn. # Resolve the log (`STDOUT` & `STDERR`) path log_path = resolve_log_path( log_path: log_path, workdir: workdir, label: label, ).to_s # Interpolate variables into command template cmd = render_cmd( cmd_template: cmd_template, label: label, workdir: workdir, **extras ) # Form the property list hash { # Unique label, format: `locd.<owner>.<name>.<agent.path...>` 'Label' => label, # What to run 'ProgramArguments' => [ # TODO Maybe this should be configurable or smarter in some way? 'bash', # *login* shell... need this to source the user profile and set up # all the ENV vars '-l', # Run the command in the login shell '-c', cmd, ], # Directory to run the command in 'WorkingDirectory' => workdir.to_s, # Where to send STDOUT 'StandardOutPath' => log_path, # Where to send STDERR (we send both to the same file) 'StandardErrorPath' => log_path, # Bring the process back up if it goes down (has backoff and stuff # built-in) 'KeepAlive' => keep_alive, # Start the process when the plist is loaded 'RunAtLoad' => run_at_load, 'ProcessType' => 'Interactive', # Extras we need... `launchd` complains in the system log about this # but it's the easiest way to handle it at the moment Locd.config[:agent, :config_key] => { # Save this too why the hell not, might help debuging at least cmd_template: cmd_template, # Add subclass-specific extras **extras, }.str_keys, # Stuff that *doesn't* work... so you don't try it again, because # Apple's online docs seems totally out of date. # # Not allowed for user agents # 'UserName' => ENV['USER'], # # "The Debug key is no longer respected. Please remove it." # 'Debug' => true, # # Yeah, it would have been nice to just use the plist to store the port, # but this runs into all sorts of impenetrable security mess... gotta # put it somewhere else! Weirdly enough it just totally works outside # of here, so I'm not what the security is really stopping..? # # 'Sockets' => { # 'Listeners' => { # 'SockNodeName' => BIND, # 'SockServiceName' => port, # 'SockType' => 'stream', # 'SockFamily' => 'IPv4', # }, # }, }.reject { |key, value| value.nil? } # Drop any `nil` values end |
.default_log_path(workdir, label) ⇒ Pathname?
Default path for a Locd::Agent output file.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/locd/agent.rb', line 117 def self.default_log_path workdir, label tmp_dir = workdir.find_up 'tmp', test: :directory?, result: :path if tmp_dir.nil? logger.warn "Unable to find `tmp` dir, output will not be redirected", workdir: workdir, label: label, stream: stream return nil end unless tmp_dir.writable? logger.warn \ "Found `tmp` dir, but not writable. Output will not be redirected", workdir: workdir, label: label, stream: stream return nil end tmp_dir / 'locd' / "#{ label }.log" end |
.exists?(label) ⇒ Boolean
Does this agent exist?
274 275 276 |
# File 'lib/locd/agent.rb', line 274 def self.exists? label File.exists? plist_abs_path( label ) end |
.find_all(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Find all the agents that match a pattern.
385 386 387 388 |
# File 'lib/locd/agent.rb', line 385 def self.find_all pattern, ** pattern = Locd::Pattern.from pattern, ** all.select { |label, agent| pattern.match? agent } end |
.find_all!(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Just like find_all but raises if result is empty.
401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/locd/agent.rb', line 401 def self.find_all! pattern, ** pattern = Locd::Pattern.from pattern, ** find_all( pattern ).tap { |agents| if agents.empty? raise NRSER::CountError.new( "No agents found for pattern from #{ pattern.source.inspect }", subject: agents, expected: '#count > 0', ) end } end |
.find_only!(*find_all_args) ⇒ Locd::Agent
Find a single Locd::Agent matching pattern
or raise.
Parameters are passed to Label.regexp_for_glob and the resulting Regexp is matched against each agent's #label.
366 367 368 |
# File 'lib/locd/agent.rb', line 366 def self.find_only! *find_all_args find_all( *find_all_args ).values.to_a.only! end |
.from_path(plist_path) ⇒ Locd::Agent
Instantiate a Locd::Agent from the path to it's .plist
file.
231 232 233 234 235 |
# File 'lib/locd/agent.rb', line 231 def self.from_path plist_path plist = Plist.parse_xml plist_path.to_s class_for( plist ).new plist: plist, path: plist_path end |
.get(label) ⇒ Locd::Agent?
Get a Loc'd agent by it's label.
346 347 348 349 350 351 352 |
# File 'lib/locd/agent.rb', line 346 def self.get label path = plist_abs_path label if path.file? from_path path end end |
.labels ⇒ Hamster::Vector<String>
Labels for all installed Loc'd agents.
330 331 332 |
# File 'lib/locd/agent.rb', line 330 def self.labels all.keys.sort end |
.plist?(plist) ⇒ Boolean
Test if the parse of a property list is for a Loc'd agent by seeing if
it has the config key (from Locd.config[:agent, :config_key]
) as a key.
72 73 74 |
# File 'lib/locd/agent.rb', line 72 def self.plist? plist plist.key? Locd.config[:agent, :config_key] end |
.plist_abs_path(label) ⇒ Pathname
Absolute path for the plist file given it's label, equivalent to expanding
~/Library/LaunchAgents/<label>.plist
.
98 99 100 |
# File 'lib/locd/agent.rb', line 98 def self.plist_abs_path label user_plist_abs_dir / "#{ label }.plist" end |
.plists ⇒ Hamster::Hash<String, Locd::Agent>
All installed Loc'd agents.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/locd/agent.rb', line 248 def self.plists Pathname.glob( user_plist_abs_dir / '*.plist' ). map { |path| begin [path, Plist.parse_xml( path.to_s )] rescue Exception => error logger.trace "{Plist.parse_xml} failed to parse plist", path: path.to_s, error: error, backtrace: error.backtrace nil end }. compact. select { |path, plist| plist? plist }. thru( &Hamster::Hash.method( :new ) ) end |
.render_cmd(cmd_template:, label:, workdir:, **extras) ⇒ String
Render a command string by substituting any {name}
parts for their
values.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/locd/agent.rb', line 449 def self.render_cmd cmd_template:, label:, workdir:, **extras t.match cmd_template, Array, ->( array ) { array.map {|arg| render_cmd \ cmd_template: arg, label: label, workdir: workdir, **extras }.shelljoin }, String, ->( string ) { { label: label, workdir: workdir, **extras, }.reduce( string ) do |cmd, (key, value)| cmd.gsub "{#{ key }}", value.to_s.shellescape end } end |
.resolve_log_path(log_path:, workdir:, label:) ⇒ Pathname?
Get the path to log STDOUT
and STDERR
to given the option value and other
options we need to figure it out if that doesn't suffice.
Note that we might not figure it out at all (returning nil
), and that
needs to be ok too (though it's not expected to happen all too often).
165 166 167 168 169 170 171 |
# File 'lib/locd/agent.rb', line 165 def self.resolve_log_path log_path:, workdir:, label: if log_path.nil? default_log_path workdir, label else log_path.to_pn. workdir end end |
.user_plist_abs_dir ⇒ Pathname
Absolute path to launchd
plist directory for the current user, which is
an expansion of ~/Library/LaunchAgents
.
85 86 87 |
# File 'lib/locd/agent.rb', line 85 def self.user_plist_abs_dir Pathname.new( '~/Library/LaunchAgents' ). end |
Instance Method Details
#<=>(other) ⇒ Fixnum
Compare to another agent by their labels.
1162 1163 1164 |
# File 'lib/locd/agent.rb', line 1162 def <=> other Locd::Label.compare label, other.label end |
#cmd_template ⇒ Object
799 800 801 |
# File 'lib/locd/agent.rb', line 799 def cmd_template config['cmd_template'] end |
#config ⇒ Object
794 795 796 |
# File 'lib/locd/agent.rb', line 794 def config plist[Locd.config[:agent, :config_key]] end |
#default_log_path? ⇒ Boolean
Returns true
if the #log_path is the default one we generate.
739 740 741 |
# File 'lib/locd/agent.rb', line 739 def default_log_path? log_path == self.class.default_log_path( workdir, label ) end |
#err_path ⇒ Pathname?
Path the agent is logging STDERR
to.
834 835 836 |
# File 'lib/locd/agent.rb', line 834 def err_path plist['StandardErrorPath'].to_pn if plist['StandardErrorPath'] end |
#label ⇒ String
752 753 754 |
# File 'lib/locd/agent.rb', line 752 def label plist['Label'].freeze end |
#last_exit_code(refresh: false) ⇒ nil, Fixnum
789 790 791 |
# File 'lib/locd/agent.rb', line 789 def last_exit_code refresh: false status( refresh: refresh ) && status[:last_exit_code] end |
#load(force: false, enable: false) ⇒ self
Load the agent by executing launchctl load [OPTIONS] LABEL
.
This is a bit low-level; you probably want to use #start.
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
# File 'lib/locd/agent.rb', line 908 def load force: false, enable: false logger.debug "Loading #{ label } agent...", force: force, enable: enable result = Locd::Launchctl.load! path, force: force, write: enable = if result.err =~ /service\ already\ loaded/ "already loaded" else "LOADED" end log_info , status: status self end |
#log_paths ⇒ Array<Pathname>
Get a list of all unique log paths.
843 844 845 846 847 848 |
# File 'lib/locd/agent.rb', line 843 def log_paths [ out_path, err_path, ].compact.uniq end |
#out_path ⇒ Pathname? Also known as: log_path
Path the agent is logging STDOUT
to.
819 820 821 |
# File 'lib/locd/agent.rb', line 819 def out_path plist['StandardOutPath'].to_pn if plist['StandardOutPath'] end |
#pid(refresh: false) ⇒ nil, Fixnum
Current process ID of the agent (if running).
767 768 769 |
# File 'lib/locd/agent.rb', line 767 def pid refresh: false status( refresh: refresh ) && status[:pid] end |
#reload(force: false, enable: false) ⇒ self
950 951 952 953 |
# File 'lib/locd/agent.rb', line 950 def reload force: false, enable: false unload load force: force, enable: enable end |
#remove(logs: false) ⇒ self
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
# File 'lib/locd/agent.rb', line 1032 def remove logs: false stop unload: true if logs log_paths.each { |log_path| if log_path.exists? FileUtils.rm log_path log_info "Removed log", path: log_path.to_s else log_info "Log path does not exist", path: log_path.to_s end } end FileUtils.rm path log_info "REMOVED" self end |
#restart(reload: true, force: true, enable: false) ⇒ self
1018 1019 1020 1021 |
# File 'lib/locd/agent.rb', line 1018 def restart reload: true, force: true, enable: false stop unload: reload start load: reload, force: force, enable: enable end |
#running?(refresh: false) ⇒ Boolean
772 773 774 |
# File 'lib/locd/agent.rb', line 772 def running? refresh: false status( refresh: refresh )[:running] end |
#start(load: true, force: false, enable: false) ⇒ self
Start the agent.
If load
is true
, calls #load first, and defaults it's force
keyword
argument to true
(the idea being that you actually want the agent to
start, even if it's #disabled?).
970 971 972 973 974 975 976 977 978 979 980 981 982 |
# File 'lib/locd/agent.rb', line 970 def start load: true, force: false, enable: false logger.trace "Starting `#{ label }` agent...", load: load, force: force, enable: enable self.load( force: force, enable: enable ) if load Locd::Launchctl.start! label log_info "STARTED" self end |
#status(refresh: false) ⇒ Hash{pid: (Fixnum | nil), status: (Fixnum | nil)}?
The agent's status from parsing launchctl list
.
Status is read on demand and cached on the instance.
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 |
# File 'lib/locd/agent.rb', line 870 def status refresh: false if refresh || @status == :UNKNOWN raw_status = Locd::Launchctl.status[label] # Happens when the agent is not loaded @status = if raw_status.nil? { loaded: false, running: false, pid: nil, last_exit_code: nil, } else { loaded: true, running: !raw_status[:pid].nil?, pid: raw_status[:pid], last_exit_code: raw_status[:status], } end end @status end |
#stop(unload: true, disable: false) ⇒ self
Stop the agent.
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 |
# File 'lib/locd/agent.rb', line 994 def stop unload: true, disable: false logger.debug "Stopping `#{ label } agent...`", unload: unload, disable: disable Locd::Launchctl.stop label log_info "STOPPED" self.unload( disable: disable ) if unload self end |
#stopped?(refresh: false) ⇒ Boolean
777 778 779 |
# File 'lib/locd/agent.rb', line 777 def stopped? refresh: false !running?( refresh: refresh ) end |
#to_h ⇒ Object
1167 1168 1169 1170 1171 |
# File 'lib/locd/agent.rb', line 1167 def to_h self.class::TO_H_NAMES.map { |name| [name, send( name )] }.to_h end |
#to_json(*args) ⇒ Object
1174 1175 1176 |
# File 'lib/locd/agent.rb', line 1174 def to_json *args to_h.to_json *args end |
#to_yaml(*args) ⇒ Object
TODO Doesn't work!
1181 1182 1183 |
# File 'lib/locd/agent.rb', line 1181 def to_yaml *args to_h.to_yaml *args end |
#unload(disable: false) ⇒ self
Unload the agent by executing launchctl unload [OPTIONS] LABEL
.
This is a bit low-level; you probably want to use #stop.
934 935 936 937 938 939 940 941 942 |
# File 'lib/locd/agent.rb', line 934 def unload disable: false logger.debug "Unloading #{ label } agent...", disable: disable result = Locd::Launchctl.unload! path, write: disable log_info "UNLOADED" self end |
#update(force: false, **values) ⇒ Locd::Agent
Update specific values on the agent, which may change it's file path if a different label is provided.
Does not mutate this instance! Returns a new Locd::Agent with the updated values.
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 |
# File 'lib/locd/agent.rb', line 1073 def update force: false, **values logger.trace "Updating `#{ label }` agent", **values # Remove the `cmd_template` if it's nil of an empty array so that # we use the current one if values[:cmd_template].nil? || (values[:cmd_template].is_a?( Array ) && values[:cmd_template].empty?) values.delete :cmd_template end # Make a new plist new_plist_data = self.class.create_plist_data( label: label, workdir: workdir, log_path: ( values.key?( :log_path ) ? values.key?( :log_path ) : ( default_log_path? ? nil : log_path ) ), # Include the config values, which have the `cmd_template` as well as # any extras. Need to symbolize the keys to make the kwds call work **config.sym_keys, # Now merge over with the values we received **values.except( :log_path ) ) new_label = new_plist_data['Label'] new_plist_abs_path = self.class.plist_abs_path new_label if new_label == label # We maintained the same label, overwrite the file path.write Plist::Emit.dump( new_plist_data ) # Load up the new agent from the same path, reload and return it self.class.from_path( path ).reload else # The label has changed, so we want to make sure we're not overwriting # another agent if File.exists? new_plist_abs_path # There's someone already there! # Bail out unless we are forcing the operation if force logger.info "Overwriting agent #{ new_label } with update to " \ "agent #{ label } (force: `true`)" else raise binding.erb " A different agent already exists at:\n \n <%= new_plist_abs_path %>\n \n Remove that agent first if you really want to replace it with an\n updated version of this one or provide `force: true`.\n \n END\n end\n end\n \n # Ok, we're in the clear (save for the obvious race condition, but,\n # hey, it's a development tool, so fuck it... it's not even clear it's\n # possible to do an atomic file add from Ruby)\n new_plist_abs_path.write Plist::Emit.dump( new_plist_data )\n \n # Remove this agent\n remove\n \n # And instantiate and load a new agent from the new path\n self.class.from_path( new_plist_abs_path ).load\n \n end\nend\n" |
#workdir ⇒ Pathname
Returns The working directory of the agent.
806 807 808 |
# File 'lib/locd/agent.rb', line 806 def workdir plist['WorkingDirectory'].to_pn end |