Skip to content
Permalink
Browse files

Add the exec builtin

 - Add the necessary functions to builtin
 - Add the necessary interfaces for the execve syscall
   - Add sys::unix::execve
   - Add sys::redox::execve
  • Loading branch information...
dlrobertson committed Nov 29, 2017
1 parent e81543b commit a2c5e5d4e1ca68c3ce77cb2135291e3ad410ba7c
Showing with 209 additions and 5 deletions.
  1. +38 −0 src/builtins/exec.rs
  2. +16 −0 src/builtins/man_pages.rs
  3. +17 −1 src/builtins/mod.rs
  4. +5 −2 src/shell/mod.rs
  5. +52 −0 src/sys/redox.rs
  6. +81 −2 src/sys/unix/mod.rs
@@ -0,0 +1,38 @@
use builtins::man_pages::{check_help, MAN_EXEC};
use shell::Shell;
use std::error::Error;
use sys::execve;

/// Executes the givent commmand.
pub(crate) fn exec(shell: &mut Shell, args: &[&str]) -> Result<(), String> {
const CLEAR_ENV: u8 = 1;

let mut flags = 0u8;
let mut idx = 0;
for &arg in args.iter() {
match arg {
"-c" => flags |= CLEAR_ENV,
_ if check_help(args, MAN_EXEC) => {
return Ok(());
}
_ => break,
}
idx += 1;
}

match args.get(idx) {
Some(argument) => {
let args = if args.len() > idx + 1 {
&args[idx + 1..]
} else {
&[]
};
shell.prep_for_exit();
match execve(argument, args, (flags & CLEAR_ENV) == 1) {
Ok(_) => Ok(()),
Err(err) => Err(err.description().to_owned()),
}
}
None => Err("no command provided".to_owned()),
}
}
@@ -197,6 +197,22 @@ DESCRIPTION
all arguments are joined using a space as a separator.
"#;

pub(crate) const MAN_EXEC: &'static str = r#"NAME
exec - Replace the shell with the given command.
SYNOPSIS
exec [-ch] [--help] [command [arguments ...]]
DESCRIPTION
Execute <command>, replacing the shell with the specified program.
The <arguments> following the command become the arguments to
<command>.
OPTIONS
-c Execute command with an empty environment.
"#;


pub(crate) const MAN_HISTORY: &'static str = r#"NAME
history - print command history
@@ -12,11 +12,13 @@ mod echo;
mod set;
mod status;
mod exists;
mod exec;
mod ion;
mod is;

use self::conditionals::{contains, ends_with, starts_with};
use self::echo::echo;
use self::exec::exec;
use self::exists::exists;
use self::functions::fn_;
use self::ion::ion_docs;
@@ -80,7 +82,8 @@ pub const BUILTINS: &'static BuiltinMap = &map!(
"drop" => builtin_drop : "Delete a variable",
"echo" => builtin_echo : "Display a line of text",
"ends-with" => ends_with :"Evaluates if the supplied argument ends with a given string",
"eval" => builtin_eval : "evaluates the evaluated expression",
"eval" => builtin_eval : "Evaluates the evaluated expression",
"exec" => builtin_exec : "Replace the shell with the given command.",
"exists" => builtin_exists : "Performs tests on files and text",
"exit" => builtin_exit : "Exits the current session",
"false" => builtin_false : "Do nothing, unsuccessfully",
@@ -499,6 +502,19 @@ fn builtin_exit(args: &[&str], shell: &mut Shell) -> i32 {
)
}

fn builtin_exec(args: &[&str], shell: &mut Shell) -> i32 {
match exec(shell, &args[1..]) {
// Shouldn't ever hit this case.
Ok(()) => SUCCESS,
Err(err) => {
let stderr = io::stderr();
let mut stderr = stderr.lock();
let _ = writeln!(stderr, "ion: exec: {}", err);
FAILURE
}
}
}

use regex::Regex;
fn builtin_matches(args: &[&str], _: &mut Shell) -> i32 {
if check_help(args, MAN_MATCHES) {
@@ -164,7 +164,7 @@ impl<'a> Shell {
}
}

pub(crate) fn exit(&mut self, status: i32) -> ! {
pub(crate) fn prep_for_exit(&mut self) {
// The context has two purposes: if it exists, this is an interactive shell; and the
// context will also be sent a signal to commit all changes to the history file,
// and waiting for the history thread in the background to finish.
@@ -174,9 +174,12 @@ impl<'a> Shell {
self.background_send(sys::SIGHUP);
}
let context = self.context.as_mut().unwrap();
context.history.commit_history()
context.history.commit_history();
}
}

pub(crate) fn exit(&mut self, status: i32) -> ! {
self.prep_for_exit();
process::exit(status);
}

@@ -1,7 +1,10 @@
extern crate syscall;

