giga-smart-mod 0.0.1

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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/giga-smart-mod.gemspec +12 -0
  3. data/thin-2.0.1/CHANGELOG +422 -0
  4. data/thin-2.0.1/README.md +88 -0
  5. data/thin-2.0.1/Rakefile +22 -0
  6. data/thin-2.0.1/bin/thin +6 -0
  7. data/thin-2.0.1/example/adapter.rb +32 -0
  8. data/thin-2.0.1/example/async_app.ru +126 -0
  9. data/thin-2.0.1/example/async_chat.ru +247 -0
  10. data/thin-2.0.1/example/async_tailer.ru +100 -0
  11. data/thin-2.0.1/example/config.ru +22 -0
  12. data/thin-2.0.1/example/monit_sockets +20 -0
  13. data/thin-2.0.1/example/monit_unixsock +20 -0
  14. data/thin-2.0.1/example/myapp.rb +1 -0
  15. data/thin-2.0.1/example/ramaze.ru +12 -0
  16. data/thin-2.0.1/example/thin.god +80 -0
  17. data/thin-2.0.1/example/thin_solaris_smf.erb +36 -0
  18. data/thin-2.0.1/example/thin_solaris_smf.readme.txt +150 -0
  19. data/thin-2.0.1/example/vlad.rake +72 -0
  20. data/thin-2.0.1/ext/thin_parser/common.rl +59 -0
  21. data/thin-2.0.1/ext/thin_parser/ext_help.h +14 -0
  22. data/thin-2.0.1/ext/thin_parser/extconf.rb +6 -0
  23. data/thin-2.0.1/ext/thin_parser/parser.c +1447 -0
  24. data/thin-2.0.1/ext/thin_parser/parser.h +49 -0
  25. data/thin-2.0.1/ext/thin_parser/parser.rl +152 -0
  26. data/thin-2.0.1/ext/thin_parser/thin.c +435 -0
  27. data/thin-2.0.1/lib/rack/adapter/loader.rb +82 -0
  28. data/thin-2.0.1/lib/rack/adapter/rails.rb +172 -0
  29. data/thin-2.0.1/lib/rack/handler/thin.rb +13 -0
  30. data/thin-2.0.1/lib/rackup/handler/thin.rb +13 -0
  31. data/thin-2.0.1/lib/thin/backends/base.rb +169 -0
  32. data/thin-2.0.1/lib/thin/backends/swiftiply_client.rb +66 -0
  33. data/thin-2.0.1/lib/thin/backends/tcp_server.rb +34 -0
  34. data/thin-2.0.1/lib/thin/backends/unix_server.rb +56 -0
  35. data/thin-2.0.1/lib/thin/command.rb +53 -0
  36. data/thin-2.0.1/lib/thin/connection.rb +219 -0
  37. data/thin-2.0.1/lib/thin/controllers/cluster.rb +178 -0
  38. data/thin-2.0.1/lib/thin/controllers/controller.rb +189 -0
  39. data/thin-2.0.1/lib/thin/controllers/service.rb +76 -0
  40. data/thin-2.0.1/lib/thin/controllers/service.sh.erb +39 -0
  41. data/thin-2.0.1/lib/thin/daemonizing.rb +199 -0
  42. data/thin-2.0.1/lib/thin/env.rb +35 -0
  43. data/thin-2.0.1/lib/thin/headers.rb +47 -0
  44. data/thin-2.0.1/lib/thin/logging.rb +174 -0
  45. data/thin-2.0.1/lib/thin/rackup/handler.rb +33 -0
  46. data/thin-2.0.1/lib/thin/request.rb +159 -0
  47. data/thin-2.0.1/lib/thin/response.rb +144 -0
  48. data/thin-2.0.1/lib/thin/runner.rb +238 -0
  49. data/thin-2.0.1/lib/thin/server.rb +290 -0
  50. data/thin-2.0.1/lib/thin/stats.html.erb +216 -0
  51. data/thin-2.0.1/lib/thin/stats.rb +52 -0
  52. data/thin-2.0.1/lib/thin/statuses.rb +48 -0
  53. data/thin-2.0.1/lib/thin/version.rb +19 -0
  54. data/thin-2.0.1/lib/thin.rb +45 -0
  55. metadata +94 -0
