Class Index [+]

Quicksearch

PhusionPassenger::Utils

Utility functions.

Protected Class Methods

passenger_tmpdir(create = true) click to toggle source

Returns the directory in which to store Phusion Passenger-specific temporary files. If create is true, then this method creates the directory if it doesn’t exist.

     # File lib/phusion_passenger/utils.rb, line 481
481:         def self.passenger_tmpdir(create = true)
482:                 dir = @@passenger_tmpdir
483:                 if dir.nil? || dir.empty?
484:                         dir = "#{Dir.tmpdir}/passenger.#{Process.pid}"
485:                         @@passenger_tmpdir = dir
486:                 end
487:                 if create && !File.exist?(dir)
488:                         # This is a very minimal implementation of the function
489:                         # passengerCreateTempDir() in Utils.cpp. This implementation
490:                         # is only meant to make the unit tests pass. For production
491:                         # systems one should pre-create the temp directory with
492:                         # passengerCreateTempDir().
493:                         system("mkdir", "-p", "-m", "u=wxs,g=wx,o=wx", dir)
494:                         system("mkdir", "-p", "-m", "u=wxs,g=wx,o=wx", "#{dir}/backends")
495:                 end
496:                 return dir
497:         end
passenger_tmpdir=(dir) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 499
499:         def self.passenger_tmpdir=(dir)
500:                 @@passenger_tmpdir = dir
501:         end

Protected Instance Methods

assert_valid_app_root(app_root) click to toggle source

Assert that app_root is a valid Ruby on Rails application root. Raises InvalidPath if that is not the case.

    # File lib/phusion_passenger/utils.rb, line 62
62:         def assert_valid_app_root(app_root)
63:                 assert_valid_directory(app_root)
64:                 assert_valid_file("#{app_root}/config/environment.rb")
65:         end
assert_valid_directory(path) click to toggle source

Assert that path is a directory. Raises InvalidPath if it isn’t.

    # File lib/phusion_passenger/utils.rb, line 68
68:         def assert_valid_directory(path)
69:                 if !File.directory?(path)
70:                         raise InvalidPath, "'#{path}' is not a valid directory."
71:                 end
72:         end
assert_valid_file(path) click to toggle source

Assert that path is a file. Raises InvalidPath if it isn’t.

    # File lib/phusion_passenger/utils.rb, line 75
75:         def assert_valid_file(path)
76:                 if !File.file?(path)
77:                         raise InvalidPath, "'#{path}' is not a valid file."
78:                 end
79:         end
assert_valid_groupname(groupname) click to toggle source

Assert that groupname is a valid group name. Raises ArgumentError if that is not the case.

    # File lib/phusion_passenger/utils.rb, line 90
90:         def assert_valid_groupname(groupname)
91:                 # If groupname does not exist then getgrnam() will raise an ArgumentError.
92:                 groupname && Etc.getgrnam(groupname)
93:         end
assert_valid_username(username) click to toggle source

Assert that username is a valid username. Raises ArgumentError if that is not the case.

    # File lib/phusion_passenger/utils.rb, line 83
83:         def assert_valid_username(username)
84:                 # If username does not exist then getpwnam() will raise an ArgumentError.
85:                 username && Etc.getpwnam(username)
86:         end
canonicalize_path(path) click to toggle source

Return the canonicalized version of path. This path is guaranteed to to be “normal”, i.e. it doesn’t contain stuff like “..” or “/”, and it fully resolves symbolic links.

Raises SystemCallError if something went wrong. Raises ArgumentError if path is nil. Raises InvalidPath if path does not appear to be a valid path.

    # File lib/phusion_passenger/utils.rb, line 53
53:         def canonicalize_path(path)
54:                 raise ArgumentError, "The 'path' argument may not be nil" if path.nil?
55:                 return Pathname.new(path).realpath.to_s
56:         rescue Errno::ENOENT => e
57:                 raise InvalidAPath, e.message
58:         end
close_all_io_objects_for_fds(file_descriptors_to_leave_open) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 95
 95:         def close_all_io_objects_for_fds(file_descriptors_to_leave_open)
 96:                 ObjectSpace.each_object(IO) do |io|
 97:                         begin
 98:                                 if !file_descriptors_to_leave_open.include?(io.fileno) && !io.closed?
 99:                                         io.close
100:                                 end
101:                         rescue
102:                         end
103:                 end
104:         end
lower_privilege(filename, lowest_user = "nobody") click to toggle source

Lower the current process’s privilege to the owner of the given file. No exceptions will be raised in the event that privilege lowering fails.

     # File lib/phusion_passenger/utils.rb, line 412
