closing std::os::Pipe in a simple user written shell in Rust - shell

I am trying to write a shell using Rust. Currently, I am implementing the pipe | function.
So I split the user input by | into a vector of programs. For the programs, I spawn a process if is not the last program in the vector. If it is the last one, I create the process and wait for it to finish.
Setting up the pipes:
let mut channels: ~[std::os::Pipe] = ~[];
for _ in range(0, progs.len()) {
channels.push(std::os::pipe());
}
Set the input and output FILENO
for i in range(0, progs.len()) {
let mut in_chan = libc::STDIN_FILENO;
let mut out_chan = libc::STDOUT_FILENO;
if i == 0 {
out_chan = channels[i].out;
}
if i > 0 {
in_chan = channels[i-1].input;
out_chan = channels[i].out;
}
if i == progs.len() - 1 {
out_chan = libc::STDOUT_FILENO;
}
}
Spawn the processes:
for i in range(0, progs.len()) {
if i == progs.len() - 1 {
let proc_run = run::Process::new(program, argv, run::ProcessOptions {
env: None,
dir: None,
in_fd: Some(in_chan),
out_fd: Some(out_chan),
err_fd: Some(libc::STDERR_FILENO)
});
proc_run.unwrap().finish();
}
else {
do spawn {
let proc_run = run::Process::new(program, argv, run::ProcessOptions {
env: None,
dir: None,
in_fd: Some(in_chan),
out_fd: Some(out_chan),
err_fd: Some(libc::STDERR_FILENO)
});
proc_run.unwrap().finish();
}
}
}
I tried to run this with a simple C++ program:
#include <iostream>
using namespace std;
int main() {
int readNo;
while(cin >> readNo) {
cout << readNo+1 << endl;
}
return 0;
}
I ran with ./a.out | ./a.out
It seems to me that the pipes are working (1 => 3, 2 => 4 etc) but when I close the stdin with ctrl + d. Only the first process (the process spawned first) finishes. The rest are still running. What is wrong here and how shall I tell the rest of the processes to terminate?
EDIT:
I can do this with blocking process for all programs in the vector:
for i in range(0, progs.len()) {
let proc_run = run::Process::new(program, argv, run::ProcessOptions {
env: None,
dir: None,
in_fd: Some(in_chan),
out_fd: Some(out_chan),
err_fd: Some(libc::STDERR_FILENO)
});
proc_run.unwrap().finish();
}
But this is not what a "shell" does, it should be non-blocking between the processes. When I enter 1 into stdin, it should spit out 3 immediately.
Any help on any parts is appreciated!

Have you tried closing the stdout descriptor when each process ends?
e.g.
extern crate libc;
...
proc_run.unwrap().finish();
libc::unistd::close(out_chan)
The pipe is owned by the Rust Shell program, not the exited process, so when it exits, the pipe remains open. Closing it yourself, should close stdin on the following process.

Related

Not able to receive 'Continued' status from waitpid using the nix crate

I'm experimenting with the nix crate in order to debug child processes. I would like to receive events when the child process stops, continues and exits. Stoppages and exits are reported using the waitpid function. I'm having trouble receiving the continued status though.
I tried to convert my code to a minimum reproduceable example and hoping someone is able to spot the problem.
fn main() {
// fork here
let res = unsafe { fork().unwrap() };
if res.is_child() {
traceme().unwrap();
unsafe {
breakpoint();
}
} else {
let status = waitpid(Pid::from_raw(-1), Some(WaitPidFlag::WCONTINUED)).unwrap();
let pid = status.pid().unwrap();
println!("{:?}", status);
println!("Calling continue....");
cont(pid, None).unwrap();
println!("{:?}", waitpid(Pid::from_raw(-1), None).unwrap());
}
}
is producing the following output:
Stopped(Pid(28411), SIGTRAP)
Calling continue....
Exited(Pid(28411), 0)

Background process in my own shell program to ignore stdin

