1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

First attempt of service:: commands as gem_plugin. It will log inot log/service.log for each service instead of one place globally. Currently stopping the service is broken.

git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@176 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
luislavena 2006-05-13 23:51:55 +00:00
parent 410ffaf77b
commit 2c2ba80b55
8 changed files with 639 additions and 0 deletions

View file

@ -0,0 +1,20 @@
Copyright (c) 2006 Luis Lavena, luislavena@gmail.com
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,20 @@
Copyright (c) 2006 Luis Lavena, luislavena@gmail.com
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,13 @@
== Mongrel Native Win32 Service Plugin
This plugin offer native win32 services for rails. This replace mongrel_rails_service.
It will work like before, with this this syntax when calling mongrel_rails:
service::install
service::remove
service::start
service::stop
service::status
= Author:
Luis Lavena

View file

@ -0,0 +1,42 @@
require 'rake'
require 'rake/testtask'
require 'rake/clean'
require 'rake/gempackagetask'
require 'rake/rdoctask'
require 'tools/rakehelp'
require 'fileutils'
include FileUtils
setup_tests
setup_clean ["pkg", "lib/*.bundle", "*.gem", ".config"]
setup_rdoc ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc']
desc "Does a full compile, test run"
task :default => [:test, :package]
version="0.1"
name="mongrel_service"
setup_gem(name, version) do |spec|
spec.summary = "Mongrel Native Win32 Service Plugin"
spec.description = "This plugin offer native win32 services for rails."
spec.author="Luis Lavena"
# added mongrel_service executable
spec.executables = ["mongrel_service"]
spec.add_dependency('gem_plugin', '>= 0.2.1')
spec.add_dependency('mongrel', '>= 0.3.12.4')
spec.files += Dir.glob("resources/**/*")
end
task :install => [:test, :package] do
sh %{sudo gem install pkg/#{name}-#{version}.gem}
end
task :uninstall => [:clean] do
sh %{sudo gem uninstall #{name}}
end

View file

@ -0,0 +1,206 @@
require 'win32/service'
require 'mongrel'
require 'mongrel/rails'
require 'optparse'
require 'yaml'
# Avoid curious users from running this script from command line
if ENV["HOMEDRIVE"]!=nil
puts "mongrel_service is not designed to run form commandline,"
puts "please use mongrel_rails service:: commands to create a win32 service."
exit
end
# We need to use OpenProcess and SetProcessAffinityMask on WinNT/2K/XP for
# binding the process to each cpu.
# Kernel32 Module Just for Win32 :D
require 'dl/win32'
module Kernel32
[
%w/OpenProcess LLL L/,
%w/SetProcessAffinityMask LL L/,
].each do |fn|
const_set fn[0].intern, Win32API.new('kernel32.dll', *fn)
end
PROCESS_ALL_ACCESS = 0x1f0fff
module_function
def set_affinity(pid, cpu)
handle = OpenProcess.call(PROCESS_ALL_ACCESS, 0, pid)
# CPU mask is a bit weird, hehehe :)
# default mask for CPU 1
mask = 1
mask = %w{1 2 4 8 16 32 64 128}[cpu.to_i - 1] if cpu.to_i.between?(1, 8)
SetProcessAffinityMask.call(handle, mask.to_i)
end
end
# End Kernel32 Module
class RailsServer
def initialize(settings)
@settings = settings
end # initialize
def configure
STDERR.puts "** Configuring Rails Server"
@config = Mongrel::Rails::RailsConfigurator.new(@settings) do
log "Starting Mongrel in #{defaults[:environment]} mode at #{defaults[:host]}:#{defaults[:port]}"
listener do
mime = {}
if defaults[:mime_map]
log "Loading additional MIME types from #{defaults[:mime_map]}"
mime = load_mime_map(defaults[:mime_map], mime)
end
if defaults[:debug]
log "Installing debugging prefixed filters. Look in log/mongrel_debug for the files."
debug "/"
end
log "Starting Rails in environment #{defaults[:environment]} ..."
uri "/", :handler => rails
log "Rails loaded."
#FIXME: We need to check why fail to load plugins.
#log "Loading any Rails specific GemPlugins"
#load_plugins
if defaults[:config_script]
log "Loading #{defaults[:config_script]} external config script"
run_config(defaults[:config_script])
end
end
end
STDERR.puts "** Done with configuration"
end
def start_serve
@runner = Thread.new do
STDERR.puts "** Configuration Thread suspended."
Thread.stop
STDERR.puts "** Configuration Thread resumed, now joining..."
@config.join
end
STDERR.puts "** Start serving, config.run"
@config.run
STDERR.puts "** Running thread"
@runner.run
end
def stop_serve
STDERR.puts "stop_serve entered"
if @runner.alive?
STDERR.puts "killing thread"
@runner.kill
end
STDERR.puts "** Stoping Server"
@config.stop
STDERR.puts "stop_serve left"
end
end
# RailsService code here
class RailsService < Win32::Daemon
def initialize(server)
@server = server
end
def service_init
STDERR.puts "** Configuring server..."
@server.configure
STDERR.puts "** Done Service_Init."
end
def service_main
STDERR.puts "** Start Serving "
@server.start_serve
STDERR.puts "Entering service_main loop"
while state == RUNNING
sleep 1
end
@server.stop_serve
STDERR.puts "leaving service_main."
end
def service_stop
STDERR.puts "Stop signal received."
end
end
# default options
OPTIONS = {
:environment => ENV['RAILS_ENV'] || "development",
:port => 3000,
:address => "0.0.0.0",
:log_file => "log/mongrel.log",
:pid_file => "log/mongrel.pid",
:num_procs => 1024,
:timeout => 0,
:mime_map => nil,
:cwd => Dir.pwd,
:docroot => "public",
:debug => false,
:config_file => nil,
:config_script => nil
}
ARGV.options do |opts|
opts.on('-e', '--environment ENV', "Rails environment to run as") { |OPTIONS[:environment]| }
opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| }
opts.on('-a', '--address ADDR', "Address to bind to") { |OPTIONS[:address]| }
opts.on('-l', '--log FILE', "Where to write log messages") { |OPTIONS[:log_file]| }
opts.on('-P', '--pid FILE', "Where to write the PID") { |OPTIONS[:pid_file]| }
opts.on('-n', '--num-procs INT', "Number of processors active before clients denied") { |OPTIONS[:num_procs]| }
opts.on('-t', '--timeout TIME', "Timeout all requests after 100th seconds time") { |OPTIONS[:timeout]| }
opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| }
opts.on('-c', '--chdir PATH', "Change to dir before starting (will be expanded)") { |OPTIONS[:cwd]| }
opts.on('-r', '--root PATH', "Set the document root (default 'public')") { |OPTIONS[:docroot]| }
opts.on('-B', '--debug', "Enable debugging mode") { |OPTIONS[:debug]| }
opts.on('-C', '--config FILE', "Use a config file") { |OPTIONS[:config_file]| }
opts.on('-S', '--script FILE', "Load the given file as an extra config script.") { |OPTIONS[:config_script]| }
opts.parse!
end
# We must bind to a specific cpu?
if OPTIONS[:cpu]
Kernel32.set_affinity(Process.pid, OPTIONS[:cpu])
end
# expand path
OPTIONS[:cwd] = File.expand_path(OPTIONS[:cwd])
# chdir to that path
Dir.chdir(OPTIONS[:cwd])
# redirecting STDERR to a file so we could trace it.
STDERR.reopen("log/service.log", "w")
STDERR.sync = true
#FIXME: I commented this because fails during load_plugins from configurator, we need to fix
#GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
# initialize the daemon and pass control to win32/service
STDERR.puts "Initializing MongrelDaemon with OPTIONS"
server = RailsServer.new(OPTIONS)
svc = RailsService.new(server)
svc.mainloop
#svc.service_init
#svc.service_main

