Cannot perform reading from pipe in C? - c

I am writing a code to interact with an application involving reading and writing to the application. Here is the code: the first one - namely input.c interacts with the second one - namely app.c
//input.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
void error(char* msg){
perror(msg);
exit(-1);
}
void got_here(char* msg){
printf("Got_here:%s\n",msg);
}
int main(int argc, char** argv, char** envp){
int fd_parent[2];
int fd_child[2]; // for parent and child to write respectively
if(pipe(fd_parent) < 0 | pipe(fd_child) < 0){
error("Fail to create a pipe"); /// just an error-handle function
}
pid_t child = fork();
if(child < 0){
error("Fail to create a child");
}
else if(child == 0){
dup2(fd_child[WRITE], STDOUT_FILENO);
dup2(fd_parent[READ], STDIN_FILENO);
close(fd_parent[WRITE]);
close(fd_child[READ]);
char str[100] = "./app";
execve(str, argv,envp);
close(fd_parent[READ]);
close(fd_child[WRITE]);
return 0;
}
else{
close(fd_parent[READ]);
close(fd_child[WRITE]);
FILE* stream = fdopen(fd_child[READ], "r");
FILE* stream_write = fdopen(fd_parent[WRITE], "w");
char str[20];
char menu[4] = "10\n";
fread(str,sizeof(char), 20, stream); // Here is where the problem lies
got_here("after read"); // it does not get here
fwrite(menu, sizeof(char), 3, stream_write);
fflush(stream_write);
fclose(stream);
fclose(stream_write);
printf("Parent Done\n");
return 0;
}
}
Here is the application code (I only include the main for shorter code):
int main(int argc, char** argv, char** envp){
char str[10];
printf("- Select Menu -1\n");
printf("1. Play Lotto\n");
scanf("%s", str);
return 0;
}
After running, my program just paused at the fread() line where it is supposed to finish reading and write to the application. The interesting is if I omit either the scanf() or printf() in the second program it works fine. I try change the place of the fwrite and fread but the problem is still there. I think it is buffer-related problem, but the application I am trying to interact with is not of my permission to change, so I cannot include fflush or something.
Is my guess right or there is another explanation for this? And how to get over this problem?

You can use stdbuf command to modify the buffering options of the program at the other end of a pipe. In order to do this in C, you can write:
char str[100] = "./app";
char **new_argv = malloc (sizeof (char *) * (argc + 9));
new_argv[0] = "stdbuf";
new_argv[1] = "-i";
new_argv[2] = "0";
new_argv[3] = "-o";
new_argv[4] = "L";
new_argv[5] = "-e";
new_argv[6] = "L";
new_argv[7] = str;
memcpy (&new_argv[8], &argv[1], argc - 1);
new_argv[argc + 8] = NULL;
execvp ("stdbuf", new_argv);
error ("execvp");
or if you don't really need to pass the arguments of the parent on to the child, then:
execlp ("stdbuf", "stdbuf", "-i", "0", "-o", "L", "-e", "L", "./app", NULL);
error ("execlp");
The stdbuf command uses LD_PRELOAD to load a library (libstdbuf.so) into the other program. This library does the trick of modifying the buffering options. You can avoid the use of stdbuf and set the preload options yourself before exec(). You can also write your own library and preload that instead. But using the stdbuf is probably the easiest option, if you have this command available.
See also stdbuf source code
Here's the full modified example of your code:
//input.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
void error (char *msg)
{
perror (msg);
exit (-1);
}
void got_here (char *msg)
{
printf ("Got_here: %s\n", msg);
}
int main (int argc, char **argv, char **envp)
{
int fd_parent[2];
int fd_child[2]; // for parent and child to write respectively
pid_t child;
if (pipe (fd_parent) < 0) {
error ("pipe(fd_parent)");
}
if (pipe (fd_child) < 0) {
error ("pipe(fd_child)");
}
child = fork ();
if (child < 0) {
error ("fork");
} else if (child == 0) {
dup2 (fd_child[WRITE], STDOUT_FILENO);
dup2 (fd_parent[READ], STDIN_FILENO);
close (fd_parent[WRITE]);
close (fd_child[READ]);
char str[100] = "./app";
char **new_argv = malloc (sizeof (char *) * (argc + 9));
new_argv[0] = "stdbuf";
new_argv[1] = "-i";
new_argv[2] = "0";
new_argv[3] = "-o";
new_argv[4] = "L";
new_argv[5] = "-e";
new_argv[6] = "L";
new_argv[7] = str;
memcpy (&new_argv[8], &argv[1], argc - 1);
new_argv[argc + 8] = NULL;
execvp ("stdbuf", new_argv);
error ("execvp");
close (fd_parent[READ]);
close (fd_child[WRITE]);
return 0;
} else {
close (fd_parent[READ]);
close (fd_child[WRITE]);
FILE *stream = fdopen (fd_child[READ], "r");
FILE *stream_write = fdopen (fd_parent[WRITE], "w");
char str[20];
char menu[4] = "10\n";
int res = fread (str, sizeof (char), 20, stream); // Here is where the problem lies
printf ("res = %d\n", res);
got_here ("after read"); // it does not get here
fwrite (menu, sizeof (char), 3, stream_write);
fflush (stream_write);
fclose (stream);
fclose (stream_write);
printf ("Parent Done\n");
return 0;
}
}

