Utility functions.

Methods
Protected Instance methods
assert_valid_app_root(app_root)

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

    # File lib/passenger/utils.rb, line 51
51:         def assert_valid_app_root(app_root)
52:                 assert_valid_directory(app_root)
53:                 assert_valid_file("#{app_root}/config/environment.rb")
54:         end
assert_valid_directory(path)

Assert that path is a directory. Raises ArgumentError if it isn‘t.

    # File lib/passenger/utils.rb, line 57
57:         def assert_valid_directory(path)
58:                 if !File.directory?(path)
59:                         raise ArgumentError, "'#{path}' is not a valid directory."
60:                 end
61:         end
assert_valid_file(path)

Assert that path is a file. Raises ArgumentError if it isn‘t.

    # File lib/passenger/utils.rb, line 64
64:         def assert_valid_file(path)
65:                 if !File.file?(path)
66:                         raise ArgumentError, "'#{path}' is not a valid file."
67:                 end
68:         end
assert_valid_groupname(groupname)

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

    # File lib/passenger/utils.rb, line 79
79:         def assert_valid_groupname(groupname)
80:                 # If groupname does not exist then getgrnam() will raise an ArgumentError.
81:                 groupname && Etc.getgrnam(groupname)
82:         end
assert_valid_username(username)

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

    # File lib/passenger/utils.rb, line 72
72:         def assert_valid_username(username)
73:                 # If username does not exist then getpwnam() will raise an ArgumentError.
74:                 username && Etc.getpwnam(username)
75:         end
close_all_io_objects_for_fds(file_descriptors_to_close)
    # File lib/passenger/utils.rb, line 84
84:         def close_all_io_objects_for_fds(file_descriptors_to_close)
85:                 ObjectSpace.each_object do |o|
86:                         if o.is_a?(IO)
87:                                 begin
88:                                         if o.closed? && file_descriptors_to_close.include?(o.fileno)
89:                                                 o.close
90:                                         end
91:                                 rescue
92:                                 end
93:                         end
94:                 end
95:         end
lower_privilege(filename, lowest_user = "nobody")

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/passenger/utils.rb, line 227
227:         def lower_privilege(filename, lowest_user = "nobody")
228:                 stat = File.lstat(filename)
229:                 begin
230:                         if !switch_to_user(stat.uid)
231:                                 switch_to_user(lowest_user)
232:                         end
233:                 rescue Errno::EPERM
234:                         # No problem if we were unable to switch user.
235:                 end
236:         end
marshal_exception(exception)
     # File lib/passenger/utils.rb, line 97
 97:         def marshal_exception(exception)
 98:                 data = {
 99:                         :message => exception.message,
100:                         :class => exception.class.to_s,
101:                         :backtrace => exception.backtrace
102:                 }
103:                 if exception.is_a?(InitializationError)
104:                         data[:is_initialization_error] = true
105:                         if exception.child_exception
106:                                 data[:child_exception] = marshal_exception(exception.child_exception)
107:                         end
108:                 else
109:                         begin
110:                                 data[:exception] = Marshal.dump(exception)
111:                         rescue ArgumentError, TypeError
112:                                 e = UnknownError.new(exception.message, exception.class.to_s,
113:                                                         exception.backtrace)
114:                                 data[:exception] = Marshal.dump(e)
115:                         end
116:                 end
117:                 return Marshal.dump(data)
118:         end
normalize_path(path)

Return the absolute version of path. This path is guaranteed to to be "normal", i.e. it doesn‘t contain stuff like ".." or "/", and it correctly respects symbolic links.

Raises SystemCallError if something went wrong. Raises ArgumentError if path is nil.

    # File lib/passenger/utils.rb, line 42
42:         def normalize_path(path)
43:                 raise ArgumentError, "The 'path' argument may not be nil" if path.nil?
44:                 return Pathname.new(path).realpath.to_s
45:         rescue Errno::ENOENT => e
46:                 raise ArgumentError, e.message
47:         end
print_exception(current_location, exception)

Print the given exception, including the stack trace, to STDERR.

current_location is a string which describes where the code is currently at. Usually the current class name will be enough.

     # File lib/passenger/utils.rb, line 151
