daemon_controller 1.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/spec/echo_server.rb CHANGED
@@ -1,138 +1,170 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  # A simple echo server, used by the unit test.
3
- require 'socket'
4
- require 'optparse'
5
+ require "socket"
6
+ require "optparse"
5
7
 
6
8
  options = {
7
- :port => 3230,
8
- :chdir => "/",
9
- :log_file => "/dev/null",
10
- :wait1 => 0,
11
- :wait2 => 0,
12
- :stop_time => 0,
13
- :daemonize => true
9
+ port: 3230,
10
+ chdir: "/",
11
+ log_file: "/dev/null",
12
+ wait1: 0,
13
+ wait2: 0,
14
+ stop_time: 0,
15
+ daemonize: true
14
16
  }
15
17
  parser = OptionParser.new do |opts|
16
- opts.banner = "Usage: echo_server.rb [options]"
17
- opts.separator ""
18
-
19
- opts.separator "Options:"
20
- opts.on("-p", "--port PORT", Integer, "Port to use. Default: 3230") do |value|
21
- options[:port] = value
22
- end
23
- opts.on("-C", "--change-dir DIR", String, "Change working directory. Default: /") do |value|
24
- options[:chdir] = value
25
- end
26
- opts.on("-l", "--log-file FILENAME", String, "Log file to use. Default: /dev/null") do |value|
27
- options[:log_file] = value
28
- end
29
- opts.on("-P", "--pid-file FILENAME", String, "Pid file to use.") do |value|
30
- options[:pid_file] = File.expand_path(value)
31
- end
32
- opts.on("--wait1 SECONDS", Float, "Wait a few seconds before writing pid file.") do |value|
33
- options[:wait1] = value
34
- end
35
- opts.on("--wait2 SECONDS", Float, "Wait a few seconds before binding server socket.") do |value|
36
- options[:wait2] = value
37
- end
38
- opts.on("--stop-time SECONDS", Float, "Wait a few seconds before exiting.") do |value|
39
- options[:stop_time] = value
40
- end
41
- opts.on("--crash-before-bind", "Whether the daemon should crash before binding the server socket.") do
42
- options[:crash_before_bind] = true
43
- end
44
- opts.on("--no-daemonize", "Don't daemonize.") do
45
- options[:daemonize] = false
46
- end
18
+ opts.banner = "Usage: echo_server.rb [options]"
19
+ opts.separator ""
20
+
21
+ opts.separator "Options:"
22
+ opts.on("-p", "--port PORT", Integer, "Port to use. Default: 3230") do |value|
23
+ options[:port] = value
24
+ end
25
+ opts.on("-C", "--change-dir DIR", String, "Change working directory. Default: /") do |value|
26
+ options[:chdir] = value
27
+ end
28
+ opts.on("-l", "--log-file FILENAME", String, "Log file to use. Default: /dev/null") do |value|
29
+ options[:log_file] = value
30
+ end
31
+ opts.on("-P", "--pid-file FILENAME", String, "Pid file to use.") do |value|
32
+ options[:pid_file] = File.absolute_path(value)
33
+ end
34
+ opts.on("--log-message1 MESSAGE", String, "Log message before opening log file.") do |value|
35
+ options[:log_message1] = value
36
+ end
37
+ opts.on("--log-message2 MESSAGE", String, "Log message after opening log file.") do |value|
38
+ options[:log_message2] = value
39
+ end
40
+ opts.on("--wait1 SECONDS", Float, "Wait a few seconds before writing pid file.") do |value|
41
+ options[:wait1] = value
42
+ end
43
+ opts.on("--wait2 SECONDS", Float, "Wait a few seconds before binding server socket.") do |value|
44
+ options[:wait2] = value
45
+ end
46
+ opts.on("--stop-time SECONDS", Float, "Wait a few seconds before exiting.") do |value|
47
+ options[:stop_time] = value
48
+ end
49
+ opts.on("--crash-before-bind", "Whether the daemon should crash before binding the server socket.") do
50
+ options[:crash_before_bind] = true
51
+ end
52
+ opts.on("--crash-signal SIGNAL", "Signal to send to the daemon when crashing.") do |value|
53
+ options[:crash_signal] = value
54
+ end
55
+ opts.on("--no-daemonize", "Don't daemonize.") do
56
+ options[:daemonize] = false
57
+ end
58
+ opts.on("--ignore-sigterm", "Ignore SIGTERM.") do
59
+ options[:ignore_sigterm] = true
60
+ end
47
61
  end
