We got nominated! Help us out and vote for GitHub as Best Bootstrapped Startup of 2008. (You can vote once a day.) [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
passenger / lib / passenger / application_spawner.rb
bd6e3b45 » Hongli Lai (Phusion) 2008-04-03 Insert GPL headers to sourc... 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 Add special error page for ... 17 require 'rubygems'
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 18 require 'socket'
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 19 require 'etc'
9d714c0d » Hongli Lai (Phusion) 2008-03-01 Rename 'mod_rails' director... 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 Add special error page for ... 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 Rename the 'ModRails' names... 37 module Passenger
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 38
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 39 # An abstract base class for AppInitError and FrameworkInitError. This represents
40 # the failure when initializing something.
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 41 class InitializationError < StandardError
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 42 # The exception that caused initialization to fail. This may be nil.
d21001ab » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 43 attr_accessor :child_exception
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 44
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 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 ApplicationSpawner: catch e... 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 Improve documentation 53
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 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 Initial import. 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 Improve documentation 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 Initial import. 73 class ApplicationSpawner < AbstractServer
74 include Utils
75
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 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 Improve API documentation. ... 80 # The user ID of the root user.
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 81 ROOT_UID = 0
382f8d52 » Hongli Lai (Phusion) 2008-02-28 Improve API documentation. ... 82 # The group ID of the root user.
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 83 ROOT_GID = 0
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 84
d73f7e27 » Hongli Lai (Phusion) 2008-02-22 Improve documentation and d... 85 # An attribute, used internally. This should not be used outside Passenger.
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 86 attr_accessor :time
b77b49bb » Hongli Lai (Phusion) 2008-03-31 Correctly support applicati... 87 # The application root of this spawner.
88 attr_reader :app_root
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 89
382f8d52 » Hongli Lai (Phusion) 2008-02-28 Improve API documentation. ... 90 # +app_root+ is the root directory of this application, i.e. the directory
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 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 Improve API documentation. ... 95 # If +lower_privilege+ is true, then ApplicationSpawner will attempt to
e8f3fc44 » Hongli Lai (Phusion) 2008-03-02 Fix a little markup error 96 # switch to the user who owns the application's <tt>config/environment.rb</tt>,
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 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 Improve API documentation. ... 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 Implement user switching su... 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 Initial import. 106 super()
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 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 Implement user switching su... 114 @lower_privilege = lower_privilege
115 @lowest_user = lowest_user
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 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 Improve error handling in A... 124 # Raises:
125 # - AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn't already been started.
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 126 # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 127 def spawn_application
ccd01b56 » Hongli Lai (Phusion) 2008-02-23 Refactor AbstractServer and... 128 server.write("spawn_application")
37eb17c0 » Hongli Lai (Phusion) 2008-02-26 Apparently abstract namespa... 129 pid, socket_name, using_abstract_namespace = server.read
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 130 if pid.nil?
131 raise IOError, "Connection closed"
132 end
90dcf236 » Hongli Lai (Phusion) 2008-02-24 Request handler: use Unix s... 133 owner_pipe = server.recv_io
37eb17c0 » Hongli Lai (Phusion) 2008-02-26 Apparently abstract namespa... 134 return Application.new(@app_root, pid, socket_name,
135 using_abstract_namespace == "true", owner_pipe)
d21001ab » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 136 rescue SystemCallError, IOError, SocketError => e
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 137 raise Error, "The application spawner server exited unexpectedly"
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 138 end
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 139
140 # Overrided from AbstractServer#start.
141 #
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 142 # May raise these additional exceptions:
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 143 # - AppInitError: The Ruby on Rails application raised an exception
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 144 # or called exit() during startup.
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 145 # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 146 def start
147 super
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 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 Reorganize exception hierar... 153 raise AppInitError.new(
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 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 Reorganize exception hierar... 159 raise AppInitError.new("Application '#{@app_root}' exited during startup")
98c09711 » Hongli Lai (Phusion) 2008-03-03 Improve error handling in A... 160 end
161 rescue IOError, SystemCallError, SocketError
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 162 stop
cec5c76b » Hongli Lai (Phusion) 2008-03-07 Reorganize exception hierar... 163 raise Error, "The application spawner server exited unexpectedly"
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 164 end
165 end
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 166
167 protected
168 # Overrided method.
d73f7e27 » Hongli Lai (Phusion) 2008-02-22 Improve documentation and d... 169 def before_fork # :nodoc:
b28fbb8f » Hongli Lai (Phusion) 2008-03-17 Synchronize with recent EMM... 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 Initial import. 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 Improve documentation and d... 178 def initialize_server # :nodoc:
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 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 Improve error handling in A... 184 rescue StandardError, ScriptError, NoMemoryError => e
c4ae459e » Hongli Lai (Phusion) 2008-04-15 - If spawning fails and Pas... 185 if ENV['TESTING_PASSENGER'] == '1'
186 print_exception(self.class.to_s, e)
187 end
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 188 client.write('exception')
b3da6b4d » Hongli Lai (Phusion) 2008-03-02 Split exception marshalling... 189 client.write_scalar(marshal_exception(e))
be79ff24 » Hongli Lai (Phusion) 2008-03-02 ApplicationSpawner: catch e... 190 return
191 rescue SystemExit
192 client.write('exit')
193 raise
194 end
195 client.write('success')
be0f3b4e » Hongli Lai (Phusion) 2008-01-25 Initial import. 196 end
197
198 private
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 199 def lower_privilege!
200 stat = File.stat("config/environment.rb")
c07e0987 » Hongli Lai (Phusion) 2008-03-22 Finish user switching confi... 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 Implement user switching su... 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 User switching: set supplem... 214 username = user
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 215 uid = pw.uid
216 gid = pw.gid
217 else
3ca31b3e » Hongli Lai (Phusion) 2008-04-26 User switching: set supplem... 218 pw = Etc.getpwuid(user)
219 username = pw.name
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 220 uid = user
3ca31b3e » Hongli Lai (Phusion) 2008-04-26 User switching: set supplem... 221 gid = pw.gid
b2d4cc2f » Hongli Lai (Phusion) 2008-02-23 Implement user switching su... 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 User switching: set supplem... 229 Process.groups = Process.initgroup