Related

Will prefixing the script path with `/usr/bin/env -i` make usage of system() and popen() secure?

I am doing a code review right now and I was blown away by the amount of code that the guy wrote just to execute one script (with hard-coded path and no input arguments) and read the output out of it. (BTW he had a lot of bugs in it.)
I encountered a similar issue before and it was suggested to me that doing pipe/fork/exec manually is "more secure". I am aware of two potential problems:
As system() and popen() execute shell commands, it is possible to slip potentially harmful environment variable values to the program executed this way
Another is that when the command is constructed from user input. I can imagine subshells doing all kinds of harmful things and so on.
I was wondering if suggesting to use popen() instead would be OK in this case. It would greatly simplify the code. The second point is not an issue as there is no user input. By using env -i to clean the environment before executing the script should make the first issue go away:
FILE *fp = popen("/usr/bin/env -i /path/to/some/fancy/script.sh", "r");
/* ... */
Are there any other potential issues I am missing, or is doing the script execution "manually" still worth the effort?
This is technically not your answer to the question on how to call popen() safely, but the answer to the question you should have asked: "How to make a better popen()"
The function child_spawn(argv, env, flags) will set up pipes for communicating with a child process, and spawn the child. It will return a
struct child that holds the child pid and file descriptors for communication.
argv is a NULL terminated string array of command and arguments, while env is a NULL terminated string array of environment variables. If env is NULL, the child will inherit the environment from the parent.
So argv should have the form
const char* argv[] = {"/bin/ls", "-l", NULL};
And env should have the form
const char **env = NULL;
or
const char *env[] =
{
"PATH=/bin:/usr/bin",
"HOME=/tmp",
"SHELL=/bin/sh",
NULL
};
When you are finished with the child process, child_wait() will close the file descriptors associated with the child and wait for it to exit.
To use child_spawn() as a substitute of popen() you call it like this:
struct child c = child_spawn(argv, NULL, CHILD_PIPE_STDOUT);
You may now read from c->fd_out to get the content of the childs stdout.
The constants CHILD_PIPE_STDIN, CHILD_PIPE_STDOUT and CHILD_PIPE_STDERR may be "or"-ed together to have a valid file descriptors in c->fd_in, c->fd_out, c->fd_err
Be aware that if you spawn a child with CHILD_PIPE_STDIN|CHILD_PIPE_STDOUT, there is a risk of deadlock when reading and writing, unless you do non-blocking io.
The function my_system() is an example on how to implement a safer system() using child_spawn()
/*
We have to #define _GNU_SOURCE to get access to `char **environ`
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>
#include <string.h>
struct child
{
pid_t pid;
int fd_in;
int fd_out;
int fd_err;
};
static void
close_if_valid(int fd)
{
if (fd != -1) close(fd);
}
/*
Closes all file-descriptors for child communication
and waits for child to exit
returns status value from waitpid().
see `man waitpid` on how to interpret that value
*/
int child_wait(struct child *c)
{
close_if_valid(c->fd_in);
close_if_valid(c->fd_out);
close_if_valid(c->fd_err);
int status;
pid_t p = waitpid(c->pid, &status, 0);
if (p == 0)
error(1, errno, "waitpid() failed");
return status;
}
int
dup_if_valid(int fd1, int fd2)
{
if (fd1 != -1 && fd1 != fd2)
return dup2(fd1, fd2);
return fd2;
}
pid_t
child_spawn_fd(const char *const argv[], const char *const env[],
int in, int out, int err)
{
fflush(stdout);
pid_t p = fork();
if (p)
return p;
/***********************
We are now in child
***********************/
/*
Set file descriptors to expected values,
-1 means inherit from parent
*/
if (dup_if_valid(in, 0) == -1)
goto CHILD_ERR;
if (dup_if_valid(out, 1) == -1)
goto CHILD_ERR;
if (dup_if_valid(err, 2) == -1)
goto CHILD_ERR;
/*
close all unneeded file descriptors
This will free resources and keep files and sockets belonging to
the parent from beeing open longer than needed
On *BSD we may call `closefrom(3);`, but this may not exits
on Linux. So we loop over all possible file descriptor numbers.
A better solution, is to look in `/proc/self/fs`
*/
int max_fd = sysconf(_SC_OPEN_MAX);
for (int fd = 3; fd <= max_fd; fd++)
close(fd);
if (env)
environ = (char **)env;
/* Change to execvp if command should be looked up in $PATH */
execv(argv[0], (char * const *)argv);
CHILD_ERR:
_exit(1);
}
#define CHILD_PIPE_STDIN (1 << 0)
#define CHILD_PIPE_STDOUT (1 << 1)
#define CHILD_PIPE_STDERR (1 << 2)
#define READ_END 0
#define WRITE_END 1
struct child
child_spawn(const char * const argv[], const char * const env[], int flags)
{
int in_pipe[2] = {-1, -1};
int out_pipe[2] = {-1, -1};
int err_pipe[2] = {-1, -1};
if (flags & CHILD_PIPE_STDIN)
if (pipe(in_pipe))
error(EXIT_FAILURE, errno, "pipe(in_pipe) failed");
if (flags & CHILD_PIPE_STDOUT)
if (pipe(out_pipe))
error(EXIT_FAILURE, errno, "pipe(out_pipe) failed");
if (flags & CHILD_PIPE_STDERR)
if (pipe(err_pipe))
error(EXIT_FAILURE, errno, "pipe(err_pipe) failed");
pid_t p = child_spawn_fd(argv, env,
in_pipe[READ_END],
out_pipe[WRITE_END],
err_pipe[WRITE_END]);
if (p == -1)
error(EXIT_FAILURE, errno, "fork() failed");
close_if_valid(in_pipe[READ_END]);
close_if_valid(out_pipe[WRITE_END]);
close_if_valid(err_pipe[WRITE_END]);
struct child c =
{
.pid = p,
.fd_in = in_pipe[WRITE_END],
.fd_out = out_pipe[READ_END],
.fd_err = err_pipe[READ_END],
};
return c;
}
/*
Safer implementation of `system()`. It does not invoke shell, and takes
command as NULL terminated list of execuatable and parameters
*/
int
my_system(const char * const argv[])
{
struct child c = child_spawn(argv, NULL, 0);
int status = child_wait(&c);
if (WIFEXITED(status))
return WEXITSTATUS(status);
else
return -1;
}
int
main (int argc, char **argv)
{
printf("Running 'ls -l' using my_system()\n");
printf("---------------------------------\n");
fflush(stdout);
const char * ls_argv[] =
{
"/bin/ls",
"-l",
NULL
};
int e = my_system(ls_argv);
printf("---------\n");
printf("\exit code ---> %d\n", e);
printf("\nRunning 'ls -l' using child_spawn() and reading from stdout\n");
printf("-----------------------------------------------------------\n");
fflush(stdout);
struct child c = child_spawn(ls_argv, NULL, CHILD_PIPE_STDOUT);
/*
Read from the childs stdout and write to current stdout
*/
size_t copied = 0;
while (1)
{
char buff[4096];
ssize_t rlen = read(c.fd_out, buff, 4096);
if (rlen == -1)
error(EXIT_FAILURE, errno, "read() failed");
if (rlen == 0)
break;
size_t written = 0;
while (written < rlen)
{
ssize_t wlen = write(1, buff + written, rlen - written);
if (wlen == -1)
error(EXIT_FAILURE, errno, "write() failed");
written += wlen;
}
copied += written;
}
/* Wait for child to end */
int status = child_wait(&c);
printf("---------\n");
if (WIFEXITED(status))
{
printf(" ---> child exited normally with exit code %d and with %ld bytes copied\n",
WEXITSTATUS(status),
copied);
}
else
printf(" ---> child exited by som other reason than _exit()");
printf("\nWriting to Elmer Fudd filter\n");
const char *quote = "Be very very quiet, I'm hunting rabbits!\n";
printf("Original text: %s", quote);
printf("-----------------------------------------------------------\n");
fflush(stdout);
const char *fudd_filter[] =
{"/bin/sed", "-e" "s/r/w/g", NULL};
struct child c2 = child_spawn(fudd_filter, NULL, CHILD_PIPE_STDIN);
size_t qlen = strlen(quote);
const char *q = quote;
while (qlen)
{
ssize_t wlen = write(c2.fd_in, q, qlen);
if (wlen == -1)
error(EXIT_FAILURE, errno, "write() failed");
q += wlen;
qlen -= wlen;
}
child_wait(&c2);
}

