Skip to content

Commit

Permalink
posix/sh: store up to 10 FDs and use them for redirection
Browse files Browse the repository at this point in the history
  • Loading branch information
Arcterus committed Jun 30, 2018
1 parent 235585a commit 7d423b6
Show file tree
Hide file tree
Showing 11 changed files with 602 additions and 163 deletions.
10 changes: 5 additions & 5 deletions Cargo.toml
Expand Up @@ -17,7 +17,7 @@ gnu = [
"yes"
]

getty = ["nix"]
getty = []
loginutils = [
"getty"
]
Expand All @@ -28,16 +28,16 @@ lsb = [
"tar_util"
]

ping = ["nix", "chrono", "crossbeam", "pnet", "byteorder", "trust-dns-resolver", "mio", "socket2"]
ping = ["chrono", "crossbeam", "pnet", "byteorder", "trust-dns-resolver", "mio", "socket2"]
networking = [
"ping"
]

cat = []
chmod = ["regex", "walkdir", "uucore"]
head = []
init = ["fnv", "nix"]
sh = ["either", "fnv", "nix", "nom", "globset", "rustyline"]
init = ["fnv"]
sh = ["either", "nom", "globset", "rustyline"]
sleep = ["uucore"]
posix = [
"cat",
Expand All @@ -55,12 +55,12 @@ clap = "2.31.2"
failure = "0.1.1"
failure_derive = "0.1.1"
libc = "0.2.40"
nix = "0.10.0"

platform-info = { git = "https://github.com/uutils/platform-info", optional = true }
uucore = { git = "https://github.com/uutils/coreutils", features = ["encoding", "fs", "mode", "parse_time"], optional = true }
tar = { version = "0.4.15", optional = true }
globset = { version = "0.4.0", optional = true }
nix = { version = "0.10.0", optional = true }
chrono = { version = "0.4.2", optional = true }
crossbeam = { version = "0.3.2", optional = true }
pnet = { version = "0.21.0", optional = true }
Expand Down
4 changes: 1 addition & 3 deletions src/lib.rs
Expand Up @@ -12,6 +12,7 @@ extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate libc;
extern crate nix;

#[cfg(feature = "byteorder")]
extern crate byteorder;
Expand All @@ -25,8 +26,6 @@ extern crate fnv;
extern crate globset;
#[cfg(feature = "mio")]
extern crate mio;
#[cfg(feature = "nix")]
extern crate nix;
#[cfg(feature = "pnet")]
extern crate pnet;
#[cfg(feature = "regex")]
Expand All @@ -49,7 +48,6 @@ extern crate rustyline;

use clap::{App, SubCommand};
use libc::EXIT_FAILURE;
use std::cell::{RefCell, RefMut};
use std::ffi::{OsStr, OsString};
use std::io::{BufRead, Read, Write};
use std::iter;
Expand Down
120 changes: 91 additions & 29 deletions src/posix/sh/ast.rs
Expand Up @@ -4,7 +4,7 @@ use globset::{self, GlobBuilder, GlobSetBuilder, Glob};
use walkdir::WalkDir;

use std::borrow::Cow;
use std::cell::RefCell;
use std::cell::{RefCell, Ref};
use std::ffi::{OsString, OsStr};
use std::fs::{File, OpenOptions};
use std::io::Write;
Expand All @@ -16,8 +16,9 @@ use std::rc::Rc;
use std::result::Result as StdResult;

use super::{NAME, UtilSetup, Result};
use super::command::{CommandEnv, CommandIo, CommandWrapper, ExecData, ExecEnv, InProcessCommand};
use super::env::Environment;
use super::command::{CommandEnv, CommandWrapper, ExecData, ExecEnv, InProcessCommand};
use super::env::{EnvFd, Environment};
use util;

pub type ExitCode = libc::c_int;

Expand Down Expand Up @@ -679,11 +680,22 @@ impl FunctionBody {
}

impl InProcessCommand for FunctionBody {
fn execute<S>(&self, setup: &mut S, env: &mut Environment, data: ExecData) -> ExitCode
fn execute<S>(&self, setup: &mut S, env: &mut Environment, _data: ExecData) -> ExitCode
where
S: UtilSetup,
{
// TODO: set positional parameters using data.args
// TODO: redirects
// the below should redirect whatever is written to whatever stdout is at the time of
// the function call to /dev/null (if a command inside the function redirects its
// stdout it goes wherever the command tells the output to go though):
//
// test() {
// echo hi
// } >/dev/null
//
// UNLIKE FUNCTIONS, SUBSHELLS WILL NEED A CLONED COPY OF THE ENVIRONMENT (which gets
// thrown away when the subshell finishes)
self.command.execute(setup, env)
}
}
Expand Down Expand Up @@ -715,7 +727,7 @@ impl SimpleCommand {

return {
// NOTE: needed to make functions return Rcs rather than borrowed
// pointers for this to function (it is possible that an execution of a
// pointers for this to work (it is possible that an execution of a
// function could remove something that is being executed, which
// would then cause that function to be freed (if the borrow checker
// didn't catch it, that is))
Expand All @@ -730,12 +742,9 @@ impl SimpleCommand {
};
} else if let Some(ref actions) = self.pre_actions {
for act in actions.iter() {
match act {
Either::Right(ref assign) => {
assign.execute(setup, env);
}
// TODO: figure out what to do with redirects (just ignore them?)
_ => unimplemented!()
// XXX: i believe we are just supposed to ignore redirects here, but not certain
if let Either::Right(ref assign) = act {
assign.execute(setup, env);
}
}
}
Expand All @@ -748,6 +757,8 @@ impl SimpleCommand {
S: UtilSetup,
E: CommandEnv,
{
env.enter_scope();

cmd.envs(env.export_iter().map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v))));

if let Some(ref actions) = self.pre_actions {
Expand Down Expand Up @@ -779,7 +790,11 @@ impl SimpleCommand {
}
}

cmd.status(setup, env).unwrap()
let res = cmd.status(setup, env).unwrap();

env.exit_scope();

res
}
}

