Writing to a long running child stdin multiple times

I spawned the child with a Tokio Command. I also piped stdout, stdin and stderr, after that I put the child into a global Mutex and use it in an actix_web route (The method below is getting called from the route). There I get the child out of the global mutex and try to write to the stdin. This works the first time, but because I'm using take(), the stdin Option is empty after that and it doesn't work anymore. How can I approach this? I tried to use as_mut() on the stdin, this compiled, but didn't write to the stdin.

Spawning the child:

        let child = Command::new(&info.command)
            .current_dir(&info.base_folder)
            .args(&info.args)
            .stdout(Stdio::piped())
            .stdin(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()?;

        if let Some(p) = child.id() {
            PROCESSES.lock().await.insert(p, child);
        }

Using the stdin:

    async fn send(&mut self, message: &str) -> Result<()> {
        let mut server_processes = PROCESSES.lock().await;
        let child = server_processes.get_mut(&self.pid.unwrap());

        if let Some(process) = child {
            let process_stdin = process.stdin.take();

            if let Some(mut stdin) = process_stdin {
                stdin.write(message.as_bytes()).await?;
            }
        }

        Ok(())
    }

This should work:

if let Some(process) = child {
    let process_stdin = process.stdin.as_mut();

    if let Some(stdin) = process_stdin {
        stdin.write(message.as_bytes()).await?;
    }
}

By the way, you probably want stdin.write_all and not stdin.writestdin.write is not guaranteed to write all the bytes you give it.

1 Like

As I wrote before, I already tried this. It works, as in it compiles and doesn't throw an error, but it doesn't write to the process. the process_stdin is not None and I don't know why it's now going to the process.

That's what I got now and doesn't actually writes to the process:

    async fn send(&mut self, message: &str) -> Result<()> {
        let mut server_processes = PROCESSES.lock().await;
        let child = server_processes.get_mut(&self.pid.unwrap());

        if let Some(process) = child {
            let process_stdin = process.stdin.as_mut();

            if let Some(stdin) = process_stdin {
                stdin.write_all(message.as_bytes()).await?;
            }
        }

        Ok(())
    }

It has to actually write to the process, unless there’s a severe bug in Tokio. Try creating an MRE of it not working, or re-assesing your other assumptions.

Maybe the process is not trying to read? This can happen e.g. if its trying to write something to you, but is blocked because you aren't reading it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.