412:         def lower_privilege(filename, lowest_user = "nobody")
413:                 stat = File.lstat(filename)
414:                 begin
415:                         if !switch_to_user(stat.uid)
416:                                 switch_to_user(lowest_user)
417:                         end
418:                 rescue Errno::EPERM
419:                         # No problem if we were unable to switch user.
420:                 end
421:         end
marshal_exception(exception) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 106
106:         def marshal_exception(exception)
107:                 data = {
108:                         :message => exception.message,
109:                         :class => exception.class.to_s,
110:                         :backtrace => exception.backtrace
111:                 }
112:                 if exception.is_a?(InitializationError)
113:                         data[:is_initialization_error] = true
114:                         if exception.child_exception
115:                                 data[:child_exception] = marshal_exception(exception.child_exception)
116:                         end
117:                 else
118:                         begin
119:                                 data[:exception] = Marshal.dump(exception)
120:                         rescue ArgumentError, TypeError
121:                                 e = UnknownError.new(exception.message, exception.class.to_s,
122:                                                         exception.backtrace)
123:                                 data[:exception] = Marshal.dump(e)
124:                         end
125:                 end
126:                 return Marshal.dump(data)
127:         end
passenger_tmpdir(create = true) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 474
474:         def passenger_tmpdir(create = true)
475:                 PhusionPassenger::Utils.passenger_tmpdir(create)
476:         end
report_app_init_status(channel, sink = STDERR) click to toggle source

Run the given block. A message will be sent through channel (a MessageChannel object), telling the remote side whether the block raised an exception, called exit(), or succeeded.

If sink is non-nil, then every operation on $stderr/STDERR inside the block will be performed on sink as well. If sink is nil then all operations on $stderr/STDERR inside the block will be silently discarded, i.e. if one writes to $stderr/STDERR then nothing will be actually written to the console.

Returns whether the block succeeded, i.e. whether it didn’t raise an exception.

Exceptions are not propagated, except SystemExit and a few non-StandardExeption classes such as SignalException. Of the exceptions that are propagated, only SystemExit will be reported.

     # File lib/phusion_passenger/utils.rb, line 311
311:         def report_app_init_status(channel, sink = STDERR)
312:                 begin
313:                         old_global_stderr = $stderr
314:                         old_stderr = STDERR
315:                         stderr_output = ""
316:                         
317:                         pseudo_stderr = PseudoIO.new(sink)
318:                         Object.send(:remove_const, 'STDERR') rescue nil
319:                         Object.const_set('STDERR', pseudo_stderr)
320:                         $stderr = pseudo_stderr
321:                         
322:                         begin
323:                                 yield
324:                         ensure
325:                                 Object.send(:remove_const, 'STDERR') rescue nil
326:                                 Object.const_set('STDERR', old_stderr)
327:                                 $stderr = old_global_stderr
328:                                 stderr_output = pseudo_stderr.done!
329:                         end
330:                         
331:                         channel.write('success')
332:                         return true
333:                 rescue StandardError, ScriptError, NoMemoryError => e
334:                         if ENV['TESTING_PASSENGER'] == '1'
335:                                 print_exception(self.class.to_s, e)
336:                         end
337:                         channel.write('exception')
338:                         channel.write_scalar(marshal_exception(e))
339:                         channel.write_scalar(stderr_output)
340:                         return false
341:                 rescue SystemExit => e
342:                         channel.write('exit')
343:                         channel.write_scalar(marshal_exception(e))
344:                         channel.write_scalar(stderr_output)
345:                         raise
346:                 end
347:         end
safe_fork(current_location = self.class, double_fork = false) click to toggle source

Fork a new process and run the given block inside the child process, just like fork(). Unlike fork(), this method is safe, i.e. there’s no way for the child process to escape the block. Any uncaught exceptions in the child process will be printed to standard output, citing current_location as the source. Futhermore, the child process will exit by calling Kernel#exit!, thereby bypassing any at_exit or ensure blocks.

If double_fork is true, then the child process will fork and immediately exit. This technique can be used to avoid zombie processes, at the expense of not being able to waitpid() the second child.

     # File lib/phusion_passenger/utils.rb, line 244