@@ -0,0 +1,219 @@
1
+ require 'socket'
2
+
3
+ module Thin
4
+ # Connection between the server and client.
5
+ # This class is instanciated by EventMachine on each new connection
6
+ # that is opened.
7
+ class Connection < EventMachine::Connection
8
+ include Logging
9
+
10
+ # This is a template async response. N.B. Can't use string for body on 1.9
11
+ AsyncResponse = [-1, {}, []].freeze
12
+
13
+ # Rack application (adapter) served by this connection.
14
+ attr_accessor :app
15
+
16
+ # Backend to the server
17
+ attr_accessor :backend
18
+
19
+ # Current request served by the connection
20
+ attr_accessor :request
21
+
22
+ # Next response sent through the connection
23
+ attr_accessor :response
24
+
25
+ # Calling the application in a threaded allowing
26
+ # concurrent processing of requests.
27
+ attr_writer :threaded
28
+
29
+ # Get the connection ready to process a request.
30
+ def post_init
31
+ @request = Request.new
32
+ @response = Response.new
33
+ end
34
+
35
+ # Called when data is received from the client.
36
+ def receive_data(data)
37
+ @idle = false
38
+ trace data
39
+ process if @request.parse(data)
40
+ rescue InvalidRequest => e
41
+ log_error("Invalid request", e)
42
+ post_process Response::BAD_REQUEST
43
+ end
44
+
45
+ # Called when all data was received and the request
46
+ # is ready to be processed.
47
+ def process
48
+ if threaded?
49
+ @request.threaded = true
50
+ EventMachine.defer { post_process(pre_process) }
51
+ else
52
+ @request.threaded = false
53
+ post_process(pre_process)
54
+ end
55
+ end
56
+
57
+ def ssl_verify_peer(cert)
58
+ # In order to make the cert available later we have to have made at least
59
+ # a show of verifying it.
60
+ true
61
+ end
62
+
63
+ def pre_process
64
+ # Add client info to the request env
65
+ @request.remote_address = remote_address
66
+
67
+ # Connection may be closed unless the App#call response was a [-1, ...]
68
+ # It should be noted that connection objects will linger until this
69
+ # callback is no longer referenced, so be tidy!
70
+ @request.async_callback = method(:post_process)
71
+
72
+ if @backend.ssl?
73
+ @request.env["rack.url_scheme"] = "https"
74
+
75
+ if @backend.respond_to?(:port)
76
+ @request.env['SERVER_PORT'] = @backend.port.to_s
77
+ end
78
+
79
+ if cert = get_peer_cert
80
+ @request.env['rack.peer_cert'] = cert
81
+ end
82
+ end
83
+
84
+ # When we're under a non-async framework like rails, we can still spawn
85
+ # off async responses using the callback info, so there's little point
86
+ # in removing this.
87
+ response = AsyncResponse
88
+ catch(:async) do
89
+ # Process the request calling the Rack adapter
90
+ response = @app.call(@request.env)
91
+ end
92
+ response
93
+ rescue Exception => e
94
+ unexpected_error(e)
95
+ # Pass through error response
96
+ can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR
97
+ end
98
+
99
+ def post_process(result)
100
+ return unless result
101
+ result = result.to_a
102
+
103
+ # Status code -1 indicates that we're going to respond later (async).
104
+ return if result.first == AsyncResponse.first
105
+
106
+ @response.status, @response.headers, @response.body = *result
107
+
108
+ log_error("Rack application returned nil body. " \
109
+ "Probably you wanted it to be an empty string?") if @response.body.nil?
110
+
111
+ # HEAD requests should not return a body.
112
+ @response.skip_body! if @request.head?
113
+
114
+ # Make the response persistent if requested by the client
115
+ @response.persistent! if @request.persistent?
116
+
117
+ # Send the response
118
+ @response.each do |chunk|
119
+ trace chunk
120
+ send_data chunk
121
+ end
122
+
123
+ rescue Exception => e
124
+ unexpected_error(e)
125
+ # Close connection since we can't handle response gracefully
126
+ close_connection
127
+ ensure
128
+ # If the body is being deferred, then terminate afterward.
129
+ if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
130
+ @response.body.callback { terminate_request }
131
+ @response.body.errback { terminate_request }
132
+ else
133
+ # Don't terminate the response if we're going async.
134
+ terminate_request unless result && result.first == AsyncResponse.first
135
+ end
136
+ end
137
+
138
+ # Logs information about an unexpected exceptional condition
139
+ def unexpected_error(e)
140
+ log_error("Unexpected error while processing request", e)
141
+ end
142
+
143
+ def close_request_response
144
+ @request.async_close.succeed if @request.async_close
145
+ @request.close rescue nil
146
+ @response.close rescue nil
147
+ end
148
+
149
+ # Does request and response cleanup (closes open IO streams and
150
+ # deletes created temporary files).
151
+ # Re-initializes response and request if client supports persistent
152
+ # connection.
153
+ def terminate_request
154
+ unless persistent?
155
+ close_connection_after_writing rescue nil
156
+ close_request_response
157
+ else
158
+ close_request_response
159
+ # Connection become idle but it's still open
160
+ @idle = true
161
+ # Prepare the connection for another request if the client
162
+ # supports HTTP pipelining (persistent connection).
163
+ post_init
164
+ end
165
+ end
166
+
167
+ # Called when the connection is unbinded from the socket
168
+ # and can no longer be used to process requests.
169
+ def unbind
170
+ @request.async_close.succeed if @request.async_close
171
+ @response.body.fail if @response.body.respond_to?(:fail)
172
+ @backend.connection_finished(self)
173
+ end
174
+
175
+ # Allows this connection to be persistent.
176
+ def can_persist!
177
+ @can_persist = true
178
+ end
179
+
180
+ # Return +true+ if this connection is allowed to stay open and be persistent.
181
+ def can_persist?
182
+ @can_persist
183
+ end
184
+
185
+ # Return +true+ if the connection must be left open
186
+ # and ready to be reused for another request.
187
+ def persistent?
188
+ @can_persist && @response.persistent?
189
+ end
190
+
191
+ # Return +true+ if the connection is open but is not
192
+ # processing any user requests
193
+ def idle?
194
+ @idle
195
+ end
196
+
197
+ # +true+ if <tt>app.call</tt> will be called inside a thread.
198
+ # You can set all requests as threaded setting <tt>Connection#threaded=true</tt>
199
+ # or on a per-request case returning +true+ in <tt>app.deferred?</tt>.
200
+ def threaded?
201
+ @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
202
+ end
203
+
204
+ # IP Address of the remote client.
205
+ def remote_address
206
+ socket_address
207
+ rescue Exception => e
208
+ log_error('Could not infer remote address', e)
209
+ nil
210
+ end
211
+
212
+ protected
213
+ # Returns IP address of peer as a string.
214
+ def socket_address
215
+ peer = get_peername
216
+ Socket.unpack_sockaddr_in(peer)[1] if peer
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,178 @@
1
+ require 'socket'
2
+
3
+ module Thin
4
+ # An exception class to handle the event that server didn't start on time
5
+ class RestartTimeout < RuntimeError; end
6
+
7
+ module Controllers
8
+ # Control a set of servers.
9
+ # * Generate start and stop commands and run them.
10
+ # * Inject the port or socket number in the pid and log filenames.
11
+ # Servers are started throught the +thin+ command-line script.
12
+ class Cluster < Controller
13
+ # Cluster only options that should not be passed in the command sent
14
+ # to the indiviual servers.
15
+ CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
16
+
17
+ # Maximum wait time for the server to be restarted
18
+ DEFAULT_WAIT_TIME = 30 # seconds
19
+
20
+ # Create a new cluster of servers launched using +options+.
21
+ def initialize(options)
22
+ super
23
+ # Cluster can only contain daemonized servers
24
+ @options.merge!(:daemonize => true)
25
+ end
26
+
27
+ def first_port; @options[:port] end
28
+ def address; @options[:address] end
29
+ def socket; @options[:socket] end
30
+ def pid_file; @options[:pid] end
31
+ def log_file; @options[:log] end
32
+ def size; @options[:servers] end
33
+ def only; @options[:only] end
34
+ def onebyone; @options[:onebyone] end
35
+ def wait; @options[:wait] end
36
+
37
+ def swiftiply?
38
+ @options.has_key?(:swiftiply)
39
+ end
40
+
41
+ # Start the servers
42
+ def start
43
+ with_each_server { |n| start_server n }
44
+ end
45
+
46
+ # Start a single server
47
+ def start_server(number)
48
+ log_info "Starting server on #{server_id(number)} ... "
49
+
50
+ run :start, number
51
+ end
52
+
53
+ # Stop the servers
54
+ def stop
55
+ with_each_server { |n| stop_server n }
56
+ end
57
+
58
+ # Stop a single server
59
+ def stop_server(number)
60
+ log_info "Stopping server on #{server_id(number)} ... "
61
+
62
+ run :stop, number
63
+ end
64
+
65
+ # Stop and start the servers.
66
+ def restart
67
+ unless onebyone
68
+ # Let's do a normal restart by defaults
69
+ stop
70
+ sleep 0.1 # Let's breath a bit shall we ?
71
+ start
72
+ else
73
+ with_each_server do |n|
74
+ stop_server(n)
75
+ sleep 0.1 # Let's breath a bit shall we ?
76
+ start_server(n)
77
+ wait_until_server_started(n)
78
+ end
79
+ end
80
+ end
81
+
82
+ def test_socket(number)
83
+ if socket
84
+ UNIXSocket.new(socket_for(number))
85
+ else
86
+ TCPSocket.new(address, number)
87
+ end
88
+ rescue
89
+ nil
90
+ end
91
+
92
+ # Make sure the server is running before moving on to the next one.
93
+ def wait_until_server_started(number)
94
+ log_info "Waiting for server to start ..."
95
+ STDOUT.flush # Need this to make sure user got the message
96
+
97
+ tries = 0
98
+ loop do
99
+ if test_socket = test_socket(number)
100
+ test_socket.close
101
+ break
102
+ elsif tries < wait
103
+ sleep 1
104
+ tries += 1
105
+ else
106
+ raise RestartTimeout, "The server didn't start in time. Please look at server's log file " +
107
+ "for more information, or set the value of 'wait' in your config " +
108
+ "file to be higher (defaults: 30)."
109
+ end
110
+ end
111
+ end
112
+
113
+ def server_id(number)
114
+ if socket
115
+ socket_for(number)
116
+ elsif swiftiply?
117
+ [address, first_port, number].join(':')
118
+ else
119
+ [address, number].join(':')
120
+ end
121
+ end
122
+
123
+ def log_file_for(number)
124
+ include_server_number log_file, number
125
+ end
126
+
127
+ def pid_file_for(number)
128
+ include_server_number pid_file, number
129
+ end
130
+
131
+ def socket_for(number)
132
+ include_server_number socket, number
133
+ end
134
+
135
+ def pid_for(number)
136
+ File.read(pid_file_for(number)).chomp.to_i
137
+ end
138
+
139
+ private
140
+ # Send the command to the +thin+ script
141
+ def run(cmd, number)
142
+ cmd_options = @options.reject { |option, value| CLUSTER_OPTIONS.include?(option) }
143
+ cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
144
+ if socket
145
+ cmd_options.merge!(:socket => socket_for(number))
146
+ elsif swiftiply?
147
+ cmd_options.merge!(:port => first_port)
148
+ else
149
+ cmd_options.merge!(:port => number)
150
+ end
151
+ Command.run(cmd, cmd_options)
152
+ end
153
+
154
+ def with_each_server
155
+ if only
156
+ if first_port && only < 80
157
+ # interpret +only+ as a sequence number
158
+ yield first_port + only
159
+ else
160
+ # interpret +only+ as an absolute port number
161
+ yield only
162
+ end
163
+ elsif socket || swiftiply?
164
+ size.times { |n| yield n }
165
+ else
166
+ size.times { |n| yield first_port + n }
167
+ end
168
+ end
169
+
170
+ # Add the server port or number in the filename
171
+ # so each instance get its own file
172
+ def include_server_number(path, number)
173
+ ext = File.extname(path)
174
+ path.gsub(/#{ext}$/, ".#{number}#{ext}")
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,189 @@
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
+ server.threadpool_size = @options[:threadpool_size] if server.threaded?
54
+
55
+ # ssl support
56
+ if @options[:ssl]
57
+ server.ssl = true
58
+ server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => !@options[:ssl_disable_verify], :ssl_version => @options[:ssl_version], :cipher_list => @options[:ssl_cipher_list]}
59
+ end
60
+
61
+ # Detach the process, after this line the current process returns
62
+ server.daemonize if @options[:daemonize]
63
+
64
+ # +config+ must be called before changing privileges since it might require superuser power.
65
+ server.config
66
+
67
+ server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]
68
+
69
+ # If a Rack config file is specified we eval it inside a Rack::Builder block to create
70
+ # a Rack adapter from it. Or else we guess which adapter to use and load it.
71
+ if @options[:rackup]
72
+ server.app = load_rackup_config
73
+ else
74
+ server.app = load_adapter
75
+ end
76
+
77
+ # If a prefix is required, wrap in Rack URL mapper
78
+ server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix]
79
+
80
+ # If a stats URL is specified, wrap in Stats adapter
81
+ server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats]
82
+
83
+ # Register restart procedure which just start another process with same options,
84
+ # so that's why this is done here.
85
+ server.on_restart { Command.run(:start, @options) }
86
+
87
+ server.start
88
+ end
89
+
90
+ def stop
91
+ raise OptionRequired, :pid unless @options[:pid]
92
+
93
+ tail_log(@options[:log]) do
94
+ if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60))
95
+ wait_for_file :deletion, @options[:pid]
96
+ end
97
+ end
98
+ end
99
+
100
+ def restart
101
+ raise OptionRequired, :pid unless @options[:pid]
102
+
103
+ tail_log(@options[:log]) do
104
+ if Server.restart(@options[:pid])
105
+ wait_for_file :creation, @options[:pid]
106
+ end
107
+ end
108
+ end
109
+
110
+ def config
111
+ config_file = @options.delete(:config) || raise(OptionRequired, :config)
112
+
113
+ # Stringify keys
114
+ @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }
115
+
116
+ File.open(config_file, 'w') { |f| f << @options.to_yaml }
117
+ log_info "Wrote configuration to #{config_file}"
118
+ end
119
+
120
+ protected
121
+ # Wait for a pid file to either be created or deleted.
122
+ def wait_for_file(state, file)
123
+ Timeout.timeout(@options[:timeout] || 30) do
124
+ case state
125
+ when :creation then sleep 0.1 until File.exist?(file)
126
+ when :deletion then sleep 0.1 while File.exist?(file)
127
+ end
128
+ end
129
+ end
130
+
131
+ # Tail the log file of server +number+ during the execution of the block.
132
+ def tail_log(log_file)
133
+ if log_file
134
+ tail_thread = tail(log_file)
135
+ yield
136
+ tail_thread.kill
137
+ else
138
+ yield
139
+ end
140
+ end
141
+
142
+ # Acts like GNU tail command. Taken from Rails.
143
+ def tail(file)
144
+ cursor = File.exist?(file) ? File.size(file) : 0
145
+ last_checked = Time.now
146
+ tail_thread = Thread.new do
147
+ Thread.pass until File.exist?(file)
148
+ File.open(file, 'r') do |f|
149
+ loop do
150
+ f.seek cursor
151
+ if f.mtime > last_checked
152
+ last_checked = f.mtime
153
+ contents = f.read
154
+ cursor += contents.length
155
+ print contents
156
+ STDOUT.flush
157
+ end
158
+ sleep 0.1
159
+ end
160
+ end
161
+ end
162
+ sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file
163
+ tail_thread
164
+ end
165
+
166
+ private
167
+ def load_adapter
168
+ adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir])
169
+ log_info "Using #{adapter} adapter"
170
+ Rack::Adapter.for(adapter, @options)
171
+ rescue Rack::AdapterNotFound => e
172
+ raise InvalidOption, e.message
173
+ end
174
+
175
+ def load_rackup_config
176
+ ENV['RACK_ENV'] = @options[:environment]
177
+ case @options[:rackup]
178
+ when /\.rb$/
179
+ Kernel.load(@options[:rackup])
180
+ Object.const_get(File.basename(@options[:rackup], '.rb').capitalize.to_sym)
181
+ when /\.ru$/
182
+ Rack::Adapter.load(@options[:rackup])
183
+ else
184
+ raise "Invalid rackup file. please specify either a .ru or .rb file"
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,76 @@
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 = File.directory?('/etc/rc.d') ? '/etc/rc.d/thin' : '/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_info "Thin service already installed at #{INITD_PATH}"
37
+ else
38
+ log_info "Installing thin service at #{INITD_PATH} ..."
39
+ sh "mkdir -p #{File.dirname(INITD_PATH)}"
40
+ log_info "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_info ''
50
+ log_info "To configure thin to start at system boot:"
51
+ log_info "on RedHat like systems:"
52
+ log_info " sudo /sbin/chkconfig --level 345 #{NAME} on"
53
+ log_info "on Debian-like systems (Ubuntu):"
54
+ log_info " sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
55
+ log_info "on Gentoo:"
56
+ log_info " sudo rc-update add #{NAME} default"
57
+ log_info ''
58
+ log_info "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
+ next if config.end_with?("~")
65
+ log_info "[#{command}] #{config} ..."
66
+ Command.run(command, :config => config, :daemonize => true)
67
+ end
68
+ end
69
+
70
+ def sh(cmd)
71
+ log_info cmd
72
+ system(cmd)
73
+ end
74
+ end
75
+ end
76
+ 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
+ :