Skip to content

Commit

Permalink
Use std::process::Command to spawn mercurial/ssh process
Browse files Browse the repository at this point in the history
  • Loading branch information
glandium committed Jan 13, 2025
1 parent 445dbef commit e76a57c
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 115 deletions.
54 changes: 24 additions & 30 deletions src/hg-connect-stdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
13 changes: 2 additions & 11 deletions src/hg-connect.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
95 changes: 57 additions & 38 deletions src/hg_connect_stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<FdFile>,
proc: *mut child_process,
proc_in: Option<ChildStdin>,
proc_out: Option<BufReader<ChildStdout>>,
proc: process::Child,
thread: Option<JoinHandle<()>>,
url: Url,
}
Expand Down Expand Up @@ -81,15 +82,16 @@ 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();
}

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()
Expand All @@ -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()))
}
}

Expand All @@ -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);

Expand All @@ -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<dyn Read>
} else {
Box::new(&mut self.proc_out)
Box::new(self.proc_out.as_mut().unwrap())
};
UnbundleResponse::Bundlev2(bundle)
} else {
Expand Down Expand Up @@ -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<Box<dyn HgRepo>> {
let userhost = url.host_str().map(|host| {
let username = percent_decode_str(url.username()).collect_vec();
Expand Down Expand Up @@ -231,29 +224,55 @@ pub fn get_stdio_connection(url: &Url, flags: c_int) -> Option<Box<dyn HgRepo>>
path.as_os_str().as_bytes().to_owned()
};
let path = CString::new(path).unwrap();
let proc = unsafe {
hg_connect_stdio(
let mut args = Vec::<OsString>::new();
unsafe extern "C" fn add_arg(ctx: *mut c_void, arg: *const c_char) {
(ctx as *mut Vec<OsString>)
.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())
Expand Down
8 changes: 0 additions & 8 deletions src/libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
10 changes: 5 additions & 5 deletions src/libcinnabar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
);
}
6 changes: 2 additions & 4 deletions src/libgit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 11 additions & 19 deletions src/run-command.c.patch
Original file line number Diff line number Diff line change
@@ -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");

0 comments on commit e76a57c

Please sign in to comment.