Class: ScoutAgent::IDCard

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_agent/id_card.rb

Overview

This class excapsulates a named process. It is used to ensure exclusive execution and to signal other processes.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(process_name) ⇒ IDCard

Pass in the process_name of the process you want to signal() or the process_name you wish to authorize() for yourself.



36
37
38
# File 'lib/scout_agent/id_card.rb', line 36

def initialize(process_name)
  @process_name = process_name
end

Instance Attribute Details

#process_nameObject (readonly)

Returns the value of attribute process_name.



40
41
42
# File 'lib/scout_agent/id_card.rb', line 40

def process_name
  @process_name
end

Class Method Details

.meObject

This global attribute should contain the IDCard for this process. It is set during a successful authorization.

Warning: Be sure to clear this attribute immediately after a fork(), with a call to me=(), so you don’t keep the parent’s identity.



17
18
19
# File 'lib/scout_agent/id_card.rb', line 17

def self.me
  @me ||= nil
end

.me=(id_card) ⇒ Object

A setter for the identity of this process. This is set automatically as part of an authorization.

Warning: Be sure to clear this attribute immediately after a fork() so you don’t keep the parent’s identity.



28
29
30
# File 'lib/scout_agent/id_card.rb', line 28

def self.me=(id_card)
  @me = id_card
end

Instance Method Details

#authorize(&block) ⇒ Object

Claims this identity for this process. This process is multiprocess-safe and will fail if another process has claimed this identity. However, stale claims are ignored and replaced, if possible.

This method returns true in the claim succeeded and false if it could not happen for any reason. A return of true indicates that IDCard::me() has been updated and an exit handle has been installed to revoke() this claim as the process ends.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/scout_agent/id_card.rb', line 90

def authorize(&block)
  File.open(pid_file, File::CREAT | File::EXCL | File::WRONLY) do |pid|
    pid.flock(File::LOCK_EX)
    if block.nil? or block.call  # allows for daemonization
      pid.puts Process.pid
    else
      pid.flock(File::LOCK_UN)
      revoke  # remove this file if anything went wrong
      return false
    end
    pid.flock(File::LOCK_UN)
  end
  
  self.class.me = self
  
  at_my_exit do
    revoke
  end
  true
rescue Errno::EEXIST  # pid_file already exists
  File.open(pid_file, "r+") do |pid|
    if pid.flock(File::LOCK_EX | File::LOCK_NB)
      if pid.read =~ /\A\d+/
        begin
          signal(0)          # check for the existing process
        rescue Errno::ESRCH  # no such process
          # stale PID file found, clearing it and reloading
          if revoke
            pid.flock(File::LOCK_UN)  # release the lock before we recurse
            return authorize(&block)  # try again
          end
        rescue Errno::EACCES  # don't have permission
          # nothing we can do so give up
        end
      end
      pid.flock(File::LOCK_UN)  # release the lock
    else
      # couldn't grab a file lock to verify existing PID file
      return false
    end
  end
  # process was already running
  false
end

#pidObject

Returns the PID for the named process or nil if it cannot be read.



56
57
58
59
60
# File 'lib/scout_agent/id_card.rb', line 56

def pid
  pid_file.read.to_i
rescue Exception  # cannot read file
  nil
end

#pid_fileObject

Returns the path to the unique PID file for this process, based on the current Plan.



51
52
53
# File 'lib/scout_agent/id_card.rb', line 51

def pid_file
  Plan.pid_dir + "#{@process_name}.pid"
end

#revokeObject

Releases a held claim on a process name. Returns true if successful or false if the PID file didn’t exist or couldn’t be destroyed.



139
140
141
142
143
144
# File 'lib/scout_agent/id_card.rb', line 139

def revoke
  pid_file.unlink
  true
rescue Exception
  false
end

#signal(message) ⇒ Object

Tries to send message as a signal to the process represented by this instance. You can pass any message Process.kill() would understand.

Returns true if the signal was sent, or false if the PID file could not be read. Any Exception raised during the send, such as Errno::ESRCH for a missing process, will bubble up to the calling code.



71
72
73
74
75
76
77
78
# File 'lib/scout_agent/id_card.rb', line 71

def signal(message)
  if id = pid
    Process.kill(message, id)
    true
  else
    false
  end
end

#to_sObject

A String representation of this process, with PID.



43
44
45
# File 'lib/scout_agent/id_card.rb', line 43

def to_s
  "#{@process_name} (#{pid || 'unauthorized'})"
end