steamcannon-thin 1.2.8

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.
Files changed (137) hide show
  1. data/CHANGELOG +288 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +44 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +82 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +22 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +1249 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +436 -0
  29. data/lib/rack/adapter/loader.rb +91 -0
  30. data/lib/rack/adapter/rails.rb +183 -0
  31. data/lib/thin.rb +56 -0
  32. data/lib/thin/backends/base.rb +149 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +29 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +224 -0
  38. data/lib/thin/controllers/cluster.rb +178 -0
  39. data/lib/thin/controllers/controller.rb +188 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +180 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +156 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +220 -0
  48. data/lib/thin/server.rb +253 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/lib/thin_parser.so +0 -0
  54. data/spec/backends/swiftiply_client_spec.rb +66 -0
  55. data/spec/backends/tcp_server_spec.rb +33 -0
  56. data/spec/backends/unix_server_spec.rb +37 -0
  57. data/spec/command_spec.rb +25 -0
  58. data/spec/configs/cluster.yml +9 -0
  59. data/spec/configs/single.yml +9 -0
  60. data/spec/connection_spec.rb +106 -0
  61. data/spec/controllers/cluster_spec.rb +267 -0
  62. data/spec/controllers/controller_spec.rb +129 -0
  63. data/spec/controllers/service_spec.rb +50 -0
  64. data/spec/daemonizing_spec.rb +196 -0
  65. data/spec/headers_spec.rb +40 -0
  66. data/spec/logging_spec.rb +46 -0
  67. data/spec/perf/request_perf_spec.rb +50 -0
  68. data/spec/perf/response_perf_spec.rb +19 -0
  69. data/spec/perf/server_perf_spec.rb +39 -0
  70. data/spec/rack/loader_spec.rb +42 -0
  71. data/spec/rack/rails_adapter_spec.rb +173 -0
  72. data/spec/rails_app/app/controllers/application.rb +10 -0
  73. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  74. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  75. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  76. data/spec/rails_app/config/boot.rb +109 -0
  77. data/spec/rails_app/config/environment.rb +64 -0
  78. data/spec/rails_app/config/environments/development.rb +18 -0
  79. data/spec/rails_app/config/environments/production.rb +19 -0
  80. data/spec/rails_app/config/environments/test.rb +22 -0
  81. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  82. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  83. data/spec/rails_app/config/routes.rb +35 -0
  84. data/spec/rails_app/public/404.html +30 -0
  85. data/spec/rails_app/public/422.html +30 -0
  86. data/spec/rails_app/public/500.html +30 -0
  87. data/spec/rails_app/public/dispatch.cgi +10 -0
  88. data/spec/rails_app/public/dispatch.fcgi +24 -0
  89. data/spec/rails_app/public/dispatch.rb +10 -0
  90. data/spec/rails_app/public/favicon.ico +0 -0
  91. data/spec/rails_app/public/images/rails.png +0 -0
  92. data/spec/rails_app/public/index.html +277 -0
  93. data/spec/rails_app/public/javascripts/application.js +2 -0
  94. data/spec/rails_app/public/javascripts/controls.js +963 -0
  95. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  96. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  97. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  98. data/spec/rails_app/public/robots.txt +5 -0
  99. data/spec/rails_app/script/about +3 -0
  100. data/spec/rails_app/script/console +3 -0
  101. data/spec/rails_app/script/destroy +3 -0
  102. data/spec/rails_app/script/generate +3 -0
  103. data/spec/rails_app/script/performance/benchmarker +3 -0
  104. data/spec/rails_app/script/performance/profiler +3 -0
  105. data/spec/rails_app/script/performance/request +3 -0
  106. data/spec/rails_app/script/plugin +3 -0
  107. data/spec/rails_app/script/process/inspector +3 -0
  108. data/spec/rails_app/script/process/reaper +3 -0
  109. data/spec/rails_app/script/process/spawner +3 -0
  110. data/spec/rails_app/script/runner +3 -0
  111. data/spec/rails_app/script/server +3 -0
  112. data/spec/request/mongrel_spec.rb +39 -0
  113. data/spec/request/parser_spec.rb +254 -0
  114. data/spec/request/persistent_spec.rb +35 -0
  115. data/spec/request/processing_spec.rb +50 -0
  116. data/spec/response_spec.rb +91 -0
  117. data/spec/runner_spec.rb +168 -0
  118. data/spec/server/builder_spec.rb +44 -0
  119. data/spec/server/pipelining_spec.rb +110 -0
  120. data/spec/server/robustness_spec.rb +34 -0
  121. data/spec/server/stopping_spec.rb +55 -0
  122. data/spec/server/swiftiply.yml +6 -0
  123. data/spec/server/swiftiply_spec.rb +32 -0
  124. data/spec/server/tcp_spec.rb +57 -0
  125. data/spec/server/threaded_spec.rb +27 -0
  126. data/spec/server/unix_socket_spec.rb +26 -0
  127. data/spec/server_spec.rb +100 -0
  128. data/spec/spec_helper.rb +220 -0
  129. data/tasks/announce.rake +22 -0
  130. data/tasks/deploy.rake +13 -0
  131. data/tasks/email.erb +30 -0
  132. data/tasks/gem.rake +66 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +43 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +251 -0
