1#![allow(non_camel_case_types)]
4
5extern crate curl_sys as _;
9extern crate libz_sys as _;
10extern crate openssl_sys as _;
11
12use std::os::raw::{c_char, c_int, c_void};
13
14pub const FLAG_DEBUG: c_int = 1 << 1;
16pub const FLAG_ERASE: c_int = 1 << 2;
17
18pub const LL_ERROR: c_int = 0;
20pub const LL_WARNING: c_int = 1;
21pub const LL_NOTICE: c_int = 2;
22pub const LL_INFO: c_int = 3;
23pub const LL_VERBOSE: c_int = 4;
24pub const LL_DEBUG: c_int = 5;
25
26extern "C" {
27 static mut log_level: c_int;
29}
30
31pub fn set_log_level(level: c_int) {
34 unsafe {
35 log_level = level;
36 }
37}
38
39use std::sync::atomic::{AtomicBool, Ordering};
40use std::sync::Mutex;
41
42extern "C" {
43 fn restorekit_install_log_capture();
45}
46
47static LOG_LINES: Mutex<Vec<(c_int, String)>> = Mutex::new(Vec::new());
48static LOG_ECHO: AtomicBool = AtomicBool::new(false);
49const LOG_CAPACITY: usize = 512;
50
51#[no_mangle]
56pub unsafe extern "C" fn restorekit_log_capture(level: c_int, msg: *const c_char) {
57 if msg.is_null() {
58 return;
59 }
60 let text = std::ffi::CStr::from_ptr(msg).to_string_lossy();
61 let line = text.trim_end_matches(['\n', '\r']).to_string();
62 if line.is_empty() {
63 return;
64 }
65 if LOG_ECHO.load(Ordering::Relaxed) {
66 eprintln!("{line}");
67 }
68 if let Ok(mut buf) = LOG_LINES.lock() {
69 if buf.len() >= LOG_CAPACITY {
70 buf.remove(0);
71 }
72 buf.push((level, line));
73 }
74}
75
76pub fn install_log_capture(echo: bool) {
79 LOG_ECHO.store(echo, Ordering::Relaxed);
80 if let Ok(mut buf) = LOG_LINES.lock() {
81 buf.clear();
82 }
83 unsafe { restorekit_install_log_capture() };
84}
85
86pub fn error_tail(max_lines: usize) -> String {
88 let buf = match LOG_LINES.lock() {
89 Ok(b) => b,
90 Err(e) => e.into_inner(),
91 };
92 let errors: Vec<&str> = buf
93 .iter()
94 .filter(|(level, _)| *level <= LL_WARNING)
95 .map(|(_, line)| line.as_str())
96 .collect();
97 let start = errors.len().saturating_sub(max_lines);
98 errors[start..].join("\n")
99}
100
101pub const RESTORE_STEP_DETECT: c_int = 0;
103pub const RESTORE_STEP_PREPARE: c_int = 1;
104pub const RESTORE_STEP_UPLOAD_FS: c_int = 2;
105pub const RESTORE_STEP_VERIFY_FS: c_int = 3;
106pub const RESTORE_STEP_FLASH_FW: c_int = 4;
107pub const RESTORE_STEP_FLASH_BB: c_int = 5;
108pub const RESTORE_STEP_FUD: c_int = 6;
109pub const RESTORE_STEP_UPLOAD_IMG: c_int = 7;
110
111#[repr(C)]
113pub struct idevicerestore_client_t {
114 _private: [u8; 0],
115}
116
117pub type idevicerestore_progress_cb_t =
118 Option<unsafe extern "C" fn(step: c_int, step_progress: f64, userdata: *mut c_void)>;
119
120extern "C" {
121 pub fn idevicerestore_client_new() -> *mut idevicerestore_client_t;
122 pub fn idevicerestore_client_free(client: *mut idevicerestore_client_t);
123 pub fn idevicerestore_set_ecid(client: *mut idevicerestore_client_t, ecid: u64);
124 pub fn idevicerestore_set_udid(client: *mut idevicerestore_client_t, udid: *const c_char);
125 pub fn idevicerestore_set_flags(client: *mut idevicerestore_client_t, flags: c_int);
126 pub fn idevicerestore_set_ipsw(client: *mut idevicerestore_client_t, path: *const c_char);
127 pub fn idevicerestore_set_cache_path(client: *mut idevicerestore_client_t, path: *const c_char);
128 pub fn idevicerestore_set_progress_callback(
129 client: *mut idevicerestore_client_t,
130 cbfunc: idevicerestore_progress_cb_t,
131 userdata: *mut c_void,
132 );
133 pub fn idevicerestore_start(client: *mut idevicerestore_client_t) -> c_int;
134 pub fn register_progress(tag: u32, label: *const c_char);
137}
138
139pub fn init_progress() {
148 unsafe { register_progress(0, std::ptr::null()) };
149}
150
151#[cfg(any(target_os = "linux", target_os = "windows"))]
154extern "C" {
155 fn restorekit_usbmuxd_start(socket_path: *const c_char) -> c_int;
156 fn restorekit_usbmuxd_run();
157 fn restorekit_usbmuxd_stop();
158 fn restorekit_usbmuxd_cleanup();
159}
160
161#[cfg(any(target_os = "linux", target_os = "windows"))]
164pub fn usbmuxd_start(path: &std::ffi::CStr) -> std::result::Result<(), c_int> {
165 let rc = unsafe { restorekit_usbmuxd_start(path.as_ptr()) };
166 if rc == 0 {
167 Ok(())
168 } else {
169 Err(rc)
170 }
171}
172
173#[cfg(any(target_os = "linux", target_os = "windows"))]
175pub fn usbmuxd_run() {
176 unsafe { restorekit_usbmuxd_run() }
177}
178
179#[cfg(any(target_os = "linux", target_os = "windows"))]
181pub fn usbmuxd_stop() {
182 unsafe { restorekit_usbmuxd_stop() }
183}
184
185#[cfg(any(target_os = "linux", target_os = "windows"))]
187pub fn usbmuxd_cleanup() {
188 unsafe { restorekit_usbmuxd_cleanup() }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use std::ffi::CString;
195
196 #[test]
197 fn error_tail_filters_by_level() {
198 if let Ok(mut b) = LOG_LINES.lock() {
199 b.clear();
200 }
201 LOG_ECHO.store(false, Ordering::Relaxed);
202
203 let cap = |level, s: &str| {
204 let c = CString::new(s).unwrap();
205 unsafe { restorekit_log_capture(level, c.as_ptr()) };
206 };
207 cap(LL_INFO, "info line");
208 cap(LL_ERROR, "boom -3");
209 cap(LL_VERBOSE, "verbose noise");
210 cap(LL_WARNING, "careful");
211
212 let tail = error_tail(10);
213 assert!(tail.contains("boom -3"));
214 assert!(tail.contains("careful"));
215 assert!(!tail.contains("info line"));
216 assert!(!tail.contains("verbose noise"));
217 }
218}