48
62
  begin
49
- parser.parse!
63
+ parser.parse!
50
64
  rescue OptionParser::ParseError => e
51
- puts e
52
- puts
53
- puts "Please see '--help' for valid options."
54
- exit 1
65
+ puts e
66
+ puts
67
+ puts "Please see '--help' for valid options."
68
+ exit 1
55
69
  end
56
70
 
57
71
  if options[:pid_file]
58
- if File.exist?(options[:pid_file])
59
- STDERR.puts "*** ERROR: pid file #{options[:pid_file]} exists."
60
- exit 1
61
- end
72
+ if File.exist?(options[:pid_file])
73
+ warn "*** ERROR: pid file #{options[:pid_file]} exists."
74
+ exit 1
75
+ end
76
+ end
77
+
78
+ if options[:log_message1]
79
+ puts options[:log_message1]
80
+ $stdout.flush
62
81
  end
63
82
 
64
- if ENV['ENV_FILE']
65
- options[:env_file] = File.expand_path(ENV['ENV_FILE'])
83
+ if options[:ignore_sigterm]
84
+ Signal.trap("SIGTERM", "IGNORE")
85
+ end
86
+
87
+ sleep(options[:wait1])
88
+
89
+ if ENV["ENV_FILE"]
90
+ options[:env_file] = File.absolute_path(ENV["ENV_FILE"])
66
91
  end
67
92
 
68
93
  def main(options)
69
- STDIN.reopen("/dev/null", 'r')
70
- STDOUT.reopen(options[:log_file], 'a')
71
- STDERR.reopen(options[:log_file], 'a')
72
- STDOUT.sync = true
73
- STDERR.sync = true
74
- Dir.chdir(options[:chdir])
75
- File.umask(0)
94
+ $stdin.reopen("/dev/null", "r")
95
+ $stdout.reopen(options[:log_file], "a")
96
+ $stderr.reopen(options[:log_file], "a")
97
+ $stdout.sync = true
98
+ $stderr.sync = true
99
+ Dir.chdir(options[:chdir])
100
+ File.umask(0)
101
+
102
+ if options[:log_message2]
103
+ puts options[:log_message2]
104
+ end
105
+
106
+ if options[:env_file]
107
+ File.write(options[:env_file], "\0")
108
+ at_exit do
109
+ File.unlink(options[:env_file])
110
+ rescue
111
+ nil
112
+ end
113
+ end
114
+
115
+ if options[:pid_file]
116
+ File.open(options[:pid_file], "w") do |f|
117
+ f.puts(Process.pid)
118
+ end
119
+ end
120
+
121
+ sleep(options[:wait2])
122
+ if options[:crash_before_bind]
123
+ puts "#{Time.now}: crashing, as instructed."
124
+ if options[:crash_signal]
125
+ Process.kill(options[:crash_signal], Process.pid)
126
+ end
127
+ exit 2
128
+ end
76
129
 
