diff --git a/.gitignore b/.gitignore index 9da7d41cb3..2072ee0b08 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ *.slo *.dSYM /config.rb -agents +/agents build/cache ext/common/libboost_oxt ext/common/libpassenger_common @@ -44,6 +44,5 @@ test/stub/apache2/*.pid test/stub/apache2/httpd.conf test/stub/zsfa/mycook test/stub/zsfa/foo -test/stub/rails_apps/3.0/*/help test/support/allocate_memory pkg diff --git a/NEWS b/NEWS index 8f9fd218be..9a873e9c0a 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ Release 3.0.1 This is the command that people can run after donating. It allows people to slightly modify Phusion Passenger's display name as a joke. In 3.0.0 it was broken because of a typo. This has been fixed. + * Removed passenger-stress-test + This tool was used during the early life of Phusion Passenger for stress + testing websites. Its performance has never been very good and there are + much better tools for stress testing, so this tool has now been removed. * [Apache] RailsEnv and RackEnv configuration options are now equivalent In previous versions, RailsEnv only had effect on Rails 1 and Rails 2 apps while RackEnv only had effect on Rack apps. Because Rails 3 apps are @@ -33,10 +37,15 @@ Release 3.0.1 Because this is confusing to users, we've now made RailsEnv and RackEnv equivalent. Issue #579. * [Nginx] Fixed compilation problems on systems with unpowerful shells - Most notable Solaris. Its default shell does not support some basic + Most notably Solaris. Its default shell does not support some basic constructs that we used in the Nginx configure script. * [Nginx] Upgraded default Nginx version to to 0.8.53 The previous default was 0.8.52. + * [Nginx] passenger_enabled now only accepts 'on' or 'off' values + Previously it would recognize any value not equal to 'on' as meaning + 'off'. This caused confusion among users who thought they could also + specify 'true', so we now throw a proper error if the value is + unrecognized. Fixes issue #583. Release 3.0.0 diff --git a/Rakefile b/Rakefile index 71fa77e6a5..d7a4f40216 100644 --- a/Rakefile +++ b/Rakefile @@ -36,6 +36,7 @@ require 'build/cxx_tests' require 'build/ruby_tests' require 'build/integration_tests' require 'build/misc' +require 'build/rpm' #### Default tasks diff --git a/bin/passenger-stress-test b/bin/passenger-stress-test deleted file mode 100755 index 334eb156a0..0000000000 --- a/bin/passenger-stress-test +++ /dev/null @@ -1,345 +0,0 @@ -#!/usr/bin/env ruby -# Phusion Passenger - http://www.modrails.com/ -# Copyright (c) 2010 Phusion -# -# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. -# -# 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. - -source_root = File.expand_path(File.dirname(__FILE__) + "/..") -$LOAD_PATH.unshift("#{source_root}/lib") -require 'phusion_passenger' -require 'rubygems' -require 'optparse' -require 'socket' -require 'thread' -require 'phusion_passenger/platform_info' -require 'phusion_passenger/message_channel' -require 'phusion_passenger/utils' - -include PhusionPassenger -include PhusionPassenger::Utils -include PhusionPassenger::PlatformInfo - -# A thread or a process, depending on the Ruby VM implementation. -class Subprocess - attr_accessor :channel - - def initialize(name, &block) - if RUBY_PLATFORM == "java" - a, b = UNIXSocket.pair - @thread = Thread.new do - block.call(true, MessageChannel.new(b)) - end - @channel = MessageChannel.new(a) - @thread_channel = b - else - a, b = UNIXSocket.pair - @pid = safe_fork(name) do - a.close - $0 = name - Process.setsid - block.call(false, MessageChannel.new(b)) - end - b.close - @channel = MessageChannel.new(a) - end - end - - def stop - if RUBY_PLATFORM == "java" - @thread.terminate - @channel.close - @thread_channel.close - else - Process.kill('SIGKILL', @pid) rescue nil - Process.waitpid(@pid) rescue nil - @channel.close - end - end -end - -class StressTester - def start - @options = parse_options - load_hawler - - Thread.abort_on_exception = true - if GC.respond_to?(:copy_on_write_friendly=) - GC.copy_on_write_friendly = true - end - @terminal_height = ENV['LINES'] ? ENV['LINES'].to_i : 24 - @terminal_width = ENV['COLUMNS'] ? ENV['COLUMNS'].to_i : 80 - - if Process.euid != 0 - puts "*** WARNING: This program might not be able to restart " << - "Apache because it's not running as root. Please run " << - "this tool as root." - puts - puts "Press Enter to continue..." - begin - STDIN.readline - rescue Interrupt - exit 1 - end - end - - run_crawlers - end - - def parse_options - options = { - :concurrency => 20, - :depth => 20, - :nice => true, - :apache_restart_interval => 24 * 60, - :app_restart_interval => 55 - } - parser = OptionParser.new do |opts| - opts.banner = "Usage: passenger-stress-test [options]\n\n" << - "Stress test the given (Passenger-powered) website by:\n" << - " * crawling it with multiple concurrently running crawlers.\n" << - " * gracefully restarting Apache at random times (please point the 'APXS2'\n" << - " variable to your Apache's 'apxs' binary).\n" << - " * restarting the target (Passenger-powered) application at random time.\n" << - "\n" << - "Example:\n" << - " passenger-stress-test mywebsite.com /webapps/mywebsite\n" << - "\n" - - opts.separator "Options:" - opts.on("-c", "--concurrency N", Integer, - "Number of crawlers to start (default = #{options[:concurrency]})") do |v| - options[:concurrency] = v - end - opts.on("-p", "--apache-restart-interval N", Integer, - "Gracefully restart Apache after N minutes\n" << - (" " * 37) << "(default = #{options[:apache_restart_interval]})") do |v| - options[:apache_restart_interval] = v - end - opts.on("-a", "--app-restart-interval N", Integer, - "Restart the application after N minutes\n" << - (" " * 37) << "(default = #{options[:app_restart_interval]})") do |v| - options[:app_restart_interval] = v - end - opts.on("-h", "--help", "Show this message") do - puts opts - exit - end - end - parser.parse! - - options[:host] = ARGV[0] - options[:app_root] = ARGV[1] - if !options[:host] || !options[:app_root] - puts parser - exit 1 - end - return options - end - - def load_hawler - begin - require 'hawler' - rescue LoadError - STDERR.puts "This tool requires Hawler (http://tinyurl.com/ywgk6x). Please install it with:" - STDERR.puts - STDERR.puts " gem install --source http://spoofed.org/files/hawler/ hawler" - exit 1 - end - end - - def run_crawlers - @started = false - @crawlers = [] - - # Start crawler processes. - GC.start if GC.copy_on_write_friendly? - @options[:concurrency].times do |i| - STDOUT.write("Starting crawler #{i + 1} of #{@options[:concurrency]}...\n") - STDOUT.flush - process = Subprocess.new("crawler #{i + 1}") do |is_thread, channel| - if !is_thread && @options[:nice] - system("renice 1 #{Process.pid} >/dev/null 2>/dev/null") - end - while true - crawl!(i + 1, channel) - end - end - @crawlers << { - :id => i + 1, - :process => process, - :channel => process.channel, - :mutex => Mutex.new, - :current_uri => nil, - :crawled => 0 - } - end - - puts - if RUBY_PLATFORM != "java" - # 'sleep' b0rks when running in JRuby? - sleep 1 - end - begin - $0 = "Passenger Crawler: control process" - io_to_crawler = {} - ios = [] - @crawlers.each do |crawler| - io_to_crawler[crawler[:channel].io] = crawler - ios << crawler[:channel].io - end - - # Tell each crawler to start crawling. - @crawlers.each do |crawler| - crawler[:channel].write("start") - end - - # Show progress periodically. - @start_time = Time.now - progress_reporter = Thread.new(&method(:report_progress)) - @next_apache_restart = Time.now + @options[:apache_restart_interval] * 60 - apache_restarter = Thread.new(&method(:restart_apache)) - @next_app_restart = Time.now + @options[:app_restart_interval] * 60 - app_restarter = Thread.new(&method(:restart_app)) - - while true - note_progress(ios, io_to_crawler) - end - rescue Interrupt - trap('SIGINT') {} - puts "Shutting down..." - @done = true - @crawlers.each do |crawler| - STDOUT.write("Stopping crawler #{crawler[:id]} of #{@options[:concurrency]}...\r") - STDOUT.flush - crawler[:process].stop - end - progress_reporter.join if progress_reporter - apache_restarter.join if apache_restarter - app_restarter.join if app_restarter - puts - end - end - - def note_progress(ios, io_to_crawler) - select(ios)[0].each do |io| - crawler = io_to_crawler[io] - uri = crawler[:channel].read[0] - crawler[:mutex].synchronize do - crawler[:current_uri] = uri - crawler[:crawled] += 1 - end - end - end - - def report_progress - while !@done - output = "\n" * @terminal_height - output << "### Running for #{duration(Time.now.to_i - @start_time.to_i)}\n" - @crawlers.each do |crawler| - crawler[:mutex].synchronize do - line = sprintf("Crawler %-2d: %-3d -> %s", - crawler[:id], - crawler[:crawled], - crawler[:current_uri]) - output << sprintf("%-#{@terminal_width}s\n", line) - end - end - output << "Next Apache restart: in #{duration(@next_apache_restart.to_i - Time.now.to_i)}\n" - output << "Next app restart : in #{duration(@next_app_restart.to_i - Time.now.to_i)}\n" - STDOUT.write(output) - sleep 0.5 - end - end - - def restart_apache - while !@done - if Time.now > @next_apache_restart - @next_apache_restart = Time.now + @options[:apache_restart_interval] * 60 - system("#{HTTPD} -k graceful") - end - end - end - - def restart_app - while !@done - if Time.now > @next_app_restart - @next_app_restart = Time.now + @options[:app_restart_interval] * 60 - system("touch #{@options[:app_root]}/tmp/restart.txt") - end - end - end - - def duration(seconds) - result = "" - if seconds >= 60 - minutes = (seconds / 60) - if minutes >= 60 - hours = minutes / 60 - minutes = minutes % 60 - if hours == 1 - result << "#{hours} hour " - else - result << "#{hours} hours " - end - end - - seconds = seconds % 60 - if minutes == 1 - result << "#{minutes} minute " - else - result << "#{minutes} minutes " - end - end - result << "#{seconds} seconds" - return result - end - - def crawl!(id, channel) - progress_reporter = lambda do |uri, referer, response| - begin - if !@started - # At the beginning, wait until the control process - # tells us to start. - @started = true - channel.read - end - channel.write(uri, referer, response) - rescue - if RUBY_PLATFORM == "java" - Thread.current.terminate - else - Process.kill('SIGKILL', Process.pid) - end - end - end - crawler = Hawler.new(@options[:host], progress_reporter) - if RUBY_PLATFORM == "java" - trap('SIGINT') do - raise Interrupt, "Interrupted" - end - end - crawler.recurse = true - crawler.depth = @options[:depth] - crawler.start - end -end - -StressTester.new.start diff --git a/build/agents.rb b/build/agents.rb index abe52741a1..dceb24f388 100644 --- a/build/agents.rb +++ b/build/agents.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. dependencies = [ 'ext/common/Watchdog.cpp', @@ -68,6 +75,6 @@ "#{EXTRA_LDFLAGS}") end -task :clean => 'common:clean' do +task 'common:clean' do sh "rm -f #{AGENT_OUTPUT_DIR}PassengerWatchdog #{AGENT_OUTPUT_DIR}PassengerLoggingAgent" end \ No newline at end of file diff --git a/build/apache2.rb b/build/apache2.rb index 383e92307a..a2821e934a 100644 --- a/build/apache2.rb +++ b/build/apache2.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. # apxs totally sucks. We couldn't get it working correctly @@ -152,7 +159,7 @@ task :clean => 'apache2:clean' desc "Clean all compiled Apache 2 files" -task 'apache2:clean' do +task 'apache2:clean' => 'common:clean' do files = APACHE2_MODULE_OBJECTS.dup files << APACHE2_MOD_PASSENGER_O files << APACHE2_MODULE diff --git a/build/basics.rb b/build/basics.rb index dbebf65b47..2e4fc40907 100644 --- a/build/basics.rb +++ b/build/basics.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. require 'rubygems' require 'pathname' diff --git a/build/common_library.rb b/build/common_library.rb index e60f1547b6..aab98b55e7 100644 --- a/build/common_library.rb +++ b/build/common_library.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. # Defines tasks for compiling a static library containing code shared between diff --git a/build/config.rb b/build/config.rb index 2ec27e16d5..8905bd8666 100644 --- a/build/config.rb +++ b/build/config.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. OPTIMIZE = boolean_option("OPTIMIZE") CC = string_option("CC", "gcc") diff --git a/build/cxx_tests.rb b/build/cxx_tests.rb index 7182f9da35..86eff8a260 100644 --- a/build/cxx_tests.rb +++ b/build/cxx_tests.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. ### C++ components tests ### diff --git a/build/documentation.rb b/build/documentation.rb index 616a34d845..66f6743027 100644 --- a/build/documentation.rb +++ b/build/documentation.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. ASCIIDOC_FLAGS = "-a toc -a numbered -a toclevels=3 -a icons" DOXYGEN = 'doxygen' diff --git a/build/integration_tests.rb b/build/integration_tests.rb index 52377184a7..7a51681df0 100644 --- a/build/integration_tests.rb +++ b/build/integration_tests.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. ### Integration tests ### diff --git a/build/misc.rb b/build/misc.rb index 275add314e..bc802fbb42 100644 --- a/build/misc.rb +++ b/build/misc.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. desc "Run 'sloccount' to see how much code Passenger has" task :sloccount do diff --git a/build/nginx.rb b/build/nginx.rb index b12a054a02..82fcac28e5 100644 --- a/build/nginx.rb +++ b/build/nginx.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. desc "Build Nginx helper agent" task :nginx => [ @@ -60,6 +67,6 @@ task :clean => 'nginx:clean' desc "Clean all compiled Nginx files" -task 'nginx:clean' do +task 'nginx:clean' => 'common:clean' do sh("rm", "-rf", AGENT_OUTPUT_DIR + "nginx/PassengerHelperAgent") end diff --git a/build/oxt_tests.rb b/build/oxt_tests.rb index 6ce5fa1f7c..2d23c8e97a 100644 --- a/build/oxt_tests.rb +++ b/build/oxt_tests.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. ### OXT library tests ### diff --git a/build/packaging.rb b/build/packaging.rb index f852f39998..59e979220b 100644 --- a/build/packaging.rb +++ b/build/packaging.rb @@ -1,18 +1,34 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. + +task 'package:check' do + require 'phusion_passenger' + + File.read("ext/common/Constants.h") =~ /PASSENGER_VERSION \"(.+)\"/ + if $1 != PhusionPassenger::VERSION_STRING + abort "Version number in ext/common/Constants.h doesn't match." + end +end spec = Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -78,7 +94,9 @@ end Rake::Task['package'].prerequisites.unshift(:doc) +Rake::Task['package'].prerequisites.unshift('package:check') Rake::Task['package:gem'].prerequisites.unshift(:doc) +Rake::Task['package:gem'].prerequisites.unshift('package:check') Rake::Task['package:force'].prerequisites.unshift(:doc) task :clobber => :'package:clean' @@ -96,13 +114,13 @@ fake_native_support_dir = "#{fakeroot}/usr/lib/ruby/#{CONFIG['ruby_version']}/#{CONFIG['arch']}" fake_agents_dir = "#{fakeroot}#{NATIVELY_PACKAGED_AGENTS_DIR}" fake_helper_scripts_dir = "#{fakeroot}#{NATIVELY_PACKAGED_HELPER_SCRIPTS_DIR}" + fake_resources_dir = "#{fakeroot}/usr/share/phusion-passenger" fake_docdir = "#{fakeroot}#{NATIVELY_PACKAGED_DOCDIR}" fake_bindir = "#{fakeroot}/usr/bin" fake_sbindir = "#{fakeroot}/usr/sbin" fake_source_root = "#{fakeroot}#{NATIVELY_PACKAGED_SOURCE_ROOT}" fake_apache2_module = "#{fakeroot}#{NATIVELY_PACKAGED_APACHE2_MODULE}" fake_apache2_module_dir = File.dirname(fake_apache2_module) - fake_resources_dir = "#{fakeroot}/usr/share/phusion-passenger" sh "rm -rf #{fakeroot}" sh "mkdir -p #{fakeroot}" @@ -124,6 +142,9 @@ sh "mkdir -p #{fake_helper_scripts_dir}" sh "cp -R #{HELPER_SCRIPTS_DIR}/* #{fake_helper_scripts_dir}/" + sh "mkdir -p #{fake_resources_dir}" + sh "cp resources/* #{fake_resources_dir}/" + sh "mkdir -p #{fake_docdir}" Packaging::ASCII_DOCS.each do |docfile| sh "cp", docfile, "#{fake_docdir}/" @@ -143,9 +164,6 @@ sh "mkdir -p #{fake_apache2_module_dir}" sh "cp #{APACHE2_MODULE} #{fake_apache2_module_dir}/" - sh "mkdir -p #{fake_resources_dir}" - sh "cp resources/* #{fake_resources_dir}/" - sh "mkdir -p #{fake_source_root}" spec.files.each do |filename| next if File.directory?(filename) @@ -159,7 +177,7 @@ end desc "Create a Debian package" -task 'package:debian' do +task 'package:debian' => 'package:check' do checkbuilddeps = PlatformInfo.find_command("dpkg-checkbuilddeps") debuild = PlatformInfo.find_command("debuild") if !checkbuilddeps || !debuild diff --git a/build/rpm.rb b/build/rpm.rb new file mode 100644 index 0000000000..ce5bbdca2d --- /dev/null +++ b/build/rpm.rb @@ -0,0 +1,98 @@ +# Phusion Passenger - http://www.modrails.com/ +# Copyright (C) 2010 Phusion +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +namespace :package do + @sources_dir = nil + @verbosity = 0 + + def sources_dir + if !@sources_dir + @sources_dir = `rpm -E '%{_sourcedir}'`.strip + else + @sources_dir + end + end + + def noisy_system(*args) + puts(args.join(' ')) if @verbosity > 0 + system(*args) + end + + def create_tarball(verbosity = 0) + working_dir = "/tmp/#{`whoami`.strip}-passenger-rpm-#{Process.pid}" + sub_dir = "passenger-#{PhusionPassenger::VERSION_STRING}" + FileUtils.rm_rf(working_dir, :verbose => verbosity > 0) + begin + FileUtils.mkdir_p("#{working_dir}/#{sub_dir}", :verbose => verbosity > 0) + noisy_system(*(%w{rsync -ra --exclude=.git --exclude=rpm/pkg --exclude=rpm/yum-repo --exclude=*.o --exclude=*.so} + (@verbosity > 2 ? %w{-v} : []) + (@verbosity > 3 ? %w{--progress} : []) + ['.', "#{working_dir}/#{sub_dir}/."] )) + noisy_system('tar', "cz#{verbosity >= 2 ? 'v' : ''}", "-C", working_dir, '-f', "#{sources_dir}/#{sub_dir}.tar.gz", sub_dir) + ensure + FileUtils.rm_rf("#{working_dir}", :verbose => verbosity > 0) + end + end + + def test_setup(*args) + require 'phusion_passenger' + require 'phusion_passenger/abstract_installer' + nginx_fetch = Class.new(PhusionPassenger::AbstractInstaller) do + def fetch(dir) + tarball = "nginx-#{PREFERRED_NGINX_VERSION}.tar.gz" + return true if File.exists?("#{dir}/#{tarball}") + download("http://sysoev.ru/nginx/#{tarball}", "#{dir}/#{tarball}") + end + end + + result = noisy_system('./rpm/release/mocksetup-first.sh', *args) + if !result + # exit status 4 means that the user needs to relogin. + if $?.exitstatus == 4 + exit + else + abort "Mock setup failed, see above for details" + end + end + nginx_fetch.new.fetch(sources_dir) + end + + desc "Package the current release into a set of RPMs" + task 'rpm' => :rpm_verbosity do + test_setup + create_tarball(@verbosity) + # Add a single -v for some feedback + noisy_system(*(%w{./rpm/release/build.rb --single --stage-dir=pkg --extra-packages=release/mock-repo} + @build_verbosity)) + end + + desc "Build a Yum repository for the current release" + task 'yum' => :rpm_verbosity do + test_setup(*%w{-p createrepo -p rubygem-gem2rpm}) + create_tarball(@verbosity) + # Add a single -v for some feedback + noisy_system(*(%w{./rpm/release/build.rb --stage-dir=yum-repo --extra-packages=release/mock-repo} + @build_verbosity)) + Dir["yum-repo/{fedora,rhel}/*/{i386,x86_64}"].each do |dir| + noisy_system('createrepo', dir) + end + end + + task 'rpm_verbosity' do + if ENV['verbosity'] && ENV['verbosity'] =~ /(true|yes|on)/i + @verbosity = 1 + @build_verbosity = %w{-v} + else + @verbosity = ENV['verbosity'].to_i + @build_verbosity = %w{-v} * (@verbosity == 0 ? 1 : @verbosity) + end + end +end diff --git a/build/ruby_extension.rb b/build/ruby_extension.rb index 5f0f51af12..7ef7134b69 100644 --- a/build/ruby_extension.rb +++ b/build/ruby_extension.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2010 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. output_dir = RUBY_EXTENSION_OUTPUT_DIR output_name = "passenger_native_support.#{LIBEXT}" diff --git a/build/ruby_tests.rb b/build/ruby_tests.rb index 89a55eae2a..ffb71618a6 100644 --- a/build/ruby_tests.rb +++ b/build/ruby_tests.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. ### Ruby components tests ### diff --git a/build/test_basics.rb b/build/test_basics.rb index 1af7734e4e..ae07be2ca6 100644 --- a/build/test_basics.rb +++ b/build/test_basics.rb @@ -1,18 +1,25 @@ # Phusion Passenger - http://www.modrails.com/ -# Copyright (C) 2008, 2009 Phusion +# Copyright (c) 2010 Phusion # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# 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: # -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# 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. TEST_BOOST_OXT_LIBRARY = LIBBOOST_OXT TEST_COMMON_LIBRARY = LIBCOMMON diff --git a/ext/apache2/Hooks.cpp b/ext/apache2/Hooks.cpp index 96eb1dc322..a8a44f3e6b 100644 --- a/ext/apache2/Hooks.cpp +++ b/ext/apache2/Hooks.cpp @@ -236,7 +236,8 @@ class Hooks { */ inline RequestNote *getRequestNote(request_rec *r) { // The union is needed in order to be compliant with - // C99/C++'s strict aliasing rules. http://tinyurl.com/g5hgh + // C99/C++'s strict aliasing rules. + // http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html union { RequestNote *note; void *pointer; @@ -876,43 +877,40 @@ class Hooks { /** * Convert an HTTP header name to a CGI environment name. */ - char *http2env(apr_pool_t *p, const char *name) { - char *env_name = apr_pstrcat(p, "HTTP_", name, NULL); - char *cp; + char *httpToEnv(apr_pool_t *p, const char *headerName) { + char *result = apr_pstrcat(p, "HTTP_", headerName, NULL); + char *current = result + sizeof("HTTP_") - 1; - for (cp = env_name + 5; *cp != 0; cp++) { - if (*cp == '-') { - *cp = '_'; + while (*current != '\0') { + if (*current == '-') { + *current = '_'; } else { - *cp = apr_toupper(*cp); + *current = apr_toupper(*current); } + current++; } - return env_name; + return result; } - char *lookupName(apr_table_t *t, const char *name) { - const apr_array_header_t *hdrs_arr = apr_table_elts(t); - apr_table_entry_t *hdrs = (apr_table_entry_t *) hdrs_arr->elts; - int i; + const char *lookupInTable(apr_table_t *table, const char *name) { + const apr_array_header_t *headers = apr_table_elts(table); + apr_table_entry_t *elements = (apr_table_entry_t *) headers->elts; - for (i = 0; i < hdrs_arr->nelts; ++i) { - if (hdrs[i].key == NULL) { - continue; - } - if (strcasecmp(hdrs[i].key, name) == 0) { - return hdrs[i].val; + for (int i = 0; i < headers->nelts; i++) { + if (elements[i].key != NULL && strcasecmp(elements[i].key, name) == 0) { + return elements[i].val; } } return NULL; } - char *lookupHeader(request_rec *r, const char *name) { - return lookupName(r->headers_in, name); + const char *lookupHeader(request_rec *r, const char *name) { + return lookupInTable(r->headers_in, name); } - char *lookupEnv(request_rec *r, const char *name) { - return lookupName(r->subprocess_env, name); + const char *lookupEnv(request_rec *r, const char *name) { + return lookupInTable(r->subprocess_env, name); } void inline addHeader(apr_table_t *table, const char *name, const char *value) { @@ -985,7 +983,7 @@ class Hooks { hdrs = (apr_table_entry_t *) hdrs_arr->elts; for (i = 0; i < hdrs_arr->nelts; ++i) { if (hdrs[i].key) { - addHeader(headers, http2env(r->pool, hdrs[i].key), hdrs[i].val); + addHeader(headers, httpToEnv(r->pool, hdrs[i].key), hdrs[i].val); } } diff --git a/ext/apache2/LICENSE-CNRI.TXT b/ext/apache2/LICENSE-CNRI.TXT deleted file mode 100644 index 2778953af4..0000000000 --- a/ext/apache2/LICENSE-CNRI.TXT +++ /dev/null @@ -1,79 +0,0 @@ -A few functions in ext/apache2/Hooks.cpp are based on the source code of -mod_scgi version 1.9. Its license is included in this file. -Please note that these licensing terms *only* encompass those few -functions, and not Passenger as a whole. - -The functions which are based on mod_scgi's code are as follows: -- Hooks::prepareRequest(). Although our version looks nothing like the - original, the idea of checking for the file's existance from the - map_to_storage/fixups hook is inspired by mod_scgi's code. -- Hooks::handleRequest(). Although our version looks nothing like the original, - the idea of passing the backend process's socket file descriptor up to the - bucket brigade chain is inspired by mod_scgi's code. -- Hooks::http2env(), Hooks::lookupName(), Hooks::lookupHeader(), - Hooks::lookupEnv(), Hooks::addHeader(): Copied from mod_scgi's functions that - are named similarly. Slightly modified to make the coding style consistent - with the rest of Phusion Passenger. -- Hooks::sendHeaders(): Based for the most part on mod_scgi's send_headers() - function. - ------------------------------------------------------------------------- -CNRI OPEN SOURCE LICENSE AGREEMENT - -IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY. BY -COPYING, INSTALLING OR OTHERWISE USING SCGI-1.9 SOFTWARE, YOU ARE -DEEMED TO HAVE AGREED TO THE TERMS AND CONDITIONS OF THIS LICENSE -AGREEMENT. - -1. This LICENSE AGREEMENT is between Corporation for National - Research Initiatives, having an office at 1895 Preston White - Drive, Reston, VA 20191 ("CNRI"), and the Individual or - Organization ("Licensee") copying, installing or otherwise using - scgi-1.9 software in source or binary form and its associated - documentation ("scgi-1.9"). - -2. Subject to the terms and conditions of this License Agreement, - CNRI hereby grants Licensee a nonexclusive, royalty-free, world- - wide license to reproduce, analyze, test, perform and/or display - publicly, prepare derivative works, distribute, and otherwise use - scgi-1.9 alone or in any derivative version, provided, however, - that CNRI's License Agreement and CNRI's notice of copyright, - i.e., "Copyright (c) 2004 Corporation for National Research - Initiatives; All Rights Reserved" are retained in scgi-1.9 alone - or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on - or incorporates scgi-1.9 or any part thereof, and wants to make - the derivative work available to others as provided herein, then - Licensee hereby agrees to include in any such work a brief - summary of the changes made to scgi-1.9. - -4. CNRI is making scgi-1.9 available to Licensee on an "AS IS" - basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR - IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO - AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY - OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF SCGI-1.9 - WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF SCGI- - 1.9 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS - AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING SCGI- - 1.9, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE - POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a - material breach of its terms and conditions. - -7. This License Agreement shall be governed by and interpreted in - all respects by the law of the State of Virginia, excluding - Virginia's conflict of law provisions. Nothing in this License - Agreement shall be deemed to create any relationship of agency, - partnership, or joint venture between CNRI and Licensee. This - License Agreement does not grant permission to use CNRI - trademarks or trade name in a trademark sense to endorse or - promote products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using scgi-1.9, Licensee - agrees to be bound by the terms and conditions of this License - Agreement. - diff --git a/ext/common/AgentsStarter.hpp b/ext/common/AgentsStarter.hpp index 99bfb6082f..4def69c57b 100644 --- a/ext/common/AgentsStarter.hpp +++ b/ext/common/AgentsStarter.hpp @@ -394,7 +394,7 @@ class AgentsStarter { string realUnionStationGatewayCert; if (unionStationGatewayCert.empty()) { - realUnionStationGatewayCert = locator.getCertificatesDir() + "/union_station_gateway.crt"; + realUnionStationGatewayCert = locator.getResourcesDir() + "/union_station_gateway.crt"; } else if (unionStationGatewayCert != "-") { realUnionStationGatewayCert = unionStationGatewayCert; } diff --git a/ext/common/Constants.h b/ext/common/Constants.h index 1664318444..ad49159afa 100644 --- a/ext/common/Constants.h +++ b/ext/common/Constants.h @@ -26,7 +26,7 @@ #define _PASSENGER_CONSTANTS_H_ /* Don't forget to update lib/phusion_passenger.rb too. */ -#define PASSENGER_VERSION "3.0.0" +#define PASSENGER_VERSION "3.0.1" #define FEEDBACK_FD 3 diff --git a/ext/common/EventedClient.h b/ext/common/EventedClient.h index 262857c29a..f8ee52743d 100644 --- a/ext/common/EventedClient.h +++ b/ext/common/EventedClient.h @@ -123,7 +123,8 @@ class EventedClient { * method fails to send all data immediately and EventedClient * schedules some data to be sent later, when the socket becomes * readable again. In here we will be watching for read - * and write events. + * and write events. Once all data has been sent out the system + * will transition back to EC_CONNECTED. */ EC_WRITES_PENDING, @@ -140,8 +141,26 @@ class EventedClient { EC_TOO_MANY_WRITES_PENDING, /** - * This state is entered from the EC_WRITES_PENDING or the - * EC_TOO_MANY_WRITES_PENDING state when disconnect() is called. + * This state is like EC_CONNECTED, but indicates that the write + * side of the connection has been closed. In this state write() + * calls won't have any effect. + */ + EC_RO_CONNECTED, + + /** + * This state is entered from EC_WRITES_PENDING when + * closeWrite() has been called. The system will continue + * to send out pending data but write() calls won't append more + * data to the outbox. After pending data has been sent out, + * the system will transition to EC_RO_CONNECTED. + */ + EC_RO_CONNECTED_WITH_WRITES_PENDING, + + /** + * This state is entered from the EC_WRITES_PENDING, + * EC_TOO_MANY_WRITES_PENDING, EC_RO_CONNECTED_WITH_WIRTES_PENDING + * or EC_RO_CONNECTED_WITH_TOO_MANY_WRITES_PENDING state when + * disconnect() is called. * It means that we want to close the connection as soon as all * pending outgoing data has been sent. As soon as that happens * it'll transition to EC_DISCONNECTED. In this state no further @@ -163,14 +182,16 @@ class EventedClient { /** Storage for data that could not be sent out immediately. */ string outbox; int refcount; - bool m_notifyReads; unsigned int outboxLimit; + bool m_notifyReads; void _onReadable(ev::io &w, int revents) { emitEvent(onReadable); } void onWritable(ev::io &w, int revents) { + assert(state != EC_CONNECTED); + assert(state != EC_RO_CONNECTED); assert(state != EC_DISCONNECTED); this_thread::disable_interruption di; @@ -185,7 +206,11 @@ class EventedClient { if (ret == -1) { if (errno != EAGAIN) { int e = errno; - disconnect(true); + if (writeErrorAction == DISCONNECT_FULL) { + disconnect(true); + } else { + closeWrite(); + } emitSystemErrorEvent("Cannot write data to client", e); return; } @@ -213,6 +238,7 @@ class EventedClient { if (outbox.empty()) { switch (state) { case EC_CONNECTED: + case EC_RO_CONNECTED: watchReadEvents(m_notifyReads); watchWriteEvents(false); break; @@ -222,6 +248,11 @@ class EventedClient { watchReadEvents(m_notifyReads); watchWriteEvents(false); break; + case EC_RO_CONNECTED_WITH_WRITES_PENDING: + state = EC_RO_CONNECTED; + watchReadEvents(m_notifyReads); + watchWriteEvents(false); + break; case EC_DISCONNECTING_WITH_WRITES_PENDING: state = EC_DISCONNECTED; watchReadEvents(false); @@ -252,7 +283,12 @@ class EventedClient { watchWriteEvents(true); } break; + case EC_RO_CONNECTED: + fprintf(stderr, "BUG: when outbox is non-empty the state should never be EC_RO_CONNECTED!\n"); + abort(); + break; case EC_WRITES_PENDING: + case EC_RO_CONNECTED_WITH_WRITES_PENDING: watchReadEvents(m_notifyReads); watchWriteEvents(true); break; @@ -300,6 +336,14 @@ class EventedClient { /** The client's file descriptor. Could be -1: see ioAllowed(). */ FileDescriptor fd; + /** Controls what to do when a write error is encountered. */ + enum { + /** Forcefully disconnect the client. */ + DISCONNECT_FULL, + /** Close the writer side of the connection, but continue allowing reading. */ + DISCONNECT_WRITE + } writeErrorAction; + /** * Called when the file descriptor becomes readable and read notifications * are enabled (see notifyRead()). When there's too much pending @@ -350,16 +394,17 @@ class EventedClient { writeWatcher(loop), fd(_fd) { - state = EC_CONNECTED; - refcount = 1; - m_notifyReads = false; - outboxLimit = 1024 * 32; - onReadable = NULL; - onDisconnect = NULL; - onDetach = NULL; + state = EC_CONNECTED; + refcount = 1; + m_notifyReads = false; + outboxLimit = 1024 * 32; + writeErrorAction = DISCONNECT_FULL; + onReadable = NULL; + onDisconnect = NULL; + onDetach = NULL; onPendingDataFlushed = NULL; - onSystemError = NULL; - userData = NULL; + onSystemError = NULL; + userData = NULL; readWatcher.set(fd, ev::READ); readWatcher.set(this); writeWatcher.set(this); @@ -393,17 +438,35 @@ class EventedClient { } /** - * Returns whether it is allowed to perform any I/O with this client. + * Returns whether it is allowed to perform some kind of I/O with + * this client, either reading or writing. * Usually true, and false when the client is either being disconnected * or has been disconnected. A return value of false indicates that * fd might be -1, but even when it isn't -1 you shouldn't * access fd anymore. + * When the connection is half-closed (e.g. after closeWrite() has + * been called) the return value is still be true. Only when I/O of any + * kind is disallowed will this function return false. */ bool ioAllowed() const { return state != EC_DISCONNECTING_WITH_WRITES_PENDING && state != EC_DISCONNECTED; } + /** + * Returns whether it is allowed to write data to the client. + * Usually true, and false when the client is either being disconnected + * or has been disconnected or when the writer side of the client + * connection has been closed. write() will do nothing if this function + * returns false. + */ + bool writeAllowed() const { + return state == EC_CONNECTED + || state == EC_WRITES_PENDING + || state == EC_TOO_MANY_WRITES_PENDING + || state == EC_RO_CONNECTED_WITH_WRITES_PENDING; + } + /** Used by unit tests. */ bool readWatcherActive() const { return readWatcher.is_active(); @@ -476,12 +539,15 @@ class EventedClient { * network congestion) then the data will be buffered and scheduled for * sending later. * - * If an I/O error was encountered then the client connection will be - * closed by calling disconnect(true). This means this method - * could potentially call the onDisconnect callback. + * If an I/O error was encountered then the action taken depends on the + * value of writeActionError. By default it is DISCONNECT_FULL, + * meaning the client connection will be closed by calling + * disconnect(true). This means this method could potentially + * call the onDisconnect callback. * - * If the client connection is already being closed or has already - * been closed then this method does nothing. + * If the client connection is already being closed, has already + * been closed or if the writer side is closed, then this method does + * nothing. * * The onPendingDataFlushed callback will be called after * this data and whatever existing pending data have been written @@ -489,7 +555,7 @@ class EventedClient { * of time. */ void write(const StaticString data[], unsigned int count) { - if (!ioAllowed()) { + if (!writeAllowed()) { return; } @@ -500,7 +566,11 @@ class EventedClient { ret = gatheredWrite(fd, data, count, outbox); if (ret == -1) { int e = errno; - disconnect(true); + if (writeErrorAction == DISCONNECT_FULL) { + disconnect(true); + } else { + closeWrite(); + } emitSystemErrorEvent("Cannot write data to client", e); } else { updateWatcherStates(); @@ -510,6 +580,39 @@ class EventedClient { } } + /** + * Close only the writer side of the client connection. + * After calling this method, subsequent write() calls won't do anything + * anymore. Any pending outgoing data will be sent out whenever the + * opportunity arises. + * + * This function does nothing if the client is being disconnected, + * already disconnected or if only the writer side is closed. + */ + void closeWrite() { + this_thread::disable_syscall_interruption dsi; + + switch (state) { + case EC_CONNECTED: + assert(outbox.empty()); + state = EC_RO_CONNECTED; + if (syscalls::shutdown(fd, SHUT_WR) == -1) { + int e = errno; + emitSystemErrorEvent( + "Cannot shutdown writer half of the client socket", + e); + } + break; + case EC_WRITES_PENDING: + case EC_TOO_MANY_WRITES_PENDING: + state = EC_RO_CONNECTED_WITH_WRITES_PENDING; + break; + default: + break; + } + updateWatcherStates(); + } + /** * Disconnects the client. This actually closes the underlying file * descriptor, even if the FileDescriptor object still has references. @@ -543,7 +646,7 @@ class EventedClient { this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; - if (state == EC_CONNECTED || force) { + if (state == EC_CONNECTED || state == EC_RO_CONNECTED || force) { state = EC_DISCONNECTED; watchReadEvents(false); watchWriteEvents(false); @@ -570,9 +673,10 @@ class EventedClient { * Detaches the client file descriptor so that this EventedClient no longer * has any control over it. Any EventedClient I/O watchers on the client file * descriptor will be stopped and further I/O on the file descriptor via - * EventedClient will become impossible. The original client file descriptor - * is returned and onDetach is called. Subsequent calls to this - * function will return -1 and will no longer call onDetach. + * EventedClient will become impossible. Any pending outgoing data will be + * discarded. The original client file descriptor is returned and + * onDetach is called. Subsequent calls to this function will + * return -1 and will no longer call onDetach. * * @post !ioAllowed() * @post fd == -1 diff --git a/ext/common/IniFile.h b/ext/common/IniFile.h new file mode 100644 index 0000000000..85fc122e74 --- /dev/null +++ b/ext/common/IniFile.h @@ -0,0 +1,488 @@ +#ifndef _PASSENGER_INI_FILE_H_ +#define _PASSENGER_INI_FILE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Passenger { + +using namespace std; +using namespace boost; + + +class IniFileSection { +protected: + typedef map ValueMap; + string sectionName; + ValueMap values; + +public: + IniFileSection(const string §ionName) { + this->sectionName = sectionName; + } + + bool hasKey(const string &keyName) const { + return values.find(keyName) != values.end(); + } + + string get(const string &keyName) const { + ValueMap::const_iterator it = values.find(keyName); + if (it != values.end()) { + return it->second; + } else { + return string(); + } + } + + string operator[](const string &keyName) const { + return get(keyName); + } + + void set(const string &keyName, const string &value) { + values[keyName] = value; + } + + string getSectionName() const { + return sectionName; + } + + void inspect() const { + ValueMap::const_iterator it = values.begin(); + while (it != values.end()) { + cout << it->first << " = " << it->second << endl; + it++; + } + } +}; + +class IniFileLexer { +public: + class Token { + public: + enum Kind { + UNKNOWN = 0, + NEWLINE, + SECTION_NAME, + IDENTIFIER, + ASSIGNMENT, + TEXT, + END_OF_FILE + }; + + const Kind kind; + const string value; + const int line; + const int column; + + // String representations of the Kind enum. + const static char *identityByKind(Kind kind) { + const static char* KIND_IDENTITY_TABLE[] = { + "", + "", + "", + "", + "", + "", + "" + }; + + return KIND_IDENTITY_TABLE[kind]; + } + + Token(const Kind kind, const string &value, const int line, const int column) + : kind(kind), value(value), line(line), column(column) { + + } + + class ExpectanceException : public std::exception { + private: + char message[255]; + + public: + ExpectanceException(char expected, char got, int line, int column) { + int messageSize = sizeof(message); + memset(message, 0, messageSize); + snprintf(message, messageSize, + "On line %i, column %i: Expected '%c', got '%c' instead.", + line, column, expected, got); + } + + ExpectanceException(Token::Kind expected, Token got) { + const char *expectedKindString = Token::identityByKind(expected); + int messageSize = sizeof(message); + memset(message, 0, messageSize); + snprintf(message, messageSize, + "On line %i, column %i: Expected '%s', got '%s' instead.", + got.line, got.column, expectedKindString, got.value.c_str()); + } + + ExpectanceException(char expected, Token::Kind got, int line, int column) { + const char *gotKindString = Token::identityByKind(got); + int messageSize = sizeof(message); + memset(message, 0, messageSize); + snprintf(message, messageSize, + "On line %i, column %i: Expected '%c', got '%s' instead.", + line, column, expected, gotKindString); + } + + virtual const char* what() const throw() { + return message; + } + }; + }; + + typedef shared_ptr TokenPtr; + + + +protected: + ifstream iniFileStream; + + char lastAcceptedChar; + char upcomingChar; + bool upcomingTokenPtrIsStale; + + int currentLine; + int currentColumn; + + TokenPtr upcomingTokenPtr; + + void expect(char ch) { + char upcomingChar = (char)iniFileStream.peek(); + + if (ch != upcomingChar) { + switch(upcomingChar) { + case EOF: + throw Token::ExpectanceException(ch, Token::END_OF_FILE, + currentLine, currentColumn + 1); + case '\n': + throw Token::ExpectanceException(ch, upcomingChar, + currentLine + 1, 0); + default: + throw Token::ExpectanceException(ch, upcomingChar, + currentLine, currentColumn + 1); + } + } + } + + void accept() { + if (upcomingChar == EOF) return; + + lastAcceptedChar = (char)iniFileStream.get(); + upcomingChar = (char)iniFileStream.peek(); + currentColumn++; + + if (lastAcceptedChar == '\n') { + currentLine++; + currentColumn = 1; + } + } + + void ignore() { + if (upcomingChar == EOF) return; + + upcomingChar = (char)iniFileStream.peek(); + currentColumn++; + + if ((char)iniFileStream.get() == '\n') { + currentLine++; + currentColumn = 1; + } + } + + void expectAndAccept(char ch) { + expect(ch); + accept(); + } + + void acceptWhileNewLine() { + while (iniFileStream.good() && upcomingChar == '\n') { + accept(); + } + } + + void ignoreWhileNotNewLine() { + while (iniFileStream.good() && upcomingChar != '\n') { + ignore(); + } + } + + Token tokenizeIdentifier() { + int line = currentLine; + int column = currentColumn; + string result; + + while (isalnum(upcomingChar) || upcomingChar == '_' || upcomingChar == '-') { + result.append(1, upcomingChar); + accept(); + } + + return Token(Token::IDENTIFIER, result, line, column); + } + + Token tokenizeSection() { + expectAndAccept('['); + Token sectionName = tokenizeSectionName(); + expectAndAccept(']'); + return sectionName; + } + + Token tokenizeSectionName() { + int line = currentLine; + int column = currentColumn; + string result; + + //while (upcomingChar != ']' && upcomingChar != '[' && upcomingChar != '\n' && upcomingChar != EOF) { + while (isalnum(upcomingChar) || upcomingChar == '_' || upcomingChar == '-') { + result.append(1, upcomingChar); + accept(); + } + + return Token(Token::SECTION_NAME, result, line, column); + } + + Token tokenizeAssignment() { + expectAndAccept('='); + return Token(Token::ASSIGNMENT, "=", currentLine, currentColumn); + } + + Token tokenizeText() { + int line = currentLine; + int column = currentColumn; + string result; + + while (upcomingChar != '\n' && upcomingChar != EOF) { + result.append(1, upcomingChar); + accept(); + } + + return Token(Token::TEXT, result, line, column); + } + + Token tokenizeKey() { + return tokenizeIdentifier(); + } + + Token tokenizeValue() { + return tokenizeText(); + } + + Token tokenizeUnknown() { + int line = currentLine; + int column = currentColumn; + string result; + + while (upcomingChar != EOF) { + result.append(1, upcomingChar); + accept(); + } + + return Token(Token::UNKNOWN, result, line, column); + } + +public: + IniFileLexer(const string &fileName) { + currentLine = 1; + currentColumn = 1; + upcomingTokenPtrIsStale = true; + iniFileStream.open(fileName.c_str()); + } + + ~IniFileLexer() { + iniFileStream.close(); + } + + int getCurrentLine() { + return currentLine; + } + + int getCurrentColumn() { + return currentColumn; + } + + TokenPtr peekToken() { + if (upcomingTokenPtrIsStale) { + Token upcomingToken = getToken(); + upcomingTokenPtr = make_shared(upcomingToken); + upcomingTokenPtrIsStale = false; + } + + return upcomingTokenPtr; + } + + Token getToken() { + if (!upcomingTokenPtrIsStale) { + upcomingTokenPtrIsStale = true; + return *upcomingTokenPtr; + } + + while (iniFileStream.good()) { + upcomingChar = (char)iniFileStream.peek(); + switch(upcomingChar) { + case '[': + return tokenizeSection(); + case '\n': + if (lastAcceptedChar != '\n') { + accept(); + return Token(Token::NEWLINE, "\n", currentLine, currentColumn); + } else { + ignore(); + break; + } + case ';': + // Comment encountered: accept all characters until newline (exclusive). + ignoreWhileNotNewLine(); + break; + case '=': + return tokenizeAssignment(); + case EOF: + return Token(Token::END_OF_FILE, "", currentLine, currentColumn); + default: + if (isblank(upcomingChar)) { + ignore(); + } else { + switch(lastAcceptedChar) { + case '\n': + return tokenizeKey(); + case '=': + return tokenizeValue(); + default: + return tokenizeUnknown(); + } + } + } + } + + return Token(Token::END_OF_FILE, "", currentLine, currentColumn); + } +}; + +typedef shared_ptr IniFileSectionPtr; + + +class IniFile { +protected: + typedef map SectionMap; + string name; + SectionMap sections; + + class IniFileParser { + typedef IniFileLexer::Token Token; + + protected: + IniFileLexer lexer; + IniFile *iniFile; + + // The Start Symbol. + void parseSections() { + while ((lexer.peekToken())->kind == Token::SECTION_NAME) { + parseSection(); + } + } + + void parseSection() { + Token token = acceptAndReturnif (Token::SECTION_NAME); + acceptIfEOL(); + + string sectionName = token.value; + IniFileSection *section = new IniFileSection(sectionName); + iniFile->addSection(section); + + parseSectionBody(section); + } + + void parseSectionBody(IniFileSection *currentSection) { + while ((lexer.peekToken())->kind == Token::IDENTIFIER) { + parseKeyValue(currentSection); + } + } + + void parseKeyValue(IniFileSection *currentSection) { + Token identifierToken = acceptAndReturnif (Token::IDENTIFIER); + acceptif (Token::ASSIGNMENT); + Token valueToken = acceptAndReturnif (Token::TEXT); + acceptIfEOL(); + currentSection->set(identifierToken.value, valueToken.value); + } + + void acceptif (Token::Kind expectedKind) { + Token token = lexer.getToken(); + + if (token.kind != expectedKind) { + throw Token::ExpectanceException(expectedKind, token); + } + } + + void acceptIfEOL() { + Token token = lexer.getToken(); + + if (token.kind != Token::NEWLINE && token.kind != Token::END_OF_FILE) { + throw Token::ExpectanceException(Token::NEWLINE, token); + } + } + + Token acceptAndReturnif (Token::Kind expectedKind) { + Token token = lexer.getToken(); + + if (token.kind != expectedKind) { + throw Token::ExpectanceException(expectedKind, token); + } + + return token; + } + + public: + IniFileParser(IniFile *iniFile) : lexer(iniFile->getName()), iniFile(iniFile) { + parseSections(); + } + }; + +public: + IniFile(const string &iniFileName) : name(iniFileName) { + IniFileParser parser(this); + } + + void addSection(IniFileSection *section) { + sections.insert(make_pair(section->getSectionName(), IniFileSectionPtr(section))); + } + + IniFileSectionPtr section(const string §ionName) { + SectionMap::iterator it = sections.find(sectionName); + if (it != sections.end()) { + return it->second; + } else { + return IniFileSectionPtr(); + } + } + + bool hasSection(const string §ionName) const { + return sections.find(sectionName) != sections.end(); + } + + string getName() const { + return name; + } + + void inspect() const { + SectionMap::const_iterator it = sections.begin(); + while (it != sections.end()) { + cout << "[" << (it->first) << "]" << endl; + it->second->inspect(); + + it++; + } + } +}; + +} // namespace Passenger + +#endif /* _PASSENGER_INI_FILE_H_ */ diff --git a/ext/common/ResourceLocator.h b/ext/common/ResourceLocator.h index a114ff2a47..5f65804c5a 100644 --- a/ext/common/ResourceLocator.h +++ b/ext/common/ResourceLocator.h @@ -26,59 +26,109 @@ #define _PASSENGER_RESOURCE_LOCATOR_H_ #include -#include "Utils.h" +#include +#include +#include namespace Passenger { +using namespace boost; + + +/** + * Locates various Phusion Passenger resources on the filesystem. + */ class ResourceLocator { private: - string root; - bool nativelyPackaged; + string agentsDir; + string helperScriptsDir; + string resourcesDir; + string docDir; + string rubyLibDir; + string compilableSourceDir; + string apache2Module; -public: - ResourceLocator(const string &passengerRoot) { - root = passengerRoot; - nativelyPackaged = !fileExists(root + "/Rakefile") || - !fileExists(root + "/DEVELOPERS.TXT"); + string getOption(const string &file, const IniFileSectionPtr §ion, const string &key) const { + if (section->hasKey(key)) { + return section->get(key); + } else { + throw RuntimeException("Option '" + key + "' missing in file " + file); + } } - string getSourceRoot() const { - if (nativelyPackaged) { - return "/usr/lib/phusion-passenger/source"; +public: + ResourceLocator(const string &rootOrFile) { + if (getFileType(rootOrFile) == FT_DIRECTORY) { + string root = rootOrFile; + bool nativelyPackaged = !fileExists(root + "/Rakefile") || + !fileExists(root + "/DEVELOPERS.TXT"); + + if (nativelyPackaged) { + agentsDir = "/usr/lib/phusion-passenger/agents"; + helperScriptsDir = "/usr/share/phusion-passenger/helper-scripts"; + resourcesDir = "/usr/share/phusion-passenger"; + docDir = "/usr/share/doc/phusion-passenger"; + rubyLibDir = ""; + compilableSourceDir = "/usr/share/phusion-passenger/compilable-source"; + apache2Module = "/usr/lib/apache2/modules/mod_passenger.so"; + } else { + agentsDir = root + "/agents"; + helperScriptsDir = root + "/helper-scripts"; + resourcesDir = root + "/resources"; + docDir = root + "/doc"; + rubyLibDir = root + "/lib"; + compilableSourceDir = root; + apache2Module = root + "ext/apache2/mod_passenger.so"; + } + } else { - return root; + string file = rootOrFile; + IniFileSectionPtr options = IniFile(file).section("locations"); + agentsDir = getOption(file, options, "agents"); + helperScriptsDir = getOption(file, options, "helper_scripts"); + resourcesDir = getOption(file, options, "resources"); + docDir = getOption(file, options, "doc"); + rubyLibDir = getOption(file, options, "rubylib"); + compilableSourceDir = getOption(file, options, "compilable_source"); + apache2Module = getOption(file, options, "apache2_module"); } } string getAgentsDir() const { - if (nativelyPackaged) { - return "/usr/lib/phusion-passenger/agents"; - } else { - return root + "/agents"; - } + return agentsDir; } string getHelperScriptsDir() const { - if (nativelyPackaged) { - return "/usr/share/phusion-passenger/helper-scripts"; - } else { - return root + "/helper-scripts"; - } + return helperScriptsDir; } string getSpawnServerFilename() const { return getHelperScriptsDir() + "/passenger-spawn-server"; } - string getCertificatesDir() const { - if (nativelyPackaged) { - return "/usr/share/phusion-passenger/certificates"; - } else { - return root + "/resources"; - } + string getResourcesDir() const { + return resourcesDir; + } + + string getDocDir() const { + return docDir; + } + + // Can be empty. + string getRubyLibDir() const { + return rubyLibDir; + } + + string getCompilableSourceDir() const { + return compilableSourceDir; + } + + string getApache2ModuleFilename() const { + return apache2Module; } }; + } #endif /* _PASSENGER_RESOURCE_LOCATOR_H_ */ diff --git a/ext/common/StaticString.h b/ext/common/StaticString.h index 1725c58121..a077c6f4eb 100644 --- a/ext/common/StaticString.h +++ b/ext/common/StaticString.h @@ -27,7 +27,9 @@ #include #include +#include #include +#include namespace Passenger { @@ -45,6 +47,36 @@ class StaticString { const char *content; string::size_type len; + static const char *memmem(const char *haystack, string::size_type haystack_len, + const char *needle, string::size_type needle_len) + { + if (needle_len == 0) { + return haystack; + } + + const char *last_possible = haystack + haystack_len - needle_len; + do { + const char *result = (const char *) memchr(haystack, needle[0], haystack_len); + if (result != NULL) { + if (result > last_possible) { + return NULL; + } else if (memcmp(result, needle, needle_len) == 0) { + return result; + } else { + ssize_t new_len = ssize_t(haystack_len) - (result - haystack) - 1; + if (new_len <= 0) { + return NULL; + } else { + haystack = result + 1; + haystack_len = new_len; + } + } + } else { + return NULL; + } + } while (true); + } + public: /** A hash function object for StaticString. */ struct Hash { @@ -121,6 +153,49 @@ class StaticString { return len == other.size() && memcmp(content, other.data(), len) == 0; } + string::size_type find(char c, string::size_type pos = 0) const { + if (pos < len) { + const char *result = (const char *) memchr(content + pos, c, len - pos); + if (result == NULL) { + return string::npos; + } else { + return result - content; + } + } else { + return string::npos; + } + } + + string::size_type find(const StaticString &s, string::size_type pos = 0) const { + if (s.empty()) { + return 0; + } else if (pos < len) { + const char *result = memmem(content + pos, len - pos, s.c_str(), s.size()); + if (result == NULL) { + return string::npos; + } else { + return result - content; + } + } else { + return string::npos; + } + } + + string::size_type find(const char *s, string::size_type pos, string::size_type n) const { + return find(StaticString(s, n), pos); + } + + StaticString substr(string::size_type pos = 0, string::size_type n = string::npos) const { + if (pos > len) { + throw out_of_range("Argument 'pos' out of range"); + } else { + if (n > len - pos) { + n = len - pos; + } + return StaticString(content + pos, n); + } + } + bool operator==(const StaticString &other) const { return len == other.len && memcmp(content, other.content, len) == 0; } diff --git a/ext/common/Utils/IOUtils.cpp b/ext/common/Utils/IOUtils.cpp index 1afff1c1f9..19b4cb7e78 100644 --- a/ext/common/Utils/IOUtils.cpp +++ b/ext/common/Utils/IOUtils.cpp @@ -49,6 +49,10 @@ #include #include +#ifdef __linux__ + #include +#endif + #include "Timer.h" #include "IOUtils.h" #include "StrIntUtils.h" @@ -150,6 +154,33 @@ setNonBlocking(int fd) { } } +int +tryAccept4(int sock, struct sockaddr *addr, socklen_t *addr_len, int options) { + #if defined(__linux__) && defined(__x86_64__) + int ret; + do { + ret = syscall(288, sock, addr, addr_len, options); + } while (ret == -1 && errno == EINTR); + return ret; + #elif defined(__linux__) && defined(__i386__) + int ret; + do { + ret = syscall(__NR_socketcall, 18, + sock, addr, addr_len, options); + } while (ret == -1 && errno == EINTR); + return ret; + #elif defined(SYS_ACCEPT4) + int ret; + do { + ret = ::accept4(sock, addr, addr_len, options); + } while (ret == -1 && errno == EINTR); + return ret; + #else + errno = ENOSYS; + return -1; + #endif +} + vector resolveHostname(const string &hostname, unsigned int port, bool shuffle) { string portString = toString(port); diff --git a/ext/common/Utils/IOUtils.h b/ext/common/Utils/IOUtils.h index 22cc2379cd..c2914b42fb 100644 --- a/ext/common/Utils/IOUtils.h +++ b/ext/common/Utils/IOUtils.h @@ -93,6 +93,12 @@ bool isLocalSocketAddress(const StaticString &address); */ void setNonBlocking(int fd); +/** + * Try to call the Linux accept4() system call. If the system call is + * not available, then -1 is returned and errno is set to ENOSYS. + */ +int callAccept4(int sock, struct sockaddr *addr, socklen_t *addr_len, int options); + /** * Resolves the given host name and returns a list of IP addresses. * hostname may also be an IP address, in which case it is diff --git a/ext/common/Watchdog.cpp b/ext/common/Watchdog.cpp index c5a4ad390e..b49070fa8d 100644 --- a/ext/common/Watchdog.cpp +++ b/ext/common/Watchdog.cpp @@ -107,6 +107,7 @@ class AgentWatcher { pid = this->pid; lock.unlock(); + // Process can be started before the watcher thread is launched. if (pid == 0) { pid = start(); } @@ -271,22 +272,19 @@ class AgentWatcher { this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; string exeFilename = getExeFilename(); - int fds[2], e, ret; + SocketPair fds; + int e, ret; pid_t pid; /* Create feedback fd for this agent process. We'll send some startup * arguments to this agent process through this fd, and we'll receive * startup information through it as well. */ - if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) { - int e = errno; - throw SystemException("Cannot create a Unix socket pair", e); - } + fds = createUnixSocketPair(); pid = syscalls::fork(); if (pid == 0) { // Child - long max_fds, i; /* Make sure file descriptor FEEDBACK_FD refers to the newly created * feedback fd (fds[1]) and close all other file descriptors. @@ -315,11 +313,7 @@ class AgentWatcher { } } - /* Close all file descriptors except 0-FEEDBACK_FD. */ - max_fds = sysconf(_SC_OPEN_MAX); - for (i = FEEDBACK_FD + 1; i < max_fds; i++) { - syscalls::close(i); - } + closeAllFileDescriptors(FEEDBACK_FD); /* Become the process group leader so that the watchdog can kill the * agent as well as all its descendant processes. */ @@ -336,25 +330,22 @@ class AgentWatcher { try { MessageChannel(FEEDBACK_FD).write("exec error", toString(e).c_str(), NULL); - _exit(1); } catch (...) { fprintf(stderr, "Passenger Watchdog: could not execute %s: %s (%d)\n", exeFilename.c_str(), strerror(e), e); fflush(stderr); - _exit(1); } + _exit(1); } else if (pid == -1) { // Error e = errno; - syscalls::close(fds[0]); - syscalls::close(fds[1]); throw SystemException("Cannot fork a new process", e); } else { // Parent - FileDescriptor feedbackFd(fds[0]); + FileDescriptor feedbackFd = fds[0]; vector args; - syscalls::close(fds[1]); + fds[1].close(); this_thread::restore_interruption ri(di); this_thread::restore_syscall_interruption rsi(dsi); ScopeGuard failGuard(boost::bind(killAndWait, pid)); @@ -526,7 +517,7 @@ class AgentWatcher { } /** - * Returns the agent process feedback fd, or NULL if the agent process + * Returns the agent process feedback fd, or -1 if the agent process * hasn't been started yet. Can be used to check whether this agent process * has exited without using waitpid(). */ @@ -667,13 +658,8 @@ class ServerInstanceDirToucher { if (pid == 0) { // Child int prio, ret, e; - long max_fds, i; - // Close all unnecessary file descriptors. - max_fds = sysconf(_SC_OPEN_MAX); - for (i = 3; i < max_fds; i++) { - syscalls::close(i); - } + closeAllFileDescriptors(2); // Make process nicer. do { diff --git a/ext/nginx/Configuration.c b/ext/nginx/Configuration.c index f24cf70b24..2d8041ac13 100644 --- a/ext/nginx/Configuration.c +++ b/ext/nginx/Configuration.c @@ -827,8 +827,14 @@ passenger_enabled(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) && clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } - } else { + } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) { passenger_conf->enabled = 0; + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"passenger_enabled\" must be either set to \"on\" " + "or \"off\""); + + return NGX_CONF_ERROR; } return NGX_CONF_OK; diff --git a/lib/phusion_passenger.rb b/lib/phusion_passenger.rb index b43817cc72..601949952b 100644 --- a/lib/phusion_passenger.rb +++ b/lib/phusion_passenger.rb @@ -25,7 +25,7 @@ module PhusionPassenger ###### Version numbers ###### # Phusion Passenger version number. Don't forget to edit ext/common/Constants.h too. - VERSION_STRING = '3.0.0' + VERSION_STRING = '3.0.1' PREFERRED_NGINX_VERSION = '0.8.53' PREFERRED_PCRE_VERSION = '8.10' @@ -48,8 +48,6 @@ def self.natively_packaged? NATIVELY_PACKAGED_SOURCE_ROOT = "/usr/share/phusion-passenger/source" NATIVELY_PACKAGED_DOCDIR = "/usr/share/doc/phusion-passenger" - NATIVELY_PACKAGED_AGENTS_DIR = "/usr/lib/phusion-passenger/agents" - NATIVELY_PACKAGED_HELPER_SCRIPTS_DIR = "/usr/share/phusion-passenger/helper-scripts" NATIVELY_PACKAGED_RESOURCES_DIR = "/usr/share/phusion-passenger" NATIVELY_PACKAGED_APACHE2_MODULE = "/usr/lib/apache2/modules/mod_passenger.so" @@ -80,12 +78,6 @@ def self.natively_packaged? # Documentation directory. DOCDIR = File.join(SOURCE_ROOT, "doc") - # Directory containing Phusion Passenger agent executables. - AGENTS_DIR = File.join(SOURCE_ROOT, "agents") - - # Directory containing Phusion Passenger helper scripts. - HELPER_SCRIPTS_DIR = File.join(SOURCE_ROOT, "helper-scripts") - # Directory containing Phusion Passenger resource files. RESOURCES_DIR = File.join(SOURCE_ROOT, "resources") @@ -98,8 +90,6 @@ def self.natively_packaged? else SOURCE_ROOT = NATIVELY_PACKAGED_SOURCE_ROOT DOCDIR = NATIVELY_PACKAGED_DOCDIR - AGENTS_DIR = NATIVELY_PACKAGED_AGENTS_DIR - HELPER_SCRIPTS_DIR = NATIVELY_PACKAGED_HELPER_SCRIPTS_DIR RESOURCES_DIR = NATIVELY_PACKAGED_RESOURCES_DIR APACHE2_MODULE = NATIVELY_PACKAGED_APACHE2_MODULE end diff --git a/lib/phusion_passenger/dependencies.rb b/lib/phusion_passenger/dependencies.rb index c5e90ed6c2..9f2f43dc6d 100644 --- a/lib/phusion_passenger/dependencies.rb +++ b/lib/phusion_passenger/dependencies.rb @@ -403,6 +403,8 @@ def self.asciidoc_required? dep.install_command = "apt-get install libaprutil1-dev" elsif tags.include?(:mandriva) dep.install_command = "urpmi libapr-util-devel" + elsif tags.include?(:redhat) + dep.install_command = "yum install apr-util-devel" end elsif RUBY_PLATFORM =~ /darwin/ dep.install_instructions = "Please install Apache from MacPorts, which will " << diff --git a/lib/phusion_passenger/packaging.rb b/lib/phusion_passenger/packaging.rb index 62d1248c40..577ac0a44a 100644 --- a/lib/phusion_passenger/packaging.rb +++ b/lib/phusion_passenger/packaging.rb @@ -37,8 +37,7 @@ module Packaging 'passenger', 'passenger-install-apache2-module', 'passenger-install-nginx-module', - 'passenger-config', - 'passenger-stress-test' + 'passenger-config' ] SUPER_USER_EXECUTABLES = [ @@ -72,7 +71,7 @@ module Packaging 'debian/*', 'helper-scripts/*', 'ext/common/**/*.{cpp,c,h,hpp}', - 'ext/apache2/*.{cpp,h,hpp,c,TXT}', + 'ext/apache2/*.{cpp,h,hpp,c}', 'ext/nginx/*.{c,cpp,h}', 'ext/nginx/config', 'ext/boost/**/*', diff --git a/lib/phusion_passenger/public_api.rb b/lib/phusion_passenger/public_api.rb index dd3a7a7ab4..75b1123c15 100644 --- a/lib/phusion_passenger/public_api.rb +++ b/lib/phusion_passenger/public_api.rb @@ -41,11 +41,11 @@ def call_event(name, *args) end end - def install_framework_extensions! + def install_framework_extensions!(*args) require 'rails/version' if defined?(::Rails) && !defined?(::Rails::VERSION) if defined?(::Rails) && ::Rails::VERSION::MAJOR == 3 require 'phusion_passenger/rails3_extensions/init' - Rails3Extensions.init!(_spawn_options) + Rails3Extensions.init!(_spawn_options, *args) end end diff --git a/lib/phusion_passenger/rails3_extensions/init.rb b/lib/phusion_passenger/rails3_extensions/init.rb index b7e22abf11..53ed6f1ca5 100644 --- a/lib/phusion_passenger/rails3_extensions/init.rb +++ b/lib/phusion_passenger/rails3_extensions/init.rb @@ -55,7 +55,7 @@ def self.install!(options) end if defined?(ActionDispatch::ShowExceptions) - Rails::Application.middleware.insert_after( + Rails.application.middleware.insert_after( ActionDispatch::ShowExceptions, ExceptionLogger, analytics_logger) end @@ -78,7 +78,10 @@ def self.install!(options) def process_action(event) log = Thread.current[PASSENGER_ANALYTICS_WEB_LOG] - log.message("View rendering time: #{(event.payload[:view_runtime] * 1000).to_i}") if log + if log + view_runtime = event.payload[:view_runtime] + log.message("View rendering time: #{(view_runtime * 1000).to_i}") if view_runtime + end end def sql(event) diff --git a/lib/phusion_passenger/standalone/command.rb b/lib/phusion_passenger/standalone/command.rb index f7528bdf87..d19ec2fdf0 100644 --- a/lib/phusion_passenger/standalone/command.rb +++ b/lib/phusion_passenger/standalone/command.rb @@ -171,6 +171,7 @@ def determine_various_resource_locations(create_subdirs = true) end def write_nginx_config_file + require 'phusion_passenger/platform_info/ruby' ensure_directory_exists(@temp_dir) File.open(@config_filename, 'w') do |f| diff --git a/lib/phusion_passenger/standalone/runtime_installer.rb b/lib/phusion_passenger/standalone/runtime_installer.rb index 5803785f3f..98b9070a7d 100644 --- a/lib/phusion_passenger/standalone/runtime_installer.rb +++ b/lib/phusion_passenger/standalone/runtime_installer.rb @@ -429,7 +429,16 @@ def install_passenger_support_files def install_nginx_from_source(source_dir) require 'phusion_passenger/platform_info/compiler' Dir.chdir(source_dir) do - command = "sh ./configure '--prefix=#{@nginx_dir}' --without-pcre " << + # RPM thinks it's being smart by scanning binaries for + # paths and refusing to create package if it detects any + # hardcoded thats that point to /usr or other important + # locations. For Phusion Passenger Standalone we do not + # care at all what the Nginx configured prefix is because + # we pass it its resource locations during runtime, so + # work around the problem by configure Nginx with prefix + # /tmp. + command = "sh ./configure --prefix=/tmp " << + "--without-pcre " << "--without-http_rewrite_module " << "--without-http_fastcgi_module " << "'--add-module=#{@support_dir}/ext/nginx'" @@ -455,9 +464,12 @@ def install_nginx_from_source(source_dir) exit 1 end - command = "#{PlatformInfo.gnu_make} install" - run_command_with_throbber(command, "Copying files...") do |status_text| - yield(1, 1, status_text) + yield(1, 1, 'Copying files...') + if !system("mkdir -p '#{@nginx_dir}/sbin'") || + !system("cp -pR objs/nginx '#{@nginx_dir}/sbin/'") + STDERR.puts + STDERR.puts "*** ERROR: unable to copy Nginx binaries." + exit 1 end end end diff --git a/lib/phusion_passenger/templates/standalone/config.erb b/lib/phusion_passenger/templates/standalone/config.erb index 2151ab10e0..1a643130ce 100644 --- a/lib/phusion_passenger/templates/standalone/config.erb +++ b/lib/phusion_passenger/templates/standalone/config.erb @@ -1,3 +1,29 @@ +##################################################### +# +# !!!!!!! WARNING, READ THIS !!!!!!! +# +# +# The fact that Phusion Passenger uses Nginx +# internally is considered to be an implementation +# detail that the user should not bother with. +# We may arbitrarily replace the Nginx core with +# something else in the future. +# +# As such, we do not support any kind of custom +# Nginx configuration in Phusion Passenger Standalone. +# If you need additional Nginx modules or if you need +# special Nginx configuration or whatever then you +# should use Phusion Passenger for Nginx, NOT +# Phusion Passenger Standalone. +# +# You are strongly discouraged from editing this file +# and treating Phusion Passenger Standalone as an easy +# way to start Nginx. We will not provide any support +# for this. +# +##################################################### + + master_process on; worker_processes 1; daemon on; diff --git a/rpm/README.rdoc b/rpm/README.rdoc new file mode 100644 index 0000000000..f741a71c6a --- /dev/null +++ b/rpm/README.rdoc @@ -0,0 +1,64 @@ += Rubygem-passenger + +This project is an attempt to build RPMs for {Phusion +Passenger}[http://modrails.org] for Fedora Core 13+, RHEL 5.5+, and CentOS +5.5+ (both via EPEL) + +== Packages + +=== Yum Package + +[passenger-release] Yum configuration necessary to query the + {main repository}[http://passenger.stealthymonkeys.com] + (and eventually mirrors) for updates. + +=== Main Packages + +[rubygem-passenger] The base passenger Gem. Most of the package lives here. + +[mod_passenger] The Apache webserver dynamically loadable module. + +[nginx-passenger] A version of the nginx webserver with the passenger module + statically linked (nginx does not support dynamically + loadable modules) + +[rubygem-passenger-standalone] The passenger-standalone server. As of + 3.0.0-11 this package is correctly built + and installed and is now safe to use. + +=== Supporting Packages + +[rubygem-passenger-native] The compiled helper executables required by + mod_passenger and nginx-passenger + +[rubygem-passenger-native-libs] The passenger_native_support.so shared library + compiled against a given ruby engine. This + package is separated to simplify building one + for a different ruby engine. Currently only + required by nginx-passenger. + +[nginx-alternatives] A meta-package to put nginx under the alternatives system + so that multiple versions of the binary can co-exist. + This package is meant to be Obsoleted by a future nginx + package which should contain this functionality. + +=== External Dependency Packages + +[rubygem-daemon_controller] A gem for robust daemon managment. Required by + rubygem-passenger. + +[rubygem-file-tail] Library to tail files in Ruby. Required by rubygem-passenger. + +[rubygem-spruz] "Useful Stuff." Required by rubygem-file-tail. + +=== Other Packages + +[rubygem-passenger-debuginfo] Standard debugger symbols for the above + packages. (Turned off in the 3.0.0 tree + due to debugging symbols containing + %builddir paths. This will be reinstaed in + a later release) + +== Etc. + +More to come! diff --git a/rpm/config/apache-passenger.conf.in b/rpm/config/apache-passenger.conf.in new file mode 100644 index 0000000000..9f0f8afd28 --- /dev/null +++ b/rpm/config/apache-passenger.conf.in @@ -0,0 +1,19 @@ +LoadModule passenger_module modules/mod_passenger.so + + PassengerRoot %ROOT + PassengerRuby %RUBY + + +# Deploying a Ruby on Rails application: an example + +# Suppose you have a Rails application in /somewhere. Add a virtual host to +# your Apache configuration file and set its DocumentRoot to /somewhere/public: +# +# +# ServerName www.yourhost.com +# DocumentRoot /somewhere/public # <-- be sure to point to 'public'! +# +# AllowOverride all # <-- relax Apache security settings +# Options -MultiViews # <-- MultiViews must be turned off +# +# diff --git a/rpm/config/nginx-passenger.conf.in b/rpm/config/nginx-passenger.conf.in new file mode 100644 index 0000000000..f9c86621b1 --- /dev/null +++ b/rpm/config/nginx-passenger.conf.in @@ -0,0 +1,10 @@ +passenger_root %ROOT; +passenger_ruby %RUBY; + +# You'll need to integrate this portion into your own config +# server { +# listen 80; +# server_name www.yourhost.com; +# root /somewhere/public; # <--- be sure to point to 'public'! +# passenger_enabled on; +# } diff --git a/rpm/config/rubygem-passenger.te b/rpm/config/rubygem-passenger.te new file mode 100644 index 0000000000..c5b521b7f7 --- /dev/null +++ b/rpm/config/rubygem-passenger.te @@ -0,0 +1,10 @@ +policy_module(rubygem_passenger, 1.0) + +require { + type httpd_t, httpd_tmp_t; +}; + + +# This should use interface macros +allow httpd_t httpd_tmp_t:sock_file { create write unlink getattr setattr }; +allow httpd_t self:capability { fowner sys_resource fsetid }; diff --git a/rpm/doc/README.nginx-alternatives b/rpm/doc/README.nginx-alternatives new file mode 100644 index 0000000000..16bd3a1213 --- /dev/null +++ b/rpm/doc/README.nginx-alternatives @@ -0,0 +1,5 @@ +This package puts the nginx webserver binary under the control of the +/etc/alternative system. + +This package is meant to be obsoleted by a future nginx package (which +will provide the same feature) diff --git a/rpm/doc/footer.shtml b/rpm/doc/footer.shtml new file mode 100644 index 0000000000..0f3a9c6e68 --- /dev/null +++ b/rpm/doc/footer.shtml @@ -0,0 +1,7 @@ +