244:         def safe_fork(current_location = self.class, double_fork = false)
245:                 pid = fork
246:                 if pid.nil?
247:                         begin
248:                                 if double_fork
249:                                         pid2 = fork
250:                                         if pid2.nil?
251:                                                 srand
252:                                                 yield
253:                                         end
254:                                 else
255:                                         srand
256:                                         yield
257:                                 end
258:                         rescue Exception => e
259:                                 print_exception(current_location.to_s, e)
260:                         ensure
261:                                 exit!
262:                         end
263:                 else
264:                         if double_fork
265:                                 Process.waitpid(pid) rescue nil
266:                                 return pid
267:                         else
268:                                 return pid
269:                         end
270:                 end
271:         end
sanitize_spawn_options(options) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 452
452:         def sanitize_spawn_options(options)
453:                 defaults = {
454:                         "lower_privilege" => true,
455:                         "lowest_user"     => "nobody",
456:                         "environment"     => "production",
457:                         "app_type"        => "rails",
458:                         "spawn_method"    => "smart-lv2",
459:                         "framework_spawner_timeout" => 1,
460:                         "app_spawner_timeout"       => 1,
461:                         "print_exceptions" => true
462:                 }
463:                 options = defaults.merge(options)
464:                 options["lower_privilege"]           = to_boolean(options["lower_privilege"])
465:                 options["framework_spawner_timeout"] = options["framework_spawner_timeout"].to_i
466:                 options["app_spawner_timeout"]       = options["app_spawner_timeout"].to_i
467:                 # Force this to be a boolean for easy use with Utils#unmarshal_and_raise_errors.
468:                 options["print_exceptions"]          = to_boolean(options["print_exceptions"])
469:                 return options
470:         end
setup_bundler_support(options = {}) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 167
167:         def setup_bundler_support(options = {})
168:                 # Rack::ApplicationSpawner depends on the 'rack' library, but the app
169:                 # might want us to use a bundled version instead of a
170:                 # gem/apt-get/yum/whatever-installed version. Therefore we must setup
171:                 # the correct load paths before requiring 'rack'.
172:                 #
173:                 # The most popular tool for bundling dependencies is Bundler. Bundler
174:                 # works as follows:
175:                 # - If the bundle is locked then a file .bundle/environment.rb exists
176:                 #   which will setup the load paths.
177:                 # - If the bundle is not locked then the load paths must be set up by
178:                 #   calling Bundler.setup.
179:                 # - Rails 3's boot.rb automatically loads .bundle/environment.rb or
180:                 #   calls Bundler.setup if that's not available.
181:                 # - Other Rack apps might not have a boot.rb but we still want to setup
182:                 #   Bundler.
183:                 # - Some Rails 2 apps might have explicitly added Bundler support.
184:                 #   These apps call Bundler.setup in their preinitializer.rb.
185:                 #
186:                 # So the strategy is as follows:
187: 
188:                 # Our strategy might be completely unsuitable for the app or the
189:                 # developer is using something other than Bundler (or possibly an
190:                 # older version of Bundler which is API incompatible with the latest
191:                 # version), so we let the user manually specify a load path setup file.
192:                 if options["load_path_setup_file"]
193:                         require File.expand(options["load_path_setup_file"])
194:                 
195:                 # The app developer may also override our strategy with this magic file.
196:                 elsif File.exist?('config/setup_load_paths.rb')
197:                         require File.expand_path('config/setup_load_paths')
198:                 
199:                 # If the Bundler lock environment file exists then load that. If it
200:                 # exists then there's a 99.9% chance that loading it is the correct
201:                 # thing to do.
202:                 elsif File.exist?('.bundle/environment.rb')
203:                         require File.expand_path('.bundle/environment')
204: 
205:                 # If the Bundler environment file doesn't exist then there are two
206:                 # possibilities:
207:                 # 1. Bundler is not used, in which case we don't have to do anything.
208:                 # 2. Bundler *is* used, but the gems are not locked and we're supposed
209:                 #    to call Bundler.setup.
210:                 #
211:                 # The existence of Gemfile indicates whether (2) is true:
212:                 elsif File.exist?('Gemfile')
213:                         # In case of Rails 3, config/boot.rb already calls Bundler.setup.
214:                         # However older versions of Rails may not so loading boot.rb might
215:                         # not be the correct thing to do. To be on the safe side we
216:                         # call Bundler.setup ourselves; calling Bundler.setup twice is
217:                         # harmless. If this isn't the correct thing to do after all then
218:                         # there's always the load_path_setup_file option and
219:                         # setup_load_paths.rb.
220:                         require 'rubygems'
221:                         require 'bundler'
222:                         Bundler.setup
223:                 end
224: 
225:                 # Bundler might remove Phusion Passenger from the load path in its zealous
226:                 # attempt to un-require RubyGems, so here we put Phusion Passenger back
227:                 # into the load path.
228:                 if $LOAD_PATH.first != LIBDIR
229:                         $LOAD_PATH.unshift(LIBDIR)
230:                         $LOAD_PATH.uniq!
231:                 end
232:         end
switch_to_user(user) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 423
423:         def switch_to_user(user)
424:                 begin
425:                         if user.is_a?(String)
426:                                 pw = Etc.getpwnam(user)
427:                                 username = user
428:                                 uid = pw.uid
429:                                 gid = pw.gid
430:                         else
431:                                 pw = Etc.getpwuid(user)
432:                                 username = pw.name
433:                                 uid = user
434:                                 gid = pw.gid
435:                         end
436:                 rescue
437:                         return false
438:                 end
439:                 if uid == 0
440:                         return false
441:                 else
442:                         NativeSupport.switch_user(username, uid, gid)
443:                         ENV['HOME'] = pw.dir
444:                         return true
445:                 end
446:         end
to_boolean(value) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 448
448:         def to_boolean(value)
449:                 return !(value.nil? || value == false || value == "false")
450:         end
unmarshal_and_raise_errors(channel, print_exception = nil, app_type = "rails") click to toggle source

