From c6f90de62a54868cbb2ec34b060cb5af6e499716 Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Mon, 10 Jul 2017 22:52:53 -0400
Subject: [PATCH] Background Jobs Can Now Display Command Arguments

When background jobs are created, the command(s) that they are executing
will be stored within the background job, which will be displayed in
the output of the `jobs` builtin.
---
 src/builtins/job_control.rs |  2 +-
 src/shell/fork.rs           |  8 +++++--
 src/shell/job_control.rs    | 38 ++++++++++++++++++++++----------
 src/shell/pipe.rs           | 44 +++++++++++++++++++++++++++++--------
 4 files changed, 68 insertions(+), 24 deletions(-)

diff --git a/src/builtins/job_control.rs b/src/builtins/job_control.rs
index cc7c3109..b5190a22 100644
--- a/src/builtins/job_control.rs
+++ b/src/builtins/job_control.rs
@@ -77,7 +77,7 @@ pub fn jobs(shell: &mut Shell) {
     for (id, process) in shell.background.lock().unwrap().iter().enumerate() {
         match process.state {
             ProcessState::Empty => (),
-            _ => { let _ = writeln!(stderr, "[{}] {} {}", id, process.pid, process.state); }
+            _ => { let _ = writeln!(stderr, "[{}] {} {}\t{}", id, process.pid, process.state, process.name); }
         }
     }
 }
diff --git a/src/shell/fork.rs b/src/shell/fork.rs
index 8da1162f..5b815459 100644
--- a/src/shell/fork.rs
+++ b/src/shell/fork.rs
@@ -60,11 +60,15 @@ use super::pipe::pipe;
 
 /// Forks the shell, adding the child to the parent's background list, and executing
 /// the given commands in the child fork.
