Repository navigation
Expand file tree
/
Copy pathwindows_task.rs
More file actions
121 lines (117 loc) · 5.28 KB
/
Copy pathwindows_task.rs
File metadata and controls
121 lines (117 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Same-user Task action lifetime and bounded runtime recovery.
//!
//! Scheduler RestartOnFailure covers action-launch failures, but an action that
//! starts and later exits can still produce TaskCompleted, even for an abnormal
//! exit status. Own the business child rather than relying on that distinction.
use std::ffi::OsString;
use std::path::Path;
use std::time::{Duration, Instant};
use hiroute_host_runtime::{StandaloneLayout, read_standalone_install_record};
use hiroute_platform::windows::{background, job, security};
const RETRIES: usize = 3;
const RETRY_INTERVAL: Duration = Duration::from_secs(60);
/// Foreground and SCM dispatch retain their existing owners. Only a verified
/// installed Task image becomes the supervisor; the persisted definition and
/// registered action arguments keep their existing contract.
pub(super) fn run_if_installed_task() -> Result<bool, String> {
let layout = StandaloneLayout::from_environment().map_err(|_| "Task layout unavailable")?;
let record = read_standalone_install_record(&layout.marker_path)
.map_err(|_| "Task installation unavailable")?;
if hiroute_host_runtime::standalone_maintenance_pending(&layout)
.map_err(|_| "Task maintenance state unavailable")?
{
return Err("Task installation maintenance pending".into());
}
match std::fs::symlink_metadata(&record.service_definition) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
Err(_) => return Err("Task definition unavailable".into()),
Ok(_) => {}
}
let definition = background::read_definition(&record.service_definition)
.map_err(|_| "Task definition invalid")?;
if definition.manager != background::Manager::Task {
return Ok(false);
}
definition
.verify_image()
.map_err(|_| "Task image verification failed")?;
same_image(&definition.daemon)?;
job::initialize_resident().map_err(|_| "Task process-tree lifetime unavailable")?;
let arguments: Vec<OsString> = ["--role", "all", "--standalone", "--windows-task-runtime"]
.into_iter()
.map(OsString::from)
.collect();
let environment: Vec<_> = std::env::vars_os().collect();
for attempt in 0..=RETRIES {
// Installation maintenance must stop this action before replacing the
// image. Never recover by launching changed bytes at a retained path.
definition
.verify_image()
.map_err(|_| "Task recovery image verification failed")?;
let launched = job::Child::spawn(job::Launch {
program: &definition.daemon,
args: &arguments,
env: &environment,
cwd: definition.daemon.parent(),
stdin: job::Stream::Null,
stdout: job::Stream::Null,
stderr: job::Stream::Null,
});
if let Ok(child) = launched {
let code = loop {
if let Some(code) = child.exit_code().map_err(|_| "Task child wait failed")? {
break code;
}
std::thread::sleep(Duration::from_millis(50));
};
let deadline = Instant::now() + Duration::from_secs(2);
while !child.empty().map_err(|_| "Task child Job query failed")?
&& Instant::now() < deadline
{
std::thread::sleep(Duration::from_millis(20));
}
let empty = child.empty().map_err(|_| "Task child Job query failed")?;
if !empty {
child.terminate().map_err(|_| "Task child cleanup failed")?;
let deadline = Instant::now() + Duration::from_secs(5);
while !child
.empty()
.map_err(|_| "Task child cleanup query failed")?
&& Instant::now() < deadline
{
std::thread::sleep(Duration::from_millis(20));
}
if !child
.empty()
.map_err(|_| "Task child cleanup query failed")?
{
return Err("Task recovery refused a live child tree".into());
}
}
if code == 0 && empty {
return Ok(true);
}
}
if attempt == RETRIES {
return Err("Task runtime recovery exhausted".into());
}
// Public stop can terminate the exact Scheduler action during backoff.
// Its resident Job then closes every owned child; no detached waiter or
// timer can start another process after that stop.
std::thread::sleep(RETRY_INTERVAL);
}
unreachable!()
}
fn same_image(expected: &Path) -> Result<(), String> {
let actual = std::env::current_exe().map_err(|_| "Task current image unavailable")?;
let actual = security::open_identity(&actual, false)
.map_err(|_| "Task current image identity unavailable")?;
let expected = security::open_identity(expected, false)
.map_err(|_| "Task installed image identity unavailable")?;
if security::file_identity(&actual).map_err(|_| "Task current FileID unavailable")?
!= security::file_identity(&expected).map_err(|_| "Task installed FileID unavailable")?
{
return Err("Task current and installed images differ".into());
}
Ok(())
}