thin 0.5.3-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of thin might be problematic. Click here for more details.
- data/CHANGELOG +40 -0
- data/COPYING +18 -0
- data/README +60 -0
- data/Rakefile +11 -0
- data/benchmark/simple.rb +48 -0
- data/bin/thin +123 -0
- data/doc/benchmarks.txt +86 -0
- data/doc/rdoc/classes/Process.html +181 -0
- data/doc/rdoc/classes/Rack.html +156 -0
- data/doc/rdoc/classes/Rack/Adapter.html +155 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
- data/doc/rdoc/classes/Rack/Handler.html +155 -0
- data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
- data/doc/rdoc/classes/Thin.html +164 -0
- data/doc/rdoc/classes/Thin/Cluster.html +399 -0
- data/doc/rdoc/classes/Thin/Connection.html +223 -0
- data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
- data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
- data/doc/rdoc/classes/Thin/Headers.html +238 -0
- data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
- data/doc/rdoc/classes/Thin/Logging.html +201 -0
- data/doc/rdoc/classes/Thin/Request.html +231 -0
- data/doc/rdoc/classes/Thin/Response.html +271 -0
- data/doc/rdoc/classes/Thin/Server.html +295 -0
- data/doc/rdoc/classes/Thin/StopServer.html +143 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/README.html +226 -0
- data/doc/rdoc/files/bin/thin.html +245 -0
- data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
- data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
- data/doc/rdoc/files/lib/thin_rb.html +152 -0
- data/doc/rdoc/index.html +10 -0
- data/doc/rdoc/logo.gif +0 -0
- data/doc/rdoc/rdoc-style.css +55 -0
- data/example/config.ru +9 -0
- data/example/thin.god +72 -0
- data/ext/thin_parser/common.rl +54 -0
- data/ext/thin_parser/ext_help.h +14 -0
- data/ext/thin_parser/extconf.rb +6 -0
- data/ext/thin_parser/parser.c +1199 -0
- data/ext/thin_parser/parser.h +49 -0
- data/ext/thin_parser/parser.rl +143 -0
- data/ext/thin_parser/thin.c +424 -0
- data/lib/rack/adapter/rails.rb +155 -0
- data/lib/rack/handler/thin.rb +13 -0
- data/lib/thin.rb +36 -0
- data/lib/thin/cluster.rb +106 -0
- data/lib/thin/connection.rb +46 -0
- data/lib/thin/daemonizing.rb +112 -0
- data/lib/thin/headers.rb +37 -0
- data/lib/thin/logging.rb +23 -0
- data/lib/thin/request.rb +72 -0
- data/lib/thin/response.rb +48 -0
- data/lib/thin/server.rb +80 -0
- data/lib/thin/statuses.rb +43 -0
- data/lib/thin/version.rb +11 -0
- data/lib/thin_parser.so +0 -0
- data/spec/cluster_spec.rb +58 -0
- data/spec/daemonizing_spec.rb +93 -0
- data/spec/headers_spec.rb +35 -0
- data/spec/rack_rails_spec.rb +92 -0
- data/spec/rails_app/app/controllers/application.rb +10 -0
- data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/views/simple/index.html.erb +15 -0
- data/spec/rails_app/config/boot.rb +109 -0
- data/spec/rails_app/config/environment.rb +64 -0
- data/spec/rails_app/config/environments/development.rb +18 -0
- data/spec/rails_app/config/environments/production.rb +19 -0
- data/spec/rails_app/config/environments/test.rb +22 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/routes.rb +35 -0
- data/spec/rails_app/public/404.html +30 -0
- data/spec/rails_app/public/422.html +30 -0
- data/spec/rails_app/public/500.html +30 -0
- data/spec/rails_app/public/dispatch.cgi +10 -0
- data/spec/rails_app/public/dispatch.fcgi +24 -0
- data/spec/rails_app/public/dispatch.rb +10 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/index.html +277 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
- data/spec/rails_app/public/javascripts/effects.js +1120 -0
- data/spec/rails_app/public/javascripts/prototype.js +4225 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/about +3 -0
- data/spec/rails_app/script/console +3 -0
- data/spec/rails_app/script/destroy +3 -0
- data/spec/rails_app/script/generate +3 -0
- data/spec/rails_app/script/performance/benchmarker +3 -0
- data/spec/rails_app/script/performance/profiler +3 -0
- data/spec/rails_app/script/performance/request +3 -0
- data/spec/rails_app/script/plugin +3 -0
- data/spec/rails_app/script/process/inspector +3 -0
- data/spec/rails_app/script/process/reaper +3 -0
- data/spec/rails_app/script/process/spawner +3 -0
- data/spec/rails_app/script/runner +3 -0
- data/spec/rails_app/script/server +3 -0
- data/spec/request_spec.rb +258 -0
- data/spec/response_spec.rb +56 -0
- data/spec/server_spec.rb +75 -0
- data/spec/spec_helper.rb +127 -0
- metadata +219 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
# This as been submitted to Rack as a patch, tested and everything.
|
2
|
+
# Bug Christian Neukirchen at chneukirchen@gmail.com to apply the patch!
|
3
|
+
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
# Adapter to run a Rails app with any supported Rack handler.
|
7
|
+
# By default it will try to load the Rails application in the
|
8
|
+
# current directory in the development environment.
|
9
|
+
# Options:
|
10
|
+
# root: Root directory of the Rails app
|
11
|
+
# env: Rails environment to run in (development, production or test)
|
12
|
+
# Based on http://fuzed.rubyforge.org/ Rails adapter
|
13
|
+
module Rack
|
14
|
+
module Adapter
|
15
|
+
class Rails
|
16
|
+
def initialize(options={})
|
17
|
+
@root = options[:root] || Dir.pwd
|
18
|
+
@env = options[:environment] || 'development'
|
19
|
+
@prefix = options[:prefix]
|
20
|
+
|
21
|
+
load_application
|
22
|
+
|
23
|
+
@file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_application
|
27
|
+
ENV['RAILS_ENV'] = @env
|
28
|
+
|
29
|
+
require "#{@root}/config/environment"
|
30
|
+
require 'dispatcher'
|
31
|
+
|
32
|
+
ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO refactor this in File#can_serve?(path) ??
|
36
|
+
def file_exist?(path)
|
37
|
+
full_path = ::File.join(@file_server.root, Utils.unescape(path))
|
38
|
+
::File.file?(full_path) && ::File.readable?(full_path)
|
39
|
+
end
|
40
|
+
|
41
|
+
def serve_file(env)
|
42
|
+
@file_server.call(env)
|
43
|
+
end
|
44
|
+
|
45
|
+
def serve_rails(env)
|
46
|
+
request = Request.new(env)
|
47
|
+
response = Response.new
|
48
|
+
|
49
|
+
session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
|
50
|
+
cgi = CGIWrapper.new(request, response)
|
51
|
+
|
52
|
+
Dispatcher.dispatch(cgi, session_options, response)
|
53
|
+
|
54
|
+
response.finish
|
55
|
+
end
|
56
|
+
|
57
|
+
def call(env)
|
58
|
+
path = env['PATH_INFO'].chomp('/')
|
59
|
+
cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
|
60
|
+
|
61
|
+
if file_exist?(path) # Serve the file if it's there
|
62
|
+
serve_file(env)
|
63
|
+
elsif file_exist?(cached_path) # Serve the page cache if it's there
|
64
|
+
env['PATH_INFO'] = cached_path
|
65
|
+
serve_file(env)
|
66
|
+
else # No static file, let Rails handle it
|
67
|
+
serve_rails(env)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
class CGIWrapper < ::CGI
|
74
|
+
def initialize(request, response, *args)
|
75
|
+
@request = request
|
76
|
+
@response = response
|
77
|
+
@args = *args
|
78
|
+
@input = request.body
|
79
|
+
|
80
|
+
super *args
|
81
|
+
end
|
82
|
+
|
83
|
+
def header(options = "text/html")
|
84
|
+
if options.is_a?(String)
|
85
|
+
@response['Content-Type'] = options unless @response['Content-Type']
|
86
|
+
else
|
87
|
+
@response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
|
88
|
+
|
89
|
+
@response['Content-Type'] = options.delete('type') || "text/html"
|
90
|
+
@response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
|
91
|
+
|
92
|
+
@response['Content-Language'] = options.delete('language') if options['language']
|
93
|
+
@response['Expires'] = options.delete('expires') if options['expires']
|
94
|
+
|
95
|
+
@response.status = options.delete('Status') if options['Status']
|
96
|
+
|
97
|
+
# Convert 'cookie' header to 'Set-Cookie' headers.
|
98
|
+
# Because Set-Cookie header can appear more the once in the response body,
|
99
|
+
# we store it in a line break seperated string that will be translated to
|
100
|
+
# multiple Set-Cookie header by the handler.
|
101
|
+
if cookie = options.delete('cookie')
|
102
|
+
cookies = []
|
103
|
+
|
104
|
+
case cookie
|
105
|
+
when Array then cookie.each { |c| cookies << c.to_s }
|
106
|
+
when Hash then cookie.each { |_, c| cookies << c.to_s }
|
107
|
+
else cookies << cookie.to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
@output_cookies.each { |c| cookies << c.to_s } if @output_cookies
|
111
|
+
|
112
|
+
@response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
|
113
|
+
end
|
114
|
+
|
115
|
+
options.each { |k,v| @response[k] = v }
|
116
|
+
end
|
117
|
+
|
118
|
+
""
|
119
|
+
end
|
120
|
+
|
121
|
+
def params
|
122
|
+
@params ||= @request.params
|
123
|
+
end
|
124
|
+
|
125
|
+
def cookies
|
126
|
+
@request.cookies
|
127
|
+
end
|
128
|
+
|
129
|
+
def query_string
|
130
|
+
@request.query_string
|
131
|
+
end
|
132
|
+
|
133
|
+
# Used to wrap the normal args variable used inside CGI.
|
134
|
+
def args
|
135
|
+
@args
|
136
|
+
end
|
137
|
+
|
138
|
+
# Used to wrap the normal env_table variable used inside CGI.
|
139
|
+
def env_table
|
140
|
+
@request.env
|
141
|
+
end
|
142
|
+
|
143
|
+
# Used to wrap the normal stdinput variable used inside CGI.
|
144
|
+
def stdinput
|
145
|
+
@input
|
146
|
+
end
|
147
|
+
|
148
|
+
def stdoutput
|
149
|
+
STDERR.puts "stdoutput should not be used."
|
150
|
+
@response.body
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/thin.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'timeout'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'eventmachine'
|
9
|
+
|
10
|
+
require 'thin/version'
|
11
|
+
require 'thin/statuses'
|
12
|
+
|
13
|
+
module Thin
|
14
|
+
NAME = 'thin'.freeze
|
15
|
+
SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
|
16
|
+
|
17
|
+
autoload :Cluster, 'thin/cluster'
|
18
|
+
autoload :Connection, 'thin/connection'
|
19
|
+
autoload :Daemonizable, 'thin/daemonizing'
|
20
|
+
autoload :Logging, 'thin/logging'
|
21
|
+
autoload :Headers, 'thin/headers'
|
22
|
+
autoload :Request, 'thin/request'
|
23
|
+
autoload :Response, 'thin/response'
|
24
|
+
autoload :Server, 'thin/server'
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'rack'
|
28
|
+
|
29
|
+
module Rack
|
30
|
+
module Handler
|
31
|
+
autoload :Thin, 'rack/handler/thin'
|
32
|
+
end
|
33
|
+
module Adapter
|
34
|
+
autoload :Rails, 'rack/adapter/rails'
|
35
|
+
end
|
36
|
+
end
|
data/lib/thin/cluster.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Thin
|
2
|
+
# Control a set of servers.
|
3
|
+
# * Generate start and stop commands and run them.
|
4
|
+
# * Inject the port number in the pid and log filenames.
|
5
|
+
# Servers are started throught the +thin+ commandline script.
|
6
|
+
class Cluster
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
# Path to the +thin+ script used to control the servers.
|
10
|
+
# Leave this to default to use the one in the path.
|
11
|
+
attr_accessor :script
|
12
|
+
|
13
|
+
# Number of servers in the cluster.
|
14
|
+
attr_accessor :size
|
15
|
+
|
16
|
+
# Command line options passed to the thin script
|
17
|
+
attr_accessor :options
|
18
|
+
|
19
|
+
# Create a new cluster of servers launched using +options+.
|
20
|
+
def initialize(options)
|
21
|
+
@options = options.merge(:daemonize => true)
|
22
|
+
@size = @options.delete(:servers)
|
23
|
+
@script = 'thin'
|
24
|
+
end
|
25
|
+
|
26
|
+
def first_port; @options[:port] end
|
27
|
+
def address; @options[:address] end
|
28
|
+
def pid_file; File.expand_path File.join(@options[:chdir], @options[:pid]) end
|
29
|
+
def log_file; File.expand_path File.join(@options[:chdir], @options[:log]) end
|
30
|
+
|
31
|
+
# Start the servers
|
32
|
+
def start
|
33
|
+
with_each_server { |port| start_on_port port }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Start the server on a single port
|
37
|
+
def start_on_port(port)
|
38
|
+
log "Starting #{address}:#{port} ... "
|
39
|
+
|
40
|
+
run :start, @options, port
|
41
|
+
end
|
42
|
+
|
43
|
+
# Stop the servers
|
44
|
+
def stop
|
45
|
+
with_each_server { |port| stop_on_port port }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stop the server running on +port+
|
49
|
+
def stop_on_port(port)
|
50
|
+
log "Stopping #{address}:#{port} ... "
|
51
|
+
|
52
|
+
run :stop, @options, port
|
53
|
+
end
|
54
|
+
|
55
|
+
# Stop and start the servers.
|
56
|
+
def restart
|
57
|
+
stop
|
58
|
+
sleep 0.1 # Let's breath a bit shall we ?
|
59
|
+
start
|
60
|
+
end
|
61
|
+
|
62
|
+
def log_file_for(port)
|
63
|
+
include_port_number log_file, port
|
64
|
+
end
|
65
|
+
|
66
|
+
def pid_file_for(port)
|
67
|
+
include_port_number pid_file, port
|
68
|
+
end
|
69
|
+
|
70
|
+
def pid_for(port)
|
71
|
+
File.read(pid_file_for(port)).chomp.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
# Send the command to the +thin+ script
|
76
|
+
def run(cmd, options, port)
|
77
|
+
shell_cmd = shellify(cmd, options.merge(:port => port, :pid => pid_file_for(port), :log => log_file_for(port)))
|
78
|
+
trace shell_cmd
|
79
|
+
ouput = `#{shell_cmd}`.chomp
|
80
|
+
log ouput unless ouput.empty?
|
81
|
+
end
|
82
|
+
|
83
|
+
# Turn into a runnable shell command
|
84
|
+
def shellify(cmd, options)
|
85
|
+
shellified_options = options.inject([]) do |args, (name, value)|
|
86
|
+
args << case value
|
87
|
+
when NilClass
|
88
|
+
when TrueClass then "--#{name}"
|
89
|
+
else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
"#{@script} #{cmd} #{shellified_options.compact.join(' ')}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_each_server
|
96
|
+
@size.times { |n| yield first_port + n }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add the port numbers in the filename
|
100
|
+
# so each instance get its own file
|
101
|
+
def include_port_number(path, port)
|
102
|
+
raise ArgumentError, "filename '#{path}' must include an extension" unless path =~ /\./
|
103
|
+
path.gsub(/\.(.+)$/) { ".#{port}.#{$1}" }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Thin
|
4
|
+
class Connection < EventMachine::Connection
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
attr_accessor :app
|
8
|
+
|
9
|
+
def post_init
|
10
|
+
@request = Request.new
|
11
|
+
@response = Response.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_data(data)
|
15
|
+
trace { data }
|
16
|
+
process if @request.parse(data)
|
17
|
+
rescue InvalidRequest => e
|
18
|
+
log "Invalid request"
|
19
|
+
log_error e
|
20
|
+
close_connection
|
21
|
+
end
|
22
|
+
|
23
|
+
def process
|
24
|
+
env = @request.env
|
25
|
+
|
26
|
+
# Add client info to the request env
|
27
|
+
env[Request::REMOTE_ADDR] = env[Request::FORWARDED_FOR] || Socket.unpack_sockaddr_in(get_peername)[1]
|
28
|
+
|
29
|
+
# Process the request
|
30
|
+
@response.status, @response.headers, @response.body = @app.call(env)
|
31
|
+
|
32
|
+
# Send the response
|
33
|
+
trace { @response.to_s }
|
34
|
+
send_data @response.to_s
|
35
|
+
|
36
|
+
close_connection_after_writing
|
37
|
+
|
38
|
+
rescue Object => e
|
39
|
+
log "Unexpected error while processing request: #{e.message}"
|
40
|
+
log_error e
|
41
|
+
close_connection rescue nil
|
42
|
+
ensure
|
43
|
+
@response.close rescue nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'daemons'
|
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::ESRCH
|
9
|
+
false
|
10
|
+
end
|
11
|
+
module_function :running?
|
12
|
+
end
|
13
|
+
|
14
|
+
module Thin
|
15
|
+
# Module included in classes that can be turned into a daemon.
|
16
|
+
# Handle stuff like:
|
17
|
+
# * storing the PID in a file
|
18
|
+
# * redirecting output to the log file
|
19
|
+
# * changing processs privileges
|
20
|
+
# * killing the process gracefully
|
21
|
+
module Daemonizable
|
22
|
+
attr_accessor :pid_file, :log_file
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
base.extend ClassMethods
|
26
|
+
end
|
27
|
+
|
28
|
+
def pid
|
29
|
+
File.exist?(pid_file) ? open(pid_file).read : nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Turns the current script into a daemon process that detaches from the console.
|
33
|
+
def daemonize
|
34
|
+
check_plateform_support
|
35
|
+
raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
|
36
|
+
|
37
|
+
pwd = Dir.pwd # Current directory is changed during daemonization, so store it
|
38
|
+
|
39
|
+
Daemonize.daemonize(File.expand_path(@log_file))
|
40
|
+
|
41
|
+
Dir.chdir(pwd)
|
42
|
+
|
43
|
+
write_pid_file
|
44
|
+
at_exit do
|
45
|
+
log ">> Exiting!"
|
46
|
+
remove_pid_file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Change privileges of the process
|
51
|
+
# to the specified user and group.
|
52
|
+
def change_privilege(user, group=user)
|
53
|
+
check_plateform_support
|
54
|
+
log ">> Changing process privilege to #{user}:#{group}"
|
55
|
+
|
56
|
+
uid, gid = Process.euid, Process.egid
|
57
|
+
target_uid = Etc.getpwnam(user).uid
|
58
|
+
target_gid = Etc.getgrnam(group).gid
|
59
|
+
|
60
|
+
if uid != target_uid || gid != target_gid
|
61
|
+
# Change process ownership
|
62
|
+
Process.initgroups(user, target_gid)
|
63
|
+
Process::GID.change_privilege(target_gid)
|
64
|
+
Process::UID.change_privilege(target_uid)
|
65
|
+
end
|
66
|
+
rescue Errno::EPERM => e
|
67
|
+
log "Couldn't change user and group to #{user}:#{group}: #{e}"
|
68
|
+
end
|
69
|
+
|
70
|
+
module ClassMethods
|
71
|
+
# Kill the process which PID is stored in +pid_file+.
|
72
|
+
def kill(pid_file, timeout=60)
|
73
|
+
if pid = open(pid_file).read
|
74
|
+
pid = pid.to_i
|
75
|
+
print "Sending INT signal to process #{pid} ... "
|
76
|
+
begin
|
77
|
+
Process.kill('INT', pid)
|
78
|
+
Timeout.timeout(timeout) do
|
79
|
+
sleep 0.1 while Process.running?(pid)
|
80
|
+
end
|
81
|
+
rescue Timeout::Error
|
82
|
+
print "timeout, Sending KILL signal ... "
|
83
|
+
Process.kill('KILL', pid)
|
84
|
+
end
|
85
|
+
puts "stopped!"
|
86
|
+
else
|
87
|
+
puts "Can't stop process, no PID found in #{@pid_file}"
|
88
|
+
end
|
89
|
+
rescue Errno::ESRCH # No such process
|
90
|
+
puts "process not found!"
|
91
|
+
ensure
|
92
|
+
File.delete(pid_file) rescue nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def check_plateform_support
|
98
|
+
raise RuntimeError, 'Daemonizing not supported on Windows' if RUBY_PLATFORM =~ /mswin/
|
99
|
+
end
|
100
|
+
|
101
|
+
def remove_pid_file
|
102
|
+
File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
|
103
|
+
end
|
104
|
+
|
105
|
+
def write_pid_file
|
106
|
+
log ">> Writing PID to #{@pid_file}"
|
107
|
+
FileUtils.mkdir_p File.dirname(@pid_file)
|
108
|
+
open(@pid_file,"w") { |f| f.write(Process.pid) }
|
109
|
+
File.chmod(0644, @pid_file)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|