mirror of
https://github.com/NixOS/mobile-nixos.git
synced 2024-12-18 05:21:47 +03:00
c4f76db65f
This is used to ensure mount points are mounted with the right options, if for some reason they were already mounted. Reasons they could be mounted? They could have been required for things like logging the boot.
167 lines
5.0 KiB
Ruby
167 lines
5.0 KiB
Ruby
# "System" helpers.
|
|
module System
|
|
class CommandError < StandardError
|
|
end
|
|
class CommandNotFound < CommandError
|
|
end
|
|
class MountError < StandardError
|
|
end
|
|
|
|
def self.prettify_command(*args)
|
|
args = args.dup
|
|
# Removes the environment hash, if present.
|
|
args.shift if args.first.is_a?(Hash)
|
|
if args.length == 1
|
|
args.first
|
|
else
|
|
args.shelljoin
|
|
end
|
|
end
|
|
|
|
# Runs and pretty-prints a command. Parameters and shelling-out have the same
|
|
# meaning as with +Kernel#spawn+; one parameter is shelling-out, multiple is
|
|
# direct +exec+.
|
|
#
|
|
# @param args [Array<String>] Command and parameters
|
|
# @raise [System::CommandNotFound] on exit status 127, commonly used for command not found.
|
|
# @raise [System::CommandError] on any other exit status.
|
|
def self.run(*args)
|
|
pretty_command = prettify_command(*args)
|
|
$logger.debug(" $ #{pretty_command}")
|
|
unless system(*args)
|
|
raise CommandError.new("Could not execute `#{pretty_command}`, status nil") if $?.nil?
|
|
status = $?.exitstatus
|
|
if status == 127
|
|
raise CommandNotFound.new("Command not found... `#{pretty_command}` (#{status})")
|
|
else
|
|
raise CommandError.new("Command failed... `#{pretty_command}` (#{status})")
|
|
end
|
|
end
|
|
end
|
|
|
|
# Execs and pretty-prints a command.
|
|
def self.exec(*args)
|
|
$logger.debug(" $ #{prettify_command(*args)}")
|
|
Kernel.exec(*args)
|
|
end
|
|
|
|
# Thin wrapper over +Kernel#exec+, but with printing.
|
|
def self.spawn(*args)
|
|
$logger.debug(" $ #{prettify_command(*args)} &")
|
|
Kernel.spawn(*args)
|
|
end
|
|
|
|
# Discovers the location of given program name.
|
|
def self.which(program_name)
|
|
ENV["PATH"].split(":").each do |path|
|
|
full = File.join(path, program_name)
|
|
return full if File.stat(full).executable? && !File.directory?(full)
|
|
end
|
|
end
|
|
|
|
def self.write(file, contents)
|
|
$logger.debug("echo #{contents.to_json} > #{file}")
|
|
File.write(file, contents)
|
|
end
|
|
|
|
# Lists all mount points.
|
|
# This handles temporarily mounting /proc if required.
|
|
# This will hide /proc in those instances.
|
|
# The return format is a hash, with keys being mount point paths,
|
|
# and values being their respective line from /proc/mounts.
|
|
def self.mount_points()
|
|
# This is the most horrible hack :(
|
|
mounted_proc = false
|
|
unless File.exists?("/proc/mounts")
|
|
$logger.debug("Temporarily mounting /proc...")
|
|
FileUtils.mkdir_p("/proc")
|
|
run("mount", "-t", "proc", "proc", "/proc")
|
|
mounted_proc = true
|
|
end
|
|
result = File.read("/proc/mounts").split("\n")
|
|
run("umount", "-f", "/proc") if mounted_proc
|
|
|
|
result = result.map do |line|
|
|
[
|
|
# Safe to split by space, spaces are escaped in the mount point.
|
|
# "/tmp/test a b" ->> tmpfs /tmp/test\040a\040b tmpfs rw,relatime 0 0
|
|
line.split(" ")[1].gsub('\040', " "),
|
|
line
|
|
]
|
|
end.to_h
|
|
|
|
# We mounted /proc? Hide it! We've now unmounted it.
|
|
result.delete("/proc") if mounted_proc
|
|
|
|
result
|
|
end
|
|
|
|
# Mounts a filesystem of type +type+ on +dest+.
|
|
#
|
|
# The +source+ parameter is optional, though kept first to keep a coherent
|
|
# order compared to the actual +mount+ command.
|
|
#
|
|
# @overload mount(source, dest, type:)
|
|
# @param source [String] (Optional) mount source, defaults to type
|
|
# @param dest [String] Destination path to mount to
|
|
# @param type [String] Type of the mount (+-t+).
|
|
# @param options [Array<String>] Mount options (+-o+).
|
|
# @overload mount(dest, type:)
|
|
# @param dest [String] Destination path to mount to
|
|
# @param type [String] Type of the mount (+-t+).
|
|
# @param options [Array<String>] Mount options (+-o+).
|
|
def self.mount(source, dest = nil, type: nil, options: nil)
|
|
# Fill-in the "reversed" optional parameters.
|
|
unless dest
|
|
dest = source
|
|
source = type
|
|
end
|
|
|
|
if source.nil? and type.nil?
|
|
raise MountError.new("Cannot mount when missing both source and type.")
|
|
end
|
|
|
|
args = []
|
|
if type
|
|
args << "-t"
|
|
args << type
|
|
end
|
|
if options
|
|
args << "-o"
|
|
args << options.join(",")
|
|
end
|
|
args << source
|
|
args << dest
|
|
|
|
# We may have some mountpoints already mounted from, e.g. early logging in
|
|
# /run/log... If we're not careful we will mount over the existing mount
|
|
# point and hide the resulting files.
|
|
# Side-step by re-mounting to have the appropriate options.
|
|
if mount_points.keys.include?(dest)
|
|
$logger.debug("#{dest} already mounted, remounting.")
|
|
run("mount", "-o", "remount", *args)
|
|
else
|
|
run("mount", *args)
|
|
end
|
|
end
|
|
|
|
def self.sad_phone(color)
|
|
begin
|
|
System.run("ply-image", "--clear=0x#{color}", "/sad-phone.png")
|
|
rescue CommandError
|
|
end
|
|
end
|
|
|
|
def self.failure(code, message="(No details given)", color: "000000")
|
|
sad_phone(color)
|
|
$logger.fatal("#{code}: #{message}")
|
|
shell if respond_to?(:shell)
|
|
sleep(Configuration["boot"]["fail"]["delay"])
|
|
hard_reboot if Configuration["boot"]["fail"]["reboot"]
|
|
end
|
|
|
|
def self.hard_reboot()
|
|
System.write("/proc/sysrq-trigger", "b\n")
|
|
end
|
|
end
|