diff --git a/src/hg-connect-stdio.c b/src/hg-connect-stdio.c index 20a8d34e..43191c3d 100644 --- a/src/hg-connect-stdio.c +++ b/src/hg-connect-stdio.c @@ -31,51 +31,45 @@ static void maybe_sq_quote_buf(struct strbuf *buf, const char *src) strbuf_addstr(buf, src); } -int stdio_finish(struct child_process *proc) -{ - int ret = finish_command(proc); - free(proc); - return ret; -} - -int proc_in(struct child_process *proc) { - return proc->in; -} +extern const char **prepare_shell_cmd(struct strvec *out, const char **argv); -int proc_out(struct child_process *proc) { - return proc->out; -} - -int proc_err(struct child_process *proc) { - return proc->err; -} - -struct child_process *hg_connect_stdio( +void hg_connect_prepare_command( + void *ctx, void (*add_arg)(void *ctx, const char *arg), const char *userhost, const char *port, const char *path, int flags) { + struct strvec out = STRVEC_INIT; struct strbuf buf = STRBUF_INIT; - struct child_process *proc = xmalloc(sizeof(*proc)); - - child_process_init(proc); + struct child_process proc = CHILD_PROCESS_INIT; + child_process_init(&proc); if (looks_like_command_line_option(path)) die("strange pathname '%s' blocked", path); - strvec_pushv(&proc->env, (const char **)local_repo_env); - proc->use_shell = 1; - proc->in = proc->out = proc->err = -1; + //strvec_pushv(&proc.env, (const char **)local_repo_env); + proc.use_shell = 1; + proc.in = proc.out = proc.err = -1; if (userhost) { - proc->trace2_child_class = "transport/ssh"; - fill_ssh_args(proc, userhost, port, protocol_v0, flags); + proc.trace2_child_class = "transport/ssh"; + fill_ssh_args(&proc, userhost, port, protocol_v0, flags); } strbuf_addstr(&buf, "hg -R "); maybe_sq_quote_buf(&buf, path); strbuf_addstr(&buf, " serve --stdio"); - strvec_push(&proc->args, buf.buf); + strvec_push(&proc.args, buf.buf); strbuf_release(&buf); - start_command(proc); - return proc; + if (proc.use_shell) { + prepare_shell_cmd(&out, proc.args.v); + } else { + strvec_pushv(&out, proc.args.v); + } + + for (size_t i = 0; i < out.nr; i++) { + add_arg(ctx, out.v[i]); + } + + child_process_clear(&proc); + strvec_clear(&out); } diff --git a/src/hg-connect.h b/src/hg-connect.h index 11d434c2..a13a8aed 100644 --- a/src/hg-connect.h +++ b/src/hg-connect.h @@ -2,15 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -struct child_process; - -int stdio_finish(struct child_process *proc); - -int proc_in(struct child_process *proc); - -int proc_out(struct child_process *proc); - -int proc_err(struct child_process *proc); - -struct child_process *hg_connect_stdio( +void hg_connect_prepare_command( + void *ctx, void (*add_arg)(void *ctx, const char *arg), const char *userhost, const char *port, const char *path, int flags); diff --git a/src/hg_connect_stdio.rs b/src/hg_connect_stdio.rs index 1490dac4..bf6680a9 100644 --- a/src/hg_connect_stdio.rs +++ b/src/hg_connect_stdio.rs @@ -3,10 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; -use std::ffi::CString; +use std::ffi::{c_char, c_void, CStr, CString, OsString}; use std::fs::File; use std::io::{copy, BufRead, BufReader, Read, Seek, SeekFrom, Write}; use std::os::raw::c_int; +use std::process::{self, ChildStdin, ChildStdout, Command, Stdio}; use std::str::FromStr; use std::thread::{self, JoinHandle}; use std::{mem, ptr}; @@ -22,16 +23,16 @@ use crate::hg_connect::{ UnbundleResponse, }; use crate::libc::FdFile; -use crate::libcinnabar::{hg_connect_stdio, stdio_finish}; -use crate::libgit::child_process; +use crate::libcinnabar::hg_connect_prepare_command; +use crate::libgit::local_repo_env; use crate::logging::{LoggingReader, LoggingWriter}; -use crate::util::{ImmutBString, OsStrExt, PrefixWriter, ReadExt}; +use crate::util::{CStrExt, ImmutBString, OsStrExt, PrefixWriter, ReadExt}; pub struct HgStdioConnection { capabilities: HgCapabilities, - proc_in: FdFile, - proc_out: BufReader, - proc: *mut child_process, + proc_in: Option, + proc_out: Option>, + proc: process::Child, thread: Option>, url: Url, } @@ -81,7 +82,7 @@ fn stdio_send_command(conn: &mut HgStdioConnection, command: &str, args: HgArgs) } else { format!("raw-wire::{command}").into() }; - LoggingWriter::new_hex(target, log::Level::Trace, &mut conn.proc_in) + LoggingWriter::new_hex(target, log::Level::Trace, conn.proc_in.as_mut().unwrap()) .write_all(&data) .unwrap(); } @@ -89,7 +90,8 @@ fn stdio_send_command(conn: &mut HgStdioConnection, command: &str, args: HgArgs) fn stdio_read_response(conn: &mut HgStdioConnection, command: &str) -> ImmutBString { let mut length_str = String::new(); let target = format!("raw-wire::{command}"); - let mut input = LoggingReader::new_hex(&target, log::Level::Trace, &mut conn.proc_out); + let mut input = + LoggingReader::new_hex(&target, log::Level::Trace, conn.proc_out.as_mut().unwrap()); input.read_line(&mut length_str).unwrap(); let length = usize::from_str(length_str.trim_end_matches('\n')).unwrap(); input.read_exactly(length).unwrap() @@ -116,10 +118,10 @@ impl HgWireConnection for HgStdioConnection { Ok(Box::new(LoggingReader::new_hex( format!("raw-wire::{command}"), log::Level::Trace, - &mut self.proc_out, + self.proc_out.as_mut().unwrap(), ))) } else { - Ok(Box::new(&mut self.proc_out)) + Ok(Box::new(self.proc_out.as_mut().unwrap())) } } @@ -131,7 +133,8 @@ impl HgWireConnection for HgStdioConnection { //TODO: handle that error. let header = stdio_read_response(self, command); let target = format!("raw-wire::{command}"); - let mut proc_in = LoggingWriter::new_hex(&target, log::Level::Trace, &mut self.proc_in); + let mut proc_in = + LoggingWriter::new_hex(&target, log::Level::Trace, self.proc_in.as_mut().unwrap()); proc_in.write_all(&header).unwrap(); drop(header); @@ -155,10 +158,10 @@ impl HgWireConnection for HgStdioConnection { Box::new(LoggingReader::new_hex( target, log::Level::Trace, - &mut self.proc_out, + self.proc_out.as_mut().unwrap(), )) as Box } else { - Box::new(&mut self.proc_out) + Box::new(self.proc_out.as_mut().unwrap()) }; UnbundleResponse::Bundlev2(bundle) } else { @@ -187,23 +190,13 @@ impl HgConnectionBase for HgStdioConnection { impl Drop for HgStdioConnection { fn drop(&mut self) { stdio_send_command(self, "", args!()); - unsafe { - libc::close(self.proc_in.raw()); - libc::close(self.proc_out.get_mut().raw()); - self.thread.take().map(JoinHandle::join); - stdio_finish(self.proc); - } + drop(self.proc_in.take()); + drop(self.proc_out.take()); + self.thread.take().map(JoinHandle::join); + self.proc.wait().unwrap(); } } -extern "C" { - fn proc_in(proc: *mut child_process) -> c_int; - - fn proc_out(proc: *mut child_process) -> c_int; - - fn proc_err(proc: *mut child_process) -> c_int; -} - pub fn get_stdio_connection(url: &Url, flags: c_int) -> Option> { let userhost = url.host_str().map(|host| { let username = percent_decode_str(url.username()).collect_vec(); @@ -231,29 +224,55 @@ pub fn get_stdio_connection(url: &Url, flags: c_int) -> Option> path.as_os_str().as_bytes().to_owned() }; let path = CString::new(path).unwrap(); - let proc = unsafe { - hg_connect_stdio( + let mut args = Vec::::new(); + unsafe extern "C" fn add_arg(ctx: *mut c_void, arg: *const c_char) { + (ctx as *mut Vec) + .as_mut() + .unwrap() + .push(CStr::from_ptr(arg).to_osstr().to_owned()); + } + unsafe { + hg_connect_prepare_command( + &mut args as *mut _ as *mut _, + add_arg, userhost.as_ref().map_or(ptr::null(), |s| s.as_ptr()), port.as_ref().map_or(ptr::null(), |s| s.as_ptr()), path.as_ref().as_ptr(), flags, - ) - }; - if proc.is_null() { - return None; + ); } + let mut command = Command::new(&args[0]); + unsafe { + let mut current = local_repo_env.as_ptr(); + + while !(*current).is_null() { + command.env_remove(CStr::from_ptr(*current).to_osstr()); + current = current.add(1); + } + } + let mut proc = if let Ok(proc) = command + .args(&args[1..]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + proc + } else { + return None; + }; + + let mut proc_err = proc.stderr.take().unwrap(); let mut conn = HgStdioConnection { capabilities: HgCapabilities::default(), - proc_in: unsafe { FdFile::from_raw_fd(proc_in(proc)) }, - proc_out: BufReader::new(unsafe { FdFile::from_raw_fd(proc_out(proc)) }), + proc_in: proc.stdin.take(), + proc_out: proc.stdout.take().map(BufReader::new), proc, thread: None, url: url.clone(), }; - let mut proc_err = unsafe { FdFile::from_raw_fd(proc_err(proc)) }; - conn.thread = Some( thread::Builder::new() .name("remote-stderr".into()) diff --git a/src/libc.rs b/src/libc.rs index 9c38e5cf..fc3c49d9 100644 --- a/src/libc.rs +++ b/src/libc.rs @@ -9,17 +9,9 @@ use std::os::raw::c_int; pub struct FdFile(c_int); impl FdFile { - pub unsafe fn from_raw_fd(fd: c_int) -> Self { - FdFile(fd) - } - pub unsafe fn stderr() -> Self { FdFile(2) } - - pub unsafe fn raw(&mut self) -> c_int { - self.0 - } } extern "C" { diff --git a/src/libcinnabar.rs b/src/libcinnabar.rs index 509c1a9b..ee6ed3cc 100644 --- a/src/libcinnabar.rs +++ b/src/libcinnabar.rs @@ -11,7 +11,7 @@ use std::os::raw::{c_char, c_int, c_uint, c_void}; use crate::git::{CommitId, GitObjectId, RawTree, TreeId}; use crate::hg::HgObjectId; use crate::libgit::{ - child_process, combine_notes_ignore, free_notes, init_notes, notes_tree, object_id, FileMode, + combine_notes_ignore, free_notes, init_notes, notes_tree, object_id, FileMode, }; use crate::oid::{Abbrev, ObjectId}; use crate::store::{store_git_commit, Store}; @@ -371,12 +371,12 @@ impl hg_notes_tree { } extern "C" { - pub fn hg_connect_stdio( + pub fn hg_connect_prepare_command( + ctx: *mut c_void, + add_arg: unsafe extern "C" fn(ctx: *mut c_void, arg: *const c_char), userhost: *const c_char, port: *const c_char, path: *const c_char, flags: c_int, - ) -> *mut child_process; - - pub fn stdio_finish(conn: *mut child_process) -> c_int; + ); } diff --git a/src/libgit.rs b/src/libgit.rs index 9b97bec4..c12d04a8 100644 --- a/src/libgit.rs +++ b/src/libgit.rs @@ -148,10 +148,6 @@ pub struct credential([u8; 0]); #[repr(C)] pub struct remote([u8; 0]); -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct child_process([u8; 0]); - #[allow(non_camel_case_types)] #[repr(C)] pub struct object_entry([u8; 0]); @@ -354,6 +350,8 @@ pub fn git_object_info( extern "C" { pub static mut the_repository: *mut repository; + pub static local_repo_env: [*const c_char; 1]; + static default_abbrev: c_int; fn repo_get_oid_committish(r: *mut repository, s: *const c_char, oid: *mut object_id) -> c_int; diff --git a/src/run-command.c.patch b/src/run-command.c.patch index 4461658d..2a35e56b 100644 --- a/src/run-command.c.patch +++ b/src/run-command.c.patch @@ -1,22 +1,14 @@ diff --git a/run-command.c b/run-command.c -index 5ec3a46dcc..9d10ed27b8 100644 +index 7e4e2e7fb66..fc4932219fe 100644 --- a/run-command.c +++ b/run-command.c -@@ -722,7 +722,7 @@ int start_command(struct child_process *cmd) - trace2_child_start(cmd); - trace_run_command(cmd); - -- fflush(NULL); -+ //fflush(NULL); - - if (cmd->close_object_store) - close_object_store(the_repository->objects); -@@ -1211,7 +1211,7 @@ int start_async(struct async *async) - - #ifdef NO_PTHREADS - /* Flush stdio before fork() to avoid cloning buffers */ -- fflush(NULL); -+ //fflush(NULL); - - async->pid = fork(); - if (async->pid < 0) { +@@ -285,7 +285,8 @@ char *git_shell_path(void) + #endif + } + +-static const char **prepare_shell_cmd(struct strvec *out, const char **argv) ++const char **prepare_shell_cmd(struct strvec *out, const char **argv); ++const char **prepare_shell_cmd(struct strvec *out, const char **argv) + { + if (!argv[0]) + BUG("shell command is empty");