1pub use anyhow::{Context, Result};
2use clap::{crate_name, crate_version};
3use core::panic;
4use fork::{Fork, daemon};
5use passwords::PasswordGenerator;
6use serde::Serialize;
7use serde_json::Value;
8use std::backtrace::Backtrace;
9use std::collections::HashMap;
10use std::error::Error;
11use std::path::PathBuf;
12use std::process::Command;
13use std::{env, fmt, fs};
14use sysinfo::{Pid, Process, System};
15use unicode_segmentation::UnicodeSegmentation;
16
17pub mod debug_tools;
18pub mod ipc;
19pub mod json;
20pub mod logging;
21pub mod panic_hooks;
22pub mod password;
23pub mod process;
24pub mod reader;
25pub mod resource_lock;
26pub mod serde_value;
27use crate::cli::Invocation;
28use crate::formats::unicode::scalars_to_string_lossy;
29use crate::utilities::ipc::Channel;
30use crate::utilities::process::ProcessManager;
31pub use crate::utilities_json_json as json;
32pub use ctb_test_macro::ctb_test;
33
34pub const COLUMN_UUID_DELIM: &str = "d13420ff-b2d7-4e52-a390-7a0d6159e8d6";
35
36pub fn strtovec(s: &str) -> Vec<u8> {
37 s.as_bytes().to_owned()
38}
39
40pub fn vectostr(v: &[u8]) -> String {
41 String::from_utf8_lossy(v).to_string()
42}
43
44pub fn strtohex<T>(s: T) -> String
45where
46 T: AsRef<[u8]>,
47{
48 hex::encode(s)
49}
50
51pub fn vectohex<T>(s: T) -> String
52where
53 T: AsRef<[u8]>,
54{
55 hex::encode(s)
56}
57
58pub fn substr_mb(s: &str, start: i128, end: i128) -> String {
59 let chars: Vec<&str> = s.graphemes(true).collect();
60 let len = i128::try_from(chars.len()).expect("usize did not fit in i128");
61
62 let start = if start < 0 { len + start } else { start };
64 let end = if end < 0 { len + end } else { end };
65
66 let start = start.max(0).min(len); let end = end.max(0).min(len);
69
70 chars[usize::try_from(start).expect("Did not fit")
72 ..usize::try_from(end).expect("Did not fit")]
73 .join("")
74}
75
76pub fn find_first_matching_key_for_value(
78 map: HashMap<Vec<u8>, Vec<u8>>,
79 needle: Vec<u8>,
80) -> Option<Vec<u8>> {
81 map.iter().find_map(|(key, val)| {
82 if *val == needle {
83 Some(key.clone())
84 } else {
85 None
86 }
87 })
88}
89
90pub use log::{
91 debug as hc_internal_log_debug, error as hc_internal_log_error,
92 info as hc_internal_log_info, warn as hc_internal_log_warn,
93};
94
95#[macro_export]
97macro_rules! log {
98 ($($arg:expr),*) => {
99 $crate::hc_internal_log_debug!("{:?}{}{}", ($($arg),*), $crate::COLUMN_UUID_DELIM, column!());
100 }
101}
102pub use crate::log;
103
104#[macro_export]
105macro_rules! debug {
106 ($($arg:expr),*) => {
107 $crate::hc_internal_log_debug!("{:?}{}{}", ($($arg),*), $crate::COLUMN_UUID_DELIM, column!());
108 }
109}
110pub use crate::debug;
111
112#[macro_export]
113macro_rules! info {
114 ($($arg:expr),*) => {
115 $crate::hc_internal_log_info!("{:?}{}{}", ($($arg),*), $crate::COLUMN_UUID_DELIM, column!());
116 }
117}
118pub use crate::info;
119
120#[macro_export]
121macro_rules! warn {
122 ($($arg:expr),*) => {
123 $crate::hc_internal_log_warn!("{:?}{}{}", ($($arg),*), $crate::COLUMN_UUID_DELIM, column!());
124 }
125}
126pub use crate::warn;
127
128#[macro_export]
129macro_rules! error {
130 ($($arg:expr),*) => {
131 $crate::hc_internal_log_error!("{:?}{}{}", ($($arg),*), $crate::COLUMN_UUID_DELIM, column!());
132 }
133}
134pub use crate::error;
135
136#[macro_export]
137macro_rules! log_fmt {
138 ($fmt:expr, $($arg:tt)*) => {
139 $crate::hc_internal_log_debug!("{}{}{}", format!($fmt, $($arg)*), $crate::COLUMN_UUID_DELIM, column!());
140 };
141 ($msg:expr) => {
142 $crate::hc_internal_log_debug!("{}{}{}", format!($msg), $crate::COLUMN_UUID_DELIM, column!());
143 };
144}
145pub use crate::log_fmt;
146
147#[macro_export]
148macro_rules! debug_fmt {
149 ($fmt:expr, $($arg:tt)*) => {
150 $crate::hc_internal_log_debug!("{}{}{}", format!($fmt, $($arg)*), $crate::COLUMN_UUID_DELIM, column!());
151 };
152 ($msg:expr) => {
153 $crate::hc_internal_log_debug!("{}{}{}", format!($msg), $crate::COLUMN_UUID_DELIM, column!());
154 };
155}
156pub use crate::debug_fmt;
157
158#[macro_export]
159macro_rules! info_fmt {
160 ($fmt:expr, $($arg:tt)*) => {
161 $crate::hc_internal_log_info!("{}{}{}", format!($fmt, $($arg)*), $crate::COLUMN_UUID_DELIM, column!());
162 };
163 ($msg:expr) => {
164 $crate::hc_internal_log_info!("{}{}{}", format!($msg), $crate::COLUMN_UUID_DELIM, column!());
165 };
166}
167pub use crate::info_fmt;
168
169#[macro_export]
170macro_rules! warn_fmt {
171 ($fmt:expr, $($arg:tt)*) => {
172 $crate::hc_internal_log_warn!("{}{}{}", format!($fmt, $($arg)*), $crate::COLUMN_UUID_DELIM, column!());
173 };
174 ($msg:expr) => {
175 $crate::hc_internal_log_warn!("{}{}{}", format!($msg), $crate::COLUMN_UUID_DELIM, column!());
176 };
177}
178pub use crate::warn_fmt;
179
180#[macro_export]
181macro_rules! log_string {
182 ($document:expr) => {
183 log!($document);
184 };
185}
186
187#[macro_export]
188macro_rules! log_type {
189 ($t:ty) => {
190 log!(std::any::type_name::<$t>());
191 };
192}
193
194#[macro_export]
195macro_rules! json_value {
196 ({ $($key:expr => $value:expr),* $(,)? }) => {
198 {
199 #[allow(unused_mut)]
200 let mut map = serde_json::Map::new();
201 $(
202 map.insert($key.into(), serde_json::to_value($value).expect("Failed to convert to JSON value"));
203 )*
204 serde_json::Value::Object(map)
205 }
206 };
207 ($key:expr) => {
209 {
210 serde_json::to_value($key).expect("Failed to convert to JSON value")
211 }
212 };
213}
214
215#[macro_export]
217macro_rules! unwrap_or_custom_error {
218 ($result:expr, $error:expr) => {
219 match $result {
220 Ok(value) => value,
221 Err(_) => {
222 error_abort!($result.context($error))
224 }
225 }
226 };
227}
228
229#[macro_export]
230macro_rules! unwrap_or_result_error {
231 ($result:expr, $error:expr) => {
232 unwrap_or_custom_error!(
233 $result.map_err(|e| anyhow::anyhow!("{}", e)),
234 $error
235 )
236 };
237}
238
239#[macro_export]
240macro_rules! bail_if_none {
241 ($opt:expr) => {
243 $opt.ok_or_else(|| anyhow::anyhow!(format!("unexpected None, at {} line: {}, column: {}", file!(), line!(), column!())))?
244 };
245
246 ($opt:expr, $msg:expr) => {
248 $opt.ok_or_else(|| anyhow::anyhow!(format!("{:?}, at {} line: {}, column: {}", $msg, file!(), line!(), column!())))?
249 };
250
251 ($opt:expr, $fmt:expr, $($args:tt)+) => {
253 $opt.ok_or_else(|| anyhow::anyhow!($fmt, $($args)+))?
254 };
255}
256
257pub fn backtrace_string() -> String {
258 format!("{}", Backtrace::capture())
259}
260
261pub fn backtrace_print() {
262 println!("Backtrace: {}", backtrace_string());
263}
264
265pub fn this_pid() -> Vec<u8> {
266 std::process::id().to_string().into_bytes()
267}
268
269pub fn in_array(needle: Vec<u8>, map: HashMap<u32, Vec<u8>>) -> bool {
270 map.iter().any(|(_, val)| *val == needle)
271}
272
273pub fn is_subprocess(invocation: &Invocation) -> bool {
274 invocation.is_subprocess()
275}
276
277pub fn get_service_name(invocation: &Invocation) -> String {
278 if is_subprocess(invocation) {
279 invocation
280 .subprocess()
281 .map(|s| s.service_name.clone())
282 .unwrap_or_default()
283 } else {
284 String::new()
285 }
286}
287
288pub fn remove_suffix<'a>(string: &'a str, suffix: &str) -> &'a str {
289 if string.to_string().ends_with(suffix) {
290 &string[..string.len() - suffix.len()]
291 } else {
292 string
293 }
294}
295
296pub fn sleep(seconds: u64) {
297 std::thread::sleep(std::time::Duration::from_secs(seconds));
298}
299
300pub fn usleep(microseconds: u64) {
301 std::thread::sleep(std::time::Duration::from_micros(microseconds));
302}
303
304pub fn setup_panic_hooks(manager: &ProcessManager) {
305 panic_hooks::setup_panic_hooks(manager)
306}
307
308pub fn send_message(channel: &Channel, message: &str) -> String {
309 crate::workspace::ipc_old::send_message(channel, message)
310}
311
312pub fn maybe_send_message(
313 channel: &Channel,
314 message: &str,
315) -> Result<String, Box<dyn Error>> {
316 crate::workspace::ipc_old::maybe_send_message(channel, message)
317}
318
319pub async fn wait_for_message(channel: &Channel, msgid: u64) -> String {
320 crate::workspace::ipc_old::wait_for_message(channel, msgid).await
321}
322
323pub fn listen_for_message(channel: &Channel, msgid: u64) -> String {
324 crate::workspace::ipc_old::listen_for_message(channel, msgid)
325}
326
327pub fn call_ipc_sync(
328 channel: &Channel,
329 method: &str,
330 args: Value,
331) -> Result<String> {
332 crate::workspace::ipc_old::call_ipc_sync(channel, method, args)
333}
334
335pub async fn call_ipc(
336 channel: &Channel,
337 method: &str,
338 args: Value,
339) -> Result<String> {
340 crate::workspace::ipc_old::call_ipc(channel, method, args).await
341}
342
343pub fn shutdown(process_manager: &ProcessManager) {
344 println!("Shutting down");
345 cleanup_processes(process_manager);
346 std::process::exit(1);
347}
348
349pub fn upgrade_in_place(
350 temp_path: &PathBuf,
351 target_path: &PathBuf,
352) -> Result<()> {
353 fs::copy(temp_path, target_path)
354 .with_context(|| "Failed to copy new executable over old one")?;
355 Ok(())
356}
357
358pub fn fork(path: &PathBuf, args: Vec<&str>) {
359 if let Ok(Fork::Child) = daemon(false, false) {
360 Command::new(path)
361 .args(args)
362 .output()
363 .expect("failed to execute process");
364 }
365}
366
367fn get_ctoolbox_process(s: &mut System, pid: u32) -> Option<&Process> {
369 s.refresh_all();
370 let process = s.process(Pid::from_u32(pid))?;
371 let subprocess_exe = process.exe()?;
372 let this_exe = env::current_exe().unwrap();
373 if this_exe != subprocess_exe {
374 return None;
375 }
376 Some(process)
377}
378
379pub fn get_this_executable() -> PathBuf {
380 let mut s = System::new_all();
381 s.refresh_all();
382 env::current_exe().unwrap()
383}
384
385pub fn wait_for_ctoolbox_process_exit<'a>(pid: u32) {
386 let mut s = System::new_all();
387 let mut process = get_ctoolbox_process(&mut s, pid);
388 while process.is_some() {
389 process = get_ctoolbox_process(&mut s, pid);
390 std::thread::sleep(std::time::Duration::from_millis(100));
391 }
392}
393
394pub fn wait_for_ctoolbox_exit_and_clean_up(pid: u32) {
395 wait_for_ctoolbox_process_exit(pid);
396}
397
398pub fn cleanup_processes(process_manager: &ProcessManager) {
399 process_manager
402 .processes
403 .iter()
404 .for_each(|(_id, process_record)| {
405 let mut s = System::new_all();
406 if let Some(process) = get_ctoolbox_process(
407 &mut s,
408 process_record
409 .system_pid
410 .try_into()
411 .expect("Invalid PID for this platform"),
412 ) {
413 process.kill();
415 }
416 });
417}
418
419pub fn generate_authentication_key() -> String {
420 let pg = PasswordGenerator {
421 length: 64,
422 numbers: true,
423 lowercase_letters: true,
424 uppercase_letters: true,
425 symbols: true,
426 spaces: true,
427 exclude_similar_characters: false,
428 strict: true,
429 };
430
431 pg.generate_one().unwrap()
432}
433
434pub fn u8_vec_to_formatted_hex(values: &[u8]) -> String {
435 let mut out = String::new();
436 for &v in values {
437 out.push_str(&format!("{v:02x} "));
438 }
439 out.to_uppercase().trim().to_string()
440}
441
442pub fn u32_vec_to_formatted_hex(values: &[u32]) -> String {
443 let mut out = String::new();
444 for &v in values {
445 out.push_str(&format!("{v:02x} "));
446 }
447 out.to_uppercase().trim().to_string()
448}
449
450pub fn assert_vec_u32_eq(expected: &[u32], actual: &[u32]) -> Vec<u32> {
452 assert!(
453 expected == actual,
454 "Vectors (u32) differ.\n{}",
455 fmt_mismatch_vec_u32(expected, actual)
456 );
457 actual.to_vec()
458}
459
460pub fn assert_vec_u8_eq(expected: &[u8], actual: &[u8]) -> Vec<u8> {
461 assert!(
462 expected == actual,
463 "Vectors (u8) differ.\n{}",
464 fmt_mismatch_vec_u8(expected, actual)
465 );
466 actual.to_vec()
467}
468
469pub fn fmt_mismatch_vec_u8(expected: &[u8], actual: &[u8]) -> String {
470 format!(
471 "Expected: {:?}\nActual: {:?}\nExpected (hex): {:?}\nActual (hex): {:?}\nExpected (lossy): {:?}\nActual (lossy): {:?}",
472 expected,
473 actual,
474 u8_vec_to_formatted_hex(expected),
475 u8_vec_to_formatted_hex(actual),
476 String::from_utf8_lossy(expected),
477 String::from_utf8_lossy(actual)
478 )
479}
480
481pub fn fmt_mismatch_vec_u32(expected: &[u32], actual: &[u32]) -> String {
482 format!(
483 "Expected: {:?}\nActual: {:?}\nExpected (hex): {:?}\nActual (hex): {:?}\nExpected (lossy): {:?}\nActual (lossy): {:?}",
484 expected,
485 actual,
486 u32_vec_to_formatted_hex(expected),
487 u32_vec_to_formatted_hex(actual),
488 scalars_to_string_lossy(expected),
489 scalars_to_string_lossy(actual)
490 )
491}
492
493pub fn fmt_mismatch_string(expected: &str, actual: &str) -> String {
494 format!(
495 "Expected: {:?}\nActual: {:?}\nExpected (hex): {:?}\nActual (hex): {:?}",
496 expected,
497 actual,
498 u8_vec_to_formatted_hex(expected.as_bytes()),
499 u8_vec_to_formatted_hex(actual.as_bytes()),
500 )
501}
502
503pub fn feature(feature_name: &str) -> bool {
504 let current_settings =
505 crate::storage::pc_settings::PcSettings::load().unwrap_or_default();
506 match feature_name {
507 "login" => current_settings.feature_login,
508 "registration" => current_settings.feature_registration,
509 _ => false,
510 }
511}
512
513thread_local! {
514 pub static CURRENT_TEST_NAME: std::cell::RefCell<Option<String>> = std::cell::RefCell::new(None);
515}
516
517#[cfg(test)]
518pub fn set_current_test_name(p: String) {
519 CURRENT_TEST_NAME.with(|c| *c.borrow_mut() = Some(p));
520}
521
522pub fn get_current_test_name() -> String {
523 assert!(cfg!(test), "get_current_test_name called outside of test");
524
525 if let Some(p) = CURRENT_TEST_NAME.with(|c| c.borrow().clone()) {
526 p
527 } else {
528 panic!("Test name not set");
529 }
530}
531
532#[derive(Serialize)]
533pub struct BuildInfo {
534 pub(crate) name: String,
535 pub(crate) version: String,
536 pub(crate) build_date: String,
537 pub(crate) commit: String,
538}
539
540pub fn build_info() -> BuildInfo {
541 BuildInfo {
542 name: crate_name!().to_string(),
543 version: crate_version!().to_string(),
544 build_date: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
545 commit: env!("VERGEN_GIT_SHA").to_string(),
546 }
547}
548
549pub fn assert_vec_u32_ok_eq(
550 expected: &[u32],
551 actual: anyhow::Result<Vec<u32>>,
552) -> Vec<u32> {
553 match actual {
554 Ok(v) => assert_vec_u32_eq(expected, &v),
555 Err(e) => panic!("Expected Ok(Vec<u32>), got Err: {e:?}"),
556 }
557}
558
559pub fn assert_vec_u8_ok_eq(
560 expected: &[u8],
561 actual: anyhow::Result<Vec<u8>>,
562) -> Vec<u8> {
563 match actual {
564 Ok(v) => assert_vec_u8_eq(expected, &v),
565 Err(e) => panic!("Expected Ok(Vec<u8>), got Err: {e:?}"),
566 }
567}
568
569pub fn assert_string_contains(expected: &str, actual: &str) {
570 assert!(
571 actual.contains(expected),
572 "String '{actual}' does not contain expected substring '{expected}'."
573 );
574}
575
576pub fn assert_string_not_contains(expected: &str, actual: &str) {
577 assert!(
578 !actual.contains(expected),
579 "String '{actual}' unexpectedly contains forbidden substring '{expected}'."
580 );
581}