How to detect EOF in piped data? - c

I'm creating a C-program under Linux, and I want to catch FFMpeg's output-data and forward it. To do so, I'm calling FFMpeg via (an implementation of) popen2. It all is working fine: I'm able to get FFMpeg's data out of the pipe via read(). Things start to get awkward if FFMpeg stops working. read() seems to be blocking, which is the expected behaviour, but it never exits when the pipe is closed.
I learned that, for read() to break and detect an EOF, the pipe should be closed on both ends, so when I detect that FFMpeg is crashed or killed, I close the reading-end of my pipe. I'm assuming that, since FFMpeg is closed, it disconnects its end of the pipe.
My assumption seems to be wrong: when I close the reading-end of the pipe by using close(), the program still seems to be hanging in read().
Would somebody be able to explain me what's wrong with my assumption and point me in the right direction so that I can properly detect when the program that I'm piping to or from (in this case FFMpeg) has stopped sending data?
The relevant code:
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "helper.h"
#define READ 0
#define WRITE 1
FILE *sink_stream;
int ffmpeg_sink;
pid_t popen2_2(const char **command, int *infp, int *outfp);
void* pthread_sink_ffmpeg(void *arg);
pid_t popen2_2(const char **command, int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
int devNull = open("/dev/null", O_WRONLY);
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) //first fork
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
//dup2(devNull,2); //pipes stderr to /dev/null....
execvp(*command, command);
_exit(1);
}
if (infp == NULL)
{
close(p_stdin[WRITE]);
close(p_stdin[READ]);
}
else
{
*infp = p_stdin[WRITE];
}
if (outfp == NULL)
{
close(p_stdout[WRITE]);
close(p_stdout[READ]);
}
else
{
*outfp = p_stdout[READ];
}
return pid;
}
void* pthread_sink_ffmpeg(void *arg)
{
char *pbuffer;
int length, res;
pbuffer = malloc(4096);
while(1)
{
/* Wait for input */
dbgfprintf("Waiting for input...");
while( (length = read(ffmpeg_sink, pbuffer, 1024)) )
{
fprintf(stderr, "Read [ %d ]...\r", length);
}
/* RIP */
dbgfprintf("Done for now..");
}
free(pbuffer);
}
int main( void )
{
int wstatus;
pid_t child_pid, w;
pthread_t t_ffmpeg_source;
const char *command_ffmpeg[] = {"ffmpeg",
"-hide_banner",
"-re",
"-i", "http://relay.ah.fm/192k",
"-c:a", "libfdk_aac",
"-profile:a", "aac_he_v2",
"-b:a", "128k",
"-f", "adts",
"pipe:1",
NULL};
child_pid = popen2_2(command_ffmpeg, NULL, &ffmpeg_sink);
dbgfprintf("Started ffmpeg with pid [ %d ]...", child_pid);
pthread_create(&t_ffmpeg_source, NULL, pthread_sink_ffmpeg, NULL);
do {
w = waitpid(child_pid, &wstatus, WUNTRACED | WCONTINUED);
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
dbgfprintf("ffmpeg terminated...");
close(ffmpeg_sink);
pthread_join(t_ffmpeg_source, NULL);
}
This code is able to start the program, receive it's data, detect the execvp'd program crash/kill, but it never gets out of read() when the execvp'd program is killed.
I've also tried to read the pipe as a filestream;
sink_stream = fdopen(ffmpeg_sink, "r");
while( !feof(sink_stream) )
{
length = fread(pbuffer, 1, 4096, sink_stream);
fprintf(stderr, "Read [ %d ]...\r", length);
}
..which gives the same result: it reads the data but it doesn't get out of fread().
Any help would be highly appreciated!
Thanks in advance!

Thanks all for the comments.
Indeed, the while-loops are ineffective, but I've isolated this code from a bigger project so that I could troubleshoot this issue more effectively. That's also why the malloc() is still there, and indeed it is very pointless.
As Ian stated, it turns out to be as simple as closing the p_stdin[READ] and p_stdout[WRITE]. It all makes sense now: I figured that I didn't need to close them since the execvp'd program is using them but that's the whole thing: the execvp'd program (child) is using them, and not me (the parent).
popen2_2() now looks like this:
{
int p_stdin[2], p_stdout[2];
pid_t pid;
int devNull = open("/dev/null", O_WRONLY);
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) //first fork
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
//dup2(devNull,2); //pipes stderr to /dev/null....
execvp(*command, command);
_exit(1);
}
if (infp == NULL)
{
close(p_stdin[WRITE]);
close(p_stdin[READ]);
}
else
{
close(p_stdin[READ]);
*infp = p_stdin[WRITE];
}
if (outfp == NULL)
{
close(p_stdout[WRITE]);
close(p_stdout[READ]);
}
else
{
*outfp = p_stdout[READ];
close(p_stdout[WRITE]);
}
return pid;
}
I can happily continue coding now! Thanks all :)