77
- if options[:env_file]
78
- File.open(options[:env_file], 'w') do |f|
79
- f.write("\0")
80
- end
81
- at_exit do
82
- File.unlink(options[:env_file]) rescue nil
83
- end
84
- end
85
-
86
- if options[:pid_file]
87
- sleep(options[:wait1])
88
- File.open(options[:pid_file], 'w') do |f|
89
- f.puts(Process.pid)
90
- end
91
- at_exit do
92
- File.unlink(options[:pid_file]) rescue nil
93
- end
94
- end
95
-
96
- sleep(options[:wait2])
97
- if options[:crash_before_bind]
98
- puts "#{Time.now}: crashing, as instructed."
99
- exit 2
100
- end
101
-
102
- server = TCPServer.new('127.0.0.1', options[:port])
103
- begin
104
- puts "*** #{Time.now}: echo server started"
105
- while (client = server.accept)
106
- puts "#{Time.now}: new client"
107
- begin
108
- while (line = client.readline)
109
- puts "#{Time.now}: client sent: #{line.strip}"
110
- client.puts(line)
111
- end
112
- rescue EOFError
113
- ensure
114
- puts "#{Time.now}: connection closed"
115
- client.close rescue nil
116
- end
117
- end
118
- rescue SignalException
119
- exit 2
120
- rescue => e
121
- puts e.to_s
122
- puts " " << e.backtrace.join("\n ")
123
- exit 3
124
- ensure
125
- puts "*** #{Time.now}: echo server exiting..."
126
- sleep(options[:stop_time])
127
- puts "*** #{Time.now}: echo server exited"
128
- end
130
+ server = TCPServer.new("127.0.0.1", options[:port])
131
+ begin
132
+ puts "*** #{Time.now}: echo server started"
133
+ while (client = server.accept)
134
+ puts "#{Time.now}: new client"
135
+ begin
136
+ while (line = client.readline)
137
+ puts "#{Time.now}: client sent: #{line.strip}"
138
+ client.puts(line)
139
+ end
140
+ rescue EOFError
141
+ ensure
142
+ puts "#{Time.now}: connection closed"
143
+ begin
144
+ client.close
145
+ rescue
146
+ nil
147
+ end
148
+ end
149
+ end
150
+ rescue SignalException
151
+ exit 2
152
+ rescue => e
153
+ puts e
154
+ puts " " + e.backtrace.join("\n ")
155
+ exit 3
156
+ ensure
157
+ puts "*** #{Time.now}: echo server exiting..."
158
+ sleep(options[:stop_time])
159
+ puts "*** #{Time.now}: echo server exited"
160
+ end
129
161
  end
130
162
 
131
163
  if options[:daemonize]
132
- fork do
133
- Process.setsid
134
- main(options)
135
- end
164
+ fork do
165
+ Process.setsid
166
+ main(options)
167
+ end
136
168
  else
137
- main(options)
169
+ main(options)
138
170
  end
data/spec/test_helper.rb CHANGED
@@ -1,116 +1,189 @@
1
- root = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
- $LOAD_PATH.unshift(File.join(root, "lib"))
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+
5
+ root = File.absolute_path(File.join(File.dirname(__FILE__), ".."))
3
6
  Dir.chdir(root)
4
7
 
