Writing data to fork pipe does not work - c

I have the following code in which I use fork to launch my script. The script is listening on the stdin. I try to send data via pipe to myscript but the scipt did not get the data from C. Am I missing something in my code?
static int pfds_in[2], pfds_out[2];
void external_init()
{
int pid;
if (pipe(pfds_in) < 0)
return;
if (pipe(pfds_out) < 0)
return;
if ((pid = fork()) == -1)
goto error;
if (pid == 0) {
/* child */
close(pfds_in[0]);
dup2(pfds_in[1], 1);
close(pfds_in[1]);
close(pfds_out[0]);
dup2(pfds_out[1], 0);
close(pfds_out[1]);
const char *argv[5];
int i = 0;
argv[i++] = "/bin/sh";
argv[i++] = fc_script;
argv[i++] = "--json";
argv[i++] = "json_continuous_input";
argv[i++] = NULL;
execvp(argv[0], (char **) argv);
exit(ESRCH);
}
close(pfds_in[1]);
close(pfds_out[1]);
return;
error:
exit(EXIT_FAILURE);
}
static void external_write_pipe_output(const char *msg)
{
char *value = NULL;
int i=0, len;
asprintf(&value, "%s\n", msg);
if (write(pfds_out[0], value, strlen(value)) == -1) {
perror("Error occured when trying to write to the pipe");
}
free(value);
}
int main()
{
external_init();
external_write_pipe_output("any");
}

You mismatched the both file descriptors you get from pipe(). pfds_in[0] is for reading, so you have to use dup2( pfds_in[0], 0 ) in your child and in the parent you write into the pipe using pfds_in[1].
Btw: What did you want to achieve by dup( ..., 1 ) in your child?. If you want to redirect child's stdout into a pipe to your parent you have to create another pipe

You've got your pipe ends in a muddle.
In the child you should have:
close(pfds_in[0]);
dup2(pfds_in[1], 1);
close(pfds_in[1]);
close(pfds_out[1]);
dup2(pfds_out[0], 0);
close(pfds_out[0]);
And in the parent:
close(pfds_in[1]);
close(pfds_out[0]);

Related

Pipe communication (parent -> child -> parent)

