1- use std:: process:: Stdio ;
1+ use std:: {
2+ ffi:: OsStr ,
3+ process:: { Command , Stdio } ,
4+ } ;
25
3- use gix_url:: ArgumentSafety :: * ;
6+ use gix_url:: { ArgumentSafety :: * , Url } ;
47
5- use crate :: { client:: blocking_io, Protocol } ;
8+ use crate :: {
9+ client:: { self , blocking_io:: file:: SpawnProcessOnDemand } ,
10+ Protocol ,
11+ } ;
612
713/// The error used in [`connect()`].
814#[ derive( Debug , thiserror:: Error ) ]
@@ -100,44 +106,18 @@ pub mod connect {
100106/// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
101107#[ allow( clippy:: result_large_err) ]
102108pub fn connect (
103- url : gix_url :: Url ,
109+ url : Url ,
104110 desired_version : Protocol ,
105111 options : connect:: Options ,
106112 trace : bool ,
107- ) -> Result < blocking_io :: file :: SpawnProcessOnDemand , Error > {
113+ ) -> Result < SpawnProcessOnDemand , Error > {
108114 if url. scheme != gix_url:: Scheme :: Ssh || url. host ( ) . is_none ( ) {
109115 return Err ( Error :: UnsupportedScheme ( url) ) ;
110116 }
111117 let ssh_cmd = options. ssh_command ( ) ;
112- let mut kind = options. kind . unwrap_or_else ( || ProgramKind :: from ( ssh_cmd) ) ;
113- if options. kind . is_none ( ) && kind == ProgramKind :: Simple {
114- let mut cmd = std:: process:: Command :: from ( {
115- let mut prepare = gix_command:: prepare ( ssh_cmd)
116- . stderr ( Stdio :: null ( ) )
117- . stdout ( Stdio :: null ( ) )
118- . stdin ( Stdio :: null ( ) )
119- . command_may_be_shell_script ( )
120- . arg ( "-G" )
121- . arg ( match url. host_as_argument ( ) {
122- Usable ( host) => host,
123- Dangerous ( host) => Err ( Error :: AmbiguousHostName { host : host. into ( ) } ) ?,
124- Absent => panic ! ( "BUG: host should always be present in SSH URLs" ) ,
125- } ) ;
126- if options. disallow_shell {
127- prepare. use_shell = false ;
128- }
129- prepare
130- } ) ;
131- gix_features:: trace:: debug!( cmd = ?cmd, "invoking `ssh` for feature check" ) ;
132- kind = if cmd. status ( ) . ok ( ) . is_some_and ( |status| status. success ( ) ) {
133- ProgramKind :: Ssh
134- } else {
135- ProgramKind :: Simple
136- } ;
137- }
138-
118+ let kind = determine_client_kind ( options. kind , ssh_cmd, & url, options. disallow_shell ) ?;
139119 let path = gix_url:: expand_path:: for_shell ( url. path . clone ( ) ) ;
140- Ok ( blocking_io :: file :: SpawnProcessOnDemand :: new_ssh (
120+ Ok ( SpawnProcessOnDemand :: new_ssh (
141121 url,
142122 ssh_cmd,
143123 path,
@@ -148,5 +128,42 @@ pub fn connect(
148128 ) )
149129}
150130
131+ fn determine_client_kind (
132+ known_kind : Option < ProgramKind > ,
133+ ssh_cmd : & OsStr ,
134+ url : & Url ,
135+ disallow_shell : bool ,
136+ ) -> Result < ProgramKind , Error > {
137+ let mut kind = known_kind. unwrap_or_else ( || ProgramKind :: from ( ssh_cmd) ) ;
138+ if known_kind. is_none ( ) && kind == ProgramKind :: Simple {
139+ let mut cmd = build_client_feature_check_command ( ssh_cmd, url, disallow_shell) ?;
140+ gix_features:: trace:: debug!( cmd = ?cmd, "invoking `ssh` for feature check" ) ;
141+ kind = if cmd. status ( ) . ok ( ) . is_some_and ( |status| status. success ( ) ) {
142+ ProgramKind :: Ssh
143+ } else {
144+ ProgramKind :: Simple
145+ } ;
146+ }
147+ Ok ( kind)
148+ }
149+
150+ fn build_client_feature_check_command ( ssh_cmd : & OsStr , url : & Url , disallow_shell : bool ) -> Result < Command , Error > {
151+ let mut prepare = gix_command:: prepare ( ssh_cmd)
152+ . stderr ( Stdio :: null ( ) )
153+ . stdout ( Stdio :: null ( ) )
154+ . stdin ( Stdio :: null ( ) )
155+ . command_may_be_shell_script ( )
156+ . arg ( "-G" )
157+ . arg ( match url. host_as_argument ( ) {
158+ Usable ( host) => host,
159+ Dangerous ( host) => Err ( Error :: AmbiguousHostName { host : host. into ( ) } ) ?,
160+ Absent => panic ! ( "BUG: host should always be present in SSH URLs" ) ,
161+ } ) ;
162+ if disallow_shell {
163+ prepare. use_shell = false ;
164+ }
165+ Ok ( prepare. into ( ) )
166+ }
167+
151168#[ cfg( test) ]
152169mod tests;
0 commit comments