@@ -0,0 +1,188 @@
1
+ require 'yaml'
2
+
3
+ module Thin
4
+ # Error raised that will abort the process and print not backtrace.
5
+ class RunnerError < RuntimeError; end
6
+
7
+ # Raised when a mandatory option is missing to run a command.
8
+ class OptionRequired < RunnerError
9
+ def initialize(option)
10
+ super("#{option} option required")
11
+ end
12
+ end
13
+
14
+ # Raised when an option is not valid.
15
+ class InvalidOption < RunnerError; end
16
+
17
+ # Build and control Thin servers.
18
+ # Hey Controller pattern is not only for web apps yo!
19
+ module Controllers
20
+ # Controls one Thin server.
21
+ # Allow to start, stop, restart and configure a single thin server.
22
+ class Controller
23
+ include Logging
24
+
25
+ # Command line options passed to the thin script
26
+ attr_accessor :options
27
+
28
+ def initialize(options)
29
+ @options = options
30
+
31
+ if @options[:socket]
32
+ @options.delete(:address)
33
+ @options.delete(:port)
34
+ end
35
+ end
36
+
37
+ def start
38
+ # Constantize backend class
39
+ @options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend]
40
+
41
+ server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket
42
+ @options[:port], # Port ignored on UNIX socket
43
+ @options)
44
+
45
+ # Set options
46
+ server.pid_file = @options[:pid]
47
+ server.log_file = @options[:log]
48
+ server.timeout = @options[:timeout]
49
+ server.maximum_connections = @options[:max_conns]
50
+ server.maximum_persistent_connections = @options[:max_persistent_conns]
51
+ server.threaded = @options[:threaded]
52
+ server.no_epoll = @options[:no_epoll] if server.backend.respond_to?(:no_epoll=)
53
+
54
+ # ssl support
55
+ if @options[:ssl]
56
+ server.ssl = true
57
+ server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => @options[:ssl_verify] }
58
+ end
59
+
60
+ # Detach the process, after this line the current process returns
61
+ server.daemonize if @options[:daemonize]
62
+
63
+ # +config+ must be called before changing privileges since it might require superuser power.
64
+ server.config
65
+
66
+ server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]
67
+
68
+ # If a Rack config file is specified we eval it inside a Rack::Builder block to create
69
+ # a Rack adapter from it. Or else we guess which adapter to use and load it.
70
+ if @options[:rackup]
71
+ server.app = load_rackup_config
72
+ else
73
+ server.app = load_adapter
74
+ end
75
+
76
+ # If a prefix is required, wrap in Rack URL mapper
77
+ server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix]
78
+
79
+ # If a stats URL is specified, wrap in Stats adapter
80
+ server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats]
81
+
82
+ # Register restart procedure which just start another process with same options,
83
+ # so that's why this is done here.
84
+ server.on_restart { Command.run(:start, @options) }
85
+
86
+ server.start
87
+ end
88
+
89
+ def stop
90
+ raise OptionRequired, :pid unless @options[:pid]
91
+
92
+ tail_log(@options[:log]) do
93
+ if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60))
94
+ wait_for_file :deletion, @options[:pid]
95
+ end
96
+ end
97
+ end
98
+
99
+ def restart
100
+ raise OptionRequired, :pid unless @options[:pid]
101
+
102
+ tail_log(@options[:log]) do
103
+ if Server.restart(@options[:pid])
104
+ wait_for_file :creation, @options[:pid]
105
+ end
106
+ end
107
+ end
108
+
109
+ def config
110
+ config_file = @options.delete(:config) || raise(OptionRequired, :config)
111
+
112
+ # Stringify keys
113
+ @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }
114
+
115
+ File.open(config_file, 'w') { |f| f << @options.to_yaml }
116
+ log ">> Wrote configuration to #{config_file}"
117
+ end
118
+
119
+ protected
120
+ # Wait for a pid file to either be created or deleted.
121
+ def wait_for_file(state, file)
122
+ Timeout.timeout(@options[:timeout] || 30) do
123
+ case state
124
+ when :creation then sleep 0.1 until File.exist?(file)
125
+ when :deletion then sleep 0.1 while File.exist?(file)
126
+ end
127
+ end
128
+ end
129
+
130
+ # Tail the log file of server +number+ during the execution of the block.
131
+ def tail_log(log_file)
132
+ if log_file
133
+ tail_thread = tail(log_file)
134
+ yield
135
+ tail_thread.kill
136
+ else
137
+ yield
138
+ end
139
+ end
140
+
141
+ # Acts like GNU tail command. Taken from Rails.
142
+ def tail(file)
143
+ cursor = File.exist?(file) ? File.size(file) : 0
144
+ last_checked = Time.now
145
+ tail_thread = Thread.new do
146
+ Thread.pass until File.exist?(file)
147
+ File.open(file, 'r') do |f|
148
+ loop do
149
+ f.seek cursor
150
+ if f.mtime > last_checked
151
+ last_checked = f.mtime
152
+ contents = f.read
153
+ cursor += contents.length
154
+ print contents
155
+ STDOUT.flush
156
+ end
157
+ sleep 0.1
158
+ end
159
+ end
160
+ end
161
+ sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file
162
+ tail_thread
163
+ end
164
+
165
+ private
166
+ def load_adapter
167
+ adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir])
168
+ log ">> Using #{adapter} adapter"
169
+ Rack::Adapter.for(adapter, @options)
170
+ rescue Rack::AdapterNotFound => e
171
+ raise InvalidOption, e.message
172
+ end
173
+
174
+ def load_rackup_config
175
+ ENV['RACK_ENV'] = @options[:environment]
176
+ case @options[:rackup]
177
+ when /\.rb$/
178
+ Kernel.load(@options[:rackup])
179
+ Object.const_get(File.basename(@options[:rackup], '.rb').capitalize.to_sym)
180
+ when /\.ru$/
181
+ Rack::Adapter.load(@options[:rackup])
182
+ else
183
+ raise "Invalid rackup file. please specify either a .ru or .rb file"
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,75 @@
1
+ require 'erb'
2
+
3
+ module Thin
4
+ module Controllers
5
+ # System service controller to launch all servers which
6
+ # config files are in a directory.
7
+ class Service < Controller
8
+ INITD_PATH = '/etc/init.d/thin'
9
+ DEFAULT_CONFIG_PATH = '/etc/thin'
10
+ TEMPLATE = File.dirname(__FILE__) + '/service.sh.erb'
11
+
12
+ def initialize(options)
13
+ super
14
+
15
+ raise PlatformNotSupported, 'Running as a service only supported on Linux' unless Thin.linux?
16
+ end
17
+
18
+ def config_path
19
+ @options[:all] || DEFAULT_CONFIG_PATH
20
+ end
21
+
22
+ def start
23
+ run :start
24
+ end
25
+
26
+ def stop
27
+ run :stop
28
+ end
29
+
30
+ def restart
31
+ run :restart
32
+ end
33
+
34
+ def install(config_files_path=DEFAULT_CONFIG_PATH)
35
+ if File.exist?(INITD_PATH)
36
+ log ">> Thin service already installed at #{INITD_PATH}"
37
+ else
38
+ log ">> Installing thin service at #{INITD_PATH} ..."
39
+ sh "mkdir -p #{File.dirname(INITD_PATH)}"
40
+ log "writing #{INITD_PATH}"
41
+ File.open(INITD_PATH, 'w') do |f|
42
+ f << ERB.new(File.read(TEMPLATE)).result(binding)
43
+ end
44
+ sh "chmod +x #{INITD_PATH}" # Make executable
45
+ end
46
+
47
+ sh "mkdir -p #{config_files_path}"
48
+
49
+ log ''
50
+ log "To configure thin to start at system boot:"
51
+ log "on RedHat like systems:"
52
+ log " sudo /sbin/chkconfig --level 345 #{NAME} on"
53
+ log "on Debian-like systems (Ubuntu):"
54
+ log " sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
55
+ log "on Gentoo:"
56
+ log " sudo rc-update add #{NAME} default"
57
+ log ''
58
+ log "Then put your config files in #{config_files_path}"
59
+ end
60
+
61
+ private
62
+ def run(command)
63
+ Dir[config_path + '/*'].each do |config|
64
+ log "[#{command}] #{config} ..."
65
+ Command.run(command, :config => config, :daemonize => true)
66
+ end
67
+ end
68
+
69
+ def sh(cmd)
70
+ log cmd
71
+ system(cmd)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,39 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: thin
4
+ # Required-Start: $local_fs $remote_fs
5
+ # Required-Stop: $local_fs $remote_fs
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: S 0 1 6
8
+ # Short-Description: thin initscript
9
+ # Description: thin
10
+ ### END INIT INFO
11
+
12
+ # Original author: Forrest Robertson
13
+
14
+ # Do NOT "set -e"
15
+
16
+ DAEMON=<%= Command.script %>
17
+ SCRIPT_NAME=<%= INITD_PATH %>
18
+ CONFIG_PATH=<%= config_files_path %>
19
+
20
+ # Exit if the package is not installed
21
+ [ -x "$DAEMON" ] || exit 0
22
+
23
+ case "$1" in
24
+ start)
25
+ $DAEMON start --all $CONFIG_PATH
26
+ ;;
27
+ stop)
28
+ $DAEMON stop --all $CONFIG_PATH
29
+ ;;
30
+ restart)
31
+ $DAEMON restart --all $CONFIG_PATH
32
+ ;;
33
+ *)
34
+ echo "Usage: $SCRIPT_NAME {start|stop|restart}" >&2
35
+ exit 3
36
+ ;;
37
+ esac
38
+
39
+ :
@@ -0,0 +1,180 @@
1
+ require 'etc'
2
+ require 'daemons' unless Thin.win?
3
+
4
+ module Process
5
+ # Returns +true+ the process identied by +pid+ is running.
6
+ def running?(pid)
7
+ Process.getpgid(pid) != -1
8
+ rescue Errno::EPERM
9
+ true
10
+ rescue Errno::ESRCH
11
+ false
12
+ end
13
+ module_function :running?
14
+ end
15
+
16
+ module Thin
17
+ # Raised when the pid file already exist starting as a daemon.
18
+ class PidFileExist < RuntimeError; end
19
+
20
+ # Module included in classes that can be turned into a daemon.
21
+ # Handle stuff like:
22
+ # * storing the PID in a file
23
+ # * redirecting output to the log file
24
+ # * changing processs privileges
25
+ # * killing the process gracefully
26
+ module Daemonizable
27
+ attr_accessor :pid_file, :log_file
28
+
29
+ def self.included(base)
30
+ base.extend ClassMethods
31
+ end
32
+
33
+ def pid
34
+ File.exist?(pid_file) ? open(pid_file).read.to_i : nil
35
+ end
36
+
37
+ # Turns the current script into a daemon process that detaches from the console.
38
+ def daemonize
39
+ raise PlatformNotSupported, 'Daemonizing is not supported on Windows' if Thin.win?
40
+ raise ArgumentError, 'You must specify a pid_file to daemonize' unless @pid_file
41
+
42
+ remove_stale_pid_file
43
+
44
+ pwd = Dir.pwd # Current directory is changed during daemonization, so store it
45
+
46
+ # HACK we need to create the directory before daemonization to prevent a bug under 1.9
47
+ # ignoring all signals when the directory is created after daemonization.
48
+ FileUtils.mkdir_p File.dirname(@pid_file)
49
+ FileUtils.mkdir_p File.dirname(@log_file)
50
+
51
+ Daemonize.daemonize(File.expand_path(@log_file), name)
52
+
53
+ Dir.chdir(pwd)
54
+
55
+ write_pid_file
56
+
57
+ at_exit do
58
+ log ">> Exiting!"
59
+ remove_pid_file
60
+ end
61
+ end
62
+
63
+ # Change privileges of the process
64
+ # to the specified user and group.
65
+ def change_privilege(user, group=user)
66
+ log ">> Changing process privilege to #{user}:#{group}"
67
+
68
+ uid, gid = Process.euid, Process.egid
69
+ target_uid = Etc.getpwnam(user).uid
70
+ target_gid = Etc.getgrnam(group).gid
71
+
72
+ if uid != target_uid || gid != target_gid
73
+ # Change process ownership
74
+ Process.initgroups(user, target_gid)
75
+ Process::GID.change_privilege(target_gid)
76
+ Process::UID.change_privilege(target_uid)
77
+ end
78
+ rescue Errno::EPERM => e
79
+ log "Couldn't change user and group to #{user}:#{group}: #{e}"
80
+ end
81
+
82
+ # Register a proc to be called to restart the server.
83
+ def on_restart(&block)
84
+ @on_restart = block
85
+ end
86
+
87
+ # Restart the server.
88
+ def restart
89
+ if @on_restart
90
+ log '>> Restarting ...'
91
+ stop
92
+ remove_pid_file
93
+ @on_restart.call
94
+ exit!
95
+ end
96
+ end
97
+
98
+ module ClassMethods
99
+ # Send a QUIT or INT (if timeout is +0+) signal the process which
100
+ # PID is stored in +pid_file+.
101
+ # If the process is still running after +timeout+, KILL signal is
102
+ # sent.
103
+ def kill(pid_file, timeout=60)
104
+ if timeout == 0
105
+ send_signal('INT', pid_file, timeout)
106
+ else
107
+ send_signal('QUIT', pid_file, timeout)
108
+ end
109
+ end
110
+
111
+ # Restart the server by sending HUP signal.
112
+ def restart(pid_file)
113
+ send_signal('HUP', pid_file)
114
+ end
115
+
116
+ # Send a +signal+ to the process which PID is stored in +pid_file+.
117
+ def send_signal(signal, pid_file, timeout=60)
118
+ if pid = read_pid_file(pid_file)
119
+ Logging.log "Sending #{signal} signal to process #{pid} ... "
120
+ Process.kill(signal, pid)
121
+ Timeout.timeout(timeout) do
122
+ sleep 0.1 while Process.running?(pid)
123
+ end
124
+ else
125
+ Logging.log "Can't stop process, no PID found in #{pid_file}"
126
+ end
127
+ rescue Timeout::Error
128
+ Logging.log "Timeout!"
129
+ force_kill pid_file
130
+ rescue Interrupt
131
+ force_kill pid_file
132
+ rescue Errno::ESRCH # No such process
133
+ Logging.log "process not found!"
134
+ force_kill pid_file
135
+ end
136
+
137
+ def force_kill(pid_file)
138
+ if pid = read_pid_file(pid_file)
139
+ Logging.log "Sending KILL signal to process #{pid} ... "
140
+ Process.kill("KILL", pid)
141
+ File.delete(pid_file) if File.exist?(pid_file)
142
+ else
143
+ Logging.log "Can't stop process, no PID found in #{pid_file}"
144
+ end
145
+ end
146
+
147
+ def read_pid_file(file)
148
+ if File.file?(file) && pid = File.read(file)
149
+ pid.to_i
150
+ else
151
+ nil
152
+ end
153
+ end
154
+ end
155
+
156
+ protected
157
+ def remove_pid_file
158
+ File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
159
+ end
160
+
161
+ def write_pid_file
162
+ log ">> Writing PID to #{@pid_file}"
163
+ open(@pid_file,"w") { |f| f.write(Process.pid) }
164
+ File.chmod(0644, @pid_file)
165
+ end
166
+
167
+ # If PID file is stale, remove it.
168
+ def remove_stale_pid_file
169
+ if File.exist?(@pid_file)
170
+ if pid && Process.running?(pid)
171
+ raise PidFileExist, "#{@pid_file} already exists, seems like it's already running (process ID: #{pid}). " +
172
+ "Stop the process or delete #{@pid_file}."
173
+ else
174
+ log ">> Deleting stale PID file #{@pid_file}"
175
+ remove_pid_file
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end