Expand All @@ -801,17 +816,19 @@ impl IoRedirect {
{
use self::IoRedirect::*;

// TODO: check for fds
match self {
File(_, ref file) => {
File(fd, ref file) => {
// FIXME: don't unwrap
file.setup(setup, env, cmd).unwrap();
file.setup(setup, env, cmd, *fd).unwrap();
}
Heredoc(_, ref doc) => {
// FIXME: don't unwrap
cmd.stdin(CommandIo::Piped(&doc.borrow().data)).unwrap();
Heredoc(fd, ref doc) => {
let fd = fd.unwrap_or(0);

let heredoc = doc.borrow();
let data = heredoc.data.clone();

env.set_local_fd(fd as _, EnvFd::Piped(data));
}
_ => {}
}
}
}
Expand All @@ -830,7 +847,7 @@ impl IoRedirectFile {
}
}

pub fn setup<S, E>(&self, setup: &mut S, env: &mut Environment, cmd: &mut E) -> Result<()>
pub fn setup<S, E>(&self, setup: &mut S, env: &mut Environment, cmd: &mut E, fd: Option<RawFd>) -> Result<()>
where
S: UtilSetup,
E: CommandEnv,
Expand All @@ -842,26 +859,71 @@ impl IoRedirectFile {
match self.kind {
Input => {
let file = File::open(name)?;
cmd.stdin(CommandIo::File(file))?;
env.set_local_fd(fd.unwrap_or(0) as _, EnvFd::File(file))
}
Output => {
// TODO: fail if noclobber option is set (by set -C), so if that option is present
// use OpenOptions with create_new() instead of File::create()
let file = File::create(name)?;
env.set_local_fd(fd.unwrap_or(1) as _, EnvFd::File(file))
}
Clobber => {
let file = File::create(name)?;
cmd.stdout(CommandIo::File(file))?;
env.set_local_fd(fd.unwrap_or(1) as _, EnvFd::File(file))
}
ReadWrite => {
let file = OpenOptions::new().create(true).read(true).write(true).open(name)?;
let second = file.try_clone()?;

cmd.stdin(CommandIo::File(file))?;
cmd.stdout(CommandIo::File(second))?;
env.set_local_fd(fd.unwrap_or(0) as _, EnvFd::File(file))
}
Append => {
let file = OpenOptions::new().create(true).append(true).open(name)?;
cmd.stdout(CommandIo::File(file))?;
env.set_local_fd(fd.unwrap_or(1) as _, EnvFd::File(file))
}
// TODO: clobber, dup input, dup output
_ => unimplemented!()
}
DupInput if name.len() == 1 => {
match name.to_string_lossy().chars().next().unwrap() {
'-' => {
// TODO: close file descriptor specified by fd (or 0 by default)
//env.get_fd()
unimplemented!()
}
ch @ '0'...'9' => {
// TODO: duplicate descriptor specified by name as that specified by fd (using dup2)
let digit = ch.to_digit(10).unwrap();
//cmd.fd_alias(fd.unwrap_or(0), digit as RawFd)
let fd = fd.unwrap_or(0) as _;
let value = env.get_fd(digit as _).current_val().try_clone()?;
env.set_local_fd(fd, value)
}
_ => {
util::string_to_err(Err("bad fd number".to_string()))?
}
}
}
DupOutput if name.len() == 1 => {
match name.to_string_lossy().chars().next().unwrap() {
'-' => {
// TODO: close file descriptor specified by fd (or 1 by default)
unimplemented!()
}
ch @ '0'...'9' => {
// TODO: duplicate descriptor specified by name as that specified by fd (using dup2)
let digit = ch.to_digit(10).unwrap();
//cmd.fd_alias(fd.unwrap_or(1), digit as RawFd)
let fd = fd.unwrap_or(1) as _;
let value = env.get_fd(digit as _).current_val().try_clone()?;
env.set_local_fd(fd, value)
}
_ => {
util::string_to_err(Err("bad fd number".to_string()))?
}
}
}
_ => {
// TODO: return error message stating invalid direction
unimplemented!()
}
};

Ok(())
}
Expand Down

0 comments on commit 7d423b6

Please sign in to comment.