151:         def print_exception(current_location, exception)
152:                 if !exception.is_a?(SystemExit)
153:                         STDERR.puts(exception.backtrace_string(current_location))
154:                         STDERR.flush
155:                 end
156:         end
report_app_init_status(channel) {|| ...}

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. Returns whether the block succeeded. Exceptions are not propagated, except for SystemExit.

     # File lib/passenger/utils.rb, line 179
179:         def report_app_init_status(channel)
180:                 begin
181:                         yield
182:                         channel.write('success')
183:                         return true
184:                 rescue StandardError, ScriptError, NoMemoryError => e
185:                         if ENV['TESTING_PASSENGER'] == '1'
186:                                 print_exception(self.class.to_s, e)
187:                         end
188:                         channel.write('exception')
189:                         channel.write_scalar(marshal_exception(e))
190:                         return false
191:                 rescue SystemExit
192:                         channel.write('exit')
193:                         raise
194:                 end
195:         end
safe_fork(current_location) {|| ...}

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.

     # File lib/passenger/utils.rb, line 162
162:         def safe_fork(current_location)
163:                 return fork do
164:                         begin
165:                                 yield
166:                         rescue Exception => e
167:                                 print_exception(current_location, e)
168:                         ensure
169:                                 exit!
170:                         end
171:                 end
172:         end
switch_to_user(user)
     # File lib/passenger/utils.rb, line 238
238:         def switch_to_user(user)
239:                 begin
240:                         if user.is_a?(String)
241:                                 pw = Etc.getpwnam(user)
242:                                 username = user
243:                                 uid = pw.uid
244:                                 gid = pw.gid
245:                         else
246:                                 pw = Etc.getpwuid(user)
247:                                 username = pw.name
248:                                 uid = user
249:                                 gid = pw.gid
250:                         end
251:                 rescue
252:                         return false
253:                 end
254:                 if uid == 0
255:                         return false
256:                 else
257:                         # Some systems are broken. initgroups can fail because of
258:                         # all kinds of stupid reasons. So we ignore any errors
259:                         # raised by initgroups.
260:                         begin
261:                                 Process.groups = Process.initgroups(username, gid)
262:                         rescue
263:                         end
264:                         Process::Sys.setgid(gid)
265:                         Process::Sys.setuid(uid)
266:                         ENV['HOME'] = pw.dir
267:                         return true
268:                 end
269:         end
unmarshal_and_raise_errors(channel, app_type = "rails")

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.

Raises:

     # File lib/passenger/utils.rb, line 205
205:         def unmarshal_and_raise_errors(channel, app_type = "rails")
206:                 args = channel.read
207:                 if args.nil?
208:                         raise EOFError, "Unexpected end-of-file detected."
209:                 end
210:                 status = args[0]
211:                 if status == 'exception'
212:                         child_exception = unmarshal_exception(channel.read_scalar)
213:                         #print_exception(self.class.to_s, child_exception)
214:                         raise AppInitError.new(
215:                                 "Application '#{@app_root}' raised an exception: " <<
216:                                 "#{child_exception.class} (#{child_exception.message})",
217:                                 child_exception,
218:                                 app_type)
219:                 elsif status == 'exit'
220:                         raise AppInitError.new("Application '#{@app_root}' exited during startup",
221:                                 nil, app_type)
222:                 end
223:         end
unmarshal_exception(data)
     # File lib/passenger/utils.rb, line 120
120:         def unmarshal_exception(data)
121:                 hash = Marshal.load(data)
122:                 if hash[:is_initialization_error]
123:                         if hash[:child_exception]
124:                                 child_exception = unmarshal_exception(hash[:child_exception])
125:                         else
126:                                 child_exception = nil
127:                         end
128:                         
129:                         case hash[:class]
130:                         when AppInitError.to_s
131:                                 exception_class = AppInitError
132:                         when FrameworkInitError.to_s
133:                                 exception_class = FrameworkInitError
134:                         else
135:                                 exception_class = InitializationError
136:                         end
137:                         return exception_class.new(hash[:message], child_exception)
138:                 else
139:                         begin
140:                                 return Marshal.load(hash[:exception])
141:                         rescue ArgumentError, TypeError
142:                                 return UnknownError.new(hash[:message], hash[:class], hash[:backtrace])
143:                         end
144:                 end
145:         end