@@ -11,8 +11,14 @@ use anyhow::{anyhow, Context, Result};
1111use rustix:: fs:: { flock, FlockOperation } ;
1212use uuid:: Uuid ;
1313
14+ use super :: utils:: env:: find_in_path;
1415use crate :: env:: prepare_env_vars;
1516use crate :: utils:: launch:: Launch ;
17+ use rustix:: path:: Arg ;
18+ use std:: ops:: Range ;
19+ use std:: process:: { Child , Command } ;
20+
21+ pub const DYNAMIC_PORT_RANGE : Range < u32 > = 50000 ..50200 ;
1622
1723pub enum LaunchResult {
1824 LaunchRequested ,
@@ -50,18 +56,79 @@ impl Display for LaunchError {
5056 }
5157}
5258
59+ fn start_socat ( ) -> Result < ( Child , u32 ) > {
60+ let run_path = env:: var ( "XDG_RUNTIME_DIR" )
61+ . map_err ( |e| anyhow ! ( "unable to get XDG_RUNTIME_DIR: {:?}" , e) ) ?;
62+ let socket_dir = Path :: new ( & run_path) . join ( "krun/socket" ) ;
63+ let socat_path =
64+ find_in_path ( "socat" ) ?. ok_or_else ( || anyhow ! ( "Unable to find socat in PATH" ) ) ?;
65+ for port in DYNAMIC_PORT_RANGE {
66+ let path = socket_dir. join ( format ! ( "port-{}" , port) ) ;
67+ if path. exists ( ) {
68+ continue ;
69+ }
70+ let child = Command :: new ( & socat_path)
71+ . arg ( format ! ( "unix-l:{}" , path. as_os_str( ) . to_string_lossy( ) ) )
72+ . arg ( "-,raw,echo=0" )
73+ . spawn ( ) ?;
74+ return Ok ( ( child, port) ) ;
75+ }
76+ Err ( anyhow ! ( "Ran out of ports." ) )
77+ }
78+
79+ fn escape_for_socat ( s : String ) -> String {
80+ let mut ret = String :: with_capacity ( s. len ( ) ) ;
81+ for c in s. chars ( ) {
82+ match c {
83+ ':' | ',' | '!' | '"' | '\'' | '\\' | '(' | '[' | '{' => {
84+ ret. push ( '\\' ) ;
85+ } ,
86+ _ => { } ,
87+ }
88+ ret. push ( c) ;
89+ }
90+ ret
91+ }
92+
93+ fn wrapped_launch (
94+ server_port : u32 ,
95+ cookie : Uuid ,
96+ mut command : PathBuf ,
97+ mut command_args : Vec < String > ,
98+ env : HashMap < String , String > ,
99+ interactive : bool ,
100+ ) -> Result < ( ) > {
101+ if !interactive {
102+ return request_launch ( server_port, cookie, command, command_args, env) ;
103+ }
104+ let ( mut socat, vsock_port) = start_socat ( ) ?;
105+ command_args. insert ( 0 , command. to_string_lossy ( ) . into_owned ( ) ) ;
106+ command_args = vec ! [
107+ format!( "vsock:2:{}" , vsock_port) ,
108+ format!(
109+ "exec:{},pty,setsid,stderr" ,
110+ escape_for_socat( command_args. join( " " ) )
111+ ) ,
112+ ] ;
113+ command = "socat" . into ( ) ;
114+ request_launch ( server_port, cookie, command, command_args, env) ?;
115+ socat. wait ( ) ?;
116+ Ok ( ( ) )
117+ }
118+
53119pub fn launch_or_lock (
54120 server_port : u32 ,
55121 command : PathBuf ,
56122 command_args : Vec < String > ,
57123 env : Vec < ( String , Option < String > ) > ,
124+ interactive : bool ,
58125) -> Result < LaunchResult > {
59126 let running_server_port = env:: var ( "MUVM_SERVER_PORT" ) . ok ( ) ;
60127 if let Some ( port) = running_server_port {
61128 let port: u32 = port. parse ( ) ?;
62129 let env = prepare_env_vars ( env) ?;
63130 let cookie = read_cookie ( ) ?;
64- if let Err ( err) = request_launch ( port, cookie, command, command_args, env) {
131+ if let Err ( err) = wrapped_launch ( port, cookie, command, command_args, env, interactive ) {
65132 return Err ( anyhow ! ( "could not request launch to server: {err}" ) ) ;
66133 }
67134 return Ok ( LaunchResult :: LaunchRequested ) ;
@@ -80,12 +147,13 @@ pub fn launch_or_lock(
80147 let env = prepare_env_vars ( env) ?;
81148 let mut tries = 0 ;
82149 loop {
83- match request_launch (
150+ match wrapped_launch (
84151 server_port,
85152 cookie,
86153 command. clone ( ) ,
87154 command_args. clone ( ) ,
88155 env. clone ( ) ,
156+ interactive,
89157 ) {
90158 Err ( err) => match err. downcast_ref :: < LaunchError > ( ) {
91159 Some ( & LaunchError :: Connection ( _) ) => {
0 commit comments