use std::{io, mem, slice};
use std::env;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::RawFd;
use std::path::PathBuf;

use syscall::SigAction;

@@ -49,6 +52,55 @@ pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> {
cvt(syscall::setpgid(pid as usize, pgid as usize)).and(Ok(()))
}

pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Result<()> {
// Construct a valid set of arguments to pass to execve. Ensure
// that the program is the first argument.
let mut cvt_args: Vec<[usize; 2]> = Vec::new();
cvt_args.push([prog.as_ptr() as usize, prog.len()]);
for arg in args {
cvt_args.push([arg.as_ptr() as usize, arg.len()]);
}

// Get the PathBuf of the program if it exists.
let prog = if prog.contains(':') || prog.contains('/') {
// This is a fully specified scheme or path to an
// executable.
Some(PathBuf::from(prog))
} else if let Ok(paths) = env::var("PATH") {
// This is not a fully specified scheme or path.
// Iterate through the possible paths in the
// env var PATH that this executable may be found
// in and return the first one found.
env::split_paths(&paths)
.filter_map(|mut path| {
path.push(prog);
if path.exists() {
Some(path)
} else {
None
}
})
.next()
} else {
None
};

// If clear_env set, clear the env.
if clear_env {
for (key, _) in env::vars() {
env::remove_var(key);
}
}

if let Some(prog) = prog {
// If we found the program. Run it!
cvt(syscall::execve(prog.as_os_str().as_bytes(), &cvt_args)).and(Ok(()))
} else {
// The binary was not found.
Err(io::Error::from_raw_os_error(syscall::ENOENT))
}
}

#[allow(dead_code)]
pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> {
let new = SigAction {
@@ -3,8 +3,10 @@ extern crate libc;
pub mod job_control;
pub mod signals;

use libc::{c_int, pid_t, sighandler_t};
use std::io;
use libc::{c_char, c_int, pid_t, sighandler_t};
use std::{io, ptr};
use std::env;
use std::ffi::CString;
use std::os::unix::io::RawFd;

pub(crate) const PATH_SEPARATOR: &str = ":";
@@ -41,6 +43,83 @@ pub(crate) fn killpg(pgid: u32, signal: i32) -> io::Result<()> {
cvt(unsafe { libc::kill(-(pgid as pid_t), signal as c_int) }).and(Ok(()))
}

pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Result<()> {
// Prepare the program string
let prog_str = match CString::new(prog) {
Ok(prog_str) => prog_str,
Err(_) => { return Err(io::Error::last_os_error()); }
};

// Create the arguments vector
let mut cvt_args: Vec<CString> = Vec::new();
cvt_args.push(prog_str);
for arg in args.iter() {
match CString::new(*arg) {
Ok(arg) => cvt_args.push(arg),
Err(_) => {
return Err(io::Error::last_os_error());
}
}
}
let mut arg_ptrs: Vec<*const c_char> = cvt_args.iter().map(|x| x.as_ptr()).collect();
// NULL terminate the argv array
arg_ptrs.push(ptr::null());

// Get the PathBuf of the program if it exists.
let prog = if prog.contains('/') {
// This is a fully specified path to an executable.
match CString::new(prog) {
Ok(prog_str) => Some(prog_str),
Err(_) => None,
}
} else if let Ok(paths) = env::var("PATH") {
// This is not a fully specified scheme or path.
// Iterate through the possible paths in the
// env var PATH that this executable may be found
// in and return the first one found.
env::split_paths(&paths)
.filter_map(|mut path| {
path.push(prog);
match (path.exists(), path.to_str()) {
(false, _) => None,
(true, Some(path)) => match CString::new(path) {
Ok(prog_str) => Some(prog_str),
Err(_) => None,
},
(true, None) => None,
}
})
.next()
} else {
None
};

let mut env_ptrs: Vec<*const c_char> = Vec::new();
// If clear_env is not specified build envp
if !clear_env {
let mut env_vars: Vec<CString> = Vec::new();
for (key, value) in env::vars() {
match CString::new(format!("{}={}", key, value)) {
Ok(var) => env_vars.push(var),
Err(_) => {
return Err(io::Error::last_os_error());
}
}
}
env_ptrs = env_vars.iter().map(|x| x.as_ptr()).collect();
}
env_ptrs.push(ptr::null());

if let Some(prog) = prog {
// If we found the program. Run it!
cvt(unsafe { libc::execve(prog.as_ptr(), arg_ptrs.as_ptr(), env_ptrs.as_ptr()) })
.and(Ok(()))
} else {
// The binary was not found.
Err(io::Error::from_raw_os_error(libc::ENOENT))
}
}

pub(crate) fn pipe2(flags: usize) -> io::Result<(RawFd, RawFd)> {
let mut fds = [0; 2];

0 comments on commit a2c5e5d

Please sign in to comment.
You can’t perform that action at this time.