Read/write stdin/out for bash interpreter linux, fork - execl - c

I've been trying to write a program that will send and receive commands to a bash shell (/bin/sh). Like a wrapper program around a bash shell. So, I could write to stdin "cd ~/Desktop", then write again "ls" and I will receive a listing of the files on the desktop. I can't get it working though. On the second write command in this code, it will echo back whatever I wrote to stdin. I've also tried using popen() but that only provides output, not allowing me to write to stdin. Could someone please help solve this problem? Thanks
void main()
{
// Create a pipe and fork
//
int fd[2];
int p = pipe(fd);
pid_t pid = fork();
if (pid > 0)
{
// Read from the pipe and output the result
//
//close(fd[1]);
char buf[1024] = { 0 };
read(fd[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write (fd[1], "ifconfig", strlen ("ifconfig") );
// problem is here, read is returning echo'd bytes from write()
read(fd[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
// Wait for child to terminate
int status;
wait(&status);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
dup2(fd[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}
EDIT - Updated code per 1st answer, now there is no output from the 2nd read() call
void main()
{
// Create a pipe and fork
//
int fd[2];
int ChildToParent[2], ParentToChild[2];
pipe (ParentToChild);
pipe (ChildToParent);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(ChildToParent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(ParentToChild[0]);
// Read from and write to the child process...
char buf[1024] = { 0 };
read(ChildToParent[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write(ParentToChild[1], "whoami", strlen ("whoami") );
memset (buf, 0, 1024);
// this call to read returns nothing
read(ChildToParent[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2 (ChildToParent[1], STDOUT_FILENO);
close(ChildToParent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2 (ParentToChild[0], STDIN_FILENO);
close(ParentToChild[1]);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}

Remember that pipes are a one-way communication stream. You can't use it for two-way communication between two processes. For that you need two pipes, one in each direction.
Perhaps something like this simple example:
// Pipe for the child process to write to the parent process
int child_to_parent[2];
// Pipe for the parent process to write to the child process
int parent_to_child[2];
// Create the TWO pipes
pipe(child_to_parent);
pipe(parent_to_child);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(child_to_parent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(parent_to_child[0]);
// Read from and write to the child process...
}
else if (pid == 0)
{
// In child process
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2(child_to_parent[1], STDOUT_FILENO);
close(child_to_parent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2(parent_to_child[0], STDIN_FILENO);
close(parent_to_child[1]);
// Do whatever the child is supposed to do
}

Related

how make cat and grep work in the first and the second pipe in c writing like heredoc in bash <<

I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}

execlp() failing to retrieve correct input

I've been trying to write a really simple program in which the parent process passes 100 lines to a child process through a pipe. The child should then use the generated lines and execute the command line program more over those lines.
However, when I try to run the program, it just freezes. I was careful to close all descriptors not being used by both processes but I don't really understand what may be causing it.
Code:
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
dup2(fd[1], STDOUT_FILENO);
int i;
for (i = 1; i <= 100; i++){
printf("Line %d\n", i);
}
close(fd[1]);
pid_t pid = fork();
if(pid == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more",(char*) NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
wait(NULL);
return 0;
}
I was careful to close all descriptors not being used by both processes
Not really.
dup2(fd[1], STDOUT_FILENO);
Here you make stdout a copy of fd[1].
close(fd[1]);
Here you close fd[1], but stdout is still open.
Then you fork. At this point both processes have access to the write end of the pipe via stdout.
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
In the child process you copy fd[0] to stdin and close fd[0].
Then, when you exec more, it still has access to both ends of the pipe (via stdin / stdout).
At the same time your parent process has access to both ends of the pipe (via fd[0] / stdout).
In effect you've closed nothing.
There's a second issue: Your parent process writes to stdout, which is bound to the write end of the pipe, without anyone reading it. Depending on how much you write, whether stdout is line buffered or block buffered, how big the stdout buffer is, and how much your pipe itself can store, this itself can deadlock. If the pipe runs full and there's no one around to read from it, printf will just block.
To fix this, don't dup2 in the parent process and don't write to the pipe before the child process has started.
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("Error spawning process");
return 2;
}
if (pid == 0) {
close(fd[1]); /* close write end of the pipe in the child */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more", (char*)NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
close(fd[0]); /* close read end of the pipe in the parent */
FILE *fp = fdopen(fd[1], "w");
if (!fp) {
perror("Error opening file handle");
return 3;
}
for (int i = 1; i <= 100; i++){
fprintf(fp, "Line %d\n", i);
}
fclose(fp); /* flush and close write end of the pipe in the parent */
wait(NULL);
return 0;
}

execve and pipe issues - how to recover original pipe?

I have been making simple shell which performs pipe.
Here is some code for operating pipe syntax.
int fd[2];
int stdin_copy;
int stdout_copy;
int status;
char * msg;
if (pipe(fd) == -1) {
perror("pipe");
exit(1);
}
// fd[0] : process read from fd[0]
// fd[1] : process write to fd[1]
if (execok(pr_words) == 0) { /* is it executable? */
status = fork(); /* yes; create a new process */
if (status == -1) { /* verify fork succeeded */
perror("fork");
exit(1);
} else if (status == 0) { /* in the child process... */
stdout_copy = dup(1);
close(1); // close standard output
dup(fd[1]);
close(fd[0]);
close(fd[1]); // close and fd[1] will be stdout
pr_words[l_nwds] = NULL; /* mark end of argument array */
status = execve(path, pr_words, environ); /* try to execute it */
perror("execve"); /* we only get here if */
exit(0); /* execve failed... */
}
/*------------------------------------------------*/
/* The parent process (the shell) continues here. */
/*------------------------------------------------*/
else if (status > 0) { // in the parent process....
wait( & status); /* wait for process to end */
if (execok(af_words) == 0) {
if (pipe(fd2) == -1) {
perror("pipe");
exit(1);
}
status = fork();
if (status == -1) {
perror("fork");
exit(1);
} else if (status == 0) { // in the child process...
stdin_copy = dup(0);
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
read(fd[0], readbuffer, sizeof(readbuffer));
af_words[r_nwds] = NULL; /* mark end of argument array */
status = execve(path, af_words, environ); /* try to execute it */
} else if (status > 0) {
wait( & status);
msg = "over";
write(2, msg, strlen(msg));
close(fd[0]);
close(fd[1]);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);
printf("%s", "hi");
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, af_words[0], strlen(af_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, pr_words[0], strlen(pr_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
pr_words and af_words is two-dimensional pointer containing command, right-side and left-side of pipe. (ex. ls | cat -> pr_words = "ls\0" , af_words = "cat\0")
And, first I make child process using fork() and register fd[1] for standard output. (and also save stdin file descriptor before closing stdin) And after execute left side of command, make other child process for handling right side of command.
Similarly, I saved stdout file descriptor before close stdout and made fd[0] standard input. By using input from first outcome of execve function, I thought every outcome would be saved in fd[1]. (Because this was currently registered as std output).
And, finally, restore pipe input and output to standard output. (I don't want to use dup2 but I have no choice because of my lack of knowledge )
However, in execution of this code, after I enter the 'ls | cat', there is no output. Furthermore, I set every entry of terminal will print '#'. (which means that '# ls' or '# cat' ...) But, after enter above pipe command, that program even does not print '#'.
I guess input and output stream of this program are completely twisted after dealing with pipe command.
How can I fix it? I mean, I want save outcome of first execve into fd[1] and after using this fd[1] for performing second execve, make final outcome will be printed through stdout file description.
I see a few issues with your code at least:
First off, you shouldn't wait() on the first process before starting the second one. A pipe only has a few KB of buffer in it, after which your shell will hang if the first child process tries to continue to write there. You need to start both children before wait()ing for each of them. Just move the first wait(&status) call down next to the other one. You'll probably want to use waitpid or something later so you know which one finished first and which status goes to which, but you can address that once you get the basics working.
Secondly, all variables and file descriptor mappings in your program are copied when you fork(). Therefore, you don't need to save stdin or stdout in either child process, because none of the changes you make in the child processes will affect the parent. Furthermore, because you only initialize stdin_copy and stdout_copy in the child processes, the versions of those variables you use in the parent process after the second fork() are uninitialized. This is what's causing the parent shell's I/O to be messed up after executing this code. You don't actually need to do anything in the parent after forking the second time to maintain the original stdin and stdout there -- you never change them in that process before that point. You probably want to remove all of this from the post-fork parent code:
close(fd[0]);
close(fd[1]);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);
Thirdly, why are you reading from the pipe before calling execve() in the second child? That's just going to strip data out of the pipe that your exec'd child will never see. That's probably what's causing the pipe itself to appear not to work. You probably want to remove this:
read(fd[0], readbuffer, sizeof(readbuffer));
Lastly, this line probably needs to go before the execok() call (and similarly for the other similar one):
pr_words[l_nwds] = NULL; /* mark end of argument array */
The skeleton of the code should look about like this, leaving off error handling and execok checks, and demonstrating the use of waitpid() if you want to know which status code is for which child:
int child_pid[2];
child_pid[0] = fork();
if (child_pid[0] == 0) {
// first child, close stdout and replace with pipe, then exec
} else {
child_pid[1] = fork();
if (child_pid[1] == 0) {
// second child, close stdin and replace with pipe, then exec
} else {
// parent, and now we have the pids of the children
waitpid(child_pid[0], &status, 0); // wait for first child
waitpid(child_pid[1], &status, 0); // wait for second child
// *do not* mess with stdin/stdout, they are okay here
}
}

Recursive Piping in Unix environment

I am trying to implement piping in Unix, and have been asked to do it recursively. I have a sh program that parses input by the pipe character and then forks a child process to begin the piping. I am going to use the command cat file | grep the | more as an example.
My sh.c program first parses the input string into pipeCmds which is just an array of char * that point at the different parts of the input string. It forks the first child and then begins the recursivePipe() call which should set up all the pipes. After the pipes are set up I exec("more") in this case.
pid = fork();
if(pid == 0){
recursivePipe(pipeCmds[numCmds-2], numCmds-1);
exec(pipeCmds[numCmds-1]); // exec("more")
}else{
pid = wait(getpid());
}
Here is the recursivePipe function that should set up each pipe based on how many commands were in the string (ie. numCmds)
int recursivePipe(char * cmd, int index){
/* cmd = more */
int pid, fd, copy;
int ttyfd;
char tty[64];
if(index < 1){
printf("index is 0... RETURN\n");
return;
}
pipe(pd);
// First fork a new child to stage the WRITING proc
printf("forking to %s from %s\n", cmd, pipeCmds[index]);
pid = fork();
if(pid == 0){
// Child
close(pd[0]); // Close the child's read
//if(index != (numCmds-2)){ // If we are not on the last command, make stdout be the pipe
dup2(pd[1], 1); // Place the WRITE end of the pipe into stdout so anything coming from the pipe
close(pd[1]);
//}
copy = dup(1);
close(1);
gettty(tty);
ttyfd = open(tty, O_WRONLY);
if(ttyfd == 1){
printf("exec(%s) from %s\n", cmd, pipeCmds[index]);
close(1);
dup(copy);
close(copy);
}
/*copy = dup(0);
close(0);
gettty(tty);
ttyfd = open(tty, O_RDONLY);
if(ttyfd == 0){
getc();
close(0);
dup(copy);
close(copy);
}*/
exec(cmd);
}else{
// Parent
printf("in parent: %s[%d]\n", pipeCmds[index], index);
close(pd[1]); // Close the parent's write since more doesn't have to write
//if(index != 0){ // If we are not on the first command, make stdin be the pipe
dup2(pd[0], 0); // Place the READ end of pipe into stdin so that more's stdin comes from the pipe
close(pd[0]);
//}
printf("lets recurse!: %s[%d]\n", pipeCmds[index], index);
// if(index > 0){
recursivePipe(pipeCmds[index-2], index-1); // This will set up all other procs too with correct pipes
//pid = wait(getpid());
// }
printf("done recurse!: %s[%d]\n", pipeCmds[index], index);
}
}
Basically, I attempt to pipe(), then in the child process I close the READ end of the pipe and set stdout to now be the pipe. So in this case, on the first call of recursivePipe(), the parent section is the "more" proc and the child section is the "grep the" part. So "more" closes its stdin and replaces it with the pipe so it reads all output from "grep the"
Based on my printf() inside the function, here is the output of that command:
forking to grep the from more
in parent: more[2]
lets recurse!: more[2]
forking to cat file from grep the
exec( grep the ) from more
exec(cat file) from grep the
in parent: grep the [1]
lets recurse!: grep the[1]
index is 0... RETURN
done recurse!: grep the [1]
done recurse!: more[2]
And then it appears to cat file correctly and send it to more, but the grep program is never used between the two. It is as if cat and more are communicating directly rather than through grep.
Can anyone with knowledge of the unix system help me figure out why my recursion isn't setting up the pipes correctly? Thanks

Pipe between 2 child processes UNIX C

I was creating a pipe between 2 child processes. One would output to the pipe, and the other would input from the pipe. I was able to parse the command and parameters required to execute a command (or 2 since its a pipe). However, I think my pipe is setup incorrectly:
[...]
type_prompt(); //Type out prompt to the user
read_command(); //Read the command from the user
pipe(&fd[0]); //Create a pipe
proc1 = fork();
//Child process 1
if (proc1 == 0)
{
close(fd[0]); //process1 doenst need to read from pipe
dup2(fd[1], STD_INPUT);
close(fd[1]);
execvp(parameter[0], parameter); //Execute the process
}
//Create a second child process
else
{
//Child process 2
proc2 = fork();
if (proc2 == 0)
{
close(fd[1]);
dup2(fd[0], STD_OUTPUT);
close(fd[0]);
execvp(parameter2[0], parameter2);
}
//Parent process
else
{
waitpid(-1, &status, 0); //Wait for the child to be done
}
}
You should pass a pointer to a buffer allocated with malloc as the first argument to getline, for example:
int bytes_read;
int nbytes = 100;
char *my_string;
/* These 2 lines are the heart of the program. */
my_string = (char *) malloc (nbytes + 1);
bytes_read = getline (&my_string, &nbytes, stdin);
See http://www.crasseux.com/books/ctutorial/getline.html for more details (the above example is taken from there and reduced).

Resources