Receive status information that was sent to channel by report_app_init_status. If an error occured according to the received information, then an appropriate exception will be raised.

If print_exception evaluates to true, then the exception message and the backtrace will also be printed. Where it is printed to depends on the type of print_exception:

  • If it responds to #puts, then the exception information will be printed using this method.

  • If it responds to #to_str, then the exception information will be appended to the file whose filename equals the return value of the #to_str call.

  • Otherwise, it will be printed to STDERR.

Raises:

  • AppInitError: this class wraps the exception information received through the channel.

  • IOError, SystemCallError, SocketError: these errors are raised if an error occurred while receiving the information through the channel.

     # File lib/phusion_passenger/utils.rb, line 371
371:         def unmarshal_and_raise_errors(channel, print_exception = nil, app_type = "rails")
372:                 args = channel.read
373:                 if args.nil?
374:                         raise EOFError, "Unexpected end-of-file detected."
375:                 end
376:                 status = args[0]
377:                 if status == 'exception'
378:                         child_exception = unmarshal_exception(channel.read_scalar)
379:                         stderr = channel.read_scalar
380:                         exception = AppInitError.new(
381:                                 "Application '#{@app_root}' raised an exception: " <<
382:                                 "#{child_exception.class} (#{child_exception.message})",
383:                                 child_exception,
384:                                 app_type,
385:                                 stderr.empty? ? nil : stderr)
386:                 elsif status == 'exit'
387:                         child_exception = unmarshal_exception(channel.read_scalar)
388:                         stderr = channel.read_scalar
389:                         exception = AppInitError.new("Application '#{@app_root}' exited during startup",
390:                                 child_exception, app_type, stderr.empty? ? nil : stderr)
391:                 else
392:                         exception = nil
393:                 end
394:                 
395:                 if print_exception && exception
396:                         if print_exception.respond_to?(:puts)
397:                                 print_exception(self.class.to_s, child_exception, print_exception)
398:                         elsif print_exception.respond_to?(:to_str)
399:                                 filename = print_exception.to_str
400:                                 File.open(filename, 'a') do |f|
401:                                         print_exception(self.class.to_s, child_exception, f)
402:                                 end
403:                         else
404:                                 print_exception(self.class.to_s, child_exception)
405:                         end
406:                 end
407:                 raise exception if exception
408:         end
unmarshal_exception(data) click to toggle source
     # File lib/phusion_passenger/utils.rb, line 129
129:         def unmarshal_exception(data)
130:                 hash = Marshal.load(data)
131:                 if hash[:is_initialization_error]
132:                         if hash[:child_exception]
133:                                 child_exception = unmarshal_exception(hash[:child_exception])
134:                         else
135:                                 child_exception = nil
136:                         end
137:                         
138:                         case hash[:class]
139:                         when AppInitError.to_s
140:                                 exception_class = AppInitError
141:                         when FrameworkInitError.to_s
142:                                 exception_class = FrameworkInitError
143:                         else
144:                                 exception_class = InitializationError
145:                         end
146:                         return exception_class.new(hash[:message], child_exception)
147:                 else
148:                         begin
149:                                 return Marshal.load(hash[:exception])
150:                         rescue ArgumentError, TypeError
151:                                 return UnknownError.new(hash[:message], hash[:class], hash[:backtrace])
152:                         end
153:                 end
154:         end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.