+ “Phusion” and “Phusion Passenger” are + trademarks of Hongli Lai & Ninh Bui. All rights reserved. +

+ + + \ No newline at end of file diff --git a/rpm/doc/header.shtml b/rpm/doc/header.shtml new file mode 100644 index 0000000000..0cad42a588 --- /dev/null +++ b/rpm/doc/header.shtml @@ -0,0 +1,113 @@ + + + + + + <!--#echo var="REQUEST_URI" --> — The Phusion Passenger RPM Repository + + + + +

The Phusion Passenger RPM Repository

+ +
+

+ Welcome to the repository. Here you’ll find RPM packages of Phusion Passenger (aka mod_rails / + mod_rack) for Fedora and RedHat Enterprise Linux (and + derivatives like CentOS and Scientific Linux). +

+ +

+ The easiest way to install Passenger and keep it up to date is to + install the passenger-release package: +

+ +
+
Fedora Core 14:
+
rpm -Uvh http:///fedora/14/passenger-release.noarch.rpm
+
Fedora Core 13:
+
rpm -Uvh http:///fedora/13/passenger-release.noarch.rpm
+
RHEL 5 / CentOS 5 / ScientificLinux 5: (note, these packages depend on EPEL)
+
rpm -Uvh http:///rhel/5/passenger-release.noarch.rpm
+
+ +

