rmux-server 0.1.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_core::LifecycleEvent;
use rmux_proto::request::DetachClientExtRequest;
use rmux_proto::{DetachClientResponse, ErrorResponse, Response, RmuxError};

use crate::pane_io::AttachControl;

use super::super::{control_support::ManagedClient, RequestHandler};

impl RequestHandler {
    async fn detach_attach_client_with_mode(
        &self,
        attach_pid: u32,
        kill_on_detach: bool,
        exec_command: Option<String>,
        command_name: &str,
    ) -> Result<rmux_proto::SessionName, RmuxError> {
        let control = if let Some(command) = exec_command {
            let session_name = self
                .attached_session_name_for_command(attach_pid, command_name)
                .await?;
            let command = self
                .attach_shell_command_for_session(&session_name, command)
                .await?;
            AttachControl::DetachExecShellCommand(command)
        } else if kill_on_detach {
            AttachControl::DetachKill
        } else {
            AttachControl::Detach
        };
        self.send_attach_control(attach_pid, control, command_name, None)
            .await
    }

    pub(in crate::handler) async fn detach_other_attach_clients_for_session(
        &self,
        session_name: &rmux_proto::SessionName,
        requester_pid: u32,
        kill_clients: bool,
    ) {
        let attach_pids = {
            let active_attach = self.active_attach.lock().await;
            active_attach.attached_client_pids_for_session(session_name, Some(requester_pid))
        };

        for attach_pid in attach_pids {
            if let Ok(detached_session) = self
                .detach_attach_client_with_mode(attach_pid, kill_clients, None, "attach-session")
                .await
            {
                self.emit(LifecycleEvent::ClientDetached {
                    session_name: detached_session,
                    client_name: Some(attach_pid.to_string()),
                })
                .await;
            }
        }
    }

    pub(in crate::handler) async fn handle_detach_client(&self, requester_pid: u32) -> Response {
        self.handle_detach_client_ext(
            requester_pid,
            DetachClientExtRequest {
                target_client: None,
                all_other_clients: false,
                target_session: None,
                kill_on_detach: false,
                exec_command: None,
            },
        )
        .await
    }

    pub(in crate::handler) async fn handle_detach_client_ext(
        &self,
        requester_pid: u32,
        request: DetachClientExtRequest,
    ) -> Response {
        if request.target_session.is_some() && request.target_client.is_some() {
            return Response::Error(ErrorResponse {
                error: RmuxError::Server("detach-client accepts -t or -s, not both".to_owned()),
            });
        }

        if let Some(session_name) = request.target_session.as_ref() {
            let attach_pids = {
                let active_attach = self.active_attach.lock().await;
                active_attach.attached_client_pids_for_session(session_name, None)
            };
            for attach_pid in attach_pids {
                if self
                    .detach_attach_client_with_mode(
                        attach_pid,
                        request.kill_on_detach,
                        request.exec_command.clone(),
                        "detach-client",
                    )
                    .await
                    .is_ok()
                {
                    self.emit(LifecycleEvent::ClientDetached {
                        session_name: session_name.clone(),
                        client_name: Some(attach_pid.to_string()),
                    })
                    .await;
                }
            }
            return Response::DetachClient(DetachClientResponse);
        }

        if request.all_other_clients {
            let keep_pid = match self
                .resolve_target_attach_client_pid(
                    requester_pid,
                    request.target_client.as_deref(),
                    "detach-client",
                )
                .await
            {
                Ok(pid) => pid,
                Err(error) => return Response::Error(ErrorResponse { error }),
            };
            let attach_pids = {
                let active_attach = self.active_attach.lock().await;
                active_attach.attached_client_pids_except(keep_pid)
            };
            for attach_pid in attach_pids {
                if let Ok(session_name) = self
                    .detach_attach_client_with_mode(
                        attach_pid,
                        request.kill_on_detach,
                        request.exec_command.clone(),
                        "detach-client",
                    )
                    .await
                {
                    self.emit(LifecycleEvent::ClientDetached {
                        session_name,
                        client_name: Some(attach_pid.to_string()),
                    })
                    .await;
                }
            }
            return Response::DetachClient(DetachClientResponse);
        }

        let client = match self
            .resolve_target_managed_client(
                requester_pid,
                request.target_client.as_deref(),
                "detach-client",
            )
            .await
        {
            Ok(client) => client,
            Err(error) => return Response::Error(ErrorResponse { error }),
        };

        match client {
            ManagedClient::Attach(attach_pid) => match self
                .detach_attach_client_with_mode(
                    attach_pid,
                    request.kill_on_detach,
                    request.exec_command,
                    "detach-client",
                )
                .await
            {
                Ok(session_name) => {
                    self.emit(LifecycleEvent::ClientDetached {
                        session_name,
                        client_name: Some(attach_pid.to_string()),
                    })
                    .await;
                    Response::DetachClient(DetachClientResponse)
                }
                Err(error) => Response::Error(ErrorResponse { error }),
            },
            ManagedClient::Control(control_pid) => {
                match self.exit_control_client(control_pid, None).await {
                    Ok(Some(session_name)) => {
                        self.emit(LifecycleEvent::ClientDetached {
                            session_name,
                            client_name: Some(control_pid.to_string()),
                        })
                        .await;
                        Response::DetachClient(DetachClientResponse)
                    }
                    Ok(None) => Response::DetachClient(DetachClientResponse),
                    Err(error) => Response::Error(ErrorResponse { error }),
                }
            }
        }
    }
}