Class: Locd::Agent
- Inherits:
-
Object
- Object
- Locd::Agent
- Includes:
- Comparable, NRSER::Log::Mixin
- Defined in:
- lib/locd/agent.rb,
lib/locd/agent/proxy.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, Status
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
.plistfile. -
#plist ⇒ Hash<String, V>
readonly
Hash of the agent's
.plistfile (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
STDOUTandSTDERRto 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
launchdplist 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
.plistfile.
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
patternor raise. -
.get(label) ⇒ Agent?
Get a Loc'd agent by it's label.
-
.get!(label) ⇒ Agent
Like Agent.get but raises if the agent is not found.
-
.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
.plistto~/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
launchdproperty 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
trueif 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
STDERRto. - #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
STDOUTto. -
#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
- #ensure_running(**start_kwds) ⇒ Object
-
#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) ⇒ Status
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
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 |
# File 'lib/locd/agent.rb', line 759 def initialize plist:, path: @path = path.to_pn. @plist = plist @status = nil # Sanity check... unless plist.key? Locd.config[:agent, :config_key] raise ArgumentError.new binding.erb <<~END Not a Loc'd plist (no <%= Locd.config[:agent, :config_key] %> key) path: <%= path %> plist: <%= plist.pretty_inspect %> END end unless @path.basename( '.plist' ).to_s == label raise ArgumentError.new binding.erb <<~END Label and filename don't match. Filename should be `<label>.plist`, found label: <%= label %> filename: <%= @path.basename %> path: <%= path %> END end init_ensure_out_dirs_exist end |
Instance Attribute Details
#path ⇒ Pathname (readonly)
Absolute path to the agent's .plist file.
753 754 755 |
# File 'lib/locd/agent.rb', line 753 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).
746 747 748 |
# File 'lib/locd/agent.rb', line 746 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.
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 |
# File 'lib/locd/agent.rb', line 667 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 <<~END Agent <%= label %> already exists at: <%= plist_abs_path %> END end end plist_data = create_plist_data label: label, workdir: workdir, **kwds logger.debug "Property list created", data: plist_data plist_string = Plist::Emit.dump plist_data plist_abs_path.write plist_string logger.debug "Property list written", path: plist_abs_path from_path( plist_abs_path ).tap { |agent| agent.send :log_info, "added" } end |
.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.
726 727 728 729 730 731 732 |
# File 'lib/locd/agent.rb', line 726 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.
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/locd/agent.rb', line 331 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.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/locd/agent.rb', line 236 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.
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 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 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 |
# File 'lib/locd/agent.rb', line 547 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.
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/locd/agent.rb', line 156 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?
313 314 315 |
# File 'lib/locd/agent.rb', line 313 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.
447 448 449 450 |
# File 'lib/locd/agent.rb', line 447 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.
463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/locd/agent.rb', line 463 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 }", value: 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.
428 429 430 |
# File 'lib/locd/agent.rb', line 428 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.
270 271 272 273 274 |
# File 'lib/locd/agent.rb', line 270 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) ⇒ Agent?
Get a Loc'd agent by it's label.
385 386 387 388 389 390 391 |
# File 'lib/locd/agent.rb', line 385 def self.get label path = plist_abs_path label if path.file? from_path path end end |
.get!(label) ⇒ Agent
Like get but raises if the agent is not found.
405 406 407 408 409 410 411 412 413 414 |
# File 'lib/locd/agent.rb', line 405 def self.get! label get( label ).tap do |agent| if agent.nil? raise NotFoundError, [ self, "with label", label, "not found. Expected the property list at", plist_abs_path( label ) ].map( &:to_s ).join( ' ' ) end end end |
.labels ⇒ Hamster::Vector<String>
Labels for all installed Loc'd agents.
369 370 371 |
# File 'lib/locd/agent.rb', line 369 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.
111 112 113 |
# File 'lib/locd/agent.rb', line 111 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.
137 138 139 |
# File 'lib/locd/agent.rb', line 137 def self.plist_abs_path label user_plist_abs_dir / "#{ label }.plist" end |
.plists ⇒ Hamster::Hash<String, Locd::Agent>
All installed Loc'd agents.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/locd/agent.rb', line 287 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.
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/locd/agent.rb', line 511 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).
204 205 206 207 208 209 210 |
# File 'lib/locd/agent.rb', line 204 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.
124 125 126 |
# File 'lib/locd/agent.rb', line 124 def self.user_plist_abs_dir Pathname.new( '~/Library/LaunchAgents' ). end |
Instance Method Details
#<=>(other) ⇒ Fixnum
Compare to another agent by their labels.
1222 1223 1224 |
# File 'lib/locd/agent.rb', line 1222 def <=> other Locd::Label.compare label, other.label end |
#cmd_template ⇒ Object
860 861 862 |
# File 'lib/locd/agent.rb', line 860 def cmd_template config['cmd_template'] end |
#config ⇒ Object
855 856 857 |
# File 'lib/locd/agent.rb', line 855 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.
800 801 802 |
# File 'lib/locd/agent.rb', line 800 def default_log_path? log_path == self.class.default_log_path( workdir, label ) end |
#ensure_running(**start_kwds) ⇒ Object
1040 1041 1042 |
# File 'lib/locd/agent.rb', line 1040 def ensure_running **start_kwds start( **start_kwds ) unless running? end |
#err_path ⇒ Pathname?
Path the agent is logging STDERR to.
895 896 897 |
# File 'lib/locd/agent.rb', line 895 def err_path plist['StandardErrorPath'].to_pn if plist['StandardErrorPath'] end |
#label ⇒ String
813 814 815 |
# File 'lib/locd/agent.rb', line 813 def label plist['Label'].freeze end |
#last_exit_code(refresh: false) ⇒ nil, Fixnum
850 851 852 |
# File 'lib/locd/agent.rb', line 850 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.
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 |
# File 'lib/locd/agent.rb', line 963 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.
904 905 906 907 908 909 |
# File 'lib/locd/agent.rb', line 904 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.
880 881 882 |
# File 'lib/locd/agent.rb', line 880 def out_path plist['StandardOutPath'].to_pn if plist['StandardOutPath'] end |
#pid(refresh: false) ⇒ nil, Fixnum
Current process ID of the agent (if running).
828 829 830 |
# File 'lib/locd/agent.rb', line 828 def pid refresh: false status( refresh: refresh ) && status[:pid] end |
#reload(force: false, enable: false) ⇒ self
1005 1006 1007 1008 |
# File 'lib/locd/agent.rb', line 1005 def reload force: false, enable: false unload load force: force, enable: enable end |
#remove(logs: false) ⇒ self
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 |
# File 'lib/locd/agent.rb', line 1092 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
1078 1079 1080 1081 |
# File 'lib/locd/agent.rb', line 1078 def restart reload: true, force: true, enable: false stop unload: reload start load: reload, force: force, enable: enable end |
#running?(refresh: false) ⇒ Boolean
833 834 835 |
# File 'lib/locd/agent.rb', line 833 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?).
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
# File 'lib/locd/agent.rb', line 1025 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) ⇒ Status
The agent's status from parsing launchctl list.
Status is read on demand and cached on the instance.
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
# File 'lib/locd/agent.rb', line 927 def status refresh: false if refresh || @status.nil? raw_status = Locd::Launchctl.status[label] # Happens when the agent is not loaded @status = if raw_status.nil? Status.new \ loaded: false, running: false, pid: nil, last_exit_code: nil else Status.new \ 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.
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 |
# File 'lib/locd/agent.rb', line 1054 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
838 839 840 |
# File 'lib/locd/agent.rb', line 838 def stopped? refresh: false !running?( refresh: refresh ) end |
#to_h ⇒ Object
1227 1228 1229 1230 1231 |
# File 'lib/locd/agent.rb', line 1227 def to_h self.class::TO_H_NAMES.map { |name| [name, send( name )] }.to_h end |
#to_json(*args) ⇒ Object
1234 1235 1236 |
# File 'lib/locd/agent.rb', line 1234 def to_json *args to_h.to_json *args end |
#to_yaml(*args) ⇒ Object
TODO Doesn't work!
1241 1242 1243 |
# File 'lib/locd/agent.rb', line 1241 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.
989 990 991 992 993 994 995 996 997 |
# File 'lib/locd/agent.rb', line 989 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.
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 |
# File 'lib/locd/agent.rb', line 1133 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 <<-END A different agent already exists at: <%= new_plist_abs_path %> Remove that agent first if you really want to replace it with an updated version of this one or provide `force: true`. END end end # Ok, we're in the clear (save for the obvious race condition, but, # hey, it's a development tool, so fuck it... it's not even clear it's # possible to do an atomic file add from Ruby) new_plist_abs_path.write Plist::Emit.dump( new_plist_data ) # Remove this agent remove # And instantiate and load a new agent from the new path self.class.from_path( new_plist_abs_path ).load end end |
#workdir ⇒ Pathname
Returns The working directory of the agent.
867 868 869 |
# File 'lib/locd/agent.rb', line 867 def workdir plist['WorkingDirectory'].to_pn end |