Skip to content

Commit

Permalink
First pass on running child processes
Browse files Browse the repository at this point in the history
  • Loading branch information
piscisaureus committed Nov 5, 2018
1 parent bd88e56 commit 044c148
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ ts_sources = [
"js/blob.ts",
"js/buffer.ts",
"js/chmod.ts",
"js/command.ts",
"js/compiler.ts",
"js/console.ts",
"js/copy_file.ts",
Expand Down
75 changes: 75 additions & 0 deletions js/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import * as msg from "gen/msg_generated";
import * as flatbuffers from "./flatbuffers";
import * as dispatch from "./dispatch";
import { assert, unreachable } from "./util";

export interface CommandOptions {
argv: string[];
dir?: string;
shell?: boolean;
}

interface ExitStatusCode {
success: boolean;
code: number;
}

interface ExitStatusSignal {
success: false;
signal: number /* TODO: Make this a string. */;
}

export type ExitStatus = ExitStatusCode | ExitStatusSignal;

// This is a convenience function a la Python's check_output.
// TODO:
// * Stdio and a lot more options.
// * Throw if not succesful.
// * Maybe a shorter name. run()? spawn()?
// * really want it to have this interface, but I don't know how to express
// that in typescript:
// run("python", "tools/build.py")
// run("magick", "convert", file, "-", { stdout: UInt8Array })
// run("curl http://deno.land > lol.hihi" { shell: true })
export async function runCommand(options: CommandOptions): Promise<ExitStatus> {
return res(await dispatch.sendAsync(...req(options)));
}

function req({
argv,
dir = undefined,
shell = false
}: CommandOptions): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const builder = flatbuffers.createBuilder();
const argvOffset = msg.RunCommand.createArgvVector(
builder,
argv.map(a => builder.createString(a))
);
const dirOffset = dir === undefined ? -1 : builder.createString(dir);
msg.RunCommand.startRunCommand(builder);
msg.RunCommand.addArgv(builder, argvOffset);
if (dir !== undefined) {
msg.RunCommand.addDir(builder, dirOffset);
}
msg.RunCommand.addShell(builder, shell);
const inner = msg.RunCommand.endRunCommand(builder);
return [builder, msg.Any.RunCommand, inner];
}

function res(baseRes: null | msg.Base): ExitStatus {
assert(baseRes != null);
assert(msg.Any.RunCommandRes === baseRes!.innerType());
const res = new msg.RunCommandRes();
assert(baseRes!.inner(res) != null);
switch (res.status()) {
case msg.CommandExitStatus.ExitedWithCode:
const code = res.exitCode();
return { code, success: code === 0 };
case msg.CommandExitStatus.ExitedWithSignal:
const signal = res.exitSignal();
return { signal, success: false };
default:
return unreachable();
}
}
1 change: 1 addition & 0 deletions js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export { FileInfo } from "./file_info";
export { connect, dial, listen, Listener, Conn } from "./net";
export { metrics } from "./metrics";
export { resources } from "./resources";
export { runCommand } from "./command";
export const args: string[] = [];

// Provide the compiler API in an obfuscated way
Expand Down
20 changes: 20 additions & 0 deletions src/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ union Any {
CwdRes,
Metrics,
MetricsRes,
RunCommand,
RunCommandRes
}

enum ErrorKind: byte {
Expand Down Expand Up @@ -383,4 +385,22 @@ table MetricsRes {
bytes_received: uint64;
}

// TODO: a process should be a resource.
table RunCommand {
argv: [string];
dir: string;
shell: bool;
}

enum CommandExitStatus: byte {
ExitedWithCode,
ExitedWithSignal
}

table RunCommandRes {
status: CommandExitStatus;
exit_code: int;
exit_signal: int;
}

root_type Base;
62 changes: 62 additions & 0 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ use std::net::{Shutdown, SocketAddr};
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::sync::Arc;
use std::time::UNIX_EPOCH;
use std::time::{Duration, Instant};
use tokio;
use tokio::net::TcpListener;
use tokio::net::TcpStream;
use tokio_process::CommandExt;
use tokio_threadpool;

type OpResult = DenoResult<Buf>;
Expand Down Expand Up @@ -97,6 +99,7 @@ pub fn dispatch(
msg::Any::Remove => op_remove,
msg::Any::Rename => op_rename,
msg::Any::Resources => op_resources,
msg::Any::RunCommand => op_run_command,
msg::Any::SetEnv => op_set_env,
msg::Any::Shutdown => op_shutdown,
msg::Any::Start => op_start,
Expand Down Expand Up @@ -1300,3 +1303,62 @@ fn op_resources(
},
))
}

fn op_run_command(
state: &Arc<IsolateState>,
base: &msg::Base,
data: &'static mut [u8],
) -> Box<Op> {
let cmd_id = base.cmd_id();

// TODO: add a separate permission for child processes.
if let Err(e) = state.check_write("/") {
return odd_future(e);
}

assert_eq!(data.len(), 0);
let inner = base.inner_as_run_command().unwrap();
let argv = inner.argv().unwrap();
let dir = inner.dir();
let _shell = inner.shell(); // TODO: currently not used, implement me.

let mut cmd = Command::new(argv.get(0));
(1..argv.len()).for_each(|i| {
let arg = argv.get(i);
cmd.arg(arg);
});
dir.map(|d| cmd.current_dir(d));

// TODO: add stdio etc.

let future = match cmd.status_async() {
Ok(v) => v,
Err(err) => {
return odd_future(err.into());
}
};
let future = future.map_err(DenoError::from).and_then(move |status| {
// TODO: rather than returning a future, make the process a resource so it
// can be interacted with (killed, piped etc) before it exits.
let builder = &mut FlatBufferBuilder::new();
let inner = msg::RunCommandRes::create(
builder,
&msg::RunCommandResArgs {
// TODO: get signal.
status: msg::CommandExitStatus::ExitedWithCode,
exit_code: status.code().unwrap_or(0),
..Default::default()
},
);
ok_future(serialize_response(
cmd_id,
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::RunCommandRes,
..Default::default()
},
))
});
Box::new(future)
}

0 comments on commit 044c148

Please sign in to comment.