Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): allow starting a session detached #3257

Merged
merged 2 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ pub(crate) fn start_client(opts: CliArgs) {
let mut config_options = config_options.clone();
let mut opts = opts.clone();
let mut is_a_reconnect = false;
let mut should_create_detached = false;

if let Some(reconnect_to_session) = &reconnect_to_session {
// this is integration code to make session reconnects work with this existing,
Expand All @@ -417,6 +418,7 @@ pub(crate) fn start_client(opts: CliArgs) {
opts.command = Some(Command::Sessions(Sessions::Attach {
session_name: reconnect_to_session.name.clone(),
create: true,
background: false,
force_run_commands: false,
index: None,
options: None,
Expand Down Expand Up @@ -476,6 +478,7 @@ pub(crate) fn start_client(opts: CliArgs) {
if let Some(Command::Sessions(Sessions::Attach {
session_name,
create,
background,
force_run_commands,
index,
options,
Expand All @@ -487,17 +490,25 @@ pub(crate) fn start_client(opts: CliArgs) {
},
None => config_options,
};
should_create_detached = background;

let client = if let Some(idx) = index {
attach_with_session_index(config_options.clone(), idx, create)
attach_with_session_index(
config_options.clone(),
idx,
create || should_create_detached,
)
} else {
let session_exists = session_name
.as_ref()
.and_then(|s| session_exists(&s).ok())
.unwrap_or(false);
let resurrection_layout =
session_name.as_ref().and_then(|s| resurrection_layout(&s));
if create && !session_exists && resurrection_layout.is_none() {
if (create || should_create_detached)
&& !session_exists
&& resurrection_layout.is_none()
{
session_name.clone().map(start_client_plan);
}
match (session_name.as_ref(), resurrection_layout) {
Expand All @@ -507,7 +518,11 @@ pub(crate) fn start_client(opts: CliArgs) {
}
ClientInfo::Resurrect(session_name.clone(), resurrection_layout)
},
_ => attach_with_session_name(session_name, config_options.clone(), create),
_ => attach_with_session_name(
session_name,
config_options.clone(),
create || should_create_detached,
),
}
};

Expand Down Expand Up @@ -541,6 +556,7 @@ pub(crate) fn start_client(opts: CliArgs) {
tab_position_to_focus,
pane_id_to_focus,
is_a_reconnect,
should_create_detached,
);
} else {
if let Some(session_name) = opts.session.clone() {
Expand All @@ -555,6 +571,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
should_create_detached,
);
} else {
if let Some(session_name) = config_options.session_name.as_ref() {
Expand Down Expand Up @@ -595,6 +612,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
should_create_detached,
);
},
_ => {
Expand All @@ -609,6 +627,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
should_create_detached,
);
},
}
Expand All @@ -632,6 +651,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
should_create_detached,
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions zellij-client/src/cli_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ fn pipe_client(
let mut buffer = String::new();
let _ = stdin.read_line(&mut buffer);
if buffer.is_empty() {
// TODO: consider notifying the relevant plugin that the pipe has ended with a
// specialized message
let msg = create_msg(None);
os_input.send_to_server(msg);
break;
} else {
// we've got data! send it down the pipe (most common)
Expand Down
69 changes: 69 additions & 0 deletions zellij-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use zellij_utils::{
errors::{ClientContext, ContextType, ErrorInstruction},
input::{config::Config, options::Options},
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
pane_size::Size,
termwiz::input::InputEvent,
};
use zellij_utils::{cli::CliArgs, input::layout::Layout};
Expand Down Expand Up @@ -168,7 +169,12 @@ pub fn start_client(
tab_position_to_focus: Option<usize>,
pane_id_to_focus: Option<(u32, bool)>, // (pane_id, is_plugin)
is_a_reconnect: bool,
start_detached_and_exit: bool,
) -> Option<ConnectToSession> {
if start_detached_and_exit {
start_server_detached(os_input, opts, config, config_options, info, layout);
return None;
}
info!("Starting Zellij client!");

let mut reconnect_to_session = None;
Expand Down Expand Up @@ -541,6 +547,69 @@ pub fn start_client(
reconnect_to_session
}

pub fn start_server_detached(
mut os_input: Box<dyn ClientOsApi>,
opts: CliArgs,
config: Config,
config_options: Options,
info: ClientInfo,
layout: Option<Layout>,
) {
envs::set_zellij("0".to_string());
config.env.set_vars();

let palette = config
.theme_config(&config_options)
.unwrap_or_else(|| os_input.load_palette());

let client_attributes = ClientAttributes {
size: Size { rows: 50, cols: 50 }, // just so size is not 0, it doesn't matter because we
// immediately detach
style: Style {
colors: palette,
rounded_corners: config.ui.pane_frames.rounded_corners,
hide_session_name: config.ui.pane_frames.hide_session_name,
},
keybinds: config.keybinds.clone(),
};

let create_ipc_pipe = || -> std::path::PathBuf {
let mut sock_dir = ZELLIJ_SOCK_DIR.clone();
std::fs::create_dir_all(&sock_dir).unwrap();
set_permissions(&sock_dir, 0o700).unwrap();
sock_dir.push(envs::get_session_name().unwrap());
sock_dir
};

let (first_msg, ipc_pipe) = match info {
ClientInfo::New(name) | ClientInfo::Resurrect(name, _) => {
envs::set_session_name(name.clone());
os_input.update_session_name(name);
let ipc_pipe = create_ipc_pipe();

spawn_server(&*ipc_pipe, opts.debug).unwrap();

(
ClientToServerMsg::NewClient(
client_attributes,
Box::new(opts),
Box::new(config_options.clone()),
Box::new(layout.unwrap()),
Box::new(config.plugins.clone()),
),
ipc_pipe,
)
},
_ => {
eprintln!("Session already exists");
std::process::exit(1);
},
};

os_input.connect_to_server(&*ipc_pipe);
os_input.send_to_server(first_msg);
}

#[cfg(test)]
#[path = "./unit/stdin_tests.rs"]
mod stdin_tests;
4 changes: 2 additions & 2 deletions zellij-client/src/os_input_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ impl Clone for Box<dyn ClientOsApi> {
}

pub fn get_client_os_input() -> Result<ClientOsInputOutput, nix::Error> {
let current_termios = termios::tcgetattr(0)?;
let orig_termios = Some(Arc::new(Mutex::new(current_termios)));
let current_termios = termios::tcgetattr(0).ok();
let orig_termios = current_termios.map(|termios| Arc::new(Mutex::new(termios)));
let reading_from_stdin = Arc::new(Mutex::new(None));
Ok(ClientOsInputOutput {
orig_termios,
Expand Down
13 changes: 8 additions & 5 deletions zellij-server/src/os_input_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,15 @@ fn handle_openpty(
fn handle_terminal(
cmd: RunCommand,
failover_cmd: Option<RunCommand>,
orig_termios: termios::Termios,
orig_termios: Option<termios::Termios>,
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>,
terminal_id: u32,
) -> Result<(RawFd, RawFd)> {
let err_context = || "failed to spawn child terminal".to_string();

// Create a pipe to allow the child the communicate the shell's pid to its
// parent.
match openpty(None, Some(&orig_termios)) {
match openpty(None, &orig_termios) {
Ok(open_pty_res) => handle_openpty(open_pty_res, cmd, quit_cb, terminal_id),
Err(e) => match failover_cmd {
Some(failover_cmd) => {
Expand Down Expand Up @@ -279,7 +279,7 @@ fn separate_command_arguments(command: &mut PathBuf, args: &mut Vec<String>) {
/// set.
fn spawn_terminal(
terminal_action: TerminalAction,
orig_termios: termios::Termios,
orig_termios: Option<termios::Termios>,
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>, // u32 is the exit_status
default_editor: Option<PathBuf>,
terminal_id: u32,
Expand Down Expand Up @@ -418,7 +418,7 @@ impl ClientSender {

#[derive(Clone)]
pub struct ServerOsInputOutput {
orig_termios: Arc<Mutex<termios::Termios>>,
orig_termios: Arc<Mutex<Option<termios::Termios>>>,
client_senders: Arc<Mutex<HashMap<ClientId, ClientSender>>>,
terminal_id_to_raw_fd: Arc<Mutex<BTreeMap<u32, Option<RawFd>>>>, // A value of None means the
// terminal_id exists but is
Expand Down Expand Up @@ -876,7 +876,10 @@ impl Clone for Box<dyn ServerOsApi> {
}

pub fn get_server_os_input() -> Result<ServerOsInputOutput, nix::Error> {
let current_termios = termios::tcgetattr(0)?;
let current_termios = termios::tcgetattr(0).ok();
if current_termios.is_none() {
log::warn!("Starting a server without a controlling terminal, using the default termios configuration.");
}
let orig_termios = Arc::new(Mutex::new(current_termios));
Ok(ServerOsInputOutput {
orig_termios,
Expand Down
2 changes: 1 addition & 1 deletion zellij-server/src/unit/os_input_output_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn get_cwd() {
termios::tcgetattr(test_terminal.slave()).expect("Could not configure the termios");

let server = ServerOsInputOutput {
orig_termios: Arc::new(Mutex::new(test_termios)),
orig_termios: Arc::new(Mutex::new(Some(test_termios))),
client_senders: Arc::default(),
terminal_id_to_raw_fd: Arc::default(),
cached_resizes: Arc::default(),
Expand Down
4 changes: 4 additions & 0 deletions zellij-utils/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub enum Sessions {
#[clap(short, long, value_parser)]
create: bool,

/// Create a detached session in the background if one does not exist
#[clap(short, long, value_parser)]
background: bool,

/// Number of the session index in the active sessions ordered creation date.
#[clap(long, value_parser)]
index: Option<usize>,
Expand Down
Loading