+ From there you can use Yum to install packages. For example: +

+ +
+
yum install nginx-passenger
+
or
+
yum install mod_passenger
+
or
+
yum install rubygem-passenger-standalone
+
+ +

More Info?

+ +

+ The source files and documentation for these packages are maintained + in a GitHub + repository. Comments, questions, patches, and pull requests are + always welcome. +

+ +

diff --git a/rpm/nginx-alternatives.spec b/rpm/nginx-alternatives.spec new file mode 100644 index 0000000000..ee76cff856 --- /dev/null +++ b/rpm/nginx-alternatives.spec @@ -0,0 +1,97 @@ +# RPM Spec file for nginx-alternatives +# This is a stop-gap solution to having multiple compiles of nginx on +# the same server. +# +# This package is meant to be obsoleted by a future nginx package that +# will provide the same feature + +%define perldir %(perl -MConfig -e 'print $Config{installarchlib}') + +Summary: Alternatives aware nginx +Name: nginx-alternatives +Version: 0.0.1 +Release: 3%{?dist} +License: MIT +Group: System Environment/Daemons +#Source0: %{name}-%{version}.tar.gz +Source0: README.%{name} +Requires: nginx +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +%description +This package puts the nginx webserver binary under the control of the +/etc/alternative system. + +This package is meant to be obsoleted by a future nginx package (which +will provide the same feature) + +%prep +#%setup -q + +%build +cp %{SOURCE0} . + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT + +%clean +rm -rf $RPM_BUILD_ROOT + +%triggerin -- nginx +if [ ! -L /usr/sbin/nginx ] ; then + mv /usr/sbin/nginx /usr/sbin/nginx.base + mv %{perldir}/auto/nginx/nginx.so %{perldir}/auto/nginx/nginx_base.so + mv %{perldir}/nginx.pm %{perldir}/nginx_base.pm + mv %{_mandir}/man3/nginx.3pm.gz %{_mandir}/man3/nginx_base.3pm.gz + + /usr/sbin/alternatives --install /usr/sbin/nginx nginx \ + /usr/sbin/nginx.base 30 \ + --slave %{perldir}/auto/nginx/nginx.so nginx.so \ + %{perldir}/auto/nginx/nginx_base.so \ + --slave %{perldir}/nginx.pm nginx.pm %{perldir}/nginx_base.pm \ + --slave %{_mandir}/man3/nginx.3pm.gz nginx.man \ + %{_mandir}/man3/nginx_base.3pm.gz +fi + +# Given that other packages will depend on this one, it's 99% likely +# that this will have been reset back to base. Still good practice to +# put the expected binary back in place. +%define undo_link \ + bin=`readlink -f /usr/sbin/nginx` \ + so=`readlink -f %{perldir}/auto/nginx/nginx.so` \ + pm=`readlink -f %{perldir}/nginx.pm` \ + man=`readlink -f %{_mandir}/man3/nginx.3pm.gz` \ + /usr/sbin/alternatives --remove nginx /usr/sbin/nginx.base \ + /usr/sbin/alternatives --remove nginx $bin \ + mv -f $bin /usr/sbin/nginx \ + mv -f $so %{perldir}/auto/nginx/nginx.so \ + mv -f $pm %{perldir}/nginx.pm \ + mv -f $man %{_mandir}/man3/nginx.3pm.gz + + +%triggerun -- nginx +if [ -L /usr/sbin/nginx ] ; then + %undo_link +fi + +# triggerun runs if either package is removed, so this isn't necessary +%postun +if [ $1 == 0 -a -L /usr/sbin/nginx ]; then + %undo_link +fi + +%files +%defattr(-,root,root,-) +%doc README.%{name} + +%changelog +* Sat Oct 30 2010 Erik Ogan - 0.0.1-3 +- Add slaves for the perl module + +* Thu Oct 21 2010 Erik Ogan - 0.0.1-2 +- Use triggers to maintain the link if nginx package is upgraded + +* Wed Oct 20 2010 Erik Ogan - 0.0.1-1 +- Initial build. diff --git a/rpm/passenger-release.spec b/rpm/passenger-release.spec new file mode 100644 index 0000000000..e74d4dc786 --- /dev/null +++ b/rpm/passenger-release.spec @@ -0,0 +1,91 @@ +Summary: Phusion Passenger release RPM/Yum repository configuration +Name: passenger-release +Version: 3 +Release: 4%{?dist} +License: MIT +Group: Group: System Environment/Base +URL: http://passenger.stealthymonkeys.com/ +Source0: mirrors-passenger +Source1: RPM-GPG-KEY-stealthymonkeys +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildArch: noarch + +%description +Phusion Passenger Yum/RPM configuration. This package contains the Yum +repository configuration to install & update Phusion Passenger, as +well as the GPG signing key to verify them. + +%prep +#%setup -c + +%{?el5:name='Red Hat Enterprise' version='5' path='rhel'} +%{?el6:name='Red Hat Enterprise' version='6' path='rhel'} + +%{?fc13:name='Fedora Core' version='13' path='fedora'} +%{?fc14:name='Fedora Core' version='14' path='fedora'} + +if [ -z "$name" ] ; then + echo "Please specify a distro to build for (f'rex: el5 or fc13)" >&2 + exit 255 +fi + +%{__cat} < passenger.repo +### Name: Phusion Passenger RPM Repository for $name $version +### URL: %{url} +[passenger] +name = $name \$releasever - Phusion Passenger +baseurl = %{url}$path/\$releasever/\$basearch +mirrorlist = %{url}$path/mirrors +#mirrorlist = file:///etc/yum.repos.d/mirrors-passenger +enabled = 1 +gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-passenger +gpgcheck = 1 + +### Name: Phusion Passenger RPM Repository for $name $version (TESTING) +### URL: %{url} +[passenger-testing] +name = $name \$releasever - Phusion Passenger - TEST +baseurl = %{url}$path/\$releasever/\$basearch/testing/ +enabled = 0 +gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-passenger +gpgcheck = 0 +EOF + +for mirror in $(%{__cat} %{SOURCE0}); do + echo "$mirror/$path/\$releasever/\$ARCH/" +done > mirrors-passenger + +%build + +%install +rm -rf %{buildroot} +%{__install} -D -p -m 0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-passenger +%{__install} -D -p -m 0644 passenger.repo %{buildroot}%{_sysconfdir}/yum.repos.d/passenger.repo + + + +%clean +rm -rf %{buildroot} + + +%files +%defattr(-,root,root,-) +%doc mirrors-passenger +%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-passenger +%{_sysconfdir}/yum.repos.d/passenger.repo + + + +%changelog +* Wed Nov 24 2010 Erik Ogan - 3-4 +- Fix EL6 & FC14 version numbers. +- Fix the errant /en in the mirrors file + +* Thu Oct 28 2010 Erik Ogan - 3-3 +- Typo in the gpgkey directives + +* Thu Oct 28 2010 Erik Ogan - 3-2 +- Update the mirrorlist URL + +* Tue Oct 26 2010 Erik Ogan - 3-1 +- Initial build. diff --git a/rpm/passenger.spec b/rpm/passenger.spec new file mode 100644 index 0000000000..0ed5b9eef4 --- /dev/null +++ b/rpm/passenger.spec @@ -0,0 +1,657 @@ +# RPM Spec file for Phusion Passenger +# +# The latest version of this file (as well as supporting files, +# documentation, and scripts to help build it) can be found here: +# +# http://github.com/erikogan/rubygem-passenger +# +# (If you fork this project feel free to add your repo below, but please +# do not remove the URL above.) + +%define gemname passenger +%if %{?passenger_version:0}%{?!passenger_version:1} + %define passenger_version 3.0.1 +%endif +%if %{?passenger_release:0}%{?!passenger_release:1} + %define passenger_release 3%{?dist} +%endif +%define passenger_epoch 1 + +%if %{?nginx_version:0}%{?!nginx_version:1} + %define nginx_version 0.8.53 +%endif +%define nginx_release %{passenger_version}_%{passenger_release} +%define nginx_user passenger +%define nginx_group %{nginx_user} +%define nginx_home %{_localstatedir}/lib/nginx +%define nginx_home_tmp %{nginx_home}/tmp +%define nginx_logdir %{_localstatedir}/log/nginx +%define nginx_confdir %{_sysconfdir}/nginx +%define nginx_datadir %{_datadir}/nginx +%define nginx_webroot %{nginx_datadir}/html + +%define httpd_confdir %{_sysconfdir}/httpd/conf.d + +# Macros on the command-line overrides these defaults. You should also +# make sure these match the binaries found in your PATH +%{?!ruby: %define ruby /usr/bin/ruby} +%{?!rake: %define rake /usr/bin/rake} +%{?!gem: %define gem /usr/bin/gem} + +# Debug packages are currently broken. So don't even build them +%define debug_package %nil + +%define ruby_sitelib %(%{ruby} -rrbconfig -e "puts Config::CONFIG['sitelibdir']") + +%define ruby_version_patch %(%{ruby} -e 'puts "#{RUBY_VERSION}#{defined?(RUBY_PATCHLEVEL) ? %q{.} + RUBY_PATCHLEVEL.to_s : nil}"') + +# Does Gem::Version crash&burn on the version defined above? (RHEL might) +%define broken_gem_version %(%{ruby} -rrubygems -e 'begin ; Gem::Version.create "%{passenger_version}" ; rescue => e ; puts 1 ; exit ; end ; puts 0') + +%if %{broken_gem_version} + # Strip any non-numeric version part + %define gemversion %(echo '%{passenger_version}'|sed -e 's/\\.[^.]*[^.0-9]\\+[^.]*//g') +%else + %define gemversion %{passenger_version} +%endif + +# Invoke a shell to do a comparison, silly but it works across versions of RPM +%define gem_version_mismatch %([ '%{passenger_version}' != '%{gemversion}' ] && echo 1 || echo 0) + +%define gemdir %(%{ruby} -rubygems -e 'puts Gem::dir' 2>/dev/null) +%define geminstdir %{gemdir}/gems/%{gemname}-%{gemversion} + +%define perldir %(perl -MConfig -e 'print $Config{installvendorarch}') + +# This will cause a chicken/egg problem where the dir isn't present yet +#% define gemnativedir % (%{ruby} -I%{_builddir}/%{gemname}-%{passenger_version}/lib -rphusion_passenger/platform_info/binary_compatibility -e 'puts PhusionPassenger::PlatformInfo.ruby_extension_binary_compatibility_ids.join("-")') +# %define native_libs_release %{passenger_release}_% (%{ruby} -I%{_builddir}/%{gemname}-%{passenger_version}/lib -rphusion_passenger/platform_info/binary_compatibility -e 'puts PhusionPassenger::PlatformInfo.ruby_extension_binary_compatibility_ids[0,2].join("_")') +%define native_libs_release %{passenger_release}_%{ruby_version_patch} + +%{!?only_native_libs: %define only_native_libs 0} + +# Really wish they'd standardize this +%define sharedir %{?fedora:%{_datarootdir}}%{?!fedora:%{_datadir}} + +Summary: Easy and robust Ruby web application deployment +Name: rubygem-%{gemname} +Version: %{passenger_version} +Release: %{passenger_release} +Group: System Environment/Daemons +License: Modified BSD +URL: http://www.modrails.com/ +Source0: %{gemname}-%{passenger_version}.tar.gz +Source1: nginx-%{nginx_version}.tar.gz +Source100: apache-passenger.conf.in +Source101: nginx-passenger.conf.in +Source200: rubygem-passenger.te +# The most recent nginx RPM no longer includes this plugin. Remove it from the +# SRPM +# # Ignore everything after the ?, it's meant to trick rpmbuild into +# # finding the correct file +# Source300: http://github.com/gnosek/nginx-upstream-fair/tarball/master?/nginx-upstream-fair.tar.gz +Patch0: passenger-force-native.patch +BuildRoot: %{_tmppath}/%{name}-%{passenger_version}-%{passenger_release}-root-%(%{__id_u} -n) +Requires: rubygems +Requires: rubygem(rake) >= 0.8.1 +Requires: rubygem(fastthread) >= 1.0.1 +Requires: rubygem(daemon_controller) >= 0.2.5 +Requires: rubygem(file-tail) +Requires: rubygem(rack) +BuildRequires: ruby-devel +BuildRequires: httpd-devel +BuildRequires: rubygems +BuildRequires: rubygem(rake) >= 0.8.1 +BuildRequires: rubygem(rack) +BuildRequires: rubygem(fastthread) >= 1.0.1 +%if %{?fedora:1}%{?!fedora:0} +BuildRequires: libcurl-devel +BuildRequires: source-highlight +%else +BuildRequires: curl-devel +%endif +BuildRequires: doxygen +BuildRequires: asciidoc +# standaline build deps +%if %{?fedora:1}%{?!fedora:0} +BuildRequires: libev-devel +%endif +BuildRequires: rubygem(daemon_controller) >= 0.2.5 +BuildRequires: rubygem(file-tail) +# native build deps +%if %{?fedora:1}%{?!fedora:0} +BuildRequires: selinux-policy +%else +BuildRequires: selinux-policy-devel +%endif +# nginx build deps +BuildRequires: pcre-devel +BuildRequires: zlib-devel +BuildRequires: openssl-devel +%if %{?fedora:1}%{?!fedora:0} +BuildRequires: perl-devel +%else +BuildRequires: perl +%endif +BuildRequires: perl(ExtUtils::Embed) +BuildRequires: libxslt-devel +BuildRequires: GeoIP-devel +BuildRequires: gd-devel +# Can't have a noarch package with an arch'd subpackage +#BuildArch: noarch +Provides: rubygem(%{gemname}) = %{passenger_version} +Epoch: %{passenger_epoch} + +%description +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +%if %{gem_version_mismatch} +**NOTE: Because the default Gem::Version doesn't accept the correct +version, it is installed as %{gemversion} instead of %{passenger_version}. +%endif + +%if !%{only_native_libs} + +%package native +Summary: Phusion Passenger native extensions +Group: System Environment/Daemons +Requires: %{name} = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +%if %{?fedora:1}%{?!fedora:0} +Requires: libev +%endif +Requires(post): policycoreutils, initscripts +Requires(preun): policycoreutils, initscripts +Requires(postun): policycoreutils +Epoch: %{passenger_epoch} +%description native +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +This package contains the native code extensions for Apache & Nginx bindings + +%endif #! only_native_libs + +%package native-libs +Summary: Phusion Passenger native extensions +Group: System Environment/Daemons +Release: %{native_libs_release} +Epoch: %{passenger_epoch} +Requires: %{name}-native = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +Requires: ruby = %{ruby_version_patch} +Provides: rubygem-passenger-native-libs = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +%description native-libs +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +This package contains the native shared library for Apache & Nginx +bindings, built against ruby sources. It has been separated so that +installing a new ruby interpreter only necessitates rebuilding this +package. + +%if !%{only_native_libs} + +%package standalone +Summary: Standalone Phusion Passenger Server +Group: System Environment/Daemons +Requires: %{name} = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +%if %{?fedora:1}%{?!fedora:0} +Requires: libev +%endif +Epoch: %{passenger_epoch} +%description standalone +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +This package contains the standalone Passenger server + +%package -n mod_passenger +Summary: Apache Module for Phusion Passenger +Group: System Environment/Daemons +Requires: %{name}-native = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +#BuildArch: %_target_arch +Obsoletes: rubygem-passenger-apache +Epoch: %{passenger_epoch} +%description -n mod_passenger +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +This package contains the pluggable Apache server module for Passenger. + +%package -n nginx-passenger +Summary: nginx server with Phusion Passenger enabled +Group: System Environment/Daemons +Requires: %{name} = %{passenger_epoch}:%{passenger_version} +Version: %{nginx_version} +Release: %{passenger_version}_%{passenger_release} +Requires: %{name}-native-libs = %{passenger_epoch}:%{passenger_version}-%{passenger_release} +Requires: pcre +Requires: zlib +Requires: openssl +Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Requires: GeoIP +Requires: gd +Requires: nginx-alternatives +Epoch: %{passenger_epoch} +%description -n nginx-passenger +Phusion Passenger™ — a.k.a. mod_rails or mod_rack — makes deployment +of Ruby web applications, such as those built on the revolutionary +Ruby on Rails web framework, a breeze. It follows the usual Ruby on +Rails conventions, such as “Don’t-Repeat-Yourself”. + +This package includes an nginx server with Passenger compiled in. + +%endif # !only_native_libs + +%define perlfileckinner $SIG{__WARN__} = sub {die @_}; +%define perlfileck BEGIN { %perlfileckinner } ; +%define perlfileescd %(echo '%{perlfileck}' | sed -e 's/[$@]/\\\\&/g') + +%prep +%setup -q -n %{gemname}-%{passenger_version} -b 1 +# %setup -q -T -D -n nginx-%{nginx_version} -a 300 +# # Fix the CWD +# %setup -q -T -D -n %{gemname}-%{passenger_version} +%patch0 -p1 + +# Rather than hard-coding the path into the patch, change it here so +# that it's consistent with the %{ruby} macro, which might be defined on +# the command-line (4 %'s = 2) +perl -pi -e '%{perlfileck} s{%%%%GEM_INSTALL_DIR%%%%}{%{geminstdir}};s{%%%%APACHE_INSTALLED_MOD%%%%}{%{_libdir}/httpd/modules/mod_passenger.so}' lib/phusion_passenger.rb ext/common/ResourceLocator.h + +%if %{gem_version_mismatch} + %{warn: +*** +*** WARNING: Your Gem::Version crashes on '%{passenger_version},' +*** Falling back to use '%gemversion' internally +*** + +} + # Use sed rather than a patch, so it's more resilliant to version changes + sed -i -e "s/\(^[ \t]VERSION_STRING *= *'[0-9]\+\.[0-9]\+\.[0-9]\+\)[^']\+/\1/" lib/phusion_passenger.rb + sed -i -e 's/^\(#define PASSENGER_VERSION "[0-9]\+\.[0-9]\+\.[0-9]\+\)[^"]\+/\1/' ext/common/Constants.h +%endif + +# Fix the preferred version +perl -pi -e "s{(PREFERRED_NGINX_VERSION\s*=\s*(['\"]))[\d\.]+\2}{\${1}%{nginx_version}\$2}" lib/phusion_passenger.rb + +# RPM finds these in shebangs and assumes they're requirements. Clean them up here rather than in the install-dir. +find test -type f -print0 | xargs -0 perl -pi -e '%{perlfileck} s{#!(/opt/ruby.*|/usr/bin/ruby1.8)}{%{ruby}}g' + + +%build +%if %{?fedora:1}%{?!fedora:0} +export USE_VENDORED_LIBEV=false +# This isn't honored +# export CFLAGS='%optflags -I/usr/include/libev' +export LIBEV_CFLAGS='-I/usr/include/libev' +export LIBEV_LIBS='-lev' +%endif + +%if %{only_native_libs} + %{rake} native_support +%else + %{rake} package + %{rake} apache2 + + ### SELINUX + rm -rf selinux + mkdir selinux + cd selinux + cp %{SOURCE200} . + echo '%{geminstdir}/agents/((apache2|nginx)/)?Passenger.* system_u:object_r:httpd_exec_t:s0' > rubygem-passenger.fc + echo '%{_var}/log/passenger-analytics system_u:object_r:httpd_log_t:s0' >> rubygem-passenger.fc + touch rubygem-passenger.if + make -f %{sharedir}/selinux/devel/Makefile + cd .. + + ### NGINX + cd ../nginx-%{nginx_version} + + #export FAIRDIR=%{_builddir}/nginx-%{nginx_version}/gnosek-nginx-upstream-fair-* + # I'm not sure why this fails on RHEL but not Fedora. I guess GCC 4.4 is + # smarter about it than 4.1? It feels wrong to do this, but I don't see + # an easier way out. + %if %{?fedora:1}%{?!fedora:0} + %define nginx_ccopt %{optflags} + %else + %define nginx_ccopt %(echo "%{optflags}" | sed -e 's/SOURCE=2/& -Wno-unused/') + %endif + + ### Stolen [and hacked] from the nginx spec file + export DESTDIR=%{buildroot} + ./configure \ + --user=%{nginx_user} \ + --group=%{nginx_group} \ + --prefix=%{nginx_datadir} \ + --sbin-path=%{_sbindir}/nginx.passenger \ + --conf-path=%{nginx_confdir}/nginx.conf \ + --error-log-path=%{nginx_logdir}/error.log \ + --http-log-path=%{nginx_logdir}/access.log \ + --http-client-body-temp-path=%{nginx_home_tmp}/client_body \ + --http-proxy-temp-path=%{nginx_home_tmp}/proxy \ + --http-fastcgi-temp-path=%{nginx_home_tmp}/fastcgi \ + --http-uwsgi-temp-path=%{nginx_home_tmp}/uwsgi \ + --http-scgi-temp-path=%{nginx_home_tmp}/scgi \ + --pid-path=%{_localstatedir}/run/nginx.pid \ + --lock-path=%{_localstatedir}/lock/subsys/nginx \ + --with-http_ssl_module \ + --with-http_realip_module \ + --with-http_addition_module \ + --with-http_xslt_module \ + --with-http_image_filter_module \ + --with-http_geoip_module \ + --with-http_sub_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_gzip_static_module \ + --with-http_random_index_module \ + --with-http_secure_link_module \ + --with-http_degradation_module \ + --with-http_stub_status_module \ + --with-http_perl_module \ + --with-mail \ + --with-file-aio \ + --with-mail_ssl_module \ + --with-ipv6 \ + --with-cc-opt="%{nginx_ccopt} $(pcre-config --cflags)" \ + --with-ld-opt="-Wl,-E" # so the perl module finds its symbols + + # THIS is ugly (yet now greatly simplified). It corrects the + # check-buildroot error on the string saved for 'nginx -V' + # + # In any case, fix it correctly later + perl -pi -e '%{perlfileck} s<%{buildroot}><>g;s<%{_builddir}><%%{_builddir}>g;' objs/ngx_auto_config.h + + # Also do it for passenger-standalone (and I thought the above was ugly) + perl -pi.nohack -0777 -e 's!(^\s*run_command_with_throbber.*"Preparing Nginx...".*\n(\s*))(yield.*?\n)!${2}\@\@hack_success = false\n$1yield_result = $3${2}abort "nginx-hack failed" unless \@\@hack_success || system(*(%w{perl -pi -e} + ["%{perlfileescd} s<%{buildroot}><>g;s<%{_builddir}><%%{_builddir}>g;", "objs/ngx_auto_config.h"]))\n${2}\# Why is this running many times?\n${2}\@\@hack_success = true\n${2}yield_result\n!im' %{_builddir}/passenger-%{passenger_version}/lib/phusion_passenger/standalone/runtime_installer.rb + + make %{?_smp_mflags} + + cd - +%endif # !only_native_libs + +%install +%if %{?fedora:1}%{?!fedora:0} +export USE_VENDORED_LIBEV=false +# This isn't honored +# export CFLAGS='%optflags -I/usr/include/libev' +export LIBEV_CFLAGS='-I/usr/include/libev' +export LIBEV_LIBS='-lev' +%endif + +rm -rf %{buildroot} +mkdir -p %{buildroot}%{gemdir} + +%if !%{only_native_libs} +%{gem} install --local --install-dir %{buildroot}%{gemdir} \ + --force --rdoc pkg/%{gemname}-%{gemversion}.gem +mkdir -p %{buildroot}/%{_bindir} +mv %{buildroot}%{gemdir}/bin/* %{buildroot}/%{_bindir} +rmdir %{buildroot}%{gemdir}/bin +# Nothing there +# find %{buildroot}%{geminstdir}/bin -type f | xargs chmod a+x + +mkdir -p %{buildroot}/%{_libdir}/httpd/modules +install -m 0644 ext/apache2/mod_passenger.so %{buildroot}/%{_libdir}/httpd/modules + +mkdir -p %{buildroot}/%{nginx_datadir} +mkdir -p %{buildroot}/%{nginx_datadir} +mkdir -p %{buildroot}/%{nginx_confdir} +mkdir -p %{buildroot}/%{nginx_logdir} +mkdir -p %{buildroot}/%{httpd_confdir} +mkdir -p %{buildroot}/%{_var}/log/passenger-analytics + +# I should probably figure out how to get these into the gem +cp -ra agents %{buildroot}/%{geminstdir} + +# PASSENGER STANDALONE (this is going to recompile nginx) +./bin/passenger package-runtime --nginx-version %{nginx_version} --nginx-tarball %{SOURCE1} %{buildroot}/%{_var}/lib/passenger-standalone +# Now unpack the tarballs it just created +# It's 2am, revisit this insanity in the light of morning +standalone_dir=$(bash -c 'ls -d $1 | tail -1' -- %{buildroot}/%{_var}/lib/passenger-standalone/%{passenger_version}-*) +native_dir=%{buildroot}/%{_var}/lib/passenger-standalone/natively-packaged + +mkdir -p $standalone_dir/support +mkdir -p $standalone_dir/nginx-%{nginx_version} +tar -zx -C $standalone_dir/nginx-%{nginx_version} -f $standalone_dir/nginx-%{nginx_version}.tar.gz +tar -zx -C $standalone_dir/support -f $standalone_dir/support.tar.gz + +# Hong Li says the binaries are relocatable, so we don't have to jump +# through hoops to change directories. Just move it. +mv $standalone_dir $native_dir +mv $native_dir/support/ext/ruby/*-linux $native_dir/support/ext/ruby/native + +# Unhack +mv lib/phusion_passenger/standalone/runtime_installer.rb.nohack lib/phusion_passenger/standalone/runtime_installer.rb +install -m 0644 lib/phusion_passenger/standalone/runtime_installer.rb $native_dir/support/lib/phusion_passenger/standalone/runtime_installer.rb + +# SELINUX +install -p -m 644 -D selinux/%{name}.pp %{buildroot}%{sharedir}/selinux/packages/%{name}/%{name}.pp + +# NGINX +cd ../nginx-%{nginx_version} +make install DESTDIR=%{buildroot} INSTALLDIRS=vendor +cd - + +%endif #!only_native_libs + +##### NATIVE LIBS INSTALL +mkdir -p %{buildroot}/%{geminstdir}/ext/ruby/native +cp -ra ext/ruby/*-linux/* %{buildroot}/%{geminstdir}/ext/ruby/native + +%if !%{only_native_libs} +#### Clean up everything we don't care about +rm -rf %{buildroot}/usr/share/nginx %{buildroot}/%{nginx_confdir} +# # Assume the old version is good enough. Probably not wise. +# rm -rf %{buildroot}%{perldir} %{buildroot}%{_mandir}/man3/nginx.3pm* +rm -f %{buildroot}%{perldir}/{auto/nginx/.packlist,perllocal.pod} +# RHEL distinguishes these dirs +rm -f %{buildroot}%(perl -MConfig -e 'print $Config{installarchlib}')/perllocal.pod +mv %{buildroot}%{perldir}/auto/nginx/nginx{,_passenger}.bs +mv %{buildroot}%{perldir}/auto/nginx/nginx{,_passenger}.so +mv %{buildroot}%{perldir}/nginx{,_passenger}.pm +mv %{buildroot}%{_mandir}/man3/nginx.3pm{,_passenger} + +install -p -d -m 0755 %{buildroot}/%{nginx_confdir}/conf.d +#install -m 0644 %{SOURCE100} %{buildroot}/%{httpd_confdir}/passenger.conf +#install -m 0644 %{SOURCE101} %{buildroot}/%{nginx_confdir}/conf.d/passenger.conf +perl -pe 's{%%ROOT}{%geminstdir}g;s{%%RUBY}{%ruby}g' %{SOURCE100} > %{buildroot}/%{httpd_confdir}/passenger.conf +perl -pe 's{%%ROOT}{%geminstdir}g;s{%%RUBY}{%ruby}g' %{SOURCE101} > %{buildroot}/%{nginx_confdir}/conf.d/passenger.conf + +# CLEANUP +rm -f $native_dir/support/ext/ruby/native/mkmf.log +# Reversed logic from most other tests +%if %{?fedora:0}%{?!fedora:1} +rm -f $native_dir/support/ext/libev/config.log +%endif + +# REMOVE THIS TO FORCE 'native-packaged' (it's still in doc) +rm %{buildroot}/%{geminstdir}/DEVELOPERS.TXT + +# This is still needed +%if %{?fedora:1}%{?!fedora:0} + %define libevmunge %nil +%else + %define libevmunge $native_dir/support/ext/libev/config.status $native_dir/support/ext/libev/Makefile +%endif + +perl -pi -e '%perlfileck s{%buildroot}{}g;s<%{_builddir}><%%{_builddir}>g' \ + $native_dir/support/ext/ruby/native/Makefile %{libevmunge} + +%define base_files base-package-files + +### BUILD FILE LIST (To remove files from the base package that will be installed by subpackages) +cat < %{base_files} +%defattr(-, root, root, -) +%doc %{gemdir}/doc/%{gemname}-%{gemversion} +%doc README +%doc DEVELOPERS.TXT +%{_bindir}/passenger-install-apache2-module +%{_bindir}/passenger-install-nginx-module +%{_bindir}/passenger-config +%{_bindir}/passenger-status +%{_bindir}/passenger-memory-stats +%{_bindir}/passenger-make-enterprisey +%{gemdir}/cache/%{gemname}-%{gemversion}.gem +%{gemdir}/specifications/%{gemname}-%{gemversion}.gemspec +EOF + +# This feels wrong (reordering arch & os) but if it helps.... +# ...Going one step further and also stripping all the installed *.o files +# Move the file find here to catch the byte-compiled Python files +%define __spec_install_post \ + %{?__debug_package:%{__debug_install_post}} \ + %{__os_install_post} \ + find $native_dir -name \*.o -o -name \*.so | xargs strip ; \ + find %{buildroot}/%{geminstdir} \\( -type d \\( -name native -o -name agents \\) \\) -prune -o \\( -type f -print \\) | perl -pe 's{^%{buildroot}}{};s{^//}{/};s/([?|*'\\''\"])/\\\\$1/g;s{(^|\\n$)}{\"$&}g' >> %{base_files} \ + %{__arch_install_post} + +%post -n nginx-passenger +if [ $1 == 1 ]; then + /usr/sbin/alternatives --install /usr/sbin/nginx nginx \ + /usr/sbin/nginx.passenger 50 \ + --slave %{perldir}/auto/nginx/nginx.so nginx.so \ + %{perldir}/auto/nginx/nginx_passenger.so \ + --slave %{perldir}/auto/nginx/nginx.bs nginx.bs \ + %{perldir}/auto/nginx/nginx_passenger.bs \ + --slave %{perldir}/nginx.pm nginx.pm %{perldir}/nginx_passenger.pm \ + --slave %{_mandir}/man3/nginx.3pm.gz nginx.man \ + %{_mandir}/man3/nginx_passenger.3pm.gz +fi + +%postun -n nginx-passenger +if [ $1 == 0 ]; then + /usr/sbin/alternatives --remove nginx /usr/sbin/nginx.passenger +fi + +%post native +if [ "$1" -le "1" ] ; then # First install +semodule -i %{sharedir}/selinux/packages/%{name}/%{name}.pp 2>/dev/null || : +fixfiles -R %{name} restore +fixfiles -R %{name}-native restore +fi + +%preun native +if [ "$1" -lt "1" ] ; then # Final removal +semodule -r rubygem_%{gemname} 2>/dev/null || : +fixfiles -R %{name} restore +fixfiles -R %{name}-native restore +fi + +%postun native +if [ "$1" -ge "1" ] ; then # Upgrade +semodule -i %{sharedir}/selinux/packages/%{name}/%{name}.pp 2>/dev/null || : +fi +%endif # !only_native_libs + +%clean +rm -rf %{buildroot} + +%if !%{only_native_libs} +%files -f %{base_files} + +%files native +%{geminstdir}/agents +%{sharedir}/selinux/packages/%{name}/%{name}.pp +%{_var}/log/passenger-analytics + +%files standalone +%doc doc/Users\ guide\ Standalone.html +%doc doc/Users\ guide\ Standalone.txt +%{_bindir}/passenger +%{_var}/lib/passenger-standalone/natively-packaged/ + +%files -n mod_passenger +%doc doc/Users\ guide\ Apache.html +%doc doc/Users\ guide\ Apache.txt +%{_libdir}/httpd/modules/mod_passenger.so +%config %{httpd_confdir}/passenger.conf + +%files -n nginx-passenger +%doc doc/Users\ guide\ Nginx.html +%doc doc/Users\ guide\ Nginx.txt +%config %{nginx_confdir}/conf.d/passenger.conf +/usr/sbin/nginx.passenger +%{perldir}/auto/nginx/nginx* +%{perldir}/nginx* +%{_mandir}/man3/nginx* +%endif # !only_native_libs + +%files native-libs +# %{geminstdir}/ext/ruby/%{gemnativedir} +%{geminstdir}/ext/ruby/native + + +%changelog +* Thu Dec 2 2010 Erik Ogan - 3.0.1-3 +- Stop double-packaging files from -native & -native-libs in the base package + +* Tue Nov 30 2010 Erik Ogan - 3.0.1-2 +- Remove (most of) the kludges to remove %%{builddir} from installed files. +- Blessed natively-packaged patch from Hong Li +- Migration to the more static directory structure + +* Mon Nov 29 2010 Erik Ogan - 3.0.1-1 +- Integration into passenger source +- Bump to 3.0.1 + +* Mon Nov 15 2010 Erik Ogan - 3.0.0-11 +- Fix passenger-standalone + +* Fri Nov 12 2010 Erik Ogan - 3.0.0-10 +- Bump nginx to version 0.8.53 and build it by hand based on the newer + nginx specfile + +* Sun Nov 7 2010 Erik Ogan - 3.0.0-9 +- Add passenger-analytics directory, so the server doesn't try to create + it. (SELinux violation) + +* Sun Oct 31 2010 Erik Ogan - 3.0.0-8 +- Fix embedded Perl module + +* Fri Oct 29 2010 Erik Ogan - 3.0.0-7 +- Add back all the missing directives from nginx.spec (Perl is + untested and may be broken) + +* Fri Oct 29 2010 Erik Ogan - 3.0.0-6 +- Add upstream-fair load-balancer back to nginx +- Add the original CFLAGS back to nginx (with -Wno-unused kludge for RHEL5) + +* Sat Oct 23 2010 Erik Ogan - 3.0.0-5 +- RHEL/CentOS Ruby is too old to support RUBY_PATCHLEVEL + +* Sat Oct 23 2010 Erik Ogan - 3.0.0-4 +- --define 'only_native_libs 1' to rebuild native_support.so for a + different ruby engine. +- make sure native-libs release includes passenger release and ruby patch level +- remove the macros that rely on %%{_builddir} already being unpacked + +* Fri Oct 22 2010 Erik Ogan - 3.0.0-3 +- Break the passenger_native_support.so into its own package + +* Thu Oct 21 2010 Erik Ogan - 3.0.0-2 +- rename rubygem-passenger-apache => mod_passenger + +* Thu Oct 21 2010 Erik Ogan - 3.0.0-1 +- Version bump to 3.0.0 + +* Wed Oct 18 2010 Erik Ogan - 3.0.0.pre4-2 +- use nginx-alternatives + +* Sun Oct 17 2010 Erik Ogan - 3.0.0.pre4-1 +- Nginx suport + +* Mon Oct 11 2010 Erik Ogan - 3.0.0.pre4-0 +- Test for Gem::Version issues with the version and work around it. +- Initial Spec File diff --git a/rpm/patches/passenger-force-native.patch b/rpm/patches/passenger-force-native.patch new file mode 100644 index 0000000000..f6b970a4f0 --- /dev/null +++ b/rpm/patches/passenger-force-native.patch @@ -0,0 +1,60 @@ +diff --git a/lib/phusion_passenger.rb b/lib/phusion_passenger.rb +index 6019499..2728455 100644 +--- a/lib/phusion_passenger.rb ++++ b/lib/phusion_passenger.rb +@@ -88,10 +88,11 @@ module PhusionPassenger + # Not available when natively packaged. + NATIVE_SUPPORT_DIR = File.join(SOURCE_ROOT, "ext", "ruby") + else +- SOURCE_ROOT = NATIVELY_PACKAGED_SOURCE_ROOT +- DOCDIR = NATIVELY_PACKAGED_DOCDIR +- RESOURCES_DIR = NATIVELY_PACKAGED_RESOURCES_DIR +- APACHE2_MODULE = NATIVELY_PACKAGED_APACHE2_MODULE ++ SOURCE_ROOT = '%%GEM_INSTALL_DIR%%' ++ DOCDIR = "#{SOURCE_ROOT}/doc" ++ RESOURCES_DIR = "#{SOURCE_ROOT}/resources" ++ APACHE2_MODULE = '%%APACHE_INSTALLED_MOD%%' ++ NATIVE_SUPPORT_DIR = "#{SOURCE_ROOT}/ext/ruby" + end + + +diff --git a/lib/phusion_passenger/native_support.rb b/lib/phusion_passenger/native_support.rb +index f1edf1c..4312d01 100644 +--- a/lib/phusion_passenger/native_support.rb ++++ b/lib/phusion_passenger/native_support.rb +@@ -38,10 +38,7 @@ class NativeSupportLoader + + private + def archdir +- @archdir ||= begin +- require 'phusion_passenger/platform_info/binary_compatibility' +- PlatformInfo.ruby_extension_binary_compatibility_ids.join("-") +- end ++ @archdir ||= 'native' + end + + def libext +diff --git a/ext/common/ResourceLocator.h b/ext/common/ResourceLocator.h +index 5f65804..e585d1a 100644 +--- a/ext/common/ResourceLocator.h ++++ b/ext/common/ResourceLocator.h +@@ -64,13 +64,13 @@ public: + !fileExists(root + "/DEVELOPERS.TXT"); + + if (nativelyPackaged) { +- agentsDir = "/usr/lib/phusion-passenger/agents"; +- helperScriptsDir = "/usr/share/phusion-passenger/helper-scripts"; +- resourcesDir = "/usr/share/phusion-passenger"; +- docDir = "/usr/share/doc/phusion-passenger"; ++ agentsDir = "%%GEM_INSTALL_DIR%%/agents"; ++ helperScriptsDir = "%%GEM_INSTALL_DIR%%/helper-scripts"; ++ resourcesDir = "%%GEM_INSTALL_DIR%%"; ++ docDir = "%%GEM_INSTALL_DIR%%/doc"; + rubyLibDir = ""; +- compilableSourceDir = "/usr/share/phusion-passenger/compilable-source"; +- apache2Module = "/usr/lib/apache2/modules/mod_passenger.so"; ++ compilableSourceDir = "%%GEM_INSTALL_DIR%%"; ++ apache2Module = "%%APACHE_INSTALLED_MOD%%"; + } else { + agentsDir = root + "/agents"; + helperScriptsDir = root + "/helper-scripts"; diff --git a/rpm/release/RPM-GPG-KEY-stealthymonkeys b/rpm/release/RPM-GPG-KEY-stealthymonkeys new file mode 100644 index 0000000000..0bdcc23bcf --- /dev/null +++ b/rpm/release/RPM-GPG-KEY-stealthymonkeys @@ -0,0 +1,33 @@ +GPG Signing key for rpms@stealthymonkeys.com + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.10 (Darwin) + +mQENBEzHVv8BCAC1nWHsSRdv7bsZYFJLfwXPMxm5oR3OD/lldIKvJXcz6G/STaB8 +FL0vshaaHd2MR8vvn70hByLEjT6YK0e5BvjTspozRFW+k/Sqlj6AjNpLPPaig4GP +rlaILL42ZrzXmC8UoxBrxJr4mrQn7nKVJDV5I1pPeZYK3kJD5yOwNDW+pGcRSeyw +idTfBmazzBlLUKX/wFp4X/KNE8NJqaR5HyFPakcTNMIq6hl0pi+zfzEpulykmrJX +7jCm1meiVyR1avtfpNRAGQWIJEhqXxBliyUFA3B+AorxKWe3Km7+499w8FdilBHB +JrJU+FgUBYQ24h2HLD81dgx4NxSIVnFr02TLABEBAAG0NkVyaWsgT2dhbiAoUlBN +IHNpZ25pbmcga2V5KSA8cnBtc0BzdGVhbHRoeW1vbmtleXMuY29tPokBPgQTAQgA +KAUCTMdW/wIbAwsLCQgHCg0MCwMCBAYVCgkICwMFFgIDAQACHgECF4AACgkQPo92 +smVzqwlSnAf9G875hB1bE3FYf22YNMIkxSCTTnY/Yw/8c76xJQVEB2+Mx9V7wmiE +75pIhL/GrqEpKMtCGK/pYy6Cvd1+VXRX40pqVHZCdkTGHWjtxTL0RunNGEueDXBb +g1Qs810fIKM/7iqpc6bOx49B8F/jTzk8w8/iK3OMHjd4wLJf6CKmdC25O44KX3Ii +QpGCzm1sV505TT4MFvO13VDg7XK/Xq+3q5w094ryDXidL3quGJTfvOWaE5/g5Bgh +ISyO3W3Ak9MfRCzOK5eWao+ZZ2eow0tai5EEhuzakvX7dRNlIhvabjucar585lLP +uP5h06nDSMXzD7PMnAF65g70gMungXLITLkBDQRMx1b/AQgArX1+UzcPYUbXj6nT +H+iiRlegG/jPwkYRBwKZkG8Bme4GlTsR6uLDZR4K8zg9DckgbtFjBBpZ1WSWpqOD +r7suhVAiL/Qev+Gk3H2Q12/4s6OBY0z8N//2UAa/h4R9DIMdP3jdhgP0XK0hmCiH +z6ZRp3e0t4fZqP4wTfktSGaCzWZL/cG1d/4yclXKHQ8CnLcGf2xE2VhAFObajcHX +sY2XWfPr7r+4PYjJ+S1lTMT4R+do8gxXDXq5ixN1TuAH5yGB2G2YzVNfFa5bXZFl +cFnGXNJn3h3tHf9yT9D+yAq37lkqaZ8oByxmNvNi9HYQr8zo+9ztOOFcGgFTlPUT +MLsk8QARAQABiQEfBBgBCAAJBQJMx1b/AhsMAAoJED6PdrJlc6sJIO0IAJgr2IrG +WVMPRaU3vVSSC6gkdvn6bIhQuHqdTpw1HFyw/3nRGiPrAH0jmUSojPmTifTpx2Yv +lsf5vkZjOUvyTB2yDU/pTO8nAarDDyVZv/KWRMruboxFUbqryN/hN747akfZGqt5 +0aAAfLukQQQ6TmDMEpy3AQKJITZnDylMw7J84UgGcL+iNeU7ogbW+H618gIhl0Sy +eyZLwwC4sEDXEiRfS0o4JJnOjVshOfNE4tDDjVuLSQ3LW2hwaYJELBJls/ns44Nw +BQTzTiSbDhOiSTGZr/IwXyRgBhbGfx9AvvPkqOIrKPY6kB+khAYiDdPunbSSg2I3 +BSuPuk9DJp+2Gsc= +=pVe5 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/rpm/release/build-release.sh b/rpm/release/build-release.sh new file mode 100644 index 0000000000..9b5884d245 --- /dev/null +++ b/rpm/release/build-release.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -x +set -e + +reldir=`dirname $0` +stage=./stage-release +rm -rf $stage +mkdir -p $stage/{SRPMS,SOURCES} +ln -s `readlink -f $reldir/mirrors` $stage/SOURCES/mirrors-passenger +ln -s `readlink -f $reldir/RPM-GPG-KEY-stealthymonkeys` $stage/SOURCES + +rpmbuild-md5 --define "_topdir $stage" --define 'dist %nil' -bs passenger-release.spec +rm -rf $stage/{SOURCES,BUILD*,RPMS,SPECS} +srpm=`ls -1t $stage/SRPMS/*rpm | head -1` + +for ver in {epel-5,fedora-{13,14}} +do + echo --------- $ver + xdir=$stage/`echo $ver | tr '-' '/'`/x86_64 + idir=`echo $xdir | sed -e 's/x86_64/i386/'` + mock -r passenger-$ver-x86_64 $srpm + mkdir -p $xdir $idir + cp /var/lib/mock/passenger-$ver-x86_64/result/*noarch.rpm $xdir + cp /var/lib/mock/passenger-$ver-x86_64/result/*noarch.rpm $idir + cd $xdir/.. + short=`ls -1t x86_64/*rpm | head -1 | perl -pe 's{.*/(.*)-[^-]+-[^-]+(.noarch.rpm)}{\1\2}'` + ln -s x86_64/*rpm $short + cd - +done + +mv $stage/epel $stage/rhel +# Don't resign symlinks +# -- arguably this should be done once for each file, since they're copied +rpm --addsign `find $stage -type f` diff --git a/rpm/release/build.rb b/rpm/release/build.rb new file mode 100755 index 0000000000..10c4ad5661 --- /dev/null +++ b/rpm/release/build.rb @@ -0,0 +1,259 @@ +#!/usr/bin/env ruby + +require 'fileutils' +require 'ftools' +require 'optparse' + +$:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib') + +require 'phusion_passenger' + +CFGLIMIT=%w{fedora-{13,14} epel-5} + +stage_dir='./stage' + +mock_base_dir = '/var/lib/mock' +mock_repo_dir = "#{mock_base_dir}/passenger-build-repo" +mock_etc_dir='/etc/mock' +#mock_etc_dir='/tmp/mock' + +# If rpmbuild-md5 is installed, use it for the SRPM, so EPEL machines can read it. +rpmbuild = '/usr/bin/rpmbuild' + (File.exist?('/usr/bin/rpmbuild-md5') ? '-md5' : '') +rpmtopdir = `rpm -E '%_topdir'`.chomp +rpmarch = `rpm -E '%_arch'`.chomp + +@verbosity = 0 + +@can_build = { + 'i386' => %w{i586 i686}, + 'i686' => %w{i586 i686}, + 'ppc' => %w{}, + 'ppc64' => %w{ppc}, + 's390x' => %w{}, + 'sparc' => %w{}, + 'sparc64' => %w{sparc}, + 'x86_64' => %w{i386 i586 i686}, +} + +#@can_build.keys.each {|k| @can_build[k].push k} +@can_build = @can_build[rpmarch.to_s == '' ? 'x86_64' : rpmarch] +@can_build.push rpmarch + +bindir=File.dirname(File.expand_path __FILE__) + +configs = Dir["#{mock_etc_dir}/{#{CFGLIMIT.join ','}}*"].map {|f| f.gsub(%r{.*/([^.]*).cfg}, '\1')} + +def limit_configs(configs, limits) + tree = configs.inject({}) do |m,c| + (distro,version,arch) = c.split /-/ + next m unless @can_build.include?(arch) + [ + # Rather than construct this list programatically, just spell it out + '', + distro, + "#{distro}-#{version}", + "#{distro}-#{version}-#{arch}", + "#{distro}--#{arch}", + "--#{arch}", + # doubtful these will be used, but for completeness + "-#{version}", + "-#{version}-#{arch}", + ].each do |pattern| + unless m[pattern] + m[pattern] = [] + end + m[pattern].push c + end + m + end + tree.default = [] + # Special case for no arguments + limits = [nil] if limits.empty? + # By splitting and rejoining we normalize the distro--, etc. cases. + return limits.map do |l| + parts = l.to_s.split(/-/).map {|v| v == '*' ? nil : v} + if parts[2] && !@can_build.include?(parts[2]) + abort "ERROR: Cannot build '#{parts[2]}' packages on '#{rpmarch}'" + end + tree[parts.join '-'] + # All of a sudden the i386 mock builds are failing IFF they're run after an + # x86_64 build. I don't understand how separate processes in separate + # (chroot'ed!) environments can pollute each other, and I find it rather + # troubling. But for now the workaround is to sort by arch & do i386 first. + end.flatten.sort do |a,b| + ap = a.split(/-/) + ap.unshift(ap.pop) + bp = b.split(/-/) + bp.unshift(bp.pop) + ap <=> bp + end +end + +def noisy_system(*args) + puts args.join ' ' if @verbosity > 0 + system(*args) +end + + +############################################################################ +options = {} +OptionParser.new do |opts| + opts.banner = "Usage: #{$0} [options] [distro-version-arch] [distro-version] [distro--arch] [*--arch]" + + opts.on("-v", "--[no-]verbose", "Run verbosely. Add more -v's to increase @verbosity") do |v| + @verbosity += v ? 1 : -1 + end + + opts.on('-s', '--single', 'Only build a single distro-rev-arch set (for this machine)') do |v| + options[:single] = true + end + + # Do these with options, because the order matters + opts.on('-b', '--mock-base-dir DIR', "Mock's base directory. Default: #{mock_base_dir}") do |v| + #mock_repo_dir = v + options[:mock_base_dir] = v + end + + opts.on('-r', '--mock-repo-dir DIR', "Directory for special mock yum repository. Default: #{mock_repo_dir}") do |v| + #mock_repo_dir = v + options[:mock_repo_dir] = v + end + + opts.on("-c", "--mock-config-dir DIR", "Directory for mock configuration. Default: #{mock_etc_dir}") do |v| + if File.directory?(v) + mock_etc_dir=v + else + abort "No such directory: #{v}" + end + end + + opts.on('-d', '--stage-dir DIR', "Staging directory. Default: #{stage_dir}") do |v| + stage_dir = v + end + + opts.on('-e', '--extra-packages DIR', "Directory for extra packages to install.") do |v| + #mock_repo_dir = v + options[:extra_packages] = v + end + + opts.on_tail("-h", "--help", "Show this message") do + puts opts + exit + end +end.parse! + +if options.key?(:mock_base_dir) || options.key?(:mock_repo_dir) + if options.key?(:mock_base_dir) + mock_base_dir = options[:mock_base_dir] + mock_repo_dir = "#{mock_base_dir}/passenger-build-repo" + end + if options.key?(:mock_repo_dir) + mock_repo_dir = options[:mock_repo_dir] + unless mock_repo_dir[0] == '/'[0] + mock_repo_dir = "#{mock_base_dir}/#{mock_repo_dir}" + end + end +end + +limit = ARGV +if options.key?(:single) + # This can probably be simplified + limit = [`rpm --queryformat '%{name}\t%{version}' -qf /etc/redhat-release`.sub(/(\w+)-release\t(\d+)/,'\1-\2').sub(/^(rhel|centos|sl)-/,'epel-') + "-#{`rpm -E '%{_host_cpu}'`.strip}"] +end + +configs = limit_configs(configs, limit) + +if configs.empty? + abort "Can't find a set of configs for '#{ARGV[0]}' (hint try 'fedora' or 'fedora-14' or even 'fedora-14-x86_64')" +end + +puts "BUILD:\n " + configs.join("\n ") if @verbosity >= 2 + +# Too much of what follows expects this. Revisit it later. +Dir.chdir(File.join(File.dirname(__FILE__), '..')) + +FileUtils.rm_rf(stage_dir, :verbose => @verbosity > 0) +FileUtils.mkdir_p(stage_dir, :verbose => @verbosity > 0) + +ENV['BUILD_VERBOSITY'] = @verbosity.to_s + +# Check the ages of the configs for validity +mtime = File.mtime("#{bindir}/mocksetup.sh") +if configs.any? {|c| mtime > File.mtime("#{mock_etc_dir}/passenger-#{c}.cfg") rescue true } + unless noisy_system("#{bindir}/mocksetup.sh", mock_repo_dir, mock_etc_dir) + abort < @verbosity > 0) + +# Force the default versions in the spec file to be the ones in the source so a given SRPM doesn't need a --define to set versions. +specdir="/tmp/#{`whoami`.strip}-specfile-#{Process.pid}" +FileUtils.rm_rf(specdir, :verbose => @verbosity > 0) +begin + FileUtils.mkdir_p(specdir, :verbose => @verbosity > 0) + FileUtils.cp('passenger.spec', specdir, :verbose => @verbosity > 0) + # + must be escaped, but * doesn't? And people wonder why I hate sed. + abort "Can't edit specfile" unless noisy_system('sed', '-i', + '-e', "s/^\\(\\([[:space:]]*\\)%define[[:space:]]\\+passenger_version[[:space:]]\\)\\+[0-9.]\\+.*/\\2# From Passenger Source\\n\\1#{PhusionPassenger::VERSION_STRING}/", + '-e', "s/^\\(\\([[:space:]]*\\)%define[[:space:]]\\+nginx_version[[:space:]]\\)\\+[0-9.]\\+.*/\\2# From Passenger Source\\n\\1#{PhusionPassenger::PREFERRED_NGINX_VERSION}/", + "#{specdir}/passenger.spec") + # No dist for SRPM + unless noisy_system(rpmbuild, *((@verbosity > 0 ? [] : %w{--quiet}) + ['--define', 'dist %nil', '-bs', "#{specdir}/passenger.spec"])) + abort "No SRPM was built. See above for the error" + end +ensure + FileUtils.rm_rf(specdir, :verbose => @verbosity > 0) +end + +# I really wish there was a way to query rpmbuild for this via the spec file, +# but rpmbuild --eval doesn't seem to work +srpm="rubygem-passenger-#{PhusionPassenger::VERSION_STRING}-#{`grep '%define passenger_release' passenger.spec | awk '{print $3}'`.strip}.src.rpm".sub(/%\{[^}]+\}/, '') + +FileUtils.mkdir_p(stage_dir + '/SRPMS', :verbose => @verbosity > 0) + +FileUtils.cp("#{rpmtopdir}/SRPMS/#{srpm}", "#{stage_dir}/SRPMS", +:verbose => @verbosity > 0) + +mockvolume = @verbosity >= 2 ? %w{-v} : @verbosity < 0 ? %w{-q} : [] + +configs.each do |cfg| + puts "---------------------- Building #{cfg}" if @verbosity >= 0 + pcfg = 'passenger-' + cfg + + idir = './pkg' + + unless options.key?(:single) + idir = File.join stage_dir, cfg.split(/-/) + end + + # Move *mockvolume to the end, since it causes Ruby to cry in the middle + # Alt sol'n: *(foo + ['bar'] ) + unless noisy_system('mock', '-r', pcfg, "#{stage_dir}/SRPMS/#{srpm}", *mockvolume) + abort "Mock failed. See above for details" + end + FileUtils.mkdir_p(idir, :verbose => @verbosity > 0) + FileUtils.cp(Dir["#{mock_base_dir}/#{pcfg}/result/*.rpm"], + idir, :verbose => @verbosity > 0) + if options.key?(:extra_packages) + FileUtils.cp(Dir["#{options[:extra_packages]}/*.rpm"], idir, :verbose => @verbosity > 0) + end + FileUtils.rm_f(Dir["#{idir}/*.src.rpm"], :verbose => @verbosity > 1) +end + +unless options.key?(:single) + if File.directory?("#{stage_dir}/epel") + FileUtils.mv "#{stage_dir}/epel", "#{stage_dir}/rhel", :verbose => @verbosity > 0 + end +end + +unless `rpm -E '%{?_signature}'`.strip == '' + noisy_system('rpm', '--addsign', *Dir["#{options.key?(:single) ? stage_dir : 'pkg'}/**/*.rpm"]) +end diff --git a/rpm/release/create-mirrors.sh b/rpm/release/create-mirrors.sh new file mode 100644 index 0000000000..f743607bd1 --- /dev/null +++ b/rpm/release/create-mirrors.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +dir=`dirname $0` +mirrors="$dir/mirrors" + +echo "D: $dir" + +for path in fedora rhel +do + for mirror in $(cat $mirrors); do + if [ "${mirror:(-1)}" != "/" ] ; then + mirror="$mirror/" + fi + echo "$mirror$path/\$releasever/\$basearch/" + done > $path/mirrors +done diff --git a/rpm/release/mirrors b/rpm/release/mirrors new file mode 100644 index 0000000000..9629eda0a6 --- /dev/null +++ b/rpm/release/mirrors @@ -0,0 +1 @@ +http://passenger.stealthymonkeys.com/ diff --git a/rpm/release/mock-repo/comps.xml b/rpm/release/mock-repo/comps.xml new file mode 100644 index 0000000000..d447feaa24 --- /dev/null +++ b/rpm/release/mock-repo/comps.xml @@ -0,0 +1,21 @@ + + + + + + build-passenger + BuildPassenger + true + Build Requirements for Passenger + true + + rubygems + pcre-devel + perl-ExtUtils-Embed + + rubygem-file-tail + rubygem-daemon_controller + rubygem-rake + + + diff --git a/rpm/release/mock-repo/rubygem-daemon_controller-0.2.5-1.noarch.rpm b/rpm/release/mock-repo/rubygem-daemon_controller-0.2.5-1.noarch.rpm new file mode 100644 index 0000000000..0115568daa Binary files /dev/null and b/rpm/release/mock-repo/rubygem-daemon_controller-0.2.5-1.noarch.rpm differ diff --git a/rpm/release/mock-repo/rubygem-file-tail-1.0.5-1.noarch.rpm b/rpm/release/mock-repo/rubygem-file-tail-1.0.5-1.noarch.rpm new file mode 100644 index 0000000000..b80f924858 Binary files /dev/null and b/rpm/release/mock-repo/rubygem-file-tail-1.0.5-1.noarch.rpm differ diff --git a/rpm/release/mock-repo/rubygem-spruz-0.2.2-1.noarch.rpm b/rpm/release/mock-repo/rubygem-spruz-0.2.2-1.noarch.rpm new file mode 100644 index 0000000000..22fc274a89 Binary files /dev/null and b/rpm/release/mock-repo/rubygem-spruz-0.2.2-1.noarch.rpm differ diff --git a/rpm/release/mocksetup-first.sh b/rpm/release/mocksetup-first.sh new file mode 100755 index 0000000000..3a5cafdeb6 --- /dev/null +++ b/rpm/release/mocksetup-first.sh @@ -0,0 +1,98 @@ +#!/bin/bash -e +# Checks whether all the prerequities for the package:rpm task are available. + +declare -a required_packages=(mock) +if grep -iq fedora /etc/redhat-release ; then + # fedora-packager has rpmbuild-md5 for the SRPM + required_packages=( ${required_packages[@]} fedora-packager ) +else + required_packages=( ${required_packages[@]} rpm-build ) +fi + +while getopts ':p:' opt +do + case $opt in + p) + required_packages=( ${required_packages[@]} $OPTARG ) + ;; + \?) + echo "Invalid flag -$OPTARG" >&2 + exit 255 + ;; + \:) + echo "missing argument to -$OPTARG" >&2 + exit 127 + ;; + esac +done + +repo=${1:-/var/lib/mock/passenger-build-repo} +etc=${2:-/etc/mock} + +if [ "$(id -u)" == "0" ]; then + cat <<-EOF >&2 + It is a very bad idea to run this build as root. + Use a less-privileged user. If that user is not a sudoer, you may need to run + a few commands as root before the first run. (Those commands will be determined + the first time through) +EOF + exit 1 +fi + +mock_installed=`rpm -q mock | grep -v 'not installed' | wc -l` +cfg_count=`ls -1 $etc/passenger-* 2>/dev/null | wc -l` +in_mock_group=`groups | grep mock | wc -l` + +# Fedora has a utility to do this, but I don't know if RHEL does, plus it requires another package install, just do it by hand +mkdir -p `rpm -E '%{_topdir}'`/{SOURCES,SPECS,SRPMS,RPMS} + +if [[ ( $cfg_count == 0 && ! -w $etc ) || ! ( -d $etc || -w $etc/.. ) || ! ( -w $repo || -w $repo/.. ) ]] ; then + run_setup=1 +fi + +declare -a yum_pkgs + +for pkg in "${required_packages[@]}" +do + if [ `rpm -q $pkg | grep -v 'not installed' | wc -l` -eq 0 ] ; then + yum_pkgs=(${yum_pkgs[@]} $pkg) + fi +done + +if [[ "$run_setup" = "1" || ! $in_mock_group == 1 || ${#yum_pkgs[@]} != 0 ]] ; then + echo "There is some setup required before building packages." + echo "We will run sudo to do the following:" + + if [[ ${#yum_pkgs[@]} != 0 ]] ; then + echo " # yum -y install ${yum_pkgs[@]}" + fi + + if [[ ! "$in_mock_group" == "1" ]] ; then + echo " # usermod -a -G mock $USER" + echo " (add you to the mock group, you will need to log back in for this to take effect)" + fi + + if [[ "$run_setup" == "1" ]] ; then + echo " # `dirname $0`/mocksetup.sh" + echo " (sets up the dependencies for mock)" + fi + echo + echo "Hit return to continue, ^C to cancel" + read + + if [[ ! $mock_installed == 1 || ! $createrepo_installed == 1 ]] ; then + sudo yum -y install "${yum_pkgs[@]}" + fi + if [[ ! "$in_mock_group" == "1" ]] ; then + sudo usermod -a -G mock $USER + fi + if [[ "$run_setup" == "1" ]] ; then + sudo `dirname $0`/mocksetup.sh $repo $etc + fi + + if [[ ! "$in_mock_group" == "1" ]] ; then + echo "You have been added to the mock group. Please relogin for this to take effect, and re-run 'rake package:rpm'." + exit 4 + fi +fi + diff --git a/rpm/release/mocksetup.sh b/rpm/release/mocksetup.sh new file mode 100755 index 0000000000..fccb14abd8 --- /dev/null +++ b/rpm/release/mocksetup.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +BUILD_VERBOSITY=${BUILD_VERBOSITY:-0} +[ $BUILD_VERBOSITY -ge 3 ] && set -x +set -e + +repo=${1:-/var/lib/mock/passenger-build-repo} +etc=${2:-/etc/mock} +# For the written files & dirs, we want g+w, this isn't consistent enough +# umask 002 + +# For the non-groupinstall configs, pull the members out of the mock-comps.xml +prereqs=`egrep 'packagereq.*default' $(dirname $0)/mock-repo/comps.xml | cut -d\> -f2 | cut -d\< -f1 | tr '\n' ' '` + +for cfg in $etc/{fedora-{13,14},epel-5}-*.cfg +do + [ $BUILD_VERBOSITY -ge 2 ] && echo $cfg + dir=`dirname $cfg` + base=`basename $cfg` + new=$dir/passenger-$base + perl -p - $repo "$prereqs" $cfg <<-'EOF' > $new + # Warning <<- only kills TABS (ASCII 0x9) DO NOT CONVERT THESE + # TABS TO SPACES -- IT WILL BREAK + + sub BEGIN { + our $repo = shift; + our $prereqs = shift; + } + + s{opts\['root'\]\s*=\s*'}{${&}passenger-}; #'; + s{groupinstall [^']+}{$& Ruby build-passenger}; #' + s{\binstall buildsys-build}{$& ruby ruby-devel $prereqs}; + s{^"""}{</dev/null || true + chmod g+w $new 2>/dev/null || true +done + +mkdir -p $repo +chmod g+s $repo 2>/dev/null || true + +if [ $BUILD_VERBOSITY -ge 1 ]; then + rsync_volume='-v' + if [ $BUILD_VERBOSITY -ge 2 ]; then + rsync_volume='-v --progress' + fi +fi + +cp -ra `dirname $0`/mock-repo/* $repo +chgrp -R mock $repo 2>/dev/null || true + +createrepo_volume= +if [ $BUILD_VERBOSITY -gt 1 ]; then + createrepo_volume='-v' +else + if [ $BUILD_VERBOSITY -le 0 ]; then + createrepo_volume='-q' + fi +fi + +createrepo $createrepo_volume -g comps.xml $repo +chmod -R g+w $repo 2>/dev/null || true diff --git a/test/cxx/EventedClientTest.cpp b/test/cxx/EventedClientTest.cpp index 7915d25d8c..fda4202b7a 100644 --- a/test/cxx/EventedClientTest.cpp +++ b/test/cxx/EventedClientTest.cpp @@ -1,13 +1,9 @@ #include "TestSupport.h" #include "EventedClient.h" -#include "MessageChannel.h" #include "Utils/ScopeGuard.h" #include "Utils/IOUtils.h" #include -#include -#include -#include using namespace Passenger; using namespace std; @@ -20,21 +16,23 @@ namespace tut { ev::dynamic_loop eventLoop; ev::async exitWatcher; oxt::thread *thr; + string lastErrorMessage; + int lastErrorCode; AtomicInt integer; + string data; EventedClientTest() : exitWatcher(eventLoop) { - int fds[2]; - - socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - fd1 = fds[0]; - fd2 = fds[1]; + SocketPair sockets = createUnixSocketPair(); + fd1 = sockets.first; + fd2 = sockets.second; setNonBlocking(fd2); exitWatcher.set(this); exitWatcher.start(); thr = NULL; + lastErrorCode = -1; } ~EventedClientTest() { @@ -50,6 +48,12 @@ namespace tut { void stopEventLoop() { if (thr != NULL) { exitWatcher.send(); + waitUntilEventLoopExits(); + } + } + + void waitUntilEventLoopExits() { + if (thr != NULL) { thr->join(); delete thr; thr = NULL; @@ -73,6 +77,28 @@ namespace tut { EventedClientTest *test = (EventedClientTest *) client->userData; test->integer = 2; } + + static void saveSystemError(EventedClient *client, const string &message, int code) { + EventedClientTest *self = (EventedClientTest *) client->userData; + self->lastErrorMessage = message; + self->lastErrorCode = code; + } + + static void exitEventLoop(EventedClient *client) { + EventedClientTest *self = (EventedClientTest *) client->userData; + self->eventLoop.unloop(); + } + + static void readAndExitOnEof(EventedClient *client) { + EventedClientTest *self = (EventedClientTest *) client->userData; + char buf[1024]; + ssize_t ret = read(client->fd, buf, sizeof(buf)); + if (ret <= 0) { + self->eventLoop.unloop(); + } else { + self->data.append(buf, ret); + } + } }; DEFINE_TEST_GROUP(EventedClientTest); @@ -129,7 +155,7 @@ namespace tut { char buf[100]; memset(buf, 0, sizeof(buf)); - ensure(MessageChannel(fd1).readRaw(buf, strlen("hello world"))); + ensure(readExact(fd1, buf, strlen("hello world"))); ensure_equals(string(buf), "hello world"); } @@ -155,7 +181,7 @@ namespace tut { char buf[str.size()]; memset(buf, 0, sizeof(buf)); - ensure(MessageChannel(fd1).readRaw(buf, str.size())); + ensure(readExact(fd1, buf, str.size())); ensure(memcmp(buf, str.c_str(), str.size()) == 0); } @@ -183,7 +209,7 @@ namespace tut { char buf[str.size()]; memset(buf, 0, sizeof(buf)); - ensure(MessageChannel(fd1).readRaw(buf, str.size())); + ensure(readExact(fd1, buf, str.size())); ensure(memcmp(buf, str.c_str(), str.size()) == 0); // readWatcher will become active again after all pending data has been sent. @@ -239,7 +265,7 @@ namespace tut { ); memset(buf, 0, sizeof(buf)); - ensure(MessageChannel(fd1).readRaw(buf, str.size() - 1)); + ensure(readExact(fd1, buf, str.size() - 1)); ensure(memcmp(buf, str.c_str() + 1, str.size() - 1) == 0); ensure_equals(read(fd1, buf, 1), (ssize_t) 0); @@ -379,9 +405,119 @@ namespace tut { EVENT_LOOP_GUARD; char buf[str.size()]; - MessageChannel(fd1).readRaw(buf, str.size()); + readExact(fd1, buf, str.size()); EVENTUALLY(2, result = integer == 2; ); } + + TEST_METHOD(17) { + // EventedClient.write() ensures that the given data is written + // after what's already in the outbox. + EventedClient client(eventLoop, fd2); + string header(1024 * 4, 'x'); + string body(1024 * 128, 'y'); + char buf[header.size() + body.size() + 1024]; + + client.write(header); + client.write(body); + ensure(client.pendingWrites() > 0); + + ensure_equals(readExact(fd1, buf, header.size()), (unsigned int) header.size()); + ensure_equals(StaticString(buf, header.size()), header); + + client.write("hello world"); + + startEventLoop(); + EVENT_LOOP_GUARD; + + unsigned int len = body.size() + strlen("hello world"); + ensure_equals(readExact(fd1, buf, len), len); + ensure_equals(StaticString(buf, len), body + "hello world"); + } + + TEST_METHOD(18) { + // If writeErrorAction is DISCONNECT_FULL then + // EventedClient.write(), upon encountering a write error, + // will forcefully disconnect the connection. + EventedClient client(eventLoop, fd2); + client.userData = this; + client.writeErrorAction = EventedClient::DISCONNECT_FULL; + client.onSystemError = saveSystemError; + fd1.close(); + client.write("hello"); + ensure_equals(lastErrorCode, EPIPE); + ensure_equals(client.fd, -1); + } + + TEST_METHOD(19) { + // If writeErrorAction is DISCONNECT_FULL then + // the background writer disconnects the connection + // on write error. + EventedClient client(eventLoop, fd2); + client.userData = this; + client.writeErrorAction = EventedClient::DISCONNECT_FULL; + client.onSystemError = saveSystemError; + + string str(1024 * 128, 'x'); + client.write(str); + ensure(client.pendingWrites() > 0); + + fd1.close(); + client.onDisconnect = exitEventLoop; + startEventLoop(); + waitUntilEventLoopExits(); + + ensure_equals(lastErrorCode, EPIPE); + ensure_equals(client.fd, -1); + } + + TEST_METHOD(20) { + // If writeErrorAction is DISCONNECT_WRITE then + // EventedClient.write(), upon encountering a write error, + // will forcefully disconnect the writer side of the + // connection but continue allowing reading. + EventedClient client(eventLoop, fd2); + client.userData = this; + client.writeErrorAction = EventedClient::DISCONNECT_WRITE; + client.onSystemError = saveSystemError; + client.onReadable = readAndExitOnEof; + client.notifyReads(true); + + writeExact(fd1, "world", 5); + fd1.close(); + client.write("hello"); + + startEventLoop(); + waitUntilEventLoopExits(); + + ensure(client.fd != -1); + ensure_equals(data, "world"); + } + + TEST_METHOD(21) { + // If writeErrorAction is DISCONNECT_WRITE then + // the background writer, upon encountering a write error, + // will forcefully disconnect the writer side of the + // connection but continue allowing reading. + EventedClient client(eventLoop, fd2); + client.userData = this; + client.writeErrorAction = EventedClient::DISCONNECT_WRITE; + client.onSystemError = saveSystemError; + client.onReadable = readAndExitOnEof; + client.notifyReads(true); + + string str(1024 * 128, 'x'); + client.write(str); + ensure(client.pendingWrites() > 0); + + writeExact(fd1, "world", 5); + fd1.close(); + + startEventLoop(); + waitUntilEventLoopExits(); + + ensure(client.fd != -1); + ensure_equals(data, "world"); + } } diff --git a/test/cxx/StaticStringTest.cpp b/test/cxx/StaticStringTest.cpp index 8daa86d20c..2bf783742b 100644 --- a/test/cxx/StaticStringTest.cpp +++ b/test/cxx/StaticStringTest.cpp @@ -83,4 +83,138 @@ namespace tut { string("hello world") < string("hello") ); } + + TEST_METHOD(4) { + // Test find(char) + ensure_equals("Assertion 1", + StaticString("").find('c'), + string::npos + ); + ensure_equals("Assertion 2", + StaticString("hello world").find('c'), + string::npos + ); + ensure_equals("Assertion 3", + StaticString("hello world").find('h'), + (string::size_type) 0 + ); + ensure_equals("Assertion 4", + StaticString("hello world").find('o'), + (string::size_type) 4 + ); + + ensure_equals("Assertion 5", + StaticString("hello world").find('h', 1), + string::npos + ); + ensure_equals("Assertion 6", + StaticString("hello world").find('o', 1), + (string::size_type) 4 + ); + ensure_equals("Assertion 7", + StaticString("hello world").find('o', 5), + (string::size_type) 7 + ); + + ensure_equals("Assertion 8", + StaticString("hello world").find('h', 12), + string::npos + ); + ensure_equals("Assertion 9", + StaticString("hello world").find('h', 20), + string::npos + ); + } + + TEST_METHOD(5) { + // Test find(str) + ensure_equals("Assertion 1", + StaticString("").find(""), + (string::size_type) 0 + ); + ensure_equals("Assertion 2", + StaticString("").find("c"), + string::npos + ); + ensure_equals("Assertion 3", + StaticString("hello world").find("c"), + string::npos + ); + ensure_equals("Assertion 4", + StaticString("hello world").find("h"), + (string::size_type) 0 + ); + ensure_equals("Assertion 5", + StaticString("hello world").find("o"), + (string::size_type) 4 + ); + ensure_equals("Assertion 6", + StaticString("hello world").find("ello"), + (string::size_type) 1 + ); + ensure_equals("Assertion 7", + StaticString("hello world").find("world"), + (string::size_type) 6 + ); + ensure_equals("Assertion 8", + StaticString("hello world").find("worldd"), + string::npos + ); + ensure_equals("Assertion 9", + StaticString("hello world").find(""), + (string::size_type) 0 + ); + + ensure_equals("Assertion 10", + StaticString("hello world").find("h", 1), + string::npos + ); + ensure_equals("Assertion 11", + StaticString("hello hello").find("ll", 1), + (string::size_type) 2 + ); + ensure_equals("Assertion 12", + StaticString("hello hello").find("ll", 3), + (string::size_type) 8 + ); + + ensure_equals("Assertion 13", + StaticString("hello world").find("he", 12), + string::npos + ); + ensure_equals("Assertion 14", + StaticString("hello world").find("he", 20), + string::npos + ); + } + + TEST_METHOD(6) { + // Test substr() + ensure_equals("Assertion 1", + StaticString("hello world").substr(), + "hello world"); + ensure_equals("Assertion 2", + StaticString("hello world").substr(1), + "ello world"); + ensure_equals("Assertion 3", + StaticString("hello world").substr(4), + "o world"); + ensure_equals("Assertion 4", + StaticString("hello world").substr(11), + ""); + + try { + StaticString("hello world").substr(12); + fail("out_of_range expected"); + } catch (out_of_range &) { + // Success. + } + + ensure_equals("Assertion 5", + StaticString("hello world").substr(2, 3), + "llo"); + ensure_equals("Assertion 6", + StaticString("hello world").substr(6, 10), + "world"); + } } diff --git a/test/stub/rails_apps/3.0/empty/.bundle/config b/test/stub/rails_apps/3.0/empty/.bundle/config new file mode 100644 index 0000000000..9aeae34115 --- /dev/null +++ b/test/stub/rails_apps/3.0/empty/.bundle/config @@ -0,0 +1,3 @@ +--- +BUNDLE_DISABLE_SHARED_GEMS: "1" +BUNDLE_PATH: /tmp/empty-rails3-app diff --git a/test/stub/rails_apps/3.0/empty/Gemfile.lock b/test/stub/rails_apps/3.0/empty/Gemfile.lock new file mode 100644 index 0000000000..bbaa2d88d8 --- /dev/null +++ b/test/stub/rails_apps/3.0/empty/Gemfile.lock @@ -0,0 +1,73 @@ +GEM + remote: http://rubygems.org/ + specs: + abstract (1.0.0) + actionmailer (3.0.3) + actionpack (= 3.0.3) + mail (~> 2.2.9) + actionpack (3.0.3) + activemodel (= 3.0.3) + activesupport (= 3.0.3) + builder (~> 2.1.2) + erubis (~> 2.6.6) + i18n (~> 0.4) + rack (~> 1.2.1) + rack-mount (~> 0.6.13) + rack-test (~> 0.5.6) + tzinfo (~> 0.3.23) + activemodel (3.0.3) + activesupport (= 3.0.3) + builder (~> 2.1.2) + i18n (~> 0.4) + activerecord (3.0.3) + activemodel (= 3.0.3) + activesupport (= 3.0.3) + arel (~> 2.0.2) + tzinfo (~> 0.3.23) + activeresource (3.0.3) + activemodel (= 3.0.3) + activesupport (= 3.0.3) + activesupport (3.0.3) + arel (2.0.4) + builder (2.1.2) + erubis (2.6.6) + abstract (>= 1.0.0) + i18n (0.4.2) + mail (2.2.10) + activesupport (>= 2.3.6) + i18n (~> 0.4.1) + mime-types (~> 1.16) + treetop (~> 1.4.8) + mime-types (1.16) + polyglot (0.3.1) + rack (1.2.1) + rack-mount (0.6.13) + rack (>= 1.0.0) + rack-test (0.5.6) + rack (>= 1.0) + rails (3.0.3) + actionmailer (= 3.0.3) + actionpack (= 3.0.3) + activerecord (= 3.0.3) + activeresource (= 3.0.3) + activesupport (= 3.0.3) + bundler (~> 1.0) + railties (= 3.0.3) + railties (3.0.3) + actionpack (= 3.0.3) + activesupport (= 3.0.3) + rake (>= 0.8.7) + thor (~> 0.14.4) + rake (0.8.7) + sqlite3-ruby (1.3.2) + thor (0.14.4) + treetop (1.4.9) + polyglot (>= 0.3.1) + tzinfo (0.3.23) + +PLATFORMS + ruby + +DEPENDENCIES + rails (~> 3.0.0) + sqlite3-ruby