Why does the execlp in child not return anything into stdout?

I have a program that reads in line by line from a text file. Each line has the layout
command arg1 arg2 arg3
and I have read it in so that I have 2 arrays, 1 which contains the string and another which points to each string value. eg
char read_in_line[128]
char* command[100]
and so:
command[0] = command arg1 arg2 arg3
command[1] = command arg1
etc.
I then have this command array as an input to a function that uses fork and pipes. The following is a snippet of this function and note it is in a while loop which will continue while *cmd != NULL
void piping(char* cmd[100]{
else if(pid == 0){
//child does not need to read
close(thepipe[0]);
dup2(thepipe[1],1);
close(thepipe[1]);
execlp(*cmd,*cmd,NULL);
However, this does not return anything. My C program compiles without showing any errors, however in my stdout I can not see the execution of any of the commands i sent into the function.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFERSIZE 128
#define oops(m,x) {perror(m); exit(x);}
void piping(char **cmd[BUFFERSIZE]){
pid_t pid;
int thepipe[2];
int in = 0;
//while there are still commands
while (*cmd != NULL){
pipe(thepipe);
//fork error case
if((pid = fork()) < 0)
oops("cannot fork",1);
//child
if(pid == 0){
//child does not need to read
close(thepipe[0]);
if(dup2(thepipe[1],1)== -1)
oops("Error redirecting stdout",2);
//duplication succesful can now close thepipe[1]
close(thepipe[1]);
//execute the command
execvp(*cmd[0], *cmd);
exit(-1);
}
else{
//parent does not write to pipe
close(thepipe[1]);
//setting up parent input to read from the pipe
dup2(thepipe[0],0);
close(thepipe[0]);
//wait until child finishes
wait(NULL);
cmd++;
}
}
}
int main(int argc, char* argv[]){
char **command[BUFFERSIZE];
char read_in_line[BUFFERSIZE];
int i = 0;
int counter =0;
int counter2 =0;
//reading in line by line until end of file is reached
FILE* fp = fopen("test.txt","r");
while( fgets(read_in_line, BUFFERSIZE, fp) != NULL ){
int j = 0;
//setting up memory for arguments given that we know there is a max
//of 10 arguments per line
char **arguments = (char**) calloc(16, sizeof(char*));
command[i] = arguments;
//Will break up the line read in when a newline is argument resulting in one
//string containing the commands and arguments
//this string will then be broken up every time a space is met so that
//commands and arguments can be seperated, and saved to command[i][j]
char *t = strtok(read_in_line, "\n");
char *argument = strtok(t, " ");
command[i][j] = strdup(argument);
while(argument != NULL){
argument =strtok(NULL, " ");
if(argument != NULL){
command[i][++j] = strdup(argument);
}
}
i++;
}
piping(command);
return (0);
}
The program below works as expected:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int main(void)
{
int rc;
rc=execlp("/bin/date", "deet", (char*) NULL);
printf("Rc=%d,%d(%s)\n", rc, errno, strerror(errno));
return 0;
}
Next step: add some arguments. (next step: fix the plumbing)
rc=execlp("/bin/ls", "ls", "-li", (char*) NULL);

How to use execvp() to execute a command

So I'm trying to create a custom shell for my school project. My method was to create child process, and have that process execute the command using the execvp() function that my professor briefly mentioned in class that we are meant to use. Here's my code, as always, any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define MAX_LINE 80
int main(int argc, char *argv[])
{
char *input = (char*)malloc(MAX_LINE*sizeof(char));
int should_run = 1;
while(should_run){
printf("osh>");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0){
printf("error with creating chiled process");
return 0;
}
if(pid == 0){
fgets(input, MAX_LINE, stdin);
char *token = strtok(input," ");
if(execvp(token[0], token) < 0){
printf("Error in execution.");
return(0);
}
//should_run = 0;
}
waitpid(pid, 1, 0);
}
return 0;
}
The prototype of execvp is
int execvp(const char *file, char *const argv[]);
It expects a pointer to char as the first argument, and a NULL-terminated
pointer to an array of char*. You are passing completely wrong arguments.
You are passing a single char as first argument and a char* as the second.
Use execlp instead:
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
So
char *token = strtok(input," \n");
if(token == NULL)
{
fprintf(stderr, "only delimiters in line\n");
exit(1);
}
if(execlp(token, token, NULL) < 0){
fprintf(stderr, "Error in execution: %s\n", strerror(errno));
exit(1);
}
Also the convention in UNIX is to print error messages to stderr and a process with an error should
have an exit status other than 0.
As Pablo's states, you are passing the wrong arguments to execvp().
You can consider coding by yourself a function (char **strsplit(char *str, char delim)) which takes a string and split it into smaller pieces, returning an array of strings.
Also don't ignore compiler's warnings, they tell you a lot of things, and I suggest you to compile with gcc -Wall -Wextra -Werror to get almost any possible error in your program.
I tell you this because waitpid() takes as second argument a pointer to integer, to get an update of the status of the forked program. With this status you how the program exited (normally, segf, bus error...), you can use it to print an error if something went wrong.
You can consider using execv() instead (I know I'm going off topic, but you can learn useful things doing this), and find by yourself the correct executable(s).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MAX_LINE 255
char **strsplit(char *str, char delim);
char *strjoin(char const *s1, char const *s2);
int isexec(char *path)
{
struct stat buf;
lstat(path, &buf);
if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode))
return (1);
return (0);
}
static char *find_exec_readdir(char *paths, char *cmd)
{
DIR *dir;
struct dirent *dirent;
char *exec;
exec = NULL;
if ((dir = opendir(paths)) != NULL)
{
while ((dirent = readdir(dir)) != NULL)
{
if (!strcmp(dirent->d_name, cmd))
{
exec = strdup(dirent->d_name);
break ;
}
}
if (closedir(dir))
dprintf(2, "Failed closing dir.\n");
}
return (exec);
}
char *find_exec(char *cmd, char **paths)
{
char *exec;
char *path;
char *tmp;
int i;
i = -1;
exec = NULL;
path = NULL;
if ((cmd[0] == '.' || cmd[0] == '/'))
{
if (isexec(cmd))
return (strdup(cmd));
return (NULL);
}
while (paths[++i])
if ((exec = find_exec_readdir(paths[i], cmd)) != NULL)
{
tmp = strjoin(paths[i], "/");
path = strjoin(tmp, exec);
free(tmp);
free(exec);
break ;
}
return (path);
}
int handle_return_status(int status)
{
int sig;
int i;
if (!WIFEXITED(status) && WIFSIGNALED(status))
{
sig = WTERMSIG(status);
i = -1;
while (++i <= 13)
{
if (print_signal_error(sig))
{
return (-1);
}
}
dprintf(2, "Process terminated with unknown signal: %d\n", sig, NULL);
return (-1);
}
return (0);
}
int main(int argc, char *argv[])
{
char *input = NULL;
char **command = NULL;
int should_run = 1;
int status = 0;
(void)argc;
(void)argv;
if ((input = (char*)malloc(MAX_LINE*sizeof(char))) == NULL)
return (dprintf(2, "Failed to malloc, abort.\n"));
while(should_run){
printf("osh> ");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0)
return (dprintf(2, "error with creating chiled process\n"));
if(pid == 0){
fgets(input, MAX_LINE, stdin);
command = strsplit(input, ' ');
command[0] = find_exec(command[0], strsplit(getenv("PATH"), ':'));
if(execv(command[0], &command[1]) < 0)
return (dprintf(2, "Error in execution.\n"));
//should_run = 0;
}
waitpid(pid, &status, 0);
handle_ret_status(status);
}
return 0;
}