-pub fn fork_pipe(shell: &mut Shell, commands: Vec<(Command, JobKind)>) -> i32 {
+pub fn fork_pipe (
+    shell: &mut Shell,
+    commands: Vec<(Command, JobKind)>,
+    command_name: String
+) -> i32 {
     match ion_fork() {
         Ok(Fork::Parent(pid)) => {
             // The parent process should add the child fork's PID to the background.
-            shell.send_to_background(pid, ProcessState::Running);
+            shell.send_to_background(pid, ProcessState::Running, command_name);
             SUCCESS
         },
         Ok(Fork::Child) => {
diff --git a/src/shell/job_control.rs b/src/shell/job_control.rs
index 806d69e9..e8ea8897 100644
--- a/src/shell/job_control.rs
+++ b/src/shell/job_control.rs
@@ -35,8 +35,8 @@ pub trait JobControl {
     fn handle_signal(&self, signal: i32);
     fn foreground_send(&self, signal: i32);
     fn background_send(&self, signal: i32);
-    fn watch_foreground(&mut self, pid: u32) -> i32;
-    fn send_to_background(&mut self, child: u32, state: ProcessState);
+    fn watch_foreground<F: Fn() -> String>(&mut self, pid: u32, get_command: F) -> i32;
+    fn send_to_background(&mut self, child: u32, state: ProcessState, command: String);
 }
 
 #[derive(Clone, Copy, Debug, PartialEq)]
@@ -128,17 +128,28 @@ pub fn watch_background (
 pub fn add_to_background (
     processes: Arc<Mutex<Vec<BackgroundProcess>>>,
     pid: u32,
-    state: ProcessState
+    state: ProcessState,
+    command: String
 ) -> usize {
     let mut processes = processes.lock().unwrap();
     match (*processes).iter().position(|x| x.state == ProcessState::Empty) {
         Some(id) => {
-            (*processes)[id] = BackgroundProcess { pid: pid, ignore_sighup: false, state: state };
+            (*processes)[id] = BackgroundProcess {
+                pid: pid,
+                ignore_sighup: false,
+                state: state,
+                name: command
+            };
             id
         },
         None => {
             let njobs = (*processes).len();
-            (*processes).push(BackgroundProcess { pid: pid, ignore_sighup: false, state: state });
+            (*processes).push(BackgroundProcess {
+                pid: pid,
+                ignore_sighup: false,
+                state: state,
+                name: command
+            });
             njobs
         }
     }
@@ -152,9 +163,8 @@ pub fn add_to_background (
 pub struct BackgroundProcess {
     pub pid: u32,
     pub ignore_sighup: bool,
-    pub state: ProcessState
-    // TODO: Each process should have the command registered to it
-    // pub command: String
+    pub state: ProcessState,
+    pub name: String
 }
 
 impl<'a> JobControl for Shell<'a> {
@@ -207,7 +217,11 @@ impl<'a> JobControl for Shell<'a> {
     }
 
     #[cfg(all(unix, not(target_os = "redox")))]
-    fn watch_foreground(&mut self, pid: u32) -> i32 {
+    fn watch_foreground <F: Fn() -> String> (
+        &mut self,
+        pid: u32,
+        get_command: F
+    ) -> i32 {
         use nix::sys::wait::{waitpid, WaitStatus, WUNTRACED};
         use nix::sys::signal::Signal;
         loop {
@@ -225,7 +239,7 @@ impl<'a> JobControl for Shell<'a> {
                     break TERMINATED;
                 },
                 Ok(WaitStatus::Stopped(pid, _)) => {
-                    self.send_to_background(pid as u32, ProcessState::Stopped);
+                    self.send_to_background(pid as u32, ProcessState::Stopped, get_command());
                     self.received_sigtstp = true;
                     break TERMINATED
                 },
@@ -307,11 +321,11 @@ impl<'a> JobControl for Shell<'a> {
         // TODO: Redox doesn't support signals yet
     }
 
-    fn send_to_background(&mut self, pid: u32, state: ProcessState) {
+    fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) {
         let processes = self.background.clone();
         let fg_signals = self.foreground_signals.clone();
         let _ = spawn(move || {
-            let njob = add_to_background(processes.clone(), pid, state);
+            let njob = add_to_background(processes.clone(), pid, state, command);
             eprintln!("ion: bg [{}] {}", njob, pid);
             watch_background(fg_signals, processes, pid, njob);
         });
diff --git a/src/shell/pipe.rs b/src/shell/pipe.rs
index 2f31a50a..60483125 100644
--- a/src/shell/pipe.rs
+++ b/src/shell/pipe.rs
@@ -128,12 +128,25 @@ pub mod crossplat {
     }
 }
 
+// This function serves two purposes:
+// 1. If the result is `Some`, then we will fork the pipeline executing into the background.
+// 2. The value stored within `Some` will be that background job's command name.
+fn check_if_background_job(pipeline: &Pipeline) -> Option<String> {
+    if pipeline.jobs[pipeline.jobs.len()-1].kind == JobKind::Background {
+        Some(pipeline.to_string())
+    } else {
+        None
+    }
+}
+
 pub trait PipelineExecution {
     fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32;
 }
 
 impl<'a> PipelineExecution for Shell<'a> {
     fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32 {
+        let background_string = check_if_background_job(&pipeline);
+
         // Generate a list of commands from the given pipeline
         let mut piped_commands: Vec<(Command, JobKind)> = pipeline.jobs
             .drain(..).map(|mut job| {
@@ -187,8 +200,8 @@ impl<'a> PipelineExecution for Shell<'a> {
 
         self.foreground.clear();
         // If the given pipeline is a background task, fork the shell.
-        if piped_commands[piped_commands.len()-1].1 == JobKind::Background {
-            fork_pipe(self, piped_commands)
+        if let Some(command_name) = background_string {
+            fork_pipe(self, piped_commands, command_name)
         } else {
             // While active, the SIGTTOU signal will be ignored.
             let _sig_ignore = SignalHandler::new();
@@ -320,7 +333,7 @@ fn execute_command(shell: &mut Shell, command: &mut Command, foreground: bool) -
     }).spawn() {
         Ok(child) => {
             if foreground { set_foreground(child.id()); }
-            shell.watch_foreground(child.id())
+            shell.watch_foreground(child.id(), || get_full_command(command))
         },
         Err(_) => {
             let stderr = io::stderr();
@@ -335,16 +348,16 @@ fn execute_command(shell: &mut Shell, command: &mut Command, foreground: bool) -
 fn wait (
     shell: &mut Shell,
     children: &mut Vec<Option<Child>>,
-    commands: Vec<Command>,
+    mut commands: Vec<Command>,
     foreground: bool
 ) -> i32 {
     let end = children.len() - 1;
-    for entry in children.drain(..end).zip(commands.into_iter()) {
-        // _cmd is never used here, but it is important that it gets dropped at the end of this
+    for entry in children.drain(..end).zip(commands.drain(..)) {
+        // It is important that `cmd` gets dropped at the end of this
         // block in order to write EOF to the pipes that it owns.
-        if let (Some(child), _cmd) = entry {
+        if let (Some(child), cmd) = entry {
             if foreground { set_foreground(child.id()); }
-            let status = shell.watch_foreground(child.id());
+            let status = shell.watch_foreground(child.id(), || get_full_command(&cmd));
             if status == TERMINATED {
                 return status
             }
@@ -352,8 +365,9 @@ fn wait (
     }
 
     if let Some(child) = children.pop().unwrap() {
+        let cmd = commands.pop().unwrap();
         if foreground { set_foreground(child.id()); }
-        shell.watch_foreground(child.id())
+        shell.watch_foreground(child.id(), || get_full_command(&cmd))
     } else {
         NO_SUCH_COMMAND
     }
@@ -362,3 +376,15 @@ fn wait (
 fn get_command_name(command: &Command) -> String {
     format!("{:?}", command).split('"').nth(1).unwrap_or("").to_string()
 }
+
+fn get_full_command(command: &Command) -> String {
+    let command = format!("{:?}", command);
+    let mut arg_iter = command.split_whitespace();
+    let command = arg_iter.next().unwrap();
+    let mut output = String::from(&command[1..command.len()-1]);
+    for argument in arg_iter {
+        output.push(' ');
+        output.push_str(&argument[1..argument.len()-1]);
+    }
+    output
+}
-- 
GitLab