mod app;
mod commands;
mod config;
mod constants;
mod dcc;
mod image_preview;
mod irc;
mod scripting;
mod session;
mod shell;
mod spellcheck;
mod state;
mod storage;
mod theme;
mod ui;
mod web;
mod nick_color;
use color_eyre::eyre::Result;
use tracing_subscriber::EnvFilter;
fn setup_logging() -> Result<()> {
if std::env::var("RUST_LOG").is_ok() {
let log_dir = constants::home_dir();
std::fs::create_dir_all(&log_dir)?;
let log_file = std::fs::File::options()
.create(true)
.append(true)
.open(log_dir.join("repartee.log"))?;
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.with_writer(log_file)
.with_ansi(false)
.init();
}
Ok(())
}
#[allow(clippy::too_many_lines)]
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
if args.iter().any(|a| a == "--version" || a == "-v") {
println!("{} {}", constants::APP_NAME, constants::APP_VERSION);
return Ok(());
}
if args.get(1).map(String::as_str) == Some("a")
|| args.get(1).map(String::as_str) == Some("attach")
{
color_eyre::install()?;
setup_logging()?;
let target_pid = args.get(2).and_then(|s| s.parse::<u32>().ok());
return tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(session::shim::run_shim(target_pid, false));
}
if args.iter().any(|a| a == "--detach" || a == "-d") {
color_eyre::install()?;
setup_logging()?;
return tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
let mut app = app::App::new()?;
app.detached = true;
let pid = std::process::id();
let sock_path = session::socket_path(pid);
eprintln!("Starting detached. PID={pid}");
eprintln!("Socket: {}", sock_path.display());
eprintln!("Attach with: repartee a");
let result = app.run().await;
app::App::remove_own_socket();
result
});
}
let fork_result = unsafe { libc::fork() };
match fork_result {
-1 => {
color_eyre::install()?;
setup_logging()?;
ui::install_panic_hook();
let mut app = app::App::new()?;
if let Ok((cols, rows)) = crossterm::terminal::size() {
app.cached_term_cols = cols;
app.cached_term_rows = rows;
}
app.terminal = Some(ui::setup_terminal()?);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let result = rt.block_on(app.run());
if let Some(ref mut terminal) = app.terminal {
let _ = ui::restore_terminal(terminal);
}
result
}
0 => {
unsafe {
libc::setsid();
let devnull = libc::open(c"/dev/null".as_ptr(), libc::O_RDWR);
if devnull >= 0 {
libc::dup2(devnull, libc::STDIN_FILENO);
libc::dup2(devnull, libc::STDOUT_FILENO);
libc::dup2(devnull, libc::STDERR_FILENO);
libc::close(devnull);
}
}
color_eyre::install()?;
setup_logging()?;
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
let mut app = app::App::new()?;
app.detached = true;
let result = app.run().await;
app::App::remove_own_socket();
result
})
}
child_pid => {
let child_pid = u32::try_from(child_pid)
.map_err(|_| color_eyre::eyre::eyre!("fork returned invalid PID: {child_pid}"))?;
color_eyre::install()?;
setup_logging()?;
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
let sock_path = session::socket_path(child_pid);
session::shim::run_splash(Some(&sock_path)).await?;
for _ in 0..100 {
if sock_path.exists() {
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
break;
}
if !session::is_pid_alive(child_pid) {
return Err(color_eyre::eyre::eyre!(
"Backend process exited unexpectedly. Check ~/.repartee/repartee.log"
));
}
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
session::shim::run_shim(Some(child_pid), false).await
})
}
}
}