I'm having the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(-1);
}
int pfd[2]; // pipe
int pfd2[2]; // pipe2
pid_t pid; // child
if (pipe(pfd) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if (pipe(pfd2) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
// child process
if (pid == 0) {
close(pfd[1]);
int v[100] = {0};
int k = 0;
int r;
char buf[128];
while ((r = read(pfd[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// some processing here vor the 'v' variable
}
close(pfd[0]); // close the reading end 1st pipe
dup2(pfd2[1], 1); // redirect
close(pfd2[1]);
execlp("bash", "bash", "script.sh", v, NULL);
exit(-1);
} else {
// parent code
close(pfd2[1]);
dup2(pfd2[0], 0);
char buf[16];
int r = 0;
while ((r = read(pfd2[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// read here from second pipe
}
close(pfd2[0]); // close reading end from 2pnd pipe
close(pfd[0]); // close reading end from 1st pipe
dup2(pfd[1], 1); // redirect
execlp("cat", "cat", argv[1], NULL); // exec 'cat' on the given arg
exit(-1);
}
return 0;
}
That respect the following flow: the parent process executes the 'cat' command on the given arg file, then pass it to the child -> the child does some processing and then stores values in the 'v' variable -> then executes the 'script.sh' script with the values taken from the 'v' variable and then passes the output to the parent which will print to stdout the output based on the result from the child.
I'm unsure where should the code for the 2nd pipe reading should go, I'm pretty sure that's the problem right now.
Can anyone take a look at it and point where the problem is? Thanks!

Creating a shell in C that handles redirection and pipes

The following function successfully executes any command that doesn't contain pipes, so don't worry about the weird functions. These work. The problem I am having is that whenever I execute any command like the following:
cat file.txt | grep string
the command is successfully executed, but it remains idle, so somehow it gets stuck and no other command can execute. why is this happening?. I think it has something to do with the way I use pipe, dup and fork, so try to approach the problem from these functions. I know you may be arguing that this code doesn't work for other commands with pipes, but I just want to get this particular example to work and to do so I just redirect STDIN to the open file in the first iteration.
int myshell_execute(struct processNode* list, int a)
{
struct processNode* myList = list; // next node to be handled
int pipefd[2];
int in=0;
if (pipe(pipefd) == -1) {
perror("pipe");
myshell_exit(-1);
}
while(myList != NULL)
{
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
int *status;
int* stdout;
int stdout_num = 1;
stdout = &stdout_num;
int fileDescriptor;
pid_t pid;
if(strcmp(program,"cd") == 0)
{
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0)
{
return myshell_exit(0);
}
pid = fork();
if(pid == 0)
{
if(in == 1)
{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
}
if(sizeOfLine(redirection_string) != 0)
{
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
if(*stdout == 1 && myList->next !=NULL)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO); // with this
close(pipefd[1]);
}
if(execvp(program,program_args) !=-1)
{
perror("myshell:");
myshell_exit(-1);
}
else{
myshell_exit(0);
}
}
else if (pid <0)
{
perror("myshell: ");
myshell_exit(-1);
}
else
{
wait(status);
}
in = 1;
myList = myList->next;
}
}
new solution:
int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
redirectionHandler(redirection);
return execvp (program, program_args);
}
return pid;
}
int myshell_execute(struct processNode* list, int a)
{
int i;
pid_t pid;
int in, fd [2];
struct processNode*new_list = list;
char ** newProgram = new_list->program_arguments;
char ** redirection = new_list->redirection;
char * program = new_list->program;
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
while(new_list->next != NULL)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
helper_execute (in, fd [1],program,newProgram,redirection);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
new_list = new_list->next;
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
char* lastProgram = new_list->program;
char ** lastRedirection = new_list->redirection;
char * lastPrArguments = new_list->program_arguments;
redirectionHandler(redirection);
return execvp (lastProgram, lastPrArguments);
}
int main() {
int i=0;
char **input;
struct processNode* list;
int tracker = 0;
while ((input = getline()) != EOF) {
list = create_list(input);
myshell_execute(list,0);
}
return 0;
}
The only problem with this solution is that as soon as one command is executed, the main immediately detects the end of the file, so it exits the shell.
This is because
your parent process also holds the pipe open,
just after forking you call wait. If your first process (cat) fills its output pipe and there is not any process yet available to consume the read end of the pipe then the process stalls forever.
It would look like this:
#define MAX_PIPE_LEN 256
int myshell_execute(struct processNode* list, int a)
{
/* fd0 are the input and output descriptor for this command. fd1
are the input and output descriptor for the next command in the
pipeline. */
int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
fd1[2] = { -1, -1 };
pid_t pids[MAX_PIPE_LEN] = { 0 };
int pipe_len;
struct processNode* myList; // next node to be handled
int status;
int failed = 0;
for (pipe_len = 0, myList = list;
pipe_len < MAX_PIPE_LEN && myList != NULL;
pipe_len++, myList = myList->next) {
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
if(strcmp(program,"cd") == 0) {
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0) {
return myshell_exit(0);
}
if (myList->next != NULL) {
/* The output of this command is piped into the next one */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
failed = 1;
break;
}
fd1[0] = pipefd[0];
fd1[1] = fd0[1];
fd0[1] = pipefd[1];
}
pids[pipe_len] = fork();
if (pids[pipe_len] < 0) {
perror("error: fork failed");
failed = 1;
break;
}
if (pids[pipe_len] == 0) {
if (fd0[0] != STDIN_FILENO) {
if (dup2(fd0[0], STDIN_FILENO) == -1) {
perror("error: dup2 input failed");
abort();
}
close(fd0[0]);
}
if (fd0[1] != STDOUT_FILENO) {
if (dup2(fd0[1], STDOUT_FILENO) == -1) {
perror("error: dup2 outut failed");
abort();
}
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0) {
close(fd1[1]);
}
if(sizeOfLine(redirection_string) != 0) {
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
execvp(program, program_args);
perror("error: execvp failed");
abort();
}
if (fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd1[1] != STDOUT_FILENO) {
close(fd0[1]);
}
fd0[0] = fd1[0];
fd0[1] = fd1[1];
fd1[0] = fd1[1] = -1;
}
if (myList->next) {
fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
MAX_PIPE_LEN);
}
if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
close(fd1[1]);
}
/* Now wait for the commands to finish */
int i;
for (i = 0; i < pipe_len; i++) {
if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
perror("error: waitpid failed");
failed = 1;
}
}
if (failed)
status = -1;
myshell_exit(status);
}

Piping in my own C shell

I've implemented the beginning of a C shell as below. So far I have my redirection working, and I thought I would implement | in a similar way but am having difficulty.
Can anyone help?
I would begin with checking for the pipe operator, then saving the sa[i-1] and sa[i+1] as the two separate commands, but I'm not sure how to fork() and exec() properly after this.
int startProcess (StringArray sa)
{
int pid;
int status;
int fd1;
int fd2;
int current_in;
int current_out;
int fd0;
int fd00;
int in = 0;
int out = 0;
char input[64]="";
char output[64]="";
char cmd1[64] ="";
char cmd2[64] ="";
int fd[2];
int pipe = 0;
switch( pid = fork()){
case -1://This is an error
perror("Failure of child.");
return 1;
case 0: // This is the child
// Redirection
/* finds where '<' or '>' occurs and make that sa[i] = NULL ,
to ensure that command wont' read that*/
for(int i=0;sa[i]!='\0';i++)
{
if(strcmp(sa[i],"<")==0)
{
sa[i]=NULL;
strcpy(input,sa[i+1]);
in=2;
}
if(strcmp(sa[i],">")==0)
{
sa[i]=NULL;
strcpy(output,sa[i+1]);
out=2;
}
}
//if '<' char was found in string inputted by user
if(in)
{
// fdo is file-descriptor
int fd0;
if ((fd0 = open(input, O_RDONLY, 0)) < 0) {
perror("Couldn't open input file");
exit(0);
}
// dup2() copies content of fdo in input of preceeding file
dup2(fd0, 0); // STDIN_FILENO here can be replaced by 0
close(fd0); // necessary
}
//if '>' char was found in string inputted by user
if (out)
{
int fd00 ;
if ((fd00 = creat(output , 0644)) < 0) {
perror("Couldn't open the output file");
exit(0);
}
dup2(fd00, STDOUT_FILENO); // 1 here can be replaced by STDOUT_FILENO
close(fd00);
}
execvp(sa[0], sa);
perror("execvp");
_exit(1);
printf("Could not execute '%s'\n", sa[0]);
default:// This is the parent
wait(&status);
return (status == 0) ? 0: 1;
}
}
Make a pipe.
fork().
In the parent set the STDOUT file descriptor (1) to the input of your pipe.
In the child set the STDIN file descriptor (0) to the output of your pipe.
exec() in both the parent and the child.
Do all of this in the child after you fork(), just like for redirection.

Creating multiple pipe in C

I'm writing a simple shell and I'd like to change my program to add the possibility of multiple pipe commands like "echo foo | cat | cat | cat | cat | wc". I have written for two commands but for multiple i can't.
Here is the source code of my program:
if (pid == 0) // in the child process
{
for (i = 0; i < command; i++) // for each cmd
{
if (argv[i][0] == '|')
{
j = i;
}
}
if (j > 0)
{
if (pipe(p))
{
fprintf(stderr, "pipe");
exit(1);
}
argv[j] = NULL;
if (fork() == 0) // child
{
j = -1;
close(p[0]);
dup2(p[1],1);
close(p[1]);
}
// parent
close(p[1]);
dup2(p[0], 0);
close(p[0]);
}
for (i = 0; dirs[i] != 0; i++)
{
snprintf(pathname, sizeof(pathname), "%s/%s", dirs[i], argv[j+1]);
execv(pathname, &argv[j+1]);
}
}
else
{
while (wait(0) != pid) // parent: wait child
}
Thank you in advance for help.
I come with an example of what you are trying to do. I use constants as commands, I leave the command line parsing to you.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static char *my_command0[] = {"cat", "stackoverflow.c", NULL};
static char *my_command1[] = {"grep", "return", NULL};
static char *my_command2[] = {"sed", "s/^ *//g", NULL};
static char **const my_commands[] = {
my_command0,
my_command1,
my_command2,
NULL
};
int create_sub_process(char *const command[], int input_stream)
{
int pipefd[2] = {-1, -1};
pid_t fk;
if (pipe(pipefd) < 0)
{
perror("pipe");
close(input_stream);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0]);
close(pipefd[1]);
close(input_stream);
return -1;
}
if (fk == 0)
{
close(pipefd[0]);
close(0);
dup(input_stream);
close(input_stream);
close(1);
dup(pipefd[1]);
close(pipefd[1]);
execvp(command[0], command);
perror("execvp");
exit(1);
}
close(input_stream);
close(pipefd[1]);
return pipefd[0];
}
int main()
{
int fd = dup(0);
for (int i = 0; my_commands[i] != NULL; i++)
{
fd = create_sub_process(my_commands[i], fd); // replace my_commands[i] by whatever you need to execute - check: man execvp
if (fd < 0)
{
exit(1);
}
}
// Also adapt the following lines to whatever you want to do with last child results
close(0);
dup(fd);
close(fd);
execlp("cat", "cat", (char *)NULL);
perror("execlp");
return 1;
}
create_sub_process() creates a pipe and creates a sub process to execute given command, taking inputs from given input stream and sending output to the stream it returns to parent.

Read a string from pipe

I have the following code:
int pfds[2], pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pfds) < 0) {
goto error;
}
if ((pid = fork()) == -1)
goto error;
if (pid == 0) {
/* child */
close(pfds[1]);
dup2(pfds[0], 1);
close(pfds[0]);
const char *argv[5];
int i = 0;
argv[i++] = "/bin/sh";
argv[i++] = "/sbin/script.sh";
argv[i++] = NULL;
execvp(argv[0], (char **) argv);
exit(ESRCH);
}
close(pfds[0]);
s.stream.string_data = true;
s.stream.notify_read = client_read_cb;
ustream_fd_init(&s, pfds[1]);
The script return a string (printed with echo command) that is mark the end of script loading.
How can I read the string returned by the /sbin/script.sh ?
Basically you are missing the differentiation between child process which executes the execv and the parent process which will do the reading:
char readbuffer[100];
if(pid == 0)
...
else
{
close(pdfs[0];
nbytes = read(pfds[1], readbuffer, sizeof(readbuffer));
}
But if you just want to wait for the end of the script, you can just do a wait_pid on the child process.

Resources