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.

Class Attribute Summary collapse

Instance Attribute 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.



25
26
27
# File 'lib/scout_agent/id_card.rb', line 25

def initialize(process_name)
  @process_name = process_name
end

Class Attribute Details

.meObject

This global attribute should contain the name of the current process. It is set during a successful authorization.

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



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

def me
  @me
end

Instance Attribute Details

#process_nameObject (readonly)

Returns the value of attribute process_name.



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

def process_name
  @process_name
end

Instance Method Details

#authorizeObject

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.



79
80
81
82
83
84
85
86
87
88
89
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
# File 'lib/scout_agent/id_card.rb', line 79

def authorize
  File.open(pid_file, File::CREAT | File::EXCL | File::WRONLY) do |pid|
    pid.flock(File::LOCK_EX)
    if not block_given? or yield  # 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) 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          # 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.



45
46
47
48
49
# File 'lib/scout_agent/id_card.rb', line 45

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.



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

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.



128
129
130
131
132
133
# File 'lib/scout_agent/id_card.rb', line 128

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.



60
61
62
63
64
65
66
67
# File 'lib/scout_agent/id_card.rb', line 60

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.



32
33
34
# File 'lib/scout_agent/id_card.rb', line 32

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