5
- if !ENV['MRI_RUBY']
6
- if RUBY_PLATFORM =~ /java/
7
- # We need a Ruby implementation that starts fast and supports forking.
8
- # JRuby is neither.
9
- abort "In order to run these tests in JRuby, you must set " +
10
- "the environment variable $MRI_RUBY to an MRI Ruby interpeter."
11
- else
12
- require 'rbconfig'
13
- rb_config = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG
14
- ENV['MRI_RUBY'] = rb_config['bindir'] + '/' + rb_config['RUBY_INSTALL_NAME'] +
15
- rb_config['EXEEXT']
16
- puts ENV['MRI_RUBY']
17
- end
8
+ # Ensure subprocesses (with could be a different Ruby) are
9
+ # started without Bundler environment variables.
10
+ ENV.replace(Bundler.with_unbundled_env { ENV.to_h.dup })
11
+
12
+ if !ENV["MRI_RUBY"]
13
+ if RUBY_PLATFORM.match?(/java/)
14
+ # We need a Ruby implementation that starts fast and supports forking.
15
+ # JRuby is neither.
16
+ abort "In order to run these tests in JRuby, you must set " \
17
+ "the environment variable $MRI_RUBY to an MRI Ruby interpeter."
18
+ else
19
+ require "rbconfig"
20
+ rb_config = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG
21
+ ENV["MRI_RUBY"] = rb_config["bindir"] + "/" + rb_config["RUBY_INSTALL_NAME"] +
22
+ rb_config["EXEEXT"]
23
+ puts ENV["MRI_RUBY"]
24
+ end
25
+ end
26
+
27
+ trap("SIGQUIT") do
28
+ if Thread.respond_to?(:list)
29
+ output = String.new("----- #{Time.now} -----\n")
30
+ Thread.list.each do |thread|
31
+ output << "##### #{thread}\n"
32
+ output << thread.backtrace.join("\n")
33
+ output << "\n\n"
34
+ end
35
+ output << "--------------------"
36
+ warn(output)
37
+ $stderr.flush
38
+ end
18
39
  end
19
40
 
20
41
  module TestHelper
