Class: Legion::Process

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/process.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Process

Returns a new instance of Process.



18
19
20
21
22
# File 'lib/legion/process.rb', line 18

def initialize(options)
  @options = options
  options[:logfile] = File.expand_path(logfile) if logfile?
  options[:pidfile] = File.expand_path(pidfile) if pidfile?
end

Class Attribute Details

.quit_flagObject

Returns the value of attribute quit_flag.



9
10
11
# File 'lib/legion/process.rb', line 9

def quit_flag
  @quit_flag
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



16
17
18
# File 'lib/legion/process.rb', line 16

def options
  @options
end

#serviceObject (readonly)

Returns the value of attribute service.



16
17
18
# File 'lib/legion/process.rb', line 16

def service
  @service
end

Class Method Details

.run!(options) ⇒ Object



12
13
14
# File 'lib/legion/process.rb', line 12

def self.run!(options)
  Legion::Process.new(options).run!
end

Instance Method Details

#check_pidObject



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/legion/process.rb', line 100

def check_pid
  if pidfile?
    case pid_status(pidfile)
    when :running, :not_owned
      exit(1)
    when :dead
      File.delete(pidfile)
    end
  end
  false
end

#daemonizeObject

DAEMONIZING, PID MANAGEMENT, and OUTPUT REDIRECTION



79
80
81
82
83
84
# File 'lib/legion/process.rb', line 79

def daemonize
  exit if fork
  ::Process.setsid
  exit if fork
  Dir.chdir '/'
end

#daemonize?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/legion/process.rb', line 28

def daemonize?
  options[:daemonize]
end

#info(msg) ⇒ Object



48
49
50
# File 'lib/legion/process.rb', line 48

def info(msg)
  puts "[#{::Process.pid}] [#{Time.now}] #{msg}"
end

#logfileObject



32
33
34
# File 'lib/legion/process.rb', line 32

def logfile
  options[:logfile]
end

#logfile?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/legion/process.rb', line 40

def logfile?
  !logfile.nil?
end

#pid_status(pidfile) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/legion/process.rb', line 112

def pid_status(pidfile)
  return :exited unless File.exist?(pidfile)

  pid = ::File.read(pidfile).to_i
  return :dead if pid.zero?

  ::Process.kill(0, pid)
  :running
rescue Errno::ESRCH => e
  Legion::Logging.debug "Process#pid_status: pid=#{pid} is dead: #{e.message}" if defined?(Legion::Logging)
  :dead
rescue Errno::EPERM => e
  Legion::Logging.debug "Process#pid_status: pid=#{pid} not owned: #{e.message}" if defined?(Legion::Logging)
  :not_owned
end

#pidfileObject



36
37
38
# File 'lib/legion/process.rb', line 36

def pidfile
  options[:pidfile]
end

#pidfile?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/legion/process.rb', line 44

def pidfile?
  !pidfile.nil?
end

#quitObject



24
25
26
# File 'lib/legion/process.rb', line 24

def quit
  @quit.is_a?(Concurrent::AtomicBoolean) ? @quit.true? : !!@quit
end

#retrap_after_pumaObject



146
147
148
149
150
151
152
153
154
# File 'lib/legion/process.rb', line 146

def retrap_after_puma
  @retrap_thread = Thread.new do
    15.times do
      sleep 1
      trap('SIGINT') { @quit.make_true }
      trap('SIGTERM') { @quit.make_true }
    end
  end
end

#run!Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/legion/process.rb', line 52

def run!
  start_time = Time.now
  @options[:time_limit] = @options[:time_limit].to_i if @options.key? :time_limit
  @quit = Concurrent::AtomicBoolean.new(false)
  self.class.quit_flag = @quit
  check_pid
  daemonize if daemonize?
  write_pid
  trap_signals
  retrap_after_puma

  until quit
    sleep(1)
    @quit.make_true if @options.key?(:time_limit) && Time.now - start_time > @options[:time_limit]
  end
  @retrap_thread&.kill
  Legion::Logging.info('Legion is shutting down!')
  Legion.shutdown
  Legion::Logging.info('Legion has shutdown. Goodbye!')

  exit
end

#trap_signalsObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/legion/process.rb', line 128

def trap_signals
  trap('SIGTERM') do
    Legion::Logging.info '[Process] received SIGTERM, shutting down' if defined?(Legion::Logging)
    @quit.make_true
  end

  trap('SIGHUP') do
    Legion::Logging.info '[Process] received SIGHUP, triggering reload' if defined?(Legion::Logging)
    info 'sighup: triggering reload'
    Thread.new { Legion.reload }
  end

  trap('SIGINT') do
    Legion::Logging.info '[Process] received SIGINT, shutting down' if defined?(Legion::Logging)
    @quit.make_true
  end
end

#write_pidObject



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/legion/process.rb', line 86

def write_pid
  if pidfile?
    begin
      File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY) { |f| f.write(::Process.pid.to_s) }
      Legion::Logging.info "[Process] PID #{::Process.pid} written to #{pidfile}" if defined?(Legion::Logging)
      at_exit { FileUtils.rm_f(pidfile) }
    rescue Errno::EEXIST
      check_pid
      retry
    end
  end
  false
end