1
1
mirror of https://github.com/NixOS/mobile-nixos.git synced 2024-12-17 13:10:29 +03:00

Merge pull request #234 from samueldr-wip/feature/stage-1-passphrase

stage-1: Add interactive LUKS decrypting
This commit is contained in:
Samuel Dionne-Riel 2020-11-07 20:10:21 -05:00 committed by GitHub
commit 63d49f51ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 666 additions and 74 deletions

View File

@ -75,6 +75,10 @@ module Mounting
[mount_point, task]
end.to_h
auto_depend_mount_points(mount_points)
(Configuration["luksDevices"] or []).each do |mapper, info|
Tasks::Luks.new(info["device"], mapper)
end
end
end

View File

@ -1,44 +1,120 @@
# Progress-reporting plumbing
module Progress
SOCKET = "/run/mobile-nixos-init.socket"
SOCKET_PREFIX = "/run/mobile-nixos-init"
# Starts the queue sockets.
# This is waiting for /run/ to be available.
# It needs to be possible to let some consumers (e.g. splash) alive from
# stage-1 and waiting for fresh messages from stage-2.
# A stage-2 process could ask that splash to "hand-off" to a stage-2 splash.
def self.start()
@progress = 0
$logger.debug("Starting progress IPC through ZeroMQ")
$logger.debug(" -> #{SOCKET}")
@pub = ZMQ::Pub.new("ipc://#{SOCKET}")
$logger.debug(" -> messages: #{SOCKET_PREFIX}")
@messages_socket = ZMQ::Pub.new("ipc://#{SOCKET_PREFIX}-messages")
$logger.debug(" -> replies: #{SOCKET_PREFIX}")
@replies_socket = ZMQ::Sub.new("ipc://#{SOCKET_PREFIX}-replies", "")
end
# Prefer not sending messages directly, rather use the helpers.
def self.publish(msg)
msg = msg.to_json
if @pub
# Given values (in a Hash), it will update the state with them, and send the
# updated state to the messages queue.
# +nil+ values are compacted out of the state.
def self.update(values)
@state ||= {}
@state.merge!(values).compact!
send_state()
end
# Get a specific value from the state.
# This should be done as little as possible.
def self.get(attr)
@state ||= {}
@state[attr]
end
# See +#get+
def self.[](name)
get(name)
end
# Send the current state over the messages socket.
def self.send_state()
msg = @state.to_json
if @messages_socket
$logger.debug("[send] #{msg}")
@pub.send(msg)
@messages_socket.send(msg)
else
$logger.debug("[send] Couldn't send #{msg}")
$logger.debug("[send] Socket not open yet.")
$logger.debug("[send] Couldn't send: #{msg}")
end
end
# Sets the progress to a specific amount
def self.set(amount)
@progress = amount
publish({
progress: @progress,
})
end
# Executes the given block, showing the message beforehand, and removing the
# message once done.
def self.with_message(msg)
publish({
progress: @progress,
label: msg,
})
yield
publish({
progress: @progress,
def self.exec_with_message(label)
previous = get(:label)
update({label: label})
ret = yield
update({label: previous})
ret
end
def self.ask(placeholder, label: nil)
identifier = "0x#{Random.rand(0xFFFFF).to_s(16)}"
previous_label = get(:label)
Progress.update({label: label}) if label
update(command: {
name: "ask",
identifier: identifier,
placeholder: placeholder,
})
value = loop do
# Keep progress state updated for processes attaching late.
send_state()
value =
each_replies do |reply|
# A reply for the current question?
if reply and reply["type"] == "reply" and reply["identifier"] == identifier
break reply["value"]
else
nil
end
end
break value if value
# Leave some breathing room to the CPU!
sleep(0.1)
end
update({label: previous_label})
value
end
# Read one reply
# If none are available, returns nil
def self.read_reply()
begin
msg = @replies_socket.recv(LibZMQ::DONTWAIT).to_str
$logger.debug("[recv] #{msg}")
JSON.parse(msg)
rescue Errno::EWOULDBLOCK
# No message?
nil
end
end
# Reads replies until there are none
def self.each_replies()
loop do
msg = read_reply
break unless msg
yield msg
end
end
end

View File

@ -51,6 +51,30 @@ module System
Kernel.spawn(*args)
end
# Runs a long-running task in the background while we keep the progress
# reporting active.
def self.run_long_running(*args)
pretty_command = prettify_command(*args)
pid = System.spawn(*args)
ret = nil
loop do
# Update progress
Progress.send_state()
# Look at the status
break if ret = Process.wait(pid, Process::WNOHANG)
# Don't loop too tightly
sleep(0.1)
end
status = $?.exitstatus
if status == 127
raise CommandNotFound.new("Command not found... `#{pretty_command}` (#{status})")
elsif !$?.success?
raise CommandError.new("Command failed... `#{pretty_command}` (#{status})")
end
end
# Discovers the location of given program name.
def self.which(program_name)
ENV["PATH"].split(":").each do |path|

View File

@ -43,7 +43,7 @@ module Tasks
# Update the current progress
count = @tasks.length.to_f
Progress.set((100 * (1 - (todo.length / count))).ceil)
Progress.update({progress: (100 * (1 - (todo.length / count))).ceil})
todo.each do |task|
if task._try_run_task then

View File

@ -11,7 +11,7 @@ class Tasks::AutoResize < Task
def run()
log("Resizing #{@device}...")
if @type.match(/^ext[234]$/)
Progress.with_message("Verifying #{@device}...") do
Progress.exec_with_message("Verifying #{@device}...") do
# TODO: Understand the actual underlying issue with e2fsck.
# It seems `e2fsck` succeeds, according to the output, but has a >0 exit
# status. Running it again in those situations is a no-op, which is weird
@ -19,14 +19,14 @@ class Tasks::AutoResize < Task
# This is why we unconditionally run it once, then twice.
# The second will hopefully abort the boot if it fails too.
begin
System.run("e2fsck", "-fp", @device)
System.run_long_running("e2fsck", "-fp", @device)
rescue System::CommandError
$logger.info("Re-running e2fsc...")
System.run("e2fsck", "-fp", @device)
System.run_long_running("e2fsck", "-fp", @device)
end
end
Progress.with_message("Resizing #{@device}...") do
System.run("resize2fs", "-f", @device)
Progress.exec_with_message("Resizing #{@device}...") do
System.run_long_running("resize2fs", "-f", @device)
end
else
$logger.warn("Cannot resize #{@type}... filesystem left untouched.")

64
boot/init/tasks/luks.rb Normal file
View File

@ -0,0 +1,64 @@
# Opens LUKS devices
class Tasks::Luks < Task
attr_reader :source
attr_reader :mapper
TRIES = 10
class ExistingLuksTask < StandardError
end
class CouldNotUnlock < StandardError
end
def self.register(mapper, instance)
@registry ||= {}
unless @registry[mapper].nil? then
raise ExistingLuksTask.new("LUKS task for '#{mapper}' already exists.")
end
@registry[mapper] = instance
end
def self.registry()
@registry
end
def initialize(source, mapper)
@source = source
@mapper = mapper
add_dependency(:Task, Tasks::UDev.instance)
add_dependency(:Files, source)
add_dependency(:Mount, "/run")
add_dependency(:Target, :Environment)
self.class.register(@mapper, self)
end
def run()
FileUtils.mkdir_p("/run/cryptsetup")
TRIES.times do
passphrase = Progress.ask("Passphrase for #{mapper}")
begin
Progress.exec_with_message("Checking...") do
# TODO: implement with process redirection rather than shelling out
System.run("echo #{passphrase.shellescape} | exec cryptsetup luksOpen #{source.shellescape} #{mapper.shellescape}")
end
Progress.update({label: nil})
# If we're there, we're done!
return
rescue System::CommandError
Progress.update({label: "Wrong passphrase given..."})
end
end
# We failed multiple times.
raise CouldNotUnlock.new("Could not unlock #{source}; tried #{TRIES} times.")
end
def name()
"#{super}(#{source}, #{mapper})"
end
end

View File

@ -24,14 +24,17 @@ class Tasks::Splash < SingletonTask
# Implementation details-y; ask for the splash applet to be exited.
def quit(reason)
# Ensures the progress is shown
Progress.update({progress: 100, label: reason})
# Command it to quit
Progress.update({command: {name: "quit"}})
# Ensures that if for any reason the splash didn't start in time for the
# socket to listen to this message, that we'll be quitting it.
loop do
# Ensures the progress is shown
Progress.publish({progress: 100, label: reason})
# Command it to quit
Progress.publish("quit")
# Repeatedly send the current state (which has the quit command).
Progress.send_state()
# If it has quit, break out!
break if Process.wait(@pid, Process::WNOHANG)

View File

@ -0,0 +1,61 @@
# Wraps a raw +lv_keyboard+ in minimal helpers
# This is intended to be used as a singleton instance, where you can
# "re-parent" the keyboard as needed.
class Keyboard < LVGUI::Widget
include Singleton
private
def initialize()
@shown = false
# Attach the keyboard to the current active screen, by default.
super(LVGL::LVKeyboard.new(LVGL::LVDisplay.get_scr_act()))
set_cursor_manage(true)
get_style(LVGL::KB_STYLE::BG).dup.tap do |style|
set_style(LVGL::KB_STYLE::BG, style)
padding = 4
style.body_padding_top = padding
style.body_padding_left = padding
style.body_padding_right = padding
style.body_padding_bottom = padding
style.body_padding_inner = padding
end
set_y(get_parent.get_height())
end
public
def set_height(value)
super(value)
_set_position()
end
def show()
_animate_y(get_parent.get_height() - get_height())
end
def hide()
_animate_y(get_parent.get_height)
end
def _set_position()
if @shown
_animate_y(get_parent.get_height() - get_height())
else
_animate_y(get_parent.get_height())
end
end
def _animate_y(ending)
LVGL::LVAnim.new().tap do |anim|
anim.set_exec_cb(self, :lv_obj_set_y)
anim.set_time(300, 0)
anim.set_values(get_y(), ending)
anim.set_path_cb(LVGL::LVAnim::Path::EASE_OUT)
# Launch the animation
anim.create()
end
end
end

View File

@ -60,6 +60,7 @@ class ProgressBar < LVGUI::Widget
end
def progress=(val)
val = 100 if val > 100
@changed = true
@progress_amount = val
refresh_progress()

View File

@ -0,0 +1,90 @@
# Wraps a raw +lv_ta+ in minimal helpers
class TextArea < LVGUI::Widget
attr_reader :hidden
def initialize(parent)
super(LVGL::LVTextArea.new(parent))
@hidden = false
set_text("")
set_placeholder_text("")
set_pwd_mode(true)
set_one_line(true)
set_opa_scale_enable(true)
get_style(LVGL::TA_STYLE::BG).dup.tap do |style|
set_style(LVGL::TA_STYLE::BG, style)
style.body_main_color = 0xFF000000
style.body_grad_color = 0xFF000000
style.body_radius = 5
style.body_border_color = 0xFFFFFFFF
style.body_border_width = 3
style.body_border_opa = 255
style.text_color = 0xFFFFFFFF
end
get_style(LVGL::TA_STYLE::PLACEHOLDER).dup.tap do |style|
set_style(LVGL::TA_STYLE::PLACEHOLDER, style)
style.text_color = 0xFFAAAAAA
end
set_cursor_type(get_cursor_type() | LVGL::CURSOR::HIDDEN)
self.event_handler = -> (event) do
return if hidden()
case event
when LVGL::EVENT::CLICKED
Keyboard.instance.set_ta(self)
Keyboard.instance.show()
when LVGL::EVENT::INSERT
# Not exactly right, but right enough.
char = LVGL::FFI.lv_event_get_data().to_str(1)
# Assume there is only one input.
# Also assume Enter sends; that it is a single line.
if char == "\n"
Keyboard.instance.set_ta(nil)
Keyboard.instance.hide()
# Create a new string
# get_text() gives us a Fiddle::Pointer (leaky abstraction!!!)
value = "#{get_text()}"
@on_submit.call(value) if @on_submit
hide()
end
#else
# puts "Unhandled event for #{self}: #{LVGL::EVENT.from_value(event)}"
end
end
end
def show()
@hidden = false
LVGL::LVAnim.new().tap do |anim|
anim.set_exec_cb(self, :lv_obj_set_opa_scale)
anim.set_time(FADE_LENGTH, 0)
anim.set_values(0, 255)
anim.set_path_cb(LVGL::LVAnim::Path::EASE_OUT)
# Launch the animation
anim.create()
end
end
def hide(skip_animation: false)
@hidden = true
if skip_animation
set_opa_scale(0)
return
end
LVGL::LVAnim.new().tap do |anim|
anim.set_exec_cb(self, :lv_obj_set_opa_scale)
anim.set_time(FADE_LENGTH, 0)
anim.set_values(255, 0)
anim.set_path_cb(LVGL::LVAnim::Path::EASE_IN)
# Launch the animation
anim.create()
end
end
def on_submit=(cb)
@on_submit = cb
end
end

View File

@ -13,22 +13,28 @@ end
class UI
attr_reader :screen
attr_reader :progress_bar
attr_reader :ask_identifier
# As this is not using BaseWindow, LVGUI::init isn't handled for us.
LVGUI.init()
def initialize()
add_screen
add_page
# Biggest of horizontal or vertical; a percent.
@unit = ([@screen.get_width, @screen.get_height].max * 0.01).ceil
add_logo
add_progress_bar
add_label
add_textarea
add_keyboard
add_cover # last
end
def add_label()
@label = LVGL::LVLabel.new(@screen)
@label = LVGL::LVLabel.new(@page)
@label.get_style(LVGL::LABEL_STYLE::MAIN).dup.tap do |style|
@label.set_style(LVGL::LABEL_STYLE::MAIN, style)
style.text_color = 0xFFFFFFFF
@ -36,7 +42,7 @@ class UI
@label.set_long_mode(LVGL::LABEL_LONG::BREAK)
@label.set_align(LVGL::LABEL_ALIGN::CENTER)
@label.set_width(@screen.get_width * 0.9)
@label.set_width(@page.get_width * 0.9)
@label.set_pos(*center(@label, 0, 5*@unit))
@label.set_text("")
end
@ -48,15 +54,15 @@ class UI
file = "./logo.svg" if File.exist?("./logo.svg")
return unless file
if @screen.get_height > @screen.get_width
if @page.get_height > @page.get_width
# 80% of the width
LVGL::Hacks::LVNanoSVG.resize_next_width((@screen.get_width * 0.8).to_i)
LVGL::Hacks::LVNanoSVG.resize_next_width((@page.get_width * 0.8).to_i)
else
# 15% of the height
LVGL::Hacks::LVNanoSVG.resize_next_height((@screen.get_height * 0.15).to_i)
LVGL::Hacks::LVNanoSVG.resize_next_height((@page.get_height * 0.15).to_i)
end
@logo = LVGL::LVImage.new(@screen)
@logo = LVGL::LVImage.new(@page)
@logo.set_src(file)
# Position the logo
@ -64,9 +70,9 @@ class UI
end
def add_progress_bar()
@progress_bar = ProgressBar.new(@screen)
@progress_bar = ProgressBar.new(@page)
@progress_bar.set_height(3 * @unit)
@progress_bar.set_width(@screen.get_width * 0.7)
@progress_bar.set_width(@page.get_width * 0.7)
@progress_bar.set_pos(*center(@progress_bar))
end
@ -81,14 +87,27 @@ class UI
end
end
def add_page()
@page = LVGL::LVContainer.new(@screen)
@page.set_width(@screen.get_width)
@page.set_height(@screen.get_height)
@page.get_style(LVGL::CONT_STYLE::MAIN).dup.tap do |style|
@page.set_style(LVGL::CONT_STYLE::MAIN, style)
style.body_main_color = 0xFF000000
style.body_grad_color = 0xFF000000
style.body_border_width = 0
end
end
# Used to handle fade-in/fade-out
# This is because opacity handles multiple overlaid objects wrong.
def add_cover()
@cover = LVGL::LVObject.new(@screen)
# Make it so we can use the opacity to fade in/out
@cover.set_opa_scale_enable(1)
@cover.set_opa_scale_enable(true)
@cover.set_width(@screen.get_width())
@cover.set_height(@screen.get_height())
@cover.set_click(false)
@cover.get_style().dup.tap do |style|
@cover.set_style(style)
@ -101,6 +120,22 @@ class UI
end
end
def add_textarea()
@ta = TextArea.new(@page)
@ta.set_width(@page.get_width * 0.9)
@ta.set_pos(*center(@ta, 0, @unit * 14))
# Always present, but initially hidden
@ta.hide(skip_animation: true)
end
def add_keyboard()
@keyboard = Keyboard.instance()
# The keyboard is not added to the page; the page holds the elements that
# may move to ensure they're not covered by the keyboard.
@keyboard.set_parent(@screen)
@keyboard.set_height(@screen.get_width * 0.55)
end
def set_progress(amount)
progress_bar.progress = amount
end
@ -109,6 +144,39 @@ class UI
@label.set_text(text)
end
# +cb+ is a proc that wille be +#call+'d with the text once submitted.
def ask_user(placeholder: "", identifier: , cb:)
return if identifier == @ask_identifier
@ask_identifier = identifier
@ta.set_placeholder_text(placeholder)
@ta.show()
@keyboard.set_ta(@ta)
@keyboard.show()
bottom_space = @screen.get_height() - (@ta.get_y() + @ta.get_height())
delta = bottom_space - @keyboard.get_height() - 3*@unit
offset_page(delta) if delta < 0
@ta.on_submit = ->(value) do
@ta.set_text("")
offset_page(0)
cb.call(value)
end
end
def offset_page(delta)
LVGL::LVAnim.new().tap do |anim|
anim.set_exec_cb(@page, :lv_obj_set_y)
anim.set_time(300, 0)
anim.set_values(@page.get_y(), delta)
anim.set_path_cb(LVGL::LVAnim::Path::EASE_OUT)
# Launch the animation
anim.create()
end
end
# Fade-in animation
# Note that this looks like inverted logic because it is!
# We're actually fading-out the cover!

View File

@ -9,14 +9,17 @@ FADE_LENGTH = 400
PROGRESS_UPDATE_LENGTH = 500
VERBOSE = !!Args.get(:verbose, false)
SOCKET = File.expand_path(Args.get(:socket, "/run/mobile-nixos-init.socket"))
SOCKET = File.expand_path(Args.get(:socket, "/run/mobile-nixos-init"))
# Create the UI
ui = UI.new
# Socket for status updates
puts "[splash] Listening on: ipc://#{SOCKET}"
$sub = ZMQ::Sub.new("ipc://#{SOCKET}", "")
puts "[splash] Listening on: ipc://#{SOCKET}-messages"
$messages = ZMQ::Sub.new("ipc://#{SOCKET}-messages", "")
puts "[splash] Replying on: ipc://#{SOCKET}-replies"
$replies = ZMQ::Pub.new("ipc://#{SOCKET}-replies")
# Initial fade-in
ui.fade_in()
@ -28,7 +31,7 @@ LVGUI.main_loop do
# Empty all messages from the queue before continuing.
loop do
begin
msg = JSON.parse($sub.recv(LibZMQ::DONTWAIT).to_str)
msg = JSON.parse($messages.recv(LibZMQ::DONTWAIT).to_str)
rescue Errno::EWOULDBLOCK
# No messages left? break out!
break
@ -39,14 +42,33 @@ LVGUI.main_loop do
p msg
end
# We might have a special command, if we got a String rather than a Hash.
if msg.is_a? String then
if msg == "quit"
# We might have a special command; handle it.
if msg["command"] then
command = msg["command"]
case command["name"]
when "quit"
ui.quit!
else
$stderr.puts "[splash] Unexpected command #{msg}..."
when "ask"
ui.ask_user(placeholder: command["placeholder"], identifier: command["identifier"], cb: ->(value) do
msg = {
type: "reply",
identifier: command["identifier"],
value: value,
}.to_json
if VERBOSE
print "[splash:send] "
p msg
end
$replies.send(msg)
end)
else
$stderr.puts "[splash] Unexpected command #{command.to_json}..."
end
end
# Update the UI...
# First updating the current progress
@ -60,7 +82,6 @@ LVGUI.main_loop do
end
end
end
end
$stderr.puts "[splash] Broke out of the rendering loop. That's not supposed to happen."
exit(1)

View File

@ -0,0 +1,13 @@
Testing examples
================
These examples are made specifically to test features.
They are not to be used as a normal system, as they likely have some fatal
flaws, either usability-wise (e.g. not usable at all) or security-wise (e.g.
default passwords).
More details are available inside each testing systems directories.
Though, please refer to those systems as reference implementation for
validating changes around their features under test!

View File

@ -0,0 +1,45 @@
`qemu-cryptsetup`
=================
What does this test?
--------------------
Using the `hello` system, pre-configured to use the `qemu` system type.
This tests:
- Encryption passphrase at boot
**This test is manual.**
The passphrase in use is:
```
1234
```
Why is this scary?
------------------
- Secrets in the store!
- Well-known insecure passphrase
How is success defined?
-----------------------
The `hello` system applet is booted-to after the user supplies the encryption
passphrase during boot.
How is success defined?
-----------------------
Assuming you are `cd`'d into the root of a Mobile NixOS checkout:
```
nix-build ./examples/testing/qemu-cryptsetup && ./result
```
As always, be mindful of your `NIX_PATH`.

View File

@ -0,0 +1,68 @@
{ config, lib, pkgs, ... }:
let
# This is not a secure or safe way to create an encrypted drive in a build.
# This is SOLELY for testing purposes.
passphrase = "1234";
uuid = "12345678-1234-1234-1234-123456789abc"; # heh
# We are re-using the raw filesystem from the hello system.
rootfsExt4 = (
import ../../hello { device = config.mobile.device.name; }
).build.rootfs;
# This is not a facility from the disk images builder because **it is really
# insecure to use**.
# So, for now, we have an implementation details-y way of producing an
# encrypted rootfs.
encryptedRootfs = pkgs.vmTools.runInLinuxVM (
pkgs.runCommand "encrypted-rootfs" {
buildInputs = [ pkgs.cryptsetup ];
} ''
(PS4=" $ "; set -x
mkdir -p /run/cryptsetup
mkdir -p $out
cd $out
slack=32 # MiB
# Some slack space we'll append to the raw fs
# Used by `--reduce-device-size` read cryptsetup(8).
dd if=/dev/zero of=tmp.img bs=1024 count=$((slack*1024))
# Catting both to ensure it's writable, and to add some slack space at
# the end
cat ${rootfsExt4}/${rootfsExt4.label}.img tmp.img > encrypted.img
rm tmp.img
echo ${builtins.toJSON passphrase} | cryptsetup \
reencrypt \
--encrypt ./encrypted.img \
--reduce-device-size $((slack*1024*1024))
#echo YES |
cryptsetup luksUUID --uuid=${builtins.toJSON uuid} ./encrypted.img
)
''
);
in
{
boot.initrd.luks.devices = {
LUKS-MOBILE-ROOTFS = {
device = "/dev/disk/by-uuid/${uuid}";
};
};
fileSystems = {
"/" = {
device = "/dev/mapper/LUKS-MOBILE-ROOTFS";
fsType = "ext4";
};
};
# Instead of the (mkDefault) rootfs, provide our raw encrypted rootfs.
mobile.generatedFilesystems.rootfs = {
raw = encryptedRootfs;
};
}

View File

@ -0,0 +1,11 @@
let
device = "qemu-x86_64";
system-build = import ../../../. {
inherit device;
configuration = [ { imports = [
../../hello/configuration.nix
./configuration.nix
]; } ];
};
in
system-build.build.default

View File

@ -62,6 +62,14 @@ let
limitations in CI situations.
'';
};
raw = lib.mkOption {
internal = true;
type = types.nullOr types.package;
default = null;
description = ''
Use an output directly rather than creating it from the options.
'';
};
};
config = {
};
@ -79,7 +87,8 @@ in
};
config = {
system.build.generatedFilesystems = lib.attrsets.mapAttrs (name: {type, id, label, ...} @ attrs:
system.build.generatedFilesystems = lib.attrsets.mapAttrs (name: {raw, type, id, label, ...} @ attrs:
if raw != null then raw else
filesystemFunctions."${type}" (attrs // {
name = label;
partitionID = id;

27
modules/luks.nix Normal file
View File

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
let
inherit (config.boot.initrd) luks;
in
lib.mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) {
mobile.boot.stage-1 = {
bootConfig = {
luksDevices = luks.devices;
};
kernel = {
modules = [
"dm_mod" "dm_crypt" "cryptd" "input_leds"
] ++ luks.cryptoModules
;
};
extraUtils = [
{ package = pkgs.cryptsetup; }
# dmsetup is required for device mapper stuff to work in stage-1.
{ package = lib.getBin pkgs.lvm2; binaries = [
"lvm" "dmsetup"
];}
];
};
}

View File

@ -30,6 +30,7 @@
./initrd-vendor.nix
./initrd.nix
./internal.nix
./luks.nix
./mobile-device.nix
./nixpkgs.nix
./quirks

View File

@ -16,11 +16,17 @@ let
install_package = set:
let
pkg = if set ? type && set.type == "derivation" then set else set.package;
binaries = if set ? binaries then set.binaries else [ "*" ];
in
''
for BIN in ${pkg}/{s,}bin/*; do
copy_bin_and_libs $BIN
(concat (map (path: ''
for BIN in ${pkg}/{s,}bin/${path}; do
if [ -e "$BIN" ]; then
copy_bin_and_libs "$BIN"
fi
done
'') binaries ))
+
''
${if set ? extraCommand then set.extraCommand else ""}
'';
install_packages = concat(map (install_package) packages);

View File

@ -18,8 +18,8 @@ mrbgems.mkGem {
src = fetchFromGitHub {
repo = "mruby-lvgui";
owner = "mobile-nixos";
rev = "ab7cf5b1b2e318a4bf5fc973507eb842dce80214";
sha256 = "09h9f3xlbvxdwpmzpf7whq0gphiv68842shy03ld712fw25393jx";
rev = "f1bb1dd9b2c5aa3d3df4fcc41ca706f426d182a8";
sha256 = "0ybjkzg743d21rn3q0vi0fa9zwp3ym9zw2q5ym24wc7gxdspjcjs";
};
gemBuildInputs = [

View File

@ -24,13 +24,13 @@ let
in
stdenv.mkDerivation {
pname = "lvgui";
version = "2020-07-25";
version = "2020-11-01";
src = fetchFromGitHub {
repo = "lvgui";
owner = "mobile-nixos";
rev = "0dc257d07271fad023a5e6e9ac42222d2397c5cf";
sha256 = "1zb7naamqfzcsfi5809c93f1ygxp4w3aiw6172bpmnk1vxdchwsh";
rev = "4f8af498a81bd669d42ce3b370fc66fe4ec681b5";
sha256 = "00rik18c3c3l4glzh2azg90cwvp56s4wnski86rsn00bxslia5ma";
};
# Document `LVGL_ENV_SIMULATOR` in the built headers.