{ config, lib, pkgs, ... }: let inherit (lib) mkIf mkMerge mkOption optionalString types; inherit (config.mobile.outputs) extraUtils; cfg = config.mobile.boot.stage-1.bootlog; in { options.mobile.boot.stage-1.bootlog = { enable = mkOption { type = types.bool; default = config.mobile.boot.stage-1.enable; description = lib.mdDoc '' Enables bootlogd logging multiplexer. ''; }; kmsg = mkOption { type = types.bool; default = false; description = lib.mdDoc '' Enables logging to /dev/kmsg. Note that this may render switching to stage-2 inoperable. ''; }; }; config.mobile.boot.stage-1 = mkMerge [ (mkIf cfg.kmsg { # This is a bit buggy: # * fast burst of \n-delimited output will not work as expected # * `printk.devkmsg=on` is required on the kernel cmdline for better results # A better implementation would be to have a binary who's sole purpose is to # duplicate the stdout/stderr to /dev/kmsg while still outputting them to # stdout/stderr as they do currently. # # Reminder: redirecting to kmsg is useful *mainly* for getting data through # console_ramoops on devices without serial and without any other means to # get the initial data out. earlyInitScripts = '' ${extraUtils}/bin/mknod /.kmsg c 1 11 exec > /.kmsg 2>&1 ''; }) (mkIf cfg.enable { earlyInitScripts = '' ( export LD_LIBRARY_PATH="${extraUtils}/lib" export PATH="${extraUtils}/bin" echo "Prepping to launch bootlog..." # I'd really like *not* to prep mounts here, but bootlogd requires them. # If we wait, we'll lose even more output. echo "(Preparing /dev for /dev/pts)" mkdir -p /dev mount -t devtmpfs devtmpfs /dev echo "(Preparing /dev/pts to identify console)" mkdir -p /dev/pts mount -t devpts devpts /dev/pts bootlogd & # Ugh, bootlogd takes a bit of time to be ready. # Let's not drop logs sleep 0.5 ) echo "Early logging started..." ''; tasks = [ (pkgs.writeText "bootlogd-task.rb" '' class Tasks::KickstartBootlogd < SingletonTask def initialize() add_dependency(:Target, :Environment) add_dependency(:Mount, "/proc") add_dependency(:Mount, "/dev") add_dependency(:Mount, "/run") add_dependency(:Mount, "/dev/pts") Targets[:Devices].add_dependency(:Task, self) end def run() # bootlogd is already waiting for the file to appear. FileUtils.mkdir_p("/run/log/") File.write("/run/log/stage-1.log", "") end end '') ]; extraUtils = [ { package = pkgs.bootlogd; } ]; }) ]; config.boot.postBootCommands = mkIf cfg.enable '' echo "Quitting bootlogd" ${pkgs.procps}/bin/pkill -x bootlogd ''; }