View file

@ -0,0 +1,231 @@
require 'gem_plugin'
require 'mongrel'
require 'mongrel/rails'
require 'rbconfig'
require 'win32/service'
DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log')
DEBUG_THREAD_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug_thread.log')
def dbg(msg)
File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
end
def dbg_th(msg)
File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
end
module Service
class Install < GemPlugin::Plugin "/commands"
include Mongrel::Command::Base
def configure
options [
['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],
['-D', '--display SVC_DISPLAY', "Adjust the display name of the service.", :@svc_display, nil],
["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
['-p', '--port PORT', "Which port to bind to", :@port, 3000],
['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024],
['-t', '--timeout TIME', "Timeout all requests after 100th seconds time", :@timeout, 0],
['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
['-B', '--debug', "Enable debugging mode", :@debug, false],
['-C', '--config PATH', "Use a config file", :@config_file, nil],
['-S', '--script PATH', "Load the given file as an extra config script.", :@config_script, nil],
['-u', '--cpu CPU', "Bind the process to specific cpu, starting from 1.", :@cpu, nil]
]
end
# When we validate the options, we need to make sure the --root is actually RAILS_ROOT
# of the rails application we wanted to serve, because later "as service" no error
# show to trace this.
def validate
@cwd = File.expand_path(@cwd)
valid_dir? @cwd, "Invalid path to change to: #@cwd"
# change there to start, then we'll have to come back after daemonize
Dir.chdir(@cwd)
# start with the premise of app really exist.
app_exist = true
%w{app config db log public}.each do |path|
if !File.directory?(File.join(@cwd, path))
app_exist = false
break
end
end
valid? app_exist == true, "The path you specified isn't a valid Rails application."
valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
valid_dir? @docroot, "Path to docroot not valid: #@docroot"
valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
valid_exists? @config_file, "Config file not there: #@config_file" if @config_file
# Validate the number of cpu to bind to.
valid? @cpu.to_i > 0, "You must specify a numeric value for cpu. (1..8)" if @cpu
# We should validate service existance here, right Zed?
begin
valid? !Win32::Service.exists?(@svc_name), "The service already exist, please remove it first."
rescue
end
valid? @svc_name != nil, "A service name is mandatory."
# default service display to service name
@svc_display = @svc_name if !@svc_display
return @valid
end
def run
# command line setting override config file settings
@options = { :host => @address, :port => @port, :cwd => @cwd,
:log_file => @log_file, :pid_file => @pid_file, :environment => @environment,
:docroot => @docroot, :mime_map => @mime_map,
:debug => @debug, :includes => ["mongrel"], :config_script => @config_script,
:num_procs => @num_procs, :timeout => @timeout, :cpu => @cpu
}
if @config_file
STDERR.puts "** Loading settings from #{@config_file} (command line options override)."
conf = YAML.load_file(@config_file)
@options = conf.merge(@options)
end
argv = []
# ruby.exe instead of rubyw.exe due a exception raised when stoping the service!
argv << '"' + Config::CONFIG['bindir'] + '/ruby.exe' + '" '
# add service_script, we now use the rubygem powered one
argv << '"' + Config::CONFIG['bindir'] + '/mongrel_service' + '" '
# now the options
argv << "-e #{@options[:environment]}" if @options[:environment]
argv << "-p #{@options[:port]}"
argv << "-a #{@options[:address]}" if @options[:address]
argv << "-l \"#{@options[:log_file]}\"" if @options[:log_file]
argv << "-P \"#{@options[:pid_file]}\""
argv << "-c \"#{@options[:cwd]}\"" if @options[:cwd]
argv << "-t #{@options[:timeout]}" if @options[:timeout]
argv << "-m \"#{@options[:mime_map]}\"" if @options[:mime_map]
argv << "-r \"#{@options[:docroot]}\"" if @options[:docroot]
argv << "-n #{@options[:num_procs]}" if @options[:num_procs]
argv << "-B" if @options[:debug]
argv << "-S \"#{@options[:config_script]}\"" if @options[:config_script]
argv << "-u #{@options[:cpu.to_i]}" if @options[:cpu]
svc = Win32::Service.new
begin
svc.create_service{ |s|
s.service_name = @svc_name
s.display_name = @svc_display
s.binary_path_name = argv.join ' '
s.dependencies = []
}
puts "Mongrel service '#{@svc_display}' installed as '#{@svc_name}'."
rescue Win32::ServiceError => err
puts "There was a problem installing the service:"
puts err
end
svc.close
end
end
module ServiceValidation
def configure
options [
['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],
]
end
def validate
valid? @svc_name != nil, "A service name is mandatory."
# Validate that the service exists
begin
valid? Win32::Service.exists?(@svc_name), "There is no service with that name, cannot proceed."
rescue
end
return @valid
end
end
class Remove < GemPlugin::Plugin "/commands"
include Mongrel::Command::Base
include ServiceValidation
def run
display_name = Win32::Service.getdisplayname(@svc_name)
begin
Win32::Service.stop(@svc_name)
rescue
end
begin
Win32::Service.delete(@svc_name)
rescue
end
puts "#{display_name} service removed."
end
end
class Start < GemPlugin::Plugin "/commands"
include Mongrel::Command::Base
include ServiceValidation
def run
display_name = Win32::Service.getdisplayname(@svc_name)
begin
Win32::Service.start(@svc_name)
started = false
while started == false
s = Win32::Service.status(@svc_name)
started = true if s.current_state == "running"
break if started == true
puts "One moment, " + s.current_state
sleep 1
end
puts "#{display_name} service started"
rescue Win32::ServiceError => err
puts "There was a problem starting the service:"
puts err
end
end
end
class Stop < GemPlugin::Plugin "/commands"
include Mongrel::Command::Base
include ServiceValidation
def run
display_name = Win32::Service.getdisplayname(@svc_name)
begin
Win32::Service.stop(@svc_name)
stopped = false
while stopped == false
s = Win32::Service.status(@svc_name)
stopped = true if s.current_state == "stopped"
break if stopped == true
puts "One moment, " + s.current_state
sleep 1
end
puts "#{display_name} service stopped"
rescue Win32::ServiceError => err
puts "There was a problem stopping the service:"
puts err
end
end
end
end

View file

@ -0,0 +1,2 @@
---
:debug: false

View file

@ -0,0 +1,105 @@
def make(makedir)
Dir.chdir(makedir) do
sh(PLATFORM =~ /win32/ ? 'nmake' : 'make')
end
end
def extconf(dir)
Dir.chdir(dir) do ruby "extconf.rb" end
end
def setup_tests
Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/test*.rb']
t.verbose = true
end
end
def setup_clean otherfiles
files = ['build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log'] + otherfiles
CLEAN.include(files)
end
def setup_rdoc files
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'doc/rdoc'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.add(files)
end
end
def setup_extension(dir, extension)
ext = "ext/#{dir}"
ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
ext_files = FileList[
"#{ext}/*.c",
"#{ext}/*.h",
"#{ext}/extconf.rb",
"#{ext}/Makefile",
"lib"
]
task "lib" do
directory "lib"
end
desc "Builds just the #{extension} extension"
task extension.to_sym => ["#{ext}/Makefile", ext_so ]
file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
extconf "#{ext}"
end
file ext_so => ext_files do
make "#{ext}"
cp ext_so, "lib"
end
end
def base_gem_spec(pkg_name, pkg_version)
pkg_version = pkg_version
pkg_name = pkg_name
pkg_file_name = "#{pkg_name}-#{pkg_version}"
Gem::Specification.new do |s|
s.name = pkg_name
s.version = pkg_version
s.platform = Gem::Platform::RUBY
s.has_rdoc = true
s.extra_rdoc_files = [ "README" ]
s.files = %w(COPYING LICENSE README Rakefile) +
Dir.glob("{bin,doc/rdoc,test,lib}/**/*") +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob("examples/**/*.rb") +
Dir.glob("tools/*.rb")
s.require_path = "lib"
s.extensions = FileList["ext/**/extconf.rb"].to_a
s.bindir = "bin"
end
end
def setup_gem(pkg_name, pkg_version)
spec = base_gem_spec(pkg_name, pkg_version)
yield spec if block_given?
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
p.need_tar = true
end
end
def setup_win32_gem(pkg_name, pkg_version)
spec = base_gem_spec(pkg_name, pkg_version)
yield spec if block_given?
Gem::Builder.new(spec).build
end