Utility functions.
- assert_valid_app_root
- assert_valid_directory
- assert_valid_file
- assert_valid_groupname
- assert_valid_username
- close_all_io_objects_for_fds
- lower_privilege
- marshal_exception
- normalize_path
- print_exception
- report_app_init_status
- safe_fork
- switch_to_user
- unmarshal_and_raise_errors
- unmarshal_exception
Assert that app_root is a valid Ruby on Rails application root. Raises ArgumentError if that is not the case.
[ show source ]
# 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 that path is a directory. Raises ArgumentError if it isn‘t.
[ show source ]
# 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 that path is a file. Raises ArgumentError if it isn‘t.
[ show source ]
# 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 that groupname is a valid group name. Raises ArgumentError if that is not the case.
[ show source ]
# 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 that username is a valid username. Raises ArgumentError if that is not the case.
[ show source ]
# 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
[ show source ]
# 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 the current process‘s privilege to the owner of the given file. No exceptions will be raised in the event that privilege lowering fails.
[ show source ]
# 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
[ show source ]
# 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
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.
[ show source ]
# 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 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.
[ show source ]
# 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
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.
[ show source ]
# 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
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.
[ show 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
[ show source ]
# 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
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:
- AppInitError
- IOError, SystemCallError, SocketError
[ show source ]
# 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
[ show source ]
# 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