This repository is private.
All pages are served over SSL and all pushing and pulling is done over SSH.
No one may fork, clone, or view it unless they are added as a member.
Every repository with this icon (
) is private.
Every repository with this icon (
This repository is public.
Anyone may fork, clone, or view it.
Every repository with this icon (
) is public.
Every repository with this icon (
Hongli Lai (Phusion) (author)
Tue Apr 29 14:53:04 -0700 2008
| bd6e3b45 » | Hongli Lai (Phusion) | 2008-04-03 | 1 | # Phusion Passenger - http://www.modrails.com/ | |
| 2 | # Copyright (C) 2008 Phusion | ||||
| 3 | # | ||||
| 4 | # This program is free software; you can redistribute it and/or modify | ||||
| 5 | # it under the terms of the GNU General Public License as published by | ||||
| 6 | # the Free Software Foundation; version 2 of the License. | ||||
| 7 | # | ||||
| 8 | # This program is distributed in the hope that it will be useful, | ||||
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| 11 | # GNU General Public License for more details. | ||||
| 12 | # | ||||
| 13 | # You should have received a copy of the GNU General Public License along | ||||
| 14 | # with this program; if not, write to the Free Software Foundation, Inc., | ||||
| 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| 16 | |||||
| ff15201f » | Hongli Lai (Phusion) | 2008-03-26 | 17 | require 'rubygems' | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 18 | require 'socket' | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 19 | require 'etc' | |
| 9d714c0d » | Hongli Lai (Phusion) | 2008-03-01 | 20 | require 'passenger/abstract_server' | |
| 21 | require 'passenger/application' | ||||
| 22 | require 'passenger/utils' | ||||
| 23 | require 'passenger/request_handler' | ||||
| ff15201f » | Hongli Lai (Phusion) | 2008-03-26 | 24 | ||
| 25 | begin | ||||
| 26 | # Preload MySQL if possible. We want to preload it and we need | ||||
| 27 | # its exception classes. | ||||
| 28 | require 'mysql' | ||||
| 29 | rescue LoadError | ||||
| 30 | end | ||||
| 31 | begin | ||||
| 32 | # Preload SQLite 3 if possible. Rails 2.0 apps use it by default. | ||||
| 33 | require 'sqlite3' | ||||
| 34 | rescue LoadError | ||||
| 35 | end | ||||
| 36 | |||||
| 16201608 » | Hongli Lai (Phusion) | 2008-03-01 | 37 | module Passenger | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 38 | ||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 39 | # An abstract base class for AppInitError and FrameworkInitError. This represents | |
| 40 | # the failure when initializing something. | ||||
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 41 | class InitializationError < StandardError | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 42 | # The exception that caused initialization to fail. This may be nil. | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 43 | attr_accessor :child_exception | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 44 | ||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 45 | # Create a new InitializationError. +message+ is the error message, | |
| 46 | # and +child_exception+ is the exception that caused initialization | ||||
| 47 | # to fail. | ||||
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 48 | def initialize(message, child_exception = nil) | |
| 49 | super(message) | ||||
| 50 | @child_exception = child_exception | ||||
| 51 | end | ||||
| 52 | end | ||||
| 2c9448a9 » | Hongli Lai (Phusion) | 2008-03-02 | 53 | ||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 54 | # Raised when ApplicationSpawner, FrameworkSpawner or SpawnManager was unable | |
| 55 | # spawn a Ruby on Rails application, because the application either threw an | ||||
| 56 | # exception or called exit. | ||||
| 57 | # | ||||
| 58 | # If the +child_exception+ attribute is nil, then it means that the application | ||||
| 59 | # called exit. | ||||
| 60 | class AppInitError < InitializationError | ||||
| 61 | end | ||||
| 62 | |||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 63 | # This class is capable of spawns instances of a single Ruby on Rails application. | |
| 64 | # It does so by preloading as much of the application's code as possible, then creating | ||||
| 65 | # instances of the application using what is already preloaded. This makes it spawning | ||||
| 66 | # application instances very fast, except for the first spawn. | ||||
| 67 | # | ||||
| 68 | # Use multiple instances of ApplicationSpawner if you need to spawn multiple different | ||||
| 69 | # Ruby on Rails applications. | ||||
| 2c9448a9 » | Hongli Lai (Phusion) | 2008-03-02 | 70 | # | |
| 71 | # *Note*: ApplicationSpawner may only be started asynchronously with AbstractServer#start. | ||||
| 72 | # Starting it synchronously with AbstractServer#start_synchronously has not been tested. | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 73 | class ApplicationSpawner < AbstractServer | |
| 74 | include Utils | ||||
| 75 | |||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 76 | # This exception means that the ApplicationSpawner server process exited unexpectedly. | |
| 77 | class Error < AbstractServer::ServerError | ||||
| 78 | end | ||||
| 79 | |||||
| 382f8d52 » | Hongli Lai (Phusion) | 2008-02-28 | 80 | # The user ID of the root user. | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 81 | ROOT_UID = 0 | |
| 382f8d52 » | Hongli Lai (Phusion) | 2008-02-28 | 82 | # The group ID of the root user. | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 83 | ROOT_GID = 0 | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 84 | ||
| d73f7e27 » | Hongli Lai (Phusion) | 2008-02-22 | 85 | # An attribute, used internally. This should not be used outside Passenger. | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 86 | attr_accessor :time | |
| b77b49bb » | Hongli Lai (Phusion) | 2008-03-31 | 87 | # The application root of this spawner. | |
| 88 | attr_reader :app_root | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 89 | ||
| 382f8d52 » | Hongli Lai (Phusion) | 2008-02-28 | 90 | # +app_root+ is the root directory of this application, i.e. the directory | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 91 | # that contains 'app/', 'public/', etc. If given an invalid directory, | |
| 92 | # or a directory that doesn't appear to be a Rails application root directory, | ||||
| 93 | # then an ArgumentError will be raised. | ||||
| 94 | # | ||||
| 382f8d52 » | Hongli Lai (Phusion) | 2008-02-28 | 95 | # If +lower_privilege+ is true, then ApplicationSpawner will attempt to | |
| e8f3fc44 » | Hongli Lai (Phusion) | 2008-03-02 | 96 | # switch to the user who owns the application's <tt>config/environment.rb</tt>, | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 97 | # and to the default group of that user. | |
| 98 | # | ||||
| 99 | # If that user doesn't exist on the system, or if that user is root, | ||||
| 100 | # then ApplicationSpawner will attempt to switch to the username given by | ||||
| 382f8d52 » | Hongli Lai (Phusion) | 2008-02-28 | 101 | # +lowest_user+ (and to the default group of that user). | |
| 102 | # If +lowest_user+ doesn't exist either, or if switching user failed | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 103 | # (because the current process does not have the privilege to do so), | |
| 104 | # then ApplicationSpawner will continue without reporting an error. | ||||
| 105 | def initialize(app_root, lower_privilege = true, lowest_user = "nobody") | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 106 | super() | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 107 | begin | |
| 108 | @app_root = normalize_path(app_root) | ||||
| 109 | rescue SystemCallError => e | ||||
| 110 | raise ArgumentError, e.message | ||||
| 111 | rescue ArgumentError | ||||
| 112 | raise | ||||
| 113 | end | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 114 | @lower_privilege = lower_privilege | |
| 115 | @lowest_user = lowest_user | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 116 | self.time = Time.now | |
| 117 | assert_valid_app_root(@app_root) | ||||
| 118 | define_message_handler(:spawn_application, :handle_spawn_application) | ||||
| 119 | end | ||||
| 120 | |||||
| 121 | # Spawn an instance of the RoR application. When successful, an Application object | ||||
| 122 | # will be returned, which represents the spawned RoR application. | ||||
| 123 | # | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 124 | # Raises: | |
| 125 | # - AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn't already been started. | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 126 | # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly. | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 127 | def spawn_application | |
| ccd01b56 » | Hongli Lai (Phusion) | 2008-02-23 | 128 | server.write("spawn_application") | |
| 37eb17c0 » | Hongli Lai (Phusion) | 2008-02-26 | 129 | pid, socket_name, using_abstract_namespace = server.read | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 130 | if pid.nil? | |
| 131 | raise IOError, "Connection closed" | ||||
| 132 | end | ||||
| 90dcf236 » | Hongli Lai (Phusion) | 2008-02-24 | 133 | owner_pipe = server.recv_io | |
| 37eb17c0 » | Hongli Lai (Phusion) | 2008-02-26 | 134 | return Application.new(@app_root, pid, socket_name, | |
| 135 | using_abstract_namespace == "true", owner_pipe) | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 136 | rescue SystemCallError, IOError, SocketError => e | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 137 | raise Error, "The application spawner server exited unexpectedly" | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 138 | end | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 139 | ||
| 140 | # Overrided from AbstractServer#start. | ||||
| 141 | # | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 142 | # May raise these additional exceptions: | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 143 | # - AppInitError: The Ruby on Rails application raised an exception | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 144 | # or called exit() during startup. | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 145 | # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly. | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 146 | def start | |
| 147 | super | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 148 | begin | |
| 149 | status = server.read[0] | ||||
| 150 | if status == 'exception' | ||||
| 151 | child_exception = unmarshal_exception(server.read_scalar) | ||||
| 152 | stop | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 153 | raise AppInitError.new( | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 154 | "Application '#{@app_root}' raised an exception: " << | |
| 155 | "#{child_exception.class} (#{child_exception.message})", | ||||
| 156 | child_exception) | ||||
| 157 | elsif status == 'exit' | ||||
| 158 | stop | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 159 | raise AppInitError.new("Application '#{@app_root}' exited during startup") | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 160 | end | |
| 161 | rescue IOError, SystemCallError, SocketError | ||||
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 162 | stop | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 163 | raise Error, "The application spawner server exited unexpectedly" | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 164 | end | |
| 165 | end | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 166 | ||
| 167 | protected | ||||
| 168 | # Overrided method. | ||||
| d73f7e27 » | Hongli Lai (Phusion) | 2008-02-22 | 169 | def before_fork # :nodoc: | |
| b28fbb8f » | Hongli Lai (Phusion) | 2008-03-17 | 170 | if GC.copy_on_write_friendly? | |
| 171 | # Garbage collect now so that the child process doesn't have to | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 172 | # do that (to prevent making pages dirty). | |
| 173 | GC.start | ||||
| 174 | end | ||||
| 175 | end | ||||
| 176 | |||||
| 177 | # Overrided method. | ||||
| d73f7e27 » | Hongli Lai (Phusion) | 2008-02-22 | 178 | def initialize_server # :nodoc: | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 179 | begin | |
| 180 | $0 = "Passenger ApplicationSpawner: #{@app_root}" | ||||
| 181 | Dir.chdir(@app_root) | ||||
| 182 | lower_privilege! if @lower_privilege | ||||
| 183 | preload_application | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 184 | rescue StandardError, ScriptError, NoMemoryError => e | |
| c4ae459e » | Hongli Lai (Phusion) | 2008-04-15 | 185 | if ENV['TESTING_PASSENGER'] == '1' | |
| 186 | print_exception(self.class.to_s, e) | ||||
| 187 | end | ||||
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 188 | client.write('exception') | |
| b3da6b4d » | Hongli Lai (Phusion) | 2008-03-02 | 189 | client.write_scalar(marshal_exception(e)) | |
| be79ff24 » | Hongli Lai (Phusion) | 2008-03-02 | 190 | return | |
| 191 | rescue SystemExit | ||||
| 192 | client.write('exit') | ||||
| 193 | raise | ||||
| 194 | end | ||||
| 195 | client.write('success') | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 196 | end | |
| 197 | |||||
| 198 | private | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 199 | def lower_privilege! | |
| 200 | stat = File.stat("config/environment.rb") | ||||
| c07e0987 » | Hongli Lai (Phusion) | 2008-03-22 | 201 | begin | |
| 202 | if !switch_to_user(stat.uid) | ||||
| 203 | switch_to_user(@lowest_user) | ||||
| 204 | end | ||||
| 205 | rescue Errno::EPERM | ||||
| 206 | # No problem if we were unable to switch user. | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 207 | end | |
| 208 | end | ||||
| 209 | |||||
| 210 | def switch_to_user(user) | ||||
| 211 | begin | ||||
| 212 | if user.is_a?(String) | ||||
| 213 | pw = Etc.getpwnam(user) | ||||
| 3ca31b3e » | Hongli Lai (Phusion) | 2008-04-26 | 214 | username = user | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 215 | uid = pw.uid | |
| 216 | gid = pw.gid | ||||
| 217 | else | ||||
| 3ca31b3e » | Hongli Lai (Phusion) | 2008-04-26 | 218 | pw = Etc.getpwuid(user) | |
| 219 | username = pw.name | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 220 | uid = user | |
| 3ca31b3e » | Hongli Lai (Phusion) | 2008-04-26 | 221 | gid = pw.gid | |
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 222 | end | |
| 223 | rescue | ||||
| 224 | return false | ||||
| 225 | end | ||||
| 226 | if uid == ROOT_UID | ||||
| 227 | return false | ||||
| 228 | else | ||||
| 3ca31b3e » | Hongli Lai (Phusion) | 2008-04-26 | 229 | Process.groups = Process.initgroup | |