I'm working on my own little shell program in C. When I run the child process as a background process, I would like to ignore the input from the user coming from the parent process. I am currently trying to pipe it then close stdin for the child, but the input still goes to the child.
else // A process
{
pid_t child_pid;
char lastArgument = args[currArgsIndex-1][0];
if (lastArgument != '&'){ //Normal process
if((child_pid = fork()) == 0) {
execvp(filepath, args);
exit(0);
}
else
{
while(wait(NULL) != child_pid);
}
}
else { // Background
args[currArgsIndex-1] = NULL;
int process_pipe[2];
pipe(process_pipe); // Piping
if((child_pid = fork()) == 0) {
close(process_pipe[0]); // Ignore stdin for child
execvp(filepath, args);
exit(0);
}
}
}
You create a pipe and close the read end, but you never say that the pipe should be stdin.
It sounds like your intention was instead to 1. open the pipe only in the child, 2. close the write end so that no data can be read, 3. set the read end as stdin:
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
int process_pipe[2];
pipe(process_pipe); // Piping
dup2(process_pipe[0], 0); // Copy read end as stdin
close(process_pipe[0]); // Close FD that is now unused
close(process_pipe[1]); // Close write end so no data can be read
execvp(filepath, args);
perror("execvp failed");
exit(1); // exit with error
}
}
There's no point having a pipe though. You can more easily open /dev/null for reading and setting that as stdin. Alternatively, simply close stdin entirely (some programs will complain):
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
close(0); // Close stdin
execvp(filepath, args);
/* error handling */
}
Be aware that real shells allow redirecting to backgrounded processes, in which case none of the above will work:
wc -l < myfile &
Real shells will in fact not close or redirect stdin at all, but will put the command in its own process group that's not controlling the terminal. The process will then receive a SIGTSTP when it tries to read from stdin, and you can then use fg to bring it to the foreground to start typing data.

Following xv6/Linux forking and waitpid processes