21
- def new_controller(options = {})
22
- @start_command = './spec/run_echo_server -l spec/echo_server.log -P spec/echo_server.pid'
23
- if options[:wait1]
24
- @start_command << " --wait1 #{options[:wait1]}"
25
- end
26
- if options[:wait2]
27
- @start_command << " --wait2 #{options[:wait2]}"
28
- end
29
- if options[:stop_time]
30
- @start_command << " --stop-time #{options[:stop_time]}"
31
- end
32
- if options[:crash_before_bind]
33
- @start_command << " --crash-before-bind"
34
- end
35
- if options[:no_daemonize]
36
- @start_command << " --no-daemonize"
37
- end
38
- new_options = {
39
- :identifier => 'My Test Daemon',
40
- :start_command => @start_command,
41
- :ping_command => method(:ping_echo_server),
42
- :pid_file => 'spec/echo_server.pid',
43
- :log_file => 'spec/echo_server.log',
44
- :start_timeout => 3,
45
- :stop_timeout => 3
46
- }.merge(options)
47
- @controller = DaemonController.new(new_options)
48
- end
49
-
50
- def ping_echo_server
51
- begin
52
- TCPSocket.new('127.0.0.1', 3230)
53
- true
54
- rescue SystemCallError
55
- false
56
- end
57
- end
58
-
59
- def write_file(filename, contents)
60
- File.open(filename, 'w') do |f|
61
- f.write(contents)
62
- end
63
- end
64
-
65
- def exec_is_slow?
66
- return RUBY_PLATFORM == "java"
67
- end
68
-
69
- def process_is_alive?(pid)
70
- begin
71
- Process.kill(0, pid)
72
- return true
73
- rescue Errno::ESRCH
74
- return false
75
- rescue SystemCallError => e
76
- return true
77
- end
78
- end
79
-
80
- def eventually(deadline_duration = 1, check_interval = 0.05)
81
- deadline = Time.now + deadline_duration
82
- while Time.now < deadline
83
- if yield
84
- return
85
- else
86
- sleep(check_interval)
87
- end
88
- end
89
- raise "Time limit exceeded"
90
- end
42
+ def new_logger
43
+ @logger ||= begin
44
+ @log_stream = StringIO.new
45
+ logger = Logger.new(@log_stream)
46
+ logger.level = Logger::DEBUG
47
+ logger
48
+ end
49
+ end
50
+
51
+ def print_logs(example)
52
+ warn "----- LOGS FOR: #{example.full_description} ----"
53
+ warn @log_stream.string
54
+ warn "----- END LOGS -----"
55
+ end
56
+
57
+ def new_controller(options = {})
58
+ @start_command = String.new("./spec/run_in_mri_ruby echo_server.rb -l spec/echo_server.log")
59
+ if (log_message1 = options.delete(:log_message1))
60
+ @start_command << " --log-message1 #{Shellwords.escape log_message1}"
61
+ end
62
+ if (log_message2 = options.delete(:log_message2))
63
+ @start_command << " --log-message2 #{Shellwords.escape log_message2}"
64
+ end
65
+ if (wait1 = options.delete(:wait1))
66
+ @start_command << " --wait1 #{wait1}"
67
+ end
68
+ if (wait2 = options.delete(:wait2))
69
+ @start_command << " --wait2 #{wait2}"
70
+ end
71
+ if (stop_time = options.delete(:stop_time))
72
+ @start_command << " --stop-time #{stop_time}"
73
+ end
74
+ if options.delete(:crash_before_bind)
75
+ @start_command << " --crash-before-bind"
76
+ end
77
+ if (crash_signal = options.delete(:crash_signal))
78
+ @start_command << " --crash-signal #{crash_signal}"
79
+ end
80
+ if options.delete(:no_daemonize)
81
+ @start_command << " --no-daemonize"
82
+ end
83
+ if options.delete(:ignore_sigterm)
84
+ @start_command << " --ignore-sigterm"
85
+ end
86
+ if !options.delete(:no_write_pid_file)
87
+ @start_command << " -P spec/echo_server.pid"
88
+ end
89
+ new_options = {
90
+ identifier: "My Test Daemon",
91
+ start_command: @start_command,
92
+ ping_command: method(:ping_echo_server),
93
+ pid_file: "spec/echo_server.pid",
94
+ log_file: "spec/echo_server.log",
95
+ start_timeout: 30,
96
+ stop_timeout: 30,
97
+ logger: new_logger
98
+ }.merge(options)
99
+ @controller = DaemonController.new(**new_options)
100
+ end
101
+
102
+ def ping_echo_server
103
+ TCPSocket.new("127.0.0.1", 3230)
104
+ true
105
+ rescue SystemCallError
106
+ false
107
+ end
108
+
109
+ def monotonic_time
110
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
111
+ end
112
+
113
+ def write_file(filename, contents)
114
+ File.write(filename, contents)
115
+ end
116
+
117
+ def exec_is_slow?
118
+ RUBY_PLATFORM == "java"
119
+ end
120
+
121
+ def process_is_alive?(pid)
122
+ Process.kill(0, pid)
123
+ true
124
+ rescue Errno::ESRCH
125
+ false
126
+ rescue SystemCallError
127
+ true
128
+ end
129
+
130
+ def eventually(deadline_duration = 1, check_interval = 0.05)
131
+ deadline = monotonic_time + deadline_duration
132
+ while monotonic_time < deadline
133
+ if yield
134
+ return
135
+ else
136
+ sleep(check_interval)
137
+ end
138
+ end
139
+ raise "Time limit exceeded"
140
+ end
141
+
142
+ def wait_until_pid_file_available
143
+ eventually(30) do
144
+ @controller.send(:pid_file_available?)
145
+ end
146
+ end
147
+
148
+ def find_echo_server_pid
149
+ process_line = `ps aux`.lines.grep(/echo_server\.rb/).first
150
+ process_line.split[1].to_i if process_line
151
+ end
152
+
153
+ def kill_and_wait_echo_server
154
+ pid = find_echo_server_pid
155
+ if pid
156
+ Process.kill("SIGTERM", pid)
157
+ Timeout.timeout(5) do
158
+ while find_echo_server_pid
159
+ sleep(0.1)
160
+ end
161
+ end
162
+ end
163
+ end
91
164
  end
92
165
 
93
166
  # A thread which doesn't execute its block until the
94
167
  # 'go!' method has been called.
95
168
  class WaitingThread < Thread
