Pipe between 2 child processes UNIX C - 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).

Related

How can I redirect the output of an exec() system call from stdout to a pipe and read the result into a buffer from the parent process?

The goal is to have the cksum_child function create a child process, and execute the checksum command on the provided file. Redirecting the output to a pipe and reading the result from the pipe in the entry function get_cksum which is functioning as the parent.
`
pid_t
create_cksum_child (int *pipe, char *const filename)
{
pid_t pid = fork();
if (pid < 0) {
printf("fork\n");
return NULL;
}
if (pid == 0) {
close(pipe[0]);
dup2(pipe[1], STDOUT_FILENO);
execlp("/usr/bin/cksum", "cksum", filename, NULL);
printf("execlp\n");
exit(1);
}
int status;
waitpid(pid, &status, 0);
return pid;
}
char *
get_cksum (char *const filename)
{
char *buffer = NULL;
int fd[2];
if (pipe(fd) < 0) {
printf("pipe\n");
exit(1);
}
pid_t child_pid = create_cksum_child(fd, filename);
if (child_pid == NULL) {
return NULL;
}
fflush(stdout);
ssize_t bytes_read = read (fd[0], buffer, sizeof (buffer));
close(fd[0]);
close(fd[1]);
if (bytes_read < 1) {
printf("bytes read: %ld\n", bytes_read);
return NULL;
}
return buffer;
}
`
The code I have here closely follows my textbook sections on IPC models, which initially caused a timeout err. From what I've gather from similar questions on SO I needed to fflush() stdout since the output was being buffered which fixed the timeout but the returned string is incorrect. I then added print statement to see how many bytes I'm reading from the pipe and I get -1 bytes on all test files.
What mistakes did I make and what can I do to redirect the output from stdout and read it in the parent properly?
The main issue is that you should merge the 2 functions together because the parent code is not only in the second function but it is splitted into both functions you wrote.
For example you should move the waiting lines
int status;
waitpid(pid, &status, 0);
on the parent side, since the parent has to wait the child and not vice versa.
After that, your code is in a good shape but probably you should refactor it after the merge of the functions.

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
}

Trying to pass data from execve child process to parent process in C

I've been trying to use pipes in order to pass a value calculated in the child process(which is created by execve in the parent) but have been unable to figure out why data isn't being transferred over. It's my understanding that unless the close on exec flag is set on the pipe that the the pipe would still be shared across processes even though child process is created with a copy of data with a new stack, heap, etc?
The parent works by allowing the user to enter in a value to change a string and the string reassignment is done in the child process to be sent back to the parent.
Here is my code for the parent process:
if((pid = fork())) {
if(pid < 0) {
printf("Fork error: %s\n",strerror(errno));
}
wait(&status);
} else {
int fd[2];
pipe(fd);
char* argv[] = {"new string", variableToChange, NULL};
ret = execve("newstringchildprocess", argv, environ);
close(fd[1]);
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
close(fd[0]);
}
And the child process(seperate C file):
int fd[2];
pipe(fd);
char readbuffer[100];
close(fd[0]);
printf("Enter new string: ");
write(fd[1], variableToChange, (strlen(variableToChange) + 1));
int charCount = sizeof argv[0];
variableToChange = (char*) malloc(charCount);
close(fd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
Currently I only get output from the child process if I remove the pipes code from both files. Any help would be great
If I understand correctly what you need is to connect your child's output to the father.
One way for doing this is piping, of course, but you have to create the pipe in the father process and then forking, so the child is going to inherit the opened pipe.
Oh, and If I'm not wrong, after execve the process "dies" as it does not comes back to your code.
int fd[2];
pipe(fd);
if((pid = fork()) < 0) {
printf("Fork error: %s\n",strerror(errno));
} else if (pi==0) { //Child process
char* argv[] = {"new string", variableToChange, NULL};
dup2(fd[1],1); //You connect to the pipe in input mode
execve("newstringchildprocess", argv, environ);
printf("Shouldn't execute this\n");
exit(1);
} else{ //Father process
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
close(fd[0]);
close(fd[1]);
}
And the other c file just need to focus on its logic and not communication:
printf("Enter new string: ");
printf(variableToChange, (strlen(variableToChange) + 1));
int charCount = sizeof argv[0];
variableToChange = (char*) malloc(charCount);
wait(NULL);
exit(EXIT_SUCCESS);
EDIT
Anyways, you could replace in the dup2 the file descriptor you want to use, and instead of being directly connected to the standard output it could be connected to the pipe in another fd. It's a bit harder but gives you a little bit more flexibility. Remember that another created pipe in the other .c file is not the same one than the father.
int fd[2];
pipe(fd);
if((pid = fork()) < 0) {
printf("Fork error: %s\n",strerror(errno));
} else if (pi==0) { //Child process
char* argv[] = {"new string", variableToChange, NULL};
execve("newstringchildprocess", argv, environ);
printf("Shouldn't execute this\n");
exit(1);
} else{ //Father process
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
close(fd[0]);
close(fd[1]);
}
And the other c file just need to focus on its logic and not communication:
printf("Enter new string: ");
write(4,variableToChange, (strlen(variableToChange) + 1)); // I put 4 as I assume it is where the input of the pipe is, as i think you can't access fd[1] form this file
int charCount = sizeof argv[0];
variableToChange = (char*) malloc(charCount);
wait(NULL);
exit(EXIT_SUCCESS);

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

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
}

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
}
}

Resources