Driver install unattended through the Power Helper task: the rights-at-install step driver-install-task (7 October 2026, 19:5x BST; the project lead's rule that evening: no PC job needs a click or a UAC prompt)
src/driverinstall.rs: RIGHT = ("driver-install-task", ...) for boot-start-22's rights::RIGHTS; register_script = the Power Helper task with a two-hour run limit (no second task, no new firewall rule); the helper's command file takes "<seq> driver <vendor>" with a vendor WORD only, and the elevated helper resolves the file, size, sha256 and Authenticode signer from the signed table itself (Intel Corporation, NVIDIA Corporation or Advanced Micro Devices, and the row's own signer), runs the row's silent arguments with the heartbeat kept (cap 45 min), and writes "<seq> <vendor> exit <code> reboot <0|1>" to driver-result.txt; a restart-required exit is the Restart now button, never a restart by the app. drivers::start_install takes the helper route when the task is registered and keeps the one-prompt path otherwise. Tests known-failed first (the helper knew no driver verb: red on build-2, then green; the refusal of a file that is not the table's or not a vendor's; the right's id and the protocol round trip). Box gate 263 + 34 + 8. Plan 3d is the step as a table for the rights lane.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a38e0da733
commit
60a30738ba
5 changed files with 281 additions and 1 deletions
229
app/igneum-app/src/driverinstall.rs
Normal file
229
app/igneum-app/src/driverinstall.rs
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
//! The unattended driver install (the rights-at-install step `driver-install-task`, 7 October 2026, 19:5x BST; the project lead's
|
||||
//! rule that evening: no PC job may need a click or a UAC prompt, and the Arc's 9034 retry on PC 2 was cancelled for
|
||||
//! it). What the installer registers once (src/rights.rs on boot-start-22 takes the id into RIGHTS): the Igneum Power
|
||||
//! Helper task (RunLevel Highest, the app's own exe with `--power-helper`) with a two-hour execution limit, so the
|
||||
//! elevated helper can run a vendor's driver installer to its end. Nothing else: no second task, no new firewall rule.
|
||||
//!
|
||||
//! The protocol, on the helper's one command file: `<seq> driver <vendor>` with a vendor WORD only (nvidia | amd |
|
||||
//! intel). The helper, elevated, resolves everything else itself from the signed table the manifest left at
|
||||
//! `<app data>/drivers.json` and the file the app downloaded into `<app data>/drivers/`: the size, the sha256 and the
|
||||
//! Authenticode signer must match the table and the signer must be one of the three vendors, or nothing runs. So a
|
||||
//! writer of cmd.txt can never choose what runs elevated (the Power Helper's rule since 6 October 2026). The helper
|
||||
//! runs the installer with the table's silent arguments, keeps its heartbeat during the run (cap 45 minutes), and
|
||||
//! writes `<seq> <vendor> exit <code> reboot <0|1>` to `driver-result.txt` in its folder. The app holds the vendor's
|
||||
//! cards before it asks (engine::driver_install), reads the result, and a "restart required" exit is the Restart now
|
||||
//! button: the app never restarts the machine by itself. When the task is not registered (an install before 0.3.22
|
||||
//! whose rights step has not run), the one-prompt path of src/drivers.rs stays as it was.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// The right's id and sentence for src/rights.rs RIGHTS (an id never changes meaning; a new need is a new id).
|
||||
pub const RIGHT: (&str, &str) = ("driver-install-task", "the Igneum Power Helper task with a two-hour run limit: a vendor's driver installs unattended through it, the vendor's cards held, no prompt (src/driverinstall.rs)");
|
||||
pub const RESULT_FILE: &str = "driver-result.txt";
|
||||
pub const VENDORS: &[&str] = &["nvidia", "amd", "intel"];
|
||||
/// The Authenticode subjects a driver installer may carry to run elevated (the table's `signer` must match one too).
|
||||
pub const ALLOWED_SIGNERS: &[&str] = &["Intel Corporation", "NVIDIA Corporation", "Advanced Micro Devices"];
|
||||
/// How long the helper waits for the installer, and how long the app waits for the helper's result line.
|
||||
pub const INSTALL_CAP: Duration = Duration::from_secs(45 * 60);
|
||||
pub const RESULT_WAIT: Duration = Duration::from_secs(50 * 60);
|
||||
|
||||
/// The installer's registration for this right: the Power Helper task as src/powertask.rs registers it, with the
|
||||
/// execution limit raised from one hour to two (a 1 GB download that the app already did is not in it; the installer
|
||||
/// itself runs 2 to 15 minutes, and the helper's own idle exit is 20 minutes).
|
||||
pub fn register_script(exe: &Path) -> String {
|
||||
crate::powertask::register_script(exe).replace("-ExecutionTimeLimit (New-TimeSpan -Hours 1)", "-ExecutionTimeLimit (New-TimeSpan -Hours 2)")
|
||||
}
|
||||
|
||||
pub fn vendor_ok(v: &str) -> bool {
|
||||
VENDORS.contains(&v)
|
||||
}
|
||||
|
||||
/// The command line the app writes for the helper (the sequence from the one wire space).
|
||||
pub fn command_line(seq: u64, vendor: &str) -> String {
|
||||
format!("{seq} driver {vendor}\n")
|
||||
}
|
||||
|
||||
/// The file the helper runs for a vendor: the table's URL's last path segment inside the app's drivers folder.
|
||||
pub fn file_for(e: &crate::drivertable::VendorEntry, drivers_dir: &Path) -> PathBuf {
|
||||
let name = e.url.rsplit('/').next().filter(|n| !n.is_empty() && !n.contains("..") && !n.contains('\\')).unwrap_or("driver.exe");
|
||||
drivers_dir.join(name)
|
||||
}
|
||||
|
||||
/// The elevated check before anything runs: size and sha256 equal to the table, the signer one of the vendors and the
|
||||
/// table's own. Pure, so the box tests it.
|
||||
pub fn verify(e: &crate::drivertable::VendorEntry, size: u64, sha256: &str, signer_subject: &str) -> Result<(), String> {
|
||||
if size != e.size {
|
||||
return Err(format!("size {size} is not the table's {}", e.size));
|
||||
}
|
||||
if !sha256.eq_ignore_ascii_case(&e.sha256) {
|
||||
return Err("sha256 is not the table's: the file is not the one the manifest names".into());
|
||||
}
|
||||
if !ALLOWED_SIGNERS.iter().any(|s| signer_subject.contains(s)) {
|
||||
return Err(format!("the signer '{signer_subject}' is not a driver vendor"));
|
||||
}
|
||||
if !e.signer.is_empty() && !signer_subject.contains(&e.signer) {
|
||||
return Err(format!("the signer '{signer_subject}' is not the table's '{}'", e.signer));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The helper's result line and its reading.
|
||||
pub fn result_line(seq: u64, vendor: &str, code: i64, reboot: bool) -> String {
|
||||
format!("{seq} {vendor} exit {code} reboot {}\n", if reboot { 1 } else { 0 })
|
||||
}
|
||||
pub fn parse_result(text: &str, seq: u64) -> Option<(i64, bool)> {
|
||||
text.lines().rev().find_map(|l| {
|
||||
let p: Vec<&str> = l.split_whitespace().collect();
|
||||
match p.as_slice() {
|
||||
[s, _, "exit", c, "reboot", r] if s.parse::<u64>().ok() == Some(seq) => Some((c.parse().ok()?, *r == "1")),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Inside the elevated helper: resolve, verify, run, report. `dir` is the helper's folder (<app data>/app/sweep).
|
||||
pub fn run_in_helper(dir: &Path, seq: u64, vendor: &str, log: &dyn Fn(&str)) {
|
||||
let app_dir = dir.parent().map(|p| p.to_path_buf()).unwrap_or_else(|| dir.to_path_buf());
|
||||
let outcome = run_in_helper_inner(&app_dir, dir, vendor, log);
|
||||
let (code, reboot) = match outcome {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log(&format!("{seq} driver {vendor}: refused: {e}"));
|
||||
(-2, false)
|
||||
}
|
||||
};
|
||||
let _ = std::fs::OpenOptions::new().append(true).create(true).open(dir.join(RESULT_FILE)).and_then(|mut f| {
|
||||
use std::io::Write;
|
||||
f.write_all(result_line(seq, vendor, code, reboot).as_bytes())
|
||||
});
|
||||
log(&format!("{seq} driver {vendor}: exit {code} reboot {reboot}"));
|
||||
}
|
||||
|
||||
fn run_in_helper_inner(app_dir: &Path, helper_dir: &Path, vendor: &str, log: &dyn Fn(&str)) -> Result<(i64, bool), String> {
|
||||
if !vendor_ok(vendor) {
|
||||
return Err(format!("'{vendor}' is not a vendor word"));
|
||||
}
|
||||
let text = std::fs::read_to_string(app_dir.join("drivers.json")).map_err(|e| format!("no driver table at {}: {e}", app_dir.join("drivers.json").display()))?;
|
||||
let v: serde_json::Value = serde_json::from_str(&text).map_err(|e| format!("the driver table does not parse: {e}"))?;
|
||||
let table = crate::drivertable::Table::parse(&v)?;
|
||||
let e = table.entry(vendor).ok_or_else(|| format!("no {vendor} row in the table"))?.clone();
|
||||
let file = file_for(&e, &app_dir.join("drivers"));
|
||||
let size = std::fs::metadata(&file).map(|m| m.len()).map_err(|x| format!("no downloaded installer at {}: {x}", file.display()))?;
|
||||
let sha = crate::manifest::sha256_file(&file).map_err(|x| x.to_string())?;
|
||||
let subject = signer_subject(&file)?;
|
||||
verify(&e, size, &sha, &subject)?;
|
||||
log(&format!("driver {vendor}: {} verified (size, sha256, signer '{subject}'); running {} {}", file.display(), file.display(), e.args.join(" ")));
|
||||
let mut c = std::process::Command::new(&file);
|
||||
c.args(&e.args);
|
||||
crate::platform::quiet(&mut c);
|
||||
let mut child = c.spawn().map_err(|x| format!("the installer did not start: {x}"))?;
|
||||
let started = Instant::now();
|
||||
loop {
|
||||
crate::powertask::beat(helper_dir, crate::platform::unix_now());
|
||||
match child.try_wait() {
|
||||
Ok(Some(st)) => {
|
||||
let code = st.code().map(|c| c as i64).unwrap_or(-1);
|
||||
let (reboot, _) = crate::drivertable::exit_meaning(code, &e);
|
||||
return Ok((code, reboot));
|
||||
}
|
||||
Ok(None) => {
|
||||
if started.elapsed() > INSTALL_CAP {
|
||||
let _ = child.kill();
|
||||
return Err(format!("the installer ran past {} minutes and was ended", INSTALL_CAP.as_secs() / 60));
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(2));
|
||||
}
|
||||
Err(x) => return Err(format!("waiting on the installer: {x}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn signer_subject(path: &Path) -> Result<String, String> {
|
||||
let ps = format!("$s = Get-AuthenticodeSignature -LiteralPath '{}'; Write-Output ('status=' + $s.Status); Write-Output ('subject=' + $s.SignerCertificate.Subject)", path.display().to_string().replace('\'', "''"));
|
||||
let mut c = std::process::Command::new(crate::platform::tool("powershell"));
|
||||
c.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps]);
|
||||
let out = crate::detect::run_timeout(&mut c, None, Duration::from_secs(60)).ok_or("PowerShell did not answer the signature check")?;
|
||||
let mut status = String::new();
|
||||
let mut subject = String::new();
|
||||
for l in out.lines() {
|
||||
if let Some(v) = l.strip_prefix("status=") { status = v.trim().to_string(); } else if let Some(v) = l.strip_prefix("subject=") { subject = v.trim().to_string(); }
|
||||
}
|
||||
if status != "Valid" {
|
||||
return Err(format!("the Authenticode signature is {status}, not Valid"));
|
||||
}
|
||||
Ok(subject)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn signer_subject(_path: &Path) -> Result<String, String> {
|
||||
Err("the installer runs on Windows only".into())
|
||||
}
|
||||
|
||||
/// The app's side: when the Power Helper task is registered, ask it and wait for the result line; None when it is not
|
||||
/// (the caller falls back to the one-prompt path). `file` is the verified download.
|
||||
pub fn via_helper(vendor: &str) -> Option<Result<(i64, bool), String>> {
|
||||
if !cfg!(windows) || !crate::powertask::registered() {
|
||||
return None;
|
||||
}
|
||||
let dir = crate::powertask::helper_dir();
|
||||
Some((|| {
|
||||
crate::powertask::ensure_running(&dir, Duration::from_secs(30))?;
|
||||
let seq = crate::powertask::wire_seq();
|
||||
let mut text = std::fs::read_to_string(dir.join("cmd.txt")).unwrap_or_default();
|
||||
text.push_str(&command_line(seq, vendor));
|
||||
std::fs::write(dir.join("cmd.txt"), text).map_err(|e| e.to_string())?;
|
||||
let started = Instant::now();
|
||||
loop {
|
||||
let res = std::fs::read_to_string(dir.join(RESULT_FILE)).unwrap_or_default();
|
||||
if let Some(r) = parse_result(&res, seq) {
|
||||
return Ok(r);
|
||||
}
|
||||
if started.elapsed() > RESULT_WAIT {
|
||||
return Err(format!("the Power Helper gave no result for the {vendor} install within {} minutes", RESULT_WAIT.as_secs() / 60));
|
||||
}
|
||||
if !crate::powertask::alive(&dir) && started.elapsed() > Duration::from_secs(120) {
|
||||
return Err("the Power Helper stopped during the install (no heartbeat)".into());
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
}
|
||||
})())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn intel() -> crate::drivertable::VendorEntry {
|
||||
let v: serde_json::Value = serde_json::from_str(include_str!("../../../packaging/ota/drivers.json")).unwrap();
|
||||
crate::drivertable::Table::parse(&v).unwrap().entry("intel").unwrap().clone()
|
||||
}
|
||||
|
||||
/// Known-failed first: an installer that is not the table's file, or not signed by a driver vendor, must never run
|
||||
/// elevated; the first cut of the unattended path had no such check.
|
||||
#[test]
|
||||
fn the_helper_refuses_a_file_that_is_not_the_tables_or_not_the_vendors() {
|
||||
let e = intel();
|
||||
assert!(verify(&e, e.size, &e.sha256, "CN=Intel Corporation, O=Intel Corporation, S=California, C=US").is_ok());
|
||||
assert!(verify(&e, e.size + 1, &e.sha256, "CN=Intel Corporation").unwrap_err().starts_with("size"));
|
||||
assert!(verify(&e, e.size, "00", "CN=Intel Corporation").unwrap_err().starts_with("sha256"));
|
||||
assert!(verify(&e, e.size, &e.sha256, "CN=Some Miner Tools Ltd").unwrap_err().contains("not a driver vendor"));
|
||||
assert!(verify(&e, e.size, &e.sha256, "CN=NVIDIA Corporation").unwrap_err().contains("not the table's"), "a vendor, but not this row's");
|
||||
assert_eq!(file_for(&e, Path::new("D")).file_name().unwrap().to_str().unwrap(), "gfx_win_101.9034.exe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_right_is_the_power_helper_task_with_a_two_hour_limit_and_the_protocol_round_trips() {
|
||||
assert_eq!(RIGHT.0, "driver-install-task", "the id src/rights.rs's test already anticipates");
|
||||
let s = register_script(Path::new("C:\\p\\Igneum Miner\\igneum-app.exe"));
|
||||
assert!(s.contains("-ExecutionTimeLimit (New-TimeSpan -Hours 2)") && !s.contains("-Hours 1"), "{s}");
|
||||
assert!(s.contains("-TaskName 'Igneum Power Helper'") && s.contains("--power-helper"), "the same task, the same action");
|
||||
assert_eq!(command_line(305327, "intel"), "305327 driver intel\n");
|
||||
let r = result_line(305327, "intel", 14, true);
|
||||
assert_eq!(parse_result(&r, 305327), Some((14, true)));
|
||||
assert_eq!(parse_result(&r, 305328), None, "another sequence's result is not this one");
|
||||
assert_eq!(parse_result("305327 intel exit -2 reboot 0\n", 305327), Some((-2, false)), "a refusal reads as exit -2");
|
||||
assert!(vendor_ok("amd") && !vendor_ok("apple") && !vendor_ok("C:\\x.exe"));
|
||||
}
|
||||
}
|
||||
|
|
@ -114,11 +114,21 @@ pub fn start_install(shared: &Arc<Shared>, e: VendorEntry, dir: PathBuf, dry_run
|
|||
return;
|
||||
}
|
||||
}
|
||||
shared2.send(Cmd::Driver(Event::Progress(0.7, format!("{}installing {} {}: Windows asks for permission once", if dry_run { "dry run: " } else { "" }, e.vendor.to_ascii_uppercase(), e.version))));
|
||||
let unattended = !dry_run && cfg!(windows) && crate::powertask::registered();
|
||||
shared2.send(Cmd::Driver(Event::Progress(0.7, format!("{}installing {} {}: {}", if dry_run { "dry run: " } else { "" }, e.vendor.to_ascii_uppercase(), e.version, if unattended { "through the Igneum Power Helper task, no prompt" } else { "Windows asks for permission once" }))));
|
||||
if dry_run {
|
||||
shared2.send(Cmd::Driver(Event::Installed(Ok((0, false, format!("dry run: would run {} {}", file.display(), e.args.join(" ")))))));
|
||||
return;
|
||||
}
|
||||
// 0.3.22 (src/driverinstall.rs, the rights step driver-install-task): with the Power Helper task registered the
|
||||
// elevated helper runs the installer unattended after its own verification of the file; no prompt
|
||||
if unattended {
|
||||
if let Some(r) = crate::driverinstall::via_helper(&e.vendor) {
|
||||
let r = r.map(|(code, _)| { let (reboot, text) = exit_meaning(code, &e); (code, reboot, text) });
|
||||
shared2.send(Cmd::Driver(Event::Installed(r)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let args = e.args.join(" ");
|
||||
let mut c = crate::platform::elevated_command(&file.display().to_string(), &args);
|
||||
let out = crate::detect::run_timeout(&mut c, None, Duration::from_secs(1800));
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ mod chainfacts;
|
|||
mod card;
|
||||
mod drivertable;
|
||||
mod drivers;
|
||||
mod driverinstall;
|
||||
mod bootcheck;
|
||||
|
||||
use std::io::{BufRead, Write};
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ pub enum HelperCmd {
|
|||
/// re-point the task at the installed exe (no path argument: the helper finds the install folder itself, so a
|
||||
/// writer of cmd.txt can never choose what runs elevated); 6 October 2026, run 6 registered a scratch copy
|
||||
Reregister,
|
||||
/// the unattended driver install (src/driverinstall.rs): a vendor WORD only; the helper resolves the file, hash,
|
||||
/// signer and arguments from the signed table itself
|
||||
Driver(String),
|
||||
}
|
||||
|
||||
/// Parses one line: `<seq> <verb> [<digits>]` (the 0.3.9 form `<seq> <watts>` reads as a power limit; `quit` and
|
||||
|
|
@ -115,6 +118,7 @@ pub fn parse_line(line: &str) -> Option<(u64, HelperCmd)> {
|
|||
[_, "rmc"] => Some((seq, HelperCmd::MemReset)),
|
||||
[_, "dev", d] if digits(d) => Some((seq, HelperCmd::Dev(d.to_string()))),
|
||||
[_, "reregister"] => Some((seq, HelperCmd::Reregister)),
|
||||
[_, "driver", v] if crate::driverinstall::vendor_ok(v) => Some((seq, HelperCmd::Driver(v.to_string()))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -323,6 +327,11 @@ pub fn run_helper(dir: &Path) -> i32 {
|
|||
let now = q.output().map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).unwrap_or_default();
|
||||
log(&format!("{seq} reregister {}: the task now runs {now}", if ok { "ok" } else { "failed" }));
|
||||
}
|
||||
HelperCmd::Driver(v) => {
|
||||
last_seq = seq;
|
||||
crate::driverinstall::run_in_helper(dir, seq, &v, &log);
|
||||
idle = Instant::now();
|
||||
}
|
||||
HelperCmd::Dev(d) => {
|
||||
last_seq = seq;
|
||||
idle = Instant::now();
|
||||
|
|
@ -367,6 +376,20 @@ pub fn helper_dir() -> PathBuf {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Known-failed first (7 October 2026, 19:5x BST): the helper knew no driver verb, so a driver install needed an
|
||||
/// elevated prompt. The verb carries a vendor WORD only: the helper resolves the file, the hash, the signer and the
|
||||
/// arguments from the signed table itself, so a writer of cmd.txt can never choose what runs elevated.
|
||||
#[test]
|
||||
fn the_helper_takes_a_driver_verb_for_a_vendor_word_and_nothing_else() {
|
||||
assert_eq!(parse_line("305327 driver intel"), Some((305327, HelperCmd::Driver("intel".into()))));
|
||||
assert_eq!(parse_line("305328 driver nvidia"), Some((305328, HelperCmd::Driver("nvidia".into()))));
|
||||
assert_eq!(parse_line("305329 driver amd"), Some((305329, HelperCmd::Driver("amd".into()))));
|
||||
assert_eq!(parse_line("305330 driver C:\\evil.exe"), None, "a path is not a vendor word");
|
||||
assert_eq!(parse_line("305331 driver apple"), None, "no installer for that vendor");
|
||||
assert_eq!(parse_line("driver intel"), None, "a sequence number is required");
|
||||
assert_eq!(smi_args("0", &HelperCmd::Driver("intel".into())), None, "a driver verb is never an nvidia-smi call");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_fixed_verbs_with_digit_arguments_parse() {
|
||||
assert_eq!(parse_line("7 pl 460"), Some((7, HelperCmd::PowerLimit(460))));
|
||||
|
|
|
|||
|
|
@ -55,6 +55,23 @@ Per tier: a miner loses the installing vendor's rate for the install (PC 2: the
|
|||
|
||||
the project lead's rule tonight: no PC job may need a click or a UAC prompt. The one-click install asks for one, so the 9034 retry on PC 2 is cancelled, 6733 stays (the worker mines at 11.0 MH/s on it with the Intel rotate rewrite), and the table's Intel row carries `min_version` 32.0.101.6733: an Arc on Windows' inbox driver reads "fine" with no button; a card with no driver at all is still offered 9034 (test `the_shipped_table_accepts_the_inbox_6733_driver_for_the_arc_b580_until_an_unattended_install_exists`). What follows: the install path moves off the elevated prompt onto the app's Power Helper task (the registered elevated task the tuner already uses), so a driver installs unattended at the next idle moment with the vendor's cards held; until then the one-click path stays as it is for a user at the keyboard. The 16:27Z minidump waits for the same unattended path (the Minidump folder refuses an unelevated read), not for a click.
|
||||
|
||||
## 3d. The rights-at-install step `driver-install-task` (7 October 2026, 19:5x BST; for boot-start-22's rights.rs)
|
||||
|
||||
Main's ask: the update-return lane's rights step (boot-start-22 3fbf4280, `src/rights.rs`, the manifest `rights.json`) takes a named right for the driver installer, so the next update asks once and a driver installs with no click ever again. The step, on this branch as `app/igneum-app/src/driverinstall.rs`:
|
||||
|
||||
| Part | What |
|
||||
|---|---|
|
||||
| Id and sentence | `driverinstall::RIGHT` = `("driver-install-task", "the Igneum Power Helper task with a two-hour run limit: a vendor's driver installs unattended through it, the vendor's cards held, no prompt")`; add it to `rights::RIGHTS` (the rights test already anticipates the id) |
|
||||
| What the installer registers once | `driverinstall::register_script(exe)`: the Igneum Power Helper task as `powertask::register_script` registers it (RunLevel Highest, the app's own exe with `--power-helper`, no trigger, the signed-in user), with `-ExecutionTimeLimit` two hours instead of one. No second task, no new firewall rule. In the rights script it replaces the Power Helper line (same task name, `-Force`) |
|
||||
| The elevated path | the Power Helper's one command file `<app data>/app/sweep/cmd.txt` gains `<seq> driver <vendor>` with a vendor WORD only (nvidia, amd, intel). The elevated helper resolves the file (`<app data>/drivers/<the table URL's file name>`), the size, the sha256 and the Authenticode signer from the signed table at `<app data>/drivers.json`; the signer must be Intel Corporation, NVIDIA Corporation or Advanced Micro Devices and the row's own; anything else is refused with exit -2 in the result line. So a writer of cmd.txt can never choose what runs elevated |
|
||||
| The vendor installers' silent flags | the table rows: Intel `-s` (no `-b`; 14 and 1014 mean restart required), NVIDIA `-s -noreboot`, AMD `-install -silent`; the helper passes the row's `args` as they are |
|
||||
| Restart handling | the helper reports `<seq> <vendor> exit <code> reboot <0|1>` in `driver-result.txt` (`exit_meaning` on the row's `reboot_codes`); the app shows "installed, Restart Windows to finish" with the Restart now button; the app never restarts the machine by itself |
|
||||
| Cards | `engine::driver_install` holds every card of the vendor before the ask (3b) and gives them back on the result or on the card's return |
|
||||
| Caps | the installer 45 minutes inside the helper (heartbeat kept), the app waits 50 for the result line; a helper that dies mid-install is a plain error on the row |
|
||||
| Fallback | no task registered (an install before 0.3.22 whose rights step never ran): the one-prompt path of `drivers::start_install` as before, and the row's text says which path it took |
|
||||
|
||||
Tests (box, known-failed first): the helper took no `driver` verb (`the_helper_takes_a_driver_verb_for_a_vendor_word_and_nothing_else`, powertask.rs: a path and an unknown vendor are refused), `the_helper_refuses_a_file_that_is_not_the_tables_or_not_the_vendors`, `the_right_is_the_power_helper_task_with_a_two_hour_limit_and_the_protocol_round_trips`. Not run on a PC tonight (the project lead's rule); the first unattended install is PC 2's Arc 9034 once 0.3.22's rights step has run there.
|
||||
|
||||
## 4. Not done, and what follows
|
||||
|
||||
- The table's publish: `publish-manifest.sh --drivers packaging/ota/drivers.json` with the 0.3.21 manifest (the shipper's step); every row is measured.
|
||||
|
|
|
|||
Loading…
Reference in a new issue