Related

How to supply input to a thread which is polling for stdin, form another thread in the same process?

Referring to following code example, I want the main thread to supply the number num that the child thread is expecting using scanf.
I tried this way to write the wordcount (9) to stdin which is to be read by child thread, but it is not working.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
sleep(2);
char buffer[50];
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
int save_stdin = dup(fileno(stdin));
dup2(fileno(wfp), fileno(stdin));
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
dup2(save_stdin, fileno(stdin));
pclose(wfp);
}
pthread_join(tid, NULL);
}
Can someone suggest a correct way or any other alternative way to do this?
Thanks.
I don't think there is any good way for a process to write text to its own stdin; stdin is meant to be a way for the parent process (or the user, if the parent process is a Terminal window) to send data to your process, not for your process to send data to itself.
However, you could achieve a similar result by having your child thread use select() or similar to read input from both stdin and from the output end of a pipe; then your parent process can send data to the child process by writing to the input end of that same pipe.
Below is a modified version of your program demonstrating the technique. Note that the child thread will print out any text that you type into stdin; and also the main thread will send a line of text to the child thread once every 5 seconds, and the child thread will also print out that text. After the main thread has sent 5 messages to the child thread, the main thread will close its end of the pipe, causing the child thread to exit and then the process can exit cleanly as well.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
static int pipeReadFD = -1;
static int ReadTextFrom(const char * descriptionOfSender, int fd)
{
char buf[256];
const int numBytesRead = read(fd, buf, sizeof(buf)-1); // -1 so we always have room to place NUL terminator byte
if (numBytesRead > 0)
{
buf[numBytesRead] = '\0'; // make sure the string is NUL-terminated
printf("ReadTextFrom(): Read %i bytes from [%s]: [%s]\n", numBytesRead, descriptionOfSender, buf);
}
return numBytesRead;
}
void* init_on_sys_ready(void* terminalflag)
{
int num=0;
printf("Child thread: trying to read text from stdin\n");
while(1)
{
const int stdinFD = fileno(stdin);
const int maxFD = (pipeReadFD > stdinFD) ? pipeReadFD : stdinFD;
fd_set readFDSet;
FD_ZERO(&readFDSet);
FD_SET(stdinFD, &readFDSet);
FD_SET(pipeReadFD, &readFDSet);
const int selRet = select(maxFD+1, &readFDSet, NULL, NULL, NULL);
if (selRet >= 0)
{
if ((FD_ISSET(stdinFD, &readFDSet))&&(ReadTextFrom("stdin", stdinFD) <= 0)) break;
if ((FD_ISSET(pipeReadFD, &readFDSet))&&(ReadTextFrom("pipe", pipeReadFD) <= 0)) break;
}
else
{
perror("select");
break;
}
}
printf("Child thread exiting!\n");
return NULL;
}
int main(int argc, char ** argv)
{
int pipeFDs[2];
if (pipe(pipeFDs) < 0)
{
perror("pipe");
return -1;
}
pipeReadFD = pipeFDs[0];
int pipeWriteFD = pipeFDs[1];
pthread_t tid;
if (pthread_create(&tid, NULL, &init_on_sys_ready, NULL) != 0) {
printf("Failed to initialize CLI\n");
exit(1);
}
int count = 0;
for (int count=0; count < 5; count++)
{
char buf[512];
snprintf(buf, sizeof(buf), "Hello #%i from main thread", ++count);
const size_t slen = strlen(buf);
if (write(pipeWriteFD, buf, slen) == slen)
{
printf("main() sent [%s] to the child thread via the pipe.\n", buf);
}
else
{
perror("write");
break;
}
sleep(5);
}
close(pipeWriteFD); // this will cause the child thread to exit ASAP
pthread_join(tid, NULL);
return 0;
}
popen's man states:
[...] the command's standard output is the same as that of the process that called popen()
So you just need a way to redirect stdout to stdin.
Which is exactly what pipe is for. It links an output fd with an input fd.
As pipe creates new fds, we need to use dup2 to replace stdin and stdout, as you've already did in your example code. Threads share the same memory, so you don't have to worry about any child/parent differences in fds.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
setbuf(stdin, NULL);
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
int save_stdin = dup(STDIN_FILENO);
int save_stdout = dup(STDOUT_FILENO);
int tube[2];
pipe(tube);
dup2(tube[0], STDIN_FILENO);
dup2(tube[1], STDOUT_FILENO);
char buffer[50] = {0};
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
pclose(wfp);
}
dup2(save_stdin, STDIN_FILENO);
dup2(save_stdout, STDOUT_FILENO);
pthread_join(tid, NULL);
}

