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_leave_open)
    # File lib/passenger/utils.rb, line 84
84:         def close_all_io_objects_for_fds(file_descriptors_to_leave_open)
85:                 ObjectSpace.each_object(IO) do |io|
86:                         begin
87:                                 if !file_descriptors_to_leave_open.include?(io.fileno) && !io.closed?
88:                                         io.close
89:                                 end
90:                         rescue
91:                         end
92:                 end
93:         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 225
225:         def lower_privilege(filename, lowest_user = "nobody")
226:                 stat = File.lstat(filename)
227:                 begin
228:                         if !switch_to_user(stat.uid)
229:                                 switch_to_user(lowest_user)
230:                         end
231:                 rescue Errno::EPERM
232:                         # No problem if we were unable to switch user.
233:                 end
234:         end
marshal_exception(exception)
     # File lib/passenger/utils.rb, line 95
 95:         def marshal_exception(exception)
 96:                 data = {
 97:                         :message => exception.message,
 98:                         :class => exception.class.to_s,
 99:                         :backtrace => exception.backtrace
100:                 }
101:                 if exception.is_a?(InitializationError)
102:                         data[:is_initialization_error] = true
103:                         if exception.child_exception
104:                                 data[:child_exception] = marshal_exception(exception.child_exception)
105:                         end
106:                 else
107:                         begin
108:                                 data[:exception] = Marshal.dump(exception)
109:                         rescue ArgumentError, TypeError
110:                                 e = UnknownError.new(exception.message, exception.class.to_s,
111:                                                         exception.backtrace)
112:                                 data[:exception] = Marshal.dump(e)
113:                         end
114:                 end
115:                 return Marshal.dump(data)
116:         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 149
149:         def print_exception(current_location, exception)
150:                 if !exception.is_a?(SystemExit)
151:                         STDERR.puts(exception.backtrace_string(current_location))
152:                         STDERR.flush
153:                 end
154:         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 177
177:         def report_app_init_status(channel)
178:                 begin
179:                         yield
180:                         channel.write('success')
181:                         return true
182:                 rescue StandardError, ScriptError, NoMemoryError => e
183:                         if ENV['TESTING_PASSENGER'] == '1'
184:                                 print_exception(self.class.to_s, e)
185:                         end
186:                         channel.write('exception')
187:                         channel.write_scalar(marshal_exception(e))
188:                         return false
189:                 rescue SystemExit
190:                         channel.write('exit')
191:                         raise
192:                 end
193:         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 160
160:         def safe_fork(current_location)
161:                 return fork do
162:                         begin
163:                                 yield
164:                         rescue Exception => e
165:                                 print_exception(current_location, e)
166:                         ensure
167:                                 exit!
168:                         end
169:                 end
170:         end
switch_to_user(user)
     # File lib/passenger/utils.rb, line 236
236:         def switch_to_user(user)
237:                 begin
238:                         if user.is_a?(String)
239:                                 pw = Etc.getpwnam(user)
240:                                 username = user
241:                                 uid = pw.uid
242:                                 gid = pw.gid
243:                         else
244:                                 pw = Etc.getpwuid(user)
245:                                 username = pw.name
246:                                 uid = user
247:                                 gid = pw.gid
248:                         end
249:                 rescue
250:                         return false
251:                 end
252:                 if uid == 0
253:                         return false
254:                 else
255:                         # Some systems are broken. initgroups can fail because of
256:                         # all kinds of stupid reasons. So we ignore any errors
257:                         # raised by initgroups.
258:                         begin
259:                                 Process.groups = Process.initgroups(username, gid)
260:                         rescue
261:                         end
262:                         Process::Sys.setgid(gid)
263:                         Process::Sys.setuid(uid)
264:                         ENV['HOME'] = pw.dir
265:                         return true
266:                 end
267:         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 203
203:         def unmarshal_and_raise_errors(channel, app_type = "rails")
204:                 args = channel.read
205:                 if args.nil?
206:                         raise EOFError, "Unexpected end-of-file detected."
207:                 end
208:                 status = args[0]
209:                 if status == 'exception'
210:                         child_exception = unmarshal_exception(channel.read_scalar)
211:                         #print_exception(self.class.to_s, child_exception)
212:                         raise AppInitError.new(
213:                                 "Application '#{@app_root}' raised an exception: " <<
214:                                 "#{child_exception.class} (#{child_exception.message})",
215:                                 child_exception,
216:                                 app_type)
217:                 elsif status == 'exit'
218:                         raise AppInitError.new("Application '#{@app_root}' exited during startup",
219:                                 nil, app_type)
220:                 end
221:         end
unmarshal_exception(data)
     # File lib/passenger/utils.rb, line 118
118:         def unmarshal_exception(data)
119:                 hash = Marshal.load(data)
120:                 if hash[:is_initialization_error]
121:                         if hash[:child_exception]
122:                                 child_exception = unmarshal_exception(hash[:child_exception])
123:                         else
124:                                 child_exception = nil
125:                         end
126:                         
127:                         case hash[:class]
128:                         when AppInitError.to_s
129:                                 exception_class = AppInitError
130:                         when FrameworkInitError.to_s
131:                                 exception_class = FrameworkInitError
132:                         else
133:                                 exception_class = InitializationError
134:                         end
135:                         return exception_class.new(hash[:message], child_exception)
136:                 else
137:                         begin
138:                                 return Marshal.load(hash[:exception])
139:                         rescue ArgumentError, TypeError
140:                                 return UnknownError.new(hash[:message], hash[:class], hash[:backtrace])
141:                         end
142:                 end
143:         end