Why are some functions not supported by my shell?

I am experimenting with writing a simple shell to support all the usual functions.
So far, despite my haphazard approach to it, I have been met with success - I have been able to fork() new processes with parameters but other certain methods don't seem to run.
When I run my shell, functions like pwd, ls, help work whilst other functions such as cd, mkdir don't - why is this & what research/investigation can I do in order to begin to address this?
I've included my code so far (although I'm not sure if it will actually help).
Thanks very much
EDIT: My output, when my shell is running in cygwin, if I type "cd /cygdrive/c" the output is "Command not found"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
const int MAX_ARGS = 9;
size_t nBytes = 64; //Data type representing size of objects (unsigned)
int bytesRead = -1;
char *cmd = NULL;
char prompt[] = "DaSh-> ";
int argc;
char **argv;
int pid;
int childpid;
int status;
void process();
int readcmd();
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
while (1) {
bytesRead = -1;
printf(prompt);
while (bytesRead == -1) {
bytesRead = readcmd();
}
process(cmd); //process the single line input into arguments
if (strcmp(argv[0], "exit") == 0) { //If user types exit
exit(0);
}
childpid = fork();
if (childpid == 0) { //This is run by the child
execvp(argv[0], argv);
printf("Command not recognised\n");
exit(1);
}
else if (childpid > 0 ) {
waitpid(-1, &status, 0);
}
}
return 0;
}
int nrows = 10;
int ncolumns = 2;
void process(char argStr[]) {
argv = malloc(MAX_ARGS * sizeof(char *)); //Array of pointers to the char first letter of each argument
char delims[] = " \n"; //Delimit about space
int i = 0;
argv[i] = strtok(argStr, delims);
while (argv[i] != NULL) {
argv[++i] = strtok( NULL, delims);
}
argc = i;
}
int readcmd() {
return getline(&cmd, &nBytes, stdin);
}