int main()
{
int count = 0;
int pid;
if ( !(pid=fork()))
{
while (((count<2) && (pid=fork()) ) {
count++;
printf("%d",count)
}
if(count>0)
{
printf("%d", count);
}
}
if (pid) {
waitpid(pid,NULL,0);
count = count<<1;
printf("%d", count)
}
}
I'm having trouble reading this piece of code. I can't figure out how many system calls are made or even remotely trace them. This is leading me to not be able to understand how the output works. I am assuming the scheduler can do anything. What are all possible outputs and how many system calls are made?
Launch with strace -f ./myprogram to trace all system calls.
From looking at the code, about 3 additional processes will be created.

How to read one single char in Rust? [duplicate]

I want to run an executable that blocks on stdin and when a key is pressed that same character is printed immediately without Enter having to be pressed.
How can I read one character from stdin without having to hit Enter? I started with this example:
fn main() {
println!("Type something!");
let mut line = String::new();
let input = std::io::stdin().read_line(&mut line).expect("Failed to read line");
println!("{}", input);
}
I looked through the API and tried replacing read_line() with bytes(), but everything I try requires me to hit Enter before read occurs.
This question was asked for C/C++, but there seems to be no standard way to do it: Capture characters from standard input without waiting for enter to be pressed
It might not be doable in Rust considering it's not simple in C/C++.
While #Jon's solution using ncurses works, ncurses clears the screen by design. I came up with this solution that uses the termios crate for my little project to learn Rust. The idea is to modify ECHO and ICANON flags by accessing tcsetattr through termios bindings.
extern crate termios;
use std::io;
use std::io::Read;
use std::io::Write;
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
fn main() {
let stdin = 0; // couldn't get std::os::unix::io::FromRawFd to work
// on /dev/stdin or /dev/tty
let termios = Termios::from_fd(stdin).unwrap();
let mut new_termios = termios.clone(); // make a mutable copy of termios
// that we will modify
new_termios.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap();
let stdout = io::stdout();
let mut reader = io::stdin();
let mut buffer = [0;1]; // read exactly one byte
print!("Hit a key! ");
stdout.lock().flush().unwrap();
reader.read_exact(&mut buffer).unwrap();
println!("You have hit: {:?}", buffer);
tcsetattr(stdin, TCSANOW, & termios).unwrap(); // reset the stdin to
// original termios data
}
One advantage of reading a single byte is capturing arrow keys, ctrl etc. Extended F-keys are not captured (although ncurses can capture these).
This solution is intended for UNIX-like platforms. I have no experience with Windows, but according to this forum perhaps something similar can be achieved using SetConsoleMode in Windows.
Use one of the 'ncurses' libraries now available, for instance this one.
Add the dependency in Cargo
[dependencies]
ncurses = "5.86.0"
and include in main.rs:
extern crate ncurses;
use ncurses::*; // watch for globs
Follow the examples in the library to initialize ncurses and wait for single character input like this:
initscr();
/* Print to the back buffer. */
printw("Hello, world!");
/* Update the screen. */
refresh();
/* Wait for a key press. */
getch();
/* Terminate ncurses. */
endwin();
You can also use termion, but you will have to enable the raw TTY mode which changes the behavior of stdout as well. See the example below (tested with Rust 1.34.0). Note that internally, it also wraps the termios UNIX API.
Cargo.toml
[dependencies]
termion = "1.5.2"
main.rs
use std::io;
use std::io::Write;
use std::thread;
use std::time;
use termion;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
fn main() {
// Set terminal to raw mode to allow reading stdin one key at a time
let mut stdout = io::stdout().into_raw_mode().unwrap();
// Use asynchronous stdin
let mut stdin = termion::async_stdin().keys();
loop {
// Read input (if any)
let input = stdin.next();
// If a key was pressed
if let Some(Ok(key)) = input {
match key {
// Exit if 'q' is pressed
termion::event::Key::Char('q') => break,
// Else print the pressed key
_ => {
write!(
stdout,
"{}{}Key pressed: {:?}",
termion::clear::All,
termion::cursor::Goto(1, 1),
key
)
.unwrap();
stdout.lock().flush().unwrap();
}
}
}
thread::sleep(time::Duration::from_millis(50));
}
}
Here's a lightweight solution only using the libc crate based some code from the console crate:
fn setup_raw_terminal() -> io::Result<()> {
unsafe {
let tty;
let fd = if libc::isatty(libc::STDIN_FILENO) == 1 {
libc::STDIN_FILENO
} else {
tty = fs::File::open("/dev/tty")?;
tty.as_raw_fd()
};
let mut ptr = core::mem::MaybeUninit::uninit();
if libc::tcgetattr(fd, ptr.as_mut_ptr()) == 0 {
let mut termios = ptr.assume_init();
let c_oflag = termios.c_oflag;
libc::cfmakeraw(&mut termios);
termios.c_oflag = c_oflag;
if libc::tcsetattr(fd, libc::TCSADRAIN, &termios) == 0 {
return Ok(());
}
}
}
Err(io::Error::last_os_error())
}
It needs to be called before reading stdin:
let mut buf = [0u8; 1024];
let mut stdin = io::stdin();
setup_raw_terminal()?;
loop {
let size = stdin.read(&mut buf)?;
let data = &buf[0..size];
println!("stdin data: {}", data);
}

How to get an X11 Window from a Process ID?

Under Linux, my C++ application is using fork() and execv() to launch multiple instances of OpenOffice so as to view some powerpoint slide shows. This part works.
Next I want to be able to move the OpenOffice windows to specific locations on the display. I can do that with the XMoveResizeWindow() function but I need to find the Window for each instance.
I have the process ID of each instance, how can I find the X11 Window from that ?
UPDATE - Thanks to Andy's suggestion, I have pulled this off. I'm posting the code here to share it with the Stack Overflow community.
Unfortunately Open Office does not seem to set the _NET_WM_PID property so this doesn't ultimately solve my problem but it does answer the question.
// Attempt to identify a window by name or attribute.
// by Adam Pierce <adam#doctort.org>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <list>
using namespace std;
class WindowsMatchingPid
{
public:
WindowsMatchingPid(Display *display, Window wRoot, unsigned long pid)
: _display(display)
, _pid(pid)
{
// Get the PID property atom.
_atomPID = XInternAtom(display, "_NET_WM_PID", True);
if(_atomPID == None)
{
cout << "No such atom" << endl;
return;
}
search(wRoot);
}
const list<Window> &result() const { return _result; }
private:
unsigned long _pid;
Atom _atomPID;
Display *_display;
list<Window> _result;
void search(Window w)
{
// Get the PID for the current Window.
Atom type;
int format;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *propPID = 0;
if(Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL,
&type, &format, &nItems, &bytesAfter, &propPID))
{
if(propPID != 0)
{
// If the PID matches, add this window to the result set.
if(_pid == *((unsigned long *)propPID))
_result.push_back(w);
XFree(propPID);
}
}
// Recurse into child windows.
Window wRoot;
Window wParent;
Window *wChild;
unsigned nChildren;
if(0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren))
{
for(unsigned i = 0; i < nChildren; i++)
search(wChild[i]);
}
}
};
int main(int argc, char **argv)
{
if(argc < 2)
return 1;
int pid = atoi(argv[1]);
cout << "Searching for windows associated with PID " << pid << endl;
// Start with the root window.
Display *display = XOpenDisplay(0);
WindowsMatchingPid match(display, XDefaultRootWindow(display), pid);
// Print the result.
const list<Window> &result = match.result();
for(list<Window>::const_iterator it = result.begin(); it != result.end(); it++)
cout << "Window #" << (unsigned long)(*it) << endl;
return 0;
}
The only way I know to do this is to traverse the tree of windows until you find what you're looking for. Traversing isn't hard (just see what xwininfo -root -tree does by looking at xwininfo.c if you need an example).
But how do you identify the window you are looking for? Some applications set a window property called _NET_WM_PID.
I believe that OpenOffice is one of the applications that sets that property (as do most Gnome apps), so you're in luck.
Check if /proc/PID/environ contains a variable called WINDOWID
Bit late to the party. However:
Back in 2004, Harald Welte posted a code snippet that wraps the XCreateWindow() call via LD_PRELOAD and stores the process id in _NET_WM_PID. This makes sure that each window created has a PID entry.
http://www.mail-archive.com/devel#xfree86.org/msg05806.html
Try installing xdotool, then:
#!/bin/bash
# --any and --name present only as a work-around, see: https://github.com/jordansissel/xdotool/issues/14
ids=$(xdotool search --any --pid "$1" --name "dummy")
I do get a lot of ids. I use this to set a terminal window as urgent when it is done with a long command, with the program seturgent. I just loop through all the ids I get from xdotool and run seturgent on them.
There is no good way. The only real options I see, are:
You could look around in the process's address space to find the connection information and window ID.
You could try to use netstat or lsof or ipcs to map the connections to the Xserver, and then (somehow! you'll need root at least) look at its connection info to find them.
When spawning an instance you can wait until another window is mapped, assume it's the right one, and `move on.
I took the freedom to re-implement the OP's code using some modern C++ features. It maintains the same functionalities but I think that it reads a bit better. Also it does not leak even if the vector insertion happens to throw.
// Attempt to identify a window by name or attribute.
// originally written by Adam Pierce <adam#doctort.org>
// revised by Dario Pellegrini <pellegrini.dario#gmail.com>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <vector>
std::vector<Window> pid2windows(pid_t pid, Display* display, Window w) {
struct implementation {
struct FreeWrapRAII {
void * data;
FreeWrapRAII(void * data): data(data) {}
~FreeWrapRAII(){ XFree(data); }
};
std::vector<Window> result;
pid_t pid;
Display* display;
Atom atomPID;
implementation(pid_t pid, Display* display): pid(pid), display(display) {
// Get the PID property atom
atomPID = XInternAtom(display, "_NET_WM_PID", True);
if(atomPID == None) {
throw std::runtime_error("pid2windows: no such atom");
}
}
std::vector<Window> getChildren(Window w) {
Window wRoot;
Window wParent;
Window *wChild;
unsigned nChildren;
std::vector<Window> children;
if(0 != XQueryTree(display, w, &wRoot, &wParent, &wChild, &nChildren)) {
FreeWrapRAII tmp( wChild );
children.insert(children.end(), wChild, wChild+nChildren);
}
return children;
}
void emplaceIfMatches(Window w) {
// Get the PID for the given Window
Atom type;
int format;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *propPID = 0;
if(Success == XGetWindowProperty(display, w, atomPID, 0, 1, False, XA_CARDINAL,
&type, &format, &nItems, &bytesAfter, &propPID)) {
if(propPID != 0) {
FreeWrapRAII tmp( propPID );
if(pid == *reinterpret_cast<pid_t*>(propPID)) {
result.emplace_back(w);
}
}
}
}
void recurse( Window w) {
emplaceIfMatches(w);
for (auto & child: getChildren(w)) {
recurse(child);
}
}
std::vector<Window> operator()( Window w ) {
result.clear();
recurse(w);
return result;
}
};
//back to pid2windows function
return implementation{pid, display}(w);
}
std::vector<Window> pid2windows(const size_t pid, Display* display) {
return pid2windows(pid, display, XDefaultRootWindow(display));
}
int main(int argc, char **argv) {
if(argc < 2)
return 1;
int pid = atoi(argv[1]);
std::cout << "Searching for windows associated with PID " << pid << std::endl;
// Start with the root window.
Display *display = XOpenDisplay(0);
auto res = pid2windows(pid, display);
// Print the result.
for( auto & w: res) {
std::cout << "Window #" << static_cast<unsigned long>(w) << std::endl;
}
XCloseDisplay(display);
return 0;
}
Are you sure you have the process ID of each instance? My experience with OOo has been that trying to run a second instance of OOo merely converses with the first instance of OOo, and tells that to open the additional file.
I think you're going to need to use the message-sending capabilities of X to ask it nicely for its window. I would hope that OOo documents its coversations somewhere.
If you use python, I found a way here, the idea is from BurntSushi
If you launched the application, then you should know its cmd string, with which you can reduce calls to xprop, you can always loop through all the xids and check if the pid is the same as the pid you want
import subprocess
import re
import struct
import xcffib as xcb
import xcffib.xproto
def get_property_value(property_reply):
assert isinstance(property_reply, xcb.xproto.GetPropertyReply)
if property_reply.format == 8:
if 0 in property_reply.value:
ret = []
s = ''
for o in property_reply.value:
if o == 0:
ret.append(s)
s = ''
else:
s += chr(o)
else:
ret = str(property_reply.value.buf())
return ret
elif property_reply.format in (16, 32):
return list(struct.unpack('I' * property_reply.value_len,
property_reply.value.buf()))
return None
def getProperty(connection, ident, propertyName):
propertyType = eval(' xcb.xproto.Atom.%s' % propertyName)
try:
return connection.core.GetProperty(False, ident, propertyType,
xcb.xproto.GetPropertyType.Any,
0, 2 ** 32 - 1)
except:
return None
c = xcb.connect()
root = c.get_setup().roots[0].root
_NET_CLIENT_LIST = c.core.InternAtom(True, len('_NET_CLIENT_LIST'),
'_NET_CLIENT_LIST').reply().atom
raw_clientlist = c.core.GetProperty(False, root, _NET_CLIENT_LIST,
xcb.xproto.GetPropertyType.Any,
0, 2 ** 32 - 1).reply()
clientlist = get_property_value(raw_clientlist)
cookies = {}
for ident in clientlist:
wm_command = getProperty(c, ident, 'WM_COMMAND')
cookies[ident] = (wm_command)
xids=[]
for ident in cookies:
cmd = get_property_value(cookies[ident].reply())
if cmd and spref in cmd:
xids.append(ident)
for xid in xids:
pid = subprocess.check_output('xprop -id %s _NET_WM_PID' % xid, shell=True)
pid = re.search('(?<=\s=\s)\d+', pid).group()
if int(pid) == self.pid:
print 'found pid:', pid
break
print 'your xid:', xid

Resources