mj_controller/targets/
worker_daemon.rs1use super::*;
2
3pub fn worker_daemon_identity_script(worker_root: &str) -> String {
7 format!(
8 r#"hel_root={root}
9hel_match="hel worker run --root $hel_root"
10hel_match_home="hel worker run --root $HOME/$hel_root"
11hel_ps() {{
12 ps -ww "$@" 2>/dev/null || ps "$@" 2>/dev/null
13}}
14hel_is_worker() {{
15 hel_args=$(hel_ps -o args= -p "$1") || return 1
16 case "$hel_args" in
17 *"$hel_match"*|*"$hel_match_home"*) return 0 ;;
18 esac
19 return 1
20}}
21hel_recorded_worker() {{
22 [ -f "$hel_root/{pid_file}" ] || return 1
23 hel_pid=$(cat "$hel_root/{pid_file}" 2>/dev/null)
24 case "$hel_pid" in
25 '' | *[!0-9]*) return 1 ;;
26 esac
27 hel_is_worker "$hel_pid" || return 1
28 printf '%s\n' "$hel_pid"
29}}"#,
30 root = posix_quote(worker_root),
31 pid_file = mj_core::relay::WORKER_PID_FILE,
32 )
33}
34
35pub fn worker_daemon_liveness_script(worker_root: &str) -> String {
39 let mut script = worker_daemon_identity_script(worker_root);
40 script.push_str(
41 r#"
42hel_report_worker_state() {
43 if [ -S "$hel_root/control.sock" ]; then
44 printf 'alive\n'
45 else
46 printf 'starting\n'
47 fi
48}
49if hel_recorded_worker >/dev/null; then
50 hel_report_worker_state
51 exit 0
52fi
53while read -r hel_pid hel_args; do
54 case "$hel_pid" in
55 '' | *[!0-9]*) continue ;;
56 esac
57 [ "$hel_pid" -eq $$ ] && continue
58 case "$hel_args" in
59 *"$hel_match"*|*"$hel_match_home"*) hel_report_worker_state; exit 0 ;;
60 esac
61done <<MJ_PS
62$(hel_ps -eo pid=,args=)
63MJ_PS
64printf 'dead\n'
65"#,
66 );
67 script
68}
69
70pub fn stop_worker_daemon_script(worker_root: &str) -> String {
77 let mut script = worker_daemon_identity_script(worker_root);
78 script.push_str(
79 r#"
80hel_signal() {
81 kill -"$1" -- "-$2" 2>/dev/null && return 0
82 kill -"$1" "-$2" 2>/dev/null && return 0
83 kill -"$1" "$2" 2>/dev/null
84}
85hel_stop() {
86 hel_signal TERM "$1" || return 0
87 hel_waited=0
88 while [ "$hel_waited" -lt 2 ]; do
89 kill -0 "$1" 2>/dev/null || return 0
90 sleep 1
91 hel_waited=$((hel_waited + 1))
92 done
93 kill -0 "$1" 2>/dev/null || return 0
94 hel_signal KILL "$1" || true
95 hel_waited=0
96 while [ "$hel_waited" -lt 3 ]; do
97 kill -0 "$1" 2>/dev/null || return 0
98 sleep 1
99 hel_waited=$((hel_waited + 1))
100 done
101}
102if hel_pid=$(hel_recorded_worker); then
103 hel_stop "$hel_pid"
104fi
105hel_ps -eo pid=,args= | while read -r hel_pid hel_args; do
106 case "$hel_pid" in
107 '' | *[!0-9]*) continue ;;
108 esac
109 [ "$hel_pid" -eq $$ ] && continue
110 case "$hel_args" in
111 *"$hel_match"*|*"$hel_match_home"*) hel_stop "$hel_pid" ;;
112 esac
113done
114hel_left=0
115while read -r hel_pid hel_args; do
116 case "$hel_pid" in
117 '' | *[!0-9]*) continue ;;
118 esac
119 [ "$hel_pid" -eq $$ ] && continue
120 case "$hel_args" in
121 *"$hel_match"*|*"$hel_match_home"*) hel_left=1 ;;
122 esac
123done <<MJ_PS
124$(hel_ps -eo pid=,args=)
125MJ_PS
126if [ "$hel_left" -ne 0 ]; then
127 echo "worker still running after stop: $hel_root" >&2
128 exit 1
129fi
130"#,
131 );
132 script
133}
134
135pub fn clear_relay_state_plan(
142 locator: &TargetLocator,
143 session_id: &str,
144) -> Result<Option<CommandSpec>> {
145 verify_locator(locator, session_id)?;
146 let session_worker_root = worker_root(locator, session_id)?;
147 let script = format!(
148 "{}\nrm -rf -- {} {}\n",
149 stop_worker_daemon_script(&session_worker_root),
150 posix_quote(&format!(
151 "{session_worker_root}/{}",
152 mj_core::relay::RELAY_STATE_FILE
153 )),
154 posix_quote(&format!(
155 "{session_worker_root}/{}",
156 mj_core::relay::RELAY_JOURNAL_DIR
157 )),
158 );
159 Ok(match locator {
160 TargetLocator::LocalBare { .. } => Some(
161 CommandSpec::new("sh", ["-c", script.as_str()])
162 .purpose("stop a leaked local Mjolnir worker and clear its relay state"),
163 ),
164 TargetLocator::SshBare { ssh, .. } => Some(
165 ssh_command(ssh, ["sh", "-c", script.as_str()])
166 .purpose("stop a leaked remote Mjolnir worker and clear its relay state"),
167 ),
168 TargetLocator::LocalPodman { .. }
169 | TargetLocator::LocalDocker { .. }
170 | TargetLocator::AppleContainer { .. }
171 | TargetLocator::SshPodman { .. }
172 | TargetLocator::SshDocker { .. }
173 | TargetLocator::AwsEc2 { .. } => None,
174 })
175}