C - WHILE Loop with fork() / pipe() inside

I have a problem where I must implement a key logger into a shell we have made in class. I am having trouble getting the flow of the program within a while loop to continue looping after a child process is created and it has ran execlp().
Here is a simple program I have made to work on the part I am having trouble with.. My main program, pipe.c, includes the parent/child process with a while loop that "should" continue getting an input from the user with fgets(), create a child process, use dup2(), write to stdout, then the child process invoke the receive.c executable which will get the input from stdin and display it..
/* file: pipe.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int key_logger_on = 0;
int p[2];
pid_t pid;
char str[256];
char input[1024];
int status;
char * file = "test.txt";
printf("Input :: ");
while(fgets(input, sizeof(input), stdin)) {
if (pipe(p)==-1) {
perror("Pipe create error");
exit(1);
}
if ((pid=fork())==-1) {
perror("Fork create error");
exit(1);
}
if (pid==0) {
close(p[1]); // Close write
dup2(p[0],0);
close(p[0]);
execlp("receive",file,NULL);
}
else {
close(p[0]); // Close read
fflush(stdout);
dup2(p[1],1);
close(p[1]);
write(1, input, strlen(input)+1);
waitpid(pid, NULL, 0);
}
printf("Input :: ");
}
}
Here is the simple receive.c that gets the stdin of the input and displays it. The file is just a test of passing a parameter.
/* file: receive.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char input[256];
fgets(input, sizeof(input), stdin);
printf("FILE: %s RECEIVE: %s", argv[0],input);
return 0;
}
Right now, all this does for me is when ran the first time, it gets the input, sends it to stdout, child calls receive, prints out the input, and then the whole parent program exits, the while loop is ignored, everything just ends. I'm very new to forks and pipes so this is very frustrating to deal with! Even made me post a question on here for the first time! Thank you very much in advance.
Did it today as repetition task for me . CHeck this code . I tested it with your receive too :
#define PREAD 0
#define PWRITE 1
/*
*
*/
int main(int argc, char** argv) {
int key_logger_on = 0;
int pIn[2];
int pOut[2];
pid_t pid;
char str[256];
char input[1024] = "";
int status;
char file[] = "test.txt";
char buf;
printf("Input :: ");
while (fgets(input,sizeof(input),stdin)) {
char nChar;
int nResult;
if (pipe(pIn) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(pOut) < 0) {
close(pIn[PREAD]);
close(pIn[PWRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
pid = fork();
if ( pid==0) {
// child continues here
// redirect stdin
if (dup2(pIn[PREAD], 0) == -1) {
perror("stdin");
return -1;
}
// redirect stdout
if (dup2(pOut[PWRITE], 1) == -1) {
perror("stdout");
return -1;
}
// redirect stderr
if (dup2(pOut[PWRITE], 2) == -1) {
perror("stderr");
return -1;
}
// all these are for use by parent only
close(pIn[PREAD]);
close(pIn[PWRITE]);
close(pOut[PREAD]);
close(pOut[PWRITE]);
// run child process image
nResult = execl("receive",file,NULL);
exit(nResult);
} else if (pid > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(pIn[PREAD]);
close(pOut[PWRITE]);
write(pIn[PWRITE], input, strlen(input));
// char by char reading
while (read(pOut[PREAD], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// close we done
close(pIn[PWRITE]);
close(pOut[PREAD]);
}
printf("Input :: ");
}
}

Resources