⎇ 227715a464b872a0ecd918a069c667e05c95ec27
crates/ente-binfmt-compat/src/main.rs
Historia
Quién tocó qué
crudo
compat-systemd1, NOTIFY_SOCKET, binfmt y timer
1//! ente-binfmt-compat: registra handlers de binfmt_misc al boot.
2//!
3//! systemd-binfmt lee `/usr/lib/binfmt.d/*.conf` y `/etc/binfmt.d/*.conf` y
4//! escribe cada línea al kernel via `/proc/sys/fs/binfmt_misc/register`.
5//! Esto habilita ejecución transparente de binarios no-ELF (qemu-user,
6//! wine, etc).
7//!
8//! Formato de cada línea:
9//! :<name>:<type>:<offset>:<magic>:<mask>:<interpreter>:<flags>
10//!
11//! Líneas que empiezan con `#` o vacías se ignoran.
12
13use std::fs;
14use std::io::Write;
15use std::path::Path;
16use tracing::{info, warn};
17use tracing_subscriber::EnvFilter;
18
19const REGISTER_PATH: &str = "/proc/sys/fs/binfmt_misc/register";
20const SEARCH_DIRS: &[&str] = &[
21 "/usr/lib/binfmt.d",
22 "/etc/binfmt.d",
23 "/run/binfmt.d",
24];
25
26fn main() {
27 init_tracing();
28 info!("ente-binfmt-compat: registrando handlers binfmt_misc");
29
30 if !Path::new(REGISTER_PATH).exists() {
31 warn!(path = REGISTER_PATH, "binfmt_misc no montado — skip");
32 std::process::exit(0);
33 }
34
35 let mut registered = 0;
36 let mut errors = 0;
37 let mut skipped = 0;
38
39 for dir in SEARCH_DIRS {
40 if !Path::new(dir).exists() { continue; }
41 let mut entries: Vec<_> = match fs::read_dir(dir) {
42 Ok(rd) => rd.filter_map(|e| e.ok()).collect(),
43 Err(_) => continue,
44 };
45 entries.sort_by_key(|e| e.file_name());
46 for entry in entries {
47 let path = entry.path();
48 if path.extension().map(|e| e != "conf").unwrap_or(true) { continue; }
49 let content = match fs::read_to_string(&path) {
50 Ok(c) => c,
51 Err(e) => { warn!(?e, path = %path.display(), "read"); continue; }
52 };
53 for line in content.lines() {
54 let line = line.trim();
55 if line.is_empty() || line.starts_with('#') { continue; }
56 match register(line) {
57 Ok(name) => {
58 info!(file = %path.display(), %name, "binfmt registrado");
59 registered += 1;
60 }
61 Err(e) => {
62 if e.is_already_exists() {
63 skipped += 1;
64 } else {
65 warn!(?e, file = %path.display(), "registro falló");
66 errors += 1;
67 }
68 }
69 }
70 }
71 }
72 }
73 info!(registered, skipped, errors, "binfmt aplicado");
74 if errors > 0 { std::process::exit(1); }
75}
76
77#[derive(Debug)]
78struct RegError(std::io::Error);
79impl std::fmt::Display for RegError {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) }
81}
82impl RegError {
83 fn is_already_exists(&self) -> bool {
84 // EEXIST = 17 en Linux.
85 self.0.raw_os_error() == Some(17)
86 }
87}
88
89/// Escribe la línea al register file. Devuelve el `name` extraído del
90/// primer campo (entre `:` separators) si tuvo éxito.
91fn register(line: &str) -> Result<String, RegError> {
92 // Sintaxis: :<name>:<type>:<offset>:<magic>:<mask>:<interpreter>:<flags>
93 // Field 0 (después del ':' inicial) es el name.
94 let name = line.split(':').nth(1)
95 .map(|s| s.to_string())
96 .unwrap_or_else(|| "?".into());
97 let mut f = fs::OpenOptions::new()
98 .write(true)
99 .open(REGISTER_PATH)
100 .map_err(RegError)?;
101 f.write_all(line.as_bytes()).map_err(RegError)?;
102 Ok(name)
103}
104
105fn init_tracing() {
106 let filter = EnvFilter::try_from_default_env()
107 .unwrap_or_else(|_| EnvFilter::new("ente_binfmt_compat=info"));
108 tracing_subscriber::fmt().with_env_filter(filter).with_target(true).init();
109}