96
- def initialize
97
- @mutex = Mutex.new
98
- @cond = ConditionVariable.new
99
- @go = false
100
- super do
101
- @mutex.synchronize do
102
- while !@go
103
- @cond.wait(@mutex)
104
- end
105
- end
106
- yield
107
- end
108
- end
109
-
110
- def go!
111
- @mutex.synchronize do
112
- @go = true
113
- @cond.broadcast
114
- end
115
- end
169
+ def initialize
170
+ @mutex = Mutex.new
171
+ @cond = ConditionVariable.new
172
+ @go = false
173
+ super do
174
+ @mutex.synchronize do
175
+ until @go
176
+ @cond.wait(@mutex)
177
+ end
178
+ end
179
+ yield
180
+ end
181
+ end
182
+
183
+ def go!
184
+ @mutex.synchronize do
185
+ @go = true
186
+ @cond.broadcast
187
+ end
188
+ end
116
189
  end
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  dir = File.dirname(__FILE__)
3
5
  Dir.chdir(File.dirname(dir))
4
- begin
5
- File.open("spec/echo_server.pid", "w") do |f|
6
- f.puts(Process.pid)
7
- end
8
- sleep 30
9
- rescue SignalException
10
- File.unlink("spec/echo_server.pid") rescue nil
11
- raise
12
- end
6
+
7
+ Signal.trap("SIGTERM", "IGNORE") if ARGV.include?("--ignore-sigterm")
8
+
9
+ File.open("spec/echo_server.pid", "w") do |f|
10
+ f.puts(Process.pid)
11
+ end
12
+ sleep 60
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daemon_controller
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hongli Lai
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2014-03-03 00:00:00.000000000 Z
10
+ date: 2025-04-08 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: A library for robust daemon management.
14
13
  email: software-signing@phusion.nl
@@ -17,50 +16,37 @@ extensions: []
17
16
  extra_rdoc_files: []
18
17
  files:
19
18
  - LICENSE.txt
20
- - README.markdown
19
+ - README.md
21
20
  - Rakefile
22
21
  - daemon_controller.gemspec
23
- - debian.template/changelog
24
- - debian.template/compat
25
- - debian.template/control.template
26
- - debian.template/copyright
27
- - debian.template/ruby-daemon-controller.install
28
- - debian.template/rules
29
- - debian.template/source/format
30
22
  - lib/daemon_controller.rb
31
23
  - lib/daemon_controller/lock_file.rb
32
24
  - lib/daemon_controller/packaging.rb
33
25
  - lib/daemon_controller/spawn.rb
34
26
  - lib/daemon_controller/version.rb
35
- - rpm/get_distro_id.py
36
- - rpm/rubygem-daemon_controller.spec.template
37
27
  - spec/daemon_controller_spec.rb
38
28
  - spec/echo_server.rb
39
- - spec/run_echo_server
40
29
  - spec/test_helper.rb
41
30
  - spec/unresponsive_daemon.rb
42
31
  homepage: https://github.com/FooBarWidget/daemon_controller
43
32
  licenses:
44
33
  - MIT
45
34
  metadata: {}
46
- post_install_message:
47
35
  rdoc_options: []
48
36
  require_paths:
49
37
  - lib
50
38
  required_ruby_version: !ruby/object:Gem::Requirement
51
39
  requirements:
52
- - - ! '>='
40
+ - - ">="
53
41
  - !ruby/object:Gem::Version
54
- version: '0'
42
+ version: 2.0.0
55
43
  required_rubygems_version: !ruby/object:Gem::Requirement
56
44
  requirements:
57
- - - ! '>='
45
+ - - ">="
58
46
  - !ruby/object:Gem::Version
59
47
  version: '0'
60
48
  requirements: []
61
- rubyforge_project:
62
- rubygems_version: 2.2.0
63
- signing_key:
49
+ rubygems_version: 3.6.2
64
50
  specification_version: 4
65
51
  summary: A library for implementing daemon management capabilities
66
52
  test_files: []