Forking a process, and using pipes to transfer data from file line by line to child process

My project is to fork and then use the parent process to read data from a file line by line and then send each line to the child process, which has to use execve to send the line as an argument for bc, and the output has to go back to the parent process. Right now, I'm just trying to send the data to the child and receive it properly, but it doesn't work. I have to use select to figure out if the child has output for the parent to get.
I have a file with 5 lines on it, and I use a while loop to go through the file. For each line I thought I would get the line back from the child, but it only does one line or two and stops. Then I get the same line twice for some reason.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <wait.h>
int main(int argc, char *argv[])
{
alarm(60);
fd_set rfds;
fd_set r2fds;
struct timeval tv;
int retval;
int retval2;
int i = argc;
int rc;
FILE *fp;
fp = fopen (argv[1],"r");
char *args[3];
int j = 0;
while (j < i)
{
args[j] = argv[j+2];
j++;
}
int stdin_pipe_fds[2], stdout_pipe_fds[2], stderr_pipe_fds[2];
pipe(stdin_pipe_fds);
pipe(stdout_pipe_fds);
pipe(stderr_pipe_fds);
rc = fork();
if (rc == -1)
{
while (rc == -1)
{
rc = fork();
}
}
pid_t child;
pid_t parent;
if (rc == 0)
{
child = getpid();
close(stdin_pipe_fds[1]);
close(stdout_pipe_fds[0]);
close(stdout_pipe_fds[0]);
close(0);
dup(stdin_pipe_fds[0]);//, 0);
close(stdin_pipe_fds[0]);
close(1);
dup(stdout_pipe_fds[1]);//,1);
close(stdout_pipe_fds[1]);
close(2);
dup(stderr_pipe_fds[1]);//,2);
close(stderr_pipe_fds[1]);
}
if (rc > 0)
{
parent = getpid();
close(stdin_pipe_fds[0]);
close(stdout_pipe_fds[1]);
close(stderr_pipe_fds[1]);
}
char str[100];
char buf2[100];
char buf[100];
FD_ZERO(&rfds);
FD_SET(stdout_pipe_fds[0], &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
while ((fgets(str,100,fp)) != NULL)
{
if (rc > 0)
{
int wstatus;
int wsta;
int status;
wsta = write(stdin_pipe_fds[1],str,strlen(str));
retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
if (FD_ISSET(stdout_pipe_fds[0], &rfds))
{
wstatus = read(stdout_pipe_fds[0], buf2, 100);
printf("From child: %s\n",buf2);
if (wstatus == -1)
{
printf("read failed\n");
//continue;
}
//wsta = write(stdin_pipe_fds[1],str,strlen(str));
}
}
if (rc == 0)
{
alarm(60);
scanf("%s",buf);
printf("%s", buf);
}
}
fclose(fp);
}
In the child
you close stdout_pipe_fds[0] twice, and not stderr_pipe_fds[0].
The scanf("%s", buf) will strip the newline from the string; you might not want that. Stdout, if you started from a terminal, will be line buffered; that is, it will take a newline to trigger an actual write. Since you snuck in and replaced the underlying FD, it doesn't know that a new buffering strategy may be appropriate.
In the parent:
you treat read and write as if they return a status; they return the number of bytes read or written. If it reads 0 bytes, the buf will contain the values from the previous read. I am not sure of the purpose of the read(), I would assume it just messes things up. I think you need to draw a picture of how the file descriptors are linked.
Suggestions: There is a generally accepted idiom when using fork:
if ((rc = fork()) == 0) {
/* do child stuff */
} else if (rc != -1) {
/* do parent stuff */
} else {
/* do error stuff */
}
Which is mainly followed to avoid warping peoples brains. It is really hard to read when it is interleaved. The "K" in K&R once quipped that "... if you're as clever as you can be when you write it, how will you ever debug it?"
The close(0); dup(); is much better expressed as dup2(fd, 0). The mere fact that you can compile code on your machine assures that dup2 functions correctly.

Write program that pretends to be a TTY

I'm writing a program that reads input from stdin, manipulates the input, and writes output to stdout. However, many programs check whether stdin is a terminal or a pipe (by calling a function like isatty), and generate output differently. How do I have my program pretend to be a TTY?
The solution should work on both Linux and macOS. Any programming language that generates a standalone binary is acceptable, but Go is preferred.
Note that I'm asking a programming question, not asking for a tool. So, things like script or unbuffer is not something I'm looking for.
The following is fully working code for running a command in a pty and capturing its output. (Not as many lines as you may have thought.)
#include <signal.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <util.h>
pid_t child = 0;
void sighandler(int signum) {
if (child > 0) {
killpg(child, signum);
exit(signum);
}
}
// Run a command in a pty.
// Usage: /path/to/this/binary command to run
int main(int argc, char *argv[]) {
if (argc < 2) {
return EX_USAGE;
}
int master;
child = forkpty(&master, NULL, NULL, NULL);
if (child == -1) {
perror("failed to fork pty");
return EX_OSERR;
}
if (child == 0) {
// we're in the child process, so replace it with the command
execvp(argv[1], argv + 1);
perror("failed to execute command");
return EX_OSERR;
}
// trap kill signals and forward them to child process
signal(SIGHUP, sighandler);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
const int buf_size = 1024;
char buf[buf_size];
fd_set fds;
ssize_t bytes_read;
// forward the output continuously
while (1) {
FD_ZERO(&fds);
FD_SET(master, &fds);
if (select(master + 1, &fds, NULL, NULL, NULL) > 0 && FD_ISSET(master, &fds)) {
bytes_read = read(master, buf, buf_size);
if (bytes_read <= 0) {
return EXIT_SUCCESS;
}
if (write(STDOUT_FILENO, buf, bytes_read) != bytes_read) {
perror("failed to write to stdout");
return EX_OSERR;
}
}
}
}

Simple shell with indirect input

I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh

UNIX Commands Implemented in C

For my Operating Systems class I have an assignment due that is built onto a previous assignment. Unfortunately my previous project doesn't work correctly in addition to me not knowing where I need to start for the next project. The code which I have below is suppose to mimic a simple UNIX/Linux shell with some additional commands that cannot be performed with execvp: background processing via the ampersand operator, the 'jobs' shell command: list the pids of all living child processes (i.e. not ones that have terminated), "reaping" of "zombie" processes, and the 'cd' shell command: change the shell's working directory.
I believe, everything but the "jobs" command, and "cd" command work, but I'm not sure why these two don't.
The next assignment is to add some I/O redirection in the form of "mysh$ cmd arg1 arg2 argN > file.out" which I don't know where to even really begin...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char **argv) {
char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
int jobs[100];
int jobList = 0;
int background;
ssize_t rBytes;
int aCount;
pid_t pid;
int status;
while(!feof(stdin)) {
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
rBytes = read(0, bBuffer, BUFSIZ-1);
if(rBytes == -1) {
perror("read");
exit(1);
}
bBuffer[rBytes-1] = '\0';
if(!strcasecmp(bBuffer, "exit")){
exit(0);
}
sPtr = bBuffer;
aCount = 0;
do {
aPtr = strsep(&sPtr, " ");
pArgs[aCount++] = aPtr;
} while(aPtr);
background = (strcmp(pArgs[aCount-2], "&") == 0);
if (background)
pArgs[aCount-2] = NULL;
if (strlen(pArgs[0]) > 1) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
jobs[jobList] = pid;
jobList++;
if(!strcasecmp(pArgs[0], "jobs")){
for(int i; i<jobList; i++) {
if(kill(jobs[i],0)==0){
printf(jobs[i]);
}
printf("these are jobs\n");
exit(1);
}
if(!strcasecmp(pArgs[0], "cd")){
int ret;
if (!pArgs[1])
strcpy(bBuffer, "pwd");
ret = chdir(pArgs[1]);
strcpy(bBuffer, "pwd");
exit(1);
}
fclose(stdin);
fopen("/dev/null", "r");
execvp(pArgs[0], pArgs);
exit(1);
} else if (!background) {
pid = waitpid(pid, &status, 0);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
}
}
}
return 0;
}
First you;ll want to parse your line and detect that you need to redirect to a file. So let;s say you use strsep or whatever and you found out output is going to file.out or input is coming from file.in.
At this point you want to redirect output using dup / dup2. For example, to redirect STDOUT:
int
do_redirect(int fileno, const char *name)
{
int newfd;
switch (fileno) {
case STDOUT_FILENO:
newfd = open(name, O_WRONLY | O_CREAT, S_IRUSR | S_IRUSR);
break;
}
if (newfd == -1) {
perror("open");
return -1;
}
return dup2(fileno, newfd);
}
/* ... */
pid = fork();
do_redirect(STDOUT_FILENO, name);
Things to note:
I didn't test the code - it might not even compile
I didn't do much error-checking - you should (the way I did for open)
You need to implement STDIN_FILENO redirection on your own
Note how I used a separate function, your main is WAY to large as it is
Your code has something like 7 levels of indentation - ever heard about arrow code ?
Since this is homework, I will not give you code directly.
dup, dup2 and freopen are good to look at for input/output redirection.
fork for starting a concurrent process (ampersand)
You are on the right track using waitpid to reap child processes.

Resources