Sorgente pubblicata
Scarica sorgente ZIP
CH-J Tools
Sfoglia directory e file per una specifica release di applicazioni.
CH-J / Server Manager
1704 file
0.1.2-beta
Aggiunto: · Costruito:
1
//! Background process. One blocking accept loop on a Unix socket inside the user's data folder (folder 0700,2
//! socket 0600), plus small KeySync and availability-monitor threads; no WebView or async runtime.3
//! Requests are one JSON line `{"action":"verify|sign|ping|stop","path":"/abs/file"}`; the answer is one line.4
//! Windows holds an exclusive file lock and polls a stop file; its menu entries start the application5
//! directly (no verify/sign IPC, see `main.rs`).6
use std::path::Path;8
pub const SOCKET: &str = "standby-helper.sock";10
#[cfg(unix)]11
mod imp {12
use super::SOCKET;13
use serde_json::{json, Value};14
use std::io::{BufRead, BufReader, Read, Write};15
use std::os::unix::net::{UnixListener, UnixStream};16
use std::path::Path;17
use std::time::Duration;19
/// `<data>/standby-helper.sock`, or, when that is longer than a Unix socket path may be (104 bytes on macOS),20
/// a per-user runtime folder (`XDG_RUNTIME_DIR`, macOS per-user `TMPDIR`) with a name derived from the data path.21
pub fn socket_path(data: &Path) -> std::path::PathBuf {22
let direct = data.join(SOCKET);23
if direct.as_os_str().len() < 100 {24
return direct;25
}26
let hash = data.to_string_lossy().bytes().fold(0xcbf2_9ce4_8422_2325u64, |h, b| (h ^ b as u64).wrapping_mul(0x100_0000_01b3));27
let base = std::env::var_os("XDG_RUNTIME_DIR").or_else(|| std::env::var_os("TMPDIR")).map(std::path::PathBuf::from).filter(|p| p.is_absolute()).unwrap_or_else(|| data.to_path_buf());28
base.join(format!("chj-standby-{hash:016x}.sock"))29
}31
fn exchange(data: &Path, request: &Value) -> Option<String> {32
let mut stream = UnixStream::connect(socket_path(data)).ok()?;33
stream.set_read_timeout(Some(Duration::from_secs(3))).ok()?;34
stream.set_write_timeout(Some(Duration::from_secs(3))).ok()?;35
writeln!(stream, "{request}").ok()?;36
let mut line = String::new();37
BufReader::new(stream).read_line(&mut line).ok()?;38
Some(line.trim().to_string())39
}41
pub fn ping(data: &Path) -> bool {42
exchange(data, &json!({"action":"ping"})).as_deref() == Some("pong")43
}45
pub fn request(data: &Path, action: &str, file: &Path) -> bool {46
exchange(data, &json!({"action": action, "path": file.to_string_lossy()})).as_deref() == Some("ok")47
}49
/// Returns once the daemon has exited (its lock is free) or after 3 s: callers such as the application's move of50
/// the data folder rely on no helper thread still using the old paths.51
pub fn stop(data: &Path) {52
if exchange(data, &json!({"action":"stop"})).is_none() {53
return;54
}55
let deadline = std::time::Instant::now() + Duration::from_secs(3);56
while crate::lockfile::held_by_other(data) && std::time::Instant::now() < deadline {57
std::thread::sleep(Duration::from_millis(50));58
}59
}61
fn handle(stream: UnixStream) -> bool {62
let _ = stream.set_read_timeout(Some(Duration::from_secs(2)));63
let mut line = String::new();64
// At most 16 KiB per request.65
if BufReader::new((&stream).take(16 * 1024)).read_line(&mut line).is_err() {66
return true;67
}68
let request: Value = serde_json::from_str(line.trim()).unwrap_or(Value::Null);69
let mut out = &stream;70
let answer = match request["action"].as_str() {71
Some("ping") => "pong",72
Some("stop") => {73
let _ = writeln!(out, "bye");74
return false;75
}76
Some(action @ ("verify" | "sign")) => match request["path"].as_str().filter(|p| Path::new(p).is_absolute()).and_then(crate::checked_file) {77
Some(file) if crate::launch_quick(action, &file).is_ok() => "ok",78
_ => "error",79
},80
_ => "error",81
};82
let _ = writeln!(out, "{answer}");83
true84
}86
pub fn run(data: &Path) -> i32 {87
let settings = crate::settings::load(data);88
if !settings.enabled {89
return 0;90
}91
// One instance for the process lifetime: only the lock owner may replace or remove the socket (two92
// concurrent starts used to unlink each other's live socket and both kept monitoring).93
let _lock = match crate::lockfile::acquire(data) {94
Ok(Some(lock)) => lock,95
Ok(None) => return 0, // already running96
Err(_) => return 1,97
};98
// Keep the system entries pointing at the installed binaries (e.g. after an update moved them).99
let _ = crate::integration::apply(&crate::settings::Context::new(data), &settings);100
let path = socket_path(data);101
let _ = std::fs::remove_file(&path);102
let listener = match UnixListener::bind(&path) {103
Ok(listener) => listener,104
Err(_) => return 1,105
};106
{107
use std::os::unix::fs::PermissionsExt;108
let _ = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600));109
}110
// KeySync while the application is closed: a pass only does work when jobs are due and Core does111
// not hold the processing lock; otherwise it is one small file read every 90 s.112
let sync_data = data.to_path_buf();113
let _ = std::thread::Builder::new().name("keysync".into()).spawn(move || {114
std::thread::sleep(Duration::from_secs(20)); // let the network come up after login115
loop {116
let _ = crate::keysync::pass(&sync_data);117
std::thread::sleep(crate::keysync::PASS_INTERVAL);118
}119
});120
let monitor_data = data.to_path_buf();121
let _ = std::thread::Builder::new().name("monitor".into()).stack_size(128 * 1024).spawn(move || {122
crate::monitor::run_loop(&monitor_data);123
});124
for stream in listener.incoming() {125
match stream {126
Ok(stream) => {127
if !handle(stream) {128
break;129
}130
}131
Err(_) => std::thread::sleep(Duration::from_millis(200)),132
}133
}134
let _ = std::fs::remove_file(&path);135
0136
}137
}139
#[cfg(not(unix))]140
mod imp {141
use crate::lockfile;142
use std::path::Path;143
use std::time::{Duration, Instant};145
pub fn ping(data: &Path) -> bool {146
lockfile::held_by_other(data)147
}148
pub fn request(_: &Path, _: &str, _: &Path) -> bool {149
false150
}151
pub fn stop(data: &Path) {152
if lockfile::request_stop(data).is_err() {153
return;154
}155
let deadline = Instant::now() + Duration::from_secs(3);156
while ping(data) || lockfile::held_by_other(data) {157
let remaining = deadline.saturating_duration_since(Instant::now());158
if remaining.is_zero() {159
break;160
}161
std::thread::sleep(remaining.min(Duration::from_millis(50)));162
}163
}164
pub fn run(data: &Path) -> i32 {165
let settings = crate::settings::load(data);166
if !settings.enabled {167
return 0;168
}169
// A stop file from before this start is stale; clearing it before taking the lock means a stop request170
// sent after the lock is held is never lost.171
if lockfile::clear_stale_stop(data).is_err() {172
return 1;173
}174
let _lock = match lockfile::acquire(data) {175
Ok(Some(lock)) => lock,176
Ok(None) => return 0, // already running177
Err(_) => return 1,178
};179
let _ = crate::integration::apply(&crate::settings::Context::new(data), &settings);180
let sync_data = data.to_path_buf();181
// Default stack like on unix: the TLS handshake (rustls) needs more than a small stack.182
let _ = std::thread::Builder::new().name("keysync".into()).spawn(move || {183
std::thread::sleep(Duration::from_secs(20)); // let the network come up after login184
loop {185
let _ = crate::keysync::pass(&sync_data);186
std::thread::sleep(crate::keysync::PASS_INTERVAL);187
}188
});189
let monitor_data = data.to_path_buf();190
let _ = std::thread::Builder::new().name("monitor".into()).stack_size(128 * 1024).spawn(move || {191
crate::monitor::run_loop(&monitor_data);192
});193
loop {194
std::thread::sleep(Duration::from_secs(1));195
if lockfile::take_stop_request(data) {196
break;197
}198
}199
0200
}201
}203
pub fn ping(data: &Path) -> bool {204
imp::ping(data)205
}206
pub fn request(data: &Path, action: &str, file: &Path) -> bool {207
imp::request(data, action, file)208
}209
pub fn stop(data: &Path) {210
imp::stop(data)211
}212
pub fn run(data: &Path) -> i32 {213
imp::run(data)214
}SHA-256
a9e7bcfcba053327a2dd590db2e64039e60d06077d46a4d0e2f3e4af11baafcdCH-J Proprietary Software License 1.14 · La sorgente è fornita sotto CH-J Proprietary Software License 1.14. La sua disponibilità non modifica i termini di licenza o concede diritti aggiuntivi.
Archivio SHA-256
ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f