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_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 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 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
[ show source ]
# 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
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 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
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 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
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 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
[ show source ]
# 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
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 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
[ show source ]
# 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