use crate::headless_project::HeadlessAppState;use crate::HeadlessProject;use anyhow::{anyhow, Context, Result};use chrono::Utc;use client::{telemetry, ProxySettings};use extension::ExtensionHostProxy;use fs::{Fs, RealFs};use futures::channel::mpsc;use futures::{select, select_biased, AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt};use git::GitHostingProviderRegistry;use gpui::{AppContext, Context as _, Model, ModelContext, SemanticVersion, UpdateGlobal as _};use http_client::{read_proxy_from_env, Uri};use language::LanguageRegistry;use node_runtime::{NodeBinaryOptions, NodeRuntime};use paths::logs_dir;use project::project_settings::ProjectSettings; use release_channel::AppVersion;use remote::proxy::ProxyLaunchError;use remote::ssh_session::ChannelClient;use remote::{ json_log::LogRecord, protocol::{read_message, write_message},};use reqwest_client::ReqwestClient;use rpc::proto::{self, Envelope, SSH_PROJECT_ID};use rpc::{AnyProtoClient, TypedEnvelope};use settings::{watch_config_file, Settings, SettingsStore};use smol::channel::{Receiver, Sender};use smol::io::AsyncReadExt; use smol::Async;use smol::{net::unix::UnixListener, stream::StreamExt as _};use std::ffi::OsStr;use std::ops::ControlFlow;use std::str::FromStr;use std::{env, thread};use std::{ io::Write, mem, path::{Path, PathBuf}, sync::Arc,};use telemetry_events::LocationData;use util::ResultExt; fn init_logging_proxy() { env_logger::builder() .format(|buf, record| { let mut log_record = LogRecord::new(record); log_record.message = format!("(remote proxy) {}", log_record.message); serde_json::to_writer(&mut *buf, &log_record)?; buf.write_all(b"\n")?; Ok(()) }) .init();} fn init_logging_server(log_file_path: PathBuf) -> Result<Receiver<Vec<u8>>> { struct MultiWrite { file: Box<dyn std::io::Write + Send + 'static>, channel: Sender<Vec<u8>>, buffer: Vec<u8>, } impl std::io::Write for MultiWrite { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { let written = self.file.write(buf)?; self.buffer.extend_from_slice(&buf[..written]); Ok(written) } fn flush(&mut self) -> std::io::Result<()> { self.channel .send_blocking(self.buffer.clone()) .map_err(|error| std::io::Error::new(std::io::ErrorKind::Other, error))?; self.buffer.clear(); self.file.flush() } } let log_file = Box::new(if log_file_path.exists() { std::fs::OpenOptions::new() .append(true) .open(&log_file_path) .context("Failed to open log file in append mode")? } else { std::fs::File::create(&log_file_path).context("Failed to create log file")? }); let (tx, rx) = smol::channel::unbounded(); let target = Box::new(MultiWrite { file: log_file, channel: tx, buffer: Vec::new(), }); env_logger::Builder::from_default_env() .target(env_logger::Target::Pipe(target)) .format(|buf, record| { let mut log_record = LogRecord::new(record); log_record.message = format!("(remote server) {}", log_record.message); serde_json::to_writer(&mut *buf, &log_record)?; buf.write_all(b"\n")?; Ok(()) }) .init(); Ok(rx)} fn init_panic_hook() { std::panic::set_hook(Box::new(|info| { let payload = info .payload() .downcast_ref::<&str>() .map(|s| s.to_string()) .or_else(|| info.payload().downcast_ref::<String>().cloned()) .unwrap_or_else(|| "Box<Any>".to_string()); let backtrace = backtrace::Backtrace::new(); let mut backtrace = backtrace .frames() .iter() .flat_map(|frame| { frame .symbols() .iter() .filter_map(|frame| Some(format!("{:#}", frame.name()?))) }) .collect::<Vec<_>>(); // Strip out leading stack frames for rust panic-handling. if let Some(ix) = backtrace .iter() .position(|name| name == "rust_begin_unwind") { backtrace.drain(0..=ix); } let thread = thread::current(); let thread_name = thread.name().unwrap_or("<unnamed>"); log::error!( "panic occurred: {}\nBacktrace:\n{}", &payload, (&backtrace).join("\n") ); let panic_data = telemetry_events::Panic { thread: thread_name.into(), payload: payload.clone(), location_data: info.location().map(|location| LocationData { file: location.file().into(), line: location.line(), }), app_version: format!( "remote-server-{}", option_env!("ZED_COMMIT_SHA").unwrap_or(&env!("ZED_PKG_VERSION")) ), release_channel: release_channel::RELEASE_CHANNEL.display_name().into(), target: env!("TARGET").to_owned().into(), os_name: telemetry::os_name(), os_version: Some(telemetry::os_version()), architecture: env::consts::ARCH.into(), panicked_on: Utc::now().timestamp_millis(), backtrace, system_id: None, // Set on SSH client installation_id: None, // Set on SSH client session_id: "".to_string(), // Set on SSH client }; if let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err() { let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); let panic_file_path = paths::logs_dir().join(format!("zed-{timestamp}.panic")); let panic_file = std::fs::OpenOptions::new() .append(true) .create(true) .open(&panic_file_path) .log_err(); if let Some(mut panic_file) = panic_file { writeln!(&mut panic_file, "{panic_data_json}").log_err(); panic_file.flush().log_err(); } } std::process::abort(); }));} fn handle_panic_requests(project: &Model<HeadlessProject>, client: &Arc<ChannelClient>) { let client: AnyProtoClient = client.clone().into(); client.add_request_handler( project.downgrade(), |_, _: TypedEnvelope<proto::GetPanicFiles>, _cx| async move { let mut children = smol::fs::read_dir(paths::logs_dir()).await?; let mut panic_files = Vec::new(); while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); if child_path.extension() != Some(OsStr::new("panic")) { continue; } let filename = if let Some(filename) = child_path.file_name() { filename.to_string_lossy() } else { continue; }; if !filename.starts_with("zed") { continue; } let file_contents = smol::fs::read_to_string(&child_path) .await .context("error reading panic file")?; panic_files.push(file_contents); // We've done what we can, delete the file std::fs::remove_file(child_path) .context("error removing panic") .log_err(); } anyhow::Ok(proto::GetPanicFilesResponse { file_contents: panic_files, }) }, );} struct ServerListeners { stdin: UnixListener, stdout: UnixListener, stderr: UnixListener,} impl ServerListeners { pub fn new(stdin_path: PathBuf, stdout_path: PathBuf, stderr_path: PathBuf) -> Result<Self> { Ok(Self { stdin: UnixListener::bind(stdin_path).context("failed to bind stdin socket")?, stdout: UnixListener::bind(stdout_path).context("failed to bind stdout socket")?, stderr: UnixListener::bind(stderr_path).context("failed to bind stderr socket")?, }) }} fn start_server( listeners: ServerListeners, log_rx: Receiver<Vec<u8>>, cx: &mut AppContext,) -> Arc<ChannelClient> { // This is the server idle timeout. If no connection comes in in this timeout, the server will shut down. const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10 * 60); let (incoming_tx, incoming_rx) = mpsc::unbounded::<Envelope>(); let (outgoing_tx, mut outgoing_rx) = mpsc::unbounded::<Envelope>(); let (app_quit_tx, mut app_quit_rx) = mpsc::unbounded::<()>(); cx.on_app_quit(move |_| { let mut app_quit_tx = app_quit_tx.clone(); async move { log::info!("app quitting. sending signal to server main loop"); app_quit_tx.send(()).await.ok(); } }) .detach(); cx.spawn(|cx| async move { let mut stdin_incoming = listeners.stdin.incoming(); let mut stdout_incoming = listeners.stdout.incoming(); let mut stderr_incoming = listeners.stderr.incoming(); loop { let streams = futures::future::join3(stdin_incoming.next(), stdout_incoming.next(), stderr_incoming.next()); log::info!("accepting new connections"); let result = select! { streams = streams.fuse() => { let (Some(Ok(stdin_stream)), Some(Ok(stdout_stream)), Some(Ok(stderr_stream))) = streams else { break; }; anyhow::Ok((stdin_stream, stdout_stream, stderr_stream)) } _ = futures::FutureExt::fuse(smol::Timer::after(IDLE_TIMEOUT)) => { log::warn!("timed out waiting for new connections after {:?}. exiting.", IDLE_TIMEOUT); cx.update(|cx| { // TODO: This is a hack, because in a headless project, shutdown isn't executed // when calling quit, but it should be. cx.shutdown(); cx.quit(); })?; break; } _ = app_quit_rx.next().fuse() => { break; } }; let Ok((mut stdin_stream, mut stdout_stream, mut stderr_stream)) = result else { break; }; let mut input_buffer = Vec::new(); let mut output_buffer = Vec::new(); let (mut stdin_msg_tx, mut stdin_msg_rx) = mpsc::unbounded::<Envelope>(); cx.background_executor().spawn(async move { while let Ok(msg) = read_message(&mut stdin_stream, &mut input_buffer).await { if let Err(_) = stdin_msg_tx.send(msg).await { break; } } }).detach(); loop { select_biased! { _ = app_quit_rx.next().fuse() => { return anyhow::Ok(()); } stdin_message = stdin_msg_rx.next().fuse() => { let Some(message) = stdin_message else { log::warn!("error reading message on stdin. exiting."); break; }; if let Err(error) = incoming_tx.unbounded_send(message) { log::error!("failed to send message to application: {:?}. exiting.", error); return Err(anyhow!(error)); } } outgoing_message = outgoing_rx.next().fuse() => { let Some(message) = outgoing_message else { log::error!("stdout handler, no message"); break; }; if let Err(error) = write_message(&mut stdout_stream, &mut output_buffer, message).await { log::error!("failed to write stdout message: {:?}", error); break; } if let Err(error) = stdout_stream.flush().await { log::error!("failed to flush stdout message: {:?}", error); break; } } log_message = log_rx.recv().fuse() => { if let Ok(log_message) = log_message { if let Err(error) = stderr_stream.write_all(&log_message).await { log::error!("failed to write log message to stderr: {:?}", error); break; } if let Err(error) = stderr_stream.flush().await { log::error!("failed to flush stderr stream: {:?}", error); break; } } } } } } anyhow::Ok(()) }) .detach(); ChannelClient::new(incoming_rx, outgoing_tx, cx, "server")} fn init_paths() -> anyhow::Result<()> { for path in [ paths::config_dir(), paths::extensions_dir(), paths::languages_dir(), paths::logs_dir(), paths::temp_dir(), paths::remote_extensions_dir(), paths::remote_extensions_uploads_dir(), ] .iter() { std::fs::create_dir_all(path) .map_err(|e| anyhow!("Could not create directory {:?}: {}", path, e))?; } Ok(())} pub fn execute_run( log_file: PathBuf, pid_file: PathBuf, stdin_socket: PathBuf, stdout_socket: PathBuf, stderr_socket: PathBuf,) -> Result<()> { init_paths()?; match daemonize()? { ControlFlow::Break(_) => return Ok(()), ControlFlow::Continue(_) => {} } init_panic_hook(); let log_rx = init_logging_server(log_file)?; log::info!( "starting up. pid_file: {:?}, stdin_socket: {:?}, stdout_socket: {:?}, stderr_socket: {:?}", pid_file, stdin_socket, stdout_socket, stderr_socket ); write_pid_file(&pid_file) .with_context(|| format!("failed to write pid file: {:?}", &pid_file))?; let listeners = ServerListeners::new(stdin_socket, stdout_socket, stderr_socket)?; let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new()); gpui::App::headless().run(move |cx| { settings::init(cx); let app_version = AppVersion::init(env!("ZED_PKG_VERSION")); release_channel::init(app_version, cx); HeadlessProject::init(cx); log::info!("gpui app started, initializing server"); let session = start_server(listeners, log_rx, cx); client::init_settings(cx); GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx); git_hosting_providers::init(cx); extension::init(cx); let extension_host_proxy = ExtensionHostProxy::global(cx); let project = cx.new_model(|cx| { let fs = Arc::new(RealFs::new(Default::default(), None)); let node_settings_rx = initialize_settings(session.clone(), fs.clone(), cx); let proxy_url = read_proxy_settings(cx); let http_client = Arc::new( ReqwestClient::proxy_and_user_agent( proxy_url, &format!( "Zed-Server/{} ({}; {})", env!("CARGO_PKG_VERSION"), std::env::consts::OS, std::env::consts::ARCH ), ) .expect("Could not start HTTP client"), ); let node_runtime = NodeRuntime::new(http_client.clone(), node_settings_rx); let mut languages = LanguageRegistry::new(cx.background_executor().clone()); languages.set_language_server_download_dir(paths::languages_dir().clone()); let languages = Arc::new(languages); HeadlessProject::new( HeadlessAppState { session: session.clone(), fs, http_client, node_runtime, languages, extension_host_proxy, }, cx, ) }); handle_panic_requests(&project, &session); cx.background_executor() .spawn(async move { cleanup_old_binaries() }) .detach(); mem::forget(project); }); log::info!("gpui app is shut down. quitting."); Ok(())} #[derive(Clone)]struct ServerPaths { log_file: PathBuf, pid_file: PathBuf, stdin_socket: PathBuf, stdout_socket: PathBuf, stderr_socket: PathBuf,} Lines 1–500 of 939. Load more
use crate::headless_project::HeadlessAppState; use crate::HeadlessProject; use anyhow::{anyhow, Context, Result}; use chrono::Utc; use client::{telemetry, ProxySettings}; use extension::ExtensionHostProxy; use fs::{Fs, RealFs}; use futures::channel::mpsc; use futures::{select, select_biased, AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt}; use git::GitHostingProviderRegistry; use gpui::{AppContext, Context as _, Model, ModelContext, SemanticVersion, UpdateGlobal as _}; use http_client::{read_proxy_from_env, Uri}; use language::LanguageRegistry; use node_runtime::{NodeBinaryOptions, NodeRuntime}; use paths::logs_dir; use project::project_settings::ProjectSettings; use release_channel::AppVersion; use remote::proxy::ProxyLaunchError; use remote::ssh_session::ChannelClient; use remote::{ json_log::LogRecord, protocol::{read_message, write_message}, }; use reqwest_client::ReqwestClient; use rpc::proto::{self, Envelope, SSH_PROJECT_ID}; use rpc::{AnyProtoClient, TypedEnvelope}; use settings::{watch_config_file, Settings, SettingsStore}; use smol::channel::{Receiver, Sender}; use smol::io::AsyncReadExt; use smol::Async; use smol::{net::unix::UnixListener, stream::StreamExt as _}; use std::ffi::OsStr; use std::ops::ControlFlow; use std::str::FromStr; use std::{env, thread}; use std::{ io::Write, mem, path::{Path, PathBuf}, sync::Arc, }; use telemetry_events::LocationData; use util::ResultExt; fn init_logging_proxy() { env_logger::builder() .format(|buf, record| { let mut log_record = LogRecord::new(record); log_record.message = format!("(remote proxy) {}", log_record.message); serde_json::to_writer(&mut *buf, &log_record)?; buf.write_all(b"\n")?; Ok(()) }) .init(); } fn init_logging_server(log_file_path: PathBuf) -> Result<Receiver<Vec<u8>>> { struct MultiWrite { file: Box<dyn std::io::Write + Send + 'static>, channel: Sender<Vec<u8>>, buffer: Vec<u8>, } impl std::io::Write for MultiWrite { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { let written = self.file.write(buf)?; self.buffer.extend_from_slice(&buf[..written]); Ok(written) } fn flush(&mut self) -> std::io::Result<()> { self.channel .send_blocking(self.buffer.clone()) .map_err(|error| std::io::Error::new(std::io::ErrorKind::Other, error))?; self.buffer.clear(); self.file.flush() } } let log_file = Box::new(if log_file_path.exists() { std::fs::OpenOptions::new() .append(true) .open(&log_file_path) .context("Failed to open log file in append mode")? } else { std::fs::File::create(&log_file_path).context("Failed to create log file")? }); let (tx, rx) = smol::channel::unbounded(); let target = Box::new(MultiWrite { file: log_file, channel: tx, buffer: Vec::new(), }); env_logger::Builder::from_default_env() .target(env_logger::Target::Pipe(target)) .format(|buf, record| { let mut log_record = LogRecord::new(record); log_record.message = format!("(remote server) {}", log_record.message); serde_json::to_writer(&mut *buf, &log_record)?; buf.write_all(b"\n")?; Ok(()) }) .init(); Ok(rx) } fn init_panic_hook() { std::panic::set_hook(Box::new(|info| { let payload = info .payload() .downcast_ref::<&str>() .map(|s| s.to_string()) .or_else(|| info.payload().downcast_ref::<String>().cloned()) .unwrap_or_else(|| "Box<Any>".to_string()); let backtrace = backtrace::Backtrace::new(); let mut backtrace = backtrace .frames() .iter() .flat_map(|frame| { frame .symbols() .iter() .filter_map(|frame| Some(format!("{:#}", frame.name()?))) }) .collect::<Vec<_>>(); // Strip out leading stack frames for rust panic-handling. if let Some(ix) = backtrace .iter() .position(|name| name == "rust_begin_unwind") { backtrace.drain(0..=ix); } let thread = thread::current(); let thread_name = thread.name().unwrap_or("<unnamed>"); log::error!( "panic occurred: {}\nBacktrace:\n{}", &payload, (&backtrace).join("\n") ); let panic_data = telemetry_events::Panic { thread: thread_name.into(), payload: payload.clone(), location_data: info.location().map(|location| LocationData { file: location.file().into(), line: location.line(), }), app_version: format!( "remote-server-{}", option_env!("ZED_COMMIT_SHA").unwrap_or(&env!("ZED_PKG_VERSION")) ), release_channel: release_channel::RELEASE_CHANNEL.display_name().into(), target: env!("TARGET").to_owned().into(), os_name: telemetry::os_name(), os_version: Some(telemetry::os_version()), architecture: env::consts::ARCH.into(), panicked_on: Utc::now().timestamp_millis(), backtrace, system_id: None, // Set on SSH client installation_id: None, // Set on SSH client session_id: "".to_string(), // Set on SSH client }; if let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err() { let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); let panic_file_path = paths::logs_dir().join(format!("zed-{timestamp}.panic")); let panic_file = std::fs::OpenOptions::new() .append(true) .create(true) .open(&panic_file_path) .log_err(); if let Some(mut panic_file) = panic_file { writeln!(&mut panic_file, "{panic_data_json}").log_err(); panic_file.flush().log_err(); } } std::process::abort(); })); } fn handle_panic_requests(project: &Model<HeadlessProject>, client: &Arc<ChannelClient>) { let client: AnyProtoClient = client.clone().into(); client.add_request_handler( project.downgrade(), |_, _: TypedEnvelope<proto::GetPanicFiles>, _cx| async move { let mut children = smol::fs::read_dir(paths::logs_dir()).await?; let mut panic_files = Vec::new(); while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); if child_path.extension() != Some(OsStr::new("panic")) { continue; } let filename = if let Some(filename) = child_path.file_name() { filename.to_string_lossy() } else { continue; }; if !filename.starts_with("zed") { continue; } let file_contents = smol::fs::read_to_string(&child_path) .await .context("error reading panic file")?; panic_files.push(file_contents); // We've done what we can, delete the file std::fs::remove_file(child_path) .context("error removing panic") .log_err(); } anyhow::Ok(proto::GetPanicFilesResponse { file_contents: panic_files, }) }, ); } struct ServerListeners { stdin: UnixListener, stdout: UnixListener, stderr: UnixListener, } impl ServerListeners { pub fn new(stdin_path: PathBuf, stdout_path: PathBuf, stderr_path: PathBuf) -> Result<Self> { Ok(Self { stdin: UnixListener::bind(stdin_path).context("failed to bind stdin socket")?, stdout: UnixListener::bind(stdout_path).context("failed to bind stdout socket")?, stderr: UnixListener::bind(stderr_path).context("failed to bind stderr socket")?, }) } } fn start_server( listeners: ServerListeners, log_rx: Receiver<Vec<u8>>, cx: &mut AppContext, ) -> Arc<ChannelClient> { // This is the server idle timeout. If no connection comes in in this timeout, the server will shut down. const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10 * 60); let (incoming_tx, incoming_rx) = mpsc::unbounded::<Envelope>(); let (outgoing_tx, mut outgoing_rx) = mpsc::unbounded::<Envelope>(); let (app_quit_tx, mut app_quit_rx) = mpsc::unbounded::<()>(); cx.on_app_quit(move |_| { let mut app_quit_tx = app_quit_tx.clone(); async move { log::info!("app quitting. sending signal to server main loop"); app_quit_tx.send(()).await.ok(); } }) .detach(); cx.spawn(|cx| async move { let mut stdin_incoming = listeners.stdin.incoming(); let mut stdout_incoming = listeners.stdout.incoming(); let mut stderr_incoming = listeners.stderr.incoming(); loop { let streams = futures::future::join3(stdin_incoming.next(), stdout_incoming.next(), stderr_incoming.next()); log::info!("accepting new connections"); let result = select! { streams = streams.fuse() => { let (Some(Ok(stdin_stream)), Some(Ok(stdout_stream)), Some(Ok(stderr_stream))) = streams else { break; }; anyhow::Ok((stdin_stream, stdout_stream, stderr_stream)) } _ = futures::FutureExt::fuse(smol::Timer::after(IDLE_TIMEOUT)) => { log::warn!("timed out waiting for new connections after {:?}. exiting.", IDLE_TIMEOUT); cx.update(|cx| { // TODO: This is a hack, because in a headless project, shutdown isn't executed // when calling quit, but it should be. cx.shutdown(); cx.quit(); })?; break; } _ = app_quit_rx.next().fuse() => { break; } }; let Ok((mut stdin_stream, mut stdout_stream, mut stderr_stream)) = result else { break; }; let mut input_buffer = Vec::new(); let mut output_buffer = Vec::new(); let (mut stdin_msg_tx, mut stdin_msg_rx) = mpsc::unbounded::<Envelope>(); cx.background_executor().spawn(async move { while let Ok(msg) = read_message(&mut stdin_stream, &mut input_buffer).await { if let Err(_) = stdin_msg_tx.send(msg).await { break; } } }).detach(); loop { select_biased! { _ = app_quit_rx.next().fuse() => { return anyhow::Ok(()); } stdin_message = stdin_msg_rx.next().fuse() => { let Some(message) = stdin_message else { log::warn!("error reading message on stdin. exiting."); break; }; if let Err(error) = incoming_tx.unbounded_send(message) { log::error!("failed to send message to application: {:?}. exiting.", error); return Err(anyhow!(error)); } } outgoing_message = outgoing_rx.next().fuse() => { let Some(message) = outgoing_message else { log::error!("stdout handler, no message"); break; }; if let Err(error) = write_message(&mut stdout_stream, &mut output_buffer, message).await { log::error!("failed to write stdout message: {:?}", error); break; } if let Err(error) = stdout_stream.flush().await { log::error!("failed to flush stdout message: {:?}", error); break; } } log_message = log_rx.recv().fuse() => { if let Ok(log_message) = log_message { if let Err(error) = stderr_stream.write_all(&log_message).await { log::error!("failed to write log message to stderr: {:?}", error); break; } if let Err(error) = stderr_stream.flush().await { log::error!("failed to flush stderr stream: {:?}", error); break; } } } } } } anyhow::Ok(()) }) .detach(); ChannelClient::new(incoming_rx, outgoing_tx, cx, "server") } fn init_paths() -> anyhow::Result<()> { for path in [ paths::config_dir(), paths::extensions_dir(), paths::languages_dir(), paths::logs_dir(), paths::temp_dir(), paths::remote_extensions_dir(), paths::remote_extensions_uploads_dir(), ] .iter() { std::fs::create_dir_all(path) .map_err(|e| anyhow!("Could not create directory {:?}: {}", path, e))?; } Ok(()) } pub fn execute_run( log_file: PathBuf, pid_file: PathBuf, stdin_socket: PathBuf, stdout_socket: PathBuf, stderr_socket: PathBuf, ) -> Result<()> { init_paths()?; match daemonize()? { ControlFlow::Break(_) => return Ok(()), ControlFlow::Continue(_) => {} } init_panic_hook(); let log_rx = init_logging_server(log_file)?; log::info!( "starting up. pid_file: {:?}, stdin_socket: {:?}, stdout_socket: {:?}, stderr_socket: {:?}", pid_file, stdin_socket, stdout_socket, stderr_socket ); write_pid_file(&pid_file) .with_context(|| format!("failed to write pid file: {:?}", &pid_file))?; let listeners = ServerListeners::new(stdin_socket, stdout_socket, stderr_socket)?; let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new()); gpui::App::headless().run(move |cx| { settings::init(cx); let app_version = AppVersion::init(env!("ZED_PKG_VERSION")); release_channel::init(app_version, cx); HeadlessProject::init(cx); log::info!("gpui app started, initializing server"); let session = start_server(listeners, log_rx, cx); client::init_settings(cx); GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx); git_hosting_providers::init(cx); extension::init(cx); let extension_host_proxy = ExtensionHostProxy::global(cx); let project = cx.new_model(|cx| { let fs = Arc::new(RealFs::new(Default::default(), None)); let node_settings_rx = initialize_settings(session.clone(), fs.clone(), cx); let proxy_url = read_proxy_settings(cx); let http_client = Arc::new( ReqwestClient::proxy_and_user_agent( proxy_url, &format!( "Zed-Server/{} ({}; {})", env!("CARGO_PKG_VERSION"), std::env::consts::OS, std::env::consts::ARCH ), ) .expect("Could not start HTTP client"), ); let node_runtime = NodeRuntime::new(http_client.clone(), node_settings_rx); let mut languages = LanguageRegistry::new(cx.background_executor().clone()); languages.set_language_server_download_dir(paths::languages_dir().clone()); let languages = Arc::new(languages); HeadlessProject::new( HeadlessAppState { session: session.clone(), fs, http_client, node_runtime, languages, extension_host_proxy, }, cx, ) }); handle_panic_requests(&project, &session); cx.background_executor() .spawn(async move { cleanup_old_binaries() }) .detach(); mem::forget(project); }); log::info!("gpui app is shut down. quitting."); Ok(()) } #[derive(Clone)] struct ServerPaths { log_file: PathBuf, pid_file: PathBuf, stdin_socket: PathBuf, stdout_socket: PathBuf, stderr_socket: PathBuf, }